先把结论甩出来:Agent 调大模型/工具,一定要在客户端这侧加限流(令牌桶)+ 熔断(连续失败就短路)+ 退避重试,三件套缺一不可。我上个月就是没加,被自己写的循环把账号打到限流封了 20 分钟,顺带烧了我自己一笔不算少的 token。下面是我踩完坑后现在固定用的那套写法。

我是怎么把自己打爆的

故事很蠢。我做了个"自动跑财报摘要"的小东西,let 它对一个 PDF 列表挨个调模型抽要点。某次列表里有 312 个文件,我图省事写了个 for 循环,await 都没好好排队,直接并发怼上去。

结果:

  • 前 40 个还行,第 41 个开始狂返 429

  • 我没做退避,失败了立刻重试,等于火上浇油

  • 大概一分钟后整个 key 被限流,凉了

最离谱的是,有十几个请求其实是同一个超大 PDF,模型那边超时 30 秒才报错,我这边没设超时,干等。那一晚上我盯着日志,心态有点崩。

三件套,逐个拆

1)限流:令牌桶,控住你自己

别等服务端 429,你自己先把流量压住。令牌桶最好用——平时匀速发,允许小突发,桶空了就排队。

import asyncio, time

class TokenBucket:
    def __init__(self, rate, burst):
        self.rate, self.cap = rate, burst
        self.tokens, self.ts = burst, time.monotonic()

    async def take(self):
        while True:
            now = time.monotonic()
            self.tokens = min(self.cap, self.tokens + (now-self.ts)*self.rate)
            self.ts = now
            if self.tokens >= 1:
                self.tokens -= 1
                return
            await asyncio.sleep((1-self.tokens)/self.rate)  # 缺多少补多少

调模型前先 await bucket.take()。我现在默认 rate=5/s、burst=10,够用了。

2)熔断:连续失败就别再戳了

服务端已经在喘了,你还一个劲发,纯属添乱。熔断器三态:

  • closed:正常放行

  • open:失败计数超阈值,直接短路,根本不发请求,立刻报错

  • half-open:冷却一段时间后放一个试探请求,成了就恢复

class Breaker:
    def __init__(self, fails=5, cool=15):
        self.fails, self.cool = fails, cool
        self.cnt, self.opened_at = 0, 0

    def allow(self):
        if self.cnt < self.fails: return True
        if time.monotonic() - self.opened_at > self.cool:
            return True            # half-open:放一个试探
        return False               # open:直接拒,省得打爆

    def ok(self): self.cnt = 0
    def bad(self):
        self.cnt += 1
        self.opened_at = time.monotonic()

熔断和限流不是一回事:限流管"我发多快",熔断管"对面挂了我还发不发"。两个一起上。

3)退避重试:抖动别忘

重试要指数退避,而且必须加随机抖动,不然一堆请求同一秒醒来,又是一波雪崩。

async def call_with_retry(fn, tries=4):
    for i in range(tries):
        try:
            return await fn()
        except RateLimited:
            wait = min(2**i, 8) + random.random()  # 抖动
            await asyncio.sleep(wait)
    raise

一张表理清楚

机制

解决啥

触发点

我的默认值

令牌桶限流

我发太快

发请求前

5/s,突发10

熔断

对面已经挂了

连续失败

5次/冷却15s

退避重试

偶发抖动

单次失败

4次,带抖动

超时

请求卡死

每个调用

20s

超时那行最容易漏。务必给每一次调用单独设超时,别让一个卡住的请求拖垮整条链路。

后来我换了个思路省心

写到第三版我有点烦——限流参数、模型并发上限、不同模型的 QPS 配额,全得我自己摸。后来给团队里非技术的同事搭东西,我干脆用了个零代码就能配智能体的平台:拖几个节点、挂上现成模型、把知识库接进去,一个能用的小助手就出来了,调用配额这些它后台帮你兜着,我不用再手搓令牌桶。

说实话第一版搭出来挺干的,回答短得像敷衍,我对着提示词调了小半天才像样;响应也比我本地直连慢个一两拍。但胜在那位同事自己就能改流程,不用每次来找我。我这边的限流熔断代码,就只留给真正高并发的批处理任务用了。

熔断这东西吧,平时根本想不起来,出事那一晚你会无比庆幸当初写了。你们的 Agent 有没有被自己打爆过?评论区聊聊你踩的是哪种——并发没控、还是重试没退避?

(模型/API 我走的讯飞星辰 MaaS,现成调,没自己部署,省了一摊算力的事)

更多推荐