Экономия токенов в AI-агентах: 6 методов с графиками | AiManual
AiManual Logo Ai / Manual.
29 Апр 2026 Гайд

Как сэкономить токены в агентных системах: 6 методов с интерактивными графиками

Инженерный гайд по снижению расхода токенов в агентных системах: prompt caching, semantic caching, lazy-loading инструментов, маршрутизация, субагенты и очистка

Почему ваш AI-агент ест токены как не в себя?

Каждый вызов LLM — это не просто запрос, это пачка денег, улетающая в никуда. Особенно в агентных системах, где модель бегает по кругу, перезапрашивает инструменты и тащит за собой тонны истории. Я сам однажды запустил агента для парсинга документации, а через час смотрел на счёт с цифрой, за которую можно купить подписку на Netflix на год. Знакомая боль?

Проблема усугубляется с выходом больших контекстных окон (Claude Opus 4.7, GPT-4.5). Модели обрабатывают больше токенов — значит, и тратят их щедрее. Как писал в недавнем посте про скрытую инфляцию токенов в Opus 4.7, новый токенизатор может накручивать до 30% без видимой причины. Но это не значит, что надо сидеть сложа руки.

Ниже — 6 методов, которые я проверял на бою. Каждый с готовым кодом, подводными камнями и прикидкой экономии. Спойлер: некоторые методы дают до 78% снижения расхода токенов — как в AVP (Agent Vector Protocol), только проще и без протокола.

⚠️ Важное замечание: Все цифры экономии приведены для Claude Opus 4.7 (2026) при тестах на типовых задачах анализа документации и написания кода. Ваши результаты могут отличаться в зависимости от паттерна использования.

1. Prompt Caching: не повторяйте одно и то же дважды

Идея до безобразия проста: если у вас в каждом запросе летит одинаковый блок (системный промпт, описание инструментов, примеры), зачем генерировать его заново? Современные API (Anthropic, OpenAI) поддерживают prompt caching — кэширование префикса промпта на стороне сервера.

Как это работает: вы помечаете статическую часть промпта как cache_control, и API держит её в кэше, пока не истечёт TTL (обычно 5-10 минут). При повторном запросе передаётся только короткий хеш кэша — платите только за разницу.

1 Реализация на Python (Anthropic API)

import anthropic

client = anthropic.Anthropic()

# Статический префикс с кэшированием
system_prompt = "Вы — опытный инженер-программист. Отвечайте кратко и по делу."

response = client.messages.create(
    model="claude-opus-4-7-20260429",  # актуальная на 29.04.2026
    system=[
        {
            "type": "text",
            "text": system_prompt,
            "cache_control": {"type": "ephemeral"}
        }
    ],
    messages=[{"role": "user", "content": "Объясни разницу между async/await в Python"}]
)
print(response.content)

Нюансы

  • TTL сбрасывается при каждом использовании кэша. Если запросов нет 10 минут — кэш удаляется.
  • Минимальный размер для кэширования у Anthropic — 1024 токена. Меньше — не кэшируется.
  • Экономия: если статическая часть 2000 токенов, а динамическая 200, то платите за 200 вместо 2200 (экономия ~90% на системном промпте). На практике — около 40-60% от общего расхода.
💡
Совет: кэшируйте не только системный промпт, но и описания всех инструментов (tools) — они обычно статичны и занимают много места. В статье про мультиагентные системы мы показывали, как общий пул инструментов раздувает контекст до 15k токенов.

2. Semantic Caching: не спрашивай одно и то же

Следующий шаг — кэшировать не только префикс, но и целые ответы. Обычный кэш по точному ключу не подходит: пользователи могут перефразировать запрос. Тут на помощь приходит семантическое кэширование с эмбеддингами.

2 Как построить semantic cache

import numpy as np
from sentence_transformers import SentenceTransformer

# Модель для эмбеддингов (легковесная, можно на CPU)
encoder = SentenceTransformer('all-MiniLM-L6-v2')

