Appearance
Agent工具设计专题
版本:
v1.1最后更新:
2026-07-07
Agent 能不能稳定工作,很多时候并不取决于模型“够不够聪明”,而取决于:
- 工具设计得是否清楚
- 工具边界是否收得住
- 参数和返回结果是否便于模型消费
- 写操作是否能被限制、审批和回放
很多线上失败并不是“模型不会推理”,而是:
- 工具职责太混,模型不知道什么时候该用
- 参数语义模糊,模型填错字段
- 输出太脏,后续步骤被噪音带偏
- 写工具没有幂等和审批,导致真实副作用失控
所以工具设计本质上不是“给模型挂几个 API”,而是在给 Agent 设计一层可控、可观测、可审计的行动接口。
1. 为什么工具是 Agent 系统的核心边界
对 Agent 来说,工具就像传统系统里的 API、命令和执行器。
模型只负责:
- 决定是否调用
- 生成参数
- 根据结果继续规划
真正的业务边界则落在工具层:
- 能做什么
- 不能做什么
- 需要什么参数
- 返回什么结构
- 失败后怎么恢复
这也是为什么 OpenAI 的 tools、function calling、agents 和 guardrails 文档都反复指向同一个工程现实:
- 模型可以建议调用工具
- 但系统必须决定工具能否被暴露、以什么 schema 被调用、在什么权限下执行、什么时候需要人工确认
2. 工具设计不是“越多越好”也不是“越少越好”
工具过少时,常见问题是:
- 单个工具承担太多职责
- 参数语义混乱
- 高风险动作和低风险动作混在一起
工具过多时,常见问题是:
- 模型选择困难
- 工具描述总长度膨胀
- token 成本和噪音上升
更稳妥的原则通常是:
- 按业务动作边界拆
- 按风险等级拆
- 按读写副作用拆
不要按“后端服务模块名”机械映射工具,也不要把“什么都能做”的巨型工具交给模型。
3. 一把好工具应该满足哪些最小条件
建议至少满足这五个条件:
3.1 职责单一
一个工具最好只表达一类稳定动作。
比起:
manage_everything
更推荐:
lookup_orderdraft_refund_decisioncreate_ticketissue_refund
3.2 参数语义清晰
模型只有在字段足够明确时,才更容易稳定调用。
3.3 输出结构可消费
工具返回结果不应只是大段自由文本,而应该能直接支持下一步判断。
3.4 副作用边界可见
工具必须显式说明:
- 它是读还是写
- 是否不可逆
- 是否需要审批
3.5 失败行为可预期
调用失败时,不应只返回“失败了”,而应返回:
- 可否重试
- 是否需要人工
- 是否已经部分成功
4. 先给工具做分类,再谈设计
一个实用的起点是先按行为类型给工具分类:
| 类别 | 典型动作 | 设计重点 |
|---|---|---|
| 查询类 | 搜索、读取、检索、查询状态 | 范围控制、结果脱敏、去噪 |
| 草稿类 | 生成草稿、生成建议、生成审批摘要 | 结构化输出、可编辑、无直接副作用 |
| 事务类 | 创建工单、更新记录、提交审批 | 幂等、审计、状态确认 |
| 高风险执行类 | 发邮件、退款、删除、改权限 | 审批、限额、回滚、强审计 |
这一步很重要,因为不同类型的工具不应该用同一套暴露、重试和审批策略。
5. 工具命名本身就是接口设计
坏名字会直接增加模型误用概率。
更稳妥的命名建议是:
- 用动作开头
- 名字体现副作用
- 避免含糊的全能词
例如:
search_knowledge_baseget_customer_profilecreate_internal_ticketsend_external_email
不推荐:
manage_customerhandle_caseprocess_data
名字越接近真实动作,模型越容易做出稳定选择。
6. 工具描述不是越长越好
描述太短时,模型不知道:
- 什么时候该用
- 和相邻工具有什么区别
描述太长时,又会带来:
- token 成本上升
- 关键信号被噪音淹没
一个更稳妥的描述结构通常是:
- 这个工具做什么。
- 什么时候应该使用。
- 什么时候不要使用。
- 是否有副作用。
例如描述一把写工具时,明确写出:
- “仅用于创建内部工单,不会直接通知客户”
这类句子往往比大段背景说明更有价值。
7. 参数 schema 是工具设计的第一道强边界
OpenAI 的 function calling、structured outputs 和 tools 相关文档都指向同一个工程事实:
- 参数越结构化,系统越容易稳定调用、验证和拒绝非法输入
建议 schema 至少明确这些内容:
- 必填字段
- 字段类型
- 枚举范围
- 长度和数值边界
- 是否允许额外字段
例如:
json
{
"name": "create_internal_ticket",
"parameters": {
"type": "object",
"properties": {
"tenant_id": { "type": "string" },
"customer_id": { "type": "string" },
"priority": {
"type": "string",
"enum": ["low", "medium", "high"]
},
"summary": { "type": "string", "maxLength": 500 }
},
"required": ["tenant_id", "customer_id", "priority", "summary"],
"additionalProperties": false
}
}如果没有这些边界,模型就容易:
- 填不存在的字段
- 传模糊值
- 把解释性文本塞进参数
8. schema 合法不等于业务允许
这是很多团队会忽略的一层。
即使参数完全符合 JSON schema,也不代表动作应该执行。
例如:
refund_amount数值合法,但超过当前用户权限额度customer_id格式合法,但不属于当前租户message_type合法,但对外发送必须审批
所以更成熟的设计通常分两层:
8.1 结构层校验
回答:
- 参数格式对不对
8.2 策略层校验
回答:
- 这次该不该做
也就是说:
- schema 解决“能不能解析”
- policy 解决“能不能执行”
9. 高风险工具一定要显式暴露副作用
高风险工具包括但不限于:
- 删除
- 发消息
- 修改权限
- 退款
- 发布内容
- 触发外部写操作
对于这类工具,建议在定义层就表达清楚:
- 这是写工具
- 这是不可逆或高风险动作
- 需要审批或人工确认
不要把高风险动作包装成看起来无害的名字,比如:
sync_customer
如果它实际会:
- 发信
- 改状态
- 扣费
那就应该拆开,让风险显式可见。
10. 不要只定义输入,错误语义也要先建模
很多团队定义工具时,只认真写了 input schema,却把错误情况统一返回成:
failederrorunknown
这会让后续 Agent 很难判断:
- 该不该重试
- 是否已经产生部分副作用
- 要不要转人工
- 还是应该换另一把工具
更稳妥的方式,是从工具设计阶段就把错误语义拆开,至少区分:
| 错误类型 | 含义 | 常见后续动作 |
|---|---|---|
validation_error | 参数缺失、格式不对、枚举越界 | 让模型重填参数或直接拒绝执行 |
permission_error | 当前用户、租户或角色无权执行 | 转审批、转人工或直接终止 |
transient_error | 网络抖动、限流、上游超时 | 可自动重试 |
conflict_error | 幂等冲突、对象状态变化、重复提交 | 先对账,再决定是否重试 |
partial_success | 外部动作已部分落地 | 进入补偿或人工接管 |
terminal_error | 明确不可恢复 | 终止并记录事故 |
更关键的是,错误返回最好显式带上:
retryableside_effect_confirmedexternal_idrecommended_next_action
也就是让工具不仅告诉模型“失败了”,还告诉模型“接下来应该怎么处理”。
根据 OpenAI Function calling 文档在 2026-07-07 可访问的说明,函数名、参数描述和指令越清晰,模型越容易稳定调用。这个原则同样适用于错误语义:返回结构越清晰,后续工作流越容易稳定恢复。
11. 幂等设计是写工具的底层前提
只要工具存在外部副作用,就应该思考:
- 这次动作能不能被安全重放
建议至少对这些动作设计幂等键:
- 创建工单
- 发通知
- 提交退款
- 创建审批单
- 更新外部记录
否则一旦发生:
- 工具执行成功,但本地状态未写入
后面系统就会不知道:
- 该不该重试
- 重试会不会造成重复写入
这会直接把工具层问题变成业务事故。
12. 工具输出不一定要原样返回给模型
很多团队会把工具结果一股脑全部喂回模型,这往往会带来三个问题:
- 噪音高
- token 成本高
- 敏感字段污染上下文
更稳妥的做法通常是:
- 工具层先清洗
- 只保留关键字段
- 对敏感值脱敏
- 对大对象做摘要
例如查询客户资料后,不一定要把原始 CRM 全量 JSON 塞回模型,而可以返回:
- 客户状态
- 关键标签
- 最近工单摘要
- 是否命中高风险规则
13. 对读工具和写工具要用不同输出策略
13.1 读工具
重点是:
- 去噪
- 去敏
- 保留证据指针
13.2 写工具
重点是:
- 返回执行结果
- 返回对象 ID
- 返回当前状态
- 返回是否部分成功
例如一把写工具返回时,最好不要只写:
- “成功”
而应该包含:
statusexternal_idretryableapproval_requiredside_effect_confirmed
14. 批量工具和复合工具不要随手做
工具设计里还有一个常见误区:
- 把一连串动作封进一把“大而全”的复合工具
例如:
process_refund_casehandle_customer_requestsync_and_notify_user
这类工具看起来减少了编排复杂度,但会同时带来:
- 模型不知道内部到底做了几步
- 某一步失败后难以恢复
- 审批和副作用边界被糊掉
- 评测无法定位具体错在什么子动作
更稳妥的判断标准通常是:
- 如果中间步骤需要单独审批、补偿、回放或评测,就不要封进一把黑盒工具
但也不是所有复合工具都不好。
适合保留为一个工具的情况通常包括:
- 内部步骤都无副作用
- 步骤之间强耦合,拆开也不会增加治理价值
- 作为一个整体暴露更能降低模型误用
所以核心不是“能不能复合”,而是:
复合之后,风险边界和恢复边界是否仍然清楚
15. 工具集合本身也要控制规模
OpenAI Tool search 文档在 2026-07-07 可访问的说明提到,一个 namespace 最好控制在 10 个以下函数,更有利于 token 效率和模型性能。
这条建议背后有非常现实的工程原因:
- 工具越多,选择难度越大
- 描述和 schema 越多,上下文固定成本越高
- 相似工具越多,误选概率越高
因此更稳的做法通常是:
- 按任务场景按需暴露工具,而不是全量暴露。
- 将大工具集按领域拆成 namespace 或 server。
- 把低频、长尾能力延迟加载,而不是默认随每次请求一起发送。
Anthropic Pricing 文档在 2026-07-07 可访问的说明也明确提到,tools 参数本身会增加输入 token,工具描述、schema、tool_use 和 tool_result 都会进入计费。
这意味着工具设计不只是“行为接口设计”,也是“上下文预算设计”。
16. 审批和人机协同要前置到工具设计里
高风险工具不应该等到流程最后才想起要审批。
更成熟的做法是,从工具定义开始就表达:
- 是否需要审批
- 谁审批
- 什么情况下自动转人工
- 拒绝后如何收束
也就是说,工具设计和:
- 审批与人机协同
- 权限沙箱
- 状态机
是天然联动的,而不是三篇分离的文档。
17. 工具设计要和上下文成本一起考虑
很多 Agent 系统成本高,不只是因为模型贵,还因为:
- 工具描述太长
- schema 太大
- 工具结果太脏
- 暴露了太多无关工具
这意味着工具设计本身就是上下文工程的一部分。
更稳妥的优化方向包括:
- 按任务按需暴露工具
- 精简工具描述
- 压缩 schema
- 结果字段白名单回传
18. MCP / Connector 时代,工具设计问题被放大了
当工具来自远端连接器、MCP server 或第三方系统时,工具设计不再只是“本地函数签名”问题,而会涉及:
- 信任边界
- 权限范围
- 远端副作用
- 远端数据质量
- 审批和审计可见性
所以在这类场景下,建议额外确认:
- 工具元数据是否足够清晰
- schema 是否足够严格
- 是否标注了 destructive action
- 是否默认要求审批
越是远端工具,越不能偷懒。
根据 OpenAI MCP and Connectors 文档在 2026-07-07 可访问的说明,远端 MCP server 和 connector 会放大 prompt injection、数据访问和外部动作风险,因此对这类工具更要做好最小权限、审批门和输入清洗。
19. 该怎么测试工具设计好不好
至少建议从五个角度评估:
19.1 工具选择正确率
- 模型是否选到了正确工具
19.2 参数正确率
- 参数是否完整、合法、符合业务边界
19.3 不必要调用率
- 是否出现“明明不该调也调了”
19.4 高风险误触发率
- 是否把高风险工具用错地方
19.5 输出可消费率
- 后续节点是否能稳定利用工具输出
对于高风险工具,还建议额外看:
approval_trigger_rateapproval_bypass_rateidempotency_conflict_rateside_effect_replay_rate
20. 一个客服 Agent 的工具设计示例
假设系统需要支持:
- 查询订单
- 查询政策
- 生成退款建议
- 发起退款
- 给客户发邮件
更合理的拆法通常是:
lookup_ordersearch_refund_policydraft_refund_recommendationcreate_refund_approvalissue_refunddraft_customer_emailsend_customer_email
这里最关键的点是:
- “建议”和“执行”分开
- “草稿”和“外发”分开
- “审批单创建”和“退款执行”分开
这样模型就不容易跨越不该跨越的边界。
21. 常见反模式
21.1 一个工具做所有事
这几乎一定会导致职责混乱和高风险副作用。
21.2 参数意义不清
字段语义模糊时,模型迟早会误填。
21.3 输出是大段非结构化文本
后续 Agent 节点会越来越不稳定。
21.4 高风险工具没有审批
短期看起来高效,长期一定出事故。
21.5 工具层没有 trace 和日志
出问题时几乎无法复盘。
22. 推荐搭配阅读
23. 重点官方资源
以下资源是本次补写时重点参考的官方资料,适合继续补强工具 schema、function calling、审批、MCP 和生产工具治理设计:
- OpenAI Tools guide:https://developers.openai.com/api/docs/guides/tools
- OpenAI Function calling guide:https://developers.openai.com/api/docs/guides/function-calling
- OpenAI Agents guide:https://developers.openai.com/api/docs/guides/agents
- OpenAI Define agents:https://developers.openai.com/api/docs/guides/agents/define-agents
- OpenAI Guardrails and human review:https://developers.openai.com/api/docs/guides/agents/guardrails-approvals
- OpenAI MCP and Connectors:https://developers.openai.com/api/docs/guides/tools-connectors-mcp
- OpenAI Tool search:https://developers.openai.com/api/docs/guides/tools-tool-search
- OpenAI Safety best practices:https://developers.openai.com/api/docs/guides/safety-best-practices
- Anthropic Tool use overview:https://docs.anthropic.com/en/docs/build-with-claude/tool-use/overview
- Anthropic Define tools:https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/implement-tool-use
- Anthropic Pricing(tool use token cost):https://docs.anthropic.com/en/docs/about-claude/pricing
24. 落地检查清单
- 是否按动作边界拆分工具,而不是暴露全能大工具
- 是否为每个工具定义了清晰 schema、必填字段和边界约束
- 是否把结构层校验和业务策略校验分成两层
- 是否为错误返回定义了可重试、部分成功、权限冲突等明确语义
- 是否显式区分读工具、草稿工具、事务工具和高风险执行工具
- 是否控制了每次请求实际暴露给模型的工具规模,而不是全量挂载
- 是否为高风险工具接入审批、幂等键和副作用确认
- 是否对工具结果做清洗、脱敏和字段白名单回传
- 是否为工具选择正确率、参数正确率和误触发率建立了评测与监控