48 KiB
Бэкенд-пак: ЗА ОДНУ ЕДИНИЦУ ПЛАТИМ ДВАЖДЫ — и по двум РАЗНЫМ причинам, которые движок не различает
Выдан оркестратором №23, 11.09. Зона записи —
backend/. Пак КОДОВЫЙ, платных вызовов НЕ несёт. ⚠ Редакция 2. Первая была построена на перевёрнутой посылке («движок лечит болезнь возбудителем») и заказывала отключить восстановление, которое на замере работало. Опровергатель это снял; ниже — исправленное, и след правки виден в §1.
1. Какая проблема и что решит твой результат
На живом платном прогоне книги 11.09 движок заплатил дважды за четыре единицы из двадцати семи: первая попытка вернула HTTP 200 без пригодного текста, выходные токены были выкуплены и выброшены, вторая попытка вернула текст. Цена выброшенного — $0.106472 = 25.4 % всей цены книги. Из них три вызова дали РОВНО ноль знаков ($0.094835 = 22.6 %), четвёртый — обрезок с текстом ($0.011637).
⛔ И вот что надо знать ДО того, как начнёшь чинить: лекарство работает. Перевыпуск с удвоенным потолком вернул текст 4 раза из 4. Претензия к нему ДЕНЕЖНАЯ (оплачена выброшенная попытка), а не медицинская. Не выключай его. Первая редакция этого промта утверждала обратное, и сессия, исполнившая её буквально, отключила бы восстановление трёх чанков из четырёх.
⛔⛔ РЕЛЕЙ 11.09, РЕДАКЦИЯ 3 — ЭХО СЕССИИ ОПРОВЕРГЛО РАСКЛАДКУ РЕДАКЦИИ 2, И Я СОГЛАСИЛСЯ. Читай ЭТО, а таблицу ниже — через эту поправку.
Редакция 2 делила четыре двойные оплаты 3:1 — «трижды потолка не хватило, один раз съело размышление». Это неверно, и опровергается полем, которое движок пишет САМ. Пере-снято мною по той же базе:
| id | стадия | comp 1-й | finish |
degraded |
цена |
|---|---|---|---|---|---|
| 1 | draft ch1u0 |
8496 | length |
empty |
$0.012044 |
| 5 | draft ch2u1 |
8496 | length |
length |
$0.011637 |
| 7 | draft ch3u0 |
8496 | length |
empty |
$0.011782 |
| 31 | edit ch2u0 |
16000 | length |
empty |
$0.071009 |
⇒ empty ×3 = $0.094835 = 89 % всей потери; length ×1 = $0.011637. Раскладка ОБРАТНАЯ моей.
И порядок решения в коде это подтверждает: disposition.go проверяет пустоту ДО обрезания
(if out == "" → FlagEmpty, и только потом finish == FinishLength → FlagLength), то есть
degraded='empty' значит РОВНО НОЛЬ знаков — весь потолок ушёл до того, как начался текст.
Почему мой довод был неверен, дословно из эхо сессии: у DeepSeek reasoning ⊆ completion, значит
completion_tokens ВТОРОЙ попытки тоже содержит размышление. ⇒ из «вторая дала 15869 > потолка 8496» НЕ
следует «потолка не хватило ТЕКСТУ»: 15869 — это текст ПЛЮС неизвестное размышление.
⛔ И мой «контроль 6 из 6 без исключений» — ТАВТОЛОГИЯ, а не улика. «Кто уложился в потолок — прошёл,
кто не уложился — упал» верно ПО ПОСТРОЕНИЮ (comp ≤ cap ⇒ stop, иначе length). Он говорит, что потолок
бьётся с comp, и ничего — про то, чем comp занят. Из акта и заказа снят.
⭐ И вот что делает пак ИСПОЛНИМЫМ, а мою редакцию делало неисполнимой. Мой дискриминатор
(«2-я больше/меньше потолка 1-й») РЕТРОСПЕКТИВЕН: он известен только после того, как вторая попытка
ОПЛАЧЕНА, и лекарство им выбрать нельзя. А различение, которое нужно, уже есть в дереве и снимается ДО
ретрая: FlagEmpty против FlagLength. ⇒ слепо именно ЛЕКАРСТВО, а не классификация:
maxTokensForAttempt(base, escalations) принимает счётчик удвоений и больше ничего. Это и есть «дать
существующему лекарству прочитать существующий класс», буквально.
⛔ ЧЕТВЁРТАЯ ПОПРАВКА: «шаг вниз по нейтральному словарю вырожден» — НЕВЕРНО, и снимается. Словарь
упорядочен лексически ("" · off · low · medium · high), но ЭФФЕКТИВНЫЙ порядок размышления у
ReasoningNone другой: low < medium < high = off = "" — off не эмитит ничего и едет
вендор-дефолт high. ⇒ off есть ВЕРХ шкалы, и шаг вниз с него на low существует, а носитель
самого дорогого случая (edit, $0.071009 = две трети потери) стоит именно на off. Строй ступень
ПАРАМЕТРОМ; боевое значение по-прежнему не трогай (§4.2).
⇒ ЧИТАЙ ТАБЛИЦУ §4.1 ТАК: строка «потолок мал» применима к ОДНОМУ случаю (degraded='length',
обрезок с текстом), строка «размышление съело потолок» — к ТРЁМ, и класс берётся из degraded, а не из
сравнения потолков. Коллизия, которую я вводил поправкой (б) в §4.1, исчезает вместе с моим
дискриминатором: degraded относит каждую единицу ровно к одному классу.
Что решит твой результат. Движок научится отличать «не хватило потолка» от «потолок съело размышление»
и перестанет платить за первое по цене второго. Плюс причина станет ВИДНА: сегодня спросить «что
случилось» нечем — колонка reasoning_tokens не несла числа ни разу за историю проекта, а цена считается
подстановкой, о которой никто не узнаёт.
⛔ Чего этот пак НЕ делает: не выключает перевыпуск (он работает) · не меняет боевые значения
reasoning в конфигах (ждёт полигонного замера, §4.2) · не правит пин цены flash (данные, ряд 413) · не
трогает books/ · не заводит новых стадий пайплайна.
2. Зона записи и git
Зона — backend/. Читать можно всё; писать — только в свою зону.
⛔ Ты НЕ коммитишь. Коммитит оркестратор при лендинге. Готовое дерево оставляешь как есть и
сообщаешь. Не делай git add, git commit, git stash, reset --hard, checkout поверх грязного
дерева: в дереве параллельно работают другие сессии, и их незакоммиченные правки стираются безвозвратно.
Канон — CLAUDE.md §«Git-координация мультисессий», он сильнее этого абзаца.
Свои итоги, пинги и вопросы пиши в СВОЮ секцию «Бэкенд» журнала docs/PROGRESS.md. Это единственное
исключение из «docs/ — зона оркестратора».
3. Карта чтения (ЗАКОН, пять позиций; дальше — только по её ссылкам)
CLAUDE.md— ты его уже получила.docs/experiments/00-provider-quirks.md, пункт 3 ЦЕЛИКОМ (подпункты 3а · 3б · 3в · 3г · 3д — именно все пять). ⛔ Читать ДО любой правки адаптеров и вызовов; здесь их копий нет, а §3г переписывался ТРИЖДЫ и две прежние редакции были неверны — читай действующую, не мой пересказ.backend/README.md— механика движка.docs/architecture/12-go-style-notes.md— норматив стиля, только больные места.- Ряды бэклога 414 · 415 · 422 и 413 (
docs/BACKLOG.md) — носители замеров, на которых стоит пак.
Код-факты добывай САМА. Все file:line ниже — отправные точки, а не истина: истина — дерево.
Если адрес уехал, это находка, и она идёт в отчёт.
Ратифицированное, что тебе понадобится (вложено сюда, грепать не надо):
- ⭐
D2— основание пака, и читать надо ОБА уточнения, 2 и 3. Решение:finish=length→ ретрай с бо́льшимmax_tokensдо капа, потом флаг; пустой оплаченный → skip+flag (не падение прогона). ⛔ п.2 ратифицировал, чтоemptyПЕРЕАТАКУЕТСЯ: «retry-flaggedпереатакует толькоlength/emptyна той же модели». Это исполнено и ЗАПИНЕНО —internal/pipeline/disposition.go:211=func (r FlagReason) retryable() boolfunc (r FlagReason) retryable() bool { return r == FlagLength || r == FlagEmpty }, пинTestRetryableSubset(disposition_test.go:90=TestRetryableSubset). ⇒ всякое изменение ТОГО, ЧТО переатакуется, отменяетD2п.2 и двигает этот пин — это пинг мне ДО кода, а не правка. Не соблазняйся «подогнать»TestRetryableSubsetкак заказанную смену поведения: она НЕ заказана. ⭐ п.3 — принцип, который ты распространяешь: «Детектор вырождения ПЕРЕД удвоением max_tokens… бо́льший max_tokens лишь оплачивает больше повтора… при вырождении сразу флаг, без оплаты цикла». Он исполнен ДЛЯ ОДНОЙ причины (петля повтора,disposition.goрядом сdistinctRatio) и не исполнен для второй — «размышление съело потолок», которую тогда не знали. Твоя работа — распространить наш же образец, а не завести новый. D24п.3 — per-model флорmin_max_tokensРАТИФИЦИРОВАН схемой (capability-слойmodels.yaml, не комментарий); флор уdeepseek-v4-flash/proтам назван 8000. ⚠ Подъёмproдо 16000 — НЕ из ноты: этоbackend/configs/models.yaml(блокdeepseek-v4-pro,min_max_tokens: 16000) по замеру мини-прогона 25.07, и цена того замера — 15.5 % счёта прогона в двух выброшенных генерациях.D39.86/D39.87(02.08) — гипотеза «поднять флор» ФАЛЬСИФИЦИРОВАНА замером. ⛔ Но узко, иD39.90п.9 это прямо сузил, сняв над-клейм: «число флора влияет на цену ретрая, а не на проходимость» — незамеренная экстраполяция; приlowфлор не варьировался НИ РАЗУ. ⇒ «потолок не рычаг» проверено на вендор-дефолтном эффорте; наlowне проверено никем — а черновик прогона шёл именно наlow. Это прямо касается §4.1: три из четырёх двойных оплат живут в непроверенной области.D39.87§2 — приёмочный вопрос, который держит тебя от тиражирования дефекта. Дословно: «появится ЧЕТВЁРТАЯ такая стадия — она унаследует ручку сама или её опять забудут?». Ответ в дереве уже структурный: единственная деривация стадии для внутреннего вызова —internal/config/internal_call.go, единственный словарь эффорта —ValidReasoningEffortтам же, и оба стережёт статический анализаторinternal/archguard/(«an engine-internal call must derive its stage through config.InternalCall.Stage()»), который гоняется обычной батареей. ⇒ новое измерение решения обязано ехать ЭТИМ швом. Рукописныхconfig.Stage{…}в шиппинг-коде сегодня ноль — проверь сама и не заведи первый.D39.121— тесты и гейты не подгонять под зелень. Правка теста, вызванная ЗАКАЗАННОЙ этим паком сменой поведения, — обслуживание, и её надо ОБЪЯВИТЬ в отчёте (что изменилось, какой тест это описывал, куда уехала гарантия). Правка чтобы прошло — недопустима.
4. Заказ. У каждого пункта размечена свобода
4.1. Две причины — два лекарства — ЯДРО ПАКА · «реши сама и аргументируй»
Что уже построено и что ты ЧИТАЕШЬ, а не заводишь. Классификация отказа есть и сохраняется:
FlagEmpty/FlagLength (internal/pipeline/disposition.go:59=2xx with no usable text), degradedTag (stagerun.go:921=degradedTag surfaces a flag reason),
колонка request_log.degraded. ⭐ И дискриминатор «размышление внутри выходных токенов» ТОЖЕ уже
построен и живёт в ДАННЫХ: в backend/configs/models.yaml у провайдера deepseek стоит
reasoning: subset, у xai — additive, у gemini — additive_total; поле объявлено в
internal/config/models.go (Reasoning string, комментарий subset | additive | additive_total),
резолвер — providerReasoning() там же, словарь — internal/llm/provider_openai.go
(ReasoningSubset/ReasoningAdditive/ReasoningAdditiveTotal). ⇒ ось «subset против additive»
ЧИТАЕТСЯ. Второй разметки не заводить. Найдёшь расхождение — находка, в отчёт.
А выбор лекарства этого не спрашивает: maxTokensForAttempt(base, escalations)
(internal/pipeline/disposition.go:481=func maxTokensForAttempt) принимает счётчик удвоений и больше ничего.
Что построить: различение двух причин и разные ответы на них. Приор — опровергается аргументом или замером, и я жду именно опровержения, если оно у тебя будет:
| причина | улика в данных | сегодня | приор |
|---|---|---|---|
| потолок мал — ответ не влез | 2-я попытка дала БОЛЬШЕ, чем потолок 1-й | удвоить и заплатить дважды | чинить ОЦЕНКУ первого потолка, а не перевыпуск: единица, которой заведомо мало, не должна покупать заведомо провальную попытку |
| размышление съело потолок | 2-я попытка дала МЕНЬШЕ, чем потолок 1-й (edit: 11717 < 16000) |
удвоить — то есть дать размышлению вдвое больше места | ступень эффорта ниже при ТОМ ЖЕ потолке — но см. ⛔ ниже, величина не замерена |
length с текстом |
обрезок доехал | удвоить | оставить как есть — это ровно решение D2 |
⛔⛔ ДВЕ ПОПРАВКИ К ТАБЛИЦЕ, СНЯТЫЕ ЗАМЕРОМ 11.09 — читай их прежде, чем исполнять её.
(а) «Ступень эффорта ниже» СЕГОДНЯ ВЫРОЖДЕНА НА ВСЕХ БОЕВЫХ ЗНАЧЕНИЯХ. Замер: во всех четырёх живых
configs/pipeline-*.yaml значений reasoning: двенадцать, и все — low (7) или off (5). Словарь
движка упорядочен "" · off · low · medium · high (ValidReasoningEffort), и ниже low эмитирующей
ступени НЕТ: off и пустая строка не эмитят ничего, то есть едет вендор-дефолт high — это БОЛЬШЕ
размышления, а не меньше. ⇒ шаг вниз по нейтральному словарю на сегодняшнем конфиге некуда сделать.
Носитель дорогого случая (edit) стоит именно на off. Из трёх законных выходов §4.1 первый
(«шаг вниз по нейтральному словарю») снимается как вырожденный, пока боевые значения такие; остаются
второй (решать через оценку первого потолка) и третий (пер-модельная таблица — только пингом до кода).
(б) ОДНА ЕДИНИЦА ПРОГОНА ПОПАДАЕТ В ДВЕ СТРОКИ ТАБЛИЦЫ СРАЗУ, и приоритета в ней не было.
draft ch2u1 (degraded='length', обрезок с текстом, ретрай дал 13004 > 8496) удовлетворяет и строке
«потолок мал» (2-я попытка больше потолка 1-й), и строке «length с текстом → оставить как есть». ⇒
приоритет задаю: строка «length с текстом» применима ТОЛЬКО если ретрай уложился в СТАРЫЙ потолок.
Не уложился — значит потолка и вправду не хватало, и побеждает первая строка. Иначе единственный носитель
третьей строки уходит в неё же и класс остаётся непроверяемым.
⛔ ЧЕГО В ТАБЛИЦЕ НЕТ И НЕ ДОЛЖНО БЫТЬ: «не перевыпускать». На замеренных данных это стоило бы трёх
восстановленных чанков из четырёх и отменяло бы D2 п.2. Вопрос — в ЦЕНЕ попытки, а не в её отмене.
⛔ И ВОТ ГДЕ ВЫРАСТЕТ ВЕЛОСИПЕД, ЕСЛИ НЕ СМОТРЕТЬ. «Ступень ниже» требует знать, какие ступени есть у
ЭТОГО провайдера, — а словарь эффорта в движке намеренно провайдер-СЛЕПОЙ, и это объявлено известным
пределом: доккомментарий ValidReasoningEffort дословно — «Validating against the RESOLVED model's
capability would need a new per-model table — a new mechanism, not a use of an existing one — so it stays
a named finding, not a silent gap». ⇒ пер-модельная таблица ступеней — НОВЫЙ механизм, молча его не
заводить. Три законных выхода, выбирай с доводом: (1) шаг вниз по НЕЙТРАЛЬНОМУ словарю движка; (2) не
трогать эффорт вовсе и решать денежную задачу через оценку первого потолка — тогда таблица не нужна
совсем; (3) таблица — но это отдельное решение и пинг мне ДО кода. ⚠ Разница с осью subset/additive
выше именно в этом: та построена и читается, эта не построена и без письма не заводится.
⛔ ДВЕ ОГОВОРКИ О СИЛЕ УЛИК, обе обязательны в твоём отчёте.
(а) Величина среза размышления НЕ УСТАНОВЛЕНА. Квирки §3г: параметр low до pro ДОХОДИТ (доказано
поведенческой пробой 30.08), но «ВЕЛИЧИНА среза по-прежнему не установлена… наблюдённые ×2.52 сняты
блоками, разнесёнными на 9 дней». То есть «ступень ниже» — законный ПАРАМЕТР, но не доказанное лекарство.
(б) Весь денежный довод стоит на ЧЕТЫРЁХ одиночных розыгрышах. Квирки §3д: «два одиночных розыгрыша
дефолтного пути deepseek-v4-pro разошлись в 38 раз… любая смета DeepSeek, снятая с ОДИНОЧНОГО прошлого
вызова, может ошибиться на порядок… сметы и сравнения нужно строить на ПОВТОРАХ». ⇒ у моих чисел есть ЗНАК
и нет ВЕЛИЧИНЫ. Контроль «6 из 6 без исключений» (§1) знак усиливает, но повторами не является.
⛔ Никаких велосипедов в остальном. Не заводи вторую таксономию отказов, не пиши свой классификатор ответа, не добавляй параллельный счётчик попыток. Работа — дать существующему лекарству прочитать существующий класс. Если существующая классификация окажется непригодной — это находка, назови её и аргументируй, прежде чем строить рядом.
4.2. Ручка усилия у редактора — ⛔ НЕ ТРОГАЙ БОЕВОЙ КОНФИГ · «делай РОВНО так»
Соблазн: «на edit стоит reasoning: "off", вызов всё равно думает — поставим low». В этом паке не
делай. Причина не та, что подскажет интуиция:
- Ручка есть и ратифицирована.
ReasoningNone— OpenAI-совместимая база:low|medium|highуходят какreasoning_effort; ничего не эмитят толькоoffи пустая строка, и тогда едет вендор-дефолтhigh. Именноoffстоит у редактора вbackend/configs/pipeline-c1.yaml— это ВЫБОР конфига, а не невозможность. Проверь чтениемinternal/llm/capability.goсама. ⛔ И ПРЕЖДЕ ЧЕМ ЧИТАТЬ БОЕВОЙ КОНФИГ — он несёт ОТОЗВАННЫЙ вендорский клейм.backend/configs/pipeline-c1.yaml:86=v4-pro maps lowдословно: «v4-pro maps low→high, effective effort unchanged». Это было верно до 30.08 и ОТОЗВАНО квирками: вендор сменил маппинг, иlowнаproтеперь даётlow. Комментарий в дереве живёт, читатель ему верит. Носитель поправки — ряд бэклога 429; правка самого комментария в твоём паке НЕ заказана, но не наследуй его как факт. - «На pro рычага нет» больше не правда. Вендор сменил маппинг; полигон снял поведенческую пробу 30.08
за $0.038276 и показал, что
lowдоproдоходит. Живая запись — квирки §3г. - ⇒ ось «
offпротивlow» измеряется ОТДЕЛЬНЫМ КОНФИГОМ КНИГИ. Это ДАННЫЕ: ни правки Go, ни снятия защиты; гейт эхо-мины требуетoffтолько у шиппинг-конфиговpipeline-c1/c2. - ⛔ Цена рычага измерена с другой стороны:
effort: "low"пере-вооружает эхо-мину — на плотном CJK с ослабленным размышлением модель возвращает ИСХОДНИК под HTTP 200. ⚠ Честно про силу этой улики: в квирках это 1 чистый китайский выход из 16, то есть знак есть, а «воспроизводимо» было бы преувеличением. Единственная чистая проба наlowшла на АНГЛИЙСКОЙ паре и предупреждения не снимает.
⇒ это РАЗМЕН «дорого и пусто» против «дёшево и эхо», и вслепую его не разменивают. Замер закажет оркестратор полигону, на плотном CJK.
Твоя работа — две вещи, обе без правки боевых значений. (а) Построй §4.1 так, чтобы ступень усилия
была ПАРАМЕТРОМ решения, а не константой, — через шов internal_call.go. (б) Напиши в отчёт ОДНИМ абзацем
заказ полигону: что меряем, на каком входе, сколько повторов (см. §3д), какой результат означает
«меняем», какой — «не меняем». Я отдам его дословно, поэтому пиши как заказ, а не как пожелание.
4.3. reasoning_tokens не несла числа ни разу · «реши сама и аргументируй»
Замер оркестратора 11.09: request_log.reasoning_tokens > 0 — 0 строк. Контроль рядом: найдено 449 файлов *.db, прочитано 58 886
строк-экземпляров, и ⛔ уникальных из них 5 413 — свод обходом диска умножает счёт на число копий базы
в 10.88 раза (ряд 428); ноль устоял и на честном знаменателе. Колонка объявлена схемой и присутствует во всех. ⇒ единственная колонка, которой доказывают «потолок съело рассуждение»,
структурно молчит, и её ноль неотличим от «рассуждения не было».
Причина известна и не баг нашего кода: DeepSeek считает рассуждение внутри completion_tokens и отдельным
полем usage не присылает. Но следствие наше: прибор врёт молчанием — а именно этим прибором отличают
две причины из §4.1 друг от друга.
Реши сама и аргументируй: вынуть число из тела ответа, где оно есть (у DeepSeek приходит
reasoning_content — проверь), либо честно пометить «провайдер не сообщает» так, чтобы ноль перестал
читаться как измеренный. ⛔ Запрет один: не оставляй ноль без различения.
4.4. Признак отказа и прибор, которым смотрят деньги · «реши сама и аргументируй»
⚠ Формулировка ряда 414 была неверна и исправлена 11.09 — не наследуй старую. Измеренная правда по базе
прогона (33 строки): провал виден тремя колонками, и все три сходятся ровно на $0.106472 — ok = 0 на
четырёх провальных и 1 на остальных 29 · finish_reason = 'length' на тех же четырёх · degraded
(empty ×3, length ×1). Из НЕСУЩИХ колонок пуста err. ⚠ Пуста не она одна: единственное значение по
всем 33 строкам дают также reasoning_tokens (предмет §4.3), cache_creation_tokens, estimated,
est_tokens — то есть «пуста ровно err» было бы отрицательным замером без контроля.
⇒ работа НЕ в том, чтобы «добавить носитель», их три. Работа — решить и обосновать: должен ли err
заполняться на 2xx-без-текста (против: это не ошибка транспорта; за: рука аудитора тянется к err
первой), и достаточно ли того, что ok=0 уже есть. Возможен полностью законный исход «менять нечего, а
надо назвать» — тогда так и напиши с доводом; это результат, а не увиливание.
4.5. Цена считается по модели, которой нет в каталоге · «делай РОВНО так» в части замера, «реши сама» в части лечения
Замер оркестратора 11.09, пере-снятый 11.09 же ⛔ после того, как первая редакция этих чисел оказалась
завышенной в 10.88 раза моей собственной ловушкой (ряд 428: свод обходом диска умножает трату на число
копий базы). Честный контроль: баз открыто 449, строк-экземпляров 58 886, УНИКАЛЬНЫХ строк 5 413.
После дедупа по идентичности строки подстановкой оплачено 70 строк, из них боевых 65:
deepseek-v4-flash → deepseek-flash — 28 строк, $0.188953; local-qwen3-8b →
huihui_ai/qwen3-abliterated:8b — 37 строк, $0. Фикстур 5 на $0.424277 (m-req→m-act 1,
fake-model 4) — их вычитать. ⚠ Если увидишь где-то «365 · 344 · 21 · $2.049737 · $5.51» — это моя
протухшая редакция, не наследуй её.
Сегодня это безвредно: подстановка падает на ЗАПРОШЕННУЮ модель той же семьи. ⛔ Опасна не подстановка,
а её молчание. PriceForResponse (internal/ledger/pricing.go:61=func (p *Pricer) PriceForResponse) пробует actual, потом requested,
потом якорь — и ни одно из двух мест вызова (stagerun.go:781=PriceForResponse(model, modelActual), reprice.go:134=ledger.CostUSD(r.Pricer.PriceForResponse) не различает, какая
ветвь сработала. Комментарий там же охраняет РОВНО ОДНО направление («премиальный ответ не должен биться
по дешёвому дефолту»); обратное — когда провайдер маршрутизирует ВНИЗ — не охраняет никто: запрошен pro,
ответила модель дешевле, счёт по пину pro, $3.96 за 1M выходных против $1.20 вендорских у flash =
×3.3 из кармана читателя, молча.
Делай РОВНО так: пере-сними число сама и напечатай рядом контроль — сколько баз прибор открыл и сколько строк прочёл, и объяви фильтр (боевые против фикстур). Моё число на слово не принимай: я уже публиковал занижённый контроль, потому что мой первый прибор не дошёл до ~170 баз.
Реши сама и аргументируй, как сделать подстановку наблюдаемой. Каталог — файл данных
(backend/configs/models.yaml), и «завести слаг, который провайдер реально возвращает» законно; но слаги
не меняются без live-фактчека вендор-доки (гардрейл CLAUDE.md), и «слаг живой» ≠ «модель та же». Нужен
вендор-заход, которого ты сделать не можешь, — это пинг, а не догадка.
4.6. Параметр, который едет по проводу и ничего не делает · «реши сама и аргументируй»
Вендор-дока DeepSeek, дословно (снято полигоном): «Thinking mode does not support the temperature,
top_p, presence_penalty, or frequency_penalty parameters… setting these parameters will not trigger
an error but will also have no effect». А ростер шлёт deepseek-v4-pro на стадии edit
temperature: 0.4 (backend/configs/pipeline-c1.yaml; на draft/flash стоит 0.3, и оно же уезжает
на pro при эхо-эскалации), и размышление у pro включено всегда. ⇒ параметр в теле запроса есть,
эффекта нет.
Само по себе денег не стоит. Стоит другое: temperature входит в RequestHash
(internal/pipeline/render.go, поле req.Temperature) — движок держит в ключе чекпоинта величину, которая
на этом классе моделей ни на что не влияет, и правка «настроим температуру редактора» выглядела бы
настройкой качества, а была бы громким --resnapshot без единого изменения в ответах.
Реши сама: снимать параметр на таких моделях (это смена ключа — назови цену) либо оставить и объявить в схеме модели, чтобы следующая смена не крутила мёртвую ручку. ⛔ Вендор-факт проверь сама по квиркам ПРЕЖДЕ чем решать — я его цитирую, а цитата не улика.
4.7. Чего НЕ брать
Не выключать перевыпуск (§1) · не менять боевые reasoning (§4.2) · не править пин цены flash — ряд 413,
это данные и отдельное решение, и рядом висит неразрешённый вопрос, алиас ли deepseek-flash · не трогать
books/ · не заводить новых стадий пайплайна.
5. Самопроверка ИСПОЛНЕНИЕМ — и где у ЭТОГО пака мягко
Мандат: после того как работа сделана, проведи адверсариальный проход по СВОЕЙ готовой работе. Глубину и веер выбираешь сама. Субагенты РАЗРЕШЕНЫ явно. Артефакт — таблица «посадка → выжила/поймана → ЧЕМ поймана», в отчёте, с выжившими поимённо.
⚠ Длинная сессия: раз в несколько часов работы — интервальная самоверификация субагентом против ЯВНЫХ
критериев этого промта (D39.121), а не «перечитаю сама».
⛔ Мутация засчитывается по ТЕКСТУ падения, а не по факту красноты. Правый вердикт по неправой причине — дыра, а не поимка.
⛔ Копия под мутацию защищается ПОСТРОЕНИЕМ, а не set -e: перед любой правкой в последовательности —
test -f go.mod и сверка pwd с ожидаемым путём; копия живёт ВНЕ общего скретчпада.
Четыре места, где мягко ИМЕННО У ЭТОГО пака:
- ⛔ Пин на «не перевыпустили» вакуумен по построению. Утверждение «второго вызова не было» удовлетворяется фикстурой, где второй вызов невозможен ни по какой причине. Нужна фикстура, где он ОБЯЗАН прозвучать при старом поведении и ДОЛЖЕН молчать при новом, и обе половины исполняются.
- ⛔ Пин может удовлетворяться ЧУЖОЙ уликой. Если фикстура гонит попытку через провод, признак «ступень снижена» может прийти не от твоего решения, а из поля предыдущей попытки. Спрашивай не «покраснело ли», а ЧТО ИМЕННО удовлетворило утверждение.
- ⛔ Денежный пин, флейковый на мутанте, измеряет пустой сценарий. Гоняй его НЕ ОДИН раз и строй фикстуру так, чтобы деньги были НЕИЗБЕЖНЫ, а не вероятны.
- ⛔ Шов между решением и вызовом тоньше самого решения (урок предыдущего пака этой зоны, 11.09: прибор был верен, провод от прибора к прогону не запинен ничем, и подмена аргумента прошла батарею зелёной). Спроси у своего кода: сколько тестов зовут путь, на котором причина доезжает до выбора лекарства, — и напечатай это число рядом со знаменателем (сколько тестовых файлов прибор прочёл).
6. Предметные оси ревью
Три, замена любой с аргументом:
- Деньги — каждая новая ветвь отвечает числом, сколько вызовов покупает и сколько экономит.
- Детерминизм и снапшот — что из правки входит в
request_hashи вmaxTokensPolicyVersion; двинулось ли что-нибудь, что заставит пере-оплатить книги в переводе. Ответ исполнением, не рассуждением. - Общность движка — «заработает ли пара, которой в репо ещё НЕТ, без правки Go?». Причина отказа и таблица лекарств не ветвятся по паре или книге. ⚠ Свойство биллинга провайдера уже живёт в данных и читается (§4.1) — его заводить не надо.
7. Записка-план
До первой правки кода положи в свою секцию docs/PROGRESS.md записку-план: что берёшь, в каком порядке,
что считаешь опасным. Перед сдачей — механическая сверка комплектности против §4, по пунктам, таблицей.
8. Заявление = команда
Каждое число и каждая категорика в отчёте — с командой, которой получены. Приёмка пере-ранит и сверит. Число без команды считается неснятым.
⛔ Отрицательный замер обязан доказать, что спросил существующее. Рядом с любым нулём ПЕЧАТАЕТСЯ контрольная величина: «файлов прибор прочёл: N», «баз открыто: N». Не упоминается — печатается.
⚠ Дифф ^func Test — ИСПОЛНЕНИЕМ, а не по памяти: список добавленных и изменённых тестов сними
командой и приложи, иначе «я добавила пины» остаётся самоотчётом.
9. Эхо-протокол старта
Первым действием, ДО работы, вернись ко мне эхом ≤10 строк: скоуп / инварианты / чего не делаю — ⚠ СВОИМИ СЛОВАМИ. Дословный пересказ подтверждает канал, но не понимание, и повторяет ошибку промта вместе с ним. Прямо скажи: что поняла, что считаешь опасным, что считаешь неверным. ⚠ Этот промт уже один раз стоял на перевёрнутой посылке и был переписан — возражение в эхо здесь особенно уместно.
10. Что НЕ удалось
Обязательная секция отчёта. ⛔ И вторая её половина, без которой она вырождена: «ГДЕ ПРИБОР СЛЕП И Я ЭТО ЗНАЮ» — что невидимо · почему не чинила · чем закрывается. Вопрос не «что не получилось», а «что ты знаешь и не сказала»: в коде названо, а в отчёте нет — значит для следующей смены НЕ названо.
11. Канал вопросов и право сказать «не надо»
Конфликт промта с кодом или доками → пинг мне, НЕ интерпретация. Ты вправе сказать «этого делать не надо» с аргументом — это исполнение пункта, а не отказ. ⛔ Особенно про §4.1: если существующая классификация окажется непригодной, я хочу знать письмом, а не увидеть рядом вторую.
⚠ Правка промта на ходу доезжает ТОЛЬКО релеем: я передаю её отдельным сообщением, ты подтверждаешь эхом и называешь ОТДЕЛЬНЫМ пунктом отчёта. Изменение, о котором не было релея, исполнению не подлежит.
12. Прямой канал
Механизм — CLAUDE.md §«Связь между сессиями»; здесь не повторяется. Впиши свой блок в
/tmp/textmachine-channel первым делом, там же ищи адреса. Эхо (§9) шли по адресу оркестратора из этого
файла — оно же проверка канала. ⚠ Нужной роли в файле нет, файла нет или имя не отвечает ⇒ КАНАЛА НЕТ, и
это НОРМАЛЬНЫЙ случай: НЕ опрашивай сессии подряд — вопрос секцией в отчёт, работа продолжается.
13. Критерий завершённости
Работу доводишь до конца САМА. Приёмка идёт последней и вторым кругом твоей сессии не служит.
- У каждого пункта §4 — исход: сделано · не делаю с доводом · пинг.
- Круги СОШЛИСЬ: последний проход не дал НОВЫХ находок; прежние закрыты таблицей «находка → что сделано → ЧЕМ ПРЕДЪЯВЛЕНО».
- Таблица мутаций полная, выжившие названы поимённо.
- Дифф
^func Testснят командой и приложен. - Числа сняты ПОСЛЕ последней правки.
- Всё живое — в ДЕРЕВЕ, а не в письме.
- Явное «работа завершена, править не планирую». Без этой строки пак считается идущим.
⚠ Перед отчётом сверь КАЖДЫЙ клейм с результатом инструмента ЭТОЙ сессии · если последний абзац отчёта — план или обещание, сделай его сейчас · правка теста ради зелени недопустима, правка по заказанной смене поведения — объявляется.