Agent 上线后翻车的 6 种死法:一个 20 万人规模项目的四版重构实录

0 阅读16分钟

Agent 上线后翻车的 6 种死法:一个 20 万人规模项目的四版重构实录

我们做了一个面向终端用户的业务 Agent,服务 20 万+ 用户,日均对话 50 万+。
前后重构四版,干掉约 90% 的幻觉,P95 从 15s+ 压到 10s 以内。
这篇文章不吹成绩,只讲每一版是怎么把自己坑死的,以及坑死之后想明白了什么。


0. 先说结论:四版没有一版是设计出来的

如果你正在做 Agent 落地,先记住这句话:四版没有一版是"设计"出来的,全是被上一版暴露的问题推着走的。

我们从头到尾都在解同一组矛盾:

  • 幻觉——模型胡说
  • token 成本——上下文越塞越多
  • 响应延迟——推理轮次越多越慢
  • 可运维性——改一处,全业务重新评审

这四个是互相拉扯的:要少幻觉就多约束多召回,token 涨、延迟涨;要快就少推理,幻觉涨;要好维护就得拆 prompt,拆开之后各段又互相打架。

判断一个 Agent 团队成不成熟,就看他能不能说清自己这一版的矛盾在哪。说不出来的,基本还停在第一版。

图 1 · 四版演进:每一版都是被上一版推着走的

图 1 · 四版演进:每一版都是被上一版推着走的


死法一:强依赖意图识别 —— 每接一个业务,算法就要训一次

起点其实很小

2024 年 11 月,V1 上线。

起因特别朴素:我们拉了每日客服进线的 top10 问题,发现里面有大量是"我这个单为什么被判异常"。用户不知道判定依据,反复进线问。

初衷只是把我自己的重复工作做成 SOP 给用户——不是要做通用智能助手。

架构

前置一个意图识别层,根据静态意图分发到对应的工具或 subagent。

用户 query → 意图识别 → 分发 → 工具/subagent → 回复

它做对了一件事

跑通了 Agent 前后端的 SSE 链路。

这个价值被严重低估。流式链路是整个体验的地基——用户在等待的时候能看到字一个个出来,而不是盯着转圈 15 秒。很多团队到最后才补这块,结果整条链路的超时预算全要重算。

它死在三件事上

  1. 没有多轮能力——问第二句就接不上
  2. 强依赖意图——意图判错,后面全错,而且错得非常自信
  3. 每次业务接入,算法都要重新训练参数——交付周期以周计

第三点是致命的。这意味着每来一个新场景,都要排算法排期、标数据、训模型、评效果。业务方等不起,我们也撑不住。

为什么后来不用传统分类模型

因为 V1 就是那么做的。

分类模型的问题不是不准,是它只能回答"这是哪一类",不能回答"这个单具体指哪一个" ——"这个单"、"上一条"、"刚才说的那个",它完全接不住。而且每加一个场景,算法就要重新训一次。

它唯一的好处是能按意图输出观测指标,利于业务收集收益数据。换句话说,分类模型给你的是可观测性,不是能力。


死法二:全量塞给大模型 —— token 从 3200 涨到 1.2 万

为什么要重构

2025 年,模型能力明显变强了。我们发现这个助手非常傻:已读乱回,多轮输入,回答都一样。

决定重构。核心想法:不强依赖意图识别,让大模型完成整个推理和决策,并支持多轮。

架构:看起来非常标准

网关层    → 鉴权、安全验证、保存对话
上下文层  → 用户信息、工单信息、异常信息
编排层    → RAG 召回
能力层    → tool、skill
core agent→ ReAct 循环推理
模型层    → 云厂商模型服务 / 内部 agent 平台
          + 监控与评测

这个架构放在任何一篇 Agent 架构文章里都挑不出毛病。

它死在一句话上

我们把全量工具和上下文,一股脑全塞给了 core agent。

结果是必然的:

  • token 从 3200 暴增到 1.2 万
  • 幻觉不断
  • 规则越界
  • 响应超时

但最致命的不是这些

是没人敢动 prompt。

所有业务共用一个 prompt。改一块,全业务都要重新评审才能上线。

