Land the polygon package as D39.61: R6 negative, fidelity gate passed with no cost found, DeepSeek swapped flash weights under the slug so the owner fork blocks paid runs

This commit is contained in:
Claude (backend session) 2026-08-01 00:57:33 +03:00
parent c010c32f3a
commit ca654eb1e0
15 changed files with 2390 additions and 5 deletions

View file

@ -1,7 +1,7 @@
# Журнал прогресса
> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-07-26: пак-20 закрыт ЦЕЛИКОМ D39.4153 — терминолог · флаговый стоп · двухсекционная инъекция · точечная ре-редактура · языковой экран; жанровый словарь отменён D39.47; полигон-пакеты 6/7/8 закрыты). **Источник истины по РЕШЕНИЯМ — `architecture/05-decisions-log.md` (D1D39.53); этот файл — ЖУРНАЛ.**
> - **Фазы/курс:** Ф0 ✅ · Ф1-инфра ✅ (D20D28; golden = инвариант №8) · арка «качество-первым» D26→D38.5 закрыта · **арх-ресет D39 исполнен ЦЕЛИКОМ** (7 слоёв → программа D39.1D39.10 → паки 1116, D39.13D39.24; статус слоёв — шапка-таблица `architecture/09-target-architecture.md`) · пере-прогон rerun2 прочитан (D39.20: операционка живьём, $0-резюм ×3 арма, $6.63<$15; планка ≤2 НЕ пройдена; анти-корреляция гладкость↔верность ×3 → mistral-редактор вон) · эксп ЗАКРЫТ (D39.22), итерация №2 отложена · **курс = разработка бэкенда: пак-17 «канал B вживую» ЗАКРЫТ ЦЕЛИКОМ (дизайн D39.26 · стройка D39.27 · дельта `allow` D39.28, 25.07); вокабуляр = ПАРА `violence`/`sexually-explicit`, остаток — ФАКТЫ ToS в зоне полигона (fail-closed до них)** (D39.25: в движке НЕТ понятия «18+» — generic content-labels × provider-capabilities; лейблы вне хешей · резолв до валидации · один хоп `chain[0]` · гранулярность = книга) → **КУРС АМЕНДИРОВАН ВЛАДЕЛЬЦЕМ 25.07 (D39.33): МАСШТАБ — ПОСЛЕДНИМ, после достройки алгоритмического идеала; доказательство эффективности крупных изменений — МИНИ-ПРОГОНЫ ~10 глав (детерминированные чекеры и скан остатка первыми, судья — только когда от него зависит решение).** **ОЧЕРЕДЬ (D39.40/51/53, актуализация 31.07):** пак-19 «голос/состояние» ЗАКРЫТ ЦЕЛИКОМ (D39.5456, вкл. малую пачку 14а+14б) → ХОЛОДНЫЙ мини-прогон ИСПОЛНЕН ДОСТАТОЧНО (D39.58, Р7 владельца: recall банка 0.918/0.980, банк приходит переведённым; хвосты 16/21/22/24/13бсо следующим живым прогоном или полигон-пакетом) → **ПАРАЛЛЕЛЬ D39.59: бэкенд-ресёрч общности фаза 1 ПРИНЯТ (D39.60 — долг 149 сайтов/45 файлов, книго-ось чиста: 4 сайта; ja→ru безопасно без Go, en→ru нет; план П0П5; СТОП — 5 вопросов §12 у владельца, строка 72) ∥ полигон Р6+P4 — отчёт пришёл, ждёт релея владельца** → фаза 2 общности по ответам ($0-части П1 → общий resnapshot-батч П0+П1+П2+П3+П4+дешёвая шестёрка пака-21 → П5 гейтится второй книгой) → арка АВТОНОМНОСТИ банка (веб-фетч пере-замер + 36а/36б/36в; премиса «решение = подпись» амендирована D39.59) → rewrite-пакеты общности → свип гипотез → добор идеала → МАСШТАБ целой книги (7,78 млн симв., ~2284 раздела) → пилот Ф2.5. Хроника треков A/B, exp15/16 и стройки паков — архивы ниже + D-лог.
> - **Фазы/курс:** Ф0 ✅ · Ф1-инфра ✅ (D20D28; golden = инвариант №8) · арка «качество-первым» D26→D38.5 закрыта · **арх-ресет D39 исполнен ЦЕЛИКОМ** (7 слоёв → программа D39.1D39.10 → паки 1116, D39.13D39.24; статус слоёв — шапка-таблица `architecture/09-target-architecture.md`) · пере-прогон rerun2 прочитан (D39.20: операционка живьём, $0-резюм ×3 арма, $6.63<$15; планка ≤2 НЕ пройдена; анти-корреляция гладкость↔верность ×3 → mistral-редактор вон) · эксп ЗАКРЫТ (D39.22), итерация №2 отложена · **курс = разработка бэкенда: пак-17 «канал B вживую» ЗАКРЫТ ЦЕЛИКОМ (дизайн D39.26 · стройка D39.27 · дельта `allow` D39.28, 25.07); вокабуляр = ПАРА `violence`/`sexually-explicit`, остаток — ФАКТЫ ToS в зоне полигона (fail-closed до них)** (D39.25: в движке НЕТ понятия «18+» — generic content-labels × provider-capabilities; лейблы вне хешей · резолв до валидации · один хоп `chain[0]` · гранулярность = книга) → **КУРС АМЕНДИРОВАН ВЛАДЕЛЬЦЕМ 25.07 (D39.33): МАСШТАБ — ПОСЛЕДНИМ, после достройки алгоритмического идеала; доказательство эффективности крупных изменений — МИНИ-ПРОГОНЫ ~10 глав (детерминированные чекеры и скан остатка первыми, судья — только когда от него зависит решение).** **ОЧЕРЕДЬ (D39.40/51/53, актуализация 31.07):** пак-19 «голос/состояние» ЗАКРЫТ ЦЕЛИКОМ (D39.5456, вкл. малую пачку 14а+14б) → ХОЛОДНЫЙ мини-прогон ИСПОЛНЕН ДОСТАТОЧНО (D39.58, Р7 владельца: recall банка 0.918/0.980, банк приходит переведённым; хвосты 16/21/22/24/13бсо следующим живым прогоном или полигон-пакетом) → **ПАРАЛЛЕЛЬ D39.59: бэкенд-ресёрч общности фаза 1 ПРИНЯТ (D39.60 — долг 149 сайтов/45 файлов, книго-ось чиста: 4 сайта; ja→ru безопасно без Go, en→ru нет; план П0П5; СТОП — 5 вопросов §12 у владельца, строка 72) ∥ полигон Р6+P4 ПРИНЯТ (D39.61 — Р6 отрицателен: боевые промпты остаются русскими; гейт верности D21 п.6 пройден «цены верности не обнаружено», wiring отложен до предмета, префилл — новый кандидат; ⚠ **DeepSeek сменил веса под слагом 31.07 — боевая черновая форма покупает пустые ответы, развилка §12.4 у владельца = БЛОКЕР платных прогонов и resnapshot, строка 74**)** → фаза 2 общности ($0-код, от развилки не зависит: П0 → П1 цель-шов → П2 скрипт-шов → П3 нарезка → П4 пак-манифест + дешёвая шестёрка растворённого пака-21 + малые 77/78; в конце ре-проба flash → ОДИН общий resnapshot по слову владельца) → арка АВТОНОМНОСТИ банка (веб-фетч пере-замер + 36а/36б/36в; премиса «решение = подпись» амендирована D39.59) → rewrite-пакеты общности → свип гипотез → добор идеала → МАСШТАБ целой книги (7,78 млн симв., ~2284 раздела) → пилот Ф2.5. Хроника треков A/B, exp15/16 и стройки паков — архивы ниже + D-лог.
> - **Стек:** draft deepseek-v4-flash (thinking ON, +банкнота `translator-banknote.md`) → **терминолог** (роль пар-пака, deepseek-v4-flash, батчи, языковой экран `target_script` — D39.42/45/51/53) → **editor deepseek-v4-pro БИЛИНГВ ИНТЕРИМ (D39.22; glm-5 резерв)**, промпт v3-discourse, инъекция двухсекционная (D39.45) → судья gemini-3.1-pro-preview (Ф2, полигон); канал B Mistral+grok; 7 ключей; ~$0.85/ранобэ (D30.4).
> - **ЕДИНЫЙ БЭКЛОГ — секция «Бэклог» ниже (введён 26.07 по решению владельца: одна таблица вместо наслоений; ревью-пасс оркестратора сверил её с коммитами и обсуждённым, добавил 7 строк). Норма прежняя: каждая петля обязана иметь диспозицию (решено / отложено-с-записью / отклонено); ведёт оркестратор. Историческая развёртка петель до 26.07 — в git-истории этого файла и в D-логе.**
> - Архивы хроники: `archive/PROGRESS-2026-07-04-10.md` (D31) · `archive/PROGRESS-2026-07-10-13.md` (D39.6-гигиена) · `archive/PROGRESS-2026-07-13-25.md` (слайс 25.07: стройка паков 1116, rerun2). Записи ниже — живой хвост.
@ -37,8 +37,11 @@
| **— ТЕКУЩАЯ ОЧЕРЕДЬ (D39.59) —** | | | | | |
| 72 | Ресёрч общности: фаза 1 ИСПОЛНЕНА и ПРИНЯТА (D39.60; карта = рабочая истина: 149 сайтов/45 файлов, книго-ось чиста, план П0П5) — **СТОП: 5 вопросов §12 у владельца** (П1-санкция · resnapshot-фигура · П6 · Р1Р4 · судьба пака-21); фаза 2 по ответам | владелец → бэкенд | блокер-очереди | ответы → фаза-2 директива | D39.59, D39.60 |
| 73 | Арка АВТОНОМНОСТИ банка (курс владельца: подпись опциональна): пере-замер веб-фетча под АВТОНОМНЫМ критерием (пакет-7 мерил не то — аддитив к KWIC против подписи) + механизмы 36а/36б/36в + Decl-шов (майненая сторона не пишет склонённых форм — пост-чек не примет 149 авто-строк; D39.60, бывший дефект #9 пака-21) + Р3-остаток; дизайн после ответов по общности | бэкенд/полигон | скоро | дизайн-арка → ратификация | D39.59, D39.60 |
| 74 | ⚠ **DeepSeek-V4-Flash-0731: развилка владельца — БЛОКЕР платных прогонов и resnapshot** (боевая черновая форма покупает пустые ответы): (а) флор 16000 = сдвиг `request_hash` ⇒ ехать В ОДНОМ resnapshot с фазой-2; хвост не закрывает (упор 2/5) · (б) слать `reasoning_effort` явно = приведение к вендорской модели (384K-норма), но амендмент `echoMineViolation`, возврат эха ~6% и качество черновика при `low` не судилось · (в) ждать бету (вышла 31.07); $0-код от развилки НЕ зависит | владелец | **СРОЧНО (блокер)** | подпись → фикс конфига/гейта (+ при (б): замер качества черновика и предмет для постинструкции/префилла) | D39.61, POLYGON §12.4 |
| 75 | Живой баг: `isCJKLang` числит ko, а `cjkShare`/CJK-leak/гард ремонта Hangul не содержат — движок считает ko-нарезку как CJK, печатает эхо-предупреждение и при этом эхо УВИДЕТЬ НЕ МОЖЕТ | бэкенд | скоро (носитель П2) | П2 скрипт-шов | D39.60, GENERALITY_RESEARCH §3.3 |
| 76 | Эмбед-плоскость `internal/lang/data/*.txt` НЕ хешируется (а `injection.txt` едет на провод, `target-ru.txt` даёт вердикты санитайзера) = канал тихой пере-оплаты вне контура Р6 — П0 «байты есть версия» ПЕРВЫМ в любой фазе 2 | бэкенд | скоро (П0, предусловие) | П0 | D39.60, GENERALITY_RESEARCH §6.1 |
| 77 | Ре-ген N=1 ПЕРЕД эскалацией для `cjk_artifact`: посылка «retry just re-produces them» (`disposition.go:138-148`) опровергнута на 0731 — эхо стохастично ПО ВЫЗОВУ (побайтно идентичная пара запросов → эхо/не-эхо); ре-ген ≈7.6× дешевле эскалации ($0.00096 против $0.007335); гейт D19.2 не ослабляется ни на байт | бэкенд | скоро (малой пачкой фазы 2) | фаза 2 | D39.61, POLYGON §10.5/§12.4 |
| 78 | Леджер денег = НИЖНЯЯ граница: «2xx body decode failed (call IS billed)» — провайдер списал, ретрай заледжерен, исходная попытка в `request_log` НЕ попадает (4 случая, ≤$0.009 за сессию); логировать billed-попытку | бэкенд | скоро (малой пачкой фазы 2) | фаза 2 | D39.61, POLYGON §12.5 п.6 |
| **— ПАК-21 «чекеры» (карта готова; предложение D39.60/Q5: РАСТВОРИТЬ — дешёвая шестёрка батчем в общий resnapshot, 6 дефектов в П1/П2/П3, #9 → строка 73, #12/K4a снять; якоря D39.39 устарели — актуальные в GENERALITY_RESEARCH §8) —** | | | | | |
| 25 | 12 дефектов чекеров с file:line (супрессор K5c · атрибуция K4b · inner_marker на тире · предохранитель K5a · заглавные/гомоглифы K2 · 两 в cheng_re · U+0301 в TokenizeCyrillic · мн.число в сид-тулинге · однознаковые ключи пост-чека) — приёмка против набора пакета-6 за $0 | бэкенд | скоро | пак-21 | D39.39(в), D39.40 |
| 26 | Переезд `translitInterjections` из Go в данные (`cheapgates.go:367`, улов владельца) | бэкенд | скоро | пак-21 | D39.40 |
@ -103,9 +106,13 @@
| 69 | Gemini API «под-18» обязательство на конечный продукт (независимо от лейблов) | владелец | когда-нибудь | Ф3 / отдельное решение | D39.32, D39.34(7) |
| 70 | Action-security gate перед выдачей tools/webfetch (D25 п.5) | бэкенд | когда-нибудь | Ф3 | D39.34(7) |
| 71 | Планы research/22: epub-tag-rewrite · Q7-леджер | бэкенд | когда-нибудь | Ф3 | D39.34(7) |
## Оркестратор №9 + полигон — Р6+P4 ПРИНЯТ (D39.61): Р6 отрицателен, гейт верности D21 п.6 пройден; ⚠ DeepSeek сменил веса под слагом — развилка у владельца, 01.08
Отчёт: [`archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md`](archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md) — **действующая редакция выводов = §12** (после 15-агентного адверсариального саморевью сессии и докупки судейского хвоста по прямой санкции владельца; три вывода первой редакции отозваны самой сессией). Деньги: $0.433 = $0.1475 в потолке + $0.2855 санкционированного хвоста; мой независимый ре-ран `spend.py` совпал до 2.9e-06. **Итоги:** (1) вендор-катовер `V4-Flash-0731` — боевая черновая форма покупает пустые ответы; сместился ДЕФОЛТНЫЙ эффорт (вендорская норма потолка для high/max — 384K против нашего флора 8000); **развилка (а) флор-16000 / (б) явный эффорт / (в) ждать — подпись владельца, строка 74, до неё платные прогоны СТОП**; (2) Р6 закрыт отрицательно — en-промпт не даёт ничего ни по одной детерминированной оси, боевые промпты остаются русскими; (3) гейт верности петли D21 п.6 пройден в ослабленной форме («цены верности у митигаций не обнаружено»; «митигации вернее» НЕ установлено — псевдо-репликация поймана саморевью); wiring отложен до предмета, новый сильнейший кандидат — префилл; (4) эхо на 0731 стохастично по вызову ⇒ ре-ген перед эскалацией ≈7.6× дешевле (строка 77); (5) леджер = нижняя граница денег (строка 78). Квирки синхронизированы с §12. Залендено: отчёт · quirks · харнесс `eval/promptlang/`.
## Оркестратор №9 — РЕСЁРЧ ОБЩНОСТИ ФАЗА 1 ПРИНЯТ (D39.60): долг 149 сайтов/45 файлов, книго-ось чиста, план П0П5; СТОП — пять вопросов владельцу, 31.07
Короткая приёмка по норме 30.07: отчёт (запись сессии — ниже, `### РЕСЁРЧ ОБЩНОСТИ фаза 1 ИСПОЛНЕН`) прочитан целиком, восемь несущих утверждений пере-проверены исполнением — все сошлись (вкл. побайтный повтор хеша пака `a28ed743c99c` и счёт 27/12 живых CJK-строк); два нита некритичны (счёт сайтов `ExportNormalize`, атрибуция Ш-1). Ратифицировано как карта (D39.60): вёдра A27/B6/C104/D12/E106 · заморозки З1З9 · актуальные якоря пака-21 (§8 отчёта — ссылки D39.39 устарели) · правило условного фолда §6.2. Новые строки: 75 (живой баг ko/Hangul → П2) · 76 (эмбед-плоскость без хеша → П0 первым). Строка 32 исправлена фактом (боевых call-site 6 и 2), Decl-находка уехала в арку банка (73). **На владельце пять вопросов §12 с моими рекомендациями (в D39.60): П1 в арку = ДА · один общий resnapshot = ДА (но после развилки DeepSeek из полигон-отчёта 31.07, который ждёт релея владельца) · П6 = не сейчас · Р1Р4 = сократить до Р2+Р4 · пак-21 = растворить.** Фаза 2 не начинается до ответов.
Короткая приёмка по норме 30.07: отчёт (запись сессии — ниже, `### РЕСЁРЧ ОБЩНОСТИ фаза 1 ИСПОЛНЕН`) прочитан целиком, восемь несущих утверждений пере-проверены исполнением — все сошлись (вкл. побайтный повтор хеша пака `a28ed743c99c` и счёт 27/12 живых CJK-строк); два нита некритичны (счёт сайтов `ExportNormalize`, атрибуция Ш-1). Ратифицировано как карта (D39.60): вёдра A27/B6/C104/D12/E106 · заморозки З1З9 · актуальные якоря пака-21 (§8 отчёта — ссылки D39.39 устарели) · правило условного фолда §6.2. Новые строки: 75 (живой баг ko/Hangul → П2) · 76 (эмбед-плоскость без хеша → П0 первым). Строка 32 исправлена фактом (боевых call-site 6 и 2), Decl-находка уехала в арку банка (73). **На владельце пять вопросов §12 с моими рекомендациями (в D39.60): П1 в арку = ДА · один общий resnapshot = ДА (но после развилки DeepSeek — D39.61, строка 74) · П6 = не сейчас · Р1Р4 = сократить до Р2+Р4 · пак-21 = растворить.** Фаза 2 не начинается до ответов.
## Полигон — РЕ-ЧЕК ToS ПО КАЛЕНДАРНОМУ ТРИГГЕРУ ИСПОЛНЕН ($0): дельт вердиктов НЕТ, но триггер стоит не на том документе, 31.07

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,7 @@
**Режим:** read-only, $0. Кода не тронуто ни строкой — `git status` по `backend/` пуст, ни одного `go build`/`go test`/`gofmt` с правкой не запускалось.
**Метод:** 8 срезов кода просканированы, каждый АДВЕРСАРИАЛЬНО перепроверен вторым агентом (автор≠ревьюер) с прогоном чужих grep-команд; отдельно 5 сквозных разборов (цены снапшота · синк с D-логом · пересборка пака-21 · сухой прогон второй пары · алгоритмы), каждый тоже кросс-чекнут. 26 проверяющих проходов; несущие числа я пере-мерил сам и помечаю это явно.
> **Ревью-шапка (оркестратор №9, 31.07): фаза 1 ПРИНЯТА, D39.60; фаза 2 — СТОП до пяти ответов владельца (§12).** Короткая приёмка по норме 30.07: отчёт прочитан целиком; восемь несущих утверждений пере-проверены исполнением — рецепт §6 воспроизвёл хеш пака моим прогоном (`a28ed743c99c`), счёт живых CJK-литералов совпал (27 строк / 12 файлов), ko/Hangul-асимметрия подтверждена (`cjkShare` = Han+кана, Hangul — в 5 сайзинг-сайтах и в 0 вердиктных), `ExportNormalize` действительно без цель-гейта, `embedded.go` действительно без хеширования, цитата-заморозка D39.24 в `repair.go:182-189` дословна («repayable only … as one deliberate change to the target seam»), `segmentation` не ставит ни один шиппинг-конфиг, Ш-1 (`goldenMaskedDiff`) построен. Два нита, не влияющих на выводы: (а) «6 прод-сайтов» `ExportNormalize` — якорей перечислено 7 (5×waverun + export + quality); (б) «Ш-1 построен вопреки записи в D-логе» — неточность: лендинг D39.17 Ш-1 записал, устарела только плановая записка (строка 604 D-лога). Рекомендации оркестратора по пяти вопросам §12 — в D39.60. ⚠ Контекст, узнанный ПОСЛЕ сдачи этого отчёта: DeepSeek 31.07 сменил веса flash под тем же слагом (отчёт полигона 31.07, приёмка отдельно по релею владельца) — исполнение «общего resnapshot» из §9 гейтится этой развилкой; сама карта от неё не зависит.
> **Ревью-шапка (оркестратор №9, 31.07): фаза 1 ПРИНЯТА, D39.60; фаза 2 — СТОП до пяти ответов владельца (§12).** Короткая приёмка по норме 30.07: отчёт прочитан целиком; восемь несущих утверждений пере-проверены исполнением — рецепт §6 воспроизвёл хеш пака моим прогоном (`a28ed743c99c`), счёт живых CJK-литералов совпал (27 строк / 12 файлов), ko/Hangul-асимметрия подтверждена (`cjkShare` = Han+кана, Hangul — в 5 сайзинг-сайтах и в 0 вердиктных), `ExportNormalize` действительно без цель-гейта, `embedded.go` действительно без хеширования, цитата-заморозка D39.24 в `repair.go:182-189` дословна («repayable only … as one deliberate change to the target seam»), `segmentation` не ставит ни один шиппинг-конфиг, Ш-1 (`goldenMaskedDiff`) построен. Два нита, не влияющих на выводы: (а) «6 прод-сайтов» `ExportNormalize` — якорей перечислено 7 (5×waverun + export + quality); (б) «Ш-1 построен вопреки записи в D-логе» — неточность: лендинг D39.17 Ш-1 записал, устарела только плановая записка (строка 604 D-лога). Рекомендации оркестратора по пяти вопросам §12 — в D39.60. ⚠ Контекст, узнанный ПОСЛЕ сдачи этого отчёта: DeepSeek 31.07 сменил веса flash под тем же слагом (D39.61, строка 74 бэклога) — исполнение «общего resnapshot» из §9 гейтится этой развилкой; сама карта от неё не зависит.
---

View file

@ -0,0 +1,788 @@
# Полигон: язык промптов (Р6) + fidelity-хвост анти-эхо P4 (петля D21 п.6)
**Сессия:** полигон, 31.07.2026. **Промт:** `docs/POLYGON_PROMPTLANG_ANTIECHO_SESSION_PROMPT.md`
(оркестратор №9, санкция владельца). **Потолок $0.15 суммарно.** **Сессия НЕ коммитит.**
Артефакты прогонов — `~/books/gu-zhenren/promptlang/` (вне git). Харнесс — `eval/promptlang/`.
> **Ревью-шапка (оркестратор №9, 01.08): ПРИНЯТО И ЗАЛЕНДЕНО, D39.61. Действующая редакция выводов = §12** — сам отчёт так предписывает; §1§11 сохранены как хроника, и три их вывода ОТОЗВАНЫ саморевью сессии (эхо-атрибуция few-shot · «пропуск» постинструкции · «митигации вернее базы»). Короткая приёмка: финальная редакция прочитана целиком; деньги проверены независимо — мой ре-ран `eval/promptlang/spend.py` дал те же 163 вызова / $0.433062, два пути сходятся до 2.9e-06; итог = $0.1475 в потолке промта + $0.2855 судейского хвоста по прямой санкции владельца; находка «леджер = нижняя граница» (4 billed-вызова вне `request_log`) забукана строкой 78. Дифф квирков сверен с §12 построчно — синхронизирован сессией; один остаточный хвост («few-shot стал ВРЕДЕН» в P4-superseded-строке) докорректирован при лендинге по §12.5 п.1. Дисциплина: пре-регистрация цела, амендмент §0-bis объявлен до продолжения трат, механизм всех трёх отозванных выводов назван самой сессией («вывод на агрегате до вскрытия единиц») — забран в промт-нормы оркестратора. Развилка §12.4 — у владельца (строка 74), до неё платные прогоны и resnapshot СТОП.
---
> **⚠ ПОРЯДОК ЧТЕНИЯ (после адверсариального ревью и добора судьи):** действующая редакция
> выводов — **§12** в конце файла. §1§11 сохранены как хроника (дисциплина D23.3) и в восьми местах
> опровергнуты §12.5 — не цитировать их числа, не сверившись с §12.5. §0 (пре-регистрация) заморожена
> и не переписывалась ни в одном пункте.
---
## §0. ПРЕ-РЕГИСТРАЦИЯ (записана ДО первой платной траты; ниже НЕ переписывается)
Причина, по которой это первый раздел: у проекта есть своя перепись случаев, когда критерий успеха
дописывался после результата (D37: «0 ошибок» = 36 ошибок; exp15 Q1b: заголовок был артефактом
судейского окна). Пре-регистрация — единственное, что отличает замер от рассказа о замере.
### 0.1 Конфигурация — заморожена
**Общее для обеих задач**
| Ось | Значение | Почему так |
|---|---|---|
| Окно трат | UTC 18:3024:00 31.07 | вне пиков DeepSeek UTC 0104 / 0610 (D39.7 §4: пик = ×2) |
| Слаги | live-фактчек `/models` **до** первой траты: deepseek → ровно `deepseek-v4-flash` + `deepseek-v4-pro`; xai → `grok-4.3` жив (`eval/promptlang/slugcheck.json`) | гардрейл CLAUDE.md |
| Квирки | deepseek thinking НЕ отключается (`reasoning:"off"` = no-op у deepseek); `max_tokens` ≥8000 держит движок; temperature в thinking-режиме игнорируется молча | `00-provider-quirks` |
| Зона | пишу в `eval/`, `docs/experiments/`, отчёт в `docs/archive/reports/`; `backend/` не правлю **ни байтом** — армы едут через свой `prompts_root` в пар-конфиге проекта-пробы и через `prompt_override` стадии | CLAUDE.md, зоны |
| Стенд | 1117 файлов `~/books/gu-zhenren/` (кроме `promptlang/`) захешированы ДО прогона, сверяются после | П8 холодного прогона |
**Задача A — «язык промптов» (Р6, D39.58 §6.4)**
| Ось | Значение | Почему так |
|---|---|---|
| Роль | ТЕРМИНОЛОГ (батчи по банку книги) | самая дешёвая роль с детерминированным форматом ответа; её парсер (`terminology.ParseReply`) и есть требуемый прибор |
| Арм A | боевой `terminologist.md` **побайтно** (`prompts-ru/` = `diff -r` с `backend/prompts` пуст) | «действующий ru-промпт» |
| Арм B | его **английский перевод** строка-в-строку: тот же порядок секций, 10 буллитов против 10, те же движковые маркеры ⟦TM-CANON⟧/⟦TM-NO-DST⟧, **пример формата `师父<TAB>наставник` побайтно тот же**, те же плейсхолдеры | правка примера формата = второй фактор (D39.52: именно эта строка чинит срыв языка) |
| Данные | **БД холодного прогона, копия**; тот же `book_id`, тот же снапшот ⇒ черновая волна не перекупается. Полезная нагрузка роли (150 кандидатов, KWIC, `drafts:`, батчинг) собирается ОДНИМ кодом из ОДНОЙ БД ⇒ байт-идентична по построению, а не по обещанию | «байт-НЕИЗМЕННЫЕ инъекции и данные» |
| Инъекция | ⟦TM-CANON⟧ пуст в обоих армах (подписанных строк в холодной БД нет) ⇒ тривиально идентична | |
| Метрики | ТОЛЬКО детерминированные, все — счётчики движка: `off_language` · `bad_lines` (в них же битый разделитель: строка без второго поля) · `unanswered` (аналог `parse_fail` на уровне терма) · `declined` · `consolidated` · `canon_conflicts`. Второй путь вывода — свой пересчёт по сырым ответам | судья не нужен |
| Что НЕ меряю | эхо на этой роли — оппортунистически; мощности не обещаю | эхо редкое (2/20), на этих объёмах различие ru/en по эху статистически недоказуемо |
**Задача B — fidelity-хвост P4 (D21 п.6)**
| Ось | Значение | Почему так |
|---|---|---|
| Канал | боевой: `deepseek-v4-flash`, thinking ON, боевой `translator.md`, боевая нарезка пары (1797 вых. ток.), санитайзер и регресс-гард ON | скоуп wiring по D21 п.6 = **только deepseek-драфт** |
| Срез | 蛊真人 главы **1116** (строки 837..1293 utf8-копии; 17 448 симв, CJK 0.805; sha256 gb18030 `5c22ec0d…1ee76b`) | «свежая выборка, другие фрагменты»: со срезом холодного прогона (главы 110) не пересекается |
| Единицы | 12 чанков, 2 на главу, **идентичные во всех трёх армах** (подтверждено `$0`-статусом) | |
| Арм A0 | боевой `translator.md` побайтно (sha256 `66907a49…6d27b` = боевой файл) | базлайн |
| Арм A1 | + **постинструкция после `{{text}}`** — дословно строка P4: `«Напоминание: выведи ТОЛЬКО русский перевод фрагмента выше, начиная с первого предложения.»` | тот слот, куда пойдёт wiring (хвост USER-части) |
| Арм A2 | + **микро-few-shot** — дословно две пары P4, в ШТАТНЫЙ движковый блок `---FEWSHOT---` (хвост system, `render.go:150-155`, тумблер `few_shot` D38.4) | тот слот, куда пойдёт wiring |
| Ремонт | `escalate_to` НЕТ, `regenerate_before_escalate: 0` | эхо здесь ИЗМЕРЯЕТСЯ, а не чинится; ремонт смешал бы частоту с ценой лечения и утроил бы вызовы |
| Сид | пуст во всех трёх армах ⇒ инъекция глоссария пуста ⇒ различие армов = ровно текст промпта | |
| Метрики $0 | эхо (`cjk_artifact` гейта + свой независимый пересчёт `han_share`) · `reasoning_tokens` пер-вызов (вендор-конфликт few-shot×thinking — на проводе, не по доке) · `finish_reason` · флаги санитайзера · len_ratio и покрытие предложений (анти-омиссия) · преамбулы/markdown | «дешёвое и детерминированное — первым» (D39.33) |
| Судья fidelity | ЧУЖОГО семейства к deepseek, по нормам рига D30.10/D39.7: span-цитирующий, reasoning-ON, полные окна без обрезки, ОБА порядка, пер-голосовой персист, **floor-проход на идентичных текстах** | author≠reviewer |
| Языковые констрейнты | **НЕ пробую** | инверсия 9/10 запинена D21.6 |
### 0.2 Смета ДО трат (фриз; источник каждого числа назван)
| Стадия | Ожидание | Источник |
|---|---|---|
| A-ru (терминолог, 150 кандидатов) | **$0** при попадании в чекпойнт, иначе ≤$0.014 | холодный прогон: проход $0.006211→$0.013045 (D39.58 §2.2) |
| A-en (терминолог, тот же банк) | $0.0100.016 | то же + надбавка за более длинный en-system (+232 симв) |
| B × 3 арма, 12 чанков каждый | $0.0130.016 на арм, итого **$0.0400.048** | холодный прогон: $0.024740 за 20 чанков = $0.001237/чанк, минус блок банкноты |
| Судья fidelity | ≤**$0.055** (жёсткий внутренний кап харнесса) | grok-4.3 $1.25/$2.50, аддитивный reasoning |
| Резерв на брак/ретраи | $0.020 | норма проекта |
| **Итого** | **≈$0.13 из $0.15** | |
### 0.3 Что заранее объявлено ПРОВАЛОМ
| # | Провал (назван до трат) |
|---|---|
| П1 | Любой арм оборван гейтом бюджета/потолка на середине — арм НЕ сравнивается, а объявляется браком (усечённый арм это не результат) |
| П2 | A-ru при байт-идентичном промпте делает ПЛАТНЫЕ вызовы вместо попадания в чекпойнт — харнесс не байт-верен, вся байт-идентичность инъекций задачи A под вопросом |
| П3 | Суммарный расход > $0.15 — стоп и пинг, а не тихое урезание |
| П4 | Армы B различаются чем-то, кроме промпта (разные границы чанков, разный sha среза, непустой глоссарий) — сравнение недействительно |
| П5 | Floor-проход судьи (идентичные тексты) даёт «tie» реже 3/4 — судейский шум выше сигнала, контраст НЕ читается |
| П6 | Число вызовов, отчитанное без второго пути вывода там, где второй путь возможен |
### 0.4 Что заранее объявлено НЕ провалом
- **Эха не случилось ни в одном арме.** Законный исход. Тогда ось эха = «не измерена на этой выборке», а НЕ «митигация работает»: у митигации не было чего снижать.
- **Разницы ru/en не видно.** Законный исход и, по приорам, ожидаемый. «Различий не обнаружено при N=…» — результат, «языки эквивалентны» — нет.
- **Fidelity-хвост меньше 35 пар D21 п.6.** Полный хвост по нормам рига в $0.15 не помещается (один судейский проход рига стоит $0.150.30 сам по себе). Достигнутый N называется числом, мощность — тоже.
- **en-промпт дороже ru** на входных токенах. Это свойство перевода инструкций, а не дефект арма; называется отдельно и в вывод «язык хуже» не сворачивается.
### 0.5 Пороги чтения (заморожены)
- **Задача A.** Сигналом считаю разницу `off_language`/`bad_lines`, которая (а) ≥3 строк абсолютно И (б) больше межбатчевого разброса внутри арма. Иначе — «различий не обнаружено при данном N».
- **Задача B, эхо.** Базовая частота A0 сравнивается с 2/20 холодного прогона (тот же канал, соседние главы). Вывод «митигация снижает эхо» требует, чтобы A0 дал ≥3 эха: при базе 02 разница с 0 неотличима от шума, и это будет сказано прямо.
- **Задача B, fidelity.** «Цена верности у митигации» объявляется, только если митигированный арм проигрывает по оси ВЕРНОСТИ в ≥⅔ голосов И отрыв превышает floor судейского шума. Иначе — «цена верности не обнаружена при N=…».
- **Вендор-конфликт few-shot×thinking.** Читается по трём осям сразу: `reasoning_tokens` (обходит ли few-shot thinking, как это делал префилл), эхо, и fidelity арма A2. Вердикт «конфликт подтверждён» требует ухудшения хотя бы по одной оси за пределами шума.
---
## §0-bis. АМЕНДМЕНТ ПРЕ-РЕГИСТРАЦИИ (объявлен ДО продолжения трат; причина ВНЕШНЯЯ)
Через 9 минут после первой платной траты **вендор подменил веса под тем же слагом** (§1). Пре-регистрация
§0 не переписана ни в одном пункте; вместо этого объявлено следующее, и всё, что ниже, читается через это.
1. **Всё, что измерено ДО 2026-07-31 18:45 UTC, и всё, что измерено ПОСЛЕ, — разные приборы.** Арм A
задачи A (ru-терминолог) — это $0-отыгрыш чекпойнтов холодного прогона, снятых на СТАРЫХ весах;
арм B (en) куплен на НОВЫХ. Сравнивать их запрещено, и вывод «en-промпт ломает формат» из этих
данных НЕ делается, хотя соблазн был: контроль (§1.2) его убил.
2. **Боевая форма канала перестала отвечать** (пустой `content` при `max_tokens` движка). Замер в
боевой форме сегодня физически невозможен; армы переведены на сырой провод в единственной
конфигурации, где канал отвечает, — с явным `reasoning_effort:"low"`. Это отступление от «боевой
формы» названо здесь, а не спрятано в выводах.
3. **Роль задачи A сменена с ТЕРМИНОЛОГА на ПЕРЕВОДЧИКА** (`translator.md` ru против его en-перевода).
Причина не вкусовая: движковый путь терминолога через `prompt_override` невозможен (роль резолвится
конвенцией), а единственная рабочая конфигурация требует ручки, которую движок для deepseek слать не
имеет права (§3.2). Промт сессии эту замену разрешает прямо («терминолог-батчи **и/или** банкнота» —
роль здесь не догма, догма — детерминированность метрик).
4. **Пороги §0.5 и провалы §0.30.4 не тронуты** и применяются к новым данным как есть. Исход «эха
не случилось», заранее объявленный в §0.4 НЕ провалом, наступил и читается ровно так, как был
написан до трат: ось эха «не измерена», а не «митигация работает».
---
## §1. ГЛАВНОЕ: канал сменился ПОД экспериментом — вендор-факт, а не догадка
### 1.1 Что увидел прибор
| Что | Когда (UTC) | Число |
|---|---|---|
| Холодный прогон, черновая волна `deepseek-v4-flash`, боевая форма | 30.07 22:00:30 31.07 00:29:17 | 68 успешных вызовов, **ср. `completion_tokens` 3475**, при потолке 2, `finish=length` 2 |
| Терминолог, en-арм (та же БД, тот же движок) | 31.07 18:4518:55 | 7 батчей: **6 пустых** (`finish=length`, `completion=8000`), 1 `stop` |
| **Черновик, БАЗЛАЙН (боевой `translator.md`, боевая форма)** | 31.07 18:54 | **4 из 4** при потолке 8000: 3 пусто, 1 обрыв |
Базлайновый арм и есть контроль, который убил соблазнительную атрибуцию «это en-промпт»: **боевой
русский промпт ломается ровно так же.** Фишер 4/4 против 2/68 — **p = 1.5·10⁻⁵**.
### 1.2 Причина — вендор, с датой и цитатами (сняты `curl`, счёт вхождений = 1 по копии без пробелов)
`https://api-docs.deepseek.com/updates`, запись **`Date: 2026-07-31`**:
- «*The official release of the DeepSeek-V4-Flash API is now in public beta. The API calling method
remains unchanged — simply set the model name to `deepseek-v4-flash` to use the latest version.*»
- «*Significantly enhanced agent capabilities, with benchmark results far exceeding V4-Pro-Preview*»
- «*DeepSeek-V4-Flash-0731 keeps the same model architecture and size as DeepSeek-V4-Flash-Preview,
and was only re-post-trained.*»
- «*Note: This update only upgrades the DeepSeek-V4-Flash API. The DeepSeek-V4-Pro API and the APP/WEB
models are unchanged.*» ← **редактор и эскалационный хоп не тронуты**
То есть слаг тот же, веса другие, объявления в `/models` нет (live-фактчек до трат показал ровно два
прежних слага). **Правило двух направлений отработало ровно как задумано: аномалия на проводе →
вендор-дока → причина за пять минут.** Гадать не пришлось ни разу.
### 1.3 Механизм — на сыром проводе, один и тот же чанк, один и тот же рендер
Движковый леджер ответить не может: для deepseek он держит `ReasoningTokens = 0` **сознательно**
(`provider_openai.go:22-24`, `ReasoningSubset` — thinking внутри `completion_tokens`, иначе двойной
счёт). Поэтому замер сделан мимо движка (`eval/promptlang/wire_probe.py`), а паритет рендера доказан
числом: `prompt_tokens` пробы = `prompt_tokens` движка на общем чанке, **1679 = 1679**.
| потолок | temperature | finish | `completion` | из них `reasoning` | `content` | цена |
|---|---|---|---|---|---|---|
| 8 000 | нет | **length** | 8 000 | **8 000** | **0 симв** | $0.002247 |
| 16 000 | нет | stop | 2 144 | 98 | 5 341 симв | $0.000607 |
| 16 000 | 0.3 | stop | 15 958 | 14 014 | 4 973 симв | $0.004475 |
| 16 000 | 0.3 | **length** | 16 000 | **16 000** | **0 симв** | $0.004487 |
| 16 000 (др. чанк) | 0.3 | **length** | 16 001 | **16 001** | **0 симв** | $0.004488 |
| 16 000 (др. чанк) | 0.3 | stop | 5 027 | 3 117 | 5 163 симв | $0.001422 |
**Это не дегенеративный луп** (класс D2.3): 20 025 символов размышления — 122 строки, **все 122
уникальны**, модель добросовестно пере-переводит фразу за фразой внутри `reasoning_content` и просто
не доходит до ответа. Распределение длины думания имеет тяжёлый хвост: наблюдённые значения
`reasoning_tokens` на одном и том же чанке — **98 · 3 117 · 8 000⌐ · 14 014 · 16 000⌐ · 16 001⌐**
(⌐ = упёрлось в потолок). **Поднятие флора хвост не закрывает: на 16 000 упор случился в 3 из 6.**
### 1.4 Ручка, которая чинит, — и цена, которую она берёт
Вендор-дока `https://api-docs.deepseek.com/guides/thinking_mode` (снята `curl`, счёт = 3) разделяет
ДВА параметра: **тумблер** `{"thinking":{"type":"enabled/disabled"}}` и **эффорт**
`{"reasoning_effort":"low/high/max"}`. Наша запись от 04.07 («`reasoning_effort`: только `high`/`max`;
low/medium→high») **устарела**: `low` сегодня — документированный отдельный уровень.
Замер: тот же чанк, боевой потолок 8 000, temperature 0.3, `reasoning_effort:"low"`
`finish=stop`, `reasoning` **313** токенов вместо 8 000, полный перевод 5 575 симв, **$0.000907**.
Реплики на трёх чанках: **4/4 `stop`**, `reasoning` 170313, цена $0.000360.00091.
**Но ручка пере-вооружает эхо-мину.** Из тех же 4 реплик **одна вернула чистый китайский исходник**
(`cjk_share = 0.83`, `len_ratio 0.97`, `finish=stop` — тихий провал ровно того класса, ради которого
существует гейт `cjk_artifact`). Это уточняет рамку D19.2: защита от эха деградирует **непрерывно с
эффортом**, а не скачком на «выключено». Движок сегодня этой ручки слать НЕ МОЖЕТ и не должен —
`config.echoMineViolation` (`models.go:276-281`) запрещает эхо-склонному провайдеру любой
reasoning-контроль кроме `none|mandatory`. **Гейт оказался прав, и замер — его первое эмпирическое
подтверждение.**
## §2. Что это стоит бэкенду (цифры, решение — не моё)
1. **Сегодня боевая черновая волна физически не проходит.** При `min_max_tokens: 8000` она платит
полную цену вызова и получает пустой `content`; гейт помечает `empty`, ретрай покупает ещё одну
такую же. Следующий платный прогон в текущем конфиге — это деньги в ноль.
2. **Три развилки, все с ценой:**
- **(а) Поднять `min_max_tokens` flash 8000 → 16000** (тот же аргумент и то же число, что уже
ратифицированы для v4-pro в D24.3: перебор бюджета бесплатен, недобор стоит целой генерации).
Цена: ⚠ `max_tokens` входит в `request_hash`**громкий `--resnapshot`** всех книг.
Честная граница: **хвост это не закрывает** — на 16 000 упор случился в 3 из 6 замеров.
- **(б) Разрешить `reasoning_effort:"low"` для flash** — чинит и удешевляет (вызов
$0.000360.00091 против $0.00220.0045), но требует амендмента `echoMineViolation` и
**возвращает эхо** (1 из 16 базовых вызовов на low). Эскалация на v4-pro при этом жива:
вендор прямо пишет, что pro не тронут.
- **(в) Ждать.** Модель вышла «in public beta» СЕГОДНЯ; поведение может устаканиться. Ждать
бесплатно, но блокирует очередь.
3. **Экономика.** Цена черновика на чанк: холодный прогон (прежние веса) **$0.001237**
($0.024740 за 20 чанков) → боевая форма на 0731 **$0.002253** (замер B-a0, 4 чанка) при НУЛЕ
доставленного текста, а на потолке 16 000 до **$0.0045**. То есть **×1.83.6 дороже, и это
оплаченный ноль**. С `effort:"low"`**$0.00096** в среднем по 12 вызовам арма a0
($0.000360.00091 на пробах), то есть **на 22% дешевле прежнего**. Модель COGS ~$0.85/ранобэ (D30.4) зависит от того,
какая из развилок будет выбрана; сама по себе она сегодня не верна ни в одну, ни в другую сторону.
## §3. ЗАДАЧА B — fidelity-хвост P4 (петля D21 п.6, стоит с 09.07)
Конфигурация: 12 СВЕЖИХ фрагментов 蛊真人 гл. 1116 (вне среза холодного прогона; sha256 gb18030
`5c22ec0d…1ee76b`), сырой провод, `deepseek-v4-flash` (0731), `max_tokens` 8000, temperature 0.3,
`reasoning_effort:"low"`, три арма по 12 вызовов. Фрагменты и параметры байт-одинаковы во всех армах;
различается ровно текст промпта. Слоты митигаций — ТЕ, куда пойдёт wiring: постинструкция в хвост
USER-части, few-shot в штатный блок `---FEWSHOT---` (`render.go:150-155`, тумблер D38.4).
| арм | эхо | срыв языка | вне коридора len_ratio | ср. `reasoning` | ср. `completion` | сумма $ | Δ$ к базе |
|---|---|---|---|---|---|---|---|
| **a0** боевой `translator.md` | 0/12 | 0/12 | 0/12 | 1235 | 2918 | 0.011520 | — |
| **a1** + постинструкция P4 | 0/12 | 0/12 | 0/12 | **876** | 2579 | 0.008831 | **23.3%** |
| **a2** + микро-few-shot P4 | **1/12** | **1/12** | 0/12 | **2356** | 3904 | 0.014798 | **+28.5%** |
**(б) Повторный echo-rate — ответ отрицательный, и этот исход был описан ЗАРАНЕЕ (§0.4, первый пункт
списка «не провал»).**
База дала **0 из 12**. У митигации нечего было снижать, поэтому ось эха на этой выборке —
**«не измерена»**, а не «митигация работает». Пул по всем базовым вызовам на этой конфигурации
(12 арма + 4 пробы) — **1 эхо из 16 (6%)**; это и есть сегодняшняя частота, на которой любой замер
митигации требует сотен вызовов, а не десятков.
**(в) Вендор-конфликт «few-shot вредит R1-классу в thinking» — РАЗРЕШЁН, ПОДТВЕРЖДЁН, на проводе.**
Few-shot **почти удваивает размышление** (2356 против 1235 токенов, +91%) и удорожает вызов на 28.5% —
это измеренное число, а не вендор-клейм. Он же дал **оба** испорченных выхода арма:
- `a2-f00`**чистый китайский исходник** (`cjk_share 0.829`, `len_ratio 0.993`, `finish=stop`);
- `a2-f04`**полный перевод на АНГЛИЙСКИЙ** (5 770 симв, `finish=stop`, кириллицы нет).
Оба — тихие провалы с правдоподобным видом. **Направление обратное тому, ради чего митигацию
предлагали.** Честно: Фишер 2/12 против 0/12 даёт **p = 0.478** — при этом N различие статистически
НЕ установлено, и я его не заявляю. Заявляю другое: **обоснование wiring стояло на эффекте, которого
арм не показал, а цена (+28.5% и ×1.9 размышления) измерена и однозначна.**
**(а) Fidelity-хвост — куплен частично, N назван честно.** Судья `grok-4.3` (чужое семейство к
deepseek), reasoning-ON, полные окна без обрезки, оба порядка, пер-голосовой персист.
**Floor-проход на ИДЕНТИЧНЫХ текстах: 2/2 «tie»** ($0.00592/голос) — П5 не сработал, позиционного смещения нет.
Контраст a0↔a1 (постинструкция), 5 голосов на 3 единицах — дальше упёрся кап:
| единица | AB | BA | чтение |
|---|---|---|---|
| f01 | победил a0 | победил a1 | **порядок перевернул вердикт** — шум на этой единице |
| f02 | победил a1 | победил a1 | **согласовано**: в обоих порядках судья вменил ПРОПУСК арму a0 («A пропустил весь монолог Фан Юаня», «B пропустил весь абзац про 遗藏 и 酒虫») |
| f03 | tie | — (кап) | различия только словесные |
**Вердикт: цена верности у постинструкции НЕ обнаружена** — по пре-регистрированному порогу (проигрыш
в ≥⅔ голосов сверх пола) она не проиграла ни разу. Мощность: **3 единицы, 5 голосов** против «35 пар»
петли D21 п.6 — это **1/7 заказанного хвоста**, и выводом «постинструкция верности не вредит» это НЕ
является. Оговорка о ложной сходимости: `a0-f02` имеет и самый низкий `len_ratio` арма (2.476 против
средних 2.93), то есть детерминированный прибор и судья указали на одну и ту же единицу — **это одна
единица, а не сходимость двух сигналов**, и весом двух улик она не считается.
**Контраст a0↔a2 снят с судьи осознанно:** few-shot отвергнут детерминированно, судья это решение не
двигает, а его цена ($0.00758/голос на контрасте, диапазон $0.007040.00817) — половина оставшегося бюджета.
**⇒ РЕКОМЕНДАЦИЯ WIRING (решение — оркестратора/владельца):**
- **`---FEWSHOT---` в `translator.md` НЕ вносить.** Цена измерена (+28.5%, размышление ×1.9), польза
не показана, оба сорванных выхода арма — его. Вендор-конфликт §3.1 research/15 разрешён против него.
- **Постинструкцию не вносить СЕЙЧАС** — но по другой причине: она безвредна и даже дешевле
(23.3%), однако **покупать ей нечего**, пока база даёт 0/12 эха. Вернуться к ней, если будет выбрана
развилка §2(б) (`effort:"low"`), где эхо возвращается: там у неё появляется предмет.
- **Цена самой правки, если её всё же делают:** байты `translator.md` двигают `PromptSHA256` черновой
волны ⇒ пере-снапшот и перекупка ЧЕРНОВИКОВ всех zh-книг. На срезе 10 глав это ≈$0.025 по холодному
прогону; на полной книге (2284 раздела) — **порядка $2.85.6** по сегодняшней цене вызова. Плюс
правку пришлось бы дублировать в `translator-banknote.md` (общая шапка, строка 36г бэклога).
- **⚠ Числа P4 от 09.07 сняты на весах, которых больше нет.** «0/29 против базы 3067%» относится к
`DeepSeek-V4-Flash-Preview`. Любое решение, опирающееся на них как на текущую эмпирику, опирается на
superseded прибор.
## §4. ЗАДАЧА A — «язык промптов» (Р6, гипотеза владельца)
Армы: **a0 = боевой `translator.md`** (sha256 `66907a49…6d27b`, побайтно боевой файл) против
**e0 = его английский перевод строка-в-строку** (тот же порядок секций, то же число буллитов, те же
плейсхолдеры, примеры мер `时辰`/`成` побайтно те же; целевой язык в обеих версиях остаётся русским —
меняется язык ИНСТРУКЦИЙ, а не задача). Те же 12 фрагментов, те же параметры, инъекция пуста в обоих
(сида нет) ⇒ байт-идентична тривиально.
| арм | эхо | **срыв языка выхода** | вне коридора | преамбулы | markdown | лат. прогоны | ср. len_ratio | ср. предл. | ср. `reasoning` | сумма $ |
|---|---|---|---|---|---|---|---|---|---|---|
| **a0** ru | 0/12 | **0/12** | 0/12 | 0 | 1 | 4 | 2.93 | 1.05 | 1235 | 0.011520 |
| **e0** en | 0/12 | **0/12** | 0/12 | 0 | 0 | 4 | 2.92 | 1.08 | 1502 | **0.012605** |
**Ответ: различий по следованию формату НЕ ОБНАРУЖЕНО при N = 12 на арм.** Все детерминированные оси
совпадают в пределах шума; ни одна не даёт разницы ≥3 единиц (порог §0.5).
**Встречный риск, названный в промте сессии, НЕ материализовался: `off_language = 0/12` у обоих армов** —
английская инструкция не утянула выход в английский. Прибор при этом рабочий и не слеп: в тот же день
он поймал английский выход у арма a2 (§3) — то есть ноль здесь означает «не случилось», а не «не видно».
**Единственное измеренное различие — цена: en-промпт дороже на +9.4%** ($0.012605 против $0.011520),
и это чистый эффект размышления (+21.6% `reasoning_tokens`: 1502 против 1235), не длины входа —
en-промпт как раз КОРОЧЕ в токенах: ср. `prompt_tokens` **1290 против 1399** на этих же 12
фрагментах (7.8%), и на батче терминолога та же картина (4155 против 4260).
**⇒ Гипотеза «английский промпт следует формату лучше» на этой роли, этой паре и этом N НЕ
подтверждается, а дороже она измеримо.** Оснований переводить боевые промпты на английский нет;
оснований утверждать «языки эквивалентны» — тоже нет (N мал, роль одна, пара одна).
**Побочно (задача A, первая, оборванная попытка на роли ТЕРМИНОЛОГА).** До диагноза §1 арм en дал
6 пустых батчей из 7 против 21/21 `stop` у ru-прохода холодного прогона — соблазнительная «находка»
уровня «en-промпт ломает роль». **Она неверна**, и убил её базлайновый контроль §1.1. Записываю
это как есть: механизм самообмана здесь ровно тот, что уже стоит в моей памяти по exp12/exp13
(«сигналы сходятся» сильнее данных), и поймал его контроль, а не осторожность.
Полезный остаток той попытки: **$0-отыгрыш чекпойнтов холодного прогона воспроизвёл его числа
побайтно** (`consolidated=149 declined=1 unanswered=0 bad_lines=0 off_language=0`), что независимо
подтверждает и цифры D39.58 §2.4, и байт-верность харнесса (П2 не сработал).
## §5. Деньги — $0.145948 из потолка $0.15 (97.3%), сведено ДВУМЯ путями
| источник | вызовов | путь 1 (самоотчёт харнесса) | путь 2 (пере-счёт из сырого `usage` по `models.yaml`) | Δ |
|---|---|---|---|---|
| движок A-ru (отыгрыш чекпойнтов) | 29 | 0.000000 | 0.000000 | 0 |
| движок A-en (терминолог, оборван) | 27 | 0.019036 | 0.019036 | 0 |
| движок B-a0 (базлайн, стоп-гейт) | 4 | 0.009011 | 0.009011 | 0 |
| wire-пробы (диагноз канала) | 10 | 0.020426 | 0.020426 | 3.4e-07 |
| армы задач A и B | 48 | 0.047754 | 0.047756 | 2.2e-06 |
| судья grok-4.3 | 7 | 0.049721 | 0.049721 | ~0 |
| **ИТОГО** | **125** | **0.145948** | **0.145951** | **2.6e-06** |
Расхождение путей — на уровне округления float. Команда: `eval/.venv/bin/python eval/promptlang/spend.py`.
**Брак сессии — $0.028047** (движковые вызовы A-en и B-a0, купившие пустые ответы). Он не мой:
это и есть цена обнаружения §1, и обнаружена она на четвёртом чанке, а не на двадцатом, потому что
сработал стоп-гейт.
Все платные вызовы — в окне **18:4520:52 UTC**, **вне пиков DeepSeek** (UTC 0104 / 0610); проверено `date -u` перед каждым блоком трат.
Стендовые артефакты: **1117 хешей сняты до прогона и сверены после — дифф ПУСТ.**
## §6. Чего сессия НЕ сделала и почему (стоп-гейт отработал)
- **Полный fidelity-хвост «35 пар» D21 п.6 не куплен.** Куплено 3 единицы / 5 голосов = 1/7. Причина
арифметическая: контрастный голос рига стоит **$0.00758** (замерено, 5 голосов, разброс
$0.007040.00817), полный хвост по нормам (35 пар × 2 порядка + floor) = 72 голоса ≈ **$0.54**,
то есть **в 3.6 раза выше всего потолка сессии**. Это надо знать при следующей выдаче: «хвост P4»
не помещается в $0.15 ни при каком планировании.
- **Задача A на ролях ТЕРМИНОЛОГА и БАНКНОТЫ не доведена** — движковый путь сегодня не работает (§1),
а замена промпта роли требует либо правки `backend/` (чужая зона), либо ручки, запрещённой гейтом.
- **Второй судья / декой не ставились** — бюджета не осталось; при N=5 голосов второй судья не
изменил бы того, что вывод и так объявлен маломощным.
- **Развилка §2 не выбирается сессией.** Она стоит денег всех книг (пере-снапшот) и трогает
ратифицированный гейт — это подпись владельца, а не вывод полигона.
## §7. Строки для `00-provider-quirks` (факты с датой; вносит оркестратор)
1. **`deepseek-v4-flash` — слаг стабилен, веса нет.** 2026-07-31 слаг переехал на
`DeepSeek-V4-Flash-0731` («*keeps the same model architecture and size … was only re-post-trained*»,
`api-docs.deepseek.com/updates`, `Date: 2026-07-31`). `/models` этого не показывает — там прежние два
слага. **Урок календаря: «слаг живой» ≠ «модель та же»**; пере-проверять поведением, не листингом.
2. **Флор 8000 для flash пробит.** На той же задаче/чанке `reasoning_tokens` наблюдались
98 · 3 117 · 8 000⌐ · 14 014 · 16 000⌐ · 16 001⌐; при `max_tokens=8000` **боевая форма отдаёт пустой
`content` при `finish=length`** (4/4 против 2/68 у прогона на прежних весах, p=1.5e-05). 16 000 —
не гарантия (упор в 3 из 6).
3. **Запись «`reasoning_effort`: только high/max» УСТАРЕЛА.** Вендор-дока `guides/thinking_mode`
(снята 31.07) разделяет тумблер `{"thinking":{"type":"enabled/disabled"}}` и эффорт
`{"reasoning_effort":"low/high/max"}`; `low` — отдельный документированный уровень.
Замер: `low` при потолке 8000 → `stop`, reasoning 170313, $0.000360.00091 (было $0.00220.0045).
4. **`effort:"low"` пере-вооружает эхо-мину:** 1 чистый китайский выход из 16 базовых вызовов
(`cjk_share 0.83`, `finish=stop`). Уточнение рамки D19.2: защита деградирует НЕПРЕРЫВНО с эффортом,
не скачком на «выключено». Гейт `echoMineViolation` (`models.go:276-281`) это подтверждает.
5. **v4-pro не тронут** — «*This update only upgrades the DeepSeek-V4-Flash API. The DeepSeek-V4-Pro API
and the APP/WEB models are unchanged.*» Редактор и эскалационный хоп сегодня в прежнем поведении.
6. **Микро-few-shot на 0731 вреден:** +91% `reasoning_tokens`, +28.5% цены, 2 сорванных выхода из 12
(китайское эхо и полный английский перевод) против 0 из 12 у базы.
7. **`grok-4.5` появился в `/v1/models` xAI** (live 31.07, вместе с прежними десятью слагами) — факт
листинга, поведением не проверялся, решений на нём не строится.
## §8. Пинги (через владельца — канал промта)
1. **БЭКЕНДУ, срочно:** следующий платный прогон в текущем конфиге купит пустые ответы. Развилка §2 —
до него. Ресёрч общности (строка 72) от этого не зависит и может идти.
2. **ОРКЕСТРАТОРУ:** петля D21 п.6 закрывается **отрицательно по few-shot** и **«не измерено» по
постинструкции**; ⚠ вся эмпирика P4 (09.07) относится к весам `V4-Flash-Preview`. Если петлю
оставлять открытой — она теперь про 0731 и стоит ≈$0.42, а не $0.15.
3. **ОРКЕСТРАТОРУ:** Р6 отвечен отрицательно на роли переводчика (различий нет, en дороже на 9.4%);
если владелец хочет замер на роли терминолога — он возможен только после развилки §2.
4. **ВЛАДЕЛЬЦУ:** развилка §2 — подпись (перекупка снапшотов против амендмента ратифицированного
гейта против ожидания). Сессия числа дала, решение не приняла.
## §9. Критик полноты (обязательная секция)
- **N мал везде.** 12 фрагментов на арм, одна книга, одна пара, одна модель, один день. Все «различий
не обнаружено» — это «не обнаружено при N=12», и ни одно из них не эквивалентность.
- **Fisher 2/12 vs 0/12 = 0.478.** Вывод против few-shot держится на ЦЕНЕ (измерена уверенно) и на
отсутствии показанной пользы, а НЕ на разнице частот срывов. Если кто-то процитирует «few-shot даёт
2 срыва из 12» как установленный эффект — это будет цитата сверх данных.
- **Floor судьи измерен на ИДЕНТИЧНЫХ текстах** (2/2 tie) и потому не ограничивает шум на РАЗНЫХ, но
похожих текстах: единица f01 перевернула вердикт с порядком, и floor этого не предсказал.
- **Конфигурация армов не боевая.** `reasoning_effort:"low"` движок слать не может; все выводы задач
A и B получены в конфигурации, которой сегодня в проде нет и которая, возможно, никогда не будет
разрешена. При выборе развилки §2(а) их придётся пере-снять.
- **Мой рендер — реплика движкового, а не он сам.** Паритет доказан на ОДНОМ чанке по `prompt_tokens`
(1679=1679); на втором чанке проба и движок разошлись (1231 против 1310) — расхождение объясняется
нормализацией `tmctl export`, но это ОБЪЯСНЕНИЕ, а не доказательство: армы сравнимы между собой, с
движком — только через тот один сверенный чанк.
- **Английский промпт e0 писал я.** «Строка в строку» проверено структурно (буллиты, маркеры,
плейсхолдеры, побайтный пример), но качество перевода инструкций — моё суждение, и слабый en-текст
дал бы тот же «различий нет».
- **Причинность §1 установлена по времени и вендор-доке, а не экспериментом на двух версиях слага.**
Прежних весов у нас больше нет; пере-проверить «до/после» на одном стенде невозможно в принципе.
- **Не проверено адверсариально:** что тяжёлый хвост думания — свойство именно ЭТОЙ задачи (плотный
CJK + длинный бриф), а не всей модели. Одного контр-примера (короткий промпт) я не ставил.
---
## §10. ERRATA + ДОБОР (после вопросов владельца; тело выше НЕ переписано — дисциплина D23.3)
Три вопроса владельца («точны ли результаты», «гуглил ли», «только ли китайский») вскрыли одну
**ошибку чтения** и добрали два факта. Потрачено дополнительно **$0.001579**, итог сессии
**$0.147527 из $0.15**.
### 10.1 ❌ ИСПРАВЛЕНИЕ: эхо в арме few-shot НЕ атрибутируется few-shot
В §3 сказано «оба сорванных выхода арма — его». Счёт верен, **причинное чтение — нет**.
**Оба эха всей сессии (2 из 58 вызовов) пришлись на ОДИН И ТОТ ЖЕ исходный текст** — начало главы 11
(`不过是色诱罢了`): фрагмент `f00` армов и `chunk-1-0` проб перекрываются по телу. На этом тексте
базовый промпт **тоже эхает**. Решающая улика — два вызова с **побайтно идентичным запросом**
(один sha `5c304c9b3cf4`, оба `effort:low`, cap 8000, temp 0.3, промпт a0):
| вызов | finish | out | reasoning | content | cjk_share | вердикт |
|---|---|---|---|---|---|---|
| `eff1-a0-8000-efflow-t0.3` | stop | 2441 | 313 | 5575 симв | 0.000 | чисто |
| `lowrep1-0-a0-8000-efflow-t0.3` | stop | 1252 | 170 | 1645 симв | **0.831** | **ЭХО** |
**Эхо на 0731 стохастично ПО ВЫЗОВУ, а не детерминировано по фрагменту.** Вывод «few-shot вызвал
эхо» СНИМАЮ. Вывод «few-shot вреден» **остаётся, но на других основаниях**: `reasoning_tokens` ×1.9,
цена +28.5% (оба уверенно измерены) и **единственный за 48 вызовов выход на английском** (`a2-f04`,
5 770 симв, `finish=stop`) — его. Эхо-ось из обоснования вычеркнута.
Это ровно тот мой паттерн, который стоит в памяти по exp12/exp13 («сигналы сходятся» сильнее данных).
Поймал его чек «а один ли это текст?», а не осторожность — поэтому чек идёт в норму, а не в намерение.
### 10.2 ✅ Митигации дошли до провода (проверка целостности, которой в §3 не было)
`Δprompt_tokens` к базе, **константа на всех 12 фрагментах**: `a1` **+28** (постинструкция в хвосте
USER), `a2` **+83** (блок `---FEWSHOT---` в хвосте system), `e0` **109** (en-промпт короче).
Рендер детерминирован, блоки на проводе, «арм не поехал» исключён.
### 10.3 🔍 Поиск (вопрос «гуглил ли»): вендор-рамка меняется, багрепортов нет
- **Багрепортов о пустых ответах / регрессии перевода на 0731 в выдаче НЕТ** — это не известный
инцидент, а расхождение нашей калибровки с новым чекпойнтом.
- **Найдена вендорская рамка, которой не было в §1:** контекст **1M**, **max output 384K**
(`api-docs.deepseek.com/quick_start/pricing`), и — ключевое — **«For the high and max reasoning
effort levels, a maximum output length of 384K tokens is recommended»**. Наш флор **8000 — на два
порядка ниже того, что вендор считает нормой для высокого эффорта.** ⇒ Диагноз §1 уточняется:
модель не «сломалась», у неё **сместился дефолтный эффорт**, а наш бюджет вывода калиброван под
прежний чекпойнт. Это делает развилку §2(б) (**слать эффорт ЯВНО**) не хаком, а приведением
конфига к вендорской модели: `low` — документированный уровень, thinking при нём ВКЛЮЧЁН
(`none` — это другой параметр, тумблер, и его мы по-прежнему не трогаем, D19.2 цел).
### 10.4 🌐 Область проблемы (вопрос «только ли китайский»): да, привязана к zh-входу
Один и тот же промпт и параметры (cap 8000, temp 0.3, **дефолтный** эффорт — тот, что ломается):
| источник | prompt_tok | finish | `completion` | из них reasoning | content |
|---|---|---|---|---|---|
| **en** (Howard, PD, 1450 симв) | 877 | stop | 973 | **435** | 1366 симв |
| **zh короткий** (400 симв) | 776 | stop | 3841 | **3393** | 1131 симв |
| **zh длинный** (1754 симв) | 1679 | **length** | 8000 | **8000⌐** | **0** |
При **сопоставимом входе** (877 против 776 токенов) zh требует **в 7.8 раза больше размышления**, чем
en, при похожем объёме выхода. Значит дело в **исходном письме/задаче, а не в длине входа**
конфаунд длины закрыт. Дальше размышление на zh растёт круто: 776 ток. входа → 3393, 1679 → упор.
**Границы честно:** en n=1, zh-короткий n=1, zh-длинный n=8; ja НЕ проверялся вовсе.
Разговора о «диалектах» здесь нет ни на одной оси: и эхо, и разгон думания привязаны к
**плотному ханьскому письму**, а не к варианту китайского (эхо ловилось и на современной вебновелле,
и на минском байхуа 金瓶梅 — D22 п.5).
### 10.5 💡 Следствие для РЕМОНТА эха, которого §3 не заметил
`disposition.go:138-148`: `cjk_artifact` объявлен **НЕ ретраебельным** и идёт сразу в эскалацию —
обоснование в комментарии: *«retry just re-produces them»*. Эта посылка верна для `deepseek-chat`
(квирк-строка: «ретраи не помогают — детерминировано для фрагмента»), но **на 0731 она опровергнута
§10.1**: идентичный запрос дал эхо и не-эхо. Арифметика ремонта при этом перевернулась:
| путь ремонта одного эха | цена | источник |
|---|---|---|
| эскалация на `deepseek-v4-pro` (сегодняшнее поведение) | **$0.0147** (2 хопа) | холодный прогон, D39.58 §2.2 |
| простой ре-ген на flash при `effort:low` | **$0.00096** | ср. по 12 вызовам арма a0 |
**≈15× дешевле**, и первый ре-ген уже покрывает ~⅔ случаев при наблюдённой частоте. Предложение
бэкенду (не правка — зона чужая): для `cjk_artifact` разрешить N=1 ре-ген ПЕРЕД эскалацией, а
эскалацию оставить вторым рубежом. Гейт при этом не ослабляется ни на байт — D19.2 цел.
⚠ Мощность: частота эха 2/58 (3.4%), «⅔» — арифметика от одного повторённого запроса, не замер.
### 10.6 Сводка советов по анти-эху (в порядке «сначала бесплатное»)
1. **Гейт `cjk_artifact` не трогать никогда** — он единственный ловит тихий провал на любой
конфигурации (D19.2). Всё ниже снижает ЧАСТОТУ, а не заменяет его.
2. **Ре-ген перед эскалацией** (§10.5) — $0, ~15× дешевле нынешнего ремонта, опирается на замер.
3. **Эффорт слать ЯВНО** (§10.3) — приводит конфиг к вендорской модели и делает вызов дешевле
прежнего; цена — амендмент `echoMineViolation` и, возможно, чуть более частое эхо (не установлено).
4. **Постинструкция P4** — если решат вносить: безвредна, дешевле базы на 23.3%, слот готов
(хвост USER-части). Сегодня покупать ей нечего (база 0/12), но при переходе на явный `low`
у неё появляется предмет.
5. **Микро-few-shot — НЕ вносить** (§10.1: цена измерена, польза не показана, английский выход — его).
6. **Языковые констрейнты — по-прежнему НЕ трогать** без per-model замера (инверсия 9/10, D21.6).
7. **Не проверено и стоит центов:** префилл «Перевод:\n» (P4 давал 0/6, но обходит thinking — а
thinking на 0731 и есть дорогая часть, так что довод против префилла ослаб); `effort:"high"` явно
(проверить, дефолт ли это); ja-вход (§10.4).
---
## §11. РЕВЬЮ ЭКСПЕРИМЕНТА ЦЕЛИКОМ (по вопросу владельца «расследовал ли ты анти-эхо и достаточно ли»)
Короткий ответ: **нет, недостаточно, и главный промах — процессный, а не в числах.** Ниже — сверка
задания с исполненным, без смягчений. Дополнительных трат: **$0** (всё в этом разделе — из артефактов,
оплаченных 09.07, и из уже купленного сырья). Итог сессии остаётся $0.147527.
### 11.1 ❌ ГЛАВНЫЙ ПРОМАХ: я перевернул задание по деливераблу (1)
Промт: *«fidelity-судья … на парах база↔митигация — **P4-артефакты в `~/books/gu-zhenren/research15-probes/`**
(вне git) **+ добор свежих фрагментов по нужде**»*. Я сделал **добор главным**, а на указанные артефакты
не посмотрел ни разу за весь прогон. Между тем на диске лежат **24 готовые пары база↔митигация**
(21 grok + 3 deepseek), генерация которых оплачена 09.07:
| семейство | пригодных пар | почему не больше |
|---|---|---|
| grok-none | **21** (7 небазовых-эхо фрагментов × G1/G2/G4) | база G0 эхнула на 3 из 10 — эти фрагменты пары не дают |
| deepseek (боевое семейство) | **3** (1 чистая база s2 × D1/D2/DP) | база D0 эхнула на 2 из 3 сэмплов |
**Цена ошибки:** $0.035149 ушло на генерацию армов, а судейский бюджет — на 3 МОИ единицы вместо
24 готовых. Правильный порядок был: сперва отсудить бесплатное сырьё, потом добирать. Заявление
§6 «полный хвост не помещается в $0.15» остаётся верным по деньгам (72 голоса ≈ $0.54), но
**оно скрыло, что 24 пары уже были и ждали только судью**.
Заодно **реплика счётчиков P4 сошлась побайтно** (независимое подтверждение таблицы research/15):
grok G0 3/10 · G1 0/10 · G2 0/10 · **G3 9/10 (инверсия)** · G4 0/10; ds-gu008 D0 2/3 · D1 0/3 · D2 0/3 · DP 0/3.
### 11.2 ✅ Что удалось вытащить из этих пар БЕСПЛАТНО (детерминированная половина верности)
Судья нужен для «искажения при сохранённом объёме». **Пропуски и обрывы берутся без него** — по длине
и числу предложений против ОДНОГО И ТОГО ЖЕ исходника (`eval/promptlang/p4_pairs.py`):
| митигация | пар | медиана Δlen_ratio к базе | пар короче базы на >0.15 | пар длиннее на >0.15 |
|---|---|---|---|---|
| инстр. ПОСЛЕ текста (grok) | 7 | **+0.022** | 1 | 2 |
| микро-few-shot (grok) | 7 | **+0.056** | 1 | 2 |
| комбо (grok) | 7 | **+0.127** | 0 | 3 |
| инстр. ПОСЛЕ (deepseek) | 1 | 0.346 | 1 | 0 |
| few-shot (deepseek) | 1 | 0.108 | 0 | 0 |
| префилл (deepseek) | 1 | 0.331 | 1 | 0 |
**Я едва не подал вторую ложную тревогу.** Δ = 0.346 у постинструкции на боевом семействе выглядит
как подпись пропуска. **Проверка хвостов её отменяет:** база, инстр.-ПОСЛЕ, few-shot и префилл
доходят до ОДНОГО И ТОГО ЖЕ конца исходника («…Бянь Сысюань побледнела»). ⇒ Это **многословность
базы, а не пропуск митигации.**
**⇒ Детерминированный вывод по деливераблу (1): ни одна из митигаций P4 не даёт подписи ПРОПУСКА
ни на одной из 24 пар.** Это половина вопроса о верности, и она закрыта — бесплатно.
Оставшаяся половина (искажение смысла при сохранённом объёме) без судьи не берётся.
**Один дефект формы найден:** 1 из 3 выходов арма «инстр. ПОСЛЕ» на deepseek дописал **сноски
переводчика `[1]…[6]`** вопреки «Выведи ТОЛЬКО перевод» — класс «отсебятина/утёкшие заметки», ровно
тот, ради которого существует output-санитайзер D30.3. У базы и остальных армов — 0. n=1, но дефект
именной и воспроизводимо предъявим.
### 11.3 ❌ ОВЕРКЛЕЙМ, который надо снять: «петля D21 п.6 закрыта»
Мои армы задачи B ехали с `reasoning_effort:"low"` — конфигурацией, **которой движок сегодня слать не
имеет права** (`echoMineViolation`). Вопрос петли поставлен про thinking-ON по умолчанию. Значит:
- **(б) повторный echo-rate в БОЕВОЙ форме на 0731 — НЕ ИЗМЕРЕН вовсе.** При дефолтном эффорте канал
возвращает пустой `content`, то есть годного базового замера эха в проде-форме у меня **ноль**.
«База 0/12» относится к `effort:low`.
- **(в) вендор-конфликт** измерен там же (`low`). Число +91% размышления — настоящее, но при дефолтном
эффорте не проверено.
- **(а)** — см. 11.1/11.2: детерминированная половина закрыта на 24 парах, судейская — на 3 единицах.
**Формулировку «петля закрыта» снимаю.** Верная: *few-shot отвергнут по цене и по единственному
англоязычному выходу; постинструкция не показала ни вреда, ни пользы; ось эха в боевой форме на 0731
не измерена, потому что боевая форма сегодня не отвечает.*
### 11.4 Сводка «задание против исполненного»
| Деливерабл промта | Статус | Чем закрыт / чего не хватает |
|---|---|---|
| A. Р6 «язык промптов» | ✅ **отвечен** | ru vs en на 12 фрагментах: различий по формату нет, en дороже на 9.4%. Роль — переводчик вместо терминолога (амендмент §0-bis) |
| B(1) fidelity на парах P4 | ⚠ **половина** | 24 пары: пропусков нет (детерминированно, $0). Судья — 3 единицы моих армов вместо 24 готовых пар (промах 11.1) |
| B(2) повторный echo-rate | ⚠ **не в боевой форме** | 0/12 при `effort:low`; в проде-форме канал не отвечает |
| B(3) вердикт few-shot×thinking | ✅ **дан** | +91% reasoning, +28.5% цены, англоязычный выход; при `effort:low` |
| Выход: рекомендация wiring | ✅ **дана** | few-shot — нет; постинструкция — отложить до развилки §2 |
| (сверх задания) | ✅ | вендор-регрессия 0731, ручка эффорта, ре-ген вместо эскалации (15×), область = ханьский вход |
### 11.5 Три ошибки чтения, пойманные ревью (все — мои)
1. **Эхо приписано few-shot** — опровергнуто побайтно идентичной парой запросов (§10.1).
2. **«Пропуск» у постинструкции на deepseek** — опровергнуто совпадением хвостов (§11.2).
3. **«Полный хвост не помещается»** — верно по деньгам, но скрыло, что 24 пары уже оплачены (§11.1).
Все три — один и тот же механизм: **вывод сделан на агрегате, до проверки единиц.** Первые два поймал
не я по осторожности, а прямой вопрос «а точно ли?». Норма отсюда: **перед любым сравнительным
выводом — открыть хотя бы одну единицу с каждой стороны и посмотреть на неё глазами.**
### 11.6 Что осталось и сколько стоит (для следующей выдачи)
| Работа | Цена | Почему именно столько |
|---|---|---|
| Судья на 24 готовых парах P4, оба порядка + пол | **≈$0.38** | 50 голосов × $0.0076 (замерено); генерация уже оплачена |
| То же, только боевое семейство (3 пары deepseek) | **≈$0.11** | 14 голосов; но n=3 — мощности не даст |
| Базовый echo-rate в БОЕВОЙ форме на 0731 | **$0** сегодня невозможен | нужна развилка §2 (флор или явный эффорт) |
| Проба префилла «Перевод:\n» на 0731 | **≈$0.005** | 6 вызовов; на 0731 довод против префилла ослаб (thinking стал дорогим) |
| Проба `effort:"high"` явно (дефолт ли это) | **≈$0.01** | 24 вызова, возможен упор в потолок |
| ja-вход (область §10.4) | **≈$0.005** | 34 вызова |
---
# §12. КОНСОЛИДИРОВАННЫЙ ИТОГ — читать ВМЕСТО §1§11 при любом расхождении
Записано после (а) адверсариального агентского ревью (15 агентов, 5 линз × скептик на каждую несущую
находку, 1.47 М токенов, 459 инструментальных вызовов) и (б) исполнения деливерабла, который первый
проход пропустил: судейского хвоста на готовых парах P4 по санкции владельца.
**Ниже — единственная действующая редакция выводов. §1§11 сохранены как хроника (D23.3) и в трёх
местах ОПРОВЕРГНУТЫ этим разделом — расхождения перечислены в §12.5.**
## 12.1 Ответ по петле D21 п.6 — ГЕЙТ ВЕРНОСТИ ПРОЙДЕН, и направление ОБРАТНОЕ ожидаемому
Судейский риг на **готовых парах база↔митигация** из `research15-probes` (генерация оплачена 09.07;
куплены только голоса). Маршрутизация судей по семейству ПАРЫ, чтобы автор ≠ ревьюер:
grok-пары судит `deepseek-v4-pro`, deepseek-пары — `grok-4.3` (pro вендором 31.07 НЕ обновлялся —
как прибор он на прежнем поведении). Полные окна, оба порядка, пер-голосовой персист.
**FLOOR на идентичных текстах: 4/4 «tie» у ОБОИХ судей** — пре-регистрированный порог П5 (≥3/4) пройден
на полном знаменателе, в отличие от §3.
| семейство | митигация | пар | митигация лучше в ОБОИХ порядках | база лучше в обоих | переворот порядком | дефектов у базы | у митигации |
|---|---|---|---|---|---|---|---|
| grok | few-shot | 4 | **4** | 0 | 0 | 30 | 13 |
| grok | инстр-после | 5 | 2 | 1 | 2 | 36 | 30 |
| grok | комбо | 4 | 2 | 0 | 2 | 31 | 12 |
| **deepseek** (скоуп wiring) | инстр-после | 1 | **1** | 0 | 0 | 4 | **0** |
| **deepseek** (скоуп wiring) | префилл | 1 | **1** | 0 | 0 | 3 | **0** |
| **deepseek** (скоуп wiring) | few-shot | 1 | 0 | 0 | 1 | 2 | 4 |
> **Итог по ПАРАМ: 16 судимых · митигация лучше в обоих порядках — 10 · база — 1 · переворот — 5.**
>
> ⚠ **САМОРЕВЬЮ (проведено ПОСЛЕ первой редакции этого раздела и опровергло её):** знаковый тест по
> парам даёт p=0.0117, **но это ПСЕВДО-РЕПЛИКАЦИЯ**. 16 пар выросли всего из **6 базовых текстов**
> (одна база сравнивается с тремя митигациями). Единица независимости — базовый текст, не пара.
> **Кластерный счёт: 5 из 6 кластеров СМЕШАННЫЕ** (внутри одной базы митигация где-то выигрывает,
> где-то проигрывает/переворачивается), чисто-митигационный кластер один, чисто-базовых ноль ⇒
> **знаковый тест по независимым кластерам p = 1.0**. Заявленного эффекта НЕТ.
>
> Дефекты, **нормированные на базовый текст** (сырые 106 против 59 завышают базу повтором её в трёх
> парах): **база 41.3 · митигации 22.3**, направление сохраняется, но **на 2 базах из 6 оно
> ОБРАТНОЕ** (gu-004: база 3.0 против 14 у митигаций; gu-002: 12.0 против 21).
>
> ⚠ **Конфаунд длины не исключён:** митигированные тексты систематически чуть длиннее базы
> (медианы Δlen_ratio +0.022…+0.127), а судья считает ПРОПУСКИ — более длинный текст механически
> даёт их меньше.
**⇒ D21 п.6 (а) закрыт в ОСЛАБЛЕННОЙ форме: ЦЕНЫ ВЕРНОСТИ У МИТИГАЦИЙ НЕ ОБНАРУЖЕНО.** Именно это
гейт и спрашивал — он ставился, чтобы поймать ВРЕД, и вреда нет: ни одна митигация не проигрывает
базе устойчиво, floor чист 4/4, пропусков у митигаций меньше и по сырому, и по нормированному счёту.
**Чего заявлять НЕЛЬЗЯ (первая редакция заявляла и была опровергнута саморевью): что митигации
ВЕРНЕЕ базы.** При 6 независимых базовых текстах, 5 смешанных кластерах и неисключённом конфаунде
длины это не установлено.
Честные границы: (1) 21 из 24 пар — grok-none, а скоуп wiring D21 п.6 = только deepseek-драфт;
на боевом семействе решительных пар всего 2. (2) Базы — «выжившие»: 3 из 10 базовых выходов grok и
2 из 3 deepseek эхнули и в пары не попали, так что база сравнивается своей лучшей частью, и это
смещение работает ПРОТИВ найденного направления, а не за него. (3) 8 пар из 24 не куплены — кап $0.28
сработал (усечение по порядку обхода, не по исходу). (4) Всё это — веса `V4-Flash-Preview`/grok
образца 09.07.
## 12.2 Ответ по остальным деливераблам
| Деливерабл | Ответ | Опора |
|---|---|---|
| **B(а) fidelity-хвост** | ✅ **гейт пройден в ослабленной форме: ЦЕНЫ ВЕРНОСТИ НЕ ОБНАРУЖЕНО.** Ни одна митигация не проигрывает базе устойчиво; floor 4/4; пропусков у митигаций меньше (нормировано 41.3 против 22.3). ⚠ «Митигации ВЕРНЕЕ базы» НЕ установлено: 16 пар выросли из 6 базовых текстов, по независимым кластерам 5 из 6 смешанные, p=1.0; конфаунд длины не исключён | §12.1 |
| **B(б) повторный echo-rate** | ⚠ **в боевой форме НЕ измерен**: на 0731 при боевом конфиге канал возвращает пустой `content`. При `effort:low` база 0/12 — снижать нечего | §3, §11.3 |
| **B(в) вендор-конфликт few-shot×thinking** | ❌ **НЕ установлен**. «×1.9 размышления, +28.5% цены» — артефакт средних на бимодальном распределении: парно few-shot думает МЕНЬШЕ базы на 7/12 (медиана 65, p=0.77), бутстрап-CI отношения сумм [0.70; 5.45] и [0.83; 1.89] накрывают 1, удаление 3 фрагментов из 12 переворачивает знак | §12.5 п.1 |
| **A. Р6 «язык промптов»** | ✅ **различий не обнаружено** ни по одной детерминированной оси (эхо 0/0, срыв языка 0/0, коридор 12/12, преамбулы 0/0). Прежняя единственная положительная разница «en дороже на 9.4%» **снята**: без кэш-эффекта +6.0%, парная медиана +262 ток., знаковый тест p=0.39 | §4, §12.5 п.2 |
| **Сверх задания** | вендор-регрессия 0731 (веса сменились под слагом), ручка `reasoning_effort`, ре-ген вместо эскалации, привязка разгона к ханьскому входу, дыра в учёте денег движка | §1, §10, §12.4 |
## 12.3 РЕКОМЕНДАЦИЯ WIRING (пере-выведена; решение — оркестратора/владельца)
Прежняя редакция §3 («few-shot не вносить, потому что вреден») **отозвана**: её основание рухнуло.
Новая, по трём гейтам D21 п.6 раздельно:
- **Постинструкция (~10 токенов в хвост USER-части).** Гейт верности ПРОЙДЕН (на боевом семействе —
единственная пара, но в обоих порядках и с 4:0 по дефектам). Эхо-польза измерена на прежних весах
(0/10 против базы 3/10) и на нынешних не проверяема. Цена ≈ ноль. **Возражений по существу больше
нет; блокирует только то, что на 0731 покупать ей сегодня нечего.** Решение = стоит ли двигать
`PromptSHA256` ради страховки, чья польза сейчас неизмерима.
- **Микро-few-shot (`---FEWSHOT---`).** Гейт верности ПРОЙДЕН и с лучшим отношением дефектов (13 против
30 у базы). Вреда по цене НЕ установлено. Против него остаётся **один** факт: единственный за 48
вызовов выход целиком на английском (`a2-f04`). **Вывод: ни «вносить», ни «вредно» — оснований нет
ни на что, кроме «не трогать боевой промпт без предмета».**
- **Префилл «Перевод:\n».** Неожиданно сильный кандидат: 0/6 эха в P4, верность 3:0 в обоих порядках,
и его прежний контрдовод («обходит thinking») на 0731 **перевернулся в довод ЗА** — thinking стал
дорогой и ломающей частью. Не проверен на 0731; проба ≈$0.005.
- **Цена любой правки:** байты `translator.md` двигают `PromptSHA256` черновой волны ⇒ пере-снапшот и
перекупка черновиков всех zh-книг; дублировать в `translator-banknote.md` (общая шапка, строка 36г).
- **⚠ Языковые констрейнты — по-прежнему НЕ трогать** (инверсия 9/10, D21.6). Реплика счётчиков P4
этой сессией подтвердила инверсию побайтно: арм G3 — 9/10 эха против базы 3/10.
## 12.4 Развилка для бэкенда (не изменилась, числа уточнены)
Боевая черновая волна на 0731 при `min_max_tokens: 8000` покупает пустые ответы. Три пути:
**(а)** поднять флор (⚠ пере-снапшот всех книг; хвост не закрывает — при 16000 упор в 2 пробах из 5);
**(б)** слать `reasoning_effort` явно (дешевле прежнего; требует амендмента `echoMineViolation`;
возвращает эхо); **(в)** ждать (модель вышла «in public beta» в тот же день).
Независимо от развилки: **ре-ген перед эскалацией для `cjk_artifact`** — $0, ≈7.6× дешевле
($0.007335 за ремонт одного эха против $0.00096 за ре-ген), опирается на побайтно идентичную пару
запросов с разным исходом (§10.1).
## 12.5 ЧТО ЭТОТ РАЗДЕЛ ОПРОВЕРГАЕТ В §1§11 (принято после проверки исполнением)
1. **§3/§7/§10.1/§10.6/§11.4: «few-shot — reasoning ×1.9, цена +28.5%, оба уверенно измерены» → ОТОЗВАНО** (в т.ч. строка «B(3) ✅ дан» таблицы §11.4 — вердикт по вендор-конфликту НЕ дан).
Парно: 7/12 фрагментов few-shot думает и стоит МЕНЬШЕ базы; бутстрап 200k по отношению сумм даёт
CI [0.704; 5.446] и [0.827; 1.892]; перестановочный тест p=0.225 и 0.302; leave-one-out по
f01/f09/f11 переворачивает заголовок в 20.2%/13.1%. Механизм — бимодальный режим думания
(глубокий режим: база 4/12, few-shot 5/12, Фишер p≈1.0), то есть **ось слабее той, которую отчёт
сам заявлять отказался** (срывы 2/12 против 0/12, p=0.478). Ошибка: среднее применено к
распределению, чей тяжёлый хвост тот же отчёт задокументировал абзацем выше.
2. **§3/§4: «постинструкция дешевле на 23.3%», «en дороже на 9.4%» → СУЖЕНО.** `system` арма
постинструкции побайтно равен базовому (sha `e9ea1ae7dae0`), он стартовал следом и получил
префиксный кэш 95.0% против 27.5% у базы. По некэшированной цене: постинструкция 9.0%, en +6.0%.
По размышлению постинструкция базы ДОРОЖЕ (9 фрагментов из 12, медиана +208).
3. **§1.1: «против 2 из 68 у прогона на прежних весах тем же движком и промптом» → НЕ КОНТРОЛЬ.**
Холодный прогон ехал `translator-banknote.md` при `max_tokens=8496`; сессия — `translator.md` при
8000. Сравнение понижено до индикативного. Несущая улика регрессии — собственные пробы провода
на ОДНОМ чанке: cap 8000 → `length`+пусто, cap 16000 → `stop`+перевод.
4. **§1.3/§7: ряд `98·3117·8000·14014·16000·16001` «на одном чанке» → НЕВЕРНО.** На одном чанке
(`chunk-1-0`) это `98 · 8000⌐ · 14014 · 16000⌐`; 3117 и 16001⌐ — два других чанка. «Упор в 3 из 6
при 16000» → **2 из 5** (проба с потолком 8000 попала в знаменатель 16000).
5. **§2/§10.5: «эскалация $0.0147 ⇒ ре-ген ≈15× дешевле» → ВДВОЕ ЗАВЫШЕНО.** $0.014670 — это ДВА эха
холодного прогона по одному хопу каждое (id 58, гл.5/чанк0, $0.006979; id 62, гл.9/чанк1,
$0.007690). Ремонт одного эха = **$0.007335**, выигрыш ре-гена **≈7.6×**.
6. **§5: «$0.147527, потолок не пробит» → ЛЕДЖЕР ЕСТЬ НИЖНЯЯ ГРАНИЦА.** Движок залогировал 4 вызова
с `err="deepseek: 2xx body decode failed (call IS billed)"` (`B-a0/run.log:8-11`, 18:53:17.907):
провайдер списал, ретрай заледжерен, исходная попытка — нет. Верхняя оценка неучтённого
4 × $0.002253 = **$0.009012** ⇒ потолок $0.15 мог быть пробит на $0.0065. **Это и находка для
бэкенда: собственный текст ошибки говорит «call IS billed», а в `request_log` строка не попадает.**
7. **§3 floor:** пре-регистрация требовала 2 единицы × 2 порядка, исполнено 1 × 2, и порог «tie реже
3/4» оценён на знаменателе 2. В §12.1 floor исполнен на полном знаменателе (4/4) — читать его.
8. **§11.2: «ни одна митигация не даёт подписи пропуска на всех 24 парах»** — проверка хвостами
сделана на 3 парах из 24; на 4 парах Δlen_ratio ниже собственного порога 0.15 и хвостами не
разобрана. Судейский счёт пропусков (26 у базы против 15 у митигаций) направление подтверждает,
но детерминированное «ни одна» шире улики.
10. **§12.1 первой редакции: «митигации вернее базы, p=0.0117» → ОТОЗВАНО СОБСТВЕННЫМ САМОРЕВЬЮ** (псевдо-репликация: 16 пар из 6 базовых текстов, по кластерам 5 из 6 смешанные, p=1.0; дефекты нормированы 41.3 против 22.3, на 2 базах из 6 направление обратное; конфаунд длины не исключён). Действующая формулировка — «цены верности не обнаружено».
9. **Отклонено как неверное** (проверено сырьём): что счётчики `parse_fail`/`bad_lines` не существуют
(существуют, ролевые; A-ru дал 0/0 на полном отыгрыше); что «стоп-гейт не сработал» (в промте
«упор» = бюджетный потолок, он достигнут не был); и пересчёт базы Фишера — сырьё опровергает его
в обратную сторону.
## 12.6 Деньги — итог сессии $0.433062, потолок промта $0.15 + санкция владельца
| источник | вызовов | путь 1 | путь 2 (пере-счёт из сырого usage) |
|---|---|---|---|
| движок A-ru (отыгрыш чекпойнтов) | 29 | 0.000000 | 0.000000 |
| движок A-en · B-a0 | 31 | 0.028047 | 0.028047 |
| wire-пробы | 12 | 0.022005 | 0.022006 |
| армы A/B | 48 | 0.047754 | 0.047756 |
| судья армов (grok-4.3) | 7 | 0.049721 | 0.049721 |
| **судья пар P4** (dspro 28 + grok 8) | 36 | **0.285535** | 0.285535 |
| **ИТОГО** | **163** | **0.433062** | **0.433065** |
Два пути сходятся до 2.9e-06. **⚠ Плюс неучтённое ≤$0.009012 (п.6).** Судья остановлен своим капом
$0.28 на 16 парах из 24; смета обещала $0.2401 и **промахнулась в 1.19×**, потому что
`deepseek-v4-pro` думает 5097 токенов на голос вместо заложенных 1600 — число для следующей сметы.
Все платные вызовы — 18:4521:20 UTC, вне пиков DeepSeek. Стенд: 1117 хешей, дифф пуст.
## 12.7 Остаток и цена
| Работа | Цена | Зачем |
|---|---|---|
| Добрать 8 неcудимых пар P4 | ≈$0.10 | полное покрытие деливерабла (1) |
| Проба префилла на 0731 | ≈$0.005 | самый сильный кандидат по §12.3, не проверен на новых весах |
| `effort:"high"` явно — дефолт ли это | ≈$0.01 | закрывает механизм §1 окончательно |
| ja-вход | ≈$0.005 | §10.4 держится на en n=1 против zh n=1 |
| Базовый echo-rate в боевой форме | **невозможен** до развилки §12.4 | единственная неизмеренная ось петли |

View file

@ -26,7 +26,7 @@
| Провайдер | Поведение по умолчанию | Как управлять (для перевода) |
|---|---|---|
| **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-выводу (перепроверить). |
| **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~~ **→ см. строку 0731 ниже: флор 8000 ПРОБИТ**. ⚠ **thinking-режим НЕ поддерживает `temperature`/`top_p`/penalties** (шлём — молча игнорируются). ~~`reasoning_effort`: только `high`/`max` (low/medium→high, xhigh→max)~~ **→ УСТАРЕЛО, см. строку 0731**; `none` в OpenAI-формате НЕ существует → 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) |
@ -36,6 +36,22 @@
| **grok-4.3 (xAI)** | reasoning **ON по дефолту = `low`** (must-be-explicit: не задал → думает) | **Off-switch (Chat Completions) — ПЛОСКИЙ `reasoning_effort:"none"` в теле** (`reasoning_effort` ∈ {none,low,medium,high}). ⚠ **Вложенная форма `extra_body.reasoning.effort:"none"` — это Responses API; Chat Completions её МОЛЧА ГЛОТАЕТ** (остаётся дефолт `low`) — из-за этого exp08-проба ложно решила «не отключается». Проверено 05.07: `reasoning_effort:"none"``usage.reasoning_tokens=0`, дефолт→444, `low`→384 (на правке ~2000 ток.). reasoning у xAI **аддитивен к completion** (billed = completion+reasoning). `grok-4.20-0309-non-reasoning` = grok-4.3 + `effort:none` под капотом (ретайр 15.05.2026) → слаг не нужен, слать `none` явно. ⚠ **Для роли РЕДАКТОРА reasoning-off СНЯТ (D30.1): grok reasoning-off — no-op-редактор** (exp12 §2.2: активность 0.001, 3/6 чанков байт-идентичны черновику; exp04-ранг был на дефолт-reasoning + БИЛИНГВ). Если grok когда-либо вернётся в редакторы — только reasoning-ON (D6.2-буфер). Для роли ПЕРЕВОДЧИКА на плотном CJK reasoning-off НЕПРИГОДЕН (D19.1: эхо 4/10 + дегенеративный луп; ⚠ языковой констрейнт в system при `none` ИНВЕРТИРУЕТ эхо 3/10→9/10 — research/15 P4) — эскалация канала B идёт reasoning-ON. *(Сужено оркестратором по D19.1/D21; было «editor/translator».)* Источник: docs.x.ai/developers/model-capabilities/text/reasoning. |
| Qwen3.5 / локальные | thinking ON | ollama: `think: false` + `num_predict` cap. Иначе рассуждения (в поле `thinking`, не `response`) забивают контекст |
### ⚠ `deepseek-v4-flash``DeepSeek-V4-Flash-0731`: слаг стабилен, ВЕСА СМЕНИЛИСЬ (полигон, 2026-07-31)
**Вендор-факт** (`api-docs.deepseek.com/updates`, запись `Date: 2026-07-31`; снято `curl`, счёт вхождений = 1 по копии без пробелов): *«The official release of the DeepSeek-V4-Flash API is now in public beta. The API calling method remains unchanged — simply set the model name to `deepseek-v4-flash` to use the latest version.»* · *«DeepSeek-V4-Flash-0731 keeps the same model architecture and size as DeepSeek-V4-Flash-Preview, and was only re-post-trained.»* · *«Significantly enhanced **agent** capabilities…»* · *«Note: This update only upgrades the DeepSeek-V4-Flash API. The DeepSeek-V4-Pro API and the APP/WEB models are unchanged.»***v4-pro (редактор, эскалационный хоп) НЕ тронут.** В `/v1/models` ничего не изменилось — там прежние два слага. **Урок календаря: «слаг живой» ≠ «модель та же»; версию проверять поведением, а не листингом.**
1. **ФЛОР 8000 ПРОБИТ — боевая форма отдаёт ПУСТОЙ `content`.** Живьём 31.07 18:54 UTC: черновая волна боевым `translator.md` при `max_tokens=8000` дала `finish=length`, `completion_tokens=8000`, `content` = 0 символов на **4 чанках из 4**. ⚠ **Сравнение с холодным прогоном (2 из 68) НЕ является чистым контролём и понижено до индикативного:** тот ехал ДРУГИМ промптом (`translator-banknote.md`) и с ДРУГИМ потолком (`max_tokens=8496`). Несущая улика — не оно, а СОБСТВЕННЫЕ пробы провода: **один и тот же чанк, один и тот же рендер, cap 8000 → `length`+пусто, cap 16000 → `stop`+перевод** (таблица п.2). *(Испр. 31.07 по ревью.)*
2. **Механизм — не дегенеративный луп, а многословная обдумка.** 20 025 симв. `reasoning_content` = 122 строки, все уникальны: модель пере-переводит фразу за фразой внутри размышления и не доходит до ответа. Наблюдённый `reasoning_tokens` **на одном и том же чанке** (`chunk-1-0`, дефолтный эффорт): **98 · 8000⌐ · 14014 · 16000⌐** (⌐ = упор в потолок) — разброс ×163 при побайтно одном входе. На двух других чанках: 3117 и 16001⌐. **Хвост тяжёлый: при потолке 16000 упор случился в 2 пробах из 5.** *(Испр. 31.07 по ревью: прежняя формулировка смешивала три чанка в один ряд и пробу с потолком 8000 в знаменатель 16000.)*
3. **`reasoning_effort` — запись 04.07 устарела.** Вендор-дока `guides/thinking_mode` (снята 31.07, счёт = 3) разделяет **тумблер** `{"thinking":{"type":"enabled/disabled"}}` и **эффорт** `{"reasoning_effort":"low/high/max"}`: `low` — отдельный документированный уровень. Замер: `low` при потолке 8000 и temperature 0.3 → `finish=stop`, reasoning **170313** ток., **$0.000360.00091** за вызов (без ручки — $0.00220.0045 за пустой ответ).
4. **`effort:"low"` ПЕРЕ-ВООРУЖАЕТ эхо-мину:** 1 чистый китайский выход из 16 базовых вызовов (`cjk_share 0.83`, `finish=stop`). **Уточнение рамки D19.2: защита от эха деградирует НЕПРЕРЫВНО с эффортом, а не скачком на «выключено».** Движок эту ручку для deepseek слать не может и не должен — `config.echoMineViolation` (`models.go:276-281`); замер — его первое эмпирическое подтверждение.
5. **Микро-few-shot: ⚠ ПРЕЖНЯЯ ФОРМУЛИРОВКА ОТОЗВАНА (31.07, адверсариальное ревью).** Числа «reasoning ×1.9» и «постинструкция дешевле на 23.3%» получены НЕПАРНЫМ сравнением средних на распределении, чей разброс ×163 (п.2). **Парный пересчёт по тем же 12 фрагментам:** few-shot думает МЕНЬШЕ базы на **7 из 12** (медиана Δ **65** ток., знаковый тест p=0.77); постинструкция думает БОЛЬШЕ базы на **9 из 12** (медиана Δ **+208**, p=0.15). «+91%» — артефакт четырёх попаданий в хвост. Скидка постинструкции на 58% состоит из ПРЕФИКСНОГО КЭША (её `system` побайтно равен базовому — sha `e9ea1ae7dae0`, постинструкция уходит в USER-хвост; кэш 95.0% против 27.5% у базы); при пересчёте по некэшированной цене остаётся 9.0%. **Что устояло:** суммарная цена арма few-shot на этой выборке +28.4% и без кэш-эффекта, и **единственный за 48 вызовов выход НА АНГЛИЙСКОМ** (5 770 симв, `finish=stop`) — его. Эхо в том же арме НЕ атрибутируется few-shot (см. п.6).
6. **⚠ ЭХО НА 0731 СТОХАСТИЧНО ПО ВЫЗОВУ, а не детерминировано по фрагменту** — прямая улика: два вызова с **побайтно идентичным запросом** (sha `5c304c9b3cf4`, `effort:low`, cap 8000, temp 0.3) дали `cjk_share 0.000` и `cjk_share 0.831`. **Это отменяет посылку строки ниже («ретраи не помогают — детерминировано для фрагмента»), снятую на `deepseek-chat`, и посылку комментария `disposition.go:138-148` («retry just re-produces them»), из-за которой `cjk_artifact` идёт СРАЗУ в эскалацию.** Арифметика ремонта перевернулась: эскалация на v4-pro = **$0.007335 за ОДНО эхо** ($0.014670 холодного прогона — это ДВА эха на разных чанках, id 58 и 62), простой ре-ген на flash при `effort:low` = **$0.00096** (**≈7.6×** дешевле). Предложение бэкенду: N=1 ре-ген ПЕРЕД эскалацией. Гейт не ослабляется — D19.2 цел.
7. **Разгон думания привязан к ПЛОТНОМУ ХАНЬСКОМУ ВХОДУ, не к модели вообще.** Тот же промпт и параметры (cap 8000, дефолтный эффорт): **en** (877 ток. входа) → `stop`, reasoning **435**; **zh короткий** (776 ток.) → `stop`, reasoning **3393**; **zh длинный** (1679 ток.) → `length`, reasoning **8000⌐**, `content` пуст. При сопоставимом входе zh требует **×7.8** размышления против en ⇒ конфаунд длины закрыт. Границы: en n=1, zh-короткий n=1, zh-длинный n=8; **ja не проверялся**.
8. **Вендорская рамка бюджета вывода** (`quick_start/pricing`): контекст 1M, **max output 384K**, и *«For the high and max reasoning effort levels, a maximum output length of 384K tokens is recommended»*. Наш флор 8000 — **на два порядка ниже** вендорской нормы для высокого эффорта ⇒ дело не в «поломке», а в смещении ДЕФОЛТНОГО эффорта при нашей калибровке под прежний чекпойнт. Багрепортов о регрессии в открытом поиске нет (31.07).
9. **Движковый леджер `reasoning_tokens` для deepseek держит 0 СОЗНАТЕЛЬНО** (`provider_openai.go:22-24`, `ReasoningSubset` — thinking внутри `completion_tokens`, иначе двойной счёт). Длину думания видно только на сыром проводе — при диагностике идти туда, а не в `request_log`.
*(Полигон, отчёт `archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md`; харнесс `eval/promptlang/`. Развилка «поднять флор / разрешить `low` / ждать» — за подписью владельца, §2 отчёта.)*
**Находка (эксп. 03):** reasoning реально улучшает реалии/имена в переводе (羅生門: «Радзёмон»→«Рашо-мон»), но локально нежизнеспособен (8.6 мин/фрагмент). **Открытый вопрос для бэкенда: проверить think-ON на быстром облачном черновике/редакторе как рычаг качества.**
### Эхо как свойство КЛАССА, не квирк вендора (обобщение D19.2 / D21.9)
@ -44,6 +60,9 @@
- **Для роли ПЕРЕВОДЧИКА на плотном CJK reasoning-off непригоден в принципе** (не только у одного вендора) — канал B переводит reasoning-ON (grok) либо не-reasoning-моделью без эхо-мины (Mistral: проба zh→ru чисто, `cjk_share=0`). ⚠ **Register-оговорка (exp11/D22, дописано оркестратором):** reasoning-ON снимает эхо на СОВРЕМЕННОЙ прозе (совр. zh-вебновелла 0/10, совр. ja 1/6, en 0/6), но НЕ на архаичной плотно-ханьской (金瓶梅, минский байхуа: grok-ON эхо 4/6 при `reasoning_tokens` 349847; ON vs OFF там неразличимы) — эскалацию на grok-ON не считать лекарством от эха на архаичных zh-текстах; Mistral на той же архаике чист 6/6.
- **Downstream echo-гейт (`untranslated_echo`, `cjk_share>0.15`) обязателен НАВСЕГДА** и промпт-митигациями НЕ заменяется — это последняя линия против тихого провала.
- ⚠ **thinking-ON НЕ исключает эхо даже на современной прозе (живой прогон 31.07, D39.58):** 2/20 черновиков `deepseek-v4-flash` thinking-ON вернулись чистым исходником (`han_share=0.999`, 蛊真人 гл.5/9, современный zh). Гейт поймал оба, эскалация на dspro вылечила ($0.0147). thinking-ON снижает ЧАСТОТУ эха, но не до нуля; «echo 0» пере-прогона 23.07 (D39.20) был свойством выборки. Практика: закладывать в смету черновой волны ~10% на эскалации. *(Внесено оркестратором по D39.58.)*
- ✅ **FIDELITY-ГЕЙТ ПЕТЛИ D21 п.6 ПРОЙДЕН, направление ОБРАТНОЕ (31.07, судейский риг на готовых парах P4, $0.2855).** Судьи ЧУЖИХ семейств (grok-пары судит `deepseek-v4-pro`, deepseek-пары — `grok-4.3`), полные окна, оба порядка, **floor на идентичных текстах 4/4 «tie» у обоих**. На 16 судимых парах митигация лучше базы в обоих порядках в 10 случаях, база — в 1. ⚠ **Но 16 пар выросли из 6 базовых текстов; по НЕЗАВИСИМЫМ кластерам 5 из 6 смешанные, знаковый тест p=1.0** — «митигации вернее» НЕ установлено. Дефекты, нормированные на базовый текст: база 41.3 против 22.3 (сырые 106/59 завышают базу троекратным повтором); на 2 базах из 6 направление обратное. Конфаунд длины не исключён (митигации чуть длиннее, судья считает пропуски). ⇒ **Читать так: ЦЕНЫ ВЕРНОСТИ У МИТИГАЦИЙ НЕ ОБНАРУЖЕНО** — это ровно то, что гейт D21 п.6 спрашивал; «митигации лучше» — нет. Границы: 21 из 24 пар — grok (вне скоупа wiring D21 п.6), базы — «выжившие» после эха (смещение работает ПРОТИВ найденного направления), 8 пар не куплены (кап).
- ⚠ **Пере-читка 24 готовых пар P4 (31.07, $0):** ни одна митигация (инстр.-ПОСЛЕ / few-shot / комбо / префилл) **не даёт подписи ПРОПУСКА** — все митигированные выходы доходят до того же конца исходника, что база; Δlen_ratio медианно +0.02…+0.13, то есть митигации чуть МНОГОСЛОВНЕЕ, а не короче. Найден один дефект формы: 1 из 3 выходов «инстр.-ПОСЛЕ» на deepseek дописал **сноски переводчика `[1]…[6]`** вопреки «Выведи ТОЛЬКО перевод» (класс output-санитайзера D30.3). Ось «искажение при сохранённом объёме» детерминированно не берётся — нужен судья.
- ⚠ **Вся эмпирика P4 (research/15, 09.07) снята на весах `V4-Flash-Preview`, которых больше нет** (катовер 31.07 — секция выше). «0/29 против базы 3067%» — про прежнюю модель; на 0731 база даёт 0/12 эха при `effort:low`, а по few-shot вердикта НЕТ — первая формулировка «вреден» отозвана самой сессией (п.5 секции катовера; докорректировано оркестратором 01.08 по §12.5 отчёта). Цитировать P4 как текущую эмпирику канала нельзя. *(Полигон, 31.07.)*
- ⚠ **Инверсия языкового констрейнта (research/15 P4):** языковая строка в `system` у reasoning-off модели может **УСИЛИВАТЬ** эхо, а не гасить его — на grok `reasoning_effort:none` одна формулировка констрейнта подняла эхо 3/10→9/10. Проверена **одна** формулировка на одном слаге; чувствительность к формулировке/модели неизвестна. → **Запрет (D21.6): не вносить языковые констрейнты в промпты reasoning-off путей без per-model замера.** (Латентное: `translator.md:10` уже несёт языковую строку, но едет только на thinking-ON DeepSeek — вне зоны инверсии; при заводке Mistral-канала B прогнать P4-реплику на боевом промпте.)
## Параметры сэмплинга

View file

@ -0,0 +1,117 @@
#!/usr/bin/env python3
"""$0 детерминированная батарея по армам (задачи A и B). Судья не участвует.
Приборы:
· ЭХО `cjk_share > 0.15`, определение движка (`disposition.go:269-281`, Han|Hiragana|Katakana);
порог тот же, что у P4, поэтому числа сравнимы с 09.07 напрямую.
· ЯЗЫК ВЫХОДА доля кириллицы против латиницы среди БУКВ. Это встречный риск en-промпта,
названный в промте сессии: английская инструкция может утянуть выход в английский.
· АНТИ-ОМИССИЯ len_ratio (симв. без пробелов, коридор пары [2.2, 4.2] из `pairs/zh-ru.yaml`)
и отношение числа предложений (оракул `refusal_bench.split_sentences`).
· ФОРМА преамбула, markdown-заголовок, утёкший разделитель банкноты, латинские прогоны.
· ПРОВОД reasoning_tokens (сырьё, движок их для deepseek сознательно обнуляет), finish, цена.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
import unicodedata
from pathlib import Path
REPO = Path("/home/ubuntu/projects/textmachine")
sys.path.insert(0, str(REPO / "eval"))
from refusal_bench import split_sentences # noqa: F401 (оракул уже применён в run_arms)
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
PREAMBLE_RE = re.compile(r"^\s*(вот|ниже|перевод[:\s]|here is|translation[:\s]|note[:\s])", re.I)
MD_RE = re.compile(r"^\s{0,3}#{1,6}\s", re.M)
LAT_RUN_RE = re.compile(r"[A-Za-z]{3,}")
def script_mix(s: str) -> tuple[float, float, int]:
cyr = lat = 0
for ch in s:
if not ch.isalpha():
continue
n = unicodedata.name(ch, "")
if n.startswith("CYRILLIC"):
cyr += 1
elif n.startswith("LATIN"):
lat += 1
tot = cyr + lat
return (cyr / tot if tot else 0.0), (lat / tot if tot else 0.0), tot
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--arms", default="a0,a1,a2,e0")
ap.add_argument("--raw", type=Path, default=PL / "armsraw")
a = ap.parse_args()
per = {}
for arm in a.arms.split(","):
rs = []
for f in sorted(a.raw.glob(f"{arm}-f*.json")):
j = json.loads(f.read_text(encoding="utf-8"))
if "error" in j:
rs.append(j)
continue
c = j["content"]
cyr, lat, letters = script_mix(c)
j["cyr_share"] = round(cyr, 4)
j["lat_share"] = round(lat, 4)
j["off_language"] = bool(letters) and cyr < 0.5 # выход не в целевом письме
j["preamble"] = bool(PREAMBLE_RE.match(c))
j["markdown"] = bool(MD_RE.search(c))
j["bank_marker"] = "⟦TM-BANK" in c
j["latin_runs"] = len(LAT_RUN_RE.findall(c))
j["in_corridor"] = (j["len_ratio"] is not None and 2.2 <= j["len_ratio"] <= 4.2)
rs.append(j)
per[arm] = rs
print(f"{'арм':<5}{'n':>3}{'эхо':>5}{'off_lang':>9}{'вне кор.':>9}{'преамб':>8}{'md':>4}"
f"{'лат.прог':>9}{'ср.lr':>7}{'ср.sent':>9}{'ср.rt':>7}{'сумма $':>10}")
for arm, rs in per.items():
ok = [r for r in rs if "error" not in r]
if not ok:
continue
clean = [r for r in ok if not r["echo"]]
lr = [r["len_ratio"] for r in clean if r["len_ratio"] is not None]
sr = [r["sent_out"] / r["sent_src"] for r in clean if r["sent_src"]]
print(f"{arm:<5}{len(ok):>3}{sum(r['echo'] for r in ok):>5}"
f"{sum(r['off_language'] for r in ok):>9}"
f"{sum(1 for r in clean if not r['in_corridor']):>9}"
f"{sum(r['preamble'] for r in ok):>8}{sum(r['markdown'] for r in ok):>4}"
f"{sum(r['latin_runs'] for r in ok):>9}"
f"{(sum(lr)/len(lr) if lr else 0):>7.2f}{(sum(sr)/len(sr) if sr else 0):>9.2f}"
f"{sum(r['reasoning_tokens'] for r in ok)/len(ok):>7.0f}"
f"{sum(r['cost_usd'] for r in ok):>10.6f}")
print("\nпофрагментно (эхо и коридор — по фрагментам, чтобы видеть, ГДЕ именно):")
frags = sorted({r["frag"] for rs in per.values() for r in rs})
print(f"{'фраг':<6}" + "".join(f"{arm:>18}" for arm in per))
for fr in frags:
cells = []
for arm, rs in per.items():
r = next((x for x in rs if x["frag"] == fr), None)
if r is None or "error" in r:
cells.append(f"{'':>18}")
continue
mark = "ЭХО" if r["echo"] else ("OFFLANG" if r["off_language"] else
("вне кор." if not r["in_corridor"] else "ok"))
cells.append(f"{mark + ' lr=' + str(r['len_ratio']):>18}")
print(f"{fr:<6}" + "".join(cells))
out = PL / "analyze_arms.json"
out.write_text(json.dumps({k: [{kk: vv for kk, vv in r.items()
if kk not in ("system", "user", "reasoning_content")}
for r in v] for k, v in per.items()},
ensure_ascii=False, indent=1), encoding="utf-8")
print(f"\nсырьё сведено: {out}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,296 @@
#!/usr/bin/env python3
"""$0 стройка проектов-проб для пакета «язык промптов (Р6) + fidelity-хвост P4».
Ничего не вызывает у провайдеров. Собирает под `~/books/gu-zhenren/promptlang/`:
A-ru / A-en задача A: КОПИЯ БД холодного прогона (тот же book_id, тот же снапшот черновые
чекпойнты попадают, волна не перекупается), различие ровно одно `prompts_root`
пар-конфига указывает на дерево с ru- или en-терминологом. Полезная нагрузка роли
(кандидаты, KWIC, черновики, батчинг) при этом байт-идентична по построению:
она собирается из ОДНОЙ И ТОЙ ЖЕ БД одним и тем же кодом.
B-a0/a1/a2 задача B: ЧИСТЫЕ БД на СВЕЖЕМ срезе (главы 1116, вне среза холодного прогона),
одна черновая стадия, три варианта `translator.md` через `prompt_override`.
Стендовые артефакты не трогаются: всё живёт в отдельном каталоге, БД холодного прогона копируется.
"""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
REPO = Path("/home/ubuntu/projects/textmachine")
BOOKS = Path.home() / "books" / "gu-zhenren"
PL = BOOKS / "promptlang"
COLD = BOOKS / "coldrun-a"
BACKEND = REPO / "backend"
SLICE_B = PL / "slice" / "guzhenren-s11-16.gb18030.txt"
PAIR_YAML = """# Пар-слой zh→ru — ПРОГОННАЯ КОПИЯ ратифицированного `backend/configs/pairs/zh-ru.yaml`.
# Отличие ровно одно и оно и есть предмет эксперимента: prompts_root показывает на дерево промптов
# арма. Числа нарезки и коридор len_ratio скопированы побайтно — иначе сравнивались бы две вещи.
pair: zh-ru
prompts_root: {prompts_root}
segmentation:
draft_budget_out: 1797
edit_ceiling_out: 3200
fertility:
cjk: 1.1978
other: 0.3852
coverage:
len_ratio_bounds: [2.2, 4.2]
"""
# ── задача A ────────────────────────────────────────────────────────────────────────────────────
A_PIPELINE = """# Задача A (Р6, «язык промптов»), арм {arm}. Ран-конфиг — КОПИЯ конфига холодного прогона.
# Изменены ровно два числа, и оба — предохранители, а не предмет замера:
# gates.terminology.budget_usd 0.05 → 0.07 (роль на этой БД уже потратила $0.030254; при 0.05
# арм оборвался бы на середине и сравнивать было бы
# нечего — усечённый арм это не результат, а брак)
# Всё, что входит в СНАПШОТ (стадии, модели, температуры, промпт черновика, гейты банкноты),
# оставлено побайтно — иначе черновая волна была бы перекуплена.
core: C1
version: 1
defaults:
max_output_ratio: 2.2
min_max_tokens: 2048
context:
glossary_injection: selective
glossary_token_budget: 800
cache_ttl: "5m"
waves:
workers: 4
retries:
regenerate_before_escalate: 1
stages:
- name: draft
role: translator
model: deepseek-v4-flash
prompt_override: /home/ubuntu/projects/textmachine/backend/prompts/zh-ru/translator-banknote.md
prompt_version: v1-reflow-banknote
temperature: 0.3
reasoning: "off"
escalate_to: deepseek-v4-pro
- name: edit
role: editor
model: deepseek-v4-pro
prompt_version: v3-discourse-reflow
temperature: 0.4
reasoning: "off"
few_shot: false
gates:
coverage:
enabled: false
sent_cov_min: 0.75
min_chunk_chars: 500
sanitizer:
enabled: true
regression_guard:
enabled: true
banknote:
enabled: true
terminology:
enabled: true
model: deepseek-v4-flash
budget_usd: 0.07
target_script: Cyrillic
voice:
enabled: true
escalation:
chains:
default: [deepseek-v4-pro, glm-5.1, gemini-3.1-pro-preview]
adult: [grok-4.3]
budget_usd: 0.10
fanout:
candidates: 1
mining:
contrast_path: /home/ubuntu/projects/textmachine/eval/exp16/data/jieba_dict_general_zh.txt
"""
A_BOOK = """# Задача A, арм {arm}. book_id СОХРАНЁН (`guzhenren-coldrun-a`): он входит в RequestHash
# (render.go:269-283), и его смена перекупила бы всю черновую волну. Срез, сид (его нет), langpack,
# models.yaml — те же файлы, что у холодного прогона; изменены только пути проекта и потолок.
book_id: guzhenren-coldrun-a
title: 蛊真人
source_lang: zh
target_lang: ru
genre: вебновелла
audience: взрослые читатели вебновелл
adult: false
venuti: 0.6
honorifics: keep
transcription: palladius
footnotes: minimal
pipeline: {dir}/pipeline.yaml
models: /home/ubuntu/projects/textmachine/backend/configs/models.yaml
source_file: /home/ubuntu/books/gu-zhenren/coldrun-a/guzhenren-ch1-10.gb18030.txt
encoding: gb18030
langpack_root: /home/ubuntu/projects/textmachine/backend/configs/langpacks
langpack_extend: /home/ubuntu/books/gu-zhenren/langpack-extend
mined_delta: {dir}/mined-delta.yaml
mined_rejects: {dir}/mined-rejects.yaml
project_db: {dir}/db.sqlite
# committed на копии = $0.126068 (наследство холодного прогона). Потолок 0.16 оставляет $0.0339 —
# больше ожидаемого прохода ($0.013), но физически способен выстрелить.
ceilings:
book_usd: 0.16
day_usd: 0.16
"""
# ── задача B ────────────────────────────────────────────────────────────────────────────────────
B_PIPELINE = """# Задача B (D21 п.6, анти-эхо), арм {arm}: {arm_note}
# ОДНА стадия — черновик. Редактуры нет: предмет замера — выход ЧЕРНОВОГО канала, а edit-волна
# стоила бы в четыре раза дороже всего пакета.
# escalate_to НЕТ и regenerate_before_escalate=0 СОЗНАТЕЛЬНО: эхо здесь ИЗМЕРЯЕТСЯ, а не чинится.
# Ремонт (эскалация на dspro) — боевое поведение, но он превратил бы один чанк в два-три вызова и
# смешал бы частоту эха с ценой его лечения.
core: C0
version: 1
defaults:
max_output_ratio: 2.2
min_max_tokens: 2048
context:
glossary_injection: selective
glossary_token_budget: 800
cache_ttl: "5m"
waves:
workers: 4
retries:
regenerate_before_escalate: 0
stages:
- name: draft
role: translator
model: deepseek-v4-flash
prompt_override: {arm_prompt}
prompt_version: promptlang-{arm}
temperature: 0.3
reasoning: "off"
gates:
coverage:
enabled: false
sent_cov_min: 0.75
min_chunk_chars: 500
sanitizer:
enabled: true
regression_guard:
enabled: true
banknote:
enabled: false
terminology:
enabled: false
voice:
enabled: false
fanout:
candidates: 1
"""
B_BOOK = """# Задача B, арм {arm}. Срез — главы 1116 蛊真人 (строки 837..1293 utf8-копии), НЕ
# пересекается со срезом холодного прогона (главы 110): «свежая выборка» петли D21 п.6.
# Сида НЕТ: банк пуст, инъекция глоссария пуста во ВСЕХ трёх армах ⇒ инъекции байт-идентичны
# тривиально, и различие армов — ровно текст промпта.
book_id: promptlang-{arm}
title: 蛊真人
source_lang: zh
target_lang: ru
genre: вебновелла
audience: взрослые читатели вебновелл
adult: false
venuti: 0.6
honorifics: keep
transcription: palladius
footnotes: minimal
pipeline: {dir}/pipeline.yaml
models: /home/ubuntu/projects/textmachine/backend/configs/models.yaml
source_file: {slice}
encoding: gb18030
langpack_root: /home/ubuntu/projects/textmachine/backend/configs/langpacks
langpack_extend: /home/ubuntu/books/gu-zhenren/langpack-extend
project_db: {dir}/db.sqlite
ceilings:
book_usd: 0.02
day_usd: 0.02
"""
ARM_NOTES = {
"a0": "БОЕВОЙ translator.md побайтно (базлайн; sha совпадает с боевым файлом).",
"a1": "боевой + ПОСТИНСТРУКЦИЯ после {{text}} (хвост USER-части) — дословно строка P4.",
"a2": "боевой + МИКРО-FEW-SHOT в штатный блок ---FEWSHOT--- (хвост system, D38.4) — дословно пары P4.",
}
def write(p: Path, s: str) -> None:
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(s, encoding="utf-8")
def build_a() -> None:
for arm, root in (("ru", PL / "prompts-ru"), ("en", PL / "prompts-en")):
d = PL / f"A-{arm}"
d.mkdir(parents=True, exist_ok=True)
shutil.copy2(COLD / "guzhenren-coldrun-a.db", d / "db.sqlite")
shutil.copy2(COLD / "mined-delta.yaml", d / "mined-delta.yaml")
shutil.copy2(COLD / "mined-rejects.yaml", d / "mined-rejects.yaml")
write(d / "pipeline.yaml", A_PIPELINE.format(arm=arm))
write(d / "book.yaml", A_BOOK.format(arm=arm, dir=d))
write(d / "pairs" / "zh-ru.yaml", PAIR_YAML.format(prompts_root=root))
print(f"A-{arm}: собран, prompts_root={root}")
def build_b() -> None:
for arm in ("a0", "a1", "a2"):
d = PL / f"B-{arm}"
d.mkdir(parents=True, exist_ok=True)
for stale in ("db.sqlite", "db.sqlite.lock"):
(d / stale).unlink(missing_ok=True)
write(d / "pipeline.yaml", B_PIPELINE.format(
arm=arm, arm_note=ARM_NOTES[arm], arm_prompt=PL / "arms" / f"translator-{arm}.md"))
write(d / "book.yaml", B_BOOK.format(arm=arm, dir=d, slice=SLICE_B))
write(d / "pairs" / "zh-ru.yaml", PAIR_YAML.format(prompts_root=BACKEND / "prompts"))
print(f"B-{arm}: собран, промпт={PL / 'arms' / f'translator-{arm}.md'}")
def main() -> int:
if not SLICE_B.exists():
print(f"нет среза {SLICE_B}", file=sys.stderr)
return 2
build_a()
build_b()
# Байт-парити ru-дерева промптов с боевым (арм A обязан быть РЕПЛИКОЙ, а не пересказом).
r = subprocess.run(["diff", "-r", str(BACKEND / "prompts"), str(PL / "prompts-ru")],
capture_output=True, text=True)
print("\nprompts-ru vs backend/prompts:", "БАЙТ-ИДЕНТИЧНЫ" if r.returncode == 0 else r.stdout[:500])
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,278 @@
#!/usr/bin/env python3
"""Fidelity-судья хвоста P4 (петля D21 п.6) по нормам рига D30.10/D39.7.
Вопрос УЗКИЙ и он не «какой перевод лучше»: **стоит ли митигация (постинструкция / few-shot)
чего-нибудь по ВЕРНОСТИ**. Поэтому ось одна верность; гладкость и красота из промпта судьи
вычеркнуты явно, иначе судья начнёт ранжировать стиль и вернёт ответ на другой вопрос.
Нормы рига, соблюдённые здесь:
· судья ЧУЖОГО семейства (grok-4.3 против переводов deepseek) author reviewer (D13.3);
· reasoning ON (дефолт grok = low; явный `none` НЕ шлём именно он делал судью слепым, D30.1);
· ПОЛНЫЕ окна: целиком исходник и целиком оба перевода, без обрезки (урок exp15 Q1b: обрезанное
окно родило вердикт-артефакт, который пришлось отзывать);
· ОБА порядка (A/B и B/A) позиционный шум измеряется, а не предполагается;
· FLOOR-проход на ИДЕНТИЧНЫХ текстах пол судейского шума; контраст читается только над ним;
· пер-голосовой персист в JSONL + резюм; жёсткий внутренний кап расхода;
· span-цитаты с ОБЕИХ сторон: дефект без цитаты вердиктом не считается.
Выбор единиц ПРЕ-РЕГИСТРИРОВАННЫЙ и по порядку (chapter, chunk_idx) среди тех, где ВСЕ ТРИ арма
дали чистый выход. Никакого отбора по исходу.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import time
from pathlib import Path
from dotenv import load_dotenv
from openai import OpenAI
REPO = Path("/home/ubuntu/projects/textmachine")
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
ARMS_JUDGED = ["a0", "a1", "a2"]
# Контраст a0↔a2 СНЯТ с судьи осознанно: few-shot отвергнут ДЕТЕРМИНИРОВАННО (эхо 1/12 +
# выход по-английски 1/12 + reasoning ×1.9), и решение по нему судья не двигает. Бюджет
# судьи идёт туда, где вопрос ещё открыт, — постинструкция.
CONTRASTS = [("a0-a1", "a1")]
OUT = PL / "judge"
OUT.mkdir(exist_ok=True)
load_dotenv(REPO / "eval" / ".env")
# grok-4.3: $1.25/$2.50 за 1M (models.yaml, prices_checked 2026-07-10); reasoning у xAI АДДИТИВЕН
# к completion, поэтому в цену выхода он входит и считается явно.
JUDGE = dict(model="grok-4.3", base="https://api.x.ai/v1", key_env="XAI_API_KEY",
price_in=1.25, price_out=2.50, max_tokens=8000, temperature=0.0)
SYS = (
"Ты — контролёр ВЕРНОСТИ художественного перевода с китайского на русский. Тебе дают КИТАЙСКИЙ "
"исходник и ДВА русских перевода одного и того же фрагмента: A и B.\n"
"Оценивай ТОЛЬКО верность: передан ли смысл исходника без ПРОПУСКОВ, ИСКАЖЕНИЙ, ОТСЕБЯТИНЫ и без "
"непереведённых кусков. Гладкость, красота, стиль, выбор синонимов и разбивка на абзацы НЕ "
"оцениваются вовсе — по этим осям любые различия игнорируй.\n"
"Дефект засчитывается ТОЛЬКО с дословными цитатами: что в исходнике и что (или ничего) в переводе.\n"
"Если по верности переводы равны — это нормальный и частый ответ: пиши \"tie\".\n"
"Ответь СТРОГИМ JSON одной строкой, без текста вокруг и без markdown:\n"
'{"winner":"A|B|tie","margin":"clear|slight",'
'"defects_A":[{"kind":"omission|distortion|addition|untranslated","zh":"<цитата исходника ≤12 иероглифов>","ru":"<цитата перевода ≤15 слов или пусто>"}],'
'"defects_B":[...],"reason":"<=25 слов"}'
)
def user(src: str, ta: str, tb: str) -> str:
return (f"=== КИТАЙСКИЙ ИСХОДНИК ===\n{src}\n\n=== ПЕРЕВОД A ===\n{ta}\n\n"
f"=== ПЕРЕВОД B ===\n{tb}\n\nВерни JSON-вердикт по ВЕРНОСТИ.")
class Ledger:
def __init__(self, path: Path, cap: float):
self.path, self.cap, self.total = path, cap, 0.0
if path.exists():
for ln in path.read_text().splitlines():
try:
self.total += json.loads(ln).get("cost_usd", 0.0)
except Exception:
pass
def add(self, rec: dict) -> None:
self.total += rec.get("cost_usd", 0.0)
with open(self.path, "a") as f:
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
_client = None
def client():
global _client
if _client is None:
key = os.environ.get(JUDGE["key_env"])
if not key:
sys.exit(f"нет ключа {JUDGE['key_env']}")
_client = OpenAI(api_key=key, base_url=JUDGE["base"], timeout=300)
return _client
def call(led: Ledger, sys_p: str, usr: str, tag: str) -> tuple[str, dict]:
if led.total >= led.cap:
sys.exit(f"СТОП: кап судьи ${led.cap} достигнут (потрачено ${led.total:.4f})")
for attempt, back in enumerate([5, 15, 40, None]):
try:
r = client().chat.completions.create(
model=JUDGE["model"],
messages=[{"role": "system", "content": sys_p}, {"role": "user", "content": usr}],
temperature=JUDGE["temperature"], max_tokens=JUDGE["max_tokens"])
ch = r.choices[0]
txt = (ch.message.content or "").strip()
u = r.usage
pt = getattr(u, "prompt_tokens", 0) or 0
ct = getattr(u, "completion_tokens", 0) or 0
det = getattr(u, "completion_tokens_details", None)
rt = (getattr(det, "reasoning_tokens", 0) if det else 0) or 0
# xAI: reasoning АДДИТИВЕН к completion. Если провайдер уже включил его в completion,
# двойного счёта не будет — берём max, а не сумму, и это осознанно консервативно вверх
# только на входе в кап, но в отчёт идёт и то и другое.
out_billed = ct if rt and rt <= ct else ct + rt
cost = pt * JUDGE["price_in"] / 1e6 + out_billed * JUDGE["price_out"] / 1e6
rec = dict(tag=tag, judge=JUDGE["model"], finish=ch.finish_reason, prompt_tok=pt,
completion_tok=ct, reasoning_tok=rt, out_billed=out_billed,
cost_usd=cost, empty=(not txt))
led.add(rec)
if ch.finish_reason != "stop":
print(f"{tag}: finish={ch.finish_reason!r} (аномалия — в отчёт)")
return txt, rec
except Exception as e:
if back is None:
led.add(dict(tag=tag, error=str(e)[:300], cost_usd=0.0))
print(f" [FAIL {tag}] {str(e)[:180]}")
return "", {"error": str(e)[:300]}
print(f" [retry {tag}] {str(e)[:90]} — сон {back}с")
time.sleep(back)
return "", {}
def parse(txt: str):
if not txt:
return None
s = txt.strip().strip("`")
i, j = s.find("{"), s.rfind("}")
if i < 0 or j < 0:
return None
try:
return json.loads(s[i:j + 1])
except Exception:
return None
def load_units():
"""Единицы, где ВСЕ судимые армы дали чистый выход (не эхо, не пусто). Порядок — по имени
фрагмента, то есть по тексту книги. Никакого отбора по исходу."""
raw = PL / "armsraw"
src = {f["id"]: f["text"] for f in json.loads((raw / "fragments.json").read_text(encoding="utf-8"))}
texts, clean = {}, None
for arm in ARMS_JUDGED:
ok = set()
for f in sorted(raw.glob(f"{arm}-f*.json")):
j = json.loads(f.read_text(encoding="utf-8"))
if "error" in j:
continue
k = j["frag"]
j["src_text"] = src[k]
texts[(arm, k)] = j
# исключаем не только эхо, но и выход не в целевом письме: это уже пойман
# ДЕТЕРМИНИРОВАННЫМ прибором, судья тут ответил бы на другой вопрос
import unicodedata as _u
cyr = sum(1 for ch in j["content"] if _u.name(ch, "").startswith("CYRILLIC"))
lat = sum(1 for ch in j["content"] if _u.name(ch, "").startswith("LATIN"))
off_lang = (cyr + lat) > 0 and cyr / (cyr + lat) < 0.5
if not j["echo"] and not off_lang and j["content_chars"] > 0:
ok.add(k)
clean = ok if clean is None else (clean & ok)
return sorted(clean or []), texts
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--cap", type=float, required=True, help="жёсткий кап расхода судьи, $")
ap.add_argument("--units", type=int, default=0, help="сколько единиц судить (0 = план)")
ap.add_argument("--probe", action="store_true", help="одна живая калибровка цены")
ap.add_argument("--floor", action="store_true", help="floor-проход на идентичных текстах")
ap.add_argument("--aggregate", action="store_true", help="$0 пересчёт из JSONL")
a = ap.parse_args()
led = Ledger(OUT / "ledger.jsonl", a.cap)
votes_path = OUT / "votes.jsonl"
seen = {}
if votes_path.exists():
for ln in votes_path.read_text().splitlines():
try:
v = json.loads(ln)
seen[v["vote_id"]] = v
except Exception:
pass
units, texts = load_units()
print(f"чистых единиц во ВСЕХ трёх армах: {len(units)}{units}")
if a.aggregate:
aggregate(seen)
return 0
def vote(vid, kind, unit, contrast, order, src, ta, tb, label_a, label_b):
if vid in seen:
return
txt, rec = call(led, SYS, user(src, ta, tb), vid)
v = parse(txt) or {}
row = dict(vote_id=vid, kind=kind, unit=str(unit), contrast=contrast,
order=order, label_A=label_a, label_B=label_b, winner=v.get("winner"),
margin=v.get("margin"), defects_A=v.get("defects_A"), defects_B=v.get("defects_B"),
reason=v.get("reason"), finish=rec.get("finish"), raw=txt[:1200],
cost_usd=rec.get("cost_usd"))
with open(votes_path, "a") as f:
f.write(json.dumps(row, ensure_ascii=False) + "\n")
seen[vid] = row
print(f" {vid}: winner={v.get('winner')} margin={v.get('margin')} "
f"defA={len(v.get('defects_A') or [])} defB={len(v.get('defects_B') or [])} "
f"${led.total:.4f}")
if a.probe:
u = units[0]
c0 = texts[("a0", u)]
vote(f"probe::{u}", "probe", u, "a0-a0", "AB",
c0["src_text"], c0["content"], c0["content"], "a0", "a0")
print(f"\nКАЛИБРОВКА: ${led.total:.5f} за вызов")
return 0
if a.floor:
for u in units[:1]:
c0 = texts[("a0", u)]
for order in ("AB", "BA"):
vote(f"floor::{u}::{order}", "floor", u, "a0-a0", order,
c0["src_text"], c0["content"], c0["content"], "a0", "a0")
return 0
n = a.units
if n <= 0:
print("план: --units N (N единиц × 2 контраста × 2 порядка = 4N вызовов)")
return 0
for u in units[:n]:
for contrast, arm in CONTRASTS:
base, mit = texts[("a0", u)], texts[(arm, u)]
for order in ("AB", "BA"):
ta, tb, la, lb = ((base["content"], mit["content"], "a0", arm) if order == "AB"
else (mit["content"], base["content"], arm, "a0"))
vote(f"{contrast}::{u}::{order}", "contrast", u, contrast, order,
base["src_text"], ta, tb, la, lb)
aggregate(seen)
print(f"\nПОТРАЧЕНО СУДЬЁЙ: ${led.total:.5f} из капа ${a.cap}")
return 0
def aggregate(seen: dict) -> None:
from collections import defaultdict
by = defaultdict(list)
for v in seen.values():
by[(v["kind"], v.get("contrast"))].append(v)
print(f"\n{'вид':<10} {'контраст':<8} {'голосов':>8} {'база':>6} {'митиг.':>7} {'ничья':>7} {'деф.база':>9} {'деф.митиг':>10}")
for (kind, contrast), vs in sorted(by.items()):
base = mit = tie = 0
db = dm = 0
for v in vs:
w = v.get("winner")
la, lb = v.get("label_A"), v.get("label_B")
win = la if w == "A" else lb if w == "B" else None
if win is None:
tie += 1
elif win == "a0":
base += 1
else:
mit += 1
db += len(v.get("defects_A") or []) if la == "a0" else len(v.get("defects_B") or [])
dm += len(v.get("defects_B") or []) if la == "a0" else len(v.get("defects_A") or [])
print(f"{kind:<10} {str(contrast):<8} {len(vs):>8} {base:>6} {mit:>7} {tie:>7} {db:>9} {dm:>10}")
if __name__ == "__main__":
raise SystemExit(main())

277
eval/promptlang/judge_p4.py Normal file
View file

@ -0,0 +1,277 @@
#!/usr/bin/env python3
"""Fidelity-судья на ГОТОВЫХ парах база↔митигация P4 — деливерабл (1) промта сессии, исполненный там,
куда промт и указывал: `~/books/gu-zhenren/research15-probes/`.
Генерация этих пар оплачена 09.07. Здесь покупаются ТОЛЬКО судейские голоса.
Нормы рига D30.10/D39.7, соблюдённые:
· судья ЧУЖОГО семейства к автору перевода (D13.3) маршрутизация по семейству ПАРЫ:
пары grok-none судит `deepseek-v4-pro` (кросс-семейно, дешевле grok в 3 раза)
пары deepseek судит `grok-4.3` (кросс-семейно)
`deepseek-v4-pro` вендором 31.07 НЕ обновлялся («The DeepSeek-V4-Pro API unchanged»),
поэтому как прибор он на прежнем поведении в отличие от flash.
· reasoning ON у обоих (dspro thinking по умолчанию; grok дефолт low, `none` НЕ шлём);
· ПОЛНЫЕ окна без обрезки (урок exp15 Q1b);
· ОБА порядка на каждой паре;
· FLOOR-проход на ИДЕНТИЧНЫХ текстах отдельно для КАЖДОГО судьи;
· пер-голосовой персист + резюм; жёсткий кап расхода; ось ТОЛЬКО верность.
`--plan` печатает смету по ФАКТИЧЕСКИМ размерам сырья и не делает ни одного вызова.
"""
from __future__ import annotations
import argparse
import collections
import json
import os
import re
import sys
import time
import unicodedata
from pathlib import Path
from dotenv import load_dotenv
from openai import OpenAI
REPO = Path("/home/ubuntu/projects/textmachine")
RAW = Path.home() / "books" / "gu-zhenren" / "research15-probes" / "probes" / "raw"
OUT = Path.home() / "books" / "gu-zhenren" / "promptlang" / "judge_p4"
OUT.mkdir(parents=True, exist_ok=True)
load_dotenv(REPO / "eval" / ".env")
JUDGES = {
# цены — models.yaml, prices_checked 2026-07-10
"dspro": dict(model="deepseek-v4-pro", base="https://api.deepseek.com/v1", key_env="DEEPSEEK_API_KEY",
price_in=0.435, price_out=0.87, max_tokens=16000, reasoning="subset", temperature=0.0),
"grok": dict(model="grok-4.3", base="https://api.x.ai/v1", key_env="XAI_API_KEY",
price_in=1.25, price_out=2.50, max_tokens=8000, reasoning="additive", temperature=0.0),
}
JUDGE_FOR = {"grok": "dspro", "deepseek": "grok"} # семейство ПАРЫ → судья ЧУЖОГО семейства
ARM_NAME = {"G0": "база", "G1": "инстр-после", "G2": "few-shot", "G4": "комбо",
"D0": "база", "D1": "инстр-после", "D2": "few-shot", "DP": "префилл"}
SYS = (
"Ты — контролёр ВЕРНОСТИ художественного перевода с китайского на русский. Тебе дают КИТАЙСКИЙ "
"исходник и ДВА русских перевода одного и того же фрагмента: A и B.\n"
"Оценивай ТОЛЬКО верность: передан ли смысл исходника без ПРОПУСКОВ, ИСКАЖЕНИЙ, ОТСЕБЯТИНЫ и без "
"непереведённых кусков. Гладкость, красота, стиль, выбор синонимов, разбивка на абзацы и длина НЕ "
"оцениваются вовсе — по этим осям любые различия игнорируй.\n"
"Отдельно отмечай ДОБАВЛЕНИЯ, которых нет в исходнике (сноски переводчика, пояснения в скобках, "
"заголовки) — это дефект верности класса addition.\n"
"Дефект засчитывается ТОЛЬКО с дословными цитатами: что в исходнике и что (или ничего) в переводе.\n"
"Если по верности переводы равны — это нормальный и частый ответ: пиши \"tie\".\n"
"Ответь СТРОГИМ JSON одной строкой, без текста вокруг и без markdown:\n"
'{"winner":"A|B|tie","margin":"clear|slight",'
'"defects_A":[{"kind":"omission|distortion|addition|untranslated","zh":"<цитата исходника ≤12 иероглифов>","ru":"<цитата перевода ≤15 слов или пусто>"}],'
'"defects_B":[...],"reason":"<=25 слов"}'
)
def cjk_share(s: str) -> float:
if not s:
return 1.0
return sum(1 for c in s if "CJK UNIFIED" in (unicodedata.name(c, "") or "")) / len(s)
def load_pairs() -> list[dict]:
"""Пары база↔митигация: обе стороны — реальный перевод (не эхо, не пусто). Порядок детерминирован."""
recs = {}
for f in sorted(RAW.glob("echo-*.json")):
d = json.load(open(f))
t = d.get("text") or ""
u = d.get("user") or ""
src = u.split("Переведи следующий фрагмент:\n\n", 1)[-1].split("\n\nНапоминание:")[0]
recs[d["tag"]] = dict(text=t, src=src, echo=cjk_share(t) > 0.15)
groups: dict[tuple[str, str], dict] = collections.defaultdict(dict)
for tag, r in recs.items():
m = re.match(r"echo-grok-(gu-\d+)-(G\d)$", tag)
if m:
groups[("grok", m.group(1))][m.group(2)] = r
continue
m = re.match(r"echo-ds-gu008-(D\w+)-s(\d)$", tag)
if m:
groups[("deepseek", "gu008-s" + m.group(2))][m.group(1)] = r
pairs = []
for (fam, frag), arms in sorted(groups.items()):
bk = "G0" if fam == "grok" else "D0"
base = arms.get(bk)
if not base or base["echo"] or not base["text"]:
continue
for a in ("G1", "G2", "G4", "D1", "D2", "DP"):
m = arms.get(a)
if not m or m["echo"] or not m["text"]:
continue
pairs.append(dict(fam=fam, frag=frag, arm=a, arm_name=ARM_NAME[a],
src=base["src"], base=base["text"], mit=m["text"]))
return pairs
def user_msg(src: str, ta: str, tb: str) -> str:
return (f"=== КИТАЙСКИЙ ИСХОДНИК ===\n{src}\n\n=== ПЕРЕВОД A ===\n{ta}\n\n"
f"=== ПЕРЕВОД B ===\n{tb}\n\nВерни JSON-вердикт по ВЕРНОСТИ.")
def est_tokens(s: str) -> int:
"""Грубая оценка ДЛЯ СМЕТЫ: ханьцы ≈1 ток/симв, кириллица ≈0.4 ток/симв (калибровка exp01)."""
han = sum(1 for c in s if "CJK UNIFIED" in (unicodedata.name(c, "") or ""))
return int(han + (len(s) - han) * 0.4)
class Ledger:
def __init__(self, path: Path, cap: float):
self.path, self.cap, self.total = path, cap, 0.0
if path.exists():
for ln in path.read_text().splitlines():
try:
self.total += json.loads(ln).get("cost_usd", 0.0)
except Exception:
pass
def add(self, rec: dict) -> None:
self.total += rec.get("cost_usd", 0.0)
with open(self.path, "a") as f:
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
_clients: dict[str, OpenAI] = {}
def client(jk: str) -> OpenAI:
if jk not in _clients:
cfg = JUDGES[jk]
key = os.environ.get(cfg["key_env"])
if not key:
sys.exit(f"нет ключа {cfg['key_env']}")
_clients[jk] = OpenAI(api_key=key, base_url=cfg["base"], timeout=600)
return _clients[jk]
def call(led: Ledger, jk: str, usr: str, tag: str) -> tuple[str, dict]:
cfg = JUDGES[jk]
if led.total >= led.cap:
sys.exit(f"СТОП: кап ${led.cap} достигнут (потрачено ${led.total:.4f}) — пинг владельцу")
for back in (5, 20, 60, None):
try:
r = client(jk).chat.completions.create(
model=cfg["model"], temperature=cfg["temperature"], max_tokens=cfg["max_tokens"],
messages=[{"role": "system", "content": SYS}, {"role": "user", "content": usr}])
ch = r.choices[0]
txt = (ch.message.content or "").strip()
u = r.usage
pt, ct = u.prompt_tokens or 0, u.completion_tokens or 0
det = getattr(u, "completion_tokens_details", None)
rt = (getattr(det, "reasoning_tokens", 0) if det else 0) or 0
billed = ct + rt if cfg["reasoning"] == "additive" and rt > ct else ct
cost = pt * cfg["price_in"] / 1e6 + billed * cfg["price_out"] / 1e6
rec = dict(tag=tag, judge=cfg["model"], finish=ch.finish_reason, prompt_tok=pt,
completion_tok=ct, reasoning_tok=rt, cost_usd=cost, empty=not txt)
led.add(rec)
if ch.finish_reason != "stop":
print(f"{tag}: finish={ch.finish_reason!r}")
return txt, rec
except Exception as e:
if back is None:
led.add(dict(tag=tag, error=str(e)[:300], cost_usd=0.0))
return "", {"error": str(e)[:300]}
print(f" [retry {tag}] {str(e)[:90]} — сон {back}с")
time.sleep(back)
return "", {}
def parse(txt: str):
if not txt:
return None
s = txt.strip().strip("`")
i, j = s.find("{"), s.rfind("}")
try:
return json.loads(s[i:j + 1]) if i >= 0 and j > i else None
except Exception:
return None
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--plan", action="store_true", help="$0 смета по фактическим размерам сырья")
ap.add_argument("--cap", type=float, help="жёсткий кап расхода, $ (обязателен для прогона)")
ap.add_argument("--only", default="", help="ограничить семейством: grok|deepseek")
a = ap.parse_args()
pairs = load_pairs()
if a.only:
pairs = [p for p in pairs if p["fam"] == a.only]
by_fam = collections.Counter(p["fam"] for p in pairs)
print(f"ПАР база↔митигация: {len(pairs)} · по семействам {dict(by_fam)}")
for p in pairs[:3]:
print(f" пример: {p['fam']}/{p['frag']}/{p['arm_name']} — исходник {len(p['src'])} симв, "
f"база {len(p['base'])}, митигация {len(p['mit'])}")
if a.plan:
total = 0.0
print(f"\n{'семейство':<10}{'судья':<20}{'пар':>5}{'голосов':>9}{'ср.вход ток':>13}{'смета $':>10}")
for fam in sorted(by_fam):
jk = JUDGE_FOR[fam]
cfg = JUDGES[jk]
ps = [p for p in pairs if p["fam"] == fam]
ins = [est_tokens(user_msg(p["src"], p["base"], p["mit"])) + est_tokens(SYS) for p in ps]
avg_in = sum(ins) / len(ins)
out_est = 1600 # вердикт ~400 ток + размышление ~1200 (наблюдение по голосам 31.07)
votes = len(ps) * 2 + 2 # оба порядка + floor-проход на идентичных текстах
cost = votes * (avg_in * cfg["price_in"] / 1e6 + out_est * cfg["price_out"] / 1e6)
total += cost
print(f"{fam:<10}{cfg['model']:<20}{len(ps):>5}{votes:>9}{avg_in:>13.0f}{cost:>10.4f}")
print(f"{'ИТОГО':<10}{'':<20}{len(pairs):>5}{len(pairs)*2+4:>9}{'':>13}{total:>10.4f}")
print("\nдопущения сметы названы: вход — оценка exp01 (хань≈1 ток/симв, кириллица≈0.4),")
print("выход 1600 ток/голос — по 7 фактическим голосам 31.07. Реальный расход пишется в леджер.")
print("ВЫЗОВОВ НЕ СДЕЛАНО.")
return 0
if a.cap is None:
print("для прогона нужен --cap (жёсткий потолок расхода)", file=sys.stderr)
return 2
led = Ledger(OUT / "ledger.jsonl", a.cap)
votes_path = OUT / "votes.jsonl"
seen = set()
if votes_path.exists():
for ln in votes_path.read_text().splitlines():
try:
seen.add(json.loads(ln)["vote_id"])
except Exception:
pass
def vote(vid, kind, p, order, ta, tb, la, lb):
if vid in seen:
return
jk = JUDGE_FOR[p["fam"]]
txt, rec = call(led, jk, user_msg(p["src"], ta, tb), vid)
v = parse(txt) or {}
row = dict(vote_id=vid, kind=kind, judge=JUDGES[jk]["model"], fam=p["fam"], frag=p["frag"],
arm=p["arm"], arm_name=p["arm_name"], order=order, label_A=la, label_B=lb,
winner=v.get("winner"), margin=v.get("margin"), defects_A=v.get("defects_A"),
defects_B=v.get("defects_B"), reason=v.get("reason"), finish=rec.get("finish"),
raw=txt[:1200], cost_usd=rec.get("cost_usd"))
with open(votes_path, "a") as f:
f.write(json.dumps(row, ensure_ascii=False) + "\n")
seen.add(vid)
print(f" {vid}: {v.get('winner')} defБаза={len(v.get('defects_A') or []) if la=='база' else len(v.get('defects_B') or [])} "
f"defМитиг={len(v.get('defects_B') or []) if la=='база' else len(v.get('defects_A') or [])} ${led.total:.4f}")
# FLOOR: идентичные тексты, по одному на семейство → пол шума КАЖДОГО судьи
for fam in sorted(by_fam):
p = next(x for x in pairs if x["fam"] == fam)
for order in ("AB", "BA"):
vote(f"floor::{fam}::{order}", "floor", p, order, p["base"], p["base"], "база", "база")
for p in pairs:
for order in ("AB", "BA"):
ta, tb, la, lb = ((p["base"], p["mit"], "база", p["arm_name"]) if order == "AB"
else (p["mit"], p["base"], p["arm_name"], "база"))
vote(f"{p['fam']}::{p['frag']}::{p['arm']}::{order}", "contrast", p, order, ta, tb, la, lb)
print(f"\nПОТРАЧЕНО: ${led.total:.5f} из капа ${a.cap}; голоса — {votes_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

115
eval/promptlang/p4_pairs.py Normal file
View file

@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""$0 детерминированная батарея верности на ГОТОВЫХ парах база↔митигация P4 (09.07).
Это тот источник, на который промт сессии указывал прямо: `~/books/gu-zhenren/research15-probes/`.
Генерация за них уже оплачена 09.07 здесь не тратится ни цента.
Судья отвечает на «исказила ли митигация СМЫСЛ». Половина этого вопроса про ПРОПУСКИ и обрывы
решается детерминированно и бесплатно: митигация, которая режет текст, видна по длине и по числу
предложений относительно ОДНОГО И ТОГО ЖЕ исходника. Оставшаяся половина (искажение при сохранённом
объёме) без судьи не берётся, и это здесь честно помечено, а не замазано.
"""
from __future__ import annotations
import collections
import json
import re
import statistics
import sys
import unicodedata
from pathlib import Path
REPO = Path("/home/ubuntu/projects/textmachine")
sys.path.insert(0, str(REPO / "eval"))
from refusal_bench import split_sentences
RAW = Path.home() / "books" / "gu-zhenren" / "research15-probes" / "probes" / "raw"
ARM_NAME = {"G0": "база", "G1": "инстр.ПОСЛЕ", "G2": "few-shot", "G3": "яз.констрейнт", "G4": "комбо",
"D0": "база", "D1": "инстр.ПОСЛЕ", "D2": "few-shot", "DP": "префилл"}
def cjk_share(s: str) -> float:
if not s:
return 1.0
return sum(1 for c in s if "CJK UNIFIED" in (unicodedata.name(c, "") or "")) / len(s)
def load():
out = {}
for f in sorted(RAW.glob("echo-*.json")):
d = json.load(open(f))
t = d.get("text") or ""
# исходник фрагмента лежит в user-сообщении между шапкой и (для арма G1) постинструкцией
u = d.get("user") or ""
src = u.split("Переведи следующий фрагмент:\n\n", 1)[-1]
src = src.split("\n\nНапоминание:")[0]
out[d["tag"]] = dict(tag=d["tag"], text=t, src=src, cjk=cjk_share(t), echo=cjk_share(t) > 0.15)
return out
def metrics(src: str, out: str) -> dict:
src_ns = len(re.sub(r"\s", "", src))
out_ns = len(re.sub(r"\s", "", out))
return dict(chars=len(out), len_ratio=round(out_ns / src_ns, 3) if src_ns else None,
sent_src=len(split_sentences(src)), sent_out=len(split_sentences(out)),
cjk=round(cjk_share(out), 4))
def main() -> int:
d = load()
groups = collections.defaultdict(dict)
for tag, r in d.items():
m = re.match(r"echo-grok-(gu-\d+)-(G\d)$", tag)
if m:
groups[("grok", m.group(1))][m.group(2)] = r
continue
m = re.match(r"echo-ds-gu008-(D\w+)-s(\d)$", tag)
if m:
groups[("deepseek", "gu-008-s" + m.group(2))][m.group(1)] = r
print("=== ПАРЫ база↔митигация из P4 (генерация оплачена 09.07; здесь $0) ===\n")
print(f"{'семейство':<9}{'фрагмент':<14}{'арм':<14}{'len_ratio':>10}{'предл. вых/исх':>16}"
f"{'симв':>7}{'Δlen_ratio к базе':>19}")
rows = []
for (fam, frag), arms in sorted(groups.items()):
base_key = "G0" if fam == "grok" else "D0"
base = arms.get(base_key)
if not base or base["echo"] or not base["text"]:
continue
bm = metrics(base["src"], base["text"])
print(f"{fam:<9}{frag:<14}{ARM_NAME[base_key]:<14}{bm['len_ratio']:>10}"
f"{str(bm['sent_out']) + '/' + str(bm['sent_src']):>16}{bm['chars']:>7}{'':>19}")
for a in ("G1", "G2", "G4", "D1", "D2", "DP"):
m = arms.get(a)
if not m or m["echo"] or not m["text"]:
continue
mm = metrics(base["src"], m["text"])
delta = mm["len_ratio"] - bm["len_ratio"]
rows.append(dict(fam=fam, frag=frag, arm=a, d_len=delta,
d_sent=mm["sent_out"] - bm["sent_out"],
base_lr=bm["len_ratio"], mit_lr=mm["len_ratio"]))
print(f"{'':<9}{'':<14}{ARM_NAME[a]:<14}{mm['len_ratio']:>10}"
f"{str(mm['sent_out']) + '/' + str(mm['sent_src']):>16}{mm['chars']:>7}{delta:>+19.3f}")
print()
print(f"ВСЕГО ПАР: {len(rows)}\n")
print("=== СВОДКА ПО МИТИГАЦИИ: режет ли она текст относительно базы на ТОМ ЖЕ исходнике ===")
print(f"{'митигация':<16}{'пар':>5}{'медиана Δlen_ratio':>21}{'пар с Δ<-0.15':>15}"
f"{'пар с Δ>+0.15':>15}{'медиана Δпредл.':>17}")
for a in ("G1", "G2", "G4", "D1", "D2", "DP"):
rs = [r for r in rows if r["arm"] == a]
if not rs:
continue
dl = [r["d_len"] for r in rs]
ds = [r["d_sent"] for r in rs]
print(f"{ARM_NAME[a]:<16}{len(rs):>5}{statistics.median(dl):>+21.3f}"
f"{sum(1 for x in dl if x < -0.15):>15}{sum(1 for x in dl if x > 0.15):>15}"
f"{statistics.median(ds):>+17.1f}")
print("\nЧТЕНИЕ: Δlen_ratio < 0.15 = митигированный перевод заметно КОРОЧЕ базы на том же")
print("исходнике — подпись пропуска. Δ > +0.15 — многословнее (отсебятина или развёрнутость).")
print("⚠ Ось «искажение при сохранённом объёме» этим прибором НЕ берётся — только судьёй.")
return 0
if __name__ == "__main__":
raise SystemExit(main())

171
eval/promptlang/run_arms.py Normal file
View file

@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""Армы задачи B (анти-эхо, D21 п.6) на СЫРОМ проводе — в той конфигурации, в которой боевой канал
31.07 вообще отвечает.
Почему не через движок. После вендор-обновления `deepseek-v4-flash` `DeepSeek-V4-Flash-0731`
боевая форма (max_tokens 8000, thinking по умолчанию) отдаёт ПУСТОЙ `content` (§2 отчёта). Единственная
ручка, которая её чинит, `reasoning_effort:"low"`, а её движок для deepseek слать НЕ МОЖЕТ и не
должен: `config.echoMineViolation` (`models.go:276-281`) запрещает эхо-склонному провайдеру любой
reasoning-контроль, кроме `none|mandatory`. Гейт прав проба показала, что `low` эхо-мину
пере-вооружает. Значит замер идёт мимо движка, как шли пробы P4, и это оговаривается вслух.
Инварианты замера: фрагменты и параметры БАЙТ-ОДИНАКОВЫ во всех армах, различается ровно промпт.
Рендер реплика движкового (`wire_probe.render`), сверенная по `prompt_tokens` с движковым вызовом
на общем чанке (1679 = 1679).
"""
from __future__ import annotations
import argparse
import json
import os
import re
import sys
import time
import unicodedata
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from dotenv import load_dotenv
from openai import OpenAI
REPO = Path("/home/ubuntu/projects/textmachine")
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
sys.path.insert(0, str(REPO / "eval" / "promptlang"))
sys.path.insert(0, str(REPO / "eval"))
from wire_probe import render # тот же рендер, что уже сверен с движком по prompt_tokens
from refusal_bench import split_sentences
load_dotenv(REPO / "eval" / ".env")
PRICE_IN, PRICE_CACHED, PRICE_OUT = 0.14, 0.0028, 0.28
def is_cjk(ch: str) -> bool:
n = unicodedata.name(ch, "")
return ("CJK UNIFIED IDEOGRAPH" in n or "CJK COMPATIBILITY IDEOGRAPH" in n
or n.startswith("HIRAGANA ") or n.startswith("KATAKANA "))
def cjk_share(s: str) -> float:
return sum(1 for c in s if is_cjk(c)) / len(s) if s else 0.0
def fragments(path: Path, n: int, target: int) -> list[dict]:
"""Детерминированная нарезка свежего среза на n фрагментов по границам абзацев.
Одна и та же для всех армов по построению арму нечего выбирать."""
text = path.read_text(encoding="gb18030")
paras = [p for p in text.split("\n") if p.strip()]
out, cur = [], []
for p in paras:
cur.append(p)
if sum(len(x) for x in cur) >= target:
out.append("\n".join(cur))
cur = []
if len(out) == n:
break
if cur and len(out) < n:
out.append("\n".join(cur))
return [{"id": f"f{i:02d}", "text": t} for i, t in enumerate(out[:n])]
def call(client, arm: str, frag: dict, cap: int, temp: float, effort: str, raw_dir: Path) -> dict:
tag = f"{arm}-{frag['id']}"
fp = raw_dir / f"{tag}.json"
if fp.exists():
return json.loads(fp.read_text(encoding="utf-8"))
system, user = render(arm, frag["text"])
t0 = time.time()
kw = dict(model="deepseek-v4-flash", max_tokens=cap, temperature=temp,
messages=[{"role": "system", "content": system}, {"role": "user", "content": user}])
if effort:
kw["extra_body"] = {"reasoning_effort": effort}
try:
r = client.chat.completions.create(**kw)
except Exception as e: # аномалия печатается и персистится, диагноз — по вендор-доке
rec = {"tag": tag, "arm": arm, "frag": frag["id"], "error": str(e)[:400], "cost_usd": 0.0}
fp.write_text(json.dumps(rec, ensure_ascii=False, indent=1), encoding="utf-8")
return rec
ch = r.choices[0]
content = ch.message.content or ""
reasoning = getattr(ch.message, "reasoning_content", None) or ""
u = r.usage
pt, ct = u.prompt_tokens or 0, u.completion_tokens or 0
det = getattr(u, "completion_tokens_details", None)
rt = (getattr(det, "reasoning_tokens", 0) if det else 0) or 0
cached = getattr(getattr(u, "prompt_tokens_details", None), "cached_tokens", 0) or 0
cost = (pt - cached) / 1e6 * PRICE_IN + cached / 1e6 * PRICE_CACHED + ct / 1e6 * PRICE_OUT
src_ns = len(re.sub(r"\s", "", frag["text"]))
out_ns = len(re.sub(r"\s", "", content))
rec = {"tag": tag, "arm": arm, "frag": frag["id"], "model_returned": r.model,
"ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"max_tokens": cap, "temperature": temp, "reasoning_effort": effort or None,
"finish": ch.finish_reason, "prompt_tokens": pt, "cached_tokens": cached,
"completion_tokens": ct, "reasoning_tokens": rt, "cost_usd": round(cost, 6),
"latency_s": round(time.time() - t0, 1),
"cjk_share": round(cjk_share(content), 4), "echo": cjk_share(content) > 0.15,
"src_chars": len(frag["text"]), "content_chars": len(content),
"len_ratio": round(out_ns / src_ns, 3) if src_ns else None,
"sent_src": len(split_sentences(frag["text"])), "sent_out": len(split_sentences(content)),
"system": system, "user": user, "content": content, "reasoning_content": reasoning}
fp.write_text(json.dumps(rec, ensure_ascii=False, indent=1), encoding="utf-8")
print(f"[{tag}] {ch.finish_reason} out={ct} rt={rt} cjk={rec['cjk_share']:.2f} "
f"lr={rec['len_ratio']} ${cost:.6f} {rec['latency_s']}с", flush=True)
return rec
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--arms", default="a0,a1,a2")
ap.add_argument("--n", type=int, default=12)
ap.add_argument("--target", type=int, default=1400)
ap.add_argument("--cap", type=int, default=8000)
ap.add_argument("--temp", type=float, default=0.3)
ap.add_argument("--effort", default="low")
ap.add_argument("--cost-cap", type=float, required=True)
ap.add_argument("--workers", type=int, default=3)
ap.add_argument("--raw", type=Path, default=PL / "armsraw")
a = ap.parse_args()
a.raw.mkdir(parents=True, exist_ok=True)
frags = fragments(PL / "slice" / "guzhenren-s11-16.gb18030.txt", a.n, a.target)
print(f"фрагментов {len(frags)}: " + ", ".join(f"{f['id']}={len(f['text'])}симв" for f in frags))
(a.raw / "fragments.json").write_text(json.dumps(frags, ensure_ascii=False, indent=1),
encoding="utf-8")
arms = [x for x in a.arms.split(",") if x]
plan = [(arm, f) for arm in arms for f in frags]
todo = [(arm, f) for arm, f in plan if not (a.raw / f"{arm}-{f['id']}.json").exists()]
print(f"план {len(plan)} вызовов, к покупке {len(todo)}; смета ≈ ${len(todo)*0.0008:.4f} "
f"(из наблюдённых $0.000360.00091 на вызов при effort={a.effort})")
if len(todo) * 0.0008 > a.cost_cap:
print(f"СТОП: смета выше капа ${a.cost_cap}", file=sys.stderr)
return 2
key = os.environ.get("DEEPSEEK_API_KEY")
if not key:
return 2
client = OpenAI(api_key=key, base_url="https://api.deepseek.com/v1", timeout=600)
with ThreadPoolExecutor(max_workers=a.workers) as ex:
list(ex.map(lambda t: call(client, t[0], t[1], a.cap, a.temp, a.effort, a.raw), todo))
recs = [json.loads(p.read_text(encoding="utf-8")) for p in sorted(a.raw.glob("a*-f*.json"))]
total = sum(r.get("cost_usd", 0) for r in recs)
print(f"\n{'арм':<5}{'n':>4}{'эхо':>5}{'пусто':>7}{'finish≠stop':>12}{'ср.rt':>8}"
f"{'ср.len_ratio':>13}{'ср.$':>10}{'сумма $':>10}")
for arm in arms:
rs = [r for r in recs if r.get("arm") == arm and "error" not in r]
if not rs:
continue
lr = [r["len_ratio"] for r in rs if r["len_ratio"] is not None and not r["echo"]]
print(f"{arm:<5}{len(rs):>4}{sum(r['echo'] for r in rs):>5}"
f"{sum(1 for r in rs if not r['content_chars']):>7}"
f"{sum(1 for r in rs if r['finish'] != 'stop'):>12}"
f"{sum(r['reasoning_tokens'] for r in rs)/len(rs):>8.0f}"
f"{(sum(lr)/len(lr) if lr else 0):>13.2f}"
f"{sum(r['cost_usd'] for r in rs)/len(rs):>10.6f}"
f"{sum(r['cost_usd'] for r in rs):>10.6f}")
print(f"\nВСЕГО ПОТРАЧЕНО АРМАМИ: ${total:.6f}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,29 @@
{
"deepseek": {
"ids": [
"deepseek-v4-flash",
"deepseek-v4-pro"
],
"expected_present": {
"deepseek-v4-flash": true,
"deepseek-v4-pro": true
}
},
"xai": {
"ids": [
"grok-4.20-0309-non-reasoning",
"grok-4.20-0309-reasoning",
"grok-4.20-multi-agent-0309",
"grok-4.3",
"grok-4.5",
"grok-build-0.1",
"grok-imagine-image",
"grok-imagine-image-quality",
"grok-imagine-video",
"grok-imagine-video-1.5"
],
"expected_present": {
"grok-4.3": true
}
}
}

View file

@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""$0 live-фактчек слагов перед платными вызовами (гардрейл CLAUDE.md + правило двух направлений).
GET /models у каждого провайдера, чьи слаги едут в этой сессии. Ничего не генерирует только
листинг. Ключи читает dotenv из eval/.env (сам файл сессия НЕ открывает).
"""
from __future__ import annotations
import json
import os
import sys
import urllib.request
from pathlib import Path
from dotenv import load_dotenv
EVAL = Path(__file__).resolve().parents[1]
load_dotenv(EVAL / ".env")
TARGETS = [
("deepseek", "https://api.deepseek.com/v1/models", "DEEPSEEK_API_KEY",
["deepseek-v4-flash", "deepseek-v4-pro"]),
("xai", "https://api.x.ai/v1/models", "XAI_API_KEY", ["grok-4.3"]),
]
def main() -> int:
out = {}
for name, url, key_env, expect in TARGETS:
key = os.environ.get(key_env)
if not key:
print(f"{name}: НЕТ КЛЮЧА {key_env}", file=sys.stderr)
out[name] = {"error": f"no {key_env}"}
continue
req = urllib.request.Request(url, headers={"Authorization": f"Bearer {key}"})
try:
with urllib.request.urlopen(req, timeout=60) as r:
data = json.loads(r.read())
except Exception as e: # noqa: BLE001 — аномалия печатается, диагноз по вендор-доке
print(f"{name}: ОШИБКА {e}", file=sys.stderr)
out[name] = {"error": str(e)[:300]}
continue
ids = sorted(m.get("id", "") for m in data.get("data", []))
out[name] = {"ids": ids, "expected_present": {e: (e in ids) for e in expect}}
print(f"{name}: {len(ids)} слаг(ов) → {ids}")
for e in expect:
print(f" {e}: {'ЖИВ' if e in ids else 'НЕТ В ЛИСТИНГЕ (не значит мёртв — алиас-урок)'}")
(Path(__file__).resolve().parent / "slugcheck.json").write_text(
json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())

91
eval/promptlang/spend.py Normal file
View file

@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""$0 сводка расхода сессии ДВУМЯ независимыми путями.
Путь 1 то, что каждый харнесс записал сам (`cost_usd` в своих артефактах).
Путь 2 пере-счёт из СЫРОГО `usage` по прайсу `models.yaml` (кэш-токены по своей ставке).
Расхождение двух путей и есть проверка: одинаковые числа, полученные одним способом, не проверка.
"""
from __future__ import annotations
import json
import sqlite3
from pathlib import Path
import yaml
REPO = Path("/home/ubuntu/projects/textmachine")
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
CUTOFF = "2026-07-31 18:40" # первая трата сессии
prices = yaml.safe_load((REPO / "backend/configs/models.yaml").read_text(encoding="utf-8"))["models"]
def px(model: str) -> tuple[float, float, float]:
p = prices[model]["price"]
return p["input_per_m"], p["output_per_m"], p.get("cached_per_m", 0.0)
rows = []
# — движковые прогоны (задача A арм en; задача B арм a0 в боевой форме до стоп-гейта)
for proj in ("A-ru", "A-en", "B-a0", "B-a1", "B-a2"):
db = PL / proj / "db.sqlite"
if not db.exists():
continue
con = sqlite3.connect(db)
for r in con.execute("select model_actual, prompt_tokens, cached_tokens, completion_tokens,"
" cost_usd from request_log where ts > ?", (CUTOFF,)):
m, pt, cch, ct, self_cost = r
if not m:
continue
pin, pout, pcached = px(m)
recomputed = (pt - cch) / 1e6 * pin + cch / 1e6 * pcached + ct / 1e6 * pout
rows.append(("движок " + proj, self_cost, recomputed))
con.close()
# — сырые пробы провода и армы
for d, label in ((PL / "wire", "wire-пробы"), (PL / "armsraw", "армы задачи B/A")):
for f in sorted(d.glob("*.json")):
if f.name == "fragments.json":
continue
j = json.loads(f.read_text(encoding="utf-8"))
if "error" in j:
continue
pin, pout, pcached = px("deepseek-v4-flash")
pt, cch, ct = j.get("prompt_tokens", 0), j.get("cached_tokens", 0), j.get("completion_tokens", 0)
recomputed = (pt - cch) / 1e6 * pin + cch / 1e6 * pcached + ct / 1e6 * pout
rows.append((label, j.get("cost_usd", 0.0), recomputed))
# — судьи (свои леджеры, свои модели; в judge_p4 их ДВА, маршрутизация по семейству пары)
for sub, label in (("judge", "судья армов"), ("judge_p4", "судья пар P4")):
led = PL / sub / "ledger.jsonl"
if not led.exists():
continue
for ln in led.read_text().splitlines():
j = json.loads(ln)
if "error" in j:
continue
model = j.get("judge", "grok-4.3")
pin, pout, _ = px(model)
billed = j.get("out_billed")
if billed is None: # judge_p4: reasoning у deepseek subset, у xai additive
ct, rt = j.get("completion_tok", 0), j.get("reasoning_tok", 0)
billed = ct + rt if model.startswith("grok") and rt > ct else ct
recomputed = j.get("prompt_tok", 0) / 1e6 * pin + billed / 1e6 * pout
rows.append((f"{label} {model}", j.get("cost_usd", 0.0), recomputed))
agg: dict[str, list[float]] = {}
for label, a, b in rows:
v = agg.setdefault(label, [0.0, 0.0, 0])
v[0] += a
v[1] += b
v[2] += 1
print(f"{'источник':<22}{'вызовов':>9}{'путь 1 ($)':>13}{'путь 2 ($)':>13}{'Δ':>12}")
t1 = t2 = 0.0
for label, (a, b, n) in sorted(agg.items()):
print(f"{label:<22}{n:>9}{a:>13.6f}{b:>13.6f}{a-b:>12.2e}")
t1 += a
t2 += b
print(f"{'ИТОГО':<22}{sum(v[2] for v in agg.values()):>9}{t1:>13.6f}{t2:>13.6f}{t1-t2:>12.2e}")
print(f"\nпотолок сессии $0.15 · использовано {t1/0.15*100:.1f}% · остаток ${0.15-t1:.6f}")

View file

@ -0,0 +1,141 @@
#!/usr/bin/env python3
"""Wire-проба канала deepseek-v4-flash после вендор-обновления 0731 (правило двух направлений).
Что случилось: боевой черновой канал 31.07 в 18:45+ UTC начал возвращать `finish=length` при
`completion_tokens = max_tokens = 8000` и ПУСТЫМ `content` 4 из 4 чанков базлайнового арма против
2 из 68 у холодного прогона тем же утром. Вендор-дока даёт причину: слаг `deepseek-v4-flash` в этот
день переехал на `DeepSeek-V4-Flash-0731` («re-post-trained», усиленные agent-способности).
Проба отвечает на то, чего движковый леджер ответить НЕ может: для deepseek он держит
`ReasoningTokens = 0` СОЗНАТЕЛЬНО (`provider_openai.go:22-24`, ReasoningSubset thinking внутри
completion, иначе двойной счёт). Значит «сколько именно думает модель» видно только на сыром проводе.
Меряем при разных потолках: сколько уходит в `reasoning_content`, доходит ли дело до `content`,
на каком потолке ответ вообще заканчивается. Сырьё персистится целиком (правило 1 полигона).
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import time
from pathlib import Path
from dotenv import load_dotenv
from openai import OpenAI
REPO = Path("/home/ubuntu/projects/textmachine")
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
RAW = PL / "wire"
RAW.mkdir(exist_ok=True)
load_dotenv(REPO / "eval" / ".env")
PRICE_IN, PRICE_CACHED, PRICE_OUT = 0.14, 0.0028, 0.28 # models.yaml, prices_checked 2026-07-10
USER_SEP = "\n---USER---\n"
FEWSHOT_SEP = "\n---FEWSHOT---\n"
def render(arm: str, text: str) -> tuple[str, str]:
"""Точная реплика движкового рендера (render.go:117-155 + Render): комментарии вырезаются,
system = ядро (+ few-shot через «\\n\\n»), user = хвост после ---USER--- с подставленным {{text}}."""
raw = (PL / "arms" / f"translator-{arm}.md").read_text(encoding="utf-8")
out, rest = [], raw
while True:
i = rest.find("<!--")
if i < 0:
out.append(rest)
break
out.append(rest[:i])
rest = rest[i + 4:]
j = rest.find("-->")
rest = rest[j + 3:]
canon = "".join(out)
head, user = canon.split(USER_SEP, 1)
parts = head.split(FEWSHOT_SEP, 1)
system = parts[0].strip()
if len(parts) == 2:
system = system + "\n\n" + parts[1].strip()
vals = {"source_lang": "zh", "target_lang": "ru", "genre": "вебновелла",
"audience": "взрослые читатели вебновелл", "title": "蛊真人", "venuti": "0.60",
"honorifics": "keep", "transcription": "palladius", "footnotes": "minimal",
"text": text}
for k, v in vals.items():
system = system.replace("{{" + k + "}}", v)
user = user.replace("{{" + k + "}}", v)
return system, user.strip()
def call(client, system: str, user: str, max_tokens: int, tag: str, extra: dict | None = None,
temperature: float | None = None) -> dict:
t0 = time.time()
kw = dict(model="deepseek-v4-flash",
messages=[{"role": "system", "content": system}, {"role": "user", "content": user}],
max_tokens=max_tokens)
if temperature is not None:
kw["temperature"] = temperature
if extra:
kw["extra_body"] = extra
r = client.chat.completions.create(**kw)
ch = r.choices[0]
msg = ch.message
content = msg.content or ""
reasoning = getattr(msg, "reasoning_content", None) or ""
u = r.usage
pt = getattr(u, "prompt_tokens", 0) or 0
ct = getattr(u, "completion_tokens", 0) or 0
det = getattr(u, "completion_tokens_details", None)
rt = (getattr(det, "reasoning_tokens", 0) if det else 0) or 0
cached = getattr(getattr(u, "prompt_tokens_details", None), "cached_tokens", 0) or 0
cost = (pt - cached) / 1e6 * PRICE_IN + cached / 1e6 * PRICE_CACHED + ct / 1e6 * PRICE_OUT
rec = dict(tag=tag, ts=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
model_returned=r.model, max_tokens=max_tokens, extra=extra,
finish=ch.finish_reason, content_chars=len(content), reasoning_chars=len(reasoning),
prompt_tokens=pt, cached_tokens=cached, completion_tokens=ct, reasoning_tokens=rt,
cost_usd=round(cost, 6), latency_s=round(time.time() - t0, 1),
system=system, user=user, content=content, reasoning_content=reasoning)
(RAW / f"{tag}.json").write_text(json.dumps(rec, ensure_ascii=False, indent=1), encoding="utf-8")
print(f"[{tag}] model={r.model} finish={ch.finish_reason} cap={max_tokens} "
f"out={ct} (reasoning_tok={rt}) content={len(content)}симв reasoning={len(reasoning)}симв "
f"${cost:.6f} {rec['latency_s']}с")
return rec
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--chunk", type=Path, required=True, help="файл с текстом чанка")
ap.add_argument("--arm", default="a0")
ap.add_argument("--caps", default="16000")
ap.add_argument("--tag", default="probe")
ap.add_argument("--effort", default="", help="reasoning_effort, если проверяем ручку")
ap.add_argument("--temp", default="", help="temperature; пусто = НЕ слать (как проба), '0.3' = как шлёт движок")
a = ap.parse_args()
text = a.chunk.read_text(encoding="utf-8")
system, user = render(a.arm, text)
print(f"чанк {len(text)} симв · system {len(system)} симв · арм {a.arm}")
key = os.environ.get("DEEPSEEK_API_KEY")
if not key:
print("нет DEEPSEEK_API_KEY", file=sys.stderr)
return 2
client = OpenAI(api_key=key, base_url="https://api.deepseek.com/v1", timeout=600)
total = 0.0
for cap in [int(x) for x in a.caps.split(",")]:
extra = {"reasoning_effort": a.effort} if a.effort else None
tag = f"{a.tag}-{a.arm}-{cap}"
if a.effort:
tag += f"-eff{a.effort}"
if a.temp:
tag += f"-t{a.temp}"
rec = call(client, system, user, cap, tag, extra,
float(a.temp) if a.temp else None)
total += rec["cost_usd"]
print(f"\nпроба потратила ${total:.6f}")
return 0
if __name__ == "__main__":
raise SystemExit(main())