loading

Loading

探索与发现

首页 AI 开发实战

推理服务请求级重放:幂等键到底该落在哪一层

字数: (2483)
阅读: (11)
0
摘要:推理服务的幂等键大多放在 HTTP 入口,只能防住同一次 POST 打两次。真正多烧钱的是同一 Agent 意图走了不同推理路径。这篇把幂等键该落在哪一层拆开,并逐条核对状态 TTL、采样重试派生键、结果缓存和幂等键是两套系统这几件事,附成本核算和命中率观测指标。

Agent 工具超时重试已经写过了。这次往回退一层,谈推理服务本身的请求级重放——特指推理网关和 Agent 编排层收到的同一条业务请求被重放时,幂等键该整在什么位置。

先给结论:绝大多数团队的幂等键放在 HTTP 入口,只防住了「同一个 POST 打了两次」。但真正烧钱的重复执行发生在语义层:同一个 Agent 目标走出了两条推理路径,幂等键完全不同,收费却是两倍。这两个问题不是一个机制能解决的。

先分清两种重试意图

tianpan.co 上那篇讲 LLM 流水线幂等的文章把这个区分讲得很清楚:为了瞬时可靠性的重试和为了采样更好答案的重试,需要完全相反的处理。

瞬时重试的前提是「活儿已经干了,只是响应在链路上丢了」。这时要的是从缓存里拿回原结果,绝不能再推理一遍。采样重试的前提是「这次输出不合格,格式错了、被护栏拦了、解析失败了」,这时要的是新样本,重试时必须换有效载荷,通常还得把上次的错误信息回填进 Prompt。

最常见的坑就是把采样重试当成瞬时重试做。一个 Agent 发出工具调用,工具超时,Agent 层判定失败后重放——但第一次调用其实已经执行完,只是回执晚了。结果就是两次副作用和一份乱套的状态机。

放到推理服务内部的对应关系是:前缀缓存命中、连续批处理里的批内复用、网关的结果缓存,这些都能降低算力开销,但它们不是幂等机制。它们没有「这条请求交付过一次」的保证。如果缓存键和幂等键是两套独立系统,重试完全可能绕过缓存,重新排进推理队列。这一点在之前写工具结果缓存那篇时就踩过:三处缓存键不一致,复用直接变成废纸。

键落在 HTTP 入口,只能防一半

腾讯云开发者社区那篇文章给的公式很直白:幂等 = 唯一请求 ID + 存储状态(处理中/已完成)+ 重复判断逻辑。客户端在请求头里带 Idempotency-Key,服务端用 Redis 记状态,INIT、PROCESSING、SUCCESS、FAILED 四个态,PROCESSING 和 SUCCESS 直接拒绝新推理。落地逻辑本身不复杂。

难点在时间参数上。推理耗时从几百毫秒到几十秒不等,如果复用一个很短的幂等状态 TTL,就会出现一种非常恶心的场景:第一次请求还在 GPU 上跑,状态过期了,重试请求穿透过去开始了第二次推理。所以幂等状态的过期时间必须覆盖「最大推理耗时加客户端重试周期」,这里的最大值要按 P999 来找,不能拿平均值拍脑袋。

只落在入口的另一个漏洞是:一个 Agent 编排步骤可能包含五次模型调用和三次工具调用。客户端给每次 HTTP 调用都生成各自的 UUID,那么整条链路重放时,每个子调用的幂等键全都是新的,一个也防不住。健必须整在逻辑操作上——也就是那个会产生外部副作用、会对应一次计费归属的工作单元,而不是某个具体的 HTTP 请求。

请求级重放的落地基线

把上面这些拼起来,我线下能推的一套最小方案是这样的:

  • 幂等键由编排层生成,作为业务请求的身份,沿整条调用链透传,禁止下游组件另行生成。
  • Redis 存两样东西:请求状态,以及首次推理的结果正文。结果比对错成本更值钱,重试时宁可多占点存储也要存,否则「已经算完但响应丢了」只能靠重算恢复。
  • 真正真正执行之前先抢分布式锁,锁键就是幂等键,锁的持有时长取模型最大推理时间,超出就当作异常释放,别把幂等状态当成死锁用。
  • 采样重试使用派生键:原文幂等键 + 重试序号,或者这个带上错误概要的哈希。这样它在语义上是新请求,但同一次网络故障下的重放仍然能找到原结果。这是前面那个「双重重试陷阱」的最小解。
  • 不管请求是否被拦,会话历史里这条用户消息只能落一次。多轮场景下重复插入比重复推理更致命,会把后续每一轮的上下文都带偏。

做到这几条,计费和会话基本干净了。但它依然只能覆盖「相同请求的重放」。

更难的那坡:同一意图走了不同路径

Agent 层的现实是,同一个子目标可以用不同的措辞、不同的参数把工具调出来,HTML ID 差一位,字符串差个空格。这时候内容哈希对不上,幂等键就是两把,副作用执行两次。

tianpan.co 那篇给了个三个渐进的漏斗:先字符串精确匹配,再对规范化表示做 Embedding 相似度比对,最后只把模棱两可的样本交给 LLM 判断。阈值上,近乎相同取 0.9 左右,意图等价取 0.85。文章引用的生产区间是真实 Agent 流量上 20% 到 45% 的命中率,这个数字我不建议当基准照搬,因为它的成败很依赖阈值调得对不对,而线上冒烟和压测几乎调不出来:阈值太低会把合法操作拦下,太高又会漏掉仅改了措辞的重复动作。另外它要求查找本身在 50 毫秒级以内,超了就谈不上省钱了。

我对这层的态度偏保守。语义去重只应该用在读多写少、重放成本明确的动作上。真正带副作用的写操作,还是要靠确定性强的幂等键来自保;用相似度来挡付款调用,没人会担这个心。

成本那笔账

为什么值当折腾这一圈,可以用一个保守的算法对齐:重试风暴期间每个未受保护的请求,付的并不是只多一次的钱,它的下游 API 也多了一份。那篇文章自己举的例子大致是一笔五十美元量级的批处理作业,网络抖动重试三次变成两左右,至于「事故期单次成本翻几倍」这类数字,我一直拿来看量级而非当精确值。真正能落地的指标只有一个:各层的幂等命中数。正常情况下它平稳,重试风暴期间它应该跳起来。如果反而掉了,说明有条代码路径根本没接去重层——这比任何仪表盘都直观。

腾讯云那篇文章列出来的收益清单里,我认为只有两条是真需要盯的:重复推理占着 GPU 算力和调度队列(在高并发下比多花的钱更要命,因为它会吃满你的批处理槽位,把后面所有正常请求的 P99 拖进去),以及幂等链路上的错配。其余几条更像是结算和对账业务的指标,不在推理服务的锅界内。

心智模型的切换点是:传统业务里幂等性是单次操作的属性,在 LLM 流水线里它是工作流的属性。核心组件本身是随机的时候,不先定义清楚「什么算同一个操作」,写多少套幂等键都是在自扫门前雪。

转载请注明出处: 皮哈皮阿

本文的链接地址: https://blog.phpat.com/ai-dev/680.html

手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

您可能对以下文章感兴趣
1 / 1
预览图