一个 prompt 变成了全公司最不敢碰的文件。改一个字,走三轮评审,拉五个业务方。到最后大家宁愿在代码里加补丁,也不愿意进 prompt——因为补丁至少不会连累别人。

根因

一句话:把「选能力」和「执行能力」混在一起了。

让一个模型既决定"用什么工具",又负责"怎么用工具完成任务",它就会在两个目标之间反复横跳。候选池越大,横跳越厉害。

图 2 · V2 的架构挑不出毛病,死于全量塞入

图 2 · V2 的架构挑不出毛病,死于全量塞入


死法三:加一层编排 —— 压力只是搬了家

做法

在 core agent 前面加一层"编排":把工具描述语料打包灌进知识库,先根据用户 query 用小模型把能力选出来,再交给 core agent 用大模型继续推理。

用户 query → 编排(小模型选能力)→ core agent(大模型推理)

结果

能力确实解耦了。core agent 拿到的工具列表干净了,prompt 也不用再塞全量工具。

但问题没消失,只是从 core agent 转移到了编排层:

  • 编排选错,后面全错(而且错得更隐蔽——core agent 会非常努力地用错工具,产出看起来很专业)
  • 编排层的语料维护成了新瓶颈

教训

把复杂度往前搬,不叫解决,只叫搬家。

那一版之后我们才想明白:真正的解法不是"谁来选工具",而是怎么让候选池足够小、让选择足够确定。


死法四(转折):分层治理,稳定优先

V4 的核心词是:分层治理、稳定优先、大规模降噪、控幻觉、控成本。

这一版没有换架构,换的是"约束放在哪一层"。

具体做法涉及不少内部设计——工具描述怎么分级、召回策略怎么配、编排该收敛到什么规模——这块是我吃饭的东西,就不逐条展开了。但思路可以讲清楚,思路比参数值钱:

  1. 同一份工具信息,不该只维护一个版本。 检索要的是"详细"才能算准相似度,选择要的是"精简"才不会把上下文撑爆。用一份语料同时满足两件事,天然是矛盾的——必须按用途拆开,并让两边能对上。
  2. 改配置不该发版。 工具的说明文字是运营资产,不是代码。它必须能热更,否则每次调描述都要排一次发布窗口,"可运维性"这一项永远是负分。
  3. 候选池要在模型推理之前就收敛。 不是因为模型选不准,是因为候选池本身就是幻觉的温床——池子越大,模型越倾向于"猜一个看起来对的"。
  4. 记忆不能原样用。 这条我们踩了很久才想明白,下面单独说。
  5. 确定的事,压根不该交给模型。 完全确定的链路直接硬编码返回;半确定的(比如从对话里提炼单号)也不走推理,但走分发能力。模型只处理真正不确定的部分。

记忆为什么不能原样用

举个具体的例子。一个异常列表,工具返回的是一个大 JSON。以前我们就把 JSON 原样存进记忆。结果三件事一起发生:

  • 模型看到 JSON 里某个数字,就把它当成了异常单号(这是"编造参数"最主要的来源)
  • token 白白烧掉
  • 指代消除变得极难——模型分不清"这个是列表里的第几条"和"这个是当前这单"

改法只有一句话:存储时不存原始结构,把里面的字段按语义拼成摘要再存。

一个动作同时解决三个问题:防幻觉、省 token、利于指代。

结果

  • 约 90% 的幻觉被干掉
  • 响应从 15s 以上降到 10s 以下

注意是"约 90%",不是"消灭"。后面我会说还剩什么。


幻觉不是一个问题,是六个

大部分人一说幻觉就加一句"不要编造"。没用的。

因为幻觉不是一个问题,是六个问题。 根因完全不同,药也不同。

#类型表现
1跳过工具该调工具,却直接拿记忆回答,直接返回
2编造参数没查到异常单,但上下文里有个数字类型,就赋值成单号(工单 ID、用户 ID)
3假成功工具调用失败却报成功——上报接口报错,它返回"已上报成功"
4跨轮污染把 A 单的诊断结论当成 B 单
5跨单污染同上,跨业务对象污染
6路由幻觉调一个根本不存在的工具

这张表值得抄下来贴墙上。因为很多人是在拿"加禁词"这一味药,去治六种完全不同的病。

图 3 · 幻觉六个入口:它们长在推理链的不同位置

