Кеш промпта

From docs/ru/prompt-cache.md at cc732c3 View on GitHub

Ломает ли rtok кеш промпта?

Нет — это измерено, а не обещано. rtok stats на этой машине (2026-09-18) показывает hit=97.5% — 5 620 803 600 токенов чтения из кеша против 145 402 240 токенов создания кеша и 667 038 некешированных входных токенов за 910 сессий, — а раздел Cache в rtok report на ту же дату не содержит строк о сбросах во всём журнале: ноль зафиксированных сбросов кеша промпта. Причина структурная, по каждому интерфейсу ниже: побайтно стабильное внедрение, переписывания вне рабочей кромки разговора и хуки, которые касаются каждого результата один раз. Воспроизведите оба числа:

rtok stats 2>/dev/null | head -4
rtok report 2>/dev/null | sed -n '/[Cc]ache/,+6p'

Хуки — фильтрация один раз, дальше байтами владеет хост

cmd и read работают на PreToolUse, до вызова: вывод сокращается один раз, и хост сохраняет этот текст в своём транскрипте. Каждый следующий ход воспроизводит собственные сохранённые байты хоста — rtok в этом цикле больше не участвует, поэтому ничто из того, что он затронул, между ходами не меняется. Та же логика, что и в аргументе rtk о кеше, плюс архив: всё вырезанное можно получить обратно, а оставшиеся байты зафиксированы на всё время жизни сессии.

inject — побайтно стабилен на каждом ходу

Внедрение SessionStart пересчитывается на каждом ходу, поэтому оно остаётся внутри закешированного префикса, только если байты никогда не сдвигаются. Они и не сдвигаются: два запуска с неизменным состоянием дают идентичный вывод, что закреплено тестом three_500_budget_800_drops_one_and_is_byte_stable (src/plugins/inject/mod.rs), а session_start_has_nudges_once_and_stable покрывает путь с подсказками. Всё, что в inject зависит от времени или счётчиков, — ошибка по собственному инварианту плагина, а не компромисс.

archive / proxy — переписывание только вне keep_turns, побайтно идентичные указатели

Фильтр archive на стороне прокси переписывает результат инструмента в указатель на архив, только когда ход старше archive.keep_turns; system, tools и самые новые ходы никогда не трогаются, поэтому рабочая кромка — та часть, которую провайдер ещё записывает в кеш, — остаётся нетронутой. Стабильность закрепляют два теста в src/plugins/archive/mod.rs: six_turn_fixtures_archive_only_turns_1_and_2_on_every_wire (из шести ходов переписываются только 1–2, в каждом формате протокола) и live_blobs_shrink_stably_outside_the_working_edge (указатель переписанного блока побайтно идентичен при повторном переписывании).

Измерено на этой машине, 2026-09-18 — mise exec -- cargo run --quiet -- stats 2>/dev/null | head -4:

sessions 910  lines 433211  malformed 0
usage input=667038 cache_create=145402240 cache_read=5620803600 output=27472613  hit=97.5%  median_context=63829
api                         input cache_create cache_read output    hit
codex                    12370533            0  243051392 671350  95.2%

и mise exec -- cargo run --quiet -- report 2>/dev/null | sed -n '/[Cc]ache/,+6p':

## Cache

Prompt-cache busts by cause, from the proxy's `usage` rows (whole ledger).

No rows in window.

Это весь журнал, записанный прокси: при том что archive переписывает старые результаты инструментов в протоколе, ни одного сброса не зафиксировано, а доля попаданий — 97.5% (95.2% для api codex, который сообщает 0 токенов создания кеша).

По полосам — доля попаданий и replay-тест

Когда прокси обслужил трафик вне полосы agent (bulk, internal, …), rtok stats добавляет рядом с api таблицу lane с теми же счётчиками и долей попаданий в кеш промпта для каждой полосы (lanes в --json). Хранилище только с ходами агента такой таблицы не печатает. Стабильность полосы agent закреплена тестом tests/proxy_cache_replay.rs: он проигрывает один растущий диалог через прокси со всеми включёнными переписываниями и проверяет, что tools, system и каждое сообщение до живого края предыдущего хода доходят до upstream побайтно одинаковыми.

guard — отказы не добавляют байтов

Отказ guard — это решение PreToolUse, а не содержимое: он подавляет повторный вызов с фиксированной однострочной причиной duplicate; rtok expand <id> и не добавляет байтов результатов инструментов ни в один ход. За результат, в котором он отказывает, уже заплатили один раз, и его можно получить через этот id, поэтому модель никогда не платит за вторую копию — а воспроизводимый транскрипт не меняется, что и есть благоприятный для кеша исход.

Отсутствие потерь не стоит кеша

Всё описанное выше сокращает байты; ничто их не теряет. Все сокращённые данные архивируются до обрезки, и expand <id> восстанавливает оригинал (--lines, --grep). Указатель в транскрипте побайтно стабилен, поэтому возможность восстановления никогда не переписывает префикс: кеш сохраняет свои попадания, а вы сохраняете полный текст.

Sticky-маршрутизация и Batch / Flex

Попаданиям в кеш промпта у провайдера нужен стабильный байтовый префикс и, на upstream из нескольких подов, привязка к одному и тому же бэкенду, на котором лежит кеш (I-84 / sticky-маршрутизация). Это ортогонально Batch (асинхронный pass-through; нет живого KV сессии, общего с интерактивным агентом) и Flex (service_tier в том же синхронном протоколе — правила кеша по-прежнему действуют, скидка даётся за уровень). rtok не будет превращать синхронный ход агента в задание Batch только ради экономии; делайте это лишь с явным клиентом Batch. Подробности: batch-flex.md.

# Запланированный флаг sticky в [proxy.routing] (сегодня не загружается) — закрепить upstream ради привязки к кешу.
# Flex — это [proxy.flex]; наблюдение за Batch — [proxy.batch]. Ни один из них не переписывает закешированные
# байты system/tools/живой кромки так, как это сделало бы небрежное сжатие.
[proxy.routing]
sticky = true