← [ BACK_TO_NOTES ]

AI Evidence-Grounded Bug 的端到端修复流程

从发现 unsupported AI output 开始,经过冻结事故、写不变量、First Bad Boundary 归因、Evidence Contract 设计、双层 durable gate、migration/replay/eval/E2E,最后走 Issue→PR→CI→Merge 的完整修复闭环。

#howto #growing #ai #test #agent #bodysense

[!info] related notes

  • 前置笔记:
  • 相关 MOC:
  • 相关资源:

AI Evidence-Grounded Bug 的端到端修复流程

目标

这是一套专门处理下面这类 production Agent Bug 的完整流程:

输入/证据不足或不支持某个 claim
→ AI 却生成了 claim / observation / decision
→ 系统把它继续投影到业务状态

典型例子:

没有体态证据
→ Assessment 生成“高低肩”
→ BodyState 保存
→ UI 展示

这篇是执行顺序页。具体概念不重复展开,分别下钻到原子笔记。

通用的 Issue / Branch / Commit / PR / CI 操作见 Bug 修复完整工作流。


全流程总图

发现异常
  ↓
冻结事故现场
  ↓
Actual / Expected
  ↓
写 Hard Invariant
  ↓
画 Boundary Chain
  ↓
收集 Replay / Provenance / Raw Output
  ↓
列可证伪假设
  ↓
Find First Bad Boundary
  ↓
区分 Introduction / Escape / Propagation
  ↓
写最窄 RED Contract Test
  ↓
设计 Evidence Contract
  ↓
缩小 Model Authority
  ↓
Primary Runtime Gate
  ↓
Durable Owner Revalidation
  ↓
No-Evidence Deterministic Path
  ↓
Schema / Config / DB Versioning
  ↓
Replay / Rollout / Regression 对齐
  ↓
Web Backward Compatibility
  ↓
Deterministic Eval
  ↓
Real E2E
  ↓
Full Repo Gates
  ↓
Diff Review
  ↓
Commit / PR / Required CI
  ↓
Merge
  ↓
Post-Merge Verification

Phase A · 先诊断,不要先修

Step 0 · 冻结事故现场

第一件事不是改 Prompt,而是保存“当时世界”。

尽量获取:

business entity/report ID
frozen input / replay input
configuration ID
prompt/schema/policy revisions
execution provenance
raw model output
validated output
persisted DB state
API response

如果事故现场会继续变化,优先保存可重放 artifact。

为什么:

current state
≠ historical run state

详见 Historical Replay 与 Counterfactual Replay。

Step 1 · 写 Actual / Expected

禁止写成:

AI 结果不太对

应该写:

Actual:
无 posture evidence,却生成 posture_asymmetry

Expected:
无 posture evidence 时,不得生成 posture-derived observation

目标是让错误可以成为 assertion。

Step 2 · 把需求压成 Hard Invariant

例如:

posture observation
⇒ posture evidence exists

或者更成熟:

accepted observation
⇒ exactly one authoritative evidence ref
⇒ ref admissible
⇒ source/kind compatible

这一步把“产品感觉”变成 Behavioral Contract。

Step 3 · 画完整 Boundary Chain

不要只画模型层。

例如:

DB / Upload / BodyState
→ Go input assembly
→ Frozen Replay
→ HTTP request
→ Python dependencies
→ Prompt
→ LLM raw output
→ Pydantic validation
→ Go validation
→ Domain projection
→ DB
→ Read Model / API
→ UI

每条箭头都是候选 fault boundary。

Step 4 · 建 Boundary Table

对每一层记录:

BoundaryInputOutputNormal?
source → assembler??✅/❌
assembler → replay??✅/❌
replay → model??✅/❌
model → validator??✅/❌
validator → durable??✅/❌

最重要的问题始终是:

这一层进去的对吗?
这一层出来的对吗?

Step 5 · 列出可证伪假设

不要只留一句:

H = 模型 hallucination

应同时覆盖其它层:

H1 hidden evidence
H2 input assembler stale/incorrect
H3 historical BodyState contamination
H4 RAG/deps injected context
H5 LLM generated unsupported output
H6 transform/mapper created the bad claim
H7 DB/read model correct but UI wrong

假设的价值在于设计下一次观测,而不是让文档看起来专业。

Step 6 · 用 Frozen Replay 先排除隐藏 Evidence

在说“无证据”之前,逐项检查:

profile
body_state
report_indicators
posture_analysis
images
rag_context
other tool observations

只有当真正的历史输入干净,才能把问题继续归因到 generation。

Step 7 · 找 First Bad Boundary

