Глава 12

Завершайте работу типизированным исходом

Свободная строка не сообщает приложению, что показать: обычный ответ, вопрос, карточку подтверждения или кнопку связи с оператором. Cross-provider приём в том, чтобы завершать работу terminal tools без внешнего side effect - каждый из них соответствует одному типизированному исходу.

TypeScript
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 были возвращены retrieval
  • ask_clarification - Один конкретный вопрос; Conversation id и безопасный UI
  • request_refund_approval - preview_id и объяснение; Подставляет сумму из доверенного preview store
  • request_handoff - draft_id и причина; Подставляет проверенный черновик и канал

Ключевая тонкость - в колонке "что добавляет runtime". Модель называет preview_id, но сумму, валюту, срок и версию заказа runtime берёт из доверенного preview store, а не из аргументов вызова.

Почему не доверяем сумме из terminal call. Даже строгая JSON-схема гарантирует форму, но не истинность значения. Сумма, полученная из аргументов модели, - это предложение, а не факт; факт лежит в серверном хранилище по preview_id.

Исход типизирован. Но пока агент работает, пользователю полезно видеть прогресс - и тут важно стримить правильные события.

Проверка знаний

Почему runtime не берёт сумму возврата из аргументов terminal call?

Ссылки