3725 字
约 12 分钟
19
4.4 ACI:像设计人机界面一样设计工具
2026-10-25
无标签

4.4 ACI:像设计人机界面一样设计工具

一句话结论:工具是 Agent 和世界之间的契约。你给人的软件设计界面(HCI),给模型的软件设计"上下文界面"(ACI)——用户是模型时,容错率更低,所以设计要更防呆。

读这篇之前

需要先读 4.1–4.3。这一篇是工具设计的"设计观":不是讲怎么写代码,而是讲怎么让模型用得顺手。


一、HCI vs ACI:一个关键类比

我们很熟悉 HCI(Human-Computer Interaction,人机交互):按钮要够大、错误提示要清楚、危险操作要二次确认。

ACI(Agent-Computer Interface)是同一件事,只是用户从人变成了模型。 [原文 4629]

维度 HCI(人) ACI(模型)
用户 人 模型
阅读能力 会浏览、会试错、会查文档 只能看上下文中给它的内容
容错方式 有经验、能举一反三 按模式匹配,容易钻牛角尖
反馈 能看到界面变化 只能看到你回传的文本
学习成本 可以培训 改不了,只能改你的设计

最后一行是关键:人可以被培训,模型不能。 所以当模型用错工具时,唯一能改的是你的设计。

工具和传统 API 的根本区别

传统 API Agent 工具
调用者 程序员(读过文档) 模型(只看到 description)
参数错误 编译期/运行时捕获,程序员修正 运行期出现,必须靠信息回填让模型自修
设计目标 完备、正交、通用 不易误用、错误可自解释
文档 给人看,可以很长 给模型看,要短、要准、要含边界

一句话:传统 API 追求"能力完备",Agent 工具追求"不易用错"。


二、四条设计原则

原文给出的四条 ACI 原则,每条都对应模型的一个具体弱点:[原文 4629]

原则一:给模型足够的 Token 空间想清楚

问题:如果你要求工具调用格式极其紧凑(比如必须在一行内完成复杂决策),模型没有"思考空间",容易出错。

做法:允许甚至鼓励模型在调用工具前先输出思考内容。这正是 ReAct 的 Thought 字段存在的意义(3.3)。

{
  "thought": "用户问的是天气,我需要调 get_weather。城市是北京,日期未指定,默认今天。",
  "action": "call_tool",
  "tool_name": "get_weather",
  "tool_args": {"city": "北京"}
}

这条原则的本质:把"隐式推理"变成"显式文本",让推理过程成为后续 token 的依据(3.3 讲过的机制)。

原则二:格式贴近模型的训练数据

问题:如果你发明了一套独特的格式(比如用 <<<>>> 包裹参数),模型没见过,只能靠推理去理解,容易错。

做法:用模型熟悉的格式。

差 好
自定义标记语言 JSON
特殊分隔符 Markdown 标题
奇怪的缩进约定 常见 YAML / 列表

这条原则的依据在 2.1:模型擅长"检索"训练数据里的模式,不擅长"推理"陌生的规则。用熟悉的格式,等于让它做擅长的事。

原则三:避免不必要的格式开销

问题:如果工具返回值里塞了大量模型用不到的信息(时间戳、请求 ID、冗长的嵌套结构),既浪费 Token(2.1),又增加噪声(2.2)。

做法:工具返回只给模型需要的。

差:{
  "code": 0, "msg": "success",
  "requestId": "a3f8...", "timestamp": 1761..., "cost": 12,
  "data": { "list": [...], "total": 42, "page": 1 }
}
好:{
  "items": [...],
  "total": 42,
  "note": "共 42 条,已返回前 20 条"
}

这份"归一化"工作属于工具层的职责(2.5 第五节)。

原则四:Poka-yoke(防呆设计)

这是四条里最有价值的一条。

Poka-yoke 来自制造业:通过设计让"错误根本无法发生"。用在工具上就是——与其报错,不如让它不可能填错。

差(容易错) 好(防呆)
status: string "状态" status: enum(active, disabled)
path: string "文件路径" path: enum(允许的 5 个路径)
format: string "格式" 三个独立工具:export_pdf / export_md / export_html
action: string + data: object 拆成 create_x / update_x / delete_x

核心思想:把"选择"从开放集合变成封闭集合。