原则:

last known good
→ first known bad

如果:

model input clean ✅
raw model output already contains bad posture claim ❌

则:

first bad boundary = model generation

通用方法见 Agent Failure Attribution。

Step 8 · 再找 Error Escape Point

找到错误第一次产生还不够。

继续问:

“哪一道 gate 本应该拦住它?”

例如:

LLM hallucination             ← introduction
Pydantic accepts              ← escape #1
Go accepts                    ← escape #2
BodyState persists            ← propagation / side effect
UI displays                   ← delivery

一个 production incident 经常同时包含:

stochastic semantic failure
+
missing deterministic guard

Step 9 · 区分 Propagation,不要误修下游

如果 DB 已经保存错误事实,而 UI 只是忠实展示:

UI 不是 root cause

不要优先写:

if no evidence:
    hide card

这只遮住症状,错误 durable state 还在。

具体案例见 无体态证据却生成高低肩的排查。


Phase B · 先写会失败的 Contract Test

Step 10 · 在 First Bad Boundary 附近写最小 RED Test

不要先写庞大的 E2E。

最小反例:

Given:
  no posture evidence

Model deliberately returns:
  posture_asymmetry

Expect:
  Evidence Gate rejects

模型最好用 deterministic fake / TestModel,故意输出错误结果。

这样测试验证的是:

runtime guard works even when model misbehaves

而不是:

希望某个真实模型今天比较听话

Step 11 · 同时写 Positive Case

只写拒绝测试容易把 gate 写成“一律拒绝”。

必须补:

valid posture evidence
+ compatible posture selection
→ accept

然后增加边界反例:

nonexistent ref → reject
wrong source/kind → reject
duplicate ref → reject
unverified evidence → unavailable

Phase C · 设计真正的 Trust Boundary

Step 12 · 不要把 Prompt Hardening 当最终修复

Prompt 可以加强,但只能作为 soft guidance:

Prompt
→ encourage correct behavior

真正 durable rule 必须是:

Runtime
→ enforce correct behavior

如果事故的影响是 durable state / side effect,Prompt-only 通常不足。

Step 13 · 定义 Authoritative Evidence Catalog

应用先从 frozen input 构建证据集合,例如:

body_state:fact:<uuid>
body_state:observation:<uuid>
report:upload:<uuid>:indicator:<index>
posture:upload:<uuid>:finding:<index>

模型不能凭空声明 ref。

再过滤:

rejected
unverified
inactive
excluded_from_reasoning

得到本轮真正可用的 catalog。

Step 14 · 实现 Evidence Consistency Contract

至少验证:

ref exists
ref unique
source allowed
kind compatible
evidence lifecycle admissible

完整原则见 Agent Evidence Consistency Contract。

Step 15 · 评估 Model Authority 是否过大

如果模型当前拥有:

kind
label
description
severity
confidence
summary
score

逐个问:

这个字段真的必须由模型拥有吗?

如果任务只是“从已有证据中选择 observation 类型”,就把权限收缩成:

kind + evidence_ref

Step 16 · 对 Durable Prose 使用 Deterministic Rendering

当正确 ref 仍可能被模型扩写时:

correct provenance
≠ claim entailment

考虑:

model = selector
application = renderer

见 Selection-Only Agent Output 与 Deterministic Rendering。

这是从结构上消掉 claim expansion,而不是再叠一个 LLM Judge。

Step 17 · Primary Runtime 先验证一次

通常模型运行服务最了解:

  • Pydantic schema;
  • exact runtime catalog;
  • model output;
  • prompt/config。

所以可在 Python/AI runtime 做第一层:

raw selection
→ typed validation
→ evidence validation
→ deterministic rendering

Step 18 · Durable Truth Owner 再独立验证

如果真正保存 Domain State 的是 Go/Java/backend:

不要完全相信 AI Service 已经校验好

在写入前:

rebuild catalog from frozen request
→ revalidate ref/source/kind
→ rerender durable representation
→ persist

这是 defense in depth,也是明确的 trust boundary。


Phase D · 把“不知道”也变成确定性行为

Step 19 · 建 No-Evidence Short-Circuit

如果 catalog 为空:

不要调用模型

直接:

status = insufficient_information
observations = []
model_executed = false

见 No-Evidence No-Model Short-Circuit。

Step 20 · 不要用假默认值表达 Unknown

避免:

unknown → grade D
unknown → score 0
unknown → score 50

这些值看起来都像真实业务判断。

应该显式表达:

not assessed
insufficient information
missing evidence

Phase E · Versioning 与 Persistence Migration

