青蛙小白

面向 Agent 的工具设计

面向 Agent 的工具设计 是本库对 Anthropic《Writing effective tools for agents – with agents》一文的归纳视角:工具不只是传统 API 的薄包装,而是 AI Agent(智能体)感知世界、选择动作和接收反馈的操作界面。

这不是一个单一标准或官方术语。它更接近 Agent-Computer Interface(智能体计算机接口)在工具层的工程清单:用少量高价值工具支撑真实工作流,用 Evals(评估)观察 Agent 实际怎么用工具,再把失败 trace 反向转化为工具命名、schema、描述、输出格式和错误反馈的改进。

工具不是 API 端点的直译

传统软件接口面对的是确定性调用方:函数名、参数和返回值可以默认由工程师理解。Agent 工具面对的是非确定性调用方:模型可能不用工具、用错工具、传错参数,或把工具结果理解偏。

因此,工具设计的目标不是“把所有后端能力暴露出来”,而是让 Agent 更容易走出合理策略。Anthropic 的建议可以压缩成三条:

  • 少做通用枚举型工具,多做贴近真实任务的高层动作
  • 避免一堆相似、重叠、边界模糊的工具同时进入上下文
  • 把常见多步链路合并进工具内部,减少 Agent 在上下文里做机械胶水工作的机会

例如,与其提供 list_userslist_eventscreate_event 三个底层接口,不如提供一个 schedule_event,让工具在内部处理查可用时间、建日程和返回关键上下文。与其让 Agent 拉取全部日志再自己筛,不如提供 search_logs,只返回相关行和必要上下文。

评估驱动的工具迭代

面向 Agent 的工具设计必须从 eval 开始闭环,而不是凭接口作者的直觉判断“这个工具应该好用”。

一个实用流程是:

flowchart LR
    P[快速原型] --> E[真实任务评估集]
    E --> R[运行 Agent 工具调用]
    R --> T[阅读 trace 与指标]
    T --> I[改工具实现/描述/schema]
    I --> E

评估任务应来自真实工作流,并且足够复杂,能够迫使 Agent 多次调用工具、组合信息、处理歧义。过于简单的 sandbox 任务通常只能证明工具“能被调用”,不能证明 Agent 在真实环境里会用对。

除了最终准确率,还应收集工具调用次数、工具错误、单次工具耗时、任务总耗时、token 消耗、是否调用了预期工具等指标。大量冗余调用可能说明分页或响应默认值不合适;大量参数错误通常指向工具描述、参数名或 schema 不清楚;Agent 没有调用某个关键工具,可能说明工具名和触发条件没有进入模型的策略空间。

文章中的示意图给了一个典型闭环:Claude Code 先根据需求写出 Gmail MCP server,再运行 Gmail eval;评估报告显示准确率、平均任务时长和失败点后,Claude Code 回到工具实现里修复 send_email 一类具体问题。右侧任务覆盖发邮件、搜索报告、总结 thread、删除垃圾邮件,说明一个好的工具 eval 不应只测单个 API 调用,而要覆盖多种真实动作意图。

两张 held-out test set 图显示,Claude 优化工具在 Anthropic 内部 Slack 与 Asana 工具上都带来提升:

工具集Human-written MCP serverClaude-optimized MCP server
Slack tools67.4%80.1%
Asana tools79.6%85.7%

这些数字的重点不是“自动生成一定超过人工”,而是:工具描述、schema、输出格式和实现细节可以被 eval 驱动地优化,并且要用 held-out 集验证是否泛化。

名称、命名空间与工具边界

工具名是 Agent 选择动作时最早看到的信号。工具数量一多,命名边界就会变成 Context Engineering(上下文工程)问题:每个 schema 都占 token,每个相似名称都会增加选择分支。

命名空间的价值在于显式划边界。比如 asana_projects_searchasana_users_search 比多个裸 search 更容易让 Agent 判断工具属于哪个服务、处理哪类资源。前缀还是后缀更好没有通用答案,应由 eval 决定;不同模型和任务可能对命名模式敏感。

更深一层的原则是:工具集合应该反映人类会如何分解任务。清晰、少量、目的不同的工具,比大量 API 端点式工具更容易让 Agent 形成稳定策略。

工具响应也是界面

工具输出不是日志转储,而是给 Agent 的下一步观察。好的响应要在“可继续行动”和“节省上下文”之间取平衡。

常见规则:

  • 返回语义字段优先于内部技术字段,例如 namefile_typeimage_url 通常比随机 UUID 更能帮助模型决策
  • 需要下游调用的技术 ID 可以保留,但不要让它淹没内容本身
  • 为响应提供 concise / detailed 这类格式选项,让 Agent 在探索和执行阶段切换信息量
  • 对大结果默认分页、过滤、范围选择或截断,并在截断信息里提示更精确的下一步查询方式
  • 错误响应要告诉 Agent 参数哪里错、可用格式是什么,而不是只返回栈追踪或模糊错误码

这和 Function Calling(函数调用)的 schema 设计是一体两面:输入 schema 决定 Agent 如何发起动作,输出结构决定 Agent 如何理解观察。

文章图片里的 Slack 搜索示例展示了 responseFormat 的取舍:

  • detailed 响应适合后续还要调用工具的阶段:除消息正文外,还返回 channel ID、user ID、timestamp、thread timestamp、文件名等可作为下游参数的字段。
  • concise 响应适合快速阅读大量候选信息:只保留频道/DM、发送者、消息正文和日期,去掉大部分机器字段。

Stripe 交易搜索的截断示例则说明:截断不应只是砍掉尾部。更好的返回会说明总命中数、当前只展示前几条,同时给出全局摘要和下一步 refinements,例如按 vendor 搜索、按金额范围过滤、请求下一页。这样 Agent 即使没拿到全量数据,也知道如何继续缩小范围。

错误响应同样应该面向 Agent。一个差的错误只返回 RESOURCE_NOT_FOUND、422、字段名和值;一个好的错误会把失败原因翻译成可行动说明:当前 userId 不存在或格式错误,合法 ID 长什么样,以及应该先调用 user_search() 解析用户 ID。后者把失败变成下一轮工具调用的路标。

Anthropic 后续提出的 MCP 代码执行式用法把“工具响应也是界面”推进到执行层:大型工具结果不一定要完整回填给模型,可以先在代码执行环境中筛选、聚合、join 或脱敏,再只打印少量样本和摘要。此时工具设计要同时服务两个调用方:模型需要能通过文件和函数签名理解工具接口,执行代码需要稳定、可组合、类型清晰的返回值。

用 Agent 改进工具

Anthropic 的一个实践要点是:可以让 Agent 参与改进自己的工具。把评估运行产生的 trace、工具调用、工具响应和失败样例交给 Claude Code 这类编码 Agent,它可以批量发现工具描述矛盾、schema 不一致、输出冗余和实现问题。

但这不等于让生成者自证成功。更稳的做法是让 Agent 提出修改,再用 held-out eval set 检查是否真的提升,避免只对训练任务过拟合。这个结构接近 Evaluator-Optimizer(评估器-优化器):优化器可以是 Agent,但是否保留修改必须由外部评估裁决。

与相关概念的关系

参考来源
  1. 1. https://www.anthropic.com/engineering/writing-tools-for-agents
  2. 2. https://www.anthropic.com/engineering/code-execution-with-mcp
评论