Паттерны Context Engineering

Паттерны Context Engineering в 2026: dynamic assembly, budget lanes, priority truncation, compaction, cacheable prefixes и observability.

Паттерны Context Engineering в 2026 лучше рассматривать не как набор “хитростей с prompt”, а как повторяющиеся архитектурные решения для управления model input. На практике самые полезные паттерны сегодня: dynamic assembly, budget lanes, priority truncation, compaction, cacheable prefixes и observability.

Именно их сочетание отличает production-ready LLM-систему от приложения, которое просто шлёт в модель слишком длинный и шумный prompt.

Если Context Engineering — это сборка рабочей папки для модели, то паттерны — это правила, по которым вы решаете, что туда положить, что убрать, что сжать и что держать в готовом виде.

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

Главные паттерны:

ПаттернЧто решаетКороткая идея
Dynamic assemblyне слать одинаковый context всемсобирать контекст под задачу
Budget lanesне переполнять окно хаотичнораспределять токены по слоям
Priority truncationне ломать ответ при переполнениирезать по важности
Compactionне тащить бесконечную историюсхлопывать старое в state/summary
Cacheable prefixesне платить за один и тот же префиксотделять статику от динамики
Observabilityпонимать, что реально работаетмерить utilization, truncation и contribution

Практический смысл

Эти паттерны нужны, когда у вас есть:

  • retrieval;
  • multi-turn state;
  • tool use;
  • personalization;
  • длинные instructions;
  • cost и latency constraints.

Без них система быстро скатывается в две крайности: либо контекста слишком мало, либо его слишком много и он шумный.

1. Dynamic Assembly

Dynamic assembly означает, что контекст собирается по интенту и текущему состоянию задачи, а не по одному универсальному шаблону.

Примеры:

  • support-запрос о заказе: instructions + order API + короткий policy block;
  • research-запрос: instructions + retrieval + citations + structured output;
  • coding-step: repo context + task state + tool results, но почти без user profile;
  • короткий follow-up: только последние turns и актуальный state.
Без техники
{ "title": "Плохо", "content": "Каждому запросу отправляем полный system prompt, 10 retrieved chunks, всю историю, user profile и все tool logs." }
С техникой
{ "title": "Лучше", "content": "Сначала routing. Потом только те слои, которые реально нужны в этом сценарии." }

2. Budget Lanes

Большое context window не отменяет discipline. Даже если модель поддерживает 128K или 1M, без budget lanes контекст быстро раздувается и теряет сигнал.

Полезнее делить budget не “в целом”, а по слоям:

LaneЧто туда попадает
Instructionssystem, policy, output contract
Retrievalchunks, docs, citations
Stateсвежая история, текущий task state
Memoryдолгоживущие user facts
ToolsAPI/DB results, workflow state
Output reserveместо под final answer / reasoning
Пример budget lanes
Instructions15%
Retrieval30%
State20%
Memory10%
Tools10%
Output reserve15%

Точные цифры зависят от кейса, но сам принцип почти всегда одинаков: у каждого слоя должен быть собственный cap.

3. Priority Truncation

Когда контекст не помещается, healthy system не режет его случайно. Она делает priority truncation.

Типичный порядок:

  1. system/policy почти не трогаются;
  2. свежие tool results и последние critical turns защищаются;
  3. retrieval режется по relevance;
  4. memory режется мягко и редко;
  5. старый transcript compact-ится или удаляется;
  6. output reserve не жертвуется первым.
ПромптPriority truncation
Контекст не влезает в окно. Что лучше убрать первым?
1. system policy
2. последний tool result
3. самые слабые retrieval chunks
4. output reserve
Ответ модели

Обычно первым режут самые слабые retrieval chunks и низкоприоритетный history tail. System policy и output reserve лучше защищать.

4. Compaction

Compaction нужен там, где диалог или workflow живут долго. Его задача — превратить длинную transcript-history в короткое рабочее состояние.

Типичные варианты:

  • summary старых turns;
  • state object с фактами и pending tasks;
  • short-term window + long-term summary;
  • latest tool snapshot вместо полного tool log.

Это особенно важно для:

  • support conversations;
  • agent loops;
  • enterprise copilots;
  • multi-step workflows.
Compaction не должен быть “вольным пересказом”. Если summary без структуры, он быстро начинает дрейфовать. Намного надёжнее хранить state как набор фактов, решений, ограничений и open questions.

5. Cacheable Prefixes

Cacheable prefix — это стабильная верхняя часть контекста, которую можно повторно использовать без полной повторной стоимости.

Лучшие кандидаты:

  • system prompt;
  • policy blocks;
  • few-shot examples;
  • schema instructions;
  • длинные неизменные reference blocks.

Плохие кандидаты:

  • свежая история;
  • retrieved chunks под конкретный запрос;
  • live tool results;
  • user-specific ephemeral state.

Смысл паттерна простой: разделите stable prefix и dynamic tail, иначе hit rate будет слабым даже при поддержке provider-side caching.

6. Routing Before Retrieval

Один из самых недооценённых паттернов: сначала lightweight routing, потом retrieval и assembly.

На практике это снижает:

  • лишний retrieval;
  • шумные documents;
  • лишний token spend;
  • latency в простых сценариях.

Хороший routing часто бывает даже rule-based:

  • support/order;
  • support/billing;
  • research;
  • coding;
  • general chat.

И уже только потом подтягиваются нужные collections, tools и contracts.

7. Observability

Без observability Context Engineering быстро превращается в guesswork.

Минимум, что полезно смотреть:

8. Как паттерны комбинируются

Обычно здоровая цепочка выглядит так:

  1. routing;
  2. dynamic assembly;
  3. budget lane allocation;
  4. compaction старого state;
  5. priority truncation при необходимости;
  6. stable prefix caching;
  7. observability и iteration.

То есть паттерны работают не поодиночке, а как pipeline.

Плюсы

  • Делают LLM-систему предсказуемее по качеству, цене и latency
  • Снижают шум и lost-in-the-middle
  • Помогают отделить stable prefix от dynamic tail
  • Упрощают debugging и production iteration

Минусы

  • Требуют отдельного routing и observability layer
  • Усложняют архитектуру по сравнению с одним prompt
  • Compaction и routing тоже стоят latency
  • Без дисциплины быстро вырождаются в набор ad-hoc if/else правил

Пример pipeline с budgets и truncation

from dataclasses import dataclass


@dataclass
class Budget:
    instructions: int = 2_000
    retrieval: int = 10_000
    state: int = 4_000
    memory: int = 1_500
    tools: int = 3_000
    output_reserve: int = 4_000


def assemble_context(
    instructions: str,
    retrieval_chunks: list[str],
    recent_messages: list[dict],
    memory_facts: list[str],
    tool_results: list[dict],
    budget: Budget,
) -> dict:
    return {
        "instructions": instructions[: budget.instructions * 4],
        "retrieval": retrieval_chunks[:4],
        "recent_messages": recent_messages[-4:],
        "memory_facts": memory_facts[:10],
        "tool_results": tool_results[-2:],
        "output_reserve": budget.output_reserve,
    }

Для реальной production-системы этого мало, но даже такой примитивный pipeline уже вводит здоровые ограничения:

  • каждый слой получает cap;
  • свежие turns важнее старого transcript;
  • tool results и retrieval ограничиваются отдельно;
  • output reserve существует заранее, а не “если что останется”.
Проверьте себя

1. Что делает dynamic assembly?

2. Что обычно лучше резать первым при переполнении?

3. Зачем нужен cacheable prefix?