做 Agent 的人早晚会遇到一次超时:请求发出去了,返回没回来,模型决定重试一遍,结果同一笔订单下了两次、同一封邮件发了三回。这篇不聊抽象概念,只回答一件事——为什么 Agent 场景对接口幂等的要求比传统后台更高,以及你在接第三方 API 时该怎么判断、怎么补。
做 Agent 的人早晚会遇到一次超时:请求发出去了,返回没回来,模型决定重试一遍,结果同一笔订单下了两次、同一封邮件发了三回。这篇不聊抽象概念,只回答一件事——为什么 Agent 场景对接口幂等的要求比传统后台更高,以及你在接第三方 API 时该怎么判断、怎么补。
先给结论:幂等不是接口的加分项,是写操作类 API 的入场券。判断标准只有一个——同样的参数执行两次,业务结果是否等价。等价就可以放行,不等价就必须在调用侧加挡板。下面按「为什么 Agent 更容易重复发」「哪些调用怕重复」「键怎么定」「接之前查什么」「什么时候可以不做」的顺序讲。
Agent 为什么比人手更容易把同一个请求发两遍
人手重试是有意识的:点了一下没反应,人会等几秒再点,而且记得自己点过。Agent 不是。它的调用链是模型决定的,超时、工具返回空、上下文被截断、甚至模型自己「觉得」上一步没成功,都可能触发它自行重试。它不记得自己发过,只看到「没有结果」,于是再发一次。
- 多步任务里失败重试通常只重跑一步,但如果这一步是下单、转账、发消息这类写操作,重复就是事故,而且往往在事后对账时才被发现。
- 流式与长耗时接口更容易出现客户端超时但服务端已执行的情况,两端状态不一致,模型看到超时就会再发一遍。
- 工具返回空数组或空字符串时,模型经常误判为失败而重试,这种「假失败」比真超时更隐蔽。
- 并发场景下模型可能同时发起多个相似请求,去重逻辑必须能处理并发而不是只处理串行重试。
先分清哪些调用怕重复,哪些不怕
不是所有调用都值得做幂等。查询类接口天然幂等,重复调最多浪费 token 和配额,属于成本问题,可以到 Token 单价换算(/tools/token-price-calculator) 估一下重复调用带来的开销,用预算上限和重试次数限制控制即可。真正要挡的是写操作。
| 调用类型 | 重复执行的后果 | 要不要幂等 | 判断标准 |
|---|---|---|---|
| 纯查询(读列表、查详情) | 多花 token 和配额 | 不需要 | 无副作用,重复结果一致 |
| 创建类(下单、建工单、发通知) | 产生重复业务记录 | 必须 | 同参数执行两次结果不等价 |
| 扣减类(扣额度、扣库存) | 额度或库存被多扣 | 必须 | 重复执行会改变余额 |
| 先查再创建 | 并发下创建出两条 | 必须,靠外部键 | 检查与写入不在一个原子操作里 |
既非纯读也非纯写的中间态最容易漏,比如「先查用户有没有、没有再创建」。这类逻辑靠接口自身的语义兜不住,必须靠外部幂等键。判断方法很简单:把同一个请求发两次,看数据库里会不会多一行。
幂等键怎么定:谁生成、放哪、存多久
- 由调用方生成键键要和「一次业务意图」绑定,比如任务 ID 加步骤名的哈希。不要用时间戳或纯随机数,否则重试时又变出一个新键,等于没做。
- 放在请求头而不是请求体放请求头便于网关和中间层统一拦截,不改业务参数结构。字段名按对方文档来,没有就自己约定并在中间层落地。
- 服务端记录处理状态存「这个键处理到哪一步、上次返回什么」。重复请求直接返回上次结果,而不是重新执行。状态机至少要有「处理中」和「已完成」两个态,处理中收到重复请求应拒绝或等待。
- 按业务窗口设过期过期时间短于用户可能的重试间隔就失去意义,长于业务对账周期又白占存储。具体数值按你们的重试策略和对账周期定,别照抄别人的配置。
还有一个常被忽略的点:幂等键要能覆盖模型层。模型看不到你的去重逻辑,它会照常发请求,所以拦截必须发生在工具层或网关层,而不是在提示词里叮嘱「不要重复调用」。
接一个新 API 之前的幂等核查清单
- 翻官方文档确认有没有幂等键、请求 ID 或去重窗口机制;没有就问清楚重复提交会发生什么,别自己猜。
- 接中转站时额外确认转发层会不会重试。转发方悄悄重试最容易把问题放大,相关排查思路见 网关超时原因排查与重试配置(/guides/gateway-timeout-502-504-retry)。
- 上线前用「同一请求连发三次」做实测,观察账单和业务侧记录,别只信文档。
- 在 Agent 的工具层加去重,键的生成和存储都在你这边,不依赖对方接口的语义。
- 把重试次数和超时阈值写进配置,不要在代码里散落魔法数字,方便后续调整。
| 核查项 | 通过标准 | 不通过怎么办 |
|---|---|---|
| 文档是否声明幂等机制 | 有明确字段或去重窗口说明 | 按「无幂等」处理,自己加键 |
| 转发层是否透明 | 确认中转不擅自重试 | 换直连或加请求指纹校验 |
| 实测重复提交 | 业务侧只产生一条记录 | 在工具层补去重再上线 |
| 错误码可区分 | 重复请求返回可识别的状态 | 在中间层做映射,避免被当网络故障重试 |
什么情况下可以不做幂等
幂等不是所有场景的必需品,硬做反而增加复杂度。以下情况可以不做,但每种都有前提,前提不成立就得补回来。
- 纯咨询类、无副作用的对话请求:重复一次只是多花钱,用预算上限和重试次数限制控制即可,不必上幂等键。
- 对方接口自身保证严格唯一约束(比如按业务主键插入冲突就报错):可以依赖它,但要确认冲突错误能被你的重试逻辑正确识别,而不是被当成网络故障再重试一遍。
- 开发调试阶段:可以先不做,但必须在灰度或上线前补齐,否则测试环境永远发现不了重复提交的问题。
- 一次性、不可重入的批处理任务:如果任务本身设计为只跑一次且有外部锁,可以不额外做键,但要确认锁的失效处理。
反过来,只要调用链里有写操作、有并发可能、有自动重试,就别省这一步。Agent 场景下这三条几乎总是同时成立。
下一步把你手上要接的 API 按上面的核查清单过一遍,重点看写操作和转发层重试;不确定中转站行为时,先到站内查它的可信度和超时记录再决定。→常见问题
幂等键是不是加在请求头里就行?
形式不重要,关键是谁生成、服务端怎么存、重复时返回什么。放在请求头、body 字段甚至 URL 参数都见过,但要确认对端真的读了它并做了去重,很多接口只是收下不处理。
第三方 API 没有幂等机制怎么办?
在你自己的服务层补:本地记一张「业务意图 → 执行状态」的表,重试前先查,执行成功但没收到响应的就查状态而不是重发。另外把 Agent 的重试次数和工具白名单收紧,降低触发概率。
重试本身是不是应该尽量少做?
不是少做而是做对。超时、限流确实该重试,但要指数退避并且只对可重试错误重试;对写操作则必须是「带幂等语义的重试」,否则宁可不重试,把失败抛出来让人工处理。