Correct the erratum I published an hour ago: the engine hole it declared open was closed on 28 August, so only the cost of the pre-fix defect changes

This commit is contained in:
heaven 2026-08-29 21:41:29 +03:00
parent d7a202edc6
commit 3fff642a43
2 changed files with 31 additions and 12 deletions

View file

@ -155,7 +155,9 @@
## Контент-фильтры / safety
- ⛔⛔ **Gemini, ДВА СИСТЕМНЫХ СООБЩЕНИЯ = ПРОМТ ТЕРЯЕТСЯ МОЛЧА (полигон 22.08, живая проба `eval/dovodka/proba_gemini.py`).** **МЕХАНИЗМ НАЗВАН** (вендор-дока `ai.google.dev/api/generate-content`): в нативном API `systemInstruction` — это ОДИН объект `Content`, поэтому слой ФИЗИЧЕСКИ не может пронести больше одного системного, и уход на нативный API проблемы НЕ решает — там пришлось бы склеивать так же. При двух и более лишние выбрасываются без ошибки. ⛔ **УСТАНОВЛЕНО 29.08: ВЫЖИВАЕТ ПОСЛЕДНЕЕ СИСТЕМНОЕ, ВЫБРАСЫВАЕТСЯ ПЕРВОЕ** (полигон, Д35.3 отчёта 23; $0, детерминированно, на 16 уже купленных клетках). Доказательство арифметическое: у арма `RE` первое системное (ролевой промт) **побайтно одинаково на всех 16 главах**, второе (глоссарий) — **разное** (255404 знака, термины из текста главы); разность `RE.prompt_tokens RQ.prompt_tokens` равна **2436 РОВНО на каждой из 16 глав, sd = 0.00** ⇒ выброшено сообщение ПОСТОЯННОГО размера, а таково только первое; будь выброшены оба, разность росла бы вместе с глоссарием. Побочно: `prompt_tokens` карантинных клеток коррелируют с длиной глоссария +0.52 и с длиной исходника +0.04 — глоссарий внутри них физически есть. ~~⚠ КАКОЕ именно выживает — НЕ УСТАНОВЛЕНО~~ — форум Google (discuss.ai.google.dev/t/86097, 30.05.2025) был ПРАВ, наша прошлая интерпретация «не выжило НИ ОДНО» неверна: она сравнила 535 токенов с «размером одного user-сообщения» на глаз, а при шкале 0.324 ток/знак русского глоссарий в 300 знаков весит ~100 токенов и в такой оценке теряется. Документированного вендором ограничения в разделе «Current limitations» НЕТ. Замер на одном и том же системном тексте в 7970 знаков: ОДНИМ сообщением → `prompt_tokens` 2994 и маркерная инструкция ИСПОЛНЕНА; ДВУМЯ → **535 токенов (это размер одного user-сообщения) и инструкция НЕ исполнена**. Ответ приходит с `finish=stop` и выглядит правильным переводом — отказа нет, есть тихая потеря инструкции, невидимая всем гейтам годности. ⚠ Так были куплены 16 клеток арма `RE` (промт-роль на gemini): они переведены ВООБЩЕ БЕЗ ИНСТРУКЦИИ, и вердикт о переносимости из них был бы ложным. Лечение: склеивать системные в одно ДЛЯ ЭТОГО ВЕНДОРА (`rol.ONE_SYSTEM`/`fit_msgs`) — общая склейка сменила бы форму запроса у всех моделей и рассогласовала бы уже купленные клетки. ⚠ Проверено, что у `deepseek-v4-pro`, `gpt-5.6-luna`, `glm-5` и `grok-4.3` два системных доезжают целиком.
- ⛔⛔ **ТОТ ЖЕ ДЕФЕКТ ЖИВЁТ В ДВИЖКЕ, А НЕ ТОЛЬКО В ПОЛИГОНЕ (аудит 22.08).** `backend/internal/pipeline/render.go:256-258` штатно строит ВТОРОЕ системное сообщение — инъекцию банка памяти (глоссарий/STM), `backend/internal/llm/httpllm.go:517-522` копирует сообщения дословно, склейки под Gemini нет нигде, а `gemini-3.1-pro-preview` стоит в ратифицированной цепочке эскалации во ВСЕХ арм-конфигах (`pipeline-c1.yaml:129`, `pipeline-arm-glm.yaml:61`, `pipeline-arm-deepseek-pro.yaml:62`, `pipeline-arm-mistral.yaml:61`). ⇒ ~~**любой эскалационный хоп на Gemini при непустой инъекции ТИХО теряет глоссарий.**~~ ⛔⛔ **ФОРМУЛИРОВКА ИСПРАВЛЕНА 29.08 И СТАЛА ХУЖЕ, А НЕ МЯГЧЕ.** По установленному выше правилу выживает ПОСЛЕДНЕЕ системное, а `render.go:247-258` кладёт первым **сам промт стадии**, вторым — инъекцию банка. ⇒ **на эскалационном хопе в Gemini при непустой инъекции движок теряет НЕ глоссарий, а ВЕСЬ ПРОМТ СТАДИИ, и оставляет глоссарий.** Модель получает список канонных форм и текст — без единого слова о том, что с ними делать. Ровно так выглядят 16 клеток `RE`: перевод есть, `finish=stop`, русский, правильной длины, инструкции за ним нет. **Тихая потеря промта не отличима от нормального ответа ничем, кроме счётчика входных токенов.** Лечение прежнее (склейка системных под этого вендора), приоритет — выше. Оговорка D22.3 «экспозиция Ф1 нулевая» держится только на том, что до Gemini в дефолте не доходят. ⚠ Класс в проекте уже был решён рядом: `backend/internal/llm/provider_anthropic.go:169-179` сворачивает ведущий system-префикс в одно поле и падает громко на system посреди диалога — просто не обобщён на Gemini. **Зона бэкенда, пинг оркестратору отправлен.**
- ⛔⛔ **ТОТ ЖЕ ДЕФЕКТ ЖИВЁТ В ДВИЖКЕ, А НЕ ТОЛЬКО В ПОЛИГОНЕ (аудит 22.08).** `backend/internal/pipeline/render.go:256-258` штатно строит ВТОРОЕ системное сообщение — инъекцию банка памяти (глоссарий/STM), `backend/internal/llm/httpllm.go:517-522` копирует сообщения дословно, склейки под Gemini нет нигде, а `gemini-3.1-pro-preview` стоит в ратифицированной цепочке эскалации во ВСЕХ арм-конфигах (`pipeline-c1.yaml:129`, `pipeline-arm-glm.yaml:61`, `pipeline-arm-deepseek-pro.yaml:62`, `pipeline-arm-mistral.yaml:61`). ⇒ **любой эскалационный хоп на Gemini при непустой инъекции ТИХО терял текст.** Оговорка D22.3 «экспозиция Ф1 нулевая» держалась только на том, что до Gemini в дефолте не доходят.
**ДЕФЕКТ ДВИЖКА ЗАКРЫТ 28.08 лендингом `7d0c6f2`** — проверено кодом 29.08, а не пересказано: у провайдера `gemini` объявлено `system_messages: single` (`backend/configs/models.yaml:137`=`system_messages: single`), адаптер склеивает ВЕДУЩИЙ system-ран в одно сообщение и падает громко на системном ходе после не-системного (`backend/internal/llm/httpllm.go:528`=`func toOpenAIMessages`), инвариант запинен тестом (`backend/internal/llm/systemmessages_test.go:78`=`TestSystemMessagesSingleCarriesTheInjection`). ⇒ строки выше про движок — ИСТОРИЯ, а не открытый долг.
**НО ЦЕНА ДЕФЕКТА ДО ПОЧИНКИ ОЦЕНИВАЛАСЬ НЕВЕРНО, и это правится здесь (полигон 29.08, Д35.3).** По установленному выше правилу выживает ПОСЛЕДНЕЕ системное, а `render.go:247-258`=`Role: "system"` кладёт первым **сам промт стадии**, вторым — инъекцию банка. ⇒ до 28.08 хоп в Gemini терял **не глоссарий, а ВЕСЬ ПРОМТ СТАДИИ**, оставляя глоссарий: модель получала список канонных форм и текст — без единого слова о том, что с ними делать. Ровно так выглядят 16 клеток `RE` полигона: перевод есть, `finish=stop`, русский, правильной длины, инструкции за ним нет. **Тихая потеря промта не отличима от нормального ответа ничем, кроме счётчика входных токенов.** ⇒ решение «склеивать самим» было единственно верным и при неизвестном тогда чтении квирка; а любой ИСТОРИЧЕСКИЙ вывод, снятый на Gemini-хопе до 28.08, надо читать как «стадия работала без своего промта». ⚠ Класс в проекте уже был решён рядом: `backend/internal/llm/provider_anthropic.go:169-179` сворачивает ведущий system-префикс в одно поле и падает громко на system посреди диалога — просто не обобщён на Gemini. **Зона бэкенда, пинг оркестратору отправлен.**
- **Gemini**: `safety_settings` через OpenAI-совместимый слой **не передаются** (HTTP 400 «Unknown name safety_settings»). Дефолт фильтров у 2.5/3 — OFF (gap-2). Для явного контроля фильтров (роль судьи 18+) нужен **нативный** Gemini API, не OpenAI-слой. ⚠ **D22.6: на ГРАФИЧНОЙ эротике Gemini 3.x fail-closed `PROHIBITED_CONTENT` (8× wire-verified, exp11) — фильтр НЕконфигурируем, нативный API не спасает; судья эротики — только Grok. На violence/SFW Gemini-судья жив.** `finish_reason` приходит СОСТАВНОЙ строкой `'content_filter: PROHIBITED_CONTENT'` (точные матчеры мертвы — D22.8а). Биллинг Gemini: thinking ТОЛЬКО в `total_tokens``reasoning: additive_total`, иначе недоучёт 146×/вызов (D22.3). Нативное имя поля — `usageMetadata.thoughtsTokenCount`; слой AI-Studio `completion_tokens_details.reasoning_tokens` не отдаёт ВОВСЕ, тогда как Vertex AI то же поле отдаёт (форум 15.01.2026) — при переезде на Vertex учёт менять. ⚠⚠ **`reasoning_tokens=0` у Gemini — НОРМА, а не измерение: вывод «размышление выключено» из этого поля НЕДЕЙСТВИТЕЛЕН.** Полигон 22.08 на этом и поскользнулся, объявив «вендор-дефолт flash-lite = размышление ВЫКЛ (замерено)»; считать надо `total prompt completion`. ⚠ **В роли редактора Gemini течёт сервис-преамбулой в выход («Вот отредактированный…», 6/6 чанков — exp12) → за output-санитайзером D30.3.****Дополнение эксп-21 (08.08):** fail-closed ответ OpenAI-слоя приходит вовсе БЕЗ объекта `message` (не с пустым `content`) — парсер обязан переживать его отсутствие; на вебновелльном violence-материале срабатывания массовые (10 клеток судейского прогона с `content_filter: PROHIBITED_CONTENT`) ⇒ оговорка «на violence/SFW Gemini-судья жив» целиком НЕ держится — Gemini непригоден как ЕДИНСТВЕННЫЙ второй контур на этом материале (эксп-21 §1/§8).
- **Qwen Model Studio**: внешний фильтр `data_inspection_failed` на вход/выход, неотключаем (риск молчаливых вырезаний) — не тестировано (нет ключа).
- Детект отказов/вырезаний — см. `eval/refusal_bench.py` (паттерны soft-отказа + coverage-гейт по len_ratio/sent_cov, пороги в эксп. 02).

