LLM Agent 生产环境翻车?实时检测与自动修复全攻略
先问你一个问题你的 LLM Agent 是不是也遇到过“Demo 很惊艳一上生产就翻车”的情况工具调用偶尔传错参数多轮对话突然忘了原始目标生成了一个看起来合理但完全是编造的结论甚至在一个不应该继续的地方循环重试。更头疼的是这些问题不是偶发代码 Bug而是模型输出的不确定性带来的系统性风险。查日志定位不到根因改一遍 Prompt 重跑又复现最后只能靠人工盯着输出。这篇文章要写的就是如何用工程手段解决这个问题Real-Time Detection and Repair of LLM Agent Failures也就是对 Agent 故障做实时检测与自动修复。核心判断先说在前面LLM Agent 的稳定性问题不是换一个更强的模型就能解决的而是要建立起一套包含失败分类、实时检测、自动修复和离线回归的闭环治理体系。读完这篇文章你能收获一个完整的 Agent 稳定性架构设计思路包括失败类型怎么分类、检测管道怎么实现、自动修复的边界在哪里以及如何避免“修了又坏、坏了再修”的恶性循环。1. 这篇文章真正要解决的问题很多开发者在落地 LLM Agent 时都会经历三个阶段。第一阶段是“能跑通”。用 LangChain、LlamaIndex 或自研框架把 Agent 搭起来调用模型、解析输出、执行工具做一个 Demo 让它在预设问题上有模有样地完成任务。第二阶段是“上生产”。接入真实业务数据放开用户并发把 Agent 交给真实场景使用。第三阶段才是真正的分水岭Agent 开始频繁失败而你没有一套有效应对手段。传统软件的失败是确定性的代码写错了报错信息是明确的堆栈是清晰的你可以在测试环境复现。但 LLM Agent 的失败完全不同。它可能没有报错只是回答得不对可能工具调用格式完全合法但调用的工具和用户意图完全不匹配可能整个过程每个步骤都在正常工作但最后得到的结果和原始任务毫无关系。这种“没有异常的失败”是 LLM Agent 生产化最大的难点。我给大量落地 Agent 的团队做过方案评审发现大家遇到的痛点集中在三类。第一检测慢。失败发生后依赖用户反馈或人工抽查日志往往过了很久才发现问题。第二定位难。一次失败的链路可能包含用户输入、模型推理、Prompt 构造、工具执行、上下文管理等多个环节没有统一的可观测数据很难判断是哪一环出了问题。第三修复无闭环。今天改一个 Prompt明天又出现另一种失败模式每次都是救火没有把失败样本沉淀下来形成回归测试集。这篇文章要解决的就是这三类问题。我们会从失败分类入手搭建一个能实时捕获 Agent 异常状态的检测管道然后设计分层自动修复策略最后把线上失败反哺到离线评测中形成长期治理闭环。这套方法论不绑定某一个 Agent 框架无论你用的是 LangGraph、AutoGen 还是自研框架都可以落地。2. LLM Agent 失败的本质与分类2.1 先理解 Agent 的执行过程要理解 Agent 为什么会失败先要看清楚它的执行结构。一个典型 Agent 的完整流程是接收用户任务。规划执行步骤决定是否需要调用工具。如果调用工具则生成结构化调用参数。执行工具将结果追加到上下文。根据工具结果决定下一步动作或生成最终答案。整个流程可以被看作一个循环推理 - 决策 - 行动 - 观察 - 再推理。传统软件在同一循环中是靠确定性的代码逻辑推进的每一步的结果都可预测。但 Agent 中每个“推理”都经过一次模型采样结果天然带有随机性。同一任务在相同条件下运行两次走出的轨迹很可能不一样。这就是 Agent 不稳定的根源失败是概率性的不是确定性的。2.2 从失败发生的层级来分类要对 Agent 失败做实时检测第一步不是抓日志而是建立失败分类体系。我的建议是按照失败发生的层级来分而不是按照错误信息来分因为 Agent 很多失败根本没有错误信息。失败层面失败类型典型表现检测难度协议层输出格式错误JSON 解析失败、必填字段缺失、工具名称非法低状态层上下文污染或超长关键信息被截断、Agent 忘记最初任务、上下文包含脏数据中动作层工具选择或参数错误调错 API、参数幻觉、传入了不存在的订单号高规划层循环、绕路、目标漂移重复调用同一工具、路线明显绕远、最终答案与任务无关高安全层越权与危险操作尝试删除生产数据、访问未授权资源、执行高危命令高协议层失败最容易被发现因为它违反的是结构约束。比如模型输出{tool: get_weather, args: city: Beijing}而不是合法的 JSON这种失败用正则或 JSON Schema 校验就能拦截。状态层失败是生产环境最容易踩的坑。当对话轮次多了、工具调用结果越来越大、上下文接近模型窗口上限时Agent 可能丢失早期关键信息。比如用户在第一轮说“请对比 A 方案和 B 方案的成本”到了第五轮 Agent 已经只关注 B 方案了。这类失败不产生异常但结果已经偏离需求。动作层失败更隐蔽。模型生成的工具调用格式完全合法JSON Schema 校验也能通过但参数是错的。比如用户要查询订单Agent 把订单号和客户号搞混了调用了查询接口却传了错误参数。这种失败靠检测代码逻辑是不行的需要语义层面的判断。规划层失败属于更高维度的问题。Agent 可能在一个问题上反复调用工具或者始终在兜圈子。站在每一步看动作都是合法的但从完整轨迹看它没有接近目标。这种失败需要结合轨迹分析才能发现。安全层失败是最不能容忍的。Agent 可能出于幻觉或对指令的误解生成一个会破坏系统状态的动作。这类失败的技术检测重点是工具授权和动作审核不能完全依赖模型自觉。2.3 为什么传统异常处理不够用你可能会想Agent 框架里不是有 try/except 吗工具调用失败不能捕获异常吗可以捕获但捕获的只是“代码异常”。传统异常机制处理的是系统已经抛出的错误而 Agent 的大部分失败是“没有异常的错误”。当模型生成了一串完全合法但含幻觉的文本时当它决定调用一个参数错误但格式正确的工具时任何 try/except 都无法感知。实时检测系统要做的正是把这一层不可见失败变成可见事件。这本质上是一次从“代码异常处理”到“语义异常处理”的升级。3. 失败检测系统的核心设计3.1 检测目标在问题放大之前拦截实时检测的“实时”到底指什么很多人以为是为每个 Agent 响应做完完整质量评估但这不是实时这会拖慢响应。更合理的实时目标是在 Agent 执行循环中在每一步动作结束后、进入下一步推理前用极低延迟的检测器判断当前状态是否健康。因为 Agent 是循环执行的所以只要在循环体里插入检测点就能在问题放大前拦截。这个设计背后的原因是Agent 一旦已经走到最后一步才检查质量修复成本极高。你可能需要重新查询多个工具、重新组织上下文、重新生成答案。但如果在第三步就发现工具选错了这时候修复成本很低甚至只需要修正参数重试一次。3.2 三层检测架构我推荐把检测拆成三层每一层的延迟和成本不同覆盖的失败类型也不同。第一层范式检测Formal Check这一层不依赖模型用确定性代码执行。主要包括 JSON 解析校验、字段类型校验、工具白名单校验、参数必填校验、调用频率校验。它的优势是零延迟、零成本、准确率 100%适合拦截协议层失败。任何 LLM 输出在进入后续流程前都必须先过这一层。第二层语义检测Semantic Check这一层需要一个轻量模型或一个调用成本可控的 LLM 作为判定器。它能判断的内容包括当前步骤是否仍然围绕原始任务、工具调用结果是否符合用户预期、上下文中的信息是否自洽。典型实现方式是给判定器一个带格式约束的 Prompt让它输出结构化判断结果。这一层延迟会高一些但设计目标是控制在几百毫秒以内。第三层回归检测Regression Check这一层发生在离线。把线上 Agent 的失败轨迹沉淀到评测集中模型或 Prompt 更新前先跑一遍回归集确保新版本没有引入回退。它的目标不是“实时”而是防止同类失败反复出现在线上。在设计检测系统时有一个容易犯的错误把三层检测都塞到在线链路上导致 Agent 响应延迟翻倍。正确的做法是范式检测全量、语义检测按风险开关控制、回归检测全部留到离线。3.3 检测结果不只有“通过/失败”实时检测系统需要输出比布尔值更丰富的信息才能支撑后续自动修复。如果只输出pass或fail修复器不知道应当修复哪个环节。合理的设计是输出一个结构化的检测事件包含是否失败。失败类型。置信度。失败证据例如不合格的 JSON 片段、偏离任务的模型输出。修复建议对应到某个修复策略。这样检测与修复才能解耦。检测器只负责发现问题并附带上下文修复器根据失败类型选择具体修复动作。4. 工程底座日志、追踪与事件结构很多做 Agent 的团队跳过可观测性直接上检测结果发现检测器根本没有数据可用。实时检测的前提是完整的轨迹数据。所谓轨迹就是 Agent 从收到任务到生成最终结果的完整执行过程包括每一步的模型输入输出、工具调用参数、工具返回结果、上下文变化。4.1 记录轨迹事件而不是只记录日志传统日志记录的是“发生的事件”比如“调用工具成功”“调用工具失败”。Agent 轨迹需要记录的是“状态变化”尤其是模型是怎么思考的、它看到了什么。推荐的落地方式是结构化事件流每个关键节点输出一条 JSON 事件。下面是一个轨迹事件的示例。{ trace_id: a7f8b3c9-0f2e-4a6d-9c5b-7e1a2d3f4b5a, agent_id: ticket-agent, task: 处理用户售后工单订单号 20250101 发货延迟, step: 3, phase: tool_call, tool_name: query_order, tool_args: { order_id: 20250101 }, llm_raw_output: {\tool\: \query_order\, \args\: {\order_id\: \20250101\}}, context_tokens: 4200, context_snapshot_id: cxt_43821, created_at: 2025-01-01T10:00:00Z }每个字段都要有意义。step是当前是第几步phase标识当前阶段llm_raw_output保留模型原始输出context_snapshot_id指向当时的上下文快照。有了这些信息检测器才能判断当前状态是否健康。4.2 用结构体约束事件格式轨迹事件如果只是自由 JSON检测和存储都会很难维护。建议定义强类型模型在写入时做校验。下面是一个用 Python 实现的事件结构示例。# 文件路径agent_observability/schemas.py from typing import Any, Optional from pydantic import BaseModel, Field class TrajectoryEvent(BaseModel): trace_id: str Field(..., description一次完整任务执行的唯一标识) agent_id: str Field(..., descriptionAgent 实例 ID) task: str Field(..., description用户原始任务) step: int Field(..., ge0, description当前执行步数) phase: str Field(..., description当前阶段plan / tool_call / observe / final) tool_name: Optional[str] Field(None, description当前调用的工具名称) tool_args: Optional[dict] Field(None, description当前工具调用参数) tool_result: Optional[str] Field(None, description工具执行返回结果) llm_raw_output: str Field(..., description模型原始输出) context_snapshot_id: Optional[str] Field(None, description上下文快照 ID) context_tokens: int Field(0, description当前上下文 token 数) created_at: str Field(..., description事件产生时间ISO8601)事件写入后检测管道就可以在每一条事件产生时即时运行。实际项目中这一步可以通过 Agent 框架的回调机制callback或者对工具调用层做切面注入来实现不需要侵入业务代码。5. 实时检测管道实现5.1 检测管道的整体结构检测管道本质上是一条责任链把多个检测器串起来按延迟从低到高依次执行。先跑最快的确定性校验再跑成本较高的语义判定命中任何一个就返回。以 Python 为例实现思路如下# 文件路径agent_observability/detector.py from typing import List, Callable class DetectionResult(BaseModel): has_failure: bool failure_type: str confidence: float 1.0 evidence: List[str] [] repair_suggestion: str class Detector: def check(self, event: TrajectoryEvent) - DetectionResult: raise NotImplementedError class DetectionPipeline: def __init__(self, detectors: List[Detector]): self.detectors detectors def run(self, event: TrajectoryEvent) - DetectionResult: for detector in self.detectors: result detector.check(event) if result.has_failure: return result return DetectionResult(has_failureFalse)这个管道的好处是你可以按需组合检测器。比如低风险生产事件只跑范式检测高风险金融场景加上语义检测。也可以给不同检测器设置开关线上流量高峰期只保留第一层低峰期再开启全量检测控制成本。5.2 范式检测器拦截格式错误范式检测器最简单也最刚需。以工具调用为例LLM 输出必须先解析成 JSON然后逐一校验工具名、参数名和必填字段。下面是具体实现。# 文件路径agent_observability/detectors/formal_detector.py import json class ToolSchemaDetector(Detector): def __init__(self, tool_whitelist: set, required_fields: dict): self.tool_whitelist tool_whitelist self.required_fields required_fields def check(self, event: TrajectoryEvent) - DetectionResult: if event.phase not in (tool_call,): return DetectionResult(has_failureFalse) try: parsed json.loads(event.llm_raw_output) except json.JSONDecodeError as e: return DetectionResult( has_failureTrue, failure_typeprotocol_malformed_json, confidence1.0, evidence[fJSON parse error: {e}], repair_suggestionretry_with_schema_fix, ) tool_name parsed.get(tool) if tool_name not in self.tool_whitelist: return DetectionResult( has_failureTrue, failure_typeprotocol_invalid_tool, confidence1.0, evidence[ftool {tool_name} is not in whitelist], repair_suggestionretry_with_tool_correction, ) args parsed.get(args, {}) missing [f for f in self.required_fields.get(tool_name, []) if f not in args] if missing: return DetectionResult( has_failureTrue, failure_typeprotocol_missing_parameter, confidence1.0, evidence[fmissing fields: {missing}], repair_suggestionretry_with_schema_fix, ) return DetectionResult(has_failureFalse)这一类检测器不要使用模型来判断就用确定性代码。因为 JSON 解析和字段校验是逻辑问题模型判断反而可能出错而且会增加延迟和成本。5.3 语义检测器识别“没报错但有问题”的失败语义检测是实时检测体系的灵魂。核心思想是用一个判定模型可以是一个轻量 LLM也可以是专精分类的模型结合检测模板判断当前状态是否健康。下面是一个判断 Agent 是否偏离原始任务的 Prompt 模板。# 文件路径agent_observability/detectors/semantic_detector.py SEMANTIC_CHECK_PROMPT 你是一个 Agent 执行状态判定器。请根据给定的原始任务和当前执行步骤判断 Agent 当前状态是否存在失败风险。 判定标准 1. 当前步骤是否偏离原始任务。 2. 工具调用结果是否支持当前任务目标。 3. 是否存在信息矛盾或关键信息丢失。 请以严格 JSON 格式输出不要输出其他内容 { has_failure: true 或 false, failure_type: 目标偏离 或 信息矛盾 或 上下文丢失 或 正常, confidence: 0.0 到 1.0, evidence: 简短说明判断依据, repair_suggestion: 修复建议没有失败则为空字符串 } def run_semantic_check(task: str, current_step_desc: str) - dict: prompt f{SEMANTIC_CHECK_PROMPT}\n\n原始任务{task}\n当前状态{current_step_desc} response call_judge_model(prompt) # 调用低延迟判定模型 return json.loads(response)注意这里的设计是把“判定模型”和“执行模型”分开。执行模型负责完成用户任务判定模型只负责检查状态。二者职责分离后检测逻辑不会影响执行逻辑你可以独立升级判定模型也不用担心检测 Prompt 干扰主任务。5.4 检测结果的消费链路检测器输出不只是给修复器用的还需要同时进入三个下游修复器立即处理在线失败。审计存储记录全部失败事件用于事后分析和复盘。失败样本库沉淀到离线数据集中作为回归评测的种子。这样就打通了“实时检测”和“长期治理”之间的数据通路。6. 自动修复机制与策略6.1 自动修复的边界自动修复听着很诱人但不能不加限制地让机器自己改提示词、自己重试、自己决定执行危险操作。必须一开始就明确自动修复的边界。我的建议是自动修复只做可逆、低风险、有明确成功判据的动作。什么是可逆的动作重新生成一次答案、修正工具参数重试、压缩上下文后重试这些是可逆的。什么是不可逆的动作删除数据、转移资产、发送不可撤回的通知、执行支付这些动作不管检测结果如何都需要人工确认。在一个售后工单 Agent 场景中重试查询订单接口是可逆的但直接给用户发送退款指令就可能是危险的。所以自动修复系统必须内置一张“危险动作清单”一旦检测到模型即将执行清单中的工具立即切换为人工确认模式。6.2 分层修复策略失败类型不同修复入口也不同。我建议按以下方式分层输入层修复调整给模型的输入。当语义检测发现 Agent 偏离任务时最直接的手段是给模型补充纠偏指令。比如“你当前正在处理的任务是 X请忽略之前与任务无关的推理回到主线任务上”。这类修复通过修改下一步的 Prompt 实现不改变 Agent 整体状态。状态层修复清理或压缩上下文。当上下文超长、或历史信息出现污染时需要对上下文做压缩或裁剪。可以总结早期轮次、删除无关的工具结果、保留关键字段。压缩之后再让 Agent 继续执行。动作层修复修正工具调用参数。当范式检测发现参数缺失或类型错误时修复器可以直接补充参数或重新生成参数。当语义检测发现工具选错时需要让 Agent 回到工具选择阶段重新决策。结果层修复重新生成最终答案。如果检测发现最终答案与事实不符或缺少依据可以让 Agent 结合工具结果重新生成。但要注意次数限制避免无限重试。失败层面修复策略修复成本是否需要人工确认协议层格式错误纠错结构化输出后重试低通常不需要上下文超长压缩历史上下文后重试中不需要工具选错回到决策点重新选择工具中视工具风险而定目标漂移注入纠偏指令重新规划中不需要越权/危险操作切换到人工确认流程高必须6.3 修复策略配置化修复策略不要写死在代码里建议做成配置方便不同业务线独立调整。一个最小配置示例是{ repair_strategies: { protocol_malformed_json: retry_with_schema_fix, protocol_missing_parameter: retry_with_schema_fix, context_overflow: compact_history_and_retry, semantic_goal_drift: inject_correction_prompt_and_retry, final_answer_unsupported: regenerate_with_faithful_decline }, max_repair_attempts: 3, dangerous_tools: [delete_order, refund, approve_payment], human_confirm_timeout_seconds: 300 }这里有个重要细节max_repair_attempts一定不能设得太大。每次自动修复都意味着多一次模型调用、多一倍成本和延迟而且修复次数越多模型陷入循环的可能性越大。实际项目里设置 2 到 3 次比较合理超过上限就转人工或者降级为保守回复。6.4 修复动作的可观测性自动修复本身也要被观测。至少要记录修复触发原因、修复策略、修复耗时、修复后是否成功、是否再次触发其他失败。否则你只能看到“Agent 最终成功了”但不知道它靠多少次修复才成功这些修复又消耗了多少成本。长期来看这些数据是优化 Prompt 和模型选择的重要依据。7. 生产落地从修复闭环到长期治理7.1 失败样本回流把线上问题变成评测集很多团队把实时检测和自动修复合在一起就算完事但这是不够的。如果没有离线回归同一个问题会在不同的 Prompt 变体上反复出现。正确的做法是线上每个检测出的失败事件都进入失败样本库每周由人工或半自动流程把样本转化为回归测试用例。完整的质量闭环是这样的线上检测到失败 - 自动修复或人工处理 - 失败样本入库 - 生成回归测试用例 - 新Prompt/新模型上线前跑回归 - 通过后灰度发布 - 继续监控新失败这个闭环中实时检测是入口离线评测是闸门二者缺一不可。7.2 提示词与模型版本也要做灰度传统开发中代码有版本管理但很多团队的 Prompt 还是“改完直接复制到线上”。Prompt 一变Agent 行为可能发生显著变化这就应当像代码发布一样走流程。建议的做法是每次修改 Prompt 后先在评测集上跑一遍离线测试确认关键指标没有回退。然后灰度发布比如先让 5% 的流量使用新 Prompt观察失败率、重试率、人工介入率和用户反馈。最后才全量发布。模型切换也是同样流程不能用“今天新模型发布了直接换上去”的方式。7.3 权限与安全边界Agent 工具调用的权限管理应当遵循最小权限原则。你可以在 Agent 里配置一个工具白名单但每个工具的可操作范围还应当细分。比如查询订单接口只能查不能改删除接口在非测试环境根本不应该出现在 Agent 的工具列表里。真正需要删除操作的场景必须走人工审批流Agent 只能发起申请不能直接执行。这里有一个生产环境的常见教训Agent 开发时为了测试方便给 Agent 配置了超级权限结果上线后忘记收敛。一旦 Agent 在真实场景中突然调用了危险工具后果会非常严重。所以权限配置必须纳入发布检查项每次环境部署时都要核对 Agent 当前拥有哪些工具权限。7.4 检测成本与业务价值的平衡最后要提醒的是检测不是越重越好。全量语义检测会带来可观的模型调用成本。如果 Agent 本身每天调用量不大全量语义检测可能可以接受如果 Agent 是高并发场景就必须做成本控制。我见过一个比较合理的配置范式检测 100% 全量语义检测在一个时间段内按采样率运行比如先对 20% 的流量做语义检测根据命中率动态调整采样率发现新失败类型后再把对应规则固化成确定性检测器逐步降低对语义检测的依赖。检测本身也是可以迭代优化的不能一上来就上最好最贵的方案。8. 常见问题与排查思路实时检测系统落地过程中一定会遇到下面这些典型问题。整理成排查表供你参考。问题现象可能原因排查方式解决方案检测管道没有拦截到任何失败检测点没有插入到 Agent 执行循环检查轨迹事件是否完整、每一步是否触发检测回调在工具调用后和最终答案生成前插入强制检测点语义检测误报率过高判定模型能力不足或检测 Prompt 标准模糊抽样人工评审统计误报和漏报比例换更强的判定模型细化判定标准加入示例自动修复后仍然失败修复策略不对症或重试预算太小查看修复事件日志确认修复后失败模式是否相同调整修复策略映射增加修复后二次检测Agent 上下文越来越大导致检测延迟上升上下文快照保存了过多历史事件统计每条事件的上下文快照大小定期压缩历史只保留关键信息摘要成本增长超出预期语义检测全量开启看语义检测调用量和失败命中率降低采样率把高频失败固化为规则检测Agent 绕过了安全限制危险工具没有从工具列表移除审查 Agent 实际可调用的工具有哪些按最小权限原则收敛工具白名单增加人工确认同一类失败反复出现失败样本没有回流到评测集检查失败样本库是否有持续更新建立失败样本入库和回归测试的例行流程排查第一原则是先确认数据再查逻辑。如果检测管道没有拦截到失败说明这个失败根本没有进入检测事件流。此时不要先怀疑检测器写得不对而要先检查 Agent 框架的回调或者拦截器是否真的在每一步都触发了。9. 总结与后续学习方向回看整篇文章核心讲透了三件事。第一LLM Agent 的失败不是代码级异常而是语义级异常必须建立按层级划分的失败分类体系。第二实时检测不是最终答案质量评估而是在 Agent 执行循环内插入低延迟检测点用范式检测加语义检测的组合捕获问题。第三自动修复必须配置化、分层化同时设定重试预算和安全边界不能无限重试也不能放任危险操作。如果你的 Agent 还停留在“能跑通 Demo”的阶段下一步最不该做的是继续堆功能而是先把实时检测与修复这套闭环补上。哪怕先用最简单的方案给轨迹写结构化日志、在工具调用后做 JSON 校验、定义三类失败策略并做配置化修复也能明显减少线上翻车概率。之后再把失败样本库、离线回归、灰度发布机制逐步接入Agent 才能真正从“实验品”变成“生产系统”。后续建议重点研究的方向包括Agent 离线评测集怎么做才有效、LLM 可观测性工具链如何选型、以及如何利用失败样本数据做模型微调或强化学习反馈。这三个方向恰好组成了一条从检测到治理、从治理到进化的完整路径。
