# FDE W4D4 评测题 · HITL 与中断恢复

> 配套手册：`FDE-W4D4-HITL与中断恢复-学习手册.html`
> 候选人背景：36 岁，Java + 大数据 + 医疗保险。所有场景以「特药理赔 Agent」为例。
> 题量：13 题（L1 基础 ×4 / L2 进阶 ×4 / L3 深度 ×3 / L4 场景 ×2）
> 用法：先口头/书面作答，再展开 `<details>` 对照参考答案，按评分维度自评。

---

## L1 基础（概念识别，能说清"是什么"）

### Q1. 什么是 HITL？它在 Agent 系统里解决什么问题？
- **考察点**：HITL 定义与存在的必要性。
- **评分维度**：① 定义是否准确（人在回路、人有最终否决权）；② 是否点出"高风险动作不可逆 / 模型不确定 / 合规审计"三类动机；③ 能否区分 HITL 与"纯人工""纯自动"。
- **达标关联**：达标线① 前置概念。

<details>
<summary>参考答案</summary>

Human-in-the-Loop 是在 Agent 自主流程中，把部分高风险决策节点交回给人确认/修改/驳回，人拥有最终否决权。它解决三类问题：高风险动作不可逆（如放款）、模型判断不确定（幻觉、边界模糊）、合规要求可审计（谁决策谁负责）。它不同于纯人工（慢）和纯自动（危险），是"Agent 跑大部分、人在关键闸门拍板"。

</details>

### Q2. LangGraph 的 Checkpointer 是做什么的？MemorySaver 和 PostgresSaver 有什么区别？
- **考察点**：状态持久化基础概念。
- **评分维度**：① 是否说清"状态快照落库"；② 是否点出 thread_id 与 checkpoint 的关系；③ 是否明确"生产用 PostgresSaver、MemorySaver 仅调试"。

<details>
<summary>参考答案</summary>

Checkpointer 把 StateGraph 每一步的状态快照持久化到外部存储（如 PostgreSQL），让图能暂停、断线、再恢复。MemorySaver 存进程内存，重启即丢，仅适合本地调试；PostgresSaver 事务持久化，生产必用。所有快照按 thread_id 组织，恢复时靠 thread_id 读回。

</details>

### Q3. interrupt() 和普通的异常（exception）有什么本质区别？
- **考察点**：受控暂停 vs 未受控崩溃。
- **评分维度**：① 是否点出"设计内 vs 设计外"；② 是否点出"状态已落库、可恢复"；③ 是否提到恢复用 Command(resume)。

<details>
<summary>参考答案</summary>

interrupt() 是设计内的受控暂停：状态已在断点落库，可恢复，是流程控制手段。异常崩溃是未受控失败，状态可能不一致，需要靠重试/幂等恢复。interrupt 恢复靠 `Command(resume=决定)` + 同一 thread_id 从断点续跑。

</details>

### Q4. 人工审批一般有哪几种结果形态？
- **考察点**：HITL 三形态。
- **评分维度**：① 审批（通过/驳回）；② 修改（回写状态）；③ 转人工/升级（降级）。是否各举一个理赔例子。

<details>
<summary>参考答案</summary>

三种：审批闸（通过/驳回，如放款前确认）、修改/纠正（人改金额或补材料，如把 8 万改 5 万）、降级转人工（低置信度/异常转坐席）。在特药理赔中分别对应：专家确认赔付、专家改金额、材料存疑转调查员。

</details>

---

## L2 进阶（机制理解，能讲清"怎么实现"）

### Q5. 描述一次完整的 LangGraph HITL 会话：从图首次运行到人工审批后恢复。
- **考察点**：interrupt + Checkpointer + resume 的完整链路。
- **评分维度**：① 首次 invoke 带 thread_id 跑到 interrupt 暂停落库；② 返回待审 payload；③ 人审批；④ Command(resume) + 同 thread_id 恢复，从断点续跑；⑤ 是否提到"不重跑已完成节点"。

<details>
<summary>参考答案</summary>

1) `graph.invoke(input, {thread_id})` 跑到审批节点 `interrupt(payload)` 暂停，状态快照落 PostgreSQL，返回待审信息；
2) 人看信息做通过/驳回/修改；
3) `graph.invoke(Command(resume=human_decision), {thread_id})` 恢复，`interrupt()` 返回 resume 值，后续节点执行，已完成的节点不重跑。

</details>

### Q6. 审批触发应该怎么设计，才能既保证安全又不过度打扰人？
- **考察点**：风险分层与触发信号。
- **评分维度**：① 是否提出"风险分层"（免审/预警/强审批）；② 触发信号是否覆盖"金额 + 规则 + 置信度 + 知识缺失"；③ 是否提到"可配置"而非硬编码；④ 是否警示"不能全量审批（审批疲劳）"。

