Skip to content

工具

工具是 Agent 在执行过程中调用的函数。它负责把语言模型的推理结果连接到真实动作或外部系统。

工具由什么组成

@agentrail/core 中,一个 RuntimeTool 通常包含五个核心部分:

部分用途
name发送给 LLM 的工具标识
label面向日志与 UI 的可读名称
description供模型判断何时调用该工具的自然语言描述
parameters使用 TypeBox 定义的输入 schema
execute执行工具逻辑并返回结果

LLM 不会直接执行工具。模型只会返回结构化工具调用请求,Agent loop 再负责参数校验、执行与结果回填。

定义工具

大多数情况下,使用 defineTool()

ts
import { Type } from "@sinclair/typebox";
import { defineTool } from "@agentrail/core";

export const customerLookupTool = defineTool({
  name: "customer-lookup",
  label: "Customer Lookup",
  description: "Look up a customer by account ID.",
  parameters: Type.Object({
    accountId: Type.String({ description: "The account identifier" }),
  }),
  async execute(params) {
    const record = await db.customers.findById(params.accountId);
    return {
      content: [{ type: "text", text: JSON.stringify(record) }],
      details: record,
    };
  },
});

无参工具可以使用 defineSimpleTool()

ts
import { defineSimpleTool } from "@agentrail/core";

export const pingTool = defineSimpleTool({
  name: "ping",
  description: "Check that the service is reachable.",
  async execute() {
    return { content: [{ type: "text", text: "pong" }], details: null };
  },
});

业务校验

如果输入结构正确,但不满足业务前提,可以通过 validate 追加校验:

ts
import { Type } from "@sinclair/typebox";
import { defineTool } from "@agentrail/core";

export const transferTool = defineTool({
  name: "transfer_funds",
  description: "Transfer an amount between two accounts.",
  parameters: Type.Object({
    fromAccountId: Type.String(),
    toAccountId: Type.String(),
    amount: Type.Number({ minimum: 0.01 }),
  }),
  async validate(params) {
    const balance = await getBalance(params.fromAccountId);
    if (balance < params.amount) {
      return { valid: false, reason: "Insufficient funds" };
    }
    return { valid: true };
  },
  async execute(params) {
    await doTransfer(params.fromAccountId, params.toAccountId, params.amount);
    return { content: [{ type: "text", text: "Transfer complete." }], details: null };
  },
});

validate 返回 { valid: false, reason } 或抛错时,execute 不会被调用,模型会收到 Tool precondition failed: <reason>

工具结果

每个工具都要返回 ToolResult

ts
{
  content: (TextContent | ImageContent)[];
  details: TDetails;
}
  • content:返回给模型可见的结果
  • details:留给 Host、日志、UI 或调试层使用的结构化数据

长任务中的进度更新

对于执行时间较长的工具,可以通过 ctx.onUpdate 发送中间结果,Host 会将其转发为 tool.update 事件。

工具从哪里来

在托管应用中,工具通常来自三类来源:

1. 自定义运行时工具

业务代码自行定义的工具,通常位于业务仓库或应用内模块中,再通过 Profile 注入。

2. @agentrail/capabilities

框架提供的通用能力包,包括:

  • 文件系统与浏览器工具
  • 知识库检索
  • 技能调用
  • 多 Agent 编排
  • 提问、待办写入等通用工具

3. 编排工具

当 Profile 接入编排能力后,父 Agent 会获得 spawn-agentsend-inputwait-agentclose-agent 等工具,用于创建与协调子 Agent。

在 Profile 中组装工具

工具应当在 defineProfile 中组装,而不是写在路由里:

ts
import { defineProfile } from "@agentrail/app";
import { filesystem } from "@agentrail/capabilities";

export const defaultProfile = defineProfile({
  id: "default",
  name: "Default Assistant",
  agent: {
    model: "anthropic:claude-sonnet-4-5",
    prompt: systemPrompt,
    tools: [customerLookupTool, pingTool],
  },
  capabilities: [filesystem({ sandboxManager })],
});

defineProfile 会自动把 agent.tools 与能力包生成的工具合并。

不适合放进工具的内容

以下内容不建议写成工具:

  • HTTP 请求解析与路由逻辑
  • Session 管理
  • 系统提示词本身
  • 跨请求的 Host 行为

这些内容分别更适合放在路由、Host、Profile 或插件层。

相关文档

基于 Apache 2.0 许可证发布。