L06 退款成功但响应丢失:可靠执行的核心实验
适用章节:M07。本实验允许多种正确实现;验收不变量固定,不强制特定框架。
工程事故
退款服务已经提交账本,Worker在收到响应之前退出。恢复逻辑不知道操作是否发生。如果直接新建请求重试,可能重复退款;如果一概标失败,会向用户谎报并遗漏真实资金变化。
最终交付
带故障注入的幂等业务服务、稳定operation_id、恢复决策表、并发测试;选修LangGraph或Temporal适配与语义对照。
已提供材料
reference/agentlab/business.py、reference/agentlab/durability.py、data/traces/refund_response_lost.json
先独立写出预期行为再看参考解。所有商店、订单、试次与价格单位都是教学构造;不连接真实资金系统。
接口与不可破坏的约束
执行身份与业务意图分开:run_id描述任务,attempt_id描述尝试,operation_id描述同一个业务操作。重试不更换operation_id。幂等键同时绑定规范化参数摘要;同key不同参数拒绝。
本地教学退款把余额、账本、去重结果放在同一SQLite事务内,因此可以检查原子性。对外部支付API,不存在横跨任意网络和数据库的自动原子提交;必须根据外部服务契约设计查询、幂等、对账或人工处理。
实施步骤
- 先实现无幂等版本,手推“提交后丢响应”的重复账本风险;不要真的接支付平台。
- 在审批前由服务器创建稳定operation_id,摘要绑定订单版本、金额、政策和有效期。
- 将业务变更、唯一账本条目与操作结果放在同一事务。
- 加入before_commit故障,确认所有写入回滚;加入after_commit故障,确认写入已持久化但调用方看到异常。
- 使用同一operation_id恢复;若已完成,授权后返回原结果,即使旧审批现在已过期,也不再次执行。
- 启两个独立数据库连接并发重试同一提案;最后必须只出现一笔账本。
- 对照Checkpoint、Outbox、幂等、fencing四种机制分别解决的问题。
验收矩阵
| 崩溃/重试窗口 | 账本/结果 |
|---|---|
| before_commit | 账本0,余额不变,后续安全重试 |
| after_commit | 调用方LostResponse,账本1 |
| 同参重试after_commit | 返回原CONFIRMED,账本仍1 |
| 同key但不同金额 | IDEMPOTENCY_CONFLICT |
| 新执行时审批已过期 | APPROVAL_EXPIRED,无新增账本 |
| 已完成操作过期后同参查询/重放 | 返回旧结果,无新副作用 |
| 旧worker在新worker接管后写状态 | STALE_WORKER |
| 两个worker同时提交 | 同结果,账本1 |
如何运行已有参考测试
在包根目录运行:
python -m unittest discover -s reference/tests -v
本实验主要参考测试类为BusinessTests。已有测试默认验证reference/;测试通过不代表你的starter/实现通过。接入自己的实现时,将被测模块显式替换/注入,保持测试和领域契约不变,不要修改断言来迁就实现。
评分
副作用不变量35;崩溃与并发测试30;参数绑定15;远程不确定性说明10;框架对照10(未选框架则用完整机制对照)。
出现跨租户泄露、未经批准的退款或伪造结果时,不以其他分数抵消。允许记录未完成的选修功能,但必须说明范围。
进阶挑战
使用LangGraph持久节点或Temporal Activity包裹相同业务服务,证明框架恢复仍需要外部幂等。记录节点重启/重放语义,不把继续执行误解为恢复任意Python栈。
提交前自查
保存代码版本、运行命令、输入数据版本、输出日志、失败案例与设计决策。解释至少一个被你否决的方案。没有运行的测试标记“未运行”,不写“预计通过”。参考解见对应答案。