<details>
<summary>参考答案</summary>

按风险三维分层：金额阈值、合规规则命中、模型置信度、知识库缺失。低风险+高置信自动过；中风险记录抽样复核；高风险（大额/用药冲突/材料缺失）才 interrupt。触发逻辑抽成可配置规则，避免硬编码。不能无差别拦截，否则审批疲劳反而降质。

</details>

### Q7. 审批通过和驳回时，状态应该如何回流到图里？
- **考察点**：分支与副作用控制。
- **评分维度**：① 通过：写 approved + 审批人/时间，继续到执行节点；② 驳回：走驳回分支、执行副作用前检查 approval 状态；③ 是否点出"驳回后不得重复触发审批（防死循环）"；④ 原因必须落库。

<details>
<summary>参考答案</summary>

通过：state 写 `approval="approved"` + 审批人/时间戳，流程继续到执行动作节点（放款）。驳回：写 `approval="rejected"` + 原因，走驳回分支（通知/记录/转人工），绝不执行放款。副作用必须放在"approved 校验之后"。驳回分支若再触发审批会形成死循环，需设最大轮次/转人工上限。原因落库供审计。

</details>

### Q8. 人工修改（如把模型算的赔付额改小）有哪些回写方式？各适用什么场景？
- **考察点**：状态回写机制。
- **评分维度**：① `Command(resume={"amount":50000})` 一次性带值；② `graph.update_state(thread_id, {...})` 多字段编辑；③ 回写时机必须在副作用之前；④ 修改留痕。

<details>
<summary>参考答案</summary>

两种方式：① resume 直接带新值，适合一次性改少量字段；② 恢复前 `graph.update_state(thread_id, {...})`，适合多字段/多次编辑的人工工作台。注意回写必须在执行副作用之前，且每次修改记录"谁、何时、改了什么"。建议保留 original 与 edited 双版本用于对账。

</details>

---

## L3 深度（可靠性设计，能论证"为什么可靠"）

### Q9. 什么是幂等键？它在 HITL 恢复里防止了什么事故？请设计一个幂等键方案。
- **考察点**：幂等性设计与并发安全。
- **评分维度**：① 定义幂等（多次执行效果同一次）；② 防止"二次放款/重复外发"；③ 键粒度（到具体动作而非整个任务）；④ 唯一索引抢注 + 同事务；⑤ 区分"已成功"与"已失败"。

<details>
<summary>参考答案</summary>

幂等指同一操作执行一次与多次效果相同。在 HITL 里它防止中断恢复/网络重试导致二次放款。方案：每次"审批+执行"生成唯一键（如 `claim_id:approval_id`），落幂等表用唯一索引抢注；只有首次执行放款，重复调用返回 `already_done`。键粒度要到"一次具体动作"，检查与执行同事务防竞态，并区分上次成功/失败避免误跳。

</details>

### Q10. 最大执行时间应该包含哪些维度？为什么超时后不能"挂起"而要"可控失败"？
- **考察点**：超时分层与兜底哲学。
- **评分维度**：① 至少列出单步 LLM/工具超时、总步数上限、审批超时、整体 SLA 中的 3 项；② 分层原因（单步卡死≠整体 SLA）；③ "可控失败"（转人工/告警/升级）优于挂起；④ 医疗场景审批超时默认驳回/升级而非放行。

<details>
<summary>参考答案</summary>

维度：单步 LLM 超时、单步工具超时、Agent 总步数上限（防循环）、人工审批超时、整体任务 SLA。只设全局超时不够，单步卡死和 SLA 违约是不同问题需分层。超时后应将"失控"转为"可控失败"（转人工/告警/升级），而非永远挂着造成业务阻塞。医疗场景审批超时默认驳回或升级，绝不能"超时自动通过"。

</details>

### Q11. 如果一次恢复执行时 LLM 调用超时了，应如何重试才能保持状态一致、不重复副作用？
- **考察点**：异常重试与一致性。
- **评分维度**：① 重试在同 thread_id 下复用快照；② 退避重试设上限；③ 重试记 Trace 用于成本归因；④ 幂等键保证副作用只一次；⑤ 超限降级而非卡死。

<details>
<summary>参考答案</summary>

重试必须在同一 thread_id 下、基于已落库的快照进行，避免状态漂移；LLM 超时用指数退避重试 ≤3 次并设上限防雪崩；所有重试记 Trace（次数/原因）做成本归因；副作用靠幂等键保证只发生一次；重试仍失败则降级（备用接口/转人工），绝不卡死——医疗场景"挂起不处理"本身也是事故。

