From 962626583b5bc8d2e6071bd3efa857408dee8bcf Mon Sep 17 00:00:00 2001 From: martyanov-av Date: Fri, 7 Aug 2026 11:52:52 +0300 Subject: [PATCH] Add AI translation page for yfm translate LLM providers --- ru/toc.yaml | 3 + ru/tools/docs/translate-ai.md | 276 ++++++++++++++++++++++++++++++++++ ru/tools/docs/translate.md | 2 + 3 files changed, 281 insertions(+) create mode 100644 ru/tools/docs/translate-ai.md diff --git a/ru/toc.yaml b/ru/toc.yaml index 68838671..19b80e5e 100644 --- a/ru/toc.yaml +++ b/ru/toc.yaml @@ -167,6 +167,9 @@ items: href: tools/docs/content.md - name: Локализация href: tools/docs/translate.md + items: + - name: AI-перевод + href: tools/docs/translate-ai.md - name: Выкладка на S3 href: tools/docs/publish-s3.md - name: Внесение изменений diff --git a/ru/tools/docs/translate-ai.md b/ru/tools/docs/translate-ai.md new file mode 100644 index 00000000..3a06cc88 --- /dev/null +++ b/ru/tools/docs/translate-ai.md @@ -0,0 +1,276 @@ +--- +keywords: ['translate', 'ai', 'llm', 'yandexgpt', 'openai', 'openrouter', 'anthropic', 'перевод', 'машинный перевод'] +--- +# AI-перевод + +Команда `{{PROGRAM}} translate` умеет переводить документацию большими языковыми моделями (LLM). Поддерживаются провайдеры `yandexgpt`, `openai`, `openrouter` и `anthropic`. + +Пайплайн тот же, что и у [остальных провайдеров перевода](translate.md): текст извлекается из разметки, переводится и собирается обратно. Разметка Markdown, HTML-теги, код и Liquid-конструкции в модель не попадают - переводятся только текстовые сегменты. + +Провайдер здесь описывает протокол API, а не конкретного вендора: любую совместимую инсталляцию (self-hosted модель, внутренний шлюз) можно подключить тем же провайдером, [заменив адрес API](#custom-api). + +## Быстрый старт {#quickstart} + +1. Получите ключ API и передайте его через переменную окружения или опцию `--auth` (значение или путь к файлу с токеном): + + ```bash + export OPENAI_API_KEY="sk-..." + ``` + +2. Оцените объем перевода без запросов к API: + + ```bash + {{PROGRAM}} translate -i . -o ./translated --provider openai --source ru --target en --dry-run + ``` + + В строке `PROCESSED` будет прогноз количества запросов и токенов. Файлы в output при этом собираются с исходным, непереведенным текстом - не принимайте их за результат перевода. + +3. Попробуйте перевод на одном файле или разделе: + + ```bash + {{PROGRAM}} translate -i . -o ./translated --provider openai --source ru --target en \ + --files ru/index.md --cache-dir .translate-cache + ``` + + Проверьте качество результата и при необходимости настройте [глоссарий](#glossary) или [промпты](#prompts). + +4. Запустите полный прогон с кэшем и копированием ассетов: + + ```bash + {{PROGRAM}} translate -i . -o ./translated --provider openai --source ru --target en \ + --cache-dir .translate-cache --copy-assets + ``` + +5. Проверьте результат: повторный запуск той же команды должен показать `requests: 0` - все сегменты берутся из [кэша](#cache). Переведенную версию можно собрать обычным `{{PROGRAM}} build`. + +Ошибка одного файла или превышение лимитов не останавливают прогон: упавшие файлы помечаются `ERR`, остальные продолжаются. Перезапуск команды доведет хвосты - уже переведенные сегменты возьмутся из кэша. + +## Провайдеры {#providers} + +#| +|| **Провайдер** | **API** | **Модель по умолчанию** | **Переменные окружения** || +|| `yandexgpt` | [Yandex AI Studio](https://yandex.cloud/ru/docs/ai-studio/) | `yandexgpt-lite` | `YANDEX_API_KEY`, `YC_IAM_TOKEN` || +|| `openai` | [OpenAI Chat Completions](https://platform.openai.com/docs/api-reference/chat) | `gpt-4o-mini` | `OPENAI_API_KEY` || +|| `openrouter` | [OpenRouter](https://openrouter.ai/docs) | `openai/gpt-4o-mini` | `OPENROUTER_API_KEY` || +|| `anthropic` | [Anthropic Messages](https://docs.anthropic.com/en/api/messages) | `claude-sonnet-4-5` | `ANTHROPIC_API_KEY` || +|# + +Авторизация: + +* `yandexgpt` - IAM-токен (`t1.`) или OAuth-токен (`y0_`) передаются как `Bearer`, любое другое значение - как `Api-Key` сервисного аккаунта. Дополнительно требуется `--folder` - [идентификатор каталога](https://yandex.cloud/ru/docs/resource-manager/operations/folder/get-id), если `--model` задана коротким именем (`yandexgpt-lite`). Полный URI модели (`gpt:///yandexgpt/latest`) можно указывать без `--folder`. +* `openai`, `openrouter` - Bearer-ключ. +* `anthropic` - ключ в заголовке `x-api-key`. + +### Подключение совместимых инсталляций {#custom-api} + +Self-hosted модель или внутренний шлюз с совместимым API подключается тем же провайдером с опцией `--api-base`. Путь запроса доклеивается к базе автоматически: + +#| +|| **Провайдер** | **База по умолчанию** | **Путь запроса** || +|| `yandexgpt` | `https://llm.api.cloud.yandex.net` | `/foundationModels/v1/completion` || +|| `openai` | `https://api.openai.com/v1` | `/chat/completions` || +|| `openrouter` | `https://openrouter.ai/api/v1` | `/chat/completions` || +|| `anthropic` | `https://api.anthropic.com/v1` | `/messages` || +|# + +Для `openai`, `openrouter` и `anthropic` включайте `/v1` в базу. Базу можно задать и переменными окружения `OPENAI_BASE_URL`, `OPENROUTER_BASE_URL`, `ANTHROPIC_BASE_URL`. + +Если шлюз требует свою схему авторизации, передайте заголовки опцией `--api-header` (можно повторять). Пользовательские заголовки перекрывают стандартные, поэтому так можно целиком заменить авторизацию. Опция `--auth` при этом формально обязательна - передайте заглушку: + +```bash +{{PROGRAM}} translate -i . -o ./translated \ + --provider openai \ + --api-base https://llm.internal.example.com/v1 \ + --model my-model \ + --auth dummy \ + --api-header "Authorization: OAuth $(cat ~/.tokens/llm)" \ + --source ru --target en --cache-dir .translate-cache +``` + +Путь запроса для каждого провайдера фиксирован: если шлюз использует нестандартный путь, переопределить его нельзя. + +## Справочник опций {#options} + +Общие опции команды (`--source`, `--target`, `--files`, `--include`, `--exclude`, `--dry-run` и другие) описаны на странице [Локализация](translate.md). Опция `--target` может быть передана несколько раз - перевод выполнится на каждый язык. Ниже - опции AI-провайдеров. + +#| +|| **Опция** | **По умолчанию** | **Описание** || +|| `--auth` | из переменной окружения | Токен или путь к файлу с токеном. В файл конфигурации класть нельзя || +|| `--model` | зависит от провайдера | Идентификатор модели || +|| `--folder` | - | Идентификатор каталога Yandex AI Studio. Только для `yandexgpt`, обязателен при коротком имени модели || +|| `--api-base` | URL API провайдера | База URL для [совместимых инсталляций](#custom-api) || +|| `--api-header` | - | Дополнительный HTTP-заголовок в формате `"Name: value"`. Можно повторять. Перекрывает стандартные заголовки || +|| `--system-prompt` | встроенный | Системный промпт: строка или путь к файлу. См. [Промпты](#prompts) || +|| `--user-prompt` | встроенный | Пользовательский промпт: строка или путь к файлу || +|| `--prompt-mode` | `append` | `append` - ваш системный промпт добавляется к встроенному, `replace` - полностью заменяет его || +|| `--glossary` | - | Путь к YAML-файлу с обязательными переводами терминов, относительно input. См. [Глоссарий](#glossary) || +|| `--judge` | выключено | Оценка качества перевода второй моделью. См. [Оценка качества](#judge) || +|| `--judge-model` | модель перевода | Модель для оценки качества || +|| `--judge-threshold` | `70` | Порог: сегменты с оценкой ниже попадают в отчет и в лог || +|| `--cache-dir` | - | Директория персистентного кэша переводов. См. [Кэш](#cache) || +|| `--no-cache` | - | Отключить кэш для текущего запуска || +|| `--temperature` | `0` | Температура сэмплирования. `0` - детерминированный перевод || +|| `--max-output-tokens` | `4000` | Максимум токенов в одном ответе модели || +|| `--max-batch-tokens` | `2000` | Бюджет входных токенов одного запроса. Сегменты группируются в батчи до этого лимита || +|| `--max-concurrency` | `5` | Максимум одновременных запросов к API || +|| `--retry` | `3` | Число повторов при временных ошибках API || +|| `--timeout` | `60000` | Таймаут одного запроса в миллисекундах || +|# + +### Конфигурация в файле {#config} + +Все опции, кроме `--auth`, можно зафиксировать в секции `translate` [файла конфигурации](../../settings.md) `.yfm`. Имена - в camelCase, флаги командной строки имеют приоритет: + +```yaml +translate: + provider: openai + model: gpt-4o-mini + cacheDir: .translate-cache + maxConcurrency: 2 + apiHeaders: + X-Custom-Header: value +``` + +Токен в конфигурации хранить нельзя: команда завершится ошибкой `Do not store authToken in public config`. Используйте переменные окружения или `--auth`. + +### Промпты {#prompts} + +Встроенный системный промпт настроен на технический перевод: сохранять разметку, не переводить код и идентификаторы, не добавлять пояснений. Свои инструкции можно добавить к нему (`--prompt-mode append`, по умолчанию) или полностью заменить его (`--prompt-mode replace`). + +Значение `--system-prompt` и `--user-prompt` - строка или путь к файлу. Поддерживаются плейсхолдеры: + +* `not_var{{source}}`, `not_var{{target}}` - языки перевода; +* `not_var{{glossary}}` - глоссарий в текстовом виде; +* `not_var{{context}}` - контекст документа (заголовок и путь файла); +* `not_var{{separator}}` - разделитель фрагментов; +* `not_var{{fragments}}`, `not_var{{text}}` - переводимые фрагменты (только в `--user-prompt`). + +Пример: потребовать соблюдения корпоративного тона: + +```bash +{{PROGRAM}} translate -i . -o ./translated --provider openai --source ru --target en \ + --system-prompt "Use formal tone. Address the reader as 'you'." +``` + +### Глоссарий {#glossary} + +Обязательные переводы терминов задаются YAML-файлом (формат тот же, что у провайдера `yandex`): + +```yaml +glossaryPairs: + - sourceText: оглавление + translatedText: table of contents + - sourceText: сборка + translatedText: build +``` + +```bash +{{PROGRAM}} translate -i . -o ./translated --provider openai --source ru --target en \ + --glossary glossary.yaml +``` + +## Кэш переводов {#cache} + +Опция `--cache-dir` включает персистентный кэш: пары «сегмент - перевод» сохраняются на диск, и повторные запуски отправляют в модель только новые и измененные сегменты. Кэш сбрасывается на диск после каждого обработанного файла, поэтому прерывание прогона безопасно - перезапуск продолжит с того же места. + +Как устроен кэш: + +* На каждую комбинацию «провайдер + модель + пара языков» создается отдельный файл `<провайдер>.<модель>.<источник>-<цель>.json`. Смена `--model` не затирает кэш другой модели, но и не использует его. +* Изменение промптов или глоссария автоматически инвалидирует кэш: сохраненные переводы устаревают и выполняются заново. Обновление CLI со встроенными промптами действует так же. +* `--no-cache` отключает кэш на один запуск, не удаляя сохраненные переводы. + +Директорию кэша имеет смысл коммитить в репозиторий или сохранять между запусками CI - тогда при регулярных переводах оплачиваются только изменившиеся сегменты. + +## Оценка качества {#judge} + +Опция `--judge` включает оценку перевода второй моделью: каждая пара «оригинал - перевод» получает балл от 0 до 100. Режим строго опциональный - расход токенов вырастает примерно вдвое. + +```bash +{{PROGRAM}} translate -i . -o ./translated --provider openai --source ru --target en \ + --cache-dir .translate-cache --judge --judge-model gpt-4o --judge-threshold 80 +``` + +По умолчанию оценивает та же модель, что переводила. Это удобно для поиска грубых ошибок, но такая самооценка завышена. Для честного сравнения используйте `--judge-model` с моделью не слабее переводящей: слабый судья не заметит ошибок сильного переводчика. + +Результаты: + +* Сегменты с оценкой ниже `--judge-threshold` попадают в лог как `WARN` с баллом и причиной. +* В output записывается отчет `translate-quality.<язык>.json`: + + ```json + { + "model": "gpt-4o", + "threshold": 80, + "scored": 214, + "averageScore": 93.4, + "low": 2, + "segments": [ + { + "path": "ru/tools/docs/build.md", + "source": "Сборка проекта выполняется командой...", + "translation": "The project is built with...", + "score": 55, + "issue": "Omitted the second sentence" + } + ] + } + ``` + + В `segments` попадают только сегменты ниже порога, отсортированные от худших к лучшим. +* Итоговая строка в логе: `judge: 214 units scored, average score 93.4/100, 2 below threshold 80`. Первое число - количество оцененных сегментов, не балл. + +Оценка не влияет на результат перевода и не прерывает прогон: сбой оценки отдельного батча логируется и пропускается. В `--dry-run` оценка не выполняется. + +## Как читать лог {#log} + +#| +|| **Строка** | **Что означает** || +|| `TRANSLATE <файл>` | Файл взят в работу. Если строки нет - файл не попал в scope прогона (фильтры `--files`, `--include`, `--exclude`, язык) || +|| `SKIPPED [reason] <файл>` | Файл отфильтрован; в скобках причина: `exclude`, `include`, `language` || +|| `REQUEST <файл> N units, ~X tokens` | Батч из N сегментов отправлен в модель. В `--dry-run` таких строк нет || +|| `TRANSLATED <файл>` | Файл переведен и записан в output || +|| `WARN ... Part is too big (~N tokens > M)` | Сегмент крупнее `--max-batch-tokens` и остался на исходном языке || +|| `WARN ... Batch of N fragments failed ... retrying one-by-one` | Ответ модели не разобрался на фрагменты, батч повторяется по одному сегменту || +|| `WARN <файл> Translation quality N/100: ...` | Оценка сегмента ниже `--judge-threshold` || +|| `ERR <файл> ...` | Файл не переведен, прогон продолжается. Фатальна только ошибка авторизации || +|| `PROCESSED requests: R input-tokens: I output-tokens: O bytes: B cached-units: C` | Итог по прогону: запросы, токены, объем текста и число сегментов из кэша || +|| `PROCESSED judge: N units scored, average score A/100, M below threshold T` | Итог оценки качества || +|# + +## Решение проблем {#troubleshooting} + +### Ошибка 429 (rate limit) {#throttling} + +CLI сам повторяет запрос до `--retry` раз с экспоненциальной паузой и учитывает заголовок `Retry-After`. Если лимиты API все равно превышаются, перезапустите прогон с меньшей параллельностью: + +```bash +{{PROGRAM}} translate -i . -o ./translated --provider openai --source ru --target en \ + --cache-dir .translate-cache --max-concurrency 2 +``` + +Уже переведенные сегменты возьмутся из кэша, в модель уйдут только оставшиеся. + +### WARN Part is too big {#too-big} + +Сегмент оказался крупнее `--max-batch-tokens` и остался на исходном языке. Увеличьте `--max-batch-tokens` (при необходимости вместе с `--max-output-tokens`) или разбейте текст в исходнике на более короткие абзацы. + +### Ответ модели обрезан {#truncated} + +Ошибки вида `response was truncated` означают, что модели не хватило лимита ответа. Увеличьте `--max-output-tokens` или уменьшите `--max-batch-tokens`. + +### Файл не переводится {#out-of-scope} + +Если правка в файле не попадает в перевод, сначала проверьте scope прогона: опции `--files` и `--include` сужают набор файлов, и изменения вне этого набора в прогон не попадают - в логе для такого файла нет строки `TRANSLATE`. Это не проблема кэша. + +Также помните, что кэш ведется отдельно на каждую модель: после смены `--model` переводы другой модели не переиспользуются. + +### В output исходный текст {#source-text-in-output} + +* После `--dry-run` это ожидаемо: файлы собираются без обращения к модели, с исходным текстом. +* Отдельный сегмент может совпадать с оригиналом и в обычном прогоне: модель осознанно не переводит текст, который уже на целевом языке, имена собственные и нетекстовые фрагменты. Пустой ответ модели никогда не принимается за перевод - в этом случае сохраняется исходный текст. + +## Известные ограничения {#limitations} + +* Страницы с блоками `::: page-constructor` внутри `.md` переводятся ненадежно ([translation#273](https://github.com/diplodoc-platform/translation/issues/273)). Пока рекомендуется исключать их из прогона через `--exclude`. +* Путь запроса для каждого провайдера фиксирован - шлюз с нестандартным путем API подключить не получится. +* Модель может повредить инлайн-разметку внутри сегмента (ссылки, выделение). Структурной валидации Markdown после перевода нет - такие случаи помогает находить [оценка качества](#judge). diff --git a/ru/tools/docs/translate.md b/ru/tools/docs/translate.md index accbcfbe..e204af93 100644 --- a/ru/tools/docs/translate.md +++ b/ru/tools/docs/translate.md @@ -5,6 +5,8 @@ keywords: ['translate', 'xliff', 'cat', 'i18n', 'l10n', 'localization', 'interna Для перевода документации на разные языки используется команда `{{PROGRAM}} translate`, которая обеспечивает быстрые [автоматические переводы](#auto). +Помимо перевода через [Yandex Translate](#auto), поддерживается [AI-перевод](translate-ai.md) большими языковыми моделями (провайдеры `yandexgpt`, `openai`, `openrouter` и `anthropic`). + Подкоманды `extract` и `compose` этой команды позволяют работать с системами [машинного перевода](#cat) (Computer Assisted Translation, или CAT), обмениваясь с ними `*.xliff` файлами. Поддерживается перевод как `*.md` файлов, так и `*.json` (в том числе `*.yaml`) файлов по [описанным схемам](#json-schemas).