Завершайте работу типизированным исходом
Свободная строка не сообщает приложению, что показать: обычный ответ, вопрос, карточку подтверждения или кнопку связи с оператором. Cross-provider приём в том, чтобы завершать работу terminal tools без внешнего side effect - каждый из них соответствует одному типизированному исходу.
export type AgentOutcome =
| { status: "answer"; message: string; citations: Citation[] }
| { status: "clarification_required"; question: string }
| { status: "approval_required"; preview: RefundPreview; explanation: string }
| { status: "handoff_required"; draft: HandoffDraft; reason: string }
| { status: "blocked"; reason: string };Runtime не просто принимает terminal call, а достраивает его до безопасного исхода.
Terminal tool · Аргументы модели · Что добавляет runtime
answer_user- Сообщение и source ids; Проверяет, что sources были возвращены retrievalask_clarification- Один конкретный вопрос; Conversation id и безопасный UIrequest_refund_approval-preview_idи объяснение; Подставляет сумму из доверенного preview storerequest_handoff-draft_idи причина; Подставляет проверенный черновик и канал
Ключевая тонкость - в колонке "что добавляет runtime". Модель называет preview_id, но сумму, валюту, срок и версию заказа runtime берёт из доверенного preview store, а не из аргументов вызова.
Почему не доверяем сумме из terminal call. Даже строгая JSON-схема гарантирует форму, но не истинность значения. Сумма, полученная из аргументов модели, - это предложение, а не факт; факт лежит в серверном хранилище по preview_id.Исход типизирован. Но пока агент работает, пользователю полезно видеть прогресс - и тут важно стримить правильные события.
Почему runtime не берёт сумму возврата из аргументов terminal call?