Skip to content

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_order
  • draft_refund_decision
  • create_ticket
  • issue_refund

3.2 参数语义清晰

模型只有在字段足够明确时,才更容易稳定调用。

3.3 输出结构可消费

工具返回结果不应只是大段自由文本,而应该能直接支持下一步判断。

3.4 副作用边界可见

工具必须显式说明:

  • 它是读还是写
  • 是否不可逆
  • 是否需要审批

3.5 失败行为可预期

调用失败时,不应只返回“失败了”,而应返回:

  • 可否重试
  • 是否需要人工
  • 是否已经部分成功

4. 先给工具做分类,再谈设计

一个实用的起点是先按行为类型给工具分类:

类别典型动作设计重点
查询类搜索、读取、检索、查询状态范围控制、结果脱敏、去噪
草稿类生成草稿、生成建议、生成审批摘要结构化输出、可编辑、无直接副作用
事务类创建工单、更新记录、提交审批幂等、审计、状态确认
高风险执行类发邮件、退款、删除、改权限审批、限额、回滚、强审计

这一步很重要,因为不同类型的工具不应该用同一套暴露、重试和审批策略。

5. 工具命名本身就是接口设计

坏名字会直接增加模型误用概率。

更稳妥的命名建议是:

  • 用动作开头
  • 名字体现副作用
  • 避免含糊的全能词

例如:

  • search_knowledge_base
  • get_customer_profile
  • create_internal_ticket
  • send_external_email

不推荐:

  • manage_customer
  • handle_case
  • process_data

名字越接近真实动作,模型越容易做出稳定选择。

6. 工具描述不是越长越好

描述太短时,模型不知道:

  • 什么时候该用
  • 和相邻工具有什么区别

描述太长时,又会带来:

  • token 成本上升
  • 关键信号被噪音淹没

一个更稳妥的描述结构通常是:

  1. 这个工具做什么。
  2. 什么时候应该使用。
  3. 什么时候不要使用。
  4. 是否有副作用。

例如描述一把写工具时,明确写出:

  • “仅用于创建内部工单,不会直接通知客户”

这类句子往往比大段背景说明更有价值。

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,却把错误情况统一返回成:

  • failed
  • error
  • unknown

这会让后续 Agent 很难判断:

  • 该不该重试
  • 是否已经产生部分副作用
  • 要不要转人工
  • 还是应该换另一把工具

更稳妥的方式,是从工具设计阶段就把错误语义拆开,至少区分:

错误类型含义常见后续动作
validation_error参数缺失、格式不对、枚举越界让模型重填参数或直接拒绝执行
permission_error当前用户、租户或角色无权执行转审批、转人工或直接终止
transient_error网络抖动、限流、上游超时可自动重试
conflict_error幂等冲突、对象状态变化、重复提交先对账,再决定是否重试
partial_success外部动作已部分落地进入补偿或人工接管
terminal_error明确不可恢复终止并记录事故

更关键的是,错误返回最好显式带上:

  • retryable
  • side_effect_confirmed
  • external_id
  • recommended_next_action

也就是让工具不仅告诉模型“失败了”,还告诉模型“接下来应该怎么处理”。

根据 OpenAI Function calling 文档在 2026-07-07 可访问的说明,函数名、参数描述和指令越清晰,模型越容易稳定调用。这个原则同样适用于错误语义:返回结构越清晰,后续工作流越容易稳定恢复。

11. 幂等设计是写工具的底层前提

只要工具存在外部副作用,就应该思考:

  • 这次动作能不能被安全重放

建议至少对这些动作设计幂等键:

  • 创建工单
  • 发通知
  • 提交退款
  • 创建审批单
  • 更新外部记录

否则一旦发生:

  • 工具执行成功,但本地状态未写入

后面系统就会不知道:

  • 该不该重试
  • 重试会不会造成重复写入

这会直接把工具层问题变成业务事故。

12. 工具输出不一定要原样返回给模型

很多团队会把工具结果一股脑全部喂回模型,这往往会带来三个问题:

  • 噪音高
  • token 成本高
  • 敏感字段污染上下文

更稳妥的做法通常是:

  • 工具层先清洗
  • 只保留关键字段
  • 对敏感值脱敏
  • 对大对象做摘要

