Agent重试bug:幂等键与unknown状态处理

原文:https://dev.to/cyclopt_dimitrisk/your-agents-retry-bug-isnt-an-ai-problem-its-a-distributed-systems-problem-with-an-llm-on-top-1i6k(作者 @cyclopt_dimitrisk)

大多数 Agent 演示走的都是顺利路径:模型选中一个工具,工具返回 200,Agent 继续往下走。生产环境不是这样。在生产环境里,工具调用有时什么都不返回。

一个本来正确的决策,就在这里变成了错误的结果。

「已决定」与「已完成」之间的鸿沟

下面是一个示意性场景(并非真实事故,但极为常见)。

一个 Agent 处理一张客服工单,判断应当给客户退款。它调用 POST /refunds。支付服务处理了这笔退款,写入自己的账本,并开始发送响应。网络出现抖动。Agent 的 HTTP 客户端触发超时并抛出错误。

从 Agent 的视角看,这次调用失败了。从支付服务的视角看,它成功了。Agent 的循环看到一个错误,而它最自然的做法就是再试一次。

于是客户收到了两笔退款。

模型的推理完全正确。它选对了动作,也传对了参数。更好的提示词、更大的模型、更聪明的规划器都解决不了这个问题,因为故障发生在推理结束之后。

失效模式 1:把超时当成「没有执行」

超时只告诉你一件事:你没拿到答案。它并不告诉你动作没有发生。那些把工具错误统一包装成「工具调用失败,请决定下一步做什么」的 Agent 框架,把三种完全不同的情况揉成了一种:

  • 请求从未到达服务
  • 请求到达了,但被拒绝
  • 请求到达并成功执行,但响应丢失了

只有前两种可以盲目重试。

失效模式 2:没有幂等键

如果 Agent 发送了幂等键,第二次退款调用就会变成一个 no-op(空操作),直接返回第一次的结果。这个键必须在第一次尝试之前由应用生成,并在同一意图的每次重试中复用:

intent = actions.record_intent(
    kind="refund",
    order_id=order.id,
    amount=amount,
    idempotency_key=uuid4(),   # created once, stored with the intent
)

try:
    result = payments.refund(
        order_id=order.id,
        amount=amount,
        idempotency_key=intent.idempotency_key,
    )
    actions.mark_confirmed(intent.id, result)
except Timeout:
    actions.mark_unknown(intent.id)   # NOT "failed"


关键在于这个键存在哪里。如果键由模型生成,或者每次尝试都重新生成,那它什么都保护不了。

失效模式 3:只存在于上下文窗口里的状态

如果「我已经尝试给订单 4812 退款」这条记录只活在对话历史里,那么进程崩溃、上下文被截断、worker 重启之后它就消失了。意图必须在执行之前写入持久化存储,这样接手这项工作的任意进程都能看到它。

失效模式 4:没有「结果未知」状态

大多数动作模型只有两种终态:成功和失败。可靠的实现至少需要:

  • submitted(已提交):我们发出去了,还没有回应
  • confirmed(已确认):下游系统已确认接收
  • failed(已失败):下游系统明确拒绝
  • unknown(未知):我们判断不了

真正重要的是 unknown。这个状态应当阻断自动重试,转而触发对账。

失效模式 5:没有对账路径

当一个动作落入 unknown,必须有东西去查明真相:用幂等键或订单 ID 查询支付服务,与账本比对,然后把该意图推进到 confirmed 或 failed。这可以是一个后台任务、一次定时检查,或者针对高价值动作的人工队列。但它不能是 Agent 自己拍板「大概没事」或「大概失败了」。

换个视角

Agent 的工具调用本质上就是远程过程调用,只不过由 LLM 来决定调用哪个过程。我们在不可靠网络上做 RPC 所积累的一切经验依然成立:幂等、持久化的意图日志、显式的不确定性、对账。

清晰的分工是:

  • 模型提出动作。
  • 应用负责执行状态:幂等键、意图记录、状态、重试策略。

「再试一次」对读操作来说是合理的默认值。对任何会改变状态的操作来说,只有在你确认第一次没有落地之后,或者下游系统保证重试不会重复生效之后,它才是安全的。

自建还是集成

如果你已经在支付或订单流程中使用幂等键和 outbox/账本模式,你多半只需要把 Agent 的动作接入同一套机制,而不是让它直接调用 API。如果你还没有,那就是应当先建(或先采用)的一环,然后再授予 Agent 对任何涉及资金或数据的写权限。

在你们的 Agent 工作流里,「不知道有没有发生」这种情况是怎么处理的?是阻断重试、走对账,还是升级给人工?

原文:https://dev.to/cyclopt_dimitrisk/your-agents-retry-bug-isnt-an-ai-problem-its-a-distributed-systems-problem-with-an-llm-on-top-1i6k(作者 @cyclopt_dimitrisk)

===REBUTT===

已按终审意见删除正文首行的生产备注(标题去空格=26字符;素材来源=无可用官方素材),信源行现置于正文开头第一行与末尾各一处,译文其余内容未改动。

发布评论
全部评论(0)