Land the polygon package as D39.61: R6 negative, fidelity gate passed with no cost found, DeepSeek swapped flash weights under the slug so the owner fork blocks paid runs
This commit is contained in:
parent
c010c32f3a
commit
ca654eb1e0
15 changed files with 2390 additions and 5 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -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 гейтится этой развилкой; сама карта от неё не зависит.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
788
docs/archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md
Normal file
788
docs/archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md
Normal file
|
|
@ -0,0 +1,788 @@
|
|||
# Полигон: язык промптов (Р6) + fidelity-хвост анти-эхо P4 (петля D21 п.6)
|
||||
|
||||
**Сессия:** полигон, 31.07.2026. **Промт:** `docs/POLYGON_PROMPTLANG_ANTIECHO_SESSION_PROMPT.md`
|
||||
(оркестратор №9, санкция владельца). **Потолок $0.15 суммарно.** **Сессия НЕ коммитит.**
|
||||
Артефакты прогонов — `~/books/gu-zhenren/promptlang/` (вне git). Харнесс — `eval/promptlang/`.
|
||||
|
||||
> **Ревью-шапка (оркестратор №9, 01.08): ПРИНЯТО И ЗАЛЕНДЕНО, D39.61. Действующая редакция выводов = §12** — сам отчёт так предписывает; §1–§11 сохранены как хроника, и три их вывода ОТОЗВАНЫ саморевью сессии (эхо-атрибуция few-shot · «пропуск» постинструкции · «митигации вернее базы»). Короткая приёмка: финальная редакция прочитана целиком; деньги проверены независимо — мой ре-ран `eval/promptlang/spend.py` дал те же 163 вызова / $0.433062, два пути сходятся до 2.9e-06; итог = $0.1475 в потолке промта + $0.2855 судейского хвоста по прямой санкции владельца; находка «леджер = нижняя граница» (4 billed-вызова вне `request_log`) забукана строкой 78. Дифф квирков сверен с §12 построчно — синхронизирован сессией; один остаточный хвост («few-shot стал ВРЕДЕН» в P4-superseded-строке) докорректирован при лендинге по §12.5 п.1. Дисциплина: пре-регистрация цела, амендмент §0-bis объявлен до продолжения трат, механизм всех трёх отозванных выводов назван самой сессией («вывод на агрегате до вскрытия единиц») — забран в промт-нормы оркестратора. Развилка §12.4 — у владельца (строка 74), до неё платные прогоны и resnapshot СТОП.
|
||||
|
||||
---
|
||||
|
||||
> **⚠ ПОРЯДОК ЧТЕНИЯ (после адверсариального ревью и добора судьи):** действующая редакция
|
||||
> выводов — **§12** в конце файла. §1–§11 сохранены как хроника (дисциплина D23.3) и в восьми местах
|
||||
> опровергнуты §12.5 — не цитировать их числа, не сверившись с §12.5. §0 (пре-регистрация) заморожена
|
||||
> и не переписывалась ни в одном пункте.
|
||||
|
||||
---
|
||||
|
||||
## §0. ПРЕ-РЕГИСТРАЦИЯ (записана ДО первой платной траты; ниже НЕ переписывается)
|
||||
|
||||
Причина, по которой это первый раздел: у проекта есть своя перепись случаев, когда критерий успеха
|
||||
дописывался после результата (D37: «0 ошибок» = 36 ошибок; exp15 Q1b: заголовок был артефактом
|
||||
судейского окна). Пре-регистрация — единственное, что отличает замер от рассказа о замере.
|
||||
|
||||
### 0.1 Конфигурация — заморожена
|
||||
|
||||
**Общее для обеих задач**
|
||||
|
||||
| Ось | Значение | Почему так |
|
||||
|---|---|---|
|
||||
| Окно трат | UTC 18: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⟧, **пример формата `师父<TAB>наставник` побайтно тот же**, те же плейсхолдеры | правка примера формата = второй фактор (D39.52: именно эта строка чинит срыв языка) |
|
||||
| Данные | **БД холодного прогона, копия**; тот же `book_id`, тот же снапшот ⇒ черновая волна не перекупается. Полезная нагрузка роли (150 кандидатов, KWIC, `drafts:`, батчинг) собирается ОДНИМ кодом из ОДНОЙ БД ⇒ байт-идентична по построению, а не по обещанию | «байт-НЕИЗМЕННЫЕ инъекции и данные» |
|
||||
| Инъекция | ⟦TM-CANON⟧ пуст в обоих армах (подписанных строк в холодной БД нет) ⇒ тривиально идентична | |
|
||||
| Метрики | ТОЛЬКО детерминированные, все — счётчики движка: `off_language` · `bad_lines` (в них же битый разделитель: строка без второго поля) · `unanswered` (аналог `parse_fail` на уровне терма) · `declined` · `consolidated` · `canon_conflicts`. Второй путь вывода — свой пересчёт по сырым ответам | судья не нужен |
|
||||
| Что НЕ меряю | эхо на этой роли — оппортунистически; мощности не обещаю | эхо редкое (2/20), на этих объёмах различие ru/en по эху статистически недоказуемо |
|
||||
|
||||
**Задача B — fidelity-хвост P4 (D21 п.6)**
|
||||
|
||||
| Ось | Значение | Почему так |
|
||||
|---|---|---|
|
||||
| Канал | боевой: `deepseek-v4-flash`, thinking ON, боевой `translator.md`, боевая нарезка пары (1797 вых. ток.), санитайзер и регресс-гард ON | скоуп wiring по D21 п.6 = **только deepseek-драфт** |
|
||||
| Срез | 蛊真人 главы **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 | единственная неизмеренная ось петли |
|
||||
|
|
@ -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-реплику на боевом промпте.)
|
||||
|
||||
## Параметры сэмплинга
|
||||
|
|
|
|||
117
eval/promptlang/analyze_arms.py
Normal file
117
eval/promptlang/analyze_arms.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#!/usr/bin/env python3
|
||||
"""$0 детерминированная батарея по армам (задачи A и B). Судья не участвует.
|
||||
|
||||
Приборы:
|
||||
· ЭХО — `cjk_share > 0.15`, определение движка (`disposition.go:269-281`, Han|Hiragana|Katakana);
|
||||
порог тот же, что у P4, поэтому числа сравнимы с 09.07 напрямую.
|
||||
· ЯЗЫК ВЫХОДА — доля кириллицы против латиницы среди БУКВ. Это встречный риск en-промпта,
|
||||
названный в промте сессии: английская инструкция может утянуть выход в английский.
|
||||
· АНТИ-ОМИССИЯ — len_ratio (симв. без пробелов, коридор пары [2.2, 4.2] из `pairs/zh-ru.yaml`)
|
||||
и отношение числа предложений (оракул `refusal_bench.split_sentences`).
|
||||
· ФОРМА — преамбула, markdown-заголовок, утёкший разделитель банкноты, латинские прогоны.
|
||||
· ПРОВОД — reasoning_tokens (сырьё, движок их для deepseek сознательно обнуляет), finish, цена.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import unicodedata
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
sys.path.insert(0, str(REPO / "eval"))
|
||||
from refusal_bench import split_sentences # noqa: F401 (оракул уже применён в run_arms)
|
||||
|
||||
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
|
||||
PREAMBLE_RE = re.compile(r"^\s*(вот|ниже|перевод[:\s]|here is|translation[:\s]|note[:\s])", re.I)
|
||||
MD_RE = re.compile(r"^\s{0,3}#{1,6}\s", re.M)
|
||||
LAT_RUN_RE = re.compile(r"[A-Za-z]{3,}")
|
||||
|
||||
|
||||
def script_mix(s: str) -> tuple[float, float, int]:
|
||||
cyr = lat = 0
|
||||
for ch in s:
|
||||
if not ch.isalpha():
|
||||
continue
|
||||
n = unicodedata.name(ch, "")
|
||||
if n.startswith("CYRILLIC"):
|
||||
cyr += 1
|
||||
elif n.startswith("LATIN"):
|
||||
lat += 1
|
||||
tot = cyr + lat
|
||||
return (cyr / tot if tot else 0.0), (lat / tot if tot else 0.0), tot
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--arms", default="a0,a1,a2,e0")
|
||||
ap.add_argument("--raw", type=Path, default=PL / "armsraw")
|
||||
a = ap.parse_args()
|
||||
|
||||
per = {}
|
||||
for arm in a.arms.split(","):
|
||||
rs = []
|
||||
for f in sorted(a.raw.glob(f"{arm}-f*.json")):
|
||||
j = json.loads(f.read_text(encoding="utf-8"))
|
||||
if "error" in j:
|
||||
rs.append(j)
|
||||
continue
|
||||
c = j["content"]
|
||||
cyr, lat, letters = script_mix(c)
|
||||
j["cyr_share"] = round(cyr, 4)
|
||||
j["lat_share"] = round(lat, 4)
|
||||
j["off_language"] = bool(letters) and cyr < 0.5 # выход не в целевом письме
|
||||
j["preamble"] = bool(PREAMBLE_RE.match(c))
|
||||
j["markdown"] = bool(MD_RE.search(c))
|
||||
j["bank_marker"] = "⟦TM-BANK" in c
|
||||
j["latin_runs"] = len(LAT_RUN_RE.findall(c))
|
||||
j["in_corridor"] = (j["len_ratio"] is not None and 2.2 <= j["len_ratio"] <= 4.2)
|
||||
rs.append(j)
|
||||
per[arm] = rs
|
||||
|
||||
print(f"{'арм':<5}{'n':>3}{'эхо':>5}{'off_lang':>9}{'вне кор.':>9}{'преамб':>8}{'md':>4}"
|
||||
f"{'лат.прог':>9}{'ср.lr':>7}{'ср.sent':>9}{'ср.rt':>7}{'сумма $':>10}")
|
||||
for arm, rs in per.items():
|
||||
ok = [r for r in rs if "error" not in r]
|
||||
if not ok:
|
||||
continue
|
||||
clean = [r for r in ok if not r["echo"]]
|
||||
lr = [r["len_ratio"] for r in clean if r["len_ratio"] is not None]
|
||||
sr = [r["sent_out"] / r["sent_src"] for r in clean if r["sent_src"]]
|
||||
print(f"{arm:<5}{len(ok):>3}{sum(r['echo'] for r in ok):>5}"
|
||||
f"{sum(r['off_language'] for r in ok):>9}"
|
||||
f"{sum(1 for r in clean if not r['in_corridor']):>9}"
|
||||
f"{sum(r['preamble'] for r in ok):>8}{sum(r['markdown'] for r in ok):>4}"
|
||||
f"{sum(r['latin_runs'] for r in ok):>9}"
|
||||
f"{(sum(lr)/len(lr) if lr else 0):>7.2f}{(sum(sr)/len(sr) if sr else 0):>9.2f}"
|
||||
f"{sum(r['reasoning_tokens'] for r in ok)/len(ok):>7.0f}"
|
||||
f"{sum(r['cost_usd'] for r in ok):>10.6f}")
|
||||
|
||||
print("\nпофрагментно (эхо и коридор — по фрагментам, чтобы видеть, ГДЕ именно):")
|
||||
frags = sorted({r["frag"] for rs in per.values() for r in rs})
|
||||
print(f"{'фраг':<6}" + "".join(f"{arm:>18}" for arm in per))
|
||||
for fr in frags:
|
||||
cells = []
|
||||
for arm, rs in per.items():
|
||||
r = next((x for x in rs if x["frag"] == fr), None)
|
||||
if r is None or "error" in r:
|
||||
cells.append(f"{'—':>18}")
|
||||
continue
|
||||
mark = "ЭХО" if r["echo"] else ("OFFLANG" if r["off_language"] else
|
||||
("вне кор." if not r["in_corridor"] else "ok"))
|
||||
cells.append(f"{mark + ' lr=' + str(r['len_ratio']):>18}")
|
||||
print(f"{fr:<6}" + "".join(cells))
|
||||
|
||||
out = PL / "analyze_arms.json"
|
||||
out.write_text(json.dumps({k: [{kk: vv for kk, vv in r.items()
|
||||
if kk not in ("system", "user", "reasoning_content")}
|
||||
for r in v] for k, v in per.items()},
|
||||
ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
print(f"\nсырьё сведено: {out}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
296
eval/promptlang/build_projects.py
Normal file
296
eval/promptlang/build_projects.py
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
#!/usr/bin/env python3
|
||||
"""$0 стройка проектов-проб для пакета «язык промптов (Р6) + fidelity-хвост P4».
|
||||
|
||||
Ничего не вызывает у провайдеров. Собирает под `~/books/gu-zhenren/promptlang/`:
|
||||
|
||||
A-ru / A-en — задача A: КОПИЯ БД холодного прогона (тот же book_id, тот же снапшот ⇒ черновые
|
||||
чекпойнты попадают, волна не перекупается), различие ровно одно — `prompts_root`
|
||||
пар-конфига указывает на дерево с ru- или en-терминологом. Полезная нагрузка роли
|
||||
(кандидаты, KWIC, черновики, батчинг) при этом байт-идентична по построению:
|
||||
она собирается из ОДНОЙ И ТОЙ ЖЕ БД одним и тем же кодом.
|
||||
B-a0/a1/a2 — задача B: ЧИСТЫЕ БД на СВЕЖЕМ срезе (главы 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())
|
||||
278
eval/promptlang/judge_fidelity.py
Normal file
278
eval/promptlang/judge_fidelity.py
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Fidelity-судья хвоста P4 (петля D21 п.6) по нормам рига D30.10/D39.7.
|
||||
|
||||
Вопрос УЗКИЙ и он не «какой перевод лучше»: **стоит ли митигация (постинструкция / few-shot)
|
||||
чего-нибудь по ВЕРНОСТИ**. Поэтому ось одна — верность; гладкость и красота из промпта судьи
|
||||
вычеркнуты явно, иначе судья начнёт ранжировать стиль и вернёт ответ на другой вопрос.
|
||||
|
||||
Нормы рига, соблюдённые здесь:
|
||||
· судья ЧУЖОГО семейства (grok-4.3 против переводов deepseek) — author ≠ reviewer (D13.3);
|
||||
· reasoning ON (дефолт grok = low; явный `none` НЕ шлём — именно он делал судью слепым, D30.1);
|
||||
· ПОЛНЫЕ окна: целиком исходник и целиком оба перевода, без обрезки (урок exp15 Q1b: обрезанное
|
||||
окно родило вердикт-артефакт, который пришлось отзывать);
|
||||
· ОБА порядка (A/B и B/A) — позиционный шум измеряется, а не предполагается;
|
||||
· FLOOR-проход на ИДЕНТИЧНЫХ текстах — пол судейского шума; контраст читается только над ним;
|
||||
· пер-голосовой персист в JSONL + резюм; жёсткий внутренний кап расхода;
|
||||
· span-цитаты с ОБЕИХ сторон: дефект без цитаты вердиктом не считается.
|
||||
|
||||
Выбор единиц — ПРЕ-РЕГИСТРИРОВАННЫЙ и по порядку (chapter, chunk_idx) среди тех, где ВСЕ ТРИ арма
|
||||
дали чистый выход. Никакого отбора по исходу.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from openai import OpenAI
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
|
||||
ARMS_JUDGED = ["a0", "a1", "a2"]
|
||||
# Контраст a0↔a2 СНЯТ с судьи осознанно: few-shot отвергнут ДЕТЕРМИНИРОВАННО (эхо 1/12 +
|
||||
# выход по-английски 1/12 + reasoning ×1.9), и решение по нему судья не двигает. Бюджет
|
||||
# судьи идёт туда, где вопрос ещё открыт, — постинструкция.
|
||||
CONTRASTS = [("a0-a1", "a1")]
|
||||
OUT = PL / "judge"
|
||||
OUT.mkdir(exist_ok=True)
|
||||
load_dotenv(REPO / "eval" / ".env")
|
||||
|
||||
# grok-4.3: $1.25/$2.50 за 1M (models.yaml, prices_checked 2026-07-10); reasoning у xAI АДДИТИВЕН
|
||||
# к completion, поэтому в цену выхода он входит и считается явно.
|
||||
JUDGE = dict(model="grok-4.3", base="https://api.x.ai/v1", key_env="XAI_API_KEY",
|
||||
price_in=1.25, price_out=2.50, max_tokens=8000, temperature=0.0)
|
||||
|
||||
SYS = (
|
||||
"Ты — контролёр ВЕРНОСТИ художественного перевода с китайского на русский. Тебе дают КИТАЙСКИЙ "
|
||||
"исходник и ДВА русских перевода одного и того же фрагмента: A и B.\n"
|
||||
"Оценивай ТОЛЬКО верность: передан ли смысл исходника без ПРОПУСКОВ, ИСКАЖЕНИЙ, ОТСЕБЯТИНЫ и без "
|
||||
"непереведённых кусков. Гладкость, красота, стиль, выбор синонимов и разбивка на абзацы НЕ "
|
||||
"оцениваются вовсе — по этим осям любые различия игнорируй.\n"
|
||||
"Дефект засчитывается ТОЛЬКО с дословными цитатами: что в исходнике и что (или ничего) в переводе.\n"
|
||||
"Если по верности переводы равны — это нормальный и частый ответ: пиши \"tie\".\n"
|
||||
"Ответь СТРОГИМ JSON одной строкой, без текста вокруг и без markdown:\n"
|
||||
'{"winner":"A|B|tie","margin":"clear|slight",'
|
||||
'"defects_A":[{"kind":"omission|distortion|addition|untranslated","zh":"<цитата исходника ≤12 иероглифов>","ru":"<цитата перевода ≤15 слов или пусто>"}],'
|
||||
'"defects_B":[...],"reason":"<=25 слов"}'
|
||||
)
|
||||
|
||||
|
||||
def user(src: str, ta: str, tb: str) -> str:
|
||||
return (f"=== КИТАЙСКИЙ ИСХОДНИК ===\n{src}\n\n=== ПЕРЕВОД A ===\n{ta}\n\n"
|
||||
f"=== ПЕРЕВОД B ===\n{tb}\n\nВерни JSON-вердикт по ВЕРНОСТИ.")
|
||||
|
||||
|
||||
class Ledger:
|
||||
def __init__(self, path: Path, cap: float):
|
||||
self.path, self.cap, self.total = path, cap, 0.0
|
||||
if path.exists():
|
||||
for ln in path.read_text().splitlines():
|
||||
try:
|
||||
self.total += json.loads(ln).get("cost_usd", 0.0)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def add(self, rec: dict) -> None:
|
||||
self.total += rec.get("cost_usd", 0.0)
|
||||
with open(self.path, "a") as f:
|
||||
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
|
||||
|
||||
|
||||
_client = None
|
||||
|
||||
|
||||
def client():
|
||||
global _client
|
||||
if _client is None:
|
||||
key = os.environ.get(JUDGE["key_env"])
|
||||
if not key:
|
||||
sys.exit(f"нет ключа {JUDGE['key_env']}")
|
||||
_client = OpenAI(api_key=key, base_url=JUDGE["base"], timeout=300)
|
||||
return _client
|
||||
|
||||
|
||||
def call(led: Ledger, sys_p: str, usr: str, tag: str) -> tuple[str, dict]:
|
||||
if led.total >= led.cap:
|
||||
sys.exit(f"СТОП: кап судьи ${led.cap} достигнут (потрачено ${led.total:.4f})")
|
||||
for attempt, back in enumerate([5, 15, 40, None]):
|
||||
try:
|
||||
r = client().chat.completions.create(
|
||||
model=JUDGE["model"],
|
||||
messages=[{"role": "system", "content": sys_p}, {"role": "user", "content": usr}],
|
||||
temperature=JUDGE["temperature"], max_tokens=JUDGE["max_tokens"])
|
||||
ch = r.choices[0]
|
||||
txt = (ch.message.content or "").strip()
|
||||
u = r.usage
|
||||
pt = getattr(u, "prompt_tokens", 0) or 0
|
||||
ct = getattr(u, "completion_tokens", 0) or 0
|
||||
det = getattr(u, "completion_tokens_details", None)
|
||||
rt = (getattr(det, "reasoning_tokens", 0) if det else 0) or 0
|
||||
# xAI: reasoning АДДИТИВЕН к completion. Если провайдер уже включил его в completion,
|
||||
# двойного счёта не будет — берём max, а не сумму, и это осознанно консервативно вверх
|
||||
# только на входе в кап, но в отчёт идёт и то и другое.
|
||||
out_billed = ct if rt and rt <= ct else ct + rt
|
||||
cost = pt * JUDGE["price_in"] / 1e6 + out_billed * JUDGE["price_out"] / 1e6
|
||||
rec = dict(tag=tag, judge=JUDGE["model"], finish=ch.finish_reason, prompt_tok=pt,
|
||||
completion_tok=ct, reasoning_tok=rt, out_billed=out_billed,
|
||||
cost_usd=cost, empty=(not txt))
|
||||
led.add(rec)
|
||||
if ch.finish_reason != "stop":
|
||||
print(f" ⚠ {tag}: finish={ch.finish_reason!r} (аномалия — в отчёт)")
|
||||
return txt, rec
|
||||
except Exception as e:
|
||||
if back is None:
|
||||
led.add(dict(tag=tag, error=str(e)[:300], cost_usd=0.0))
|
||||
print(f" [FAIL {tag}] {str(e)[:180]}")
|
||||
return "", {"error": str(e)[:300]}
|
||||
print(f" [retry {tag}] {str(e)[:90]} — сон {back}с")
|
||||
time.sleep(back)
|
||||
return "", {}
|
||||
|
||||
|
||||
def parse(txt: str):
|
||||
if not txt:
|
||||
return None
|
||||
s = txt.strip().strip("`")
|
||||
i, j = s.find("{"), s.rfind("}")
|
||||
if i < 0 or j < 0:
|
||||
return None
|
||||
try:
|
||||
return json.loads(s[i:j + 1])
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def load_units():
|
||||
"""Единицы, где ВСЕ судимые армы дали чистый выход (не эхо, не пусто). Порядок — по имени
|
||||
фрагмента, то есть по тексту книги. Никакого отбора по исходу."""
|
||||
raw = PL / "armsraw"
|
||||
src = {f["id"]: f["text"] for f in json.loads((raw / "fragments.json").read_text(encoding="utf-8"))}
|
||||
texts, clean = {}, None
|
||||
for arm in ARMS_JUDGED:
|
||||
ok = set()
|
||||
for f in sorted(raw.glob(f"{arm}-f*.json")):
|
||||
j = json.loads(f.read_text(encoding="utf-8"))
|
||||
if "error" in j:
|
||||
continue
|
||||
k = j["frag"]
|
||||
j["src_text"] = src[k]
|
||||
texts[(arm, k)] = j
|
||||
# исключаем не только эхо, но и выход не в целевом письме: это уже пойман
|
||||
# ДЕТЕРМИНИРОВАННЫМ прибором, судья тут ответил бы на другой вопрос
|
||||
import unicodedata as _u
|
||||
cyr = sum(1 for ch in j["content"] if _u.name(ch, "").startswith("CYRILLIC"))
|
||||
lat = sum(1 for ch in j["content"] if _u.name(ch, "").startswith("LATIN"))
|
||||
off_lang = (cyr + lat) > 0 and cyr / (cyr + lat) < 0.5
|
||||
if not j["echo"] and not off_lang and j["content_chars"] > 0:
|
||||
ok.add(k)
|
||||
clean = ok if clean is None else (clean & ok)
|
||||
return sorted(clean or []), texts
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--cap", type=float, required=True, help="жёсткий кап расхода судьи, $")
|
||||
ap.add_argument("--units", type=int, default=0, help="сколько единиц судить (0 = план)")
|
||||
ap.add_argument("--probe", action="store_true", help="одна живая калибровка цены")
|
||||
ap.add_argument("--floor", action="store_true", help="floor-проход на идентичных текстах")
|
||||
ap.add_argument("--aggregate", action="store_true", help="$0 пересчёт из JSONL")
|
||||
a = ap.parse_args()
|
||||
|
||||
led = Ledger(OUT / "ledger.jsonl", a.cap)
|
||||
votes_path = OUT / "votes.jsonl"
|
||||
seen = {}
|
||||
if votes_path.exists():
|
||||
for ln in votes_path.read_text().splitlines():
|
||||
try:
|
||||
v = json.loads(ln)
|
||||
seen[v["vote_id"]] = v
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
units, texts = load_units()
|
||||
print(f"чистых единиц во ВСЕХ трёх армах: {len(units)} → {units}")
|
||||
|
||||
if a.aggregate:
|
||||
aggregate(seen)
|
||||
return 0
|
||||
|
||||
def vote(vid, kind, unit, contrast, order, src, ta, tb, label_a, label_b):
|
||||
if vid in seen:
|
||||
return
|
||||
txt, rec = call(led, SYS, user(src, ta, tb), vid)
|
||||
v = parse(txt) or {}
|
||||
row = dict(vote_id=vid, kind=kind, unit=str(unit), contrast=contrast,
|
||||
order=order, label_A=label_a, label_B=label_b, winner=v.get("winner"),
|
||||
margin=v.get("margin"), defects_A=v.get("defects_A"), defects_B=v.get("defects_B"),
|
||||
reason=v.get("reason"), finish=rec.get("finish"), raw=txt[:1200],
|
||||
cost_usd=rec.get("cost_usd"))
|
||||
with open(votes_path, "a") as f:
|
||||
f.write(json.dumps(row, ensure_ascii=False) + "\n")
|
||||
seen[vid] = row
|
||||
print(f" {vid}: winner={v.get('winner')} margin={v.get('margin')} "
|
||||
f"defA={len(v.get('defects_A') or [])} defB={len(v.get('defects_B') or [])} "
|
||||
f"${led.total:.4f}")
|
||||
|
||||
if a.probe:
|
||||
u = units[0]
|
||||
c0 = texts[("a0", u)]
|
||||
vote(f"probe::{u}", "probe", u, "a0-a0", "AB",
|
||||
c0["src_text"], c0["content"], c0["content"], "a0", "a0")
|
||||
print(f"\nКАЛИБРОВКА: ${led.total:.5f} за вызов")
|
||||
return 0
|
||||
|
||||
if a.floor:
|
||||
for u in units[:1]:
|
||||
c0 = texts[("a0", u)]
|
||||
for order in ("AB", "BA"):
|
||||
vote(f"floor::{u}::{order}", "floor", u, "a0-a0", order,
|
||||
c0["src_text"], c0["content"], c0["content"], "a0", "a0")
|
||||
return 0
|
||||
|
||||
n = a.units
|
||||
if n <= 0:
|
||||
print("план: --units N (N единиц × 2 контраста × 2 порядка = 4N вызовов)")
|
||||
return 0
|
||||
for u in units[:n]:
|
||||
for contrast, arm in CONTRASTS:
|
||||
base, mit = texts[("a0", u)], texts[(arm, u)]
|
||||
for order in ("AB", "BA"):
|
||||
ta, tb, la, lb = ((base["content"], mit["content"], "a0", arm) if order == "AB"
|
||||
else (mit["content"], base["content"], arm, "a0"))
|
||||
vote(f"{contrast}::{u}::{order}", "contrast", u, contrast, order,
|
||||
base["src_text"], ta, tb, la, lb)
|
||||
aggregate(seen)
|
||||
print(f"\nПОТРАЧЕНО СУДЬЁЙ: ${led.total:.5f} из капа ${a.cap}")
|
||||
return 0
|
||||
|
||||
|
||||
def aggregate(seen: dict) -> None:
|
||||
from collections import defaultdict
|
||||
by = defaultdict(list)
|
||||
for v in seen.values():
|
||||
by[(v["kind"], v.get("contrast"))].append(v)
|
||||
print(f"\n{'вид':<10} {'контраст':<8} {'голосов':>8} {'база':>6} {'митиг.':>7} {'ничья':>7} {'деф.база':>9} {'деф.митиг':>10}")
|
||||
for (kind, contrast), vs in sorted(by.items()):
|
||||
base = mit = tie = 0
|
||||
db = dm = 0
|
||||
for v in vs:
|
||||
w = v.get("winner")
|
||||
la, lb = v.get("label_A"), v.get("label_B")
|
||||
win = la if w == "A" else lb if w == "B" else None
|
||||
if win is None:
|
||||
tie += 1
|
||||
elif win == "a0":
|
||||
base += 1
|
||||
else:
|
||||
mit += 1
|
||||
db += len(v.get("defects_A") or []) if la == "a0" else len(v.get("defects_B") or [])
|
||||
dm += len(v.get("defects_B") or []) if la == "a0" else len(v.get("defects_A") or [])
|
||||
print(f"{kind:<10} {str(contrast):<8} {len(vs):>8} {base:>6} {mit:>7} {tie:>7} {db:>9} {dm:>10}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
277
eval/promptlang/judge_p4.py
Normal file
277
eval/promptlang/judge_p4.py
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Fidelity-судья на ГОТОВЫХ парах база↔митигация P4 — деливерабл (1) промта сессии, исполненный там,
|
||||
куда промт и указывал: `~/books/gu-zhenren/research15-probes/`.
|
||||
|
||||
Генерация этих пар оплачена 09.07. Здесь покупаются ТОЛЬКО судейские голоса.
|
||||
|
||||
Нормы рига D30.10/D39.7, соблюдённые:
|
||||
· судья ЧУЖОГО семейства к автору перевода (D13.3) — маршрутизация по семейству ПАРЫ:
|
||||
пары grok-none → судит `deepseek-v4-pro` (кросс-семейно, дешевле grok в 3 раза)
|
||||
пары deepseek → судит `grok-4.3` (кросс-семейно)
|
||||
⚠ `deepseek-v4-pro` вендором 31.07 НЕ обновлялся («The DeepSeek-V4-Pro API … unchanged»),
|
||||
поэтому как прибор он на прежнем поведении — в отличие от flash.
|
||||
· reasoning ON у обоих (dspro — thinking по умолчанию; grok — дефолт low, `none` НЕ шлём);
|
||||
· ПОЛНЫЕ окна без обрезки (урок exp15 Q1b);
|
||||
· ОБА порядка на каждой паре;
|
||||
· FLOOR-проход на ИДЕНТИЧНЫХ текстах — отдельно для КАЖДОГО судьи;
|
||||
· пер-голосовой персист + резюм; жёсткий кап расхода; ось — ТОЛЬКО верность.
|
||||
|
||||
`--plan` печатает смету по ФАКТИЧЕСКИМ размерам сырья и не делает ни одного вызова.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import unicodedata
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from openai import OpenAI
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
RAW = Path.home() / "books" / "gu-zhenren" / "research15-probes" / "probes" / "raw"
|
||||
OUT = Path.home() / "books" / "gu-zhenren" / "promptlang" / "judge_p4"
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
load_dotenv(REPO / "eval" / ".env")
|
||||
|
||||
JUDGES = {
|
||||
# цены — models.yaml, prices_checked 2026-07-10
|
||||
"dspro": dict(model="deepseek-v4-pro", base="https://api.deepseek.com/v1", key_env="DEEPSEEK_API_KEY",
|
||||
price_in=0.435, price_out=0.87, max_tokens=16000, reasoning="subset", temperature=0.0),
|
||||
"grok": dict(model="grok-4.3", base="https://api.x.ai/v1", key_env="XAI_API_KEY",
|
||||
price_in=1.25, price_out=2.50, max_tokens=8000, reasoning="additive", temperature=0.0),
|
||||
}
|
||||
JUDGE_FOR = {"grok": "dspro", "deepseek": "grok"} # семейство ПАРЫ → судья ЧУЖОГО семейства
|
||||
|
||||
ARM_NAME = {"G0": "база", "G1": "инстр-после", "G2": "few-shot", "G4": "комбо",
|
||||
"D0": "база", "D1": "инстр-после", "D2": "few-shot", "DP": "префилл"}
|
||||
|
||||
SYS = (
|
||||
"Ты — контролёр ВЕРНОСТИ художественного перевода с китайского на русский. Тебе дают КИТАЙСКИЙ "
|
||||
"исходник и ДВА русских перевода одного и того же фрагмента: A и B.\n"
|
||||
"Оценивай ТОЛЬКО верность: передан ли смысл исходника без ПРОПУСКОВ, ИСКАЖЕНИЙ, ОТСЕБЯТИНЫ и без "
|
||||
"непереведённых кусков. Гладкость, красота, стиль, выбор синонимов, разбивка на абзацы и длина НЕ "
|
||||
"оцениваются вовсе — по этим осям любые различия игнорируй.\n"
|
||||
"Отдельно отмечай ДОБАВЛЕНИЯ, которых нет в исходнике (сноски переводчика, пояснения в скобках, "
|
||||
"заголовки) — это дефект верности класса addition.\n"
|
||||
"Дефект засчитывается ТОЛЬКО с дословными цитатами: что в исходнике и что (или ничего) в переводе.\n"
|
||||
"Если по верности переводы равны — это нормальный и частый ответ: пиши \"tie\".\n"
|
||||
"Ответь СТРОГИМ JSON одной строкой, без текста вокруг и без markdown:\n"
|
||||
'{"winner":"A|B|tie","margin":"clear|slight",'
|
||||
'"defects_A":[{"kind":"omission|distortion|addition|untranslated","zh":"<цитата исходника ≤12 иероглифов>","ru":"<цитата перевода ≤15 слов или пусто>"}],'
|
||||
'"defects_B":[...],"reason":"<=25 слов"}'
|
||||
)
|
||||
|
||||
|
||||
def cjk_share(s: str) -> float:
|
||||
if not s:
|
||||
return 1.0
|
||||
return sum(1 for c in s if "CJK UNIFIED" in (unicodedata.name(c, "") or "")) / len(s)
|
||||
|
||||
|
||||
def load_pairs() -> list[dict]:
|
||||
"""Пары база↔митигация: обе стороны — реальный перевод (не эхо, не пусто). Порядок детерминирован."""
|
||||
recs = {}
|
||||
for f in sorted(RAW.glob("echo-*.json")):
|
||||
d = json.load(open(f))
|
||||
t = d.get("text") or ""
|
||||
u = d.get("user") or ""
|
||||
src = u.split("Переведи следующий фрагмент:\n\n", 1)[-1].split("\n\nНапоминание:")[0]
|
||||
recs[d["tag"]] = dict(text=t, src=src, echo=cjk_share(t) > 0.15)
|
||||
groups: dict[tuple[str, str], dict] = collections.defaultdict(dict)
|
||||
for tag, r in recs.items():
|
||||
m = re.match(r"echo-grok-(gu-\d+)-(G\d)$", tag)
|
||||
if m:
|
||||
groups[("grok", m.group(1))][m.group(2)] = r
|
||||
continue
|
||||
m = re.match(r"echo-ds-gu008-(D\w+)-s(\d)$", tag)
|
||||
if m:
|
||||
groups[("deepseek", "gu008-s" + m.group(2))][m.group(1)] = r
|
||||
pairs = []
|
||||
for (fam, frag), arms in sorted(groups.items()):
|
||||
bk = "G0" if fam == "grok" else "D0"
|
||||
base = arms.get(bk)
|
||||
if not base or base["echo"] or not base["text"]:
|
||||
continue
|
||||
for a in ("G1", "G2", "G4", "D1", "D2", "DP"):
|
||||
m = arms.get(a)
|
||||
if not m or m["echo"] or not m["text"]:
|
||||
continue
|
||||
pairs.append(dict(fam=fam, frag=frag, arm=a, arm_name=ARM_NAME[a],
|
||||
src=base["src"], base=base["text"], mit=m["text"]))
|
||||
return pairs
|
||||
|
||||
|
||||
def user_msg(src: str, ta: str, tb: str) -> str:
|
||||
return (f"=== КИТАЙСКИЙ ИСХОДНИК ===\n{src}\n\n=== ПЕРЕВОД A ===\n{ta}\n\n"
|
||||
f"=== ПЕРЕВОД B ===\n{tb}\n\nВерни JSON-вердикт по ВЕРНОСТИ.")
|
||||
|
||||
|
||||
def est_tokens(s: str) -> int:
|
||||
"""Грубая оценка ДЛЯ СМЕТЫ: ханьцы ≈1 ток/симв, кириллица ≈0.4 ток/симв (калибровка exp01)."""
|
||||
han = sum(1 for c in s if "CJK UNIFIED" in (unicodedata.name(c, "") or ""))
|
||||
return int(han + (len(s) - han) * 0.4)
|
||||
|
||||
|
||||
class Ledger:
|
||||
def __init__(self, path: Path, cap: float):
|
||||
self.path, self.cap, self.total = path, cap, 0.0
|
||||
if path.exists():
|
||||
for ln in path.read_text().splitlines():
|
||||
try:
|
||||
self.total += json.loads(ln).get("cost_usd", 0.0)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def add(self, rec: dict) -> None:
|
||||
self.total += rec.get("cost_usd", 0.0)
|
||||
with open(self.path, "a") as f:
|
||||
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
|
||||
|
||||
|
||||
_clients: dict[str, OpenAI] = {}
|
||||
|
||||
|
||||
def client(jk: str) -> OpenAI:
|
||||
if jk not in _clients:
|
||||
cfg = JUDGES[jk]
|
||||
key = os.environ.get(cfg["key_env"])
|
||||
if not key:
|
||||
sys.exit(f"нет ключа {cfg['key_env']}")
|
||||
_clients[jk] = OpenAI(api_key=key, base_url=cfg["base"], timeout=600)
|
||||
return _clients[jk]
|
||||
|
||||
|
||||
def call(led: Ledger, jk: str, usr: str, tag: str) -> tuple[str, dict]:
|
||||
cfg = JUDGES[jk]
|
||||
if led.total >= led.cap:
|
||||
sys.exit(f"СТОП: кап ${led.cap} достигнут (потрачено ${led.total:.4f}) — пинг владельцу")
|
||||
for back in (5, 20, 60, None):
|
||||
try:
|
||||
r = client(jk).chat.completions.create(
|
||||
model=cfg["model"], temperature=cfg["temperature"], max_tokens=cfg["max_tokens"],
|
||||
messages=[{"role": "system", "content": SYS}, {"role": "user", "content": usr}])
|
||||
ch = r.choices[0]
|
||||
txt = (ch.message.content or "").strip()
|
||||
u = r.usage
|
||||
pt, ct = u.prompt_tokens or 0, u.completion_tokens or 0
|
||||
det = getattr(u, "completion_tokens_details", None)
|
||||
rt = (getattr(det, "reasoning_tokens", 0) if det else 0) or 0
|
||||
billed = ct + rt if cfg["reasoning"] == "additive" and rt > ct else ct
|
||||
cost = pt * cfg["price_in"] / 1e6 + billed * cfg["price_out"] / 1e6
|
||||
rec = dict(tag=tag, judge=cfg["model"], finish=ch.finish_reason, prompt_tok=pt,
|
||||
completion_tok=ct, reasoning_tok=rt, cost_usd=cost, empty=not txt)
|
||||
led.add(rec)
|
||||
if ch.finish_reason != "stop":
|
||||
print(f" ⚠ {tag}: finish={ch.finish_reason!r}")
|
||||
return txt, rec
|
||||
except Exception as e:
|
||||
if back is None:
|
||||
led.add(dict(tag=tag, error=str(e)[:300], cost_usd=0.0))
|
||||
return "", {"error": str(e)[:300]}
|
||||
print(f" [retry {tag}] {str(e)[:90]} — сон {back}с")
|
||||
time.sleep(back)
|
||||
return "", {}
|
||||
|
||||
|
||||
def parse(txt: str):
|
||||
if not txt:
|
||||
return None
|
||||
s = txt.strip().strip("`")
|
||||
i, j = s.find("{"), s.rfind("}")
|
||||
try:
|
||||
return json.loads(s[i:j + 1]) if i >= 0 and j > i else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--plan", action="store_true", help="$0 смета по фактическим размерам сырья")
|
||||
ap.add_argument("--cap", type=float, help="жёсткий кап расхода, $ (обязателен для прогона)")
|
||||
ap.add_argument("--only", default="", help="ограничить семейством: grok|deepseek")
|
||||
a = ap.parse_args()
|
||||
|
||||
pairs = load_pairs()
|
||||
if a.only:
|
||||
pairs = [p for p in pairs if p["fam"] == a.only]
|
||||
by_fam = collections.Counter(p["fam"] for p in pairs)
|
||||
print(f"ПАР база↔митигация: {len(pairs)} · по семействам {dict(by_fam)}")
|
||||
for p in pairs[:3]:
|
||||
print(f" пример: {p['fam']}/{p['frag']}/{p['arm_name']} — исходник {len(p['src'])} симв, "
|
||||
f"база {len(p['base'])}, митигация {len(p['mit'])}")
|
||||
|
||||
if a.plan:
|
||||
total = 0.0
|
||||
print(f"\n{'семейство':<10}{'судья':<20}{'пар':>5}{'голосов':>9}{'ср.вход ток':>13}{'смета $':>10}")
|
||||
for fam in sorted(by_fam):
|
||||
jk = JUDGE_FOR[fam]
|
||||
cfg = JUDGES[jk]
|
||||
ps = [p for p in pairs if p["fam"] == fam]
|
||||
ins = [est_tokens(user_msg(p["src"], p["base"], p["mit"])) + est_tokens(SYS) for p in ps]
|
||||
avg_in = sum(ins) / len(ins)
|
||||
out_est = 1600 # вердикт ~400 ток + размышление ~1200 (наблюдение по голосам 31.07)
|
||||
votes = len(ps) * 2 + 2 # оба порядка + floor-проход на идентичных текстах
|
||||
cost = votes * (avg_in * cfg["price_in"] / 1e6 + out_est * cfg["price_out"] / 1e6)
|
||||
total += cost
|
||||
print(f"{fam:<10}{cfg['model']:<20}{len(ps):>5}{votes:>9}{avg_in:>13.0f}{cost:>10.4f}")
|
||||
print(f"{'ИТОГО':<10}{'':<20}{len(pairs):>5}{len(pairs)*2+4:>9}{'':>13}{total:>10.4f}")
|
||||
print("\nдопущения сметы названы: вход — оценка exp01 (хань≈1 ток/симв, кириллица≈0.4),")
|
||||
print("выход 1600 ток/голос — по 7 фактическим голосам 31.07. Реальный расход пишется в леджер.")
|
||||
print("ВЫЗОВОВ НЕ СДЕЛАНО.")
|
||||
return 0
|
||||
|
||||
if a.cap is None:
|
||||
print("для прогона нужен --cap (жёсткий потолок расхода)", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
led = Ledger(OUT / "ledger.jsonl", a.cap)
|
||||
votes_path = OUT / "votes.jsonl"
|
||||
seen = set()
|
||||
if votes_path.exists():
|
||||
for ln in votes_path.read_text().splitlines():
|
||||
try:
|
||||
seen.add(json.loads(ln)["vote_id"])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def vote(vid, kind, p, order, ta, tb, la, lb):
|
||||
if vid in seen:
|
||||
return
|
||||
jk = JUDGE_FOR[p["fam"]]
|
||||
txt, rec = call(led, jk, user_msg(p["src"], ta, tb), vid)
|
||||
v = parse(txt) or {}
|
||||
row = dict(vote_id=vid, kind=kind, judge=JUDGES[jk]["model"], fam=p["fam"], frag=p["frag"],
|
||||
arm=p["arm"], arm_name=p["arm_name"], order=order, label_A=la, label_B=lb,
|
||||
winner=v.get("winner"), margin=v.get("margin"), defects_A=v.get("defects_A"),
|
||||
defects_B=v.get("defects_B"), reason=v.get("reason"), finish=rec.get("finish"),
|
||||
raw=txt[:1200], cost_usd=rec.get("cost_usd"))
|
||||
with open(votes_path, "a") as f:
|
||||
f.write(json.dumps(row, ensure_ascii=False) + "\n")
|
||||
seen.add(vid)
|
||||
print(f" {vid}: {v.get('winner')} defБаза={len(v.get('defects_A') or []) if la=='база' else len(v.get('defects_B') or [])} "
|
||||
f"defМитиг={len(v.get('defects_B') or []) if la=='база' else len(v.get('defects_A') or [])} ${led.total:.4f}")
|
||||
|
||||
# FLOOR: идентичные тексты, по одному на семейство → пол шума КАЖДОГО судьи
|
||||
for fam in sorted(by_fam):
|
||||
p = next(x for x in pairs if x["fam"] == fam)
|
||||
for order in ("AB", "BA"):
|
||||
vote(f"floor::{fam}::{order}", "floor", p, order, p["base"], p["base"], "база", "база")
|
||||
|
||||
for p in pairs:
|
||||
for order in ("AB", "BA"):
|
||||
ta, tb, la, lb = ((p["base"], p["mit"], "база", p["arm_name"]) if order == "AB"
|
||||
else (p["mit"], p["base"], p["arm_name"], "база"))
|
||||
vote(f"{p['fam']}::{p['frag']}::{p['arm']}::{order}", "contrast", p, order, ta, tb, la, lb)
|
||||
|
||||
print(f"\nПОТРАЧЕНО: ${led.total:.5f} из капа ${a.cap}; голоса — {votes_path}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
115
eval/promptlang/p4_pairs.py
Normal file
115
eval/promptlang/p4_pairs.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#!/usr/bin/env python3
|
||||
"""$0 детерминированная батарея верности на ГОТОВЫХ парах база↔митигация P4 (09.07).
|
||||
|
||||
Это тот источник, на который промт сессии указывал прямо: `~/books/gu-zhenren/research15-probes/`.
|
||||
Генерация за них уже оплачена 09.07 — здесь не тратится ни цента.
|
||||
|
||||
Судья отвечает на «исказила ли митигация СМЫСЛ». Половина этого вопроса — про ПРОПУСКИ и обрывы —
|
||||
решается детерминированно и бесплатно: митигация, которая режет текст, видна по длине и по числу
|
||||
предложений относительно ОДНОГО И ТОГО ЖЕ исходника. Оставшаяся половина (искажение при сохранённом
|
||||
объёме) без судьи не берётся, и это здесь честно помечено, а не замазано.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
import json
|
||||
import re
|
||||
import statistics
|
||||
import sys
|
||||
import unicodedata
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
sys.path.insert(0, str(REPO / "eval"))
|
||||
from refusal_bench import split_sentences
|
||||
|
||||
RAW = Path.home() / "books" / "gu-zhenren" / "research15-probes" / "probes" / "raw"
|
||||
ARM_NAME = {"G0": "база", "G1": "инстр.ПОСЛЕ", "G2": "few-shot", "G3": "яз.констрейнт", "G4": "комбо",
|
||||
"D0": "база", "D1": "инстр.ПОСЛЕ", "D2": "few-shot", "DP": "префилл"}
|
||||
|
||||
|
||||
def cjk_share(s: str) -> float:
|
||||
if not s:
|
||||
return 1.0
|
||||
return sum(1 for c in s if "CJK UNIFIED" in (unicodedata.name(c, "") or "")) / len(s)
|
||||
|
||||
|
||||
def load():
|
||||
out = {}
|
||||
for f in sorted(RAW.glob("echo-*.json")):
|
||||
d = json.load(open(f))
|
||||
t = d.get("text") or ""
|
||||
# исходник фрагмента лежит в user-сообщении между шапкой и (для арма G1) постинструкцией
|
||||
u = d.get("user") or ""
|
||||
src = u.split("Переведи следующий фрагмент:\n\n", 1)[-1]
|
||||
src = src.split("\n\nНапоминание:")[0]
|
||||
out[d["tag"]] = dict(tag=d["tag"], text=t, src=src, cjk=cjk_share(t), echo=cjk_share(t) > 0.15)
|
||||
return out
|
||||
|
||||
|
||||
def metrics(src: str, out: str) -> dict:
|
||||
src_ns = len(re.sub(r"\s", "", src))
|
||||
out_ns = len(re.sub(r"\s", "", out))
|
||||
return dict(chars=len(out), len_ratio=round(out_ns / src_ns, 3) if src_ns else None,
|
||||
sent_src=len(split_sentences(src)), sent_out=len(split_sentences(out)),
|
||||
cjk=round(cjk_share(out), 4))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
d = load()
|
||||
groups = collections.defaultdict(dict)
|
||||
for tag, r in d.items():
|
||||
m = re.match(r"echo-grok-(gu-\d+)-(G\d)$", tag)
|
||||
if m:
|
||||
groups[("grok", m.group(1))][m.group(2)] = r
|
||||
continue
|
||||
m = re.match(r"echo-ds-gu008-(D\w+)-s(\d)$", tag)
|
||||
if m:
|
||||
groups[("deepseek", "gu-008-s" + m.group(2))][m.group(1)] = r
|
||||
|
||||
print("=== ПАРЫ база↔митигация из P4 (генерация оплачена 09.07; здесь $0) ===\n")
|
||||
print(f"{'семейство':<9}{'фрагмент':<14}{'арм':<14}{'len_ratio':>10}{'предл. вых/исх':>16}"
|
||||
f"{'симв':>7}{'Δlen_ratio к базе':>19}")
|
||||
rows = []
|
||||
for (fam, frag), arms in sorted(groups.items()):
|
||||
base_key = "G0" if fam == "grok" else "D0"
|
||||
base = arms.get(base_key)
|
||||
if not base or base["echo"] or not base["text"]:
|
||||
continue
|
||||
bm = metrics(base["src"], base["text"])
|
||||
print(f"{fam:<9}{frag:<14}{ARM_NAME[base_key]:<14}{bm['len_ratio']:>10}"
|
||||
f"{str(bm['sent_out']) + '/' + str(bm['sent_src']):>16}{bm['chars']:>7}{'—':>19}")
|
||||
for a in ("G1", "G2", "G4", "D1", "D2", "DP"):
|
||||
m = arms.get(a)
|
||||
if not m or m["echo"] or not m["text"]:
|
||||
continue
|
||||
mm = metrics(base["src"], m["text"])
|
||||
delta = mm["len_ratio"] - bm["len_ratio"]
|
||||
rows.append(dict(fam=fam, frag=frag, arm=a, d_len=delta,
|
||||
d_sent=mm["sent_out"] - bm["sent_out"],
|
||||
base_lr=bm["len_ratio"], mit_lr=mm["len_ratio"]))
|
||||
print(f"{'':<9}{'':<14}{ARM_NAME[a]:<14}{mm['len_ratio']:>10}"
|
||||
f"{str(mm['sent_out']) + '/' + str(mm['sent_src']):>16}{mm['chars']:>7}{delta:>+19.3f}")
|
||||
print()
|
||||
|
||||
print(f"ВСЕГО ПАР: {len(rows)}\n")
|
||||
print("=== СВОДКА ПО МИТИГАЦИИ: режет ли она текст относительно базы на ТОМ ЖЕ исходнике ===")
|
||||
print(f"{'митигация':<16}{'пар':>5}{'медиана Δlen_ratio':>21}{'пар с Δ<-0.15':>15}"
|
||||
f"{'пар с Δ>+0.15':>15}{'медиана Δпредл.':>17}")
|
||||
for a in ("G1", "G2", "G4", "D1", "D2", "DP"):
|
||||
rs = [r for r in rows if r["arm"] == a]
|
||||
if not rs:
|
||||
continue
|
||||
dl = [r["d_len"] for r in rs]
|
||||
ds = [r["d_sent"] for r in rs]
|
||||
print(f"{ARM_NAME[a]:<16}{len(rs):>5}{statistics.median(dl):>+21.3f}"
|
||||
f"{sum(1 for x in dl if x < -0.15):>15}{sum(1 for x in dl if x > 0.15):>15}"
|
||||
f"{statistics.median(ds):>+17.1f}")
|
||||
print("\nЧТЕНИЕ: Δlen_ratio < −0.15 = митигированный перевод заметно КОРОЧЕ базы на том же")
|
||||
print("исходнике — подпись пропуска. Δ > +0.15 — многословнее (отсебятина или развёрнутость).")
|
||||
print("⚠ Ось «искажение при сохранённом объёме» этим прибором НЕ берётся — только судьёй.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
171
eval/promptlang/run_arms.py
Normal file
171
eval/promptlang/run_arms.py
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Армы задачи B (анти-эхо, D21 п.6) на СЫРОМ проводе — в той конфигурации, в которой боевой канал
|
||||
31.07 вообще отвечает.
|
||||
|
||||
Почему не через движок. После вендор-обновления `deepseek-v4-flash` → `DeepSeek-V4-Flash-0731`
|
||||
боевая форма (max_tokens 8000, thinking по умолчанию) отдаёт ПУСТОЙ `content` (§2 отчёта). Единственная
|
||||
ручка, которая её чинит, — `reasoning_effort:"low"`, а её движок для deepseek слать НЕ МОЖЕТ и не
|
||||
должен: `config.echoMineViolation` (`models.go:276-281`) запрещает эхо-склонному провайдеру любой
|
||||
reasoning-контроль, кроме `none|mandatory`. Гейт прав — проба показала, что `low` эхо-мину
|
||||
пере-вооружает. Значит замер идёт мимо движка, как шли пробы P4, и это оговаривается вслух.
|
||||
|
||||
Инварианты замера: фрагменты и параметры БАЙТ-ОДИНАКОВЫ во всех армах, различается ровно промпт.
|
||||
Рендер — реплика движкового (`wire_probe.render`), сверенная по `prompt_tokens` с движковым вызовом
|
||||
на общем чанке (1679 = 1679).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import unicodedata
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from openai import OpenAI
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
|
||||
sys.path.insert(0, str(REPO / "eval" / "promptlang"))
|
||||
sys.path.insert(0, str(REPO / "eval"))
|
||||
from wire_probe import render # тот же рендер, что уже сверен с движком по prompt_tokens
|
||||
from refusal_bench import split_sentences
|
||||
|
||||
load_dotenv(REPO / "eval" / ".env")
|
||||
PRICE_IN, PRICE_CACHED, PRICE_OUT = 0.14, 0.0028, 0.28
|
||||
|
||||
|
||||
def is_cjk(ch: str) -> bool:
|
||||
n = unicodedata.name(ch, "")
|
||||
return ("CJK UNIFIED IDEOGRAPH" in n or "CJK COMPATIBILITY IDEOGRAPH" in n
|
||||
or n.startswith("HIRAGANA ") or n.startswith("KATAKANA "))
|
||||
|
||||
|
||||
def cjk_share(s: str) -> float:
|
||||
return sum(1 for c in s if is_cjk(c)) / len(s) if s else 0.0
|
||||
|
||||
|
||||
def fragments(path: Path, n: int, target: int) -> list[dict]:
|
||||
"""Детерминированная нарезка свежего среза на n фрагментов по границам абзацев.
|
||||
Одна и та же для всех армов по построению — арму нечего выбирать."""
|
||||
text = path.read_text(encoding="gb18030")
|
||||
paras = [p for p in text.split("\n") if p.strip()]
|
||||
out, cur = [], []
|
||||
for p in paras:
|
||||
cur.append(p)
|
||||
if sum(len(x) for x in cur) >= target:
|
||||
out.append("\n".join(cur))
|
||||
cur = []
|
||||
if len(out) == n:
|
||||
break
|
||||
if cur and len(out) < n:
|
||||
out.append("\n".join(cur))
|
||||
return [{"id": f"f{i:02d}", "text": t} for i, t in enumerate(out[:n])]
|
||||
|
||||
|
||||
def call(client, arm: str, frag: dict, cap: int, temp: float, effort: str, raw_dir: Path) -> dict:
|
||||
tag = f"{arm}-{frag['id']}"
|
||||
fp = raw_dir / f"{tag}.json"
|
||||
if fp.exists():
|
||||
return json.loads(fp.read_text(encoding="utf-8"))
|
||||
system, user = render(arm, frag["text"])
|
||||
t0 = time.time()
|
||||
kw = dict(model="deepseek-v4-flash", max_tokens=cap, temperature=temp,
|
||||
messages=[{"role": "system", "content": system}, {"role": "user", "content": user}])
|
||||
if effort:
|
||||
kw["extra_body"] = {"reasoning_effort": effort}
|
||||
try:
|
||||
r = client.chat.completions.create(**kw)
|
||||
except Exception as e: # аномалия печатается и персистится, диагноз — по вендор-доке
|
||||
rec = {"tag": tag, "arm": arm, "frag": frag["id"], "error": str(e)[:400], "cost_usd": 0.0}
|
||||
fp.write_text(json.dumps(rec, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
return rec
|
||||
ch = r.choices[0]
|
||||
content = ch.message.content or ""
|
||||
reasoning = getattr(ch.message, "reasoning_content", None) or ""
|
||||
u = r.usage
|
||||
pt, ct = u.prompt_tokens or 0, u.completion_tokens or 0
|
||||
det = getattr(u, "completion_tokens_details", None)
|
||||
rt = (getattr(det, "reasoning_tokens", 0) if det else 0) or 0
|
||||
cached = getattr(getattr(u, "prompt_tokens_details", None), "cached_tokens", 0) or 0
|
||||
cost = (pt - cached) / 1e6 * PRICE_IN + cached / 1e6 * PRICE_CACHED + ct / 1e6 * PRICE_OUT
|
||||
src_ns = len(re.sub(r"\s", "", frag["text"]))
|
||||
out_ns = len(re.sub(r"\s", "", content))
|
||||
rec = {"tag": tag, "arm": arm, "frag": frag["id"], "model_returned": r.model,
|
||||
"ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||
"max_tokens": cap, "temperature": temp, "reasoning_effort": effort or None,
|
||||
"finish": ch.finish_reason, "prompt_tokens": pt, "cached_tokens": cached,
|
||||
"completion_tokens": ct, "reasoning_tokens": rt, "cost_usd": round(cost, 6),
|
||||
"latency_s": round(time.time() - t0, 1),
|
||||
"cjk_share": round(cjk_share(content), 4), "echo": cjk_share(content) > 0.15,
|
||||
"src_chars": len(frag["text"]), "content_chars": len(content),
|
||||
"len_ratio": round(out_ns / src_ns, 3) if src_ns else None,
|
||||
"sent_src": len(split_sentences(frag["text"])), "sent_out": len(split_sentences(content)),
|
||||
"system": system, "user": user, "content": content, "reasoning_content": reasoning}
|
||||
fp.write_text(json.dumps(rec, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
print(f"[{tag}] {ch.finish_reason} out={ct} rt={rt} cjk={rec['cjk_share']:.2f} "
|
||||
f"lr={rec['len_ratio']} ${cost:.6f} {rec['latency_s']}с", flush=True)
|
||||
return rec
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--arms", default="a0,a1,a2")
|
||||
ap.add_argument("--n", type=int, default=12)
|
||||
ap.add_argument("--target", type=int, default=1400)
|
||||
ap.add_argument("--cap", type=int, default=8000)
|
||||
ap.add_argument("--temp", type=float, default=0.3)
|
||||
ap.add_argument("--effort", default="low")
|
||||
ap.add_argument("--cost-cap", type=float, required=True)
|
||||
ap.add_argument("--workers", type=int, default=3)
|
||||
ap.add_argument("--raw", type=Path, default=PL / "armsraw")
|
||||
a = ap.parse_args()
|
||||
|
||||
a.raw.mkdir(parents=True, exist_ok=True)
|
||||
frags = fragments(PL / "slice" / "guzhenren-s11-16.gb18030.txt", a.n, a.target)
|
||||
print(f"фрагментов {len(frags)}: " + ", ".join(f"{f['id']}={len(f['text'])}симв" for f in frags))
|
||||
(a.raw / "fragments.json").write_text(json.dumps(frags, ensure_ascii=False, indent=1),
|
||||
encoding="utf-8")
|
||||
arms = [x for x in a.arms.split(",") if x]
|
||||
plan = [(arm, f) for arm in arms for f in frags]
|
||||
todo = [(arm, f) for arm, f in plan if not (a.raw / f"{arm}-{f['id']}.json").exists()]
|
||||
print(f"план {len(plan)} вызовов, к покупке {len(todo)}; смета ≈ ${len(todo)*0.0008:.4f} "
|
||||
f"(из наблюдённых $0.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())
|
||||
29
eval/promptlang/slugcheck.json
Normal file
29
eval/promptlang/slugcheck.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"deepseek": {
|
||||
"ids": [
|
||||
"deepseek-v4-flash",
|
||||
"deepseek-v4-pro"
|
||||
],
|
||||
"expected_present": {
|
||||
"deepseek-v4-flash": true,
|
||||
"deepseek-v4-pro": true
|
||||
}
|
||||
},
|
||||
"xai": {
|
||||
"ids": [
|
||||
"grok-4.20-0309-non-reasoning",
|
||||
"grok-4.20-0309-reasoning",
|
||||
"grok-4.20-multi-agent-0309",
|
||||
"grok-4.3",
|
||||
"grok-4.5",
|
||||
"grok-build-0.1",
|
||||
"grok-imagine-image",
|
||||
"grok-imagine-image-quality",
|
||||
"grok-imagine-video",
|
||||
"grok-imagine-video-1.5"
|
||||
],
|
||||
"expected_present": {
|
||||
"grok-4.3": true
|
||||
}
|
||||
}
|
||||
}
|
||||
54
eval/promptlang/slugcheck.py
Normal file
54
eval/promptlang/slugcheck.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env python3
|
||||
"""$0 live-фактчек слагов перед платными вызовами (гардрейл CLAUDE.md + правило двух направлений).
|
||||
|
||||
GET /models у каждого провайдера, чьи слаги едут в этой сессии. Ничего не генерирует — только
|
||||
листинг. Ключи читает dotenv из eval/.env (сам файл сессия НЕ открывает).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
EVAL = Path(__file__).resolve().parents[1]
|
||||
load_dotenv(EVAL / ".env")
|
||||
|
||||
TARGETS = [
|
||||
("deepseek", "https://api.deepseek.com/v1/models", "DEEPSEEK_API_KEY",
|
||||
["deepseek-v4-flash", "deepseek-v4-pro"]),
|
||||
("xai", "https://api.x.ai/v1/models", "XAI_API_KEY", ["grok-4.3"]),
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
out = {}
|
||||
for name, url, key_env, expect in TARGETS:
|
||||
key = os.environ.get(key_env)
|
||||
if not key:
|
||||
print(f"{name}: НЕТ КЛЮЧА {key_env}", file=sys.stderr)
|
||||
out[name] = {"error": f"no {key_env}"}
|
||||
continue
|
||||
req = urllib.request.Request(url, headers={"Authorization": f"Bearer {key}"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
data = json.loads(r.read())
|
||||
except Exception as e: # noqa: BLE001 — аномалия печатается, диагноз по вендор-доке
|
||||
print(f"{name}: ОШИБКА {e}", file=sys.stderr)
|
||||
out[name] = {"error": str(e)[:300]}
|
||||
continue
|
||||
ids = sorted(m.get("id", "") for m in data.get("data", []))
|
||||
out[name] = {"ids": ids, "expected_present": {e: (e in ids) for e in expect}}
|
||||
print(f"{name}: {len(ids)} слаг(ов) → {ids}")
|
||||
for e in expect:
|
||||
print(f" {e}: {'ЖИВ' if e in ids else 'НЕТ В ЛИСТИНГЕ (не значит мёртв — алиас-урок)'}")
|
||||
(Path(__file__).resolve().parent / "slugcheck.json").write_text(
|
||||
json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
91
eval/promptlang/spend.py
Normal file
91
eval/promptlang/spend.py
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/env python3
|
||||
"""$0 сводка расхода сессии ДВУМЯ независимыми путями.
|
||||
|
||||
Путь 1 — то, что каждый харнесс записал сам (`cost_usd` в своих артефактах).
|
||||
Путь 2 — пере-счёт из СЫРОГО `usage` по прайсу `models.yaml` (кэш-токены по своей ставке).
|
||||
Расхождение двух путей и есть проверка: одинаковые числа, полученные одним способом, — не проверка.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
|
||||
CUTOFF = "2026-07-31 18:40" # первая трата сессии
|
||||
|
||||
prices = yaml.safe_load((REPO / "backend/configs/models.yaml").read_text(encoding="utf-8"))["models"]
|
||||
|
||||
|
||||
def px(model: str) -> tuple[float, float, float]:
|
||||
p = prices[model]["price"]
|
||||
return p["input_per_m"], p["output_per_m"], p.get("cached_per_m", 0.0)
|
||||
|
||||
|
||||
rows = []
|
||||
|
||||
# — движковые прогоны (задача A арм en; задача B арм a0 в боевой форме до стоп-гейта)
|
||||
for proj in ("A-ru", "A-en", "B-a0", "B-a1", "B-a2"):
|
||||
db = PL / proj / "db.sqlite"
|
||||
if not db.exists():
|
||||
continue
|
||||
con = sqlite3.connect(db)
|
||||
for r in con.execute("select model_actual, prompt_tokens, cached_tokens, completion_tokens,"
|
||||
" cost_usd from request_log where ts > ?", (CUTOFF,)):
|
||||
m, pt, cch, ct, self_cost = r
|
||||
if not m:
|
||||
continue
|
||||
pin, pout, pcached = px(m)
|
||||
recomputed = (pt - cch) / 1e6 * pin + cch / 1e6 * pcached + ct / 1e6 * pout
|
||||
rows.append(("движок " + proj, self_cost, recomputed))
|
||||
con.close()
|
||||
|
||||
# — сырые пробы провода и армы
|
||||
for d, label in ((PL / "wire", "wire-пробы"), (PL / "armsraw", "армы задачи B/A")):
|
||||
for f in sorted(d.glob("*.json")):
|
||||
if f.name == "fragments.json":
|
||||
continue
|
||||
j = json.loads(f.read_text(encoding="utf-8"))
|
||||
if "error" in j:
|
||||
continue
|
||||
pin, pout, pcached = px("deepseek-v4-flash")
|
||||
pt, cch, ct = j.get("prompt_tokens", 0), j.get("cached_tokens", 0), j.get("completion_tokens", 0)
|
||||
recomputed = (pt - cch) / 1e6 * pin + cch / 1e6 * pcached + ct / 1e6 * pout
|
||||
rows.append((label, j.get("cost_usd", 0.0), recomputed))
|
||||
|
||||
# — судьи (свои леджеры, свои модели; в judge_p4 их ДВА, маршрутизация по семейству пары)
|
||||
for sub, label in (("judge", "судья армов"), ("judge_p4", "судья пар P4")):
|
||||
led = PL / sub / "ledger.jsonl"
|
||||
if not led.exists():
|
||||
continue
|
||||
for ln in led.read_text().splitlines():
|
||||
j = json.loads(ln)
|
||||
if "error" in j:
|
||||
continue
|
||||
model = j.get("judge", "grok-4.3")
|
||||
pin, pout, _ = px(model)
|
||||
billed = j.get("out_billed")
|
||||
if billed is None: # judge_p4: reasoning у deepseek subset, у xai additive
|
||||
ct, rt = j.get("completion_tok", 0), j.get("reasoning_tok", 0)
|
||||
billed = ct + rt if model.startswith("grok") and rt > ct else ct
|
||||
recomputed = j.get("prompt_tok", 0) / 1e6 * pin + billed / 1e6 * pout
|
||||
rows.append((f"{label} {model}", j.get("cost_usd", 0.0), recomputed))
|
||||
|
||||
agg: dict[str, list[float]] = {}
|
||||
for label, a, b in rows:
|
||||
v = agg.setdefault(label, [0.0, 0.0, 0])
|
||||
v[0] += a
|
||||
v[1] += b
|
||||
v[2] += 1
|
||||
|
||||
print(f"{'источник':<22}{'вызовов':>9}{'путь 1 ($)':>13}{'путь 2 ($)':>13}{'Δ':>12}")
|
||||
t1 = t2 = 0.0
|
||||
for label, (a, b, n) in sorted(agg.items()):
|
||||
print(f"{label:<22}{n:>9}{a:>13.6f}{b:>13.6f}{a-b:>12.2e}")
|
||||
t1 += a
|
||||
t2 += b
|
||||
print(f"{'ИТОГО':<22}{sum(v[2] for v in agg.values()):>9}{t1:>13.6f}{t2:>13.6f}{t1-t2:>12.2e}")
|
||||
print(f"\nпотолок сессии $0.15 · использовано {t1/0.15*100:.1f}% · остаток ${0.15-t1:.6f}")
|
||||
141
eval/promptlang/wire_probe.py
Normal file
141
eval/promptlang/wire_probe.py
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Wire-проба канала deepseek-v4-flash после вендор-обновления 0731 (правило двух направлений).
|
||||
|
||||
Что случилось: боевой черновой канал 31.07 в 18:45+ UTC начал возвращать `finish=length` при
|
||||
`completion_tokens = max_tokens = 8000` и ПУСТЫМ `content` — 4 из 4 чанков базлайнового арма против
|
||||
2 из 68 у холодного прогона тем же утром. Вендор-дока даёт причину: слаг `deepseek-v4-flash` в этот
|
||||
день переехал на `DeepSeek-V4-Flash-0731` («re-post-trained», усиленные agent-способности).
|
||||
|
||||
Проба отвечает на то, чего движковый леджер ответить НЕ может: для deepseek он держит
|
||||
`ReasoningTokens = 0` СОЗНАТЕЛЬНО (`provider_openai.go:22-24`, ReasoningSubset — thinking внутри
|
||||
completion, иначе двойной счёт). Значит «сколько именно думает модель» видно только на сыром проводе.
|
||||
|
||||
Меряем при разных потолках: сколько уходит в `reasoning_content`, доходит ли дело до `content`,
|
||||
на каком потолке ответ вообще заканчивается. Сырьё персистится целиком (правило 1 полигона).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from openai import OpenAI
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
|
||||
RAW = PL / "wire"
|
||||
RAW.mkdir(exist_ok=True)
|
||||
load_dotenv(REPO / "eval" / ".env")
|
||||
|
||||
PRICE_IN, PRICE_CACHED, PRICE_OUT = 0.14, 0.0028, 0.28 # models.yaml, prices_checked 2026-07-10
|
||||
|
||||
USER_SEP = "\n---USER---\n"
|
||||
FEWSHOT_SEP = "\n---FEWSHOT---\n"
|
||||
|
||||
|
||||
def render(arm: str, text: str) -> tuple[str, str]:
|
||||
"""Точная реплика движкового рендера (render.go:117-155 + Render): комментарии вырезаются,
|
||||
system = ядро (+ few-shot через «\\n\\n»), user = хвост после ---USER--- с подставленным {{text}}."""
|
||||
raw = (PL / "arms" / f"translator-{arm}.md").read_text(encoding="utf-8")
|
||||
out, rest = [], raw
|
||||
while True:
|
||||
i = rest.find("<!--")
|
||||
if i < 0:
|
||||
out.append(rest)
|
||||
break
|
||||
out.append(rest[:i])
|
||||
rest = rest[i + 4:]
|
||||
j = rest.find("-->")
|
||||
rest = rest[j + 3:]
|
||||
canon = "".join(out)
|
||||
head, user = canon.split(USER_SEP, 1)
|
||||
parts = head.split(FEWSHOT_SEP, 1)
|
||||
system = parts[0].strip()
|
||||
if len(parts) == 2:
|
||||
system = system + "\n\n" + parts[1].strip()
|
||||
vals = {"source_lang": "zh", "target_lang": "ru", "genre": "вебновелла",
|
||||
"audience": "взрослые читатели вебновелл", "title": "蛊真人", "venuti": "0.60",
|
||||
"honorifics": "keep", "transcription": "palladius", "footnotes": "minimal",
|
||||
"text": text}
|
||||
for k, v in vals.items():
|
||||
system = system.replace("{{" + k + "}}", v)
|
||||
user = user.replace("{{" + k + "}}", v)
|
||||
return system, user.strip()
|
||||
|
||||
|
||||
def call(client, system: str, user: str, max_tokens: int, tag: str, extra: dict | None = None,
|
||||
temperature: float | None = None) -> dict:
|
||||
t0 = time.time()
|
||||
kw = dict(model="deepseek-v4-flash",
|
||||
messages=[{"role": "system", "content": system}, {"role": "user", "content": user}],
|
||||
max_tokens=max_tokens)
|
||||
if temperature is not None:
|
||||
kw["temperature"] = temperature
|
||||
if extra:
|
||||
kw["extra_body"] = extra
|
||||
r = client.chat.completions.create(**kw)
|
||||
ch = r.choices[0]
|
||||
msg = ch.message
|
||||
content = msg.content or ""
|
||||
reasoning = getattr(msg, "reasoning_content", None) or ""
|
||||
u = r.usage
|
||||
pt = getattr(u, "prompt_tokens", 0) or 0
|
||||
ct = getattr(u, "completion_tokens", 0) or 0
|
||||
det = getattr(u, "completion_tokens_details", None)
|
||||
rt = (getattr(det, "reasoning_tokens", 0) if det else 0) or 0
|
||||
cached = getattr(getattr(u, "prompt_tokens_details", None), "cached_tokens", 0) or 0
|
||||
cost = (pt - cached) / 1e6 * PRICE_IN + cached / 1e6 * PRICE_CACHED + ct / 1e6 * PRICE_OUT
|
||||
rec = dict(tag=tag, ts=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||
model_returned=r.model, max_tokens=max_tokens, extra=extra,
|
||||
finish=ch.finish_reason, content_chars=len(content), reasoning_chars=len(reasoning),
|
||||
prompt_tokens=pt, cached_tokens=cached, completion_tokens=ct, reasoning_tokens=rt,
|
||||
cost_usd=round(cost, 6), latency_s=round(time.time() - t0, 1),
|
||||
system=system, user=user, content=content, reasoning_content=reasoning)
|
||||
(RAW / f"{tag}.json").write_text(json.dumps(rec, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
print(f"[{tag}] model={r.model} finish={ch.finish_reason} cap={max_tokens} "
|
||||
f"out={ct} (reasoning_tok={rt}) content={len(content)}симв reasoning={len(reasoning)}симв "
|
||||
f"${cost:.6f} {rec['latency_s']}с")
|
||||
return rec
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--chunk", type=Path, required=True, help="файл с текстом чанка")
|
||||
ap.add_argument("--arm", default="a0")
|
||||
ap.add_argument("--caps", default="16000")
|
||||
ap.add_argument("--tag", default="probe")
|
||||
ap.add_argument("--effort", default="", help="reasoning_effort, если проверяем ручку")
|
||||
ap.add_argument("--temp", default="", help="temperature; пусто = НЕ слать (как проба), '0.3' = как шлёт движок")
|
||||
a = ap.parse_args()
|
||||
|
||||
text = a.chunk.read_text(encoding="utf-8")
|
||||
system, user = render(a.arm, text)
|
||||
print(f"чанк {len(text)} симв · system {len(system)} симв · арм {a.arm}")
|
||||
|
||||
key = os.environ.get("DEEPSEEK_API_KEY")
|
||||
if not key:
|
||||
print("нет DEEPSEEK_API_KEY", file=sys.stderr)
|
||||
return 2
|
||||
client = OpenAI(api_key=key, base_url="https://api.deepseek.com/v1", timeout=600)
|
||||
|
||||
total = 0.0
|
||||
for cap in [int(x) for x in a.caps.split(",")]:
|
||||
extra = {"reasoning_effort": a.effort} if a.effort else None
|
||||
tag = f"{a.tag}-{a.arm}-{cap}"
|
||||
if a.effort:
|
||||
tag += f"-eff{a.effort}"
|
||||
if a.temp:
|
||||
tag += f"-t{a.temp}"
|
||||
rec = call(client, system, user, cap, tag, extra,
|
||||
float(a.temp) if a.temp else None)
|
||||
total += rec["cost_usd"]
|
||||
print(f"\nпроба потратила ${total:.6f}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Loading…
Add table
Reference in a new issue