Writing effective tools for agents — with agents
AI 代理的“上下文窗口”(Context)是其最宝贵的资源,类似于人类的“注意力”或“短期记忆”。你不会想通过从头到尾读完一本字典来查找一个词的意思(消耗巨大注意力),你会直接使用索引或按字母顺序查找(高效的工具)。是的,它提出了一个非常实用的、以评估为驱动的迭代开发循环。这两个都是为了让工具对代理更“友好”(Ergonomic)的关键操作。的思想在 AI 领域的应用。,从而实现性能的自我提升。
https://www.anthropic.com/engineering/writing-tools-for-agents
代理工具深度解析:Q&A
Q1:这篇文章的核心论点是什么?为什么为 AI 代理(Agent)编写工具,和我们平时为其他程序员写 API 有本质区别?
A: 这篇文章的核心论点是:为非确定性的 AI 代理设计工具,必须从根本上改变我们编写和思考软件的方式。
这与传统 API 设计的本质区别在于交互对象的不同:
-
传统 API (确定性世界):
- 本质: 是一个确定性系统与另一个确定性系统之间的契约。
- 类比: 就像两台精确咬合的齿轮。你调用
getWeather("NYC"),它就一定会、也只会用完全相同的方式去获取纽约市的天气。输入相同,过程和输出永远相同。
-
代理工具 (非确定性世界):
- 本质: 是一个确定性系统 (工具) 与一个非确定性系统 (AI 代理) 之间的契约。
- 类比: 更像是你给一个极富创造力但有时会分心、会误解的“人类实习生”(AI 代理)下达指令。当你问“我今天需要带伞吗?”,代理可能会:
- 直接调用天气工具。
- 先反问你在哪个城市。
- 甚至可能“幻想”出一个天气信息。
- 结论: 我们不能再假设调用者会 100% 精确、理性地使用工具。因此,工具的设计目标不再仅仅是实现功能,更是要引导、辅助代理,并对它的“不确定性”行为有鲁棒性。
Q2:文章提出了一个怎样的开发流程来打造高效的代理工具?听起来像个可以动手实践的循环。
A: 是的,它提出了一个非常实用的、以评估为驱动的迭代开发循环。这很符合你动手实践的风格:
-
第一步:构建原型 (Build a Prototype)
- 快速搭建工具的初始版本。你可以把相关的 SDK、API 文档喂给像 Claude Code 这样的编码助手,让它帮你生成工具代码的初稿。
-
第二步:运行评估 (Run an Evaluation)
- 这是最关键的一步。你需要系统性地衡量代理使用这些工具的效果。你需要:
- 创建真实的测试任务: 基于真实世界的使用场景,设计需要调用工具(甚至多次调用)才能完成的复杂任务。例如,“安排下周与张三的会议,讨论项目A,并附上上次的会议纪要”。
- 建立验证机制: 定义一个标准来判断代理是否成功完成了任务。
- 这是最关键的一步。你需要系统性地衡量代理使用这些工具的效果。你需要:
-
第三步:与代理协作分析与改进 (Collaborate with Agents to Improve)
- 让代理(比如 Claude)去分析评估过程中产生的日志、对话记录和失败案例。代理非常擅长从大量的文本中发现模式,比如:
- “看起来代理总是把
user_id和user_name搞混。” - “这个工具的描述似乎有歧义,导致代理用错了。”
- “看起来代理总是把
- 基于这些分析,你(或者让代理帮你)重构和优化工具代码、描述等。
- 让代理(比如 Claude)去分析评估过程中产生的日志、对话记录和失败案例。代理非常擅长从大量的文本中发现模式,比如:
-
第四步:重复循环
- 将优化后的工具再次投入评估,如此循环往复,直到性能达到满意的水平。
这个过程的本质是将“工具开发”从一个纯粹的工程问题,转变为一个包含“实验、测量、优化”的科学问题。
Q3:在选择实现哪些工具时,有什么“避坑”指南吗?为什么说“工具并非越多越好”?
A: 这是一个非常深刻的观点。这里的核心原则是:“面向工作流,而非面向功能”。
-
本质: AI 代理的“上下文窗口”(Context)是其最宝贵的资源,类似于人类的“注意力”或“短期记忆”。如果一个工具返回大量无关信息,就会严重消耗这个资源。
-
要避免的坑:
- 无脑封装: 不要简单地把现有的每一个 API 终端都封装成一个工具。
- 返回“全量数据”: 避免创建像
list_all_users这样会返回巨大列表的工具。代理需要“逐字逐句”地在上下文中阅读这个列表来找到目标,极其低效。
-
推荐的做法:
- 合并操作: 将人类处理任务时经常连续执行的多个步骤,合并成一个工具。
- 例子: 不要提供
list_users,list_events,create_event三个工具,而是提供一个schedule_event工具,它在内部完成查找参会者有空的时间并创建日历事件的所有操作。
- 例子: 不要提供
- 提供搜索/过滤能力: 将“遍历”的计算压力从代理的“大脑”(上下文)转移到工具本身。
- 例子: 用
search_logs(keyword="error")代替read_logs()。
- 例子: 用
- 合并操作: 将人类处理任务时经常连续执行的多个步骤,合并成一个工具。
-
类比: 你不会想通过从头到尾读完一本字典来查找一个词的意思(消耗巨大注意力),你会直接使用索引或按字母顺序查找(高效的工具)。
Q4:文章中提到的“命名空间 (Namespacing)”和“优化返回内容”具体指什么骚操作?
A: 这两个都是为了让工具对代理更“友好”(Ergonomic)的关键操作。
-
命名空间 (Namespacing):
- 作用: 分组和消歧。当代理有权访问来自不同服务(比如 Asana, Jira, Slack)的几百个工具时,它很容易混淆。
- 操作: 给工具名称加上前缀,形成逻辑分组。例如,
asana_search_tasks和jira_search_issues。 - 本质: 这就像在代码中用
module或package来组织函数一样,为代理提供了一个清晰的“工具地图”,帮助它在正确的“抽屉”里找到正确的工具。
-
优化返回内容 (Returning Meaningful Context):
- 作用: 提升信噪比,降低代理的理解成本。
- 操作:
- 返回自然语言而非ID: 尽量返回人类可读的名称(
name: "Project Phoenix")而不是一串无意义的ID (uuid: "a1b2c3d4...")。这能显著减少代理的“幻觉”。 - 提供不同详细度的格式: 可以给工具增加一个
response_format参数(如"concise"或"detailed")。默认用简洁模式节省Token,当代理需要ID去调用下一个工具时,再让它自己请求详细模式。 - 优化错误和截断提示: 如果返回内容被截断,提示应该告诉代理如何获取更多信息(例如,“结果已截断,请使用
page参数翻页”)。如果调用出错,错误信息应该清晰地告诉代理如何修正它的输入。
- 返回自然语言而非ID: 尽量返回人类可读的名称(
- 本质: 这是在和代理进行“高效沟通”。你不会跟同事说“去处理工单
8f5b...”,你会说“去处理那个关于登录问题的工单”。
Q5:这篇文章最让我感兴趣的是“与代理协作”。它具体是如何让代理来优化自己的工具的?
A: 这确实是全文最“赛博朋克”也最具启发性的一点。其核心是利用 LLM 强大的代码分析和重构能力。
具体流程如下:
-
收集“第一手资料”: 运行完一轮评估后,你会得到大量的“交互记录 (transcripts)”,里面包含了:
- 用户的原始请求。
- 代理的“思考链 (Chain-of-Thought)”。
- 代理实际调用的工具和参数。
- 工具返回的结果。
- 代理的最终回答。
- 任务成功或失败的标记。
-
让代理成为“代码审查员”:
- 你把这些原始的、混杂的交互记录整个喂给一个编码能力强的代理(如 Claude Code)。
- 然后给它一个指令,例如:“分析以上评估日志,找出导致任务失败或效率低下的原因,并重构相关的工具定义(包括函数实现、参数和描述),以提升整体性能。”
-
代理能发现的问题:
- 描述歧义: “
search这个描述太模糊了,我应该把它改成search_documents_by_keyword。” - 参数设计不佳: “代理总是忘记传
user_id,也许我应该把这个参数设为必填,并在描述里举个例子。” - 实现效率低下: “这个工具返回了太多无用字段,我来修改一下,只返回
name和summary。” - 命名不一致: “所有 Asana 相关的工具都应该以
asana_开头,我来统一一下。”
- 描述歧义: “
本质: 这是一种元编程 (Metaprogramming) 的思想在 AI 领域的应用。你不再是手动调试和优化每一行代码,而是创建一个评估环境,让 AI 在这个环境中“演化”和“迭代”自己的工具集,从而实现性能的自我提升。这极大地提升了优化的效率和规模。
更多推荐

所有评论(0)