class SemanticCache:
    def __init__(self, threshold=0.92):
        self.threshold = threshold
        self.cache = []  # (embedding, response)

    def get(self, query: str) -> str | None:
        q_emb = encoder.encode(query, normalize_embeddings=True)
        for emb, resp in self.cache:
            sim = np.dot(q_emb, emb)
            if sim > self.threshold:
                return resp
        return None

    def set(self, query: str, response: str):
        emb = encoder.encode(query, normalize_embeddings=True)
        self.cache.append((emb, response))

cache = SemanticCache(threshold=0.95)
# Пример
resp = cache.get("как сохранить токены")
if resp:
    print("Hit cache!")
else:
    # зовём LLM
    resp = call_llm("как сохранить токены")
    cache.set("как сохранить токены", resp)

Болевые точки

  • Порог схожести — основной рычаг. 0.95 даст мало попаданий, зато безопасно. 0.85 — больше попаданий, риск выдать некорректный ответ.
  • Память: эмбеддинги занимают место. Для 10k запросов — ~50 МБ, приемлемо.
  • Инвалидация: если модель обновилась или данные устарели, кэш надо чистить. Иначе получите «застывший» AI.

На практике semantic cache отсекает 20-40% повторяющихся запросов в типовых задачах поддержки или код-ревью. Я встроил его в своего агента для джуниоров — экономия около $200 в месяц.

3. Lazy Loading инструментов: не носите всё сразу

Агентные системы часто объявляют десятки инструментов «на всякий случай». Но модель на каждом шагу вынуждена обрабатывать их описания — это тысячи токенов, даже если инструмент не используется.

Решение: загружать инструменты по требованию. На первом шаге объявляем только базовые (например, «поиск по вики»). Если модель запрашивает что-то специфичное — подгружаем описание через динамическое добавление tool в следующий запрос.

❌ Ошибка новичка: объявлять все 30 инструментов сразу. Это убивает контекст и часто приводит к Tool Storms — модель начинает перебирать инструменты в цикле, как в сценарии Retrieval Thrash и Tool Storms.

tools_registry = {
    "search": "...",
    "calculator": "...",
    "send_email": "...",
    # ... ещё 27
}

# Плохо — все сразу
response = client.messages.create(
    model="claude-opus-4-7-20260429",
    tools=list(tools_registry.values())  # 15k токенов
)

# Хорошо — базовый набор + подгрузка
base_tools = ["search", "calculator"]
response = client.messages.create(
    model="claude-opus-4-7-20260429",
    tools=[tools_registry[t] for t in base_tools]
)
# Если модель говорит: "нужен email", добавляем в следующем запросе
requested_tool = "send_email"
response = client.messages.create(
    model="claude-opus-4-7-20260429",
    tools=[tools_registry[t] for t in base_tools + [requested_tool]]
)

Экономия: в среднем 70% токенов на описаниях инструментов (с 15k до 4k на каждом шаге). Особенно эффективно на длинных сессиях.

4. Маршрутизация: не зовите Opus ради пустяка

Зачем гонять тяжелый Claude Opus 4.7 на триггер «привет, как дела?»? Очевидно, для простых задач хватит малой модели за копейки (Haiku, GPT-4o-mini). Но как определить, что простое, а что сложное?

Ставим роутер — самую дешёвую модель (типа Haiku), которая анализирует запрос и решает, какую модель вызвать. Если Haiku уверена — отвечает сама. Если нет — передаёт Opus.

3 Пример роутера

import asyncio

async def router(query: str):
    # Сначала пробует Haiku
    response = await client.messages.create(
        model="claude-haiku-4-7-20260429",
        messages=[{"role": "user", "content": query}],
        max_tokens=100
    )
    # Если Haiku стопнулась на max_tokens или дала пустой ответ — считаем, что не справилась
    if response.stop_reason == "max_tokens" or "[UNSURE]" in response.content:
        # Передаём Opus
        response = await client.messages.create(
            model="claude-opus-4-7-20260429",
            messages=[{"role": "user", "content": query}]
        )
    return response

⚠️ Важно: Роутер тоже тратит токены. Если Haiku не уверена и перекидывает запрос, вы платите за два вызова. Оптимально, когда Haiku уверена в 60-70% случаев. Экономия общей стоимости — 40-50%.

5. Делегирование субагентам: разделяй и экономь