例如查询客户资料后,不一定要把原始 CRM 全量 JSON 塞回模型,而可以返回:

  • 客户状态
  • 关键标签
  • 最近工单摘要
  • 是否命中高风险规则

13. 对读工具和写工具要用不同输出策略

13.1 读工具

重点是:

  • 去噪
  • 去敏
  • 保留证据指针

13.2 写工具

重点是:

  • 返回执行结果
  • 返回对象 ID
  • 返回当前状态
  • 返回是否部分成功

例如一把写工具返回时,最好不要只写:

  • “成功”

而应该包含:

  • status
  • external_id
  • retryable
  • approval_required
  • side_effect_confirmed

14. 批量工具和复合工具不要随手做

工具设计里还有一个常见误区:

  • 把一连串动作封进一把“大而全”的复合工具

例如:

  • process_refund_case
  • handle_customer_request
  • sync_and_notify_user

这类工具看起来减少了编排复杂度,但会同时带来:

  • 模型不知道内部到底做了几步
  • 某一步失败后难以恢复
  • 审批和副作用边界被糊掉
  • 评测无法定位具体错在什么子动作

更稳妥的判断标准通常是:

  • 如果中间步骤需要单独审批、补偿、回放或评测,就不要封进一把黑盒工具

但也不是所有复合工具都不好。

适合保留为一个工具的情况通常包括:

  • 内部步骤都无副作用
  • 步骤之间强耦合,拆开也不会增加治理价值
  • 作为一个整体暴露更能降低模型误用

所以核心不是“能不能复合”,而是:

  • 复合之后,风险边界和恢复边界是否仍然清楚

15. 工具集合本身也要控制规模

OpenAI Tool search 文档在 2026-07-07 可访问的说明提到,一个 namespace 最好控制在 10 个以下函数,更有利于 token 效率和模型性能。

这条建议背后有非常现实的工程原因:

  • 工具越多,选择难度越大
  • 描述和 schema 越多,上下文固定成本越高
  • 相似工具越多,误选概率越高

因此更稳的做法通常是:

  1. 按任务场景按需暴露工具,而不是全量暴露。
  2. 将大工具集按领域拆成 namespace 或 server。
  3. 把低频、长尾能力延迟加载,而不是默认随每次请求一起发送。

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_rate
  • approval_bypass_rate
  • idempotency_conflict_rate
  • side_effect_replay_rate

20. 一个客服 Agent 的工具设计示例

假设系统需要支持:

  • 查询订单
  • 查询政策
  • 生成退款建议
  • 发起退款
  • 给客户发邮件

更合理的拆法通常是:

  • lookup_order
  • search_refund_policy
  • draft_refund_recommendation
  • create_refund_approval
  • issue_refund
  • draft_customer_email
  • send_customer_email

这里最关键的点是:

  • “建议”和“执行”分开
  • “草稿”和“外发”分开
  • “审批单创建”和“退款执行”分开

这样模型就不容易跨越不该跨越的边界。

21. 常见反模式

21.1 一个工具做所有事

这几乎一定会导致职责混乱和高风险副作用。

21.2 参数意义不清

字段语义模糊时,模型迟早会误填。

21.3 输出是大段非结构化文本

后续 Agent 节点会越来越不稳定。

21.4 高风险工具没有审批

短期看起来高效,长期一定出事故。

21.5 工具层没有 trace 和日志

出问题时几乎无法复盘。

22. 推荐搭配阅读

23. 重点官方资源

以下资源是本次补写时重点参考的官方资料,适合继续补强工具 schema、function calling、审批、MCP 和生产工具治理设计:

24. 落地检查清单

  • 是否按动作边界拆分工具,而不是暴露全能大工具
  • 是否为每个工具定义了清晰 schema、必填字段和边界约束
  • 是否把结构层校验和业务策略校验分成两层
  • 是否为错误返回定义了可重试、部分成功、权限冲突等明确语义
  • 是否显式区分读工具、草稿工具、事务工具和高风险执行工具
  • 是否控制了每次请求实际暴露给模型的工具规模,而不是全量挂载
  • 是否为高风险工具接入审批、幂等键和副作用确认
  • 是否对工具结果做清洗、脱敏和字段白名单回传
  • 是否为工具选择正确率、参数正确率和误触发率建立了评测与监控