用 LLM 搭一个能用的原型,可能只需要一个下午。但把它上线,稳定运行,让真实用户用起来满意——这是另一个量级的工程问题。
以下是我在把 RAG 系统推到生产时踩过的真实坑,每一条都有代价。
坑 1:召回了,但 LLM 没用上
RAG 系统里最常见的隐患:向量召回的文档确实相关,但模型输出的答案完全没有利用到它们。
原因通常是 prompt 结构问题。模型倾向于「相信自己的训练知识」,而不是相信你塞进去的上下文,特别是当你的 context 格式混乱、没有明确 instruction 的时候。
解决方案: 在 system prompt 里明确要求模型「只基于以下文档回答,如果文档中没有,说不知道」,并且对 context 使用结构化标记(如 XML 标签),帮助模型定位。
SYSTEM = """
你是一个知识库助手。请严格基于<documents>标签中的内容回答用户问题。
如果文档中没有相关信息,请直接说「文档中没有相关内容」。
不要根据自身知识补充未在文档中出现的内容。
"""
context = "\n".join([f"<doc id='{i}'>{d}</doc>" for i, d in enumerate(docs)])
坑 2:延迟不可预测
同一个 prompt,有时 2 秒返回,有时 30 秒。用户体验极差。
根本原因:大模型的 token 生成速度相对固定(~50 tokens/s),但输出长度差异巨大。一个「写一段 200 字总结」和「写一段 2000 字分析」的价格/时间完全不同。
解决方案:
- 在 prompt 中明确限制输出长度(
max_tokens) - 使用流式输出(SSE),让用户感知到「在生成」而不是「卡住了」
- 给 API 调用加超时,触发后给用户友好提示而不是白屏
坑 3:向量数据库的「幻觉召回」
向量相似度高不代表语义相关。Cosine similarity = 0.85 的文档,可能是讲完全不同话题但使用了相似词汇。
解决方案:
- 加 BM25 倒排索引做混合检索(hybrid search),召回后 RRF 融合
- 用 cross-encoder reranker 对召回结果重排序(延迟 +50ms,但准确率大幅提升)
- 设置相似度阈值,低于阈值的文档直接丢弃
坑 4:对话历史的 token 爆炸
多轮对话系统里,不加处理地把全部历史塞进 context,很快就会撞到 token 上限,API 调用失败。
解决方案:
def compress_history(messages, max_tokens=4000):
# 保留最近 N 轮,超出部分总结为摘要
recent = messages[-6:] # 最近 3 轮
if len(messages) > 6:
summary = summarize(messages[:-6]) # 用 LLM 总结历史
return [{"role": "system", "content": f"对话历史摘要:{summary}"}] + recent
return messages
坑 5:错误处理不够健壮
API 限流(429)、超时、偶发的 5xx,在高并发下比你想象的更常见。一个没有重试和降级的系统,在压力下很容易崩。
import tenacity
@tenacity.retry(
wait=tenacity.wait_exponential(multiplier=1, min=4, max=10),
stop=tenacity.stop_after_attempt(3),
retry=tenacity.retry_if_exception_type(RateLimitError),
)
async def call_llm(messages):
return await client.chat.completions.create(...)
坑 6:没有可观测性
上线之后出了问题,不知道是 embedding 的问题、召回的问题,还是生成的问题。Debug 全靠猜。
解决方案: 从第一天就接入 tracing。推荐 LangFuse(开源可自托管),记录:
- 每次 LLM 调用的 prompt / response / token 数
- 向量检索的查询和召回结果
- 端到端延迟分解
坑 7:评估体系缺失
没有量化指标,就没有迭代方向。不知道模型究竟在哪些问题上表现差,优化只能靠感觉。
最小可行评估集:
- 建立 100-200 条「黄金问答对」(真实用户问题 + 人工标注的理想答案)
- 定义指标:Recall@K(召回准确率)、Answer Faithfulness(答案忠实度)、User Satisfaction(用户满意率)
- 每次大改之后跑一遍评估集,对比分数
工具推荐:RAGAS(专门评估 RAG 系统,开源)
把 LLM 用好是一个工程问题,和模型本身的质量关系反而没那么大。上面这些坑,每一个都是用线上故障换来的教训。
希望你能跳过它们,直接踩更有趣的坑。