Harness Engineering
Моделът връща текст. Всичко останало — какво влиза, дали действието ще се случи, кой го спира и как агентът разбира, че е успял — е код, който някой е написал.

В последната статия ставаше дума за Context Engineering. Тя обаче подмина един въпрос, който стои под всичко останало: кой решава какво влиза в контекста?
Това не е моделът. Моделът получава прозорец и връща текст. Всичко останало — какво е влязло, дали изходът му ще има някакъв ефект върху света, дали ще бъде извикан отново и кога ще спре — се решава от кода около него.
Този код стана известен като harness, и проектирането му е отделна дисциплина.
Продължаваме със същия пример: агент, който мигрира двеста файла от една библиотека към друга. Context engineering питаше какво вижда агентът на файл 60. Harness engineering пита друго:
- Откъде агентът знае, че файл 59 наистина работи?
- Какво се случва, когато файл 61 счупи билда?
- Кой го спира, ако започне да трие?
- Кой продължава от файл 140, ако процесът умре?
- Кой решава кое е вярното, ако два подагента се върнат с различни отговори?
Нито един от тези въпроси не е за модела.
Моделът, контекстът и harness-ът
Едно сравнение от работното място подрежда трите неща наведнъж.
Моделът е способността. Компанията ви наема високоинтелигентен и образован служител — този ценз не се променя от местоработата на този човек.
Контекстът е онбордингът. Първите две седмици му се дава да чете документация, обясняват се процесите, показват му се проблеми в дизайна и защо някой процес или модул изглежда странно. Това отново не го прави по-умен — прави го информиран за служебния контекст.
Harness-ът е всичко останало в компанията. Лаптопът, достъпът до софтуер, правата в production, CI-ят, код ревюто, това че някой одобрява деплоя, дефиницията на „готово“, ретроспективата.
Третото не е пасивна инфраструктура, която служителят ползва, когато му потрябва. То определя какво изобщо може да се случи.
Harness-ът решава кога моделът се извиква. Решава какво вижда — значи цялото context engineering се случва вътре в harness-а, не до него. Решава дали предложеното действие ще бъде изпълнено, или ще бъде спряно за одобрение. Решава кога работата е приключила. Моделът не може да откаже нито едно от тези решения, защото изобщо не знае, че се вземат.
Не моделът ползва harness-а. Harness-ът ползва модела.
Оттук идва изводът, който прави дисциплината смислена:
Блестящ нов служител без лаптоп, без достъп до кода, без начин да пусне тестове и без никой, който да му каже дали работата му върши работа, не произвежда нищо. Никой не би казал „трябва ни по-умен служител“. Всички биха казали, че организацията е счупена.
При агентите казваме „трябва ни по-добър модел“ изненадващо често.
Най-подценяваното в целия този списък е възприятието — дали агентът изобщо вижда резултата от действието си. Агент, който действа, но не вижда какво е станало, е един от най-честите провали в цялата дисциплина, и затова ще се говори по-подробно след малко.

