Skip to content

Протокол парного replay: стоят ли rag-first подсказки токенов исследования ​

Принимает критерий AC 8 задачи v14b-rag-first-nudges (отложен 2026-05-03): «прогнать сессию исследования до и после подсказок и сравнить расход». Задача v14b-rag-nudge-replay-benchmark фиксирует здесь прибор, корпус, условия, формулу и место улик — до прогонов, чтобы число потом было о чём-то.

1. Прибор и его границы (проверено на реальной записи, смена #249) ​

Источник посессионных чисел — session_usage_metrics, которую заполняет scripts/hooks/session_metrics.parse_transcript через окно смены make_session_resolver() (починено в session-rollup-window-attribution). Проверка на транскрипте 89b9d743 этой машины: один файл разложился на семь окон — #243 180 730 / #244 135 941 / #245 176 220 / #246 119 638 / #247 50 592 / #248 120 869 / #249 169 024 tokens_total; сумма 953 014 из 954 210, вне окон — 1 196. Раньше все семь строк были бы одним числом.

Две границы, обе решающие для этого замера:

  1. tokens_total = некэшированный вход + выход. В тех же окнах поля usage транскрипта дают для #248 cache_read_input_tokens 129 334 844 и cache_creation_input_tokens 183 391 против tokens_total 120 869. Рост контекста — то, что подсказки должны менять, — этот счётчик не видит. Поэтому основная метрика берётся из полей usage транскрипта по тому же окну, а не из session_usage_metrics.
  2. Смесь инструментов слепа к Bash. В окнах #248/#249 ноль вызовов Read/Grep/Glob/search_code — агент исследовал через grep/sed/cat в Bash. Метрика смеси обязана классифицировать команды Bash по первому слову, иначе «ноль Read» будет выдан за успех подсказок.

Отвергнуто и остаётся отвергнутым: token_metrics.jsonl и usage_events источника posttool — они штампуют посообщенческий usage на каждый вызов и пересчитывают кэш (решение #201).

2. Корпус запросов (фиксирован, только чтение) ​

Десять вопросов о ЭТОМ репозитории, каждый — отдельный ход одной сессии, ответ без правок файлов и без задач TAUSIK. Порядок фиксирован.

  1. Где и как task done читает кэш verify и что делает QG-2 при просроченном кэше?
  2. Какие генераторы файлов правил вызывают warn_output_mode_not_applied и что общего у их сигнатур?
  3. Как memory_relevance.search_any строит FTS5-запрос и почему не через memory_search?
  4. Что именно verify_commit_ownership.py считает «своим» файлом — перечислите три яруса с именами функций.
  5. Где хранится ключ подписи квитанции verify и кто его читает при task done?
  6. Как config_trust решает, что глобальная настройка относится к чужому проекту?
  7. Какие хуки установлены в .claude/settings.json и какой скрипт стоит за каждым?
  8. Как knowledge export --redacted выбирает, что маскировать, и что никогда не маскируется?
  9. Где задан потолок CAVEMAN_DIRECTIVE_MAX_CHARS и какие тесты его стерегут?
  10. Как гейт test_dedupe считает «структурно неразличимые» тесты и где его базовая линия?

Ответы не оцениваются на правильность внутри протокола (это замер расхода, не качества), но сохраняются рядом с уликами: сравнение без ответов даёт возможность «сэкономить», ответив хуже.

3. Условия ​

условиехарнесскак получить
A — подсказки естьразвёрнутый профиль как естьpython bootstrap/bootstrap.py --ide claude в рабочем дереве
B — подсказок неттот же коммит без мест впрыскаgit worktree add ../tausik-B HEAD; в нём: (1) TOOL_ROUTING не включается в build_full_body (bootstrap/bootstrap_templates.py); (2) секции «Code search hierarchy» убраны из harness/skills/{start,task,debug,explore}/SKILL.md; (3) в scripts/hooks/session_start.py убраны строка RAG-summary с search_code и bullet reminders про RAG; (4) scripts/hooks/keyword_detector.py — на HEAD 396de834 уже не рекомендует search_code, снимать нечего; (5) в scripts/hooks/user_prompt_submit.py отключён SEARCH_RECOMMENDATION (сюда nudge переехал из keyword_detector); (6) в scripts/hooks/tool_output_truncation_nudge.py из строки-лекарства убрано упоминание search_code; затем bootstrap там же. Проверка чистоты — не по списку, а по транскрипту: ноль вхождений текстов подсказок (§7)

Всё остальное одинаково: модель (model_id записывается смену в session start), окно контекста, индекс RAG (в B сервер codebase-rag остаётся подключён — снимаются подсказки, не инструмент), одна свежая сессия IDE на условие, одна смена TAUSIK на условие, открытая session start и закрытая session end до первого и после последнего вопроса.

Порядок: сначала B, потом A, в разные дни не обязательно, но между ними — ни одного коммита в основное дерево, иначе корпус отвечает на разный код.

4. Метрика ​

Для окна смены [started_at, ended_at) через make_session_resolver():

  • основная: Σ cache_creation_input_tokens + Σ tokens_output по сообщениям assistant окна — «сколько нового контекста и вывода породило исследование»;
  • вторичная: Σ cache_read_input_tokens — стоимость перечитывания, функция числа ходов × размера контекста;
  • смесь: число вызовов и байты результатов по классам search_code, Read, Grep/Glob, Bash:grep|rg|sed|cat|head|tail|find|ls (класс по первому слову команды после cd … &&), остальное — other;
  • сверка: session_usage_metrics.tokens_total за то же окно должен совпасть с Σ input_tokens + Σ output_tokens транскрипта — иначе окно выбрано неверно и сравнение недействительно.

Дельта: A − B по каждой строке, абсолютно и в процентах от B. Утверждение «подсказки экономят» допустимо только если основная метрика и байты результатов исследования у A меньше, чем у B, на одном корпусе, при совпавшей сверке; в любом другом исходе записывается «сравнение недействительно: <причина>» — и это тоже результат закрытия (AC-3).

5. Улики ​

docs/ru/research/_internal/rag-replay/<дата>/{A,B}/ — usage.json (суммы по формуле §4), mix.json, answers.md, session_id.txt, transcript.sha256 (сам транскрипт в репозиторий не кладётся: он содержит пути и вывод инструментов, граница публикации). Цифры дублируются в журнал задачи строкой AC-3 … с обоими абсолютными значениями и дельтой.

6. Что этот протокол не обещает ​

Никакой экономии до парного прогона (AC-4). Одна сессия — не улика: разброс tokens_total между сменами #243–#249 одной и той же работы — от 50 592 до 180 730 — больше любого правдоподобного эффекта подсказок, поэтому две сессии дают лишь один парный отсчёт на фиксированном корпусе, и вывод формулируется как «на этом корпусе, в этой паре», без обобщения. Стоимость: две сессии по десять вопросов, ориентировочно по 20–40 минут каждая, — решение владельца, когда их прогонять и прогонять ли.

7. Результат парного прогона 14 сентября 2026 (смены #261–#263) ​

Пара получена, сравнение действительно (сверка §4 сошлась в обоих условиях, один коммит 396de834, одна модель claude-opus-5[1m], один корпус), и утверждение «подсказки экономят» на этой паре не допускается: основная метрика и байты результатов исследования у A больше, чем у B.

метрикаB (без подсказок)A (с подсказками)A − B% от B
основная: Σ cache_creation + Σ output195 055198 848+3 793+1,9 %
— Σ cache_creation_input_tokens163 546172 114+8 568+5,2 %
— Σ output_tokens31 50926 734−4 775−15,2 %
вторичная: Σ cache_read_input_tokens6 264 3645 652 601−611 763−9,8 %
сообщений assistant (вызовов API)5244−8−15,4 %
вызовов инструментов7662−14−18,4 %
— search_code000—
— Read3739+2+5,4 %
— Grep/Glob3721−16−43,2 %
— Bash:search (grep, sed, cat…)000—
байты результатов исследования292 715326 323+33 608+11,5 %
— из них Read252 001285 738+33 737+13,4 %
время, с516462−54−10,5 %
сверка: session_usage_metrics.tokens_total = Σ input + Σ output57 135 = 57 13547 221 = 47 221

Главная строка — search_code: ноль вызовов в обоих условиях. В A агент получил все три текста подсказки (строку RAG-summary и bullet при SessionStart, один nudge UserPromptSubmit) и ни разу не выбрал инструмент, который они рекомендуют; в B инструмент был подключён и тоже не выбран. Подсказки не изменили выбор инструмента — они изменили только форму исследования (меньше Grep, больше и крупнее Read), и это обошлось дороже.

Что ещё измерено и что это значит:

  • Разброс между прогонами одного условия больше эффекта. Первая попытка B (отброшена: один nudge прошёл) дала основную метрику 220 343 и 366 544 байт — на 13 % и 25 % больше чистого B при том же дереве и корпусе. Дельта A − B в 1,9 % внутри этого разброса; вывод формулируется только как «на этом корпусе, в этой паре подсказки не сэкономили», без обобщения (§6).
  • Счётчик session_metrics.parse_transcript завышает tokens_total примерно в 1,8 раза: он суммирует usage каждой записи JSONL, а Claude Code пишет по записи на каждый блок содержимого одного сообщения с одним и тем же usage (52 сообщения B лежат в 321 записи). Сведённая по message.id сумма input + output для B — 31 613 против 57 135 у счётчика. Сверка §4 намеренно считается «как счётчик», чтобы равенство означало верно выбранное окно; числа таблицы сведены. Дефект заведён отдельной задачей.
  • Подсказки физически не доходили. SessionStart-хук на этой машине идёт 5,9 с при таймауте 6 с и в пробном прогоне был отменён — весь автовставляемый контекст, включая подсказку, не попал в сессию. Причина — tausik status 5,3 с, из них 5,04 с одна EXISTS по tasks.defect_of без индекса (status-takes-five-seconds-on-a-missing-defect-of-index). На время прогонов таймауты SessionStart/Stop подняты в обоих профилях, после — восстановлены побайтово.
  • Мест впрыска было шесть, а не четыре (§3 исправлен). Полноту B доказывает не список, а транскрипт: ноль вхождений текстов подсказок.

Улики: docs/ru/research/_internal/rag-replay/2026-09-14/{A,B,B1-contaminated}/ (usage.json, mix.json, answers.md, session_id.txt, transcript.sha256, turns.json), там же драйвер, метрика и сравнение как скрипты и README.md с отклонениями. Обе сессии — безголовый Claude Code 2.1.270 с одной сессией на условие и десятью отдельными ходами (stream-json), а не две ручные сессии IDE.