Практичні нотатки: Harness Engineering: The Naked Agent: Чому ваша фреймворк-система передає дані
Покроковий посібник з практичних нотаток: Harness Engineering: The Naked Agent – чому ваша архітектура потребує контрактів, перевірок та готових фрагментів коду для команд, які використовують цю схему.
Використовуйте цей матеріал як оновлену версію ідей з книги „Harness Engineering: The Naked Agent: Why Your Framework Hands You a Loop, Not a Harness — I“ для співробітників-операторів: чіткі етапи, впорядковані блоки коду та записи про відновлення, які залишаються при передачі обов’язків.
Частина 1: Простий цикл агента здається потужним, поки до нього не починає надходити реальний трафік. Ось чому збої в продакшені зазвичай виникають через відсутність необхідних механізмів навколо моделі, а не через саму модель.
У частині 1 простий етап найкраще функціонує, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис роботи, один випадок збою та запис про скасування змін перед розширенням обсягу роботи. Документуйте як успішний, так і відновлювальний шляхи роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Визначте ліміти токенів на кожну спробу та сесію. Інструменти агентів активно розширюють контекст; жорсткі обмеження запобігають тому, що демонстрації перетворюються на несподівані рахунки.
Більшість збоїв агентів не є наслідком проблем з моделлю. Вони виникають через відсутній шар дисципліни навколо неї. Ось як виглядає ШІ-агент без контролювальних механізмів у Claude Agent SDK та LangChain Deep Agents, а також три конкретні способи, якими він зламується під реальним навантаженням.
Найкраще розглядати збої агентів як вимірювану характеристику. Збережіть один ідеальний запис роботи, один випадок збою та примітки щодо скасування дій перед тим, як розширювати обсяг роботи. Віддавайте перевагу невеликим, тестованим одиницям коду замість об’ємних скриптів. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Встановіть ліміти кількості токенів на кожен крок та сеанс — інструменти агентів активно розширюють контекст; жорсткі обмеження запобігають тому, що демонстрації перетворюються на несподівані рахунки.
Модель не є змінною
Модель працює найкраще, якщо її розглядати як вимірювану поверхню. Запишіть один ідеальний результат, один випадок невдачі та примітки щодо скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не погоджуйтесь на мовчазне часткове виконання завдань. Встановіть ліміт токенів на кожен крок та на кожну сесію. Інструменти типу агентів активно розширюють контекст; жорсткі обмеження запобігають тому, щоб демонстрації перетворювалися на несподівані рахунки.
Що насправді означає „голий“ стан
Підход «The What naked» найкраще функціонує, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний приклад роботи, один випадок невдачі та примітки щодо скасування змін перед розширенням обсягу завдань. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ. Використовуйте інструменти з вузькими схемами та чіткими позначеннями побічних ефектів. Хостам потрібно знати, які виклики змінюють стан системи, перш ніж вони автоматично схвалюють їх.
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY
TOOLS = [
{"name": "search_flights",
"description": "Search flights between two cities for a date.",
"input_schema": {"type": "object", "properties": {
"origin": {"type": "string"}, "destination": {"type": "string"},
"date": {"type": "string", "description": "YYYY-MM-DD"}},
"required": ["origin", "destination", "date"]}},
{"name": "book_flight",
"description": "Book a specific flight.",
"input_schema": {"type": "object", "properties": {
"flight_id": {"type": "string"}, "passenger_name": {"type": "string"}},
"required": ["flight_id", "passenger_name"]}},
]
def run_naked(user_msg: str) -> str:
messages = [{"role": "user", "content": user_msg}]
while True: # ① no iteration cap
resp = client.messages.create(
model="claude-sonnet-4-6", max_tokens=1024,
tools=TOOLS, messages=messages,
)
if resp.stop_reason != "tool_use":
return resp.content[0].text
call = next(b for b in resp.content if b.type == "tool_use")
result = dispatch(call.name, call.input)
# ② direct side effect, no check
messages.extend([
# ③ whole history, every turn
{"role": "assistant", "content": resp.content},
{"role": "user", "content": [{"type": "tool_result",
"tool_use_id": call.id, "content": result}]},
])
Агент «The naked» у Claude Agent SDK
Агент у стадії розробки працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та прапорці функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Запроваджуйте інструменти з вузькими схемами та чіткими позначеннями побічних ефектів. Хостам потрібно знати, які виклики змінюють стан, перш ніж вони автоматично схвалюють їх. Агент у стадії розробки працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Віддавайте перевагу малим, тестованим одиницям перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій.
import asyncio
from claude_agent_sdk import (
query, ClaudeAgentOptions, tool,
create_sdk_mcp_server, AssistantMessage, ResultMessage,
)
@tool("search_flights", "Search flights between two cities for a date.",
{"origin": str, "destination": str, "date": str})
async def search_flights(args):
# ① no check that date exists
hits = flights_api.search(**args)
return {"content": [{"type": "text", "text": str(hits)}]}
@tool("book_flight", "Book a specific flight.",
{"flight_id": str, "passenger_name": str})
async def book_flight(args):
# ② destructive, ungated
confirmation = flights_api.book(**args)
return {"content": [{"type": "text", "text": confirmation}]}
server = create_sdk_mcp_server("travel", tools=[search_flights, book_flight])
async def main():
options = ClaudeAgentOptions(
mcp_servers={"travel": server},
allowed_tools=["mcp__travel__search_flights",
"mcp__travel__book_flight"],
)
async for msg in query(prompt="Rebook this customer for March 32nd.",
options=options):
if isinstance(msg, AssistantMessage):
for b in msg.content:
if hasattr(b, "text"):
print(b.text)
elif isinstance(msg, ResultMessage):
print("done:", msg.subtype)
# ③ no state survives this run
Агент у LangChain Deep Agents
Для етапу «Оголений агент на сцені» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Призначте назви artefaktам, визначте перевірки успіху та не допускайте беззвучного часткового завершення. Аутентифікуйтеся біля шлюзу та повторно авторизуйтесь на рівні обробки даних. Один лише токен-носій не є межею тенантства.
from langchain.tools import tool
from deepagents import create_deep_agent
@tool
def search_flights(origin: str, destination: str, date: str) -> str:
"""Search flights between two cities for a date (YYYY-MM-DD)."""
return str(flights_api.search(origin, destination, date))
# ① no date check
@tool
def book_flight(flight_id: str, passenger_name: str) -> str:
"""Book a specific flight."""
return flights_api.book(flight_id, passenger_name)
# ② ungated side effect
agent = create_deep_agent(
# ③ the loop, no controls
model="anthropic:claude-sonnet-4-6",
tools=[search_flights, book_flight],
)
result = agent.invoke({"messages": [{"role": "user",
"content": "Rebook this customer for March 32nd."}]})
print(result["messages"][-1].content)
# Ask a follow-up in a second invoke, and it starts from zero: no thread,
# no memory.
Спостерігайте, як це може зламатися трьома способами
Для моніторингу процесу розбивають його на три етапи: визначають вхідні дані, власника кожного етапу та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити етап з відомої точки контролю, не намагаючись визначити прихований стан. Фіксують час виконання та витрати на токени чи запити поруч із функціональними результатами. Відображення витрат заздалегідь запобігає несподіваним рахункам під час переходу з демо-середовища у спільні. Аутентифікація відбувається біля шлюзу, а повторна авторизація — на рівні обробки даних. Один лише токен-носій не є межею окремого тенантства.
Помилка 1: неправильний аргумент потрапляє до руйнівного виклику
У випадку «Помилка 1: неправильна стадія» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функціоналу мають знаходитися в одному місці, яке оператори можуть перевірити, не читаючи весь алгоритм. Аутентифікуватися слід біля шлюзу, а повторна авторизація — на рівні обробки даних. Один лише токен не є межею окремого тенантства. У випадку «Помилка 1: неправильна стадія» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, перевірювані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру обробки даних.
book_flight(flight_id=”AC-PHANTOM”, passenger_name=”J. Moffatt”)
# -> “Booked.” The action fired. Nothing in the loop asked whether it should.
Помилка 2: контекст вибухає, а якість тихо погіршується
Під час роботи над етапом «Контекст вибухає» у рамках Помилки 2 спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається при частковій помилці. Цей перелік допомагає зберігати чесність у подальших змінах коду. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Призначте назви елементам, визначте критерії успіху та не допускайте тихого часткового виконання. Фіксуйте назву інструменту, хеш аргументів, час відгуку та результат кожного виклику. Без цих записів процес дебаггінгу займає години.
Помилка 3: інструмент дає помилку, а агент повідомляє про успіх
Під час роботи над етапом інструменту «Поразка 3» спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається при частковій невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Запишіть час виконання та витрати на токени або запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-середовища до спільних. Записуйте назву інструменту, хеш аргументів, час відгуку та результат кожного виклику. Без цих записів дебагування може займати години.
Формат, якого має дотримуватися кожна частина
Під час роботи над етапом «Форма кожної частини» спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається при частковій невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Записуйте назву інструменту, хеш аргументів, час відгуку та результат кожного виклику. Без цих записів дебагування займає години. Під час роботи над етапом «Форма кожної частини» спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається при частковій невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Віддавайте перевагу малим, тестованим одиницям перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.
Зробіть це сьогодні
Етап «Зробіть це сьогодні» найкраще функціонує, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний результат, один випадок невдачі та примітку про скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Використовуйте інструменти з вузькими схемами та чіткими позначеннями побічних ефектів. Адміністраторам потрібно знати, які виклики змінюють стан системи, перш ніж автоматично схвалювати їх.
Модель — це найпростіша частина
Модель працює найкраще, коли її розглядають як вимірювану поверхню. Запишіть один ідеальний приклад роботи, один випадок збою та примітки щодо скасування змін перед розширенням обсягу завдань. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ. Визначте бюджет на токени на кожен хід та сесію. Інструменти типу агентів активно розширюють контекст; жорсткі обмеження не дозволяють демо-версіям перетворюватися на несподівані рахунки.
Чек-лист операцій
Під час роботи за чек-листом операцій спочатку запишіть умови контракту: необхідні вхідні дані, сигнал успіху та наслідки часткового збою. Цей чек-лист допомагає залишатися чесними під час подальших змін у коді.
Документуйте як ідеальний, так і плановий сценарії роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.
Записуйте назву інструменту журналізації, хеш аргументів, час затримки та результат кожного виклику. Без цих даних процес налагодження триває годинами.
Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв.
Коли дозволяє бюджет, додайте тест на базову функціональність, який перевіряє критичний шлях у середовищі CI за допомогою фікстур, а не реальних платних API.
Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу з демо-середовища у спільні.
Перш ніж підвищувати версії стека, заморозьте їх, збережіть ідеальний запис діяльності для критичного шляху та підтвердьте кроки для скасування змін. У спільних середовищах необхідні обмеження на кількість запитів, перевірки прав доступу та чіткий власник для зміни секретів. Віддавайте перевагу надійності перед креативними одноразовими демонстраціями.
Примітка до пакету 765280e2df21: не включайте ключі постачальників у репозиторій, встановіть ліміт токенів на сеанс та зберігайте транскрипції поруч із фіксами для оцінки, щоб подальша заміна моделей залишалася порівнянною.