生产级Agent(19):执行回放与故障取证
文章摘要前十八篇已经把生产级 Agent 的执行、权限、审计和治理链条搭起来Run、Planner、Tool、Checkpoint、Approval、Capability、Delegated Authority、Audit Ledger 都有了。但真正发生线上事故以后团队很快会遇到一个更难的问题“这次 Agent 到底为什么这么做”普通日志往往只能看到调用了哪个模型 调用了哪个Tool 返回了什么错误却很难完整还原当时模型看到了什么上下文 使用的是哪个Prompt版本 Tool返回了哪一版数据 Policy如何判断 审批绑定了什么Action 为什么进入下一步 外部副作用到底成功没有 如果重新执行能不能稳定复现所以生产 Agent 不能只有 Audit Ledger还需要一套真正的Execution Replay Failure Forensics把一次运行需要的输入、版本、决策、工具结果、外部副作用和时间条件固化成可重放证据事故时在隔离环境里选择“纯回放”“从某一步分叉”“替换模型重跑”“冻结Tool结果重跑”等模式比较新旧轨迹遇到外部系统返回未知状态时必须先 Reconcile而不是直接 Retry。本篇从数据模型、Replay Manifest、事件时间线、Tool Fixture、UNKNOWN Side Effect、Counterfactual Replay、Snapshot、Artifact Hash、State Migration、Privacy Redaction、故障分类、Spring Boot Orchestrator 和自动化测试一路实现下来目标是让一次复杂 Agent 事故从“看日志猜原因”升级成“可以稳定重放、比较和举证”。一、为什么普通Trace还不够一个生产 Agent RunUser Request ↓ Planner ↓ Search ↓ CRM ↓ Reviewer ↓ Email SendOpenTelemetry 能告诉你Span A Span B Span C但重放需要更多东西模型版本 Prompt Hash Context Snapshot Tool Fixture Policy Version Approval Hash Clock Randomness Knowledge Snapshot如果这些没有保存Trace 只能描述“发生了什么”不能重新构造“当时的世界”这就是 Replay 和 Observability 的区别。二、Replay的第一原则不要重新访问今天的外部世界事故发生在2026-08-20 10:15今天重放时CRM 客户状态可能已经变了知识库文档也更新了。如果 Replay 又实时查询CRM Vector DB 网页 库存结果不同并不能说明 Agent 不稳定。所以默认 Replay 应使用Recorded Tool Result而不是 Live Tool。三、Replay ManifestpublicrecordReplayManifest(StringreplayId,StringsourceRunId,StringsourceRunVersion,StringmodelProfile,StringpromptBundleHash,StringgraphVersion,StringpolicyVersion,StringcapabilityRegistryVersion,StringknowledgeSnapshot,StringevaluatorVersion,InstantlogicalTime,StringenvironmentHash,ReplayModemode){}每次重放都要绑定源 Run。四、Replay Mode至少分四种publicenumReplayMode{EXACT,MODEL_SWAP,PROMPT_SWAP,FORK_FROM_STEP}EXACT完全使用原模型、原Prompt、原Tool Fixture。目的验证能否复现MODEL_SWAP只替换模型。目的如果换新模型 同样输入是否还会失败PROMPT_SWAP只换 Prompt。FORK_FROM_STEP前 N 步使用历史事实。从某一步开始重新执行。这最适合调试。五、Tool FixturepublicrecordToolFixture(StringtoolCallId,StringcapabilityId,StringrequestHash,JsonNodesanitizedRequest,JsonNodesanitizedResponse,ToolOutcomeoutcome,InstantstartedAt,InstantcompletedAt,StringproviderRequestId){}Replay 时同样 requestHash → 返回历史 response六、Request Hash必须基于Canonical JSONJSON 字段顺序不同{a:1,b:2}和{b:2,a:1}逻辑上相同。不能产生两个 Hash。所以Normalize Sort Keys Normalize Number Normalize Unicode ↓ SHA-256七、Tool Fixture找不到时怎么办不要自动访问生产。定义publicenumFixtureMissPolicy{FAIL,STUB,SANDBOX_LIVE}默认FAIL只有明确允许的无副作用 Tool 才可以SANDBOX_LIVE八、Replay不能执行真实副作用以下 Toolemail.send payment.refund crm.update cloud.deploy file.deleteReplay 只能返回Recorded Result或者Dry-run不能真的再做一次。九、Side Effect LedgerpublicrecordSideEffectRecord(StringsideEffectId,StringrunId,StringstepId,StringcapabilityId,StringidempotencyKey,StringactionHash,SideEffectStatusstatus,StringproviderReceipt,InstantexecutedAt){}状态publicenumSideEffectStatus{PLANNED,EXECUTING,CONFIRMED,FAILED,UNKNOWN,RECONCILED}十、UNKNOWN为什么必须单独建模最危险的窗口Agent → Payment API → 实际退款成功 → 返回途中网络超时Agent 看到timeout真实世界已经退款如果系统把 Timeout 直接记FAILEDAgent Retry重复退款所以无法确定副作用结果必须记录UNKNOWN十一、UNKNOWN状态下禁止自动Retry规则if(statusSideEffectStatus.UNKNOWN){thrownewReconciliationRequiredException();}先进入Reconcile十二、Reconciliation AdapterpublicinterfaceSideEffectReconciler{booleansupports(StringcapabilityId);ReconciliationResultreconcile(SideEffectRecordrecord);}支付用 provider_request_id 查询退款状态邮件用 provider_message_id 查询发送记录如果 Provider 没查询接口进入人工不能盲重试。十三、Replay首先要冻结Clock很多 Agent Prompt 里有今天 当前时间 本周 30分钟前如果 Replay 用今天2026-08-24原 Run 用2026-08-20行为自然会不同。所以 Runtime 不能直接调用Instant.now()而应该publicinterfaceAgentClock{Instantnow();}生产SystemAgentClockReplayFixedAgentClock十四、任何时间相关Tool也要读Logical Time例如get_today_ordersReplay 不应该查询真正今天。Tool Fixture里直接返回源 Run 结果。如果是 Sandbox Liverequest_time必须传入历史 Logical Time。十五、Prompt必须保存Compiled Artifact不能只保存prompt-template-v8因为运行时还可能注入System Policy Tool Schema Skill Tenant Config Feature Flag真正 Replay 要保存最终 Compiled Prompt Hash最好 Artifact 本身也可取回。十六、Prompt ArtifactpublicrecordPromptArtifact(StringartifactId,StringtemplateVersion,StringcompiledHash,StringcontentRef,inttokenCount,InstantcompiledAt){}日志里不要直接长期保存完整敏感 Prompt。保Hash Encrypted Ref十七、Context Snapshot模型输入还包括Conversation Memory RAG Evidence Tool Schema Run State所以需要publicrecordContextSnapshot(StringsnapshotId,StringrunId,StringstepId,ListStringmessageRefs,ListStringevidenceRefs,ListStringmemoryRefs,StringtoolCatalogHash,StringstateHash,StringcontentHash){}十八、RAG必须保存Evidence ID不要只保存answer used 5 chunks必须保存document_id document_version chunk_id chunk_hash rank score否则知识库更新以后无法还原。十九、RAG ReplaypublicrecordRetrievalFixture(StringqueryHash,StringretrieverVersion,ListRetrievedEvidenceevidence){}Replay 直接返回原证据集合。然后可以做 Counterfactual同一证据 换新模型或者同一模型 换新检索器二十、Graph Version必须锁定Agent WorkflowPlanner → Search → Tool → Reviewer两周后改成Planner → Search → Reviewer → Tool如果 Replay 不锁 Graph复现没有意义。保存graph_version并且旧 Graph Definition 要可加载。二十一、状态Schema也会变化Run State v3{goal:...,plan:[...]}Run State v4{objective:...,steps:[...]}Replay 老 Run 时需要State Migration但要注意迁移后Replay和原样Replay不是同一件事。二十二、保留Raw Snapshot和Migrated Snapshotraw_state_ref migrated_state_ref migration_version这样事故分析时知道差异是不是Migration引入二十三、决策节点也要保存输入例如 PolicyALLOW DENY REQUIRE_APPROVAL不要只保存结果。保存publicrecordPolicyDecisionRecord(StringpolicyId,StringpolicyVersion,StringinputHash,JsonNodesanitizedInput,Stringdecision,ListStringreasons){}未来 Policy 改了可以重放同一输入 新Policy会怎么判二十四、Approval也要进入Replay记录谁批准 批准哪个Action Hash 何时批准 何时过期Replay 默认不能重新向用户发审批。而是使用Recorded Approval只有 Counterfactual Mode 才允许模拟如果当时拒绝会怎样二十五、一个完整Execution EventpublicrecordExecutionEvent(longsequence,StringeventId,StringrunId,StringstepId,ExecutionEventTypetype,StringpayloadRef,StringpayloadHash,StringpreviousHash,InstantlogicalTime,InstantrecordedAt){}这里logicalTime和recordedAt必须分开。二十六、为什么需要Sequence分布式系统里时间戳可能相同 漂移 乱序所以每个 Run 建立monotonic sequenceReplay 按 Sequence。不是按日志时间字符串排序。二十七、Hash Chain用于防篡改event_1_hash ↓ event_2.previous_hash ↓ event_2_hash计算hash( previous_hash canonical_payload metadata )后续修改历史事件Chain Break这对合规取证很有价值。二十八、Replay OrchestratorServicepublicclassReplayOrchestrator{publicReplayResultreplay(ReplayRequestrequest){SourceRunsourcesourceRunLoader.load(request.sourceRunId());ReplayEnvironmentenvenvironmentFactory.create(source,request);ReplayExecutionexecutionreplayExecutor.execute(env);returncomparator.compare(source,execution);}}二十九、Environment FactorypublicrecordReplayEnvironment(AgentClockclock,ModelGatewaymodel,ToolGatewaytools,PolicyEnginepolicy,KnowledgeGatewayknowledge,GraphDefinitiongraph,ReplayManifestmanifest){}Replay 的关键不是再跑一遍代码而是重新构建正确环境。三十、Exact Replay真的能100%一致吗不能保证。LLM 本身可能仍有Non-determinism Provider Backend Drift所以 Exact Replay 不是要求字节级完全相同而是比较关键决策 Tool Selection Task Outcome Failure Code三十一、定义Behavioral EquivalencepublicrecordReplayComparison(booleansameOutcome,booleansameToolSequence,booleansameSideEffects,doubleanswerSimilarity,ListTrajectoryDiffdiffs){}如果最终措辞不同没关系如果原来没调用refund 现在调用refund就是重大差异。三十二、Trajectory DiffpublicrecordTrajectoryDiff(DiffTypetype,StringsourceStep,StringreplayStep,Stringexplanation){}类型MODEL_DECISION_CHANGED TOOL_CHANGED ARGUMENT_CHANGED POLICY_CHANGED OUTCOME_CHANGED三十三、Counterfactual Replay这是最有价值的调试方式之一。例如事故Agent发错邮件你怀疑Prompt v17做历史Context 历史Tool Fixture 历史时间 ↓ 只换Prompt v18看是否仍然发错这比拿今天新数据测试更有说服力。三十四、Model Swap Replay同一个事故旧模型 → 新模型比较Tool选择 Loop 成本 Outcome可以直接评估模型升级是否真的修复生产失败。三十五、Fork from Step完整 Run 可能 40 分钟。没必要每次从头跑。选择step_17之前使用历史 Event。从step_18重新生成。需要保存Checkpoint Snapshot三十六、Replay SnapshotpublicrecordReplayCheckpoint(StringrunId,StringstepId,longsequence,StringstateRef,StringstateHash,StringcontextSnapshotId,InstantlogicalTime){}加载以后继续。三十七、Artifact必须版本化Agent 生成代码Patch CSV 报告 图片Replay 引用artifact_id version content_hash不要只保存路径。文件内容变化后必须检测。三十八、Source Code ReplayCoding Agent 需要保存base_commit head_commit working_tree_patch dependency_lock toolchain_version否则两周后依赖更新测试结果完全不同。三十九、Environment Hash可以由OS Image Runtime Dependency Lock Model Profile Tool Version生成。publicrecordEnvironmentManifest(StringcontainerImageDigest,StringjdkVersion,StringpythonVersion,StringdependencyLockHash,StringtoolBundleHash){}四十、执行代码的Replay必须Sandbox历史 Prompt 可能包含恶意输入。历史代码也可能不可信。所以 Replay Worker网络默认关闭 生产Credential不可见 只读基础镜像 临时文件系统 CPU/Memory限额绝不能因为“这是内部历史任务”就放松。四十一、数据脱敏Replay Dataset 很容易包含用户消息 客户数据 密钥 邮件 合同所以 Artifact Store 要支持Encrypted Raw Sanitized Replay View一般工程师默认只看 Sanitized。高权限取证才访问 Raw。四十二、Redaction必须可重放如果每次脱敏规则变化Replay Input 也变化。所以保存redaction_policy_version以及raw_hash sanitized_hash四十三、删除请求怎么办用户要求删除个人数据以后Replay Artifact也必须遵守数据生命周期。不能因为“审计需要”就永久保留全部内容。需要Retention Class Legal Hold Deletion Propagation四十四、Failure Taxonomy我会把事故至少分成publicenumAgentFailureType{MODEL_DECISION,PROMPT,RETRIEVAL,TOOL,AUTHORITY,APPROVAL,SIDE_EFFECT,STATE,CONCURRENCY,INFRASTRUCTURE,POLICY,DATA}Replay 报告最终要归到这些类别。四十五、Failure Signature例如toolpayment.refund statusUNKNOWN retrytrue duplicate_side_effecttrue生成稳定 SignatureSIDE_EFFECT_UNKNOWN_RETRY_DUPLICATE以后同类事故自动聚合。四十六、不要让LLM自己定义最终Root CauseLLM 可以辅助总结 Trace。但最终 Root Cause 应该由Evidence Replay 规则 人工确认共同形成。例如“模型推理错误”这个结论太泛。更好的Prompt v17 未明确优先使用 customer_id 模型选择手机号检索 在重复号码数据下返回错误客户。 Prompt v18 Counterfactual Replay 50/50 不再复现。这才是可行动根因。四十七、Replay ReportpublicrecordFailureForensicsReport(StringincidentId,StringsourceRunId,StringfailureSignature,ListEvidenceRefevidence,ListReplayResultreplays,ListTrajectoryDiffcriticalDiffs,StringconfirmedRootCause,StringfixVersion,StringverifiedBy){}四十八、一个完整事故流程线上告警 ↓ 冻结Run Evidence ↓ Side Effect Reconcile ↓ 生成Failure Signature ↓ Exact Replay ↓ Counterfactual Replay ↓ 确认Root Cause ↓ 修复 ↓ Regression Case ↓ Canary这才是 Agent 的标准事故闭环。四十九、线上发现失败后第一件事不是“再跑一次”尤其涉及副作用。先Freeze Evidence否则后续日志轮转数据变化Tool结果覆盖Artifact更新会破坏证据。五十、Freeze EvidencepublicrecordEvidenceFreezeRequest(StringrunId,StringincidentId,SetEvidenceTypetypes,InstantretentionUntil){}冻结Event Trace Prompt Tool Fixture Artifact Approval Authority Side Effect Receipt五十一、Replay Worker最好独立集群不要和生产 Agent Worker 共用。原因历史任务可能恶意 需要不同网络策略 需要不同Credential 负载不可预测Replay ClusterNo Production Write Recorded Tool First Low Priority Separate Quota五十二、Replay也会很贵一次长 Agent Run50次模型调用跑Baseline Prompt v18 Model B Policy v9就变200次调用所以要做 Replay Budget。publicrecordReplayBudget(intmaxModelCalls,longmaxTokens,BigDecimalmaxCost,DurationmaxDuration){}五十三、优先重放“分歧最大的步骤”可以先用历史 Trace 找关键决策点例如Tool Selection Approval Side Effect Reviewer Rejection不需要每次全量。五十四、自动回归样本事故确认以后把 Source Run 转成Regression Case但不能直接保存所有 PII。流程Production Run ↓ Sanitize ↓ Freeze Tool Fixtures ↓ Define Expected Outcome ↓ Add to Eval Dataset五十五、事故案例最终要能进入CI例如INC-2026-081之后每个 Agent Candidate 发布前必须跑直到明确下线这个 Case。这叫Never Repeat Incident五十六、Replay和Shadow的区别Replay历史真实输入 冻结外部世界 离线重跑Shadow当前真实流量 候选版本同步运行 不影响用户两个都需要。Replay适合事故取证 历史回归Shadow适合未来发布验证五十七、Replay和Simulation的区别Replay尽量还原真实历史Simulation故意构造不存在的世界例如Payment API 50% Timeout属于 Simulation。同一 Runtime 可以支持两种模式但 Manifest 要明确。五十八、Replay APIPOST /api/replays{sourceRunId:run-9182,mode:PROMPT_SWAP,candidatePromptVersion:v18,startFromStep:planner-4,budget:{maxModelCalls:20,maxCost:3.0}}返回{replayId:replay-882,status:QUEUED}五十九、状态机publicenumReplayStatus{CREATED,PREPARING,RUNNING,COMPARING,COMPLETED,FAILED,CANCELED}PREPARING阶段做Evidence完整性检查 Fixture检查 版本检查 数据权限检查缺证据就不要假装 Exact Replay。六十、Evidence Completeness ScorePrompt 1 Context 1 Tool Fixture 1 Policy 1 Approval 1 Environment 1例如5 / 6报告Replay Confidence LIMITED而不是Exact Replay Successful六十一、Mainline Metricsagent_replay_total{ mode, result } agent_replay_fixture_miss_total{ capability } agent_replay_behavior_diff_total{ type } agent_unknown_side_effect_total{ capability } agent_reconciliation_total{ result } agent_incident_regression_case_total六十二、SLO我会设Critical Run Evidence Completeness 100% Side Effect UNKNOWN Auto Retry 0 Unreconciled Critical Side Effect 0 Replay using Production Write Credential 0 Confirmed Incident Regression Coverage 100%六十三、必须测试的故障窗口1. Tool成功网络响应丢失 2. Outbox写入成功Broker发送失败 3. Broker重复投递 4. Approval过期后执行 5. Prompt版本找不到 6. Tool Fixture缺失 7. Knowledge Snapshot已删除 8. State Migration失败 9. Replay误访问生产Write API 10. Hash Chain断裂六十四、一个最关键的自动化断言TestvoidunknownSideEffectMustNotRetry(){SideEffectRecordrecordfixture.unknownRefund();assertThrows(ReconciliationRequiredException.class,()-retryService.retry(record));verify(paymentGateway,never()).refund(any());}这条测试的价值可能比100个普通Prompt Eval都高。六十五、为什么“可解释执行”最终一定会走向Replay只给用户展示Agent说 “我这样做是因为……”不够。真正可解释应该能拿出当时输入 当时证据 当时规则 当时批准 当时Tool结果然后重新跑看解释是否成立。这和 CHIVE 的 Counterfactual 思路其实是同一种工程哲学解释必须能够经受干预验证六十六、本篇上线检查清单□ 每个Run可以生成Replay Manifest □ 模型、Prompt、Graph、Policy均版本化 □ Tool结果可以冻结为Fixture □ Replay默认不访问生产Write Tool □ Clock可注入并冻结 □ RAG保存Evidence ID与Hash □ Approval保存Action Hash □ Side Effect有UNKNOWN状态 □ UNKNOWN禁止自动Retry □ 每种关键副作用有Reconciler □ Event使用单调Sequence □ Audit Event形成Hash Chain □ Replay支持Exact/Model Swap/Prompt Swap/Fork □ State保留Raw与Migration版本 □ Artifact保存Content Hash □ Replay Worker独立Sandbox □ Raw Evidence加密默认使用Sanitized View □ 事故修复后自动沉淀Regression Case □ Critical事故进入发布回归集总结生产 Agent 真正进入复杂业务以后事故不会只是模型答错一句话而会出现模型做了错误决策 Tool已经执行 网络返回丢了 系统以为失败 然后又重试这类问题靠普通日志很难处理。Audit Ledger 解决“历史上记录了什么”Execution Replay 解决“能不能重新构造当时的执行环境”Failure Forensics 解决“能不能通过重放和对照证明根因在哪里”三者合起来Agent 平台才真正具备类似成熟分布式系统的事故处理能力。下一篇继续沿着这条生产治理主线生产级Agent20Agent SLO与错误预算——把Task Success、成本、延迟和副作用风险放进同一套可靠性指标。

相关新闻