上下文窗口不是你的问题,你塞进去的东西才是
"The LLM is usually fine. The retrieval is the bottleneck."
这句话戳中了大量 RAG 应用开发者的痛点。当你的 AI Agent 回答质量差、幻觉高、延迟慢,第一反应往往是:上下文窗口太小了,得换个更大的模型。
但这个诊断是错的。
真正的病灶在检索层------不是你拿到了多少内容,而是你拿到的内容有多精准。
一、标准 RAG 的原罪:把垃圾塞进提示词
典型的 RAG 流程是这样的:
- 用向量数据库做余弦相似度检索,召回 Top-200 候选文档
- 把所有候选全部塞进 prompt,指望 LLM 自己找到信号
- 等待 2000ms+ 的响应,得到一个可信度不稳定的答案
这套方案的问题不是"上下文窗口不够大",而是你给 LLM 喂了一堆噪音。
向量检索(Bi-Encoder)的本质是把 query 和文档分别编码成向量,然后计算余弦相似度。速度极快(<1ms/千条),但精度有限------它衡量的是语义相似性,而不是"这段话对当前问题有多有用"。
结果:召回的 200 条里,真正有用的可能只有 10 条,剩下 190 条都是噪音。
二、Transformer 的 Attention 是 O(n²) 的
这不是细节,这是核心。
Self-Attention 的计算复杂度与序列长度的平方成正比:
| Context Token 数 | 相对计算量 | 相对延迟 |
|---|---|---|
| 1,000 | 1x | ~200ms |
| 4,000 | 16x | ~800ms |
| 16,000 | 256x | ~3.2s |
| 64,000 | 4,096x | ~12s+ |
| 128,000 | 16,384x | ~30s+ |
你每往 context 里多塞一个无关的 chunk,都在指数级地拖慢模型、推高成本。
更糟的是,研究表明 LLM 在上下文被无关信息"稀释"时,会出现**迷失在中间(Lost in the Middle)**现象------关键信息即使存在,也可能被忽略。
三、两阶段检索:精排才是正解
工程上的标准解法是两阶段检索:
yaml
Stage 1: 向量检索(快,低精度)→ 召回 Top-100~200 候选
Stage 2: 精排 Reranker(慢,高精度)→ 筛选 Top-10 传入 LLM
第一阶段用 Bi-Encoder(如 BGE-M3、text-embedding-3-large)做大范围召回,速度优先。
第二阶段 用 Cross-Encoder(如 BGE-Reranker-v2-M3、Cohere Rerank 3.5)对 query 和每个候选文档联合编码,精确评估相关性。
Cross-Encoder 为什么更准?因为它同时看到了问题和文档,能捕捉两者之间的交互语义,而不只是各自的独立特征。
代码示例(LangChain + FAISS + CrossEncoder)
python
from sentence_transformers import CrossEncoder
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
# 初始化 Cross-Encoder reranker
cross_encoder = CrossEncoder('BAAI/bge-reranker-v2-m3')
# Stage 1: 向量召回 Top-50
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.load_local("my_index", embeddings)
candidates = vectorstore.similarity_search(query, k=50)
# Stage 2: Cross-Encoder 精排,取 Top-10
pairs = [(query, doc.page_content) for doc in candidates]
scores = cross_encoder.predict(pairs)
# 按分数排序,取前 10
ranked = sorted(zip(scores, candidates), reverse=True)
top_docs = [doc for _, doc in ranked[:10]]
# 只将 Top-10 传入 LLM
context = "\n\n".join([doc.page_content for doc in top_docs])
这一改动,通常能让进入 LLM 的 token 数从 5 万降到 2500 左右。
四、主流精排方案对比
| 方案 | 类型 | 优势 | 局限 |
|---|---|---|---|
| BGE-Reranker-v2-M3 | 开源 Cross-Encoder | 免费,多语言,性能强 | 需要自托管 |
| Cohere Rerank 3.5 | 商业 API | 开箱即用,效果优秀 | 付费,有延迟 |
| ColBERT / RAGatouille | Late Interaction | 兼顾速度与精度 | 索引复杂 |
| FlashRank | 轻量 Cross-Encoder | 极快(CPU 友好) | 精度略低 |
推荐起点 :中文场景用 BAAI/bge-reranker-v2-m3,英文或混合场景试 Cohere Rerank 3.5,对延迟要求极高时考虑 ColBERT。
五、精排之外:更深层的问题
精排解决了"相关性评分"的问题,但 Naive RAG 还有三个更难的问题:
1. 每个用户都拿到一样的结果
余弦相似度不知道"谁在问"------一个新手和一个专家问同一个问题,应该得到不同深度的答案,但向量检索给不了。
2. 没有学习反馈
用户点击了哪些结果?哪些答案被标记为有用?这些行为信号在标准 RAG 中完全被忽略了。
3. 多样性缺失
Top-10 往往是同一段落的 10 个相似分块,信息密度极低。
这些问题需要引入用户上下文建模 和行为信号学习 来解决------这也是 Shaped、Cohere 等商业产品做的事,但对大多数工程团队来说,先把两阶段精排做好,已经能解决 80% 的问题。
六、效果到底能好多少?
根据公开数据和社区实践汇总:
- Token 消耗 :从 ~50,000 tokens 降到 ~2,500 tokens,节省 95%
- 延迟:从 ~2,300ms 降到 ~200-400ms(取决于 reranker 速度)
- 幻觉率:显著下降(噪音少了,LLM 更容易找到准确答案)
- RAGAS 指标:加入 BGE-Reranker 后,Faithfulness、Answer Relevancy 均有明显提升
这些收益不是免费的------精排阶段会增加额外延迟(Cross-Encoder 对 100 个候选的评分大约需要 100-300ms)。但相比省下的 LLM 推理时间和 token 费用,这笔账是划算的。
结语
上下文窗口从 4K 扩展到 128K,甚至 1M,是模型能力的进步------但它不是让你往里面随便塞东西的借口。
精准 > 全面。10 条准确的结果,远胜 200 条模糊的噪音。
RAG 系统的优化路径不是"给模型更大的窗口",而是"给模型更好的信息"。两阶段检索 + 精排,是目前最成熟、最易落地的工程方案。
如果你的 Agent 质量不尽如人意,先别换模型------先检查你的检索。