Какво е harness
Harness-ът е runtime слоят около модела: цикълът, инструментите, състоянието, обратната връзка, гейтовете, възстановяването, прекратяването, наблюдаемостта и оркестрацията, когато агентите станат повече от един.
Най-краткото операционно определение обаче е: harness-ът е цикълът, който превръща извикването на модел в работещ процес.
messages = [{"role": "user", "content": task}]
while True:
response = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
tools=tools,
messages=messages,
)
if response.stop_reason == "end_turn":
break
messages.append({"role": "assistant", "content": response.content})
results = []
for block in response.content:
if block.type == "tool_use":
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": execute(block.name, block.input),
})
messages.append({"role": "user", "content": results})
Петнадесет реда. Това е работещ агент — и е целият harness в най-примитивната си форма.
Заслужава внимание какво не е в тези петнадесет реда, а трябва да бъде в продукция:
execute()изпълнява всичко, което моделът поиска, без да пита никого;- никъде не се проверява дали действието е успяло — резултатът се подава наивно;
- при грешка целият цикъл гърми и часовете работа изчезват;
- няма условие за спиране освен добрата воля на модела;
- ако процесът умре на файл 140, всичко започва от нула;
- никой отвън не вижда какво се е случило вътре.
Harness engineering е попълването на този списък. Всяка следваща глава е един от редовете му.
Няма нужда цикълът да се пише на ръка — SDK-тата предлагат готов tool runner, а има и платформи, които го хостват. Но решенията остават на този, който строи системата. Готовият цикъл спестява
while-а, не архитектурата.
Три вида провали
Това е най-практичната част от статията, защото много загубено време в агентни системи отива в лекуване на грешния слой.
| Слой | Провалът е | Симптом |
|---|---|---|
| Модел | разсъждава зле при добра информация и работещи инструменти | логиката е грешна, но действията са изпълнени и видени |
| Контекст | разсъждава добре, но върху грешна, остаряла или отровена информация | уверено прави нещо, което е било вярно преди тридесет стъпки |
| Harness | не може да действа, не вижда резултата, не е спрян навреме или работата му се губи | твърди, че е готово; повтаря същото действие; спира по средата |
Диагностиката по симптом:
| Наблюдаваш | Най-вероятно е |
|---|---|
| Агентът казва „готово“, а нищо не работи | harness — няма обратна връзка |
| Повтаря същото неуспешно действие | harness — грешката не стига до него |
| Изтрива или деплойва нещо, което не е трябвало | harness — липсва гейт |
| Спира по средата и обявява успех | harness — няма дефиниция за „готово“ |
| Работата изчезва при рестарт | harness — състоянието е живяло в прозореца |
| Избира грешния от петдесет инструмента | контекст — объркване |
| Ползва API, което не съществува | контекст — отравяне… или harness (вж. по-долу) |
| Прави глупост с пълна и вярна информация | модел |
Предпоследният ред от таблицата заслужава внимание, защото свързва двете статии.
В статията за Context Engineering отравянето беше описано като контекстен провал: агентът измисля сигнатура на файл 12 и я преизползва до файл 90. Вярно е — но е и harness провал. Ако в цикъла има type checker, измислената сигнатура умира на файл 12 и никога не влиза в историята.
Оттук следва принцип, който държи изненадващо широко:
Добрата обратна връзка намалява нуждата от context engineering.
Агент, който може да провери, не носи предположения напред. Голяма част от натрупването, срещу което се борим с компактиране и изчистване, съществува само защото агентът е трябвало да помни неща, вместо да може да ги провери.

