Agents and tool use: when the model starts to act
Tool contracts, an agent loop with budgets, separating planning from execution, handling tool errors and human-in-the-loop for risky actions.
Lesson content is currently available in Russian.
What you will learn
- →Писать tool contracts с чёткими входами, выходами и структурированными ошибками.
- →Строить агентный loop с лимитами итераций, бюджетом и трассировкой.
- →Разделять планирование и исполнение и ставить approval gates на write-действия.
Tool contract: вход, выход, ошибки
Инструмент для агента — это функция с описанием, которое читает модель. Ключевой сдвиг мышления: name и description — это часть промпта. Модель выбирает инструмент и заполняет аргументы, опираясь только на них; расплывчатое описание — и агент начинает «догадываться» вместо того, чтобы действовать.
const getInvoiceStatus = {
name: 'get_invoice_status',
description:
'Возвращает статус счёта по его номеру. Только чтение. ' +
'Используй, когда клиент спрашивает про оплату или задолженность.',
input_schema: {
type: 'object',
properties: {
invoiceId: { type: 'string', pattern: '^INV-[0-9]{6}$' },
},
required: ['invoiceId'],
},
};
Правила контракта, проверенные болью:
- Строгая схема входа с паттернами и enum: модель, ограниченная схемой, ошибается реже, а невалидный вызов отбивается до исполнения.
- Ошибки — это данные, а не исключения. Инструмент возвращает структурированный результат и в успехе, и в провале:
{ ok: false, error: "invoice_not_found", hint: "проверь формат INV-XXXXXX" }. Модель умеет реагировать на такой ответ; на голый stack trace — нет. - Идемпотентность где возможно: агент может повторить вызов после таймаута — повторное создание платежа должно быть невозможным на уровне инструмента (idempotency key), а не на уровне надежды.
- Таймауты и лимиты внутри инструмента: инструмент, висящий 60 секунд, вешает весь агентный цикл.
Один инструмент — одна ответственность. «Швейцарский нож» с параметром action из десяти значений — антипаттерн: модель путает режимы, а вы не можете дать разным действиям разные права.
Агентный цикл и его пределы
Ядро любого агента — цикл: модель получает задачу и список инструментов → возвращает либо ответ, либо tool calls → система исполняет вызовы → результаты возвращаются модели → повтор. Всё остальное (память, планирование, UI) — обвязка вокруг этого цикла.
Наивная реализация цикла — while (true) — в проде запрещена. Обязательные ограничители:
- max_iterations: обычно 5–15 в зависимости от задачи. Агент, не решивший задачу за лимит, возвращает частичный результат и честное объяснение, а не крутится вечно.
- Бюджет в токенах и деньгах на задачу: одна залипшая задача не должна стоить как месяц нормальной работы. Считайте расход в цикле и останавливайте по порогу.
- Таймаут стенового времени: сумма таймаутов инструментов плюс инференс.
- Детектор зацикливания: два одинаковых вызова с одинаковыми аргументами подряд — почти всегда петля; вместо третьего повтора модели возвращается сигнал «этот вызов уже делался, результат тот же».
Состояние агента (история вызовов, промежуточные результаты) храните вне контекста модели — в своей структуре данных. Контекст — это представление состояния для модели, а не само состояние: при длинных задачах историю можно сжимать (суммаризация старых шагов), не теряя аудит.
И каждый шаг цикла — в trace: какой был контекст, какие инструменты предлагались, что модель выбрала, что вернул инструмент, сколько токенов сожжено. Без этого отладка агента превращается в гадание. Норма здоровой задачи — 2–4 tool calls; если медиана растёт к лимиту, это ранний сигнал деградации промпта или инструментов.
Планирование против исполнения
Для простых задач достаточно ReAct-паттерна: модель думает и действует в одном цикле, чередуя рассуждение и вызовы. Но когда шагов больше трёх-четырёх, а инструментов много, смешение стратегии и тактики начинает давать хаотичные траектории: агент делает вызов, отвлекается на промежуточный результат, забывает исходную цель.
Рабочее разделение:
- Planner получает задачу и формирует план — короткий структурированный список шагов с ожидаемыми результатами. План — это артефакт: его можно показать человеку до исполнения, залогировать, сравнить с фактической траекторией.
- Executor исполняет шаги по одному, с доступом только к инструментам текущего шага. Ему не нужно держать в голове всю стратегию — только текущий шаг и его контекст.
- Re-planning по триггеру: если шаг провалился или реальность разошлась с планом (инструмент вернул неожиданное), executor не импровизирует — управление возвращается planner-у с фактами о расхождении.
Что это даёт на практике. Во-первых, предсказуемость: план из пяти шагов с понятными инструментами — это то, что можно ревьюить, в отличие от свободного полёта на пятнадцать вызовов. Во-вторых, точку для human approval: человек утверждает план целиком до исполнения — это в разы дешевле, чем подтверждать каждый вызов. В-третьих, дешёвую диагностику: сравнение план/факт сразу показывает, где агент сошёл с рельсов.
Антипаттерн — планирование ради планирования: если задача решается за 1–2 вызова, planner-слой только добавляет латентность и токены. Начинайте с ReAct, выделяйте planner, когда траектории станут длинными и нестабильными — это видно по trace-ам, а не по ощущениям.
Human-in-the-loop и отказоустойчивость
Классифицируйте инструменты по последствиям — от этого зависит вся политика безопасности:
- Read-only (поиск, чтение статуса): агент вызывает свободно.
- Write обратимые (черновик письма, комментарий, создание тикета): допустимы автоматически при хорошем аудите, либо через мягкое подтверждение.
- Write необратимые или дорогие (отправка денег, удаление данных, письмо клиенту от имени компании): только через approval gate — действие ставится в очередь, человек видит контекст (что агент хочет сделать и почему, с каким входом) и явно подтверждает или отклоняет. Каждое решение — в audit trail.
Обработка ошибок инструментов — то, что отличает игрушку от продовой системы:
- Transient-ошибки (таймаут, 429, 503) — ограниченный retry с экспоненциальным backoff на уровне системы, модель об этом даже не узнаёт.
- Постоянные ошибки (404, невалидные аргументы, отказ в доступе) — не повторять: структурированная ошибка возвращается модели, и она решает — переформулировать вызов, взять другой инструмент или честно сообщить пользователю.
- Железное правило: инструмент недоступен — данные не выдумываются. Модель, не получившая статус счёта, обязана сказать «не удалось получить статус», а не сгенерировать правдоподобный. Это правило прописывается в промпте и проверяется отдельным eval-кейсом с намеренно сломанным инструментом.
Для необратимых действий продумайте compensating actions: как откатить то, что откатывается (отменить бронь), и как минимизировать то, что не откатывается (лимиты сумм, allowlist получателей). Лимиты на уровне инструмента надёжнее любых инструкций в промпте: промпт можно уговорить, лимит — нет.
Practice
Support agent
Сделайте агента поддержки с одним read-only инструментом get_invoice_status (со структурированными ошибками) и одним write-инструментом send_reply за approval gate. Агентный цикл: max 3 итерации, бюджет токенов, детектор повторных вызовов, trace каждого шага. Обязательный eval-кейс: инструмент возвращает ошибку — агент не выдумывает статус счёта.
Deliverable
Tool loop не более чем в 3 calls, очередь approval для send_reply, trace-лог всех шагов и прохождение кейса «инструмент сломан → агент честно сообщает о сбое».
Checkpoint
Что делать, если инструмент вернул ошибку или неполные данные?
- ✓ Transient и постоянные ошибки различаются: retry с backoff против структурированной ошибки модели.
- ✓ Есть fallback и честное сообщение пользователю, а не фантазия модели.
- ✓ Все вызовы, ошибки и решения отражены в trace, write-действия проходят approval gate.
Check yourself
Sources