Инструменты
Как дать агенту типизированные функции и управлять их вызовами.
Инструмент — функция, которую модель может вызвать внутри execute(). Код
инструмента определяет действие. Описание помогает модели выбрать момент и
аргументы вызова.
Объявите инструмент
import { Autonomous, z } from '@holocronlab/botruntime-runtime'
export const findOrder = new Autonomous.Tool({
name: 'find_order',
description: 'Найди заказ по номеру. Вызывай после того, как пользователь назвал номер.',
input: z.object({
orderNumber: z.string().describe('Номер заказа, например A-1042'),
}),
output: z.object({
found: z.boolean(),
status: z.string().optional(),
}),
handler: async ({ orderNumber }) => {
const order = await orders.get(orderNumber)
return order ? { found: true, status: order.status } : { found: false }
},
})input валидируется до запуска handler. Выход также должен соответствовать
схеме output. Добавляйте .describe() к полям, смысл которых не следует из
имени.
Передайте инструмент в текущий ход:
await execute({
instructions: 'Помогай пользователю проверять заказы.',
tools: [findOrder],
})Передайте контекст
Создавайте фабрику, если инструменту нужны данные текущего диалога. Фабрика
замыкает контекст, а модель получает только безопасные аргументы из input.
type ToolContext = {
conversationId: string
state: { feedbackSent: boolean }
}
export function buildFeedbackTool(ctx: ToolContext) {
return new Autonomous.Tool({
name: 'save_feedback',
description: 'Сохрани отзыв после явного согласия пользователя.',
input: z.object({ text: z.string().min(1).max(1000) }),
output: z.object({ saved: z.boolean() }),
handler: async ({ text }) => {
await feedback.save({ conversationId: ctx.conversationId, text })
ctx.state.feedbackSent = true
return { saved: true }
},
})
}Не передавайте в input токены, внутренние идентификаторы и значения, которые
код уже знает из контекста.
Перенаправьте модель
Если вызов сейчас невозможен, бросьте Autonomous.ThinkSignal. Модель получит
причину и инструкцию для следующей итерации.
if (!order) {
throw new Autonomous.ThinkSignal(
'Заказ не найден',
'Попроси пользователя проверить номер. Не придумывай статус заказа.',
)
}Не возвращайте пустой успешный объект. Модель может принять его за результат и продолжить с неверным предположением.
Добавьте хуки
execute({ hooks }) принимает перехваты вокруг цикла и вызовов инструментов.
| Хук | Назначение |
|---|---|
onBeforeTool | Проверить или изменить вход до побочного эффекта. |
onAfterTool | Проверить или изменить выход. |
onBeforeExecution | Проверить сгенерированный код перед исполнением. |
onIterationStart, onIterationEnd | Собрать метрики итерации. |
onExit | Обработать типизированный выход из цикла. |
onTrace | Получить trace-событие. |
const hooks: Autonomous.Hooks = {
onBeforeTool: async ({ tool, input }) => {
if (tool.name === 'cancel_order' && !state.cancellationConfirmed) {
throw new Autonomous.ThinkSignal(
'Отмена не подтверждена',
'Сначала попроси пользователя подтвердить отмену.',
)
}
return { input }
},
}Проверяйте критичные условия в коде или onBeforeTool. Промпт не заменяет
валидацию, авторизацию и подтверждение операции.
Выберите подходящий примитив
Autonomous.Toolнужен только внутри агентного цикла.Actionподходит для переиспользуемой операции из кода и HTTP API.Workflowподходит для процесса с паузами, таймерами и возобновлением.
Action и Workflow можно передать модели через .asTool().
await execute({
instructions: 'Помогай с заказами.',
tools: [refreshCatalog.asTool(), deliveryWorkflow.asTool()],
})Точные значения вычисляйте в handler. Модель может объяснить цену, срок или
лимит, но не должна быть источником этих данных.
Дальше
Обработчики диалога
Примитив Conversation задаёт поведение диалога: единый props по типу события, ответ пользователю, состояние, lifecycle и прерывание.
Действия и триггеры
Action — переиспользуемое типизированное бизнес-действие. Trigger — подписка на событие. Как объявить, чем действие отличается от инструмента и когда что выбирать.