02-工具与Agent循环
核对日期:2026-05-18
官方资料:Tools https://docs.langchain.com/oss/javascript/langchain/tools;Agents https://docs.langchain.com/oss/javascript/langchain/agents。
官方概念
Tool 是模型可调用的外部能力描述,包含名称、说明、输入 schema 和执行函数。Agent 是模型驱动的循环:读消息、决定是否调用工具、接收工具结果、继续推理或输出最终答案。
机制
工具的说明会影响模型选择,但工具的权限、参数校验、幂等和审计不能交给模型决定。
TypeScript 写法
本手册用 zod 描述工具输入:
const weatherTool = tool({
name: "get_weather",
description: "查询城市天气",
schema: z.object({ city: z.string().min(1) }),
execute: ({ city }) => ({ city, summary: "晴" }),
});
对应案例:src/examples/02-tools.ts、src/examples/03-agent-dynamic-tools.ts。
可用化任务流
如果你要在真实项目里新增一个工具,不要从“写一个函数”开始,而是按下面顺序做:
| 步骤 | 要做什么 | 产物 | 验收方式 |
|---|---|---|---|
| 1. 定义业务动作 | 明确工具是读操作、写操作还是外发操作 | tool-risk.md 或设计记录 | 能说清楚风险等级和失败后果 |
| 2. 定义 schema | 用 zod 写输入字段、枚举、范围和默认值 | schema | 错误输入测试能失败 |
| 3. 定义权限 | 把权限放到 Tool Gateway 或 runtime context | requiredPermission / policy | 缺权限测试返回拒绝 |
| 4. 定义幂等 | 写操作增加 request id / idempotency key | 工具参数或上下文 | 重复调用不会重复扣款/发信/建单 |
| 5. 定义输出 | 工具返回结构化结果,不返回原始内部响应 | ToolResult | 输出不含密钥、内部 URL、堆栈 |
| 6. 加 eval | 覆盖正确调用、缺参、缺权限、工具失败、相似工具混淆 | eval cases | npm test 或 CI gate 通过 |
最小工具模板:
const createTicketTool = tool({
name: "create_ticket",
description: "创建内部工单。只在用户明确要求建单时使用。",
schema: z.object({
title: z.string().min(3).max(80),
priority: z.enum(["P1", "P2", "P3"]),
idempotencyKey: z.string().min(8),
}),
requiredPermission: "ticket:create",
execute: async ({ title, priority, idempotencyKey }, context) => {
// 真实项目这里调用内部工具网关,而不是让 Agent 直连工单系统。
return { ticketId: `T-${idempotencyKey}`, title, priority };
},
});
Agent 工具选择模板:
function selectToolsForRequest(userRole: string, intent: string) {
const tools = [searchDocsTool];
if (userRole === "manager" && intent === "create-ticket") {
tools.push(createTicketTool);
}
return tools;
}
这个模式对应官方“tools + agents”的能力,但把安全边界放在业务层。模型可以建议调用哪个工具,不能决定自己是否有权限。
可运行改造点
在本仓库里练习时,直接改这些文件:
| 文件 | 改造任务 | 验收命令 |
|---|---|---|
src/examples/02-tools.ts | 增加 requiredPermission 缺失场景 | npm run examples |
src/examples/03-agent-dynamic-tools.ts | 按角色动态隐藏 search_docs | npm run examples |
src/projects/multi-tool-task-agent.ts | 给 create_ticket 增加 idempotency key | npm test |
test/langchain-learning.test.ts | 增加相似工具误调用测试 | npm test |
工具失败策略
| 失败类型 | Agent 应该怎么做 | 不应该怎么做 |
|---|---|---|
| schema 校验失败 | 要求用户补参数或选择默认值 | 猜一个参数继续执行 |
| 权限不足 | 拒绝并说明需要的权限类型 | 让模型换个工具绕过 |
| transient error | 有上限地重试,写 trace | 无限重试 |
| permanent error | 返回可读错误,保留审计 | 把内部堆栈暴露给用户 |
| 高风险动作缺审批 | 暂停等待 HITL | 自动执行后再通知 |
Python 差异
Python 常用函数签名、docstring 或 decorator 生成 tool schema。TypeScript 侧更常见 zod schema。两边都需要写清楚工具能力和参数约束,但生产权限不应藏在 docstring 或 description 里。
工程边界
- 高风险工具必须经过 Tool Gateway。
- 工具输入必须校验,工具输出也要视为不可信。
- 同类工具过多会增加误调用,应动态暴露最少必要工具。
常见反模式
| 反模式 | 后果 |
|---|---|
| 工具 description 写得像 prompt | 真实权限和失败处理缺失 |
| Agent 直接调用生产写接口 | 无审批、无幂等、无审计 |
| 缺少工具错误样本 | 模型遇到失败时容易编造结果 |
练习任务
- 给
02-tools.ts增加一个缺权限调用,确认返回ok: false。 - 给
03-agent-dynamic-tools.ts增加一个只对 manager 暴露的工具。 - 设计一个工具失败时的用户可读错误,不泄露内部栈。
- 给
multi-tool-task-agent.ts的外发工具增加审批原因字段,并要求 eval 检查 audit log。 - 把两个语义相近的工具放进同一个 Agent,设计一个测试证明不会误调用。