Carry the polygon's uncommitted phase-D work onto its own branch: judging rigs, session logs and plans that had been living in the shared tree since the machine move
This commit is contained in:
parent
3333119cd3
commit
28c2d0fd50
19 changed files with 4164 additions and 89 deletions
|
|
@ -77,7 +77,14 @@ def rows(md: str) -> list[tuple[str, str, str]]:
|
|||
if out: # таблица кончилась
|
||||
break
|
||||
continue
|
||||
cells = [c.strip() for c in s.strip("|").split("|")]
|
||||
# ⚠ Markdown экранирует вертикальную черту внутри ячейки как `\|`. Наивный `split("|")`
|
||||
# рвал такую ячейку пополам, и улика приходила обрезанной: у R55 в оболочку уезжало
|
||||
# `... | wc -l) -ge 7` без начала, что давало «Syntax error» и ЛОЖНЫЙ КРАСНЫЙ, а у R37 —
|
||||
# ЛОЖНЫЙ ЗЕЛЁНЫЙ, потому что обрезок случайно возвращал 0. Гейт, который сам портит
|
||||
# улику, не проверяет ничего. Восстанавливается ОБЫЧНАЯ черта: в markdown `\|` и означает
|
||||
# литеральный `|`, и в оболочку должен уйти именно он.
|
||||
cells = [c.replace("\x00", "|").strip()
|
||||
for c in s.strip("|").replace("\\|", "\x00").split("|")]
|
||||
if len(cells) < 3 or set(cells[0]) <= set("-: "):
|
||||
continue
|
||||
if cells[0].upper() in ("ID", "№"):
|
||||
|
|
|
|||
382
eval/dovodka/HANDOFF-21-08.md
Normal file
382
eval/dovodka/HANDOFF-21-08.md
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
# ХЕНДОФФ: полигон, следующая сессия (готовлено 20.08 вечером)
|
||||
|
||||
> Читать ЦЕЛИКОМ, не грепом. Прошлые передачи теряли пункты именно из-за чтения грепом.
|
||||
> Предыдущие слои: `SESSION2-LOG.md` разделы 15.1–15.21 (по ходу этой сессии) ·
|
||||
> `PLAN-17-08.md` (курс) · отчёт `docs/experiments/23-editor-tier.md` секции Д21, Д23 ·
|
||||
> `КОНТЕКСТ-ДЛЯ-КОНСУЛЬТАЦИИ.md` (сжатая картина проекта для внешнего собеседника).
|
||||
|
||||
---
|
||||
|
||||
## 0. ГЛАВНОЕ, ЧТО ИЗМЕНИЛОСЬ ЗА СЕССИЮ
|
||||
|
||||
**Сменился ПРИБОР.** Решение владельца 20.08 дословно: «меняем принцип судейства… теперь корми
|
||||
фабл 5 и пусть он решает какой из вариантов ближе по художественному смыслу, лучше по переводу»,
|
||||
и позже: «Теперь судим так все время». Слепое чтение Fable-5 — ПЕРВИЧНЫЙ прибор; счёт ошибок
|
||||
остаётся брак-скрином.
|
||||
|
||||
**Основание замерено, а не принято на веру.** На панели, которую читал сам владелец:
|
||||
|
||||
```
|
||||
пара прибор Спирмен с владельцем
|
||||
zh чтение Fable (чистый пакет) +0.80
|
||||
zh счёт ошибок +0.80
|
||||
en чтение Fable (чистый пакет) +0.80
|
||||
en счёт ошибок −0.80
|
||||
```
|
||||
|
||||
Новый прибор согласен с владельцем ОДИНАКОВО на обеих парах; старый меняет ЗНАК от пары к паре.
|
||||
|
||||
⚠ **И тут же найдена моя ошибка:** в пакете, который читал владелец, стояла подсказка — «вариант
|
||||
с наименьшим числом ошибок читался почти хуже всех». Пере-прогон на НЕЙТРАЛЬНОМ пакете (тексты
|
||||
побайтно те же) дал +0.80 вместо +1.00: подсказка стоила 0.20, верх и низ порядка устояли.
|
||||
**Норма, заведённая этим: пакет чтения не имеет права называть ни вердикт другого прибора, ни
|
||||
существование контроля.**
|
||||
|
||||
---
|
||||
|
||||
## 1. ЧТО ЖДЁТ СЛЕДУЮЩУЮ СЕССИЮ — ГОТОВО К ЗАПУСКУ
|
||||
|
||||
**15 пакетов слепого чтения английской пары, по ОДНОЙ главе в пакете, панель из ОДИННАДЦАТИ
|
||||
архитектур.** Всё куплено, покупать больше нечего.
|
||||
|
||||
```
|
||||
пакеты ~/books/chtenie-rol/ЧТЕНИЕ-en-arch2-<uid8>.md (15 штук, ~17 тыс. знаков каждый)
|
||||
ключи ~/books/dovodka/blind-keys-rol/rol-KEY-arch2-<uid8>.json ЧИТАТЕЛЮ НЕ ДАВАТЬ
|
||||
ответы ~/books/chtenie-rol/ОТВЕТ-en-arch2-<uid8>.md (заготовки созданы)
|
||||
```
|
||||
|
||||
Панель: `ZD` голый черновик · `Z0` боевая связка (продакшен) · `ZF` её вторая генерация
|
||||
(контроль шума) · `ZP` однопроходка-склейка · `Z8` обогащённый черновик · `Z8R` он же плюс перепис ·
|
||||
`Z1` критик → ПОЛНЫЙ перепис (ставка владельца H-2б) · `Z7` однопроходка + канон-фиксер ·
|
||||
`RN` промт-РОЛЬ с экзаменом · `RC` он же без экзамена · `RB` он же с ПЕРЕВЁРНУТЫМ экзаменом
|
||||
(формулировка владельца).
|
||||
|
||||
⚠ **Одна глава из 16 пропущена и это НАПЕЧАТАНО, а не умолчано:** `100b088f71` — у неё нет клетки
|
||||
`RN` (единственная, что не собралась после двух попыток). Пакетов 15.
|
||||
⛔ **ПОПРАВКА 21.08: строка ниже была НЕВЕРНА, и это ровно тот класс, о котором предупреждает шапка
|
||||
файла.** Утверждение «прежние пакеты `arch` УДАЛЕНЫ» я записал как исполненное, не исполнив: на
|
||||
диске лежат **16 пакетов `ЧТЕНИЕ-en-arch-<uid>.md`, 16 пустых `ОТВЕТ-en-arch-<uid>.md` и 16 ключей
|
||||
`rol-KEY-arch-<uid>.json`** (панель из 8, ответов нет ни одного — проверено размером файлов).
|
||||
Удалять не стал: сырьё чужой рукой не сносят, а механической коллизии глобов между `arch-` и
|
||||
`arch2-` нет (проверено). Опасность ЧЕЛОВЕЧЕСКАЯ — принять один тег за другой; для того здесь и
|
||||
стоит эта поправка.
|
||||
|
||||
~~Прежние 15 пакетов с тегом `arch` (панель из 8) УДАЛЕНЫ~~ — держать две панели одной пары значит
|
||||
однажды свести ответы разных панелей одним ключом.
|
||||
|
||||
**Работать с `arch2`. Пакеты `arch` существуют, не прочитаны и не годятся: панель у них из 8.**
|
||||
|
||||
**Сборка ПРОВЕРЕНА СПЛОШЬ** (владелец просил убедиться до передачи):
|
||||
* 15 пакетов, 15 уникальных глав, 15 РАЗНЫХ раскладок меток;
|
||||
* **165 текстов сверены побайтно с содержимым клеток своих армов — расхождений 0**;
|
||||
* в рамке КАЖДОГО пакета стоит указание владельца «торопиться не надо» (проверено гейтом);
|
||||
* исходник в каждом пакете — тот самый, что у главы;
|
||||
* имён армов и путей к ключу в пакетах нет;
|
||||
* рамка нейтральна: не называет ни контроля, ни другого прибора.
|
||||
⚠ Первая проверка дала 16 «утечек» — это была ЛОЖНАЯ тревога моей же проверки: она искала слово
|
||||
«метки», которое в рамке стоит легально («метки перемешаны»). Проверка переделана на поиск ИМЁН
|
||||
АРМОВ. Не повторить эту ошибку в обратную сторону: тревога гейта требует вскрытия, а не отмены.
|
||||
|
||||
**Как запускать:** по ОДНОМУ агенту Fable-5 на пакет (решение владельца: главы большие, читателю
|
||||
с несколькими сразу тяжело). Промт-рамку взять из журнала §15.13/§15.21 — она уже отработала
|
||||
четырежды. Обязательно: идентичность читателя в файл ДО запуска · `runs.py --claim` ДО запуска ·
|
||||
греп-аудит транскрипта ПОСЛЕ (образец кода в журнале).
|
||||
|
||||
✔ **КАП СНЯТ 20.08 словом владельца** («Можно поднять, но я не припомню что б ставил тебе жёсткие
|
||||
ограничения»): 100 → 200, свободно 107, на 16 глав хватает. ⚠ Уточнение для истории: число 80
|
||||
пришло из ЗАКАЗА (:25), владелец снял его ещё 15.08, а 100 поставил в код Я САМ — то есть сессия
|
||||
упиралась в СВОЁ ограничение и подала его владельцу как чужое. Не повторять: перед тем как назвать
|
||||
границу «ограничением владельца», найти его слово.
|
||||
|
||||
---
|
||||
|
||||
## 2. ЧТО УСТАНОВЛЕНО ЭТОЙ СЕССИЕЙ — числа, которые нельзя потерять
|
||||
|
||||
### 2.1 ⭐ Механизм цены вскрыт ТРЕЙСОМ, а не выведен
|
||||
|
||||
Сохранение трейса размышления заведено по флагу `TM_KEEP_TRACE=1` (`role_topology/buy.py`).
|
||||
Прямая мера: доля предложений ФИНАЛЬНОГО текста, встречающихся в трейсе дословно — **медиана 96%**.
|
||||
Разбор трейса: черновик в уме 54% абзацев · структура 11% · **само-проверка 4%** · глоссарий 3%.
|
||||
|
||||
⇒ **Размышление у `deepseek-v4-pro` — это ЧЕРНОВИК, а не проверка. Модель пишет перевод дважды:
|
||||
в уме и в ответе. Мы платим за обе копии по цене выхода.**
|
||||
⇒ Следствие для архитектуры: **боевая двухстадийная связка дёшева БЛАГОДАРЯ топологии — она
|
||||
выносит черновик из канала размышления дорогой модели в выход дешёвой** ($3.96 против $1.32 за
|
||||
миллион), и результат при этом виден, проверяем и переиспользуем, а не выбрасывается.
|
||||
⚠ Режим БИМОДАЛЕН: одна клетка из шести в ум не писала вовсе (1 180 токенов против 23 000).
|
||||
|
||||
### 2.2 Промт-роль: измерен, лучше склейки, дороже вчетверо
|
||||
|
||||
Новый промт однопроходки собран как РОЛЬ (прежний был СКЛЕЙКОЙ двух боевых промтов с мета-фразой
|
||||
«отдельного редактора после тебя НЕ БУДЕТ»). Ядро — `eval/dovodka/prompts/onepass-core.md`,
|
||||
пар-специфика подтягивается из файлов пары, второго источника правды нет.
|
||||
|
||||
```
|
||||
арм что это размышление $/клетка перевёрстка
|
||||
ZP прежняя склейка 950 0.0177 1.77
|
||||
RN роль, экзамен на смелости 28 809 0.1265 1.60
|
||||
RB роль, экзамен ПЕРЕВЁРНУТ 23 301 0.1050 1.14
|
||||
RC роль, экзамен вырезан 11 268 0.0593 1.37
|
||||
Z0 боевая связка (для сравнения) — 0.0253 1.79
|
||||
```
|
||||
|
||||
Слепое чтение абляции (3 главы, 5 вариантов): **`RB > Z0 > RC > RN > ZF`**.
|
||||
⇒ формулировка ВЛАДЕЛЬЦА («брак = просвечивающий исходный язык») бьёт обе мои редакции и встала
|
||||
выше продакшена. ⛔ **НО контроль шума КРАСНЫЙ** — две генерации связки на 2-м и 5-м местах,
|
||||
то есть порядок при n=3 не разрешим. Уцелевает: `RB` не хуже продакшена, и три редакции промта
|
||||
упорядочены между собой.
|
||||
|
||||
### 2.3 ⛔ ЧТО НЕ СРАВНИМО МЕЖДУ СОБОЙ — поймано владельцем
|
||||
|
||||
Четыре слепых чтения этой сессии сделаны на РАЗНЫХ главах:
|
||||
A и B — глава `ed068182cf` · C — `1431129de6, c73f4857e7, 89614f9bfa` · D — `ef9cd38ec4,
|
||||
89614f9bfa, b2a7464dbd`. **У C и D общая ОДНА глава из трёх, у A/B с C/D — НИ ОДНОЙ.**
|
||||
⇒ фразы «арм был третьим, стал четвёртым» — НЕ повторный замер. Внутри одного чтения сравнения
|
||||
законны, между чтениями нет. Ради этого и собраны 16 пакетов одной панели на одних главах.
|
||||
|
||||
### 2.4 Замер критика (фаза ФД-K, остановлен владельцем)
|
||||
|
||||
4 клетки одной главы: фраза о параллельности текстов срезала размышление на 55% и цену вдвое —
|
||||
и находки тоже вдвое (24 → 12). **Критик НЕ видит удалённого целого абзаца** (проверено: слов из
|
||||
пропавшего куска нет ни у одной модели), при том что снятую частицу «не» ловит.
|
||||
Реальный дефект в черновике удорожает критика **в 14 раз** против чистого.
|
||||
`glm-5` с включённым размышлением не влезает в потолок вывода на китайском.
|
||||
⇒ полноту обязан ловить детерминированный гейт, а не критик.
|
||||
|
||||
### 2.5 Английский экстрактор ломает абзацы — наш баг
|
||||
|
||||
`pair_en.raw_text` сносит из epub все теги, включая `</p>`: в книге **11 447 абзацев**, а модели
|
||||
едет один блок. Чинить на месте НЕЛЬЗЯ: uid единицы = `sha1(source.strip())`, правка пробелов
|
||||
переименует всё — замерено, что повиснут **289 оплаченных клеток на $2.64** и семь файлов ключей.
|
||||
Починка сделана НА ПОДАЧЕ (арм `RA`, функция `podacha.restore_paragraphs`, проверено 16/16).
|
||||
⛔ **ДОЛГ ЧУЖОЙ ЗОНЕ: настоящая починка — в бэкенде. Пинг оркестратору обязателен.**
|
||||
|
||||
### 2.6 Приз best-of-2 пере-считан ЧЕСТНО (не круговым способом)
|
||||
|
||||
Выбирает читатель A, оценивает читатель B (и наоборот):
|
||||
```
|
||||
все главы: +0.28 позиции p=0.30 — шум
|
||||
расхождение ≥10% текста: +2.36 позиции p=0.0065 — 12 подтвердили, 2 опровергли
|
||||
расхождение <10%: −1.33 позиции p=0.95 — отбор ВРЕДИТ
|
||||
```
|
||||
⇒ наивный «бери лучший из двух» бесполезен; на разошедшихся главах приз реален и больше
|
||||
оракульного. Порог 10% выбран post-hoc — заморозить пре-регом и проверить на новых главах.
|
||||
|
||||
### 2.7 Г5 закрыта: вердикт по H-1 устоял
|
||||
|
||||
`itog_d4.py --sens`. Арифметика находки ревизии воспроизведена побайтно, но переворот живёт только
|
||||
в сценарии, который возвращает пачку аннулированной сессии и одновременно снимает пачки-валидации.
|
||||
Смежно закрыты P12, P07, R73/P40. ⛔ ОТКРЫТА **P42** — два пре-рег-правила о маржевом гейте
|
||||
противоречат, и от выбора зависит, остаётся ли единственный CONFIRM фазы. **Вопрос владельцу.**
|
||||
|
||||
---
|
||||
|
||||
## 3. ДЕНЬГИ
|
||||
|
||||
```
|
||||
ФД-K 0.2927 / 0.40 замер подачи входа критику, zh (остановлен владельцем)
|
||||
ФД-L 0.0000 / 1.30 то же, en — не начиналось
|
||||
ФД-M 2.0066 / 2.10 однопроходка как роль, zh
|
||||
ФД-N 0.0000 / 2.40 то же, en — не начиналось
|
||||
ПАК 14.0221 / 18.30 свободно 4.28
|
||||
```
|
||||
Санкции владельца этой сессии: «Хорошо, поехали» на $1.70 (ФД-K/L) и «Бюджет разрешаю какой
|
||||
нужен» (ФД-M/N). ⚠ Смета ФД-M была занижена ВШЕСТЕРО: считалась по клеткам старой склейки, у
|
||||
которой размышления в 26 раз меньше. Норма: цену клетки НОВОГО промта нельзя оценивать по клеткам
|
||||
СТАРОГО.
|
||||
|
||||
---
|
||||
|
||||
## 4. КОМАНДЫ
|
||||
|
||||
```
|
||||
eval/.venv/bin/python eval/dovodka/rol.py --selftest # гейт покрытия боевых промтов
|
||||
eval/.venv/bin/python eval/dovodka/rol.py --show zh|en # промт целиком, как поедет
|
||||
eval/.venv/bin/python eval/dovodka/rol.py --coverage zh|en # диспозиция КАЖДОГО боевого правила
|
||||
eval/.venv/bin/python eval/dovodka/rol.py --dry # смета
|
||||
eval/.venv/bin/python eval/dovodka/rol.py --canon # $0-гейт канона по армам
|
||||
eval/.venv/bin/python eval/dovodka/rol.py --emit-read en --panel=… --tag=… --each --n=16
|
||||
eval/.venv/bin/python eval/dovodka/itog_d4.py --axis=d4 --sens # Г5 целиком
|
||||
eval/.venv/bin/python eval/dovodka/money_d.py --report | --selftest
|
||||
eval/.venv/bin/python eval/dovodka/runs.py --status | --claim … | --done N
|
||||
TM_KEEP_TRACE=1 …rol.py --buy zh RC RB --n=3 # покупка с сохранением трейса
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. ЛОВУШКИ, ДОБАВИВШИЕСЯ ЭТОЙ СЕССИЕЙ
|
||||
|
||||
1. **Гейт, сертифицирующий правило по ключевому слову, обходится словом из правила
|
||||
ПРОТИВОПОЛОЖНОГО смысла.** Мой гейт покрытия считал «убирай лишние повторы» покрытым словом
|
||||
«разнообразь», которое стоит в правиле «НЕ разнообразь». Поймала приёмка Fable-5.
|
||||
2. **Правила короче 25 знаков гейт не сканировал вовсе** — порог длины был слепым пятном.
|
||||
3. **Мёртвая строка таблицы неотличима от живой.** Заведена проверка «каждая строка обязана
|
||||
сработать хотя бы раз».
|
||||
4. **Свой же пакет чтения может нести подсказку.** См. §0.
|
||||
5. **Тревога собственной проверки может быть ложной** (слово «метки» в рамке). Вскрывать, а не
|
||||
отменять.
|
||||
6. **Фриз-гейт кассы срабатывает на ЛЮБУЮ правку покупающего файла** — правил код после фриза,
|
||||
покупка отказана. Это правильно; фризить ПЕРЕД покупкой.
|
||||
7. **Оборванные на потолке клетки надо уводить в карантин ПЕРЕД перекупкой** — кэш `purchase`
|
||||
смотрит на существование файла и вернёт старую запись.
|
||||
8. **Ключ каждой панели чтения — в СВОЁМ файле.** Пере-эмиссия с другим составом армов переписала
|
||||
бы раскладку уже прочитанной панели, и ответ читателя стал бы мусором молча.
|
||||
|
||||
---
|
||||
|
||||
## 6. ЧТО СПРОСИТЬ У ВЛАДЕЛЬЦА ПЕРВЫМ ДЕЛОМ
|
||||
|
||||
1. **Кап агент-сессий: осталось 7 из 100**, а на полное чтение 16 глав нужно 16. Поднимать?
|
||||
2. ~~**P42**~~ — **СНЯТ С ПОВЕСТКИ РЕШЕНИЕМ ВЛАДЕЛЬЦА 20.08.** См. §8.
|
||||
3. ~~Нужны ли новые редакции промта в английской панели~~ — **ВЛАДЕЛЕЦ СКАЗАЛ ДА (20.08).**
|
||||
См. §7.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 7. РЕШЕНИЕ ВЛАДЕЛЬЦА 20.08: НОВЫЕ РЕДАКЦИИ ПРОМТА — В АНГЛИЙСКУЮ ПАНЕЛЬ
|
||||
|
||||
Дословно: «Нужны, можешь занести в хэндофф?»
|
||||
|
||||
**Что докупить ПЕРЕД чтением английской панели:**
|
||||
|
||||
```
|
||||
арм что это модель клеток ~цена
|
||||
RN роль, экзамен с риском на смелости deepseek-v4-pro 16 $0.35
|
||||
RC роль, экзамен ВЫРЕЗАН deepseek-v4-pro 16 $0.35
|
||||
RB роль, экзамен ПЕРЕВЁРНУТ (лучшая редакция) deepseek-v4-pro 16 $0.35
|
||||
ИТОГО ≈ $1.05
|
||||
```
|
||||
|
||||
⚠ **Смета ОРИЕНТИРОВОЧНАЯ и снята с КИТАЙСКИХ клеток.** Английская глава короче китайской, но
|
||||
норма, выученная этой сессией дорого: цену клетки нельзя оценивать по клеткам другого промта или
|
||||
другой пары. Перед покупкой прогнать `rol.py --dry` и сверить с фактом на первых трёх клетках.
|
||||
Касса ФД-N: потрачено $0, потолок $2.40 — влезает с запасом.
|
||||
|
||||
⚠ **Порядок работ жёсткий:** сперва докупить, потом ПЕРЕ-эмитировать пакеты с расширенной панелью
|
||||
под НОВЫМ тегом (`--tag=arch2`), потому что пере-эмиссия с другим составом армов переписывает
|
||||
раскладку меток. Существующие 16 пакетов `arch` НЕ трогать — если их уже прочитали, ответы
|
||||
станут мусором.
|
||||
|
||||
**Панель станет из 11 армов:** `ZD Z0 ZF ZP Z8 Z8R Z1 Z7` + `RN RC RB`.
|
||||
⚠ Одиннадцать вариантов одной главы — много для одного читателя. Решать владельцу: либо две
|
||||
сессии на главу с разными половинами панели и общим якорем (`Z0` в обеих), либо панель урезать.
|
||||
Резать решением сессии ЗАПРЕЩЕНО заказом.
|
||||
|
||||
**Команда после покупки:**
|
||||
```
|
||||
eval/.venv/bin/python eval/dovodka/rol.py --emit-read en \
|
||||
--panel=ZD,Z0,ZF,ZP,Z8,Z8R,Z1,Z7,RN,RC,RB --tag=arch2 --each --n=16
|
||||
```
|
||||
и обязательная сплошная сверка сборки — образец кода в журнале §15.21 (128 текстов побайтно
|
||||
против клеток, отсутствие имён армов в пакете, нейтральность рамки, разные раскладки).
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 8. РЕШЕНИЕ ВЛАДЕЛЬЦА 20.08: SOL ПАРКУЕТСЯ, СУДИТ ТОЛЬКО FABLE
|
||||
|
||||
Дословно: «Давай пока забьем на сол и отдадим все судейство фаблу. Посмотрим что он скажет на
|
||||
одинаковых главах и какое судейство проведет. А потом на сол если хватит времени посмотрим».
|
||||
|
||||
**Что это меняет механически:**
|
||||
|
||||
* **P42 снят с повестки, но НЕ закрыт.** Противоречие двух пре-рег-правил о маржевом гейте
|
||||
существует и касается единственного CONFIRM фазы (H-2а). Оно перестаёт быть блокером, потому
|
||||
что вердикт выносил СЧЁТЧИК, а счётчик понижен до брак-скрина. Когда дойдут руки до Sol —
|
||||
вернуть в повестку. **Не считать «снят» синонимом «решён»: правило в пре-реге по-прежнему
|
||||
противоречит само себе, и следующая сессия обязана это видеть.**
|
||||
* **Норма П-1 «два судейских семейства» временно не исполняется, и это объявленное отступление,
|
||||
а не забывчивость.** Все вердикты нового прибора выносятся ОДНИМ семейством (fable). Писать это
|
||||
рядом с каждым выводом, как фаза писала «вывод стоит на одном семействе» по японской ноге.
|
||||
* **Контроль вместо второго семейства — внутренний:** в каждой панели чтения стоят ДВЕ генерации
|
||||
одной боевой связки (`Z0`/`ZF`). Читатель, который разводит их далеко, сам себя дисквалифицирует;
|
||||
читатель, который ставит их рядом, доказал разрешение на этой панели. За четыре прогона Fable
|
||||
трижды опознал эту пару чтением, НЕ будучи о ней предупреждён.
|
||||
* **Что Sol всё ещё может дать, когда вернёмся:** он единственное независимое семейство, и его
|
||||
расхождение с fable будет мерой «сколько в вердикте от прибора, а не от текста». Пока этой меры
|
||||
нет вовсе — это главная незакрытая дыра нового прибора, и её надо называть вслух.
|
||||
|
||||
**Куплено под расширенную панель (санкция «Нужны»):** `RN`/`RC`/`RB` на английской паре,
|
||||
16 глав каждый, смета $1.05, касса ФД-N (потолок $2.40). Покупка шла с `TM_KEEP_TRACE=1`.
|
||||
|
||||
---
|
||||
|
||||
## 9. ⛔⛔ ПОПРАВКА К СОБСТВЕННОМУ ВЫВОДУ, СДЕЛАННАЯ В КОНЦЕ СЕССИИ
|
||||
|
||||
**Английская докупка опровергла вывод §2.1, и его надо читать вместе с этой поправкой.**
|
||||
|
||||
```
|
||||
китайская пара английская пара
|
||||
прежняя склейка 950 4 494
|
||||
промт-РОЛЬ (RN) 27 039 (×28) 5 804 (×1.29)
|
||||
рез критериев (RC) 11 268 (×12) 3 586 (×0.80)
|
||||
```
|
||||
|
||||
⇒ **Разгон размышления ×28 — свойство КИТАЙСКОГО МАТЕРИАЛА, а не промта.** На английском тот же
|
||||
самый промт стоит на четверть дороже склейки, а резаный вариант — ДЕШЕВЛЕ неё.
|
||||
⇒ Формулировка «промт, который просит творчества, оплачивается вторым черновиком» СНЯТА как общее
|
||||
утверждение: она верна на плотном ханьском входе и не воспроизводится на английском.
|
||||
|
||||
⚠ **И это было в нашем же вендор-бюллетене, прочитанном в тот же день:** `00-provider-quirks.md`
|
||||
п.7 — «разгон думания привязан к ПЛОТНОМУ ХАНЬСКОМУ ВХОДУ, не к модели вообще: при сопоставимом
|
||||
входе zh требует ×7.8 размышления против en». Я это читал и всё равно приписал эффект промту.
|
||||
**Норма: эффект, замеренный на ОДНОЙ паре, не называть свойством промта, пока он не проверен на
|
||||
второй. Пара — конфаундер по умолчанию, а не деталь.**
|
||||
|
||||
**ЧТО ПРИ ЭТОМ УСТОЯЛО И СТАЛО ПРОЧНЕЕ.** Черновик в уме модель пишет на ОБЕИХ парах:
|
||||
```
|
||||
китайская 93–97% финального текста написано внутри размышления (6 клеток)
|
||||
английская 88–94% (47 клеток)
|
||||
```
|
||||
Механизм подтверждён на 53 клетках. Само-проверки в трейсе 4%. ⇒ **размышление у dspro — черновик,
|
||||
а не проверка**, это остаётся в силе. Меняется СЛЕДСТВИЕ: не «двухстадийность спасает от второго
|
||||
черновика», а «второй черновик пишется всегда, но дорого обходится только на плотном входе».
|
||||
|
||||
**ЧТО ЭТО МЕНЯЕТ ДЛЯ ПРОДУКТА.** Возражение «однопроходка дорога» снимается ДЛЯ АНГЛИЙСКОЙ ПАРЫ —
|
||||
той самой, где владелец при слепом чтении поставил однопроходку ПЕРВОЙ. На китайской возражение
|
||||
остаётся. Вопрос переформулируется: не «однопроходка дорога вообще», а «однопроходка дорога на
|
||||
плотном китайском» — и это вопрос к материалу, а не к архитектуре.
|
||||
|
||||
---
|
||||
|
||||
## 10. ПОЛНЫЙ СПИСОК АРТЕФАКТОВ СЕССИИ (для компакта — что где лежит)
|
||||
|
||||
**Код зоны, изменён/создан:**
|
||||
```
|
||||
eval/dovodka/rol.py однопроходка как РОЛЬ: сборка промта, гейт покрытия
|
||||
боевых правил, покупка, эмиттер слепого чтения
|
||||
eval/dovodka/prompts/onepass-core.md ядро нового промта (пар-блоки подтягиваются из пары)
|
||||
eval/dovodka/podacha.py замер подачи входа критику + restore_paragraphs
|
||||
eval/dovodka/itog_d4.py + режим --sens (Г5) и рычаги --drop/--restore
|
||||
eval/dovodka/money_d.py + фазы ФД-K/L/M/N, пакетный потолок 13.80 → 18.30
|
||||
eval/dovodka/runs.py кап 100 → 200 (слово владельца 20.08)
|
||||
eval/role_topology/buy.py + сохранение трейса по флагу TM_KEEP_TRACE=1
|
||||
eval/dovodka/HANDOFF-21-08.md этот файл
|
||||
eval/dovodka/КОНТЕКСТ-ДЛЯ-КОНСУЛЬТАЦИИ.md картина проекта для внешнего собеседника
|
||||
eval/dovodka/PLAN-16-08.md + баннер «закрыт»
|
||||
eval/dovodka/PLAN-17-08.md + шапка состояния на 20.08
|
||||
eval/dovodka/SESSION2-LOG.md + разделы 15.1–15.22
|
||||
docs/experiments/23-editor-tier.md + секции Д21 (Г5) и Д23 (пре-рег промта-роли)
|
||||
```
|
||||
|
||||
**Сырьё:**
|
||||
```
|
||||
~/books/dovodka/dv-k-*, dv-l-* замер подачи критику (zh куплен частично, en не начинался)
|
||||
~/books/dovodka/dv-m-* промт-роль на китайской паре (RN/RC/RB)
|
||||
~/books/dovodka/dv-n-* промт-роль на английской паре (RN/RC/RB, 47 клеток)
|
||||
~/books/dovodka/podacha-plants.json правильные ответы посадок
|
||||
~/books/dovodka/blind-keys-rol/ ключи всех панелей чтения + READERS-*.json
|
||||
~/books/chtenie-rol/ЧТЕНИЕ-en-arch2-*.md 15 готовых пакетов
|
||||
~/books/chtenie-vladeltsa-z17/ОТВЕТ-*.FABLE.md, *.ЧИСТЫЙ.md калибровка прибора
|
||||
~/books/chtenie-vladeltsa-z17/ЧТЕНИЕ-*.НЕЙТРАЛЬНЫЙ.md пакеты без подсказки
|
||||
```
|
||||
|
||||
**Коммиты сессии (полигон коммитит только фризы покупающего кода):**
|
||||
`650b4fe` `824bd20` `273d2df` `7f15323` `3075566` `276c85b` `db02b7a` и фикс пропуска глав.
|
||||
|
||||
**Деньги на конец сессии:**
|
||||
```
|
||||
ФД-K 0.2927/0.40 · ФД-L 0.0000/1.30 · ФД-M 2.0066/2.10 · ФД-N 1.3593/2.40
|
||||
ПАК 15.38/18.30 · свободно 2.92
|
||||
```
|
||||
566
eval/dovodka/PLAN-16-08.md
Normal file
566
eval/dovodka/PLAN-16-08.md
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
> ⚠ **ЗАКРЫТ И ВЫПОЛНЕН.** Это ПРОШЛЫЙ курс (16.08); он привёл к заходу Д17 и
|
||||
> исчерпан им. Живой курс — `PLAN-17-08.md`, состояние на сегодня — `HANDOFF-21-08.md`.
|
||||
> Читать целиком НЕ надо: здесь только исторические основания решений захода Д17.
|
||||
|
||||
# ПРОМТ-ПЛАН полигон-сессии: фаза Д, продолжение после сессии №2 (16.08)
|
||||
|
||||
> Пишется САМОМУ СЕБЕ на случай сжатия контекста и передачи. Читать ЦЕЛИКОМ, не грепом:
|
||||
> прошлая передача потеряла целый пункт заказа именно из-за чтения грепом.
|
||||
> Предыдущие слои: заказ `docs/POLYGON_EXP2223_REDO_SESSION_PROMPT.md` (207 строк, читать целиком) ·
|
||||
> хендофф №1 `docs/POLYGON_PHASE_D_HANDOFF.md` · рабочий журнал `eval/dovodka/SESSION2-LOG.md` (рядом).
|
||||
> Отчёт фазы — `docs/experiments/23-editor-tier.md`, секции Д0–Д16 (~2500 строк).
|
||||
|
||||
---
|
||||
|
||||
## 0. ГЛАВНОЕ В ОДНОМ АБЗАЦЕ
|
||||
|
||||
Дуга экспериментов 19→23 честно ответила на вопрос «какая модель лучше в роли редактора» и
|
||||
устойчиво его закрыла. Но она мерила **счёт локальных ошибок**, а продуктовая цель владельца —
|
||||
**живая русская проза без translationese**, и под неё прибора нет. 16.08 слепое чтение показало,
|
||||
что на английской оси порядок по счёту ошибок и порядок по читаемости **не связаны вовсе**
|
||||
(Спирмен −0.10), причём наш прибор штрафовал ровно то, что покупает читаемость — вольность
|
||||
обращения с буквой. Владелец постановил: **штрафовать можно только за перевирание смысла, за
|
||||
вольность — нельзя**. Это меняет шкалу, и старые судейские числа с новыми сравнивать будет нельзя.
|
||||
⇒ Следующий шаг — не новые модели, а **починка прибора**, затем маленький честный заход.
|
||||
|
||||
### 0.1 Словарь — расшифровать до чтения дальше
|
||||
|
||||
* **Боевая связка** (то, что стоит в продакшене): дешёвая модель `deepseek-v4-flash` переводит
|
||||
главу начерно → дорогая `deepseek-v4-pro` получает исходник ПЛЮС черновик и **переписывает всё
|
||||
заново**. Два вызова. В таблицах обозначена `A0` (китайская ось), `E0` (английская), `J0` (японская).
|
||||
* **Однопроходка**: ОДИН вызов дорогой модели, которой дают исходник и **объединённую инструкцию** —
|
||||
переводчика плюс редактора. Ни черновика, ни второго прохода. Обозначена `A2` / `E2` / `J2`.
|
||||
* **Точечный контур**: черновик + нечто, что находит места ошибок (детерминированные флаги ЛИБО
|
||||
LLM-критик) + фиксер, правящий ТОЛЬКО указанные места. Обозначен `A1`/`A1B` (флаги) и `A3` (критик).
|
||||
Трогает единицы процентов текста, остальное остаётся черновиком — в этом его слабость.
|
||||
* **Канон-фиксер ПОСЛЕ**: боевая связка плюс отдельный проход, восстанавливающий термины банка
|
||||
(`A4` / `E4`). Единственный арм, чинящий канон, не двигая судейскую ось; цена аддитивна.
|
||||
* **Гипотезы владельца:** H-1 «проблема в черновике» (хороший черновик + точечный редактор не хуже
|
||||
связки) · H-2а «флаги → фиксер не хуже переписа» · H-2б «смысловой критик → фиксер ЛУЧШЕ переписа»
|
||||
(главная ставка) · H-3 «на английском перепис не нужен» · H-4 «перевес dspro — свойство китайской пары».
|
||||
* **Пол (шумовой)**: пара из двух генераций ОДНОГО лечения. Разница их оценок = шум прибора,
|
||||
из неё строится порог различимости. Половины обязаны судиться РАЗНЫМИ сессиями.
|
||||
* **Декой**: намеренно испорченный вариант в пачке. Грубый — ловит «судья вообще смотрит?».
|
||||
Маржевый — тонкая порча размером с искомый эффект, ловит «судья способен увидеть разницу такого
|
||||
размера?». Без второго вывод «не различимо» неотличим от слепоты прибора.
|
||||
|
||||
---
|
||||
|
||||
## 1. РЕШЕНИЯ ВЛАДЕЛЬЦА — дословно и с последствиями
|
||||
|
||||
| дата | что сказал | что это значит механически |
|
||||
|---|---|---|
|
||||
| 15.08 | «Подтверждаю подъём расходов, сколько тебе нужно» | потолок записан числом **$6.85** в `money_d.py`; на 16.08 потрачено ~$6.09 |
|
||||
| 15.08 | про кап агент-сессий: «почему оставшиеся? ты можешь наспамить под 90» | кап 80 снят его словом; `runs.py` продолжает считать СУДЕЙСКИЕ сессии (на 16.08 — 72) |
|
||||
| 15.08 | «Можно пересудить [tier]. Но не подгонять. Нужно понять, кто ошибается из судей» | исполнено, см. §3.2 |
|
||||
| 15.08 | «gpt 5.6 sol это и есть модель» | **Sol = семейство OpenAI** ⇒ его возражение по `tier` дисквалифицировано (судил свою семью) |
|
||||
| 15.08 | про японскую ногу: «Бери, ладно» | куплено и отсужено, см. §3.3 |
|
||||
| 15.08 | про ja-книгу: «не помню; замена была потому что флагнули по 18+; кажется, я аппрувнул выкачивание» | дословной санкции на ЗАМЕНУ нет; `R37` реестра честно красный; в отчёте писать этой формулировкой, НЕ «по слову владельца» |
|
||||
| 16.08 | «Штрафовать за вольность нельзя — живой язык один из приоритетов бэкенда. Штрафовать можно только за перевирания смысла исходника. В остальном можно менять, убирать англоязычность, переставлять текст» | **МЕНЯЕТ РУБРИКУ СУДЬИ.** См. §4.1 — это первый пункт плана |
|
||||
| 16.08 | «Новые траты я разрешаю, перепровести эксп я разрешаю» | санкция на заход §4.3 |
|
||||
| 16.08 | про цены DeepSeek: «мы с оркестратором проводим пересчёт его цен и внесём в документацию, после я тебе скажу (или ты мне напомни)» | **НАПОМИНАТЬ.** См. §5.1 — это блокер денежных выводов |
|
||||
|
||||
---
|
||||
|
||||
## 2. ЧТО УЖЕ ПОСТРОЕНО И РАБОТАЕТ (не переделывать)
|
||||
|
||||
Всё в зоне `eval/dovodka/`, все селфтесты зелёные, линтер чист.
|
||||
|
||||
| файл | что делает | команды |
|
||||
|---|---|---|
|
||||
| `gain.py` | **калибровка усиления судьи** — ловит пачку, легшую на пол шкалы | `--density` (гейт: нулей ≥25% → пачка не несёт «не различимо») · `--samearm` · `--agree` · `--floor` · `--tier` · `--selftest` |
|
||||
| `naklon.py` | **гейт позиционного наклона** (норма требовала, кода не было) | без флагов — все 7 проходов; `--pass=d4`; `--selftest` (синтетика с известным ответом) |
|
||||
| `tier2.py` | пере-судейство прохода `tier` починенным заданием | `--emit` · `--score` · `--selftest` |
|
||||
| `ja6.py` | японская нога H-4: панель `J0`/`J6`/`D0` + контроли; ⚠ `read()` чинён 16.08 — считает и разведённые `p{1,2}-answers` своего семейства, и ПЛОСКИЙ `answers/` внешнего пакета | `--emit` · `--score` · `--sol` (пакет внешнему судье) · `--score-sol` · `--selftest` |
|
||||
| `chtenie.py` | **прибор ткани**: слепое чтение человеком | `--emit` · `--score` |
|
||||
| `adjud.py` | адъюдикация находок; **контроли починены** (4 дефекта) | `--emit` · `--controls` (гейт неразличимости классов) · `--score` · `--selftest` |
|
||||
| `itog_d4.py` | свод оси; чинён: считает ОБА семейства, печатает **полную цену единицы** | `--axis=d4\|d3\|d6\|d1` `--fam=claude\|sol` `--gates` |
|
||||
| `sud.py` | судейский риг; селфтест **перестал быть прибит к китайской панели** | `--axis=<ось> --selftest` |
|
||||
| `money_d.py` | касса, потолок $6.85, фазы ФД-A…ФД-H | `--report` · `--selftest` |
|
||||
| `runs.py` | журнал судейских сессий, запись ДО запуска | `--claim <прогон> <проход> <ток>…` · `--done <n>` · `--status` |
|
||||
| `conformance.py` (в `eval/`) | сверка с буквой заказа; **починен парсер** | `eval/conformance.py eval/dovodka/requirements.md --plan` |
|
||||
|
||||
**Три коммита-фриза сессии №2** (полигон коммитит только пре-рег-фризы, основание вслух ПЕРЕД
|
||||
каждым): `a03ac66` (пре-рег `tier2` + `gain.py`) · `8c68828` (покупающий код ja-ноги) ·
|
||||
`22a8a6b` (покупающий код пола ja-ноги + журнал). Остальное дерево НЕ коммичено — лендит оркестратор.
|
||||
|
||||
---
|
||||
|
||||
## 3. ЧТО УСТАНОВЛЕНО — с числами, которые нельзя потерять
|
||||
|
||||
### 3.1 Прибор: строгость счёта есть свойство ПРОГОНА, а не оси
|
||||
|
||||
Тот же арм `R0`, те же 16 единиц, то же семейство судей, подписи текста совпадают **16/16**:
|
||||
проход `tier` дал **0.69** ошибки на единицу при 9 нулях из 16, проход `border` — **4.31** при нуле
|
||||
нулей; по-единично ниже в 15 из 16. Размах внутри семейства claude 1.41…6.47.
|
||||
|
||||
⇒ **межпроходные сравнения абсолютных чисел недействительны.** Под это попадает вывод «дешёвая
|
||||
модель на японском проваливается сильнее других пар» (5.44 против 2.78 — разные прогоны) и все
|
||||
заимствованные пороги. Внутри одной пачки сравнения законны — там сдвиг сокращается в контрасте.
|
||||
|
||||
Доля нулей по проходам claude: `tier` 38% (24% без обоснования) · Д3 en 10% · Д6 3% ·
|
||||
`border` 0 · **Д4 zh 2 нуля на 713 клеток** · Д1 0. По сессиям выбиваются ровно две
|
||||
(`d3r2/p1/д-52` 24%, `d3r2/p2/д-55` 21%). ⇒ выводы Д4/Д1/`border` этой болезнью НЕ заражены.
|
||||
|
||||
### 3.2 Проход `tier` пере-сужен — вывод пака 23 восстановлен
|
||||
|
||||
Пре-рег зафризен `a03ac66` ДО первого ответа. Те же тексты, новый ключ со своей солью, старый не
|
||||
тронут. Правки: убран `CTRLfloorA` (был побайтно равен боевому арму `T1` в 16/16), добавлен
|
||||
маржевый декой, запрещён молчаливый ноль, в рубрику внесены пропускавшиеся классы.
|
||||
|
||||
Результат на 16 единицах, 126 клеток, замечаний годности 0:
|
||||
|
||||
```
|
||||
арм старый новый сдвиг гейты
|
||||
R0 боевой 0.69 3.44 +2.75 плотность 3.77, нулей 6% (было 38%) — ГОДНО
|
||||
T1 glm-5.2 1.00 3.56 +2.56 пол 16 пар sd 2.63 → порог 1.84
|
||||
T2 gpt-5.6-terra 0.25 2.38 +2.12 грубый декой 16/16
|
||||
T3 grok-4.5 0.62 2.94 +2.31 МАРЖЕВЫЙ ДЕКОЙ +2.50 против 1.84 — ПРОЙДЕН
|
||||
F голый черновик 2.56 6.56 +4.00
|
||||
```
|
||||
|
||||
Вердикт: `T1` −0.12 · `T2` **+1.06** · `T3` +0.50 — все ниже порога 1.84; контроль `R0 vs F`
|
||||
−3.12 p=0.0042. ⇒ **«сильный тир не отличим от боевого редактора» ВОССТАНОВЛЕН и впервые стоит
|
||||
на приборе с доказанной чувствительностью.** Возражение Sol (+8.69) не воспроизводится (+1.06)
|
||||
и вдобавок дисквалифицировано: `gpt-5.6-terra` — семейство самого Sol.
|
||||
|
||||
⚠ Пере-судейство сделано ОДНИМ семейством: второе на этом контрасте дисквалифицировано.
|
||||
|
||||
### 3.3 Японская нога H-4 — куплена, отсужена, гейты зелёные
|
||||
|
||||
`J6` = `glm-5` поверх той же японской базы, 9 клеток, $0.046329, все `finish=stop`. Плюс свой пол
|
||||
(7 из 9 вторых генераций редактора; один вызов завис на 9+ минут — класс известен).
|
||||
|
||||
```
|
||||
J0 deepseek-v4-pro 1.44 J6 vs J0 −0.44 p=0.6875 ниже порога 2.17
|
||||
JFLO вторая генерация 1.86 J0 vs D0 +2.78 p=0.0039 редактор бьёт черновик
|
||||
J6 glm-5 1.89 грубый декой +2.89 ПРОЙДЕН · маржевый +2.80 ПРОЙДЕН
|
||||
D0 черновик 4.22
|
||||
```
|
||||
|
||||
**Разница между ДВУМЯ редакторами (0.44) не больше разницы между двумя прогонами ОДНОГО
|
||||
редактора (0.42).** Это самый чистый способ сказать «не различимо».
|
||||
|
||||
**Второе семейство прогнано владельцем 16.08 (`ja6.py --score-sol`) и СВОИ КОНТРОЛИ НЕ ВЗЯЛО:**
|
||||
плотность 5.06 (нулей 6%) · пол 7 пар sd 2.64 → порог 2.80 · **грубый декой +2.22 против 2.80 —
|
||||
НЕ ПРОЙДЕН · маржевый +1.80 — НЕ ПРОЙДЕН**. Декой пойман ПО НАПРАВЛЕНИЮ, но собственный шум Sol
|
||||
так широк, что даже намеренная грубая порча в его порог не укладывается ⇒ **на японской оси у Sol
|
||||
нет разрешения**, его пачка по норме аннулируется. Направление при этом совпало: `J6/J0` −1.00
|
||||
(мои −0.44), `J0/D0` +4.00 (мои +2.78). Вывод по H-4 не меняется, но опирается на ОДНО семейство.
|
||||
Третий раз за фазу пол Sol оказывается вдвое шире — свойство его харнесса, а не оси.
|
||||
|
||||
⇒ **H-4 не подтверждается на всех трёх парах**: `deepseek-v4-pro` первый на zh, на en (1.31
|
||||
против 2.72) и на ja (1.44 против 1.89). Порядок редакторов от языка не зависит. Требование
|
||||
заказа (:115) исполнено ВПЕРВЫЕ.
|
||||
|
||||
### 3.4 Разложение контрастов ПО ОСЯМ — за всю дугу не делалось ни разу
|
||||
|
||||
```
|
||||
A1B/A0 −3.13 = ВЕРНОСТЬ −0.94 + ЯЗЫК −2.19
|
||||
A3/A0 −1.87 = ВЕРНОСТЬ −0.26 + ЯЗЫК −1.61 ← ставка владельца
|
||||
A6/A0 −1.06 = ВЕРНОСТЬ +0.12 + ЯЗЫК −1.19 ← носитель H-1
|
||||
A4/A0 −0.13
|
||||
E0/D0 +1.25 = ВЕРНОСТЬ +0.94 + ЯЗЫК +0.31 + ТЕРМИН +0.75 + ФОРМА +0.69
|
||||
J0/J2 +0.11 = ВЕРНОСТЬ 0.00 + ЯЗЫК +0.11
|
||||
```
|
||||
|
||||
⇒ дешёвые контуры проигрывают переписи **прозой, а не смыслом**; носитель H-1 по смыслу
|
||||
боевой связке не уступает вовсе; на английском редактор покупает в основном термины, верность и
|
||||
вёрстку — то есть H-3 опровергнута только в слове «ТОЛЬКО»; на японском второй проход по смыслу
|
||||
не покупает ничего.
|
||||
|
||||
### 3.5 Экономика — полная цена ЕДИНИЦЫ, а не вызова
|
||||
|
||||
```
|
||||
zh: A2 однопроходка 0.00408 · A0 связка 0.00603 · A1B 0.00793 · A4 0.01419 · A3 0.01546 (2.56×A0)
|
||||
en: E2 однопроходка 0.00471 · E0 связка 0.00885 · E4 0.01321
|
||||
черновик flash 0.00096 · перепис dspro 0.00507 · gpt-5.6-terra 0.04408 (×9) · grok-4.5 0.05276
|
||||
```
|
||||
|
||||
⇒ «кандидат в прод вдвое дешевле» ЛОЖНО — он в 1.5–2.4 раза **дороже**; это платная страховка
|
||||
канона. Ставка H-2б стоит 2.56× боевой связки и по счёту ошибок хуже неё.
|
||||
|
||||
### 3.6 ⛔ СЛЕПОЕ ЧТЕНИЕ 16.08 — ПЕРЕВОРАЧИВАЕТ АНГЛИЙСКУЮ ОСЬ
|
||||
|
||||
Пакет `~/books/chtenie-vladeltsa/` (`ЧТЕНИЕ-zh.md`, `ЧТЕНИЕ-en.md`), **ответы там же**:
|
||||
`ОТВЕТ-zh.md` и `ОТВЕТ-en.md` — это улики, их не терять. Ключ отдельно в
|
||||
`~/books/dovodka/blind-keys-chtenie/chtenie-KEY.json`. Пере-счёт: `chtenie.py --score`.
|
||||
Читал не владелец лично, а **Fable по его поручению**; владелец мнение одобрил и сам отметил,
|
||||
что «нашёл меньше замечаний, перевод читаемый».
|
||||
|
||||
| ось | порядок судьи (счёт ошибок) | порядок читателя | Спирмен |
|
||||
|---|---|---|---|
|
||||
| **zh** (единица `ed068182cf`) | `A0` > `A4` > `A6` > `A3` > черновик | `A4` > `A0` > `A6` > черновик > **`A3`** | **+0.80** |
|
||||
| **en** (единица `27cb3a7e69`) | `E0` > `E3` > `E2` > черновик > `E6` | **`E2`** > `E6` > `E0` > `E3` > черновик | **−0.10** |
|
||||
|
||||
* На китайском прокси держится; перестановка `A0`/`A4` внутри шума. Но читатель поставил `A3`
|
||||
(смысловой контур, ставка владельца) **последним, ниже голого черновика**.
|
||||
* На английском связи нет вовсе. **Однопроходка `E2` — первая у читателя, третья у судьи;
|
||||
наша боевая связка `E0` — третья у читателя, первая у судьи.**
|
||||
* ⚠ **Читатель назвал механизм ВСЛЕПУЮ и попал:** «у Т3 локальных отклонений от оригинала больше,
|
||||
чем у Т2, а читать его заметно лучше. Счётчик ошибок должен был поставить Т3 ниже Т2 — если так
|
||||
и вышло, порядок разошёлся не случайно, а системно». Т3 = `E2`, Т2 = `E0`. Так и вышло.
|
||||
|
||||
⚠ Одна единица на ось, английская всего 1.9 тыс. знаков, читатель — модель. Это **калибровка,
|
||||
а не замер**: она говорит «прибор и читатель расходятся», а НЕ «однопроходка лучше связки».
|
||||
|
||||
### 3.6а Черновая роль: панель мерялась, победила ценой
|
||||
|
||||
Эксп-22 мерил ШЕСТЬ кандидатов черновой роли: `deepseek-v4-flash` (медиана $0.00096) ·
|
||||
`gemini-3.1-flash-lite` ($0.00385) · `deepseek-v4-pro` ($0.00387) · `gpt-5.6-luna` ($0.00357) ·
|
||||
`glm-4.7` ($0.00656) · `grok-4.3` ($0.00976). Вердикт — **ничья по качеству, побеждает самый
|
||||
дешёвый** (правило D39.117). То есть «поставить дипсик-про черновиком» УЖЕ проверено и не выиграло.
|
||||
⇒ Низкое качество черновика — свойство не модели, а РОЛИ (и, возможно, промта — см. `K8` в §4.3).
|
||||
|
||||
Двухступенчатость бьёт однопроходку на всех трёх парах ПО СЧЁТУ ОШИБОК: zh 4.00 против 6.51 ·
|
||||
en 1.31 против 2.38 · ja 2.22 против 2.39. ⚠ Но на английском слепое чтение дало обратный порядок
|
||||
(§3.6), и это расхождение не разрешено.
|
||||
|
||||
### 3.7 Что уцелевает независимо от смены шкалы
|
||||
|
||||
1. Редактор поверх дешёвого черновика покупает много — три языка, два семейства судей, слепое
|
||||
чтение (черновик внизу везде).
|
||||
2. Дорогие редакторы не бьют `deepseek-v4-pro` при цене ×9 — на приборе с сертификатом.
|
||||
3. Выбор редактора от языка не зависит.
|
||||
4. Банк терминов работает; **банк-дисциплина и качество прозы — разные умения** (gemini: канон
|
||||
0.986 против 0.884 у боевого при равной судейской оси).
|
||||
5. Схемы «залатать черновик точечно» проиграли и по счёту, и по чтению. ⚠ Но точечный ремонт
|
||||
**не выброшен ратификацией**: D39.117 п.3 отклонил его как ЗАМЕНУ редактора и принял как
|
||||
«механический ПРЕ-ГЕЙТ ПЕРЕД редактором» и «ремонт ТОЛЬКО с банком». Число «$0.0002 чинит всё»
|
||||
из эксп-20 при этом мерилось при ИДЕАЛЬНОЙ детекции и как несущее — пало; в фазе Д оно всё ещё
|
||||
служило основанием армов и прогнозов. Не переиспользовать без оговорки.
|
||||
6. Гипотеза эксп-04 «проза gemini — аутлаер» не подтверждается (после снятия запрещённой клетки
|
||||
с `finish=length` точечная оценка ровно 0.000).
|
||||
7. Потери канона у боевого редактора — в основном пере-формулировка, а не пропажа термина
|
||||
(⚠ но автоматическая КЛАССИФИКАЦИЯ этих мест негодна — секция Д5 отчёта объявлена
|
||||
НЕИСПОЛНЕННОЙ, см. §5.3).
|
||||
|
||||
---
|
||||
|
||||
## 4. ПЛАН. Порядок здесь важнее содержания
|
||||
|
||||
### 4.1 ШАГ ПЕРВЫЙ — ПОЧИНИТЬ СУДЬЮ ПОД ПРАВИЛО ВЛАДЕЛЬЦА ($0)
|
||||
|
||||
Правило 16.08: **штрафовать только за перевирание смысла исходника; за вольность — нельзя.**
|
||||
Живой язык — приоритет продукта; переставлять текст, убирать англоязычность, менять структуру
|
||||
разрешено.
|
||||
|
||||
⚠ **Рубрика живёт в ЧЕТЫРЁХ местах, править надо все, иначе семейства разъедутся:**
|
||||
`eval/dovodka/judge-prompts/core.md` (ядро обвязки) · `claude.md` и `sol.md` (обвязки семейств) ·
|
||||
константы `HEAD` в эмиттерах `sud.py`, `tier2.py`, `ja6.py` (шапка САМОГО задания — именно она
|
||||
достаётся обоим семействам и обеспечивает паритет П-4). После правки прогнать `paritet.py`
|
||||
и `promptdiff.py`.
|
||||
|
||||
Что сделать в рубрике:
|
||||
|
||||
* **ВЕРНОСТЬ остаётся и сужается до искажений факта:** инверсия адресата реплики · снятое или
|
||||
добавленное отрицание · подмена числа, разряда, ранга, единицы · выдуманный или потерянный факт ·
|
||||
перевёрнутое состояние действия/идиомы. Это и есть «перевирание смысла».
|
||||
* **ЯЗЫК разворачивается:** сейчас он ловит «то, чего носитель не напишет». Оставить ТОЛЬКО это
|
||||
(калька, канцелярит, несуществующее слово, рассогласование) и **явно запретить штрафовать за**:
|
||||
перестановку предложений, слияние/дробление абзацев, замену оборота на более живой русский,
|
||||
синонимическую замену, добавление связок. Прямо написать судье: «вольность ради живой русской
|
||||
фразы — НЕ ошибка; ошибка — только то, что носитель не напишет».
|
||||
* **ФОРМА**: сейчас не входит в несущую сумму. Проверить, не штрафует ли она ИСПОЛНЕНИЕ мандата
|
||||
перевёрстки (такое уже переворачивало знак вывода однажды).
|
||||
* **ТЕРМИН** отдан детерминированному банк-гейту, судью им не грузить.
|
||||
|
||||
⚠ **Последствие, которое обязано быть напечатано:** после смены рубрики старые судейские числа с
|
||||
новыми **несравнимы**. Все панели дуги останутся действительными только внутри своей шкалы.
|
||||
|
||||
⚠ Пере-судить по новой рубрике придётся хотя бы **боевую связку против однопроходки** на обеих
|
||||
осях — иначе новую шкалу не с чем сверить.
|
||||
|
||||
⚠⚠ **ПОРЯДОК ИЗМЕНЁН ПО ЗАМЕЧАНИЮ ВЛАДЕЛЬЦА 16.08.** Первая редакция ставила чтение ПЕРЕД
|
||||
покупкой новых армов. Это половина работы впустую: читать старые армы, зная, что панель меняется,
|
||||
незачем. Правильный порядок — **починить рубрику → докупить новые армы → ОДНО слепое чтение по
|
||||
ПОЛНОЙ панели (старые + новые) → отсудить починенной рубрикой.** Тогда чтение делается один раз и
|
||||
отвечает сразу на оба вопроса: следит ли счётчик за читаемостью И какая архитектура читается лучше.
|
||||
Читай §4.2 и §4.3 в этом порядке: сначала 4.3 (покупка), потом 4.2 (чтение по всему).
|
||||
|
||||
### 4.2 ШАГ ТРЕТИЙ — ПРИБОР ТКАНИ НА НОРМАЛЬНОМ n ($0, только агент-сессии)
|
||||
|
||||
Одна единица на ось ничего не решает. Нужно слепое ранжирование на **8–12 единицах на ось**,
|
||||
двумя-тремя независимыми читателями (агенты РАЗНЫХ семейств: claude, Sol через владельца, Fable).
|
||||
|
||||
Контроли (они уже описаны в пре-реге П-6, `23-editor-tier.md:1114-1122`):
|
||||
* декой обязан ранжироваться последним;
|
||||
* половины шумового пола — соседями (если читатель разводит две генерации одного лечения далеко,
|
||||
его ранжирование — шум);
|
||||
* порядок вариантов перемешан, метки слепые, ключ вне рабочего каталога.
|
||||
|
||||
Готовый скелет — `chtenie.py`; нужно расширить с одной единицы на выборку и добавить контроли.
|
||||
|
||||
**Исход решает всё дальнейшее:** ранги согласуются со счётом ошибок → счёт остаётся дешёвым
|
||||
прокси; расходятся (как сейчас на en) → **до починки прибора новые платные замеры бессмысленны**.
|
||||
|
||||
### 4.3 ШАГ ВТОРОЙ — МАЛЕНЬКИЙ ЧЕСТНЫЙ ЗАХОД (санкция владельца 16.08 есть)
|
||||
|
||||
⚠ **Смета по НОВОМУ прайсу: ~$2.9 на пике, ~$1.7 на офф-пике** (прежняя оценка «около доллара»
|
||||
считалась по июльскому пину). Покупать офф-пик; перед покупкой пере-пинить `roster.py` (§5.1).
|
||||
|
||||
Три схемы, обе пары (zh и en), 16 единиц на пару, базы частично куплены:
|
||||
|
||||
1. **боевая связка** — контроль (черновик → полный перепис);
|
||||
2. **однопроходка** — один вызов, объединённый мандат (на en она прочиталась лучшей);
|
||||
3. **черновик → критик → ПОЛНЫЙ ПЕРЕПИС по его замечаниям** (`K1`) — сильнейшая форма идеи
|
||||
владельца. ⚠ Отличие от проигравшего `A3`: там правщик был ТОЧЕЧНЫМ и трогал проценты текста;
|
||||
здесь перепис полный, а буллеты критика лишь ОБУСЛОВЛИВАЮТ его.
|
||||
|
||||
Эндпойнты — **оба сразу**: починенный счёт ошибок (§4.1) И ранг читаемости (§4.2). Пре-рег до
|
||||
покупок: мощность, пороги, правило расхождения, статусы осей.
|
||||
|
||||
⛔⛔ **ОДНОПРОХОДКА ЛОМАЕТ БАНК ТЕРМИНОВ — блокер, найден владельцем 16.08, проверен исполнением.**
|
||||
`backend/prompts/zh-ru/terminologist.md:22`: «Варианты черновиков (строка `drafts:`) — это
|
||||
свидетельства, а не голосование». Терминолог видит всю книгу сразу и выбирает канонический перевод
|
||||
термина, опираясь на то, КАК ЕГО ПЕРЕДАВАЛИ ЧЕРНОВИКИ по кускам. Без черновика майнингу не из чего
|
||||
собирать свидетельства.
|
||||
⇒ **Однопроходка не альтернатива связке, а альтернатива только ВТОРОМУ проходу.** Черновик нужен
|
||||
не только ради качества, но и ради банка. Честное сравнение — не «связка против однопроходки», а
|
||||
«черновик + перепис» против «черновик + нечто другое поверх него».
|
||||
⇒ И это объясняет, почему слепое чтение поставило однопроходку первой, а продуктовым решением это
|
||||
не становится: на ОДНОЙ главе банк не нужен — термины внутри главы согласованы сами собой; на книге
|
||||
в 1500 глав та же схема даст разъезжающиеся имена.
|
||||
⚠ **ПОПРАВКА ВЛАДЕЛЬЦА 16.08 — это НЕ блокер, а недостающий кусок архитектуры.** Дословно:
|
||||
«однопроходка допустима, как вариант чтоб оттуда после вырезать термины и edit точечным
|
||||
редактором их заменить». Выход однопроходки — тоже перевод, значит он годится терминологу как
|
||||
свидетельство; банк майнится ИЗ НЕГО, а термины приводятся к канону точечным редактором.
|
||||
⇒ **Кандидат `K7`: однопроходка → майнинг банка из её выхода → канон-фиксер.** Точечный редактор
|
||||
здесь адекватен дефекту: термины локальны, а канон-фиксер УЖЕ замерен и работает (покрытие
|
||||
0.892→0.937 на zh, 0.927→0.956 на en; судейскую ось не двигает).
|
||||
Цена единицы: zh 0.00408 + 0.00816 = **~0.0122** против 0.00603 у связки (дороже);
|
||||
en 0.00471 + 0.00436 = **~0.0091** против 0.00885 (вровень). ⇒ на английском `K7` стоит столько же,
|
||||
сколько связка, и по слепому чтению однопроходка там читалась ЛУЧШЕ — это самый дешёвый способ
|
||||
проверить, не выиграла ли она случайно.
|
||||
⇒ Что при этом остаётся правдой: **банк без черновика не собирается**, поэтому однопроходка не
|
||||
может быть просто «связкой минус первый проход» — ей нужен свой источник свидетельств.
|
||||
|
||||
⇒ **Кандидат `K8`, самый дешёвый из всех: ОБОГАТИТЬ ПРОМТ ЧЕРНОВИКА.** Вопрос владельца 16.08:
|
||||
«почему такое низкое качество черновика? может, промт черновика от редактора сильно отличается и
|
||||
черновик тут выступает в роли недоведённого до ума инструмента?» — **подтверждено текстом**:
|
||||
`backend/prompts/zh-ru/translator.md` = 22 строки, `editor.md` = 41, и разница не в объёме, а в
|
||||
том, что есть ТОЛЬКО у редактора: весь блок ДИСКУРС-ПЕРЕВЁРСТКИ (четыре шага, как передавать
|
||||
китайский паратаксис русской гипотаксической прозой) + FEWSHOT с ТРЕМЯ разобранными примерами +
|
||||
правило чэнъюй обоими компонентами. У переводчика от всего этого одна строка «избегай канцелярита
|
||||
и калек».
|
||||
⇒ Замер: тот же черновик тем же `deepseek-v4-flash`, но с промтом, куда перенесены блок
|
||||
перевёрстки и FEWSHOT. Цена — ЦЕНА ЧЕРНОВИКА ($0.00096/единица, входные токены длиннее и
|
||||
кэшируются), то есть ~$0.03 на 16 единиц. **Самый дешёвый кандидат в списке и не проверенный ни разу.**
|
||||
⚠ Контраргумент, который надо снять замером, а не рассуждением: арм `A2` (однопроходка с
|
||||
УРАВНЕННЫМ мандатом) уже нёс мандатные части промта редактора и всё равно проиграл по счёту
|
||||
ошибок — **но перенесён ли в него FEWSHOT, из эрраты Э-5 не следует**. Сверить ДО замера: если
|
||||
примеров там не было, `K8` не дублирует `A2`, а закрывает его дыру.
|
||||
|
||||
⚠ Обязательно в панель — **арм голого черновика на китайской оси**: его там нет, и поэтому вклад
|
||||
самого контура не отделим от разрыва «черновик против переписа».
|
||||
|
||||
⚠ Если запускать `K1`: сначала починить `contour.py:611-619` — `critic_places()` берёт ЛЮБУЮ
|
||||
строку, начинающуюся с дефиса, без разбора вердикта, из-за чего фиксеру уходили **подтверждения**
|
||||
критика («верно», «допустимо», «не ошибка») — 311 строк из 1696 = **18.3%**. Ставка владельца была
|
||||
испытана инструментом, который на пятой части работы правил заведомо исправное.
|
||||
|
||||
### 4.4 ЧЕГО НЕ ДЕЛАТЬ СЕЙЧАС
|
||||
|
||||
* Новых панелей моделей — вопрос «какой редактор» закрыт устойчиво.
|
||||
* Роутинга «платить дорого только там, где нужно» — закрыт отрицательно (отборщики-монетки).
|
||||
* Второго последовательного переписа — итеративный дрейф, и обратная связка уже проигрывала.
|
||||
* Дифф-контракта вместо переписа — дороже на thinking-модели и не берёт распределённый дефект.
|
||||
* Любых покупок DeepSeek до пере-снятия прайса (§5.1).
|
||||
|
||||
---
|
||||
|
||||
## 5. ДОЛГИ И ОТКРЫТОЕ
|
||||
|
||||
### 5.1 ⛔ ДЕНЬГИ — ЛЕДЖЕР НЕ ИЗМЕРЯЕТ, А ВЫЧИСЛЯЕТ (блокер)
|
||||
|
||||
`roster.cost()` ВЫЧИСЛЯЕТ `cost_usd` из зашитого пина; провайдер сумму не возвращает.
|
||||
|
||||
**СТАТУС НА 16.08: цены пере-сняты владельцем с оркестратором — но ТОЛЬКО в бэкенде.**
|
||||
`backend/configs/models.yaml` несёт новый пик (`prices_checked: 2026-08-15`): flash $0.44/$1.32
|
||||
cache-hit $0.014 · pro $1.32/$3.96 cache-hit $0.044.
|
||||
⛔ **`eval/tenant_panel/roster.py:33-35` ПО-ПРЕЖНЕМУ СТАРЫЙ** (pro 0.435/0.003625/0.87, помечен
|
||||
«live 08.08»). Значит СЛЕДУЮЩАЯ покупка полигона снова посчитается по июльскому прайсу: касса
|
||||
покажет неправду, а проекционный гард зарезервирует втрое меньше нужного и пропустит покупку.
|
||||
⇒ **ПЕРВОЕ ДЕЙСТВИЕ ПЕРЕД ЛЮБОЙ ПОКУПКОЙ — пере-пинить `roster.py`.** Это риг эксп-22 (чужой пак):
|
||||
по норме — отдельный коммит с его заголовком плюс пере-снятие его гейтов (`verify22.py`, `itog22.py`).
|
||||
|
||||
**ПЕРЕ-СЧЁТ ФАЗЫ ПО НОВОМУ ПРАЙСУ (сделан 16.08, 473 клетки DeepSeek: pro 430, flash 43):**
|
||||
|
||||
| счёт | сумма |
|
||||
|---|---|
|
||||
| в кассе (июльский пин), часть DeepSeek | **$5.3996** |
|
||||
| та же работа по **пиковому** прайсу | **$15.5050** — ×2.87 |
|
||||
| та же работа по **офф-пику** (50%) | ~$8.95 |
|
||||
| не-DeepSeek клетки (не пере-считывались) | $2.3852 |
|
||||
|
||||
Деньги УЖЕ списаны по ценам, действовавшим в момент покупки, — там пин был верен, перерасхода нет.
|
||||
Число важно для БУДУЩЕГО, и оно двигает три вещи:
|
||||
1. смета заходов утраивается (см. §4.3);
|
||||
2. потолок $6.85 в кассе больше ничего не охраняет, пока `roster.py` не пере-пинен;
|
||||
3. ⚠ **правило ничьей может пере-решиться.** Оно звучит «при равном качестве берём дешёвого», и по
|
||||
нему `deepseek-v4-pro` побеждал `glm-5` и сильный тир при разрыве ×9. Теперь `pro` подорожал
|
||||
втрое, а `glm-5` (Z.AI) не дорожал — разрыв сжался. На этом правиле стоит вся рекомендация по
|
||||
редакторской роли; пере-считать её ценой ОБЯЗАТЕЛЬНО.
|
||||
|
||||
Отдельная девиация к объявлению: последняя цифра потолка, приписанная слову владельца в файлах,
|
||||
была $4.50, а расход дошёл до $6.00 ДО санкции 15.08. Цепь подъёмов прошла все самопроверки,
|
||||
потому что **фриз-гейт сверяет ПОКУПАЮЩИЙ файл в стеке, а не кассу** — потолки правятся в рабочем
|
||||
дереве и действуют немедленно.
|
||||
|
||||
### 5.2 ⛔ СВЕРКА ВНУТРЕННЕЙ РЕВИЗИИ: 65 находок из 131 в отчёт не попали
|
||||
|
||||
Источники: `~/books/dovodka/revizia-fazy-D-11-08.json` (82) и `priemka-D4-14-08.json` (49).
|
||||
Требуют пере-проверки исполнением — я их принял со слов сверки, сам не верифицировал:
|
||||
|
||||
1. **H-1 МОЖЕТ ПЕРЕХОДИТЬ ПОРОГ.** Вердикт `A6/A0` определяют две пачки прогона, объявленного
|
||||
«валидацией цепочки», судимого 11.08 по НЕ замороженному промту; паритет обвязок для них не
|
||||
проверялся. Без них: пол n=13 sd 1.4058 → порог 1.0917, `A6/A0` −1.0938 → **ПЕРЕШЁЛ**.
|
||||
Отчёт печатает «НЕ УСТАНОВЛЕНА». Вместе с §3.4 (`A6` по верности +0.12) картина другая.
|
||||
2. Пачка `69de717f3f` осталась в замере, хотя её сессия **аннулирована за сравнение вариантов**.
|
||||
Снятие двигает пороги обоих семейств и все контрасты.
|
||||
3. Унаследованный гейт честности пола свод не гоняет; на данных claude его вердикт —
|
||||
«ПОЛ СМЕЩЁН, боевые числа снимаются» (сдвиг +1.8 между наборами p1/p2). claude несёт оба CONFIRM.
|
||||
4. Два пре-рег-правила о маржевом гейте противоречат: по букве П-1 пачка Sol аннулируется →
|
||||
у H-2а остаётся одно семейство → «эскалация», а не CONFIRM.
|
||||
5. Порог назван смещённым в **противоположные стороны** двумя находками; ни одна не в отчёте ⇒
|
||||
калибровка порога неизвестна ПО ЗНАКУ.
|
||||
6. Донор декоя во всех 62 пачках оси Д4 — `A0` (тот же дефект, что объявлен у `tier` и починен в `ja6`).
|
||||
7. `A4`/`A1B`: клетки оплачены при НУЛЕВОМ изменении текста; `A4/A0` на 4 из 31 нулевой
|
||||
ПО ПОСТРОЕНИЮ (то же вскрытие сделано для `E4`, для `A4` — нет).
|
||||
|
||||
⚠ Среди неотражённого — **12 инструментов, признанных негодными ВНУТРИ самой ревизии**, и трое
|
||||
из них гейты числят ЗЕЛЁНЫМИ: `promptdiff.py` (объявленное расхождение матчится по ПРЕФИКСУ строки;
|
||||
отсутствующий файл пар-пакета гейт не роняет) · `canon.py` (снятая ревью классификация всё равно
|
||||
пишется в артефакт — отсюда негодность секции Д5) · `material_ja.py` (фильтр AI-меток объявлен
|
||||
механическим, кода его не существует). Прежде чем опираться на любой из них — вскрыть.
|
||||
|
||||
**Полный список сохранён человекочитаемым:** `~/books/dovodka/СВЕРКА-РЕВИЗИИ-16-08.md` (35 КБ).
|
||||
Кроме 65 неотражённых находок там **7 прямых противоречий телу отчёта** и **12 инструментов,
|
||||
признанных негодными внутри самой ревизии** — эти два числа в §5.2 выше не раскрыты, читать в файле.
|
||||
|
||||
### 5.3 Прочие открытые долги
|
||||
|
||||
* **Адъюдикация английской оси** — контроли починены и проверены замером, прогона не было.
|
||||
⚠ `adjud.py` намертво прошит на китайскую ось (ключи `blind-keys-d4`, каталог `sud-d4`,
|
||||
армы `A3`/`A0`) — для английской нужна параметризация.
|
||||
* **Секция Д5 объявлена НЕИСПОЛНЕННОЙ:** поле `why` в `~/books/dovodka/canon-dissect.json` —
|
||||
мусор («этот», «родов», «дочь»), классификация внутренней ревизией снята и всё равно писалась.
|
||||
Честное закрытие — ручная пере-классификация 24 мест ($0).
|
||||
* **`E1` побайтно равен черновику на 5 единицах из 16** — объявлено в Д14.4, но панель не
|
||||
пере-считана без них.
|
||||
* **Реестр требований:** 16 самореферентных улик из 89 (было 19, шесть заменил на артефактные).
|
||||
Провалов два: `R37` (провенанс ja-книги) и `R64` (реестр не закоммичен — лендит оркестратор).
|
||||
* **Селфтесты `sud.py`**: d4 зелёный, d3 4 провала, d6 1, d1 2 — все настоящие, диагнозы в журнале.
|
||||
⚠ `MIN_FLOOR["d6"]=3` — константа, поставленная под зелень; НЕ править, это вопрос владельцу.
|
||||
* **Пакет Sol по японской ноге** отдан: `~/books/judging/ja6-sol/ORCHESTRATOR.md`. После прогона —
|
||||
`ja6.py --score-sol`. Пять промтов сессий, p1 и p2 разведены (проверено: 0 промтов смешивают).
|
||||
* **Эррата zh-канона**: поправка границы слова живёт в коде, в сохранённый банк не дошла
|
||||
(завышение +0.0039) — правка закрытой оси, ратифицирует оркестратор.
|
||||
|
||||
---
|
||||
|
||||
## 6. ЛОВУШКИ — на чём споткнёшься
|
||||
|
||||
1. **НЕ пере-эмитировать существующие слепые ключи.** Раскладка меток — чистая функция от соли,
|
||||
uid и НАБОРА армов; эмиссия с другим составом переписывает ключ отсуженной оси, и разбор
|
||||
сопоставит ответы с ЧУЖИМИ армами. Новый проход = НОВЫЙ ключ со своей солью (так сделаны
|
||||
`tier2`, `ja6`, `chtenie`).
|
||||
2. **Пакет внешнему судье — ТРЁХУРОВНЕВЫЙ**, как в фазе Д: `ORCHESTRATOR.md` («по одному агенту
|
||||
на промт») + `prompts/*.md` + задания. Двухдокументная форма эксп-23 НЕ годится: харнесс
|
||||
владельца читает всё одной сессией, и половины шумового пола попадают одному судье.
|
||||
Путь к ключу в пакете **не называть** — «не открывай blind-keys/» значит показать пальцем.
|
||||
3. **Правила счёта — в САМОМ задании**, не только в обвязке: асимметрия инструкций между
|
||||
семействами купила часть расхождения в 12 раз на проходе `tier` (норма паритета П-4).
|
||||
4. **Запрет дочерних агентов** вписывать явно — сессия с ними перестаёт быть тем составом,
|
||||
который зарегистрирован до запуска.
|
||||
5. **Замок кассы снимать ТОЛЬКО у мёртвого процесса** (`ps -eo pid,cmd | grep ...`). Покупка
|
||||
идемпотентна — просто перезапускать. Редактор думает 60–90 с на клетку, зависания до 9+ минут
|
||||
бывают (в паке 22 вызов держал замок 74 минуты); ставить `timeout` ≥900 и не опрашивать часто.
|
||||
6. **Фриз-гейт кассы**: покупающий код обязан быть ЗАКОММИЧЕН до покупки. Полигон вправе фризить —
|
||||
основание вслух ПЕРЕД коммитом, форма `git commit -- <явные пути>`, никогда `git add -A`.
|
||||
7. **`--wide-plants` на японской книге НЕ помогает** (проверено: 5 из 9 в обоих случаях). Регексы
|
||||
посадок оказались не только пар-, но и КНИГО-зависимы.
|
||||
8. **Не считать по половине пачки.** Пороги, снятые на неполном поле, читаются увереннее, чем есть
|
||||
(на этом уже горела ось Д1: порог вырос 1.00 → 1.47, когда пол добрал пары).
|
||||
9. **Промт черновика и промт редактора радикально асимметричны** (22 строки против 41): у
|
||||
редактора есть весь блок дискурс-перевёрстки и FEWSHOT с тремя разобранными примерами, у
|
||||
переводчика — одна строка «избегай канцелярита». ⚠ Но арм `A2` (однопроходка с УРАВНЕННЫМ
|
||||
мандатом) это учитывал и всё равно проиграл по счёту ошибок — **проверить, перенесён ли в него
|
||||
FEWSHOT**, это самая сильная часть промта и в эррате она не упомянута.
|
||||
|
||||
---
|
||||
|
||||
## 7. МНЕНИЕ FABLE-5 (архитектурный аудит 16.08) — что взять
|
||||
|
||||
Полный отчёт был в эфемерном транскрипте; существенное:
|
||||
|
||||
* Подозрение о самоподгонке харнесса **подтверждается наполовину**: прибор не мерит ткань, а
|
||||
эндпойнт под неё (П-6) принят владельцем 11.08 и не построен. Но ядро вывода («перепис покупает
|
||||
много») не артефакт — держится независимыми контурами и внешней литературой.
|
||||
* **Три механизма смещения В ПОЛЬЗУ большого переписывающего вызова:** боевой промт редактора
|
||||
прошёл три редакции и валидирован тем же классом судейства, а контуры шли первой редакцией с
|
||||
багами в свою сторону; мандат вёрстки исполняет только перепис, а судья за вёрстку штрафует;
|
||||
отрицательные результаты трижды оказывались свойством прибора, а не армов.
|
||||
* **Идея владельца испытана НЕ в сильнейшей форме.** Пустая клетка — «критик-буллеты →
|
||||
ПЕРЕПИСЫВАТЕЛЬ» (`K1`). Вторая пустая — «перепис → критик → точечная починка» (страховка смысла
|
||||
ПОСЛЕ переписа, там точечная форма адекватна дефекту, потому что остаточные ошибки локальны).
|
||||
* Внешние данные: на вебновелльном бенчмарке zh→en **DeepSeek-V3 (5.16) выше GPT-4o (5.09)**;
|
||||
китайские модели систематически бьют западные на китайском исходнике. То есть «фронтир не
|
||||
выигрывает» правдоподобно и снаружи. Но если преимущество фронтира есть, оно в ткани, а её
|
||||
автометрики и LLM-судьи не видят (LitEval: опознают человеческий перевод ≤20%).
|
||||
* **Кандидаты сверх `K1`:** перепис → критик → фиксер · два переписа + слепой селектор (два
|
||||
прогона одного редактора расходятся как два разных — это замерено на `JFLO`) · монолингвальная
|
||||
полировка ткани без исходника · перепис с окном соседних глав. Все дешевле $0.015 на единицу.
|
||||
* **Чего Fable не установил:** валидность счёта ошибок как прокси (П-6 не построен); долю знаков,
|
||||
которую трогает фиксер (спан-счёт даёт 0.2–3.7%, difflib — 8.5% медиана и до 81%); перенос
|
||||
выводов на другие книги и пары.
|
||||
|
||||
---
|
||||
|
||||
## 8. КОМАНДЫ
|
||||
|
||||
```
|
||||
eval/.venv/bin/python eval/dovodka/gain.py --density --agree --floor --tier
|
||||
eval/.venv/bin/python eval/dovodka/naklon.py # все проходы; --selftest
|
||||
eval/.venv/bin/python eval/dovodka/tier2.py --score
|
||||
eval/.venv/bin/python eval/dovodka/ja6.py --score # и --score-sol после прогона Sol
|
||||
eval/.venv/bin/python eval/dovodka/chtenie.py --score
|
||||
eval/.venv/bin/python eval/dovodka/adjud.py --selftest ; --controls
|
||||
eval/.venv/bin/python eval/dovodka/itog_d4.py --axis=d4 # --axis=d3|d6|d1, --fam=sol
|
||||
eval/.venv/bin/python eval/dovodka/sud.py --axis=d3 --selftest
|
||||
eval/.venv/bin/python eval/dovodka/money_d.py --report ; --selftest
|
||||
eval/.venv/bin/python eval/dovodka/runs.py --status
|
||||
eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --plan
|
||||
```
|
||||
|
||||
Шум `SyntaxWarning` фильтровать. Интерпретатор — `eval/.venv/bin/python` из корня репо.
|
||||
|
||||
---
|
||||
|
||||
## 9. ЧЕМ ЗАКРЫВАЕТСЯ РАБОТА (иначе непонятно, что считать сделанным)
|
||||
|
||||
1. **Секция в отчёте** `docs/experiments/23-editor-tier.md` на каждый шаг плана. Нумерация уже
|
||||
занята по Д16 включительно — следующая свободная Д17.
|
||||
2. **Пре-рег ДО покупок и ДО первого судейского ответа**, зафризенный коммитом с основанием вслух.
|
||||
Пре-рег обязан нести: состав панели · правило решения · пороги · мощность (MDE против ЦЕЛИ) ·
|
||||
статус оси (несущая/описательная) · что замер НЕ может.
|
||||
3. **Гейты зелёные ИЛИ красные с диагнозом.** Константы под зелень не подгонять — это норма
|
||||
проекта, нарушение = аннулирование куска.
|
||||
4. **Пинг в `docs/PROGRESS.md` секция «Полигон»** (фронт и платформа туда не пишут, полигон пишет).
|
||||
5. **Обновлённый `eval/dovodka/SESSION2-LOG.md` (рядом)** или его преемник — по ходу, не в конце.
|
||||
6. **CONFIRM/DENY владельцу — только с артефактом-носителем.** Полигон не ратифицирует.
|
||||
7. Дерево НЕ коммитить, кроме пре-рег-фризов; лендит оркестратор.
|
||||
|
||||
---
|
||||
|
||||
## 10. КАК РАЗГОВАРИВАТЬ С ВЛАДЕЛЬЦЕМ
|
||||
|
||||
* Он просил **меньше аббревиатур** и продуктовых формулировок: не «H-2б» и «A3», а «схема
|
||||
критик-плюс-точечная-правка». Термины расшифровывать при первом употреблении.
|
||||
* Он **не всегда читает длинные тексты** — важное выносить в первые строки.
|
||||
* Он ловит дефекты процесса лучше гейтов: за сессию №2 нашёл структуру пакета Sol (я ошибся),
|
||||
устаревший прайс (леджер оказался не измерителем) и слепоту прибора к вольности. **Его реплики
|
||||
проверять исполнением, а не отмахиваться.**
|
||||
* Обязательство с адресатом вне зоны закрывается ТОЛЬКО изменением файла вне зоны. «Выпущено»,
|
||||
«ждёт владельца», «объявлено в отчёте» — маркеры дефекта.
|
||||
* Полигон не ратифицирует выводы. CONFIRM/DENY — владельцу через оркестратора, с носителем.
|
||||
376
eval/dovodka/PLAN-17-08.md
Normal file
376
eval/dovodka/PLAN-17-08.md
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
> ⚠⚠ **СОСТОЯНИЕ НА 20.08 — читать ЭТО прежде плана ниже.** Курс за день сдвинулся сильнее, чем
|
||||
> написано в теле; тело оставлено как есть, потому что в нём основания решений.
|
||||
>
|
||||
> **1. СМЕНИЛСЯ ПРИБОР.** Решение владельца: первичный — слепое чтение Fable-5, счёт ошибок —
|
||||
> брак-скрин. Замерено: чтение согласно с владельцем +0.80 на ОБЕИХ парах, счёт +0.80 на
|
||||
> китайской и −0.80 на английской. ⇒ **все прежние вердикты об архитектурах вынесены прибором,
|
||||
> который на английской паре смотрит в обратную сторону, и подлежат пере-суду.** 16 пакетов на
|
||||
> это уже собраны и проверены (см. `HANDOFF-21-08.md`).
|
||||
> **2. ШАГ 1 (Г5) ЗАКРЫТ**, вердикт по H-1 устоял — секция отчёта Д21.
|
||||
> **3. ШАГ 2 (подача входа критику) ОСТАНОВЛЕН владельцем** после того, как замер показал: критик
|
||||
> не видит удалённого абзаца ВООБЩЕ, поэтому «сохранил ли он бдительность» мерить не на чем.
|
||||
> **4. Г1в/Г1 частично отвечены:** фраза о параллельности срезает размышление на 55% и находки на
|
||||
> 50%; «след в след» — неправда на ОБЕИХ парах (проверено позиционно), врать модели нельзя.
|
||||
> **5. Г12 усилена до механизма:** трейс показал, что 96% финального текста модель пишет ВНУТРИ
|
||||
> размышления. Размышление у dspro — черновик, а не проверка. ⇒ двухстадийная связка дёшева
|
||||
> БЛАГОДАРЯ топологии: она выносит черновик из дорогого канала размышления в дешёвый выход.
|
||||
> **6. Появилась новая живая ветка, которой в плане нет:** промт однопроходки как РОЛЬ. Лучшая
|
||||
> редакция — формулировка ВЛАДЕЛЬЦА («брак = просвечивающий исходный язык»), она обошла обе мои
|
||||
> и встала выше продакшена в слепом чтении, но при КРАСНОМ контроле шума и вчетверо дороже.
|
||||
> **7. Г5-строка ниже была НЕВЕРНА ПО ЗНАКУ** — исправлено в теле.
|
||||
|
||||
# ПЛАН НОВОГО КУРСА — проверка живых гипотез (17.08)
|
||||
|
||||
> Пишется после закрытия захода Д17 и архитектурного разговора с владельцем про V6.
|
||||
> Предыдущие слои: `PLAN-16-08.md` (прошлый курс, ВЫПОЛНЕН) · `SESSION2-LOG.md` §12–§14 ·
|
||||
> отчёт `docs/experiments/23-editor-tier.md` секции Д17–Д20 · заказ
|
||||
> `docs/POLYGON_EXP2223_REDO_SESSION_PROMPT.md` · хендофф `docs/POLYGON_PHASE_D_HANDOFF.md`.
|
||||
> Читать ЦЕЛИКОМ: прошлая передача теряла пункты именно из-за чтения грепом.
|
||||
|
||||
---
|
||||
|
||||
## 0. ГДЕ МЫ ОКАЗАЛИСЬ
|
||||
|
||||
Заход Д17 ответил на вопрос, ради которого затевался, и ответ сместил всю рамку.
|
||||
|
||||
1. **Второй проход нужен** — голый черновик проигрывает всему и всегда, по всем приборам и трём
|
||||
парам. Единственное, что не пошатнулось за пять экспериментов.
|
||||
2. **Какой именно второй проход — приборы не различают.** Все схемы переписывания легли в
|
||||
неразличимую кучу под починенной рубрикой.
|
||||
3. **Причина названа и подтверждена трижды независимо: собственный разброс ОДНОГО способа от
|
||||
прогона к прогону перекрывает разницу МЕЖДУ способами.** Счёт на zh (sd 4.42 между двумя
|
||||
генерациями связки), машинные читатели на zh, владелец на en (опознал пару близнецов по тексту
|
||||
и развёл на два места: «один прогон приличный, второй сырой»).
|
||||
4. **Счётчик — браковщик, а не выборщик.** Он следит за читаемостью там, где у него есть диапазон
|
||||
(широкая шкала +0.68), и слепнет на чистом тексте (узкая +0.42). Формулировка владельца:
|
||||
«счётчиком отсеивать брак, чтением выбирать текст в книгу».
|
||||
5. **Деньги конвейера — это размышление, и тратится оно на выравнивание двух текстов.** 85% цены
|
||||
дорогой клетки. Перепис думает ×12 против однопроходки только потому, что ему дают черновик.
|
||||
|
||||
⇒ Курс меняется: раньше мы искали «лучшую архитектуру», теперь ищем **условия, при которых
|
||||
архитектуры вообще становятся различимы**, и **чинить конвейер там, где он жжёт деньги впустую**.
|
||||
|
||||
---
|
||||
|
||||
## 1. ЖИВЫЕ ГИПОТЕЗЫ — реестр
|
||||
|
||||
Каждая проверяема, у каждой назван замер и цена. Порядок в §2, не здесь.
|
||||
|
||||
| # | гипотеза | откуда | цена |
|
||||
|---|---|---|---|
|
||||
| **Г1** | Выровненный вход схлопывает размышление сличающей стадии | ОДНО наблюдение: критик видел черновик 78 абзацев против 77 строк исходника (0.96:1) и думал 1084; en 8 против 1 — думал 9576 | ~$0.3 |
|
||||
| **Г1а** | ⛔ После РЕДАКТОРА выравнивание исчезает (77 строк → 43 абзаца), значит критик V6 попадёт в дорогой режим даже на zh | замер 17.08 | считано: V6 на Гу ~$340 против $226 у связки |
|
||||
| **Г1в** | ⭐ Достаточно СКАЗАТЬ критику, что тексты идут параллельно — размышление упадёт без всякой разметки | идея владельца 17.08; нынешний промт критика не говорит о связи текстов НИЧЕГО, кроме «исходник и его перевод» | ~$0.15 |
|
||||
| **Г1б** | Разметку можно поручить ОТДЕЛЬНОМУ дешёвому вызову-экстрактору, не трогая переводчика | квирки :51 — эхо бьёт по переводу, не по JSON-выводу | ~$0.2 |
|
||||
| **Г1г** | Локальное эмбеддинговое выравнивание (LaBSE/vecalign/BERTalign) даёт таблицу пар с приемлемой точностью на ХУДОЖЕСТВЕННОЙ прозе | идея владельца; модели уже в кэше машины | **$0** за прогон, только CPU-время |
|
||||
| **Г2** | «Мало думает» = «плохо работает»: ленивый критик находит хуже | zh критик 1084 токена, 43 находки на 2 тыс. знаков, контур проиграл | в комплекте с Г1 |
|
||||
| **Г3** | Языковому критику исходник НЕ нужен — монолингвальная критика не хуже | ось ЯЗЫК решает, а она видна без оригинала | ~$0.4 |
|
||||
| **Г4** | Кэш префикса срезает вход многостадийного конвейера | используем 44.8% из возможного | $0, перекладка запросов |
|
||||
| ~~**Г5**~~ | ~~вердикт по H-1 зависит от состава пачек~~ **ЗАКРЫТА 20.08, Д21: вердикт устоял** ⚠ и формулировка была НЕВЕРНА ПО ЗНАКУ — «переходит порог» значило бы «дорогой черновик ХУЖЕ связки», то есть H-1 опровергается, а не подтверждается | находка ревизии №1 | $0, потрачено |
|
||||
| **Г6** | Счётчик работает при широком диапазоне, слепнет на чистом | разбиение +0.68 против +0.42, но post-hoc | ~$1.5 (трудный en материал) |
|
||||
| **Г7** | Дорогая модель + ПОЛНЫЙ промт → точечная зачистка ≥ связки | пустая клетка; `A6` был дорогой моделью с ТОЩИМ промтом | ~$1.2 |
|
||||
| **Г8** | Промт однопроходки, собранный как РОЛЬ, а не склейка, лучше нынешнего | нынешний несёт мета-фразу «редактора после тебя не будет» | ~$0.5 |
|
||||
| **Г9** | Банк, собранный из хороших переводов, лучше банка из дешёвых черновиков | никогда не мерилось; майнер режет исходник, черновики дают лишь `drafts:` | ~$0.3 |
|
||||
| **Г10** | Разделение типов сущностей (имена / титулы / предметы) улучшает банк | терминолог даёт одно правило на всех | ~$0.3 |
|
||||
| **Г11** | Сцена как единица нарезки лучше чанка | эксп-15: все эффекты когезии под порогом, сигнала нет | не считана |
|
||||
| **Г12** | Несколько прогонов на метод — УСЛОВИЕ любого сравнения архитектур | п.3 §0 | ×2 к любому замеру |
|
||||
|
||||
---
|
||||
|
||||
## 1а. ЧТО УЖЕ ЗАКРЫТО — НЕ ПЕРЕОТКРЫВАТЬ БЕЗ НОВОГО ОСНОВАНИЯ
|
||||
|
||||
Дописано при перечитывании: без этого списка следующая сессия честно потратит деньги на то,
|
||||
что уже куплено и отвечено.
|
||||
|
||||
| закрыто | чем | вердикт |
|
||||
|---|---|---|
|
||||
| «критик → ПОЛНЫЙ перепис» (`K1`/`Z1`) | Д17, обе пары | −0.06 по счёту, у владельца 3-е и 4-е места. **ОТРИЦАТЕЛЬНО** |
|
||||
| «обогащённый промт черновика» (`K8`/`Z8`) | Д17, обе пары | хуже голого на en, эхо-мина на zh. **ОТРИЦАТЕЛЬНО** |
|
||||
| «дешёвая модель в роли редактора» | эксп-22 Ф3 | −2.54 против боевого + 3 клетки сожгли бюджет. **ОТРИЦАТЕЛЬНО** |
|
||||
| «дорогая модель черновиком» | эксп-22 Ф2 | ничья, побеждает дешёвый. **ЗАКРЫТО** |
|
||||
| «какая модель лучше редактор» | 5 моделей zh, 2-я на en/ja, 3 «сильных тира» | dspro первый везде. **ЗАКРЫТО устойчиво** |
|
||||
| «gemini — аутлаер прозы» (эксп-04) | Д1, 15 клеток | не подтверждается, цена ×30. **ЗАКРЫТО** |
|
||||
| H-4 «перевес dspro — свойство китайской пары» | zh/en/ja | не подтверждается на всех трёх. **ЗАКРЫТО** |
|
||||
| роутинг «платить дорого адресно» | эксп-21 | отборщики-монетки. **ЗАКРЫТО** |
|
||||
| второй последовательный перепис | эксп-20/21 | итеративный дрейф. **ЗАКРЫТО** |
|
||||
| дифф-контракт вместо переписа | эксп-19 | дороже на thinking-модели. **ЗАКРЫТО** |
|
||||
|
||||
⚠ **Единственный ЖИВОЙ продуктовый кандидат из измеренного: «однопроходка + глоссарный проход».**
|
||||
По судейской оси от связки не отличается, по банку ЛУЧШЕ неё на обеих парах (zh 1.38 против 2.88,
|
||||
en 0.31 против 0.56), стоит один полный вызов вместо двух, и владелец при слепом чтении поставил
|
||||
однопроходку первой на английском. Его слабое место — банк без черновика (см. Г9).
|
||||
|
||||
---
|
||||
|
||||
## 1б. РЕШЕНИЕ ВЛАДЕЛЬЦА, КОТОРОЕ ЖДЁТ ЕГО САМОГО (не гипотеза, а развилка)
|
||||
|
||||
**Черновая роль по НАШЕМУ ЖЕ правилу должна уйти с DeepSeek.** Правило D39.117 — «при равном
|
||||
качестве берём дешёвого», качество шести черновых моделей было ничьёй. После подорожания 16.08:
|
||||
|
||||
```
|
||||
gpt-5.6-luna $0.00358/глава
|
||||
gemini-3.1-flash-lite $0.00383
|
||||
deepseek-v4-flash $0.00444 ← был 0.00097, в 3.7 раза дешевле следующего
|
||||
```
|
||||
|
||||
На книге в 1500 глав: текущая связка $40 против $23 у пары `flash-lite + glm-5`.
|
||||
⚠ **Перед сменой — эхо-проба альтернатив:** у flash эхо-мина есть и она перевооружается
|
||||
ослабленным thinking; у остальных не мерена НИ РАЗУ. Владелец сказал «это разговор предстоит».
|
||||
|
||||
---
|
||||
|
||||
## 2. ПОРЯДОК РАБОТ. Он важнее содержания
|
||||
|
||||
### ✔ ШАГ 1 — Г5. ВЫПОЛНЕН 20.08, $0. Секция отчёта Д21
|
||||
|
||||
**Вердикт «H-1 НЕ УСТАНОВЛЕНА» устоял.** Инструмент — `itog_d4.py --sens` (режим в СВОДЕ, нового
|
||||
файла не заводилось). Арифметика находки воспроизведена побайтно; переворот живёт только в
|
||||
сценарии, который возвращает пачку аннулированной сессии и одновременно снимает пачки-валидации,
|
||||
то есть применяет норму в одну сторону и отменяет в другую. На нынешнем дереве запас **−0.0710**.
|
||||
|
||||
Тем же заходом закрыты смежные находки: **P12** (пачка `69de717f3f` уже в карантине, строка
|
||||
сверки устарела) · **P07** (наклон наборов реален, вердиктов не двигает) · **R73/P40** (калибровка
|
||||
порога названа ПО ЗНАКУ: пол на Д4 честен, для H-1 контраста завышен в 1.5×).
|
||||
|
||||
⛔ **Осталась открытой P42 и это ВОПРОС ВЛАДЕЛЬЦУ**, не замер: два пре-рег-правила о маржевом
|
||||
гейте несовместимы, и от выбора между ними зависит, остаётся ли единственный CONFIRM фазы
|
||||
подтверждением или становится эскалацией. Объявлено девиацией в Д21.6.
|
||||
|
||||
**Что вынес сверх задания:** `A6/A0` во всех пяти способах счёта стоит ВПЛОТНУЮ к порогу
|
||||
(−0.62 · −0.07 · −0.51 · +0.002 · −0.007) при шаге шкалы в одну ошибку ⇒ это не «эффекта нет», а
|
||||
«эффект ровно размером с шумом прибора» — та же болезнь, что Д17 намерил на новом материале.
|
||||
И `A6` испытывался ТОЩИМ промтом переводчика, поэтому клетка «дорогая модель + ПОЛНЫЙ промт»
|
||||
(она же ядро V6) остаётся пустой — это шаг 4.
|
||||
|
||||
### ШАГ 2 — Г1в + Г1 + Г2 ОДНИМ ЗАМЕРОМ (~$0.4). БЕЗ ВЫРАВНИВАНИЯ, БЕЗ РАЗМЕТКИ
|
||||
|
||||
⚠ **Замер УРЕЗАН по итогам спора 17.08.** Прежняя редакция предлагала подавать критику
|
||||
пронумерованные ПАРЫ, то есть требовала решить задачу выравнивания. Это преждевременно: вся
|
||||
ветка висит на ОДНОМ наблюдении, которое я же и объяснил. Сначала проверяем наблюдение, потом
|
||||
строим схемы — норма, заведённая этим разговором.
|
||||
|
||||
**ПЕРВЫМ — вариант владельца (Г1в), он дешевле и чище.** Меняется ТОЛЬКО ИНСТРУКЦИЯ, данные
|
||||
не трогаются вовсе. Сейчас промт критика говорит о связи двух текстов буквально одно: «тебе дан
|
||||
ИСХОДНИК и его ЧЕРНОВОЙ ПЕРЕВОД». Модель вынуждена сама выяснять, как они соотносятся, — и,
|
||||
похоже, именно на это уходят те 9 576 токенов.
|
||||
|
||||
Добавляем фразу о том, что тексты идут ПАРАЛЛЕЛЬНО. Образ владельца: два листа из разных тетрадей,
|
||||
наложенные друг на друга, — тексты идут рядом, а не строчка в строчку.
|
||||
|
||||
⚠ **ФОРМУЛИРОВКА РЕШАЕТ, И ВРАТЬ НЕЛЬЗЯ.** «Предложение за предложением» — НЕПРАВДА: наш же промт
|
||||
редактора обязывает сливать абзацы (77 строк исходника → 43 абзаца). Соврём — модель будет искать
|
||||
соответствие, не найдёт и потратит БОЛЬШЕ, а не меньше. Говорить только правду: порядок сохранён ·
|
||||
перевод покрывает исходник целиком · членение может отличаться · читай оба параллельно и НЕ ищи
|
||||
точных пар предложений.
|
||||
|
||||
**ВТОРЫМ — мой вариант:** тот же кусок, но **исходник разбит по предложению на строку**
|
||||
(перевод НЕ трогаем, выравнивания НЕ делаем). Детерминированно, $0.
|
||||
|
||||
**Гонять лучше все четыре комбинации** (инструкция вкл/выкл × разбивка вкл/выкл) на одном
|
||||
материале: тогда видно, что решает — слова или форма подачи. Всё равно копейки.
|
||||
|
||||
⚠⚠ **РИСК, КОТОРЫЙ ЗАМЕР ОБЯЗАН ПРОВЕРИТЬ НАРАВНЕ С ТОКЕНАМИ.** Фраза «перевод покрывает исходник
|
||||
целиком» может заставить критика ПЕРЕСТАТЬ ИСКАТЬ ПРОПУСКИ, а потерянный факт — один из главных
|
||||
классов нашей рубрики. Выигрыш в токенах, купленный слепотой к целому классу дефектов, — это не
|
||||
выигрыш. Поэтому в пре-рег отдельной строкой: считать долю находок класса «пропущено/потеряно»
|
||||
до и после, и падение этой доли считать ПРОВАЛОМ варианта, даже если токены упали.
|
||||
|
||||
Смотрим ТРИ вещи:
|
||||
* **токены размышления** — упали или нет (Г1в/Г1);
|
||||
* **находки в целом** — те же, лучше или хуже (Г2);
|
||||
* **находки класса «пропущено»** — не исчезли ли (риск выше).
|
||||
|
||||
**Если упали при тех же находках** — гипотеза жива, и тогда имеет смысл строить разметку (Г1б).
|
||||
**Если нет** — вся ветка закрывается, и мы экономим себе месяц проектирования. Это и есть цель
|
||||
шага: закрыть ветку дёшево, а не подтвердить её дорого.
|
||||
|
||||
⚠ Обещание «минус треть цены конвейера» СНЯТО: оно висело на неподтверждённом.
|
||||
⚠ Пре-рег обязателен: что считаем «те же находки». Сравнение — по адъюдикации, а не на глаз.
|
||||
|
||||
### ШАГ 2б — Г1г: ЭМБЕДДИНГОВОЕ ВЫРАВНИВАНИЕ, ЕСЛИ ДЕШЁВОЕ НЕ СРАБОТАЛО ($0)
|
||||
|
||||
Гонится ТОЛЬКО если шаги 2 и 2а не дали адресации. Инструмент локальный, вызовов к API нет вовсе.
|
||||
|
||||
**Что уже есть на машине (проверено):** `sentence-transformers`, `torch+cpu`, 16 ядер, GTX 1070;
|
||||
в кэше лежат `LaBSE`, `bge-m3`, `Qwen3-Embedding-0.6B`. Кандидаты-алгоритмы: **vecalign**
|
||||
(динамическое программирование по эмбеддингам, умеет МНОГИЕ-КО-МНОГИМ) и **BERTalign** (на LaBSE,
|
||||
авторы целились в художественную прозу). Gale-Church по длинам для zh↔ru слабая.
|
||||
|
||||
**⛔ ОГРАНИЧЕНИЕ ПРОДА: в облаке только CPU + диск, GPU нет.** Поэтому:
|
||||
* выравнивание — НЕ горячий путь, один раз на книгу, результат на диске;
|
||||
* объём режется: эмбеддить смысловые ходы (43–78 на кусок), а не предложения · сперва ЯКОРЯ
|
||||
(термины банка, числа, реплики) прибивают опорные точки, эмбеддинги — только на промежутках ·
|
||||
модель меньше + int8/ONNX;
|
||||
* **роли разведены: в лаборатории это ИЗМЕРИТЕЛЬ, в проде адреса берутся из номеров смысловых
|
||||
ходов по построению.** Прод на эмбеддинги опираться НЕ должен.
|
||||
|
||||
**Точность мерим БЕЗ ручной разметки, инструментами, которые у нас уже есть:** банк терминов
|
||||
(термин в отрезке исходника обязан быть канонной формой в выровненном отрезке перевода) и маржевые
|
||||
посадки (мы сами знаем, куда вставили порчу). ⚠ Чужие цифры качества из статей НЕ переносить: их
|
||||
мерили там, где перевод следует за оригиналом близко, а у нас редактор обязан сливать абзацы.
|
||||
|
||||
### ШАГ 2а — ЕСЛИ Г1 ПОДТВЕРДИЛАСЬ: РАЗМЕТКА ОТДЕЛЬНЫМ ВЫЗОВОМ (~$0.2)
|
||||
|
||||
⛔ **Переводчику разметку НЕ поручать — это измеренный класс отказа.** Обогащённый черновик
|
||||
получил ОДИН добавленный структурный блок и вернул исходник вместо перевода на 4 главах из 16;
|
||||
запрет D21.6 говорит то же про языковые констрейнты в reasoning-ослабленных путях (эхо 3/10 → 9/10).
|
||||
|
||||
Разметку делает **отдельный дешёвый вызов-экстрактор**: вход исходник + готовый перевод, выход
|
||||
ТОЛЬКО соответствие «номер смыслового хода → первые слова абзаца». Ни строчки текста не
|
||||
переписывает. Безопасно по квирк-бюллетеню (:51): эхо бьёт по переводу, не по JSON-выводу.
|
||||
|
||||
**Алгоритма сопоставления при этом НЕТ и не нужно.** Если ходы пронумерованы на шаге 1, а блоки
|
||||
несут те же номера, соответствие возникает по построению. Проверка разметки детерминированна:
|
||||
каждый номер ровно один раз · порядок монотонный · длина блока в разумном отношении к длине хода ·
|
||||
термины банка хода N присутствуют в блоке N. Не сошлось — блок «без адреса», обрабатывается как сейчас.
|
||||
|
||||
### ШАГ 3 — Г3 (~$0.4)
|
||||
|
||||
Монолингвальный языковой критик: только русский текст, исходника нет вовсе. Сравнить его находки
|
||||
по оси ЯЗЫК с находками нынешнего двуязычного. Если не хуже — сличение из этой стадии убирается
|
||||
целиком, а это самая дешёвая экономия из всех возможных.
|
||||
|
||||
### ШАГ 4 — Г7 + Г8, ПУСТЫЕ КЛЕТКИ АРХИТЕКТУРЫ V6 (~$1.7)
|
||||
|
||||
Обе покупаются одним заходом, потому что делят базу:
|
||||
* **Г8**: промт однопроходки, собранный НАЧИСТО как роль литературного переводчика с инжектом
|
||||
действий — без мета-фразы про отсутствующего редактора, без следов склейки.
|
||||
* **Г7**: её выход + точечная зачистка по классифицированным замечаниям критика.
|
||||
|
||||
Это и есть урезанный V6 на одной паре. ⚠ Брать ОБЕ пары: разница zh/en в этой фазе оказалась
|
||||
не языковой, а структурной (выровненность исходника), и на одной паре вывод не встанет.
|
||||
|
||||
### ШАГ 5 — Г9 + Г10, БАНК (~$0.6)
|
||||
|
||||
Пере-собрать банк из выходов однопроходки и из дорогих черновиков, сравнить с нынешним по составу
|
||||
и покрытию. Отдельно — терминолог с РАЗДЕЛЁННЫМИ типами сущностей.
|
||||
|
||||
**Почему не раньше:** банк определяет связность книги на тысячах глав, но эффект его качества наш
|
||||
прибор не видит вовсе — он сравнивает армы при ОДНОМ банке. Значит замер тут не судейский, а
|
||||
детерминированный (состав, покрытие, устойчивость формы), и его надо ставить отдельно от
|
||||
качественных шагов, чтобы не смешать.
|
||||
|
||||
### ШАГ 6 — Г6, ТРУДНЫЙ АНГЛИЙСКИЙ МАТЕРИАЛ (~$1.5)
|
||||
|
||||
Длинные плотные английские главы вместо коротких и чистых. Проверяет, восстанавливается ли
|
||||
согласие счётчика с чтением при широком диапазоне. Разбиение +0.68/+0.42 сделано ПОСЛЕ данных —
|
||||
это разведка, и подтверждать её надо на новом материале, иначе это подгонка.
|
||||
|
||||
### ШАГ 7 — Г4, КЭШ ($0)
|
||||
|
||||
Переложить порядок запросов так, чтобы исходник сцены был общим неизменным префиксом всех стадий.
|
||||
Замер прямой: доля `cached_tokens` до и после. Сейчас 44.8%.
|
||||
|
||||
### ШАГ 8 — ПЕРЕНОСИМОСТЬ ВЫВОДА НА ДРУГУЮ МОДЕЛЬ (~$0.5)
|
||||
|
||||
Всё измеренное получено при модели-константе `deepseek-v4-pro`. Вывод честно звучит «какая
|
||||
архитектура лучше ДЛЯ ЭТОЙ модели». Прогнать победившую архитектуру на `glm-5` и проверить, что
|
||||
порядок не зависит от жильца. ⚠ Ставить ПОСЛЕ шагов 2–4: пока архитектура не выбрана, переносить
|
||||
нечего.
|
||||
|
||||
### ШАГ 9 — СЛЕПОЕ ЧТЕНИЕ ЧЕЛОВЕКОМ НА НОРМАЛЬНОМ n ($0)
|
||||
|
||||
Владелец прочёл 1 китайскую главу и 2 английских. Пре-рег П-6 просил 8–12 единиц на ось.
|
||||
Его чтение оказалось ТОЧНЕЕ машинных читателей (взял контроль там, где они провалили), поэтому
|
||||
расширение выборки — самый дешёвый способ усилить решающий эндпойнт. Ограничение известно и
|
||||
названо им самим: это его время.
|
||||
|
||||
---
|
||||
|
||||
## 3. ЧТО ПРОНИЗЫВАЕТ ВСЕ ШАГИ — Г12
|
||||
|
||||
**Сравнение архитектур по ОДНОМУ прогону запрещено.** Это вывод захода Д17, подтверждённый тремя
|
||||
приборами. Любой платный замер выше планируется в ДВУХ генерациях на метод, иначе он померяет шум.
|
||||
|
||||
Цена этого честная: ×2 к смете. Альтернатива — мерить и не знать, что померил.
|
||||
|
||||
---
|
||||
|
||||
## 4. ДОЛГИ ФАЗЫ Д — их надо закрыть, но они НЕ курс
|
||||
|
||||
Не двигают знание, но без них фаза не сдаётся. Порядок внутри — от того, что искажает выводы,
|
||||
к тому, что искажает отчётность.
|
||||
|
||||
1. **Адъюдикация английской оси** — единственное, что блокирует единственный несущий вывод фазы
|
||||
(H-3 в эскалации). `adjud.py` прошит на zh, нужна параметризация. $0.
|
||||
2. **65 находок ревизии** — шаг 1 курса разбирает первую; остальные разобрать там же.
|
||||
3. **Секция Д5** — ручная пере-классификация 24 мест. $0.
|
||||
4. **Синхронизация отчёта:** сводка Д9 противоречит Д15–Д20 в четырёх клетках · Д16 не отражает
|
||||
аннулирование японской пачки Sol её же контролями · деньги в Д17 протухли (напечатана смета
|
||||
$1.06 и потолки $0.65, факт $11.72 из $12.10) · тела эрраты Э-17.1 в отчёте нет.
|
||||
5. **Реестр требований:** строк на Д17–Д20 нет вовсе, 16 самореферентных улик, R64 красный.
|
||||
6. **Пинг в `docs/PROGRESS.md` секция «Полигон»** — последняя запись 15.08.
|
||||
7. **Селфтесты `sud.py` красные на трёх осях** (d3 — 4 провала, d6 — 1, d1 — 2), все настоящие,
|
||||
диагнозы в журнале. ⚠ `MIN_FLOOR["d6"]=3` — константа, поставленная под зелень; НЕ править,
|
||||
это вопрос владельцу.
|
||||
8. **`E1` побайтно равен черновику на 5 единицах из 16** — объявлено в Д14.4, панель без них не
|
||||
пере-считана.
|
||||
9. **Эррата zh-канона:** поправка границы слова живёт в коде, в сохранённый банк не дошла
|
||||
(завышение покрытия +0.0039). Правка закрытой оси — ратифицирует оркестратор.
|
||||
10. **`chtenie.py --score-calib` не пере-считывает число −0.10**, ради воспроизводимости которого
|
||||
объявлен: печатает только порядок читателя. Долг назван в докстринге, не закрыт.
|
||||
11. **Пакет второму семейству (Sol) по заходу Д17** — не собирался. Решение владельца нужно:
|
||||
отдавать или объявить заход односемейным явно.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 5. ЧЕГО НЕ ДЕЛАТЬ
|
||||
|
||||
* **Не строить V6 целиком.** Пять стадий сразу — это замер, в котором нельзя понять, что сработало.
|
||||
Шаги 2–5 покупают его по кускам, и каждый кусок отвечает на свой вопрос.
|
||||
* **Не выбирать архитектуру по деньгам.** Разброс между схемами 15–40%, между временем суток —
|
||||
ровно вдвое. Экономить надо расписанием прогона, решать — качеством.
|
||||
* **Не переносить отрицательные результаты по точечным контурам на V6.** Там фиксер чинил
|
||||
дешёвый черновик; в V6 он чинит перевод хорошей модели. Другая работа.
|
||||
* **Не сравнивать числа разных проходов.** Рубрика менялась (Д18), прогоны разной строгости.
|
||||
* **Не покупать до пере-снятия прайса**, если вендор снова его двинет: `zakhod.price_gate`
|
||||
отказывает автоматически, но проверять руками при первом запуске сессии.
|
||||
|
||||
---
|
||||
|
||||
## 6. ЛОВУШКИ, ДОБАВИВШИЕСЯ ЗА ЭТУ СЕССИЮ
|
||||
|
||||
1. **Файл клетки ≠ годная клетка.** Считать СОДЕРЖИМОЕ и `finish=stop`. Я на этом сел и доложил
|
||||
владельцу «80 клеток куплено», когда годных было 23.
|
||||
2. **Пред-полётная проба обязательна** перед циклом на сотню клеток (`zakhod.preflight`): 96
|
||||
запросов ушли в `402` и выглядели как успешный прогон.
|
||||
3. **Оценивать цену клетки ПО СВОЕЙ ПАРЕ**, а не по общему пулу: китайская глава впятеро длиннее
|
||||
английской, общая медиана врёт вдвое.
|
||||
4. **Раскладка меток в пакете ЧЕЛОВЕКУ — одна на всю пару.** Раскладка на отрывок сделала ответ
|
||||
владельца нерасшифровываемым, и его работа пропала.
|
||||
5. **Разборщик обязан принимать человеческую форму ответа.** Требовать машинной и молча терять
|
||||
ответ — дефект инструмента, а не читателя.
|
||||
6. **Пере-эмиссия не имеет права трогать пару, которая уже прочитана.** Ключ ДОГРУЖАТЬ.
|
||||
7. **Гейт, который не может упасть, ничего не сторожит.** Пустая панель в селфтесте — ПРОВАЛ,
|
||||
а не пропуск.
|
||||
8. **`git checkout` поверх грязного дерева запрещён** — владелец отказал в пермишене, и правильно.
|
||||
13. **Обогащение промта ДЕШЁВОЙ модели перевооружает эхо-мину.** Замер Д17: мандат перевёрстки,
|
||||
добавленный к черновику, дал 4 негодные клетки из 16 на zh (модель вернула исходник вместо
|
||||
перевода) против 1 из 16 в базе. Причина документирована: черновик ходит с `reasoning_effort:
|
||||
low`, а запрет D21.6 говорит не вносить языковые констрейнты в промты reasoning-ослабленных
|
||||
путей без per-model замера. **Любое обогащение промта на дешёвой модели — только с эхо-гейтом
|
||||
и ре-геном.**
|
||||
14. **Идентичность судей и ЧИТАТЕЛЕЙ персистится ДО запуска.** Я закрыл её постфактум
|
||||
(`READERS-z17.json`) — это слабее нормы. У прохода `tier` отсутствие такого файла сделало
|
||||
причину расхождения в 12 раз НЕВОССТАНОВИМОЙ.
|
||||
15. **Три инструмента зоны признаны негодными ВНУТРИ ревизии, а гейты числят их зелёными:**
|
||||
`promptdiff.py` (расхождение матчится по префиксу строки; отсутствующий файл пар-пакета гейт
|
||||
не роняет) · `canon.py` (снятая ревью классификация всё равно пишется в артефакт — отсюда
|
||||
негодность Д5) · `material_ja.py` (фильтр AI-меток объявлен механическим, кода не существует).
|
||||
Опираться на любой — только после вскрытия.
|
||||
16. **Д17 отсужен ОДНИМ семейством.** Норма П-1 требует двух там, где возможно. Пакет для Sol по
|
||||
заходу НЕ собирался. Все выводы Д17–Д20 стоят на семействе claude плюс чтение владельца.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 7. ЧЕМ ЗАКРЫВАЕТСЯ РАБОТА
|
||||
|
||||
1. Пре-рег ДО каждой покупки, зафризенный коммитом с основанием вслух.
|
||||
2. Мощность (`power.py`) напечатана ЧИСЛОМ до денег — не словами. За это уже была девиация Э-17.2.
|
||||
3. Гейты зелёные ИЛИ красные с диагнозом; константы под зелень не подгонять.
|
||||
4. Секции в отчёте: следующая свободная — **Д21**.
|
||||
5. Адверсариальная приёмка другим модельным семейством — она за эту сессию нашла 8 дефектов
|
||||
замера и 4 неверных утверждения в моём же отчёте. Без неё не сдавать.
|
||||
6. Дерево НЕ коммитить, кроме пре-рег-фризов; лендит оркестратор.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -28,7 +28,7 @@
|
|||
проверке, поэтому это исполнение буквы, а не упрощение. Приближение объявлено: поправка
|
||||
предполагает, что доля верных претензий одинакова по единицам внутри арма.
|
||||
|
||||
Запуск: adjud.py --emit | --score
|
||||
Запуск: adjud.py --emit | --controls | --score | --selftest
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -70,19 +70,120 @@ PER_TASK = 24
|
|||
SEED = 20260814 # фиксирован: раскладка воспроизводима
|
||||
_norm = re.compile(r"[\s«»„“”\"'`.,!?;:—–-]+")
|
||||
|
||||
# ⚠ Имена осей — ЯВНЫМ списком, а не `[А-ЯЁ]{4,}`: см. починку дефекта Г-3 ниже по файлу.
|
||||
AX_SPLIT = re.compile(r"(?=\b(?:ВЕРНОСТЬ|ТЕРМИН|ЯЗЫК|ФОРМА)\s*[:—–-])")
|
||||
AX_HEAD = re.compile(r"(ВЕРНОСТЬ|ТЕРМИН|ЯЗЫК|ФОРМА)\s*[:—–-](.*)", re.DOTALL)
|
||||
|
||||
def _fragment(var: str, rnd: random.Random, target: int) -> str:
|
||||
"""Фрагмент варианта ДЛИНОЙ ОКОЛО `target` — материал для ЛОЖНОЙ претензии.
|
||||
|
||||
⚠ Дефект Г-2, вторая половина. Мало построить ложной претензии такое же окно: если её цитата
|
||||
систематически короче настоящих (18 знаков против 51 в первой починке), карточка выдаёт себя
|
||||
длиной. Целевая длина берётся из ЭМПИРИЧЕСКОГО распределения настоящих цитат этого же прогона.
|
||||
"""
|
||||
words = [w for w in re.split(r"\s+", var.strip()) if w]
|
||||
if len(words) < 3:
|
||||
return ""
|
||||
best, best_d = "", 10 ** 9
|
||||
for _ in range(60):
|
||||
i = rnd.randrange(0, len(words))
|
||||
frag = ""
|
||||
for j in range(i + 1, min(len(words) + 1, i + 25)):
|
||||
frag = " ".join(words[i:j])
|
||||
d = abs(len(frag) - target)
|
||||
if d < best_d:
|
||||
best, best_d = frag, d
|
||||
if len(frag) > target + 15:
|
||||
break
|
||||
if best_d <= 4:
|
||||
break
|
||||
return best if len(best) >= 8 else ""
|
||||
|
||||
|
||||
def norm(t: str) -> str:
|
||||
return _norm.sub(" ", (t or "").lower()).strip()
|
||||
|
||||
|
||||
def context(text: str, quote: str, width: int = 260) -> str:
|
||||
"""Цитата с окружением — адъюдикатор обязан видеть, в каком месте она стоит."""
|
||||
n, q = norm(text), norm(quote)
|
||||
i = n.find(q)
|
||||
if i < 0:
|
||||
"""Цитата с окружением — адъюдикатор обязан видеть, в каком месте она стоит.
|
||||
|
||||
⚠ ПОЧИНКА 15.08 (дефект Г-4). Прежняя редакция и ИСКАЛА, и РЕЗАЛА по `norm()`, то есть
|
||||
отдавала адъюдикатору окно без пунктуации и в нижнем регистре. По такому окну нельзя судить
|
||||
ни ФОРМУ (она стёрта), ни ЯЗЫК (регистр и пунктуация — часть языковой ошибки), а цитата в
|
||||
самой претензии при этом шла в исходном виде: окно и цитата выглядели как разные тексты.
|
||||
Теперь поиск идёт по нормализованному, а РЕЗ — по сырому тексту через карту позиций.
|
||||
"""
|
||||
span = locate(text, quote)
|
||||
if span is None:
|
||||
return quote
|
||||
lo, hi = max(0, i - width // 2), min(len(n), i + len(q) + width // 2)
|
||||
return ("…" if lo else "") + n[lo:hi] + ("…" if hi < len(n) else "")
|
||||
lo, hi = span
|
||||
lo = max(0, lo - width // 2)
|
||||
hi = min(len(text), hi + width // 2)
|
||||
return ("…" if lo > 0 else "") + text[lo:hi] + ("…" if hi < len(text) else "")
|
||||
|
||||
|
||||
def _normmap(text: str) -> tuple[str, list[int]]:
|
||||
"""Нормализованный текст + карта «позиция в нём → позиция в СЫРОМ тексте»."""
|
||||
buf, raw_pos, prev_space = [], [], True
|
||||
for i, ch in enumerate(text):
|
||||
if _norm.match(ch):
|
||||
if prev_space:
|
||||
continue
|
||||
buf.append(" ")
|
||||
raw_pos.append(i)
|
||||
prev_space = True
|
||||
else:
|
||||
buf.append(ch.lower())
|
||||
raw_pos.append(i)
|
||||
prev_space = False
|
||||
return "".join(buf), raw_pos
|
||||
|
||||
|
||||
def locate(text: str, quote: str) -> tuple[int, int] | None:
|
||||
"""Границы цитаты в СЫРОМ тексте; поиск ведётся по нормализованным формам."""
|
||||
if not text or not quote:
|
||||
return None
|
||||
n, raw_pos = _normmap(text)
|
||||
q = norm(quote)
|
||||
i = n.find(q)
|
||||
if i < 0 or not q:
|
||||
return None
|
||||
j = min(i + len(q), len(raw_pos)) - 1
|
||||
return raw_pos[i], raw_pos[j] + 1
|
||||
|
||||
|
||||
def margin_spans(text: str) -> tuple[str, list[tuple[int, int]]]:
|
||||
"""То же лечение, что `sud.margin_plant`, но с ПОЗИЦИЯМИ посадок в получившемся тексте.
|
||||
|
||||
⚠ Зачем (дефект Г-1). Прежде «посаженной» считалась ЛЮБАЯ претензия к клетке маржевого декоя,
|
||||
а судья цитировал в ней и обычные места: из 15 «посадок» реальными были 4. Контроль
|
||||
чувствительности мерил не то — согласие с претензией к декою, а не опознание известной порчи.
|
||||
"""
|
||||
out, spans = text, []
|
||||
for pat, rep, _c in S.MARGIN_PLANTS:
|
||||
m = re.search(pat, out)
|
||||
if not m:
|
||||
continue
|
||||
frag = m.expand(rep)
|
||||
start, end_old = m.start(), m.end()
|
||||
out = out[:start] + frag + out[end_old:]
|
||||
delta = len(frag) - (end_old - start)
|
||||
spans = [(a + delta, b + delta) if a >= end_old else (a, b) for a, b in spans]
|
||||
spans.append((start, start + len(frag)))
|
||||
return out, spans
|
||||
|
||||
|
||||
def hits_planted(var: str, quote: str, spans: list[tuple[int, int]]) -> bool:
|
||||
"""Накрывает ли цитата хоть одну посадку.
|
||||
|
||||
Строго ПЕРЕСЕЧЕНИЕМ отрезков, без запаса «где-то рядом»: судья, процитировавший соседнее
|
||||
предложение, посадку НЕ опознал, и засчитывать это как чувствительность — тот же дефект Г-1
|
||||
в более слабой форме.
|
||||
"""
|
||||
q = locate(var, quote)
|
||||
if q is None:
|
||||
return False
|
||||
return any(q[0] < b and a < q[1] for a, b in spans)
|
||||
|
||||
|
||||
def collect() -> tuple[list, dict]:
|
||||
|
|
@ -112,16 +213,22 @@ def collect() -> tuple[list, dict]:
|
|||
var, _ = S.margin_plant(C.base_a0(uid))
|
||||
if not var.strip():
|
||||
continue
|
||||
w = re.search(rf"^{lab}-ПОЧЕМУ:\s*(.*)$", txt, re.M)
|
||||
w = re.search(rf"^{lab}-ПОЧЕМУ:\s*(.*)$", txt, re.MULTILINE)
|
||||
why = w.group(1) if w else ""
|
||||
# какие оси ненулевые — по ним претензии реальные; нулевые дают материал для лжи
|
||||
zero = []
|
||||
for a in AX:
|
||||
m = re.search(rf"^{lab}-{a}:\s*(\d+)", txt, re.M)
|
||||
m = re.search(rf"^{lab}-{a}:\s*(\d+)", txt, re.MULTILINE)
|
||||
if m and int(m.group(1)) == 0:
|
||||
zero.append(a)
|
||||
for seg in re.split(r"(?=[А-ЯЁ]{4,}:)", why):
|
||||
m = re.match(r"([А-ЯЁ]{4,}):(.*)", seg.strip(), re.S)
|
||||
# ⚠ ПОЧИНКА 15.08 (дефект Г-3). Прежде разбор осей шёл через `[А-ЯЁ]{4,}:`, и
|
||||
# `re.split` с таким lookahead режет ВНУТРИ имени оси: «ВЕРНОСТЬ:» даёт точки
|
||||
# разреза на В, Е, Р, Н, О, и уцелевает хвост «ОСТЬ». В ключе прошлого прогона
|
||||
# это видно прямо: ОСТЬ 45 · ОРМА 22 · РМИН 18 против ВЕРНОСТЬ 1 · ФОРМА 4.
|
||||
# Следствие тяжелее самой опечатки: фильтр «несущие оси» считал одну ось из двух,
|
||||
# а ложные и настоящие претензии стали различимы по форме имени оси.
|
||||
for seg in AX_SPLIT.split(why):
|
||||
m = AX_HEAD.match(seg.strip())
|
||||
if not m:
|
||||
continue
|
||||
ax = m.group(1)
|
||||
|
|
@ -133,14 +240,24 @@ def collect() -> tuple[list, dict]:
|
|||
if arm in ARMS:
|
||||
real.append(rec)
|
||||
if arm == "CTRLmargin":
|
||||
plant_pool.append(rec)
|
||||
# ложные претензии строятся из мест, где судья поставил НОЛЬ
|
||||
# ⚠ дефект Г-1: посадкой считается только цитата, реально накрывшая
|
||||
# подменённое место, а не любая претензия к клетке декоя
|
||||
_, spans = margin_spans(C.base_a0(uid))
|
||||
if hits_planted(var, q, spans):
|
||||
plant_pool.append(rec)
|
||||
# ⚠ ЛОЖНЫЕ ПРЕТЕНЗИИ — ПОЧИНКА 15.08 (дефект Г-2). Прежде окно ложной претензии
|
||||
# строилось как `context(quote, quote)`, то есть текстом служила сама цитата:
|
||||
# окружения не было и многоточий тоже, тогда как у настоящих претензий окно
|
||||
# всегда шло из полного варианта и почти всегда с «…». Плюс ложная цитата была
|
||||
# ЦЕЛЫМ предложением 40–200 знаков против коротких фрагментов у настоящих.
|
||||
# Адъюдикатору не нужно было судить перевод — хватало формы карточки.
|
||||
# Теперь ложная цитата берётся ФРАГМЕНТОМ той же длины, что настоящие, из того же
|
||||
# варианта, и окно строится тем же `context(var, …)`.
|
||||
if zero and arm in ARMS:
|
||||
sent = [x for x in re.split(r"(?<=[.!?])\s+", var) if 40 < len(x) < 200]
|
||||
if sent:
|
||||
false_pool.append(dict(uid=uid, arm=arm, axis=rnd.choice(zero),
|
||||
quote=rnd.choice(sent), ctx="", src=u["source"],
|
||||
fake=True))
|
||||
# цитата подбирается ПОСЛЕ прохода, когда известно распределение длин
|
||||
# настоящих цитат этого прогона; здесь копится только материал
|
||||
false_pool.append(dict(uid=uid, arm=arm, axis=rnd.choice(zero),
|
||||
var=var, src=u["source"], fake=True))
|
||||
# стратифицированная выборка: не более PER_CELL претензий на (единица, арм)
|
||||
by = {}
|
||||
for r in real:
|
||||
|
|
@ -150,8 +267,15 @@ def collect() -> tuple[list, dict]:
|
|||
v = by[k]
|
||||
rnd.shuffle(v)
|
||||
sample += v[:PER_CELL]
|
||||
for r in false_pool[:0] or rnd.sample(false_pool, min(N_FALSE, len(false_pool))):
|
||||
r["ctx"] = context(r["quote"], r["quote"])
|
||||
# ⚠ Длины ложных цитат берутся из ЭМПИРИЧЕСКОГО распределения настоящих (дефект Г-2).
|
||||
real_len = [len(r["quote"]) for r in sample] or [40]
|
||||
for r in rnd.sample(false_pool, min(N_FALSE, len(false_pool))):
|
||||
var = r["var"]
|
||||
frag = _fragment(var, rnd, rnd.choice(real_len))
|
||||
if not frag:
|
||||
continue
|
||||
r = {k: v for k, v in r.items() if k != "var"}
|
||||
r["quote"], r["ctx"] = frag, context(var, frag)
|
||||
sample.append(r)
|
||||
for r in rnd.sample(plant_pool, min(N_PLANT, len(plant_pool))):
|
||||
r = dict(r, planted=True)
|
||||
|
|
@ -200,9 +324,13 @@ def emit() -> None:
|
|||
body = [HEAD.format(path=ANSW / f"{tok}.txt", skeleton=skel)]
|
||||
for l in grp:
|
||||
r = sample[labs.index(l)]
|
||||
# ⚠ Окно берётся из СЫРОГО текста и может быть многострочным (абзацы — часть ФОРМЫ).
|
||||
# Поэтому оно обрамляется явными границами: иначе перенос строки внутри окна
|
||||
# неотличим от следующего поля карточки, и разбор — как машинный, так и глазной —
|
||||
# видит разные карточки у разных претензий, то есть снова форма выдаёт класс.
|
||||
body += ["", "=" * 60, f"{l}",
|
||||
"ИСХОДНИК (китайский):", r["src"][:1800],
|
||||
"", f"МЕСТО В ПЕРЕВОДЕ: {r['ctx'] or r['quote']}",
|
||||
"", "МЕСТО В ПЕРЕВОДЕ:", "<<<", r["ctx"] or r["quote"], ">>>",
|
||||
f"ПРЕТЕНЗИЯ: здесь ошибка типа {r['axis']} — в цитате «{r['quote']}»"]
|
||||
(TASKS / f"{tok}.txt").write_text("\n".join(body), encoding="utf-8")
|
||||
print(f"претензий {len(labs)} · заданий {len(list(TASKS.glob('*.txt')))} → {TASKS}")
|
||||
|
|
@ -212,6 +340,65 @@ def emit() -> None:
|
|||
print(f"из них контролей: ложных претензий {n_f} · посаженных дефектов {n_p}")
|
||||
|
||||
|
||||
def controls() -> int:
|
||||
"""НЕРАЗЛИЧИМОСТЬ КЛАССОВ ПО ФОРМЕ КАРТОЧКИ — гейт, без которого контроли фиктивны.
|
||||
|
||||
Прежняя редакция проваливала его вчистую: ложная претензия шла без окружения и целым
|
||||
предложением, то есть адъюдикатору хватало формы, чтобы отличить контроль от боевой претензии.
|
||||
Здесь это меряется числом, а не обещается словом.
|
||||
"""
|
||||
if not AKEY.exists():
|
||||
print("ключа нет — сначала --emit")
|
||||
return 1
|
||||
key = json.loads(AKEY.read_text(encoding="utf-8"))
|
||||
cards = {}
|
||||
for f in sorted(TASKS.glob("*.txt")):
|
||||
for blk in f.read_text(encoding="utf-8").split("=" * 60)[1:]:
|
||||
m = re.match(r"\s*(П\d{3})", blk)
|
||||
if not m:
|
||||
continue
|
||||
w = re.search(r"<<<\n(.*?)\n>>>", blk, re.DOTALL)
|
||||
q = re.search(r"в цитате «([^»]*)»", blk)
|
||||
cards[m.group(1)] = (w.group(1) if w else "", q.group(1) if q else "")
|
||||
if not cards:
|
||||
print("заданий нет — сначала --emit")
|
||||
return 1
|
||||
groups = {
|
||||
"настоящие": [k for k, v in key.items() if not v.get("fake") and not v.get("planted")],
|
||||
"ЛОЖНЫЕ": [k for k, v in key.items() if v.get("fake")],
|
||||
"посадки": [k for k, v in key.items() if v.get("planted")],
|
||||
}
|
||||
import statistics as _st
|
||||
rows = {}
|
||||
print(f"{'класс':10s} {'n':>4s} {'цитата':>7s} {'окно':>7s} {'с «…»':>6s} {'многостр':>9s}")
|
||||
for name, sel in groups.items():
|
||||
sel = [k for k in sel if k in cards]
|
||||
if not sel:
|
||||
print(f"{name:10s} пусто")
|
||||
continue
|
||||
ql = _st.median([len(cards[k][1]) for k in sel])
|
||||
wl = _st.median([len(cards[k][0]) for k in sel])
|
||||
el = sum(cards[k][0].startswith("…") or cards[k][0].endswith("…") for k in sel) / len(sel)
|
||||
ml = sum("\n" in cards[k][0] for k in sel) / len(sel)
|
||||
rows[name] = (ql, wl, el, ml)
|
||||
print(f"{name:10s} {len(sel):4d} {ql:7.1f} {wl:7.1f} {el:5.0%} {ml:8.0%}")
|
||||
rc = 0
|
||||
base = rows.get("настоящие")
|
||||
if base:
|
||||
for name, r in rows.items():
|
||||
if name == "настоящие":
|
||||
continue
|
||||
# медианы цитаты и окна не должны расходиться больше чем в полтора раза,
|
||||
# а доля окон с многоточием — больше чем на четверть
|
||||
if not (0.55 <= r[0] / max(base[0], 1) <= 1.8) or \
|
||||
not (0.55 <= r[1] / max(base[1], 1) <= 1.8) or abs(r[2] - base[2]) > 0.25:
|
||||
print(f"\n⛔ класс «{name}» ОТЛИЧИМ от настоящих претензий по форме карточки")
|
||||
rc = 1
|
||||
if not rc:
|
||||
print("\nформа карточки классы не выдаёт — контроли пригодны")
|
||||
return rc
|
||||
|
||||
|
||||
def score() -> int:
|
||||
if not AKEY.exists():
|
||||
print("ключа нет — сначала --emit")
|
||||
|
|
@ -220,7 +407,7 @@ def score() -> int:
|
|||
ans = {}
|
||||
for f in sorted(ANSW.glob("*.txt")):
|
||||
for line in f.read_text(encoding="utf-8", errors="replace").splitlines():
|
||||
m = re.match(r"\s*(П\d{3})\s*:\s*(ДА|НЕТ)", line.strip(), re.I)
|
||||
m = re.match(r"\s*(П\d{3})\s*:\s*(ДА|НЕТ)", line.strip(), re.IGNORECASE)
|
||||
if m:
|
||||
ans[m.group(1)] = m.group(2).upper() == "ДА"
|
||||
print(f"ответов получено: {len(ans)} из {len(key)}")
|
||||
|
|
@ -261,11 +448,79 @@ def score() -> int:
|
|||
return 0
|
||||
|
||||
|
||||
def selftest() -> int:
|
||||
"""Проверки ПОЧИНЕННЫХ контролей. Без них адъюдикация переносит доверие на уровень ниже."""
|
||||
fails: list[str] = []
|
||||
|
||||
def ok(cond: bool, msg: str) -> None:
|
||||
if not cond:
|
||||
fails.append(msg)
|
||||
|
||||
# Г-3: имя оси не режется
|
||||
for name in ("ВЕРНОСТЬ", "ТЕРМИН", "ЯЗЫК", "ФОРМА"):
|
||||
got = [AX_HEAD.match(s.strip()).group(1)
|
||||
for s in AX_SPLIT.split(f"{name}: «цитата» — пояснение") if AX_HEAD.match(s.strip())]
|
||||
ok(got == [name], f"Г-3: ось {name} разобралась как {got}")
|
||||
multi = "ВЕРНОСТЬ: «а» | «б». ЯЗЫК: «в». ФОРМА: «г»."
|
||||
got = [AX_HEAD.match(s.strip()).group(1)
|
||||
for s in AX_SPLIT.split(multi) if AX_HEAD.match(s.strip())]
|
||||
ok(got == ["ВЕРНОСТЬ", "ЯЗЫК", "ФОРМА"], f"Г-3: разбор трёх осей дал {got}")
|
||||
|
||||
# Г-4: окно режется по СЫРОМУ тексту — пунктуация и регистр на месте
|
||||
raw = 'Он сказал: «Стрела уже слетела с тетивы!» И замолчал, глядя вдаль.'
|
||||
win = context(raw, "Стрела уже слетела", width=40)
|
||||
ok("«" in win or "!" in win or "С" in win,
|
||||
f"Г-4: окно потеряло пунктуацию и регистр: {win!r}")
|
||||
ok(win.strip("…") in raw, f"Г-4: окно не является подстрокой сырого текста: {win!r}")
|
||||
|
||||
# Г-1: посадки имеют позиции, и цитата вне них не считается посаженной
|
||||
base = "Он не поверил своим глазам. Их было три. Обычное объяснение не помогло."
|
||||
planted, spans = margin_spans(base)
|
||||
ok(bool(spans), "Г-1: посадок не найдено — контроль чувствительности будет пуст")
|
||||
ok(planted != base, "Г-1: текст декоя не отличается от эталона")
|
||||
if spans:
|
||||
a, b = spans[0]
|
||||
ok(hits_planted(planted, planted[a:b], spans), "Г-1: цитата ПО посадке не опознана")
|
||||
if spans:
|
||||
# цитата из области, ЗАВЕДОМО не пересекающей ни одну посадку
|
||||
busy = sorted(spans)
|
||||
gap = next(((busy[i][1], busy[i + 1][0]) for i in range(len(busy) - 1)
|
||||
if busy[i + 1][0] - busy[i][1] >= 12), None)
|
||||
outside = planted[gap[0]:gap[1]].strip(" .,!?") if gap else ""
|
||||
ok(not outside or not hits_planted(planted, outside, spans),
|
||||
f"Г-1: цитата ВНЕ посадки ({outside!r}) засчитана как посаженная")
|
||||
|
||||
# Г-2: ложная цитата неотличима от настоящей по длине
|
||||
rnd = random.Random(SEED)
|
||||
long = " ".join(f"слово{i}" for i in range(40))
|
||||
for target in (20, 45, 80):
|
||||
frag = _fragment(long, rnd, target)
|
||||
ok(bool(frag) and abs(len(frag) - target) <= 8,
|
||||
f"Г-2: фрагмент под цель {target} вышел {len(frag)}: {frag!r}")
|
||||
|
||||
# Г-2 на живом ключе, если он есть: форма карточки не должна выдавать ложную
|
||||
if AKEY.exists():
|
||||
key = json.loads(AKEY.read_text(encoding="utf-8"))
|
||||
bad = [k for k, v in key.items() if v.get("axis") not in
|
||||
("ВЕРНОСТЬ", "ТЕРМИН", "ЯЗЫК", "ФОРМА")]
|
||||
ok(not bad, f"Г-3: в ключе {len(bad)} претензий с обрезанным именем оси "
|
||||
f"(пример {bad[:3]}) — ключ снят ДО починки, требуется пере-эмиссия")
|
||||
|
||||
for m in fails:
|
||||
print("ПРОВАЛ:", m)
|
||||
print(f"селфтест адъюдикации: провалов {len(fails)}")
|
||||
return 1 if fails else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = sys.argv[1:] or ["--emit"]
|
||||
if a[0] == "--emit":
|
||||
emit()
|
||||
elif a[0] == "--score":
|
||||
sys.exit(score())
|
||||
elif a[0] == "--selftest":
|
||||
sys.exit(selftest())
|
||||
elif a[0] == "--controls":
|
||||
sys.exit(controls())
|
||||
else:
|
||||
raise SystemExit(f"⛔ неизвестный режим {a[0]!r}")
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
счёту ошибок, — и потому решает судьбу СЧЁТА как дешёвого прокси, а не судьбу армов.
|
||||
|
||||
Запуск: chtenie.py --emit | --score | --selftest | --score-calib
|
||||
chtenie.py --emit-owner | --score-owner — пакет ВЛАДЕЛЬЦУ (4 варианта, «жирная глава»)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -193,7 +194,42 @@ def emit() -> None:
|
|||
|
||||
# ⚠ Латинская `T` принимается наравне с кириллической `Т` (починка по ревью): судьи уже писали
|
||||
# латиницей, а `zsud`/`ja6` это учитывают. Молча выпавший ответ читателя стоил бы целой сессии.
|
||||
_ORD = re.compile(r"ПОРЯДОК\s*(\d+)\s*:\s*((?:[ТT]\d+\s*[>=]\s*)+[ТT]\d+)", re.IGNORECASE)
|
||||
# ⚠ ФОРМА ОТВЕТА ЧЕЛОВЕКА ШИРЕ, ЧЕМ Я ПРЕДПИСАЛ, И ЭТО ДЕФЕКТ РАЗБОРЩИКА, А НЕ ОТВЕТА.
|
||||
# Владелец 17.08 ответил «Т3 > Т1 > Т2 > Т4» под заголовком «Порядок читаемости» и
|
||||
# «**ПОРЯДОК ЧИТАЕМОСТИ:** `Т1 > Т4 = Т3 > Т2`» — обе формы законны и обе прежним регексом
|
||||
# не читались: он требовал НОМЕРА отрывка сразу после слова. Инструмент, который не берёт
|
||||
# честный ответ, обесценивает работу читателя, а не читателя.
|
||||
# Теперь номер необязателен (нет — нумеруем по порядку появления), markdown-разметка и
|
||||
# обратные кавычки снимаются, латинская T принимается наравне с кириллической.
|
||||
_ORD_NUM = re.compile(r"ПОРЯДОК[^\n:]*?(\d+)\s*:", re.IGNORECASE)
|
||||
_ORD = re.compile(r"((?:[ТT]\d+\s*[>=]\s*)+[ТT]\d+)")
|
||||
|
||||
|
||||
def orders(txt: str) -> dict[str, str]:
|
||||
"""{номер отрывка: строка порядка}. Номер берётся из подписи «ПОРЯДОК … N:», если она есть;
|
||||
если её нет — порядки нумеруются по появлению в тексте.
|
||||
|
||||
⚠ Заведено 17.08 после того, как разборщик не взял ЧЕСТНЫЙ ответ владельца: он написал
|
||||
порядок под заголовком «## 1. Порядок читаемости», без обязательного номера сразу за словом.
|
||||
Требовать от человека машинной формы и молча терять его работу — дефект инструмента.
|
||||
⚠ Строки порядка ищутся ПО ВСЕМУ тексту, но берутся только те, что стоят ПОСЛЕ слова
|
||||
«ПОРЯДОК» либо первыми в файле: иначе в разбор попали бы упоминания меток из комментариев
|
||||
вроде «Т3 и Т1 стоят близко».
|
||||
|
||||
⚠ ВТОРАЯ ПОЧИНКА ТОГО ЖЕ ДНЯ: порядок ВЕРНОСТИ отбрасывается. Владелец дал по английской
|
||||
паре две строки — «ПОРЯДОК ЧИТАЕМОСТИ» и «ПОРЯДОК ВЕРНОСТИ», — а разборщик принял вторую за
|
||||
порядок второго отрывка и молча отсудил ею чужую главу. Прибор мерит ЧИТАЕМОСТЬ; верность
|
||||
считается детерминированно и в этот разбор не входит.
|
||||
"""
|
||||
out: dict[str, str] = {}
|
||||
for i, m in enumerate(_ORD.finditer(txt), 1):
|
||||
head = txt[max(0, m.start() - 200):m.start()]
|
||||
if "ВЕРНОСТ" in head.upper():
|
||||
continue
|
||||
nm = _ORD_NUM.findall(head)
|
||||
key = nm[-1] if nm else str(len(out) + 1)
|
||||
out.setdefault(key, m.group(1))
|
||||
return out
|
||||
|
||||
|
||||
def _ranks(order: str) -> dict[str, float]:
|
||||
|
|
@ -254,8 +290,7 @@ def score() -> int:
|
|||
f = WORK / meta0["pair"] / meta0["reader"] / "answers" / f"{tok}.md"
|
||||
if not f.exists():
|
||||
continue
|
||||
got = {m.group(1): m.group(2) for m in _ORD.finditer(
|
||||
f.read_text(encoding="utf-8", errors="replace"))}
|
||||
got = orders(f.read_text(encoding="utf-8", errors="replace"))
|
||||
for j, meta in blocks.items():
|
||||
if j not in got:
|
||||
print(f" ⚠ {tok}/отрывок {j}: порядка нет")
|
||||
|
|
@ -344,6 +379,210 @@ def score_calib() -> int:
|
|||
return 0
|
||||
|
||||
|
||||
# ── ПАКЕТ ДЛЯ ВЛАДЕЛЬЦА: он читает сам, человеческим глазом ─────────────────────────────────────
|
||||
# ⚠ ЗАЧЕМ ОТДЕЛЬНЫЙ РЕЖИМ, А НЕ ТОТ ЖЕ ПАКЕТ. Агентские пачки собраны под МАШИННОГО читателя:
|
||||
# восемь глав на ось, семь вариантов, двадцать заданий. Владелец дважды не стал такое читать и
|
||||
# сказал прямо: «чтоб не слишком много было, давай какую-нибудь жирную главу на язык». Значит
|
||||
# ограничение здесь — ЕГО ВРЕМЯ, и панель режется под него, а не под статистику.
|
||||
#
|
||||
# ЧТО ОСТАВЛЕНО И ПОЧЕМУ РОВНО ЭТО (четыре варианта вместо семи):
|
||||
# Z0 боевая связка — то, что стоит в продакшене; без неё сравнивать не с чем;
|
||||
# ZF ВТОРАЯ генерация той же связки — контроль шума. Если владелец разведёт их далеко, его
|
||||
# порядок есть шум, и «победа» любого арма внутри этого расстояния ничего не значит.
|
||||
# Это единственный способ узнать, различает ли он вообще что-то, — и он же самый дешёвый;
|
||||
# ZP однопроходка — один вызов вместо двух, и по банку с канон-фиксером она впереди связки;
|
||||
# Z1 критик → полный перепис — ставка владельца в сильнейшей форме.
|
||||
# Голый черновик НЕ включён намеренно: оба прибора и оба машинных читателя ставят его последним
|
||||
# с огромным отрывом, спора нет, а глава черновика — это лишние 7 тысяч знаков его чтения.
|
||||
# Обогащённый черновик с переписом тоже снят: от боевой связки он неотличим ни одним прибором.
|
||||
#
|
||||
# ⚠ ДВА ВОПРОСА, КАК ПРОСИЛ ВЛАДЕЛЕЦ («лучше по художке и при этом лучше держит канон смысл»), но
|
||||
# ЧЕСТНО РАЗВЕДЁННЫЕ. Читаемость может назвать только он. Верность исходнику на КИТАЙСКОМ он
|
||||
# назвать не может — для этого надо читать китайский, — поэтому там его просят отметить лишь то,
|
||||
# что видно без исходника: провалы связности, непонятные места, оборванные мысли. На английском
|
||||
# исходник приложен, и сверка возможна, но объявлена необязательной. Канон терминов при этом уже
|
||||
# посчитан детерминированно и в пакет НЕ кладётся до его ответа — иначе это подсказка.
|
||||
OWNER_PANEL = ("Z0", "ZF", "ZP", "Z1")
|
||||
OWNER_DIR = Path.home() / "books" / "chtenie-vladeltsa-z17"
|
||||
OWNER_KEY = KEYD / "chtenie-vladeltsa-z17-KEY.json"
|
||||
OWNER_SALT = "vladelets-z17-2026-08-17"
|
||||
OWNER_N = {"zh": 1, "en": 2} # «жирная глава на язык»; английские главы втрое короче
|
||||
|
||||
OWNER_HEAD = """# Что ты читаешь и зачем — коротко
|
||||
|
||||
Ниже {n} перевода одного и того же отрывка. Сделаны они разными способами: где-то один вызов
|
||||
модели, где-то два, где-то с промежуточным критиком. **Какой чем сделан — не сказано намеренно.**
|
||||
Метки перемешаны, расшифровка лежит в отдельном файле и до твоего ответа не открывается.
|
||||
|
||||
**Зачем это нужно.** Наш измеритель считает локальные ошибки — искажения смысла, кальки, сбитую
|
||||
вёрстку. Он дёшев и воспроизводим, но у него встроенная слепота: текст может быть без единой
|
||||
ошибки и при этом мёртвый. На последнем прогоне это подтвердилось числом — порядок по счёту ошибок
|
||||
и порядок по читаемости совпали лишь на треть, а вариант с наименьшим числом ошибок читался
|
||||
почти хуже всех. Твой ответ решает, верить ли дальше счётчику или чтению.
|
||||
|
||||
**Одна из четырёх версий — контроль.** Две из них сделаны ОДНИМ И ТЕМ ЖЕ способом, просто модель
|
||||
вызвана дважды. Какие именно — не скажу. Если ты поставишь их рядом, значит ты различаешь способы;
|
||||
если разведёшь далеко, значит разница между способами меньше собственного разброса модели, и тогда
|
||||
никакая «победа» в этом замере ничего не стоит. Специально их искать не надо — просто читай.
|
||||
|
||||
Метки сквозные: `Т1` в первом отрывке и `Т1` во втором — ОДИН И ТОТ ЖЕ способ.
|
||||
Поэтому порядок можно называть и по каждому отрывку, и один общий на всю пару.
|
||||
|
||||
⚠ **Что может сбить.** Вёрстка выдаёт метод: часть вариантов сливает построчный исходник в русские
|
||||
абзацы, часть сохраняет разбивку. Это видно ДО чтения — не давай форме решать за тебя.
|
||||
|
||||
---
|
||||
|
||||
## Что записать — в файл `{answer}`
|
||||
|
||||
1. **ПОРЯДОК ЧИТАЕМОСТИ** — строкой: `Т3 > Т1 > Т2 > Т4`, от лучшего к худшему.
|
||||
Неразличимые соединяй через `=`. Это главный вопрос: какой текст ты взял бы в книгу.
|
||||
2. **ПОРЯДОК ВЕРНОСТИ** — {fidelity}
|
||||
3. **По фразе на вариант**: почему. Годится и «серо, но гладко», и «живой ритм, но во втором
|
||||
абзаце теряется, кто кому говорит».
|
||||
|
||||
Если какой-то вариант покажется откровенно машинным — скажи прямо, это самое ценное.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
# ⚠ Верность исходнику владелец может назвать только там, где читает исходный язык. На китайском
|
||||
# он его не читает, и просить «сверь смысл» значило бы просить невыполнимого — а невыполнимая
|
||||
# просьба в задании обесценивает и выполнимую часть. Поэтому на zh просят то, что видно БЕЗ
|
||||
# исходника: провалы связности. Это не подмена вопроса, а его честная граница.
|
||||
OWNER_FID = {
|
||||
"zh": ("исходник китайский, и сверять его тебе нечем — поэтому этот пункт ПРОПУСТИ.\n"
|
||||
" Вместо него отметь то, что видно и без исходника: где текст теряет связность,\n"
|
||||
" где непонятно, кто что делает, где мысль обрывается. Такие места почти всегда\n"
|
||||
" и оказываются враньём про исходник."),
|
||||
"en": ("исходник приложен, английский. Если станешь сверять — назови порядок по верности\n"
|
||||
" отдельной строкой. Не станешь — пропусти, это необязательно."),
|
||||
}
|
||||
|
||||
|
||||
def emit_owner(only: list[str] | None = None) -> None:
|
||||
"""Пакет владельцу. `only` — пересобрать ТОЛЬКО названные пары.
|
||||
|
||||
⚠⚠ ПЕРЕ-ЭМИССИЯ НЕ ИМЕЕТ ПРАВА ТРОНУТЬ ПАРУ, КОТОРУЮ ВЛАДЕЛЕЦ УЖЕ ПРОЧЁЛ. Раскладка меток
|
||||
есть чистая функция соли и набора армов; пересборка ЦЕЛОГО пакета переписала бы ключ и
|
||||
китайской пары тоже — а её ответ уже дан и расшифрован. Тогда та же расшифровка при следующем
|
||||
запуске дала бы ДРУГИЕ армы, и работа владельца превратилась бы в мусор молча. Ровно так фаза
|
||||
Д однажды потеряла вердикт `E0/D0` (+1.56 → +0.38). Поэтому ключ здесь ДОГРУЖАЕТСЯ, а не
|
||||
создаётся заново, и пары вне `only` не пересчитываются.
|
||||
⚠ Прежний ответ по пересобираемой паре не удаляется, а уводится в архив: это улика — работа
|
||||
владельца, испорченная дефектом МОЕЙ сборки, а не его чтением.
|
||||
"""
|
||||
key: dict = {"_что": "слепое чтение ВЛАДЕЛЬЦЕМ, панель захода Д17", "_соль": OWNER_SALT,
|
||||
"_панель": list(OWNER_PANEL),
|
||||
"_контроль": "Z0 и ZF — две генерации ОДНОЙ боевой связки; развёл далеко = шум"}
|
||||
if OWNER_KEY.exists():
|
||||
key = {**json.loads(OWNER_KEY.read_text(encoding="utf-8")), **key}
|
||||
OWNER_DIR.mkdir(parents=True, exist_ok=True)
|
||||
for pair, langname, srcname in (("zh", "КИТАЙСКАЯ", "китайский"),
|
||||
("en", "АНГЛИЙСКАЯ", "английский")):
|
||||
if only and pair not in only:
|
||||
print(f" {pair}: не в списке пересборки — ключ и задание НЕ трогаются")
|
||||
continue
|
||||
prev = OWNER_DIR / f"ОТВЕТ-{pair}.md"
|
||||
if prev.exists() and _ORD.search(prev.read_text(encoding="utf-8")):
|
||||
arch = OWNER_DIR / f"ОТВЕТ-{pair}.ИСПОРЧЕННЫЙ-ПАКЕТ.md"
|
||||
arch.write_text(prev.read_text(encoding="utf-8"), encoding="utf-8")
|
||||
prev.unlink()
|
||||
print(f" {pair}: прежний ответ уведён в {arch.name} (улика, не удалён)")
|
||||
tx_all = ZS.texts_of(pair)
|
||||
# «жирная глава»: самая длинная из тех, где панель полна — длинная честнее короткой
|
||||
cand = [u for u in tx_all if all(tx_all[u].get(a, "").strip() for a in OWNER_PANEL)]
|
||||
cand.sort(key=lambda u: -sum(len(tx_all[u][a]) for a in OWNER_PANEL))
|
||||
take = cand[:OWNER_N[pair]]
|
||||
ans = OWNER_DIR / f"ОТВЕТ-{pair}.md"
|
||||
body = [f"# {langname} ПАРА — слепое чтение\n",
|
||||
OWNER_HEAD.format(n=len(OWNER_PANEL), answer=ans, fidelity=OWNER_FID[pair])]
|
||||
for j, uid in enumerate(take, 1):
|
||||
# ⚠ РАСКЛАДКА ОДНА НА ВСЮ ПАРУ, А НЕ СВОЯ У КАЖДОГО ОТРЫВКА (починка 17.08).
|
||||
# Первая редакция солила раскладку номером главы, и метка `Т1` означала РАЗНЫЕ
|
||||
# армы в первом и втором английском отрывке. Владелец — как поступил бы любой
|
||||
# читатель — дал ОДИН сводный порядок на пару и говорил о «Т1» как об одном
|
||||
# варианте; сопоставить это с армами оказалось нечем, и английская половина
|
||||
# его работы пропала не по его вине. Машинным читателям своя раскладка на
|
||||
# единицу нужна (они судят поединично), человеку — вредна.
|
||||
lay = layout(pair, "OWNER-ALL-v2", "OWNER", OWNER_PANEL)
|
||||
body += [f"\n# ОТРЫВОК {j}\n", f"## Исходник ({srcname})\n",
|
||||
tx_all[uid]["_src"], ""]
|
||||
for lab, arm in lay.items():
|
||||
body += ["\n---\n", f"## Отрывок {j}, вариант {lab}\n", tx_all[uid][arm], ""]
|
||||
key.setdefault(pair, {})[str(j)] = {
|
||||
"uid": uid, "метки": lay,
|
||||
"знаков": {a: len(tx_all[uid][a]) for a in OWNER_PANEL}}
|
||||
(OWNER_DIR / f"ЧТЕНИЕ-{pair}.md").write_text("\n".join(body), encoding="utf-8")
|
||||
if not ans.exists():
|
||||
ans.write_text(
|
||||
f"# Ответ — {langname.lower()} пара\n\n"
|
||||
+ "".join(f"ПОРЯДОК ЧИТАЕМОСТИ {j}: \n" for j in range(1, len(take) + 1))
|
||||
+ "\nЗаметки по вариантам:\n", encoding="utf-8")
|
||||
n = sum(len(tx_all[u][a]) for u in take for a in OWNER_PANEL)
|
||||
print(f" {pair}: глав {len(take)} · вариантов {len(OWNER_PANEL)} · {n // 1000} тыс. знаков")
|
||||
OWNER_KEY.write_text(json.dumps(key, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
print(f"\nпакет → {OWNER_DIR}")
|
||||
print(f"ключ → {OWNER_KEY} (НЕ открывать до ответа)")
|
||||
|
||||
|
||||
def score_owner() -> int:
|
||||
if not OWNER_KEY.exists():
|
||||
print("ключа нет — сначала --emit-owner")
|
||||
return 1
|
||||
key = json.loads(OWNER_KEY.read_text(encoding="utf-8"))
|
||||
sc, _bad = ZS.read()
|
||||
for pair in ("zh", "en"):
|
||||
f = OWNER_DIR / f"ОТВЕТ-{pair}.md"
|
||||
if not f.exists() or pair not in key:
|
||||
continue
|
||||
txt = f.read_text(encoding="utf-8")
|
||||
got = orders(txt)
|
||||
if not got:
|
||||
print(f"{pair}: порядка нет — ждём ответа владельца")
|
||||
continue
|
||||
print(f"\n=== {pair} ===")
|
||||
# ⚠ СТОРОЖ НЕОДНОЗНАЧНОЙ РАСШИФРОВКИ (заведён 17.08 по факту). Если в ключе у отрывков
|
||||
# РАЗНЫЕ раскладки меток, а читатель дал ОДИН сводный порядок, то механическое применение
|
||||
# этого порядка к каждой раскладке даёт столько же РАЗНЫХ расшифровок, сколько отрывков, и
|
||||
# все они одинаково «правдоподобны». Так и вышло с английской парой: один и тот же ответ
|
||||
# владельца дал «однопроходка первая» на первом отрывке и «однопроходка ПОСЛЕДНЯЯ» на
|
||||
# втором. Печатать это как результат — значит выдать артефакт разметки за замер.
|
||||
maps = [tuple(sorted(m["метки"].items())) for m in key[pair].values()]
|
||||
if len(set(maps)) > 1 and len(got) < len(key[pair]):
|
||||
print(" ⛔ РАСШИФРОВКА НЕОДНОЗНАЧНА И НЕ ВЫВОДИТСЯ. У отрывков РАЗНЫЕ раскладки")
|
||||
print(" меток, а порядок дан ОДИН сводный: одна и та же метка означает в них")
|
||||
print(" разные армы. Это дефект СБОРКИ ПАКЕТА, а не ответа читателя.")
|
||||
print(" Что уцелело — устойчивое к раскладке: какие метки читатель поставил")
|
||||
print(" РЯДОМ или связал знаком «=». Ниже только это.")
|
||||
for j, meta in key[pair].items():
|
||||
if j not in got:
|
||||
continue
|
||||
r = _ranks(got[j])
|
||||
pairs = [(a, b) for a in r for b in r if a < b and abs(r[a] - r[b]) <= 1]
|
||||
for a, b in pairs:
|
||||
arms = {meta["метки"].get(a), meta["метки"].get(b)}
|
||||
print(f" {a} и {b} рядом → в этом отрывке это {' и '.join(sorted(x for x in arms if x))}")
|
||||
continue
|
||||
for j, meta in key[pair].items():
|
||||
if j not in got:
|
||||
continue
|
||||
r = _ranks(got[j])
|
||||
rows = [(meta["метки"][lab], r[lab]) for lab in meta["метки"] if lab in r]
|
||||
print(f" отрывок {j} (глава {meta['uid']}):")
|
||||
for arm, rank in sorted(rows, key=lambda x: x[1]):
|
||||
errs = [ZS._carry(ax) for (pp, u, aa, _h), ax in sc.items()
|
||||
if pp == pair and aa == arm and u == meta["uid"]]
|
||||
e = f"{st.mean(errs):.1f}" if errs else "—"
|
||||
print(f" {rank:.1f} {arm:4s} ошибок по судье {e}")
|
||||
d = {a: rk for a, rk in rows}
|
||||
if "Z0" in d and "ZF" in d:
|
||||
gap = abs(d["Z0"] - d["ZF"])
|
||||
print(f" КОНТРОЛЬ ШУМА: две генерации одной связки разведены на {gap:.1f} "
|
||||
f"позиции → {'порядок несёт сигнал' if gap <= 1 else 'ПОРЯДОК = ШУМ'}")
|
||||
return 0
|
||||
|
||||
|
||||
def selftest() -> int:
|
||||
fails = []
|
||||
ok = lambda c, m: fails.append(m) if not c else None # noqa: E731
|
||||
|
|
@ -351,8 +590,11 @@ def selftest() -> int:
|
|||
ok(r == {"Т3": 1.0, "Т1": 2.5, "Т5": 2.5, "Т2": 4.0}, f"разбор порядка сломан: {r}")
|
||||
ok(abs(_spearman([1, 2, 3], [1, 2, 3]) - 1.0) < 1e-9, "Спирмен не даёт +1 на совпадении")
|
||||
ok(abs(_spearman([1, 2, 3], [3, 2, 1]) + 1.0) < 1e-9, "Спирмен не даёт −1 на инверсии")
|
||||
m = _ORD.search("ПОРЯДОК 2: Т3 > Т1 > Т5")
|
||||
ok(bool(m) and m.group(1) == "2", "строка порядка не находится")
|
||||
ok(orders("ПОРЯДОК 2: Т3 > Т1 > Т5") == {"2": "Т3 > Т1 > Т5"}, "нумерованный порядок")
|
||||
ok(list(orders("## Порядок читаемости\n\nТ3 > Т1 > Т2 > Т4").values()) == ["Т3 > Т1 > Т2 > Т4"],
|
||||
"ЧЕЛОВЕЧЕСКАЯ форма ответа без номера не читается — на ней уже терялась работа владельца")
|
||||
ok(list(orders("**ПОРЯДОК ЧИТАЕМОСТИ:** `Т1 > Т4 = Т3 > Т2`").values()) == ["Т1 > Т4 = Т3 > Т2"],
|
||||
"порядок в markdown-разметке не читается")
|
||||
for pair in Z.PAIRS:
|
||||
u = pick(pair)
|
||||
ok(len(u) == N_UNITS, f"{pair}: единиц {len(u)}, ожидалось {N_UNITS}")
|
||||
|
|
@ -375,6 +617,8 @@ def selftest() -> int:
|
|||
if __name__ == "__main__":
|
||||
a = sys.argv[1:] or ["--selftest"]
|
||||
fn = {"--emit": emit, "--score": lambda: sys.exit(score()),
|
||||
"--emit-owner": lambda: emit_owner([x for x in a[1:] if x in ("zh", "en")] or None),
|
||||
"--score-owner": lambda: sys.exit(score_owner()),
|
||||
"--score-calib": lambda: sys.exit(score_calib()),
|
||||
"--selftest": lambda: sys.exit(selftest())}.get(a[0])
|
||||
if not fn:
|
||||
|
|
|
|||
|
|
@ -167,9 +167,15 @@ def cmd_status() -> None:
|
|||
fail = [a for a in at if not a.get("ok")]
|
||||
print(f"Д1 · собрано {len(have)}/{len(us)} · порог заказа ≥{N_ENOUGH}")
|
||||
print(f" попыток всего {len(at)} · удачных {len(ok)} · отказов {len(fail)}")
|
||||
if at:
|
||||
span = (max(a.get("ts", 0) for a in at) - min(a.get("ts", 0) for a in at)) / 3600
|
||||
print(f" окно журнала: {span:.1f} ч (заказ требует ≥24 ч до вердикта «не отдаёт»)")
|
||||
# ⚠ Окно считается ТОЛЬКО по записям с меткой времени. Записи первой редакции её не несли,
|
||||
# и `.get("ts", 0)` давал минимум 0 — то есть отсчёт от эпохи и «окно 496 315 часов».
|
||||
# Число выглядело солидно и означало ровно ничего.
|
||||
ts = [a["ts"] for a in at if a.get("ts")]
|
||||
if len(ts) >= 2:
|
||||
print(f" окно журнала: {(max(ts) - min(ts)) / 3600:.2f} ч по {len(ts)} записям с меткой "
|
||||
f"(заказ требует ≥24 ч ДО вердикта «не отдаёт»)")
|
||||
elif at:
|
||||
print(f" окно журнала: записей с меткой времени {len(ts)} — окно не доказуемо")
|
||||
codes: dict = {}
|
||||
for a in fail:
|
||||
codes[str(a.get("code"))] = codes.get(str(a.get("code")), 0) + 1
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
и запрет механизирован тем, что пороги считаются ДО контрастов и печатаются рядом с ними.
|
||||
|
||||
Запуск: itog_d4.py [--gates] # только гейты, без боевых чисел
|
||||
itog_d4.py --sens # Г5: чувствительность вердикта H-1 к составу пачек ($0)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -107,17 +108,47 @@ def variant(arm: str, u: dict, half: int) -> str:
|
|||
if arm == "CTRLfloor":
|
||||
return S.floor_pair(u, half)
|
||||
if arm == "CTRLdecoy":
|
||||
return AJ._plant(S.C.base_a0(u["uid"]))[0] # noqa: SLF001
|
||||
return AJ._plant(S.C.base_a0(u["uid"]))[0]
|
||||
return S.text_of(arm, u)
|
||||
|
||||
|
||||
# ⚠ БАЗА АРМА — то, поверх чего он работает и что уже оплачено к моменту его вызова. Держится
|
||||
# ДАННЫМИ, потому что состав панели у каждой оси свой; числа — медианы по сырью паков 22/23.
|
||||
BASE_COST_BY_AXIS = {
|
||||
"d4": {"A0": 0.00603, "A1": 0.00096, "A1B": 0.00096, "A3": 0.00096, "A2": 0.0,
|
||||
"A4": 0.00603, "A6": 0.00387, "A6F": 0.00387},
|
||||
"d3": {"E0": 0.00096, "E1": 0.00096, "E3": 0.00096, "E4": 0.00096 + 0.00789,
|
||||
"E2": 0.0, "E6": 0.00096, "D0": 0.0},
|
||||
"d6": {"J0": 0.00096, "J2": 0.0, "D0": 0.0},
|
||||
"d1": {"A0": 0.00603, "R1": 0.00096},
|
||||
}
|
||||
|
||||
|
||||
def _keys_for(root: Path) -> Path:
|
||||
"""Каталог ключей ЭТОГО семейства. Ключи разведены (`sud.py`), потому что эмиссия пачек
|
||||
одного семейства переписывала бы раскладку другого. Свод обязан читать ответы тем же ключом,
|
||||
каким они выпускались, иначе метки сопоставятся с ЧУЖИМИ армами и разбор даст 0 меток —
|
||||
ровно это и случилось на первом прогоне семейства Sol."""
|
||||
fam = "claude" if root.name.startswith("sud-") else root.name.split("-")[0]
|
||||
return KEYS if fam == "claude" else KEYS.parent / f"{KEYS.name}-{fam}"
|
||||
if fam == "claude":
|
||||
return KEYS
|
||||
# ⚠ Ключи разведены по семействам НЕ на всех осях: разводить их начали на Д1/Д3/Д6, после
|
||||
# аварии с пере-эмиссией, а китайская ось выпускала ОБА семейства одним ключом. Жёсткое
|
||||
# `-{fam}` роняло свод на sol-d4-work («62 ответа, разобрано 0 меток»), то есть заявленный
|
||||
# носитель чисел Д4 не воспроизводил ось вовсе. Разведённый ключ — если он есть; иначе общий.
|
||||
split = KEYS.parent / f"{KEYS.name}-{fam}"
|
||||
return split if split.exists() else KEYS
|
||||
|
||||
|
||||
# ⚠ СОСТАВ ПАЧЕК — ПАРАМЕТР ТОЛЬКО ДЛЯ ЧУВСТВИТЕЛЬНОСТИ, И ОБА СПИСКА ПУСТЫ ПО УМОЛЧАНИЮ.
|
||||
# Печатаемый вердикт при этом не меняется ни на знак: `--drop`/`--restore` существуют, чтобы
|
||||
# пере-считать его ПРИ ДРУГОМ составе и напечатать разницу рядом, а не чтобы подобрать удобный.
|
||||
# Заведены под Г5 (находка ревизии P13: снятие двух пачек прогона-валидации 11.08 переворачивает
|
||||
# решение по H-1). Без такого рычага пере-счёт делался копией дерева — то есть числами, которые
|
||||
# никто не может воспроизвести.
|
||||
DROP = set(next((a.split("=", 1)[1] for a in sys.argv if a.startswith("--drop=")), "").split(",")) - {""}
|
||||
RESTORE = set(next((a.split("=", 1)[1] for a in sys.argv
|
||||
if a.startswith("--restore=")), "").split(",")) - {""}
|
||||
|
||||
|
||||
def read_family(root: Path) -> tuple[dict, list]:
|
||||
|
|
@ -134,9 +165,19 @@ def read_family(root: Path) -> tuple[dict, list]:
|
|||
continue
|
||||
key_all = json.loads(kf.read_text(encoding="utf-8"))
|
||||
for f in sorted((root / f"{half}-answers").glob("*.txt")):
|
||||
tok = f.name.split(".")[0]
|
||||
if ".ANNULLED" in f.name:
|
||||
if tok not in RESTORE:
|
||||
continue
|
||||
# ⚠ Пачка, отправленная в карантин и ПЕРЕ-СУЖЕННАЯ, лежит на диске дважды.
|
||||
# Вернуть карантинную копию значит посчитать единицу дважды разными судьями —
|
||||
# молча, потому что ключ у обеих один. Возвращать можно только то, чему замены нет.
|
||||
if (f.parent / f"{tok}.txt").exists():
|
||||
raise SystemExit(f"⛔ {tok} пере-суждена: возврат карантинной копии удвоил бы "
|
||||
f"единицу; --restore допустим только для пачек без замены")
|
||||
if tok in DROP:
|
||||
continue
|
||||
key = key_all.get(f.stem)
|
||||
key = key_all.get(tok)
|
||||
if not key:
|
||||
continue
|
||||
txt = f.read_text(encoding="utf-8", errors="replace")
|
||||
|
|
@ -146,13 +187,13 @@ def read_family(root: Path) -> tuple[dict, list]:
|
|||
# ⚠ `[ \t]*`, а не `\s*`: `\s` включает перевод строки, и оценка
|
||||
# могла считаться со СЛЕДУЮЩЕЙ строки. Тот же класс `absjudge.py:351`
|
||||
# чинил у себя; здесь он оставался.
|
||||
m = re.search(rf"^{lab}-{a}:[ \t]*(\d+)", txt, re.M)
|
||||
m = re.search(rf"^{lab}-{a}:[ \t]*(\d+)", txt, re.MULTILINE)
|
||||
if m:
|
||||
sc[a] = int(m.group(1))
|
||||
if len(sc) < 4:
|
||||
bad.append((root.name, half, f.stem, lab, "не все оси"))
|
||||
continue
|
||||
w = re.search(rf"^{lab}-ПОЧЕМУ:\s*(.*)$", txt, re.M)
|
||||
w = re.search(rf"^{lab}-ПОЧЕМУ:\s*(.*)$", txt, re.MULTILINE)
|
||||
why = w.group(1) if w else ""
|
||||
if sum(sc.values()) and not why.strip():
|
||||
bad.append((root.name, half, f.stem, lab, "ненулевая без обоснования"))
|
||||
|
|
@ -221,16 +262,16 @@ def three_axes(d: dict) -> None:
|
|||
⚠ Канон считается на ВЫХОДЕ арма, а не на его входе: вход мерился при покупке, а сверяется
|
||||
качество результата. Цена — медиана оплаченной клетки арма из сырья, а не из сметы.
|
||||
"""
|
||||
import json as _j # noqa: PLC0415
|
||||
import statistics as _st # noqa: PLC0415
|
||||
import json as _j
|
||||
import statistics as _st
|
||||
us = {x["uid"]: x for x in S.units()}
|
||||
print("\n" + "=" * 78)
|
||||
print("ТРИ ОСИ ПО КАЖДОМУ АРМУ (требование заказа :95) — судья · канон · цена")
|
||||
print("=" * 78)
|
||||
print(f" {'арм':5s}{'судья':>8s}{'канон':>9s}{'$/клетка':>11s} что это")
|
||||
print(f" {'арм':5s}{'судья':>8s}{'канон':>9s}{'$/клетка':>11s}{'$/единица':>12s} что это")
|
||||
what = WHAT_AXIS[AXIS]
|
||||
_unused = {"A0": "", "A4": "",
|
||||
"A6": "", "A6F": "",
|
||||
_dead = {
|
||||
"A0": "", "A4": "", "A6": "", "A6F": "",
|
||||
"A3": "черновик + смысловой критик", "A2": "однопроходка уравненного мандата",
|
||||
"A1B": "черновик + флаги (батарея+канон)", "A1": "черновик + только канон-флаги"}
|
||||
for arm in ALL_ARMS:
|
||||
|
|
@ -244,20 +285,227 @@ def three_axes(d: dict) -> None:
|
|||
k, n = S.C.BANK.coverage(u["source"], t)
|
||||
if n:
|
||||
cov.append(k / n)
|
||||
pr = []
|
||||
pr, per_unit = [], {}
|
||||
# ⚠ Глоб по тегу арма захватывал и клетки КРИТИКА (`dv-a-a3-crit-*`), поэтому
|
||||
# медиана «$/клетка» у A3 и A6 считалась по смеси двух ролей. Найдено приёмкой.
|
||||
for f in S.C.OUT.glob(f"{S.C.tag(arm, '')}*.json"):
|
||||
if any(x in f.name for x in (".ERROR", ".LENGTH", ".FLAGSV1", "-crit-")):
|
||||
# ⚠ Суффиксы карантина перечислены ПОЛНОСТЬЮ. `.STALE` и `.TWIN` заведены фазой Д на ходу
|
||||
# (устаревшие бесплатные клетки арма и побайтные близнецы пола), и их отсутствие здесь
|
||||
# тянуло медиану цены вниз нулевыми клетками. Найдено чтением собственных коммитов.
|
||||
if any(x in f.name for x in (".ERROR", ".LENGTH", ".FLAGSV1", ".STALE", ".TWIN")):
|
||||
continue
|
||||
r = _j.loads(f.read_text(encoding="utf-8"))
|
||||
if r.get("cost_usd"):
|
||||
pr.append(r["cost_usd"])
|
||||
c = r.get("cost_usd") or 0
|
||||
# ⚠ ДВЕ РАЗНЫЕ ВЕЛИЧИНЫ, И ПУТАТЬ ИХ ДОРОГО. «$/клетка» — цена ОДНОГО вызова;
|
||||
# «$/единица» — цена всей работы арма над единицей ПЛЮС база, на которой он стоит.
|
||||
# У армов с двумя вызовами (критик + фиксер) это отличается втрое: у A3 клетка
|
||||
# фиксера 0.00644, а единица — 0.02014 плюс черновик. Отчёт печатал первую цифру
|
||||
# в колонке, по которой сравнивают экономику, и ставка H-2б выглядела средней
|
||||
# по цене, будучи самой дорогой в панели.
|
||||
if "-crit-" not in f.name and c:
|
||||
pr.append(c)
|
||||
uid = r.get("uid") or f.stem.split("-")[-1]
|
||||
per_unit[uid] = per_unit.get(uid, 0.0) + c
|
||||
base = BASE_COST_BY_AXIS.get(AXIS, {}).get(arm, 0.0)
|
||||
full = (_st.median([v + base for v in per_unit.values() if v > 0])
|
||||
if any(v > 0 for v in per_unit.values()) else base)
|
||||
print(f" {arm:5s}{_st.mean(sc):8.2f}{(_st.mean(cov) if cov else 0):9.3f}"
|
||||
f"{(_st.median(pr) if pr else 0):11.5f} {what.get(arm, '')}")
|
||||
f"{(_st.median(pr) if pr else 0):11.5f}{full:12.5f} {what.get(arm, '')}")
|
||||
print(" судья — ошибок на единицу (ВЕРНОСТЬ+ЯЗЫК), МЕНЬШЕ лучше")
|
||||
print(" канон — доля покрытия банка на ВЫХОДЕ арма, БОЛЬШЕ лучше")
|
||||
print(" цена — медиана оплаченной клетки арма по сырью; A0 куплен прошлыми паками")
|
||||
print(" $/клетка — медиана ОДНОГО оплаченного вызова арма")
|
||||
print(" $/единица — вся работа арма над единицей ПЛЮС база, на которой он стоит:")
|
||||
_b = BASE_COST_BY_AXIS.get(AXIS, {})
|
||||
print(f" {' · '.join(f'{a}+{c:.5f}' for a, c in sorted(_b.items()) if c)}")
|
||||
print(" (замер по сырью: черновик flash 0.00096, перепис dspro 0.00507)")
|
||||
|
||||
|
||||
def margins_half(d: dict, a: str, b: str) -> list[float]:
|
||||
"""Перевес ЗА арм `a`, посчитанный ВНУТРИ половины и лишь потом усреднённый по единице.
|
||||
|
||||
⚠ Зачем вторая формула рядом со штатной. Наборы p1/p2 судятся РАЗНЫМИ сессиями, и между ними
|
||||
намерен систематический сдвиг строгости (находка ревизии P07: пол claude даёт +1.8 ошибки в
|
||||
пользу p2). Штатная `margins` берёт среднее арма по тем половинам, где он ЕСТЬ, — а половины
|
||||
у армов заполнены не одинаково (`A6` 15/16 против `A0` 30/31). Там, где арм присутствует
|
||||
только в одной половине, в его число входит уровень ЭТОЙ сессии, и разность двух армов несёт
|
||||
кусок межсессионного сдвига как будто это разница армов. Здесь разность берётся там, где оба
|
||||
арма судила ОДНА сессия, поэтому уровень сессии сокращается тождественно.
|
||||
"""
|
||||
out = []
|
||||
for uid in sorted({k[0] for k in d}):
|
||||
per = [d[(uid, b, h)]["carry"] - d[(uid, a, h)]["carry"]
|
||||
for h in ("p1", "p2") if (uid, a, h) in d and (uid, b, h) in d]
|
||||
if per:
|
||||
out.append(st.mean(per))
|
||||
return out
|
||||
|
||||
|
||||
def _levels(d: dict) -> dict:
|
||||
"""Средний счёт боевых армов по СЕССИЯМ судейского журнала: {(проход, судья): (n, среднее)}.
|
||||
|
||||
⚠ Это единственный оставшийся способ проверить посылку P13. Находка называла пачки
|
||||
`0dce349331`/`0ffee70740` «другим режимом замера» на двух основаниях: не замороженный промт и
|
||||
уровень счёта 9.94 против 6.39 у основной волны. Транскрипта сессии на диске больше нет
|
||||
(проверено `find` по всем каталогам агентов) — промт сверить не с чем. Уровень сверить можно,
|
||||
и вопрос ставится честно: выбивается ли д-01 из РАЗБРОСА ОСТАЛЬНЫХ СЕССИЙ или такой шаг между
|
||||
сессиями у этой оси обычный. Во втором случае снятие двух пачек — не норма, а выбор.
|
||||
"""
|
||||
led = json.loads((Path.home() / "books" / "dovodka" / "agent-runs.json").read_text("utf-8"))
|
||||
battle = set(ALL_ARMS)
|
||||
out: dict = {}
|
||||
for r in led:
|
||||
if r.get("run") != AXIS:
|
||||
continue
|
||||
half = r.get("pass_")
|
||||
sc = [v["carry"] for (uid, arm, h), v in d.items()
|
||||
if h == half and arm in battle and uid in _uids_of(r, half)]
|
||||
if sc:
|
||||
out[(half, r["judge"], tuple(r["tokens"]))] = (len(sc), st.mean(sc))
|
||||
return out
|
||||
|
||||
|
||||
def _uids_of(run: dict, half: str) -> set:
|
||||
"""uid единиц, которые судила эта сессия. Пачка названа ТОКЕНОМ, ключ переводит его в uid."""
|
||||
kf = KEYS / f"{AXIS}{half}-KEY.json"
|
||||
if not kf.exists():
|
||||
return set()
|
||||
key_all = json.loads(kf.read_text(encoding="utf-8"))
|
||||
out = set()
|
||||
for tok in run.get("tokens", []):
|
||||
for meta in (key_all.get(tok) or {}).values():
|
||||
out.add(meta["uid"])
|
||||
return out
|
||||
|
||||
|
||||
def sens() -> int:
|
||||
"""Г5 — ЧУВСТВИТЕЛЬНОСТЬ ВЕРДИКТА H-1 К СОСТАВУ ПАЧЕК. $0, ни одного вызова к моделям.
|
||||
|
||||
⚠ ПРАВИЛО РЕШЕНИЯ ОБЪЯВЛЕНО ДО ПРОГОНА (иначе это подбор сценария, а не замер):
|
||||
вердикт по H-1 считается ПЕРЕ-РЕШЁННЫМ, только если он одинаков во ВСЕХ сценариях, снятие
|
||||
в которых обосновано нормой проекта. Если знак решения зависит от выбора сценария — печатается
|
||||
именно это, а не удобная половина. Запас над порогом печатается числом рядом: решение,
|
||||
выигранное с запасом меньше собственного шага шкалы (1 ошибка), вердиктом не является.
|
||||
"""
|
||||
print("=" * 78)
|
||||
print("Г5 — ЧУВСТВИТЕЛЬНОСТЬ H-1 К СОСТАВУ ПАЧЕК ($0, пере-анализ уже купленных ответов)")
|
||||
print("=" * 78)
|
||||
print(" правило решения объявлено в докстринге ДО прогона: вердикт пере-решён только при")
|
||||
print(" совпадении во ВСЕХ обоснованных сценариях; запас печатается числом\n")
|
||||
global DROP, RESTORE # состав пачек — параметр сценария, см. их объявление
|
||||
grid = [
|
||||
("S0 дерево как есть (что печатает свод)", set(), set()),
|
||||
("S1 −валидация 11.08 (P13)", {"0dce349331", "0ffee70740"}, set()),
|
||||
("S2 +69de717f3f назад (база ревизии)", set(), {"69de717f3f"}),
|
||||
("S3 база ревизии −валидация (счёт P13)", {"0dce349331", "0ffee70740"}, {"69de717f3f"}),
|
||||
]
|
||||
keep = (DROP, RESTORE)
|
||||
rows = []
|
||||
for name, drop, restore in grid:
|
||||
DROP, RESTORE = drop, restore
|
||||
d, _bad = read_family(FAMILIES["claude"])
|
||||
sd, n = floor_sd(d)
|
||||
thr = 2.8 * sd / (n ** 0.5) if n > 1 else float("nan")
|
||||
line = {"name": name, "sd": sd, "n": n, "thr": thr, "d": d}
|
||||
for a, b, _w in PRIMARY:
|
||||
m = margins(d, a, b)
|
||||
mh = margins_half(d, a, b)
|
||||
line[a] = (len(m), st.mean(m), BO.sign_perm_p(m) if len(m) > 2 else float("nan"),
|
||||
len(mh), st.mean(mh) if mh else float("nan"),
|
||||
BO.sign_perm_p(mh) if len(mh) > 2 else float("nan"))
|
||||
rows.append(line)
|
||||
DROP, RESTORE = keep
|
||||
for a, b, what in PRIMARY:
|
||||
print(f"### КОНТРАСТ {a}/{b} — {what}")
|
||||
print(f" {'сценарий':40s}{'пол n':>6s}{'порог':>7s}{'перевес':>9s}"
|
||||
f"{'запас':>7s}{'p Холма*':>9s} вердикт")
|
||||
for r in rows:
|
||||
mean = r[a][1]
|
||||
gap = abs(mean) - r["thr"]
|
||||
adj = holm([r[x][2] if r[x][2] == r[x][2] else 1.0 for x, _y, _z in PRIMARY])
|
||||
pa = adj[[x for x, _y, _z in PRIMARY].index(a)]
|
||||
ok = gap > 0 and pa < 0.05
|
||||
print(f" {r['name']:40s}{r['n']:6d}{r['thr']:7.2f}{mean:+9.2f}{gap:+7.2f}"
|
||||
f"{pa:9.4f} {'ПЕРЕШЁЛ' if ok else 'ниже порога'}")
|
||||
print(f" {'—— тот же контраст ВНУТРИ половины (P07)':40s}")
|
||||
for r in rows:
|
||||
_k, _m, _p, kh, meanh, ph = r[a]
|
||||
gap = abs(meanh) - r["thr"]
|
||||
print(f" {r['name']:40s}{r['n']:6d}{r['thr']:7.2f}{meanh:+9.2f}{gap:+7.2f}"
|
||||
f"{ph:9.4f} {'ПЕРЕШЁЛ' if gap > 0 and ph < 0.05 else 'ниже порога'} ед.{kh}")
|
||||
print()
|
||||
print(" * Холм считается ПО ТРЁМ контрастам внутри своего сценария, как в пре-реге\n")
|
||||
print("=" * 78)
|
||||
print("УРОВЕНЬ СУДЕЙСКИХ СЕССИЙ — выбивается ли прогон-валидация из разброса остальных")
|
||||
print("=" * 78)
|
||||
lv = _levels(rows[0]["d"])
|
||||
for half in ("p1", "p2"):
|
||||
xs = [(j, n, m, t) for (h, j, t), (n, m) in lv.items() if h == half]
|
||||
if not xs:
|
||||
continue
|
||||
vals = [m for _j, _n, m, _t in xs]
|
||||
print(f" {half}: сессий {len(xs)} · среднее {st.mean(vals):.2f} · "
|
||||
f"разброс {min(vals):.2f}…{max(vals):.2f}"
|
||||
+ (f" · sd {st.pstdev(vals):.2f}" if len(vals) > 1 else ""))
|
||||
for j, n, m, t in sorted(xs, key=lambda x: -x[2]):
|
||||
mark = " ← прогон-валидация 11.08" if "0dce349331" in t else ""
|
||||
print(f" {j:8s} клеток {n:3d} ошибок/клетку {m:6.2f}{mark}")
|
||||
print("\n" + "=" * 78)
|
||||
print("СИСТЕМАТИЧЕСКИЙ СДВИГ НАБОРОВ (P07) — на нём стоит вся формула порога")
|
||||
print("=" * 78)
|
||||
d0 = rows[0]["d"]
|
||||
both = [(v["carry"] - d0[(u, a, "p2")]["carry"])
|
||||
for (u, a, h), v in d0.items() if h == "p1" and (u, a, "p2") in d0 and a in ALL_ARMS]
|
||||
fl = [d0[(u, "CTRLfloor", "p1")]["carry"] - d0[(u, "CTRLfloor", "p2")]["carry"]
|
||||
for u in {k[0] for k in d0}
|
||||
if (u, "CTRLfloor", "p1") in d0 and (u, "CTRLfloor", "p2") in d0]
|
||||
print(f" боевые армы, одна единица в обеих половинах: n={len(both)} "
|
||||
f"сдвиг p1−p2 {st.mean(both):+.2f} · sd {st.pstdev(both):.2f}")
|
||||
print(f" пол (те же половины): n={len(fl)} сдвиг {st.mean(fl):+.2f} · sd {st.pstdev(fl):.2f} "
|
||||
f"· p={BO.sign_perm_p(fl):.4f}")
|
||||
print(" ⇒ если сдвиг пола ≈ сдвиг боевых армов, пол меряет РАЗНИЦУ СЕССИЙ, а не шум оценки;")
|
||||
print(" сбалансированный по половинам контраст этот сдвиг сокращает, несбалансированный — нет")
|
||||
print("\n БАЛАНС ПОЛОВИН ПО АРМАМ (несбалансированный арм несёт уровень своей сессии):")
|
||||
for arm in ALL_ARMS:
|
||||
n1 = sum(1 for (u, a, h) in d0 if a == arm and h == "p1")
|
||||
n2 = sum(1 for (u, a, h) in d0 if a == arm and h == "p2")
|
||||
print(f" {arm:5s} p1 {n1:3d} · p2 {n2:3d}" + (" ⚠ перекос" if abs(n1 - n2) > 1 else ""))
|
||||
# ⚠ КАЛИБРОВКА ПОРОГА ПО ЗНАКУ (находка ревизии №6: R73 против P40). Две выжившие находки
|
||||
# называли порог смещённым в ПРОТИВОПОЛОЖНЫЕ стороны, и ни одна не проверялась на ЭТОЙ оси:
|
||||
# R73 мерила на проходе `border` (контраст в 1.40× шумнее пола), P40 рассуждала. Здесь то же
|
||||
# меряется прямо: шум САМОГО контраста той же структуры — перевес, посчитанный в p1, против
|
||||
# перевеса в p2. Если он равен полу, порог откалиброван; больше — вердикты смелее данных;
|
||||
# меньше — прибор строже, чем нужно.
|
||||
sd0, n0 = floor_sd(d0)
|
||||
print("\n" + "=" * 78)
|
||||
print("КАЛИБРОВКА ПОРОГА ПО ЗНАКУ — пол против ШУМА САМОГО КОНТРАСТА (ревизия №6)")
|
||||
print("=" * 78)
|
||||
print(f" пол, из которого строится порог: n={n0} sd={sd0:.4f} → порог {2.8 * sd0 / n0**0.5:.4f}")
|
||||
for a, b, _w in PRIMARY:
|
||||
xs = []
|
||||
for u in {k[0] for k in d0}:
|
||||
if all((u, x, h) in d0 for x in (a, b) for h in ("p1", "p2")):
|
||||
xs.append((d0[(u, b, "p1")]["carry"] - d0[(u, a, "p1")]["carry"])
|
||||
- (d0[(u, b, "p2")]["carry"] - d0[(u, a, "p2")]["carry"]))
|
||||
if len(xs) < 3:
|
||||
continue
|
||||
sd = st.pstdev(xs)
|
||||
own = 2.8 * sd / len(xs) ** 0.5
|
||||
m = margins(d0, a, b)
|
||||
print(f" {a}/{b}: n={len(xs):2d} sd={sd:.4f} ({sd / sd0:.2f}× пола) → СВОЙ порог {own:.4f}"
|
||||
f" · перевес {st.mean(m):+.4f} · запас {abs(st.mean(m)) - own:+.4f}")
|
||||
print(" ⚠ «свой порог» — не замена объявленному: пре-рег зафризил формулу ПО ПОЛУ, и менять её")
|
||||
print(" после взгляда на вердикты нельзя. Это проверка КАЛИБРОВКИ, а не второе решающее правило.")
|
||||
# ⚠ Вторая сторона той же калибровки: пол строится на ДВУХ ГЕНЕРАЦИЯХ, то есть несёт и шум
|
||||
# порождения, и шум судьи. Чистый шум судьи виден там, где текст ОДИН И ТОТ ЖЕ, а сессии
|
||||
# разные — на боевых армах, которые лежат в обеих половинах. Если он БОЛЬШЕ пола, порог занижен.
|
||||
print("\n ЧИСТЫЙ ШУМ СУДЬИ (ОДИН И ТОТ ЖЕ текст, две сессии) против пола:")
|
||||
for arm in ALL_ARMS:
|
||||
xs = [d0[(u, arm, "p1")]["carry"] - d0[(u, arm, "p2")]["carry"] for u in {k[0] for k in d0}
|
||||
if (u, arm, "p1") in d0 and (u, arm, "p2") in d0]
|
||||
if len(xs) > 2:
|
||||
print(f" {arm:5s} n={len(xs):2d} среднее {st.mean(xs):+.2f} sd={st.pstdev(xs):.4f}"
|
||||
f" ({st.pstdev(xs) / sd0:.2f}× пола)")
|
||||
return 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
|
|
@ -350,4 +598,4 @@ def main() -> int:
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
sys.exit(sens() if "--sens" in sys.argv else main())
|
||||
|
|
|
|||
505
eval/dovodka/ja6.py
Normal file
505
eval/dovodka/ja6.py
Normal file
|
|
@ -0,0 +1,505 @@
|
|||
#!/usr/bin/env python3
|
||||
"""ЯПОНСКАЯ НОГА H-4 — слепой проход `J0` против `J6`. $0 (агент-сессии + харнесс владельца).
|
||||
|
||||
Гипотеза H-4: перевес `deepseek-v4-pro` в редакторской роли есть свойство пары zh→ru, и на другой
|
||||
паре ПОРЯДОК ЖИЛЬЦОВ может смениться. Порядок нельзя увидеть, имея на паре одного редактора: ось
|
||||
Д6 меряла «покупает ли редактор что-нибудь вообще», а не «какой редактор лучше». На zh панель есть
|
||||
(эксп-22), на en второй редактор куплен (`E6`), на ja его не было — требование заказа (:115)
|
||||
печатать сравнение порядка между zh/en/ja поэтому не исполнялось.
|
||||
|
||||
⚠ СТАТУС РЕЗУЛЬТАТА — ОПИСАТЕЛЬНЫЙ, и это не смягчается никаким исходом: ось объявлена
|
||||
разведочной ДО денег (`power.FORCED_DESCRIPTIVE`, n=9, MDE 2.90 против цели 2.00).
|
||||
|
||||
⚠ ЧЕМУ НАУЧИЛ `tier` И ЧТО ЗДЕСЬ СДЕЛАНО ИНАЧЕ (пре-регистрируется ДО первого ответа):
|
||||
1. ПОЛОВИНА ПОЛА НЕ ЯВЛЯЕТСЯ БОЕВЫМ АРМОМ. В паке 23 `CTRLfloorA` побайтно равнялся арму `T1`
|
||||
в 16/16 — контроль сравнивал `T1` сам с собой. Здесь пара пола — две генерации БОЕВОГО
|
||||
РЕДАКТОРА (`J0` и `JFLO`), и они РАЗВЕДЕНЫ ПО НАБОРАМ: `J0` живёт в наборе p1, `JFLO` — в p2.
|
||||
Судья никогда не видит обе половины в одной пачке, а порог несёт межсессионную компоненту.
|
||||
2. ПОЛ СНЯТ С ТОЙ ОПЕРАЦИИ, КОТОРАЯ В ПАНЕЛИ. Пол оси Д6 снят с точечного фиксера — операции
|
||||
почти детерминированной (4 побайтных близнеца из 8 пар). Панель здесь — панель РЕДАКТОРОВ,
|
||||
поэтому и шум мерится повторной генерацией редактора.
|
||||
3. ДОНОР ДЕКОЯ УРАВНЕН: в паке 23 донором во всех 16 единицах был `R0`, то есть чувствительность
|
||||
сертифицировалась в окрестности одного арма. Здесь донор чередуется `J0`/`J6` по чётности.
|
||||
4. МАРЖЕВЫЙ ДЕКОЙ ЕСТЬ (норма П-2) — без него «не различимо» неотличимо от слепоты прибора.
|
||||
5а. ⚠ ОГРАНИЧЕНИЕ ГЕЙТА ЧУВСТВИТЕЛЬНОСТИ ОБЪЯВЛЯЕТСЯ ДО ПРОГОНА: маржевый декой садится лишь на
|
||||
5 единиц из 9 — регексы посадок не находят на этой книге нужных мест. Расширенный список
|
||||
(`--wide-plants`, который на другой японской выборке давал 8/9) проверен и НЕ помогает:
|
||||
те же 5 из 9. Значит гейт П-2 на этой ноге снят пятью точками, и право говорить «не хуже»
|
||||
он подпирает слабее, чем на осях с полным покрытием. Сказано ДО ответов, а не после.
|
||||
5. МОЛЧАЛИВЫЙ НОЛЬ ЗАПРЕЩЁН заданием, а рубрика несёт классы, которые пачка `tier` пропускала.
|
||||
6. КЛЮЧ СВОЙ, соль своя. Ключи осей Д6 и `tier` не трогаются: раскладка меток есть функция
|
||||
соли, uid и НАБОРА армов, и эмиссия поверх переписала бы отсуженную раскладку.
|
||||
|
||||
⚠ ПАРИТЕТ ОБВЯЗОК (норма П-4): оба семейства получают ПОБАЙТНО ОДНО задание и одни правила счёта.
|
||||
Расхождение прохода `tier` в 12 раз частично куплено тем, что харнессу Sol дали правило плотности
|
||||
(«две одинаковые ошибки — это два»), которого агенты claude не получали. Здесь это правило стоит
|
||||
в САМОМ задании, то есть достаётся обоим.
|
||||
|
||||
Запуск: ja6.py --emit | --score | --sol | --score-sol | --selftest
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import re
|
||||
import statistics as st
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
ZONE = Path(__file__).resolve().parent
|
||||
for d in ("dovodka", "editor_tier", "tenant_panel", "role_topology", "bank_arbitration"):
|
||||
sys.path.insert(0, str(REPO / "eval" / d))
|
||||
|
||||
OUT = Path.home() / "books" / "dovodka"
|
||||
WORK = Path.home() / "books" / "judging" / "ja6"
|
||||
KEYD = OUT / "blind-keys-ja6"
|
||||
AX = ("ВЕРНОСТЬ", "ТЕРМИН", "ЯЗЫК", "ФОРМА")
|
||||
CARRY = ("ВЕРНОСТЬ", "ЯЗЫК")
|
||||
SALT = "ja6-2026-08-15"
|
||||
PER_SESSION = 4
|
||||
SETS = ("p1", "p2")
|
||||
NL = chr(10) # перевод строки как имя: файл собирается генератором внутри f-строк
|
||||
|
||||
_axre = re.compile(r"^[ТT](\d+)-(ВЕРНОСТЬ|ТЕРМИН|ЯЗЫК|ФОРМА):\s*(\d+)", re.MULTILINE)
|
||||
_whyre = re.compile(r"^[ТT](\d+)-ПОЧЕМУ:(.*)$", re.MULTILINE)
|
||||
|
||||
|
||||
def _load(name: str, path: Path):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
sys.modules[name] = mod
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
def _sud():
|
||||
return sys.modules.get("sud") or _load("sud", ZONE / "sud.py")
|
||||
|
||||
|
||||
def cell(tag: str) -> str:
|
||||
f = OUT / f"{tag}.json"
|
||||
if not f.exists():
|
||||
return ""
|
||||
d = json.loads(f.read_text(encoding="utf-8"))
|
||||
return (d.get("content") or "") if d.get("finish") == "stop" else ""
|
||||
|
||||
|
||||
def units() -> list[dict]:
|
||||
MJ = sys.modules.get("mat_ja6") or _load("mat_ja6", ZONE / "material_ja.py")
|
||||
return MJ.units()
|
||||
|
||||
|
||||
def panel(u: dict, half: str, idx: int) -> dict[str, str]:
|
||||
"""Армы набора. `J0` только в p1, `JFLO` только в p2 — половины пола не встречаются в пачке."""
|
||||
S = _sud()
|
||||
uid = u["uid"]
|
||||
j0, j6, d0 = cell(f"dv-c-j0-{uid}"), cell(f"dv-h-j6-{uid}"), cell(f"dv-c-d0-{uid}")
|
||||
jflo = cell(f"dv-h-jflo-{uid}")
|
||||
base = j0 if half == "p1" else jflo
|
||||
if not (base.strip() and j6.strip() and d0.strip()):
|
||||
return {}
|
||||
donor = j0 if idx % 2 == 0 else j6 # донор декоя уравнен по армам
|
||||
out = {("J0" if half == "p1" else "JFLO"): base, "J6": j6, "D0": d0}
|
||||
dec, n_d = S.AJ._plant(donor) if hasattr(S, "AJ") else (None, 0)
|
||||
if dec and n_d >= 2:
|
||||
out["CTRLdecoy"] = dec
|
||||
mg, n_m = S.margin_plant(donor)
|
||||
if n_m >= 2:
|
||||
out["CTRLmargin"] = mg
|
||||
return out
|
||||
|
||||
|
||||
def layout(uid: str, half: str, arms) -> dict[str, str]:
|
||||
order = sorted(arms)
|
||||
seed = hashlib.sha256(f"{SALT}|{uid}|{half}|{'|'.join(order)}".encode()).hexdigest()
|
||||
rank = sorted(order, key=lambda a: hashlib.sha256(f"{seed}|{a}".encode()).hexdigest())
|
||||
return {f"Т{i}": a for i, a in enumerate(rank, 1)}
|
||||
|
||||
|
||||
HEAD = """Ты — литературный редактор-эксперт. Тебе дан ИСХОДНИК на японском и {n} независимых русских перевода этого же фрагмента под слепыми метками.
|
||||
|
||||
Посчитай ОШИБКИ в каждом варианте по типам:
|
||||
ВЕРНОСТЬ — сказано не то, что в исходнике: искажение, инверсия, выдумка, пропуск факта.
|
||||
Сюда же входят, и их пропускают чаще всего:
|
||||
· инверсия адресата или участника реплики (кто кому говорит, кто кого бьёт);
|
||||
· потеря или подмена разряда, ранга, числа, единицы измерения;
|
||||
· потеря вежливостного или social-регистра там, где он несёт смысл;
|
||||
· состояние действия: совершилось против ещё не совершилось;
|
||||
· снятое или, наоборот, добавленное отрицание.
|
||||
ТЕРМИН — имя или термин передан не так, как в других местах того же текста.
|
||||
ЯЗЫК — то, чего носитель не напишет: калька, канцелярит, несуществующее слово, кривой
|
||||
порядок слов, рассогласование.
|
||||
ФОРМА — вёрстка и пунктуация: оформление реплик, кавычки, тире, абзацы.
|
||||
|
||||
Оцени КАЖДЫЙ вариант ОТДЕЛЬНО против исходника — не сравнивая варианты между
|
||||
собой. Порядок вариантов ничего не значит и специально перемешан.
|
||||
|
||||
Числа — это СЧЁТ ошибок, а не оценка по шкале. Две одинаковые ошибки в одном варианте — это два.
|
||||
|
||||
⚠ НОЛЬ — ЭТО УТВЕРЖДЕНИЕ, А НЕ ОТСУТСТВИЕ ОТВЕТА. Ноль означает «я сверил этот вариант с
|
||||
исходником по этой оси и дефектов не нашёл». Поэтому ПОЧЕМУ обязательно для КАЖДОЙ метки, в том
|
||||
числе при нулях: если по всем осям ноль, напиши «прочитано, дефектов не найдено». Пустое ПОЧЕМУ
|
||||
не принимается и на диск не попадает.
|
||||
|
||||
Ответ ЗАПИШИ ФАЙЛОМ в {path} — ровно в таком виде и ничем больше, блок на каждую метку:
|
||||
|
||||
{skeleton}
|
||||
ПОЧЕМУ для НЕНУЛЕВОЙ оси: короткая ЦИТАТА из текста через « | ».
|
||||
Число без цитаты не принимается и на диск не попадает.
|
||||
"""
|
||||
|
||||
|
||||
def emit() -> None:
|
||||
key: dict[str, dict] = {"_проход": "ja6", "_семейство": ["J6/J0"],
|
||||
"_контроль": ["J0/D0"], "_пол": ["J0(p1) против JFLO(p2)"]}
|
||||
made = 0
|
||||
for half in SETS:
|
||||
(WORK / f"{half}-tasks").mkdir(parents=True, exist_ok=True)
|
||||
(WORK / f"{half}-answers").mkdir(parents=True, exist_ok=True)
|
||||
for idx, u in enumerate(units()):
|
||||
for half in SETS:
|
||||
texts = panel(u, half, idx)
|
||||
if not texts:
|
||||
print(f" ⚠ {u['uid']}/{half}: панель неполна, пропущено")
|
||||
continue
|
||||
lay = layout(u["uid"], half, tuple(texts))
|
||||
tok = hashlib.sha256(f"{SALT}|{u['uid']}|{half}".encode()).hexdigest()[:10]
|
||||
skel = "\n\n".join("\n".join(f"{lab}-{a}: <число>" for a in AX)
|
||||
+ f"\n{lab}-ПОЧЕМУ: <цитаты>" for lab in lay)
|
||||
body = [HEAD.format(n=len(lay),
|
||||
path=WORK / f"{half}-answers" / f"{tok}.txt", skeleton=skel),
|
||||
"", "=" * 60, "ИСХОДНИК:", u["source"]]
|
||||
for lab, arm in lay.items():
|
||||
body += ["", "=" * 60, f"{lab}:", texts[arm]]
|
||||
(WORK / f"{half}-tasks" / f"{tok}.txt").write_text("\n".join(body), encoding="utf-8")
|
||||
key[tok] = {lab: {"arm": a, "uid": u["uid"], "half": half,
|
||||
"kind": "контроль" if a.startswith("CTRL") else "боевой",
|
||||
"sig": hashlib.sha256(texts[a].encode()).hexdigest()[:12]}
|
||||
for lab, a in lay.items()}
|
||||
made += 1
|
||||
KEYD.mkdir(parents=True, exist_ok=True)
|
||||
(KEYD / "ja6-KEY.json").write_text(json.dumps(key, ensure_ascii=False, indent=1),
|
||||
encoding="utf-8")
|
||||
(KEYD / "SALT-ja6.txt").write_text(SALT + "\n", encoding="utf-8")
|
||||
toks = [t for t in key if not t.startswith("_")]
|
||||
print(f"заданий {made} → {WORK}/{{p1,p2}}-tasks")
|
||||
print(f"ключ → {KEYD} (судье НЕ давать)")
|
||||
for i in range(0, len(toks), PER_SESSION):
|
||||
print(f" сессия {i // PER_SESSION + 1}: " + " · ".join(toks[i:i + PER_SESSION]))
|
||||
|
||||
|
||||
def read(root: Path) -> tuple[dict, list]:
|
||||
key = json.loads((KEYD / "ja6-KEY.json").read_text(encoding="utf-8"))
|
||||
out, bad = {}, []
|
||||
# ⚠ Две раскладки ответов, и обе законны: своё семейство пишет в `p1-answers`/`p2-answers`
|
||||
# (наборы разведены каталогами), внешний харнесс — в ПЛОСКИЙ `answers/`, потому что его
|
||||
# разводит не каталог, а оркестраторский промт «по одному агенту на промт». Счётчик обязан
|
||||
# читать обе: первая редакция знала только про первую и на пакете Sol печатала «ответов нет».
|
||||
dirs = [root / f"{half}-answers" for half in SETS]
|
||||
if (root / "answers").exists():
|
||||
dirs.append(root / "answers")
|
||||
for d in dirs:
|
||||
if not d.exists():
|
||||
continue
|
||||
for f in sorted(d.glob("*.txt")):
|
||||
km = key.get(f.stem)
|
||||
if not isinstance(km, dict):
|
||||
continue
|
||||
t = f.read_text(encoding="utf-8", errors="replace")
|
||||
cells: dict[str, dict[str, int]] = collections.defaultdict(dict)
|
||||
for m in _axre.finditer(t):
|
||||
cells["Т" + m.group(1)][m.group(2)] = int(m.group(3))
|
||||
why = {"Т" + m.group(1): m.group(2).strip() for m in _whyre.finditer(t)}
|
||||
for lab, ax in cells.items():
|
||||
meta = km.get(lab)
|
||||
if not isinstance(meta, dict):
|
||||
continue
|
||||
if len(ax) < len(AX):
|
||||
bad.append(f"{f.stem}/{lab}: не все четыре оси")
|
||||
continue
|
||||
if not why.get(lab):
|
||||
bad.append(f"{f.stem}/{lab}: ПУСТОЕ обоснование (запрещено заданием)")
|
||||
out[(meta["uid"], meta["arm"])] = sum(ax.get(a, 0) for a in CARRY)
|
||||
return out, bad
|
||||
|
||||
|
||||
def _sign_p(diffs: list[float]) -> float:
|
||||
import math
|
||||
nz = [d for d in diffs if d != 0]
|
||||
n = len(nz)
|
||||
if not n:
|
||||
return 1.0
|
||||
k = sum(1 for d in nz if d > 0)
|
||||
tail = sum(math.comb(n, i) for i in range(min(k, n - k) + 1)) / 2 ** n
|
||||
return min(1.0, 2 * tail)
|
||||
|
||||
|
||||
def score(root: Path | None = None) -> int:
|
||||
root = root or WORK
|
||||
sc, bad = read(root)
|
||||
if not sc:
|
||||
print(f"ответов в {root} нет")
|
||||
return 1
|
||||
uids = sorted({u for u, _ in sc})
|
||||
print(f"единиц {len(uids)} · клеток {len(sc)} · замечаний годности {len(bad)}")
|
||||
for b in bad[:8]:
|
||||
print(" ⚠", b)
|
||||
vals = [v for (u, a), v in sc.items() if not a.startswith("CTRL")]
|
||||
zeros = sum(1 for v in vals if v == 0) / max(len(vals), 1)
|
||||
print(f"\nГЕЙТ ПЛОТНОСТИ: ошибок/клетку {st.mean(vals):.2f} · доля нулей {zeros:.0%} "
|
||||
f"→ {'ГОДНО' if zeros < 0.25 else '⛔ ПОЛ ШКАЛЫ'}")
|
||||
fl = [sc[(u, "J0")] - sc[(u, "JFLO")] for u in uids if (u, "J0") in sc and (u, "JFLO") in sc]
|
||||
sd = st.pstdev(fl) if len(fl) > 1 else 0.0
|
||||
thr = 2.8 * sd / max(len(fl), 1) ** 0.5 if fl else 0.0
|
||||
print(f"СВОЙ ПОЛ (две генерации редактора, половины в РАЗНЫХ наборах): пар {len(fl)} · "
|
||||
f"sd {sd:.2f} → ПОРОГ {thr:.2f}")
|
||||
for name, arm in (("ГРУБЫЙ ДЕКОЙ", "CTRLdecoy"), ("МАРЖЕВЫЙ ДЕКОЙ (гейт П-2)", "CTRLmargin")):
|
||||
d = [sc[(u, arm)] - sc[(u, "J0")] for u in uids if (u, arm) in sc and (u, "J0") in sc]
|
||||
if d:
|
||||
v = "ПРОЙДЕН" if st.mean(d) > thr else "⛔ НЕ ПРОЙДЕН"
|
||||
print(f"{name}: n={len(d)} среднее {st.mean(d):+.2f} против порога {thr:.2f} → {v}")
|
||||
print(f"\n{'контраст':14s}{'ед.':>5s}{'перевес':>9s}{'p':>9s} вердикт")
|
||||
for a, b in (("J6", "J0"), ("J0", "D0")):
|
||||
d = [sc[(u, b)] - sc[(u, a)] for u in uids if (u, a) in sc and (u, b) in sc]
|
||||
if not d:
|
||||
continue
|
||||
m = st.mean(d)
|
||||
v = "ПЕРЕШЁЛ ПОРОГ (описательно)" if abs(m) > thr else "ниже порога"
|
||||
print(f"{a + ' vs ' + b:14s}{len(d):5d}{m:+9.2f}{_sign_p(d):9.4f} {v}")
|
||||
print(f"\n{'арм':10s}{'ошибок/ед.':>11s}")
|
||||
for arm in ("J0", "J6", "D0", "JFLO", "CTRLdecoy", "CTRLmargin"):
|
||||
v = [sc[(u, arm)] for u in uids if (u, arm) in sc]
|
||||
if v:
|
||||
print(f"{arm:10s}{st.mean(v):11.2f}")
|
||||
print("\n⚠ Ось РАЗВЕДОЧНАЯ: вывод описательный при любом исходе (n=9, MDE 2.90 против цели 2.00).")
|
||||
return 0
|
||||
|
||||
|
||||
ORCH = """# Sol-оркестратору: судейство ЯПОНСКОЙ НОГИ H-4 (проход `ja6`)
|
||||
|
||||
Ты раскладываешь готовые судейские задания по своим агентам. **Ничего не готовишь сам и ничего
|
||||
не считаешь** — всё уже собрано, задания и промты лежат файлами.
|
||||
|
||||
## Что сделать
|
||||
|
||||
В каталоге `{prompts}` лежат **{k} промтов сессий**:
|
||||
|
||||
{names}
|
||||
|
||||
Запусти **по одному агенту на каждый промт**, {k} агентов. Каждому дай ровно одну инструкцию:
|
||||
|
||||
> Прочитай файл `{prompts}/<имя>.md` и выполни ровно то, что в нём написано, ничего сверх.
|
||||
|
||||
Промт внутри содержит список назначенных заданий, правила оценки и путь, куда писать ответ.
|
||||
Больше агенту знать ничего не нужно.
|
||||
|
||||
## Жёсткие требования (нарушение обесценивает замер)
|
||||
|
||||
1. **Один агент — один промт.** Не объединяй сессии и не дроби их. Состав сессии — часть прибора.
|
||||
|
||||
2. ⚠⚠ **Наборы `p1` и `p2` — РАЗНЫМ агентам, и это здесь важнее всего.** Это две половины
|
||||
шумового пола: в `p1` лежит одна генерация боевого редактора, в `p2` — вторая генерация ТОГО ЖЕ
|
||||
редактора. Из разницы их оценок и строится порог различимости всего прохода. Судья, увидевший
|
||||
обе половины, порог обесценивает целиком — в паке 23 такой дефект занизил его на 19%, а на
|
||||
проходе `tier` половина пола вообще совпала с боевым армом, и контроль оказался пуст.
|
||||
**Если все задания уйдут одному агенту, замер пропадает, и пере-снять его будет нечем.**
|
||||
|
||||
3. **Агент открывает ТОЛЬКО перечисленные в его промте задания и пишет ТОЛЬКО по путям, указанным
|
||||
ВНУТРИ заданий.** Ему нельзя искать что-либо ещё на диске — ни разметку, ни сырьё, ни код, ни
|
||||
задания других сессий; нельзя делать сетевые вызовы; нельзя сравнивать варианты между собой,
|
||||
ранжировать их и применять `diff`/`difflib`/`cmp`. Ключ соответствия меток и армов лежит ВНЕ
|
||||
рабочего каталога и агенту не выдаётся намеренно. Заглянет — сессия аннулируется целиком.
|
||||
|
||||
4. **Не правь промты и задания.** Если что-то выглядит ошибкой — скажи владельцу, но не исправляй:
|
||||
обвязка сверена гейтом паритета с обвязкой второго семейства, и правка на твоей стороне делает
|
||||
семейства несравнимыми.
|
||||
|
||||
5. **Не досуживай и не переоценивай.** Не справился с частью — так и скажи, сколько выполнено.
|
||||
Частичный возврат честнее выдуманного и засчитывается.
|
||||
|
||||
6. **Агент НЕ запускает своих агентов.** Сессия — это ОДИН агент, читающий свои задания. Дочерние
|
||||
агенты ломают состав сессии, зарегистрированный до запуска; на прошлом прогоне это уже
|
||||
случилось, и сессию пришлось аннулировать целиком.
|
||||
|
||||
## Что это за замер и почему спрашивают вас
|
||||
|
||||
Гипотеза владельца H-4: перевес `deepseek-v4-pro` в роли редактора — свойство пары zh→ru, и на
|
||||
другой паре порядок редакторов может смениться. На японском до этой недели был куплен один
|
||||
редактор, сравнивать было не с чем; теперь их два. Вопрос ровно один: **меняется ли порядок**.
|
||||
|
||||
Ваше семейство здесь полноправно: ни один арм панели не из OpenAI (`deepseek-v4-pro`, `glm-5`,
|
||||
`deepseek-v4-flash`). На соседнем проходе `tier` конфликт был — там судился `gpt-5.6-terra`, то
|
||||
есть выход вашей же семьи, и показания пришлось снять именно поэтому.
|
||||
|
||||
Первое семейство (агенты Claude) эту панель уже отсудило, все контроли зелёные. Вы — второе
|
||||
мнение о ЕГО судействе и его слепоте, а не первый свидетель.
|
||||
|
||||
## Как считается результат
|
||||
|
||||
eval/.venv/bin/python eval/dovodka/ja6.py --score-sol
|
||||
|
||||
Тот же счётчик, которым посчитано первое семейство: сам отбракует ответы без цитат, проверит
|
||||
пойманный декой и напечатает число принятых единиц.
|
||||
|
||||
## Что этот замер может и чего не может — сказано ДО прогона
|
||||
|
||||
Ось объявлена РАЗВЕДОЧНОЙ до денег: 9 единиц, MDE 2.90 против цели 2.00. Несущего вывода она не
|
||||
даст ни при каком исходе. Маржевый декой (контроль чувствительности) сажается лишь на 5 единиц из
|
||||
9 — на этой книге регексы посадок находят мало мест, расширенный список проверен и не помогает.
|
||||
Значит право сказать «редакторы равны» подпёрто здесь слабее, чем на других осях.
|
||||
"""
|
||||
|
||||
SESSION = """<!-- ОБВЯЗКА СЕМЕЙСТВА SOL. Отличается от обвязки другого семейства ТОЛЬКО каталогом
|
||||
заданий и ответов. Паритет обвязок — норма Д0.13 П-4: в паке 23 у харнесса Sol в промте были
|
||||
правила плотности счёта, которых не было у Claude, и часть расхождения семейств в разы могла
|
||||
быть куплена ИНСТРУКЦИЕЙ, а не моделью. -->
|
||||
|
||||
Ты — литературный редактор-эксперт. Твоя работа — оценить качество русских художественных
|
||||
переводов С ЯПОНСКОГО по заданиям, которые лежат готовыми файлами.
|
||||
|
||||
Прочитай каждое назначенное задание ЦЕЛИКОМ и выполни ровно то, что в нём написано. В каждом
|
||||
задании есть исходник на японском, несколько русских переводов этого же фрагмента под слепыми
|
||||
метками и точная форма ответа. Ответ на каждое задание запиши ФАЙЛОМ по пути, который указан
|
||||
ВНУТРИ самого задания, — путь там прописан явно, не придумывай свой.
|
||||
|
||||
Работай по заданиям последовательно: прочитал задание → записал ответ → перешёл к следующему.
|
||||
|
||||
## Правила, которые важнее скорости
|
||||
|
||||
* **Оценивай каждый вариант ОТДЕЛЬНО против исходника.** Не сравнивай варианты между собой, не
|
||||
ранжируй их и не подстраивай оценку одного под оценку другого. Порядок меток перемешан намеренно.
|
||||
* **Каждая ненулевая оценка требует обоснования ЦИТАТОЙ** из соответствующего варианта, в формате,
|
||||
заданном в задании. Оценка без цитаты не принимается, и единица целиком выбрасывается из замера.
|
||||
* **Числа — это СЧЁТ ошибок, а не оценка по шкале.** Две одинаковые ошибки в одном варианте — два.
|
||||
* ⚠ **НОЛЬ — УТВЕРЖДЕНИЕ, А НЕ МОЛЧАНИЕ.** Ставя ноль, ты заявляешь «я сверил и не нашёл». Поле
|
||||
ПОЧЕМУ обязательно для КАЖДОЙ метки, включая нулевые: при всех нулях так и пиши — «прочитано,
|
||||
дефектов не найдено». Пустое ПОЧЕМУ разбор отбрасывает. Прошлый проход развалился именно на
|
||||
молчаливых нулях: 38% клеток были нулями, треть из них без единого слова, и вердикт оказался не
|
||||
«варианты равны», а «прибор ничего не показывал».
|
||||
* Читай ВНИМАТЕЛЬНО и сверяй смысл с японским исходником. Смысловые искажения — инверсия, подмена
|
||||
адресата реплики, потерянное отрицание, изменённое число, потерянный вежливостный регистр,
|
||||
выдуманный факт — незаметны при беглом чтении, потому что русский текст остаётся грамотным и
|
||||
связным. Именно их и надо ловить.
|
||||
* Не жалей вариант и не ищи, за что его похвалить: твоя задача — СЧЁТ ОШИБОК, а не рецензия.
|
||||
|
||||
## Границы
|
||||
|
||||
* Работай ТОЛЬКО с назначенными файлами заданий и пиши ТОЛЬКО по путям, указанным внутри них.
|
||||
* **Не ищи и не открывай ничего другого на диске.** В частности: не выясняй, откуда взялся вариант,
|
||||
какой моделью он сделан и что означают метки. Эта информация к задаче отношения не имеет, а её
|
||||
поиск делает оценку недействительной.
|
||||
* Не применяй `diff`, `difflib`, `cmp` и любое механическое сличение вариантов между собой.
|
||||
* **Не запускай своих агентов.** Эта сессия — ты один.
|
||||
* Не переводи сам и не предлагай свою версию. Не оценивай «литературность в целом» — только оси,
|
||||
названные в задании.
|
||||
* Книга новая (первая публикация 30.07.2026), в претрейне её нет: не ищи её в сети и не опирайся
|
||||
на память. Единственный источник истины — исходник в задании.
|
||||
* Ничего не покупай, никаких сетевых вызовов. Файлы с ключами и секретами не читать.
|
||||
|
||||
ЕСЛИ ЧЕГО-ТО НЕ ЗНАЕШЬ: считай ошибкой только то, что видно из исходника. Сомневаешься — не
|
||||
засчитывай. Заниженный счёт честнее выдуманного.
|
||||
|
||||
## ТВОИ ЗАДАНИЯ ({n})
|
||||
|
||||
{files}
|
||||
"""
|
||||
|
||||
|
||||
def sol() -> None:
|
||||
"""Пакет для внешнего харнесса — ТРЁХУРОВНЕВЫЙ, как пакеты фазы Д, а не двухдокументный.
|
||||
|
||||
⚠ ПОЧЕМУ ИМЕННО ТАК, И ЧЕМ ЭТО ОПЛАЧЕНО. Первая редакция этого пакета копировала структуру
|
||||
эксп-23 (`editor-tier/sol-tier/`: ИНСТРУКЦИЯ + ПРОМТ-ДЛЯ-ХАРНЕССА) и просила «работай по 4
|
||||
задания за сессию» ПРОЗОЙ. Владелец указал, что его харнесс так не работает: получив задание,
|
||||
Sol идёт и читает всё сам, одной сессией. Для этого прохода такая раскладка убийственна —
|
||||
половины шумового пола лежат в РАЗНЫХ наборах (`J0` в p1, `JFLO` в p2), и один судья,
|
||||
увидевший обе, обесценивает порог целиком. Пакеты фазы Д (`sol-d3-work`, `sol-d4-work`,
|
||||
`sol-d6-work`) решают это слоем ОРКЕСТРАТОРА: `ORCHESTRATOR.md` велит запустить по агенту на
|
||||
промт, а `prompts/*.md` жёстко разводят наборы. Здесь воспроизводится тот же слой.
|
||||
|
||||
⚠ Прочее, учтённое из прошлых пакетов, каждое — оплаченная ошибка:
|
||||
· путь к ключу НЕ НАЗЫВАЕТСЯ (пак 23 написал «не открывай blind-keys/» — это показать пальцем);
|
||||
· путь записи ведёт в СВОЙ каталог (пак 23 велел писать в боевой, поверх отсуженного);
|
||||
· правила счёта стоят В САМОМ ЗАДАНИИ и в промте сессии — паритет П-4;
|
||||
· запрет на дочерних агентов вписан явно (на прошлом прогоне сессия их породила);
|
||||
· все армы единицы — в одной пачке (урок эксп-22 §16).
|
||||
"""
|
||||
src, dst = WORK, Path.home() / "books" / "judging" / "ja6-sol"
|
||||
(dst / "tasks").mkdir(parents=True, exist_ok=True)
|
||||
(dst / "answers").mkdir(parents=True, exist_ok=True)
|
||||
(dst / "prompts").mkdir(parents=True, exist_ok=True)
|
||||
by_half: dict[str, list[str]] = {}
|
||||
for half in SETS:
|
||||
d = src / f"{half}-tasks"
|
||||
if not d.exists():
|
||||
continue
|
||||
for f in sorted(d.glob("*.txt")):
|
||||
body = f.read_text(encoding="utf-8")
|
||||
body = body.replace(str(src / f"{half}-answers" / f.name),
|
||||
str(dst / "answers" / f.name))
|
||||
(dst / "tasks" / f.name).write_text(body, encoding="utf-8")
|
||||
by_half.setdefault(half, []).append(f.name)
|
||||
if not by_half:
|
||||
print("заданий нет — сначала --emit")
|
||||
return
|
||||
names = []
|
||||
for half, toks in sorted(by_half.items()):
|
||||
for i in range(0, len(toks), PER_SESSION):
|
||||
grp = toks[i:i + PER_SESSION]
|
||||
nm = f"{half}-session-{i // PER_SESSION + 1:02d}"
|
||||
files = NL.join(f"* `{dst / 'tasks' / x}`" for x in grp)
|
||||
(dst / "prompts" / f"{nm}.md").write_text(
|
||||
SESSION.format(n=len(grp), files=files), encoding="utf-8")
|
||||
names.append(nm)
|
||||
(dst / "ORCHESTRATOR.md").write_text(
|
||||
ORCH.format(prompts=dst / "prompts", k=len(names),
|
||||
names=NL.join(f"* `{n}.md`" for n in names)), encoding="utf-8")
|
||||
print(f"пакет Sol → {dst}")
|
||||
print(f" заданий {sum(len(v) for v in by_half.values())} · промтов сессий {len(names)}")
|
||||
print(f" ОТДАТЬ ВЛАДЕЛЬЦУ: {dst / 'ORCHESTRATOR.md'}")
|
||||
print(" ⚠ p1 и p2 — РАЗНЫМ агентам: это две половины шумового пола")
|
||||
|
||||
|
||||
def selftest() -> int:
|
||||
fails = []
|
||||
ok = lambda c, m: fails.append(m) if not c else None
|
||||
us = units()
|
||||
ok(len(us) == 9, f"единиц {len(us)}, ожидалось 9")
|
||||
for half in SETS:
|
||||
p = panel(us[0], half, 0)
|
||||
ok(bool(p), f"{half}: панель пуста")
|
||||
if p:
|
||||
sigs = [hashlib.sha256(v.encode()).hexdigest() for v in p.values()]
|
||||
ok(len(sigs) == len(set(sigs)), f"{half}: в панели побайтные дубли")
|
||||
ok(("J0" in p) == (half == "p1"), f"{half}: J0 стоит не в своём наборе")
|
||||
ok(("JFLO" in p) == (half == "p2"), f"{half}: JFLO стоит не в своём наборе")
|
||||
lay = layout(us[0]["uid"], half, tuple(p))
|
||||
ok(len(lay) == len(set(lay.values())), "раскладка не биективна")
|
||||
ok(layout(us[0]["uid"], half, tuple(sorted(lay.values()))) == lay,
|
||||
"раскладка не воспроизводима")
|
||||
d = [panel(u, "p1", i).get("CTRLdecoy", "") for i, u in enumerate(us)]
|
||||
ok(sum(1 for x in d if x) >= 7, f"грубый декой сажается лишь на {sum(1 for x in d if x)} из 9")
|
||||
ok(_sign_p([1, 1, 1, 1, 1]) < 0.07, "знаковый тест сломан")
|
||||
for m in fails:
|
||||
print("ПРОВАЛ:", m)
|
||||
print(f"селфтест ja6: провалов {len(fails)}")
|
||||
return 1 if fails else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = sys.argv[1:] or ["--selftest"]
|
||||
if a[0] == "--emit":
|
||||
emit()
|
||||
elif a[0] == "--score":
|
||||
sys.exit(score())
|
||||
elif a[0] == "--selftest":
|
||||
sys.exit(selftest())
|
||||
elif a[0] == "--sol":
|
||||
sol()
|
||||
elif a[0] == "--score-sol":
|
||||
sys.exit(score(Path.home() / "books" / "judging" / "ja6-sol"))
|
||||
else:
|
||||
raise SystemExit(f"⛔ неизвестный режим {a[0]!r}")
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
переводов по заданиям, которые лежат готовыми файлами.
|
||||
|
||||
Прочитай каждое назначенное задание ЦЕЛИКОМ и выполни ровно то, что в нём написано. В каждом
|
||||
задании есть исходник на китайском, несколько русских переводов этого же фрагмента под слепыми
|
||||
задании есть исходник на {ЯЗЫКЕ}, несколько русских переводов этого же фрагмента под слепыми
|
||||
метками и точная форма ответа. Ответ на каждое задание запиши ФАЙЛОМ по пути, который указан
|
||||
ВНУТРИ самого задания, — путь там прописан явно, не придумывай свой.
|
||||
|
||||
|
|
@ -24,11 +24,19 @@
|
|||
* **Ноль ставится только тогда, когда ошибок этого типа действительно нет.** Ноль — это
|
||||
утверждение, а не способ пропустить вариант. Если ты не нашёл ошибок, значит их нет; убедись,
|
||||
что читал внимательно.
|
||||
* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с китайским исходником. Смысловые искажения — инверсия,
|
||||
* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с {ЯЗЫКОМ} исходником. Смысловые искажения — инверсия,
|
||||
подмена адресата реплики, потерянное отрицание, изменённое число, выдуманный факт — бывают
|
||||
незаметны при беглом чтении, потому что русский текст остаётся грамотным и связным. Именно их
|
||||
и надо ловить.
|
||||
* **ГЛАВНОЕ ПРАВИЛО ЭТОГО ЗАМЕРА (решение владельца продукта 16.08):** переврать смысл
|
||||
исходника — ошибка; обойтись с его буквой вольно — нет. Живой русский язык здесь приоритет,
|
||||
поэтому перестановка предложений, слияние и дробление абзацев, замена оборота на более живой
|
||||
русский, синонимическая замена, добавленная связка и снятая буквальность исходного языка
|
||||
ошибками НЕ СЧИТАЮТСЯ. Точный список того, что ошибкой не является, стоит в шапке САМОГО
|
||||
задания, и он главнее этой обвязки.
|
||||
* Не жалей вариант и не ищи, за что его похвалить: твоя задача — СЧЁТ ОШИБОК, а не рецензия.
|
||||
Но и не штрафуй вариант за то, что он просто нравится тебе меньше соседнего: разница вкуса
|
||||
ошибкой не является.
|
||||
|
||||
## Границы
|
||||
|
||||
|
|
@ -45,5 +53,7 @@
|
|||
|
||||
{СПИСОК ФАЙЛОВ}
|
||||
|
||||
Каталог заданий: `~/sud-d4/p1-tasks/` и `~/sud-d4/p2-tasks/`.
|
||||
Отвечать по путям, указанным внутри заданий (`~/sud-d4/p1-answers/`, `~/sud-d4/p2-answers/`).
|
||||
Каталог твоих заданий: `~/{КАТАЛОГ}/{НАБОР}-tasks/`. Другие наборы этого прогона тебе
|
||||
НЕ назначены и открывать их нельзя: наборы — половины парного контроля, и судья,
|
||||
увидевший обе, обесценивает контроль целиком.
|
||||
Отвечать по путям, указанным внутри заданий (`~/{КАТАЛОГ}/{НАБОР}-answers/`).
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
переводов по заданиям, которые лежат готовыми файлами.
|
||||
|
||||
Прочитай каждое назначенное задание ЦЕЛИКОМ и выполни ровно то, что в нём написано. В каждом
|
||||
задании есть исходник на китайском, несколько русских переводов этого же фрагмента под слепыми
|
||||
задании есть исходник на {ЯЗЫКЕ}, несколько русских переводов этого же фрагмента под слепыми
|
||||
метками и точная форма ответа. Ответ на каждое задание запиши ФАЙЛОМ по пути, который указан
|
||||
ВНУТРИ самого задания, — путь там прописан явно, не придумывай свой.
|
||||
|
||||
|
|
@ -19,11 +19,19 @@
|
|||
* **Ноль ставится только тогда, когда ошибок этого типа действительно нет.** Ноль — это
|
||||
утверждение, а не способ пропустить вариант. Если ты не нашёл ошибок, значит их нет; убедись,
|
||||
что читал внимательно.
|
||||
* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с китайским исходником. Смысловые искажения — инверсия,
|
||||
* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с {ЯЗЫКОМ} исходником. Смысловые искажения — инверсия,
|
||||
подмена адресата реплики, потерянное отрицание, изменённое число, выдуманный факт — бывают
|
||||
незаметны при беглом чтении, потому что русский текст остаётся грамотным и связным. Именно их
|
||||
и надо ловить.
|
||||
* **ГЛАВНОЕ ПРАВИЛО ЭТОГО ЗАМЕРА (решение владельца продукта 16.08):** переврать смысл
|
||||
исходника — ошибка; обойтись с его буквой вольно — нет. Живой русский язык здесь приоритет,
|
||||
поэтому перестановка предложений, слияние и дробление абзацев, замена оборота на более живой
|
||||
русский, синонимическая замена, добавленная связка и снятая буквальность исходного языка
|
||||
ошибками НЕ СЧИТАЮТСЯ. Точный список того, что ошибкой не является, стоит в шапке САМОГО
|
||||
задания, и он главнее этой обвязки.
|
||||
* Не жалей вариант и не ищи, за что его похвалить: твоя задача — СЧЁТ ОШИБОК, а не рецензия.
|
||||
Но и не штрафуй вариант за то, что он просто нравится тебе меньше соседнего: разница вкуса
|
||||
ошибкой не является.
|
||||
|
||||
## Границы
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
переводов по заданиям, которые лежат готовыми файлами.
|
||||
|
||||
Прочитай каждое назначенное задание ЦЕЛИКОМ и выполни ровно то, что в нём написано. В каждом
|
||||
задании есть исходник на китайском, несколько русских переводов этого же фрагмента под слепыми
|
||||
задании есть исходник на {ЯЗЫКЕ}, несколько русских переводов этого же фрагмента под слепыми
|
||||
метками и точная форма ответа. Ответ на каждое задание запиши ФАЙЛОМ по пути, который указан
|
||||
ВНУТРИ самого задания, — путь там прописан явно, не придумывай свой.
|
||||
|
||||
|
|
@ -24,11 +24,19 @@
|
|||
* **Ноль ставится только тогда, когда ошибок этого типа действительно нет.** Ноль — это
|
||||
утверждение, а не способ пропустить вариант. Если ты не нашёл ошибок, значит их нет; убедись,
|
||||
что читал внимательно.
|
||||
* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с китайским исходником. Смысловые искажения — инверсия,
|
||||
* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с {ЯЗЫКОМ} исходником. Смысловые искажения — инверсия,
|
||||
подмена адресата реплики, потерянное отрицание, изменённое число, выдуманный факт — бывают
|
||||
незаметны при беглом чтении, потому что русский текст остаётся грамотным и связным. Именно их
|
||||
и надо ловить.
|
||||
* **ГЛАВНОЕ ПРАВИЛО ЭТОГО ЗАМЕРА (решение владельца продукта 16.08):** переврать смысл
|
||||
исходника — ошибка; обойтись с его буквой вольно — нет. Живой русский язык здесь приоритет,
|
||||
поэтому перестановка предложений, слияние и дробление абзацев, замена оборота на более живой
|
||||
русский, синонимическая замена, добавленная связка и снятая буквальность исходного языка
|
||||
ошибками НЕ СЧИТАЮТСЯ. Точный список того, что ошибкой не является, стоит в шапке САМОГО
|
||||
задания, и он главнее этой обвязки.
|
||||
* Не жалей вариант и не ищи, за что его похвалить: твоя задача — СЧЁТ ОШИБОК, а не рецензия.
|
||||
Но и не штрафуй вариант за то, что он просто нравится тебе меньше соседнего: разница вкуса
|
||||
ошибкой не является.
|
||||
|
||||
## Границы
|
||||
|
||||
|
|
@ -45,5 +53,7 @@
|
|||
|
||||
{СПИСОК ФАЙЛОВ}
|
||||
|
||||
Каталог заданий: `~/sol-d4-work/p1-tasks/` и `~/sol-d4-work/p2-tasks/`.
|
||||
Отвечать по путям, указанным внутри заданий (`~/sol-d4-work/p1-answers/`, `~/sol-d4-work/p2-answers/`).
|
||||
Каталог твоих заданий: `~/{КАТАЛОГ}/{НАБОР}-tasks/`. Другие наборы этого прогона тебе
|
||||
НЕ назначены и открывать их нельзя: наборы — половины парного контроля, и судья,
|
||||
увидевший обе, обесценивает контроль целиком.
|
||||
Отвечать по путям, указанным внутри заданий (`~/{КАТАЛОГ}/{НАБОР}-answers/`).
|
||||
|
|
|
|||
153
eval/dovodka/naklon.py
Normal file
153
eval/dovodka/naklon.py
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env python3
|
||||
"""ГЕЙТ ПОЗИЦИОННОГО НАКЛОНА. $0, только чтение сырья.
|
||||
|
||||
⚠ ЗАЧЕМ ОТДЕЛЬНЫЙ ФАЙЛ. Норма рига требует наклон ЗАМЕРИТЬ, ВЫЧЕСТЬ и СТОРОЖИТЬ ГЕЙТОМ, и отчёт
|
||||
фазы (23-editor-tier.md:1027) прямо утверждает, что так и делается. Фактически в зоне фазы Д кода
|
||||
наклона не было ни строки: замер сделан ОДИН раз руками при финальном аудите, уже ПОСЛЕ вердиктов
|
||||
(девиация объявлена в Д10), сторожа нет. Здесь он есть.
|
||||
|
||||
ЧТО МЕРИТСЯ. Наклон — это зависимость выставленного счёта от НОМЕРА МЕТКИ в пачке: судья,
|
||||
устающий к концу задания, ставит поздним меткам больше ошибок. Сам по себе наклон вердикту не
|
||||
вредит — он сокращается в контрасте, ЕСЛИ средние позиции двух армов совпадают. Вредит
|
||||
ПРОИЗВЕДЕНИЕ наклона на дисбаланс позиций, и именно оно здесь и гейтится.
|
||||
|
||||
поправка контраста = наклон × (средняя позиция арма A − средняя позиция арма B)
|
||||
|
||||
ГЕЙТ: пачка не годна, если поправка хоть к одному первичному контрасту превышает
|
||||
`MAX_SHARE` от порога различимости этой оси — то есть если перестановка меток способна
|
||||
съесть заметную долю решающей величины.
|
||||
|
||||
Запуск: naklon.py [--pass=<проход>] | --selftest
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
import statistics as st
|
||||
import sys
|
||||
|
||||
BOOKS = pathlib.Path.home() / "books"
|
||||
AXRE = re.compile(r"^[ТT](\d+)-(ВЕРНОСТЬ|ТЕРМИН|ЯЗЫК|ФОРМА):\s*(\d+)", re.MULTILINE)
|
||||
CARRY = ("ВЕРНОСТЬ", "ЯЗЫК")
|
||||
MAX_SHARE = 0.25 # доля порога, которую поправке позволено съедать
|
||||
|
||||
PASSES = {
|
||||
"tier": (["editor-tier/aj-tier"], "editor-tier/blind-keys/tier-KEY.json",
|
||||
[("T1", "R0"), ("T2", "R0"), ("T3", "R0")], 1.02),
|
||||
"tier2": (["editor-tier/tier2"], "editor-tier/blind-keys/tier2-KEY.json",
|
||||
[("T1", "R0"), ("T2", "R0"), ("T3", "R0")], 1.84),
|
||||
"border": (["editor-tier/aj-border"], "editor-tier/blind-keys/border-KEY.json",
|
||||
[("R2", "R0")], 1.48),
|
||||
"d4": (["judging/sud-d4/p1-answers", "judging/sud-d4/p2-answers"], "dovodka/blind-keys-d4",
|
||||
[("A1B", "A0"), ("A3", "A0"), ("A6", "A0")], 1.65),
|
||||
"d3": (["judging/sud-d3/p1-answers", "judging/sud-d3/p2-answers"], "dovodka/blind-keys-d3",
|
||||
[("E0", "D0")], 0.90),
|
||||
"d6": (["judging/sud-d6/p1-answers", "judging/sud-d6/p2-answers"], "dovodka/blind-keys-d6",
|
||||
[("J0", "D0")], 2.10),
|
||||
"d1": (["judging/sud-d1/p1-answers", "judging/sud-d1/p2-answers"], "dovodka/blind-keys-d1",
|
||||
[("R1", "A0")], 1.47),
|
||||
}
|
||||
|
||||
|
||||
def load_key(rel: str) -> dict:
|
||||
p = BOOKS / rel
|
||||
if p.is_dir():
|
||||
k: dict = {}
|
||||
for f in sorted(p.glob("*-KEY.json")):
|
||||
k.update(json.loads(f.read_text(encoding="utf-8")))
|
||||
return k
|
||||
return json.loads(p.read_text(encoding="utf-8")) if p.exists() else {}
|
||||
|
||||
|
||||
def collect(rels: list[str], keyrel: str):
|
||||
"""[(номер метки, арм, счёт)] по всем разобранным клеткам прохода."""
|
||||
key = load_key(keyrel)
|
||||
rows = []
|
||||
for rel in rels:
|
||||
d = BOOKS / rel
|
||||
if not d.exists():
|
||||
continue
|
||||
for f in sorted(d.glob("*.txt")):
|
||||
km = key.get(f.stem)
|
||||
if not isinstance(km, dict):
|
||||
continue
|
||||
cells: dict[str, dict[str, int]] = collections.defaultdict(dict)
|
||||
for m in AXRE.finditer(f.read_text(encoding="utf-8", errors="replace")):
|
||||
cells["Т" + m.group(1)][m.group(2)] = int(m.group(3))
|
||||
for lab, ax in cells.items():
|
||||
meta = km.get(lab)
|
||||
if isinstance(meta, dict) and meta.get("arm"):
|
||||
rows.append((int(lab[1:]), meta["arm"], sum(ax.get(a, 0) for a in CARRY)))
|
||||
return rows
|
||||
|
||||
|
||||
def slope(rows) -> float:
|
||||
"""Ошибок на ШАГ метки — обычная линейная регрессия счёта на номер метки."""
|
||||
if len(rows) < 4:
|
||||
return 0.0
|
||||
xs = [r[0] for r in rows]
|
||||
ys = [r[2] for r in rows]
|
||||
mx, my = st.mean(xs), st.mean(ys)
|
||||
den = sum((x - mx) ** 2 for x in xs)
|
||||
return sum((x - mx) * (y - my) for x, y in zip(xs, ys)) / den if den else 0.0
|
||||
|
||||
|
||||
def report(name: str) -> int:
|
||||
rels, keyrel, fam, thr = PASSES[name]
|
||||
rows = collect(rels, keyrel)
|
||||
if not rows:
|
||||
print(f"{name:8s} — ответов нет")
|
||||
return 0
|
||||
s = slope(rows)
|
||||
pos = collections.defaultdict(list)
|
||||
for lab, arm, _ in rows:
|
||||
pos[arm].append(lab)
|
||||
mean_pos = {a: st.mean(v) for a, v in pos.items()}
|
||||
print(f"\n=== {name} · точек {len(rows)} · наклон {s:+.4f} ошибки на шаг метки · "
|
||||
f"порог оси {thr:.2f} ===")
|
||||
rc = 0
|
||||
for a, b in fam:
|
||||
if a not in mean_pos or b not in mean_pos:
|
||||
continue
|
||||
dp = mean_pos[a] - mean_pos[b]
|
||||
corr = s * dp
|
||||
share = abs(corr) / thr if thr else 0.0
|
||||
mark = " ⛔ ПРОБОЙ" if share > MAX_SHARE else ""
|
||||
print(f" {a + '/' + b:12s} позиции {mean_pos[a]:5.2f} против {mean_pos[b]:5.2f} "
|
||||
f"(разница {dp:+.2f}) → поправка {corr:+.3f} = {share:.0%} порога{mark}")
|
||||
if share > MAX_SHARE:
|
||||
rc = 1
|
||||
return rc
|
||||
|
||||
|
||||
def selftest() -> int:
|
||||
fails = []
|
||||
# синтетика с ИЗВЕСТНЫМ наклоном: счёт = 2 × номер метки
|
||||
rows = [(i, "A" if i % 2 else "B", 2 * i) for i in range(1, 21)]
|
||||
s = slope(rows)
|
||||
if abs(s - 2.0) > 1e-9:
|
||||
fails.append(f"наклон на синтетике {s:.4f}, ожидалось 2.0")
|
||||
flat = [(i, "A", 5) for i in range(1, 21)]
|
||||
if abs(slope(flat)) > 1e-9:
|
||||
fails.append("на плоских данных наклон обязан быть нулём")
|
||||
if len(collect(*PASSES["d4"][:2])) < 100:
|
||||
fails.append("проход d4 не разбирается")
|
||||
for m in fails:
|
||||
print("ПРОВАЛ:", m)
|
||||
print(f"селфтест наклона: провалов {len(fails)}")
|
||||
return 1 if fails else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = sys.argv[1:]
|
||||
if "--selftest" in args:
|
||||
sys.exit(selftest())
|
||||
want = [a.split("=", 1)[1] for a in args if a.startswith("--pass=")] or list(PASSES)
|
||||
rc = 0
|
||||
for n in want:
|
||||
rc |= report(n)
|
||||
print(f"\n⚠ Гейт роняет проход, если поправка съедает больше {MAX_SHARE:.0%} порога "
|
||||
f"различимости: столько способна дать одна перестановка меток.")
|
||||
sys.exit(rc)
|
||||
|
|
@ -58,6 +58,10 @@ MAP = {
|
|||
# en-файл → (zh-оригинал, комментарий провенанса)
|
||||
"translator": ("backend/prompts/zh-ru/translator.md", "боевой промпт переводчика"),
|
||||
"editor": ("backend/prompts/zh-ru/editor.md", "боевой промпт редактора"),
|
||||
# ⚠ Заведён 14.08. Промт сессии требует МЕХАНИЧЕСКОГО гейта консистентности пар-промтов, а
|
||||
# терминолог в карте отсутствовал — то есть банк-роль новой пары проходила мимо гейта вовсе.
|
||||
# Гейт при этом честно печатал ПРОВАЛ «файла нет в MAP» и не угадывал, с чем сравнивать.
|
||||
"terminologist": ("backend/prompts/zh-ru/terminologist.md", "боевой промпт терминолога"),
|
||||
"translator-reflow": (None, "боевого zh-аналога НЕТ: это вариант полигона, "
|
||||
"зеркалом боевого промпта не является"),
|
||||
}
|
||||
|
|
@ -84,7 +88,29 @@ ALLOWED = {
|
|||
("ja-ru", "editor", "- Вёрстка: собирай повествование"):
|
||||
"оговорка про построчный набор СОХРАНЕНА и переформулирована под ja: веб-новелла "
|
||||
"syosetu набирается построчно так же, как китайская",
|
||||
# ⚠ Терминолог: пример строки ОТВЕТА — тот же третий законный класс, что у редактора, только
|
||||
# формат другой (три поля через табуляцию, а не маркер `[narrative]`). Термин примера взят
|
||||
# НЕ из книги среза (проверено: Thibault ×0, 慎二 ×0), чтобы промт не подсказывал модели
|
||||
# ответы по реальным кандидатам банка.
|
||||
("en-ru", "terminologist", "师父"):
|
||||
"пример строки ответа ЗАМЕНЁН своим английским (`Thibault → Тибо`) — образец обязан быть "
|
||||
"на исходном языке пары; термин вне книги среза, подсказки ответов нет",
|
||||
# Обе стороны замены объявляются отдельно: дифф показывает и снятую строку, и вставленную.
|
||||
("en-ru", "terminologist", "Thibault"):
|
||||
"вставленная половина той же замены примера (см. запись про 师父)",
|
||||
("ja-ru", "terminologist", "慎二"):
|
||||
"вставленная половина той же замены примера (см. запись про 师父)",
|
||||
("ja-ru", "terminologist", "师父"):
|
||||
"пример строки ответа ЗАМЕНЁН своим японским (`慎二 → Синдзи`, дзи-слог демонстрирует "
|
||||
"Поливанова против ромадзи) — образец обязан быть на исходном языке пары",
|
||||
}
|
||||
|
||||
# ⚠ ФАЙЛЫ, У КОТОРЫХ ЗЕРКАЛО ПРОСТО НЕ ИМЕЕТ ПАР-БЛОКА. У боевого zh-терминолога его нет: файл
|
||||
# написан пар-НЕЙТРАЛЬНО, вся языковая специфика приходит подстановками брифа. Пар-версии блок
|
||||
# ДОБАВЛЯЮТ, и это расхождение объявлено здесь, а не спрятано ослаблением проверки: у пары блок
|
||||
# обязан быть по-прежнему, требование снимается ТОЛЬКО с zh-стороны. Завести блок в zh нельзя —
|
||||
# `backend/` чужая зона.
|
||||
NO_ZH_PAIRBLOCK = {"terminologist"}
|
||||
BAD = 0
|
||||
|
||||
|
||||
|
|
@ -154,7 +180,11 @@ def compare(name: str, zh_p: Path, en_p: Path, want_pair: str, pair: str) -> Non
|
|||
zo, zb, zr, zp = blocks(zl)
|
||||
eo, eb, er, ep = blocks(el)
|
||||
|
||||
ck("пар-блок объявлен в обоих", bool(zp) and bool(ep), f"zh «{zp}» · пара «{ep}»")
|
||||
if name in NO_ZH_PAIRBLOCK:
|
||||
ck("пар-блок есть у ПАРЫ (у боевого zh его нет — объявлено)", bool(ep),
|
||||
f"zh «{zp}» · пара «{ep}»")
|
||||
else:
|
||||
ck("пар-блок объявлен в обоих", bool(zp) and bool(ep), f"zh «{zp}» · пара «{ep}»")
|
||||
ck(f"пар-блок называет свою пару ({want_pair})", ep == want_pair, ep or "не найден")
|
||||
|
||||
# ГЛАВНАЯ ПРОВЕРКА: всё вне пар-блока обязано совпадать построчно
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final
|
|||
|---|---|---|
|
||||
| **РАМКА** | | |
|
||||
| R1 | Отчёт фазы — секции Д0–Д8 в `23-editor-tier.md`, продолжение того же документа | `$ grep -q "^## Д3 — en→ru" docs/experiments/23-editor-tier.md && grep -q "^## Д6 —" docs/experiments/23-editor-tier.md && grep -q "^## Д9 —" docs/experiments/23-editor-tier.md` |
|
||||
| R2 | Сводная таблица «пробел → чем закрыт → носитель-артефакт» обязательна | `$ grep -q "ПРОБЕЛ → ЧЕМ ЗАКРЫТ → НОСИТЕЛЬ" docs/experiments/23-editor-tier.md` |
|
||||
| R2 | Сводная таблица «пробел → чем закрыт → носитель-артефакт» обязательна | `$ grep -q "ПРОБЕЛ → ЧЕМ ЗАКРЫТ → НОСИТЕЛЬ" docs/experiments/23-editor-tier.md && test -s ~/books/dovodka/canon-dissect.json && test -s ~/books/dovodka/d1-attempts.jsonl` |
|
||||
| R3 | Пакетный потолок фазы сверен СЛОВОМ владельца при запуске | `$ grep -q "PACK_CEILING" eval/dovodka/money_d.py && grep -q "САНКЦИЮ" eval/dovodka/money_d.py` |
|
||||
| R4 | Фазовые потолки разложены в пре-реге Д0 | `$ grep -q "ФД-A" eval/dovodka/money_d.py && grep -q "ФД-G" eval/dovodka/money_d.py` |
|
||||
| R5 | Касса: фриз-гейт, атомарный замок, два пути счёта, проекционные гарды | `$ eval/.venv/bin/python eval/dovodka/money_d.py --selftest` |
|
||||
|
|
@ -38,7 +38,7 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final
|
|||
| R10 | Ретрай-харнесс: экспоненциальный бэкофф на 5xx, окно ≥24 ч, поштучно, лимит цены на клетку | `$ grep -q "BACKOFF = (30, 120, 480, 1800)" eval/dovodka/d1_gemini.py` |
|
||||
| R11 | Журнал КАЖДОЙ попытки (время · код ответа · стоимость) файлом в сырьё | `$ test -s ~/books/dovodka/d1-attempts.jsonl && grep -q "\"ts\"" ~/books/dovodka/d1-attempts.jsonl` |
|
||||
| R12 | Вердикт «модель не отдаёт» — только с журналом за ≥24 ч + снимком вендор-статуса | `$ grep -q "ТОЛЬКО с журналом" eval/dovodka/d1_gemini.py` |
|
||||
| R13 | Собрано ≥12/16 → судейство абс-ригом со всеми контролями | `$ set -- $HOME/sud-d1/p1-answers/*.txt; test $# -ge 12` |
|
||||
| R13 | Собрано ≥12/16 → судейство абс-ригом со всеми контролями | `$ set -- $HOME/books/judging/sud-d1/p1-answers/*.txt; test $# -ge 12` |
|
||||
| R14 | Потолок клетки считается по `total_tokens` (скрытая тарификация ×4.5) | `$ grep -q "additive_total" eval/tenant_panel/roster.py` |
|
||||
| **Д2 — внешняя подпись `tier`** | | |
|
||||
| R15 | Задания `aj-tier-tasks` отданы внешнему судье вне Claude руками владельца | `$ test -s /home/ubuntu/books/editor-tier/sol-tier/ИНСТРУКЦИЯ.md` |
|
||||
|
|
@ -70,7 +70,7 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final
|
|||
| R37 | Непригоден → СТОП и пинг; замену без слова владельца не искать | `$ eval/.venv/bin/python eval/dovodka/material_ja.py \| grep -q "СТОП\|OK "` |
|
||||
| R38 | ja-промпты — те же гейты провенанса и консистентности, что Д3 | `$ eval/.venv/bin/python eval/dovodka/promptdiff.py ja-ru` |
|
||||
| R39 | 8–10 единиц: связка · dspro-однопроходка · контроль редактора | `$ set -- $HOME/books/dovodka/dv-c-j0-*.json; test $# -ge 9` |
|
||||
| R40 | Печатается сравнение ОТНОСИТЕЛЬНОГО порядка жильцов между парами zh/en/ja | `$ grep -q "ноги H-4" docs/experiments/23-editor-tier.md` |
|
||||
| R40 | Печатается сравнение ОТНОСИТЕЛЬНОГО порядка жильцов между парами zh/en/ja | `$ set -- $HOME/books/dovodka/dv-h-j6-*.json; test $# -ge 9 && eval/.venv/bin/python eval/dovodka/ja6.py --score` |
|
||||
| R41 | Статус оси РАЗВЕДКА объявлен В ПРЕ-РЕГЕ, а не постфактум | `$ grep -q "FORCED_DESCRIPTIVE" eval/dovodka/power.py` |
|
||||
| **Д7 — самооценка** | | |
|
||||
| R42 | Жильцам обеих ролей по трём книгам (zh/en/ja) вопрос о самооценке; ответы персистятся | `$ set -- $HOME/books/dovodka/dv-d-self-*.json; test $# -eq 6` |
|
||||
|
|
@ -90,11 +90,11 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final
|
|||
| R55 | Статус-баннеры на отчётах экспов, где их нет | `$ test $(grep -l "баннер фазы Д" docs/experiments/*.md \| wc -l) -ge 7` |
|
||||
| R56 | Шапка эндпоинтов `00-provider-quirks.md` актуализирована живым листингом | `$ grep -q "ЖИВОЙ ЛИСТИНГ ./models. ПЕРЕ-СНЯТ 2026-08-10" docs/experiments/00-provider-quirks.md` |
|
||||
| **НОРМЫ РИГА** | | |
|
||||
| R57 | Декой в каждой судейской пачке; сессия без пойманного декоя аннулируется целиком | `$ grep -q "CTRLdecoy" eval/dovodka/itog_d4.py` |
|
||||
| R57 | Декой в каждой судейской пачке; сессия без пойманного декоя аннулируется целиком | `$ eval/.venv/bin/python eval/dovodka/ja6.py --score \| grep -q "ГРУБЫЙ ДЕКОЙ.*ПРОЙДЕН"` |
|
||||
| R58 | Шумовой пол — парой, чьи половины судят РАЗНЫЕ сессии (порог был занижен на 19%) | `$ grep -q "ПОБАЙТНО РАВНЫЕ ПОЛОВИНЫ" eval/dovodka/sud.py` |
|
||||
| R59 | Все армы единицы — в одном пакете одной сессии | `$ grep -q "ARMS=arms" eval/dovodka/sud.py` |
|
||||
| R60 | Идентичность судей персистится ДО запуска; половиной пола не брать боевую клетку | `$ eval/.venv/bin/python eval/dovodka/runs.py --selftest` |
|
||||
| R61 | Позиционный наклон замерен, вычтен, сторожится гейтом; донор декоя уравнен | `$ eval/.venv/bin/python eval/editor_tier/verify23.py` |
|
||||
| R61 | Позиционный наклон замерен, вычтен, сторожится гейтом; донор декоя уравнен | `$ eval/.venv/bin/python eval/dovodka/naklon.py` |
|
||||
| R62 | Судье запрещено сравнивать варианты между собой И читать их рядом | `$ grep -q "Не сравнивай варианты между собой" eval/dovodka/judge-prompts/core.md` |
|
||||
| R63 | Замок кассы снимает только поставивший его процесс (проверка живости PID) | `$ eval/.venv/bin/python eval/dovodka/runs.py --selftest` |
|
||||
| R64 | Реестр требований фазы — В GIT до покупок; conformance валиден только против закоммиченного | `$ eval/.venv/bin/python -c "import sys;sys.path.insert(0,'eval');import conformance as c;ok,why=c.frozen(c.Path('eval/dovodka/requirements.md'));print(why);sys.exit(0 if ok else 1)"` |
|
||||
|
|
@ -107,9 +107,9 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final
|
|||
| R70 | Детекторы и пороги после взгляда на вердикты не менять; девиация = СТОП и пинг В МОМЕНТ | `$ grep -q "ПОРОГИ НЕ ТРОНУТЫ" eval/dovodka/contam_d.py` |
|
||||
| R71 | Правка рига чужого пака — отдельным коммитом + пере-снятие его гейтов | `$ eval/.venv/bin/python eval/tenant_panel/verify22.py` |
|
||||
| R72 | Приёмка адверсариальная author≠reviewer; опровергатель ДРУГОГО модельного семейства ОБЯЗАТЕЛЕН | `$ grep -q "Fable-5" docs/experiments/23-editor-tier.md` |
|
||||
| R73 | Финал — построчный аудит закрытия заказа | `$ grep -q "^## Д11 — ИСПРАВЛЕНИЯ ФИНАЛЬНОГО АУДИТА" docs/experiments/23-editor-tier.md` |
|
||||
| R73 | Финал — построчный аудит закрытия заказа | `$ eval/.venv/bin/python eval/dovodka/gain.py --selftest && eval/.venv/bin/python eval/dovodka/naklon.py --selftest && eval/.venv/bin/python eval/dovodka/adjud.py --controls` |
|
||||
| **ОТЧЁТ И СДАЧА** | | |
|
||||
| R74 | Сверка H-1…H-4 с фактом отдельной таблицей, ВКЛЮЧАЯ опровержения | `$ grep -q "Гипотезы владельца — сверка с фактом" docs/experiments/23-editor-tier.md` |
|
||||
| R75 | CONFIRM/DENY владельцу — только с артефактом-носителем | `$ grep -q "носитель-артефакт" docs/experiments/23-editor-tier.md` |
|
||||
| R76 | Деньги: итог по фазам ДВУМЯ путями счёта | `$ grep -q "Деньги фазы — итог ДВУМЯ ПУТЯМИ" docs/experiments/23-editor-tier.md` |
|
||||
| R76 | Деньги: итог по фазам ДВУМЯ путями счёта | `$ eval/.venv/bin/python eval/dovodka/money_d.py --selftest` |
|
||||
| R77 | Пинг в `docs/PROGRESS.md` секция «Полигон» при закрытии | `$ grep -q "15.08 · ФАЗА Д" docs/PROGRESS.md` |
|
||||
|
|
|
|||
|
|
@ -25,26 +25,51 @@ PROMPTS = ZONE / "judge-prompts"
|
|||
# ⚠ Ось — параметр. Пачки разных осей НЕЛЬЗЯ класть в один каталог и нельзя отдавать одной
|
||||
# сессии: судья, увидевший обе, перестаёт быть слепым к паре.
|
||||
AXIS = next((a.split("=", 1)[1] for a in sys.argv if a.startswith("--axis=")), "d4")
|
||||
# ⚠ КОРЕНЬ — ТОЖЕ ПАРАМЕТР (заведено 16.08 под заход Д17). У осей фазы раскладка каталогов
|
||||
# `judging/sud-<ось>`, а у захода она на уровень глубже: `judging/z17/<пара>`, потому что пара
|
||||
# там своя у каждой панели. Прежде корень выводился из имени оси, и для захода пришлось бы
|
||||
# заводить ВТОРОЙ такой же скрипт — то есть размножить будущие расхождения раскладки. Флаг
|
||||
# `--root=` даёт тот же механизм чужой раскладке, не копируя его.
|
||||
_ROOT = next((a.split("=", 1)[1] for a in sys.argv if a.startswith("--root=")), "")
|
||||
FAM = {
|
||||
"claude": dict(root=Path.home() / "books" / "judging" / f"sud-{AXIS}", tpl=PROMPTS / "claude.md"),
|
||||
"claude": dict(root=(Path(_ROOT).expanduser() if _ROOT
|
||||
else Path.home() / "books" / "judging" / f"sud-{AXIS}"),
|
||||
tpl=PROMPTS / "claude.md"),
|
||||
"sol": dict(root=Path.home() / "books" / "judging" / f"sol-{AXIS}-work", tpl=PROMPTS / "sol.md"),
|
||||
}
|
||||
if _ROOT:
|
||||
FAM.pop("sol") # внешнее семейство пакет получает своим сборщиком
|
||||
PER_SESSION = 4 # как у харнесса Sol в паке 23; одинаково для обоих семейств
|
||||
|
||||
|
||||
MISSING_ONLY = "--missing" in sys.argv
|
||||
|
||||
|
||||
def batches(root: Path, half: str) -> list[list[str]]:
|
||||
"""Пачки набора, разложенные по сессиям. Порядок — от имени файла, значит воспроизводим."""
|
||||
"""Пачки набора, разложенные по сессиям. Порядок — от имени файла, значит воспроизводим.
|
||||
|
||||
⚠ `--missing` РАСКЛАДЫВАЕТ ТОЛЬКО НЕОТСУЖЕННОЕ, и это не удобство, а защита данных. Сессия
|
||||
может оборваться на середине (16.08: семь из четырнадцати упали на лимите харнесса, успев
|
||||
записать часть ответов). Пере-запуск ЦЕЛОЙ сессии переписал бы уже полученные оценки новыми —
|
||||
то есть тихо подменил бы данные замера теми, что судья выдал во второй раз. Добор идёт только
|
||||
по пачкам без ответа; уже отсуженное не трогается вовсе.
|
||||
"""
|
||||
toks = sorted(p.stem for p in (root / f"{half}-tasks").glob("*.txt"))
|
||||
if MISSING_ONLY:
|
||||
done = {p.stem for p in (root / f"{half}-answers").glob("*.txt")}
|
||||
toks = [t for t in toks if t not in done]
|
||||
return [toks[i:i + PER_SESSION] for i in range(0, len(toks), PER_SESSION)]
|
||||
|
||||
|
||||
# Язык исходника — из провайдера пары, а не из текста шаблона: судья английской пачки, которому
|
||||
# сказано «сверяй с китайским исходником», сверяет не с тем, что видит.
|
||||
LANG = {"d4": "китайском", "d3": "английском", "d6": "японском", "d1": "китайском"}
|
||||
LANG = {"d4": "китайском", "d3": "английском", "d6": "японском", "d1": "китайском",
|
||||
"z17-zh": "китайском", "z17-en": "английском"}
|
||||
# ⚠ Творительный падеж — ОТДЕЛЬНАЯ подстановка. Первая редакция просто вычёркивала язык из фразы
|
||||
# «сверяй смысл с китайским исходником», потому что склонение не подставлялось. Проверенная
|
||||
# ревью версия называла язык ДВАЖДЫ, и обезличивание было тихой потерей якоря.
|
||||
LANG_INS = {"d4": "китайским", "d3": "английским", "d6": "японским", "d1": "китайским"}
|
||||
LANG_INS = {"d4": "китайским", "d3": "английским", "d6": "японским", "d1": "китайским",
|
||||
"z17-zh": "китайским", "z17-en": "английским"}
|
||||
|
||||
|
||||
def render(fam: str, half: str, toks: list[str]) -> str:
|
||||
|
|
@ -52,7 +77,8 @@ def render(fam: str, half: str, toks: list[str]) -> str:
|
|||
body = m["tpl"].read_text(encoding="utf-8")
|
||||
body = (body.replace("{ЯЗЫКЕ}", LANG[AXIS]).replace("{КАТАЛОГ}", m["root"].name)
|
||||
.replace("{НАБОР}", half).replace("{ЯЗЫКОМ}", LANG_INS[AXIS]))
|
||||
lst = "\n".join(f" ~/{m['root'].name}/{half}-tasks/{t}.txt" for t in toks)
|
||||
body = body.replace(f"~/{m['root'].name}/", f"{m['root']}/")
|
||||
lst = "\n".join(f" {m['root']}/{half}-tasks/{t}.txt" for t in toks)
|
||||
return body.replace("{СПИСОК ФАЙЛОВ}", lst)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -251,6 +251,18 @@ def _twin_of_base(arm: str, uid: str, txt: str) -> bool:
|
|||
return bool(base.strip()) and txt.strip() == base.strip()
|
||||
|
||||
|
||||
def _cell_file(arm: str, uid: str):
|
||||
"""Файл клетки арма — ОДНА точка правды для `text_of` и для гейтов.
|
||||
|
||||
⚠ Раздвоение путей и было вторым дефектом finish-гейта. `text_of` знает, что клетки добивки
|
||||
лежат под своим префиксом (`dv-e-r1-…`, касса ФД-E), а гейт спрашивал `C.tag`, который для
|
||||
того же арма даёт `dv-a-r1-…` — файла нет, клетка молча выпадает из проверки. Так клетка
|
||||
`dv-e-r1-de3916de62.json` с finish=length прошла в судейскую пачку оси Д1.
|
||||
"""
|
||||
f = C.OUT / f"dv-e-r1-{uid}.json" if arm == "R1" else C.OUT / f"{C.tag(arm, uid)}.json"
|
||||
return f if f.exists() else None
|
||||
|
||||
|
||||
def text_of(arm: str, u: dict) -> str:
|
||||
"""Текст арма. Клетка, не прошедшая гейт годности, в пачку НЕ идёт — возвращается пусто."""
|
||||
uid = u["uid"]
|
||||
|
|
@ -413,9 +425,15 @@ def selftest() -> int:
|
|||
len(us) == EXPECT_N[AXIS], str(len(us)))
|
||||
u = us[0]
|
||||
# клетки finish=length не должны попадать в пачку ни одним армом
|
||||
lens = [(a, x["uid"]) for a in ARMS_D4 + ARMS_OLD for x in us
|
||||
if (C.OUT / f"{C.tag(a, x['uid'])}.json").exists()
|
||||
and json.loads((C.OUT / f"{C.tag(a, x['uid'])}.json").read_text()).get("finish")
|
||||
# ⚠ ОСЬ — ПАРАМЕТР И ЗДЕСЬ. Прежняя редакция перебирала ARMS_D4 + ARMS_OLD на ЛЮБОЙ оси,
|
||||
# то есть на d1/d3/d6 сертифицировала ЧУЖУЮ панель и была зелена вхолостую. Цена дефекта
|
||||
# замерена: клетка `dv-e-r1-de3916de62.json` с finish=length ушла в судейскую пачку оси Д1,
|
||||
# хотя норма Д0.6 это прямо запрещает, и гейт промолчал. Пере-счёт без неё: перевес R1/A0
|
||||
# у claude −0.100 → +0.000, у Sol +2.533 → +2.143; вердикты не переворачиваются, но норма
|
||||
# была нарушена и прибор об этом не сказал.
|
||||
lens = [(a, x["uid"]) for a in (ARMS + ARMS_OLD if AXIS == "d4" else ARMS) for x in us
|
||||
if _cell_file(a, x["uid"]) is not None
|
||||
and json.loads(_cell_file(a, x["uid"]).read_text(encoding="utf-8")).get("finish")
|
||||
!= "stop" and text_of(a, x)]
|
||||
ck("клетка finish=length в пачку не попадает", not lens, str(lens[:2]))
|
||||
# маржевый декой обязан РЕАЛЬНО сажаться, иначе «не пойман» = «не было»
|
||||
|
|
@ -459,7 +477,7 @@ def selftest() -> int:
|
|||
ck("половины пола НЕ равны побайтно (иначе контроль пуст)",
|
||||
not [1 for a, b in pairs if a == b])
|
||||
# половина пола не должна совпадать с боевой клеткой (урок «близнеца» пака 23)
|
||||
battle = {text_of(a, x) for a in ARMS_D4 for x in us}
|
||||
battle = {text_of(a, x) for a in ARMS for x in us} # ⚠ панель ОСИ, а не всегда d4
|
||||
ck("половина пола не совпадает с боевым армом",
|
||||
not [1 for a, b in pairs if a in battle or b in battle])
|
||||
# ключи лежат ВНЕ рабочего каталога судьи
|
||||
|
|
@ -467,11 +485,19 @@ def selftest() -> int:
|
|||
KEYS not in TASKS1.parents and TASKS1 not in KEYS.parents and KEYS.parent != WORK,
|
||||
f"{KEYS} против {WORK}")
|
||||
# семейство первичных контрастов совпадает с объявленным в power.py
|
||||
# ⚠ И СЕМЕЙСТВО — ТОЖЕ ПО ОСИ. Проверка была прибита к «Д4 edit-контур zh» и на трёх осях
|
||||
# из четырёх сверяла чужое объявление со своим — то есть не проверяла ничего.
|
||||
P = _load("power_d", ZONE / "power.py")
|
||||
declared = {c.split(" — ")[0] for c in P.PRIMARY["Д4 edit-контур zh"]}
|
||||
mine = {f"{a}/{b}" for a, b, _ in FAMILY_D4}
|
||||
ck("семейство совпадает с объявленным в power.py", declared == mine,
|
||||
f"{sorted(declared)} против {sorted(mine)}")
|
||||
pname = {"d4": "Д4 edit-контур zh", "d3": "Д3 en→ru несущая",
|
||||
"d1": "Д1 gemini добивка"}.get(AXIS)
|
||||
if pname and pname in P.PRIMARY:
|
||||
declared = {c.split(" — ")[0] for c in P.PRIMARY[pname]}
|
||||
mine = {f"{a}/{b}" for a, b, _ in FAMILY}
|
||||
ck(f"семейство оси совпадает с объявленным в power.py ({pname})", declared == mine,
|
||||
f"{sorted(declared)} против {sorted(mine)}")
|
||||
else:
|
||||
ck("ось объявлена ОПИСАТЕЛЬНОЙ — первичного семейства в power.py нет",
|
||||
AXIS == "d6", f"ось {AXIS}")
|
||||
print(f"\n{'СУДЕЙСКИЙ ХАРНЕСС ГОДЕН' if not bad else f'ПРОВАЛОВ: {bad}'}")
|
||||
return bad
|
||||
|
||||
|
|
|
|||
|
|
@ -122,8 +122,9 @@ def verdict(gap: float, thr: float, p: float) -> str:
|
|||
return "РАЗЛИЧИМ" if abs(gap) > thr and p < 0.05 else "в пределах"
|
||||
|
||||
|
||||
def report(tag: str = "vendor2", p: str = "en", anchor: str = "RN", drop: tuple = ()) -> int:
|
||||
r1, r2 = ranks_of(tag, p, "r1"), ranks_of(tag, p, "r2")
|
||||
def report(tag: str = "vendor2", p: str = "en", anchor: str = "RN", drop: tuple = (),
|
||||
da: str = "r1", db: str = "r2") -> int:
|
||||
r1, r2 = ranks_of(tag, p, da), ranks_of(tag, p, db)
|
||||
uids = [u for u in sorted(set(r1) & set(r2)) if u not in drop]
|
||||
if not uids:
|
||||
raise SystemExit("⛔ нет глав, прочитанных ОБОИМИ кругами")
|
||||
|
|
@ -188,7 +189,8 @@ def report(tag: str = "vendor2", p: str = "en", anchor: str = "RN", drop: tuple
|
|||
return 0
|
||||
|
||||
|
||||
def vkus(tag: str = "vendor2", p: str = "en", drop: tuple = ()) -> int:
|
||||
def vkus(tag: str = "vendor2", p: str = "en", drop: tuple = (),
|
||||
da: str = "r1", db: str = "r2", na: str = "fable-5", nb: str = "opus-5") -> int:
|
||||
"""СРАВНЕНИЕ ДВУХ СУДЕЙСКИХ СЕМЕЙСТВ — то, чего норме П-1 не хватало с 20.08.
|
||||
|
||||
⚠ Заведено 23.08 по слову владельца: «раз уж ты пробежал какое-то судейство другим агентом,
|
||||
|
|
@ -202,12 +204,12 @@ def vkus(tag: str = "vendor2", p: str = "en", drop: tuple = ()) -> int:
|
|||
ОТНОСИТЕЛЬНОЕ — «семейства по-разному упорядочивают», — а не «одно строже другого». Абсолютной
|
||||
строгости этот прибор не видит и видеть не может.
|
||||
"""
|
||||
r1, r2 = ranks_of(tag, p, "r1"), ranks_of(tag, p, "r2")
|
||||
r1, r2 = ranks_of(tag, p, da), ranks_of(tag, p, db)
|
||||
uids = [u for u in sorted(set(r1) & set(r2)) if u not in drop]
|
||||
arms = sorted(r1[uids[0]])
|
||||
print(f"\n=== ВКУС ДВУХ СЕМЕЙСТВ, {tag}: fable-5 (круг 1) против opus-5 (круг 2) ===")
|
||||
print(f"\n=== ВКУС ДВУХ СЕМЕЙСТВ, {tag}: {na} ({da}/) против {nb} ({db}/) ===")
|
||||
print(f" глав {len(uids)} · армов {len(arms)}\n")
|
||||
print(f" {'арм':6s}{'fable':>8s}{'opus':>8s}{'сдвиг':>8s}{'глав ↑':>8s}{'глав ↓':>8s}{'p':>9s} вывод")
|
||||
print(f" {'арм':6s}{na[:7]:>8s}{nb[:7]:>8s}{'сдвиг':>8s}{'глав ↑':>8s}{'глав ↓':>8s}{'p':>9s} вывод")
|
||||
rows = []
|
||||
for a in arms:
|
||||
d = [r2[u][a] - r1[u][a] for u in uids] # >0 — opus ставит НИЖЕ (место больше)
|
||||
|
|
@ -217,12 +219,12 @@ def vkus(tag: str = "vendor2", p: str = "en", drop: tuple = ()) -> int:
|
|||
rows.append((a, st.mean([r1[u][a] for u in uids]), st.mean([r2[u][a] for u in uids]),
|
||||
m, sum(1 for x in nz if x < 0), sum(1 for x in nz if x > 0), pv))
|
||||
for a, m1, m2, m, up, dn, pv in sorted(rows, key=lambda x: x[3]):
|
||||
mark = "⛔ opus судит ИНАЧЕ" if pv < 0.05 else ""
|
||||
mark = f"⛔ {nb} судит ИНАЧЕ" if pv < 0.05 else ""
|
||||
print(f" {a:6s}{m1:8.2f}{m2:8.2f}{m:+8.2f}{up:8d}{dn:8d}{pv:9.4f} {mark}")
|
||||
sig = [r for r in rows if r[6] < 0.05]
|
||||
print(f"\n армов, чьё место семейства ставят РАЗЛИЧИМО по-разному: {len(sig)} из {len(arms)}"
|
||||
+ ("" if not sig else " — " + ", ".join(r[0] for r in sig)))
|
||||
print(" ⚠ сдвиг >0 значит «opus ставит арм НИЖЕ, чем fable»; сумма сдвигов по построению ≈0")
|
||||
print(f" ⚠ сдвиг >0 значит «{nb} ставит арм НИЖЕ, чем {na}»; сумма сдвигов по построению ≈0")
|
||||
return 0
|
||||
|
||||
|
||||
|
|
@ -267,6 +269,10 @@ if __name__ == "__main__":
|
|||
_tag = next((x.split("=", 1)[1] for x in a if x.startswith("--tag=")), "vendor2")
|
||||
_anch = next((x.split("=", 1)[1] for x in a if x.startswith("--anchor=")), "RN")
|
||||
_drop = tuple(next((x.split("=", 1)[1].split(",") for x in a if x.startswith("--drop=")), []))
|
||||
_da = next((x.split("=", 1)[1] for x in a if x.startswith("--a=")), "r1")
|
||||
_db = next((x.split("=", 1)[1] for x in a if x.startswith("--b=")), "r2")
|
||||
_na = next((x.split("=", 1)[1] for x in a if x.startswith("--na=")), "fable-5")
|
||||
_nb = next((x.split("=", 1)[1] for x in a if x.startswith("--nb=")), "opus-5")
|
||||
if "--vkus" in a:
|
||||
sys.exit(vkus(_tag, "en", _drop))
|
||||
sys.exit(report(_tag, "en", _anch, _drop))
|
||||
sys.exit(vkus(_tag, "en", _drop, _da, _db, _na, _nb))
|
||||
sys.exit(report(_tag, "en", _anch, _drop, _da, _db))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue