返回 AIPA 笔记
AIPA Day 82

结构化错误恢复实装 — 让用户保持控制,而非自动重试

结构化错误恢复实装 — 让用户保持控制,而非自动重试

2026-09-04
error-recoverygraceful-degradationagent-ux

日期: 2026-09-04 阶段: Phase 3 - AML 调查 Copilot 标签: #error-recovery #graceful-degradation #agent-ux

核心问题

P3 的 AML Copilot 已经能跑通「证据汇集 → 类型学比对 → SAR 草稿 → HITL 复核」。但前面所有的笔记都默认每一步都成功。现实里 agent 的每一步都可能炸:规则引擎超时、检索返回空、LLM 输出不合契约、SAR 模板渲染缺字段。Day 11 的 6 类失败分类法(failureTaxonomy)告诉我们「失败长什么样」;今天回答失败发生后怎么办——这是 Fuselab 七模式(2025-08)里第四个模式「错误恢复」的实装。

三个子问题:

  1. 失败步骤怎么可解释地报错——给合规官看「Error 503」是合规事故级的产品失败,必须翻译成业务语言。
  2. 恢复路径怎么选——自动重试 / 修正输入重跑 / 人工接管,三条路什么时候走哪条?
  3. 错误类怎么映射到恢复路径——能不能复用 P1 的 6 类 taxonomy,让「失败归类」直接驱动「恢复决策」,而不是每个错误现想现办?

贯穿全篇的反直觉主线:好的错误恢复不是「悄悄自动重试到成功」,而是让用户始终知道发生了什么、且能随时夺回控制权。在 AML 这种合规场景,一个无声自动重试的 agent 比一个会报错停下的 agent 危险得多。

关键内容

A. 失败步骤的可解释报错:把异常翻译成业务语言

第一条铁律来自 AI UX Design Guide 的错误恢复模式(2026)——用大白话解释发生了什么

"plain-language error messages that explain what happened (e.g., 'We're at capacity' not 'Error 503')." "Always show that user work is saved to reduce anxiety."

对 AML Copilot 而言,「可解释报错」有三层,缺一层都算产品缺陷:

  1. 业务语义层:合规官看到的不是 stack trace,而是「类型学评估服务暂时不可用,已保留你已确认的证据集」。
  2. 归因层:这次失败属于 6 类 taxonomy 的哪一类(tool_failure / retrieval_miss / format_violation…)——这个分类不是给合规官看的,是给恢复路径选择器用的(见 C 节)。
  3. 状态保全层:明确告诉用户「你已确认的 8 笔证据、你改过的 Who 段落都没丢」。AI UX Guide 的原话是 "Preserve user context across errors — don't lose their input or workflow state."

这里有个被多数 demo 忽略的工程细节:错误本身是数据,不是异常。LangGraph 错误处理范式(2026)的核心洞察是「errors are data, not just exceptions — store them in state so the LLM can see what went wrong and adjust its approach」。落到本项目:失败时不能让异常冒泡到 React 错误边界把整页炸白,而要把错误结构化写进工作流 state,成为一条可渲染、可归类、可驱动决策的记录:

interface StepError {
  stepId: string                    // 哪一步失败(对齐 checkpointMachine 的 step id)
  failureClass: FailureClassId      // 复用 P1 的 6 类 taxonomy
  userMessage: string               // 业务语义层(给合规官看)
  technicalDetail: string           // 归因层(给排错/observability,不渲染给合规官)
  retriable: boolean                // 是否是瞬时错误(决定能否走自动重试路径)
  attempt: number                   // 第几次尝试(驱动退避与熔断)
}

反直觉洞察①(cleaned error text 不等于隐瞒,而是分层):直觉会觉得「把技术细节藏起来不给用户看」是不透明、违反 Day 5 的透明推理原则。错。真正的透明是分层——technicalDetail 进 observability(Day 22-25 的 OTel trace),userMessage 进 UI。AI UX Guide 明确:「use custom handlers that clean up error text to avoid leaking internal details to end users」,但同时要 "Transparency builds trust — users are forgiving when they understand what's happening"。两者不矛盾:透明的是发生了什么、状态有没有丢、下一步怎么办,隐藏的只是对合规官无意义的内部实现细节。在 AML 里多塞一行 KeyError: 'scores' 给合规官,不是更透明,是制造焦虑。

B. 恢复路径选择:自动重试 / 修正输入 / 人工接管

行业 2026 共识是分层升级而非二选一(Fastio/Cordum/Mantlr,2026):

"Use a layered approach: exponential backoff for transient errors, circuit breakers for persistent failures, fallback models for LLM unavailability, and human escalation for unrecoverable errors."

三条恢复路径的判定边界:

  1. 自动重试(auto-retry):仅用于瞬时错误(retriable=true:超时、限流、503)。带指数退避防止把已经在挣扎的下游服务打死——AI UX Guide 的警告 "Balance retry options with preventing users from overwhelming failing services"。退避公式:第 n 次重试等待 $t_n = \min(t_{base} \cdot 2^{n}, t_{cap})$,且有上限重试次数 N,超过 N 不再自动重试。
  2. 修正输入重跑(fix-and-retry):错误是确定性的、源于输入(如 LLM 输出 format_violation、检索 query 太宽 context_pollution)。盲目重试同样的输入会得到同样的失败——必须先修正输入再重跑:收紧检索 query、给 LLM 补一段格式约束、剔除被污染的噪声交易。这对应 LangGraph 的「self-fixing loop」——把错误文本喂回 LLM 让它换个方法。
  3. 人工接管(human takeover):错误不可自动恢复hallucinationtypology_misjudge——这些是语义错误,机器自己发现不了,发现了也不该自己改),或自动重试/修正已耗尽预算。这条路不是「失败」,是优雅降级——agent 仍然交出它已经做对的部分,把控制权交还给合规官,附带可恢复的工作状态。Mantlr(2026)的「I'm stuck」状态:「triggers human takeover with a resumable plan」。

熔断器横切这三条路:当某个下游工具持续失败(不是偶发),不能让每次调查都白白重试 N 次再降级——要用三态熔断器快速失败(Cordum,2026):

熔断态含义Copilot 行为
CLOSED正常正常调用工具,失败计数
OPEN失败超阈值跳过该工具直接走降级路径,不再浪费重试
HALF-OPEN试探恢复放一个探测请求,成功则回 CLOSED,失败回 OPEN

C. 错误类 × 恢复路径映射:让 taxonomy 驱动决策

把 A 节的归因和 B 节的三条路径接起来——复用 P1 的 6 类 taxonomy 当决策表的 key,这样恢复逻辑不是散落的 if-else,而是一张可审计、可单测的映射表:

FailureClassId(P1 taxonomy)severityretriable恢复路径理由
tool_failurecritical是(瞬时)/否(结构错)瞬时→auto-retry+退避;结构错→人工接管503/超时可重试;契约缺字段重试也没用
retrieval_misshighfix-and-retry(放宽/换 query)→ 仍空则人工同 query 重试必然再空,须改输入
context_pollutionhighfix-and-retry(剔噪声交易后重跑)输入里混了良性噪声,须先净化
format_violationmediumfix-and-retry(补格式约束,self-fixing loop)确定性输出错,给 LLM 补约束再生成
hallucinationcritical人工接管(强制)语义造假,机器不可自纠,合规红线
typology_misjudgehigh人工接管分类语义错,须合规官判断

恢复路径选择器是一个状态机,输入 StepError、输出下一动作:

        ┌─────────────────────────────────────────┐
        │  step 抛错 → 归类为 FailureClassId       │
        │  写 StepError 进 workflow state          │
        └──────────────────┬───────────────────────┘
                           ▼
                  熔断器 OPEN?  ──是──► 跳过工具 → [优雅降级/人工接管]
                           │否
                           ▼
              retriable && attempt < N ?
                  ┌────────┴────────┐
                 是                 否
                  ▼                 ▼
           [AUTO_RETRY]      路径 = map[failureClass]
           退避 t_n 后重跑          │
           attempt++          ┌────┴─────────────┐
           失败→回判定      fix-and-retry    human-takeover
                                │                 │
                          修正输入后重跑     冻结调查、保全 state
                          (≤M 次)           交还合规官 + resumable plan
                                │                 │
                          仍失败 → human ─────────┘

反直觉洞察②(自动重试到成功,反而是最危险的设计):在普通 SaaS 里,「用户根本没察觉就自动恢复了」是好体验。在 AML 里恰恰相反——一个 typology_misjudge 如果被 agent「自动重试」碰巧蒙对了,合规官就失去了一次本该人工介入的复核机会,而最终签发 SAR 的法律责任仍在合规官身上(Day 5 的 Article 50 编辑责任人逻辑)。所以本项目的恢复设计刻意不追求「无感自动恢复」:critical 类失败强制冒泡到人工,宁可打断流程也不让 agent 自作主张。好的错误恢复让用户保持控制感("keep the user in control"),而不是用自动重试把用户排除在决策之外。这与 Day 5「copilot 不是 autopilot」、Day 37「自动化卡住时有兜底人工降级路径」是同一条产品哲学的不同切面。

设计要点/决策表

要点决策理由
错误形态结构化 StepError 写进 state,不让异常冒泡炸页errors are data not exceptions(LangGraph 范式 2026)
报错语言业务语义层给合规官、技术细节进 OTel分层透明 ≠ 隐瞒;"We're at capacity" not "Error 503"
状态保全失败时显式展示「已确认证据/已编辑段落未丢」"preserve user context across errors",降焦虑
自动重试范围仅瞬时错误(retriable),指数退避 + 上限 N防止把挣扎中的下游打死
确定性错误fix-and-retry(改输入再跑),≤M 次同输入重试必然同失败
critical 类hallucination/tool 结构错→强制人工接管语义造假/合规红线不可机器自纠
熔断器三态 CLOSED/OPEN/HALF-OPEN 横切持续失败快速降级,不每次白重试
控制权不追求无感自动恢复,关键失败必打断让用户保持控制感而非被排除在决策外

对本项目的落地

  • 新建 src/aml/errorRecovery.ts:导出 StepError 类型(A 节)、classifyError(raw, stepId) → StepError(把原始异常 → 归类为 FailureClassId直接 import failureTaxonomy.tsFailureClassIdSEVERITY_WEIGHT,不另造分类)、以及核心决策函数 selectRecoveryPath(err: StepError, ctx: { attempt, circuitOpen }) → 'auto_retry' | 'fix_and_retry' | 'human_takeover'(实装 C 节映射表 + B 节退避/熔断判定)。映射表做成 const RECOVERY_MAP: Record<FailureClassId, RecoveryPolicy>,与 FAILURE_TAXONOMY 的 6 个 id 一一对应,单测断言两者 key 集合相等(防止新增 taxonomy 类时漏配恢复路径)。
  • 退避与熔断为纯函数backoffMs(attempt) = min(base * 2^attempt, cap)circuitState(failures, successes) → 'closed'|'open'|'half_open' 都做成无副作用纯函数(对齐 checkpointMachine.ts 的确定性纪律:不读时钟、不掷随机),时钟由调用方注入,便于单测。
  • UI 落 src/components/aml/AmlCopilot.tsx:在每步状态旁渲染失败态——失败时显示 userMessage(业务语言,amber 而非 harsh red,对齐 AI UX Guide「warm colors for capacity issues」)、一行「你的工作已保存」、以及 2-3 个恢复按钮(重试 / 修正后重跑 / 转人工复核),按 selectRecoveryPath 的返回值高亮推荐路径但不自动执行——把最终触发权留给合规官(呼应反直觉洞察②)。复用 AmlCopilot.tsx 现有的 reviewStatus 状态机(已含 returned 退回态),新增 error 态。
  • 与 HITL/durable 的接口human_takeover 路径不是简单弹窗,而是走 Day 37 的 approvalQueue 同一通道——失败的调查冻结成一个 ApprovalTask(kind 扩展一个 'error_review'),保全 state、附 resumable plan,合规官从待审队列接手。这样「错误接管」和「SAR 审批」共用一套持久化基础设施(Day 83 会把这条接口打通)。
  • 诚实标注errorRecovery.ts 头注明确 v1 为前端态恢复编排(退避/熔断为内存计数),生产级熔断需后端共享状态;critical 类「强制人工」是产品硬约束不可配置关闭;不谎称已接 LLM self-fixing loop——fix_and_retry 的「补格式约束再生成」在 W12 仅落路径与契约,真实 LLM 重生成是 P3 后续接入动作。

