2026/10/18 22:23:10

LLM Agent非确定性治理:评估、稳定化与线上可靠实践

LLM Agent非确定性治理:评估、稳定化与线上可靠实践 你有没有遇到过这种场景用户向 Agent 问了同一个问题第一次得到一条完整准确的回答第二次却得到完全不同的结果甚至直接跑偏。用户不会关注底层模型有多聪明只会留下一句这个机器人不稳定。这其实是所有生产环境 LLM Agent 系统都必须面对的核心矛盾模型本身具备非确定性而生产体验要求稳定可靠。这篇文章就来聊聊 Agent 工程里最关键的非确定性治理问题从哪来、如何用评估和迭代的方式把随机输出一步步约束住最终让系统在线上交出可靠体验。1. 非确定性不是 bug而是 LLM 系统的底层属性1.1 同样的问题为什么第二次回答就不一样了LLM 本质上是一个基于概率的序列生成模型。它在生成每一个 token 的时候都在预测下一个词最可能是什么再加上采样策略temperature、top-p导致同一个 prompt 被多次调用时输出天然存在波动。很多团队会下意识认为只要把 temperature 调成 0 就没问题了。实际上并非如此部分模型在批量推理、投机采样、并行解码等场景下即便 temperature 为 0也会因为计算顺序和浮点精度差异产生不一致的输出。所以非确定性是消除不了的只能通过外围工程手段去控制这是 Agent 工程和传统软件开发在认知层面最本质的区别。1.2 Prompt 远比你以为的更敏感Agent 系统的每个决策环节都以 prompt 为驱动意图识别、工具选择、参数填充、结果解析……每个环节都暴露在 prompt 敏感性之下。修改一个措辞、改变示例的排列顺序都可能引发行为偏移而且这种偏移往往不是局部性的。举个例子。某团队在系统提示词里把不要编造订单号改成请严格使用用户提供的订单号本意是更严谨结果模型开始对没有附带订单号的正常提问也拒绝回答。这就是典型的 prompt 敏感性你以为是在做收紧约束模型理解成所有查询必须有订单号。这类行为漂移只有通过回归测试才能发现靠肉眼 review 文本很难觉察。1.3 上下文越长决策越容易漂移Agent 是多步交互系统每执行一步对话历史、工具调用记录就会继续往上下文里追加。上下文一大模型对早期约束的记忆会逐渐衰减尤其当工具返回的内容里存在重复、冗余或矛盾信息时模型很容易被带偏。比如一开始告诉模型只回答与订单相关的问题但在第五轮对话时模型可能已经忘了这条约束开始回答无关内容。这种约束遗忘不是模型变笨了而是注意力分配被不断更新的内容挤占。处理办法不是反复在对话里提醒而是把关键约束放在用户输入之前并且每轮都强制模型先输出一段结构化思考摘要把约束检测显式化。1.4 单步小误差链路大事故一个任务可能要拆成理解意图→选择工具→填充参数→解析返回→组织回答好几步。每一步的正确率就算有 95%五步下来整体成功率也只有 77% 左右。链路越长不确定性越是乘法叠加的关系而 Agent 恰恰是天然的长链路系统。0.95^5 ≈ 0.774这也是为什么 Agent 类应用比单轮问答的出错率高一个数量级。单轮问答错了重新问一次即可Agent 链路里任何一个节点出错最终交给用户的结果就是错的而且排查成本高得多。理解这个放大效应才会明白为什么要在每一个节点上都做防御而不是只在最终输出处加一道检查。1.5 环境漂移线上系统永远在流动除了模型本身的不确定生产环境里还有一层外部漂移。模型服务商升级了底层版本、工具接口返回结构的字段名变了、Prompt 模板被同事顺手改了一句……这些变化都不会立刻暴露在功能测试里但会在某个时间点突然改变 Agent 的行为分布。很多团队遇到过一种诡异现象某天线上成功率突然掉了几个点但代码和配置都没有改动。查到最后往往是底层模型服务悄悄更新了版本或者某个数据源的结构发生了变化。这种漂移无法预测所以必须靠监控基线和版本管理来接住。这也是为什么迭代优化不是一个阶段性的动作而是 Agent 系统的常规状态。2. 没有评估基线迭代就是原地打转2.1 为什么感觉变好了不可信对非确定性系统做优化最难的是判断这次改动到底有没有用。今天感觉效果好可能只是随机波动明天感觉变差了也可能只是某个样本恰好碰上了。没有稳定的评估基线所有迭代都建立在感觉之上改来改去也说不清哪一版更好。我在实际项目里见过不少团队优化 prompt 完全靠多试几次看结果顺不顺眼。这种方式的致命问题在于非确定性系统里单次结果的好坏不能代表整体质量。一次调用成功可能是运气一次失败也可能是运气。只有在一个固定、可复现的样本集上统计多次运行的结果才能得出有统计意义的结论。2.2 评估集从哪里来评估集是迭代优化的地基。它不是随便找几十条问题凑合而是从真实业务场景里沉淀出来的。通常建议从这么几个渠道收集线上日志中抽样的真实用户问题历史 Bad Case用户反馈不满意、规则引擎告警、人工抽检查出来的错误边界场景空输入、超长输入、多轮对话、指令模糊的请求工具异常场景工具返回空结果、超时、参数非法等。这里有个容易被忽略的点评估集里既要有常见问题也要刻意保留稀有但重要的问题。如果评估集全是高频场景优化的结果很容易牺牲掉长尾场景。评估集不是一次建完就完事。我见过做到比较成熟的项目每星期都会往评估集里补充新的失败样本保持它对线上真实分布的覆盖度。一套半年不更新的评估集就像一套半年不更新的测试用例参考价值会越来越低。2.3 评估维度怎么设计不同业务对可靠的定义不同。以带工具调用的 Agent 为例通常会拆出这样几个维度意图识别正确率用户到底要什么模型有没有理解对工具选择正确率选没选对工具参数填充正确率参数是否完整、格式是否正确回答与工具返回的一致性模型有没有忠实于检索/查询结果约束与安全该拒绝的请求是否被正确拒绝效率平均调用次数、耗时、Token 消耗。实际项目中不一定每个维度都建独立指标但至少要把这些维度都覆盖到。因为优化某一项很容易牺牲另一项比如为了让回答更友好模型可能开始绕开工具直接编造结果为了让成功率数字更好看模型可能总是拒绝复杂任务。评估维度就是给系统立规矩告诉它哪些红线不能碰。2.4 评估怎么执行怎么形成基线离线评估流程一般是把评估集批量回放给 Agent 系统让系统带着真实链路该调工具就调工具、该读数据就读数据跑一遍然后打分。打分可以分三层规则匹配输出格式、枚举值、必填字段是否合规大模型裁判让一个独立的 LLM 按评分标准对回答质量打分人工抽检对置信度低的样本由人来复核。三层机制各有用处规则匹配快且稳定但只能检查表面大模型裁判覆盖度高但本身也是非确定性模型需要把评分标准写得很细人工抽检准但成本高只适合用来校准裁判的尺度。这样得到的一组分数就是当前版本的基线baseline。以后每次改动 prompt、升级模型、调整链路都在同一组评估集上重跑对比基线看是变好还是变差。这才是迭代优化真正的起点——没有基线之前的一切修改都只能叫试错。3. 把随机输出约束进工程框架四个稳定化手段评估解决了怎么知道好不好的问题接下来要解决怎么让它好的问题。这里分享四个在生产里被反复验证有效的手段。3.1 结构化输出别让模型自由发挥Agent 工程里最怕的是模型输出一段自然语言让下游去猜意思。正确做法是给每一步输出定义严格结构用 JSON Schema 约束字段、类型和枚举值。工具调用最好走 function calling 机制即对话模型原生的函数调用接口让模型输出结构化的调用指令而不是自由文本。给一个简单的 schema 示例{ type: object, properties: { intent: { type: string, enum: [query_order, cancel_order, other] }, order_id: { type: string }, confidence: { type: number, minimum: 0, maximum: 1 } }, required: [intent, confidence] }有了这个约束下游解析就不需要费劲理解自然语言格式错误率会大幅下降。但要注意JSON Schema 只能约束形状不能约束语义。模型完全可能输出一个格式合法但内容错误的 JSON所以结构化输出只是第一步不能替代校验层。3.2 校验-重试回路给模型第二次机会再严格的结构化约束也不可能 100% 生效。模型可能漏掉必填字段也可能把枚举值写错。所以生产级 Agent 必须在模型输出之后加一道校验层校验不通过就把错误信息回传给模型让它带着错误信息重新生成。伪代码示意MAX_ATTEMPTS 3 for attempt in range(MAX_ATTEMPTS): output call_llm(prompt, schema) errors validate(output, schema) if not errors: return output prompt prompt \n上次输出未通过校验错误 str(errors) \n请修正后重新输出。 return fallback_response()这个循环看起来简单但实际效果非常好。因为它利用的是模型知错能改的能力——只要告诉它哪里错了它大概率会按正确格式再生成一次。要注意的是设置最大尝试次数防止无限调用浪费 Token每次重试把错误信息明确拼接进 prompt而不是让模型盲猜超出次数后走降级逻辑给固定兜底回答。很多人会担心重试增加延迟和成本实际跑下来这个开销远小于问题重新排队带来的损失。多数情况下一次重试就能纠正格式问题额外代价在可接受范围内。3.3 降低链路复杂度拆分任务、锚定示例非确定性在单步上的风险无法根除但可以通过设计把风险收敛到最小。比如把一个大而复杂的任务拆成多个小步骤单纯意图识别 单独参数抽取每一步只负责一件事模型犯错的概率会小很多。一个小任务意味着更短的 prompt、更聚焦的指令、更少的上下文干扰。同时尽量把 Few-shot 示例与控制指令放在显眼位置。示例的作用就是锚定行为一个贴合的示例比十句抽象的不要犯错有效得多。但示例也不是越多越好过多的示例会拉长上下文反而稀释注意力。实践下来同类型示例三到五条足够关键是覆盖到高频变化形态。3.4 护栏与降级确定失败时的行为可靠的系统不是永不失败而是在任何失败路径上都有确定的行为。生产部署时必须设计好这样几层护栏内容安全过滤模型输出先过一层敏感词/内容策略检查调用失败兜底工具异常、网络异常时返回固定话术无结果降级确认不了的信息绝不伪造明确说无法确认上限保护单次任务限制最大工具调用次数防止死循环。这些兜底行为本身要写死在代码里而不是交给模型自己决定。很多 Agent 系统翻车不是因为模型能力不行而是失败路径上没有任何确定性的兜底最终行为和用户的期望随机匹配。把失败时会怎样提前设计清楚系统从可能失控变成可控的波动。4. 线上失败模式与排查从一次错误回答定位到根因4.1 高频失败模式生产环境里最常见的失败往往是几类固定模式失败模式典型表现常见根因参数幻觉工具调用里的参数来自模型编造用户输入缺失信息模型自行补全解析错误工具返回正确但模型没正确提取结果结构复杂prompt 没交代清楚行为漂移改动整体 prompt 后旧场景变差prompt 敏感性缺少回归测试循环调用反复调用同一工具状态不前进没有设置调用上限模型陷入死局忽略约束用户诱导下绕过系统约束护栏不严密对抗性提问没有拦截这些失败模式最麻烦的地方在于它们不一定每次出现可能出现一次消失一次所以很难通过多试几次来定位。必须有完整的 trace 记录才能在失败发生的那一次里去还原现场。4.2 排查链路不要靠猜要靠轨迹对非确定性系统靠重新跑一次看看能不能复现来排查是最低效的方式。因为随机性决定了复现不了不等于问题不存在。正确的排查链路是从监控面板或用户反馈拿到出问题的请求 ID打开该请求的完整轨迹trace逐条查看每一步的输入输出找到第一个与预期不一致的节点而不是盯住最终回答针对该节点单独做小范围复测确认根因把这次请求保存为回归用例修复后在同一请求上验证。这里我建议每个 Agent 项目从一开始就接入可观测性平台记录完整调用链、Token 消耗、模型输出。没有轨迹数据任何排查都只能看到冰山一角。很多团队第一批产出评估集和 trace 系统的时间往往比他们预期得更晚而恰恰是这个环节决定了后面优化效率的高低。4.3 一个完整的定位修复实例某团队做了一个物流查询 Agent模拟项目X上线后约 12% 的查询会把运输中的包裹说成已签收。最终回答五花八门很让人头大。排查过程先拿到一条失败请求的 trace。发现 Agent 成功调用了物流查询接口工具返回了一个包含多条轨迹记录的 JSON 数组。但模型在组织回答时直接读取了数组的最后一个元素没有对状态字段做任何枚举判断而最后一条记录恰好是已签收的历史状态于是把历史节点当成了当前状态回答自然出错。根因确认后做了两件事第一在 prompt 里明确交代必须使用状态字段最新记录且只把状态值映射为运输中、已签收、已揽收等枚举结果。 第二在回答生成前加一道规则校验状态值必须存在于预设枚举集合中否则触发一次重试。改动后同一评估集上的失败率从 12% 降到 1% 以下且连续一周没有再出现同类错误。这个案例比较小但是很有代表性问题不在模型不聪明而在于缺少对输出语义的校验层。很多稳定化手段都是在这个层级上做文章。排查的意义就在这里——把一次线上故障变成一条新的评估用例让同类问题之后再也跑不出视线。5. 让可靠性成为常态监控、回放与灰度迭代5.1 线上监控什么怎么设置告警生产环境永远会出现离线评估覆盖不到的意外。所以上线之后必须建立一套持续监控体系。核心指标可以参考这样一个清单指标统计口径关注原因任务成功率完整跑完且最终输出合法的请求占比整体可靠性最直接的度量平均调用次数每任务调用大模型的次数次数异常升高说明可能循环重试率触发校验重试的比例过高说明结构化输出没约束好工具错误率工具侧抛错占比是模型填错参数还是外部服务异常负面反馈率用户点踩/投诉比例最终靠真实用户体验来兜底单请求成本Token 消耗重试和循环会烧钱需要设阈值所有这些指标都需要按版本维度做对比。改了一版 prompt成本涨了成功率涨了这没问题成功率涨了但负面反馈率也涨了就要警惕是不是评估集覆盖不全系统用某种隐蔽的方式在牺牲用户体验。告警阈值不要直接拍脑袋。比较实用的做法是先观察 2 到 4 周的基线数据取均值加减若干倍标准差作为告警线。比如平均调用次数超过 8 次或重试率超过 15%就要触发调查。宁可先定得宽松一点也不要让告警噪音把团队训练成不看告警的习惯。5.2 Bad Case 自动回流构建数据闭环监控不只是看数字更重要的是把异常样本自动收回来。我比较推荐的做法是三层收集机制规则触发输出格式不合法、工具调用数值超范围等自动打标用户反馈用户点踩或有投诉入口的自动携带 trace 入库人工抽检每天抽一部分成功样本复查因为有时候模型答错用户也根本发现不了。收集回来的 Bad Case 经过清洗和归类每周补充进评估集。这样迭代的素材就源源不断越迭代越贴近真实线上分布。这套机制跑起来后团队优化 prompt 就不再依赖灵感而是手里有一批真实的、会反复出错的样本改一版测一版效果肉眼可见。5.3 灰度发布与版本管理改 prompt 也要像改代码一样谨慎很多团队改模型 prompt 像聊天一样随手改。这在大模型应用里是大忌因为一次措辞调整可能同时影响十个场景的性能。推荐的做法是prompt 和配置全部纳入版本管理每次改动都能看到差异每次改动先在评估集上跑离线回归和上一版本基线对比线上下发采用灰度策略先放 5% 流量观察一两天确认成功率、重试率、成本指标都没有恶化再逐步放大到全量。模型版本升级同理底层模型一变所有 prompt 的效果都可能重构必须完整回归一遍不能凭感觉直接切。这一点我有过教训某次底层模型升级后之前调好的 prompt 在部分场景出现了严重的格式问题由于直接全量切换问题在线上爆了两天才被发现。灰度流程不完善交的学费都是实打实的。5.4 迭代节奏怎么定可靠性不是一次性搞出来的而是靠持续的小步快跑磨出来的。结合我接触过的项目比较健康的节奏是每周固定补充和清洗一次 Bad Case每周跑一次全量离线评估产生最新基线每两到四周做一次 prompt/链路优化版本的上线每次上线前必须有离线评估报告和灰度计划支撑。节奏可以根据团队资源调整但核心是保证评估-优化-验证-上线这个循环一直转下去。很多 Agent 项目一开始效果不错后来问题越来越多正是因为循环停掉了线上样本不回流评估集不更新prompt 越改越散最后系统变成一团谁都动不得的乱麻。这篇内容写到最后我想说一个自己在实际项目里的感受做 LLM Agent 工程最大的心态转变是从追求模型一次性输出完美答案变成接受模型会犯错然后把犯错变成可控事件。非确定性堵不住但可以用评估、约束、校验、降级、监控这一整套工程手段把不确定的范围越缩越小。如果你现在手头的 Agent 也在上线后不断冒出新毛病建议先从两件事入手搭一套可回放的评估集然后把校验-重试回路加到所有模型输出的下游。这两件事做完系统体验通常会有肉眼可见的提升。之后再把失败样本回流、灰度发布这些机制逐步补上。等这套循环转起来你手头的 LLM 系统离可靠生产体验就不远了。