loading

Loading

探索与发现

首页 Agent 工程

工具网关限流漂移:Agent 被卡住时怎么安抚

分类:Agent 工程
字数: (2736)
阅读: (4)
0
摘要:网关限流漂移常常是从配额窗口、Agent 的步计数对不齐开始,加上错误分类和 retry-after 消息丢失,问题被成倍放大;可完成一次任务在从入口退避、对 Agent 可见的合作信号、结构化降速、如实表达分三层做到位。

Agent 出问题,很多时候不是模型变笨了,是网关开始限流,而 Agent 不知道。它看到一个工具调用返回错误(或者更糟,返回一段被网关改写过的错误 JSON),于是换参数重试、换工具重试、拆步骤重试,每一步都在多烧 token,最后报一个跟真实原因毫无关系的失败——比如“任务规划失败”。

这篇聊三件事:限流为什么会漂移,Agent 能感知到的信号应该长什么样,以及被限流之后怎么做安抚(backoff + 提示)才不至于让一次配额超限演变成一次幻觉式的连环失败。

限流漂移的根源:配额是按 Token 算的,请求是按工作负载算的

商业大模型与托管平台的配额基本是 QPM/TPM 维度,按 API Key 计算。阿里云那套 AI 网关方案里讲得很直白:闭源模型和托管平台的 QPS/Token 限制上调困难,企业要么多申请账号、多 Key 轮询摊薄限制,要么靠结果缓存减少对模型的请求。这两种做法都会让“我看到的上限”和“运营商账本上的上限”错位。

再加上网关自己还有一层流控:按 consumer 的 token 消耗统计、按模型的 token 消耗统计、被限流拦截次数统计、是哪些 consumer 在被限流。这些指标说明了一件事——限流不是一个静态数字,而是一条随调用模式漂移的曲线。同一个租户,早高峰跑三个 Agent,晚高峰跑十个,配额没动,但撞线的概率完全不同。

再往下还有一层漂移来自 Agent 自己:多轮重试、并行工具调用、工具返回分页回填,都会改变单次任务的实际 token 消耗。工具的 schema、返回体长度一变,同一条任务链的 token 账本跟着变。网关的限流阈值如果不跟着走,就会出现“昨天还好好的,今天上游封号”的情况。

一句话:限制项是对 Token 和请求数的,Agent 的行为单位是“步”。两个单位对不上,中间就需要一层翻译,翻译不好就是漂移。

返回码本身要负一半责任

传统 API 挂了是 5xx,Agent 至少知道要退。LLM 调用和工具调用会“软失败”:HTTP 200,内容是空的、乱码的、输出异常;工具返回里塞一个 is_error: true 但文本还在。腾讯云那篇架构思考提到的点是同一个:故障判定更模糊,不能只靠状态码判断健康。

限流错误尤其容易被吞掉。网关为了兼容各家的错误格式,把上游的 429、配额超限、并发拒绝统一封装,这层封装如果丢了 retry-after 或者模糊了错误类别,Agent 那边就只剩一句“调用失败”。LLM 收到这个方法后最常见的动作是把参数改小一点再试——在限流场景里这是纯粹的浪费,重试命中的还是同一个配额窗口。

建议在网关出口用一套显式错误契约:区分 quota_exceeded(窗口内总量用尽)、rate_limited(瞬时太快)、concurrency_rejected(并发槽位不够)、upstream_unavailable(真的挂了)。前三者都要带上建议退避时长和可恢复标记。Agent 侧的 handler 按类别分派,而不是一堆 try/except 里面塞一个 sleep。

{
  "error": "rate_limited",
  "recoverable": true,
  "retry_after_ms": 2400,
  "scope": "consumer:team-a/qwen-max",
  "hint": "do not change arguments"
}

最后那个 hint 不是为了给模型读,是给写 handler 的人读到之后不要自作聪明地换参数。

安抚分三级:入口退避、观察降速、对外表达

第一级是入口退避。限流就应该让请求停在网关前,不要让 Agent 一股脑往外发。具体做法是每个工具/模型通道后面挂一个带抖动的令牌桶或滑动窗口,重试带指数退避,并且设置一个任务级别的重试预算——前面写过 Agent 多轮重试的预算怎么分,这里同理,配额被限的时候重试预算花得飞快,“最多重试 5 次”的说法比较空泛,“这个任务累计最多花 30 秒在退避上”才有约束力。

第二级是观察降速。Agent 应该能读到自己的配额水位。网关那边本来就有“每单位时间内有多少次请求因为限流被拦截”和 consumer 维度的统计指标,把它做成 Agent 可见的协作信号:当前窗口用了多少、水位线在哪个百分比、下一次窗口什么时候重置。水位超过阈值以后,Agent 主动压缩上下文、砍掉不必要的工具试探调用。可观测性在这里的作用不是画 Grafana,而是让被治理的一方知道自己在被治理。

第三级是对外表达。给最终用户的输出要坦白,不要伪造。工具调用连环失败以后,Agent 最好输出“当前工具使用额度已满,X 秒后我会再试”;最差的做法是编造一个中间结果把界面糊过去,这就从限流问题升级成了信任问题。

两条容易被忽略的工程细节

第一,KV/上下文别跟着重试一起崩。限流期间用户会把原本的一步拆成多轮,历史越长,输入侧的 token 配额消耗得越快——这就进入上限流触发压缩、压缩又可能丢关键信息、丢信息后重新探索的环路。做法是在限流状态下冻结工具面,把尚未确认的中间观察结果做一次结构化摘要,把“接下来做什么”写进一个显式的状态,而不是靠不断增长的对话历史来承载。

第二,多 API Key 轮询只是延迟了撞墙时间。网关多 Key 轮询、结果缓存确实是缓解配额的可用手段——语义缓存落 embedding 模型和向量库,精确缓存落 Redis,后者只适合非常垂直的场景。但轮询不会消除不均衡,多个 Agent 的对齐形成“羊群同步”:所有 key 在同一秒一起撞线。抖动、租户级令牌桶分配、按 key 的独立水位告警,都是要的。还有一个别忘了的概念,权限是多项的交集:调用者原有授权、工作负载授权、任务委托范围、资源侧限制,取决于四者公共部分。限流配置同样得落到比某个单一日志条目更细的粒度上。

写在限流调试排查清单之前

排查顺序建议按这个来:先看 Agent 收到的错误到底是哪个类别,再看重试行为有没有放大问题(比较单任务要话和成功的 token 消耗),再看阈值跟上没跟上配额变化,最后再谈换模型。多数时候没想模型侧的问题,是模块自己把问题放大了。

最小告警列也列三条:consumer 级被限流拦截计数、单任务因限流发起的重试次数分布、限流时间占任务总时间的比例。第三个指标特别能够暴露一件事——用户感觉的“慢”,有多少其实是等待窗口,这部分值得在接受范围内以成本换吞吐,即高峰用贵模型错峰不用,或接入备用模型,而不是重试、重试。

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

本文的链接地址: https://blog.phpat.com/agent-engineering/661.html

手机扫码阅读

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

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