45 миллионов токенов в трубу: как OpenCode обанкротился на глупости
Это случилось в прошлом месяце. Команда OpenCode запустила своего код-агента в продакшен. Через неделю пришел счет: 45 миллионов токенов. 150 диалогов. Стоимость – несколько тысяч долларов. И все из-за одной архитектурной ошибки, которую совершает 9 из 10 разработчиков AI-агентов.
Агент был построен на Gemini 2.0 Pro (последняя стабильная версия на январь 2026). Каждый запрос пользователя – "допиши функцию", "найди баг" – отправлял в модель ВЕСЬ контекст репозитория. Весь код, всю историю чата, все системные промпты. Снова и снова. Это как пересылать энциклопедию Британника почтой каждый раз, когда нужно узнать столицу Франции.
Цифры пугают: один токен Gemini 2.0 Pro стоит $0.0000005 для ввода. 45 миллионов токенов – это $22.5 только за ввод. Умножьте на тысячи пользователей. Теперь понимаете масштаб?
Почему ваши агенты жрут токены как не в себя
Проблема в шаблонном мышлении. Мы берем пример из документации OpenAI или Anthropic, где в чат отправляется массив сообщений. User, assistant, system. И думаем: "Так и надо". Но в продакшене этот массив растет как снежный ком.
Каждый turn диалога – это вся предыдущая история плюс новый запрос. Если ваш агент анализирует код, то с каждым шагом контекст пухнет. Через 10 turns вы отправляете 10 версий одного и того же файла. Модель платит за обработку одинаковых данных многократно. Это не просто дорого – это технически тупо.
И вот здесь многие идут по пути выгорания, пытаясь вручную обрезать контекст или изобретать хитрые схемы. Есть способ проще.
Контекстное кэширование: не магия, а просто умный кэш
В конце 2025 года Google анонсировал фичу context_caching для Gemini API. OpenAI добавила аналогичное в GPT-4.5. Суть: вы отправляете часть контекста (например, системный промпт или документацию) один раз, получаете cache_id, а в следующих запросах подставляете только этот ID. Модель знает, что уже видела этот текст, и не заряжает за его обработку.
Это не RAG. Это кэширование на уровне API провайдера. Разница принципиальная: RAG ищет релевантные куски в векторах, а context_caching говорит "этот кусок текста неизменен, запомни его". Идеально для системных инструкций, кодовой базы, документации – всего, что не меняется в течение сессии.
| Подход | Экономия токенов | Сложность | Когда использовать |
|---|---|---|---|
| Полный контекст каждый раз (как у OpenCode) | 0% | Низкая | Никогда. Серьезно. |
| Контекстное кэширование API | До 70% на статичных данных | Средняя | Системные промпты, базовая документация |
| RAG с индексацией | До 90% на больших репозиториях | Высокая | Кодовая база, техдокументация, базы знаний |
Комбинируя оба подхода, вы получаете агента, который не сжигает бюджет впустую. Давайте разберем, как это внедрить, на примере того же OpenCode.
1 Анализируем текущий поток контекста
Первое – аудит. Что вы отправляете в модель? Откройте логи вашего агента. Вы увидите три категории данных:
- Статичное: Системный промпт ("Ты – помощник для написания кода..."), лицензии, конфигурационные файлы. Это кандидаты на context_caching.
- Динамичное, но повторяющееся: Код файлов, которые не меняются в течение сессии. Тоже можно кэшировать.
- Уникальное: Вопрос пользователя, новый код, ответы агента. Это отправляется всегда.
# Как НЕ надо делать
messages = [
{"role": "system", "content": "Ты код-ассистент. Твои правила..."}, # 500 токенов
{"role": "user", "content": "Вот мой код: {весь_файл.py}"}, # 2000 токенов
{"role": "assistant", "content": "Я вижу проблему..."},
{"role": "user", "content": "А теперь исправь функцию X"} # И весь предыдущий контекст летит снова!
]
# Каждый turn – нарастающий итог. Кошмар.
2 Выбираем стратегию кэширования
Для статичных данных – используем нативный API. Для Gemini 2.0 это параметр tools.caching. Для OpenAI – cache_control. Кэш живет до 24 часов и привязан к API-ключу.
Для кода – гибридный подход. Если файл не менялся, кэшируем его хеш. При изменении – инвалидируем кэш. Тут поможет LiteLLM – библиотека, которая абстрагирует провайдеров и добавляет слой управления кэшем поверх.
3 Интегрируем с API Gemini или OpenAI
Вот рабочий пример для Gemini 2.0 на январь 2026. Обратите внимание на параметр cached_content.
import google.generativeai as genai
# Настройка клиента с кэшированием
genai.configure(api_key="YOUR_KEY")
model = genai.GenerativeModel('gemini-2.0-pro')
# Шаг 1: Кэшируем системный промпт (делаем один раз за сессию)
cache_response = model.cache_content(
contents=[{"role": "user", "parts": ["Ты экспертный код-ассистент..."]}],
tools=[{"caching": {"mode": "ENABLED"}}]
)
cache_id = cache_response.cache_id
# Шаг 2: Используем в основном запросе
response = model.generate_content(
contents=[
{"role": "user", "parts": ["Проанализируй функцию:"]},
{"role": "model", "parts": []}, # Место для кэшированного контента
{"role": "user", "parts": ["def foo():\n return 42"]}
],
tools=[{"caching": {"mode": "READ_ONLY", "cache_id": cache_id}}]
)
# Модель получит системный промпт из кэша, а не из тела запроса
Для OpenAI GPT-4.5 логика похожа, но используйте параметр cache_control в запросе.
4 Настраиваем RAG для статичного кода
Context_caching не панацея. Если у вас 1000 файлов, кэшировать их все – неэффективно. Здесь нужна архитектура как у исследовательских агентов: индексация репозитория.
Используйте векторную базу (Chroma, Pinecone) для индексации файлов. Когда агент получает вопрос "Как работает функция X?", сначала ищите релевантные фрагменты кода через RAG, а затем отправляйте только их в контекст. Это сократит объем в 10-50 раз.
# Упрощенная схема гибридного агента
class EfficientCodeAgent:
def __init__(self):
self.cache_id = None # ID кэшированного системного промпта
self.vector_index = Chroma() # Индекс кода
def query(self, user_question, repo_context=None):
# 1. RAG: находим релевантный код
relevant_code = self.vector_index.similarity_search(user_question, k=3)
# 2. Формируем компактный контекст
context = "\n".join([doc.page_content for doc in relevant_code])
# 3. Отправляем запрос с кэшированным системным промптом
messages = [
{"role": "user", "parts": [user_question]},
{"role": "model", "parts": []}, # Кэш
{"role": "user", "parts": [f"Релевантный код:\n{context}"]}
]
# ... вызов модели с cache_id
return response
5 Мониторим и оптимизируем
Внедрили – отлично. Теперь следите за метриками. Среднее количество токенов на запрос должно упасть в 2-3 раза. Используйте дашборды LiteLLM для отслеживания стоимости по провайдерам. Настройте алерты при аномальном росте.
Важный нюанс: кэшированный контент тоже учитывается в лимитах контекстного окна модели, но не тарифицируется повторно. То есть если ваше кэшированное системное сообщение – 1000 токенов, а контекстное окно Gemini 2.0 Pro – 1 млн токенов, то для расчета "вместимости" окна эти 1000 токенов учитываются, но платите вы за них только один раз.
Ошибки, которые вы сделаете (и как их избежать)
Я видел эти грабли десятки раз. Вот топ-3:
- Кэшировать динамичные данные. Кэш должен быть инвалидирован при изменении. Хешируйте контент и сравнивайте хеши перед использованием cache_id.
- Забыть про безопасность. Кэшированный системный промпт – такая же цель для промпт-инъекций, как и обычный. Не кэшируйте промпты, которые содержат чувствительные инструкции или могут быть переопределены пользователем.
- Слепо доверять RAG. Векторный поиск иногда пропускает критически важные фрагменты кода. Всегда добавляйте ручной механизм "force include" для ключевых файлов (например, main.py).
И да, ваш агент все еще уязвим для джейлбрейка, даже с кэшем. Безопасность – отдельная история.
Что будет дальше? Прогноз от инсайдера
К 2027 году контекстное кэширование станет стандартом де-факто. API провайдеры начнут тарифицировать не сырые токены, а "уникальные токены за сессию". Появятся специализированные базы данных для AI-контекста, которые будут делать то, что Redis для веб-сессий.
Но главный сдвиг – в мышлении. Разработчики перестанут думать об агентах как о "чат-интерфейсе к модели" и начнут строить их как полноценные stateful-системы. С сессиями, кэшем, индексами. Это уже не prompt engineering, это агентная инженерия.
Совет напоследок: если ваш агент сегодня работает с полным контекстом, остановите его. Прямо сейчас. Посчитайте, сколько он уже сжег денег. Затем внедрите хотя бы базовое кэширование системного промпта. Это займет час, а сэкономит тысячи. Не повторяйте ошибку OpenCode – они сейчас переписывают архитектуру с нуля, и это больно.