</details>

---

## L4 场景（真实业务综合，能串成"一段故事"）

### Q12. 请完整讲述「特药理赔 Agent」的 HITL 落地：从申请到放款，怎么保证不二次放款、不误放款。
- **考察点**：综合应用（Checkpointer + Interrupt + 幂等 + 超时）串成故事。
- **评分维度**：① 流程图正确（材料抽取→合规校验→规则判断→高风险 interrupt）；② Checkpointer 落库可恢复；③ interrupt 受控暂停；④ 人可改金额再批（update_state/resume）；⑤ 恢复后幂等键保护放款；⑥ 超时/步数上限兜底；⑦ 全程留痕可审计。

<details>
<summary>参考答案</summary>

流程：接收申请→材料抽取(LLM)→知识库校验用药合规/医保目录→规则引擎(金额/黑名单)→高风险则 interrupt 人工审批，否则自动通过。人可把 8 万改 5 万并点通过。系统用 `Command(resume)` + 同 thread_id 恢复，执行节点校验幂等键 `claim_id:approval_id` 未消费才放款，标记已消费；即使网络重试恢复，键已消费直接返回已处理，绝不二次放款。Checkpointer(PostgresSaver) 保证状态不丢、可恢复；Interrupt 实现受控暂停；超时/步数上限防失控；所有操作留痕可审计。

</details>

### Q13. 你的特药理赔 Agent 上线后，发现"有时人驳回后系统又发了一次审批通知"——请定位可能的原因并给出修复方案。
- **考察点**：故障排查与防重设计（综合）。
- **评分维度**：① 可能原因：a. 恢复时又重新触发审批节点（分支未检查 approval 状态）；b. 重试无幂等，重复发通知；c. 状态回写时序问题（通知在审批前已发）；② 对应修复：执行前检查 approval 状态、通知动作加幂等键、统一状态机；③ 能否结合 Trace 定位（查重试次数/重复 span）。

<details>
<summary>参考答案</summary>

可能原因：① 执行/通知节点没检查 approval 状态，驳回后仍跑到"发审批通知"逻辑（分支缺陷）；② 恢复重试未加幂等，通知动作被重复执行；③ 状态回写时序：通知在审批结果落库前就发出了。修复：把发通知作为状态机里的确定分支，仅在相对应状态触发；通知动作套幂等键（claim_id:notification_type）去重；写回审批结果后再进入通知环节；用 Trace 查重复 span/重试次数确认根因。

</details>

---

## 评分汇总表

| 层级 | 题号 | 主题 | 满分(自评分) | 关键失分点 |
|------|------|------|------|------|
| L1 基础 | Q1 | HITL 定义与动机 | /5 | 未区分三动机 |
| L1 基础 | Q2 | Checkpointer 选型 | /5 | 混淆 Memory/Postgres |
| L1 基础 | Q3 | interrupt vs 异常 | /5 | 未提可恢复 |
| L1 基础 | Q4 | 审批三形态 | /5 | 漏"修改/转人工" |
| L2 进阶 | Q5 | 完整 HITL 会话 | /10 | 漏 thread_id/resume |
| L2 进阶 | Q6 | 审批触发设计 | /10 | 未分层/硬编码 |
| L2 进阶 | Q7 | 通过/驳回回流 | /10 | 副作用未校验状态 |
| L2 进阶 | Q8 | 状态回写 | /10 | 漏 update_state |
| L3 深度 | Q9 | 幂等键设计 | /10 | 键粒度/事务错 |
| L3 深度 | Q10 | 最大执行时间 | /10 | 未分层/超时放行 |
| L3 深度 | Q11 | 重试一致性 | /10 | 未同 thread_id |
| L4 场景 | Q12 | 理赔 HITL 故事 | /15 | 四机制未串起 |
| L4 场景 | Q13 | 故障排查 | /15 | 未用幂等/Trace |

**总分**：/130

### 达标线
- **达标线①（40 分）**：Q1~Q5 能完整讲清"LangGraph 里 HITL = Checkpointer(落库) + Interrupt(受控暂停) + Command(resume)+同 thread_id(恢复)"，且 Q2 选型正确。
- **达标线②（40 分）**：Q9（幂等键）、Q10（最大执行时间）、Q11（重试一致性）合计 ≥32，且 Q12 能把"中断恢复 + 幂等 + 超时"串成特药理赔故事，说明四者如何共同保证可靠性。
- **总分 ≥ 90 / 130** 视为本日达标；L4 两题任一并达到 12/15 视为具备"场景讲述"能力。
