工具调用的重试与幂等

一次请求发出后连接断开了。界面没有收到成功消息,远端却可能已经保存了记录。这时直接重试,究竟是在补做一次工作,还是在重复做一次工作?
这个问题在普通表单里就会出现,加入 Agent 后也不会自动消失。本文设计一个“把阅读笔记加入清单”的小接口,练习如何表达意图、记录结果,并在不确定时恢复。
给操作一个稳定身份
AWS Builders’ Library 关于幂等 API 的文章讨论了让重试更安全的设计。这里借用请求身份这一思路,下面的字段和状态划分是本文的小项目方案。
对于同一个“添加笔记”的意图,客户端在重试时复用同一个操作编号。若用户真的要再添加一份内容,那是一个新的意图,应当使用新的编号。仅根据文本是否相同来猜测意图,很容易把用户希望保留的重复记录吞掉。
type AddReadingNote = {
operationId: string
notebookId: string
sourceUrl: string
note: string
}
服务器还需要把操作编号和参数摘要联系起来。相同编号却带来了不同内容时,应当明确报告冲突,而不是随机采用最后一次请求。这个检查同样要在真正执行写入的一侧完成。
把记录与写入放进一个边界
在这个练习里,操作记录和业务笔记保存在同一个数据库中,便于放入同一事务。若它们位于不同系统,就需要另外设计恢复流程,不能只画一个事务框便声称解决了全部问题。
| 当前状态 | 再次收到同一操作 | 应当做什么 |
|---|---|---|
| 尚未处理 | 正常请求 | 校验并尝试写入 |
| 已完成 | 参数一致 | 返回已保存结果 |
| 已完成 | 参数不同 | 报告冲突 |
| 处理中或结果未知 | 重复请求 | 查询实际状态,再决定下一步 |
这张表的重点是“结果未知”。网络错误只说明调用方没有拿到完整结果,不能直接证明远端什么都没做。
为失败安排不同的路
参数不合法,通常不应该原样重试。连接暂时不可用,可以在预算内等待后再尝试。结果已经存在,则应当读取原结果,而不是重新执行副作用。把所有错误都塞进同一个重试循环,会让日志越来越长,行为却越来越难解释。
可以先人为制造两种故障:写入完成后丢弃响应,以及写入之前断开连接。用同一操作编号重试,检查最终是否只有一条业务记录,并且能看到一次清楚的恢复过程。
发布需要更明确的状态
当工具涉及发布、发送或其他对外动作时,我会把“准备内容”和“正式执行”分成不同阶段。准备阶段生成可审阅结果,执行阶段记录具体确认与结果。是否需要确认取决于用户授权和实际产品流程,而不是每次都增加一个空泛的弹窗。
一个可靠的小助手,不一定永远不出错。它需要在出错以后留下足够的信息,让人知道工作停在哪里,已经发生了什么,以及重复一次会不会造成新的问题。
工具调用的重试与幂等 · LovelyCat · 2026-10-21
个人学习与创作札记 · 设计参考 Yohaku