Патърните
1. Обратната връзка — най-важното нещо.
Агент без обратна връзка не работи. Той генерира. Разликата е, че генерирането изглежда еднакво успешно и когато е вярно, и когато не е.
Ако агентът напише код и никога не го пусне, той ще ти докладва успех. Не защото лъже, а защото няма как да знае друго. Изходът му е текст, който изглежда като работещ код, и това е цялата информация, с която разполага.
Затова първият въпрос към всеки агент не е „какви инструменти има“, а „как разбира, че е успял?“
Нивата, в нарастващ ред на стойност:
| Ниво | Инструмент | Хваща |
|---|---|---|
| Никакво | — | нищо; агентът винаги „успява“ |
| Синтактично | linter, парсер | счупен код |
| Типово | type checker, компилатор | несъществуващи API, грешни сигнатури |
| Поведенческо | тестове | грешна логика |
| Семантично | човек или втори агент | правилно изпълнена грешна задача |
За миграцията: агент с тестове в цикъла работи. Агент без тестове произвежда двеста файла, които изглеждат мигрирани.
Ключовото при имплементацията е, че грешката трябва да се върне като резултат, не да се хвърли като изключение:
def execute(name, args):
if name == "migrate_file":
write_file(args["path"], args["content"])
result = run_tests(args["path"]) # ← harness-ът проверява
if result.failed:
return {
"content": f"Тестовете паднаха:\n{result.output}",
"is_error": True, # ← и връща провала обратно
}
return {"content": "OK, тестовете минават"}
Хвърлена грешка прекъсва нормалния agent loop, освен ако harness-ът не я прихване и не я превърне в tool result. Върната грешка като структуриран резултат е информация, върху която моделът може да действа — вижда какво е паднало и коригира.
Правило, което си струва да се запомни: на агента се дава същата обратна връзка, която би получил и човек. Никой не онбордва разработчик, като му забранява да пуска тестовете.
2. Дизайн на инструментите — какво може harness-ът да прихване
Тук се крие най-подценяваното решение в цялата дисциплина.
Моделът не знае къде минава границата на сигурност, каква е политиката за одобрения, нито как изглежда интерфейсът. Той емитира заявки за действия. Формата на тези заявки определя какво harness-ът изобщо може да направи с тях.
bash дава максимална широта. Агентът може почти всичко. Но на harness-а му остава непрозрачен низ — една и съща форма за всяко действие:
{ "name": "bash", "input": { "command": "rm -rf ./generated && npm run build" } }
Специализираният инструмент дава типизирани аргументи, върху които може да се вземе решение:
{ "name": "delete_path", "input": { "path": "./generated", "recursive": true } }
Втората форма може да бъде спряна за одобрение, логната, показана в интерфейс, засечена за паралелна безопасност. Първата също може да бъде контролирана, но вече изисква анализ на shell командата — много по-трудна и ненадеждна граница.
Кога си струва действието да получи собствен инструмент:
- Граница на сигурност. Нещо, което трябва да минава през одобрение.
- Проверка за остарялост. Специализиран
editможе да откаже запис, ако файлът е променен, откакто агентът го е чел последно. При общbashподобно ограничение е значително по-трудно да се наложи структурно — и си струва да се забележи, че така harness-ът предотвратява контекстен провал. - Визуализация. Действия, които трябва да се покажат на човек по определен начин.
- Паралелизъм. Harness-ът може да пуска едновременно неща, за които знае, че са безопасни. Ако всичко е обвито в общи shell команди, класифицирането на тези действия става много по-трудно.
Практическото правило: започва се с bash за широта, а в отделен инструмент се повишава онова, което трябва да се гейтва, показва, одитира или паралелизира.
3. Гейтове — критерият е обратимостта
Изкушението е да се гейтва по „опасност“. По-полезната ос е друга.
Колко струва да се върне назад?
Евтино за връщане — пуска се. Скъпо или невъзможно — гейтва се, независимо колко безобидно изглежда.
| Действие | Обратимо? | Гейт |
|---|---|---|
| Редакция на файл в git | да, лесно | не |
| Пускане на тестове | да, нищо не се променя | не |
rm -rf извън работната директория |
не | да |
| Изпращане на имейл | не | да |
| Деплой в продукция | понякога, скъпо | да |
| Заявка към платен API | не — парите са похарчени | зависи от сумата |
Забелязва се, че „изпращане на имейл“ може да е по-опасно от изтриването на git-tracked файл, въпреки че звучи по-безобидно. Файлът може да се възстанови. Имейлът вече е тръгнал.
Двата практични детайла: гейтът трябва да е в harness-а, не в промпта — инструкция „винаги питай преди да триеш“ е препоръка, а не ограничение. И отказът трябва да носи причина, за да може агентът да опита друго, вместо да блъска в същата стена.
4. Възстановяване — състоянието не бива да живее в прозореца
Ако процесът умре на файл 140 и работата започне от нула, harness-ът е счупен.
Тук двете статии се срещат директно. Патърнът „пиши навън“ от context engineering не е само за икономия на токени — той е условието за възстановимост. Прогрес файлът оцелява след рестарт. Контекстният прозорец — не.
Минимумът е три неща да бъдат извън прозореца: докъде е стигнала работата, какво е научено по пътя и кое е следващото. Ако това е в устойчиво хранилище, рестартът струва един прочит.
Свързан въпрос е този за повторните опити. Разликата, която има значение:
- Преходна грешка — мрежа, rate limit, timeout — harness-ът може да повтори сам, без да безпокои модела. Нищо не е нужно да влиза в контекста.
- Смислена грешка — тестът падна, файлът не съществува — отива при модела, защото е информация, върху която трябва да се разсъждава.
Смесването на двете е чест дефект: преходните грешки влизат в прозореца като съдържание и го тровят, а смислените се повтарят механично и никога не се коригират.
5. Прекратяване — агентите не знаят кога да спрат
Три различни провала се крият под една дума.
Спира твърде рано. Обявява успех на файл 60. Лекува се с проверимо условие за „готово“: не „когато моделът каже“, а „когато 200 от 200 файла имат минаващи тестове“. Дефиницията за готово принадлежи на harness-а.
Не спира изобщо. Върти един и същ цикъл. Harness-ът може да засича повторения — три пъти едно и също действие с един и същ резултат е сигнал за прекъсване, а не за четвърти опит.
Свършва ресурсът по средата. max_tokens е ограничение върху конкретния model response. Но задачата има и друг бюджет: време, токени, стъпки, пари или брой tool calls. Този бюджет принадлежи на harness-а.
Например:
MAX_STEPS = 200
MAX_COST = 10.00
while not done:
if steps >= MAX_STEPS or cost >= MAX_COST:
checkpoint_state()
break
response = call_model(...)
cost += estimate_cost(response)
steps += 1
Разликата е важна. Ако се разчита само на лимита на един response, моделът може да бъде отрязан по средата на работа. Ако harness-ът следи бюджета на цялата задача, той може да спре контролирано, да запише прогреса и да остави процеса във възстановимо състояние.
6. Наблюдаемост — не се оправя това, което не се вижда
Агентът е недетерминиран процес, който може да вземе стотици решения. Когато сгреши, единственият начин да разбереш защо е да видиш какво е видял и какво се е случило след това.
Минимумът за всяка стъпка: какво е влязло в контекста, какво действие е предложил моделът, какво е върнал инструментът, колко време и ресурси е струвало. Без това дебъгването е гадаене, а „работи по-добре днес“ — не е инженерство.
Полезните метрики зависят от системата, но няколко почти винаги си струват: колко стъпки отнема една задача, колко tool calls се провалят, колко retry има, колко често се стига до гейт, каква е цената на успешна задача и как се използва контекстният кеш.
Първите хващат цикли и излишна работа. Последното може да покаже тихи промени в контекста, които унищожават cache hit rate и качват цената, без поведението видимо да се е променило.

