textmachine/docs/experiments/00-provider-quirks.md

13 KiB
Raw Blame History

00. Провайдерские грабли — справочник для адаптеров

Назначение: единый список практических особенностей LLM-провайдеров, найденных полигоном при живых вызовах. Бэкенд-сессия читает это напрямую при написании/правке адаптеров internal/llm, чтобы не переоткрывать в Go то, что уже поймано в Python. Дата: 2026-07-04, проверять при смене версий моделей.

Разделение ролей: полигон (eval/, Python) — тупой замерочный зонд, характеризует сырое поведение провайдера (отказ/качество/латентность); бэкенд (backend/internal/llm, Go) — продакшн-адаптеры (retry/failover/ledger). Код не общий (разные языки) и не должен быть — но знание о граблях общее и живёт здесь.

Эндпоинты и модели (OpenAI-совместимые, если не указано иное)

Провайдер base_url Модель (на 2026-07-04) env-ключ
DeepSeek https://api.deepseek.com/v1 deepseek-v4-flash (deepseek-chat не в /models, но работает как алиас до депрекации 24.07.2026; эхает zh — см. ниже, брать v4-flash с thinking) DEEPSEEK_API_KEY
xAI Grok https://api.x.ai/v1 grok-4.20-0309-non-reasoning (явный не-reasoning слаг; grok-4-fast не в /v1/models, но работает как алиас — проверено HTTP 200; флагман grok-4.3) XAI_API_KEY
GLM (Z.ai) https://api.z.ai/api/paas/v4 glm-4.5-air (дёшево) / glm-4.6; в /models: glm-4.7, glm-5, glm-5.1, glm-5.2 ZAI_API_KEY
Gemini https://generativelanguage.googleapis.com/v1beta/openai gemini-2.5-flash, gemini-2.5-pro, gemini-3.1-pro-preview GEMINI_API_KEY
Kimi (Moonshot) https://api.moonshot.ai/v1 (intl, не .cn) kimi-k2.6 (доступны k2.5, k2.7-code) KIMI_API_KEY
OpenAI https://api.openai.com/v1 gpt-5-mini OPENAI_API_KEY
Qwen (DashScope intl) https://dashscope-intl.aliyuncs.com/compatible-mode/v1 qwen-flash DASHSCOPE_API_KEY
OpenRouter https://openrouter.ai/api/v1 зависит от хостера OPENROUTER_API_KEY
Локаль (ollama) http://localhost:11434/v1 (chat) или /api/generate тег модели не нужен

Thinking / reasoning — главный источник граблей

У всех современных моделей режим «размышления» включён по умолчанию и ломает роль переводчика: рассуждения съедают бюджет вывода → перевод обрезается или пустой, латентность ×35. Для роли переводчика/редактора reasoning обязателен к управлению. Как — зависит от провайдера:

Провайдер Поведение по умолчанию Как управлять (для перевода)
DeepSeek v4-flash гибридная reasoning-модель, thinking ON по умолчанию Управление (официальные доки, api-docs.deepseek.com/guides/thinking_mode, проверено 04.07): в body extra_body={"thinking":{"type":"enabled"|"disabled"}} (дефолт enabled). Т.е. выключить МОЖНО, параметр есть. НО для роли ПЕРЕВОДЧИКА не выключать: disabled→non-thinking режим, который на плотном CJK ЭХАЕТ исходник. Оставлять thinking ВКЛ; reasoning→reasoning_content, content чистый; max_tokens ≥8000 (иначе reasoning съест бюджет → content пуст, finish=length — это НЕ отказ). ⚠ thinking-режим НЕ поддерживает temperature/top_p/penalties (шлём — молча игнорируются). reasoning_effort: только high/max (low/medium→high, xhigh→max); none НЕ существует → 400. Для дешёвой роли ЭКСТРАКЦИИ (не перевод) thinking можно выключить — echo бьёт по переводу, не по JSON-выводу (перепроверить).
GLM-4.5-air / 4.6 thinking ON, не влезал в 180с отключить: extra_body: {"thinking": {"type": "disabled"}}
Gemini 2.5 Flash thinking ON, съедал max_tokens, молча обрезал перевод отключить: extra_body.google.thinking_config.thinking_budget: 0
Gemini 3.1 Pro thinking обязателен thinking_budget:0 → HTTP 400 «only works in thinking mode». НЕ отключать; дать большой max_tokens (≥8000)
gpt-5-mini (reasoning) reasoning ON, выжигал бюджет → пустой ответ reasoning_effort: "minimal"; без temperature; max_completion_tokens вместо max_tokens
Kimi K2.6 думающая, очень многословная: ~11k reasoning-токенов на абзац; reasoning в reasoning_content, ответ в content; при малом бюджете reasoning съедает лимит → content пуст (finish=length, не ошибка!) дать max_tokens ≥16000; ответ из content. Дорогой в роли редактора — reasoning биллится как выход
Qwen3.5 / локальные thinking ON ollama: think: false + num_predict cap. Иначе рассуждения (в поле thinking, не response) забивают контекст

Находка (эксп. 03): reasoning реально улучшает реалии/имена в переводе (羅生門: «Радзёмон»→«Рашо-мон»), но локально нежизнеспособен (8.6 мин/фрагмент). Открытый вопрос для бэкенда: проверить think-ON на быстром облачном черновике/редакторе как рычаг качества.

