Agent Failure Attribution
沿 Agent 的输入、语义推理、证据获取、runtime facts、DecisionAuthority、持久化与交付链定位第一个违反 Behavioral Contract 的节点,避免把所有线上异常都误归因给模型或 Prompt。
[!info] related notes
Agent Failure Attribution
现象
生产 Agent 出现“最终结果不对”时,最常见的错误反应是:
答案不对
→ 模型不够强?
→ 改 Prompt?
→ 换模型?
但一个 production-shaped Agent 的最终结果经过了很多层。模型只是其中一层。
正确目标是:
沿因果链从上游往下,找到第一个违反 Behavioral Contract 的节点。
这个“第一个坏点”通常比最终看到错误的位置更接近 root cause。
为什么 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 ❌
第一个 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
然后才有资格说“模型推理错了”。
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 ❌
不要创建三个彼此独立的“AI bug / policy bug / UI bug”。
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 ❌
root cause 在 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 ❌
第一个 contract violation 在 event idempotency/reducer,而不是 React component 本身。
Case:断线后消息缺失
Run continues ✅
Durable event exists ✅
client maxSeq=10 ✅
recovery calls after_seq=0 ❌
projection duplicates/misses events ❌
root cause 是 recovery cursor,而不是 Agent runtime。
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。
什么时候才值得改 Prompt / Model
至少先证明:
Input correct
Configuration correct
Tool/evidence runtime correct
Facts correct
Policy correct
Delivery correct
并且 failure 仍发生在 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。