
上个月我给一个客户演示 Agent 客服系统现场效果近乎完美用户问天气、查订单、改地址、申请退款Agent 一路顺畅主持人随机刁钻提问都能接住。客户当场拍板推进度。两周后上线灰度第一天就翻车——Agent 把两笔订单的地址搞混了还对着一个投诉用户重复道歉了三次。客户没好意思说重话但那个眼神我读懂了这玩意怎么一上生产就稀碎这个场景我相信很多做 Agent 项目的同行都不陌生。模型还是同一个模型为什么 Demo 里光芒四射生产环境里就原形毕露我的答案很直接锅不在模型在工程。LLM 的能力已经足够撑起绝大多数业务场景真正让 Agent 项目“上线即翻车”的是我们把演示当成了交付把单条路径当成了系统把“能跑通”当成了“能扛住”。这篇文章我把自己踩过的坑、拆过的问题、最后沉淀下来的工程改造方法完整梳理一遍。核心思路是Demo 考验的是模型的“上限”生产考验的是系统的“下限”。你要是不服这个归因看完下面的分析再来辩。1. 先把话说清楚Demo 惊艳到底惊艳在哪在甩锅给工程之前我得先承认一个事实Demo 阶段的 Agent 确实看着很棒这种“棒”不是错觉但它的形成条件很特殊。大多数惊艳的演示本质上是在三个“温室条件”下完成的而生产环境恰好把这三个条件全部拿掉了。**第一个温室条件是输入被精选过。**我们演示时用的用户问题不管显得多随机其实都是经过筛选的有明确意图、没有歧义、信息完整。但生产环境的用户输入是长尾分布一句话可能带着错别字、口语碎片、情绪化表达甚至是一段粘贴错误的乱码。我在一个项目中统计过真实用户问题里大约有三成到四成属于“低质量输入”这类输入在演示时根本不会出现。**第二个温室条件是执行路径单一。**演示脚本通常是一条主路径走到底识别意图、调一个工具、拿到结果、回复用户。但实际业务里一次用户请求往往需要串联多个工具中间任何一步都可能返回异常。更麻烦的是多步操作之间存在依赖关系A 步骤的结果决定了 B 步骤的参数B 步骤的参数又决定了 C 步骤的走向只要中间有一个环节理解偏差后面全跟着错。**第三个温室条件是现场有人肉兜底。**演示时讲解者始终在场一旦 Agent 跑偏人可以用话术圆回来或者干脆悄悄切到备选方案。但上线之后系统面对的是真实用户没有人能实时干预每一步决策。那种“90% 时间表现优秀10% 时间需要人工介入”的模式在演示时是成功的表演在生产环境就是事故率。我习惯用一个类比来向同事解释这件事Demo 像概念车——展示的是设计上限讲究的是惊艳全场生产像量产车——考验的是可靠下限讲究的是十万公里不趴窝。概念车永远不会堵车量产车要面对早晚高峰、暴雨天气和野蛮驾驶。Agent 项目翻车不是因为引擎不行而是我们直接拿概念车上了量产线。2. 断裂点一上下文与状态管理——Agent“失忆”的根源我拆过的最典型的线上事故就是 Agent 在对话中“失忆”。用户先问了订单配送范围Agent 回答可以送到某个区域用户紧接着说“那帮我改到这个地址”Agent 却反问“请问您要改哪个订单的哪个地址”——明明上一轮已经明确过订单号和地址了。这类问题在演示中极少出现因为演示脚本里的对话通常不超过三轮上下文在一个 prompt 里塞得下。但生产环境的真实对话动不动就是十几轮。问题就出在Agent 的“记忆”本质上是把聊天记录塞进 prompt 里而 prompt 有长度上限。拿一个我经手过的客服项目举例系统用的模型上下文窗口是 8K token一开始我们把全部聊天记录都塞进去。对话超过五轮后历史记录就开始截断——最先被挤掉的是系统设定的业务规则和工具说明因为它们在 token 排列中往往靠前。结果就是Agent 越往后越“笨”前期承诺过的事情全忘了甚至开始用错误的规则作答。更隐蔽的问题在于状态散落。订单状态、用户地址、退款进度这些信息散落在对话历史的不同位置。Agent 要推理出“当前用户处于什么阶段”等于要在长文本里做实体抽取和关系推理这远超它在单轮短对话中的可靠区间。我见过一个案例Agent 把 A 订单的收货地址填到了 B 订单上原因就是它在历史消息里抽取“地址”时定位错了对象。工程上怎么解核心思路是“把上下文从 prompt 里搬出去把状态做显式化”。我实践下来最有用的做法是引入显式状态管理会话开始时初始化一个状态对象里面存好用户 ID、当前步骤、已确认信息、待办动作每一步 Agent 决策之前先把状态对象的结构化字段注入 prompt而不是塞全部历史。例如用户输入 → 解析意图 → 更新状态对象 → 生成回复 → 状态对象持久化这样无论对话多少轮prompt 里的关键信息永远是完整的系统规则不会被截断当前状态清晰可读历史记录只在必要时才作为参考材料引入。我在项目中把字段设计成类似下面的结构{ session_id: xxx, current_step: collecting_address, confirmed_info: { order_id: A123, delivery_address: 上海市浦东新区… }, pending_action: update_delivery_address, history_count: 12 }对话超过一定轮数后用阶段性总结替代原始历史每五轮生成一条结构化摘要比如“用户已确认订单 A123 的地址变更等待执行”。这样 token 占用稳定信息不丢。这里有个实操细节容易被忽略状态对象的 schema 设计要和业务方一起定不要自己拍脑袋写。我在某个项目里把状态字段写成了英文驼峰命名业务方看不懂后面联调时来回折腾了好几轮。后来统一改成中文字段名 英文标识符双轨问题才消停。这不算技术难题但确实是工程协作里常见的内耗点。3. 断裂点二可观测性缺失——线上问题根本无从查起Agent 项目有一个和传统后端完全不同的调试难题**它是非确定性的。**同样的输入两次运行可能给出不同结果因为大模型采样过程本身带随机性。这意味着什么意味着出问题时你没法靠“复现”来定位你必须完整记录每一次决策的轨迹。我第一次负责一个 Agent 项目的线上排查时承接过一个让人崩溃的工单用户反馈说 Agent 承诺“24 小时内退款”但三天过去了退款没到账。我拿到线上日志一看傻眼了——日志里只有一句“调用了退款接口”参数是什么、返回什么、Agent 为什么做出这个决策、中间经历了哪些思考环节通通没有。我们对着代码猜了一天最后没办法只能让用户重新走了一遍流程才抓到有效数据。从那以后我立了个规矩Agent 项目没有高质量 trace等于没有调试能力。要建可观测性我建议至少打五类关键节点的日志节点记录内容排查时能回答的问题输入解析原始用户输入、意图识别结果、抽取出的实体意图是不是识别错了实体有没有抽漏推理决策模型完整输出含思考过程、置信度Agent 为什么选择某个工具理由是否充分工具调用工具名、入参完整 JSON、出参参数对不对下游返回了什么异常处理重试次数、错误类型、降级策略哪一步挂了有没有按预期自愈最终回复给用户看到的完整文案话术是否安全有没有幻觉内容实现层面最有效的办法是给整个请求链路贯穿一个 trace_id从用户发起请求生成到每次工具调用、每次内部循环都带上。所有日志统一输出到集中式日志平台支持按 trace_id 一键拉出全链路。这没什么高深的但就是特别管用。有一次线上用户反馈 Agent “答非所问”我拉出 trace 一看意图识别把“退货”识别成了“换货”后面所有决策全跟着错。一条日志十分钟定位换做以前至少折腾半天。有一个细节值得单独拿出来说**工具的入参和出参一定要完完整整落日志不要嫌数据量大。**尤其是大模型的工具调用经常出现参数幻觉——它可能自信地把一个订单号写成另一个格式。没有完整入参这类问题基本无解。我在项目里放过一次“低成本尝试”只记录了工具名和状态码结果参数错误类问题排查起来等于瞎猜后来乖乖把完整 JSON 都打了。另外强烈建议在开发阶段就打开一个“决策回放”页面把 trace 里的关键节点用可视化方式还原成流程图。demo 阶段这样做看似麻烦但等到上线后你会感谢这个决定——运营同事也能自己看回放定位问题不用每次都是研发扑上去查。这个投资回报率极高。4. 断裂点三错误恢复与边界处理——生产环境最花钱的环节如果你问做过生产级 Agent 的人什么最耗时十有八九回答不是模型调优而是错误处理和边界条件。模型的推理能力再强也架不住外部接口不稳定、网络延迟波动、第三方服务限流。这些在 Demo 里可以当作“不考虑”生产环境里每一个都是事故源。我处理过一起典型的连环事故过程很有代表性Agent 调用外部订单系统的接口外部服务偶发超时大约 5% 概率超时时 Agent 会重试。如果只是单次重试还好问题是 Agent 的重试逻辑没有限制——它发现超时后会“认为”操作未成功于是再次调用外部服务此时刚好恢复结果导致重复提交。用户被重复扣了钱追责追到我们头上。这里面暴露了两个问题重试策略缺失、幂等性设计缺失。工程上必须给所有工具调用加上明确的超时和重试规范超时设置外部 API 连接超时 3 秒读超时 5 秒超过就判定失败。重试策略最多重试 2 次退避策略用指数退避 随机抖动比如 1s、2s、4s 基础上加随机数避免同时重试造成流量尖峰。幂等机制所有写操作必须携带业务侧生成的唯一请求 ID下游根据 ID 去重。这招能直接避免“重复扣款”这类最严重的后果。兜底降级重试仍失败后Agent 必须停止自动操作转入人工处理流程或明确告知用户稍后重试绝不“硬编”一个假成功。还有一个常被忽视的点**并发控制。**用过 Agent 的人都知道模型可以同时发起多个工具调用并行执行。这看起来效率高但如果下游接口只支持每秒 5 个请求Agent 一次并发开了 10 个直接把对方的接口打爆。工程上的解法是给每个工具定义速率限制用信号量控制最大并发数超额请求排队等待。我给出的初始参数是单工具并发上限 3给模型加一条指令说明“并行调用时不得超过 3 个工具”实测稳定后视情况调整。关键操作的人机协同也是生产环境必须做的一步。我的原则是凡是涉及资金、隐私、对外承诺的操作必须加人工确认环节。Agent 可以把流程做到“最后一公里”但按下确认键的是人。这不是能力不够是风险兜底。我们项目上线初期设定了 25% 的关键操作需要人工审批跑了一个月稳定后降到 10%再往后根据数据调。这张“人工兜底网”虽然牺牲了一点效率但换来了零重大事故的交付记录值。5. 从 Demo 到生产的工程改造实操清单前面讲了原理和断裂点这章给你一份可以直接照做的改造清单。我每次接手新的 Agent 项目都会按这条路线推进顺序很重要别跳过。5.1 先把评估集建起来再谈优化很多团队评估 Agent 靠“拍脑袋测两个例子”这是上线翻车的第一大原因。工程改造第一步应该是建立一个可复用的评估集。我现在建评估集的最低标准是不少于 50 条真实场景问题覆盖以下类型主流程正常提问15 条左右边界条件信息缺失、地址不详、数量异常多轮对话延续要求 Agent 记住上文信息情绪化表达用户生气、催促、质疑工具异常场景外部接口不可用时 Agent 的应对每条用例要有标准答案或关键评判点。比如“用户要求修改地址但未提供新地址”预期行为是主动询问而不是乱猜。评估集的用例来源必须是真实用户数据脱敏后的结果不要自己编——自己编的用例永远是你以为的痛点不是真实的痛点。建立评估集之后每次工程改动都跑一遍回归对比回答质量、工具调用正确率、回复合规率几个指标。我们项目定了一个量化标准主流程正确率不低于 90%边界条件不低于 70%才允许进灰度。5.2 把上下文改成显式状态机这是第二优先级的改造。前期 Demo 版本往往是“把所有历史塞进 prompt”上线前必须换掉。具体做法梳理业务流程的状态流转图定义每个状态下的可用动作和切换条件。以订单售后场景为例至少有这些状态待确认问题类型收集订单信息中确认退款方案中等待用户确认退款执行中完成 / 失败每轮对话先做状态判定再生成回复。状态的迁移必须走代码逻辑不允许模型自己“自由发挥”。我在实践中的体会是状态机越严格Agent 越容易被约束在安全范围内整体可靠性显著提升代价是灵活性下降但生产环境里这个代价值得付。5.3 给工具调用上协议让大模型调用工具是 Agent 的核心能力但裸调用等于裸奔。所有工具调用必须有规范参数格式用 JSON Schema 校验模型输出不符合格式时不要直接传给下游先做格式纠偏。工具返回要有标准错误码Agent 能据此区分“参数错误”“服务不可用”“限流中”等不同失败原因。重试、超时、幂等策略统一封装成调用层不在 prompt 里用“请你重试一下”这种软性控制。我见过太多团队把重试逻辑写进 prompt比如“如果失败请重试”这完全不可控——模型可能重试三次也可能重试十次还可能因为觉得“重试没用”而放弃。把重试逻辑代码化是生产级 Agent 和玩具级 Agent 的明显分界线。5.4 把可观测性接到位第四步是日志与 trace 建设。最低要求是每次完整的 Agent 执行周期必须留下包含 trace_id 的结构化日志至少覆盖本章前面列出的五类节点。实操顺序先定义一个统一的日志格式规范字段包括时间戳、trace_id、节点类型、业务字段、耗时。在 Agent 框架的钩子函数里埋点如果有框架的话没有框架就手动在每个关键调用处打日志。接日志采集支持按 trace_id 检索。最好做一个简易的可视化回放页面这一步能极大提升定位问题的效率。5.5 上保护与治理第五步是给系统套“安全网”包括限流、降级、熔断、人工复核四件事限流token 级限流和接口级限流都要做防止用户刷爆你的模型预算。降级模型服务不可用时自动切换备选模型或者退化为纯规则回复。熔断连续错误率达到阈值比如 20%时自动熔断某个工具调用避免引爆下游。人工复核关键操作人工确认非关键操作抽样审查。5.6 灰度发布别一把梭最后一步是发布策略。很多人上线 Agent 系统跟上线普通 Web 应用一样直接全量这是最危险的。我给的建议是先内部团队试用一周再开放给 5% 的外部用户跑两周期间盯紧 trace 日志和用户反馈稳定后逐步放量 20%、50%、100%。灰度期间重点关注三件事一是用户实际输入和评估集的偏差大不大及时补充评估用例二是工具调用失败率的基线数据三是“Agent 给出错误但自信回复”的比例这类问题在人工复盘时最容易暴露一旦发现要及时加约束规则。6. 常见问题排查实录与我的避坑心得最后分享几个实际排查过的典型案例附带我的处理思路希望能帮你少走弯路。**案例一Agent 突然开始用“残缺句式”回复用户。**某项目上线第三天Agent 开始出现半截话回复例如用户问“我的包裹到哪了”Agent 回“您的包裹正在”。第一反应是模型抽风加大 prompt 约束后仍偶发。最终通过 trace 定位到根因上下文过长导致系统自动截断模型生成到一半时上下文窗口溢出输出被强制截断。解法是把所有历史记录改为摘要式存储控制 prompt 长度稳定在 5K token 以内。这个问题后面再没出现过。**案例二工具调用参数格式不稳定导致下游系统报错。**Agent 调用查单接口时偶尔会把订单号带上前导空格或者把参数名从order_id漂移成orderId。排查发现是模型在长对话中受历史消息影响格式发生了“漂移”。解法是两层一是在代码层做参数清洗和格式校验不依赖模型的自觉二是在工具描述里加上“参数名必须严格使用order_id禁止使用任何变体”的强约束。**案例三Agent 在用户情绪激动时提供了不符合规范的赔付承诺。**这本质上不是 bug而是业务规则没有被显式约束。我们在 prompt 里加了“赔付规则由系统工具返回为准不得自行承诺额外金额”并在工具侧增加了规则查询能力。这也是我在所有项目中坚定不移推动“规则外置”的原因——把业务规则放进工具调用比放进 prompt 可靠得多。**案例四并发高峰期外部接口被限流Agent 连环失败。**这个前文已经说过根本原因是没有并发控制和重试退避。修复方案是加了信号量并发限制和指数退避重试故障率从 15% 降到 0.5% 以下。排查经验汇总成一张速查表方便你遇到问题时对照故障现象优先怀疑点处理建议对话久了“变笨”上下文截断检查 token 用量做状态外置和摘要化工具参数偶尔错模型格式漂移代码层强校验工具描述强约束重复扣款/重复操作缺乏幂等机制引入请求 ID全链路幂等高峰时段连环失败并发控制缺失加信号量限流指数退避重试无法定位问题原因没有 trace补全链路日志按 trace_id 检索承诺超出规则规则在 prompt 里规则外置到工具调用代码层兜底最后分享一个我个人的踩坑体会接手 Agent 项目之后我最大的变化是心态上的——永远默认“模型会犯错”然后围绕这个前提设计系统。以前我做传统后端默认代码是确定性的逻辑被验证过就可以依赖。但 Agent 不是它是概率系统我们要做的不是祈祷它不犯错而是把错误的影响限制在可控范围。这个转变听起来是心态问题落到行动上就是工程决策的全面变化日志怎么打、状态怎么存、重试怎么做、人工兜底放在哪里。我始终觉得Agent 项目的核心壁垒不是“谁的模型调得好”而是“谁的工程能兜住模型的随机性”。Demo 阶段比的确实是模型上限但生产阶段拼的是工程下限。谁的下限高谁才能在真实世界里站稳。这篇分享如果能让你的 Agent 项目在上线后少踩几个坑我就觉得值了。