Tools and effects
После главы вы сможете спроектировать tool boundary, где модель не вызывает произвольный код, а предлагает typed operation, которую runtime валидирует и авторизует.
Tool call — недоверенный input
Модель может выбрать несуществующее имя, пропустить поле, передать строку вместо числа или повторить старый call. Поэтому выполнение начинается не с handler:
В Helios этот pipeline разделён на prepare → authorize → invoke. Между фазами runtime перечитывает authoritative state. Это важно: interrupt, пришедший пока policy engine думал, должен остановить handler до side effect; результат handler, завершившегося уже после interrupt, наоборот, нельзя потерять — его нужно один раз записать как факт.
Контракт хорошего tool
- одно ясное назначение;
- machine-readable input schema;
- структурированный output;
- effect class;
- typed failures;
- tenant/user identity из trusted runtime context, а не из model arguments;
- operation ID для записи;
- postcondition или фактический resource ID.
Плохое имя manage_account скрывает десятки полномочий. get_account, prepare_transfer, confirm_transfer позволяют разнести read, preview и commit.
Anthropic описывает похожий эффект tool design на model performance; это provider observation, а не гарантия для любого model/tool set: Writing effective tools for agents.
Typed errors
| Ошибка | Значение для runtime | Следующий шаг |
|---|---|---|
unknown_tool | Model/runtime contract drift | Не исполнять; re-plan или fail |
invalid_arguments | Schema не пройдена | Дать компактную ошибку модели |
policy_denied | Действие запрещено | Не retry; объяснить/эскалировать |
approval_required | Нужен human decision | Durable pause |
timeout | Outcome может быть неизвестен | Reconcile/idempotent retry |
retryable_error | Временный сбой до outcome | Bounded retry |
permanent_error | Те же args не помогут | Изменить plan/data |
retry_exhausted | Budget исчерпан | Degrade/escalate |
«Tool failed» стирает recovery semantics. Timeout особенно опасен: внешний effect мог завершиться, хотя ответ потерялся.
Effect classes
Helios использует:
read— не меняет domain state;reversible_write— запись с понятным rollback/release;external_write— сообщение, публикация, создание внешнего ресурса;destructive— необратимое удаление/изменение.
Это эвристика. Добавьте sensitivity, amount, destination, environment и legal jurisdiction, если они меняют policy.
Model decision ≠ policy decision
Модель отвечает: «полезно вызвать reserve_funds». Policy engine отвечает: «этому tenant, на эту сумму, в этом run, с этим approval — разрешено или нет».
Если prompt одновременно выбирает действие и авторизует его, prompt injection становится confused deputy: недоверенные данные убеждают систему применить чужие credentials.
Helios policy:
- read разрешён;
- reversible/external write требует approval;
- destructive отключён даже при
approved: true.
Commit-time authorization
Проверяйте policy максимально близко к effect. Approval, полученный час назад для 10 SOL, не должен автоматически разрешать изменённый call на 1000 SOL или другого получателя.
Approval event должен включать:
- точный tool и arguments/preview;
- identity approver;
- scope и expiry;
- решение
approve/edit/reject; - policy/version, по которой решали.
Учебный runtime реализует три решения, но не хранит реальную identity — это явно не production auth service.
Idempotency
При crash после effect до checkpoint runtime повторит pending call. Без idempotency получится двойная reservation.
idempotency identity = tenant + operation ID
same ID + same arguments → вернуть прежний outcome
same ID + different arguments → conflict
Проверка current state перед записью помогает, но не заменяет atomic uniqueness: два worker могут одновременно увидеть «ещё не выполнено».
Lease/fence runtime защищает только собственный event store. Если stale worker уже отправил запрос во внешний банк или marketplace, downstream также должен дедуплицировать operation ID или проверять fencing/version token. Без такой поддержки остаётся reconciliation и честная at-least-once семантика.
Где должен жить business rule
| Место | Сильная сторона | Слабость |
|---|---|---|
| Prompt | Объясняет intent модели | Не security boundary |
| Tool schema | Форма и локальные constraints | Не знает полный domain state |
| Policy engine | Authority и cross-cutting rules | Нужны свежие inputs |
| Domain service/DB | Инварианты и atomicity | Не объясняет модели варианты |
Надёжная система использует все четыре, не выбирает одно.
Helios и реальные аналоги
reserve_funds моделирует escrow hold. В neobank аналог — card authorization; в marketplace — reservation stock; в telehealth — booking slot. Во всех случаях convincing text не доказывает effect, а repeated timeout не должен умножать запись.
Лаборатория
npm run test:course:pattern -- "tool|policy|reservation"
Попробуйте добавить external_write tool. Решите, какие fields должны входить в approval preview и idempotency fingerprint.
Стратегии интерфейса
| Стратегия | Выигрыш | Цена | Когда плоха |
|---|---|---|---|
| Один универсальный tool | Быстрый prototype | Огромная authority, сложные evals | Production writes |
| Много узких tools | Ясные effects | Tool selection overload | Сотни почти одинаковых операций |
| Search/lazy-loaded tools | Малый context | Discovery latency и miss | 5 стабильных tools |
| Composite domain workflow tool | Atomic business operation | Меньше свободы модели | Open-ended investigation |
Self-check
- Кто задаёт tenant identity?
- Что отличает validation error от policy denial?
- Какие writes идемпотентны и по какому key?
- Проверяется ли authorization прямо перед commit?
- Возвращает ли tool фактический outcome и resource ID?
- Что произойдёт при timeout после внешней записи?