Agent Failure Attribution
沿 Agent 的输入、语义推理、证据获取、runtime facts、DecisionAuthority、持久化与交付链定位第一个违反 Behavioral Contract 的节点,避免把所有线上异常都误归因给模型或 Prompt。
[!info] related notes
Agent Failure Attribution
[!note] 排障示例 F1–F9、L2–L4 和 BodySense 场景是本文的定位分类与设计案例,不表示已经发生的事故或当前代码布局。实际排障先核对同一 run、revision、时间窗口与契约版本。
现象
生产 Agent 出现“最终结果不对”时,最常见的错误反应是:
答案不对
→ 模型不够强?
→ 改 Prompt?
→ 换模型?
但一个 production-shaped Agent 的最终结果经过了很多层。模型只是其中一层。
正确目标是:
沿因果链从上游往下,找到第一个违反 Behavioral Contract 的节点。
这个“第一个坏点”是缩小范围的起点,不等于已证明唯一根因。分支、重试、并发和共享依赖可能形成多个故障点;应按实际依赖链关联证据,并用可区分假设的实验确认。
为什么 Agent 故障比普通函数更容易误归因
普通 pure function:
input
→ function
→ output
出错时变量相对少。
Agent 系统则可能是:
Durable Input
→ Context Builder
→ Configuration Resolution
→ LLM Reasoning
→ Tool Selection
→ Tool Runtime
→ Evidence Policy
→ Fact Extraction
→ DecisionAuthority
→ Persistence
→ Read Model
→ API
→ Frontend Projection
最终 UI 上一句错误文案,可能是上游任意一层导致。
因此“看到哪里错就改哪里”特别危险。
快速判断路径
把一条 Agent 业务链压缩成:
Input / Context
↓
Semantic Reasoning
↓
EvidenceGap
↓
Evidence Acquisition
↓
Runtime Facts
↓
DecisionAuthority
↓
Authorized Output
↓
Persistence
↓
Read Model / API
↓
Frontend / Delivery
排障时不要从 UI 反向猜模型,而应该从已保存的 DecisionTrace、Execution Provenance 和业务数据按顺序确认每个 transition。
“第一个 Contract Violation”为什么是关键
假设最终:
UI 展示了不该展示的 Candidate
你继续往上查发现:
Python identified critical gap ✅
Go fact criticalGapCount=1 ✅
Decision=ABSTAIN ✅
DB authorized candidates=[] ✅
API candidate=[] ✅
Frontend stale cache still has old candidate ❌
在这些证据属于同一 run/revision 且已验证缓存行为的前提下,最早观测到的 violation 位于 frontend/cache。
此时如果改 Prompt:
“遇到 critical gap 请更谨慎”
完全是在错误层修 bug。
所以调试目标不是“让症状消失”,而是确定:
first bad transition
七类典型 Failure
F1 · Input / Context Failure
现象: 模型遗漏了本来应该考虑的重要信息。
先检查:
Durable Domain State
→ pinned revision
→ Go/API context construction
→ Python runtime actually received input
例子:
BodyState R53:
right_thumb_numbness = true
Python context:
right_thumb_numbness 字段丢失
模型可能只是“基于错误输入正确推理”。此时调 Prompt 会把根因藏得更深。
常见根因
- pin 错 BodyState revision;
- Context Builder 漏字段;
- DTO rename 后 mapper 没更新;
- profile/history filter 过度裁剪;
- serialization 默认值把 UNKNOWN 变成 false;
- resume 时加载了 current state,而不是 frozen run state。
最小证据
比较:
source durable state
vs
HTTP payload
vs
Python deps/runtime input
不要一上来分析模型输出。
F2 · Semantic Reasoning Failure
现象: 输入完整、工具事实正常,但模型没有识别关键候选或关键 EvidenceGap。
例如:
输入:右拇指麻木 + 颈旋转加重
模型:完全没有提出 neural involvement
或本应提出 critical gap,却完全没有识别。
这才主要属于:
- Model capability;
- Prompt;
- schema 引导;
- few-shot;
- semantic eval coverage。
如何证明是 F2
你必须先证明:
input correct
configuration correct
model actually received expected context
这些是定位 F2 的前提,还需由审定的参考结果或 rubric 说明语义输出为何不合格,并检查实际 provider、采样波动及配置交互;不能仅靠一次不同回答认定模型缺陷。
F3 · Evidence Acquisition Failure
现象: Gap 已经识别正确,但系统选择了不允许的获取方式,或者越过 budget / source policy。
典型例子:
G7 = “这个用户是否肌力下降?”
kind = user_fact
模型分类正确,但 runtime 竟允许:
search_knowledge()
→ 用一般医学文章“证明”该用户肌力下降
这里根因是 Acquisition Policy / Tool contract,不是模型对疾病的推理能力。
F3 还包括基础设施失败
DB timeout
Embedding executor saturated
provider 429
knowledge source unavailable
这些会导致 Gap unresolved。
如果没有 EvidenceAttempt / stopping reason,容易误判成:
“模型没找到证据”
实际可能是系统根本没完成检索。
F4 · Runtime Fact Failure
现象: 上游结果正确,但结构化事实在边界转换中丢失或变形。
例如 Python 实际返回:
unresolved_critical_gaps = [G7]
Go 却提取成:
criticalGapCount = 0
应检查:
Python schema
→ HTTP serialization
→ Go DTO
→ fact extraction
这是 contract propagation 问题。
F4 为什么很隐蔽
Policy 之后的所有层都会“忠实地执行错误 facts”。
例如:
criticalGapCount=0 ❌
→ Decision ALLOW_NORMAL
→ DB persist candidate
→ UI display candidate
最终看起来像 Policy 和 UI 都错,其实它们可能都只是消费了上游错误事实。
F5 · DecisionAuthority Failure
现象: runtime facts 已经正确,但 policy 产生了违反确定性规则的 authority。
例如:
criticalGapCount = 1
policy = v1
→ ALLOW_NORMAL ❌
若 v1 明确规定 critical unresolved gap 必须 ABSTAIN,那么模型已经不是主要嫌疑对象。
应检查:
- policy revision 是否正确;
- rule order;
- fail-closed 逻辑;
- DecisionAuthority implementation;
- ApplyDecision 是否真正执行了 authority。
Decision 计算正确但 ApplyDecision 错,也要分开
Decision = ABSTAIN ✅
ApplyDecision still keeps candidate ❌
严格说第一个 violation 已经不在 pure policy,而在 authority application/side-effect boundary。
所以实际工程可以把 F5 再拆:
F5a Decision Calculation
F5b Decision Application
F6 · Persistence Failure
现象: 内存里的授权结果正确,用户当前也可能暂时看起来正常,但数据库没有保存关键 trace/provenance 或保存了错误状态。
例如:
Runtime:
Decision = ABSTAIN
G7 unresolved
Database:
DecisionTrace = {}
这种问题会让:
- audit;
- replay;
- incident investigation;
- regression extraction
在以后全部失真。
另一类 Persistence Failure:事务不一致
Treatment acceptance:
revision accepted ✅
current pointer update ❌
会造成两个 durable truth 冲突。
所以 persistence correctness 不只是“INSERT 成功”,还包括 transaction invariant。
F7 · Delivery / UI Failure
现象: Authority、authorized output 和数据库都正确,但最终用户仍看到被禁止的结果。
典型 BodySense case:
BodyState red flag ✅
Python new_red_flag = true ✅
Go Runtime Fact = true ✅
Decision = ESCALATE ✅
ApplyDecision candidates=[] ✅
DB safety_blocked ✅
UI 仍显示 HIGH confidence candidate ❌
这时首先调查:
Repository read
→ Service/Public Read Model
→ Handler serialization
→ API response
→ frontend query/cache/store
→ render
前三四层已经有 trace 证明正确,就没有理由先换模型。
F8 · Configuration / Identity Failure(生产中值得单独列出)
虽然可以归到 Input/Runtime,但 Agent 平台里它非常重要:
Go selected C15
Python resolved C16
或:
resume should use C15
but current production pointer is C17
runtime silently switches to C17
这会导致同一个业务 run 内行为身份漂移。
检查:
selected configuration
resolved manifest
execution provenance
resume identity
这类问题往往不是“模型随机”,而是 configuration control plane 断裂。
F9 · Async / Resource Boundary Failure
L4 后还应该增加一个常见类别:
sync DB blocks event loop
local embedding blocks event loop
pool exhaustion
executor saturation
它们最终可能表现成:
tool timeout
stream stalls
interrupt slow
critical EvidenceGap unresolved
如果只从业务结果看,很容易归因到 retrieval/model。
所以 performance/concurrency 也可能成为 Agent correctness root cause。
“第一个 Contract Violation”原则
假设观察到:
Input ✅
Reasoning ✅
Gap ✅
Acquisition ✅
Runtime Fact ❌
Policy Output ❌
UI ❌
不要仅凭传播后的三个症状就认定存在三个独立根因。
Policy 与 UI 很可能只是消费了上游错误数据。
更优先的 root cause 是:
Runtime Fact propagation bug
[!important] 调试原则 最终哪里表现错,不等于根因就在哪里。找从上游开始第一次偏离契约的位置。
一张实用 Fault Localization 表
| 已验证到哪层 | 下一步优先检查 | 不要先做 |
|---|---|---|
| Durable input 就错 | source state / revision pinning | Prompt tuning |
| Input 对,semantic output 错 | model/prompt/schema | DB/UI |
| Gap 对,tool path 错 | acquisition policy/runtime | Model benchmark |
| Tool 对,facts 错 | DTO / mapper / fact extraction | DecisionPolicy |
| Facts 对,decision 错 | policy revision/rules | Prompt |
| Decision 对,DB 错 | transaction/repository | Model |
| DB/API 对,UI 错 | query cache/projection/render | Backend rewrite |
DecisionTrace 如何把“AI 抽风”变成可定位问题
假设线上报告:
“为什么有 critical gap 还展示了 Candidate?”
可以按因果链问:
1. BodyState input 对吗? → yes
2. Python 有没有识别 G8? → yes
3. G8 是否 unresolved + critical? → yes
4. Go criticalGapCount 是否 = 1? → yes
5. DecisionPolicy 输出什么? → ABSTAIN
6. ApplyDecision candidates? → []
7. DB 保存什么? → []
8. API/UI 展示什么? → C17 ❌
故障范围瞬间缩小到 read/delivery path。
这说明 trace 的价值不只是:
LLM latency = 800ms
tokens = 1200
更重要的是保存业务因果链。
Configuration Provenance 如何帮助归因
如果同一个 case 昨天正常、今天异常,先对比:
Configuration ID
Execution provider/model revision
Evidence snapshot
DecisionPolicy revision
如果 configuration 不同:
可能是 behavior revision regression
如果 config 相同但 provider fallback 不同:
可能是 execution/infrastructure variance
如果都相同但 Evidence 版本不同:
可能是 knowledge environment drift
Provenance 能防止所有变化都被叫做“模型不稳定”。
Evidence 与 Hypothesis-Driven Debugging
Fault localization 与 Hypothesis-Driven Debugging 一致:
- 明确当前已验证事实;
- 为最早可能故障层提出假设;
- 找能区分假设的证据;
- 不在尚未证伪上游问题时大范围修改下游;
- 修复后建立对应 regression / behavioral contract test。
一个推荐调试模板
Observed symptom:
用户看到不该出现的 candidate
Expected contract:
critical gap → no normal candidate delivery
Checkpoint 1 Input:
PASS
Checkpoint 2 Semantic gap:
PASS
Checkpoint 3 Acquisition:
PASS
Checkpoint 4 Runtime facts:
PASS
Checkpoint 5 Decision:
PASS (ABSTAIN)
Checkpoint 6 Persistence:
PASS (candidates=[])
Checkpoint 7 API:
PASS
Checkpoint 8 UI cache:
FAIL
Root cause:
stale query cache resurrected historical raw proposal
这种格式比:
“AI UI 有时会显示错”
更有工程价值。
修复后的回归验证
故障定位完成后,测试应覆盖被破坏的 contract,而不仅仅是复现最终 UI 文案。
例如 F7 delivery bypass 的回归测试应证明:
Decision = ABSTAIN/BLOCK/ESCALATE
→ API/read model 不得暴露 ordinary candidates
→ frontend 不得从 raw proposal / stale cache 恢复候选
如果事故具有通用价值,还应进入:
production incident
→ frozen replay/regression case
→ eval dataset
→ future qualification
为什么事故修复应该补“最早层”的测试
如果 root cause 是:
fact extraction dropped critical gap
只写一个 E2E:
UI should not show candidate
虽然能防最终症状,但定位能力弱。
更好同时加:
unit: mapper must produce criticalGapCount=1
integration: policy must ABSTAIN
E2E: UI must not show candidate
测试金字塔对应因果链各层。
Failure Attribution 与 Eval 的闭环
失败分类可以直接形成 Eval slice:
F1 context-loss regression
F2 reasoning challenge
F3 evidence-policy violation
F4 contract propagation
F5 authority invariant
F7 delivery bypass
这样 Eval 不只是“模型质量集”,而是整个 Agent platform 的 behavioral regression suite。
L2 Treatment 中典型故障链
Case:过期 Proposal 被接受
检查:
Proposal generated at R50 ✅
Current BodyState R51 ✅
Material change exists ✅
Acceptance facts say no material change ❌
Decision ALLOW_ACCEPTANCE ❌
第一个 violation 在 acceptance fact extraction,而不是 Treatment Agent proposal。
Case:Outcome 没投影 BodyState
Outcome persisted ✅
BodyState projection missing ❌
Review not triggered ❌
如果契约要求同步投影,或已超过异步投影的约定时限且确认交付失败,应调查 feedback projection;若仍在允许延迟内,暂时缺失还不能证明故障。
L3 Consultation 中典型故障链
Case:重复 Tool Card
Server event log has one tool.call ✅
Live SSE delivers duplicated event twice
Reducer seq guard missing ❌
UI renders two cards ❌
若传输契约允许至少一次交付,重复事件本身可预期,缺少幂等投影才是问题;若服务端承诺去重,还要核查该边界。不能仅凭双卡片断言具体组件无责。
Case:断线后消息缺失
Run continues ✅
Durable event exists ✅
client maxSeq=10 ✅
recovery calls after_seq=0
projection duplicates/misses events(需核对恢复契约)
after_seq=0 本身不一定错误:全量重放并清空重建或按事件 ID 去重可以正确恢复;增量追加模式则通常应从已确认游标继续。需检查响应分页、事件保留范围、run identity 与 reducer 行为,再判断是否是游标错误。
L4 Async/RAG 中典型故障链
Case:EvidenceGap 经常 unresolved
不要立刻认为 knowledge base 不够。
检查:
Gap correct ✅
query correct ✅
embedding started ✅
local transformer blocks loop 800ms ❌
request timeout
Attempt = timeout
Gap unresolved
若已测得事件循环阻塞与该请求超时的因果关系,并通过卸载阻塞工作或限制并发的对照验证,才可确认 async resource boundary 是原因;单个 800ms 记录只是调查线索。
什么时候才值得改 Prompt / Model
针对语义失败,先核实该 case 的相关依赖:
Input correct
Configuration correct
Tool/evidence runtime correct
Facts correct
Policy correct
Delivery correct
并且 failure 可以通过明确 rubric 或受控对照定位到 semantic reasoning 层。这里列的是排查维度,不要求先证明整个系统所有层永远无缺陷;必要的隔离实验可以并行验证多个候选原因。
这时 Prompt/Model tuning 才是针对根因的动作。
否则改 Prompt 很可能只是让某些 case 偶然绕过底层 bug。
自测题
- 为什么最终答案错不能直接归因给模型?
- “第一个 Contract Violation”原则是什么?
- F2 Semantic Failure 成立前必须先证明什么?
- F3 Evidence Failure 为什么可能其实是 DB/Event Loop 问题?
- Runtime Fact Failure 为什么会让 Policy/UI 看起来同时错误?
- Decision calculation 与 ApplyDecision 为什么要分开?
- Configuration identity mismatch 为什么值得单列?
- 修复 root cause 后为什么最好同时补 unit/integration/E2E contract tests?
- Production incident 如何进入 Eval dataset?
- 什么情况下才应该优先改 Prompt/Model?
常见误区
- 最终答案错 = 模型错:Agent 是纵向系统,模型只是其中一层。
- 看到 Policy 输出错就一定是 Policy bug:先确认 Policy 消费的 runtime facts 是否正确。
- DB 正确就代表用户看到的一定正确:read model、API、cache、frontend 都可能旁路授权结果。
- 修 Prompt 最快:如果真正问题在 contract propagation 或 authority,Prompt 调优不仅无效,还会增加变量。
- 性能问题不会影响业务正确性:Async/RAG 阻塞可能造成 timeout、Gap unresolved 和错误降级。
- 一个 E2E 回归测试就够了:最好在 first bad transition 所在层增加更窄的 contract test。