7. Оркестрация — когато агентите станат повече от един
Ако задачата се разпада на независими части, всяка може да получи свой прозорец. В предишната статия това беше „Изолирай“ — един от четирите глагола, разгледан като начин да се пести контекст. Тук е другата му страна: изолацията е контекстна печалба, но оркестрацията е harness работа. Кой поражда подагент, какво вижда той, какво връща и кой решава какво значи всичко това — нищо от това не се случва в модела.
За миграцията: подагент на файл или на група файлове. Всеки получава своя файл, правилата от прогрес файла и нищо друго. Родителят никога не вижда съдържанието на двестате файла — вижда двеста реда резултат.
Това е патърнът, който може да умножи капацитета, вместо просто да го разтяга. Останалите шест го пестят. И е единственият, при който harness-ът може да сгреши по начин, който при един агент просто не съществува.
Четирите решения
Какво вижда подагентът. Той тръгва от нула. Каквото му трябва, трябва да му се подаде — това е брифингът и е най-честият провал. Недобрифиран подагент преоткрива всичко сам и струва повече, отколкото спестява.
Какво може да прави. Правата се раздават на подагент, не глобално. Агент, който само чете и обобщава, няма нужда от право на запис. Тук раздел 3 се прилага повторно, но на друго ниво.
Какво връща. Ако петима подагенти върнат свободен текст, родителят трябва да прочете пет есета — преместил си контекстния проблем, не си го решил. Договорът за връщане е конкретен harness лост:
RESULT_SCHEMA = {
"type": "object",
"properties": {
"file": {"type": "string"},
"status": {"enum": ["ok", "failed", "skipped"]},
"tests_pass": {"type": "boolean"},
"new_rule": {"type": ["string", "null"]}, # правило, ако е научено
"blocked_by": {"type": ["string", "null"]},
},
"required": ["file", "status", "tests_pass"],
}
Пет полета вместо пет параграфа. Родителят може да ги обработи, без да ги чете.
Какво става при провал. Един от петимата умира. Пада ли всичко? Повтаря ли се? Продължава ли с четирима и отбелязва ли липсващия? Това решение трябва да е взето предварително — по подразбиране обикновено е „тихо изчезва“.
Проблемът със сливането
Ето въпроса, който няма аналог при един агент.
Два подагента мигрират различни файлове. И двата срещат .timeout(ms). Първият работи по файл, в който таймаутът само ограничава заявката — заключава, че еквивалентът е AbortSignal.timeout(ms). Вторият работи по файл, в който таймаутът трябва да може да се отменя отвън — заключава, че е нужен ръчен AbortController.
И двамата са прави. За своя файл.
Родителят получава две правила, които си противоречат, и записва едното в прогрес файла. Оттам нататък сто и петдесет файла се мигрират по правило, вярно за половината от тях.
Забелязва се, че никой подагент не е халюцинирал, никой не е сгрешил и никакъв контекст не е бил отровен. Дефектът е изцяло в сливането — тоест в harness-а.
Какво може да направи родителят:
| Стратегия | Кога | Цена |
|---|---|---|
| Взима последното | никога — това е позиция, не преценка | тиха грешка |
| Избира по критерий | когато критерият е дефиниран предварително | трябва да го измислиш |
| Пита трети агент | когато има с какво да се разсъди | пари, и може да не реши |
| Ескалира към човек | при необратими или скъпи решения | латентност |
| Задържа и двете | когато правилото има обхват | усложнява прогрес файла |
Последният ред е верният отговор в този пример: правилото не е едно, а зависи от това дали таймаутът трябва да е отменяем. Но за да стигне дотам, harness-ът първо трябва да е забелязал противоречието.
Оттук и принципът:
Противоречието между подагенти е сигнал, не шум.
Ако двама агенти с еднакви инструкции стигат до различни изводи, това е информация: задачата може да е двусмислена, контекстът — недостатъчен, или решението — действително да има повече от един валиден вариант. Harness, който тихо избира един от двата отговора, изхвърля най-ценното нещо, което току-що е произвел.

