Context Window Budgeting

Context Window Budgeting в 2026: budget lanes, output reserve, token counting, truncation и compaction для LLM-приложений.

Context Window Budgeting в 2026 полезно понимать не как “таблицу лимитов моделей”, а как распределение ограниченного input budget между слоями контекста. Даже если модель поддерживает большое окно, это не отменяет economics, latency и риск lost-in-the-middle.

Именно поэтому главный вопрос звучит не “сколько токенов держит модель”, а какие токены действительно заслуживают места в текущем вызове.

Контекстное окно похоже на багаж в ручной клади. Формально чемодан может быть большим, но если вы заполнили его случайными вещами, нужные предметы либо не поместятся, либо потеряются внутри. Budgeting решает, что взять обязательно, а что сжать или оставить дома.

Короткая версия

Хороший budgeting почти всегда строится вокруг шести lane-ов:

LaneЧто туда кладут
Instructionssystem, policy, output contract
Retrievalretrieved chunks и citations
Stateсвежая история и task state
Memoryдолгоживущие user facts
ToolsAPI/DB results
Output reserveзапас под ответ и reasoning

Три главных правила

  1. Всегда оставляйте output reserve.
  2. Не давайте retrieval и history бесконечно разрастаться.
  3. Режьте по priority, а не случайно.
ПромптBudget lanes
Запрос: billing support

Budget:
- instructions: 2K
- retrieval: 8K
- state: 3K
- memory: 1K
- tools: 2K
- output reserve: 3K
Ответ модели

Такой budget сразу задаёт дисциплину: tool result не вытеснит policy, retrieval не съест весь input, а у модели останется место под нормальный ответ.

1. Budgeting начинается не с модели, а со сценария

Одна и та же модель может требовать разного budget design для разных задач.

Примеры:

  • support chat: больше state и tools;
  • RAG answer engine: больше retrieval и citations;
  • coding workflow: больше repo context и task state;
  • analytics agent: больше structured tool results;
  • short deterministic extraction: минимальный state, высокий output discipline.

Поэтому universal budget template почти никогда не работает одинаково хорошо для всех route-ов.

2. Lane-based budgeting лучше, чем “один общий лимит”

Самый практичный способ распределять окно — через budget lanes.

LaneРоль
Instructionsзадаёт правила и contract
Retrievalприносит внешние знания
Stateдержит свежую continuity
Memoryхранит долгоживущие факты
Toolsдаёт live data
Output reserveзащищает место под answer/reasoning
Пример budget lanes для support route
Instructions15%
Retrieval25%
State20%
Memory10%
Tools15%
Output reserve15%

Это не универсальные проценты. Но сама логика почти всегда полезнее, чем хаотичное “влезло или не влезло”.

3. Output reserve нужен всегда

Одна из самых частых ошибок — использовать почти всё окно на input и оставлять модели слишком мало места под output.

Из-за этого происходят типовые проблемы:

  • обрезанный final answer;
  • сломанный structured output;
  • неуместно короткие ответы;
  • ухудшение reasoning-heavy сценариев.

Поэтому output reserve лучше закладывать заранее как отдельный budget lane, а не как остаток “что останется после history и retrieval”.

4. Где чаще всего утекают токены

На практике токенный budget сильнее всего портят:

  • длинные system prompts без явной пользы;
  • retrieval без relevance cap;
  • полная transcript-history;
  • сырой JSON от tools;
  • дубли между layers;
  • отсутствие compaction.
Без техники
{ "title": "Плохо", "content": "12 retrieved chunks, 40 сообщений истории, полный tool JSON и длинный policy block в одном запросе." }
С техникой
{ "title": "Лучше", "content": "3 полезных chunks, последние 4 turns, compact tool result и короткий versioned policy block." }

5. Counting и estimation

Точный счёт токенов зависит от модели и токенизатора, но operationally важнее не абсолютная магия подсчёта, а стабильная практика:

  • считать токены до вызова;
  • логировать tokens per lane;
  • видеть input/output separately;
  • следить за route-level drift.

Полезно измерять:

  • total input tokens;
  • output tokens;
  • tokens by lane;
  • truncation events;
  • cacheable prefix size.

6. Priority-based truncation лучше случайной обрезки

Когда budget exceeded, контекст не должен ломаться хаотично.

Healthy priority часто выглядит так:

  1. instructions и output reserve защищены;
  2. свежий tool result и последние critical turns защищены;
  3. retrieval режется по relevance;
  4. memory режется мягко;
  5. старый history tail удаляется или compact-ится.
Обрезать “последние N символов” всего assembled prompt почти всегда неправильно. Так вы рискуете удалить и важный state, и часть schema, и критичный tool result без какой-либо логики.

7. Compaction почти всегда дешевле бесконечной истории

Когда диалог длинный, лучше перевести старую историю в state object или summary, чем продолжать слать весь transcript.

Обычно здоровый набор такой:

  • последние 2-6 turns как есть;
  • summary старой части;
  • long-term memory отдельно;
  • latest tool snapshot отдельно.

Это одновременно:

  • экономит токены;
  • улучшает signal-to-noise ratio;
  • делает контекст устойчивее к длинным сессиям.

8. Budgeting зависит от типа route

Ниже не “правильные проценты навсегда”, а ориентиры:

RouteЧто обычно получает больший budget
Supportstate, tools, немного retrieval
RAG QAretrieval, citations, output reserve
Codingretrieval/repo context, state
Analyticstools, compact state
Short extractioninstructions, output contract, reserve

Главная идея: budget должен следовать за задачей, а не за красивой статической таблицей.

9. Что мониторить у budgeting layer

Минимальный набор метрик:

10. Простой budgeting checklist

Перед отправкой запроса полезно задать себе пять вопросов:

  1. Этот route вообще требует retrieval?
  2. Нужна ли полная история или хватит summary + последних turns?
  3. Можно ли сократить tool result до ключевых полей?
  4. Достаточно ли output reserve?
  5. Какие слои будут урезаны первыми при overflow?

Плюсы

  • Делает input budget управляемым и предсказуемым
  • Снижает риск lost-in-the-middle и шумного контекста
  • Помогает контролировать cost и latency
  • Упрощает route-specific tuning

Минусы

  • Требует отдельной логики budgets и truncation
  • Нуждается в измерениях, а не в интуиции
  • Плохо переносит статические проценты для всех сценариев
  • Без compaction быстро деградирует на длинных диалогах

Пример budget config по lane-ам

from dataclasses import dataclass


@dataclass
class Budget:
    instructions: int
    retrieval: int
    state: int
    memory: int
    tools: int
    output_reserve: int


SUPPORT_BUDGET = Budget(
    instructions=2_000,
    retrieval=8_000,
    state=3_000,
    memory=1_000,
    tools=2_000,
    output_reserve=3_000,
)

Пример простого truncation policy

def trim_retrieval(chunks: list[dict], max_chunks: int = 3) -> list[dict]:
    ranked = sorted(chunks, key=lambda c: c["score"], reverse=True)
    return ranked[:max_chunks]


def trim_history(messages: list[dict], keep_last: int = 4) -> list[dict]:
    return messages[-keep_last:]


def compact_tool_result(result: dict) -> dict:
    keys = ["id", "status", "amount", "updated_at"]
    return {k: result[k] for k in keys if k in result}

Даже такой примитив уже лучше, чем отправлять:

  • весь retrieval;
  • всю историю;
  • весь tool payload;
  • нулевой output reserve.
Проверьте себя

1. Какой принцип budgeting самый здоровый?

2. Что чаще всего нужно резать первым?

3. Зачем нужен compaction?