applied-ai2026-08-0416 минNikolay Angelov

Context Engineering

Контекстният прозорец не е кутия, която се пълни, а бюджет, който се администрира — какво влиза, какво остава и какво се изхвърля на всяка стъпка от един агентен цикъл.

Context Engineering — hero

Доскоро въпросът беше „как да формулирам това по-добре". Днес въпросът е „какво изобщо трябва да вижда моделът в момента, в който взема това решение". Смяната на въпроса има име и това е статията за него.

Един пример минава през целия текст. На агент е възложена миграция: двеста файла трябва да минат от една библиотека към друга. Работата не се събира в едно извикване, нито в десет. Агентът ще чете, ще пише, ще пуска тестове, ще се връща назад — часове наред.

Всичко, което следва, се вижда върху този пример.

Една задача, стотици решения, натрупващ се контекст


Какво е Context Engineering

Всеки модел взема решения на база на това, което вижда в момента.

Проблемът е, че при агентните системи това „какво вижда“ непрекъснато се променя.

Точно това е Context Engineering: дисциплината да се управлява какво точно вижда моделът на всяка стъпка от една многостъпкова задача.

Контекстният прозорец не е кутия, която се пълни. Той е бюджет, който се администрира. Всичко, което влезе в него, се конкурира за вниманието на модела с всичко останало — и се плаща при всяко следващо извикване, защото историята се препраща наново всеки път.

Може би най-точната формулировка е на Андрей Карпати: изкуството контекстният прозорец да се напълни точно с това, което трябва за следващата стъпка. Обърнете внимание на „за следващата стъпка". Не става въпрос за задачата, а само за стъпката.

Какво е контекст — всичко изброено:

  • системният промпт;
  • дефинициите на инструментите (те се рендерират преди системния промпт);
  • цялата история на разговора;
  • резултатите от всяко извикване на инструмент;
  • изтеглени документи, файлове, резултати от търсене;
  • файловете с памет, ако агентът има такива;
  • и накрая — това, което потребителят е написал.

Контекстният прозорец като бюджет

Промптът е последният елемент в списъка и обикновено най-малкият. При двеста файла системният промпт е може би 2% от прозореца. Останалите 98% са натрупване.

Натрупването е проблемът.

Оттук нататък статията е за това какво се прави по въпроса. Схемата долу показва пътя от всичко, до което агентът има достъп, до това, което реално влиза в прозореца сега.

Context Engineering като pipeline

Това се случва на всяка стъпка, наново. По-нататък ще говорим за стратегиите, които го правят възможно — но механиката е тази.


Разликата от Prompt Engineering

Prompt engineering не е заменен. Той е сведен до един компонент от нещо по-голямо — и компонентът, върху който имате най-пълен контрол, се оказва най-малкият.

При prompt engineering входът е авторски. При context engineering входът е натрупан.

Prompt Engineering Context Engineering
Единица работа едно извикване цикъл от N стъпки
Кой пише входа човекът натрупва се сам
Какво се оптимизира формулировка какво влиза и какво излиза
Основен провал моделът разбра грешно моделът вижда грешното
Инструменти думи архитектура
Кога се случва преди изпълнение по време на изпълнение

Това е по-дълбоко, отколкото звучи. Когато пишете промпт, всяка дума в него е минала през вас. Когато агентът е на файл 60 от 200, деветдесет процента от прозореца му е съдържание, което никой човек не е чел: изходи от grep, съдържания на файлове, резултати от тестове, негови собствени разсъждения отпреди четиридесет стъпки. Нито сте го виждали, нито сте го чели.

Могат да се контролират само две неща: какво има право да влезе и кога нещо трябва да излезе.

Аналогията, която върши работа: prompt engineering е писането на добра SQL заявка. Context engineering е проектирането на схемата. Второто не заменя първото — но ако схемата е грешна, никоя заявка няма да спаси ситуацията.


Пет вида грешки в контекста

Тук си струва да сме конкретни, защото „моделът се обърква при дълъг контекст" е безполезно като диагноза. Провалите са различни и се лекуват различно.

Пет вида грешки в контекста

Преди грешките: изчерпване, цена и латентност