原文提到一个真实案例:SWE-bench 里的某个改动就是一个防呆设计——把原本需要模型自己拼的完整路径,改成让模型只选文件名,路径由工具内部处理。错误率显著下降。[原文 4629]


三、Think Tool:让 AI 先想后做

原文介绍了一个非常简洁但有效的技巧。

问题场景:Agent 在长任务中会犯错,因为它在"边做边想",缺少一个"停下来梳理"的环节。

Think Tool 是什么:一个不执行任何操作的工具,模型调用它只是"把想法写出来"。[原文 4629]

name: "think"
description: "用于梳理思路、检查约束、规划下一步。不产生任何副作用。"
parameters:
  thought: "你的思考内容"

它的实现极其简单——工具实现就是"把输入原样返回"。

为什么有效?

机制 解释
强制"想"变成显式 Token 模型后续输出能看到这些思考(3.3)
打断"惯性执行" 长任务中提供一个"检查点"
让约束被重新提及 在 think 里复述规则,等于重申约束

什么时候该用:任务步骤多、约束多、或者发现 Agent 常在某类判断上出错时。简单任务不需要。


四、用 Agent 优化 Agent 的工具

原文还给了一个进阶思路:工具描述可以自动优化,流程三步:[原文 4629]

步 做什么
Prototype 写一版初始工具描述
Evaluate 用评测集跑,统计"该调没调"和"参数正确率"
Optimize 让模型分析失败案例,提出描述改进

五个优化时的检查点:

  1. 模型是否在该调用时调用了?
  2. 模型是否在不该调用时误调了?
  3. 参数是否填对了?
  4. 是否反复调用同一个工具(说明结果没被正确理解)?
  5. 描述里是否缺少边界说明?

这里的关键前提是 Evaluate:没有评测集,优化就是瞎改(第 13 章)。


五、常见误区

误区一:工具设计是后端工程问题。
它是交互设计问题。用户是模型,交互通道是 description 和返回值。

误区二:防呆设计限制了能力。
恰恰相反,它把"可能出错的空间"消掉,让能做的事更可靠。能选三个值比能填任意字符串更强大,因为前者几乎不会错。

误区三:Think Tool 是多余的调用,浪费 Token。
它在复杂任务上减少的返工远多于它的成本。简单任务别用,复杂任务值得。

误区四:工具返回值给全一点,模型自己会挑。
模型不擅长"过滤",它会把整段都当真。返回要精。

误区五:工具描述写得越像技术文档越专业。
模型的读者不是程序员。它需要的是"什么时候用",不是"内部实现"。

误区六:ACI 只对弱模型重要。
强模型的错误率更低,但结构性误用的风险是一样的。防呆设计的收益对所有模型都存在。


六、动手:四条 ACI 原则的对照改造

把 4.4 的四条原则各举一个改造实例。

原则一:给模型思考空间

差:要求模型只输出 {"action": "..."}
好:允许 {"thought": "...", "action": "..."}

改造点:加一个 thought 字段。成本几十 Token,收益是决策质量明显提升(3.3)。

原则二:格式贴近训练数据

差:用自定义分隔符包裹(如三个尖括号 + BEGIN / END)
好:用 JSON 或 Markdown 标题

理由:模型见过无数 JSON 与 Markdown,没见过你的自定义标记。

原则三:避免不必要的格式开销

差:返回 {"code":0,"msg":"ok","requestId":"...","timestamp":...,
         "data":{"list":[...],"total":42,"page":1}}
好:返回 {"items":[...],"total":42,"note":"共 42 条,已返回前 20"}

改造点:删掉 requestId、timestamp(模型不需要,而且会破坏缓存)。

原则四:防呆(最有效)

差:{"format": "string"}  ← 模型可能填 pdf / markdown / PDF / MD
好:三个工具 export_pdf / export_md / export_html
   或者 {"format": {"enum": ["pdf","md","html"]}}

改造点:把开放集合变成封闭集合。这条能一次性消掉一整类错误。

一个判断

如果你的工具经常"参数传错",先问:能不能让这个参数根本没法传错?

一个来自真实项目的例子

某系统有个工具 export(format, path, options)。上线后 30% 的调用失败。

改造方式:

原设计 改造后
format 自由文本 enum: [pdf, md, html]
path 任意路径 固定输出到 exports/ 目录,只传文件名
options 对象(6 个可选字段) 拆成 2 个布尔参数:include_toc、include_images

失败率从 30% 降到 2%,一次改造,没改模型、没改提示词。

这就是防呆的威力:它消灭的是"出错的可能性",而不只是"把错误报出来"。


七、一个补充:防呆的三个层次

防呆不只有"改枚举"一种。三个层次从弱到强:

层次 做法 效果
① 约束参数 枚举、范围、格式校验 拦住格式错
② 消除参数 能从上下文推断的,就不要让模型传 消灭一整类错误
③ 消除工具 用更少的工具覆盖更多场景 消灭选择错误

第二层最容易被忽略

差:delete_file(path, confirm, dry_run)
   → 模型要传 3 个参数,每个都可能错

好:delete_file(path) 且默认 dry_run
   → 真正执行需要单独的 confirm_delete(path)

把"确认"从参数变成"第二次调用",模型就不会忘记传 confirm: true。

第三层的例子

差:5 个工具 read_file / read_json / read_yaml / read_csv / read_xlsx
好:1 个 read_file,按扩展名自动解析

工具少了,选择错误就消失了。

注意与 4.5 的张力:合并工具会降低"选择粒度"。判据是"副作用是否相同"——相同则合并,不同则拆开。


八、一个补充:ACI 的最小实践清单

如果只做三件事来改善 ACI,做这三件:

# 动作 收益
1 给所有参数加示例值 参数错误率下降最明显
2 把开放集合改成枚举 消灭一整类错误
3 在描述里写"不适用时用谁" 误选率下降

这三件都不需要改代码,只改元数据。

顺序建议:先做第 2 件(收益最大、改动最小),再做第 1、3 件。

一个反例:把 ACI 做成大工程

错:专门设计一套工具描述 DSL、写生成器、加校验层

收益不成比例。ACI 的改进绝大多数来自"元数据写得好",而不是"框架复杂"。

判据:如果你花了三天做工具系统的框架改造,但描述还是"搜索"两个字,那顺序反了。


九、一个补充:ACI 与提示词的关系

一个容易混淆的问题:工具描述和系统提示,该谁写"什么时候用工具"?

位置 该写什么
工具描述 这个工具本身何时用、不用、返回什么
系统提示 整体的工作流程(先做什么、后做什么)

分界线:"工具自己的属性"放描述,"任务的编排"放系统提示。

错:系统提示里写"search_code 用于查找函数定义"
   → 工具改动要改两处,容易不一致

对:描述里写;系统提示只写"第一步先定位代码,用 search_code"

这样工具可以独立演进——这是 8.5 讲的"协议层与业务层分离"的同一条原则。


十、一句话收尾

ACI 的全部努力,都是在弥补一个事实:模型没有常识,只有模式。

人会自然地"知道"不该把 ../../etc/passwd 传进文件路径参数;模型不会——它只看到"这里该填一个路径字符串"。

所以防呆不是限制,是把人类的常识编码进接口设计。


十一、一个补充:工具集的"年龄"问题

工具集和代码一样会老化。三个老化信号:

信号 含义
描述里出现过时的功能名 实现改了,描述没跟
有工具的实现已经没人记得 无人维护
有工具被"绕过"而不被使用 它已不适用

第二个最危险:一个没人维护但有权限的工具,是最容易出事的地方(14.1)。

建议:每季度做一次"工具清点",把没人维护的工具先降权(缩小权限)再决定去留。


十二、最后一条

防呆的最高境界是"让工具根本不存在犯错的可能"——不是拦住错误,而是让错误无处发生。

参数要自由文本 → 改成枚举;动作要拼装 → 拆成固定工具;路径要用户填 → 由系统决定。

每消掉一个"可错空间",就少一整类需要排查的问题。


十三、下一篇

工具设计的原则讲完了。但还有一个问题越来越现实:当工具从 5 个变成 50 个,会怎样?

能力膨胀不是线性成本。下一篇讲"工具爆炸"——以及它对应的解法。

4.4 ACI:像设计人机界面一样设计工具
https://clxhxhhr.top/posts/4885/
作者
clxstart
发布于
2026-10-25
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。