← 返回首页返回博客列表

昨天拿Claude API跑了2万条标题,凌晨3点收到账单,我立马改了这三个参数

📌 核心要点:

批量调Claude API被429和输出截断搞到凌晨,改速率控制、放宽max_tokens、开流式,成本还降了40%。

上周被一个紧急需求砸到脸上:24小时内给2000个SKU生成中文详情页。我直接调Claude 3.5 Sonnet API,单线程跑,前50条顺畅得不像话。跑到第63条,控制台开始喷429。不是偶发,是连续7个请求全被拒,剩下一千多条全卡住。

我一开始以为是API Key的问题,换了一个key,同样。看了眼响应头里的`x-ratelimit-remaining-requests`,已经归零。

Claude API的速率限制是按组织级别算的,不是按key。同一个组织下的所有key共享每分钟请求数(RPM)和每分钟token数(TPM)。Sonnet默认的RPM是1000,TPM是40000。我那个脚本虽然没有并发,但每次请求大概吃1500个token输出,每分钟发到80条就被限了。肉眼看去就是跑一截死一截。

别把速率限制当成一个开关

Anthropic在响应头里给了几个关键字段:

  • `x-ratelimit-requests-remaining` – 当前分钟剩余请求数
  • `x-ratelimit-tokens-remaining` – 当前分钟剩余token数
  • `x-ratelimit-reset-requests` / `x-ratelimit-reset-tokens` – 重置时间(秒)
  • 但很多人只看remaining,不看reset。我的做法是写了一个简易的本地速率控制器:每次请求发出前,先读`remaining`。如果剩余量低于每分钟上限的10%,就把下一次调用的时间延迟到`ratelimit-reset`指示的那个时间点之后,不是傻等几秒。

    代码逻辑很简单,不用上什么复杂的队列。关键代码段:

    remaining = int(response.headers.get('x-ratelimit-requests-remaining', 0))
    

    if remaining < 100: # 上限1000的10%

    reset_time = float(response.headers.get('x-ratelimit-reset-requests', 0))

    time.sleep(reset_time + 0.5)

    加上这段之后,429彻底消失。我不需要主动降并发,也不需要去扩容什么Tier等级。

    max_tokens设小了,输出直接给你腰斩

    处理完速率,我就发现另一个恶心问题:生成的详情页经常在关键位置断掉。比如写到“材质:精选高密度”这句话,后面就没了。因为我在请求里把`max_tokens`设成2048,觉得够用,但中文一个token不等于一个字。Claude的tokenizer对中文大概1–2个字符算一个token,一篇600字的详情页随口就要1200个token以上,加上产品参数表格那种结构化输出,token消耗翻倍。2048根本兜不住。

    我改了两个��西。

    第一步,把`max_tokens`放宽到4096,给输出留足余量。不是一刀切,而是根据prompt里要求的输出结构估算。如果要输出约500中文汉字,我会把max_tokens设成2000,留下截断缓冲。

    第二步,用了`stop_sequences`。我在prompt末尾要求用“## 结束”标记收尾,然后把`stop_sequences`设为["## 结束"]。一旦模型吐出这个字符串,API就自动停,不浪费后续token,也不怕它扯废话。这招在批量跑内容时尤其值钱——省token就是省钱,还能保证输出干净。

    这套思路在做SEO内容时被我反复用。我之前专门整理过用Claude做SEO内容的一套prompt和输出控制方法,包括怎么防止模型给标题塞感叹号、怎么压住它自动生成英文链接的毛病,都写在了Claude SEO优化实战里,这边不再展开。

    延迟不稳?不是模型慢,是你没用stream

    速率和截断搞定之后,大批量跑着跑着,发现另一个爽不起来的点:一条请求的端到端延迟经常从1.2秒飘到3秒以上。我一开始怀疑是API服务端波动,看了Anthropic的状态页,全绿。

    问题出在我没开流式。我一直用非流式调用,等模型生成完所有token才返回。TPOT(每输出token时间)大概在25–40ms,按输出1000个token算,理论耗时25-40秒?不对,我其实是把`max_tokens`设大了,但实际输出控制在500左右,所以耗时在10秒出头。但这不是关键,关键是3秒以上的毛刺来自模型首token延迟(TTFT)。非流式调用会把TTFT和生成时间打包到一起,你感知到的就是“偶尔抽风”。

    改成stream模式,`stream=True`,逐token地接收`completion`事件。我在客户端记录第一个可显示文本token到达的时间,首token延迟大概在400–800ms,后续token一个接一个往外蹦。这样一来,总耗时没变,但感知延迟降了非常多。前端可以把已接收的token实时渲染,用户根本不会觉得慢。

    如果你在做实时对话产品,这块能玩得更深。我有一篇专门讲如何把GPT-4级别模型的推理延迟从3秒压到800ms的总结,里边的策略对Claude同样有效,详见大模型推理延迟优化

    最后一行代码控制成本

    跑完那2000条后,一周账单出来,$60多。我回查了一下,发现有一个批次的prompt里,我把之前的几个示例也塞进去了,每个请求额外多消耗2000个输入token。Claude的价格:Sonnet输入$3/M tokens,输出$15/M tokens。多出来的成本就是这些示例撑出来的。

    我后面加了一条规则:系统prompt里只留指令,示例用`messages`里的user/assistant交替放,且不超过2轮。这样每次调API时,我可以单独控制是否发送示例,而不是让示例永远躺在系统prompt里吃输入费用。

    另外,Sonnet和Haiku的价差三倍,非核心场景我全切Haiku。提取产品属性、分类、标签那种不需要强语义的任务,Haiku完全够用。

    现在就这些。API调顺了,能省掉一大堆无效排查时间。

    🤖 你的网站能被AI搜索到吗?

    免费检测你的网站GEO健康分,看看ChatGPT、DeepSeek会不会推荐你

    🔍 免费GEO检测 📊 注册解锁AI分析