45 миллионов токенов в трубу - и как этого избежать
Вы помните историю OpenCode? Команда запустила код-агента в продакшен, а через неделю получила счет за 45 миллионов токенов. Несколько тысяч долларов сгорели из-за одной архитектурной ошибки, которую повторяют 90% разработчиков.
Каждый запрос пользователя отправлял в модель Gemini 2.0 Pro ВЕСЬ контекст репозитория. Весь код, всю историю чата, все системные промпты. Снова и снова.
Это как пересылать энциклопедию Британника почтой каждый раз, когда нужно узнать столицу Франции.
На 19.04.2026 цены на токены не стали ниже. GPT-5 Turbo стоит $0.002/1K токенов ввода, Claude 3.7 Sonnet - $0.003/1K. Каждая тысяча диалогов с полным контекстом легко сжигает $50-100. Умножьте на команду из 10 разработчиков.
Почему ваши агенты едят токены как драконы
Проблема в шаблонном мышлении. Берем пример из документации OpenAI, где в чат отправляется массив сообщений: user, assistant, system. И думаем: "Так и надо".
Но в продакшене этот массив растет как снежный ком. Каждый turn диалога - это вся предыдущая история плюс новый запрос.
Если ваш агент анализирует код, то с каждым шагом контекст пухнет. Через 10 turns вы отправляете 10 версий одного и того же файла. Модель платит за обработку одинаковых данных многократно.
Это не просто дорого - это технически тупо.
И вот здесь многие идут по пути выгорания, пытаясь вручную обрезать контекст или изобретать хитрые схемы. Есть способ проще.
Markdown-кэш: не магия, а просто умный кэш
В конце 2025 года Google анонсировал context_caching для Gemini API. OpenAI добавила аналогичное в GPT-4.5. Суть: вы отправляете часть контекста один раз, а модель запоминает его для последующих запросов.
Но есть проблема - эти встроенные системы кэширования работают только на уровне одной сессии. Перезапустили агента - кэш сбросился.
Мое решение: внешнее кэширование в markdown-файлах. Почему markdown?
- Текстовый формат, который отлично понимают все современные LLM
- Поддерживает структурирование через заголовки и списки
- Легко читается человеком (можно проверить, что там внутри)
- Минимальные накладные расходы на токенизацию
- Совместим с любой моделью: GPT-5, Claude 3.7, Gemini 3.0, Qwen 2.5-32B
Исследование ETH Zurich в 2025 году показало, что неправильно сформированные контекстные файлы ухудшают работу AI-агентов на 3%. Но правильно организованные - ускоряют на 40% и сокращают расход токенов на 70%.
Вот как это работает на практике.
1Анализируем, что действительно нужно кэшировать
Первая ошибка - пытаться кэшировать всё подряд. Это бесполезно и даже вредно.
Откройте логи вашего агента. Посмотрите, какие части контекста повторяются в 80% запросов:
- Системные промпты (роль агента, правила поведения)
- Структура проекта (папки, основные файлы)
- Документация API, которую вы используете постоянно
- Конфигурационные файлы (docker-compose, package.json, .env.example)
А что НЕ нужно кэшировать:
- Код, который меняется с каждым запросом
- Временные ответы модели
- Контекст, уникальный для конкретной задачи
Не делайте как в том самом исследовании про CLAUDE.md. Там разработчики клали в кэш ВЕСЬ код проекта, а потом удивлялись, почему агент работает хуже. Кэш - это сжатая выжимка, а не копия репозитория.
2Создаем структуру markdown-кэша
Забудьте про один огромный CONTEXT.md. Это антипаттерн.
Создайте иерархическую структуру:
ai_context_cache/
├── system_prompt.md # Роль агента, правила
├── project_structure.md # Папки и основные файлы
├── api_docs/
│ ├── backend_api.md
│ └── third_party_apis.md
├── configs/
│ ├── docker_setup.md
│ └── environment.md
└── knowledge/
├── business_rules.md
└── common_patterns.mdКаждый файл должен быть сфокусирован на одной теме. Максимальный размер - 500-1000 токенов. Если нужно больше - разбивайте на подфайлы.
Вот пример system_prompt.md:
# Системная роль: Senior DevOps инженер
## Основные правила
1. Всегда предлагать решение с учетом лучших практик DevOps
2. Писать код с обработкой ошибок и логгированием
3. Предлагать варианты для разных окружений (dev/stage/prod)
## Стиль кода
- Использовать Python 3.11+ features где уместно
- Добавлять type hints для всех функций
- Docker образы делать минимальными (Alpine-based)
## Безопасность
- Никогда не хардкодить пароли или ключи
- Предлагать использовать секреты из vault
- Проверять входные данные на инъекцииОбратите внимание на структуру: заголовки, списки, четкие правила. Модели отлично парсят markdown.
3Реализуем загрузку кэша в агента
Теперь нужно научить агента читать эти файлы. Не загружать все сразу, а подгружать по необходимости.
Вот код на Python для агента, использующего OpenAI API (GPT-5 на 19.04.2026):
import os
from pathlib import Path
from openai import OpenAI
class ContextCacheAgent:
def __init__(self, cache_dir="ai_context_cache"):
self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.cache_dir = Path(cache_dir)
self.loaded_cache = {}
def get_context_for_task(self, task_description):
"""Определяем, какой кэш нужен для задачи"""
context_parts = []
# Всегда добавляем системный промпт
context_parts.append(self._load_cache_file("system_prompt.md"))
# Анализируем задачу и добавляем релевантные части
task_lower = task_description.lower()
if any(word in task_lower for word in ["docker", "container", "image"]):
context_parts.append(self._load_cache_file("configs/docker_setup.md"))
if any(word in task_lower for word in ["api", "endpoint", "request"]):
context_parts.append(self._load_cache_file("api_docs/backend_api.md"))
if any(word in task_lower for word in ["deploy", "infrastructure", "cloud"]):
context_parts.append(self._load_cache_file("knowledge/common_patterns.md"))
# Добавляем структуру проекта для задач, связанных с кодом
if any(word in task_lower for word in ["file", "folder", "structure", "project"]):
context_parts.append(self._load_cache_file("project_structure.md"))
return "\n\n".join(context_parts)
def _load_cache_file(self, file_path):
"""Ленивая загрузка файлов кэша"""
if file_path in self.loaded_cache:
return self.loaded_cache[file_path]
full_path = self.cache_dir / file_path
if full_path.exists():
content = full_path.read_text(encoding="utf-8")
self.loaded_cache[file_path] = content
return content
return ""
def ask(self, question):
"""Основной метод для вопросов к агенту"""
context = self.get_context_for_task(question)
messages = [
{"role": "system", "content": context},
{"role": "user", "content": question}
]
response = self.client.chat.completions.create(
model="gpt-5-turbo",
messages=messages,
temperature=0.1
)
return response.choices[0].message.contentКлючевой момент: мы НЕ отправляем весь кэш каждый раз. Мы анализируем задачу и подгружаем только релевантные части.
4Добавляем инвалидацию и обновление кэша
Статический кэш - это мертвый кэш. Когда меняется структура проекта или обновляется API, кэш должен обновляться.
Реализуем простую систему инвалидации:
import hashlib
from datetime import datetime, timedelta
class SmartContextCache(ContextCacheAgent):
def __init__(self, cache_dir="ai_context_cache"):
super().__init__(cache_dir)
self.cache_metadata = {}
self._load_metadata()
def update_cache_if_needed(self, file_path, current_content):
"""Обновляем кэш, если контент изменился"""
content_hash = hashlib.md5(current_content.encode()).hexdigest()
if file_path not in self.cache_metadata:
# Новая запись в кэше
self._save_to_cache(file_path, current_content, content_hash)
return True
metadata = self.cache_metadata[file_path]
# Проверяем по хэшу
if metadata["hash"] != content_hash:
print(f"Обновляю кэш для {file_path}")
self._save_to_cache(file_path, current_content, content_hash)
return True
# Проверяем по времени (обновлять раз в неделю)
last_updated = datetime.fromisoformat(metadata["last_updated"])
if datetime.now() - last_updated > timedelta(days=7):
print(f"Периодическое обновление кэша для {file_path}")
self._save_to_cache(file_path, current_content, content_hash)
return True
return False
def _save_to_cache(self, file_path, content, content_hash):
"""Сохраняем в markdown с метаданными"""
full_path = self.cache_dir / file_path
full_path.parent.mkdir(parents=True, exist_ok=True)
# Добавляем мета-информацию в комментарий
meta_comment = f"\n\n"
full_path.write_text(meta_comment + content, encoding="utf-8")
self.cache_metadata[file_path] = {
"hash": content_hash,
"last_updated": datetime.now().isoformat()
}
self._save_metadata()Теперь вы можете автоматически обновлять кэш при изменении исходных файлов. Например, при коммите в git.
Ошибки, которые сведут на нет всю экономию
Я видел десятки реализаций. Вот что делают не так:
| Ошибка | Последствие | Как исправить |
|---|---|---|
| Кэшировать весь код проекта | Контекст становится больше оригинала | Кэшировать только структуру и ключевые файлы |
| Нет инвалидации кэша | Агент работает с устаревшими данными | Добавить проверку хэша и времени |
| Один файл на всё | Невозможно подгрузить частично | Разбить на тематические файлы |
| Кэшировать вывод модели | Быстрое устаревание, бесполезные данные | Кэшировать только статическую информацию |
Самая опасная ошибка - забыть про контекстную амнезию. Если ваш агент работает долго (часы или дни), markdown-кэш становится его долговременной памятью. Но эта память должна быть релевантной.
Интеграция с существующими агентами
Вы уже используете Claude Code, Cursor, или свой кастомный агент? Внедрить markdown-кэш проще, чем кажется.
Для Claude Code создайте файл .claude_context в корне проекта:
# Контекст проекта
## Структура
- /src - исходный код
- /tests - тесты
- /docs - документация
## Важные файлы
- `src/main.py` - точка входа
- `src/config.py` - конфигурация
- `Dockerfile` - сборка контейнера
## Правила кода
1. Используйте async/await для IO операций
2. Добавляйте docstring ко всем функциям
3. Тестируйте с pytest, покрытие >80%Claude автоматически учтет этот файл при работе с проектом.
Для Cursor используйте папку .cursor/rules. Создайте там markdown-файлы с контекстом.
Для кастомных агентов на LangChain или других фреймворках - добавьте middleware, который будет подгружать кэш перед отправкой в модель. Примерно как в моем коде выше.
Не повторяйте ошибку из статьи про сжатие вывода инструментов. Там пытались сжимать ВЕСЬ вывод, включая критичные данные. Кэшируйте только то, что действительно повторяется.
Сколько вы сэкономите на самом деле
Давайте посчитаем на примере типичного DevOps агента:
- Без кэша: каждый запрос включает системный промпт (500 токенов) + история диалога (в среднем 2000 токенов) + контекст проекта (3000 токенов) = 5500 токенов
- С кэшем: системный промпт загружен один раз в сессию + только релевантные части проекта (в среднем 1000 токенов) + история (только последние 3-4 сообщения, 800 токенов) = 1800 токенов
Экономия: 67% на каждом запросе.
При стоимости GPT-5 Turbo $0.002/1K токенов:
- Без кэша: 1000 запросов × 5.5K токенов = 5.5M токенов = $11
- С кэшем: 1000 запросов × 1.8K токенов = 1.8M токенов = $3.6
- Экономия: $7.4 на 1000 запросов
Для команды из 5 разработчиков, каждый делает 50 запросов в день, 20 рабочих дней в месяце:
5 × 50 × 20 = 5000 запросов в месяц
Экономия: 5000 × $0.0074 = $37 в месяц
За год: $444
И это только на одном проекте. Если у вас несколько проектов или больше разработчиков - цифры растут кратно.
Что будет с кэшированием в 2027 году
Судя по roadmap крупных вендоров, встроенное кэширование контекста станет стандартом. OpenAI уже тестирует функцию "persistent context" в GPT-5, Anthropic работает над аналогичным для Claude 4.
Но markdown-кэш не умрет. Потому что:
- Он дает контроль над тем, ЧТО именно кэшируется
- Работает кросс-платформенно (один кэш для разных моделей)
- Позволяет версионировать и делиться контекстом между членами команды
Мой прогноз: к 2027 году мы увидим гибридные системы. Встроенное кэширование модели + внешние markdown-файлы для специфичного контекста проекта.
А пока - начинайте с простого. Создайте папку ai_context_cache, наполните её markdown-файлами с самым важным контекстом. Подключите к своему агенту. Через неделю проверьте счета за токены.
Разница вас удивит.