图 3 · 幻觉六个入口:它们长在推理链的不同位置

治理有顺序,顺序不能反

架构层:确定性链路剥离、早退、缩小候选池

输入约束:schema 规范、参数格式约束、记忆摘要、强制工具白名单

流程管控:编排层预先收敛工具集

兜底保障:持续评测(实时采样 + 离线回放)

顺序很重要:先在架构上不让它有幻觉的机会,再用输入约束收紧口子,然后用流程管控限制选择空间,最后才用评测兜底。

很多团队是反的——一上来就靠 prompt 加禁词,治不了。禁词能压住"语气",压不住"事实"。

图 4 · 治理四层的顺序:越往下越省事,越往上越费钱

图 4 · 治理四层的顺序:越往下越省事,越往上越费钱

三个真正起作用的动作

一、让模型在上下文里见过"用工具完成任务"的样例。

我们一开始的记忆只存结果,不存工具调用,因为觉得和模型返回冗余。结果模型学会了直接用文本回答——它看到的上下文里,从来没有"靠工具完成"的先例,只有"用话回答"的先例。

这是一个很典型的坑:你训练它看见什么,它就成为什么。 该调工具的场景,记忆里必须留着工具调用的痕迹。

二、把提取规则写进参数 schema。

比如异常单号:多少位、从上下文哪里找、从历史记忆里哪里找。模型不知道你的 ID 格式,你不写清楚,它就编一个凑上去。

三、让候选工具在推理开始前就基本确定。

ReAct 阶段能用哪些工具,理想状态下在编排阶段就定了。如果推理想用的工具不在候选池里,正确处理是重试或追问,不是就地放行。


早退:降幻觉最大的一步,不是让模型变聪明

ReAct 的节奏是:模型 → 调工具 → 回模型 → 再决定。

但有些工具调完就是最终结果,不需要模型再推理。

比如把处理结果卡片推给用户——返回的结果本身就是终态,不需要再把 subagent 结果喂回模型、让它思考"该怎么回复"。

这时候直接结束:省 token,更少一次幻觉机会。

这一条和确定性剥离配合起来,是"幻觉降 90%、15s→10s"的主要来源。

不是靠模型变聪明,是靠不让模型做它不该做的事。 这句话我认为是整个 Agent 工程的纲领。


评测:怎么证明判官自己没判错

一个原则

规则能判的,不给模型;给模型的,必须有锚点、参考答案、证据、可复核。

按这个原则分三层:

层场景做法
L1规则能判的硬编码直接判,不经过模型。比如调了不该调的工具、该上报的没上报
L2需要理解语义的对照判。必须给判官锚点和工具返回作为证据,不许它自由发挥
L3判官自己也不确定的放弃单次判断,用重复采样收敛

评测一定会拟合

就是判官跟着标准答案说,分数越评越高,上线就露馅。

这是评测体系最隐蔽的失效方式——它不会报错,只会让你越来越有信心。

唯一的解法是结构性的:出评测集时按意图分域打标,减少跨域干扰;参与诊断的数据和最终验收的数据必须是两批,物理上不重叠。

如果诊断和打分用同一批数据,模型就是朝着那批数据进化,看起来越来越准,实际什么都没学到。

现状

评测里人工仍占 80% 。这是我们目前最不体面的数字,也是下一步要啃的。


自进化:方案写完了,但还没实现

我们想做的是一条闭环:日志归集 → 自动判卷 → 分层诊断 → 开方 → 人工复核 → 生成新版本 → 重新评测 → 灰度上线 → 实时流量验证。

链路细节这里不展开(它还没跑起来,写出来是空话)。但有一个判断可以先给出来:

自进化最难的不是"发现问题",是"安全地改"。

一个能自动改 prompt 的系统,如果改坏了不能快速回切,它就是负债不是资产。所以这条链的每一环都得能刹车——这是它至今没上线的真实原因。


直接改 prompt,不怕改坏吗

怕。但怕不等于不改,等于给它加约束。

前提是 prompt 必须是分段拼接的,而不是一整坨。角色、边界规则、工具描述、历史记忆、实时上下文、强制注入——每一段职责清晰,出了问题才能定位到段。

