Skip to content

AI 踩坑记录 ​

G AI 域的踩坑与排查。每条固定四段:症状 → 原因 → 解法 → 预防。 系统学习见 AI 域,成本相关见 13 · 成本与模型路由。


429 / 限流:请求被拒 ​

症状

text
HTTP 429 或错误:rate limit exceeded / 429 too many requests,
偶发在并发请求或批量任务中,重试后恢复。

原因 超出供应商的 RPM/TPM(每分钟请求数/Token 数)配额。常见触发:循环里同步请求、并发没控制、消息变长后 TPM 先到顶。

解法

python
# 控制并发 + 退避重试
import asyncio, random

async def call_with_retry(func, *, max_retries=3):
    for i in range(max_retries):
        try:
            return await func()
        except RateLimitError:
            await asyncio.sleep(2 ** i + random.random())  # 指数退避
    raise RuntimeError("重试后仍限流")

预防 并发打满前先看配额;批量任务用固定速率(semaphore + 间隔);超长输入先算 token 数再决定是否截断。


token 超限:输入太长被截断/报错 ​

症状

text
报错 max context length exceeded / context_length_exceeded,
或响应看起来"忘记"了对话开头的内容。

原因 请求总 token(输入 + 输出预留)超过模型上下文窗口。对话越长越容易触发;有些 SDK 静默截断最早的轮次导致"失忆"。

解法

python
# 请求前估算并裁剪,预留输出空间
max_context = 128_000      # 按模型配置
reserve = 4_000            # 给输出留的预算
while estimate_tokens(messages) + reserve > max_context:
    messages.pop(0)        # 丢最旧的一轮,再裁剪

预防 长对话要做摘要/分层裁剪(见 12 · 上下文工程);固定输出预算,别让用户消息无限撑爆。


流式响应中断 / 解析错位 ​

症状

text
SSE 流式输出到一半断开,或 JSON 被切成两半、`JSON.parse` 抛错;
前端拿到的是拼接错位的碎片。

原因 三处都可能:网络/网关超时中断流;输出超过 max_tokens 被截断(JSON 不完整);前端按"整块"解析而不是按流累积。

解法

js
// 正确姿势:累积整个流,流结束时再一次性解析
let text = ''
const reader = res.body.getReader()
while (true) {
  const { done, value } = await reader.read()
  if (done) break
  text += new TextDecoder().decode(value)
}
const data = JSON.parse(text)   // 流结束后再 parse

预防 流式场景不要对半成品做 JSON.parse;需要"边出边用"就要求模型输出稳定结构(如每行一条 JSON);max_tokens 留足余量。


embedding 维度不一致:向量入库/查询报错 ​

症状

text
向量维度 mismatch:dimension 1536 vs 3072 / column "embedding" is of type vector(1536)

原因 入库和查询用了不同模型(如先 text-embedding-3-small=1536,后 text-embedding-3-large=3072),或表结构写死了维度。pgvector 要求列维度固定且一致。

解法

sql
-- 统一模型后,要么重建列,要么迁移数据
ALTER TABLE documents DROP COLUMN embedding;
ALTER TABLE documents ADD COLUMN embedding vector(3072);
-- 全量重算 embedding 再入库

预防 embedding 模型定死一个,写入元数据记录 model + dims;升级模型要走"双写 + 校验 + 迁移"流程。


检索质量差:召回一堆无关内容 ​

症状

text
RAG 回答答非所问,明明库里有答案却检索不到,或召回结果与问题无关。

原因 常见三类:分块太大把关键句淹没、相似度阈值设置错误(太高漏检/太低误检)、混合检索里某一路静默降级(如关键词路挂了没发现)。

解法

python
# 诊断:对比"问题"与"召回片段"的相似度分布
# 健康值参考:关键词 30% / 向量 30% / 重排 40% 左右的贡献比
# 先看单路召回有没有问题,再谈融合

预防 分块策略按内容类型设计(表格/长文不同);阈值用真实问题集校准;混合检索要监控每路贡献率(详见 GP08)。


模型"自信地胡说":幻觉难发现 ​

症状

text
回答流畅、语气笃定,但事实是错的;特别是引用"不存在的文献/链接/API"时。

原因 LLM 是"预测下一个词",不是数据库。开放域生成没有可靠来源时也会编;检索质量差时会基于错误片段放大。

解法

text
1. 要求引用来源并做可溯源校验(RAG 场景强制)
2. 高风险场景用工具/函数调用拿事实,不靠模型记忆
3. 对输出做二次校验(正则/白名单/独立模型交叉)

预防 明确"哪里必须用工具、哪里允许生成"的边界;上线前用评测集跑回归,量化幻觉率(见 GP05)。


成本失控:账单超预算 ​

症状

text
月底账单暴涨,排查发现某条链路 token 消耗异常:重复调用、缓存没生效、日志里误记录。

原因 常见:每次请求都带超长上下文、prompt 没缓存、循环里重复调用同一模型、流式/重试放大了次数、把调试输出也走了计费调用。

解法

text
1. 请求前算 token,超预算直接拒绝/降级
2. 相同前缀用 prompt 缓存(cache hit 便宜一个数量级)
3. 模型路由:简单任务用小模型,复杂任务才用大模型
4. 全链路记 usage,按天出报表对比

预防 上线前设预算上限与告警;对每类请求预估单次成本并压测峰值;定期看 usage 报表找异常链路(详见 13 · 成本与模型路由)。


API Key 泄露:被陌生人调用扣费 ​

症状

text
账单出现陌生来源调用,或第三方扫描工具报"key 明文出现在前端代码/公开仓库"。

原因 Key 写进前端代码、提交进 Git 仓库、环境变量被打印到日志/构建产物。前端代码任何人可见,放进去等于公开。

解法

text
1. 立即撤销泄露的 key 并换新
2. 前端只调自己的后端,由后端持有 key(见 C19)
3. 清理 Git 历史中的明文(rewrite + 通知泄漏渠道)

预防 Key 只放后端环境变量/密钥服务;用 .gitignore 排除 .env;监控平台开用量告警,异常调用自动停。