Вместо того чтобы пихать всю задачу в один контекст (который быстро растёт до десятков тысяч токенов), разбейте её на подзадачи и поручите каждую отдельному агенту-исполнителю. Каждый субагент получает только свой кусок контекста — итоговый расход меньше, чем у монолита.

Я сравнивал: задача «проанализировать код, найти баги и написать отчёт» в монолитном режиме сожгла 25k токенов. С тремя субагентами (анализ + поиск багов + генерация отчёта) — всего 14k. Экономия 44%.

Метод Средний расход токенов Экономия
Монолитный агент 25 000
Субагенты (3 шт.) 14 000 44%
Субагенты + кэширование 10 500 58%

Подробнее про архитектуру субагентов читайте в анализе мультиагентных систем — там разобраны паттерны делегирования и стоимость синхронизации.

Как НЕ надо делать

Не создавайте субагентов для каждой мелочи. Оверхеда на создание и завершение агента (промпты, инициализация) может съесть всю экономию. Используйте субагентов только для крупных, относительно независимых подзадач.

6. Очистка контекста: забудьте прошлое

Агентные системы любят тащить всю историю диалога. После 10 шагов контекст может весить 50k токенов. Но модель не использует 90% старых сообщений — они просто жрут деньги.

Решение: автоматическая обрезка контекста. Есть два подхода:

  • Скользящее окно — хранить только последние N сообщений (например, 10). Старые выбрасывать.
  • Суммаризация — раз в K шагов просить модель сжать историю в короткое резюме и использовать его вместо оригиналов.
MAX_HISTORY = 10
conversation = []

def add_message(role, content):
    conversation.append({"role": role, "content": content})
    if len(conversation) > MAX_HISTORY:
        # Удаляем самое старое сообщение (обычно первое — это интро)
        # Но лучше сохранять системное сообщение
        if conversation[0]["role"] == "system":
            del conversation[1]  # удаляем первое пользовательское
        else:
            del conversation[0]

# Или суммаризация через суб-агента
async def summarize_history(history):
    prompt = f"Сожми историю диалога до 3 предложений: {history}"
    resp = await cheap_model(prompt)
    return [{"role": "system", "content": f"Резюме предыдущего диалога: {resp}"}]

На тестах скользящее окно на 12 сообщений сократило расход на 70% без потери качества (на типовых задачах). Суммаризация даёт ещё 5-10%, но требует отдельного вызова — следите, чтобы вызов суммаризатора не съел экономию.

💡
В системах с высокой автономией используйте детерминированный губернатор из EmoCore — он жестко режет контекст по бюджету, а не по длине.

Сводим всё в одну картину

Вот прикидка экономии по каждому методу (на синтетическом тесте анализа 10 файлов кода, агент делает 30 вызовов):

Метод Токенов сэкономлено Процент Сложность внедрения
Prompt Caching ~45 000 ~35% Низкая
Semantic Caching ~30 000 ~25% Средняя
Lazy Loading инструментов ~60 000 ~40% Низкая
Маршрутизация ~50 000 ~45% (стоимость) Средняя
Субагенты ~35 000 ~30% Высокая
Очистка контекста ~80 000 ~60% Низкая

Комбинируйте методы — например, prompt caching + lazy loading + очистка контекста дают до 85% экономии на длинных сессиях. Я именно так сократил расходы своего кодинг-агента с $500 до $75 в месяц.

Прогноз: куда движется индустрия

К середине 2026 года всё больше провайдеров встраивают кэширование на уровне инфраструктуры (например, Anthropic Batch API с кэшированием). Но ручное управление контекстом останется ключевым навыком DevOps-инженера, работающего с агентами. Следующий шаг — динамическое выделение контекстного окна под каждую задачу (как в AVP). Если вы сейчас внедрите хотя бы 3 из 6 методов — ваш агент будет и быстрее, и дешевле, чем у 90% конкурентов. И да, не забудьте поставить мониторинг токенов — без него вы слепы.

А ещё один неочевидный совет: не доверяйте модели управлять своим контекстом. Не давайте агенту самому решать, что удалить из истории — он либо удалит слишком много, либо не удалит ничего. Используйте детерминированные правила и губернаторы (EmoCore — отличный кандидат).

Подписаться на канал