1. 接大模型 API 之前,先把这几个坑摸清楚
大模型 API 接入这件事,看起来就是拿个 Key、拼个请求、解析一下返回,三行代码跑通 Demo 的事。但我自己从最早折腾各种大模型接口到现在,帮团队和外部合作方排查过的接入问题少说也有几十次,真正让人卡住的从来不是“怎么调通”,而是调通之后在生产环境里冒出来的一堆幺蛾子:429 限流、Key 失效、缓存穿透、上下文超限、错误码看不懂。这些问题在 Demo 阶段一个都不会出现,一上量就全来了。
这篇内容适合两类人看:一类是正准备把大模型 API 接进自己项目里的开发者,另一类是已经接了但被各种报错折腾得够呛的工程师。我会围绕大模型 API 接入这条主线,把429 限流、Key 管理、缓存策略、上下文长度、错误排查这几个核心坑位逐个拆开讲,每个坑都会说清楚它为什么会发生、怎么提前规避、出了问题怎么快速定位。文中涉及的具体参数和配置,我会给出可直接参考的数值和代码片段,但你要根据自己的业务场景做调整,不要照搬。
先说一个基本认知:大模型 API 和传统的 REST API 有本质区别。传统接口你调一百次和调一万次,响应时间基本是线性的;大模型 API 不是,它的响应时间跟你的输入长度、输出长度、当前服务端的负载情况都强相关,而且计费方式也完全不同。这意味着你在做架构设计的时候,不能套用以前那套“无状态接口随便调”的思路。下面我按坑位一个一个说。
2. 坑位一:429 限流不是简单的“请求太快”
2.1 429 到底在限什么
很多人第一次遇到 429 的反应是“我请求发太快了,加个 sleep 就行”。这个理解只对了一半。大模型 API 的 429 通常由多个维度的限制共同触发,不同厂商的侧重点不一样,但大体上离不开这几类:
- RPM(Requests Per Minute):每分钟请求数上限,这个最好理解。
- TPM(Tokens Per Minute):每分钟 Token 消耗上限,这个才是最容易踩的。你发了 10 个请求,每个请求输入 8000 Token,那这一分钟就消耗了 80000 Token,哪怕你只发了 10 次请求,照样可能触发限流。
- 并发连接数:同时保持的请求数量上限,超过就排队或直接拒绝。
- 日配额:每天的总调用量或总 Token 量上限,这个通常在免费额度或低档套餐里卡得比较死。
我见过一个典型的场景:某个团队用大模型做批量文档摘要,脚本里开了 20 个线程并发跑,每个文档平均 6000 Token 输入。结果跑了不到 30 秒就开始大面积 429,他们以为是并发太高,把线程降到 5 个,还是 429。原因就是 TPM 被打满了——20 个线程每个 6000 Token,一轮就是 120000 Token,远超他们套餐的 TPM 上限。
2.2 限流触发的判断与处理策略
处理 429 的核心思路不是“避免触发”,而是“触发之后怎么优雅地退避和恢复”。因为不管你怎么优化,只要业务量上去了,限流是一定会碰到的。你需要做的是:
第一,区分 429 的类型。有些厂商会在返回头里带上具体的限流维度信息,比如x-ratelimit-remaining-requests、x-ratelimit-remaining-tokens这类字段。你在代码里应该把这些头信息打日志,方便排查到底是哪个维度被打满了。
第二,实现指数退避重试。不要用固定间隔重试,那样只会让情况更糟。标准的做法是:第一次重试等 1 秒,第二次等 2 秒,第三次等 4 秒,以此类推,加上随机抖动(jitter)避免多个客户端同时重试造成惊群。
import time import random import requests def call_llm_api(url, headers, payload, max_retries=5): for attempt in range(max_retries): resp = requests.post(url, headers=headers, json=payload) if resp.status_code == 429: wait = (2 ** attempt) + random.uniform(0, 1) print(f"触发限流,第 {attempt+1} 次重试,等待 {wait:.2f}s") time.sleep(wait) continue return resp raise Exception("超过最大重试次数,仍然被限流")第三,做客户端侧的令牌桶限流。与其等服务端给你 429,不如自己在客户端就控制好发送速率。你可以根据自己套餐的 RPM 和 TPM 算出一个安全的发送速率,用令牌桶算法在本地做限流。比如你的 TPM 是 100000,平均每个请求消耗 3000 Token,那你每分钟最多发 33 个请求,折算下来大约每 1.8 秒发一个。这个速率你可以在代码里硬编码,也可以做成配置项动态调整。
注意:指数退避的最大重试次数不要设太多,一般 3 到 5 次就够了。如果重试 5 次还是 429,说明你的请求速率已经严重超出套餐限制,继续重试只会浪费时间和配额,不如直接报错让上层业务决定是降级还是排队。
2.3 多 Key 轮换的正确姿势
有些团队会申请多个 Key 来做轮换,试图绕过单 Key 的限流。这个做法本身没问题,但有几个细节要注意:
- 轮换策略要跟限流维度对齐。如果限流是按账号维度算的,那你同一个账号下申请再多 Key 也没用。
- 轮换要有健康检查。某个 Key 被限流了,你应该把它暂时标记为不可用,过一段时间再放回池子里,而不是每次都随机选一个然后撞运气。
- 日志里要记录每个 Key 的使用情况,方便你判断哪个 Key 快到期了、哪个 Key 的配额快用完了。
我自己的做法是维护一个 Key 池,每个 Key 带一个状态标记和冷却时间。请求的时候优先选状态为“健康”的 Key,如果全部都在冷却中,就选冷却时间最早结束的那个,同时触发告警。
3. 坑位二:Key 管理看起来简单,出事就是大事
3.1 Key 泄露的常见路径
大模型 API 的 Key 本质上就是你的钱包,谁拿到谁就能花你的钱。我见过太多因为 Key 管理不当导致账单爆炸的案例,常见的泄露路径包括:
- 硬编码在前端代码里。这是最致命的,前端代码是公开的,任何人打开开发者工具就能看到你的 Key。我见过一个创业团队把 Key 直接写在 React 组件里,上线第二天就被刷了几百万 Token。
- 提交到了公开的代码仓库。GitHub 上有大量爬虫专门扫描新提交的代码里的 Key,你前脚 push,后脚就有人拿去用。
- 在日志里打印了完整的 Key。有些调试代码会把请求头完整打出来,Key 就跟着进了日志系统,然后被各种有日志权限的人看到。
- 在客户端应用里明文存储。桌面端或移动端应用如果直接把 Key 存在本地配置文件里,用户很容易就能提取出来。
3.2 Key 的安全管理方案
正确的做法是:Key 永远只存在于服务端。你的客户端应用应该调用你自己的后端服务,由后端服务去调用大模型 API。这样 Key 不会暴露给最终用户,你也可以在后端做限流、审计、计费等控制。
如果你确实需要在客户端直接调用大模型 API(比如做一些离线场景),那至少要做到:
- 使用临时凭证或短期 Token,而不是长期有效的 Key。
- 对 Key 做权限限制,比如只允许调用特定的模型、限制每日调用量。
- 在客户端对 Key 做加密存储,不要明文写在配置文件里。
对于服务端的 Key 管理,我建议用环境变量或密钥管理服务来注入,不要写在代码里。如果你用 Docker 部署,可以用 Docker Secret;如果用云服务,可以用对应的密钥管理产品。至少至少,要把 Key 放在.env文件里,并且把.env加入.gitignore。
注意:如果你怀疑 Key 已经泄露了,第一件事是去控制台吊销旧 Key 并生成新 Key,第二件事是检查账单和调用日志,看有没有异常调用。不要先想着排查泄露原因,先把损失止住。
3.3 Key 失效的排查思路
除了泄露,Key 失效也是常见问题。典型报错包括no api key for provider route、the specified key does not exist、permission denied等。排查的时候按这个顺序来:
- 确认 Key 是否过期。很多平台的 Key 有有效期,到期需要续期或重新生成。
- 确认 Key 是否有权限调用目标模型。有些 Key 只对特定模型或特定接口有权限。
- 确认 Key 是否被禁用。如果平台检测到异常调用,可能会自动禁用 Key。
- 确认环境变量是否正确加载。我遇到过好几次是
.env文件改了但服务没重启,导致加载的还是旧 Key。 - 确认请求头格式是否正确。不同平台的认证头格式不一样,有的是
Authorization: Bearer xxx,有的是x-api-key: xxx,搞错了就会报 Key 不存在。
4. 坑位三:缓存用不好,省钱变烧钱
4.1 大模型场景下的缓存特殊性
缓存这个词在传统后端开发里大家都熟,但大模型 API 的缓存有几个特殊之处:
- 输入相同不代表输出相同。大模型的输出有随机性(temperature 参数控制),同样的输入可能得到不同的输出。所以你不能简单地用输入做 Key 来缓存输出,除非你把 temperature 设为 0。
- Token 成本高。传统接口的缓存主要是为了降低延迟和数据库压力,大模型 API 的缓存更多是为了省钱。一次调用可能花掉几毛钱甚至几块钱,缓存命中一次就省一次。
- 缓存粒度不好把握。按整个请求缓存太粗,按 Token 缓存又太细,需要根据业务场景找到合适的粒度。
4.2 什么场景适合做缓存
不是所有场景都适合做缓存。我总结了几类适合缓存的场景:
| 场景类型 | 是否适合缓存 | 原因 |
|---|---|---|
| 固定问答对 | 适合 | 问题和答案都是确定的,缓存命中率高 |
| 文档摘要 | 适合 | 同一文档多次摘要需求,可以缓存结果 |
| 代码生成 | 谨慎 | 同样需求可能期望不同实现,缓存可能不合适 |
| 创意写作 | 不适合 | 每次都需要不同的输出 |
| 数据提取 | 适合 | 从固定格式文档提取固定字段,结果确定 |
| 对话系统 | 部分适合 | 系统提示词部分可以缓存,用户输入部分不适合 |
4.3 缓存实现的关键细节
如果你决定做缓存,有几个细节必须处理好:
缓存 Key 的设计。不要只用用户输入做 Key,要把模型名称、temperature、max_tokens 等参数一起纳入 Key 的计算。否则你改了参数但缓存还是旧的,就会出现“明明改了 prompt 但输出没变”的诡异现象。
import hashlib import json def make_cache_key(model, messages, temperature, max_tokens): raw = json.dumps({ "model": model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens }, sort_keys=True) return hashlib.sha256(raw.encode()).hexdigest()缓存过期策略。大模型的输出可能会随着模型版本更新而变化,所以缓存不能永久有效。我一般设置 24 到 72 小时的过期时间,具体看业务对时效性的要求。如果是固定知识类的问答,可以设长一点;如果是跟实时信息相关的,就要设短一点甚至不做缓存。
缓存穿透和雪崩。这两个传统缓存的问题在大模型场景下同样存在。缓存穿透是指大量请求查询不存在的 Key,导致每次都打到 API;缓存雪崩是指大量缓存同时过期,导致瞬间大量请求打到 API。解决办法跟传统缓存一样:空结果也缓存(设短过期时间)、过期时间加随机抖动。
注意:如果你用的是 Redis 做缓存,注意大模型返回的内容可能很长,要确认 Redis 的 value 大小限制和内存容量。我见过有人缓存了几万条长文本,把 Redis 内存打满导致整个服务不可用。
5. 坑位四:上下文长度不是“越大越好”
5.1 上下文窗口的真实限制
每个大模型都有一个上下文窗口限制,比如 4K、8K、32K、128K 等。这个限制指的是输入 Token 加输出 Token 的总和,不是单独的输入或输出。很多人只关注输入长度,忘了给输出留空间,结果请求发出去直接被拒绝。
举个例子:某个模型的上下文窗口是 8192 Token,你发了 8000 Token 的输入,然后设置max_tokens=2000。这时候总需求是 10000 Token,超过了 8192 的限制,API 会直接报错。正确的做法是确保输入 Token 数 + max_tokens <= 上下文窗口大小,并且留一些余量。
5.2 Token 计算与截断策略
Token 和字符不是一一对应的。英文大约 1 Token 等于 4 个字符,中文大约 1 Token 等于 1 到 2 个汉字。但这不是精确的换算,不同模型的 Tokenizer 不一样,最好用官方提供的 Token 计算工具来算。
如果你不确定输入有多长,可以在代码里做预检查:
def estimate_tokens(text): # 粗略估算:中文按 1.5 字符/Token,英文按 4 字符/Token chinese_chars = sum(1 for c in text if '\u4e00' <= c <= '\u9fff') other_chars = len(text) - chinese_chars return int(chinese_chars / 1.5 + other_chars / 4) def check_context_limit(messages, max_tokens, context_window): total_input = sum(estimate_tokens(m["content"]) for m in messages) if total_input + max_tokens > context_window: raise ValueError(f"输入 {total_input} Token + 输出 {max_tokens} Token 超过上下文窗口 {context_window}")如果输入确实太长了,你有几个选择:截断、分段处理、或者用支持更长上下文的模型。截断的时候要注意保留最重要的部分,比如系统提示词和最近几轮对话通常比早期的对话更重要。
5.3 长上下文带来的隐性成本
即使模型支持很长的上下文,也不意味着你应该无脑用满。长上下文有几个隐性成本:
- 费用更高。大部分平台按 Token 计费,输入越长费用越高。
- 延迟更大。输入越长,模型处理时间越长,响应越慢。
- 效果可能下降。有研究表明,当上下文过长时,模型对中间部分信息的注意力会下降,也就是所谓的“迷失在中间”现象。
所以我的建议是:能用短上下文解决的,不要用长上下文。如果确实需要处理长文档,考虑先做摘要或检索,只把最相关的部分送给模型。
6. 坑位五:错误码看不懂,排查全靠猜
6.1 常见错误码速查
大模型 API 的错误码五花八门,我整理了一份常见错误码的速查表:
| 错误码/错误信息 | 含义 | 处理方式 |
|---|---|---|
| 429 Too Many Requests | 触发限流 | 指数退避重试,检查 RPM/TPM 配额 |
| 401 Unauthorized | Key 无效或过期 | 检查 Key 是否正确、是否过期 |
| 403 Forbidden | 无权限 | 检查 Key 是否有目标模型的权限 |
| 400 Bad Request | 请求格式错误 | 检查请求体 JSON 格式、参数类型 |
| 404 Not Found | 接口路径错误 | 检查 API 地址和路径 |
| 500 Internal Server Error | 服务端错误 | 重试,如果持续则联系平台 |
| context_length_exceeded | 上下文超限 | 缩短输入或增大上下文窗口 |
| no api key for provider | 未配置 Key | 检查环境变量和配置文件 |
| the specified key does not exist | Key 不存在 | 检查 Key 是否被删除或拼写错误 |
| exceeded retry limit | 重试次数超限 | 检查重试逻辑,可能是持续限流 |
6.2 排查思路与工具
遇到错误的时候,不要慌,按这个顺序排查:
第一步,看错误码和错误信息。大部分问题从错误码就能看出来,比如 401 就是 Key 的问题,429 就是限流的问题。
第二步,看请求日志。把你的请求 URL、请求头(脱敏后)、请求体、响应状态码、响应体都打出来。我见过太多人排查半天,结果发现是请求体里某个字段拼错了。
第三步,用 curl 或 Postman 单独测试。把代码里的请求复制出来,用 curl 单独发一次,排除代码层面的问题。如果 curl 能通但代码不通,那就是代码的问题;如果 curl 也不通,那就是配置或网络的问题。
第四步,检查网络和代理。如果你在公司内网,可能有防火墙或代理限制。确认你的服务能正常访问大模型 API 的域名和端口。
第五步,看平台的状态页。有些平台会有服务状态页面,如果平台本身在维护或故障,你怎么排查都没用。
6.3 日志与监控的建设
生产环境一定要做好日志和监控。我建议至少记录以下信息:
- 每次请求的模型名称、输入 Token 数、输出 Token 数、耗时、状态码
- 每个 Key 的使用情况:调用次数、Token 消耗、错误率
- 缓存命中率和缓存节省的 Token 数
- 限流触发的次数和频率
有了这些数据,你才能知道你的系统到底健不健康,哪里需要优化。我见过一个团队,一直觉得自己的调用量不大,结果一上监控发现每天有 30% 的请求是重复的,加上缓存之后直接省了三分之一的费用。
7. 一些实操中的经验与避坑建议
7.1 接入前的检查清单
在正式接入之前,我建议你先过一遍这个清单:
- Key 是否已经配置在服务端,没有硬编码在任何客户端代码里
- 是否已经了解了目标平台的 RPM、TPM、日配额限制
- 是否已经实现了指数退避重试逻辑
- 是否已经做了客户端侧的限流
- 是否已经确认了上下文窗口大小和 Token 计算方式
- 是否已经设计了缓存策略(如果适用)
- 是否已经配置了日志和监控
- 是否已经准备了降级方案(比如 API 不可用时切换到备用模型或返回兜底结果)
7.2 关于免费额度和试用
很多平台会提供免费额度或试用期,用来做开发和测试是够的,但不要指望用免费额度跑生产。免费额度通常有严格的限流和配额限制,而且随时可能调整。如果你的业务依赖大模型 API,一定要提前做好付费方案和预算。
7.3 关于模型选择
不同模型的定价、能力、上下文窗口都不一样。不要一上来就用最贵的模型,先想清楚你的业务到底需要什么级别的能力。很多场景用中等模型就能达到可接受的效果,成本却能降一大截。我一般会先用小模型跑一遍,看效果能不能满足需求,不行再往上换。
7.4 关于超时设置
大模型 API 的响应时间波动很大,短则一两秒,长则几十秒。你的 HTTP 客户端超时时间要设置得合理,太短会导致正常请求被中断,太长会导致资源被长时间占用。我一般设置连接超时 10 秒,读取超时 60 秒,具体根据你的业务场景调整。如果是流式输出,超时逻辑要另外处理。
7.5 关于并发控制
不要无限制地并发调用大模型 API。除了会触发限流之外,还会导致你的服务端资源被大量占用。我建议用一个信号量或线程池来控制并发数,具体数值根据你的配额和服务端资源来定。一般来说,从 5 到 10 个并发开始测试,逐步调整。
8. 最后说几句实在的
接大模型 API 这件事,技术门槛其实不高,难的是把各种边界情况处理好。我上面说的这些坑,每一个都是我或者我身边的人真实踩过的。有些坑踩一次就记住了,有些坑因为业务变化会反复出现,所以做好监控和日志特别重要。
如果你刚开始接,建议先用最小的成本跑通一个完整流程,把限流、重试、缓存、日志这些基础设施搭好,然后再逐步扩大规模。不要一上来就追求大而全,那样很容易在细节上翻车。
另外,大模型这个领域变化很快,平台的限制、定价、接口格式都可能调整。你今天看到的参数,下个月可能就变了。所以不要把任何配置写死在代码里,尽量做成可配置的,方便随时调整。我自己是把所有跟平台相关的配置都放在一个独立的配置文件里,改的时候只改一个地方,不用满代码找。
还有一个我踩过的坑:不要忽略错误处理。很多人写代码的时候只考虑成功的情况,结果一遇到错误就整个流程崩掉。大模型 API 的错误率比传统接口高不少,你必须把每一种错误都考虑到,并且有对应的处理策略。该重试的重试,该降级的降级,该告警的告警,不要让一个 API 错误把你的整个服务拖垮。