在这个前提下,几条硬规矩:

  • 一次只允许动极少数几段,且限定字数——逼你改得准,而不是改得多
  • 每次改动用 diff 记录,保留快照,随时可回退
  • 必须走灰度,必须有一键回切
  • 连续打擂失败到一定次数,强制冷却,锁一段时间不许再改,同时告警到群里

最后这条是最反直觉、也最有效的:给系统装一个"冷静期"。 人急了会连续乱改,改得越多越乱,最后没人说得清是哪一版坏的。

关键认识:不是把 prompt 当代码管,是把它当配置 + 实验管。


工具调用失败怎么办:不是异常终止

所有工具调用统一封装,捕获异常,抛自定义异常(带工具返回的错误码)。然后分两类处理:

可重试的(超时、网络波动、IO 异常):自动退避重试若干次,仍失败则返回兜底。这类重试不消耗模型资源,因为重试本身不需要模型参与。

不可重试的:作为 tool response 喂给模型,让模型自己抉择——改参数?换工具?还是兜底回复。

这才是 ReAct 的本质:失败是信息,不是终态。你要做的是把失败正确地表达给模型,而不是替它做决定,也不是让它崩掉。

subagent 异常同理:全局捕获但不抛异常,转成结构化的失败信号,主 agent 收到后自己决策是换域、追问还是兜底。

再往外还有最后一层兜底:轮次上限、思考超时上限,到点直接输出兜底话术,配监控告警和降级策略。这一层不是为了提高效果,是为了保证系统不会无限卡住。


记忆:不是存得越多越好

几个结论,不讲实现:

  • 会话内记忆必须有上限,且要有异步压缩机制。上下文不是免费资源,到阈值就必须压。
  • 跨会话只需要摘要,不需要详情。 长期记忆我们至今没做——不是做不了,是没想清楚它在客服场景里到底值多少。
  • 记忆的写入格式,比存多少重要得多。 前面说的"不存原始结构、拼成语义摘要",是这一条最具体的体现。

现在依然存在的问题(不装作解决了)

  1. RAG 召回的误触率和漏召率——召回错了,后面全对也没用
  2. 幻觉依然存在,只是比之前小很多
  3. 评测人工占比 80%
  4. 自进化尚未实现,方案写完,没跑起来

写这一段不是为了显得诚实。是因为一个说自己什么都解决了的 Agent 团队,通常只做过 demo。


下一步的方向

在试一个更"确定"的路子:不用生成式模型来做筛选判断,而是用确定性的筛选 + 打分 + 多选,只在给定候选池里输出、只判分不写答案。成本和延迟都比大模型低一个量级,而且类型安全。

用在两个地方:召回后做一次"query 与工具是否匹配"的过滤;再在收敛后的候选池里做多选。原编排模型降级为兜底,只处理低置信且复杂的 query。

效果还没验证,验证完再写一篇。我更愿意先给出判断,再给出结论——反过来就变成卖方案了。


最后:这套东西真正的方法论

回头看四版,真正的进步不是哪个技术选型,而是学会了把不确定性摆到台面上分级:

  • 完全确定的 → 硬编码,不走模型
  • 半确定的 → 走分发,不走推理
  • 不确定的 → 才给模型推理,且必须有锚点、证据、可复核

这个思路同时解释了幻觉治理、评测判卷、自进化三个看起来完全不相关的模块。

图 5 · 把不确定性摆到台面上分级

图 5 · 把不确定性摆到台面上分级

如果只让我留一句话给正在做 Agent 落地的团队:不要问"怎么让模型更聪明",要问"这件事凭什么要模型来做"。


关于细节

有人会问:工具描述到底怎么分级?召回阈值给多少?判卷怎么投票?改 prompt 的字数限制是多少?

这些我确实没写。原因不复杂:这些数字是我在真实业务里试出来的,脱离那个场景,抄过去大概率是错的。

如果你正在做类似的事,卡在幻觉、成本、延迟、评测任何一环——把你们的具体场景发我,我可以直接说我的判断,比看参数表有用。


做过:面向终端用户的 AI Agent「业务助手」0→1(20 万+ 用户 / 日 50 万+ 对话)、业务运营低代码平台 0→1(新场景周级→天级)、资金安全 5 倍洪峰零资损。