Оркестрация tool-calling AI-агента: SOP, лимиты и audit trail
Оркестрация tool-calling в бизнес-агенте — это не «модель сама вызовет API», а жёсткий SOP: planner решает что делать, executor вызывает только whitelist, а каждый шаг пишет audit trail с лимитами и причиной остановки.
Я вывожу tool-calling в прод только когда вижу три артефакта: карту инструментов с правами, лимиты на сессию и полный журнал «запрос → tool → результат → решение». Без этого агент либо молчит на полезных шагах, либо жжёт бюджет и CRM в цикле.
Planner / Executor: две роли вместо одного «умного» цикла
Смешивать «подумай и сразу дерни API» в одном промпте — путь к непредсказуемым side-effect. Я разделяю поток на две роли с разными правами.
| Роль | Вход | Выход | Права на tool |
|---|---|---|---|
| Planner | Цель, контекст CRM, политика | План шагов + stop-условия | Только read/search |
| Executor | Один шаг плана + схема args | Результат tool + raw/normalized | Whitelist write/action |
| Guard | План или результат | allow / deny / escalate | Нет внешних вызовов |
| Auditor | События сессии | Запись в лог, score риска | Append-only |
Минимальный SOP на один тикет
- Intake: тип задачи, обязательные поля, channel.
- Planner: 1–5 шагов, без «и ещё что-нибудь полезное».
- Guard на план: запреты policy (деньги, юр., удаление).
- Executor по одному шагу; после каждого — проверка схемы ответа.
- Stop: цель достигнута / нехватка данных / лимит / escalate.
- Handoff-пакет или закрытие с кратким summary для человека.
Один «агент в while True» без явного stop я в бою не держу: стоимость и риск растут быстрее качества.
Whitelist инструментов и контракт аргументов
Tool-calling ломается чаще на кривых args и «лишних» правах, чем на «глупой» модели. Я фиксирую контракт на каждый инструмент.
| Поле контракта | Зачем | Пример |
|---|---|---|
| name | Стабильный id в логах | crm.update_deal_stage |
| description | Когда вызывать (и когда нет) | Только после подтверждённого intent |
| input_schema | JSON Schema / pydantic | deal_id uuid, stage enum |
| side_effect | none / write / money / external | write |
| auth_scope | Минимальные права | deals:write:own |
| timeout_ms | Защита от висяков | 8000 |
| retry_policy | idempotent only | max 2, backoff |
| on_deny | Что сказать пользователю | «Нужен менеджер» |
Чеклист перед подключением нового tool
- Есть ли идемпотентный ключ (чтобы retry не создал дубль)?
- Можно ли выполнить действие без tool (тогда tool не нужен)?
- Записан ли пример bad args и ответ API на ошибку?
- Есть ли маскирование PII в логах запроса/ответа?
- Кто владелец tool в команде (on-call при 5xx)?
- Есть ли feature-flag на отключение без релиза агента?
Пока на эти вопросы нет ответов, tool остаётся в staging с синтетическими тикетами.
Лимиты сессии: токены, шаги, деньги, время
Без лимитов tool-calling превращается в неконтролируемый счёт. Я ставлю жёсткие потолки на сессию и на инструмент.
| Лимит | Типичное значение на старте | Что делать при hit |
|---|---|---|
| max_tool_calls | 6–10 на тикет | escalate + summary |
| max_planner_revisions | 2 | handoff «не смог спланировать» |
| max_wall_time | 90–180 сек | partial result + human |
| max_cost_tokens | бюджет на intent-класс | degrade: только read-tools |
| max_write_tools | 1–3 write за сессию | дальнейшие write → human |
| circuit_breaker | N ошибок API подряд | pause tool на 5–15 мин |
Правила, которые снижают «зацикливание»
- После двух одинаковых ошибок tool — не повторять с теми же args.
- Write-tool только после явного плана с обоснованием.
- Любой tool с side_effect=money — только через human confirm (отдельный шаг UI/сообщения).
- Параллельные tool-call — только для read; write строго последовательно.
Лимиты я храню рядом с политикой, а не «в голове у промпта»: промпт можно обойти формулировкой, код лимита — нет.
Audit trail: что писать, чтобы разбирать инциденты за минуты
Когда клиент говорит «бот сам поменял сделку», спор решает журнал. Я пишу события в одном формате.
| Событие | Обязательные поля |
|---|---|
| session_start | session_id, channel, user/hash, intent |
| plan_created | steps[], policy_version |
| tool_call | tool, args_redacted, attempt |
| tool_result | ok/error, latency_ms, result_hash |
| decision | continue / stop / escalate, reason |
| handoff | package_id, assignee_queue |
| session_end | outcome, cost_tokens, tool_count |
Как читать audit при разборе
- Найти
session_idпо времени и channel. - Сверить
policy_versionи whitelist — не уехал ли конфиг. - Посмотреть первый deny/error — часто корень там, а не в последнем шаге.
- Сравнить args write-tool с тем, что в CRM сейчас (дрейф данных).
- Если модели «передумали» mid-flight — усилить guard на план, не «сменить модель».
Без audit trail я не спорю с бизнесом о «вине ИИ»: нет следа — нет улучшения, только мнения.
Часто задаваемые вопросы
Нужен ли отдельный planner, если модель «умеет» tool-calling из коробки?
Для демо — нет. Для боя с CRM и деньгами — да: отдельный план и guard на write снижают сюрпризы сильнее, чем смена провайдера.
Что важнее: больше tools или жёстче контракты?
Жёстче контракты. Десять размытых tools хуже трёх с схемой, timeout и idempotency key.
Как понять, что лимиты слишком жёсткие?
Смотрю долю session_end по причине max_tool_calls при успешном partial progress. Если >15% и handoff «дожимает» в 1 шаг — поднимаю лимит точечно на этот intent.
Можно ли логировать полные ответы API?
Только в secure store с TTL и redaction. В обычные чаты/тикеты — hash, статус и бизнес-поля без секретов.
Читайте также
- Архитектура AI-агента для бизнеса: роли, guardrails и handoff
- Протокол handoff AI-агента: QA-скоринг в production
- Pipeline от заявки до результата: интеграции, сбои, эскалация
Готов разобрать ваш tool-calling контур и собрать SOP с лимитами под ваш CRM: https://raisovich.ru
Зачем разделять planner и executor в tool-calling агенте?
Чтобы план с read-правами не смешивался с write-вызовами: executor идёт по whitelist по одному шагу, а guard режет money/legal side-effect до факта.
Что обязательно писать в audit trail tool-calling сессии?
События «запрос → tool → args/result (с redaction) → решение/stop» с session_id, policy_version и причиной остановки — иначе инцидент в CRM не разобрать за минуты.
Какие лимиты ставить на tool-calling в первой боевой версии?
Типичный старт: 6–10 tool calls на тикет, 1–3 write за сессию, wall time 90–180 с и circuit breaker на серии ошибок API — лимиты в коде, не только в промпте.
Подпишитесь на @raisovich_news
Первыми получайте новые статьи об AI-автоматизации, нейросетях для бизнеса и создании сайтов. Без спама — только полезный контент.
Часто задаваемые вопросы
Зачем разделять planner и executor в tool-calling агенте?
Чтобы план с read-правами не смешивался с write-вызовами: executor идёт по whitelist по одному шагу, а guard режет money/legal side-effect до факта.
Что обязательно писать в audit trail tool-calling сессии?
События «запрос → tool → args/result (с redaction) → решение/stop» с session_id, policy_version и причиной остановки — иначе инцидент в CRM не разобрать за минуты.