Step 21 · 判断这是不是 Behavior Contract 变更

如果你改变了:

model output authority
prompt contract
schema
Evidence Policy
Decision Policy

这不是“小修一下 validator”,而是行为 identity 改变。

应产生新的:

configuration revision
prompt revision
output schema revision
evidence policy revision
decision policy revision

旧 configuration 保留用于 historical replay,不要原地改写。

Step 22 · 设计 DB Migration

新 contract 可能需要:

contract_revision
evidence_coverage
evidence_gaps
nullable legacy fields

同时明确:

historical rows keep historical semantics
new rows use new semantics

不要把旧数据“升级成好像当时就是新 contract”。

Step 23 · 审查 Down Migration 的业务语义

如果新世界表达:

grade = NULL = not assessed

旧世界要求:

grade NOT NULL

不要为了让 down 成功而写:

NULL → D

应判断是否要 fail closed。

见 语义不可逆 Schema Downgrade 的 Fail-Closed 策略。

Migration 测试至少:

no-new-data: up → down → up = PASS
with-new-semantic-data: down = REFUSED

Phase F · Replay / Rollout / Regression 不能成为旁路

Step 24 · Historical Replay 保留历史语义

旧报告:

old input + old config + old contract

继续可以解释历史。

不要用新 contract 改写历史 artifact。

Step 25 · Counterfactual Replay 走同一个新 Gate

如果新 config 的 replay:

frozen input
→ model
→ directly compare raw output

就绕过了 serving contract。

正确:

frozen input
→ candidate config
→ evidence validation
→ deterministic rendering
→ compare

Step 26 · 把 Production Incident 导出成 Regression Case

事故不要只留在 Issue。

保存:

frozen input fingerprint
contract revision
configuration identity
expected hard invariant
expected model_executed
expected coverage/gaps
forbidden legacy fields/side effects

让同类事故进入未来 qualification。


Phase G · API / Web 向后兼容

Step 27 · 不要让新旧 Contract 假装成同一个类型

如果历史报告有:

health_grade
dimension_scores

新报告有:

contract_revision
evidence_coverage
evidence_gaps

前端应使用 discriminated union 或显式版本分支:

v1 report
vs
v2 report

不要给旧报告补一个假的 {} coverage 然后告诉 TypeScript“它是 v2”。

Step 28 · Error Mapping 不泄漏 Raw Model Output

Evidence rejection 应成为明确业务/上游错误类别,例如:

AI runtime 422
→ backend ErrOutputRejected
→ public API 502 / controlled error

同时不要把完整模型输出、Prompt、内部 evidence 直接泄漏给客户端。


Phase H · 建立 Qualification,而不是只写几个 Unit Tests

Step 29 · 写 Deterministic Policy Eval

Evidence Contract 最适合确定性 dataset。

覆盖:

profile-only is not health evidence
valid source-only rendering
unverified evidence exclusion
wrong source/kind
nonexistent ref
duplicate ref
posture governed finding
report vs injury domain separation

它验证的是 contract behavior,不依赖某个供应商模型质量。

Step 30 · Eval 接入正式 Repo Gate

不要让新 eval 只能手动运行。

加入:

Nx / Make / task target
→ repo verify/release gate
→ CI quality lane

否则它很快会腐化成没人跑的脚本。


Phase I · E2E 必须证明 Durable Invariant

Step 31 · 跑 Model-Executed Grounded Path

真实 E2E 应验证:

confirmed evidence exists
→ Assessment executes model/selector
→ observation exact ref
→ deterministic source restatement
→ BodyState candidate persisted
→ provenance says model_executed=true

Step 32 · 跑 Zero-Evidence Path

另一条 E2E:

no admissible health evidence
→ insufficient_information
→ model requests = 0
→ BodyState revision unchanged
→ provenance model_executed=false

这两个正好覆盖 stochastic path 与 deterministic path。


Phase J · 全量工程验证

Step 33 · 从小到大跑 Gate

顺序建议:

focused unit
→ related service tests
→ language full tests
→ lint/typecheck/vet
→ Web tests
→ migration validation
→ contract eval
→ focused E2E
→ repo-level verify/release

这样失败时容易定位,不会一上来跑 15 分钟全仓库然后不知道哪层红。

Step 34 · 对 Migration 做真实数据库验证

不要只看 SQL 文件。

用 disposable database 验证:

full up
latest down
latest up again
semantic incompatible down refusal

禁止拿真实开发/生产数据随便 reset 来做 migration experiment。

Step 35 · 最终重启运行态再 E2E

特别是使用:

  • dev reload;
  • Go compiled process;
  • Python reload;
  • Vite