View file

@ -5172,7 +5172,7 @@ fable-5: медиана ρ +0.52 · среднее +0.55 · глав согла
### Д35.2 ⛔ ПЕРЕЕЗД ОБНУЛИЛ mtime, И ОДИН ГЕЙТ ФАЗЫ БОЛЬШЕ НЕ ПЕРЕ-СНИМАЕТСЯ
`eval/tenant_panel/verify22.py:174-185` выводит верхнюю границу двойной оплаты Ф2 как «сумма
`eval/tenant_panel/verify22.py:174-185`=`граница двойной оплаты` выводит верхнюю границу двойной оплаты Ф2 как «сумма
клеток, купленных ДО коммита атомарного замка `fd3e522`», и отделяет «до» от «после»
**временем файла на диске**. После переезда **все 335 клеток Ф2 имеют ОДИН И ТОТ ЖЕ mtime**
(проверено: `уникальных mtime: 1`), причём поздний. ⇒ граница считается как `$0.000000`,
@ -5184,7 +5184,7 @@ fable-5: медиана ρ +0.52 · среднее +0.55 · глав согла
из сырья больше НЕ ВЫВОДИМА ничем.
**R71 объявляется НЕ ПЕРЕ-СНИМАЕМЫМ на этой машине.** Гейт не правится под зелень (D39.121):
подгонка спрятала бы ровно тот факт, что провенанс покупок держался на файловой системе.
**Класс шире одного гейта:** `runs.py:166` и `role_topology/absjudge.py:323` атрибутируют
**Класс шире одного гейта:** `eval/dovodka/runs.py:166`=`def judge_of` и `eval/role_topology/absjudge.py:323`=`path.stat().st_mtime` атрибутируют
судью ответу тем же способом — по mtime файла ответа. Для БУДУЩИХ чтений это работает (файлы
пишутся сейчас), для исторических — атрибуция утрачена там же и тогда же.
**Норма на будущее: время покупки и время ответа писать ПОЛЕМ В АРТЕФАКТ.** Файловая система —
@ -5217,20 +5217,37 @@ RE.prompt_tokens RQ.prompt_tokens = 2436 на КАЖДОЙ из 16 гла
user-сообщения» на глаз. 2 436 токенов на 7 525 знаков русского — это 0.324 ток/знак; при такой
шкале глоссарий в 300 знаков весит ~100 токенов и в оценке «на глаз» просто теряется.
**⛔ И это меняет вывод про ДВИЖОК в худшую сторону.** `backend/internal/pipeline/render.go:247-258`
кладёт первым `system`**сам промт стадии**, вторым `system` — инъекцию банка (глоссарий/STM).
Записанное следствие звучало как «эскалационный хоп на Gemini тихо теряет глоссарий»
(`00-provider-quirks.md:158`). По установленному правилу всё наоборот:
**⛔ И это правит записанную ЦЕНУ движкового дефекта — но не открывает его заново.**
`backend/internal/pipeline/render.go:247-258`=`Role: "system"` кладёт первым `system` — **сам промт
стадии**, вторым `system` — инъекцию банка (глоссарий/STM). Записанное следствие звучало как
«эскалационный хоп на Gemini тихо теряет глоссарий» (`00-provider-quirks.md:158`). По
установленному правилу всё наоборот:
> **на эскалационном хопе в Gemini при непустой инъекции банка движок теряет НЕ глоссарий, а ВЕСЬ
> ПРОМТ СТАДИИ, и оставляет глоссарий.**
> **до починки эскалационный хоп в Gemini при непустой инъекции терял НЕ глоссарий, а ВЕСЬ ПРОМТ
> СТАДИИ, и оставлял глоссарий.**
То есть модель получает список канонных форм и текст — без единого слова о том, что с ними делать.
То есть модель получала список канонных форм и текст — без единого слова о том, что с ними делать.
Ровно это и произошло с 16 клетками `RE`, и ровно так они выглядят: перевод есть, `finish=stop`,
русский, правильной длины — и никакой инструкции за ним. **Тихая потеря промта не отличима от
нормального ответа ничем, кроме счётчика входных токенов.**
⇒ Пинг оркестратору (зона бэкенда): лечение прежнее — склейка системных под этого вендора, — но
**приоритет и формулировка дефекта в бэклоге неверны и подлежат правке**.
**ДЕФЕКТ ДВИЖКА УЖЕ ЗАКРЫТ, И Я ЭТО ПРОВЕРИЛ КОДОМ, А НЕ ПРЕДПОЛОЖИЛ.** Лендинг `7d0c6f2`
(28.08) ввёл ось `Capability.SystemMessages`: у провайдера `gemini` стоит
`backend/configs/models.yaml:137`=`system_messages: single`, адаптер склеивает ВЕДУЩИЙ system-ран в
одно сообщение и громко падает на системном ходе после не-системного
(`backend/internal/llm/httpllm.go:528`=`func toOpenAIMessages`), инвариант запинен тестом
(`backend/internal/llm/systemmessages_test.go:78`=`TestSystemMessagesSingleCarriesTheInjection`).
⚠ Первая редакция ЭТОГО ЖЕ абзаца, написанная час назад, объявляла дефект живым и слала пинг в
бэкенд — она стояла на строке `00-provider-quirks.md:158`, устаревшей на один день.
**Класс ошибки тот же, что фаза ловит у себя постоянно: доверился заголовку дока вместо кода.**
Поймано собственной сверкой до публикации вывода, но ПОСЛЕ того, как формулировка уже уехала
в коммит `a6c50fc` — поэтому исправление стоит здесь, а история не переписывается.
**Что находка меняет по существу:** (а) закрывает открытое расхождение в квирк-доке;
(б) подтверждает, что «склеивать самим» было верным решением и при тогда ещё неизвестном чтении
квирка — оно не могло потерять текст ни при каком ответе на вопрос «какое выживает»;
(в) **любой ИСТОРИЧЕСКИЙ результат, снятый на Gemini-хопе до 28.08, надо читать как «стадия
работала без своего промта»** — включая 16 клеток `RE` этой фазы.
---