Промахи кэша промптов в Claude Code: почему 86% попаданий скрывают реальную стоимость
Модель одной сессии в iOS-монорепозитории с ценами показывает, что семь промахов кэша промптов перевешивают 33 попадания, и как стабилизировать инструменты, скиллы и CLAUDE.md как ключи кэша.
Доля попаданий 86% звучит здорово. Но в смоделированный вечер работы с Claude Code семь промахов кэша обошлись дороже, чем остальные 33 хода вместе взятые. Картина меняется, когда вы назначаете цену каждому промаху и находите его причину.
Версия 2.1.260 Claude Code добавила в /cost новую строку: лучшую догадку о том, почему случился последний промах кэша промптов. Кандидаты — изменённые определения инструментов, изменённый системный промпт или пауза дольше TTL. Затем 2.1.261 принесла /skill-doctor, который показывает загруженные, но ни разу не использованные скиллы и сколько они стоят в контексте. В том же релизе исправили баг, из-за которого участники агентной команды повторяли объявления инструментов первого хода на втором, меняя префикс запроса и промахиваясь мимо кэша.
Вместе эти записи говорят о том, что вендор начал относиться к сборке контекста как к проектированию ключей кэша, а большинство пользователей — нет. Кэш промптов — это кэш префиксов. Всё, что стоит перед диалогом, в том порядке, в каком вы это поставили, и есть ключ. Чтобы увидеть, сколько это стоит в обычный день, цифры ниже взяты из модели одной сессии.
Как кэш строит ключи запросов
Документация Anthropic по кэшированию описывает структуру прямо. Запрос состоит из слоёв: инструменты, потом system, потом сообщения, и любое изменение на одном слое стирает этот слой и все последующие. Переименовали инструмент или тронули его схему — выбрасывается весь кэш; поправили системный промпт — запись с инструментами остаётся, остальное теряется. При каждом запросе API хэширует префикс до вашей точки разрыва (breakpoint), а если совпадения нет, отступает назад блок за блоком, проверяя не больше 20 позиций. Точек разрыва можно задать до четырёх; в этой модели они стоят после массива инструментов, после системного промпта и на последнем сообщении.
По умолчанию запись живёт пять минут, и каждое чтение бесплатно её продлевает. Запись на час стоит вдвое дороже базовой цены ввода. Claude Fable 5.1 берёт $10 за миллион входных токенов, $12,50 за миллион при записи пятиминутной записи, $20 за часовую и $0,25 за миллион при чтении. Значит, чтение из кэша стоит 2,5% от отправки тех же токенов без кэша, а новая пятиминутная запись — в 50 раз дороже чтения. Поэтому промах — это не мелкое замедление: всё, что выпало из кэша, внезапно тарифицируется в 50 раз дороже.
Один вечер, семь обычных событий
Swift-пакет ContextCacheKit раскладывает запрос так, как его хэширует API, симулирует кэш по задокументированным правилам TTL и отступа назад и назначает цену каждому ходу. Его тестовый сценарий имитирует реалистичную сессию в iOS-монорепозитории. Набор инструментов: 16 инструментов харнесса и 17 MCP-инструментов от Xcode, GitHub и Linear (19 140 токенов). Остальное — преамбула харнесса, CLAUDE.md на 200 строк, файл памяти и пара скиллов, загружаемых при старте (17 700 токенов). Каждый ход начинается с этого общего префикса на 36 840 токенов, и дальше идут 40 пользовательских ходов с интервалом 45 секунд.
Потом случаются семь событий из тех, что бывают в реальных командах. На втором ходу коллега переписывает первое сообщение. Восьмой ход загружает скилл для работы с сетью, вклеенный в системный промпт. На 14-м ходу MCP-сервер GitHub переподключается и заново регистрирует свои восемь инструментов в конце списка. Перед 20-м ходом — обед на 22 минуты. На 26-м ходу вливается изменение в CLAUDE.md, на 33-м загружается новый скилл для тестов, а на 37-м переподключившийся клиент заново рендерит определение инструмента Bash, и оно растёт с 1450 до 1480 токенов.
В целом сессия попадает в кэш в 85,6% случаев — на дашборде такая цифра ни у кого не вызовет вопросов. Журнал расходов говорит другое. Ввод обошёлся в $4,65. Идеальное переиспользование предыдущего префикса на каждом ходу стоило бы $1,53, а без кэша вообще — $23,10. Семь промахов дают $3,41 счёта, а остальные 33 хода вместе — $1,24.
Если отсортировать по долларам сверх идеала, главный виновник — повторный рендер схемы инструмента, $0,91. Дальше три отдельных изменения системного промпта, вместе $0,88, потом обеденный перерыв — $0,69, перестановка инструментов — $0,64 и далеко позади переписывание коллеги — $0,002.
Два пункта в этом рейтинге меняют представление о том, откуда берутся расходы. Тридцать лишних токенов в схеме Bash заставили систему заново отправить все 75 249 токенов запроса, потому что инструменты стоят во главе иерархии. Простая перестановка инструментов GitHub, при побайтово нетронутой схеме, сделала то же самое с 52 648 токенами. А баг, удостоившийся записи в ченджлоге, — коллега, переписавший нулевое сообщение, — оказался самым дешёвым промахом: перекэшировалось всего 157 токенов, потому что сообщения находятся в самом низу. Харнесс починил промах, который был виден ему, а дорогие промахи идут из вашей собственной конфигурации.
Логика диагностики простая: побеждает первое в порядке хэширования, что ломает префикс.
// Abridged from MissDiagnoser.swift: the first thing in hash order that would break the prefix wins.
public static func diagnose(previous: ContextSnapshot?, current: ContextSnapshot,
elapsed: TimeInterval, ttl: CacheTTL) -> MissCause {
guard let previous else { return .none } // cold start, not a miss
if toolsDiffer(previous, current) { return .toolSetChanged(...) }
if previous.tools.map(\.name) != current.tools.map(\.name) { return .toolOrderChanged }
if previous.system != current.system { return .systemSegmentChanged(segments: changed) }
if let i = firstRewrittenMessage(previous, current) { return .messagePrefixRewritten(atIndex: i) }
return elapsed > ttl.seconds ? .idlePastTTL(idleSeconds: elapsed) : .none
}
Относитесь к префиксу как к ключу кэша
Прогоним тот же вечер со спроектированным префиксом — сценарием stabilized. Порядок инструментов фиксирован, схемы заморожены на всю сессию. Скиллы попадают в диалог как сообщения с ролью system, а не как текст, вклеенный в системный промпт верхнего уровня; по документации на Fable 5.1 это оставляет кэши system и сообщений нетронутыми. Слияние CLAUDE.md откладывается до границы сессии. Обед остаётся как есть, потому что TTL — это решение политики, а не дефект префикса.
В этом прогоне один промах — обеденный перерыв. Доля попаданий — 94,2%. Стоимость — $2,22, то есть на $0,69 выше идеала. Все исправления меняли то, что отправляет харнесс; поведение модели не трогал ни одно.
Оставшийся рычаг — TTL. Переведите стабилизированный прогон на часовые записи, и промахов не останется, при стоимости $2,11. Это экономит всего 11 центов по сравнению с пятиминутным вариантом, потому что каждый свежий токен теперь стоит $20 на запись, а не $12,50, и за вечер эта надбавка набегает примерно на один обеденный перерыв. Длинный TTL окупается только в дни с паузами, так что настраивайте его для каждой команды, а не глобально.
Больше всего денег двигает размещение скилла. Тот же скилл, те же 5200 токенов, отличается только место:
// Same skill, same 5,200 tokens. Only the placement changes.
.loadSkill(name: "skill:networking-layer", tokens: 5_200, placement: .systemPrompt)
// → system prompt changed: re-sends the transcript after it
.loadSkill(name: "skill:networking-layer", tokens: 5_200, placement: .appendedMessage)
// → hit: costs only its own 5,200 tokens, once
Что отслеживать
Планируйте общий префикс, как время запуска приложения. Инструменты и скиллы, которые ваш монорепозиторий грузит по умолчанию, складываются в 36 840 токенов, хэшируемых на каждом ходу, а доля попаданий усредняет всю сессию и прячет, на каком ходу сгорели деньги. Цифра, которую стоит вывести на дашборд, — доллары сверх идеала, по причинам и по дням. Причину теперь даёт /cost, доллары — журнал расходов.
Доля попаданий — это среднее, а промах кэша — событие, так что закладывайте бюджет на события.
Сценарий — это модель, а не телеметрия, и его константы — допущения, так что с вашим списком инструментов и размерами префикса рейтинг сдвинется. Общая форма — нет. Дороже всего обходятся причины наверху иерархии, и никакой ченджлог их за вас не починит. Симулятор, сценарии, 30 тестов и представление журнала на SwiftUI лежат в демо-репозитории проекта; на момент публикации таргет Demo в симуляторе ещё не запускали.