做了近两年的Agent开发,从最开始用LangChain拼Demo时的兴奋,到后来在Spring AI里被各种配置和抽象层折磨,再到现在带团队落地Agent项目,回头看,真正需要掌握的东西其实就那么几件。市面上教程铺天盖地,框架文档翻来覆去,但很多人学完还是不知道怎么把一个Agent从"能跑"做到"能用"。这篇内容不讲某个框架的API怎么调,而是把这近两年踩过的坑、想明白的事,拆成五件真正要学的事,每一件都配着我实际项目里的场景和判断逻辑。不管你是刚接触Agent开发,还是已经用LangChain、Spring AI写过几个Demo想往生产环境推,这五件事都值得对照着捋一遍。
1. 先搞清楚Agent到底在解决哪类问题
1.1 别把Agent当成"更聪明的函数"
我见过太多人一上来就问"LangChain怎么用""Spring AI的ChatClient怎么配",然后拿一个问答场景套上去,跑通了就觉得自己会Agent开发了。这其实是在用调API的思路做Agent,方向就偏了。
Agent的本质不是"输入问题输出答案",而是让模型在一个循环里自主决定下一步做什么。普通函数调用是你写死逻辑:先查数据库,再拼字符串,最后返回。Agent是你给模型一个目标、一组工具、一个停止条件,然后它自己决定先调哪个工具、拿到结果后要不要再调一次、什么时候算完成。
这个区别决定了你学Agent的第一件事不是学框架,而是学会把一个业务问题拆成"目标+工具+判断条件"三件套。举个例子,我做过一个合同审核的Agent,目标是从合同文本里找出风险条款,工具包括"读取PDF""检索法规库""比对历史合同",判断条件是"风险条款全部标注且给出依据"。这三样定义清楚了,用LangChain还是Spring AI其实差别不大;定义不清楚,换什么框架都是白搭。
1.2 哪些场景适合Agent,哪些纯属自找麻烦
不是所有任务都值得上Agent。我总结了一个简单的判断标准:
| 场景特征 | 适合Agent | 不适合Agent |
|---|---|---|
| 步骤是否固定 | 步骤动态,依赖中间结果 | 步骤固定,可预先编排 |
| 工具调用次数 | 不确定,可能多次 | 固定一两次 |
| 错误容忍度 | 可重试、可回退 | 必须一次成功 |
| 输出格式 | 相对灵活 | 严格结构化 |
比如"根据用户描述生成一份周报"这种,步骤基本固定,用Prompt模板加一次模型调用就够了,硬套Agent反而增加不确定性和成本。而"帮我在多个数据源里排查一个线上问题"这种,排查路径依赖每一步的发现,就非常适合Agent。
我踩过的一个坑是:早期做一个数据清洗Agent,本来规则很明确,非要用Agent让它"自主决定清洗策略",结果每次跑出来的清洗结果都不一致,调试了两周最后退回成固定Pipeline,十分钟搞定。Agent的价值在于处理不确定性,如果你的问题本身没有不确定性,就别引入它。
1.3 从"能跑"到"能用"中间隔着什么
Demo能跑和线上能用,中间隔着的是可控性。Demo里模型调错工具无所谓,重跑一次就行;线上调错工具可能触发一次真实的退款、发一封错误的邮件。所以学Agent的第二层认知是:Agent开发的核心工作量不在"让它动起来",而在"让它在该停的时候停、该问的时候问、该错的时候错得可控"。
这就引出后面四件事:工具怎么设计才不容易被误用、上下文怎么管理才不会越跑越乱、多Agent怎么协作才不互相打架、以及怎么测试和观测一个概率性的系统。这五件事是一条线,缺一件,Agent就停在Demo阶段。
2. 工具设计:Agent能力的天花板由它决定
2.1 工具不是越多越好,而是越"不容易被误用"越好
刚开始做Agent时,我恨不得把公司所有API都封装成工具塞给模型,觉得工具越多能力越强。结果模型在十几个工具里反复横跳,选错率极高。后来才明白,工具设计的核心不是数量,而是每个工具的"意图清晰度"。
什么叫意图清晰?就是模型只看工具名和描述,就能准确判断"这个场景该不该用它"。我现在的做法是:
- 工具名用"动词+对象"结构,比如
search_contract_by_id而不是contract_tool - 描述里写清楚什么时候用、什么时候不用,而不只是功能
- 参数尽量少,必填参数控制在3个以内
- 返回值结构化,避免让模型去解析一大段自然语言
举个实际对比。早期我写过一个工具描述是"查询合同信息",模型经常在只需要合同金额的时候也去调它,拿回一大堆无关字段。改成"根据合同ID查询合同的签署方和金额,仅在已知合同ID且需要核对金额时使用"之后,误调用率明显下降。
2.2 工具粒度的取舍:粗一点还是细一点
这是被问得最多的问题之一。我的经验是:优先做"业务语义级"的工具,而不是"API级"的工具。
比如后端有一个/api/order/detail接口,你不要直接把它封装成get_order_detail丢给模型,而是想清楚Agent在这个业务里真正需要完成的动作是什么。如果Agent的任务是"处理用户退款咨询",那工具应该是check_refund_eligibility(判断是否符合退款条件),而不是让模型自己去调订单详情、再调退款规则、再自己判断。
原因很简单:把业务判断逻辑放在工具里(代码里),比放在模型的推理里可靠得多。模型擅长的是"决定调用哪个工具",不擅长"精确执行多步业务规则"。我见过太多项目把本该写死在代码里的规则交给模型推理,结果就是线上时不时出幺蛾子。
当然,粒度太粗也有问题——工具变成一个黑盒,模型无法根据中间结果调整策略。折中方案是:主流程用粗粒度工具,需要模型介入判断的地方暴露细粒度工具。
2.3 工具报错怎么返回,直接决定Agent会不会"发疯"
这一点极少有人讲,但极其重要。工具执行失败时,你返回给模型的内容,直接决定它下一步的行为。
我踩过的坑:工具抛异常后,我把原始堆栈直接返回给模型,结果模型看到一堆英文报错,开始胡乱重试,甚至编造参数。后来改成结构化错误返回:
{ "success": false, "error_type": "NOT_FOUND", "message": "未找到ID为12345的合同,请确认ID是否正确", "retryable": false, "suggestion": "请向用户确认合同ID" }关键字段是retryable和suggestion。模型看到retryable: false就不会无脑重试,看到suggestion就知道下一步该干嘛。实测下来,加了这两个字段之后,Agent在工具报错后的"发疯率"下降了一大半。
提示:工具返回里永远不要暴露内部堆栈、数据库字段名、服务器路径这类信息,一是模型可能拿去做奇怪的事,二是这些信息可能通过Agent的输出泄露给用户。
3. 上下文管理:Agent跑着跑着就"失忆"或"跑偏"的根因
3.1 上下文不是越长越好,而是要"该记的记,该忘的忘"
Agent和普通对话最大的区别是:它会跑很多轮,每轮都产生工具调用和结果。如果不加管理,上下文会迅速膨胀,然后出现两个典型症状:一是模型开始忽略早期的重要指令,二是token成本飙升。
我在一个数据分析Agent上吃过这个亏。它需要连续调用七八个工具查数据,跑到第五轮的时候,模型已经忘了最初用户说的"只看华东区"。原因就是中间塞了太多工具返回的原始数据,把最初的指令"挤"到了注意力边缘。
解决办法是分层管理上下文:
- 系统层:角色、核心约束、工具定义,永远保留
- 任务层:当前目标、关键约束(如"只看华东区"),显式重述
- 历史层:工具调用记录,做摘要压缩
- 临时层:当前轮的原始数据,用完即弃
具体操作上,我通常会在每轮循环开始时,把"任务层"的关键约束重新拼进Prompt,而不是指望模型自己记住。这看起来笨,但极其有效。
3.2 工具返回结果要不要全塞进上下文
不要。这是另一个高频坑。工具返回的原始数据往往很长(比如一个查询返回50条记录),全塞进去既浪费token又干扰模型判断。
我的做法是在工具层做预处理:工具返回给模型之前,先做一次筛选或摘要。比如查询返回50条订单,工具内部先聚合成"共50条,其中异常3条,异常ID为...",再返回给模型。模型需要的是"决策依据",不是"原始数据"。
如果确实需要保留原始数据供后续使用,就存到外部(比如一个临时的state对象或缓存),上下文里只放一个引用ID。模型需要时再通过工具去取。这就是所谓的上下文与状态分离——上下文放"模型需要知道的",状态放"系统需要记住的"。
3.3 循环终止条件:Agent什么时候该停
Agent跑飞最常见的原因就是没有明确的终止条件。模型会一直觉得"我还能再调一次工具",然后无限循环。
终止条件要设计成多层的:
- 硬性轮次上限:比如最多10轮,到了就强制停
- 目标达成判断:模型显式输出"任务完成"信号
- 无进展检测:连续两轮工具调用结果相同或高度相似,判定卡住,停止
- 成本上限:token消耗或工具调用次数超过阈值,停止
我一般把这四个都配上。硬性上限是兜底,目标达成是正常出口,无进展检测防止死循环,成本上限防止烧钱。少任何一个,线上都可能出问题。
注意:终止后要给用户一个明确的交代,而不是静默停止。比如"我已尝试X种方式,未能完成Y,建议您...",这比直接卡住体验好得多。
4. 多Agent协作:什么时候该拆,拆了怎么不打架
4.1 单Agent搞不定的时候,先想是不是Prompt的问题
很多人一遇到复杂任务就想上多Agent,觉得"分工协作"听起来更高级。但我的经验是:大部分所谓需要多Agent的场景,其实是单Agent的Prompt没写好,或者工具没设计好。
在拆多Agent之前,先问自己三个问题:
- 单Agent的上下文是不是太长了?如果是,先做上下文压缩
- 是不是工具太多导致选择困难?如果是,先做工具分组或路由
- 是不是任务本身有明确的阶段划分?如果是,才考虑拆
真正适合多Agent的场景,是子任务之间需要不同的"人格"或"权限"。比如一个Agent负责和用户对话(需要友好、耐心),另一个Agent负责执行敏感操作(需要严格、谨慎),这两者的行为准则冲突,放在一个Agent里会互相干扰,这时候拆开才合理。
4.2 主从模式 vs 对等模式,怎么选
多Agent协作主要有两种拓扑:
| 模式 | 结构 | 适用场景 | 风险 |
|---|---|---|---|
| 主从(Orchestrator-Worker) | 一个主Agent调度多个子Agent | 任务可分解、子任务相对独立 | 主Agent成为瓶颈 |
| 对等(Peer-to-Peer) | Agent之间互相通信 | 需要协商、辩论的场景 | 容易陷入无限对话 |
我90%的项目用的是主从模式。主Agent负责理解目标、拆解任务、分发给子Agent、汇总结果;子Agent只负责执行自己那块,不关心全局。这种结构可控性最强,因为决策权集中在主Agent,子Agent的行为边界清晰。
对等模式我只在一个"方案评审"的场景用过——让两个Agent分别扮演"提出方案"和"挑刺"的角色互相辩论。这种模式很有意思,但极难控制,很容易两个Agent客套半天或者吵起来没完。用的话一定要设对话轮次上限。
4.3 Agent之间传什么,不传什么
多Agent协作最容易出问题的地方是信息传递。传多了,子Agent被无关信息干扰;传少了,子Agent缺上下文做不了事。
我的原则是:主Agent给子Agent传"任务描述+必要上下文+期望输出格式",子Agent给主Agent回"结果+置信度+异常说明"。
具体来说,主Agent调用子Agent时,不要把它自己的完整对话历史传过去,而是提炼出一个干净的任务包。子Agent返回时,也不要返回一大段自然语言,而是结构化结果。这样主Agent汇总时才好处理。
我踩过的坑:早期让子Agent返回自然语言总结,结果主Agent要花大量token去解析这些总结,还经常理解错。改成结构化返回后,主Agent的处理逻辑简单多了,整个系统的稳定性也上来了。
5. 测试与观测:概率性系统怎么保证线上不出事
5.1 Agent的测试和传统软件测试根本不是一回事
传统软件测试是"给定输入,断言输出"。Agent不行,因为同样的输入,模型可能给出不同的工具调用路径,最终结果也可能有差异。所以Agent测试的核心不是断言具体输出,而是断言"行为边界"。
我现在的测试分三层:
- 单元层:测工具函数本身,这个和传统测试一样,输入输出确定
- 行为层:测Agent在给定场景下"该调的工具调了没、不该调的调了没、该停的时候停了没"
- 端到端层:用一批真实场景跑,人工评估结果质量
行为层是最关键的,也是最容易被忽略的。我会为每个核心场景写一组"期望行为",比如"用户问退款政策时,应该调用query_refund_policy而不是process_refund"。然后用自动化脚本跑一批case,统计行为符合率。这个符合率不需要100%,但要有基线,且每次改动后不能明显下降。
5.2 观测什么:Agent的"黑盒"要打开哪些窗口
Agent上线后,最怕的是"它出错了但我不知道"。所以观测体系必须覆盖:
- 每轮的输入输出:模型看到了什么、输出了什么
- 工具调用链:调了哪些工具、顺序、参数、结果
- token消耗:每轮、每任务的消耗,用于成本监控
- 异常与重试:哪些工具报错、模型怎么处理的
- 终止原因:正常完成、轮次上限、无进展、成本超限
这些数据我一般会打到日志系统里,按任务ID串起来。出问题时,能完整回放一个任务的执行过程。这个能力极其重要——没有它,Agent出问题你只能靠猜。
提示:日志里记录模型输入输出时,注意脱敏。用户隐私、内部数据这些不能明文落盘。
5.3 上线策略:灰度、回滚、人工兜底
Agent上线千万别一把梭。我的标准流程是:
- 影子模式:Agent跑,但不真正执行操作,只记录"如果执行会怎样",人工对比
- 灰度放量:先放1%流量,观察行为符合率和异常率
- 人工兜底:敏感操作(退款、发邮件、改数据)必须有人工确认环节,或者设置金额/影响范围阈值
- 快速回滚:一旦异常率超过阈值,能一键切回旧逻辑
这套流程看起来保守,但Agent是概率性系统,保守一点不丢人。我见过太多团队Demo效果好就直接全量,结果线上出了几次事故,反而拖慢了整个项目的推进。
6. 框架选型:LangChain、Spring AI还是自己写
6.1 框架解决的是"重复造轮子",不是"替你思考"
LangChain和Spring AI这类框架,核心价值是把"模型调用、工具封装、循环控制、上下文管理"这些通用逻辑封装好,让你少写样板代码。但它们不解决业务问题,也不替你决定工具怎么设计、上下文怎么管。
我见过有人纠结"到底用LangChain还是Spring AI",纠结了两周。其实这个决策没那么重要,因为你真正要学的那五件事,换任何框架都一样。框架只是外壳,里面的设计思想才是核心。
6.2 选型看什么:团队技术栈、可控性、生态
我的选型逻辑很简单:
- 团队是Java栈:优先Spring AI,和现有Spring Boot服务集成顺滑,运维体系统一
- 团队是Python栈:LangChain生态更成熟,工具和示例多
- 需要深度定制循环逻辑:考虑自己写,框架的抽象层有时候反而是束缚
- 需要快速验证:用框架,别自己造
Spring AI这两年在国内落地越来越多,尤其是和Spring Boot、若依这类框架结合的企业项目。它的优势是和Java生态无缝,缺点是抽象层有时候不够灵活,遇到框架没覆盖的场景要绕。LangChain的优势是灵活、生态全,缺点是版本迭代快、API变动大,升级一次可能改一堆代码。
6.3 我的实际选择:框架打底,关键逻辑自己控
我现在的做法是混合:用框架处理模型调用、工具注册这些标准化的部分,但循环控制、上下文管理、终止条件这些核心逻辑自己写。这样既享受了框架的便利,又保留了对关键行为的控制权。
比如用Spring AI的ChatClient做模型调用和工具绑定,但整个Agent的循环、状态管理、终止判断是我自己实现的。这样框架升级时,我的核心逻辑不受影响;框架没覆盖的场景,我也能自己补。
这个思路可能不适合所有人,但对需要长期维护、对可控性要求高的项目,我觉得是最稳的。
7. 一些没人告诉你但很重要的实操心得
7.1 Prompt里的"不要做什么"比"要做什么"更重要
给Agent写系统Prompt时,我花在"禁止事项"上的时间比"任务描述"还多。因为模型天然倾向于"多做",你不明确禁止,它就会自作主张。
比如我会写:"不要在没有用户确认的情况下执行任何写操作""不要在工具返回错误时编造数据""不要假设用户没说过的信息"。这些负面约束,每一条都是踩坑换来的。
7.2 模型选型不是越强越好,而是越"稳"越好
Agent场景下,模型的稳定性比聪明程度更重要。一个偶尔超神但经常抽风的模型,不如一个稳定发挥的中等模型。因为Agent是多轮循环,任何一轮的不稳定都会被放大。
我一般会准备两个模型:一个主力模型跑正常流程,一个更强的模型做兜底(当主力模型连续失败时切换)。这样成本和稳定性兼顾。
7.3 版本管理:Prompt、工具、模型都要版本化
Agent的行为由Prompt、工具定义、模型版本共同决定。任何一个变了,行为都可能变。所以这三样都要版本化,并且记录"哪个版本组合产生了哪个结果"。出问题时,能快速定位是哪个变更导致的。
这一点在团队协作时尤其重要。没有版本管理,两个人同时改Prompt和工具,出了问题根本查不出来。
7.4 别追求"全自动",该让人介入就介入
最后一条,也是最重要的:Agent不是越自动越好。在关键节点设置人工确认,不是技术不行,而是负责任。我做的所有生产级Agent,在涉及资金、对外沟通、数据修改这些操作时,都有人工确认环节。这不是妥协,是设计。
真正成熟的Agent系统,是知道"什么时候该自己决定,什么时候该问人"的系统。这个判断能力,比任何框架技巧都值钱。
回头看这两年,Agent开发真正难的不是学某个框架的API,而是建立一套关于"如何让一个概率性系统可控地完成业务目标"的思维方式。工具设计、上下文管理、多Agent协作、测试观测、框架选型,这五件事本质上都是在回答同一个问题:怎么让不确定的模型,做出确定的业务结果。想清楚这个,剩下的都是工程细节。