← The Practical AI Product Engineering Playbook
04 / 10 · 90 min

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

Инструмент вернул 503 Service Unavailable. Как должен повести себя правильно спроектированный агентный цикл?

01Инструмент вернул 503 Service Unavailable. Как должен повести себя правильно спроектированный агентный цикл?

Почему description инструмента нужно писать так же тщательно, как системный промпт?

02Почему description инструмента нужно писать так же тщательно, как системный промпт?

Зачем агентному циклу лимит max_iterations, если модель «сама поймёт», когда остановиться?

03Зачем агентному циклу лимит max_iterations, если модель «сама поймёт», когда остановиться?

Какое из действий обязано проходить через approval gate с участием человека?

04Какое из действий обязано проходить через approval gate с участием человека?

В чём главная практическая выгода разделения planner/executor для многошаговых задач?

05В чём главная практическая выгода разделения planner/executor для многошаговых задач?

Sources

Agents and tool use: when the model starts to act · The Practical AI Product Engineering Playbook · VORCL