Agent Failure Attribution

沿 Agent 的输入、语义推理、证据获取、runtime facts、DecisionAuthority、持久化与交付链定位第一个违反 Behavioral Contract 的节点,避免把所有线上异常都误归因给模型或 Prompt。

#type / debug #status / growing #tech / ai #tech / dev / test #tech / architecture #tech / ops #resource / agent

[!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 反向猜模型,而应该从已保存的 DecisionTraceExecution 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 pinningPrompt tuning
Input 对,semantic output 错model/prompt/schemaDB/UI
Gap 对,tool path 错acquisition policy/runtimeModel benchmark
Tool 对,facts 错DTO / mapper / fact extractionDecisionPolicy
Facts 对,decision 错policy revision/rulesPrompt
Decision 对,DB 错transaction/repositoryModel
DB/API 对,UI 错query cache/projection/renderBackend 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 一致:

  1. 明确当前已验证事实;
  2. 为最早可能故障层提出假设;
  3. 找能区分假设的证据;
  4. 不在尚未证伪上游问题时大范围修改下游;
  5. 修复后建立对应 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。

自测题

  1. 为什么最终答案错不能直接归因给模型?
  2. “第一个 Contract Violation”原则是什么?
  3. F2 Semantic Failure 成立前必须先证明什么?
  4. F3 Evidence Failure 为什么可能其实是 DB/Event Loop 问题?
  5. Runtime Fact Failure 为什么会让 Policy/UI 看起来同时错误?
  6. Decision calculation 与 ApplyDecision 为什么要分开?
  7. Configuration identity mismatch 为什么值得单列?
  8. 修复 root cause 后为什么最好同时补 unit/integration/E2E contract tests?
  9. Production incident 如何进入 Eval dataset?
  10. 什么情况下才应该优先改 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。
创建于 2026/8/22 更新于 2026/8/23