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

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

Какво е Context Engineering
Всеки модел взема решения на база на това, което вижда в момента.
Проблемът е, че при агентните системи това „какво вижда“ непрекъснато се променя.
Точно това е Context Engineering: дисциплината да се управлява какво точно вижда моделът на всяка стъпка от една многостъпкова задача.
Контекстният прозорец не е кутия, която се пълни. Той е бюджет, който се администрира. Всичко, което влезе в него, се конкурира за вниманието на модела с всичко останало — и се плаща при всяко следващо извикване, защото историята се препраща наново всеки път.
Може би най-точната формулировка е на Андрей Карпати: изкуството контекстният прозорец да се напълни точно с това, което трябва за следващата стъпка. Обърнете внимание на „за следващата стъпка". Не става въпрос за задачата, а само за стъпката.
Какво е контекст — всичко изброено:
- системният промпт;
- дефинициите на инструментите (те се рендерират преди системния промпт);
- цялата история на разговора;
- резултатите от всяко извикване на инструмент;
- изтеглени документи, файлове, резултати от търсене;
- файловете с памет, ако агентът има такива;
- и накрая — това, което потребителят е написал.

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

Това се случва на всяка стъпка, наново. По-нататък ще говорим за стратегиите, които го правят възможно — но механиката е тази.
Разликата от 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% от прозореца, то е на най-лошото възможно място.

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

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. Редът на рендиране е tools → system → messages.
Оттук следва цялата дисциплина:
- Стабилното отпред, променливото отзад. Дата в системния промпт инвалидира целия прозорец при всяка заявка.
- Не пипайте инструментите по средата. Те са на позиция нула — добавяне на един инструмент инвалидира всичко.
- Сериализирайте детерминистично.
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 пита: какво вижда моделът, кой е решил това и какво трябва да си отиде, преди да влезе следващото.
Промяната не е в техниките. Тя е в това, че входът вече не е авторски текст, а натрупано състояние — и състоянието се управлява, не се формулира.
Затова и работата приличаше на писане, а вече прилича на архитектура. Въпросите са същите като за всяка система със състояние: какво влиза, какво живее колко дълго, какво се изхвърля и кой го решава.
Промптът е това, което казвате. Контекстът е това, което моделът знае. Втората работа е по-голямата.