Land accepted editor-role experiment 20 with review header downgrading unpersisted judge claims, plus its harness and judge scripts
This commit is contained in:
parent
0c3b55f074
commit
3d5d5d54f3
18 changed files with 2078 additions and 0 deletions
408
docs/experiments/20-editor-role.md
Normal file
408
docs/experiments/20-editor-role.md
Normal file
|
|
@ -0,0 +1,408 @@
|
||||||
|
# 20. Роль редактора в пайплайне: что он покупает и можно ли купить это дешевле
|
||||||
|
|
||||||
|
**Полигон-сессия 05.08.2026.** Продолжение эксп-19 по расширенной санкции владельца: «не понимаю,
|
||||||
|
какую роль занимает редактор… если редактору приходится переписывать весь текст, то в чём смысл
|
||||||
|
чернового перевода… как выстроить эксп, чтоб понять, как добьёмся максимальной эффективности и
|
||||||
|
дешевизны». Зона: `eval/editor_harness/` + этот файл + пинг в `docs/PROGRESS.md`. Потолок $2.00
|
||||||
|
(поднимался дважды, оба раза объявлено ДО прогона — §1.3).
|
||||||
|
|
||||||
|
> **РЕВЬЮ-ШАПКА ПРИЁМКИ (оркестратор №15, D39.108, 06.08) — ПРИНЯТ С ОГОВОРКАМИ.** Санкция пересмотра постановки и подъёмы потолка подтверждены владельцем 06.08; эксп шёл БЕЗ пре-рег-фриза (единственный из трёх) — принят как exploratory под расширенной санкцией. **Оговорки и поправки приёмки:** (1) **решающий замер §5.2 НЕ персистирован** — вердикты Claude-судьи существуют только в транскрипте сессии; таблица §5.2 и позиционная проверка 10:8 имеют статус «СО СЛОВ СЕССИИ». Направление «на боевой паре второй проход отрицателен» держит персистированный второй судья (§5.3: dp один проход 2:0 среди устойчивых, знак совпал у всех трёх моделей); величина — не держится ничем. (2) §3.1: цена раунда починки (-fix вызовы) не входила в цену `diff-harness` — с ней строка lu/ON 0.887→**1.082** (знак переворачивается), lu/OFF 1.011→1.059, gl/ON 0.858→0.909: «дифф дороже — свойство только deepseek» ослаблен до «у luna — паритет/чуть дороже». Колонка «×к full» — медиана отношений, не отношение медиан (у dp 3.54 тем способом); «правок/вызов» — ops_total с двойным счётом переподанных операций. (3) §5.5: парсер судьи молча выбросил валидный голос jx-lu-w3-d — устойчивость: верность 8/12 = 67%, полнота 3/4 = 75%, чистота 4/5 = 80%, общий 15/23 = 65%; победители (русский 10:2, общий 8:7) не двигаются. (4) ИТОГ: «вся сессия $2.1793» включает $0.107335 сырья ПРЕДЫДУЩЕЙ сессии (research/24); фактическая цена 18+19+20 = **$2.0720**. (5) §3.3: locate/repair у luna = 4.53×, не «×5»; «в 5–6 раз» относится к dp. (6) §5.6: вердикт «боевой гейт этого не поймает» ОПРОВЕРГНУТ исполнением боевого Go-кода — санитайзер (detectCJKLeak, класс вердикт-гейта D30.3, включён в c1) флагает все 6 утёкших финалов и вырезает утечку из экспорта; реальная дыра — сноска-примечание без слова-заголовка («[1] …») мимо детектора примечаний + условность тройки isFinal (строка 46). (7) §6 п.2 (пинг о банкноте): наблюдения верны, вердикт «тихо выключен» — нет: блока в боевом `translator.md` не было никогда, канал жив ратифицированным путём D39.42 п.4 (ран-локальный конфиг книги — им и собраны 56/128); реальный дефект — декларация «+банкнота» в CURRENT-STATE при shipping-конфиге без канала (строка 140). (8) §5.4 мерил разброс только dp-судьи на dp-материале в прямой ориентации — порог экстраполирован; дисперсия Claude-судьи не мерена (сам отчёт это фиксирует).
|
||||||
|
|
||||||
|
**Роль редактора измерена и оказалась узкой.** В бейк-оффе «перевод с нуля против редактуры
|
||||||
|
черновика» (24 пары, чужая модель-судья, зеркальная раскладка) второй проход доказанно выигрывает
|
||||||
|
ОДНУ ось из шести — русскую прозу (10:2 при 80% устойчивости). Верность — ничья 4:4, общий
|
||||||
|
вердикт — ничья 8:7. Это совпадает с ратифицированным
|
||||||
|
замыслом D32.1(г): «ось решения для черновика — верность, не стиль; стиль редактор переписывает».
|
||||||
|
⇒ схема не сломана, но и не даёт того, что ей часто приписывают.
|
||||||
|
|
||||||
|
**Цену редакторского вызова держит ПОИСК, а не правка.** Три независимых замера сходятся: арм
|
||||||
|
«только найди, ничего не чини» стоит 2.14× от полного переписывания; починка по УКАЗАННОМУ месту
|
||||||
|
дешевле поиска в 5–6 раз; сужение мандата режет число правок вдвое, а размышление не режет вовсе.
|
||||||
|
|
||||||
|
**Точечная починка по флагу работает и стоит копейки:** 12/12 дефектов снято на обеих
|
||||||
|
проверенных моделях, 7/8 восстановлено строго, по **$0.000173** за починку у `gpt-5.6-luna` —
|
||||||
|
в 6 раз дешевле, чем переписать тот же кусок. Это механизм, который есть у конкурентов
|
||||||
|
(AiNiee `ProofreadTask.py:250`, LinguaGacha) и которого у нас нет: `research/22:176` фиксирует
|
||||||
|
«у нас НЕТ — флагаем, не чиним».
|
||||||
|
|
||||||
|
**РЕШАЮЩИЙ ЗАМЕР: гипотеза «мандат в промпте заменяет второй проход» ОПРОВЕРГНУТА.** Механический
|
||||||
|
показатель её обещал — один проход транслятора с дословным блоком дискурс-перевёрстки даёт ту же
|
||||||
|
сборку абзацев, что связка (у `deepseek-v4-pro` 6 против 6). Но слепой суд 18 окон показал, что
|
||||||
|
число абзацев было ПРОКСИ, а не качеством: русский язык 3:12 и вёрстка 5:10 в пользу связки.
|
||||||
|
Позиционная проверка пройдена — предвзятости нет. ⚠ Перекрёстная проверка чужой моделью (§5.3) дала
|
||||||
|
вдвое меньшую величину (русский 6:4, общий 5:4), а замер разброса судьи (§5.4) показал, что такая
|
||||||
|
разница лежит ВНУТРИ его шума ⇒ перекрёстная проверка §5.2 не подтверждает и не опровергает.
|
||||||
|
Крупные перевесы §5.2 кратно больше шума и устояли, но собственная дисперсия Claude-судьи НЕ мерена.
|
||||||
|
|
||||||
|
**И главное — ответ ЗАВИСИТ ОТ МОДЕЛИ и переворачивается.** У `glm-5` второй проход отрабатывает
|
||||||
|
как обещано (общий 5:1, вёрстка 6:0 — чистый свип). У **`deepseek-v4-pro`, который стоит редактором
|
||||||
|
в `pipeline-c1.yaml` СЕЙЧАС, второй проход ВРЕДИТ**: один проход берёт общий вердикт 5:1 и верность
|
||||||
|
5:1, а обе целевые оси — ровно 3:3, то есть переписывание черновика не купило ничего и при этом
|
||||||
|
ломало фактуру (сорванные шкалы процентов, потерянные реалии `蛊虫`/`家老`). ⇒ «нужен ли редактор» —
|
||||||
|
не свойство архитектуры, а свойство пары «модель × роль», и на боевой паре ответ отрицательный.
|
||||||
|
|
||||||
|
**Отозвано из эксп-19 дважды.** (1) Вывод «дифф дороже full-regen» сужен до одной модели И одного
|
||||||
|
целевого языка: у `deepseek-v4-pro` отношение 2.83 на русской цели и **0.67 на английской** (§3.4).
|
||||||
|
(2) Объяснение, которым эксп-19 снял арм E («виновато обеднённое зеркало промпта»), ОПРОВЕРГНУТО
|
||||||
|
репликацией с выверенным по проводу зеркалом — эффект 6.7× сохранился. Исходная формулировка: При включённом
|
||||||
|
мышлении у всех отношение дифф/full: `glm-5` 0.86 · `grok-4.3` 0.66 · `gpt-5.6-luna` 0.89 ·
|
||||||
|
и `deepseek-v4-pro` **2.83**. Это свойство конкретной модели, а не думающих моделей как класса,
|
||||||
|
и не артефакт выключенного мышления.
|
||||||
|
|
||||||
|
**⛔ ГЛАВНАЯ ОГОВОРКА, обесценивающая часть выводов ЭТОГО ЖЕ отчёта.** Разброс судьи измерен
|
||||||
|
(§5.4): на ИДЕНТИЧНОМ входе три повтора единогласны лишь в 56% клеток, а на несущих осях — русский
|
||||||
|
язык и общий вердикт — в **33%**. ⇒ судейская методика не разрешает разницу меньше ~2:1. Числа
|
||||||
|
перекрёстной проверки (§5.3) лежат внутри этого шума и понижены до «не разрешает». Крупные
|
||||||
|
перевесы §5.2 шумом не объясняются, но собственная дисперсия Claude-судьи НЕ мерена. Порядок
|
||||||
|
работ был неверным: инструмент надо было померить ДО того, как им мерить.
|
||||||
|
|
||||||
|
**Деньги:** эксп-20 $1.2285 при потолке $2.00; вся сессия (18+19+20) $2.1793.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §1. Дизайн
|
||||||
|
|
||||||
|
### 1.1 Что было не так с эксп-19
|
||||||
|
|
||||||
|
Эксп-19 спрашивал «какая форма контракта лучше» и сравнивал дифф с full-regen. Рамка неверна по
|
||||||
|
двум причинам, обе вскрыты этой сессией:
|
||||||
|
|
||||||
|
1. **Полное переписывание — это и есть ратифицированная работа редактора** (D32.1г,
|
||||||
|
`research/11-gap-3.md:10`: «финальный стиль задаёт МОДЕЛЬ-РЕДАКТОР»). Дифф физически не может
|
||||||
|
её делать: кальки и канцелярит — дефект РАСПРЕДЕЛЁННЫЙ, он в каждом втором предложении, и якорь
|
||||||
|
на него не ставится. Спан-инструмент чинит ЛОКАЛИЗОВАННОЕ.
|
||||||
|
2. **Дифф испытывался не в том слоте.** Его место — не замена прозаического прохода, а починка
|
||||||
|
того, что уже нашли детерминированные гейты.
|
||||||
|
|
||||||
|
### 1.2 Контракты (все на одном материале: 6 окон, те же черновики, тот же блок закона)
|
||||||
|
|
||||||
|
| Контракт | Что делает | Промпт |
|
||||||
|
|---|---|---|
|
||||||
|
| `full` | переписывает черновик целиком | боевой `zh-ru/editor.md`, FEWSHOT дропнут как в проде |
|
||||||
|
| `diff-strict` | точечные правки, уникальность якоря с первого раза | `editor_contract/prompts/editor-diff.md` (эксп-19) |
|
||||||
|
| `diff-harness` | то же, но **с правом на ошибку и раундом починки** — копия харнеса | `prompts/editor-diff-harness.md` |
|
||||||
|
| `locate` | ТОЛЬКО находит дефекты, ничего не чинит и не форматирует | `prompts/editor-locate.md` |
|
||||||
|
| `direct` | переводит исходник с нуля, черновика нет | боевой `zh-ru/translator.md` |
|
||||||
|
| `direct-reflow` | то же + ДОСЛОВНЫЙ блок перевёрстки из `editor.md` | `prompts/translator-reflow.md` (собран скриптом из двух боевых) |
|
||||||
|
| `repair` | чинит ОДНО указанное предложение по названному типу ошибки | `prompts/repair.md` |
|
||||||
|
|
||||||
|
`diff-harness` скопирован с настоящих харнесов, а не сочинён: opencode `tool/edit.txt` («Found
|
||||||
|
multiple matches… provide a larger string»), gemini-cli `tools/edit.ts` + `llm-edit-fixer.ts`
|
||||||
|
(отдельный дешёвый LLM чинит промахнувшийся якорь). Ключевое отличие от эксп-19 — у модели НЕТ
|
||||||
|
обязанности гарантировать уникальность якоря заранее.
|
||||||
|
|
||||||
|
### 1.3 Девиации, объявленные до прогонов
|
||||||
|
|
||||||
|
- Потолок поднимался дважды: $0.60 → $1.00 (добавлен арм с включённым мышлением, чтобы сравнение
|
||||||
|
моделей не смешивало модель с режимом) → $2.00 (на $1.00 проекционный гард заблокировал
|
||||||
|
РЕШАЮЩИЙ арм `direct-reflow`). Оба раза объявлено владельцу ДО запуска.
|
||||||
|
- `translator-reflow.md` собран СКРИПТОМ из двух боевых промптов, а не написан руками — чтобы блок
|
||||||
|
перевёрстки совпадал с редакторским дословно и расхождение нельзя было внести опечаткой.
|
||||||
|
- Гардрейл соблюдён: у DeepSeek мышление не гасится ни при каких условиях (эхо-мина); риг на
|
||||||
|
попытку падает намеренно, это проверяется самотестом.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §2. Селф-ревью рига: шесть дефектов, пойманных ДО и ВО ВРЕМЯ прогонов
|
||||||
|
|
||||||
|
Требование владельца — проверять код на адекватность до запуска. Поймано:
|
||||||
|
|
||||||
|
| № | Дефект | Когда | Последствие, если бы прошёл |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | неверный эндпоинт `glm-5` (`open.bigmodel.cn` вместо `api.z.ai`) | до трат | прогон упал бы |
|
||||||
|
| 2 | раунд починки читал `ApplyResult.details` как словари, а это список строк | до трат | главная фича арма умерла бы на первом отказе |
|
||||||
|
| 3 | мой модуль `probe` затенялся одноимённым из `editor_contract` | до трат | импортировался чужой файл |
|
||||||
|
| 4 | **арм `full` уехал с блоком `---FEWSHOT---`**, который в проде дропается | В ХОДЕ прогона | база сравнения была не боевым контрактом; 7 вызовов удалены и пересняты |
|
||||||
|
| 5 | `nothink` выставлялся только для механизма `disable`, а grok/luna используют `effort_none` | на первом вызове | grok/luna ехали бы с ВКЛЮЧЁННЫМ мышлением под видом выключенного |
|
||||||
|
| 6 | у 4 посадок из 12 эталон `fix_rx` совпадал с испорченным предложением ДО починки | до трат | бездействие модели засчиталось бы как успех |
|
||||||
|
|
||||||
|
Плюс два расхождения, уравненных до прогона: user-сообщения армов `direct` и `direct-reflow`
|
||||||
|
отличались одним переносом строки; метрика `reasoning_chars` СЛЕПА для OpenAI-семейства (провайдер
|
||||||
|
не возвращает текст размышления, только счётчик) — для этих моделей считается `reasoning_tokens`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §3. Экономика: что именно стоит денег
|
||||||
|
|
||||||
|
### 3.1 Отношение дифф/full по моделям и режимам (медианы 6 окон)
|
||||||
|
|
||||||
|
| Модель | режим | `full` | `diff-harness` | × к full | правок/вызов |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| `deepseek-v4-pro` | мышление НЕСЪЁМНО | $0.001316 | $0.004661 | **2.83** | 2.5 |
|
||||||
|
| `glm-5` | OFF | $0.003837 | $0.002368 | 0.64 | 4.5 |
|
||||||
|
| `glm-5` | ON | $0.028886 | $0.025068 | 0.86 | 4.8 |
|
||||||
|
| `grok-4.3` | OFF | $0.004142 | $0.002445 | 0.60 | 0.5 |
|
||||||
|
| `grok-4.3` | ON | $0.004185 | $0.002674 | 0.66 | 1.8 |
|
||||||
|
| `gpt-5.6-luna` | OFF | $0.001122 | $0.001096 | 1.01 | 16.0 |
|
||||||
|
| `gpt-5.6-luna` | ON | $0.001592 | $0.001643 | 0.89 | 9.7 |
|
||||||
|
|
||||||
|
⇒ «дифф дороже» — свойство `deepseek-v4-pro`, а не режима и не класса моделей. Побочно: `glm-5`
|
||||||
|
с мышлением стоит в 7.5× больше и как редактор экономически неприемлем.
|
||||||
|
|
||||||
|
⚠ Цену за вызов нельзя сравнивать между моделями без поправки на объём работы: `grok` дёшев потому,
|
||||||
|
что делает 0.5 правки на вызов (почти везде «менять нечего»), а `luna` — 16.
|
||||||
|
|
||||||
|
### 3.2 Из чего состоит выход (медианы, `deepseek-v4-pro`)
|
||||||
|
|
||||||
|
| контракт | размышление, зн. | видимый текст, зн. | доля размышления |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `full` | 2 523 | 1 838 | 58% |
|
||||||
|
| `diff-strict` | 12 570 | 434 | 97% |
|
||||||
|
| `diff-harness` | 12 326 | 426 | 97% |
|
||||||
|
| `locate` (только найти) | 8 062 | 584 | 93% |
|
||||||
|
|
||||||
|
**Копия харнеса не помогла:** 2.83 против 2.87 у строгого контракта, размышление то же. Право на
|
||||||
|
ошибку и раунд починки против несъёмного мышления бессильны — раундов починки не потребовалось ни
|
||||||
|
одного, якоря попадали с первого раза во всех 12 вызовах.
|
||||||
|
|
||||||
|
### 3.3 Поиск дороже починки в 5–6 раз
|
||||||
|
|
||||||
|
| | `deepseek-v4-pro` | `gpt-5.6-luna` |
|
||||||
|
|---|---|---|
|
||||||
|
| починка по флагу | $0.000625 | **$0.000173** |
|
||||||
|
| «только найти» | ×6 дороже починки | ×5 дороже |
|
||||||
|
| переписать весь кусок | ×2 дороже | ×6 дороже |
|
||||||
|
| дефект снят | **12/12** | **12/12** |
|
||||||
|
| восстановлен строго | 7/8 | 7/8 |
|
||||||
|
|
||||||
|
Обе модели чинят одинаково при разнице цены в 3.7× ⇒ на этой задаче дорогая модель не покупает
|
||||||
|
ничего. ⚠ Арм меряет починку при ИДЕАЛЬНОЙ детекции; гейта на «несуществующее слово» и на
|
||||||
|
смысловую инверсию у нас нет, поэтому это ВЕРХНЯЯ ГРАНИЦА схемы, а не её цена в проде.
|
||||||
|
|
||||||
|
### 3.4 АНГЛИЙСКАЯ ЦЕЛЬ: знак переворачивается от смены языка
|
||||||
|
|
||||||
|
Тот же материал, те же контракты, английские черновики (`fp-draft-E*`, 0 кириллицы) и зеркала
|
||||||
|
промптов, **выверенные по проводу**: системный промпт ru 3071 зн. против en 3195 (отношение 1.04),
|
||||||
|
дифф 2104 против 2172 (1.03), locate 1314 против 1347 (1.03); черновики 1840 против 2068 зн.
|
||||||
|
То есть конфаунд эксп-19 (обеднённое en-зеркало без блока перевёрстки) здесь устранён.
|
||||||
|
|
||||||
|
| модель | цель | `full` | `diff-harness` | × к full | `locate` × к full | правок |
|
||||||
|
|---|---|---|---|---|---|---|
|
||||||
|
| `deepseek-v4-pro` | ru | $0.001316 | $0.004661 | **2.83** | **2.14** | 2.5 |
|
||||||
|
| `deepseek-v4-pro` | **en** | $0.004356 | $0.003015 | **0.67** | **0.94** | 1.7 |
|
||||||
|
| `gpt-5.6-luna` | ru | $0.001122 | $0.001096 | 1.01 | 0.72 | 16.0 |
|
||||||
|
| `gpt-5.6-luna` | en | $0.000851 | $0.000616 | 0.74 | 0.66 | 9.3 |
|
||||||
|
|
||||||
|
**На одной и той же модели знак переворачивается от смены целевого языка: 2.83 → 0.67.** Причина
|
||||||
|
видна в проводе — размышление `deepseek-v4-pro` при переписывании:
|
||||||
|
|
||||||
|
| цель | вход | выход | размышление | видимый текст |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| ru | 2232 ток. | 1466 ток. | 2 523 зн. | 1 838 зн. |
|
||||||
|
| **en** | 1596 ток. | **4529 ток.** | **16 989 зн.** | 2 008 зн. |
|
||||||
|
|
||||||
|
**⚠ САМО-ОПРОВЕРЖЕНИЕ.** Эксп-19 намерил ровно это отношение 6.7× и **списал его на обеднённое
|
||||||
|
зеркало промпта** (§6.5 отчёта 19: «прорежение асимметрично именно по контрактам»). С выверенным
|
||||||
|
зеркалом эффект сохранился ⇒ **та причина была неверной**. Отзыв вердикта арма E остаётся в силе
|
||||||
|
(арм действительно был неконтролируем), но его ОБЪЯСНЕНИЕ опровергнуто лучше контролируемой
|
||||||
|
репликацией. Правильная формулировка: `deepseek-v4-pro` тратит на редактуру в английскую цель
|
||||||
|
в 6.7× больше обдумывания, чем в русскую, и это свойство пары «модель × целевой язык».
|
||||||
|
|
||||||
|
**Побочно:** утечка исходника у `gpt-5.6-luna` при английской цели практически исчезает — 1 знак
|
||||||
|
против 19 (`full`) и 16 (`diff`) на русской. Микро-утечка тоже язык-зависима.
|
||||||
|
|
||||||
|
⇒ Ни «дифф дороже», ни «поиск дорог» не являются свойствами контракта. Оба — свойства тройки
|
||||||
|
**контракт × модель × целевой язык**, и любое обобщение через эту тройку эмпирически ложно.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §4. Что редактор реально меняет в тексте
|
||||||
|
|
||||||
|
| | совпало знаков с черновиком | слов дожило | абзацев было → стало |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `full` (`deepseek-v4-pro`) | 81% | **65%** | 13 → **6** |
|
||||||
|
| `full` (`glm-5`) | 89% | 83% | 13 → 8 |
|
||||||
|
| `diff-harness` (обе) | 98–99% | **98%** | 13 → 12–13 |
|
||||||
|
|
||||||
|
Полное переписывание меняет треть слов и вдвое схлопывает абзацы; дифф трогает 2%. Это объясняет
|
||||||
|
судейский счёт по русскому языку без всякой мистики.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §5. Слепое судейство
|
||||||
|
|
||||||
|
### 5.1 Методика и её исправления
|
||||||
|
|
||||||
|
Первый раунд судейства был методически дефектен, и дефект найден вопросом владельца («какой смысл
|
||||||
|
давать 24 агентам одно и то же, если это одна модель?»). Пакеты были разные — 24 сравнения, а не
|
||||||
|
24 повтора, — но судья один, значит систематическая ошибка общая и от числа агентов не убывает.
|
||||||
|
Симптом: **позиция «А» победила 18 раз из 24**, а редактура — 12/12 в позиции А против 6/12 в Б.
|
||||||
|
|
||||||
|
Исправления, внесённые во второй и третий раунды:
|
||||||
|
1. **зеркальная раскладка** — каждый пакет судится дважды, с переставленными А и Б;
|
||||||
|
2. **чужая модель-судья** (`deepseek-v4-pro` через тот же провод, что и эксперимент);
|
||||||
|
3. **раскладка расцеплена с армом** — у каждой модели свой сдвиг чётности, иначе позиция и арм
|
||||||
|
коррелируют по набору (дефект первого раунда);
|
||||||
|
4. **вёрстка вынесена в отдельную ось** — раньше тонула внутри «русского языка»;
|
||||||
|
5. **чистота вывода — отдельная ось**, потому что `luna` роняет иероглифы в русский текст;
|
||||||
|
6. судьям сказано, что слияние построчных абзацев — ТРЕБОВАНИЕ, а не вольность: в первом раунде
|
||||||
|
они штрафовали за исполнение мандата, которого не видели.
|
||||||
|
|
||||||
|
### 5.2 РЕШАЮЩИЙ ЗАМЕР: один проход с мандатом перевёрстки против связки (18 окон)
|
||||||
|
|
||||||
|
**Гипотеза сессии ОПРОВЕРГНУТА.** Я ожидал, что мандат абзацной сборки в промпте транслятора
|
||||||
|
сделает второй проход ненужным — механический показатель это обещал (у `deepseek-v4-pro` 6 абзацев
|
||||||
|
против 6 у связки). Слепой суд показал обратное: **число абзацев было прокси, а не качеством**.
|
||||||
|
|
||||||
|
| ось (арм-победы: 1 проход / 2 прохода / ничья) | dp | gl | lu | ВСЕГО n=18 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| верность | **5**/1/0 | 1/**5**/0 | 0/**4**/2 | 6 / **10** / 2 |
|
||||||
|
| русский язык | 3/3/0 | 0/**4**/2 | 0/**5**/1 | 3 / **12** / 3 |
|
||||||
|
| абзацная вёрстка | 3/3/0 | 0/**6**/0 | **2**/1/3 | 5 / **10** / 3 |
|
||||||
|
| глоссарий | 1/1/4 | **3**/0/3 | 0/1/5 | **4** / 2 / 12 |
|
||||||
|
| **ОБЩИЙ** | **5**/1/0 | 1/**5**/0 | 1/**5**/0 | 7 / **11** / 0 |
|
||||||
|
|
||||||
|
**Проверка позиции пройдена — предвзятости НЕТ.** Общий счёт по позициям 10:8 (симметричен,
|
||||||
|
p≈0.8); у КАЖДОЙ из трёх моделей её победитель побеждает в обеих позициях. Это первый раунд
|
||||||
|
судейства в сессии, методически чистый: расцепление раскладки с армом сработало.
|
||||||
|
|
||||||
|
**Разложение по моделям переворачивает вывод для боевой конфигурации:**
|
||||||
|
|
||||||
|
- **`deepseek-v4-pro` — второй проход ВРЕДИТ.** Один проход берёт общий вердикт 5:1 и верность
|
||||||
|
5:1; русский и вёрстка ровно 3:3. Переписывание черновика НЕ купило ни одной целевой оси, зато
|
||||||
|
ломало фактуру: сорванные шкалы процентов (`四成八分` → «сорок восемь десятых» = 480%), потерянные
|
||||||
|
реалии (`蛊虫` → «насекомых», `家老` → «Наставник»), ломаная грамматика («не хватило бы на то, как
|
||||||
|
их тратил»). **Это модель, которая стоит редактором в `pipeline-c1.yaml` сейчас.**
|
||||||
|
- **`glm-5` — второй проход даёт максимум и именно там, где обещал:** 5:1 общий, 4:0 русский,
|
||||||
|
**6:0 вёрстка** (единственный чистый свип в наборе, обе позиции, p=0.031). Встречно один проход
|
||||||
|
у glm нестабилен: в одном окне 9 непереведённых иероглифов и генеративный залипон.
|
||||||
|
- **`gpt-5.6-luna` — связка берёт язык и верность, но НЕ вёрстку** (1:2 при 3 ничьих). У модели
|
||||||
|
арм-НЕзависимый дефект чистоты: иероглифы в теле русского текста у ОБЕИХ версий.
|
||||||
|
|
||||||
|
⇒ **Ответ на «нужен ли редактор» зависит от модели и переворачивается.** Это не свойство
|
||||||
|
архитектуры и не свойство контракта. Для `glm-5` второй проход оправдан; для `deepseek-v4-pro`
|
||||||
|
он отрицателен по общему вердикту.
|
||||||
|
|
||||||
|
### 5.3 ПЕРЕКРЁСТНАЯ ПРОВЕРКА решающего арма: чужая модель + зеркальная раскладка (18 пар)
|
||||||
|
|
||||||
|
Те же 18 пакетов пересужены `deepseek-v4-pro` в ДВУХ раскладках. Вердикт засчитывается только если
|
||||||
|
при перестановке А/Б побеждает тот же ТЕКСТ.
|
||||||
|
|
||||||
|
| ось | устойчивость | чужой судья | Claude (§5.2) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| русский язык | 10/12 = **83%** | связка **6 : 4** | связка 12 : 3 |
|
||||||
|
| общий вердикт | 9/15 = 60% | связка **5 : 4** | связка 11 : 7 |
|
||||||
|
| верность | 3/7 = 43% | сигнала нет | связка 10 : 6 |
|
||||||
|
|
||||||
|
**Что подтвердилось:** модель-зависимость. По общему вердикту среди устойчивых — `deepseek-v4-pro`
|
||||||
|
**один проход 2:0**, `glm-5` связка 2:1, `gpt-5.6-luna` связка 3:1. Знак совпадает с Claude у всех
|
||||||
|
трёх моделей, то есть вывод «на боевой паре второй проход отрицателен» держат ДВА разных судьи.
|
||||||
|
|
||||||
|
**Что НЕ подтвердилось в заявленной величине:** преимущество связки по русскому языку вдвое слабее
|
||||||
|
(6:4 против 12:3), общий вердикт сходится к ничьей (5:4), а верность у чужого судьи не даёт сигнала
|
||||||
|
вовсе (43% устойчивости = уровень монетки).
|
||||||
|
|
||||||
|
⇒ **Корректная формулировка:** второй проход даёт преимущество по русской прозе — направление
|
||||||
|
подтверждено двумя судьями, но ВЕЛИЧИНА судья-зависима и на независимом судье близка к порогу
|
||||||
|
различимости. Числа §5.2 читать как верхнюю оценку, §5.3 — как нижнюю.
|
||||||
|
|
||||||
|
⚠ Чужой судья заметно шумнее: устойчивых сравнений 15 из 18 по общему вердикту против практически
|
||||||
|
всех у Claude, а по глоссарию и полноте устойчивых нет вовсе (сплошные ничьи). Разброс судьи на
|
||||||
|
ПОВТОРЕ одной клетки не мерен ни разу — это остаётся открытым.
|
||||||
|
|
||||||
|
### 5.4 ⛔ РАЗРЕШАЮЩАЯ СПОСОБНОСТЬ СУДЬИ: разница меньше 2:1 сигналом не является
|
||||||
|
|
||||||
|
Последняя дыра методики закрыта замером, и он обесценивает ЧАСТЬ собственных выводов отчёта.
|
||||||
|
Те же пакеты, та же ориентация, **три независимых голоса `deepseek-v4-pro` на идентичном входе**:
|
||||||
|
|
||||||
|
| ось | клеток | единогласно | доля |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **общий вердикт** | 6 | 2 | **33%** |
|
||||||
|
| **русский язык** | 6 | 2 | **33%** |
|
||||||
|
| верность | 6 | 2 | 33% |
|
||||||
|
| глоссарий | 6 | 4 | 67% |
|
||||||
|
| полнота | 6 | 5 | 83% |
|
||||||
|
| чистота | 6 | 5 | 83% |
|
||||||
|
| **ИТОГО** | 36 | 20 | **56%** |
|
||||||
|
|
||||||
|
По общему вердикту судья меняет СОБСТВЕННЫЙ ответ между повторами в **4 клетках из 6**
|
||||||
|
(единогласны только w2 и w3). ⇒ **Разница между армами, меньшая этого разброса, сигналом считаться
|
||||||
|
не может.**
|
||||||
|
|
||||||
|
**Что это отменяет:**
|
||||||
|
- Числа §5.3 (чужой судья: русский 6:4, общий 5:4) лежат ВНУТРИ шума ⇒ понижаются с
|
||||||
|
«подтверждает направление» до **«не разрешает»**. Перекрёстная проверка не подтвердила и не
|
||||||
|
опровергла §5.2 — она оказалась слепа к разнице такого размера.
|
||||||
|
- Скачок устойчивости 45% → 68% между частичными (11 пар) и полными (24 пары) данными §5.3
|
||||||
|
объясняется не малой выборкой, а нестабильностью инструмента.
|
||||||
|
|
||||||
|
**Что устояло:** крупные перевесы Claude в §5.2 (русский 3:12, вёрстка gl 6:0, dp общий 5:1)
|
||||||
|
кратно больше замеренного разброса, поэтому шумом не объясняются. ⚠ Но **собственная дисперсия
|
||||||
|
Claude-судьи НЕ мерена** — этот замер сделан на `deepseek-v4-pro`. Переносить его на Claude
|
||||||
|
без замера нельзя, и §5.2 остаётся утверждением с неизмеренной погрешностью.
|
||||||
|
|
||||||
|
**Норматив на будущее (главный методический вывод сессии):** односудейный однопроходный вердикт
|
||||||
|
на этой задаче — не измерение. Прежде чем сравнивать армы, надо померить разброс судьи на повторе
|
||||||
|
и объявить порог различимости; всё, что меньше порога, печатать как «не разрешено», а не как
|
||||||
|
результат.
|
||||||
|
|
||||||
|
### 5.5 Устойчивость вердиктов к перевороту раскладки (бейк-офф, 24 пары)
|
||||||
|
|
||||||
|
| ось | устойчивость | кто выигрывает там, где устойчиво |
|
||||||
|
|---|---|---|
|
||||||
|
| русский язык | 80% | **редактура 10 : 2** |
|
||||||
|
| верность | 73% | ничья 4 : 4 |
|
||||||
|
| общий вердикт | 68% | ничья 8 : 7 |
|
||||||
|
| чистота | 100% | редактура 3 : 1 |
|
||||||
|
| полнота | 100% | редактура 2 : 1 |
|
||||||
|
|
||||||
|
**Вывод «редактура бьёт прямой перевод 18:6» ОТОЗВАН** — у независимой модели с зеркальной
|
||||||
|
раскладкой это 8:7, ничья. Уцелела одна ось: русский язык.
|
||||||
|
|
||||||
|
### 5.6 Утечка исходника в выход
|
||||||
|
|
||||||
|
| модель | окон с утечкой (из 6), `full` / `diff` | ханьцзы всего, `full` / `diff` |
|
||||||
|
|---|---|---|
|
||||||
|
| `deepseek-v4-pro` | 0 / 0 | 0 / 0 |
|
||||||
|
| `glm-5` | 0 / 1 | 0 / 2 |
|
||||||
|
| `grok-4.3` | 0 / 1 | 0 / 2 |
|
||||||
|
| **`gpt-5.6-luna`** | **3 / 3** | **19 / 16** |
|
||||||
|
|
||||||
|
`luna` роняет иероглифы в русскую прозу в половине окон и в ОБОИХ контрактах ⇒ это свойство
|
||||||
|
модели, а не контракта. Максимальная доля 0.49%. **Боевой гейт этого не поймает:**
|
||||||
|
`disposition.go` держит `cjkEchoThreshold = 0.15`, то есть ловит сплошное эхо, а микро-утечка
|
||||||
|
(«сделать его 本命蛊») проходит насквозь. Вход для бэкенда.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §6. Что это даёт архитектуре
|
||||||
|
|
||||||
|
Складывается конфигурация, которую стоит проверять как целое:
|
||||||
|
|
||||||
|
1. **Один проход сильной моделью с мандатом перевёрстки** вместо двух. Абзацная сборка достигается
|
||||||
|
промптом (§ИТОГ), а `exp14:239` независимо показывает, что силой модели она НЕ достигается
|
||||||
|
(«сильнее модель сама по себе абзацы не чинит»).
|
||||||
|
2. **Банкноты остаются на этом проходе.** У черновика два продукта, и второй — блок банкнот,
|
||||||
|
WHAT-канал банка памяти: майнер находит термины в ИСХОДНИКЕ, а переводческая волна предлагает
|
||||||
|
для них ПЕРЕВОДЫ (`mining.go:85`). Подтверждено по базе холодного прогона: разделитель
|
||||||
|
`⟦TM-BANK-v1⟧` в **56 из 128** черновиковых ответов, у стадии терминологии — 0.
|
||||||
|
⚠ **Пинг бэкенду:** боевой `prompts/zh-ru/translator.md` блока банкнот НЕ содержит, отдельный
|
||||||
|
`translator-banknote.md` ни одним конфигом не выбирается (`prompt_override` в `pipeline-c1.yaml`
|
||||||
|
не задан). Похоже на тихо выключённый канал — проверить.
|
||||||
|
3. **Гейты флагают, точечная починка чинит** — за $0.0002 вместо $0.0011 за переписывание.
|
||||||
|
Это и есть механизм конкурентов, которого у нас нет.
|
||||||
|
|
||||||
|
**Чего эти данные НЕ позволяют утверждать.** Доля дефектов, которую гейты видят САМИ, не измерена
|
||||||
|
(гейта на выдуманное слово и на смысловую инверсию нет). Разброс судьи на повторе одной клетки не
|
||||||
|
мерен ни разу. Английская сторона посчитана по цене и формату, но не судилась. n=6 окон на модель.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §7. Как ставить такие эксперименты (мета-вывод сессии)
|
||||||
|
|
||||||
|
**0. Сначала померить ИНСТРУМЕНТ, потом им мерить.** Главный урок сессии: судья на этой задаче
|
||||||
|
единогласен на идентичном входе лишь в 56% клеток и в 33% — на несущих осях (§5.4). Всё судейство
|
||||||
|
трёх раундов ставилось БЕЗ этого знания, и часть выводов пришлось понизить задним числом. Порядок
|
||||||
|
обязан быть обратным: замер разброса судьи → объявленный порог различимости → только потом
|
||||||
|
сравнение армов.
|
||||||
|
|
||||||
|
|
||||||
|
1. **Не «инструмент А против инструмента Б», а «класс дефекта → самый дешёвый достаточный
|
||||||
|
механизм»**, включая $0. Локализованные дефекты (термин, число, полярность) чинятся спаном;
|
||||||
|
распределённые (кальки) — только переписыванием; механические (вёрстка) — промптом или кодом.
|
||||||
|
2. **Любое утверждение о качестве обязано пройти зеркальную раскладку и второго судью другой
|
||||||
|
модели.** Односудейный замер с фиксированной раскладкой дал 18:6 там, где на деле 8:7.
|
||||||
|
3. **Ревью посылки — ДО прогона, инструментом, а не глазами.** Из шести дефектов рига четыре
|
||||||
|
пойманы до трат; два оставшихся стоили пере-съёмки семи вызовов и первого прогона grok.
|
||||||
|
4. **Проверять, что параметр не только УШЁЛ на провод, но и ПОДЕЙСТВОВАЛ.** Эксп-19 проверил
|
||||||
|
первое и не проверил второе — через эту дыру проехала посылка целого арма.
|
||||||
112
eval/editor_harness/judge_crossmodel.py
Normal file
112
eval/editor_harness/judge_crossmodel.py
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""ПЕРЕКРЁСТНОЕ судейство: чужая модель-судья + ЗЕРКАЛЬНАЯ раскладка А/Б.
|
||||||
|
|
||||||
|
Повод (владелец 05.08): «какой смысл давать 24 агентам одно и то же, если это одна модель?
|
||||||
|
От спама их количества суть не поменяется». Довод верный. Пакеты были разные (24 сравнения,
|
||||||
|
а не 24 повтора), но судья один — Claude с одной рубрикой, значит систематическая ошибка у всех
|
||||||
|
общая, и добавление агентов её не уменьшает. Симптом уже вылез: позиция «А» победила 18 раз из 24,
|
||||||
|
а редактура — 12/12 в позиции А против 6/12 в позиции Б.
|
||||||
|
|
||||||
|
Здесь снимаются обе проблемы разом, дизайном 2×2:
|
||||||
|
· судья ДРУГОЙ МОДЕЛИ (через тот же провод, что и сам эксперимент),
|
||||||
|
· каждый пакет судится в ДВУХ раскладках — прямой и зеркальной (А и Б переставлены).
|
||||||
|
|
||||||
|
Тогда:
|
||||||
|
вердикт совпал в обеих раскладках → это про текст;
|
||||||
|
вердикт следует за ПОЗИЦИЕЙ → это предвзятость судьи, а не качество;
|
||||||
|
чужая модель расходится с Claude → расхождение локализуется в судье, а не в данных.
|
||||||
|
|
||||||
|
Формат ответа судьи намеренно жёсткий и куцый: разбор нам от него не нужен (он уже есть от Claude),
|
||||||
|
нужен голос, который можно сложить.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "editor_contract"))
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||||||
|
sys.path.insert(0, str(HERE))
|
||||||
|
|
||||||
|
import probe as Q # noqa: E402
|
||||||
|
|
||||||
|
PACK = Path.home() / "books" / "gu-zhenren" / "editor-harness" / ("judge-reflow" if "--reflow" in sys.argv else "judge-direct")
|
||||||
|
OUT = PACK.parent / ("judge-cross-reflow" if "--reflow" in sys.argv else "judge-cross")
|
||||||
|
AXES = ["верность", "глоссарий", "русский", "полнота", "чистота", "общий"]
|
||||||
|
|
||||||
|
RUBRIC = """Ты — эксперт по художественному переводу с китайского на русский. Ниже исходник,
|
||||||
|
утверждённый глоссарий и две версии перевода. Сравни их.
|
||||||
|
|
||||||
|
Оси:
|
||||||
|
1 верность — искажения смысла, полярность, числа, потерянные или добавленные смысловые атомы
|
||||||
|
2 глоссарий — канонические формы терминов соблюдены дословно (склонение допустимо)
|
||||||
|
3 русский — живость, отсутствие калек и канцелярита, абзацная вёрстка, оформление прямой речи
|
||||||
|
4 полнота — все предложения исходника переданы, ничего не выброшено и не дописано
|
||||||
|
5 чистота — нет непереведённых фрагментов, иероглифов, латиницы, преамбул, комментариев
|
||||||
|
6 общий — что лучше как издательский перевод
|
||||||
|
|
||||||
|
⚠ Слияние коротких построчных абзацев исходника в связные русские абзацы — ТРЕБОВАНИЕ, а не
|
||||||
|
вольность. Само по себе оно не дефект.
|
||||||
|
|
||||||
|
ФОРМАТ ОТВЕТА — ровно шесть строк, без единого слова пояснений:
|
||||||
|
верность: А|Б|ничья
|
||||||
|
глоссарий: А|Б|ничья
|
||||||
|
русский: А|Б|ничья
|
||||||
|
полнота: А|Б|ничья
|
||||||
|
чистота: А|Б|ничья
|
||||||
|
общий: А|Б|ничья
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def mirror(packet: str) -> str:
|
||||||
|
"""Меняет местами разделы «Версия А» и «Версия Б», не трогая исходник и глоссарий."""
|
||||||
|
m = re.search(r"(## Версия А\n\n)(.*?)(\n## Версия Б\n\n)(.*)$", packet, re.S)
|
||||||
|
if not m:
|
||||||
|
raise ValueError("не нашёл секции версий")
|
||||||
|
head = packet[:m.start()]
|
||||||
|
return f"{head}## Версия А\n\n{m.group(4).rstrip()}\n\n## Версия Б\n\n{m.group(2).rstrip()}\n"
|
||||||
|
|
||||||
|
|
||||||
|
def parse(reply: str) -> dict:
|
||||||
|
out = {}
|
||||||
|
for ln in reply.splitlines():
|
||||||
|
mm = re.match(r"\s*(\w+)\s*:\s*(А|Б|ничья)\s*$", ln.strip())
|
||||||
|
if mm and mm.group(1).lower() in AXES:
|
||||||
|
out[mm.group(1).lower()] = mm.group(2)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def main(model: str, models_short: list[str]) -> None:
|
||||||
|
OUT.mkdir(parents=True, exist_ok=True)
|
||||||
|
nothink = Q.MODELS[model][5] in ("disable", "effort_none")
|
||||||
|
total = 0.0
|
||||||
|
rows = []
|
||||||
|
for ms in models_short:
|
||||||
|
for k in range(6):
|
||||||
|
p = PACK / f"packet-{ms}-w{k}.md"
|
||||||
|
if not p.exists():
|
||||||
|
continue
|
||||||
|
body = p.read_text(encoding="utf-8")
|
||||||
|
for orient, txt in (("прямая", body), ("зеркальная", mirror(body))):
|
||||||
|
pre = "jr" if "--reflow" in sys.argv else "jx"
|
||||||
|
tag = f"{pre}-{ms}-w{k}-{'d' if orient == 'прямая' else 'm'}"
|
||||||
|
msgs = [{"role": "system", "content": RUBRIC},
|
||||||
|
{"role": "user", "content": txt}]
|
||||||
|
rec = Q.call(model, msgs, tag, nothink)
|
||||||
|
total += rec["cost_usd"]
|
||||||
|
v = parse(rec["content"])
|
||||||
|
rows.append(dict(model_short=ms, window=k, orient=orient, verdict=v))
|
||||||
|
time.sleep(0.2)
|
||||||
|
(OUT / f"votes-{model}.json").write_text(json.dumps(rows, ensure_ascii=False, indent=1),
|
||||||
|
encoding="utf-8")
|
||||||
|
print(f"голосов: {len(rows)}; потрачено ${total:.6f}; → votes-{model}.json")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = [a for a in sys.argv[1:] if not a.startswith("--")]
|
||||||
|
mdl = args[0] if args else "deepseek-v4-pro"
|
||||||
|
main(mdl, args[1:] or (["dp", "gl", "lu"] if "--reflow" in sys.argv else ["dp", "gl", "gr", "lu"]))
|
||||||
57
eval/editor_harness/judge_direct.py
Normal file
57
eval/editor_harness/judge_direct.py
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Пакеты для слепого суда БЕЙК-ОФФА: перевод с нуля против редактуры дешёвого черновика.
|
||||||
|
|
||||||
|
⚠ Отличие от `judge_packets.py`, и оно принципиальное: здесь черновик в пакет НЕ кладётся.
|
||||||
|
В сравнении «full-regen против диффа» черновик был нужен как база отсчёта. Здесь он бы РАЗВАЛИЛ
|
||||||
|
СЛЕПОТУ: full-regen сохраняет 65% слов черновика, и судья опознал бы отредактированную версию
|
||||||
|
за секунду. Судья видит только исходник, глоссарий и две готовые версии.
|
||||||
|
|
||||||
|
Раскладка А/Б чередуется по чётности окна, ключ пишется отдельно и судьям не отдаётся.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||||
|
RAW = Path.home() / "books" / "gu-zhenren" / "editor-harness"
|
||||||
|
OUT = RAW / "judge-direct"
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||||||
|
|
||||||
|
import editor_wire_probe as P # noqa: E402
|
||||||
|
from inject_probe import pick_windows # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def text_of(tag: str) -> str | None:
|
||||||
|
f = RAW / f"{tag}.json"
|
||||||
|
return json.loads(f.read_text(encoding="utf-8"))["content"] if f.exists() else None
|
||||||
|
|
||||||
|
|
||||||
|
def build(ms: str) -> int:
|
||||||
|
OUT.mkdir(parents=True, exist_ok=True)
|
||||||
|
key, n = {}, 0
|
||||||
|
for k, (_, src, terms) in enumerate(pick_windows()):
|
||||||
|
a_tag, b_tag = f"eh-{ms}-direct-w{k}", f"eh-{ms}-full-w{k}"
|
||||||
|
if k % 2:
|
||||||
|
a_tag, b_tag = b_tag, a_tag
|
||||||
|
ta, tb = text_of(a_tag), text_of(b_tag)
|
||||||
|
if not ta or not tb:
|
||||||
|
print(f" w{k}: пропуск")
|
||||||
|
continue
|
||||||
|
gloss = "\n".join(f"- {t} → «{P.TERMS[t][0]}»" for t in terms)
|
||||||
|
(OUT / f"packet-{ms}-w{k}.md").write_text(
|
||||||
|
f"# Материалы для оценки перевода, окно {k}\n\n"
|
||||||
|
f"## Исходный текст (китайский)\n\n{src}\n\n"
|
||||||
|
f"## Утверждённый глоссарий (канонические формы обязательны)\n\n{gloss}\n\n"
|
||||||
|
f"## Версия А\n\n{ta}\n\n## Версия Б\n\n{tb}\n", encoding="utf-8")
|
||||||
|
key[f"w{k}"] = {"А": a_tag, "Б": b_tag}
|
||||||
|
n += 1
|
||||||
|
(OUT / f"KEY-{ms}.json").write_text(json.dumps(key, ensure_ascii=False, indent=1),
|
||||||
|
encoding="utf-8")
|
||||||
|
print(f"{ms}: пакетов {n}")
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
for ms in (sys.argv[1:] or ["dp", "gl", "gr", "lu"]):
|
||||||
|
build(ms)
|
||||||
80
eval/editor_harness/judge_packets.py
Normal file
80
eval/editor_harness/judge_packets.py
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Сборка СЛЕПЫХ пакетов для судей качества (санкция владельца 05.08: «вердикты выносят отдельные
|
||||||
|
чистые агенты с чистым контекстом»).
|
||||||
|
|
||||||
|
Что важно в дизайне и почему:
|
||||||
|
· СЛЕПОТА. Судья получает две версии как «Версия А» и «Версия Б» и НЕ знает, какая из них
|
||||||
|
full-regen, а какая — точечная правка. Иначе сработает ожидание («переписанное выглядит
|
||||||
|
глаже» / «диффы — наша ставка»). Раскладка А/Б чередуется по чётности окна, ключ пишется
|
||||||
|
отдельным файлом, который судьям НЕ отдаётся.
|
||||||
|
· ЧИСТЫЙ КОНТЕКСТ. В пакете нет ни гипотез, ни цен, ни названий контрактов, ни слова «дифф».
|
||||||
|
Судья видит: исходник, глоссарий, черновик и две отредактированные версии.
|
||||||
|
· ЧЕРНОВИК ТОЖЕ ДАЁТСЯ. Без него нельзя отличить «версия лучше» от «версия просто не тронула
|
||||||
|
плохое место»: правка оценивается относительно того, что было.
|
||||||
|
· ОСИ РАЗДЕЛЕНЫ. Верность исходнику, соблюдение глоссария и русский язык — три отдельных
|
||||||
|
вердикта плюс общий. Слитая оценка прячет ровно тот случай, ради которого всё затевалось:
|
||||||
|
контракт может быть дешевле и при этом хуже по одной оси.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
RAW = Path.home() / "books" / "gu-zhenren" / "editor-harness"
|
||||||
|
OUT = RAW / "judge"
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "editor_contract"))
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||||||
|
|
||||||
|
import editor_wire_probe as P # noqa: E402
|
||||||
|
from inject_probe import pick_windows # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def text_of(tag: str) -> str | None:
|
||||||
|
"""Для full — content вызова; для дифф-армов — текст ПОСЛЕ применения операций."""
|
||||||
|
ap = RAW / f"{tag}.applied.json"
|
||||||
|
if ap.exists():
|
||||||
|
return json.loads(ap.read_text(encoding="utf-8"))["text"]
|
||||||
|
f = RAW / f"{tag}.json"
|
||||||
|
if f.exists():
|
||||||
|
return json.loads(f.read_text(encoding="utf-8"))["content"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def build(model_short: str, left: str, right: str) -> int:
|
||||||
|
OUT.mkdir(parents=True, exist_ok=True)
|
||||||
|
key = {}
|
||||||
|
n = 0
|
||||||
|
for k, (_, src, terms) in enumerate(pick_windows()):
|
||||||
|
a_tag, b_tag = (f"eh-{model_short}-{left}-w{k}", f"eh-{model_short}-{right}-w{k}")
|
||||||
|
if k % 2: # чередование раскладки — детерминированное, не случайное
|
||||||
|
a_tag, b_tag = b_tag, a_tag
|
||||||
|
ta, tb = text_of(a_tag), text_of(b_tag)
|
||||||
|
if not ta or not tb:
|
||||||
|
print(f" w{k}: пропуск — нет {a_tag if not ta else b_tag}")
|
||||||
|
continue
|
||||||
|
gloss = "\n".join(f"- {t} → «{P.TERMS[t][0]}»" for t in terms)
|
||||||
|
draft = json.loads((Path.home() / "books/gu-zhenren/bank-arbitration" /
|
||||||
|
f"inj-w{k}-B.json").read_text(encoding="utf-8"))["content"]
|
||||||
|
packet = (
|
||||||
|
f"# Материалы для оценки перевода, окно {k}\n\n"
|
||||||
|
f"## Исходный текст (китайский)\n\n{src}\n\n"
|
||||||
|
f"## Утверждённый глоссарий (канонические формы обязательны)\n\n{gloss}\n\n"
|
||||||
|
f"## Черновой перевод ДО редактуры\n\n{draft}\n\n"
|
||||||
|
f"## Версия А (после редактуры)\n\n{ta}\n\n"
|
||||||
|
f"## Версия Б (после редактуры)\n\n{tb}\n")
|
||||||
|
(OUT / f"packet-{model_short}-w{k}.md").write_text(packet, encoding="utf-8")
|
||||||
|
key[f"w{k}"] = {"А": a_tag, "Б": b_tag}
|
||||||
|
n += 1
|
||||||
|
(OUT / f"KEY-{model_short}.json").write_text(json.dumps(key, ensure_ascii=False, indent=1),
|
||||||
|
encoding="utf-8")
|
||||||
|
print(f"пакетов собрано: {n}; ключ раскладки — KEY-{model_short}.json (судьям НЕ отдавать)")
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ms = sys.argv[1] if len(sys.argv) > 1 else "dp"
|
||||||
|
left = sys.argv[2] if len(sys.argv) > 2 else "full"
|
||||||
|
right = sys.argv[3] if len(sys.argv) > 3 else "diff-harness"
|
||||||
|
build(ms, left, right)
|
||||||
63
eval/editor_harness/judge_reflow.py
Normal file
63
eval/editor_harness/judge_reflow.py
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Пакеты для слепого суда РЕШАЮЩЕГО арма: один проход С МАНДАТОМ ПЕРЕВЁРСТКИ против связки.
|
||||||
|
|
||||||
|
⚠ Отличие от `judge_packets.py`, и оно принципиальное: здесь черновик в пакет НЕ кладётся.
|
||||||
|
В сравнении «full-regen против диффа» черновик был нужен как база отсчёта. Здесь он бы РАЗВАЛИЛ
|
||||||
|
СЛЕПОТУ: full-regen сохраняет 65% слов черновика, и судья опознал бы отредактированную версию
|
||||||
|
за секунду. Судья видит только исходник, глоссарий и две готовые версии.
|
||||||
|
|
||||||
|
Раскладка А/Б чередуется по чётности окна, ключ пишется отдельно и судьям не отдаётся.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||||
|
RAW = Path.home() / "books" / "gu-zhenren" / "editor-harness"
|
||||||
|
OUT = RAW / "judge-reflow"
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||||||
|
|
||||||
|
import editor_wire_probe as P # noqa: E402
|
||||||
|
from inject_probe import pick_windows # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
OFFSET = {"dp": 0, "gl": 1, "lu": 0, "gr": 1}
|
||||||
|
|
||||||
|
|
||||||
|
def text_of(tag: str) -> str | None:
|
||||||
|
f = RAW / f"{tag}.json"
|
||||||
|
return json.loads(f.read_text(encoding="utf-8"))["content"] if f.exists() else None
|
||||||
|
|
||||||
|
|
||||||
|
def build(ms: str) -> int:
|
||||||
|
OUT.mkdir(parents=True, exist_ok=True)
|
||||||
|
key, n = {}, 0
|
||||||
|
for k, (_, src, terms) in enumerate(pick_windows()):
|
||||||
|
a_tag, b_tag = f"eh-{ms}-direct-reflow-w{k}", f"eh-{ms}-full-w{k}"
|
||||||
|
# ⚠ ФИКС 05.08: в прошлом раунде правило раскладки было ОДНО на все модели, из-за чего
|
||||||
|
# позиция и арм слиплись (арм в позиции А выиграл 12/12, в Б — 6/12). Здесь у каждой
|
||||||
|
# модели свой сдвиг, поэтому позиция не коррелирует с армом по набору.
|
||||||
|
if (k + OFFSET[ms]) % 2:
|
||||||
|
a_tag, b_tag = b_tag, a_tag
|
||||||
|
ta, tb = text_of(a_tag), text_of(b_tag)
|
||||||
|
if not ta or not tb:
|
||||||
|
print(f" w{k}: пропуск")
|
||||||
|
continue
|
||||||
|
gloss = "\n".join(f"- {t} → «{P.TERMS[t][0]}»" for t in terms)
|
||||||
|
(OUT / f"packet-{ms}-w{k}.md").write_text(
|
||||||
|
f"# Материалы для оценки перевода, окно {k}\n\n"
|
||||||
|
f"## Исходный текст (китайский)\n\n{src}\n\n"
|
||||||
|
f"## Утверждённый глоссарий (канонические формы обязательны)\n\n{gloss}\n\n"
|
||||||
|
f"## Версия А\n\n{ta}\n\n## Версия Б\n\n{tb}\n", encoding="utf-8")
|
||||||
|
key[f"w{k}"] = {"А": a_tag, "Б": b_tag}
|
||||||
|
n += 1
|
||||||
|
(OUT / f"KEY-{ms}.json").write_text(json.dumps(key, ensure_ascii=False, indent=1),
|
||||||
|
encoding="utf-8")
|
||||||
|
print(f"{ms}: пакетов {n}")
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
for ms in (sys.argv[1:] or ["dp", "gl", "lu"]):
|
||||||
|
build(ms)
|
||||||
81
eval/editor_harness/judge_variance.py
Normal file
81
eval/editor_harness/judge_variance.py
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Разброс СУДЬИ на повторе одной и той же клетки — последняя незакрытая дыра методики.
|
||||||
|
|
||||||
|
Проблема. Во всех раундах судейства каждая пара текстов судилась ровно один раз на раскладку.
|
||||||
|
Значит любое расхождение между армами нельзя отделить от собственного шума судьи: если он на
|
||||||
|
ОДНОМ И ТОМ ЖЕ входе отвечает по-разному, то «связка 6:4» может целиком лежать внутри его дисперсии.
|
||||||
|
Отчёт это признаёт («разброс судьи на повторе одной клетки не мерен ни разу»), но не измеряет.
|
||||||
|
|
||||||
|
Здесь: те же пакеты, та же ориентация, N независимых голосов. Меряется доля клеток, где все N
|
||||||
|
голосов совпали. Это ВЕРХНЯЯ граница разрешающей способности всей судейской методики: разница между
|
||||||
|
армами меньше собственного разброса судьи не может считаться сигналом.
|
||||||
|
|
||||||
|
Температура намеренно та же, что в основном судействе, — меряется разброс РЕАЛЬНОГО инструмента,
|
||||||
|
а не гипотетического детерминированного.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from collections import Counter
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "editor_contract"))
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||||||
|
sys.path.insert(0, str(HERE))
|
||||||
|
|
||||||
|
import probe as Q # noqa: E402
|
||||||
|
import judge_crossmodel as J # noqa: E402
|
||||||
|
|
||||||
|
PACK = Path.home() / "books" / "gu-zhenren" / "editor-harness" / "judge-reflow"
|
||||||
|
REPS = 3
|
||||||
|
|
||||||
|
|
||||||
|
def main(model: str, ms: str) -> None:
|
||||||
|
nothink = Q.MODELS[model][5] in ("disable", "effort_none")
|
||||||
|
votes = {}
|
||||||
|
spent = 0.0
|
||||||
|
for k in range(6):
|
||||||
|
p = PACK / f"packet-{ms}-w{k}.md"
|
||||||
|
if not p.exists():
|
||||||
|
continue
|
||||||
|
body = p.read_text(encoding="utf-8")
|
||||||
|
for r in range(1, REPS + 1):
|
||||||
|
rec = Q.call(model, [{"role": "system", "content": J.RUBRIC},
|
||||||
|
{"role": "user", "content": body}],
|
||||||
|
f"jv-{ms}-w{k}-r{r}", nothink)
|
||||||
|
spent += rec["cost_usd"]
|
||||||
|
votes.setdefault(k, []).append(J.parse(rec["content"]))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
print(f"\nРАЗБРОС СУДЬИ {model} на {REPS} повторах одного входа (модель материала {ms})\n")
|
||||||
|
print(f"{'ось':11s}{'клеток':>8s}{'единогласно':>13s}{'доля':>8s}")
|
||||||
|
print("-" * 42)
|
||||||
|
total_cells = total_unan = 0
|
||||||
|
for ax in J.AXES:
|
||||||
|
unan = cells = 0
|
||||||
|
for k, vs in votes.items():
|
||||||
|
picks = [v.get(ax) for v in vs if v.get(ax)]
|
||||||
|
if len(picks) < REPS:
|
||||||
|
continue
|
||||||
|
cells += 1
|
||||||
|
unan += int(len(set(picks)) == 1)
|
||||||
|
if cells:
|
||||||
|
print(f"{ax:11s}{cells:8d}{unan:13d}{unan / cells:8.0%}")
|
||||||
|
total_cells += cells
|
||||||
|
total_unan += unan
|
||||||
|
if total_cells:
|
||||||
|
print(f"\nИТОГО единогласно {total_unan}/{total_cells} = {total_unan / total_cells:.0%}")
|
||||||
|
print("⇒ разница между армами МЕНЬШЕ этого разброса сигналом считаться не может.")
|
||||||
|
print(f"потрачено ${spent:.6f}")
|
||||||
|
for k, vs in sorted(votes.items()):
|
||||||
|
c = Counter(v.get("общий") for v in vs)
|
||||||
|
print(f" w{k} общий вердикт по повторам: {dict(c)}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(sys.argv[1] if len(sys.argv) > 1 else "deepseek-v4-pro",
|
||||||
|
sys.argv[2] if len(sys.argv) > 2 else "dp")
|
||||||
356
eval/editor_harness/probe.py
Normal file
356
eval/editor_harness/probe.py
Normal file
|
|
@ -0,0 +1,356 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""ЭКСП 20: контракт редактора, скопированный с харнесов, против full-regen — на нескольких моделях.
|
||||||
|
|
||||||
|
Повод (владелец 05.08): эксп-19 намерил «дифф в 2.3–2.9× дороже full-regen» и объяснил это несъёмным
|
||||||
|
размышлением. Пере-проверка арифметики дефекта не нашла (0 расхождений из 24 при пересчёте цены с
|
||||||
|
нуля), но нашла ДЕФЕКТ ДИЗАЙНА: мой дифф-промпт требовал гарантировать уникальность якоря с первого
|
||||||
|
раза, без обратной связи. Настоящие харнесы так не делают — они ВОЗВРАЩАЮТ ОШИБКУ и дают починить
|
||||||
|
(opencode `tool/edit.txt`: «Found multiple matches… provide a larger string»; gemini-cli держит под
|
||||||
|
это отдельный дешёвый `llm-edit-fixer`). То есть эксп-19 померил «дифф без права на ошибку».
|
||||||
|
|
||||||
|
Три гипотезы, которые арм-матрица разводит:
|
||||||
|
H-ФОРМАТ цену держит требование one-shot-уникальности ⇒ харнес-контракт (право на ошибку +
|
||||||
|
раунд починки) должен быть заметно дешевле строгого.
|
||||||
|
H-РЕШЕНИЕ цену держит необходимость НАЙТИ дефект; full-regen не ищет — он переводит заново,
|
||||||
|
попутно исправляя ⇒ арм `locate` (только найди, ничего не чини и не форматируй)
|
||||||
|
будет стоить как дифф, и тогда формат ни при чём.
|
||||||
|
H-МОДЕЛЬ цену держит несъёмное размышление DeepSeek ⇒ на glm-5 с ВЫКЛЮЧЕННЫМ мышлением
|
||||||
|
экономика диффа обязана перевернуться в его пользу.
|
||||||
|
|
||||||
|
НОВОЕ ПРОТИВ ЭКСП-19: сохраняется ТЕКСТ размышления (`reasoning_content`), а не только его длина —
|
||||||
|
без этого нельзя посмотреть, на что оно тратится, и эксп-19 этого сделать не мог.
|
||||||
|
|
||||||
|
Гардрейл: у DeepSeek thinking НЕ отключается ни при каких условиях (эхо-мина, CLAUDE.md).
|
||||||
|
Отключение мышления применяется ТОЛЬКО к glm-5 (`control: extra_body_disable`, models.yaml).
|
||||||
|
"""
|
||||||
|
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")
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
RAW = Path.home() / "books" / "gu-zhenren" / "editor-harness"
|
||||||
|
RAW.mkdir(parents=True, exist_ok=True)
|
||||||
|
load_dotenv(REPO / "eval" / ".env")
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "editor_contract"))
|
||||||
|
|
||||||
|
import editor_wire_probe as P # noqa: E402 рендер + окна + блок закона
|
||||||
|
from apply import apply_ops, parse_ops, fold, _fold_map # noqa: E402 аппликатор (самотест 15/15)
|
||||||
|
from inject_probe import pick_windows, render_translator, block_for # noqa: E402
|
||||||
|
|
||||||
|
# ⚠ Потолок поднят 05.08 с $0.60 до $1.00 ДО прогона (объявлено владельцу): добавлен арм с
|
||||||
|
# ВКЛЮЧЁННЫМ мышлением для glm-5/grok/luna. Без него сравнение несправедливо — deepseek-v4-pro
|
||||||
|
# мышление выключить не может, и разница «дифф дорог у dp, дёшев у прочих» смешивает модель с режимом.
|
||||||
|
# Потолок поднят вторично 05.08 с $1.00 до $2.00, объявлено ДО прогона: на $1.00 проекционный гард
|
||||||
|
# заблокировал РЕШАЮЩИЙ арм (C0+перевёрстка) — тот единственный, который проверяет, покупается ли
|
||||||
|
# промптом то самое, чем оправдан второй проход. Санкция владельца — $3-5 на модель.
|
||||||
|
CEILING_USD = 2.00
|
||||||
|
HARD_STOP = 1.80
|
||||||
|
|
||||||
|
# (base_url, env, in, cached, out, как гасить размышление)
|
||||||
|
MODELS = {
|
||||||
|
"deepseek-v4-pro": ("https://api.deepseek.com/v1", "DEEPSEEK_API_KEY", 0.435, 0.003625, 0.87, None),
|
||||||
|
"glm-5": ("https://api.z.ai/api/paas/v4", "ZAI_API_KEY", 1.0, 0.2, 3.2, "disable"),
|
||||||
|
"deepseek-v4-flash": ("https://api.deepseek.com/v1", "DEEPSEEK_API_KEY", 0.14, 0.0028, 0.28, None),
|
||||||
|
# Съёмное мышление через ЯВНЫЙ reasoning_effort:"none" (models.yaml grok: control effort/off_effort
|
||||||
|
# none; для gpt-5.6 живой замер пробы C: none → reasoning_tokens 0). Цены — прайс-страница
|
||||||
|
# вендора, сверена живьём 05.08: изменений против 04.08 нет.
|
||||||
|
"grok-4.3": ("https://api.x.ai/v1", "XAI_API_KEY", 1.25, 1.25, 2.50, "effort_none"),
|
||||||
|
"gpt-5.6-luna": ("https://api.openai.com/v1", "OPENAI_API_KEY", 0.20, 0.02, 1.20, "effort_none"),
|
||||||
|
}
|
||||||
|
OPENAI_FAMILY = {"gpt-5.6-luna"} # max_completion_tokens вместо max_tokens, temperature не шлём
|
||||||
|
# Контракт → (файл промпта, откуда). `full` и `diff-strict` берутся ИЗ БОЕВОГО/эксп-19 без правок,
|
||||||
|
# чтобы сравнение шло против ровно того, что уже намерено.
|
||||||
|
CONTRACTS = {
|
||||||
|
"full": REPO / "backend/prompts/zh-ru/editor.md",
|
||||||
|
"diff-strict": REPO / "eval/editor_contract/prompts/editor-diff.md",
|
||||||
|
"diff-harness": HERE / "prompts/editor-diff-harness.md",
|
||||||
|
"locate": HERE / "prompts/editor-locate.md",
|
||||||
|
"direct": REPO / "backend/prompts/zh-ru/translator.md", # спец-путь, см. messages()
|
||||||
|
"direct-reflow": HERE / "prompts/translator-reflow.md", # спец-путь, см. messages()
|
||||||
|
}
|
||||||
|
# Английская цель. `full` — ПОЛНОЕ зеркало боевого editor.md вместе с блоком дискурс-перевёрстки:
|
||||||
|
# в эксп-19 зеркало было обеднено ровно на этот блок, и арм E из-за этого не засчитали.
|
||||||
|
CONTRACTS_EN = {
|
||||||
|
"full": HERE / "prompts/editor-en.md",
|
||||||
|
"diff-harness": HERE / "prompts/editor-diff-harness-en.md",
|
||||||
|
"locate": HERE / "prompts/editor-locate-en.md",
|
||||||
|
}
|
||||||
|
EN_FORMS = {"空窍": "Aperture", "真元": "primeval essence", "元石": "primeval stone",
|
||||||
|
"元海": "sea of primeval essence", "蛊师": "Gu Master", "族长": "clan leader",
|
||||||
|
"月光蛊": "Moonlight Gu", "春秋蝉": "Spring Autumn Cicada"}
|
||||||
|
EN_HEADER = ("CANONICAL RENDERINGS of names and terms (in the draft, the source term on the left MUST "
|
||||||
|
"be rendered exactly by the form on the right — bring any divergence to it, inflecting by "
|
||||||
|
"context; do not introduce other variants and do not change anything else):")
|
||||||
|
EN_VARS = dict(P.RENDER_VARS, target_lang="en", audience="adult webnovel readers",
|
||||||
|
genre="webnovel", transcription="pinyin")
|
||||||
|
OLD_RAW = Path.home() / "books" / "gu-zhenren" / "bank-arbitration"
|
||||||
|
|
||||||
|
|
||||||
|
def client(model: str) -> OpenAI:
|
||||||
|
base, env, *_ = MODELS[model]
|
||||||
|
return OpenAI(api_key=os.environ[env], base_url=base, timeout=900)
|
||||||
|
|
||||||
|
|
||||||
|
def price(model: str, pt: int, cached: int, ct: int) -> float:
|
||||||
|
_, _, pin, pcache, pout, _ = MODELS[model]
|
||||||
|
return (pt - cached) / 1e6 * pin + cached / 1e6 * pcache + ct / 1e6 * pout
|
||||||
|
|
||||||
|
|
||||||
|
def render(path: Path, text: str, draft: str, en: bool = False) -> tuple[str, str]:
|
||||||
|
"""Рендер как у движка. ⚠ ФИКС 05.08 (дефект пойман В ХОДЕ прогона): блок `---FEWSHOT---`
|
||||||
|
ОБЯЗАН дропаться — в проде `stages[edit].few_shot: false`, и эксп-19 его тоже дропал.
|
||||||
|
Первая версия резала только по `---USER---`, из-за чего арм `full` уехал с примерами
|
||||||
|
перевёрстки внутри системного промпта, то есть база сравнения была не боевым контрактом."""
|
||||||
|
canon = P.strip_comments(path.read_text(encoding="utf-8"))
|
||||||
|
sys_part, user = canon.split(P.USER_SEP, 1)
|
||||||
|
core = sys_part.split(P.FEWSHOT_SEP, 1)[0].strip()
|
||||||
|
old_vars = P.RENDER_VARS
|
||||||
|
if en:
|
||||||
|
P.RENDER_VARS = EN_VARS
|
||||||
|
try:
|
||||||
|
return P.render(core, text, draft), P.render(user.strip(), text, draft)
|
||||||
|
finally:
|
||||||
|
P.RENDER_VARS = old_vars
|
||||||
|
|
||||||
|
|
||||||
|
def en_block(terms: list[str]) -> str:
|
||||||
|
return EN_HEADER + "\n" + "\n".join(f"- {t} → «{EN_FORMS[t]}»" for t in terms if t in EN_FORMS)
|
||||||
|
|
||||||
|
|
||||||
|
def messages(contract: str, text: str, draft: str, block: str | None,
|
||||||
|
en: bool = False) -> list[dict]:
|
||||||
|
# `direct` — БЕЙК-ОФФ (запрос владельца 05.08): перевод исходника БЕЗ черновика, один проход,
|
||||||
|
# боевым translator.md и боевым глоссарий-блоком транслятора. Сравнивается с `full`, где та же
|
||||||
|
# модель переписывает черновик flash. Это тот самый бейк-офф, про который конфиг говорит
|
||||||
|
# «draft качеством не мерен» (D30.6) и который не гонялся ни разу.
|
||||||
|
if contract in ("direct", "direct-reflow"):
|
||||||
|
if contract == "direct-reflow":
|
||||||
|
# РЕШАЮЩИЙ арм: тот же транслятор, но с ДОСЛОВНЫМ блоком перевёрстки редактора.
|
||||||
|
# Рендер идёт общим путём (плейсхолдеры + срез FEWSHOT), а не render_translator,
|
||||||
|
# потому что файл собран из двух боевых промптов и лежит в зоне полигона.
|
||||||
|
sys_msg, user = render(HERE / "prompts" / "translator-reflow.md", text, "")
|
||||||
|
# ⚠ боевой render_translator обрезает user ПОСЛЕ подстановки, общий рендер — до;
|
||||||
|
# разница в один перенос строки. Уравниваем, иначе армы отличаются не только лечением.
|
||||||
|
user = user.strip()
|
||||||
|
else:
|
||||||
|
sys_msg, user = render_translator(text)
|
||||||
|
terms = [t for t in P.TERMS if t in text]
|
||||||
|
return [{"role": "system", "content": sys_msg},
|
||||||
|
{"role": "system", "content": block_for(terms, None, False)},
|
||||||
|
{"role": "user", "content": user}]
|
||||||
|
tpl = (CONTRACTS_EN if en else CONTRACTS)[contract]
|
||||||
|
sys_msg, user = render(tpl, text, draft, en)
|
||||||
|
m = [{"role": "system", "content": sys_msg}]
|
||||||
|
if block and block.strip():
|
||||||
|
m.append({"role": "system", "content": block})
|
||||||
|
m.append({"role": "user", "content": user})
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
def call(model: str, msgs: list[dict], tag: str, nothink: bool, think_low: bool = False) -> dict:
|
||||||
|
"""nothink применяется ТОЛЬКО там, где провайдер это умеет; для DeepSeek — запрещено (эхо-мина)."""
|
||||||
|
f = RAW / f"{tag}.json"
|
||||||
|
if f.exists():
|
||||||
|
return json.loads(f.read_text(encoding="utf-8"))
|
||||||
|
kw: dict = dict(model=model, messages=msgs)
|
||||||
|
if model in OPENAI_FAMILY:
|
||||||
|
kw["max_completion_tokens"] = 16000 # quirks 00: не max_tokens у reasoning-моделей
|
||||||
|
else:
|
||||||
|
kw["max_tokens"] = 16000
|
||||||
|
kw["temperature"] = 0.4
|
||||||
|
ctrl = MODELS[model][5]
|
||||||
|
extra = {}
|
||||||
|
if think_low:
|
||||||
|
# Режим «мышление ВКЛЮЧЕНО, но дёшево»: явный low. У glm выключатель живёт в extra_body,
|
||||||
|
# поэтому «включено» = просто НЕ слать disable. У grok/luna ручка — reasoning_effort.
|
||||||
|
if ctrl == "effort_none":
|
||||||
|
if model in OPENAI_FAMILY:
|
||||||
|
kw["reasoning_effort"] = "low"
|
||||||
|
else:
|
||||||
|
extra["reasoning_effort"] = "low"
|
||||||
|
elif nothink:
|
||||||
|
if ctrl == "disable":
|
||||||
|
extra["thinking"] = {"type": "disabled"}
|
||||||
|
elif ctrl == "effort_none":
|
||||||
|
if model in OPENAI_FAMILY:
|
||||||
|
kw["reasoning_effort"] = "none" # плоский параметр, живой замер пробы C
|
||||||
|
else:
|
||||||
|
extra["reasoning_effort"] = "none"
|
||||||
|
else:
|
||||||
|
raise SystemExit(f"ОТКАЗ: у {model} мышление гасить нельзя (ctrl={ctrl}); эхо-мина")
|
||||||
|
if extra:
|
||||||
|
kw["extra_body"] = extra
|
||||||
|
t0 = time.time()
|
||||||
|
r = client(model).chat.completions.create(**kw)
|
||||||
|
ch = r.choices[0]
|
||||||
|
u = r.usage
|
||||||
|
pt = getattr(u, "prompt_tokens", 0) or 0
|
||||||
|
ct = getattr(u, "completion_tokens", 0) or 0
|
||||||
|
cached = getattr(getattr(u, "prompt_tokens_details", None), "cached_tokens", 0) or 0
|
||||||
|
reasoning = getattr(ch.message, "reasoning_content", None) or ""
|
||||||
|
rt = getattr(getattr(u, "completion_tokens_details", None), "reasoning_tokens", 0) or 0
|
||||||
|
rec = dict(tag=tag, model=model, model_returned=r.model, nothink=nothink,
|
||||||
|
extra_body=extra, effort_param=kw.get("reasoning_effort"),
|
||||||
|
reasoning_tokens=rt, finish=ch.finish_reason,
|
||||||
|
ts=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||||
|
prompt_tokens=pt, cached_tokens=cached, completion_tokens=ct,
|
||||||
|
cost_usd=round(price(model, pt, cached, ct), 6),
|
||||||
|
latency_s=round(time.time() - t0, 1),
|
||||||
|
content=ch.message.content or "",
|
||||||
|
reasoning_chars=len(reasoning),
|
||||||
|
reasoning=reasoning, # ⚠ НОВОЕ: текст, а не только длина
|
||||||
|
messages=msgs)
|
||||||
|
f.write_text(json.dumps(rec, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||||
|
print(f"[{tag}] {model}{' nothink' if nothink else ''} finish={ch.finish_reason} "
|
||||||
|
f"in={pt}(c{cached}) out={ct} размышл={len(reasoning)}зн текст={len(rec['content'])}зн "
|
||||||
|
f"${rec['cost_usd']:.6f} {rec['latency_s']}s")
|
||||||
|
return rec
|
||||||
|
|
||||||
|
|
||||||
|
REPAIR = ("Часть твоих операций НЕ ПРИМЕНИЛАСЬ. Ниже — они и причина отказа.\n"
|
||||||
|
"Дошли исправленные версии ТОЛЬКО для них, в том же формате SEARCH/REPLACE. "
|
||||||
|
"Не придумывай новых правок и не меняй смысл замены — чини только якорь SEARCH.\n"
|
||||||
|
"Если исправить нельзя, выведи NO_CHANGE.\n\n")
|
||||||
|
|
||||||
|
|
||||||
|
def classify_ops(draft: str, reply: str) -> list[tuple[str, str, str]]:
|
||||||
|
"""[(search, replace, статус)] по тем же правилам, что у аппликатора.
|
||||||
|
|
||||||
|
⚠ Своя реализация, а НЕ чтение `ApplyResult.details`: селф-ревью 05.08 показало, что details —
|
||||||
|
список СТРОК с обрезанным до 60 знаков превью, из него исходный якорь не восстановить, а раунд
|
||||||
|
починки обязан вернуть модели её собственный текст операции целиком.
|
||||||
|
`apply.py` при этом не правится — он заморожен и самопроверен 15/15.
|
||||||
|
"""
|
||||||
|
ops, malformed = parse_ops(reply)
|
||||||
|
if malformed:
|
||||||
|
return []
|
||||||
|
hay, _ = _fold_map(draft)
|
||||||
|
out = []
|
||||||
|
for search, replace in ops:
|
||||||
|
key = fold(search)
|
||||||
|
if not key:
|
||||||
|
out.append((search, replace, "reject_empty"))
|
||||||
|
continue
|
||||||
|
n = hay.count(key)
|
||||||
|
out.append((search, replace,
|
||||||
|
"ok" if n == 1 else ("reject_notfound" if n == 0 else "reject_ambiguous")))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def repair_round(model: str, base_msgs: list[dict], first: str, draft: str, tag: str,
|
||||||
|
nothink: bool) -> dict | None:
|
||||||
|
"""Копия харнес-петли: отклонённые операции возвращаются модели С ТЕКСТОМ ОШИБКИ."""
|
||||||
|
bad = [(s, r, st) for s, r, st in classify_ops(draft, first) if st != "ok"]
|
||||||
|
if not bad:
|
||||||
|
return None
|
||||||
|
why = {"reject_notfound": "фрагмент не найден в черновике посимвольно",
|
||||||
|
"reject_ambiguous": "фрагмент встречается несколько раз — нужен более длинный якорь",
|
||||||
|
"reject_empty": "пустой якорь"}
|
||||||
|
lines = [f"--- ОШИБКА: {why.get(st, st)}\n<<<<<<< SEARCH\n{s}\n=======\n{r}\n>>>>>>> REPLACE"
|
||||||
|
for s, r, st in bad]
|
||||||
|
msgs = base_msgs + [{"role": "assistant", "content": first},
|
||||||
|
{"role": "user", "content": REPAIR + "\n\n".join(lines)}]
|
||||||
|
return call(model, msgs, f"{tag}-fix", nothink)
|
||||||
|
|
||||||
|
|
||||||
|
def spent() -> float:
|
||||||
|
"""Сумма по ЗАПИСЯМ ВЫЗОВОВ. ⚠ Фильтр на dict обязателен: в том же каталоге лежат сводки
|
||||||
|
армов (`repair-*.json` — список строк), и без фильтра `spent()` падал, блокируя весь риг."""
|
||||||
|
total = 0.0
|
||||||
|
for f in RAW.glob("*.json"):
|
||||||
|
if f.name.endswith(".applied.json"):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
d = json.loads(f.read_text(encoding="utf-8"))
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if isinstance(d, dict) and "cost_usd" in d:
|
||||||
|
total += d["cost_usd"]
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
def plan(models: list[str], contracts: list[str], en: bool = False,
|
||||||
|
think_on: bool = False) -> list[tuple]:
|
||||||
|
out = []
|
||||||
|
for k, (_, buf, terms) in enumerate(pick_windows()):
|
||||||
|
if en:
|
||||||
|
f = OLD_RAW / f"fp-draft-E{k}.json"
|
||||||
|
if not f.exists():
|
||||||
|
print(f" w{k}: нет английского черновика {f.name}, пропуск")
|
||||||
|
continue
|
||||||
|
draft = json.loads(f.read_text(encoding="utf-8"))["content"]
|
||||||
|
blk = en_block(terms)
|
||||||
|
else:
|
||||||
|
draft = P.draft_of(k)
|
||||||
|
blk = P.editor_block(terms, P.HEADER_LAW_CLAUSE, None)
|
||||||
|
for m in models:
|
||||||
|
# ⚠ ФИКС 05.08 (пойман на первом вызове grok): было `== "disable"`, из-за чего модели
|
||||||
|
# с механизмом `effort_none` (grok-4.3, gpt-5.6-luna) уезжали С ВКЛЮЧЁННЫМ мышлением —
|
||||||
|
# арм измерял бы не то, что заявлено. Гасим везде, где провайдер это умеет.
|
||||||
|
nothink = (not think_on) and MODELS[m][5] in ("disable", "effort_none")
|
||||||
|
short = {"deepseek-v4-pro": "dp", "glm-5": "gl", "deepseek-v4-flash": "df",
|
||||||
|
"grok-4.3": "gr", "gpt-5.6-luna": "lu"}[m]
|
||||||
|
for c in contracts:
|
||||||
|
pre = "ehen" if en else ("eht" if think_on else "eh")
|
||||||
|
out.append((f"{pre}-{short}-{c}-w{k}", m, c, buf, draft, blk, nothink))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_run(models, contracts, dry, en=False, think_on=False):
|
||||||
|
pl = plan(models, contracts, en, think_on)
|
||||||
|
total = spent()
|
||||||
|
print(f"план: {len(pl)} вызовов; уже потрачено ${total:.6f}; потолок ${CEILING_USD}")
|
||||||
|
if dry:
|
||||||
|
for tag, m, c, *_ in pl:
|
||||||
|
print(f" {tag:28s} {m:18s} {c}")
|
||||||
|
return
|
||||||
|
for tag, m, c, buf, draft, blk, nothink in pl:
|
||||||
|
worst = 16000 / 1e6 * MODELS[m][4] + 6000 / 1e6 * MODELS[m][2]
|
||||||
|
if total + worst > CEILING_USD or total >= HARD_STOP:
|
||||||
|
print(f"СТОП (проекционный гард): ${total:.4f} + худший ${worst:.4f} > ${CEILING_USD}")
|
||||||
|
return
|
||||||
|
msgs = messages(c, buf, draft, blk, en)
|
||||||
|
rec = call(m, msgs, tag, nothink, think_on)
|
||||||
|
total = spent()
|
||||||
|
if c.startswith("diff"):
|
||||||
|
res = apply_ops(draft, rec["content"])
|
||||||
|
fix = None
|
||||||
|
if c == "diff-harness":
|
||||||
|
fix = repair_round(m, msgs, rec["content"], draft, tag, nothink)
|
||||||
|
if fix:
|
||||||
|
res2 = apply_ops(res.text, fix["content"])
|
||||||
|
res.applied += res2.applied
|
||||||
|
res.ops_total += res2.ops_total
|
||||||
|
res.text = res2.text
|
||||||
|
total = spent()
|
||||||
|
(RAW / f"{tag}.applied.json").write_text(json.dumps(dict(
|
||||||
|
tag=tag, ops_total=res.ops_total, applied=res.applied, no_change=res.no_change,
|
||||||
|
malformed=res.malformed, rejected_notfound=res.rejected_notfound,
|
||||||
|
rejected_ambiguous=res.rejected_ambiguous, rejected_overlap=res.rejected_overlap,
|
||||||
|
rejected_empty=res.rejected_empty, repaired=bool(fix), text=res.text),
|
||||||
|
ensure_ascii=False, indent=1), encoding="utf-8")
|
||||||
|
print(f" apply: ops={res.ops_total} applied={res.applied} "
|
||||||
|
f"no_change={res.no_change} починка={'да' if fix else 'нет'}")
|
||||||
|
time.sleep(0.3)
|
||||||
|
print(f"ИТОГО эксп 20: ${spent():.6f}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--models", default="deepseek-v4-pro")
|
||||||
|
ap.add_argument("--contracts", default="full,diff-harness,locate")
|
||||||
|
ap.add_argument("--dry", action="store_true")
|
||||||
|
ap.add_argument("--en", action="store_true", help="английская цель (черновики fp-draft-E*)")
|
||||||
|
ap.add_argument("--think", action="store_true", help="мышление ВКЛЮЧЕНО (low), префикс eht-")
|
||||||
|
a = ap.parse_args()
|
||||||
|
cmd_run([m.strip() for m in a.models.split(",")],
|
||||||
|
[c.strip() for c in a.contracts.split(",")], a.dry, a.en, a.think)
|
||||||
42
eval/editor_harness/prompts/editor-diff-harness-en.md
Normal file
42
eval/editor_harness/prompts/editor-diff-harness-en.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<!-- ПОЛИГОН, эксп 20. Английская версия харнес-контракта. Зеркало `editor-diff-harness.md`
|
||||||
|
строка в строку: тот же мандат содержания, то же право на ошибку, тот же формат SEARCH/REPLACE.
|
||||||
|
Меняется только язык цели и связанные с ним указания. -->
|
||||||
|
You are a literary editor of a fiction translation into English. You are given the SOURCE text and its draft translation.
|
||||||
|
Book genre: {{genre}}. Audience: {{audience}}. Book: «{{title}}».
|
||||||
|
|
||||||
|
Your mandate is TARGETED editing. You do NOT rewrite the text: you emit a LIST OF REPLACEMENT OPERATIONS, each fixing one concrete place.
|
||||||
|
|
||||||
|
What to fix, in order of importance:
|
||||||
|
- FIDELITY: distortions of meaning against the source (language «{{source_lang}}») — inverted polarity, wrong numbers and ranks, lost or added meaning atoms, misread realia, calqued gestures and idioms (e.g. a bow as "struck his head").
|
||||||
|
- TERMS: proper names, terms and realia STRICTLY per the supplied glossary (if attached): bring any divergence to the canonical form, inflecting by context; do not invent other renderings.
|
||||||
|
- STYLE: calques from «{{source_lang}}», officialese, unnatural constructions, broken word forms. BUT a repetition standing in the same (parallel) positions of the source is an authorial refrain: keep it.
|
||||||
|
- Units and devices (common to zh→en): convert MEASURES into units the reader knows (时辰 = 2 hours, 三个时辰 ≈ six hours; the fraction 成 means tenths: 六成六 = 66%, not "6.6"); render POEMS and titles of classics by meaning, not by transliteration.
|
||||||
|
- Honorifics: {{honorifics}}. Foreignization/domestication balance: {{venuti}}.
|
||||||
|
|
||||||
|
Do NOT re-paragraph, do NOT merge or split sentences for structure. Do NOT "improve" places that are already correct and read normally. **An empty list of edits is a full and respected answer.**
|
||||||
|
|
||||||
|
RESPONSE FORMAT.
|
||||||
|
|
||||||
|
If there are no edits, output exactly one line:
|
||||||
|
NO_CHANGE
|
||||||
|
|
||||||
|
Otherwise output one or more operations, each exactly in this form:
|
||||||
|
|
||||||
|
<<<<<<< SEARCH
|
||||||
|
verbatim fragment of the DRAFT to replace
|
||||||
|
=======
|
||||||
|
replacement text
|
||||||
|
>>>>>>> REPLACE
|
||||||
|
|
||||||
|
Rules: SEARCH must match the draft character for character; take the minimal sufficient fragment, not a whole paragraph; operations must not overlap; nothing except operations (or NO_CHANGE) may appear in the response.
|
||||||
|
|
||||||
|
If the fragment turns out not to be found, or occurs several times in the draft, the operation will come back to you with an error and you will be able to send a corrected anchor. Do not spend effort verifying uniqueness in advance.
|
||||||
|
|
||||||
|
---USER---
|
||||||
|
Source text (language «{{source_lang}}»):
|
||||||
|
|
||||||
|
{{text}}
|
||||||
|
|
||||||
|
Draft translation to edit:
|
||||||
|
|
||||||
|
{{draft}}
|
||||||
54
eval/editor_harness/prompts/editor-diff-harness.md
Normal file
54
eval/editor_harness/prompts/editor-diff-harness.md
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<!-- ПОЛИГОН, эксп 20. Дифф-контракт, СКОПИРОВАННЫЙ с харнесов (opencode `tool/edit.txt`,
|
||||||
|
gemini-cli `tools/edit.ts` + `llm-edit-fixer.ts`), а не сочинённый.
|
||||||
|
|
||||||
|
Что именно скопировано и чем отличается от `editor_contract/prompts/editor-diff.md` (эксп 19):
|
||||||
|
1. У модели НЕТ обязанности гарантировать уникальность якоря с первого раза. В эксп-19 стояло
|
||||||
|
«SEARCH обязан быть УНИКАЛЕН в черновике (расширяй соседними словами, пока не станет)» —
|
||||||
|
это требование one-shot-верификации N якорей без обратной связи, и оно (гипотеза) и есть
|
||||||
|
источник многотысячного обдумывания. Харнесы вместо этого ВОЗВРАЩАЮТ ОШИБКУ и дают починить.
|
||||||
|
2. Правила ужаты до того, что реально написано в харнес-инструменте: точное совпадение, минимальный
|
||||||
|
фрагмент, не перекрываться. Никаких «нарушение = операция отбрасывается» под каждым пунктом.
|
||||||
|
3. Введён ВТОРОЙ РАУНД: отклонённые операции возвращаются модели с текстом ошибки, как в харнесе.
|
||||||
|
Раунд починки — отдельный вызов и отдельная строка в смете.
|
||||||
|
|
||||||
|
⚠ Мандат содержания (верность/термины/стиль) оставлен ДОСЛОВНО тем же, что в эксп-19, иначе
|
||||||
|
сравнение поедет по второй оси. Меняется ТОЛЬКО механика формата и право на ошибку. -->
|
||||||
|
Ты — литературный редактор художественного перевода на русский язык. Тебе даны ИСХОДНЫЙ текст и его черновой перевод.
|
||||||
|
Жанр книги: {{genre}}. Аудитория: {{audience}}. Книга: «{{title}}».
|
||||||
|
|
||||||
|
Твой мандат — ТОЧЕЧНАЯ правка. Ты НЕ переписываешь текст: ты выдаёшь СПИСОК ОПЕРАЦИЙ ЗАМЕНЫ, каждая чинит одно конкретное место.
|
||||||
|
|
||||||
|
Что чинить, по важности:
|
||||||
|
- ВЕРНОСТЬ: искажения смысла против исходника (язык «{{source_lang}}») — перевёрнутая полярность, неверные числа и ранги, потерянные или добавленные смысловые атомы, неверно понятые реалии, кальки жестов и идиом (напр. поклон как «ударил головой»).
|
||||||
|
- ТЕРМИНЫ: имена собственные, термины и реалии — СТРОГО по приведённому глоссарию (если он приложен): приводи любые расхождения к каноническим формам, склоняя по контексту; не придумывай иных вариантов их передачи.
|
||||||
|
- СТИЛЬ: кальки с языка «{{source_lang}}», канцелярит, неестественные конструкции, битые словоформы. НО повтор, стоящий в тех же (параллельных) позициях исходника, — авторский рефрен: сохраняй его.
|
||||||
|
- Единицы и приёмы (общие для zh→ru): МЕРЫ приводи к привычным читателю единицам (时辰 = 2 часа, 三个时辰 ≈ шесть часов; доля 成 — десятые: 六成六 = 66%, а не «6,6»); СТИХИ и названия классики — по смыслу, не транслитерацией.
|
||||||
|
- Хонорифики: {{honorifics}}. Баланс форенизация/доместикация: {{venuti}}.
|
||||||
|
|
||||||
|
НЕ переверстывай абзацы, НЕ сливай и НЕ дели предложения ради структуры. НЕ «улучшай» места, которые верны и читаются нормально. **Пустой список правок — полноценный и уважаемый ответ.**
|
||||||
|
|
||||||
|
ФОРМАТ ОТВЕТА.
|
||||||
|
|
||||||
|
Если правок нет, выведи ровно одну строку:
|
||||||
|
NO_CHANGE
|
||||||
|
|
||||||
|
Иначе выведи одну или несколько операций, каждую ровно в таком виде:
|
||||||
|
|
||||||
|
<<<<<<< SEARCH
|
||||||
|
дословный фрагмент ЧЕРНОВИКА, который надо заменить
|
||||||
|
=======
|
||||||
|
текст замены
|
||||||
|
>>>>>>> REPLACE
|
||||||
|
|
||||||
|
Правила: SEARCH совпадает с черновиком посимвольно; бери минимальный достаточный фрагмент, не абзац целиком; операции не должны перекрываться; кроме операций (или NO_CHANGE) в ответе ничего быть не должно.
|
||||||
|
|
||||||
|
Если фрагмент окажется не найден или встретится в черновике несколько раз — операция вернётся тебе с ошибкой, и ты сможешь дослать исправленный якорь. Не трать усилия на проверку уникальности заранее.
|
||||||
|
|
||||||
|
---USER---
|
||||||
|
Исходный текст (язык «{{source_lang}}»):
|
||||||
|
|
||||||
|
{{text}}
|
||||||
|
|
||||||
|
Черновой перевод для правки:
|
||||||
|
|
||||||
|
{{draft}}
|
||||||
39
eval/editor_harness/prompts/editor-en.md
Normal file
39
eval/editor_harness/prompts/editor-en.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!-- ПОЛИГОН, эксп 20. ПОЛНОЕ зеркало боевого `backend/prompts/zh-ru/editor.md` для английской цели.
|
||||||
|
|
||||||
|
⚠ Это ИСПРАВЛЕНИЕ дефекта эксп-19. Прежнее зеркало (`eval/editor_contract/prompts/editor-en.md`)
|
||||||
|
было 21 строка против 41 боевой, и в нём ОТСУТСТВОВАЛ блок ДИСКУРС-ПЕРЕВЁРСТКИ (39% системного
|
||||||
|
промпта на проводе). Из-за этого английский full-regen жёг 14711 знаков размышления против 2183
|
||||||
|
у русского, арм E «перевернулся» по знаменателю, и адверсариальное ревью 05.08 его не засчитало.
|
||||||
|
Здесь мирроринг построчный: те же семь пунктов мандата, тот же блок перевёрстки из четырёх правил,
|
||||||
|
тот же формат вывода. Отличается только то, что в оригинале зашито под русскую цель.
|
||||||
|
|
||||||
|
Секция ---FEWSHOT--- НЕ включена намеренно: в проде она дропается (`few_shot: false`), и в
|
||||||
|
русском арме на провод тоже не идёт — иначе стороны были бы несимметричны. -->
|
||||||
|
You are a literary editor of a fiction translation into English. You are given the SOURCE text and its draft translation.
|
||||||
|
Book genre: {{genre}}. Audience: {{audience}}. Book: «{{title}}».
|
||||||
|
|
||||||
|
Your mandate is literary editing of the draft against the source:
|
||||||
|
- Check the draft's meaning against the source (language «{{source_lang}}»): fix distortions of meaning, misread realia, calqued gestures and idioms — the translator may have rendered an expression literally (e.g. a bow as "struck his head"). Lean on the source, but write English, not an interlinear.
|
||||||
|
- Fix style: remove calques from «{{source_lang}}», officialese, needless repetition, unnatural constructions; write living literary English. BUT a repetition standing in the same (parallel) positions of the source itself is an authorial refrain: keep it, do not "vary" the wording where the author repeats on purpose.
|
||||||
|
- Render proper names, terms and realia STRICTLY per the supplied glossary (if attached): bring any divergence to the canonical form, inflecting by context; do not invent other renderings.
|
||||||
|
- Layout: assemble the narrative into natural English paragraphs, NOT copying the source's line-by-line breaks (in the original one sentence is often one line). Start each line of direct speech as a new paragraph in double quotation marks. There is no maximum paragraph length.
|
||||||
|
- Preserve the draft's COMPLETENESS: do not drop or add sentences; do not re-invent scenes.
|
||||||
|
- Units and devices (common to zh→en): convert MEASURES into units the reader knows (时辰 = 2 hours, 三个时辰 ≈ six hours; the fraction 成 means tenths: 六成六 = 66%, not "6.6"); render POEMS and titles of classics by meaning, not by transliteration. Check the form of names and terms against the glossary.
|
||||||
|
- Honorifics: {{honorifics}}. Foreignization/domestication balance: {{venuti}}.
|
||||||
|
|
||||||
|
Output ONLY the edited translation — with no service preambles, comments, explanations, notes about the edits, or markdown headings.
|
||||||
|
|
||||||
|
DISCOURSE RE-PARAGRAPHING (a mandatory operation carrying Chinese parataxis into English hypotactic prose — this is a CONVENTION of the target language, not a liberty):
|
||||||
|
1. Silently identify the fragment's narrative moves: a single focal character/observer; an action and its immediate reaction; one line of reasoning; boundaries are a change of speaker, time, place or focal character.
|
||||||
|
2. Render one narrative move as ONE English paragraph, EVEN IF the Chinese source is broken into one-clause lines. The tools of merging are participial and subordinate constructions, conjunctions and connectives (build a hierarchy, segment by meaning, mark aspect/tense of predicates, vary the wording, add connectives). Do NOT merge merely for length, and do not lose meaning atoms.
|
||||||
|
3. Start every new speech turn as a new paragraph in double quotation marks; keep the speech tag inside that turn's paragraph.
|
||||||
|
4. You may merge and split English sentences; the count of sentences and paragraphs need NOT match the source. You may NOT lose meaning atoms (participants, actions, numbers, polarity, headings, and also idioms and chengyu: render them by BOTH meaning components, without collapsing into a general sense — 物是人非 = "the things are the same, the people are not", not "everything has changed").
|
||||||
|
|
||||||
|
---USER---
|
||||||
|
Source text (language «{{source_lang}}»):
|
||||||
|
|
||||||
|
{{text}}
|
||||||
|
|
||||||
|
Draft translation to edit:
|
||||||
|
|
||||||
|
{{draft}}
|
||||||
27
eval/editor_harness/prompts/editor-locate-en.md
Normal file
27
eval/editor_harness/prompts/editor-locate-en.md
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<!-- ПОЛИГОН, эксп 20. Английская версия арма РАЗВЕДЕНИЯ МЕХАНИЗМА. Зеркало `editor-locate.md`. -->
|
||||||
|
You are a literary editor of a fiction translation into English. You are given the SOURCE text and its draft translation.
|
||||||
|
Book genre: {{genre}}. Audience: {{audience}}. Book: «{{title}}».
|
||||||
|
|
||||||
|
Your task is ONLY TO FIND the problem spots in the draft. Do not fix or rewrite anything.
|
||||||
|
|
||||||
|
What to look for, in order of importance:
|
||||||
|
- FIDELITY: distortions of meaning against the source (language «{{source_lang}}») — inverted polarity, wrong numbers and ranks, lost or added meaning atoms, misread realia, calqued gestures and idioms (e.g. a bow as "struck his head").
|
||||||
|
- TERMS: proper names, terms and realia STRICTLY per the supplied glossary (if attached).
|
||||||
|
- STYLE: calques from «{{source_lang}}», officialese, unnatural constructions, broken word forms. BUT a repetition standing in the same (parallel) positions of the source is an authorial refrain, not a defect.
|
||||||
|
- Units and devices (common to zh→en): MEASURES (时辰 = 2 hours; the fraction 成 means tenths: 六成六 = 66%); POEMS and titles of classics — by meaning.
|
||||||
|
|
||||||
|
Do NOT count as defects the places that are correct and read normally. An empty list is a full answer.
|
||||||
|
|
||||||
|
RESPONSE FORMAT: a numbered list, one line per defect, in the form
|
||||||
|
N. <first 5-8 words of the problem spot> — <what the defect is, up to 10 words>
|
||||||
|
If there are no defects, output exactly one line: NO_CHANGE
|
||||||
|
Nothing except this list may appear in the response.
|
||||||
|
|
||||||
|
---USER---
|
||||||
|
Source text (language «{{source_lang}}»):
|
||||||
|
|
||||||
|
{{text}}
|
||||||
|
|
||||||
|
Draft translation to edit:
|
||||||
|
|
||||||
|
{{draft}}
|
||||||
34
eval/editor_harness/prompts/editor-locate.md
Normal file
34
eval/editor_harness/prompts/editor-locate.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!-- ПОЛИГОН, эксп 20. Арм РАЗВЕДЕНИЯ МЕХАНИЗМА: только НАЙДИ дефекты, ничего не чини и не форматируй.
|
||||||
|
|
||||||
|
Зачем: эксп-19 показал, что дифф тратит ~95% выхода на обдумывание, и приписал это формату. Но у
|
||||||
|
full-regen нет и задачи НАЙТИ дефект — он просто переводит заново, попутно исправляя. Этот арм
|
||||||
|
измеряет цену ОДНОГО ТОЛЬКО РЕШЕНИЯ «что здесь не так», без формата якорей и без текста замены.
|
||||||
|
Если он стоит столько же, сколько дифф, — виноват не формат, а необходимость решать.
|
||||||
|
|
||||||
|
Мандат содержания — ДОСЛОВНО как в дифф-армах, иначе сравнение поедет. -->
|
||||||
|
Ты — литературный редактор художественного перевода на русский язык. Тебе даны ИСХОДНЫЙ текст и его черновой перевод.
|
||||||
|
Жанр книги: {{genre}}. Аудитория: {{audience}}. Книга: «{{title}}».
|
||||||
|
|
||||||
|
Твоя задача — ТОЛЬКО НАЙТИ проблемные места в черновике. Ничего не исправляй и не переписывай.
|
||||||
|
|
||||||
|
Что искать, по важности:
|
||||||
|
- ВЕРНОСТЬ: искажения смысла против исходника (язык «{{source_lang}}») — перевёрнутая полярность, неверные числа и ранги, потерянные или добавленные смысловые атомы, неверно понятые реалии, кальки жестов и идиом (напр. поклон как «ударил головой»).
|
||||||
|
- ТЕРМИНЫ: имена собственные, термины и реалии — СТРОГО по приведённому глоссарию (если он приложен).
|
||||||
|
- СТИЛЬ: кальки с языка «{{source_lang}}», канцелярит, неестественные конструкции, битые словоформы. НО повтор, стоящий в тех же (параллельных) позициях исходника, — авторский рефрен, он не дефект.
|
||||||
|
- Единицы и приёмы (общие для zh→ru): МЕРЫ (时辰 = 2 часа; доля 成 — десятые: 六成六 = 66%); СТИХИ и названия классики — по смыслу.
|
||||||
|
|
||||||
|
НЕ считай дефектом места, которые верны и читаются нормально. Пустой список — полноценный ответ.
|
||||||
|
|
||||||
|
ФОРМАТ ОТВЕТА: пронумерованный список, по одной строке на дефект, в виде
|
||||||
|
N. <первые 5–8 слов проблемного места> — <в чём дефект, до 10 слов>
|
||||||
|
Если дефектов нет, выведи ровно одну строку: NO_CHANGE
|
||||||
|
Ничего, кроме этого списка, в ответе быть не должно.
|
||||||
|
|
||||||
|
---USER---
|
||||||
|
Исходный текст (язык «{{source_lang}}»):
|
||||||
|
|
||||||
|
{{text}}
|
||||||
|
|
||||||
|
Черновой перевод для правки:
|
||||||
|
|
||||||
|
{{draft}}
|
||||||
32
eval/editor_harness/prompts/repair.md
Normal file
32
eval/editor_harness/prompts/repair.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<!-- ПОЛИГОН, эксп 20, арм SCOPED REPAIR — механизм, который есть у конкурентов и которого нет у нас.
|
||||||
|
|
||||||
|
`research/22:176` фиксирует раскол дословно: «Checker→typed-error→scoped repair | AiNiee
|
||||||
|
`ProofreadTask.py:250`; LinguaGacha retrans-by-keyword | у нас НЕТ (флагаем, не чиним)». Наши гейты
|
||||||
|
(coverage, sanitizer, regression_guard) дефект НАХОДЯТ и на этом останавливаются.
|
||||||
|
|
||||||
|
Что этот арм проверяет. Мои же замеры показали, что цену редакторского вызова держит РЕШЕНИЕ, а не
|
||||||
|
формат: арм `locate` (только найди, ничего не чини) стоил 2.14× от полного переписывания на
|
||||||
|
deepseek-v4-pro. Если так, то починка ПО УЖЕ НАЙДЕННОМУ месту обязана быть на порядок дешевле —
|
||||||
|
модели не нужно ни искать, ни решать, ни держать в голове весь текст. Здесь ей дают ровно одно
|
||||||
|
предложение и тип ошибки.
|
||||||
|
|
||||||
|
Материал — те же 12 посаженных дефектов пробы 18 (§0.5), где эталон известен посимвольно, поэтому
|
||||||
|
починку можно скорить детерминированно, без судьи.
|
||||||
|
|
||||||
|
⚠ Ограничение, объявляется заранее: арм меряет ПОЧИНКУ при идеальной детекции. Он НЕ отвечает на
|
||||||
|
вопрос, найдут ли гейты эти дефекты сами — у нас нет гейта на «несуществующее слово» и на
|
||||||
|
смысловую инверсию. Поэтому результат читается как верхняя граница схемы, а не как её цена в проде. -->
|
||||||
|
Ты — редактор художественного перевода с языка «{{source_lang}}» на русский.
|
||||||
|
|
||||||
|
Автоматическая проверка нашла в переводе ОДНО проблемное место и определила тип ошибки. Тебе даны: фрагмент исходника, проблемное предложение перевода и тип ошибки.
|
||||||
|
|
||||||
|
Почини ТОЛЬКО названное предложение. Не трогай ничего за его пределами, не переписывай стиль, не переверстывай, не улучшай то, о чём не спрашивали.
|
||||||
|
|
||||||
|
Выведи РОВНО одно исправленное предложение и больше ничего — ни пояснений, ни кавычек вокруг ответа, ни markdown.
|
||||||
|
|
||||||
|
---USER---
|
||||||
|
Фрагмент исходника (язык «{{source_lang}}»):
|
||||||
|
|
||||||
|
{{text}}
|
||||||
|
|
||||||
|
Тип найденной ошибки: {{draft}}
|
||||||
43
eval/editor_harness/prompts/translator-reflow.md
Normal file
43
eval/editor_harness/prompts/translator-reflow.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<!-- ПОЛИГОН, эксп 20, арм C0+reflow. РЕШАЮЩИЙ замер: даёт ли ОДИН прямой проход то же, что
|
||||||
|
связка «дешёвый черновик + дорогой редактор».
|
||||||
|
|
||||||
|
Почему именно так. Слепое судейство (24 сравнения, две модели-судьи, зеркальная раскладка) оставило
|
||||||
|
у второго прохода ровно ОДНО доказанное преимущество — русская проза и вёрстка (10:2 при 80%
|
||||||
|
устойчивости); верность и общий вердикт — ничьи (4:4 и 8:7). Но арм `direct` шёл боевым
|
||||||
|
translator.md, где вёрстка задана ОДНОЙ строкой, тогда как у editor.md это блок ДИСКУРС-ПЕРЕВЁРСТКИ
|
||||||
|
из четырёх правил. То есть сравнивались промпты, а не схемы.
|
||||||
|
|
||||||
|
Здесь строка вёрстки транслятора заменена на ДОСЛОВНЫЙ блок редактора. Всё остальное — байт-в-байт
|
||||||
|
боевой translator.md. Если после этого прямой проход догоняет связку по оси русского языка, то
|
||||||
|
единственное оправдание второго прохода исчезает.
|
||||||
|
|
||||||
|
⚠ Файл собран скриптом из двух боевых промптов, а не написан руками — чтобы блок совпадал с
|
||||||
|
редакторским дословно и расхождение нельзя было внести опечаткой. -->
|
||||||
|
Ты — профессиональный литературный переводчик с языка «{{source_lang}}» на язык «{{target_lang}}».
|
||||||
|
Жанр книги: {{genre}}. Аудитория: {{audience}}. Книга: «{{title}}».
|
||||||
|
|
||||||
|
Правила перевода (translation brief):
|
||||||
|
- Хонорифики: {{honorifics}}. Транскрипция имён и реалий: {{transcription}}.
|
||||||
|
- Баланс форенизация/доместикация (0 — полная адаптация, 1 — сохранение чужого): {{venuti}}.
|
||||||
|
- Сноски: {{footnotes}}.
|
||||||
|
- Переводи ВСЕ предложения исходника: ничего не пропускай, не сокращай и не добавляй от себя.
|
||||||
|
- Вёрстка — по правилам ДИСКУРС-ПЕРЕВЁРСТКИ, приведённым ниже.
|
||||||
|
- Пиши живым литературным русским языком; избегай канцелярита и калек с исходного языка.
|
||||||
|
|
||||||
|
Единицы и приёмы (общие для zh→ru):
|
||||||
|
- МЕРЫ переводи в привычные читателю единицы: китайский 时辰 = 2 часа (三个时辰 ≈ шесть часов, 半个时辰 ≈ час), сам термин не транслитерируй. Доля 成 — это десятые (六成 = 60%, 六成六 = 66%), а НЕ десятичная дробь «6,6».
|
||||||
|
- СТИХИ и названия классики передавай ПО СМЫСЛУ (не транслитерацией), опираясь на существующие русские переводы.
|
||||||
|
- Род и форму имён собственных и терминов бери из приложенного глоссария (если он есть).
|
||||||
|
|
||||||
|
Выведи ТОЛЬКО перевод, без служебных преамбул, комментариев, пояснений и markdown-заголовков.
|
||||||
|
|
||||||
|
ДИСКУРС-ПЕРЕВЁРСТКА (обязательная операция передачи китайского паратаксиса в русскую гипотактическую прозу — это КОНВЕНЦИЯ русского языка, а не вольность):
|
||||||
|
1. Молча определи смысловые ходы фрагмента: единый фокал/наблюдатель; действие и его непосредственная реакция; одна тема рассуждения; границы — смена говорящего, времени, места или фокала.
|
||||||
|
2. Один повествовательный ход оформляй ОДНИМ русским абзацем, ДАЖЕ если китайский исходник разбит на однофразовые строки. Инструмент слияния — причастные и деепричастные обороты, подчинительные союзы и связки (построй иерархию, сегментируй по смыслу, промаркируй вид/время предикатов, варьируй лексику, добавляй связки). Не сливай ТОЛЬКО ради длины и не теряй смысловые атомы.
|
||||||
|
3. Каждую новую реплику начинай с нового абзаца через тире «—»; слова автора при той же реплике оставляй в её абзаце.
|
||||||
|
4. Разрешено объединять и делить русские предложения; число предложений и абзацев совпадать с исходником НЕ обязано. Запрещено терять смысловые атомы (участников, действия, числа, полярность, заголовки, а также идиомы и чэнъюй: их передавай ОБОИМИ смысловыми компонентами, не сворачивая в общий смысл — 物是人非 = «вещи те же, люди не те», а не «всё изменилось»).
|
||||||
|
|
||||||
|
---USER---
|
||||||
|
Переведи следующий фрагмент:
|
||||||
|
|
||||||
|
{{text}}
|
||||||
112
eval/editor_harness/repair_probe.py
Normal file
112
eval/editor_harness/repair_probe.py
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Арм SCOPED REPAIR: починка по УЖЕ НАЙДЕННОМУ спану — механизм, которого у нас нет.
|
||||||
|
|
||||||
|
`research/22:176` фиксирует раскол дословно: «Checker→typed-error→scoped repair | AiNiee
|
||||||
|
`ProofreadTask.py:250`; LinguaGacha retrans-by-keyword | у нас НЕТ (флагаем, не чиним)».
|
||||||
|
|
||||||
|
Что проверяется. Мои же замеры дали, что цену редакторского вызова держит РЕШЕНИЕ, а не формат:
|
||||||
|
арм `locate` (только найди, ничего не чини) стоил 2.14× от полного переписывания на deepseek-v4-pro.
|
||||||
|
Если так — починка по указанному месту обязана быть на порядок дешевле: модели не нужно ни искать,
|
||||||
|
ни держать в голове весь текст, ей дают одно предложение и тип ошибки.
|
||||||
|
|
||||||
|
Материал — те же 12 посаженных дефектов пробы 18 (§0.5): 6 несуществующих слов (к1) и 6 смысловых
|
||||||
|
инверсий (к2). Эталон известен посимвольно, поэтому починка скорится детерминированно, без судьи,
|
||||||
|
теми же регексами `fix_rx`/`bad_rx`, что валидированы ревью посылки (12/12 корректны).
|
||||||
|
|
||||||
|
⚠ Объявляется заранее: арм меряет починку при ИДЕАЛЬНОЙ детекции. Он НЕ отвечает, найдут ли гейты
|
||||||
|
эти дефекты сами — гейта на «несуществующее слово» и на смысловую инверсию у нас нет. Поэтому
|
||||||
|
результат читается как ВЕРХНЯЯ ГРАНИЦА схемы, а не как её цена в проде.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "editor_contract"))
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||||||
|
sys.path.insert(0, str(HERE))
|
||||||
|
|
||||||
|
import probe as Q # noqa: E402
|
||||||
|
import editor_wire_probe as P # noqa: E402
|
||||||
|
from inject_probe import pick_windows # noqa: E402
|
||||||
|
|
||||||
|
RAW = Q.RAW
|
||||||
|
TYPE = {"k1": "несуществующее слово (модель выдумала слово, которого в русском языке нет)",
|
||||||
|
"k2": "смысловое искажение против исходника (сказано обратное или иное, чем в оригинале)"}
|
||||||
|
|
||||||
|
|
||||||
|
def sentence_with(text: str, needle: str) -> str | None:
|
||||||
|
"""Предложение, содержащее посаженный фрагмент. Границы — по .!?… и переносу строки."""
|
||||||
|
i = text.find(needle)
|
||||||
|
if i < 0:
|
||||||
|
return None
|
||||||
|
starts = [m.end() for m in re.finditer(r"[.!?…]\s+|\n", text[:i])]
|
||||||
|
s = starts[-1] if starts else 0
|
||||||
|
m = re.search(r"[.!?…](\s|$)|\n", text[i + len(needle):])
|
||||||
|
e = i + len(needle) + (m.end() if m else 0)
|
||||||
|
return text[s:e].strip()
|
||||||
|
|
||||||
|
|
||||||
|
def plan() -> list[tuple]:
|
||||||
|
out = []
|
||||||
|
for k, (_, buf, _) in enumerate(pick_windows()):
|
||||||
|
planted, _ = P.planted_draft(k, P.draft_of(k))
|
||||||
|
for cls, old, new, bad_rx, fix_rx in P.DEFECTS[k]:
|
||||||
|
sent = sentence_with(planted, new)
|
||||||
|
if not sent:
|
||||||
|
print(f" ⚠ w{k}/{cls}: не выделил предложение вокруг посадки — пропуск")
|
||||||
|
continue
|
||||||
|
# ⚠ Селф-ревью 05.08: у части посадок дефект ДОБАВЛЯЕТ слово, не заменяя ключевое
|
||||||
|
# («мреязное СОКРОВИЩЕ природы»), поэтому `fix_rx` совпадает с испорченным предложением
|
||||||
|
# ЕЩЁ ДО починки. В таких клетках «восстановлен» не несёт информации — помечаем их и
|
||||||
|
# держим вне строгого знаменателя, иначе бездействие модели засчиталось бы как успех.
|
||||||
|
degenerate = bool(re.search(fix_rx, sent))
|
||||||
|
out.append((k, cls, sent, buf, bad_rx, fix_rx, old, degenerate))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def main(models: list[str]) -> None:
|
||||||
|
pl = plan()
|
||||||
|
print(f"починок в плане: {len(pl)} (ожидается 12)")
|
||||||
|
tpl = HERE / "prompts" / "repair.md"
|
||||||
|
for m in models:
|
||||||
|
short = {"deepseek-v4-pro": "dp", "glm-5": "gl", "gpt-5.6-luna": "lu",
|
||||||
|
"grok-4.3": "gr"}[m]
|
||||||
|
nothink = Q.MODELS[m][5] in ("disable", "effort_none")
|
||||||
|
rows, spent = [], 0.0
|
||||||
|
for k, cls, sent, buf, bad_rx, fix_rx, old, degen in pl:
|
||||||
|
flagged = f"{TYPE[cls]}\n\nПроблемное предложение перевода:\n{sent}"
|
||||||
|
sys_msg, user = Q.render(tpl, buf, flagged)
|
||||||
|
rec = Q.call(m, [{"role": "system", "content": sys_msg},
|
||||||
|
{"role": "user", "content": user}],
|
||||||
|
f"rp-{short}-w{k}-{cls}", nothink)
|
||||||
|
spent += rec["cost_usd"]
|
||||||
|
got = rec["content"].strip()
|
||||||
|
rows.append(dict(window=k, cls=cls, degenerate=degen,
|
||||||
|
still=bool(re.search(bad_rx, got)),
|
||||||
|
restored=bool(re.search(fix_rx, got)),
|
||||||
|
cost=rec["cost_usd"], out_tokens=rec["completion_tokens"],
|
||||||
|
reasoning=rec["reasoning_chars"], chars=len(got)))
|
||||||
|
time.sleep(0.2)
|
||||||
|
inf = [r for r in rows if not r["degenerate"]]
|
||||||
|
snyat = sum(1 for r in rows if not r["still"])
|
||||||
|
strict = sum(1 for r in inf if not r["still"] and r["restored"])
|
||||||
|
print(f"\n{m}: СНЯТ дефект {snyat}/{len(rows)} · "
|
||||||
|
f"ВОССТАНОВЛЕН строго {strict}/{len(inf)} (клеток с информативным эталоном {len(inf)} "
|
||||||
|
f"из {len(rows)}) · ${spent:.6f}, ${spent / max(len(rows), 1):.6f} за починку")
|
||||||
|
for cls in ("k1", "k2"):
|
||||||
|
sub = [r for r in rows if r["cls"] == cls]
|
||||||
|
si = [r for r in sub if not r["degenerate"]]
|
||||||
|
if sub:
|
||||||
|
print(f" {cls}: снят {sum(1 for r in sub if not r['still'])}/{len(sub)}"
|
||||||
|
+ (f" · восстановлен {sum(1 for r in si if not r['still'] and r['restored'])}/{len(si)}" if si else ""))
|
||||||
|
(RAW / f"repair-{short}.json").write_text(json.dumps(rows, ensure_ascii=False, indent=1),
|
||||||
|
encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(sys.argv[1:] or ["deepseek-v4-pro"])
|
||||||
307
eval/editor_harness/run.log
Normal file
307
eval/editor_harness/run.log
Normal file
|
|
@ -0,0 +1,307 @@
|
||||||
|
план: 48 вызовов; уже потрачено $0.056981; потолок $0.6
|
||||||
|
[eh-dp-full-w0] deepseek-v4-pro finish=stop in=2271(c2176) out=1169 размышл=1713зн текст=1741зн $0.001066 27.8s
|
||||||
|
apply: ops=2 applied=2 no_change=False починка=нет
|
||||||
|
apply: ops=3 applied=3 no_change=False починка=нет
|
||||||
|
[eh-gl-full-w0] glm-5 nothink finish=stop in=2200(c0) out=546 размышл=0зн текст=1798зн $0.003947 16.1s
|
||||||
|
apply: ops=4 applied=4 no_change=False починка=нет
|
||||||
|
apply: ops=6 applied=6 no_change=False починка=нет
|
||||||
|
[eh-dp-full-w1] deepseek-v4-pro finish=stop in=2158(c2048) out=1554 размышл=2971зн текст=1790зн $0.001407 37.9s
|
||||||
|
apply: ops=4 applied=3 no_change=False починка=нет
|
||||||
|
apply: ops=3 applied=3 no_change=False починка=нет
|
||||||
|
[eh-gl-full-w1] glm-5 nothink finish=stop in=2102(c1152) out=508 размышл=0зн текст=1784зн $0.002806 14.8s
|
||||||
|
apply: ops=0 applied=0 no_change=True починка=нет
|
||||||
|
apply: ops=0 applied=0 no_change=True починка=нет
|
||||||
|
[eh-dp-full-w2] deepseek-v4-pro finish=stop in=2243(c2176) out=1282 размышл=1841зн текст=1886зн $0.001152 24.6s
|
||||||
|
apply: ops=9 applied=8 no_change=False починка=нет
|
||||||
|
apply: ops=6 applied=6 no_change=False починка=нет
|
||||||
|
[eh-gl-full-w2] glm-5 nothink finish=stop in=2140(c1152) out=513 размышл=0зн текст=1679зн $0.002860 14.5s
|
||||||
|
apply: ops=4 applied=3 no_change=False починка=нет
|
||||||
|
apply: ops=6 applied=6 no_change=False починка=нет
|
||||||
|
[eh-dp-full-w3] deepseek-v4-pro finish=stop in=2185(c2176) out=4990 размышл=12115зн текст=1708зн $0.004353 70.7s
|
||||||
|
[eh-dp-diff-strict-w3] deepseek-v4-pro finish=stop in=2038(c1920) out=4472 размышл=11643зн текст=460зн $0.003949 60.3s
|
||||||
|
apply: ops=3 applied=3 no_change=False починка=нет
|
||||||
|
[eh-dp-diff-harness-w3] deepseek-v4-pro finish=stop in=1849(c896) out=4647 размышл=12088зн текст=335зн $0.004461 58.8s
|
||||||
|
apply: ops=2 applied=2 no_change=False починка=нет
|
||||||
|
[eh-dp-locate-w3] deepseek-v4-pro finish=stop in=1581(c0) out=1057 размышл=2592зн текст=372зн $0.001607 18.3s
|
||||||
|
[eh-gl-full-w3] glm-5 nothink finish=stop in=2103(c0) out=513 размышл=0зн текст=1684зн $0.003745 14.8s
|
||||||
|
[eh-gl-diff-strict-w3] glm-5 nothink finish=stop in=1937(c0) out=307 размышл=0зн текст=1183зн $0.002919 9.6s
|
||||||
|
apply: ops=6 applied=6 no_change=False починка=нет
|
||||||
|
[eh-gl-diff-harness-w3] glm-5 nothink finish=stop in=1767(c0) out=104 размышл=0зн текст=457зн $0.002100 4.6s
|
||||||
|
apply: ops=5 applied=5 no_change=False починка=нет
|
||||||
|
[eh-gl-locate-w3] glm-5 nothink finish=stop in=1535(c0) out=430 размышл=0зн текст=1305зн $0.002911 12.9s
|
||||||
|
[eh-dp-full-w4] deepseek-v4-pro finish=stop in=2311(c2304) out=4803 размышл=12025зн текст=2011зн $0.004190 53.0s
|
||||||
|
[eh-dp-diff-strict-w4] deepseek-v4-pro finish=stop in=2164(c2048) out=6667 размышл=18174зн текст=620зн $0.005858 85.5s
|
||||||
|
apply: ops=2 applied=2 no_change=False починка=нет
|
||||||
|
[eh-dp-diff-harness-w4] deepseek-v4-pro finish=stop in=1975(c0) out=4601 размышл=12565зн текст=701зн $0.004862 60.4s
|
||||||
|
apply: ops=1 applied=1 no_change=False починка=нет
|
||||||
|
[eh-dp-locate-w4] deepseek-v4-pro finish=stop in=1707(c512) out=4426 размышл=11522зн текст=602зн $0.004372 47.2s
|
||||||
|
[eh-gl-full-w4] glm-5 nothink finish=stop in=2223(c0) out=594 размышл=0зн текст=1972зн $0.004124 17.1s
|
||||||
|
[eh-gl-diff-strict-w4] glm-5 nothink finish=stop in=2057(c0) out=495 размышл=0зн текст=1739зн $0.003641 14.4s
|
||||||
|
apply: ops=5 applied=5 no_change=False починка=нет
|
||||||
|
[eh-gl-diff-harness-w4] glm-5 nothink finish=stop in=1887(c0) out=456 размышл=0зн текст=1606зн $0.003346 13.1s
|
||||||
|
apply: ops=3 applied=3 no_change=False починка=нет
|
||||||
|
[eh-gl-locate-w4] glm-5 nothink finish=stop in=1655(c0) out=422 размышл=0зн текст=1164зн $0.003005 12.5s
|
||||||
|
[eh-dp-full-w5] deepseek-v4-pro finish=stop in=2220(c2176) out=1377 размышл=2075зн текст=1940зн $0.001225 15.0s
|
||||||
|
[eh-dp-diff-strict-w5] deepseek-v4-pro finish=stop in=2073(c2048) out=6239 размышл=17846зн текст=387зн $0.005446 68.4s
|
||||||
|
apply: ops=4 applied=4 no_change=False починка=нет
|
||||||
|
[eh-dp-diff-harness-w5] deepseek-v4-pro finish=stop in=1884(c768) out=6245 размышл=17145зн текст=9зн $0.005921 61.3s
|
||||||
|
apply: ops=0 applied=0 no_change=True починка=нет
|
||||||
|
[eh-dp-locate-w5] deepseek-v4-pro finish=stop in=1616(c512) out=1432 размышл=3296зн текст=978зн $0.001728 18.1s
|
||||||
|
[eh-gl-full-w5] glm-5 nothink finish=stop in=2140(c0) out=559 размышл=0зн текст=1880зн $0.003929 15.7s
|
||||||
|
[eh-gl-diff-strict-w5] glm-5 nothink finish=stop in=1974(c0) out=74 размышл=0зн текст=254зн $0.002211 3.9s
|
||||||
|
apply: ops=1 applied=1 no_change=False починка=нет
|
||||||
|
[eh-gl-diff-harness-w5] glm-5 nothink finish=stop in=1804(c0) out=237 размышл=0зн текст=866зн $0.002562 7.3s
|
||||||
|
apply: ops=7 applied=7 no_change=False починка=нет
|
||||||
|
[eh-gl-locate-w5] glm-5 nothink finish=stop in=1572(c0) out=407 размышл=0зн текст=1138зн $0.002874 12.7s
|
||||||
|
ИТОГО эксп 20: $0.155558
|
||||||
|
план: 36 вызовов; уже потрачено $0.155558; потолок $0.6
|
||||||
|
[eh-gr-full-w0] grok-4.3 finish=stop in=2335(c192) out=525 размышл=248зн текст=1848зн $0.004231 11.2s
|
||||||
|
[eh-gr-diff-harness-w0] grok-4.3 finish=stop in=2008(c192) out=92 размышл=955зн текст=348зн $0.002740 14.7s
|
||||||
|
apply: ops=1 applied=1 no_change=False починка=нет
|
||||||
|
[eh-gr-locate-w0] grok-4.3 finish=stop in=1773(c192) out=138 размышл=586зн текст=397зн $0.002561 14.3s
|
||||||
|
план: 36 вызовов; уже потрачено $0.155558; потолок $0.6
|
||||||
|
[eh-gr-full-w0] grok-4.3 nothink finish=stop in=2327(c128) out=522 размышл=0зн текст=1804зн $0.004214 4.7s
|
||||||
|
[eh-gr-diff-harness-w0] grok-4.3 nothink finish=stop in=2000(c128) out=702 размышл=0зн текст=2548зн $0.004255 5.2s
|
||||||
|
apply: ops=3 applied=3 no_change=False починка=нет
|
||||||
|
[eh-gr-locate-w0] grok-4.3 nothink finish=length in=1765(c128) out=16000 размышл=0зн текст=39895зн $0.042206 94.5s
|
||||||
|
[eh-lu-full-w0] gpt-5.6-luna nothink finish=stop in=2256(c0) out=614 размышл=0зн текст=2031зн $0.001188 5.3s
|
||||||
|
[eh-lu-diff-harness-w0] gpt-5.6-luna nothink finish=stop in=1927(c0) out=527 размышл=0зн текст=1924зн $0.001018 4.2s
|
||||||
|
apply: ops=14 applied=14 no_change=False починка=нет
|
||||||
|
[eh-lu-locate-w0] gpt-5.6-luna nothink finish=stop in=1699(c0) out=402 размышл=0зн текст=1228зн $0.000822 3.8s
|
||||||
|
[eh-gr-full-w1] grok-4.3 nothink finish=stop in=2220(c128) out=451 размышл=0зн текст=1709зн $0.003903 4.2s
|
||||||
|
[eh-gr-diff-harness-w1] grok-4.3 nothink finish=stop in=1893(c128) out=2 размышл=0зн текст=9зн $0.002371 0.8s
|
||||||
|
apply: ops=0 applied=0 no_change=True починка=нет
|
||||||
|
[eh-gr-locate-w1] grok-4.3 nothink finish=stop in=1658(c128) out=364 размышл=0зн текст=1022зн $0.002982 4.5s
|
||||||
|
[eh-lu-full-w1] gpt-5.6-luna nothink finish=stop in=2148(c1207) out=528 размышл=0зн текст=1928зн $0.000846 4.1s
|
||||||
|
[eh-lu-diff-harness-w1] gpt-5.6-luna nothink finish=stop in=1819(c0) out=827 размышл=0зн текст=3211зн $0.001356 5.2s
|
||||||
|
apply: ops=23 applied=21 no_change=False починка=нет
|
||||||
|
[eh-lu-locate-w1] gpt-5.6-luna nothink finish=stop in=1591(c0) out=358 размышл=0зн текст=1084зн $0.000748 3.6s
|
||||||
|
[eh-gr-full-w2] grok-4.3 nothink finish=stop in=2287(c128) out=537 размышл=0зн текст=1800зн $0.004201 5.3s
|
||||||
|
[eh-gr-diff-harness-w2] grok-4.3 nothink finish=stop in=1960(c128) out=2 размышл=0зн текст=9зн $0.002455 1.2s
|
||||||
|
apply: ops=0 applied=0 no_change=True починка=нет
|
||||||
|
[eh-gr-locate-w2] grok-4.3 nothink finish=stop in=1725(c128) out=2 размышл=0зн текст=9зн $0.002161 1.0s
|
||||||
|
[eh-lu-full-w2] gpt-5.6-luna nothink finish=stop in=2212(c0) out=539 размышл=0зн текст=1764зн $0.001089 4.4s
|
||||||
|
[eh-lu-diff-harness-w2] gpt-5.6-luna nothink finish=stop in=1883(c0) out=579 размышл=0зн текст=2072зн $0.001071 4.1s
|
||||||
|
[eh-lu-diff-harness-w2-fix] gpt-5.6-luna nothink finish=stop in=2590(c1880) out=31 размышл=0зн текст=86зн $0.000217 1.2s
|
||||||
|
apply: ops=19 applied=18 no_change=False починка=да
|
||||||
|
[eh-lu-locate-w2] gpt-5.6-luna nothink finish=stop in=1655(c0) out=390 размышл=0зн текст=1184зн $0.000799 3.8s
|
||||||
|
[eh-gr-full-w3] grok-4.3 nothink finish=stop in=2246(c128) out=510 размышл=0зн текст=1726зн $0.004083 4.2s
|
||||||
|
[eh-gr-diff-harness-w3] grok-4.3 nothink finish=stop in=1919(c128) out=2 размышл=0зн текст=9зн $0.002404 1.0s
|
||||||
|
apply: ops=0 applied=0 no_change=True починка=нет
|
||||||
|
[eh-gr-locate-w3] grok-4.3 nothink finish=stop in=1684(c128) out=373 размышл=0зн текст=1045зн $0.003038 4.0s
|
||||||
|
[eh-lu-full-w3] gpt-5.6-luna nothink finish=stop in=2155(c0) out=541 размышл=0зн текст=1857зн $0.001080 4.7s
|
||||||
|
[eh-lu-diff-harness-w3] gpt-5.6-luna nothink finish=stop in=1826(c0) out=631 размышл=0зн текст=2311зн $0.001122 4.7s
|
||||||
|
apply: ops=18 applied=18 no_change=False починка=нет
|
||||||
|
[eh-lu-locate-w3] gpt-5.6-luna nothink finish=stop in=1598(c0) out=377 размышл=0зн текст=1242зн $0.000772 3.7s
|
||||||
|
[eh-gr-full-w4] grok-4.3 nothink finish=stop in=2356(c128) out=565 размышл=0зн текст=1983зн $0.004358 3.1s
|
||||||
|
[eh-gr-diff-harness-w4] grok-4.3 nothink finish=stop in=2029(c192) out=2 размышл=0зн текст=9зн $0.002541 0.8s
|
||||||
|
apply: ops=0 applied=0 no_change=True починка=нет
|
||||||
|
[eh-gr-locate-w4] grok-4.3 nothink finish=stop in=1794(c192) out=327 размышл=0зн текст=884зн $0.003060 3.7s
|
||||||
|
[eh-lu-full-w4] gpt-5.6-luna nothink finish=stop in=2310(c0) out=594 размышл=0зн текст=2125зн $0.001175 6.1s
|
||||||
|
[eh-lu-diff-harness-w4] gpt-5.6-luna nothink finish=stop in=1981(c0) out=726 размышл=0зн текст=2752зн $0.001267 6.6s
|
||||||
|
apply: ops=13 applied=13 no_change=False починка=нет
|
||||||
|
[eh-lu-locate-w4] gpt-5.6-luna nothink finish=stop in=1753(c0) out=440 размышл=0зн текст=1328зн $0.000879 3.8s
|
||||||
|
[eh-gr-full-w5] grok-4.3 nothink finish=stop in=2271(c128) out=465 размышл=0зн текст=1588зн $0.004001 4.4s
|
||||||
|
[eh-gr-diff-harness-w5] grok-4.3 nothink finish=stop in=1944(c128) out=2 размышл=0зн текст=9зн $0.002435 0.9s
|
||||||
|
apply: ops=0 applied=0 no_change=True починка=нет
|
||||||
|
[eh-gr-locate-w5] grok-4.3 nothink finish=stop in=1709(c128) out=201 размышл=0зн текст=594зн $0.002639 2.4s
|
||||||
|
[eh-lu-full-w5] gpt-5.6-luna nothink finish=stop in=2216(c0) out=593 размышл=0зн текст=1969зн $0.001155 4.9s
|
||||||
|
[eh-lu-diff-harness-w5] gpt-5.6-luna nothink finish=stop in=1887(c0) out=266 размышл=0зн текст=921зн $0.000697 2.4s
|
||||||
|
apply: ops=9 applied=9 no_change=False починка=нет
|
||||||
|
[eh-lu-locate-w5] gpt-5.6-luna nothink finish=stop in=1659(c0) out=351 размышл=0зн текст=1122зн $0.000753 3.6s
|
||||||
|
ИТОГО эксп 20: $0.270919
|
||||||
|
план: 24 вызовов; уже потрачено $0.270919; потолок $0.6
|
||||||
|
[eh-dp-direct-w0] deepseek-v4-pro finish=stop in=888(c0) out=4812 размышл=10955зн текст=1881зн $0.004573 45.3s
|
||||||
|
[eh-gl-direct-w0] glm-5 nothink finish=stop in=903(c0) out=578 размышл=0зн текст=1910зн $0.002753 16.5s
|
||||||
|
[eh-gr-direct-w0] grok-4.3 nothink finish=stop in=1077(c128) out=325 размышл=0зн текст=463зн $0.002159 3.1s
|
||||||
|
[eh-lu-direct-w0] gpt-5.6-luna nothink finish=stop in=993(c0) out=592 размышл=0зн текст=2007зн $0.000909 5.7s
|
||||||
|
[eh-dp-direct-w1] deepseek-v4-pro finish=stop in=864(c384) out=2004 размышл=3679зн текст=1950зн $0.001954 23.2s
|
||||||
|
[eh-gl-direct-w1] glm-5 nothink finish=stop in=875(c0) out=503 размышл=0зн текст=1780зн $0.002485 14.8s
|
||||||
|
[eh-gr-direct-w1] grok-4.3 nothink finish=stop in=1044(c128) out=482 размышл=0зн текст=1777зн $0.002510 5.2s
|
||||||
|
[eh-lu-direct-w1] gpt-5.6-luna nothink finish=stop in=953(c0) out=529 размышл=0зн текст=1993зн $0.000825 4.4s
|
||||||
|
[eh-dp-direct-w2] deepseek-v4-pro finish=stop in=860(c512) out=2153 размышл=3884зн текст=1639зн $0.002026 22.8s
|
||||||
|
[eh-gl-direct-w2] glm-5 nothink finish=stop in=870(c384) out=507 размышл=0зн текст=1687зн $0.002185 15.1s
|
||||||
|
[eh-gr-direct-w2] grok-4.3 nothink finish=stop in=1051(c128) out=476 размышл=0зн текст=1621зн $0.002504 6.6s
|
||||||
|
[eh-lu-direct-w2] gpt-5.6-luna nothink finish=stop in=960(c0) out=543 размышл=0зн текст=1817зн $0.000844 6.0s
|
||||||
|
[eh-dp-direct-w3] deepseek-v4-pro finish=stop in=840(c384) out=2689 размышл=6063зн текст=1708зн $0.002539 30.1s
|
||||||
|
[eh-gl-direct-w3] glm-5 nothink finish=stop in=859(c0) out=533 размышл=0зн текст=1764зн $0.002565 15.1s
|
||||||
|
[eh-gr-direct-w3] grok-4.3 nothink finish=stop in=1035(c128) out=495 размышл=0зн текст=1654зн $0.002531 5.6s
|
||||||
|
[eh-lu-direct-w3] gpt-5.6-luna nothink finish=stop in=933(c0) out=559 размышл=0зн текст=1928зн $0.000857 5.6s
|
||||||
|
[eh-dp-direct-w4] deepseek-v4-pro finish=stop in=856(c384) out=2258 размышл=4189зн текст=2061зн $0.002171 26.4s
|
||||||
|
[eh-gl-direct-w4] glm-5 nothink finish=stop in=869(c384) out=481 размышл=0зн текст=1627зн $0.002101 14.8s
|
||||||
|
[eh-gr-direct-w4] grok-4.3 nothink finish=stop in=1051(c128) out=527 размышл=0зн текст=1926зн $0.002631 5.6s
|
||||||
|
[eh-lu-direct-w4] gpt-5.6-luna nothink finish=stop in=975(c0) out=605 размышл=0зн текст=2204зн $0.000921 6.3s
|
||||||
|
[eh-dp-direct-w5] deepseek-v4-pro finish=stop in=848(c384) out=2847 размышл=5681зн текст=1719зн $0.002680 28.8s
|
||||||
|
[eh-gl-direct-w5] glm-5 nothink finish=stop in=867(c0) out=610 размышл=0зн текст=1961зн $0.002819 17.4s
|
||||||
|
[eh-gr-direct-w5] grok-4.3 nothink finish=stop in=1040(c128) out=499 размышл=0зн текст=1690зн $0.002547 5.1s
|
||||||
|
[eh-lu-direct-w5] gpt-5.6-luna nothink finish=stop in=955(c0) out=602 размышл=0зн текст=2054зн $0.000913 5.6s
|
||||||
|
ИТОГО эксп 20: $0.321921
|
||||||
|
план: 36 вызовов; уже потрачено $0.321921; потолок $1.0
|
||||||
|
[eht-gl-full-w0] glm-5 finish=stop in=2200(c0) out=8753 размышл=23981зн текст=1870зн $0.030210 221.1s
|
||||||
|
[eht-gl-diff-harness-w0] glm-5 finish=stop in=1864(c0) out=9647 размышл=30787зн текст=491зн $0.032734 243.2s
|
||||||
|
apply: ops=5 applied=5 no_change=False починка=нет
|
||||||
|
[eht-gr-full-w0] grok-4.3 finish=stop in=2335(c192) out=539 размышл=606зн текст=1851зн $0.004266 15.4s
|
||||||
|
[eht-gr-diff-harness-w0] grok-4.3 finish=stop in=2008(c192) out=157 размышл=770зн текст=626зн $0.002903 11.4s
|
||||||
|
apply: ops=1 applied=1 no_change=False починка=нет
|
||||||
|
[eht-lu-full-w0] gpt-5.6-luna finish=stop in=2256(c0) out=948 размышл=0зн текст=1952зн $0.001589 9.0s
|
||||||
|
[eht-lu-diff-harness-w0] gpt-5.6-luna finish=stop in=1927(c0) out=1346 размышл=0зн текст=1154зн $0.002001 12.5s
|
||||||
|
apply: ops=9 applied=9 no_change=False починка=нет
|
||||||
|
[eht-gl-full-w1] glm-5 finish=stop in=2102(c0) out=10937 размышл=31975зн текст=1827зн $0.037100 277.1s
|
||||||
|
[eht-gl-diff-harness-w1] glm-5 finish=stop in=1766(c0) out=6772 размышл=17916зн текст=202зн $0.023436 171.9s
|
||||||
|
[eht-gl-diff-harness-w1-fix] glm-5 finish=stop in=1950(c0) out=583 размышл=1505зн текст=312зн $0.003816 16.4s
|
||||||
|
apply: ops=4 applied=2 no_change=False починка=да
|
||||||
|
[eht-gr-full-w1] grok-4.3 finish=stop in=2228(c192) out=473 размышл=684зн текст=1791зн $0.003968 10.4s
|
||||||
|
[eht-gr-diff-harness-w1] grok-4.3 finish=stop in=1901(c192) out=117 размышл=881зн текст=443зн $0.002669 16.6s
|
||||||
|
apply: ops=2 applied=2 no_change=False починка=нет
|
||||||
|
[eht-lu-full-w1] gpt-5.6-luna finish=stop in=2148(c1207) out=916 размышл=0зн текст=1911зн $0.001312 9.1s
|
||||||
|
[eht-lu-diff-harness-w1] gpt-5.6-luna finish=stop in=1819(c0) out=686 размышл=0зн текст=1100зн $0.001187 9.9s
|
||||||
|
apply: ops=4 applied=4 no_change=False починка=нет
|
||||||
|
[eht-gl-full-w2] glm-5 finish=stop in=2140(c0) out=5993 размышл=15243зн текст=1737зн $0.021318 152.3s
|
||||||
|
[eht-gl-diff-harness-w2] glm-5 finish=stop in=1804(c0) out=7780 размышл=20462зн текст=716зн $0.026700 1097.4s
|
||||||
|
apply: ops=8 applied=8 no_change=False починка=нет
|
||||||
|
[eht-gr-full-w2] grok-4.3 finish=stop in=2295(c192) out=533 размышл=263зн текст=1790зн $0.004201 9.9s
|
||||||
|
[eht-gr-diff-harness-w2] grok-4.3 finish=stop in=1968(c192) out=88 размышл=298зн текст=325зн $0.002680 9.8s
|
||||||
|
apply: ops=4 applied=4 no_change=False починка=нет
|
||||||
|
[eht-lu-full-w2] gpt-5.6-luna finish=stop in=2212(c0) out=1053 размышл=0зн текст=1768зн $0.001706 11.1s
|
||||||
|
[eht-lu-diff-harness-w2] gpt-5.6-luna finish=stop in=1883(c0) out=922 размышл=0зн текст=1862зн $0.001483 8.1s
|
||||||
|
[eht-lu-diff-harness-w2-fix] gpt-5.6-luna finish=stop in=2535(c0) out=262 размышл=0зн текст=178зн $0.000821 4.0s
|
||||||
|
apply: ops=18 applied=17 no_change=False починка=да
|
||||||
|
[eht-gl-full-w3] glm-5 finish=stop in=2103(c0) out=7956 размышл=21564зн текст=1696зн $0.027562 200.9s
|
||||||
|
[eht-gl-diff-harness-w3] glm-5 finish=stop in=1767(c0) out=3239 размышл=8517зн текст=812зн $0.012132 82.8s
|
||||||
|
apply: ops=5 applied=5 no_change=False починка=нет
|
||||||
|
[eht-gr-full-w3] grok-4.3 finish=stop in=2254(c192) out=515 размышл=390зн текст=1748зн $0.004105 10.3s
|
||||||
|
[eht-gr-diff-harness-w3] grok-4.3 finish=stop in=1927(c192) out=29 размышл=738зн текст=136зн $0.002481 9.3s
|
||||||
|
apply: ops=1 applied=1 no_change=False починка=нет
|
||||||
|
[eht-lu-full-w3] gpt-5.6-luna finish=stop in=2155(c0) out=971 размышл=0зн текст=1770зн $0.001596 9.3s
|
||||||
|
[eht-lu-diff-harness-w3] gpt-5.6-luna finish=stop in=1826(c0) out=792 размышл=0зн текст=1048зн $0.001316 7.3s
|
||||||
|
apply: ops=6 applied=6 no_change=False починка=нет
|
||||||
|
[eht-gl-full-w4] glm-5 finish=stop in=2223(c0) out=11655 размышл=33432зн текст=1994зн $0.039519 293.2s
|
||||||
|
[eht-gl-diff-harness-w4] glm-5 finish=stop in=1887(c0) out=5640 размышл=16537зн текст=449зн $0.019935 143.7s
|
||||||
|
apply: ops=6 applied=6 no_change=False починка=нет
|
||||||
|
[eht-gr-full-w4] grok-4.3 finish=stop in=2364(c192) out=566 размышл=685зн текст=1987зн $0.004370 7.2s
|
||||||
|
[eht-gr-diff-harness-w4] grok-4.3 finish=stop in=2037(c192) out=191 размышл=548зн текст=705зн $0.003024 9.9s
|
||||||
|
apply: ops=2 applied=2 no_change=False починка=нет
|
||||||
|
[eht-lu-full-w4] gpt-5.6-luna finish=length in=2310(c0) out=16000 размышл=0зн текст=0зн $0.019662 64.4s
|
||||||
|
[eht-lu-diff-harness-w4] gpt-5.6-luna finish=stop in=1981(c0) out=1360 размышл=0зн текст=1773зн $0.002028 11.9s
|
||||||
|
apply: ops=12 applied=12 no_change=False починка=нет
|
||||||
|
[eht-gl-full-w5] glm-5 finish=stop in=2140(c0) out=7311 размышл=20105зн текст=1890зн $0.025535 185.2s
|
||||||
|
[eht-gl-diff-harness-w5] glm-5 finish=stop in=1804(c0) out=9601 размышл=31259зн текст=89зн $0.032527 242.2s
|
||||||
|
apply: ops=1 applied=1 no_change=False починка=нет
|
||||||
|
[eht-gr-full-w5] grok-4.3 finish=stop in=2279(c192) out=528 размышл=811зн текст=1881зн $0.004169 11.5s
|
||||||
|
[eht-gr-diff-harness-w5] grok-4.3 finish=stop in=1952(c192) out=62 размышл=827зн текст=210зн $0.002595 10.7s
|
||||||
|
apply: ops=1 applied=1 no_change=False починка=нет
|
||||||
|
[eht-lu-full-w5] gpt-5.6-luna finish=stop in=2216(c0) out=825 размышл=0зн текст=1959зн $0.001433 9.0s
|
||||||
|
[eht-lu-diff-harness-w5] gpt-5.6-luna finish=stop in=1887(c0) out=1189 размышл=0зн текст=979зн $0.001804 11.0s
|
||||||
|
apply: ops=9 applied=9 no_change=False починка=нет
|
||||||
|
ИТОГО эксп 20: $0.733814
|
||||||
|
план: 18 вызовов; уже потрачено $0.913560; потолок $1.0
|
||||||
|
СТОП (проекционный гард): $0.9136 + худший $0.0165 > $1.0
|
||||||
|
план: 18 вызовов; уже потрачено $0.913560; потолок $2.0
|
||||||
|
[eh-dp-direct-reflow-w0] deepseek-v4-pro finish=stop in=1301(c0) out=1616 размышл=2736зн текст=2028зн $0.001972 28.6s
|
||||||
|
[eh-gl-direct-reflow-w0] glm-5 nothink finish=stop in=1284(c0) out=585 размышл=0зн текст=1911зн $0.003156 16.4s
|
||||||
|
[eh-lu-direct-reflow-w0] gpt-5.6-luna nothink finish=stop in=1375(c0) out=597 размышл=0зн текст=2033зн $0.000991 7.1s
|
||||||
|
[eh-dp-direct-reflow-w1] deepseek-v4-pro finish=stop in=1277(c0) out=3968 размышл=9597зн текст=1727зн $0.004008 57.8s
|
||||||
|
[eh-gl-direct-reflow-w1] glm-5 nothink finish=stop in=1256(c896) out=519 размышл=0зн текст=1816зн $0.002200 15.5s
|
||||||
|
[eh-lu-direct-reflow-w1] gpt-5.6-luna nothink finish=stop in=1335(c0) out=522 размышл=0зн текст=1909зн $0.000893 5.5s
|
||||||
|
[eh-dp-direct-reflow-w2] deepseek-v4-pro finish=stop in=1273(c896) out=1681 размышл=2819зн текст=1947зн $0.001630 27.9s
|
||||||
|
[eh-gl-direct-reflow-w2] glm-5 nothink finish=stop in=1251(c768) out=526 размышл=0зн текст=1677зн $0.002320 14.8s
|
||||||
|
[eh-lu-direct-reflow-w2] gpt-5.6-luna nothink finish=stop in=1342(c0) out=537 размышл=0зн текст=1785зн $0.000913 6.2s
|
||||||
|
[eh-dp-direct-reflow-w3] deepseek-v4-pro finish=stop in=1253(c896) out=4919 размышл=11874зн текст=1751зн $0.004438 66.7s
|
||||||
|
[eh-gl-direct-reflow-w3] glm-5 nothink finish=stop in=1240(c768) out=514 размышл=0зн текст=1721зн $0.002270 14.3s
|
||||||
|
[eh-lu-direct-reflow-w3] gpt-5.6-luna nothink finish=stop in=1315(c0) out=554 размышл=0зн текст=1855зн $0.000928 6.9s
|
||||||
|
[eh-dp-direct-reflow-w4] deepseek-v4-pro finish=stop in=1269(c0) out=4331 размышл=11938зн текст=2186зн $0.004320 76.8s
|
||||||
|
[eh-gl-direct-reflow-w4] glm-5 nothink finish=stop in=1250(c768) out=522 размышл=0зн текст=1762зн $0.002306 15.2s
|
||||||
|
[eh-lu-direct-reflow-w4] gpt-5.6-luna nothink finish=stop in=1357(c0) out=623 размышл=0зн текст=2298зн $0.001019 16.7s
|
||||||
|
[eh-dp-direct-reflow-w5] deepseek-v4-pro finish=stop in=1261(c768) out=1439 размышл=2532зн текст=1981зн $0.001469 27.9s
|
||||||
|
[eh-gl-direct-reflow-w5] glm-5 nothink finish=stop in=1248(c768) out=597 размышл=0зн текст=1942зн $0.002544 16.9s
|
||||||
|
[eh-lu-direct-reflow-w5] gpt-5.6-luna nothink finish=stop in=1337(c0) out=606 размышл=0зн текст=2002зн $0.000995 5.5s
|
||||||
|
ИТОГО эксп 20: $0.951932
|
||||||
|
[jr-dp-w0-d] deepseek-v4-pro finish=stop in=2095(c768) out=717 размышл=1954зн текст=70зн $0.001204 14.7s
|
||||||
|
[jr-dp-w0-m] deepseek-v4-pro finish=stop in=2095(c768) out=5305 размышл=13742зн текст=78зн $0.005195 77.3s
|
||||||
|
[jr-dp-w1-d] deepseek-v4-pro finish=stop in=1815(c768) out=542 размышл=1477зн текст=78зн $0.000930 11.4s
|
||||||
|
[jr-dp-w1-m] deepseek-v4-pro finish=stop in=1815(c768) out=359 размышл=1010зн текст=66зн $0.000771 7.6s
|
||||||
|
[jr-dp-w2-d] deepseek-v4-pro finish=stop in=2123(c768) out=496 размышл=758зн текст=102зн $0.001024 11.1s
|
||||||
|
[jr-dp-w2-m] deepseek-v4-pro finish=stop in=2123(c768) out=6705 размышл=18348зн текст=82зн $0.006426 115.2s
|
||||||
|
[jr-dp-w3-d] deepseek-v4-pro finish=stop in=1930(c768) out=7177 размышл=20228зн текст=74зн $0.006752 117.4s
|
||||||
|
[jr-dp-w3-m] deepseek-v4-pro finish=stop in=1930(c640) out=6220 размышл=17591зн текст=74зн $0.005975 101.6s
|
||||||
|
[jr-dp-w4-d] deepseek-v4-pro finish=stop in=2131(c768) out=3054 размышл=9388зн текст=78зн $0.003253 45.7s
|
||||||
|
[jr-dp-w4-m] deepseek-v4-pro finish=stop in=2131(c768) out=5900 размышл=16702зн текст=82зн $0.005729 96.6s
|
||||||
|
[jr-dp-w5-d] deepseek-v4-pro finish=stop in=2080(c640) out=6222 размышл=17258зн текст=82зн $0.006042 103.1s
|
||||||
|
[jr-dp-w5-m] deepseek-v4-pro finish=stop in=2080(c640) out=3476 размышл=9556зн текст=78зн $0.003653 59.2s
|
||||||
|
[jr-gl-w0-d] deepseek-v4-pro finish=stop in=2071(c768) out=1184 размышл=3323зн текст=74зн $0.001600 22.8s
|
||||||
|
[jr-gl-w0-m] deepseek-v4-pro finish=stop in=2071(c768) out=925 размышл=2236зн текст=114зн $0.001374 16.7s
|
||||||
|
[jr-gl-w1-d] deepseek-v4-pro finish=stop in=1863(c768) out=5405 размышл=14936зн текст=78зн $0.005181 79.5s
|
||||||
|
[jr-gl-w1-m] deepseek-v4-pro finish=stop in=1863(c768) out=470 размышл=1296зн текст=78зн $0.000888 10.1s
|
||||||
|
[jr-gl-w2-d] deepseek-v4-pro finish=stop in=1947(c768) out=1045 размышл=2876зн текст=78зн $0.001425 19.2s
|
||||||
|
[jr-gl-w2-m] deepseek-v4-pro finish=stop in=1947(c768) out=1887 размышл=4869зн текст=82зн $0.002157 33.1s
|
||||||
|
[jr-gl-w3-d] deepseek-v4-pro finish=stop in=1907(c768) out=543 размышл=1343зн текст=102зн $0.000971 9.9s
|
||||||
|
[jr-gl-w3-m] deepseek-v4-pro finish=stop in=1907(c768) out=2487 размышл=6332зн текст=78зн $0.002662 40.6s
|
||||||
|
[jr-gl-w4-d] deepseek-v4-pro finish=stop in=2003(c768) out=4916 размышл=13128зн текст=82зн $0.004817 83.4s
|
||||||
|
план: 36 вызовов; уже потрачено $1.029813; потолок $2.0
|
||||||
|
[jr-gl-w4-m] deepseek-v4-pro finish=stop in=2003(c768) out=3695 размышл=9784зн текст=78зн $0.003755 51.8s
|
||||||
|
[jr-gl-w5-d] deepseek-v4-pro finish=stop in=2019(c640) out=2924 размышл=7541зн текст=78зн $0.003146 52.0s
|
||||||
|
[jr-gl-w5-m] deepseek-v4-pro finish=stop in=2019(c768) out=671 размышл=1952зн текст=70зн $0.001131 12.9s
|
||||||
|
[ehen-dp-full-w0] deepseek-v4-pro finish=stop in=1641(c0) out=5299 размышл=19882зн текст=2005зн $0.005324 84.5s
|
||||||
|
[jr-lu-w0-d] deepseek-v4-pro finish=stop in=2168(c768) out=2465 размышл=6597зн текст=82зн $0.002756 45.5s
|
||||||
|
[ehen-dp-diff-harness-w0] deepseek-v4-pro finish=stop in=1447(c0) out=3385 размышл=12735зн текст=154зн $0.003574 59.4s
|
||||||
|
apply: ops=2 applied=2 no_change=False починка=нет
|
||||||
|
[jr-lu-w0-m] deepseek-v4-pro finish=stop in=2168(c768) out=4656 размышл=12571зн текст=78зн $0.004663 72.8s
|
||||||
|
[ehen-dp-locate-w0] deepseek-v4-pro finish=stop in=1267(c0) out=2460 размышл=8661зн текст=242зн $0.002691 44.8s
|
||||||
|
[ehen-lu-full-w0] gpt-5.6-luna nothink finish=stop in=1756(c0) out=422 размышл=0зн текст=1968зн $0.000858 4.9s
|
||||||
|
[ehen-lu-diff-harness-w0] gpt-5.6-luna nothink finish=stop in=1552(c0) out=206 размышл=0зн текст=952зн $0.000558 2.6s
|
||||||
|
apply: ops=6 applied=6 no_change=False починка=нет
|
||||||
|
[ehen-lu-locate-w0] gpt-5.6-luna nothink finish=stop in=1383(c0) out=146 размышл=0зн текст=596зн $0.000452 3.8s
|
||||||
|
[jr-lu-w1-d] deepseek-v4-pro finish=stop in=1904(c768) out=2878 размышл=7615зн текст=92зн $0.003001 45.0s
|
||||||
|
[ehen-dp-full-w1] deepseek-v4-pro finish=stop in=1594(c0) out=3843 размышл=12529зн текст=2011зн $0.004037 62.5s
|
||||||
|
[jr-lu-w1-m] deepseek-v4-pro finish=stop in=1904(c768) out=5398 размышл=16458зн текст=82зн $0.005193 76.7s
|
||||||
|
[ehen-dp-diff-harness-w1] deepseek-v4-pro finish=stop in=1400(c0) out=2432 размышл=8016зн текст=96зн $0.002725 45.1s
|
||||||
|
apply: ops=1 applied=1 no_change=False починка=нет
|
||||||
|
[jr-lu-w2-d] deepseek-v4-pro finish=stop in=1999(c768) out=1101 размышл=3050зн текст=82зн $0.001496 18.9s
|
||||||
|
[ehen-dp-locate-w1] deepseek-v4-pro finish=stop in=1220(c0) out=4423 размышл=14900зн текст=9зн $0.004379 72.3s
|
||||||
|
[ehen-lu-full-w1] gpt-5.6-luna nothink finish=stop in=1694(c0) out=405 размышл=0зн текст=2009зн $0.000825 4.0s
|
||||||
|
[ehen-lu-diff-harness-w1] gpt-5.6-luna nothink finish=stop in=1490(c0) out=158 размышл=0зн текст=751зн $0.000488 1.8s
|
||||||
|
[ehen-lu-diff-harness-w1-fix] gpt-5.6-luna nothink finish=stop in=1773(c1487) out=40 размышл=0зн текст=174зн $0.000135 1.5s
|
||||||
|
apply: ops=6 applied=5 no_change=False починка=да
|
||||||
|
[ehen-lu-locate-w1] gpt-5.6-luna nothink finish=stop in=1321(c0) out=239 размышл=0зн текст=825зн $0.000551 4.1s
|
||||||
|
[jr-lu-w2-m] deepseek-v4-pro finish=stop in=1999(c768) out=4685 размышл=12817зн текст=78зн $0.004614 70.4s
|
||||||
|
[jr-lu-w3-d] deepseek-v4-pro finish=stop in=1986(c768) out=999 размышл=2529зн текст=78зн $0.001402 17.7s
|
||||||
|
[ehen-dp-full-w2] deepseek-v4-pro finish=stop in=1601(c768) out=3713 размышл=13398зн текст=1993зн $0.003595 57.6s
|
||||||
|
[jr-lu-w3-m] deepseek-v4-pro finish=stop in=1986(c768) out=3901 размышл=10721зн текст=78зн $0.003926 64.9s
|
||||||
|
[ehen-dp-diff-harness-w2] deepseek-v4-pro finish=stop in=1407(c0) out=1969 размышл=7080зн текст=248зн $0.002325 32.0s
|
||||||
|
apply: ops=2 applied=2 no_change=False починка=нет
|
||||||
|
[jr-lu-w4-d] deepseek-v4-pro finish=stop in=2150(c768) out=471 размышл=1226зн текст=74зн $0.001014 9.6s
|
||||||
|
[ehen-dp-locate-w2] deepseek-v4-pro finish=stop in=1227(c384) out=3768 размышл=13000зн текст=153зн $0.003646 53.5s
|
||||||
|
[ehen-lu-full-w2] gpt-5.6-luna nothink finish=stop in=1712(c0) out=418 размышл=0зн текст=1960зн $0.000844 6.6s
|
||||||
|
[ehen-lu-diff-harness-w2] gpt-5.6-luna nothink finish=stop in=1508(c0) out=448 размышл=0зн текст=2148зн $0.000839 3.7s
|
||||||
|
[ehen-lu-diff-harness-w2-fix] gpt-5.6-luna nothink finish=stop in=2082(c1505) out=23 размышл=0зн текст=112зн $0.000173 1.7s
|
||||||
|
apply: ops=17 applied=16 no_change=False починка=да
|
||||||
|
[ehen-lu-locate-w2] gpt-5.6-luna nothink finish=stop in=1339(c0) out=235 размышл=0зн текст=966зн $0.000550 2.7s
|
||||||
|
[jr-lu-w4-m] deepseek-v4-pro finish=stop in=2150(c768) out=4984 размышл=14171зн текст=78зн $0.004940 80.3s
|
||||||
|
[ehen-dp-full-w3] deepseek-v4-pro finish=stop in=1556(c768) out=4101 размышл=15684зн текст=1822зн $0.003913 57.4s
|
||||||
|
[ehen-dp-diff-harness-w3] deepseek-v4-pro finish=stop in=1362(c512) out=2347 размышл=8176зн текст=118зн $0.002413 39.8s
|
||||||
|
apply: ops=2 applied=2 no_change=False починка=нет
|
||||||
|
[jr-lu-w5-d] deepseek-v4-pro finish=stop in=2079(c640) out=5736 размышл=15778зн текст=78зн $0.005619 86.7s
|
||||||
|
[ehen-dp-locate-w3] deepseek-v4-pro finish=stop in=1182(c384) out=2011 размышл=6774зн текст=329зн $0.002098 30.3s
|
||||||
|
[ehen-lu-full-w3] gpt-5.6-luna nothink finish=stop in=1664(c0) out=400 размышл=0зн текст=1808зн $0.000813 5.7s
|
||||||
|
[ehen-lu-diff-harness-w3] gpt-5.6-luna nothink finish=stop in=1460(c0) out=318 размышл=0зн текст=1487зн $0.000674 4.6s
|
||||||
|
apply: ops=7 applied=7 no_change=False починка=нет
|
||||||
|
[ehen-lu-locate-w3] gpt-5.6-luna nothink finish=stop in=1291(c0) out=408 размышл=0зн текст=1551зн $0.000748 5.1s
|
||||||
|
[jr-lu-w5-m] deepseek-v4-pro finish=stop in=2079(c640) out=3894 размышл=11013зн текст=78зн $0.004016 61.2s
|
||||||
|
голосов: 36; потрачено $0.118701; → votes-deepseek-v4-pro.json
|
||||||
|
[ehen-dp-full-w4] deepseek-v4-pro finish=stop in=1570(c768) out=5419 размышл=20676зн текст=2110зн $0.005066 78.1s
|
||||||
|
[ehen-dp-diff-harness-w4] deepseek-v4-pro finish=stop in=1376(c512) out=4207 размышл=16051зн текст=101зн $0.004038 63.2s
|
||||||
|
apply: ops=1 applied=1 no_change=False починка=нет
|
||||||
|
[ehen-dp-locate-w4] deepseek-v4-pro finish=stop in=1196(c384) out=5210 размышл=19554зн текст=110зн $0.004887 75.4s
|
||||||
|
[ehen-lu-full-w4] gpt-5.6-luna nothink finish=stop in=1705(c0) out=458 размышл=0зн текст=2245зн $0.000891 4.6s
|
||||||
|
[ehen-lu-diff-harness-w4] gpt-5.6-luna nothink finish=stop in=1501(c0) out=554 размышл=0зн текст=2801зн $0.000965 4.4s
|
||||||
|
apply: ops=13 applied=13 no_change=False починка=нет
|
||||||
|
[ehen-lu-locate-w4] gpt-5.6-luna nothink finish=stop in=1332(c0) out=303 размышл=0зн текст=1253зн $0.000630 3.1s
|
||||||
|
[ehen-dp-full-w5] deepseek-v4-pro finish=stop in=1597(c768) out=4957 размышл=18294зн текст=2044зн $0.004676 72.8s
|
||||||
|
[ehen-dp-diff-harness-w5] deepseek-v4-pro finish=stop in=1403(c512) out=3351 размышл=12469зн текст=150зн $0.003305 54.9s
|
||||||
|
apply: ops=2 applied=2 no_change=False починка=нет
|
||||||
|
[ehen-dp-locate-w5] deepseek-v4-pro finish=stop in=1223(c384) out=4459 размышл=15714зн текст=188зн $0.004246 71.5s
|
||||||
|
[ehen-lu-full-w5] gpt-5.6-luna nothink finish=stop in=1727(c0) out=441 размышл=0зн текст=2041зн $0.000875 5.7s
|
||||||
|
[ehen-lu-diff-harness-w5] gpt-5.6-luna nothink finish=stop in=1523(c0) out=151 размышл=0зн текст=693зн $0.000486 2.3s
|
||||||
|
apply: ops=7 applied=7 no_change=False починка=нет
|
||||||
|
[ehen-lu-locate-w5] gpt-5.6-luna nothink finish=stop in=1354(c0) out=175 размышл=0зн текст=652зн $0.000481 2.2s
|
||||||
|
ИТОГО эксп 20: $1.160259
|
||||||
118
eval/editor_harness/selfcheck.py
Normal file
118
eval/editor_harness/selfcheck.py
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Селф-ревью рига эксп-20 ДО платных вызовов (требование владельца 05.08).
|
||||||
|
|
||||||
|
Проверяется исполнением, а не глазами:
|
||||||
|
С1 ключи всех заявленных провайдеров присутствуют в окружении (значения НЕ печатаются);
|
||||||
|
С2 все четыре контракта рендерятся без незаполненных плейсхолдеров, и блок закона одинаков;
|
||||||
|
С3 `classify_ops` воспроизводит счёт отказов настоящего аппликатора на УЖЕ ОПЛАЧЕННОМ сырье
|
||||||
|
эксп-19 — иначе раунд починки вернёт модели не те операции;
|
||||||
|
С4 контракты действительно разные на проводе (иначе меряем один и тот же промпт дважды);
|
||||||
|
С5 гардрейл: попытка погасить мышление у DeepSeek обязана падать, а не тихо проходить.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
# ⚠ ПОРЯДОК ВАЖЕН (селф-ревью 05.08): в `eval/editor_contract/` лежит СВОЙ `probe.py`, и при
|
||||||
|
# обратном порядке вставок он затеняет наш — `import probe` подтягивал чужой модуль.
|
||||||
|
# HERE вставляется ПОСЛЕДНИМ, поэтому стоит первым в sys.path и выигрывает.
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "editor_contract"))
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||||||
|
sys.path.insert(0, str(HERE))
|
||||||
|
|
||||||
|
import probe as Q # noqa: E402
|
||||||
|
import editor_wire_probe as P # noqa: E402
|
||||||
|
from apply import apply_ops # noqa: E402
|
||||||
|
from inject_probe import pick_windows # noqa: E402
|
||||||
|
|
||||||
|
OLD = Path.home() / "books" / "gu-zhenren" / "bank-arbitration"
|
||||||
|
FAIL = 0
|
||||||
|
|
||||||
|
|
||||||
|
def check(name: str, ok: bool, note: str = "") -> None:
|
||||||
|
global FAIL
|
||||||
|
if not ok:
|
||||||
|
FAIL += 1
|
||||||
|
print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {name}" + (f" — {note}" if note and not ok else ""))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
# С1 — ключи (значения наружу не идут)
|
||||||
|
for m, spec in Q.MODELS.items():
|
||||||
|
check(f"С1 ключ {spec[1]} для {m}", bool(os.environ.get(spec[1])), "не найден в окружении")
|
||||||
|
|
||||||
|
wins = pick_windows()
|
||||||
|
_, buf, terms = wins[0]
|
||||||
|
draft = P.draft_of(0)
|
||||||
|
blk = P.editor_block(terms, P.HEADER_LAW_CLAUSE, None)
|
||||||
|
|
||||||
|
# С2 — рендер
|
||||||
|
sysmsgs = {}
|
||||||
|
for c in Q.CONTRACTS:
|
||||||
|
msgs = Q.messages(c, buf, draft, blk)
|
||||||
|
holes = set()
|
||||||
|
for mm in msgs:
|
||||||
|
holes |= set(re.findall(r"\{\{(\w+)\}\}", mm["content"]))
|
||||||
|
check(f"С2 {c}: плейсхолдеров нет", not holes, ", ".join(sorted(holes)))
|
||||||
|
check(f"С2 {c}: блок закона на месте", any("КАНОНИЧЕСКИЕ" in mm["content"] for mm in msgs))
|
||||||
|
sysmsgs[c] = msgs[0]["content"]
|
||||||
|
|
||||||
|
# С4 — контракты различны
|
||||||
|
uniq = len(set(sysmsgs.values()))
|
||||||
|
check("С4 все четыре контракта различны на проводе", uniq == len(Q.CONTRACTS),
|
||||||
|
f"уникальных системных промптов {uniq} из {len(Q.CONTRACTS)}")
|
||||||
|
for c, t in sysmsgs.items():
|
||||||
|
print(f" {c:13s} системный промпт {len(t):5d} зн.")
|
||||||
|
|
||||||
|
# С6 — секция FEWSHOT обязана дропаться (дефект 05.08, пойман в ходе прогона: арм `full`
|
||||||
|
# уехал с примерами перевёрстки, то есть база сравнения не была боевым контрактом).
|
||||||
|
fullsys = sysmsgs["full"]
|
||||||
|
check("С6 full: блок FEWSHOT дропнут, как в проде",
|
||||||
|
"---FEWSHOT---" not in fullsys and "Он вышел за дверь" not in fullsys)
|
||||||
|
check("С6 full: блок ДИСКУРС-ПЕРЕВЁРСТКИ на месте", "ДИСКУРС-ПЕРЕВЁРСТКА" in fullsys)
|
||||||
|
check("С6 full: длина совпала с проводом эксп-19 (3071 зн.)", len(fullsys) == 3071,
|
||||||
|
f"сейчас {len(fullsys)}")
|
||||||
|
|
||||||
|
# С3 — классификатор против настоящего аппликатора на оплаченном сырье
|
||||||
|
tot = mism = seen = 0
|
||||||
|
for k in range(6):
|
||||||
|
for r in (1, 2, 3):
|
||||||
|
f = OLD / f"ec-D1-w{k}-r{r}.json"
|
||||||
|
if not f.exists():
|
||||||
|
continue
|
||||||
|
reply = json.loads(f.read_text(encoding="utf-8"))["content"]
|
||||||
|
d = P.draft_of(k)
|
||||||
|
res = apply_ops(d, reply)
|
||||||
|
mine = Q.classify_ops(d, reply)
|
||||||
|
seen += 1
|
||||||
|
# аппликатор считает отказы notfound+ambiguous; мой классификатор — те же классы
|
||||||
|
ref = res.rejected_notfound + res.rejected_ambiguous
|
||||||
|
got = sum(1 for _, _, st in mine if st in ("reject_notfound", "reject_ambiguous"))
|
||||||
|
tot += ref
|
||||||
|
if ref != got:
|
||||||
|
mism += 1
|
||||||
|
print(f" ⚠ {f.stem}: аппликатор {ref}, классификатор {got}")
|
||||||
|
if len(mine) != res.ops_total and not res.malformed:
|
||||||
|
mism += 1
|
||||||
|
print(f" ⚠ {f.stem}: операций аппликатор {res.ops_total}, классификатор {len(mine)}")
|
||||||
|
check(f"С3 классификатор совпал с аппликатором на {seen} вызовах эксп-19 (отказов в сырье {tot})",
|
||||||
|
mism == 0, f"расхождений {mism}")
|
||||||
|
|
||||||
|
# С5 — гардрейл
|
||||||
|
try:
|
||||||
|
Q.call("deepseek-v4-pro", [{"role": "user", "content": "x"}], "selfcheck-guard", True)
|
||||||
|
check("С5 гашение мышления у DeepSeek запрещено", False, "вызов ПРОШЁЛ — гардрейл дырявый")
|
||||||
|
except SystemExit:
|
||||||
|
check("С5 гашение мышления у DeepSeek запрещено", True, "падает, как и должно")
|
||||||
|
|
||||||
|
print(f"\n{'РИГ ГОТОВ' if not FAIL else f'ПРОВАЛОВ: {FAIL}'}")
|
||||||
|
sys.exit(1 if FAIL else 0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
113
eval/editor_harness/verify_report.py
Normal file
113
eval/editor_harness/verify_report.py
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Сверка НЕСУЩИХ ЧИСЕЛ отчёта `docs/experiments/20-editor-role.md` с сырьём.
|
||||||
|
|
||||||
|
Зачем отдельным скриптом: числа в отчёт попадают через мою же аналитику, и опечатка или устаревшая
|
||||||
|
цифра после пере-съёмки арма визуально неотличимы от факта. Здесь каждое заявленное число
|
||||||
|
пере-выводится из персистированных артефактов заново и сравнивается с тем, что написано в тексте.
|
||||||
|
|
||||||
|
Запуск: eval/.venv/bin/python eval/editor_harness/verify_report.py
|
||||||
|
Ненулевой код возврата = отчёт разошёлся с сырьём и его нельзя лендить.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import difflib
|
||||||
|
import json
|
||||||
|
import statistics
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||||
|
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||||||
|
RAW = Path.home() / "books" / "gu-zhenren" / "editor-harness"
|
||||||
|
|
||||||
|
import editor_wire_probe as P # noqa: E402
|
||||||
|
|
||||||
|
FAILS = 0
|
||||||
|
|
||||||
|
|
||||||
|
def g(tag: str):
|
||||||
|
f = RAW / f"{tag}.json"
|
||||||
|
return json.loads(f.read_text(encoding="utf-8")) if f.exists() else None
|
||||||
|
|
||||||
|
|
||||||
|
def ck(claim: str, ok: bool, got: str = "") -> None:
|
||||||
|
global FAILS
|
||||||
|
if not ok:
|
||||||
|
FAILS += 1
|
||||||
|
print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {claim}" + ("" if ok else f" — сырьё даёт {got}"))
|
||||||
|
|
||||||
|
|
||||||
|
def cjk(s: str) -> int:
|
||||||
|
return sum(1 for ch in s if 0x3400 <= ord(ch) <= 0x9FFF)
|
||||||
|
|
||||||
|
|
||||||
|
def out_text(tag: str) -> str | None:
|
||||||
|
a = RAW / f"{tag}.applied.json"
|
||||||
|
if a.exists():
|
||||||
|
return json.loads(a.read_text(encoding="utf-8"))["text"]
|
||||||
|
r = g(tag)
|
||||||
|
return r["content"] if r else None
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
# §3.1 — отношение цены дифф/full по моделям и режимам
|
||||||
|
for ms, pre, claim in (("dp", "eh", 2.83), ("gl", "eh", 0.64), ("gl", "eht", 0.86),
|
||||||
|
("gr", "eh", 0.60), ("gr", "eht", 0.66),
|
||||||
|
("lu", "eh", 1.01), ("lu", "eht", 0.89)):
|
||||||
|
f = [g(f"{pre}-{ms}-full-w{k}") for k in range(6)]
|
||||||
|
d = [g(f"{pre}-{ms}-diff-harness-w{k}") for k in range(6)]
|
||||||
|
if any(x is None for x in f + d):
|
||||||
|
ck(f"§3.1 {ms}/{pre}", False, "нет артефактов")
|
||||||
|
continue
|
||||||
|
r = statistics.median(d[k]["cost_usd"] / f[k]["cost_usd"] for k in range(6))
|
||||||
|
ck(f"§3.1 {ms}/{pre} отношение дифф/full = {claim}", abs(r - claim) < 0.015, f"{r:.3f}")
|
||||||
|
|
||||||
|
# §3.3 — цена починки и полнота снятия дефектов
|
||||||
|
for ms, claim in (("dp", 0.000625), ("lu", 0.000173)):
|
||||||
|
f = RAW / f"repair-{ms}.json"
|
||||||
|
if not f.exists():
|
||||||
|
ck(f"§3.3 {ms}", False, "нет repair-*.json")
|
||||||
|
continue
|
||||||
|
rows = json.loads(f.read_text(encoding="utf-8"))
|
||||||
|
med = statistics.median(x["cost"] for x in rows)
|
||||||
|
ck(f"§3.3 {ms} цена одной починки = {claim}", abs(med - claim) < 1e-6, f"{med:.6f}")
|
||||||
|
snyat = sum(1 for x in rows if not x["still"])
|
||||||
|
ck(f"§3.3 {ms} дефект снят 12/12", snyat == 12, str(snyat))
|
||||||
|
inf = [x for x in rows if not x["degenerate"]]
|
||||||
|
strict = sum(1 for x in inf if not x["still"] and x["restored"])
|
||||||
|
ck(f"§3.3 {ms} восстановлен строго 7/8", (strict, len(inf)) == (7, 8),
|
||||||
|
f"{strict}/{len(inf)}")
|
||||||
|
|
||||||
|
# §4 — доля слов черновика, доживших до выхода
|
||||||
|
for ms, c, claim in (("dp", "full", 0.65), ("gl", "full", 0.83),
|
||||||
|
("dp", "diff-harness", 0.98)):
|
||||||
|
vals = []
|
||||||
|
for k in range(6):
|
||||||
|
o = g(f"eh-{ms}-{c}-w{k}")
|
||||||
|
if not o:
|
||||||
|
continue
|
||||||
|
dw = P.draft_of(k).split()
|
||||||
|
ow = (out_text(f"eh-{ms}-{c}-w{k}") or "").split()
|
||||||
|
sm = difflib.SequenceMatcher(None, dw, ow, autojunk=False)
|
||||||
|
vals.append(sum(b.size for b in sm.get_matching_blocks()) / len(dw))
|
||||||
|
ck(f"§4 {ms}/{c} слов дожило {claim:.0%}",
|
||||||
|
bool(vals) and abs(statistics.median(vals) - claim) < 0.015,
|
||||||
|
f"{statistics.median(vals):.3f}" if vals else "нет данных")
|
||||||
|
|
||||||
|
# §5.4 — утечка исходника в выход у luna
|
||||||
|
for c, w_claim, n_claim in (("full", 3, 19), ("diff-harness", 3, 16)):
|
||||||
|
outs = [cjk(out_text(f"eh-lu-{c}-w{k}") or "") for k in range(6)]
|
||||||
|
ck(f"§5.4 luna/{c}: окон с утечкой {w_claim}, знаков {n_claim}",
|
||||||
|
sum(1 for x in outs if x) == w_claim and sum(outs) == n_claim,
|
||||||
|
f"{sum(1 for x in outs if x)} окон, {sum(outs)} знаков")
|
||||||
|
|
||||||
|
# deepseek обязан быть чист по обоим контрактам — это утверждение отчёта
|
||||||
|
for c in ("full", "diff-harness"):
|
||||||
|
outs = [cjk(out_text(f"eh-dp-{c}-w{k}") or "") for k in range(6)]
|
||||||
|
ck(f"§5.4 deepseek/{c}: утечки нет", sum(outs) == 0, f"{sum(outs)} знаков")
|
||||||
|
|
||||||
|
print(f"\n{'ВСЕ ЧИСЛА ОТЧЁТА СХОДЯТСЯ' if not FAILS else f'РАСХОЖДЕНИЙ: {FAILS}'}")
|
||||||
|
sys.exit(1 if FAILS else 0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Add table
Reference in a new issue