工具设计的艺术
ACI:Agent-Computer Interface
HCI (人-机交互) 领域已经研究了几十年,但 Agent 和计算机之间的交互(ACI)才刚刚开始。实战经验表明:工具设计的质量,直接决定了 Agent 的能力上限。
核心概念:工具是 Agent 和世界之间的契约
传统软件开发中,我们花大量精力设计用户界面(HCI):按钮放在哪里、文案怎么写、交互怎么反馈。但当 Agent 成为系统的用户时,界面变成了工具定义。工具的名字、参数、描述,就是 Agent 的用户界面。
HCI 人 → 系统
人类通过按钮、表单、菜单与系统交互。UI 设计的好坏直接影响用户体验。
用户点击「查天气」按钮
→ 系统调用 getWeather("NYC")
→ 返回结果给用户
确定性:相同操作 → 相同结果
ACI Agent → 系统
Agent 通过工具定义(名称、参数、描述)与系统交互。工具设计的好坏直接影响 Agent 表现。
用户说:「要不要带伞?」
→ Agent 思考:需要调天气工具吗?
→ 先问用户在哪个城市?
→ 调用 get_weather(city="上海")
→ 综合判断后回答
非确定性:相同问题 → 不同调用路径
"Plan to invest as much effort into your Agent-Computer Interface (ACI) as you would into a Human-Computer Interface (HCI)."
工具和传统 API 的根本区别
用户说「要不要带伞」时,Agent 的决策过程
1
用户在哪?
如果对话历史中没提到位置,Agent 可能先问「你在哪个城市?」,再决定是否调工具。
2
需要调天气工具吗?
如果上一轮刚查过天气,Agent 可能直接用缓存结果回答,跳过工具调用。
3
调哪个工具?
是调 get_weather 还是 get_forecast?当前天气 vs 未来预报,工具名和描述决定了 Agent 的选择。
4
参数怎么填?
city 参数应该填「Shanghai」还是「上海」?格式不清晰时 Agent 经常出错。
传统 API 是确定性的:开发者写
getWeather("NYC"),每次执行路径完全一样。Agent 工具是非确定性的:模型需要理解什么时候用、怎么用,这完全取决于工具的设计质量。
工具设计四原则
PRINCIPLE 01
给模型足够的 Token 空间想清楚
模型生成参数是逐 Token 进行的,一旦开始写就很难回头修改。工具设计应该让模型在写复杂参数前,先写简单的方向性参数。
反例:第一个参数就要求写 500 行代码补丁
正例:先写 file_path、再写 change_type、最后写 content
正例:先写 file_path、再写 change_type、最后写 content
PRINCIPLE 02
格式贴近模型的训练数据
模型在训练时见过大量自然语言和常见代码格式。工具参数格式越接近这些熟悉的模式,模型越不容易出错。
反例:用自定义 DSL 描述文件变更
正例:用标准 unified diff 格式,模型在训练数据中见过无数次
正例:用标准 unified diff 格式,模型在训练数据中见过无数次
PRINCIPLE 03
避免不必要的格式开销
不要让模型做数行数、JSON 转义这类机械操作。模型不擅长精确计数,强迫它做只会增加出错概率。
反例:要求
正例:用唯一的上下文字符串匹配目标位置
{"start_line": 15, "end_line": 23} 精确行号正例:用唯一的上下文字符串匹配目标位置
PRINCIPLE 04
Poka-yoke(防呆设计)
源自丰田生产系统的理念:通过改变设计,让错误更难发生。与其期望模型不犯错,不如让工具本身就不容易用错。
反例:参数接受相对路径(模型经常搞错当前目录)
正例:只接受绝对路径,从源头消除歧义
正例:只接受绝对路径,从源头消除歧义
真实案例:SWE-bench 中的一个改动
文件路径:相对路径 vs 绝对路径
BEFORE -- 相对路径
{
"tool": "edit_file",
"path": "src/utils/helper.py",
"content": "..."
}
Agent 频繁搞错当前工作目录,导致编辑错误文件或文件找不到
AFTER -- 绝对路径
{
"tool": "edit_file",
"path": "/repo/src/utils/helper.py",
"content": "..."
}
消除路径歧义,工具调用从频繁出错变成几乎完美
这个改动的代码量极小:只是把参数从接受相对路径改为要求绝对路径。但效果巨大:一个参数设计的改变,让整个 Agent 的可靠性大幅提升。这就是 Poka-yoke 的力量。
工具描述的学问
业界最佳实践建议:像给一个聪明但没有上下文的初级开发者写文档一样写工具描述。这个开发者什么都不知道,但理解力很强,你需要告诉他所有前提条件。
好的工具描述应该包含
示例用法:具体的输入输出样例,让模型一看就会
边界情况说明:输入为空怎么办?找不到结果返回什么?
输入格式要求:日期用 ISO 8601 还是时间戳?路径用绝对还是相对?
和其他工具的区别:「用 search_code 搜代码,用 search_files 搜文件名,不要搞混」
何时不该用这个工具:「如果只需要检查文件是否存在,用 file_exists;read_file 留给需要读取内容的场景」
工具描述对比
差的工具描述
{
"name": "search",
"description": "Search for things"
}
模型不知道搜什么(代码?文件?网页?),参数格式不清楚,和其他搜索工具分不清
好的工具描述
{
"name": "search_code",
"description": "Search for code patterns
across the repository using
regex. Returns matching file
paths and line numbers.
Use search_files for filename
matching instead.
Example: search_code({
pattern: 'def process_',
file_glob: '*.py'
})"
}
名字精确、描述清晰、有示例、有和其他工具的边界说明
工具设计的投入应该和 Prompt 设计一样多。工具名称、参数结构、描述文案,都是 Agent 的用户界面。一个参数的改动可能让 Agent 从不可用变得可靠,正如 SWE-bench 中的绝对路径案例所示。