AI API 接入有哪些坑?踩了一周总结出 5 个必避雷点

2 阅读1分钟

上个月我帮朋友的小团队搭一套多模型调度的后端,需求不复杂——用户提问走 Claude Sonnet 4.6,生成代码走 GPT-5.5,总结长文走 Gemini 3.1 Flash。听起来三个 API Key 搞定的事,结果折腾了整整一周,各种诡异问题轮番上阵。

直接回答:AI API 接入最常踩的坑集中在 5 个方面——Key 权限与计费陷阱、速率限制(429)、响应格式不一致、流式传输断流、以及多模型切换时的 SDK 兼容性问题。大部分坑不是代码写错了,而是文档没说清楚或者你压根没注意到某个默认配置。下面逐个说。

为什么 AI API 的坑比普通 REST API 多

普通 API 你调一次拿一次结果,状态码清晰,报错信息明确。AI API 不一样——响应时间从 200ms 到 30s 都有可能,计费按 token 不按次数,流式传输还会半路断掉。各家 SDK 接口长得像但细节不同,踩坑概率比调 Stripe 高一个数量级。

graph TD
 A[你的代码] --> B{API 网关}
 B -->|正常| C[模型推理]
 C -->|正常| D[返回结果]
 B -->|429 限流| E[等待重试?]
 C -->|超时| F[连接断开]
 D -->|格式异常| G[JSON 解析失败]
 E --> H[指数退避]
 F --> I[重连逻辑]
 G --> J[兼容层处理]

坑一:429 Too Many Requests——不是你调太多,是 Tier 太低

4 月 22 号晚上我跑压测,10 并发打 Claude Sonnet 4.6,第三秒开始疯狂报错:

Error: 429 Too Many Requests
{"error":{"type":"rate_limit_error","message":"Number of request tokens has exceeded your per-minute rate limit (https://docs.anthropic.com/en/api/rate-limits)"}}

一开始以为是代码有 bug 在重复发请求,排查半天发现不是。Anthropic 新账号默认 Tier 1,每分钟只给 40k input tokens。我一条 prompt 带了 8k 上下文,5 个并发就爆了。

解决办法:

没什么花哨的方案。要么充钱升 Tier(充 $100 到 Tier 2,限额翻几倍),要么在代码里加令牌桶限流。我最后写了个简单的信号量:

import asyncio
semaphore = asyncio.Semaphore(3) # 控制并发为3

async def call_api(prompt):
 async with semaphore:
 # 你的 API 调用
 pass

但这治标不治本,生产环境并发上来还是得升 Tier 或者走聚合平台分散流量。

坑二:流式响应半路断掉,前端拿到半截 JSON

这个坑最恶心。GPT-5.5 的 streaming 模式偶尔会在传到一半的时候断开连接,没有报错码,TCP 连接直接 reset。我抓包看了下,P95 大概每 200 次请求出现 1 次,集中在响应超过 2000 tokens 的长回复上。

前端那边收到的 SSE 事件流突然停了,最后一个 chunk 的 content 字段是半截内容:

data: {"choices":[{"delta":{"content":"```python\ndef calculate_"}}]}
data: 

然后就没了。没有 [DONE],没有 finish_reason

我的处理方式:

  1. 前端加一个 15s 无新 chunk 的超时检测,超时就认为断流
  2. 断流后自动重试,把已收到的内容拼上 [继续] 作为新 prompt 发出去
  3. 后端日志记录断流频率,超过阈值告警

这个问题我也不确定是 OpenAI 那边的负载均衡问题还是我们网络链路的问题,反正加了重试之后用户体感好多了。

坑三:各家 SDK 的 messages 格式"看着一样但不一样"

Claude 和 GPT 都用 messages 数组,都有 rolecontent,但细节差异能让你 debug 到怀疑人生:

  • Claude 的 system 不放在 messages 里,要单独传 system 参数
  • GPT-5.5 的 function calling 返回的 tool_calls 字段结构和 Claude 的完全不同
  • Gemini 3.1 Pro 压根不用 messages,用的是 contents + parts

我当时写了个统一适配层,结果发现每加一个模型就要改一遍。

# 以为统一了,其实没有
def build_messages(model, system_prompt, user_msg):
 if "claude" in model:
 return {"system": system_prompt, "messages": [{"role": "user", "content": user_msg}]}
 elif "gpt" in model:
 return {"messages": [{"role": "system", "content": system_prompt}, {"role": "user", "content": user_msg}]}
 elif "gemini" in model:
 # 完全不同的结构...
 pass

解决办法: 要么自己维护一个 adapter 层(累),要么用 OpenAI 兼容协议的聚合网关。OpenRouter、ofox.ai 这类平台都支持把不同模型统一成 OpenAI 格式调用,ofox.ai 额外支持 Anthropic 原生协议和 Gemini 原生协议三种格式,改个 base_url 就行,不用每个模型写一套适配逻辑。

from openai import OpenAI

# 统一用 OpenAI SDK 调所有模型
client = OpenAI(api_key="your-key", base_url="https://api.ofox.ai/v1")

# 调 Claude
resp = client.chat.completions.create(model="claude-sonnet-4-20250514", messages=[...])

# 调 GPT
resp = client.chat.completions.create(model="gpt-5.5", messages=[...])

坑四:计费黑洞——你以为省钱,实际在烧钱

这个坑不报错,静悄悄地掏空你余额。

上周三我发现项目的 Claude API 账单突然从日均 8涨到8 涨到 23。查了半天,原因是有个同事的 RAG 流水线把检索到的 5 篇文档全塞进 context,每次请求 input tokens 高达 32k。他本地测试没问题因为量小,一上线并发就炸了。

Anthropic 的 dashboard 只能看到总消耗,不能按 API Key 维度拆分到具体哪个接口在烧钱。

教训:

  • 每个功能模块用独立的 API Key
  • 代码里加 token 计数(tiktoken 或模型自带的 usage 字段),超过阈值就截断
  • 设置每日消费上限,OpenAI 和 Anthropic 都支持在后台设 hard limit

坑五:模型版本更新导致输出格式漂移

4 月初 GPT-5.5 有一次静默更新(OpenAI 没发 changelog),我的 JSON mode 输出突然多了一个 metadata 字段,下游解析直接炸了:

json.decoder.JSONDecodeError: Extra data: line 1 column 234 (char 233)

因为我用的是 json.loads() 严格解析,多一个没预期的字段倒不会报错,但嵌套结构变了就寄了。

防御措施:

  • 用 Pydantic 做 schema 校验,多余字段自动忽略
  • 固定模型版本号(比如 gpt-5.5-0410 而不是 gpt-5.5
  • 写集成测试,每天跑一次检测输出格式是否漂移

我的最终方案

折腾一周之后我的结论:

  1. 限流——代码层面加信号量 + 指数退避,预算够就升 Tier
  2. 断流——客户端超时检测 + 自动重试
  3. 格式兼容——要么自己写 adapter,要么用聚合网关省事
  4. 计费监控——独立 Key + 每日上限 + token 计数
  5. 版本漂移——Pydantic 校验 + 固定版本号 + 日常集成测试

没有哪个方案是完美的。第 3 点我目前用聚合网关感觉最省心,但也意味着多了一层依赖。第 2 点的断流问题到现在也没彻底根治,只是用重试兜住了。如果你有更好的办法欢迎评论区聊聊。

接 AI API 这事,代码量不大,但心智负担比你想象的重。提前知道这 5 个坑,至少能少熬两个通宵。