Най-очевидният. Двеста файла не се събират. Дори с милион токена прозорец, агент, който чете файл, редактира го, пуска тестове и чете изхода, изразходва по няколко хиляди токена на файл. На файл 70 прозорецът е пълен.

Това поне е честен провал — забелязва се, има грешка или compaction се задейства. Останалите са по-коварни, защото системата продължава да работи и просто дава по-лоши отговори.

Историята се препраща цялата, при всяко извикване. Разговор, който е стигнал 200K токена, плаща 200K на всяка следваща стъпка. Без кеширане дълъг агентен цикъл струва квадратично на дължината си.

Това са физическите ограничения на прозореца. Следващите пет проблема са по-коварни: системата продължава да работи, но започва да работи по-зле.

Разсейване

Колкото повече история се натрупва, толкова повече моделът се обляга на нея, вместо да разсъждава наново. Проявява се като повтаряне: агентът мигрира файл, който вече е мигрирал, защото отпреди тридесет стъпки в контекста стои съобщение, че този файл предстои.

Симптом, по който се разпознава: агентът започва да преразказва какво е правил, вместо да прави следващото нещо.

Отравяне

Най-скъпият. На файл 12 агентът е сгрешил сигнатурата на новото API — измислил е параметър, който не съществува. Тази грешка сега стои в контекста като факт. На файлове от 13 до 90 той я преизползва последователно и уверено, защото за него тя е точно толкова достоверна, колкото и останалото в прозореца.

Контекстът няма понятие за „това беше предположение". Всичко вътре има еднаква тежест. Една халюцинация, влязла рано, се самовъзпроизвежда до края на сесията.

Сблъсък

Агентът е прочел файл на стъпка 20. На стъпка 45 сам го е редактирал. И двете версии са в контекста. Коя е истината?

Или: на стъпка 5 е направил план. На стъпка 60 планът е ревизиран. Старият план не е изчезнал — той продължава да седи горе и да дърпа поведението към себе си.

Объркване

Ако на модел се дадат петдесет инструмента, той започва да избира грешния. Не защото не разбира кой какво прави, а защото описанията се конкурират. Същото важи за изтеглени документи: пет релевантни са по-добре от петдесет, от които пет са релевантни.

Загубено в средата

Информация в средата на дълъг прозорец се ползва по-слабо от същата информация в началото или в края. Значи позицията е дизайнерско решение, а не случайност. Ако критично ограничение стои на 40% от прозореца, то е на най-лошото възможно място.

Lost in the Middle


Патърните

Индустрията се уталожи около четири глагола. Полезни са, защото са изчерпателни: всяка техника е един от тях.

Пишете навън. Избирайте. Свивайте. Изолирайте.

Четирите действия в Context Engineering

1. Пишете навън — извадете състоянието от прозореца

Най-подценяваният патърн, и най-евтиният.

Ако нещо трябва да преживее контекста, то не бива да живее в контекста. Трябва да се запази на диска.

Progress файл. За миграцията: един markdown файл, който агентът поддържа сам.

# Миграция: legacy-http → fetch-client

## Готови (67/200)
- src/api/users.ts — ok, тестовете минават
- src/api/orders.ts — ok
- src/api/billing.ts — ЧАСТИЧНО: retry логиката няма еквивалент, виж бележка

## Правила, научени по пътя
- `.timeout(ms)` става `signal: AbortSignal.timeout(ms)` — НЕ `timeout:`
- при POST старият клиент сериализира сам; новият иска explicit JSON.stringify
- файловете в src/legacy/ се пропускат — маркирани са deprecated

## Следващ
- src/api/reports.ts

Този файл струва двеста токена и решава четири от изброените по-горе провала. Разсейването изчезва, защото „какво е готово" се чете от файл, а не се реконструира от история. Отравянето става поправимо, защото правилата са на едно място и могат да се редактират. Сблъсъкът изчезва, защото има един източник на истина за прогреса.

Ключово: файлът не е log. Той е текущото състояние. Агентът го презаписва, не го допълва. Log-ът расте, състоянието — не.

Не разчитайте моделът да помни. Накарайте го да записва.

Памет между сесиите. Progress файлът умира със задачата. Това, което трябва да преживее и нея, е друг патърн: отделни файлове, по един факт във файл, с кратко описание отгоре и индекс, който се зарежда в началото на всяка сесия.

