Function calling не исполняет действие автоматически
Модель возвращает структуру с именем функции и аргументами. Ваше приложение решает, разрешена ли функция, корректны ли параметры и можно ли выполнить действие. Этот промежуточный слой является границей безопасности.
Даже при strict structured output схема подтверждает форму, но не право пользователя и не бизнес-смысл. Валидный JSON способен содержать чужой account ID или чрезмерную сумму.
Жизненный цикл tool call
Propose
Модель выбирает tool и arguments.
Validate
Runtime проверяет права, policy и данные.
Execute
Идемпотентное действие и typed result.
После result модель может продолжить. Весь loop ограничивается числом шагов, дедлайном и бюджетом.
Шаг 1. Выберите узкую бизнес-операцию
Tool manage_customer скрывает чтение, изменение и удаление. Разделите его на get_customer_summary, propose_customer_update и apply_approved_customer_update. Это упрощает права и тестирование.
| Опасный tool | Узкая альтернатива | Контроль |
|---|---|---|
| execute_sql | get_invoice_status | Server-side tenant filter |
| http_request | lookup_shipping_quote | Fixed destination/schema |
| run_shell | build_project_in_sandbox | Isolated workspace/limits |
| send_message | preview_email + send_approved_email | Recipient approval |
| delete_resource | archive_selected_resource | Recoverable action |
Шаг 2. Название и description - часть routing policy
Модель выбирает инструмент по имени, description и parameter docs. Пишите, что tool делает, когда его использовать, когда не использовать и какой precondition нужен. Не прячьте критический запрет только в system prompt.
Tool name: get_order_status Does: returns current status for one order visible to actor. Use when: user asks about an existing order and order reference is known. Do not use for: searching all customers, changing or cancelling orders. Inputs: public order reference; actor/tenant are injected server-side. Output: typed status, timestamps, next allowed actions. Errors: NOT_FOUND_OR_NOT_ALLOWED, TEMPORARY_UNAVAILABLE. Side effects: none. Data sensitivity: internal.
Шаг 3. Строгая schema уменьшает пространство ошибок
Используйте конкретные типы, required, enums, formats, bounds и additionalProperties: false, если поддерживается API. Помните, что провайдеры поддерживают разные подмножества JSON Schema - проверяйте актуальную документацию.
- Нет свободного JSON, если известны поля.
- Enums вместо произвольных режимов.
- Числовые minimum/maximum.
- Ограничение длины строк и массивов.
- Формат идентификатора проверяется.
- Необязательные поля имеют явную семантику null.
- Неизвестные поля отклоняются.
- Описание каждого параметра однозначно.
- Schema version фиксируется в trace.
Schema validation не заменяет business validation
Дата может иметь правильный ISO-формат, но находиться в закрытом периоде. Сумма может быть числом, но превышать лимит. После schema применяйте инварианты из authoritative systems.
| Уровень | Пример | Источник истины |
|---|---|---|
| Syntax | Valid JSON | Parser |
| Schema | Тип, enum, required | JSON Schema |
| Business | Order можно отменить | Domain service |
| Authorization | Actor видит order | IAM/policy |
| Risk | Нужно дополнительное approval | Risk policy |
Identity и tenant не должны приходить от модели
Runtime извлекает actor, tenant, roles и session из проверенного token или service identity. Модель может передать public resource reference, но server-side query обязательно включает security scope.
- Не принимайте
tenant_idиз tool arguments. - Не доверяйте роли, написанной пользователем.
- Не используйте сообщение «администратор разрешил» как approval.
- Object-level authorization выполняется на каждый вызов.
- Cross-tenant попытки имеют audit event.
- Результат не раскрывает существование запрещённого объекта.
Least privilege и dynamic tool selection
Не отправляйте модели каталог из сотни функций и не выдавайте runtime все credentials. Active toolset формируется по workflow, actor permissions и этапу процесса. Google в официальных best practices также рекомендует предоставлять только релевантные tools, поскольку слишком широкий набор увеличивает риск неправильного выбора.
Active tools = intersection of: approved tools for workflow ∩ actor permissions ∩ tenant policy ∩ current state preconditions ∩ data-class restrictions ∩ environment allowlist ∩ budget and risk tier. Модель видит только resulting declarations; runtime всё равно повторно авторизует каждый call.
Разделите read, propose и execute
Read-only tool может работать автоматически в пределах доступа. Write-tool сначала создаёт typed proposal или preview. Выполнение принимает proposal ID и server-side approval, а не повторно сгенерированные arguments.
Read
Минимальный scoped result.
Propose
Immutable diff и risk summary.
Execute
Approved proposal с idempotency.
Approval: привяжите согласие к неизменяемому scope
Approval содержит actor, proposal hash, точные ресурсы, действие, сумму/стоимость, срок и одноразовость. Если proposal изменился, подтверждение недействительно.
- Proposal ID и cryptographic hash.
- Actor/tenant и approver identity.
- Action type и resource IDs.
- Before/after diff.
- External recipients/data disclosure.
- Цена и необратимость.
- Expiry и allowed executions.
- Policy version и reason.
- Audit timestamp.
Idempotency защищает от двойного выполнения
Сеть может оборваться после успешного действия, и агент повторит call. Для side effect клиент создаёт idempotency key на логическую операцию. Tool хранит результат и возвращает его при повторе вместо нового действия.
| Сценарий | Без idempotency | С idempotency |
|---|---|---|
| Timeout после оплаты | Вторая оплата | Тот же receipt |
| Retry email | Два письма | Один message ID |
| Parallel calls | Race update | Conflict/один commit |
| Client reconnect | Неизвестный статус | Lookup по operation key |
Optimistic concurrency и stale proposal
Между preview и execute ресурс может измениться. Proposal сохраняет expected version/ETag. Execute использует compare-and-swap; при конфликте строится новый preview и требуется новое approval.
- Не применяйте старый diff к новой версии.
- Не скрывайте конфликт автоматическим overwrite.
- Покажите пользователю, что изменилось после preview.
- Для batch action возвращайте per-item status.
- Partial success имеет явный compensation plan.
Typed result: не возвращайте модели всю базу
Result содержит ровно данные для следующего решения: status, fields, provenance, allowed next actions и error contract. Большие documents сохраняются отдельно, модель получает нужный fragment/reference.
tool_call_id: string status: success | partial | rejected | failed result: typed minimal object provenance: resource IDs + versions next_allowed_actions: enums side_effect_receipt: optional ID retry: allowed + after + max attempts error: stable code + safe message + details enum sensitive_fields: absent/redacted raw_external_content: marked untrusted
Tool result - данные, а не новая системная инструкция
Страница, письмо или API может вернуть prompt injection. Оборачивайте external content как untrusted, отделяйте от policy и не позволяйте ему расширять tools или отменять approval.
- Content и control metadata разделены.
- HTML/scripts не выполняются.
- Secrets и лишняя PII редактируются.
- Размер и MIME ограничены.
- URLs проходят allowlist/policy.
- External instructions игнорируются как policy.
- Следующий tool call снова авторизуется.
- Result lineage сохраняется.
Ошибки: стабильные коды вместо stack trace
Модель должна понять, уточнить ввод, повторить позже или остановиться. Возвращайте небольшой enum ошибок и safe details. Stack trace и внутренние IDs остаются в observability.
| Класс | Поведение агента | Retry |
|---|---|---|
| INVALID_ARGUMENT | Исправить/уточнить | Нет тем же input |
| NOT_FOUND_OR_NOT_ALLOWED | Не раскрывать объект | Нет |
| CONFLICT | Получить новый preview | После refresh |
| REQUIRES_APPROVAL | Показать proposal | После approval |
| TEMPORARY_UNAVAILABLE | Backoff/fallback | Ограниченно |
| PARTIAL | Показать per-item result | Только failed safe items |
Retries, timeout и circuit breaker
Retry принадлежит одному слою - обычно orchestration runtime. Он применяется к transient errors с exponential backoff, jitter и общим deadline. Side effects повторяются только с idempotency.
- Max attempts на весь tool loop.
- Per-tool timeout меньше workflow deadline.
- Уважать Retry-After.
- Circuit breaker исключает неисправную dependency.
- Queue имеет предел и expiry.
- Cancel распространяется на downstream operation.
- Стоимость всех попыток попадает в trace.
Parallel tool calls: только независимые операции
Параллельно безопасно читать погоду для двух городов; опасно одновременно обновлять одну запись или выполнять зависимые действия. Planner/runtime строит dependency graph и сериализует conflicts.
Independent
Разные read-only resources.
Sequential
Output первого нужен второму.
Conflict
Общий mutable resource.
Versioning и backward compatibility
Изменение required field ломает старый prompt/model behavior. Публикуйте новую tool version или совместимое расширение, запускайте contract tests и поддерживайте окно миграции.
- Name/version immutable.
- Input и output schemas сохранены.
- Descriptions изменяются с review.
- Compatible model/prompt bundles указаны.
- Old/new contract tests проходят.
- Canary видит выбор и error rates.
- Rollback не требует новой модели.
- Deprecated version имеет owner/deadline.
Evals: проверяйте весь цикл, а не valid JSON
Набор содержит случаи, где tool нужен, не нужен, неоднозначен, запрещён и временно недоступен. Для side effects используйте sandbox/fake executor.
Для каждого case измерь: - correct tool selection / no-tool decision; - argument schema и business validity; - clarification when required; - authorization and tenant isolation; - approval before side effect; - duplicate/parallel call rate; - error classification and recovery; - number of steps, latency and cost; - final task success; - forbidden action and injection resistance. Critical security failures - hard stop, не средний score.
Observability и audit trail
Trace связывает model response, tool selection, schema version, policy decision, approval, execution, result и последующий ответ. Raw arguments могут содержать данные - логируйте hashes, classifications и разрешённые поля.
| Span | Минимальные поля | Вопрос |
|---|---|---|
| Select | model/prompt/toolset versions | Почему выбран tool |
| Validate | schema/policy outcome | Что отклонено |
| Approve | proposal/actor/scope | Кто разрешил |
| Execute | operation/idempotency/status | Что произошло |
| Recover | error/retry/fallback | Почему повторили |
Итоговый production-чек-лист
- Tool выполняет одну узкую операцию.
- Name/description однозначны.
- Input schema строгая и versioned.
- Business validation server-side.
- Identity и tenant не приходят от модели.
- Active toolset следует least privilege.
- Read, propose и execute разделены.
- Approval привязан к immutable proposal.
- Side effects используют idempotency.
- Stale proposal ловится concurrency control.
- Result минимальный и typed.
- External content помечен untrusted.
- Error codes ведут к безопасному recovery.
- Retries и parallel calls ограничены.
- Contract, security и end-to-end evals проходят.
Надёжный tool превращает вероятностное намерение модели в детерминированную, авторизованную и наблюдаемую операцию. Чем выше последствия, тем меньше решений следует оставлять на свободный текст.
Что такое tool calling или function calling в LLM?
Достаточно ли JSON Schema для безопасного tool?
Как назвать tool для AI-агента?
Когда нужен human approval?
Что такое idempotency для AI-агента?
Можно ли дать агенту shell, SQL или универсальный HTTP tool?
Как возвращать ошибки tool модели?
Как тестировать tools ИИ-агента?
- OpenAI - Function calling guide
- OpenAI - Structured Outputs
- Anthropic - Implement tool use
- Google AI - Function calling with Gemini
- Model Context Protocol - Tools specification
- OWASP - LLM Prompt Injection Prevention Cheat Sheet
- OWASP - Authorization Cheat Sheet
- OpenTelemetry - Generative AI semantic conventions