《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实时互动,在线版设计了课后习题与实操演练,欢迎阅读原文体验。



文章原文