Handoff передает доказанный контекст, а не мнение модели
Передача оператору нужна при конфликтной политике, недостатке данных, повторной ошибке tool, высокой цене решения или прямой просьбе пользователя. И передавать надо не "мнение модели", а доказанный контекст: черновик handoff состоит из проверенных фактов и ссылок на события, а не из догадок и пересказа.
type HandoffDraft = {
id: string;
category: "refund" | "delivery" | "policy" | "other";
userQuestion: string;
verifiedOrderId: string | null;
verifiedFacts: Array<{ label: string; value: string }>;
sourceIds: string[];
toolErrors: string[];
};
async function handoffPrepare(raw: unknown, context: TrustedToolContext) {
const input = handoffInput.parse(raw);
return handoffs.createDraft({
actorId: context.actorId,
conversationId: input.conversation_id,
category: input.category
});
}Что класть в черновик, а что нет - вопрос и безопасности, и уважения к оператору.
Добавлять · Не добавлять
- Исходный вопрос пользователя - Скрытый reasoning
- Проверенный order id - Предположение, что пользователь "мошенничает"
- Sources и tool results - Сырые персональные поля
- Что уже пробовали и какая ошибка возникла - Выдуманную срочность
Добавляют исходный вопрос, проверенный order id, sources и tool results, что уже пробовали и какая ошибка возникла. Не добавляют скрытый reasoning, предположение "пользователь мошенничает", сырые персональные поля и выдуманную срочность. Оператор должен получить факты, а не интерпретацию.
И оговорка про сам тикет.
Создание тикета тоже side effect. Если продукт создаёт тикет автоматически, явно сообщите об этом пользователю и обеспечьте deduplication. Для чувствительных каналов сначала покажите подготовленный handoff и получите согласие.
Все части примера готовы по отдельности. Соберём их в один runtime вокруг registry - и увидим, как contract, tools и провайдер стыкуются на сервере.