---
name: fetch-client-timeout-migration
description: Как се мигрира timeout при преход от legacy-http към fetch-client
---

`.timeout(ms)` няма пряк еквивалент. Използва се
`signal: AbortSignal.timeout(ms)`. Полето `timeout:` в options се игнорира мълчаливо —
това е причината тестовете да минават, а заявките да висят в продукция.

Три неща правят този патърн работещ:

  • Един факт на файл. Файл с двадесет факта се зарежда целият, дори когато е нужен само един от тях.
  • Описанието е ключът за търсене. То е това, което се чете, за да се реши дали файлът изобщо да се отвори. Пише се за намиране, не за човек.
  • Markdown, не векторна база. Може да се чете, да се редактира, влиза в git, вижда се в diff, и когато нещо е грешно — трие се. Векторната база е непрозрачна точно когато най-много е нужно да се види какво агентът „помни".

Ако това звучи като AGENTS.md или CLAUDE.md — да, същото семейство е. Разликата е, че CLAUDE.md е статичен и го пише човек, а паметта се пише от агента по време на работа.

В Anthropic API това има и готова форма: memory tool-ът (memory_20250818) излага на модела команди за четене и писане в директория с памет. Бекендът е ваш — точно затова е проста директория, а не услуга.

2. Избирайте — контролирайте какво влиза

Не зареждайте предварително това, което може да се вземе навреме. Класическата грешка е всичките двеста файла да се натъпчат в началото „за контекст". Правилното е агентът да има glob и read и да чете файла, когато стигне до него.

Това е и разликата между RAG от 2023 г. и днешния подход: тогава изтеглянето на информация ставаше преди разсъждението, сега агентът изтегля по време на разсъждението, защото сам знае какво му трябва.

Прогресивно разкриване. Skills работят точно така: описанието на умението стои в контекста постоянно и струва един ред, а тялото се зарежда само когато задачата го поиска. Съдържанието на диска може да е сто страници — цената в прозореца е изречение.

Tool search. Ако агентът има петдесет инструмента, петдесетте схеми стоят в прозореца от първата секунда. Алтернативата е инструментите да се маркират defer_loading: true и моделът да си ги търси, когато потрябват (tool_search_tool_regex_20251119 или BM25 вариантът). Схемите се добавят, а не се подменят — което пази кеша.

tools = [
    # Търсачката сама НЕ се отлага — иначе моделът няма с какво да търси.
    {"type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex"},

    # Всичко останало влиза в прозореца едва когато потрябва.
    {"name": "get_leave_balance", "description": "...",
     "input_schema": {...}, "defer_loading": True},
    {"name": "team_coverage", "description": "...",
     "input_schema": {...}, "defer_loading": True},
    # … още четиридесет и осем
]

Ако се отложи всичко, включително търсачката, API-то връща 400. Поне един инструмент трябва да е зареден — иначе моделът е сляп за собствения си инструментариум.

Филтрирайте преди да влезе. Ако инструмент връща 40K токена JSON, а на модела му трябват три полета, филтрирането е ваша работа, не негова. При programmatic tool calling резултатът отива в изпълняващ се код, а не в контекста — в прозореца влиза само финалният изход.

3. Свивайте — контролирайте какво остава

Тук има две различни неща, които постоянно се бъркат.

Compaction обобщава. Когато контекстът наближи прага, историята се резюмира и резюмето замества оригинала. В API-то е сървърна функция (бета compact-2026-01-12, праг по подразбиране около 150K токена).

Един капан си струва да се има в предвид предварително: в следващата заявка се връща целият response.content, не само текстът. Compaction блоковете са част от content-а и API-то ги ползва, за да замести компактираната история. Ако се извади само текстът, състоянието се губи мълчаливо.

response = client.beta.messages.create(
    model="claude-opus-5",
    max_tokens=16000,
    betas=["compact-2026-01-12"],
    context_management={"edits": [{"type": "compact_20260112"}]},
    messages=messages,
)

# Точно тук се къса. Целият content, не само текстът:
messages.append({"role": "assistant", "content": response.content})

# А НЕ това — компактираното състояние изчезва мълчаливо, без грешка:
# messages.append({"role": "assistant", "content": response.content[0].text})

