给Agent调用做限流熔断保护
先把结论甩出来: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,现成调,没自己部署,省了一摊算力的事)
更多推荐



所有评论(0)