429 不是意外,是常态:重新思考 AI 助手的容错设计
凌晨 2 点,你的 Clawdbot 停止响应。
日志里全是这个:
429 rate_limit_error: This request would exceed your account's rate limit
429,HTTP 协议里的"你太快了,歇会儿"。
但你的 AI 助手不会"歇会儿",它会一直重试,一直报错,直到你手动介入。
这不是 AI 助手,这是需要保姆的智障机器人。
为什么 429 是必然的?
大模型 API 的限流策略比你想象的严格:
Anthropic Claude 限制(企业外用户):
RPM (Requests Per Minute): ~50
TPM (Tokens Per Minute): ~100k
并发请求: 5-10
OpenAI 限制(个人账户):
RPM: 10-60 (根据套餐)
TPM: 40k-150k
日总量: 可能有隐藏上限
当你把 Clawdbot 接到 3 个 WhatsApp 群、5 个 Telegram 频道、10 个私聊时,消息可能同时涌入。
每条消息都触发一次 API 调用。
429 不是异常,是你触碰到了系统设计的边界。
最原始的「重试」逻辑有多糟
很多人的第一反应是加重试:
async function callAPI(prompt) {
for (let i = 0; i < 5; i++) {
try {
return await claude.chat(prompt);
} catch (error) {
if (error.status === 429) {
await sleep(60000); // 等 1 分钟
continue;
}
throw error;
}
}
throw new Error('重试 5 次全部失败');
}
这套逻辑的问题:
问题1:线性等待浪费时间
假设限流窗口是 1 分钟,你已经触发限流。
- 第1次重试:等 60 秒 → 仍然 429(因为窗口还没过)
- 第2次重试:等 60 秒 → 仍然 429
- 第3次重试:等 60 秒 → 成功
总耗时:180 秒,但实际上 API 在第 65 秒时就恢复了。
问题2:惊群效应
5 个请求同时触发 429:
请求A: 429 → 等60秒
请求B: 429 → 等60秒
请求C: 429 → 等60秒
...
60秒后,5个请求同时苏醒 → 同时重试 → 又是 429
这叫"惊群",会让限流时间无限延长。
问题3:成本无限
如果是因为"配额用完"(比如月度总量耗尽),重试根本没用。
但代码会傻傻地重试 5 次,每次等 60 秒,最后还是失败。
浪费 5 分钟 + 用户体验极差。
我认为应该这样设计
策略1:区分错误类型
不是所有错误都适合重试:
enum ErrorStrategy {
RETRY, // 网络抖动、偶发超时
FALLBACK, // 429、模型不可用
ABORT // 认证失败、参数错误
}
function classifyError(error: APIError): ErrorStrategy {
switch (error.status) {
case 429:
return error.type === 'rate_limit'
? ErrorStrategy.FALLBACK // 立刻切换模型
: ErrorStrategy.RETRY; // 偶发限流,重试
case 503:
return ErrorStrategy.RETRY; // 服务暂时不可用
case 401:
case 403:
return ErrorStrategy.ABORT; // 认证问题,重试无意义
case 400:
return ErrorStrategy.ABORT; // 参数错误,不应该重试
default:
return ErrorStrategy.RETRY;
}
}
策略2:指数退避
不要固定等 60 秒,使用指数增长的等待时间:
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
// 指数退避:1s, 2s, 4s, 8s...
const delay = Math.min(1000 * Math.pow(2, i), 30000);
// 加入随机抖动,避免惊群
const jitter = Math.random() * 1000;
await sleep(delay + jitter);
}
}
}
策略3:快速 Fallback
当检测到 429 时,不应该重试,应该立刻切换备用模型:
async function callWithFallback(
prompt: string,
models: string[]
): Promise<string> {
for (const model of models) {
try {
return await callAPI(prompt, model);
} catch (error) {
if (error.status === 429) {
console.log(`${model} 限流,切换到下一个模型`);
continue; // 立刻尝试下一个,不等待
}
throw error;
}
}
throw new Error('所有模型都不可用');
}
// 使用
const result = await callWithFallback('你好', [
'anthropic/claude-opus-4.5',
'openai/gpt-4-turbo',
'deepseek/deepseek-r1'
]);
策略4:熔断器
如果某个模型连续失败 N 次,停止尝试一段时间:
class CircuitBreaker {
private failures = new Map<string, number>();
private openUntil = new Map<string, number>();
async call<T>(model: string, fn: () => Promise<T>): Promise<T> {
// 检查熔断状态
if (this.isOpen(model)) {
throw new Error(`${model} 已熔断,${this.getResetTime(model)}秒后恢复`);
}
try {
const result = await fn();
this.recordSuccess(model);
return result;
} catch (error) {
this.recordFailure(model);
// 连续失败 5 次,熔断 10 分钟
if (this.failures.get(model) >= 5) {
this.openUntil.set(model, Date.now() + 600000);
}
throw error;
}
}
private isOpen(model: string): boolean {
const until = this.openUntil.get(model);
if (!until) return false;
if (Date.now() > until) {
this.openUntil.delete(model);
this.failures.delete(model);
return false;
}
return true;
}
}
更激进的想法:放弃"完美响应"
传统软件的思维是:要么成功,要么报错。
但 AI 助手可以有第三种状态:降级服务。
降级策略
当所有高质量模型都 429 时,不应该直接报错"服务不可用",而是:
降级1:使用缓存响应
if (isRateLimited()) {
const cached = await getCachedResponse(prompt);
if (cached) {
return `[使用缓存] ${cached}`;
}
}
适合重复性问题("今天天气怎么样"可能每天都有人问)。
降级2:简化任务
原始请求: "分析这份 50 页的财报,找出 3 个风险点"
降级请求: "总结这份财报的主要内容"
降低任务复杂度,使用更便宜的模型(Haiku 而不是 Opus)。
降级3:延迟处理
即时响应: "当前请求量较大,您的任务已加入队列,预计 5 分钟后完成。"
后台执行: 等限流解除后处理,处理完主动推送结果。
这需要一个"任务队列"系统。
结论
429 错误是大模型时代的"日常"。
传统的"重试解决一切"思维不再适用。
真正稳定的 AI 助手应该:
- 预判风险:监控 API usage,在接近限流前就降低频率
- 快速切换:429 出现后立刻 fallback,不浪费时间
- 熔断保护:连续失败后停止尝试,避免雪崩
- 降级服务:在无法提供完美服务时,给用户"次优方案"
这不是技术问题,是设计哲学的转变:
从"零容错"到"优雅降级"。
承认系统会失败,提前设计失败时的 Plan B,才是成熟的工程思维。
技术参考:
- Circuit Breaker 模式: https://martinfowler.com/bliki/CircuitBreaker.html
- 指数退避算法: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
- Rate Limiting 设计: https://stripe.com/blog/rate-limiters