Грешката няма да се обади сама. Разговорът просто започва да губи история и изглежда, че моделът забравя.

Context editing изчиства. Не резюмира — маха. Стратегиите са две: clear_tool_uses_20250919 за стари резултати от инструменти и clear_thinking_20251015 за мисловни блокове (бета context-management-2025-06-27).

client.beta.messages.create(
    model="claude-opus-5",
    max_tokens=16000,
    betas=["context-management-2025-06-27"],
    context_management={
        "edits": [
            # Маха и аргументите, с които е извикан инструментът, не само резултата.
            {"type": "clear_tool_uses_20250919", "clear_tool_inputs": True},
            {"type": "clear_thinking_20251015"},
        ]
    },
    tools=tools,
    messages=messages,
)

Кое кога: изчистването е за неща, които нямат остатъчна стойност — изходът от grep отпреди четиридесет стъпки, съдържанието на файл, който вече е мигриран. Обобщаването е за неща, чиято същина трябва да оцелее — какво беше решено и защо.

Практическото правило: изчиствайте агресивно резултатите от инструменти, обобщавайте пестеливо разсъжденията. Резултатите се получават повторно с едно извикване. Решенията — не.

Task budget. Ако има цикъл, който може да избяга, на модела може да се даде таван, за който той да бъде уведомен (бета task-budgets-2026-03-13, минимум 20 000 токена). Разликата от max_tokens е, че моделът вижда обратното броене и приоритизира — приключва чисто, вместо да бъде отрязан по средата.

4. Изолирайте — не един прозорец, а няколко

Ако задачата се разпада на независими части, всяка може да получи свой прозорец.

За миграцията: подагент на файл. Всеки получава файла, правилата от progress файла и нищо друго. Връща „готово" плюс евентуално ново правило. Родителят никога не вижда съдържанието на файловете — той вижда шестдесет и седем реда резултат.

Това е единственият патърн, който наистина умножава капацитета, вместо да го пести. Останалите три го разтягат.

Цената: координация, латентност, и загуба на кеша, ако подагентът е на друг модел. Струва си, когато частите са наистина независими. Не си струва за задача, която родителят би свършил сам с три извиквания — тогава подагентът плаща повече за установяване на контекст, отколкото спестява.

Отделен случай на изолация: карантина за ненадеждно съдържание. Ако агентът чете уеб страници или чужди документи, това съдържание може да носи инструкции. Да мине първо през подагент, който извлича само данните, е и защита, и context engineering.


Кеширането е част от дисциплината

Това се пропуска, а е директно последствие от реда, в който се сглобява контекстът.

Кеширането е префиксно съвпадение. Един променен байт на позиция N инвалидира всичко след N. Редът на рендиране е toolssystemmessages.

Оттук следва цялата дисциплина:

  • Стабилното отпред, променливото отзад. Дата в системния промпт инвалидира целия прозорец при всяка заявка.
  • Не пипайте инструментите по средата. Те са на позиция нула — добавяне на един инструмент инвалидира всичко.
  • Сериализирайте детерминистично. JSON.stringify без сортирани ключове е тих убиец.
  • Проверявайте, не предполагайте. usage.cache_read_input_tokens. Ако е нула при повтарящи се заявки с еднакъв префикс, някъде има невидим инвалидатор.

На практика цялата дисциплина се свежда до това къде стои една граница:

client.messages.create(
    model="claude-opus-5",
    max_tokens=16000,
    system=[
        {"type": "text",
         "text": STABLE_INSTRUCTIONS,                 # никога не се променя
         "cache_control": {"type": "ephemeral"}},     # ← границата на кеша
    ],
    messages=[
        *history,
        # Днешната дата стои ТУК, след границата — не в системния промпт.
        {"role": "user", "content": f"Днес е {today}. {question}"},
    ],
)

Същият низ с датата, преместен тридесет реда по-нагоре в system, инвалидира целия прозорец при всяка заявка. Разликата между работещ и неработещ кеш често е точно толкова — един ред, преместен от грешната страна на границата.

Цената: четене от кеш е около 0.1× от нормалното, запис е 1.25× (за петминутния TTL) или 2× (за едночасовия). При дълъг агентен цикъл това е разликата между поносимо и абсурдно.

