From ca654eb1e0db40edb034913096cefdbba7d26e7b Mon Sep 17 00:00:00 2001 From: "Claude (backend session)" Date: Sat, 1 Aug 2026 00:57:33 +0300 Subject: [PATCH] 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 --- docs/PROGRESS.md | 11 +- docs/architecture/05-decisions-log.md | 4 +- .../reports/GENERALITY_RESEARCH_2026-07-31.md | 2 +- .../POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md | 788 ++++++++++++++++++ docs/experiments/00-provider-quirks.md | 21 +- eval/promptlang/analyze_arms.py | 117 +++ eval/promptlang/build_projects.py | 296 +++++++ eval/promptlang/judge_fidelity.py | 278 ++++++ eval/promptlang/judge_p4.py | 277 ++++++ eval/promptlang/p4_pairs.py | 115 +++ eval/promptlang/run_arms.py | 171 ++++ eval/promptlang/slugcheck.json | 29 + eval/promptlang/slugcheck.py | 54 ++ eval/promptlang/spend.py | 91 ++ eval/promptlang/wire_probe.py | 141 ++++ 15 files changed, 2390 insertions(+), 5 deletions(-) create mode 100644 docs/archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md create mode 100644 eval/promptlang/analyze_arms.py create mode 100644 eval/promptlang/build_projects.py create mode 100644 eval/promptlang/judge_fidelity.py create mode 100644 eval/promptlang/judge_p4.py create mode 100644 eval/promptlang/p4_pairs.py create mode 100644 eval/promptlang/run_arms.py create mode 100644 eval/promptlang/slugcheck.json create mode 100644 eval/promptlang/slugcheck.py create mode 100644 eval/promptlang/spend.py create mode 100644 eval/promptlang/wire_probe.py diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index d94e93a8..b6ed9cb1 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1,7 +1,7 @@ # Журнал прогресса > **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-07-26: пак-20 закрыт ЦЕЛИКОМ D39.41–53 — терминолог · флаговый стоп · двухсекционная инъекция · точечная ре-редактура · языковой экран; жанровый словарь отменён D39.47; полигон-пакеты 6/7/8 закрыты). **Источник истины по РЕШЕНИЯМ — `architecture/05-decisions-log.md` (D1–D39.53); этот файл — ЖУРНАЛ.** -> - **Фазы/курс:** Ф0 ✅ · Ф1-инфра ✅ (D20–D28; golden = инвариант №8) · арка «качество-первым» D26→D38.5 закрыта · **арх-ресет D39 исполнен ЦЕЛИКОМ** (7 слоёв → программа D39.1–D39.10 → паки 11–16, D39.13–D39.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.54–56, вкл. малую пачку 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-инфра ✅ (D20–D28; golden = инвариант №8) · арка «качество-первым» D26→D38.5 закрыта · **арх-ресет D39 исполнен ЦЕЛИКОМ** (7 слоёв → программа D39.1–D39.10 → паки 11–16, D39.13–D39.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.54–56, вкл. малую пачку 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: стройка паков 11–16, 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 diff --git a/docs/architecture/05-decisions-log.md b/docs/architecture/05-decisions-log.md index be58ace8..8abba942 100644 --- a/docs/architecture/05-decisions-log.md +++ b/docs/architecture/05-decisions-log.md @@ -1193,4 +1193,6 @@ API-529-долг закрыт: 8-осевой refute-by-default воркфлоу Прочее прогона: язык при пустом якоре — `off_language` 0/149 (экран-прибор работает) · утечка алфавита 0/150, но `甲等 → класс Цзя` при `乙等 → ранг И` вскрыла два механизма: НЕПРОВЕРЕННОЕ поле `type` решает ветку транскрипции (`元石`=name ⇒ «юаньши» — механизм «слишком китаизированно») и роль не видит СЕРИЙ (батчи режут) · `蛊` не приходит НИ ОДНИМ каналом (строка 18 переформулирована: структурная дыра эмиссии на голове частотного понятия; механизм доставки подписанной короткой строки проверен $0-пином) · эхо-метрика закрыта живым фактом (2 recovered) · стоп-режим ×4; авто-режим, edit-волна, полная цена (16), P1/веса (21/22 — сырьё снято, метод полигонский), оси голоса (профили не подписаны; черновик в `coldrun-a/SIGN-PACKAGE.md` §3; 13б открыта) — со следующим живым прогоном/полигон-пакетом. **Принятые нормы (в промт-нормы оркестратора):** итерации промптов — на проекте-пробе 1–2 главы (≈$0.005), полный срез после; правка-«терпимость» движка к собственным данным = воркэраунд (правило владельца 31.07); канон конкретной книги в пар-промпте = утечка §0 (поймано владельцем). Новые строки бэклога: 36а валидация `type` (Р3, приоритет) · 36б второе мнение-рецензент (Р4; условие строки 5 наступило) · 36в серии · 36г формат каналов в шаблон движка (Р5) · квадратичность src-правила → строка 37. Эксперимент «язык промптов» (Р6, гипотеза владельца, ≈$0.02–0.05, детерминированные счётчики) — к выдаче полигону по слову владельца. Отчёты: `archive/reports/COLDRUN_REPORT_2026-07-31.md` (пре-регистрация §0 до трат · критик полноты §9 · ревью-шапка) + `COLDRUN_HANDOFF_2026-07-31.md`. Мутации сессии 23/23 красные (две выжившие первого круга = дыры тестов, закрыты с премис-проверками). Критик полноты честен: оси до холодного прогона дают 0 срабатываний («не измерено», не «чисто») · ось A может ловить цитату · ось B precision-over-recall · качество осей = функция полноты `decl` сида · SourceReplies без выравнивания не сопоставим с Replies. Хвосты: бэклог 13б (`speech-cue.txt` — владельцу) · 49а (ALTER-миграции v8–v14 не идемпотентны вопреки шапке `migrate.go:9-11`) · golden-счётчики голоса — кандидат следующего ратифицированного пере-капчера. Первое измерение = `gates.voice` ON на холодном мини-прогоне (бэклог 24). Отчёт: `archive/reports/PACK19_BUILD_2026-07-30.md` (ревью-шапка). -## D39.60 — РЕСЁРЧ ОБЩНОСТИ ФАЗА 1 ПРИНЯТ ($0, read-only, кода не тронуто; спот-чеки оркестратора 8/8, вкл. побайтный повтор хеша пака `a28ed743c99c` и счёт 27 строк/12 файлов живых CJK-литералов): долг = **149 сайтов в 45 файлах** (111 не забуканы; вёдра A=27 вынести-в-данные · B=6 пар-модуль-без-гейта · C=104 общий-алгоритм+параметр-из-данных · D=12 удалить · E=106 легитимно) · **ось долга: источник/пара 63% · ЦЕЛЬ (ru) 30% · книга 3%** — по оси «НИКОГДА» владельца движок чист (4 сайта поимённо) · ja→ru проходит БЕЗОПАСНО без правки Go (деградируют майнер и калибровка), en→ru — НЕБЕЗОПАСНО (молча спят эхо-детектор `cjkShare`=0 и майнер `hanRuns`=0; устойчиво разделяют пары G2/G3) · строка 32 бэклога долг ЗАВЫШАЛА (боевых call-site: `isRuTarget` 6, `TokenizeCyrillic` 2 — остальное греп по комментариям/определениям). РАТИФИЦИРУЮ КАК КАРТУ И РАБОЧУЮ ИСТИНУ: пять вёдер с якорями · заморозки З1–З9 (синк с ратификациями подтверждён) · актуальные якоря пака-21 (ВСЕ ссылки D39.39 устарели после пака-19 — брать из §8 отчёта) · правило условного фолда §6.2 (фолдить ⟺ выход входит в оплаченный запрос/вердикт/final_hash; новый фолд обязан быть условным по присутствию) · находка «майненая сторона не пишет `Decl` ⇒ 149 авто-строк холодного банка не пройдут пост-чек» — уезжает в арку автономности банка (D39.59), не в пак-21 · живой баг ko/Hangul (`isCJKLang` числит ko, три вердикт-детектора Hangul не содержат — корейское эхо невидимо; строка 75, носитель П2) · дыра эмбед-плоскости (`internal/lang/data/*.txt` не хешируются ничем при `injection.txt` на проводе и `target-ru.txt` в вердиктах санитайзера ⇒ канал тихой пере-оплаты вне контура Р6, `rebill.go:137-139`; строка 76, П0 первым). СТОП: пять вопросов §12 — у владельца, рекомендации оркестратора: (1) П1 цель-шов ВКЛЮЧИТЬ в арку — D39.24 морозил «не ЭТИМ паком», сам код называет цель-шов назначенным носителем (`repair.go:182-189`), слой 7 целевой архитектуры держит «target-aware гейты — ОТКРЫТО» с 26.07; (2) фигуру «ОДИН общий resnapshot» ратифицировать (сегодня центы — корпус два мини-прогона по $0.13–0.23, после МАСШТАБА порядки; ре-пинуема 1 ось из 20), НО исполнение — ПОСЛЕ развилки DeepSeek-0731 (факт из отчёта полигона 31.07, приёмка отдельно по релею владельца: перепокупка сегодня купит пустые ответы; при выборе флора-16000 его сдвиг `request_hash` едет В ТОМ ЖЕ resnapshot); (3) П6-расщепление хеша пака — НЕ сейчас (посылка «наблюдаемость — не вердикт» противоречит записанной позиции `snapshot.go:397-402`; сессия сама отказалась подавать как решение); (4) Р1–Р4 сократить до ДВУХ: Р2-расширенный («детекторы единиц/шкал судят ЗНАЧЕНИЕ рендера или ФОРМУ?» — один ответ на семейство) + Р4-норма (потолок recall by construction пишется в контракт правила); Р3 закрыт D39.44 Q4 (остаток-работа сливается с Decl-швом в арке банка), Р1 расщеплён (данные→пар-пак · политика→Р2 · книжный остаток снят); (5) пак-21 РАСТВОРИТЬ: ~6 дешёвых пунктов (#11 U+0301 · #2 атрибуция-спан · #4 inner_marker · гомоглиф-детектор · строгий OffLanguage с оговоркой 36а · строка 26) батчем в общий resnapshot, 6 дефектов — в П1/П2/П3, #9 — в арку банка, #12/K4a — сняты как не-дефекты кода. Фаза 2 НЕ начинается до ответов (31.07, оркестратор №9). ✅ +## D39.60 — РЕСЁРЧ ОБЩНОСТИ ФАЗА 1 ПРИНЯТ ($0, read-only, кода не тронуто; спот-чеки оркестратора 8/8, вкл. побайтный повтор хеша пака `a28ed743c99c` и счёт 27 строк/12 файлов живых CJK-литералов): долг = **149 сайтов в 45 файлах** (111 не забуканы; вёдра A=27 вынести-в-данные · B=6 пар-модуль-без-гейта · C=104 общий-алгоритм+параметр-из-данных · D=12 удалить · E=106 легитимно) · **ось долга: источник/пара 63% · ЦЕЛЬ (ru) 30% · книга 3%** — по оси «НИКОГДА» владельца движок чист (4 сайта поимённо) · ja→ru проходит БЕЗОПАСНО без правки Go (деградируют майнер и калибровка), en→ru — НЕБЕЗОПАСНО (молча спят эхо-детектор `cjkShare`=0 и майнер `hanRuns`=0; устойчиво разделяют пары G2/G3) · строка 32 бэклога долг ЗАВЫШАЛА (боевых call-site: `isRuTarget` 6, `TokenizeCyrillic` 2 — остальное греп по комментариям/определениям). РАТИФИЦИРУЮ КАК КАРТУ И РАБОЧУЮ ИСТИНУ: пять вёдер с якорями · заморозки З1–З9 (синк с ратификациями подтверждён) · актуальные якоря пака-21 (ВСЕ ссылки D39.39 устарели после пака-19 — брать из §8 отчёта) · правило условного фолда §6.2 (фолдить ⟺ выход входит в оплаченный запрос/вердикт/final_hash; новый фолд обязан быть условным по присутствию) · находка «майненая сторона не пишет `Decl` ⇒ 149 авто-строк холодного банка не пройдут пост-чек» — уезжает в арку автономности банка (D39.59), не в пак-21 · живой баг ko/Hangul (`isCJKLang` числит ko, три вердикт-детектора Hangul не содержат — корейское эхо невидимо; строка 75, носитель П2) · дыра эмбед-плоскости (`internal/lang/data/*.txt` не хешируются ничем при `injection.txt` на проводе и `target-ru.txt` в вердиктах санитайзера ⇒ канал тихой пере-оплаты вне контура Р6, `rebill.go:137-139`; строка 76, П0 первым). СТОП: пять вопросов §12 — у владельца, рекомендации оркестратора: (1) П1 цель-шов ВКЛЮЧИТЬ в арку — D39.24 морозил «не ЭТИМ паком», сам код называет цель-шов назначенным носителем (`repair.go:182-189`), слой 7 целевой архитектуры держит «target-aware гейты — ОТКРЫТО» с 26.07; (2) фигуру «ОДИН общий resnapshot» ратифицировать (сегодня центы — корпус два мини-прогона по $0.13–0.23, после МАСШТАБА порядки; ре-пинуема 1 ось из 20), НО исполнение — ПОСЛЕ развилки DeepSeek-0731 (D39.61, строка 74: перепокупка сегодня купит пустые ответы; при выборе флора-16000 его сдвиг `request_hash` едет В ТОМ ЖЕ resnapshot); (3) П6-расщепление хеша пака — НЕ сейчас (посылка «наблюдаемость — не вердикт» противоречит записанной позиции `snapshot.go:397-402`; сессия сама отказалась подавать как решение); (4) Р1–Р4 сократить до ДВУХ: Р2-расширенный («детекторы единиц/шкал судят ЗНАЧЕНИЕ рендера или ФОРМУ?» — один ответ на семейство) + Р4-норма (потолок recall by construction пишется в контракт правила); Р3 закрыт D39.44 Q4 (остаток-работа сливается с Decl-швом в арке банка), Р1 расщеплён (данные→пар-пак · политика→Р2 · книжный остаток снят); (5) пак-21 РАСТВОРИТЬ: ~6 дешёвых пунктов (#11 U+0301 · #2 атрибуция-спан · #4 inner_marker · гомоглиф-детектор · строгий OffLanguage с оговоркой 36а · строка 26) батчем в общий resnapshot, 6 дефектов — в П1/П2/П3, #9 — в арку банка, #12/K4a — сняты как не-дефекты кода. Фаза 2 НЕ начинается до ответов (31.07, оркестратор №9). ✅ + +## D39.61 — ПОЛИГОН Р6+P4 ПРИНЯТ И ЗАЛЕНДЕН (итог $0.433062 = $0.147527 в потолке промта $0.15 + $0.285535 судейского хвоста по прямой санкции владельца; два пути счёта сходятся до 2.9e-06, независимый ре-ран spend.py оркестратором совпал; ⚠ леджер движка = НИЖНЯЯ граница — 4 вызова «2xx body decode failed (call IS billed)» не заледжерены, ≤$0.009, строка 78): отчёт трёхслойный — §1–§9 хроника · §10–§11 errata/саморевью по вопросам владельца · **§12 = единственная действующая редакция** (15-агентное адверсариальное ревью + исполнение пропущенного деливерабла); три ложных вывода сессия поймала и отозвала САМА (эхо-атрибуция few-shot · «пропуск» постинструкции · псевдо-репликация «митигации вернее», кластерный p=1.0) — механизм «вывод на агрегате до вскрытия единиц» забран в промт-нормы. **ГЛАВНОЕ — вендор-катовер: DeepSeek 31.07 сменил веса под слагом** (`DeepSeek-V4-Flash-0731`, «only re-post-trained»; `/models` молчит; **v4-pro НЕ тронут**) ⇒ **боевая черновая форма ПОКУПАЕТ ПУСТОЕ** (cap 8000 → `finish=length`, reasoning 8000, `content`=""; несущая улика — пробы ОДНОГО чанка: cap 8000 пусто / cap 16000 перевод); механизм — многословная обдумка с тяжёлым хвостом (разброс reasoning ×163 на побайтно одном входе; упор и в 16000 в 2/5), привязана к ПЛОТНОМУ ХАНЬСКОМУ входу (×7.8 против en при сопоставимом входе; ja не проверялся), вендор для high/max рекомендует потолок 384K — наш флор на два порядка ниже ⇒ сместился ДЕФОЛТНЫЙ эффорт, а не «модель сломалась» ⇒ **развилка владельца (строка 74): (а) флор 16000 + resnapshot · (б) слать `reasoning_effort` явно (= приведение к вендорской модели; амендмент `echoMineViolation`; `low` дешевле прежних весов ~22%, но эхо возвращается ~6% и литературное качество черновика при low НЕ судилось) · (в) ждать бету; до развилки платные прогоны и resnapshot СТОП.** **Эхо на 0731 стохастично ПО ВЫЗОВУ** (побайтно идентичные запросы → эхо/не-эхо) ⇒ посылка «retry just re-produces them» (`disposition.go:138-148`) опровергнута — ре-ген N=1 перед эскалацией ≈7.6× дешевле ремонта ($0.00096 против $0.007335 за эхо; строка 77). **Р6 закрыт отрицательно:** ru против en-перевода промпта строка-в-строку — различий НЕТ ни по одной детерминированной оси (эхо 0/0 · срыв языка 0/0 · коридор 12/12 · преамбулы 0/0); даже «en дороже +9.4%» снято ревью (кэш-эффект; без кэша +6.0%, знаковый p=0.39) — **боевые промпты остаются русскими**; язык промпта как фактор эха не подтверждён и не закрыт окончательно (N=12, роль одна). **Петля D21 п.6: гейт верности ПРОЙДЕН в ослабленной форме — «ЦЕНЫ ВЕРНОСТИ У МИТИГАЦИЙ НЕ ОБНАРУЖЕНО»** (риг на 16 из 24 готовых пар research15-probes, генерация оплачена 09.07; floor 4/4 tie у ОБОИХ судей; маршрутизация автор≠ревьюер: grok-пары судит dspro, deepseek-пары — grok-4.3; «митигации ВЕРНЕЕ» НЕ установлено — 16 пар из 6 базовых текстов, 5/6 кластеров смешанные, конфаунд длины). **Wiring-вердикт (пере-выведен §12.3):** few-shot — оснований ни на «вносить», ни на «вредно» (против — один полностью АНГЛИЙСКИЙ выход из 48; «×1.9/+28.5%» ОТОЗВАНО как артефакт средних на бимодальном распределении) · постинструкция — возражений по существу нет, но на 0731 покупать ей нечего (эхо в боевой форме НЕ измерено — канал мёртв), байты двигают PromptSHA256 ⇒ не вносить до предмета · **префилл «Перевод:\n» — сильнейший кандидат** (0/6 эха в P4, верность 3:0, контрдовод «обходит thinking» на 0731 перевернулся в довод ЗА; не проверен на новых весах, проба ≈$0.005) · языковые констрейнты — НИКОГДА без per-model замера (инверсия 9/10 подтверждена репликой побайтно). Эмпирика P4 09.07 = superseded (весов Preview больше нет); квирки синхронизированы сессией с §12 (один хвост докорректирован при лендинге); остаток с ценами — §12.7 (8 несудимых пар ≈$0.10 · префилл $0.005 · effort-high $0.01 · ja-вход $0.005 · echo-rate боевой формы невозможен до развилки). Лендинг: отчёт с ревью-шапкой · quirks · харнесс `eval/promptlang/` (01.08, оркестратор №9). ✅ diff --git a/docs/archive/reports/GENERALITY_RESEARCH_2026-07-31.md b/docs/archive/reports/GENERALITY_RESEARCH_2026-07-31.md index 5d0ff3d3..42cbc7ef 100644 --- a/docs/archive/reports/GENERALITY_RESEARCH_2026-07-31.md +++ b/docs/archive/reports/GENERALITY_RESEARCH_2026-07-31.md @@ -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 гейтится этой развилкой; сама карта от неё не зависит. --- diff --git a/docs/archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md b/docs/archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md new file mode 100644 index 00000000..08295e8e --- /dev/null +++ b/docs/archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md @@ -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:30–24:00 31.07 | вне пиков DeepSeek UTC 01–04 / 06–10 (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⟧, **пример формата `师父наставник` побайтно тот же**, те же плейсхолдеры | правка примера формата = второй фактор (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-драфт** | +| Срез | 蛊真人 главы **11–16** (строки 837..1293 utf8-копии; 17 448 симв, CJK 0.805; sha256 gb18030 `5c22ec0d…1ee76b`) | «свежая выборка, другие фрагменты»: со срезом холодного прогона (главы 1–10) не пересекается | +| Единицы | 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.010–0.016 | то же + надбавка за более длинный en-system (+232 симв) | +| B × 3 арма, 12 чанков каждый | $0.013–0.016 на арм, итого **$0.040–0.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.15–0.30 сам по себе). Достигнутый N называется числом, мощность — тоже. +- **en-промпт дороже ru** на входных токенах. Это свойство перевода инструкций, а не дефект арма; называется отдельно и в вывод «язык хуже» не сворачивается. + +### 0.5 Пороги чтения (заморожены) + +- **Задача A.** Сигналом считаю разницу `off_language`/`bad_lines`, которая (а) ≥3 строк абсолютно И (б) больше межбатчевого разброса внутри арма. Иначе — «различий не обнаружено при данном N». +- **Задача B, эхо.** Базовая частота A0 сравнивается с 2/20 холодного прогона (тот же канал, соседние главы). Вывод «митигация снижает эхо» требует, чтобы A0 дал ≥3 эха: при базе 0–2 разница с 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.3–0.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:45–18: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` 170–313, цена $0.00036–0.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.00036–0.00091 против $0.0022–0.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.8–3.6 дороже, и это + оплаченный ноль**. С `effort:"low"` — **$0.00096** в среднем по 12 вызовам арма a0 + ($0.00036–0.00091 на пробах), то есть **на 22% дешевле прежнего**. Модель COGS ~$0.85/ранобэ (D30.4) зависит от того, + какая из развилок будет выбрана; сама по себе она сегодня не верна ни в одну, ни в другую сторону. + +## §3. ЗАДАЧА B — fidelity-хвост P4 (петля D21 п.6, стоит с 09.07) + +Конфигурация: 12 СВЕЖИХ фрагментов 蛊真人 гл. 11–16 (вне среза холодного прогона; 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.00704–0.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.8–5.6** по сегодняшней цене вызова. Плюс + правку пришлось бы дублировать в `translator-banknote.md` (общая шапка, строка 36г бэклога). +- **⚠ Числа P4 от 09.07 сняты на весах, которых больше нет.** «0/29 против базы 30–67%» относится к + `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:45–20:52 UTC**, **вне пиков DeepSeek** (UTC 01–04 / 06–10); проверено `date -u` перед каждым блоком трат. +Стендовые артефакты: **1117 хешей сняты до прогона и сверены после — дифф ПУСТ.** + +## §6. Чего сессия НЕ сделала и почему (стоп-гейт отработал) + +- **Полный fidelity-хвост «35 пар» D21 п.6 не куплен.** Куплено 3 единицы / 5 голосов = 1/7. Причина + арифметическая: контрастный голос рига стоит **$0.00758** (замерено, 5 голосов, разброс + $0.00704–0.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 170–313, $0.00036–0.00091 (было $0.0022–0.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** | 2–4 вызова, возможен упор в потолок | +| ja-вход (область §10.4) | **≈$0.005** | 3–4 вызова | + +--- + +# §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:45–21: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 | единственная неизмеренная ось петли | diff --git a/docs/experiments/00-provider-quirks.md b/docs/experiments/00-provider-quirks.md index 8dabd93e..84741aed 100644 --- a/docs/experiments/00-provider-quirks.md +++ b/docs/experiments/00-provider-quirks.md @@ -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 **170–313** ток., **$0.00036–0.00091** за вызов (без ручки — $0.0022–0.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` 349–847; 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 против базы 30–67%» — про прежнюю модель; на 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-реплику на боевом промпте.) ## Параметры сэмплинга diff --git a/eval/promptlang/analyze_arms.py b/eval/promptlang/analyze_arms.py new file mode 100644 index 00000000..7d0a0b3e --- /dev/null +++ b/eval/promptlang/analyze_arms.py @@ -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()) diff --git a/eval/promptlang/build_projects.py b/eval/promptlang/build_projects.py new file mode 100644 index 00000000..54a3fd28 --- /dev/null +++ b/eval/promptlang/build_projects.py @@ -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: ЧИСТЫЕ БД на СВЕЖЕМ срезе (главы 11–16, вне среза холодного прогона), + одна черновая стадия, три варианта `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}. Срез — главы 11–16 蛊真人 (строки 837..1293 utf8-копии), НЕ +# пересекается со срезом холодного прогона (главы 1–10): «свежая выборка» петли 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()) diff --git a/eval/promptlang/judge_fidelity.py b/eval/promptlang/judge_fidelity.py new file mode 100644 index 00000000..9a22f798 --- /dev/null +++ b/eval/promptlang/judge_fidelity.py @@ -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()) diff --git a/eval/promptlang/judge_p4.py b/eval/promptlang/judge_p4.py new file mode 100644 index 00000000..0f2d979e --- /dev/null +++ b/eval/promptlang/judge_p4.py @@ -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()) diff --git a/eval/promptlang/p4_pairs.py b/eval/promptlang/p4_pairs.py new file mode 100644 index 00000000..c0559182 --- /dev/null +++ b/eval/promptlang/p4_pairs.py @@ -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()) diff --git a/eval/promptlang/run_arms.py b/eval/promptlang/run_arms.py new file mode 100644 index 00000000..e14ea9f7 --- /dev/null +++ b/eval/promptlang/run_arms.py @@ -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.00036–0.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()) diff --git a/eval/promptlang/slugcheck.json b/eval/promptlang/slugcheck.json new file mode 100644 index 00000000..e626c839 --- /dev/null +++ b/eval/promptlang/slugcheck.json @@ -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 + } + } +} \ No newline at end of file diff --git a/eval/promptlang/slugcheck.py b/eval/promptlang/slugcheck.py new file mode 100644 index 00000000..b5ab4cb6 --- /dev/null +++ b/eval/promptlang/slugcheck.py @@ -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()) diff --git a/eval/promptlang/spend.py b/eval/promptlang/spend.py new file mode 100644 index 00000000..cf1a5299 --- /dev/null +++ b/eval/promptlang/spend.py @@ -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}") diff --git a/eval/promptlang/wire_probe.py b/eval/promptlang/wire_probe.py new file mode 100644 index 00000000..522e96e4 --- /dev/null +++ b/eval/promptlang/wire_probe.py @@ -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("") + 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())