惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Attack and Defense Labs
Attack and Defense Labs
宝玉的分享
宝玉的分享
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Darknet – Hacking Tools, Hacker News & Cyber Security
V
Vulnerabilities – Threatpost
博客园_首页
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
罗磊的独立博客
V
Visual Studio Blog
Know Your Adversary
Know Your Adversary
Hacker News - Newest:
Hacker News - Newest: "LLM"
美团技术团队
L
LINUX DO - 最新话题
The Last Watchdog
The Last Watchdog
博客园 - 三生石上(FineUI控件)
T
Tor Project blog
云风的 BLOG
云风的 BLOG
N
Netflix TechBlog - Medium
MyScale Blog
MyScale Blog
The GitHub Blog
The GitHub Blog
有赞技术团队
有赞技术团队
I
InfoQ
Last Week in AI
Last Week in AI
V2EX - 技术
V2EX - 技术
量子位
S
Secure Thoughts
L
LangChain Blog
The Hacker News
The Hacker News
H
Help Net Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
小众软件
小众软件
K
Kaspersky official blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
I
Intezer
Vercel News
Vercel News
Hacker News: Ask HN
Hacker News: Ask HN
Cisco Talos Blog
Cisco Talos Blog
Google DeepMind News
Google DeepMind News
S
Securelist
阮一峰的网络日志
阮一峰的网络日志
G
Google Developers Blog
Help Net Security
Help Net Security
Martin Fowler
Martin Fowler
爱范儿
爱范儿
Y
Y Combinator Blog
C
Check Point Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Короткий промпт ≠ дешёвый промпт: как оптимизация ломает prefix cache в LLM-агентах
Ser_no (Битр · 2026-05-12 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение12 мин

Охват и читатели14

Привет, Хабр! Меня зовут Сергей Нотевский, я AI Platform Lead в Битрикс24.

Это третья статья серии про prefix caching: первая - про экономику кэширования и особенности разных провайдеров, вторая - про антипаттерны в простых сценариях. А здесь про то, как та же механика работает против вас в агентном цикле.

TL;DR

  • Если на каждом шаге менять tools, system prompt или ранние блоки context, prefix cache будет часто начинаться заново.

  • Поэтому большой, но стабильный список tools иногда дешевле, чем маленький список, который постоянно пересобирается.

  • Для своего инференса часто работает схема «стабильные tools в промпте + masking / constrained decoding».

  • Для API-провайдеров стоит смотреть на allowed_tools, tool_search, defer_loading или фиксированные наборы tools по маршрутам.

  • Минимальный guardrail: логировать cached_tokens / cache_read_input_tokens, prefix_hash, tools_count и алертить при падении cache hit rate.

Если у агента 30+ tools, первое желание - не тащить их все в каждый запрос. Отобрать 5-7 релевантных на текущем шаге, убрать лишний шум, сделать промпт короче. На уровне одного запроса это выглядит как очевидный выигрыш по стоимости и скорости. Я бы сам так сделал.

Проблема начинается там, где запрос перестает быть одиночным. В агентском цикле оптимизация работает уже на цепочке из 10, 20, 50 вызовов. И там внезапно оказывается, что короткий промпт может быть дороже длинного. Под эффективной стоимостью дальше я имею в виду стоимость после учета cache hits и cache misses, а не просто сырое число входных токенов.

Упрощённо симптом в логах выглядит так. Системный промпт не меняется, история растет append-only, но cached_tokens почти каждый раз начинаются заново:

Шаг

Tools

Prefix hash

Cached

1

32

a91f…

0

2

32

a91f…

41 800

3

7

c03b…

1 200

4

9

8aa1…

900

5

6

f17c…

1 100

На бумаге все стало лучше: tools меньше, промпт короче.

В логах - наоборот: модель получала новый префикс почти на каждом шаге. Для человека это тот же агент с другим набором доступных действий. Для prefix cache - новый промпт.

А вот как выглядит та же сессия, если список tools оставить стабильным и ограничивать доступность отдельно:

Шаг

Tools

Стратегия

Prefix hash

Cached

1

32

все tools

a91f…

0

2

32

все tools

a91f…

41 800

3

32

+ masking

a91f…

45 200

4

32

+ masking

a91f…

48 900

5

32

+ masking

a91f…

52 100

Tools стало больше. Эффективная стоимость - ниже.

Почему короткий промпт проиграл

В обычном одиночном запросе к модели все довольно просто: системный промпт, сообщение пользователя, ответ. Если вы уменьшили промпт на 1000 токенов, вы действительно почти наверняка сэкономили 1000 входных токенов.

С агентом это перестает быть локальной оптимизацией. Агентный цикл выглядит так:

системный промпт
-> tool definitions
-> сообщение пользователя
-> assistant tool_call
-> tool result
-> assistant tool_call
-> tool result
-> ...

К 20-му шагу контекст легко уходит за 50K токенов. Соотношение входа и выхода в таких сценариях может доходить до 100:1. Вы платите за новый вопрос и за повторную обработку всей растущей истории.

Для масштаба: при цене входа $5/MTok и 50K входных токенов на позднем шаге один cache miss стоит $0.25. Если cached input дешевле в 10 раз, cache hit при тех же 50K стоит $0.025. Разница за один шаг небольшая. За 20 шагов агента это уже порядка $4–5 на сессию. При тысячах сессий в день это превращается в отдельную строку в бюджете.

Исследование Don’t Break the Cache на 500+ агентских сессиях показывает тот же порядок эффекта: в итоговой таблице лучшего cache mode стоимость снижается на 41–80%, а TTFT - на 6–31%. Кстати, очень рекомендую исследование - оно непосредственно про кэширование в задачах агентов, подразумевающих большие контексты.

Но тут конечно важен здравый смысл. Если агент ходит 3–5 раз, модель дешевая, трафик небольшой, динамический отбор tools может быть нормальным решением. Экономия от стабильного префикса будет копеечной, а выигрыш в точности выбора tool может оказаться важнее.

Проблема начинается в другом режиме: 15–50 шагов, дорогая модель, длинная история, тысячи сессий. Или свой инференс, где cache miss - это не только деньги, но и лишний prefill на GPU, рост TTFT и узкое место при высоком RPS.

Главный тезис тут: сначала посчитайте стоимость cache miss на последних шагах. Если она незаметна - не усложняйте архитектуру. Если заметна - стабильность префикса быстро становится важнее локальной экономии токенов.

Где именно ломается кэш

Когда cached_tokens перестают расти, первое подозрение обычно падает на историю: может, compaction переписывает старые сообщения, может, фреймворк добавляет trace_id, может, где-то меняется системный промпт.

Но в системе ai-агента первым делом стоит проверить набор тулов(tools).

Tools почти всегда находятся очень рано в промпте. OpenAI в документации по function calling прямо пишет, что функции инжектятся в системное сообщение и учитываются как входные токены. У Anthropic в документации по prompt caching порядок инвалидации описан каскадом: tools -> system -> messages.

То есть если на шаге 5 вы поменяли список tools, вы поменяли не маленький хвост запроса. Вы поменяли начало, после которого стоит вся накопленная история агента.

На своем инференсе внутренняя механика другая, но эффект тот же: кэш работает по самому длинному общему префиксу. Первый несовпавший блок или токен обрезает переиспользование всего, что стоит дальше. Даже другой порядок tools в массиве - это другая JSON-сериализация и другой prefix hash.

Это не теоретическая придирка. В Claude Code был реальный баг: порядок перечисления sub-agent types в описании тулов агента оказался недетерминированным. tools[0] менялся между вызовами, и весь prefix hash после него летел. Фикс оказался скучным: стабилизировать сериализацию.

Здесь легко сделать неправильный вывод: «значит, много tools - плохо». Но как бы не хотелось - не все так просто.

Don’t Break the Cache уточняет картину: при длинных агентных сессиях стоимость кэша остается стабильной при росте числа tool calls в сессии от 3 до 50. Это про количество вызовов, а не про количество описаний tools в промпте. Но вывод для нашей темы важный: многошаговость сама по себе кэш не ломает. Ломает его изменение кэшируемого префикса между шагами.

Главный вывод такой:

  • большой стабильный список tools - неприятен, но кэшируем;

  • маленький плавающий список - красив в одном запросе, но дорог в агентском цикле.

Фикс, который выглядит неправильно

После такого диагноза хочется сделать странную вещь: вернуть tools обратно.

Не потому что «всегда надо пихать все в контекст». Это тоже плохая догма. А потому что фильтрация доступности и изменение кэшируемого префикса - разные задачи.

Весь вопрос в том, где вы ограничиваете список доступных действий.

// Динамический отбор: тело запроса меняется на каждом шаге
{
  "tools": [tool_A, tool_C, tool_E], // шаг 5: выбрали 3 из 5
  "messages": [...]
}
// -> префикс НЕ совпадает с шагом 4
// -> cache miss, full prefill
// Masking / allowed_tools: описания стабильны
{
  "tools": [tool_A, tool_B, tool_C, tool_D, tool_E], // всегда все 5
  "messages": [...]
}
// -> префикс совпадает
// -> cache hit

// Ограничения - на уровне декодирования или метаданных,
// не через переписывание tools.

В первом случае вы меняете входной текст. Во втором - тело запроса остается стабильным, а доступность tools контролируется уже после формирования кэшируемого префикса.

Динамический отбор tools реально помогает: пространство действий уже и модели проще не промахнуться. Поэтому это не «глупая идея». Ошибка начинается там, где вы сужаете это пространство через изменение тела запроса на каждом шаге.

Более безопасные варианты:

  • masking / constrained decoding на своем инференсе;

  • allowed_tools, если провайдер умеет отделять полный набор tools от разрешенного подмножества;

  • tool_search / defer_loading, если tools много и полные схемы не хочется держать в начале;

  • фиксированные наборы tools для определенных сценариев, если приложение их подразумевает.

Это не отменяет проблемы размывания контекста. Большой список tools - это шум, засорение внимания и риск ошибки выбора tool. У OpenAI в документации есть мягкая рекомендация держать меньше 20 функций доступными на старте turn, а дальше - измерять на своем сценарии. Моя рабочая эвристика: 20–30 компактных tools часто еще терпимо, при 50+ я бы уже не тащил все полные схемы в начало. Лучше отложенная загрузка, tool_search или несколько специализированных агентов с фиксированными наборами tools.

Смысл в том, чтобы блок инструментов и системных инструкции в начале промпта оставался монолитным. Если вы измените хотя бы один символ в начале, весь накопленный кэш огромной истории сообщений под ним просто “сгорит”.

Это не только наша проблема

Когда понимаешь, что ломается именно префикс, публичные кейсы начинают читаться иначе. Все они упираются в одну инженерную дисциплину: статичное - в начало, динамичное - в конец.

Manus сформулировал это почти буквально: mask, don’t remove. Команда пробовала динамически менять пространство действий, но отказалась: описания tools находятся в начале контекста, а их изменение инвалидирует KV-cache для всех последующих действий. Плюс модель начинает путаться, если в истории уже есть вызовы tools, которые потом «исчезли».

Их решение: все описания tools остаются в запросе, а запрещенные tools блокируются на уровне декодирования. Второй механизм - предзаполнение ответа: например, <tool_call>{"name": "browser_ заставляет модель продолжить именем из группы browser_*. Формат имен превращается в простой способ групповой маскировки.

Для своего инференса это можно реализовать через свой logit processor. Упрощенный пример:

from vllm import SamplingParams


def make_tool_logit_processor(tokenizer, blocked_tools: list[str]):
    blocked_ids = set()

    for name in blocked_tools:
        blocked_ids.update(tokenizer.encode(name, add_special_tokens=False))

    def processor(token_ids, logits):
        # Псевдокод: в продакшене маску нужно включать только
        # в позиции выбора имени tool, а не во всем ответе.
        for tid in blocked_ids:
            logits[tid] = float("-inf")
        return logits

    return processor


params = SamplingParams(
    logits_processors=[make_tool_logit_processor(tokenizer, blocked_tools)]
)

В продакшене нельзя банить эти токены во всем тексте. Маска должна включаться только в позиции выбора имени tool. Это делается через проверку текущего state генерации (например, если мы внутри тега <tool_call>). Но идея та же: промпт стабилен, контроль уходит на уровень декодирования.

Claude Code - хороший публичный пример того же паттерна. В разборе от LMCache получилось 92 вызова, около 2 миллионов входных токенов и переиспользование префикса на уровне 92%. В опубликованной трассе у основного агента было 18 tools, а субагенты получали подмножество 10/18. Цифра вторична. Важна форма: список tools фиксируется заранее, прогревается warm-up вызовами и дальше не плавает от шага к шагу.

Показательный кейс - Plan mode. Наивная реализация: заменить tools на read-only подмножество. Реальная: оставить все tools, добавить EnterPlanMode / ExitPlanMode, а переключение состояния делать через сообщения пользователя. Префикс не трогается.

Еще одна деталь из Claude Code: часть динамики вроде cwd, platform, date и git status можно выносить из системного промпта в первое сообщение пользователя через excludeDynamicSections / exclude_dynamic_sections. Это делает системный промпт стабильнее и улучшает переиспользование кэша между пользователями. В разборе Claude Code Camp со ссылкой на инженера Claude Code эта мысль сформулирована жестко: prompt caching - архитектурное ограничение продукта, а просадка cache hit rate может становиться SEV.

У API-провайдеров появляются похожие механизмы. OpenAI в документации по tool_search прямо пишет, что tool_search спроектирован так, чтобы сохранять кэш модели: новые tools добавляются в конец контекста, а не переписывают префикс. Для gpt-5.4 и новее можно откладывать схему параметров или детали namespace/MCP-сервера.

allowed_tools у OpenAI - промежуточный вариант: полный набор tools передается стабильно, а на текущем шаге разрешается только подмножество через tool_choice.allowed_tools. В документации GPT-5.4 и Prompt Caching 201 это описывается как способ ограничивать доступные tools, не меняя массив tools.

У Anthropic похожая идея реализована через Tool Search и defer_loading: отложенные tools не попадают в префикс system prompt до вычисления ключа кэша, а найденный tool раскрывается позже в теле диалога как tool_reference.

История тоже может ломать префикс

Даже если tools стабилизировали, агент все равно живет в растущей истории. Через 20 шагов контекст становится большим, tool results занимают много места, и появляется желание «почистить» середину.

Здесь важно не сломать начало.

Наивный вариант - кэшировать весь контекст целиком, включая результаты tools. Don’t Break the Cache показывает, что это проигрывает стратегии Exclude Tool Results: результаты tools обычно специфичны для конкретной сессии и почти не помогают будущим запросам.

Для себя я держу простую шкалу: raw -> compaction -> summarization. В разборе Phil Schmid эта идея хорошо сформулирована: сначала сохраняем структурные данные - пути файлов, URL, ID — и вырезаем объемные тела ответов. Summarization уже lossy, ее лучше оставлять на крайний случай.

Практический паттерн выглядит так:

anchor: system + tools + первые сообщения
middle: compacted tool results
tail: свежие шаги без изменений

Если вы заменяете только последний tool_result компактной версией, предыдущие сообщения не менялись, их префикс остается в кэше. Потеря кэша ограничена последним блоком. Если вы суммаризируете раннюю часть истории, вы ломаете префикс почти с самого начала.

Отдельная ловушка - фреймворки. Иногда run_id, trace_id, timestamp или недетерминированная сериализация попадают слишком рано в промпт. Ошибки в логах может не быть: модель отвечает нормально, промпт даже выглядит разумно, но доля попаданий в кэш падает до нуля.

Минимальная диагностика выглядит так:

import hashlib
import json


def log_prefix_hash(messages, tools, step: int):
    """Хэш первых N токенов — должен быть стабильным между шагами."""
    prefix = json.dumps(
        {"tools": tools, "system": messages[0]},
        sort_keys=True,
        ensure_ascii=False,
    )
    h = hashlib.sha256(prefix.encode()).hexdigest()[:12]
    print(f"Step {step}: prefix_hash={h}")
    return h


# Если хэш меняется между шагами — ищите динамический ID
# или плавающий список tools.

Такой хэш - не точная замена токенизации на стороне провайдера, но хороший guardrail. Истина все равно в usage-метаданных: cached_tokens, cache_read_input_tokens, доля попаданий в кэш.

Что выбрать у себя

Я бы выбирал стратегию по длине агентского цикла, числу tools, стоимости модели и уровню контроля над инференсом.

Стратегия

Доля попаданий в кэш

Сложность

Когда подходит

Динамический отбор tools

Низкая

Низкая

Прототипы, короткие агенты, дешевые модели

Все tools + masking / constrained decoding

Высокая

Высокая

Свой инференс, контроль над декодированием

allowed_tools

Средняя / высокая

Средняя

Набор tools стабилен, но права или режимы отличаются по шагам

tool_search / defer_loading

Высокая

Низкая на стороне приложения

API-провайдеры, большие наборы tools, тяжелые схемы

Фиксированные наборы tools на маршрут

Высокая

Средняя

Мультидоменные приложения

Ориентиры грубые, но полезные:

  • До 10 компактных tools - держите все в промпте, сортируйте по имени, мониторьте долю попаданий в кэш. Сложные схемы обычно не нужны.

  • 10–50 tools, свой инференс - держите tools стабильно и ограничивайте доступность через masking / constrained decoding. Схемы внутри цикла не меняйте.

  • 30–100+ tools, API-провайдер - смотрите в сторону tool_search, defer_loading или allowed_tools. Стабильная верхняя часть промпта важнее, чем агрессивная фильтрация на каждом шаге.

  • Несколько независимых сценариев - используйте семантический роутер до агентского цикла. Каждый маршрут получает фиксированный набор tools и свой стабильный префикс. Запросы на пересечении доменов требуют fallback или оркестрации поверх роутера.

  • Динамический отбор tools - нормален для прототипа, короткого агента или дешевой модели. В продакшене оставляйте его только после явного расчета стоимости cache misses.

Короткие вопросы

«Кэш считается по всему сообщению целиком?»

Нет. Prefix cache работает по самому длинному общему префиксу. Первый несовпавший участок обрезает кэш на все, что дальше, но все до него остается. Отсюда правило: статика в начало, динамика в конец.

«А если tools положить в сообщение пользователя, системный префикс сохранится?»

Технически да. Практически - анти-паттерн. Параметр tools в API существует как раз для того, чтобы описания попадали в правильную зону. В сообщении пользователя они будут обрабатываться заново и дублироваться в истории.

«У нас 5 tools. Нужен ли masking?»

Скорее нет. При 5 стабильных tools префикс маленький, а выигрыш от masking обычно не окупает сложность. Он становится интересен, когда tools много и на них приходится заметная часть префикса.

«30 tools в каждом запросе - модель не начнет путаться?»

Может. Это реальный компромисс. Поэтому при 50+ tools лучше уходить от выбора между «все всегда» и «плавающий список» к отложенной загрузке, tool_search, allowed_tools или фиксированным маршрутам.

«Можно ли обойти строгое совпадение префикса совсем?»

Начинают появляться альтернативы вроде CacheBlend, который переиспользует ранее вычисленные связные блоки вне границы общего префикса и выборочно пересчитывает часть токенов. Это выглядит многообещающе, но пока не отменяет базовую дисциплину: если можно не ломать префикс - не ломайте.

Аудит агента за 30 минут

Минимальные guardrails

  • Логируйте cached_tokens / cache_read_input_tokens на каждом шаге агента.

  • Хэшируйте каноническое представление начала запроса: tools + системный промпт.

  • Логируйте prefix_hash рядом с tools_count.

  • Смотрите корреляцию: меняется ли hash каждый раз, когда меняется список tools.

  • Введите алерт, если доля попаданий в кэш падает ниже ожидаемого порога.

Tools

  • Описания tools фиксируются при старте сессии.

  • Порядок tools в массиве детерминированный: sorted by name.

  • JSON-ключи в схемах сериализуются с sort_keys=True.

  • В описаниях нет timestamp, run_id, версий или других динамических полей.

  • Если нужна динамическая доступность - используются masking, allowed_tools или отложенная загрузка, а не фильтрация массива tools.

История

  • Предыдущие сообщения не модифицируются без необходимости.

  • Tool results компактируются: пути, ID и URL оставить, большие тела ответов убрать.

  • Compaction не трогает начало истории: system + tools + первые сообщения остаются якорем.

  • Summarization применяется только когда compaction уже недостаточно.

Фреймворк

  • Фреймворк не инжектит run_id, trace_id, timestamp в начало промпта.

  • Сериализация tools стабильна между вызовами.

  • В CI/smoke-тесте есть синтетическая сессия на 3–5 шагов.

  • Если cache_read_input_tokens или cached_tokens падают ниже порога - тест падает.

Вывод

В агенте нельзя оптимизировать один запрос в отрыве от всей траектории.

Динамический отбор tools выглядит разумно:меньше токенов, меньше шума. Но если список tools меняется на каждом шаге, вы покупаете короткий промпт ценой постоянных cache misses.

Для обычного одиночного запроса к модели это может быть терпимо. Для агента на 20–50 шагов - это уже архитектурная ошибка.

Главное правило после такого инцидента простое: не меняйте то, что стоит раньше растущей истории.

Сначала стабилизируйте префикс. Потом уменьшайте промпт.


Бонус

Да, информации, нюансов и особенностей провайдеров для поддержания хорошей метрики попадания в кэш - достаточно много. И держать все это в голове или документации - вариант немного странный для 2026 года, да?

Поэтому я разложил весь свой опыт и найденные материалы по полочкам и сформировал Skill для агента, который позволяет Codex, Claude Code и тд:

  • Сделать аудит вашего AI-проекта на анти-паттерны кэширования

  • Посоветует что из обсервабилити добавить

  • Какие из особенностей кэширования провайдеров учесть

Проект открытый, предложения приветствуются!

Если материал показался вам полезным - в канале @sergeinotevskii я делюсь опытом построения AI-платформы. Метрики, guardrails, семантические роутеры, движки для сервинга LLM и другие аспекты.