《Agent原理,架构与实践》
第6讲:Agent工具设计
准备起一个新的系列《Agent原理,架构与实践》。目标不贪心:
1. 希望每篇文章有1-2个点能让大家有收获;
2. 希望不被骂;
本期第六讲,咱们开始。
上一讲提到,Agent架构的核心是两层循环:
1. 外循环,负责感知用户与Agent的交互;
2. 内循环,规划+行动+观察,负责任务的实现。
其中,"行动"这一步,Agent必须要靠工具,今天来聊聊Agent架构里,工具的设计要点。
【1】问题一:Agent工具设计,为什么不能直接包装API?
工程系统完成任务的工具,是接口。通过各个API的组合调用,完成登录,下单,支付等核心业务。
工程架构架构师,在给Agent架构设计工具时,最容易犯的错误是什么?
把现有的API挨个包装一遍:
1. 有list_users接口,就包个list_users工具;
2. 有get_user接口,就包个get_user工具;
3. 有update_user接口,就包个update_user工具;
直觉上这很合理:接口现成,包装省事。
画外音:工程架构和Agent架构,真的很不一样。
但我想说的是,用户工程系统API的思维,来设计Agent系统工具,是Agent系统架构设计的大忌!
Anthropic在工具设计指南里讲了一个特别形象的例子。
假设咱们要做一个通讯录Agent,帮用户查联系人。如果按照"包装API"的思路,会做出一个list_contacts工具:返回全部联系人,让Agent自己慢慢找。
那么问题来了:假如通讯录里有1000个联系人,这个工具会返回1000条记录,Agent得把1000条逐个读进上下文,token一个一个地过,才能找到那一个要找的人。
回想第3讲的内容:上下文是稀缺资源。1000条联系人记录灌进来,token烧掉一大截,注意力被稀释,后面的正经事反而干不好。
正确的做法是search_contacts:按名字搜索,只返回匹配的那几个。把"找"这件事放在工具里完成,而不是把数据全倒给Agent让它自己翻。
为什么呢?
API是给程序用的,程序的内存便宜,遍历一千条效率高,还不花钱;Agent的"内存"是上下文窗口,贵得很。让Agent翻一千条记录,就像在一本没有目录的字典里查字,查是能查,但谁受得了。
这就是核心认知:Agent用工具的方式,和传统软件不一样。官方有个词叫affordance,可以理解为"这东西到底能怎么用"。软件遍历便宜、缓存随便、想造多少造多少;Agent读一条信息就烧一份token。所以,Agent工具不是API的包装,而是按Agent干活的方式重新设计的接口。
【2】问题二:Agent工具的设计原则是什么?
Anthropic把工具叫做"确定性系统和非确定性Agent之间的契约"。契约写得好不好,直接决定Agent的成功率,其中有四条核心原则:
第一条:按工作流合并,不按API拆分。
设计调度工具时,不要做成list_users + list_events + create_event三个工具(Agent要调三次,中间结果全过上下文),而是做一个schedule_event——找空闲、定时间、建日程,一次完成。工具应该对应"人做这件事的自然步骤",而不是数据库的表结构。
第二条:命名空间和边界要清晰。
工具多了之后,名字就是路标:log_search和jira_search一眼就能分清。功能重叠、名字含糊的工具会让Agent不知道调用哪一个。这,比没有工具更糟。
画外音:Agent会一个个试。
第三条:返回AI容易理解的“人话”,而不是技术标识符。
这一条非常重要:返回name、file_type这样的人话,而不是uuid、mime_type这样的系统标识。
Anthropic实测发现,仅仅把UUID换成有语义的标识符,就显著减少了Agent的幻觉——模型处理自然语言,比处理uuid=550e8400-e29b这种字符串可靠得多。
第四条:返回的错误信息要可操作。
报错别只甩一个"Error 403",要告诉Agent下一步要怎么办。比如:"没有权限访问该项目,请先请求项目所有者授权"。
汇总上面的栗子:
Bad case:list_contacts,把所有联系人列出来,让Agent理解与计算。
Good case:search_contacts,按需检索联系人,工具来计算。
Bad case:read_logs
Good case:search_logs,原因同上
Bad case:返回uuid,系统标识
Good case:返回name,说人话
Bad case:"Error 403",系统标识
Good case:"没有权限,请先找管理员开权限",说人话
最后,还补充一点最最最重要的:工具描述就是提示词。工具的描述文字是写进上下文、给模型读的。要像给新同事写工作简报一样写它:干什么用、什么时候用、什么时候别用、参数是什么、返回什么。
工具的入餐出餐,决定工具的效果!
Anthropic在他们的多Agent系统里,仅仅重写了一遍工具描述,后续Agent的任务耗时就降了40%。没错,40%,只是改了description而已。description是写给模型看的,不是写给人看的。相关细节,咱们在skill直播里反复强调过,这里不再展开。
【3】问题三:怎么知道工具设计得好不好?
靠实测,不靠感觉。
把工具交给Agent跑真实任务,然后看它的调用轨迹:
1. 调错工具:该用search用了list,名字或描述有歧义?
2. 参数给错:该传名字传了ID,参数设计不清晰?
3. 调用太少:该搜的不搜,工具能力没传达清楚?
4. 结果误读:返回了正确数据但理解错,返回格式不友好?
看轨迹、找翻车、改设计、再跑一遍,根据实际执行来优化工具的设计,这套"评估与迭代"的完整方法,后面的内容会详细展开。今天先建立这个认知:工具设计不是写完就完,是跑出来的。
【4】我们的sdd-site-agent,工具要怎么设计?
回到我们自己要实现的SDD规范驱动开发Agent。上一讲,咱们给四步循环列了工具清单初稿,用今天的原则,sdd-site-agent的工具至少有这几个:
1. read_spec:按名字读某个规范(读文件,只读)
入参:规范名(需求规范,原型规范,技术规范等)
出参:规范内容
2. write_artifact:写产出到指定目录(写需求/原型/方案/代码)
入参:类型+文件名+内容
出参:写入确认
3. run_check:验证规范
入参:某个阶段的产出(具体需求,具体原型,具体技术方案,具体代码)
出参:结果摘要+是否通过
有几点要注意:
1. 咱们是SDD场景个性化Agent,不是Claude Code通用Agent,工具数量要克制;
2. 每个工具都有权限边界(read只读、write限定目录、run限定白名单命令),后面的内容会讲;
3. 哪些该接MCP?哪些该自己为Agent提供工具?通用的,接别人造好的"手",业务专属的工具,必须自己写。
【5】简单总结
今天的内容有点长,但工具是Agent的手,对Agent架构真的很重要:
1. 工具不是API的包装,要按Agent干活的方式重新设计;
2. 好的工具设计四条原则:
- 按工作流合并
- 边界清晰
- 说人话
- 报错可操作,工具描述就是提示词
3. 工具设计得好不好,靠实测不靠感觉,不断执行,不断优化。
补充阅读:《给Agents设计工具》
https://www.anthropic.com/research/writing-tools-for-agents
工具给了Agent手,可光有手不会干活,它还得知道"咱们这个场景要怎么干活":需求文档按什么格式写、代码按什么规范出。这些经验诀窍如何沉淀到Agent里?怎么下一讲见!
欢迎持续关注,让我们一起,一步一步,实现自己业务专属的mini-agent。
知其然,知其所以然。
思路比结论更重要。
===全文完==
PS:公众号只能贴静态图文,无法用js实时互动,在线版设计了课后习题与实操演练,欢迎阅读原文体验。
文章原文