Кога да не го правиш
Подагентът плаща, за да установи контекст, който родителят вече има. Ако задачата се свършва с три извиквания, делегирането губи.
Цената е реална: координация, латентност, загуба на кеша при друг модел — и най-подценяваното, че отстрани изглежда като напредък. Пет паралелни агента изглеждат по-продуктивни от един. Не са, ако четири от тях преоткриват едно и също.
Правило: делегира се, когато частите са наистина независими и всяка е по-голяма от брифинга си.
Защо harness-ът е продуктът
Тук е интересната индустриална част.
Коденето с агенти се превърна в конкурентен пазар за много кратко време, а продуктите често имат достъп до модели със сходни способности. Разликата в резултата обаче може да е огромна — и голяма част от нея идва от harness-а.
Кой инструмент е повишен от bash в отделен. Какво се показва на човека и какво се изпълнява мълчаливо. Кога се пита за одобрение. Какво се случва при паднал тест. Как се възстановява прекъсната сесия. Как се управлява контекстът между стъпките.
Това е и причината един и същ модел да върши работа в едната среда и да разочарова в другата. Когато моделът е един и същ, първото място за търсене на разликата често е системата около него.
Няколко неща от екосистемата, които си струва да се знаят по име:
Готови agent loops. Не е нужно while-ът винаги да се пише на ръка. SDK-та и agent frameworks могат да въртят цикъла над вече дефинираните инструменти и да оставят точки за намеса — за одобрение, логване, промяна на резултата или прекратяване. Има и хоствани варианти, при които част от цикъла и execution средата са чужда грижа. Изборът е между това колко от harness-а се пише на ръка и колко от него остава под контрол.
MCP е част от инструменталната повърхност. Той стандартизира начина, по който capabilities като tools и resources могат да бъдат изложени към AI приложения. Това премахва част от интеграционната работа, но не решава orchestration-а: кои действия се гейтват, какви права има агентът, как се правят retry, recovery и termination — това остава работа на системата около модела.
A2A е протоколна граница за делегиране. Когато една част от задачата се предава на друг агент, вече не е нужно да се споделя неговият вътрешен цикъл, инструменти и контекст. Виждат се договорената задача, статусът и резултатът, които минават през протокола. Това прави границата между две агентни системи експлицитна: едната делегира работа, другата сама решава как да я изпълни.
Evals са обратната връзка за самия harness. Обратната връзка в цикъла казва на агента дали е успял. Evals казват на инженера дали промяната в harness-а е подобрила нещо. Без тях всяка настройка е вкус.
Какво да се има предвид
Първият въпрос е откъде агентът знае, че е успял. Ако отговорът е „не знае“, всичко останало е второстепенно. Оттам се започва.
Проверява се какво се случва при провал. Пуска се агент срещу задача, която ще падне. Ако грешката не стига до него в използваема форма, това е една от най-евтините възможни поправки.
Прави се списък на необратимите действия. После се проверява кои от тях минават през гейт. Разликата между двата списъка е рискът, който системата носи в момента.
Процесът се убива по средата. Буквално. Ако рестартът не може да продължи, състоянието живее на грешното място.
Преглежда се един пълен trace. Не обобщение — целият. Повечето хора не са гледали какво точно е видял агентът им на стъпка четиридесет, а точно там често е отговорът.
Преброяват се колко от инструментите са bash. После — за колко от тях би било добре да може да се каже „не“.
Ако има подагенти, се проверява какво прави родителят при противоречие. Ако отговорът е „взима последния прочетен“, това е решение, което никой не е взел съзнателно.
Финални думи
Prompt engineering питаше какво да се каже на модела.
Context engineering пита какво вижда моделът.
Harness engineering пита какво може моделът да направи, как разбира дали е станало и кой го спира.
Трите не се конкурират — те са три слоя на една и съща система. И много от провалите в продукция се оказват в най-долния, защото той е слоят, върху който контролът е най-пряк.
Моделът е даден. Контекстът е дисциплина. Harness-ът е код, който някой трябва да напише.
Изкушаващо е да се мисли за модела като за нещото, което ползва инструменти. По-полезно е обратното:
Harness-ът е системата. Моделът е компонент в нея — най-способният, но не той решава.
Работата не е моделът да стане по-умен. Тя е да се построи организацията, в която един умен служител може да свърши нещо полезно, без да счупи нищо необратимо, и в която някой ще забележи, ако сгреши.