Минималният кешируем префикс зависи от модела и не е монотонен между поколенията — 512 токена при най-новите, но 4096 при някои по-стари. Промпт, който не се е кеширал, може да започне да се кешира само от смяната на модела.


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

Интересното е, че почти всички модерни агентни рамки постепенно започнаха да решават именно тези проблеми — макар и по различен начин.

Терминът се утвърди през 2025 — първо в разговори около това, че „prompt engineering" вече не описва работата, която реално се върши около агентите. Оттогава има няколко неща, които се срещат навсякъде и си струва да се познават по име.

Четирите глагола (write / select / compress / isolate) са най-разпространената таксономия и я използвах по-горе.

Таксономията на провалите — poisoning, distraction, confusion, clash — е другата широко цитирана рамка и е полезна, защото превръща „моделът се обърка" в диагноза с лечение.

AGENTS.md / CLAUDE.md се утвърди като конвенция: файл в корена на проекта, който описва на агента как се работи по този код. Това е context engineering, направен от човек и версиониран в git.

MCP е водопроводът. Той не решава кое влиза в прозореца — но стандартизира откъде може да дойде, и прави tool search и прогресивното разкриване приложими еднакво за всички източници.

Конкретиката по-долу е от Anthropic API — кодовите примери в статията са с него. Другите доставчици имат еквиваленти на част от механизмите, но имената и поведението се различават. Ето какво е налично и си струва да се знае поименно:

Механизъм Какво прави
Prompt caching префиксът не се преизчислява
Compaction обобщава историята при приближаване на прага
Context editing изчиства стари tool резултати и мисловни блокове
Memory tool директория с памет, която преживява сесията
Tool search схемите на инструментите се зареждат при нужда
Skills инструкции с прогресивно разкриване
Task budgets моделът вижда таван и приоритизира
Programmatic tool calling междинните резултати не влизат в контекста

Тази таблица ще остарее по-бързо от останалата част на статията. Точните имена на бета хедърите и праговете се менят — проверявайте ги, преди да ги вписвате в код.


Какво да направите утре

Ако имате агент в продукция и искате да започнете отнякъде, редът е този.

Първо мерете, после оптимизирайте. Вижте докъде стига прозорецът в реален цикъл и колко от него е кеширано. Повечето хора оптимизират грешното нещо, защото са предположили къде е проблемът.

u = response.usage
print("некеширано:      ", u.input_tokens)
print("записано в кеша: ", u.cache_creation_input_tokens)
print("прочетено от кеш:", u.cache_read_input_tokens)

Целият промпт е сборът от трите. input_tokens е само остатъкът — ако агентът е работил с часове, а числото показва 4K, останалото е дошло от кеша. Точно тук се крие най-честата грешна диагноза: гледа се едно число, което по дизайн не показва размера на контекста.

Дайте му къде да пише. Progress файл е половин ден работа и решава повече от всичко останало в тази статия. Започнете оттам.

Изчиствайте резултатите от инструменти агресивно. Те са най-обемното и най-бързо обезценяващото се съдържание в прозореца.

Не свивайте преждевременно. Compaction върху разговор, който така или иначе щеше да свърши, е чиста загуба — плаща се обобщение за нещо, което не е пречело.

Дръжте инструментите под контрол. Ако са над двадесет, погледнете tool search, преди да добавите двадесет и първия.

Запишете това, което иначе ще бъде преизведено. Правило, научено на файл 12 и записано, струва двеста токена. Същото правило, преоткрито на файл 90, струва цял цикъл — ако изобщо бъде преоткрито, а не подминато.


Финални думи

Prompt engineering питаше: какво да кажа на модела.

Context engineering пита: какво вижда моделът, кой е решил това и какво трябва да си отиде, преди да влезе следващото.

Промяната не е в техниките. Тя е в това, че входът вече не е авторски текст, а натрупано състояние — и състоянието се управлява, не се формулира.

Затова и работата приличаше на писане, а вече прилича на архитектура. Въпросите са същите като за всяка система със състояние: какво влиза, какво живее колко дълго, какво се изхвърля и кой го решава.

Промптът е това, което казвате. Контекстът е това, което моделът знае. Втората работа е по-голямата.