时,要防止测试其实还跑在旧进程上。

最终:

stop old app processes
→ start exact final source
→ health check
→ rerun focused E2E

Phase K · Diff / Architecture Review

Step 36 · 看 Diff Stat,也看职责边界

不仅问:

测试绿了吗?

还问:

是不是把 600 行 contract 全塞进 Service?
是否出现重复 evidence builder?
是否有旁路仍使用旧 schema?
是否有 test stub 绕过真实 governance?

如果功能正确但结构糟糕,在提交前做纯结构重构,然后重新跑相关 gate。

Step 37 · 审计所有旁路

至少搜索:

E2E stub
deterministic fake
replay
rollout shadow
regression export
old config pointer
legacy grade/score refs

每个旁路都问:

“它能否绕过新 contract?”

Step 38 · 检查 Provenance 自洽

例如 no-evidence 结果如果写:

execution_provenance = skipped_no_evidence

但 generation trace 仍写:

status = generated
model_executed = true

这也是 bug。

Trace 不只是日志,还是未来 replay / eval / incident attribution 的事实。


Phase L · GitHub 修复闭环

Step 39 · Issue 记录 Problem 与 Acceptance Criteria

Issue 不要只写“修 AI 幻觉”。

应写:

Problem:
unsupported observation can enter durable BodyState

Acceptance:
- unsupported source/kind rejected
- no-evidence path does not call model
- replay cannot bypass gate
- historical reports remain readable
- migration semantics safe
- regression tests/eval/E2E exist

Step 40 · 从最新主干创建 Fix Branch

遵循仓库 Git 流程,保持 scope 单一。

Step 41 · Commit 前跑 Staged Diff Review

至少:

git diff --check
git diff --staged --check
git diff --staged --stat

确认:

  • 没有 secrets;
  • 没有生成物污染;
  • config IDs 一致;
  • migration checksum 正确;
  • 文档描述和最终实现一致。

Step 42 · PR 写清根因,不只列文件

推荐 PR Summary 结构:

Root cause
→ shape validation without evidence consistency

Contract change
→ selection-only + exact evidence refs

Durable safety
→ Python gate + backend revalidation

Compatibility
→ legacy reports preserved

Migration
→ fail-closed downgrade

Verification
→ unit / full / eval / E2E / repo gate

Step 43 · Required CI 全绿后才 Merge

本地 REPO_QUALITY=PASS 只是证据之一。

仍要等待仓库真正 required checks,例如:

commit lint
governance
quality
database
browser E2E
experience
delivery observation

不要把“本地全绿”当成可以绕过 protected main 的理由。

Step 44 · Merge 后同步 Main,再区分 Deploy

Merge:

code entered main

不等于:

staging deployed
production released

最后明确记录:

PR merged?
Issue closed?
main synced?
post-merge CI?
staging?
production?

不要混成一句“已经上线”。


Definition of Done

一次 Evidence-Grounded Agent Bug 真正收口,至少满足:

[ ] historical incident frozen
[ ] Actual / Expected 明确
[ ] hard invariant 写出
[ ] boundary chain 完整
[ ] first bad boundary 已定位
[ ] introduction / escape / propagation 已区分
[ ] first bad boundary 有最窄 regression test
[ ] evidence truth 由应用拥有
[ ] unsupported output deterministic fail closed
[ ] durable owner 独立 revalidation
[ ] model authority 已评估并最小化
[ ] no-evidence path 不制造假事实
[ ] configuration/schema/policy 已正确版本化
[ ] historical replay 语义保留
[ ] counterfactual replay 不绕 gate
[ ] DB migration + downgrade 语义验证
[ ] API/Web 兼容旧 contract
[ ] deterministic policy eval
[ ] real E2E 证明 durable invariant
[ ] full tests/lint/typecheck/build/migration gate
[ ] final runtime restart + E2E
[ ] staged diff review
[ ] PR required checks green
[ ] merge/post-merge state verified
[ ] deployment state 单独确认

最短记忆模型

Freeze
→ Invariant
→ Boundaries
→ First Bad Boundary
→ RED Contract Test
→ Evidence Gate
→ Minimize Model Authority
→ Durable Revalidation
→ Version + Migrate
→ Replay + Eval + E2E
→ Repo Gate
→ PR / CI / Merge

核心不是“把某次 hallucination 修没了”,而是:

把一次事故转化成新的系统不变量、运行时 trust boundary、回归数据和发布门禁,使同一类错误以后结构上更难再次进入 durable truth。

创建于 2026/9/1 更新于 2026/10/16
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本