Параметры сэмплинга

Провайдер Грабля
Kimi K2.6 принимает только temperature: 1; иное → HTTP 400 «only 1 is allowed for this model»
gpt-5-mini temperature не принимается вовсе (см. выше)
Локаль (ollama /v1) top_k/min_p/repeat_penalty не пробрасываются (ollama issue #11325) → запекать в Modelfile (паттерн qwen3-vojo). Для llama-server — расширить запрос этими полями
Локаль (ollama) max_tokens/num_predict покрывает thinking + ответ вместе (в отличие от xAI, где thinking сверх лимита)

Контент-фильтры / safety

  • Gemini: safety_settings через OpenAI-совместимый слой не передаются (HTTP 400 «Unknown name safety_settings»). Дефолт фильтров у 2.5/3 — OFF (gap-2). Для явного контроля фильтров (роль судьи 18+) нужен нативный Gemini API, не OpenAI-слой.
  • Qwen Model Studio: внешний фильтр data_inspection_failed на вход/выход, неотключаем (риск молчаливых вырезаний) — не тестировано (нет ключа).
  • Детект отказов/вырезаний — см. eval/refusal_bench.py (паттерны soft-отказа + coverage-гейт по len_ratio/sent_cov, пороги в эксп. 02).

Транспорт / инфраструктура

  • Прокси на localhost: в env стенда webshare-прокси, NO_PROXY=<local> — WinINET-нотация, которую curl/urllib/Go не понимают → запрос к 127.0.0.1 уходит на прокси и получает 403. Лечение: явный NO_PROXY="localhost,127.0.0.1,0.0.0.0,::1" + в коде обходить прокси для loopback/172.x (бэкенд: local-адаптер с no-proxy транспортом — уже сделано).
  • DeepSeek cache-поля: в usage два варианта именования (бэкенд обрабатывает оба).
  • «Эхо» черновика (тихий отказ): deepseek-chat на плотной CJK-прозе воспроизводимо (3 из 3 ретраев на zh-фрагменте Лу Синя «祝福») возвращает оригинал вместо перевода (82% CJK в «переводе»). Не ошибка API — валидный ответ с неправильным содержимым; ретраи не помогают (детерминировано для фрагмента). Митигация в eval/editor_bench.py: детектор доли CJK (порог 15%) → фолбэк на другого провайдера (GLM перевёл тот же текст чисто). Бэкенд/гейт: детектор CJK-артефактов в русском выходе обязателен (смыкается с anti-omission coverage-гейтом Р7 и уже запланированным «детектором CJK-артефактов» — 7 из 18 моделей грешат); эскалация на другого провайдера, а не ретрай. NB: проверено на deepseek-chat; актуальную роль-модель deepseek-v4-flash перепроверить.
  • Таймауты: книжные чанки на локали занимают 63278 с (не 45 с как в чат-vojo). Нужен per-роль лег-таймаут ~300600 с + стриминг как keep-alive. Донорский транспорт vojo имел скрытый per-attempt потолок 60 с.

Календарь деприкаций / цен (мониторить ежеквартально)

  • deepseek-chatdeepseek-v4-flash к 24.07.2026.
  • Grok 4.1 Fast retired 15.05.2026 → слаги молча редиректят на grok-4.3 (цена ×9). Ретайрнулся дешёвый тир, НЕ сам xAI: grok-4.3 остаётся основным пермиссивным тиром канала B.
  • Аудит /models 04.07.2026 (воркфлоу) — с поправкой на перепроверку: и deepseek-chat, и grok-4-fast НЕ в списках /models (там v4-flash/v4-pro у DeepSeek; версионные слаги у xAI), НО оба работают как алиасы (перепроверено вживую: HTTP 200, переводят). Агент-аудитор написал «сняты» — это была неточность: «нет в /models» ≠ «не работает». deepseek-chat депрекируется 24.07.2026. У xAI reasoning/non-reasoning — РАЗНЫЕ слаги (grok-4.20-0309-non-reasoning / -reasoning / -multi-agent); для перевода брать non-reasoning. xAI usage отдаёт нестандартные cost_in_usd_ticks/num_sources_used — игнорировать. Урок: имя модели проверять не только /models, но и пробным вызовом — алиас может работать, даже если его нет в списке; и наоборот. Реальная причина сменить deepseek на v4-flash — не «chat умер», а ЭХО на zh (лечится thinking-on), см. раздел thinking.
  • Claude Sonnet 5 промо $2/$10 до 31.08.2026 (не закладывать в долгий прайс).
  • ПОПРАВКА (04.07, владелец): удалён только Anthropic (за дороговизну). OpenAI ОСТАЁТСЯ (ключ есть — GPT-5 nano/mini). Живой набор ключей у бэкенда и полигона: DEEPSEEK, ZAI, KIMI, OPENAI, GEMINI, XAI. Источник истины по стеку — architecture/05-decisions-log.md (D3) и Р4. Ранее эта строка ошибочно исключала OpenAI — исправлено оркестратором.

Провенанс

Кто что нашёл: эндпоинты/thinking/temp/safety — полигон (эксп. 02, 03, 04, живые вызовы); Grok-retired/cache-write Anthropic/per-attempt-60с — бэкенд (шаг 0 валидации); localhost-прокси — оба стенда независимо.