
过去半年我一直在跟AI Agent打交道从最早觉得这就是个玩具到后来把它接进了真实业务里做自动化流程。期间换了三版架构推倒重来过两次踩了不少生产环境的坑也积累了一些有点价值的心得。这篇文章不打算讲复杂原理只讲实操经验——Agent项目怎么选架构、怎么拆任务、怎么扛并发、怎么部署和观察、以及有哪些花钱买来的教训。适合正在搭建或打算搭建Agent应用的人读尤其适合那种Demo跑得很欢一上生产就翻车的状态。我会尽量把每个决策背后的理由也说清楚这样你可以结合自己的场景判断怎么取舍。1. 先用一个真实场景说清楚Agent和普通程序到底差在哪1.1 我给Agent的第一个正经任务抓取竞品信息我接的第一个正经Agent任务是帮市场团队抓一批竞品的产品更新信息。需求其实不复杂每天去看五六个竞品的官网和博客找出更新内容整理成摘要发到工作群里。如果按传统方式做要么写爬虫针对每个网站单独写解析规则要么让运营同事每天手动刷新页面。前者维护成本高后者太费人。用Agent的方式思路完全不一样给它一个工具列表网页抓取工具、搜索工具、文档解析工具再给它一段任务描述它自己决定先访问哪个页面、怎么提取正文、判断内容里哪些算更新最后按固定格式输出摘要。第一次跑通时我是有点惊讶的——它没有按照我预想的顺序去抓而是先抓了首页判断有没有News或Blog入口再顺着链接进去。中途有个竞品改版了页面结构导致链接失效它甚至换了搜索入口又找了一遍。这个计划-执行-修正的过程是传统爬虫很难低成本复现的。这个例子让我意识到Agent的本质它不是一个函数而是一个会变通的小工。你交代的是目标和约束具体路径它自己走。这件事放在普通程序里意味着你要为每个意外情况写分支判断而在Agent里你只需要给它足够的工具和清晰的边界。1.2 试探过但最终放弃的场景当然也有试过之后果断放弃的。比如个人用Agent去做期货交易我确实试过一阵子。Agent能完成行情分析、新闻情绪判断、策略回测这些步骤但最终下单的那一步我始终不敢全权交给它。原因不是技术上做不到而是Agent的推理链路一旦出现幻觉在金融场景里代价是真实的钱。后来我把Agent定位成分析助手只负责输出决策参考下单必须走人工确认。还有小红书自动发消息这个方向也值得说一句。很多教程教你用Agent自动刷屏发帖、发私信我的建议是别搞。平台风控很严格用明显的自动化脚本去发消息账号被封的概率极高而且这种行为对社区生态也不友好。要做内容自动化Agent更适合做内容生产的中台——它负责写选题、出草稿、做图文素材发布动作还是人工来做效率和安全性都能兼顾。1.3 我给Agent画的能力边界清单经历了正面和反面试探之后我给Agent的定位总结成一句话适合用在判断路径执行操作结果评估这个循环里不适合用在一步都不能错且有明确最优解的场景。我做一个简单的分类适合信息收集与摘要、多步骤运维操作、跨系统数据整理、内容初稿生成、代码库改造与重构、客户工单分类与初步回复。谨慎涉及真实资金操作、涉及账号权限变更、对外发布内容、医疗或法律等强责任领域。不适合低延迟高并发的原子操作、对数字精度要求极高的计算、完全无人工监督的长期无人值守任务。这个表在我后来做架构设计时非常有用。很多团队一上来就想让Agent接管一切事实证明先把边界画清楚才是项目能活下去的前提。后面所有架构选择本质上都是在为这条边界寻找最合适的实现方式。2. 架构选型LangGraph、Spring AI、Rust我为什么做了这个选择2.1 三条主路线的底层差异真正开始搭建Agent项目时你会遇到一个绕不开的问题用哪种框架现在主流路线大致分三类。第一类是偏重流程编排的框架典型代表是LangChain和LangGraph。LangGraph基于图状态机把Agent的思考过程拆成节点和边每个节点可以是LLM调用、工具调用或者普通函数。它的核心价值是可控性你可以在任意节点插入拦截、校验、人工确认。第二类是Java生态的Spring AI。如果你所在团队全是Java技术栈它会比LangChain更顺滑可以和Spring Boot、Spring Cloud无缝集成。但它的Agent编排能力还比较年轻灵活性不如LangGraph适合业务逻辑固定、Agent只是其中一环的场景。第三类是Rust生态像近年出现的一些Rust Agent框架。Rust的优势是性能和内存安全单节点吞吐量可以做得非常高。不过坦白说Rust做Agent目前最大的问题是生态不成熟常见的工具链、模型SDK、可观测组件都还在早期。如果没有很强的理由我不建议一上来就用Rust硬啃。我画一个简单的对比表路线优势劣势适合场景LangGraph / LangChain生态成熟、编排灵活、社区资料多抽象层级多、版本变动快大多数通用Agent项目Spring AI与Java技术栈无缝集成Agent能力相对年轻企业内部Java系统增强Rust框架性能好、资源占用低生态早期、资料少探索性项目、高吞吐网关其实还有一个隐藏选项手写状态机。如果你的Agent只有两三个步骤完全不必要引入框架。我用一个计数器做过统计当Agent的决策路径超过5个分支时手写状态机的代码复杂度会急剧上升这时候再上LangGraph就值了。2.2 我最终使用的组合FastAPI LangChain LangGraph我的最终选择是 FastAPI LangChain LangGraph 这个组合理由很实际。FastAPI负责把Agent暴露成HTTP服务它天然支持异步配合Python的asyncio能让并发请求在等待模型API时不阻塞线程。LangChain提供模型封装、工具调用、向量存储这些基础能力。LangGraph负责画状态图定义Agent的流程骨架。为什么不直接用LangChain的AgentExecutor我在早期版本用过它确实能跑但一旦流程复杂这种老式的Agent循环会变得不可控——你不知道模型下一步会调用什么工具也无法精确插入一个人工确认步骤。而LangGraph里每个节点都是显式的Agent在节点之间根据条件跳转这让我终于能回答老板的经典问题它现在跑到哪一步了还有一点是关于部署的。FastAPI的服务可以直接打包成Docker镜像和LangGraph的StateGraph对象天然兼容整个链路没有多余的序列化开销。你要是选了某些偏门框架光是在服务边界把Agent状态转换成可传输的JSON就够折腾一阵子。2.3 什么情况下值得换别的这个组合也不是银弹。如果你只是做一个简单的知识库问答工具调用应用LangChain的LCEL反而更轻不需要引入LangGraph这么重的图状态。如果你们的Agent要承载大量Java业务系统调用比如对接一堆内部Spring服务我会建议认真考虑Spring AI。至少它的类型系统和事务语义在Java环境里更自然团队维护起来也顺手。如果是做中间层网关比如要为一个Agent集群做路由和限流Rust的价值就体现出来了。我见过有人用Rust写了一个高性能转发网关LLM调用本身还是交给Python服务去跑这个思路是很聪明的——让Rust做它擅长的事而不是让Agent全程Rust化。这里的选择逻辑很简单框架的复杂度要匹配你项目的复杂度别为了追新技术硬上不合适的架构。3. 状态图设计把Agent从失控实习生变成可控工人3.1 用节点和边而不是堆Prompt很多第一次做Agent的朋友会把所有逻辑塞进一个巨大的Prompt期望大模型开悟。我一开始也这么干过结果很惨——Prompt越长模型越容易忘记指令越容易输出与预期无关的内容。改用LangGraph之后我学到的第一个原则是不要用Prompt控制流程用图控制流程。比如一个需求分析Agent我把它拆成接收需求 - 任务拆解 - 检索相关资料 - 编写草稿 - 自检与修订 - 输出。每一步是一个节点每个节点的Prompt只负责这一件事。这样做还有个意外好处调试效率大幅提升。当输出不对时能立刻定位是哪一步出了问题而不是对着一个几千字的Prompt反复猜。有一次Agent在任务拆解环节输出了错误的分支我只看节点日志就找到了原因——不是大模型智商问题而是我传给它的任务描述里有几个字段没带全。3.2 条件分支、人工审批、重试节点状态图设计里我认为最关键的是三个技巧。第一个技巧条件分支要尽量用代码判断而不是让模型判断。比如结果是否包含错误这类判断完全可以写一个Python函数检查输出结构命中就给模型反馈不命中就走另一个分支。模型的判断只能用在真正模糊的地方比如这段总结是否足够精炼。用代码做确定性判断用模型做模糊判断这是Agent稳定性差距的一个根源。第二个技巧人工审批要设计成独立的节点。涉及对外发送消息、删除数据、修改权限等危险操作流程中一定要有一个Human-in-the-loop节点。它不是简单的暂停而是把当前Agent的状态序列化后挂起等待人工返回审批结果再决定继续或回滚。第三个技巧重试不能简单地在节点外面包一层try-except。更实用的做法是把错误信息回填给模型让模型基于错误反馈调整。比如一个工具调用返回了超时你可以让Agent重新换一种工具或重试策略而不是死磕同一个请求。3.3 状态传递的三个细节LangGraph里的状态本质上是一个共享的字典节点之间通过它传递数据。我踩过的坑主要有三个。第一个坑是状态膨胀。每当模型决定调用工具工具结果会累积在状态里几个轮次后状态可能膨胀到数万token。我的解决办法是引入工作记忆清理节点当对话轮次超过一定数量自动把早期的中间过程压缩成摘要只保留关键结论。这个清理节点本身也是一个LLM调用但一次成本能省掉后面每一轮的token开销非常划算。第二个坑是状态字段命名混乱。多个节点读写同一个字典时稍不注意就会把已经覆盖的字段当成新字段重新赋一遍。我后来给每个状态字段加了前缀规范比如user_input_、tool_result_、final_output_这个小小约定大大降低了调试成本。第三个坑是并行节点的数据合并。LangGraph支持并行执行如果你有个节点同时调用了两个工具两个结果需要正确合并。默认的合并逻辑是简单的字段覆盖一定要自定义reduce函数比如用append模式收集列表字段否则并行结果会互相覆盖。这三个细节都属于文档里不会写、但生产环境一定遇到的老问题。4. 任务拆解与提示词治理稳定性的七成秘密藏在哪4.1 五段式任务包我在项目里总结了一套五段式任务包写法对提示词治理特别有效。每个节点收到的指令都按这个模板组织角色与目标一句话说清你是谁、你要产出什么。边界条件明确你不能做什么、遇到什么情况必须停下。输入数据给出当前节点可用的具体数据而不是让模型脑补。输出格式用JSON Schema或代码块模板规定输出。自检要求让模型在输出前进行内部检查。举个真实例子一个摘要节点的提示词可以这样写你是一个技术资讯摘要员。你的任务是把给定的新闻文本压缩成100字以内的中文摘要。 边界只处理输入文本不引入外部知识如果文本内容是英文翻译后再压缩。 输入{text} 输出{summary: ...} 自检输出前确认摘要没有丢失关键数据点、参数和结论。有人可能会觉得这样写提示词太机械但我试过对比加了边界条件和自检要求之后模型的有效输出率从约七成上升到接近九成。大模型天生擅长自由发挥你给它的框越多它反而越稳定。这就像给新同事一份任务清单和验收标准他干活的质量一定比一句你看着办高得多。4.2 工具函数要薄Agent的决定要厚这是我从一次事故里领悟的。当时我让Agent自己去数据库执行多步查询Agent自己选择查询条件结果它生成了一条错误SQL导致批量更新了不该更新的数据。还好是测试环境没有酿成大祸但教训很深。事后我复盘出两个原则。第一工具函数要薄。所谓薄就是每个工具只做单一且明确的动作。不要把查询数据库并分析结果做成一个工具要拆成执行SQL查询读取表结构对比数据生成分析报告四个工具。Agent只需要决定调用顺序和参数具体执行由你写的可靠代码完成。第二危险动作不要暴露给Agent。能用白名单解决的不要让模型产生自由参数。比如更新数据库的工具根本不应该出现在这个Agent的工具列表里或者必须设置为手动审批模式。我的教训是任何模型自主调用的工具都假定它会被误用然后问自己一句能承受吗。4.3 结构化输出比提示词强制更管用最后一个重要经验是与其在提示词里反复强调请按JSON格式输出不如直接强制工具返回结构化数据。在LangChain里最可靠的方式是用Pydantic定义输出模型让模型通过function calling机制输出。模型返回的不是自然语言而是一个符合Schema的JSON对象。这个方法几乎没有输出格式错误的问题。如果模型API不支持function calling也可以用输出双通道方案让模型先输出一段文本决策再用一个轻量级的解析函数提取字段解析失败就把错误信息回传重新生成一次。这种约束校验重试的闭环和后面要说的重试策略是配套的。结构化输出还有一个隐藏优势下游逻辑可以直接消费数据不需要写一大堆正则表达式去猜模型输出的格式。我早期一个项目有六成代码都在处理模型输出格式不固定的问题改成结构化输出后这部分代码直接砍掉了。5. 并发、成本与稳定性Agent在生产环境活下去的底线5.1 并发队列化与令牌桶限流AI Agent怎么扛并发是大家在技术社区问得最多的问题。我的答案可能反直觉不要让模型API直接承受你的并发要在Agent服务前面做排队。如果用FastAPI直接开异步接口每个请求会并行等待模型返回看起来是并发的但模型API有速率限制一旦超过就返回429错误。更糟糕的是Agent内部还有多轮工具调用一轮Agent任务往往等于几次甚至十几次模型请求。如果同时有100个用户发起Agent任务实际模型的调用压力可能是1000次必炸。我的做法是引入队列系统比如Redis或RabbitMQ。Agent任务全部进入队列由Worker按固定速率消费。单个Worker的并发数控制在模型API限流的一半甚至三分之一留出余量。配合令牌桶限流保证客户端速率平稳不会出现瞬时尖峰。如果一个任务内部还有多个并行子任务比如同时抓取多个网页可以用LangGraph的并行节点去并发执行工具调用但必须给并行度设置上限。我习惯把并行度限制在5以内因为模型API的限流是总量级的子任务并行反而会更快撞到限制。5.2 每次请求的令牌预算怎么算成本失控是这个领域的经典问题。一个看起来简单的Agent任务因为多轮推理和未收敛的循环最终消耗的token数可能是预期值的十倍。我在生产环境见过一个小伙伴的调试脚本一天内触发了超千次Agent调用账单数字非常吓人。我给自己定了一个简单的预算公式每次任务在启动时根据任务类型预设一个最大模型调用次数和最大token上限。比如摘要汇总类任务预设5次模型调用、12000 token深度分析类任务预设10次模型调用、30000 token。每完成一次调用就扣减预算预算耗尽时强制终止Agent并返回预算不足的结果。除了单任务预算还需要给每个用户设置每日配额。这个配额可以先粗后细第一版设置一个安全值跑起来过一周拿到真实消耗数据再慢慢调整。注意配额超过的请求不要简单报错最好进入冷却队列或自动降级让用户体验好一点。5.3 超时、重试与死循环防护模型调用或工具调用都有可能卡死所以超时设置必须显式配置。很多主流API的SDK默认超时都很保守我一般设置为模型响应15秒、工具调用30秒超过就中止并记录原因。重试策略上遇到429或5xx要区分处理429说明撞到限流要指数退避再加随机抖动5xx说明服务端有问题可以快速重试一两次不行就放弃本次任务并写入错误日志。最容易被忽略的是Agent死循环。模型可能因为某个负反馈在同一个工具调用上反复打转或者两个节点之间来回跳。我的防护手段有三个一是全局限定最大轮次比如LangGraph的recursion_limit设为25二是给每个工具分配调用Id检测到同一节点在短时间内重复调用同一工具超过3次就强制中断三是在工作流里加一个循环检测节点比对最近几次工具调用的哈希如果完全重复就跳到错误分支。这些细节看起来很繁琐但生产环境的稳定性就是由一个一个细节堆出来的。6. 部署与可观测性让Agent在线上下地干活6.1 一个直白的部署形态我用的技术栈部署形态比较直接。FastAPI服务接收HTTP请求后丢进队列Worker进程从队列里取任务跑LangGraph流程。模型API是外部服务工具调用则打到企业内部API或公开数据源。部署上的建议是不要把Agent服务塞进已有的单体应用里拆出一个独立的Agent Service。原因很简单Agent任务处理时间长、资源消耗高拆开后你能独立调整Worker数量出问题也方便单独降级重启。GPU要不要取决于你用开源模型还是商用API。我主力用的是商用API服务本身只需要CPU和内存不需要GPU。如果你用开源模型自己部署推理那需要单独的推理服务Agent服务连接模型的OpenAI兼容接口即可。还有一个容易被忽略的点Agent任务的执行时间可能长达几分钟HTTP层要设计好超时和回调机制。不要指望客户端一直同步等着我一般做成提交任务 - 返回任务Id - 客户端轮询或Webhook通知的模式。这个改动虽然简单但能避开大量超时问题。6.2 给Agent加驾驶记录仪Agent的可观测性和普通Web服务不太一样。普通服务有PV、延迟、错误率就够但Agent还需要看到它当时在想什么。我建议在LangGraph的每个节点前后做事件埋点记录节点名称、进入时间、离开时间、耗时该节点发送给模型的Prompt关键部分模型返回的原始输出Agent在此节点做出的决策比如调用了哪个工具、选择了哪个分支状态字典的快照去掉大字段只存键名和大小。有了这份日志当Agent做出一个离谱行为时你能回放它是如何一步步走到那一步的。我还会配合做一个简单的轨迹面板页面展示每次任务的生命周期、当前节点、已用轮次、token消耗。上线第一周我几乎每天都在翻这些日志翻完才敢说我知道它在干嘛。6.3 降级开关与人工兜底最后一个部署经验是给Agent设降级开关。开关分三级L3全自动正常流程Agent自主决策并执行。L2半自动Agent只做分析和建议所有执行动作都需要人工确认。L1停用直接把相关接口切回人工流程Agent不可用。我在代码里用一个配置中心存储当前级别开关切换实时生效不需要重启服务。比如某个晚上模型API整体故障我可以一键从L3降到L1接口立刻回到最基础的兜底逻辑。做Agent项目的人必须习惯一个心态发生事故时最快恢复系统的方式不是修复模型而是绕开模型。另外一个和降级配套的做法是影子模式。新版本Agent先在预发环境跑一段时间模拟线上流量但真实执行观察输出质量后再切换正式环境。这类似传统的暗发布对Agent特别有价值因为模型升级、提示词调整、工具变更都会引起行为漂移只靠离线测试很难发现。7. 踩坑实录与掏心窝的建议7.1 七个最常见的坑我把我踩过的坑整理成一张表方便你对照排查坑表现解法状态膨胀长时间任务后提示词被截断引入工作记忆压缩节点工具调用循环同一个工具反复触发重复调用检测轮次上限提示词遗忘长Prompt后模型行为失焦分解节点让Prompt变短并行合并丢失并行节点结果只有一个生效自定义状态reduce函数模型判断不可靠条件分支选错用代码判断优先替代模型判断成本失控单个任务token超预期数倍预设预算并强制扣减日志不全线上问题无法复现每个节点前后埋点这张表里每一条我都在生产环境真实遇到过不是凭空猜的。尤其是并行合并丢失这个坑表面上看是LangGraph的配置问题实际上是我对状态合并语义理解不够造成的。我会在每次进入新任务前拿着这张表过一遍能省掉很多不必要的返工。7.2 低成本提升稳定性的几个小技巧分享几个成本很低但很有效的小技巧。第一给Agent注入当前时间。模型对时间判断经常不准你可以在任务开始时把当前时间写入状态并在相关节点明确告知当前时间是XXXX年XX月XX日。这个小小的信息能避免很多因为时间认知错误导致的任务失败。第二用系统助手节点汇总信息。当Agent在多轮中收集了大量零散信息可以加一个节点专门负责把这些信息整合成结构化摘要这比让每一步节点都携带全量上下文高效。它在中间阶段把事实和噪声分离后续节点读到的都是精炼后的信息。第三给模型提供放弃选项。在提示词里明确写如果任务无法完成或者条件不满足可以直接返回失败原因不要硬编。这个小小的许可能大幅减少模型胡编乱造。人干活都知道不会可以说不会模型其实也需要这种授权。第四对关键数字做二次校验。如果任务里涉及数量、日期、金额等信息让一个独立节点用另一种方式再算一次比如用Python写的规范化校验规则或者交叉验证两个来源的数据。模型擅长的是语义理解不是精确计算关键数据点必须有程序兜底。7.3 给新手的路线建议如果你现在正准备学AI Agent开发我建议的路线是这样的。第一步先用LangChain的LCEL做一个简单的知识库问答工具调用项目把模型封装、检索、工具调用的基本套路跑通。这一步先不要碰LangGraph先把基础组件熟悉了。第二步引入LangGraph把同一个项目改造成状态图版本。这个阶段重点理解节点、边、状态、条件分支这几个概念尝试把之前那个项目的流程改造成有分支有重试的结构。第三步把项目部署成FastAPI服务加队列和限流理解生产环境与本地运行的差距。你会发现在本地好好的任务上了生产就会出现超时、限流、状态丢失这些问题处理这些问题的过程才是真正的成长。第四步补可观测性和成本控制尝试把事件埋点和预算机制加上。这一步做完你手里的Agent才勉强算得上产品而不是脚本。第五步等你对架构有了手感再去研究Rust框架、Spring AI这些不同技术栈的实现思路。技术栈拓展的顺序应该是先把一个项目跑到生产再横向扩展而不是一上来就收集一堆框架知识结果哪个都没用深。我个人始终认为AI Agent项目能不能活下去七分在工程三分在模型。模型能力会持续变强但任务拆解、状态设计、并发治理、可观测性这些工程基本功不会因为模型升级就消失。如果你正准备把Agent从Demo推向生产希望上面这些经验能帮你少踩几个坑把更多时间留给真正有意思的部分。