智能体这个词在过去一年被用得太泛了。打开任何一个技术社区,满屏都是"三分钟搭建你的第一个Agent""零代码智能体平台横评",但真正把智能体当系统工程来做的人并不多。Anthropic 前阵子发了一篇关于构建有效智能体的架构笔记,在圈子里传得很广,我前后读了三遍,又对着自己手头两个落地项目做了验证,发现里面很多判断跟市面上主流的"智能体=LLM+工具调用+循环"的粗暴理解完全不是一回事。这篇东西我想把那份笔记的核心逻辑拆开讲清楚,顺带补上我自己在工程实践里踩过的坑,尤其是那些文档里不会写、但一上手就会撞上的问题。
如果你正在做智能体开发,或者团队里有人天天喊着要"上Agent"但说不清楚到底要解决什么问题,这篇内容应该能帮你把思路理一遍。我不会给你一个万能框架,因为那东西不存在,但我会把"什么样的任务值得用智能体""工作流和智能体的边界在哪""怎么设计工具接口才不会被模型玩坏"这几个真问题讲透。
1. 先搞清楚:你的任务到底该不该用智能体
这是 Anthropic 那篇笔记开篇就在强调的事情,也是我觉得最容易被跳过的一步。大部分人拿到需求的第一反应是"这个能不能用Agent做",而不是"这个任务的结构到底适不适合Agent"。这两个问题的答案经常是相反的。
1.1 工作流和智能体的本质区别
先把概念理清楚。所谓工作流(Workflow),指的是通过预定义的代码路径来编排LLM和工具,路径是写死的,模型只在特定节点做特定判断。而**智能体(Agent)**是让LLM动态决定自己的执行路径,自己决定调什么工具、调几次、什么时候停。
这个区别听起来很学术,但落到工程上就是一句话:工作流是你控制流程,智能体是模型控制流程。
Anthropic 的笔记里给了一个很实用的判断标准——看任务的可预测性。如果一个任务的步骤基本固定,只是每步需要一点语言理解能力,那它就是工作流的活。比如简历筛选,流程就是"解析简历→提取关键字段→对照JD打分→生成评语",这个链路是确定的,你完全可以用代码把它串起来,中间该调LLM的地方调LLM。这种场景硬上智能体,只会让系统变得又慢又不稳定。
反过来,如果任务需要根据中间结果动态决定下一步做什么,而且步骤数量事先说不准,那才轮到智能体上场。比如"帮我调研一下竞品X最近三个月的定价策略变化",这个任务你没法预先写死路径——你可能要先搜新闻,发现某条新闻提到了一次调价,然后去官网抓历史价格页,发现页面结构变了,又得换个数据源。这种"走一步看一步"的任务,才是智能体的主场。
我自己的经验是,80%号称需要智能体的场景,其实一个设计良好的工作流就够了。剩下20%里,又有一半可以通过"工作流为主+局部智能体"的混合模式解决。真正需要端到端自主智能体的场景,比大家想象的要少得多。
1.2 一个反直觉的结论:智能体不是越自主越好
市面上有一种论调,觉得智能体越自主越高级,能自己规划、自己反思、自己纠错才叫真智能体。Anthropic 的笔记对这个观点是持保留态度的,我完全同意。
自主性带来的是不确定性和调试难度。一个完全自主的智能体,你很难预测它在某个输入下会走什么路径,出了问题也很难复现。我在一个客服场景里试过让智能体完全自主决定回复策略,结果它有时候会自作主张地承诺一些公司政策里没有的补偿方案,这种问题在测试阶段很难穷举出来。
更务实的做法是在关键节点加约束。比如规定智能体在涉及退款、赔偿这类敏感操作时,必须先走一个固定的确认流程,不能自主决定。这就是所谓的"有边界的自主"——在安全范围内给它自由,在风险点上收回来。
提示:判断一个任务该不该用智能体,先问自己三个问题——步骤是否可预测?步骤数量是否固定?出错成本是否可控?三个都是"否"才考虑智能体,否则优先工作流。
1.3 从成本角度看这件事
还有一个很多人忽略的维度:成本。智能体的每一次自主决策都意味着一次甚至多次LLM调用,token消耗是工作流的几倍甚至几十倍。我做过一个对比测试,同样一个信息提取任务,工作流方案平均消耗1200个token,智能体方案平均消耗8500个token,差了7倍。
这不只是钱的问题,还有延迟。智能体多轮决策带来的响应时间,在交互式场景里是致命的。用户问一个问题等30秒,体验直接崩掉。
所以我的建议是,能用工作流解决的绝不上智能体,能用简单智能体解决的绝不上复杂智能体。工程上追求的是"够用且可靠",不是"看起来最先进"。
2. 工具设计:智能体能不能干活,八成看这里
如果确定要用智能体了,接下来最关键的不是选哪个模型,而是怎么设计工具接口。Anthropic 的笔记里花了很大篇幅讲这个,因为这是决定智能体成败的地方。我见过太多项目,模型选的是最好的,prompt 也调得很用心,但工具设计得一塌糊涂,最后智能体表现得像个智障。
2.1 工具描述是给模型看的,不是给人看的
这是第一个认知转变。很多人写工具描述的时候,是按照给人写API文档的思路来的,写得很简洁,觉得模型应该"能理解"。但模型不是人,它没有常识补全能力,你写得含糊,它就真的不知道该怎么用。
Anthropic 的建议是,工具描述要写得像给一个刚入职、什么都不懂的新人看的操作手册。要包含:这个工具是干什么的、什么时候该用、什么时候不该用、输入参数的具体含义和格式、返回值的结构、可能的错误情况。
举个例子,一个查询订单的工具,差的描述是"查询订单信息",好的描述是"根据订单号查询订单的详细状态,包括支付状态、物流状态、预计送达时间。当用户询问订单进度、物流信息或需要确认订单是否已支付时使用此工具。输入必须是完整的订单号,格式为18位数字,不接受部分订单号或用户名查询。"
这个差别在实际运行中非常明显。我做过A/B测试,用详细描述的工具,智能体调用正确率从62%提升到了89%。
2.2 工具粒度:太粗和太细都是坑
工具设计有个经典的粒度问题。工具太粗,一个工具干太多事,模型很难用对;工具太细,工具数量爆炸,模型选择困难。
我的经验法则是:一个工具对应一个明确的意图。比如"发送邮件"这个动作,不要设计成"send_email(to, subject, body, cc, bcc, attachments, priority, ...)"这样一个大而全的工具,而是拆成"发送简单通知邮件"和"发送带附件的正式邮件"两个工具,每个工具的参数都精简到必需。
但也不能拆得太细。我见过有人把"读取文件"拆成"打开文件""读取第一行""读取下一行""关闭文件"四个工具,这就过度了,模型得调四次才能读完一个文件,效率极低还容易出错。
一个实用的判断标准是:如果一个工具需要模型连续调用三次以上才能完成一个用户能理解的原子操作,那这个粒度就太细了;如果一个工具的参数超过7个,或者返回值需要模型做复杂解析才能用,那这个粒度就太粗了。
2.3 错误处理:让工具自己说清楚哪里错了
工具调用失败是常态,关键是怎么让模型知道失败了、为什么失败、该怎么补救。很多工具出错就返回一个"Error"或者抛个异常,模型拿到这个信息完全不知道下一步该干嘛。
好的做法是返回结构化的错误信息,包含错误类型、错误原因、建议的补救措施。比如:
{ "status": "error", "error_type": "invalid_parameter", "message": "订单号格式不正确,应为18位数字", "received": "12345", "suggestion": "请向用户确认完整的订单号,或使用订单查询工具通过手机号查找" }这样模型拿到错误后,能自己判断是该重试、该换个工具、还是该向用户追问。我在实际项目里加上结构化错误返回后,智能体的自主纠错成功率提升了一倍多。
注意:工具的错误信息里不要暴露内部实现细节,比如数据库表名、堆栈信息,这些既没用又可能带来安全风险。
2.4 工具的数量控制
Anthropic 的笔记里提到一个经验值:同时提供给智能体的工具数量最好控制在10-20个之间。太少不够用,太多模型会挑花眼。
如果业务确实需要很多工具,解决方案是分层。先给智能体一组"元工具",比如"查询类工具""操作类工具""分析类工具",让它先选类别,再在类别里选具体工具。或者用动态工具加载,根据当前上下文只把相关的工具塞进prompt。
我在一个电商客服项目里,工具总数有40多个,直接全塞进去模型经常选错。后来改成按对话阶段动态加载——售前阶段只加载商品查询类工具,售后阶段只加载订单和退款类工具,准确率立刻上来了。
3. 记忆与上下文:智能体的"工作台"怎么摆
智能体跟普通LLM应用最大的区别之一,就是它需要维护一个跨多轮、跨多步骤的上下文。这个上下文怎么组织,直接决定了智能体能不能"记住"自己干过什么、干到哪了。
3.1 上下文不是越长越好
很多人有个误区,觉得上下文窗口越大越好,把所有历史都塞进去。但实际测试下来,上下文越长,模型对关键信息的注意力越分散,而且成本和延迟都上去了。
Anthropic 笔记里推荐的做法是分层记忆:把上下文分成几个层次,每层有不同的保留策略。
- 工作记忆:当前任务相关的即时信息,完整保留
- 任务记忆:当前任务的进展摘要,压缩保留
- 长期记忆:跨任务的知识和经验,按需检索
我自己的实现是,工作记忆保留最近5-10轮的完整对话,任务记忆用LLM定期把前面的内容压缩成一段摘要,长期记忆存在向量库里按相关性检索。这样既保证了关键信息不丢,又控制了上下文长度。
3.2 摘要压缩的时机和方式
摘要压缩什么时候做,是个技术活。做太频繁,信息损失大;做太少,上下文又爆了。
我的经验是按token数触发,而不是按轮数。设定一个阈值,比如上下文用到窗口的60%时触发压缩,把最早的那部分内容压缩成摘要。这样能自适应不同长度的对话。
压缩的方式也有讲究。不要简单地让LLM"总结一下",而是给它明确的指令:保留哪些信息(任务目标、已完成步骤、关键结论、待办事项),丢弃哪些信息(寒暄、重复确认、中间过程的细节)。我用的prompt大概是这样:
请将以下对话历史压缩成一段简洁的进展摘要,用于后续任务继续执行。 必须保留:任务目标、已完成的步骤及结果、当前状态、待解决的问题、重要的用户偏好或约束。 可以省略:问候语、重复的确认、已经解决的中间问题、工具调用的原始返回(只保留结论)。 输出格式:一段不超过300字的自然语言摘要。3.3 上下文超长时的降级策略
即使做了压缩,有些任务就是会超长,比如处理一个几百页的文档。这时候需要有降级策略。
我常用的几种:
分块处理:把大任务拆成小任务,每个小任务独立处理,最后汇总。比如分析一份长报告,先按章节拆开,每章单独分析,最后合并结论。
外部存储+检索:把中间结果存到外部,需要时再检索回来。比如处理过程中产生的中间数据,不放在上下文里,而是存到文件或数据库,需要时用工具读回来。
人工介入点:在上下文快满的时候,主动暂停,让用户确认或提供额外信息,然后开一个新的上下文继续。这个策略在长流程任务里特别有用。
4. 从Demo到生产:那些只有上线才会暴露的问题
前面讲的都是设计层面的东西,这一节我想聊聊落地。Demo跑通和生产可用之间,隔着一条巨大的鸿沟。我踩过的坑基本都在这一段。
4.1 循环失控:智能体停不下来
这是最常见的问题。智能体陷入一个循环,反复调用同一个工具,或者反复在几个状态之间跳来跳去,就是不停。
原因通常有几个:工具返回的信息让模型误以为任务没完成;模型对"完成"的判断标准不清晰;错误处理逻辑让模型一直在重试。
解决方案是设置硬性约束。我一般会加三层保护:
第一层是最大步数限制,比如最多执行20步,到了就强制停止并返回当前结果。第二层是重复检测,如果连续三次调用同一个工具且参数相同,就中断并报错。第三层是超时控制,整个任务超过设定时间就终止。
这些约束看起来简单,但能避免90%的失控情况。关键是中断之后要有合理的降级处理,不能直接给用户一个错误,而是返回"任务未完成,已完成的部分是XXX,建议XXX"。
4.2 工具调用的参数幻觉
模型会编造工具参数,这是很常见的问题。比如工具要求传订单号,模型可能编一个看起来像订单号的字符串出来。
这个问题的根源往往是上下文里缺少真实参数。模型不知道真实订单号,又必须填这个参数,就只能编。
解决办法有两个方向。一是在prompt里明确告诉模型,如果缺少必要参数,应该先向用户询问,而不是猜测。二是在工具层面做校验,参数格式不对直接返回错误,让模型重新处理。
我在实际项目里还加了一个技巧:把已知的真实参数显式地放在上下文里,比如"当前用户的订单号是XXX",这样模型就不用猜了。
4.3 多轮对话中的状态丢失
智能体在多轮对话里,经常忘记之前确认过的信息。比如用户第一轮说了要查北京到上海的航班,第三轮智能体又问"请问您要查哪条航线"。
这个问题一部分是上下文管理的问题,一部分是状态没有显式化。我的做法是维护一个结构化的状态对象,把对话中确认的关键信息都存进去,每轮都把这个状态注入到上下文里。这样即使对话历史被压缩了,关键状态也不会丢。
状态对象大概长这样:
{ "task": "查询航班", "confirmed_params": { "departure": "北京", "arrival": "上海", "date": "2024-06-15" }, "pending_params": ["舱位偏好"], "completed_steps": ["确认出发地", "确认目的地", "确认日期"] }4.4 评估:怎么知道你的智能体到底行不行
最后一个大坑是评估。智能体的输出是不确定的,同一个输入可能走出不同的路径,传统的单元测试根本不够用。
我的做法是建一个场景测试集,覆盖典型场景、边界场景、异常场景。每个场景定义好输入、期望的关键行为(不要求完全一致的输出)、以及不能出现的行为。然后用自动化脚本批量跑,人工抽查结果。
关键指标我一般看这几个:
| 指标 | 含义 | 目标值 |
|---|---|---|
| 任务完成率 | 成功完成任务的对话占比 | >85% |
| 平均步数 | 完成任务的平均工具调用次数 | 越少越好 |
| 工具调用准确率 | 工具选择正确的比例 | >90% |
| 循环发生率 | 出现循环的对话占比 | <2% |
| 平均响应时间 | 从用户输入到最终回复的时间 | <15秒 |
这些指标不是一次性能达标的,需要持续迭代。我一般每周跑一次全量测试,看指标变化,定位回归问题。
5. 几个真实场景的架构选择
理论讲完了,我想用几个具体场景说明一下怎么选架构。这些都是我实际做过或深度参与过的项目。
5.1 客服工单分类与路由
这个场景看起来很适合智能体,但我的选择是纯工作流。流程是固定的:接收工单→提取关键信息→分类→路由到对应处理组。每一步都是确定性的,中间用LLM做信息提取和分类判断就够了。上智能体只会增加不确定性和成本。
5.2 竞品情报监控
这个场景我用了工作流+局部智能体的混合模式。整体流程是工作流:定时触发→确定监控目标→抓取数据→生成报告。但"抓取数据"这一步用了智能体,因为不同来源的页面结构不一样,需要智能体动态决定用什么方式抓、抓哪些字段。
5.3 复杂研究助手
这个场景是端到端智能体。用户给一个研究问题,智能体需要自己决定查什么资料、怎么查、查到之后怎么分析、发现信息不足时怎么补充查询。这种开放式任务没有固定路径,只能靠智能体自主决策。
这三个场景的对比很能说明问题:任务越开放、路径越不确定,越需要智能体;任务越固定、步骤越明确,越应该用工作流。
6. 一些零散但重要的经验
最后分享几个我在实践中总结的、不太成体系但很有用的点。
Prompt 里的"不要做什么"比"要做什么"更重要。模型很容易过度发挥,明确告诉它哪些事不能做,比告诉它要做什么更能约束行为。比如"不要编造用户没有提供的信息""不要在未确认的情况下执行写操作"。
给智能体一个"思考"的空间。在让它调用工具之前,先让它输出一段简短的推理,说明为什么要调这个工具、期望得到什么结果。这个推理过程不仅能让决策更准确,也方便你调试。
日志要记全。智能体的每一步决策、每次工具调用、每个中间结果都要记下来。出问题的时候,这些日志是唯一的线索。我一般会把完整的对话轨迹存下来,包括模型的原始输出。
版本管理要严格。Prompt、工具定义、模型版本,任何一个变了都可能影响智能体行为。我见过因为prompt里改了一个标点导致行为变化的案例。所有这些东西都要版本化,每次变更都要跑回归测试。
别追求一次做完美。智能体系统是迭代出来的,先跑通核心流程,再逐步优化边界情况。我第一个上线的智能体版本,工具只有5个,prompt也很粗糙,但解决了80%的常见场景。后面根据实际badcase慢慢加工具、调prompt,才逐渐完善。
这套东西说到底,核心就一句话:把智能体当软件工程来做,而不是当魔法来拜。它有自己的适用边界,有需要精心设计的接口,有必须处理的异常情况。Anthropic 那份笔记的价值,就在于它把这些工程问题讲清楚了,而不是又给你画一个"通用人工智能"的大饼。