botruntime
Разработка

Инструменты

Как дать агенту типизированные функции и управлять их вызовами.

Инструмент — функция, которую модель может вызвать внутри execute(). Код инструмента определяет действие. Описание помогает модели выбрать момент и аргументы вызова.

Объявите инструмент

src/tools/orders.ts
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.

src/tools/feedback.ts
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. Модель может объяснить цену, срок или лимит, но не должна быть источником этих данных.

Дальше

On this page