参考资料

  1. AI UX Design Guide — Error Recovery & Graceful Degradation(aiuxdesign.guide/patterns/error-recovery,2026):plain-language 报错("We're at capacity" not "Error 503");保全用户上下文/工作状态;2-3 个恢复选项(retry/queue/offline);warm amber 非 harsh red;clean up error text 避免泄露内部细节但保持透明 — 本日 WebFetch 一手
  2. Cordum — AI Agent Circuit Breaker Pattern: Stop Cascading Tool Failures(cordum.io,2026):三态熔断 CLOSED/OPEN/HALF-OPEN,持续失败快速降级
  3. Fastio — AI Agent Retry Patterns: Exponential Backoff Guide(fast.io,2026):分层恢复——瞬时用退避、持续用熔断、LLM 不可用用 fallback、不可恢复升级人工
  4. Mantlr — Designing for AI Agents: 10 UX Patterns (2026):「I'm stuck」状态触发人工接管 + resumable plan;errors are data 存进 state 让 LLM 自纠
  5. Fuselab — Agent UX 七设计模式(2025-08):错误恢复为第四模式;本项目 W1 已实装透明推理/置信度/审计,W12 补错误恢复
  6. 本项目 Day 11(failureTaxonomy 6 类)、Day 5(copilot 非 autopilot / Article 50 编辑责任)、Day 37(approvalQueue 兜底降级);src/aml/failureTaxonomy.tssrc/components/aml/AmlCopilot.tsxsrc/agent/durable/checkpointMachine.ts(2026-06)

SOTA 检查 (2026-06-11)

  • 「分层错误恢复(退避→熔断→fallback→人工)」是 2026 agent 工程主流共识:Fastio/Cordum/Mantlr/buildmvpfast 多家 2026 文章口径一致;本笔记机制与主线同向,未见被替代。AI UX 侧「plain-language 报错 + 保全上下文 + 2-3 恢复选项 + warm color」也是 aiuxdesign.guide 2026 的稳定 pattern。
  • 「errors are data, not exceptions」是 LangGraph 等框架的 live 范式:把错误结构化进 state 而非冒泡异常,让 self-fixing loop 成为可能;本项目的 StepError 写进 workflow state 与此同源。
  • 反直觉洞察②(自动重试到成功在合规场景是危险设计)是 AML 特化点、非通用结论:通用 SaaS 追求无感自动恢复;但 SAR 签发的法律责任在人,故 critical 类必须强制人工——这是把通用 agent UX 模式「为金融合规特化」的关键差异,Day 84 周总结会归纳此类特化点。
  • 过时认知警示:(1) 不要让异常冒泡到 UI 错误边界把页面炸白——错误要成为可渲染的 state;(2) 不要对确定性错误盲目重试(同输入同失败),须 fix-and-retry;(3) 不要用 harsh red 标所有错误——容量类用 amber,红色留给 critical(AI UX Guide 2026)。
  • 待跟踪:MCP Apps(2026-07-28 终版)服务端渲染 UI 是否会标准化「错误恢复界面」的承载方式;多智能体系统 41-86.7% 的生产失败率(Augment Code 2026)说明错误恢复在多 agent 编排里更难——本项目 Day 33 已决定不上多 agent,规避了这层复杂度,W12 复查该决策仍成立。