两种范式
随着模型上下文窗口从 4K 扩展到 1M+ token,出现了一个根本性问题:
- RAG 方案:检索相关片段,只放入少量上下文
- 长上下文方案:把所有文档直接塞进上下文窗口
两种方案各有优劣,选择哪种取决于具体场景。
长上下文模型现状
| 模型 | 上下文长度 | 约等于 | 定价倍数(vs 4K) |
|---|---|---|---|
| GPT-4o | 128K | ~300页文档 | 2x |
| Claude 3.5 Sonnet | 200K | ~500页文档 | 2x |
| Gemini 1.5 Pro | 1M | ~2500页文档 | 1.5x |
| Gemini 1.5 Flash | 1M | ~2500页文档 | 0.5x |
| GPT-4 Turbo | 128K | ~300页文档 | 2x |
RAG 优势
1. 成本优势
场景: 10万页文档库,每次查询需要其中 5 页
RAG: 检索 5 页 (~2K token) → 输入 2K token → 成本: $0.006
长上下文: 全部塞入 (~250K token) → 输入 250K token → 成本: $0.75
RAG 成本仅为长上下文的 1/125。
2. 延迟优势
RAG: 向量检索 ~50ms + LLM 2K token ~800ms = ~850ms
长上下文: LLM 250K token ~15s = ~15000ms
3. 可扩展性
RAG 的向量库可以轻松扩展到数亿文档,长上下文受限于窗口大小。
4. 来源可追溯
RAG 返回具体文档片段,可标注引用来源。长上下文中模型可能"忘记"中间部分内容。
长上下文优势
1. 无需检索错误
RAG 的检索阶段可能漏掉关键信息(Recall < 100%)。长上下文将所有信息放入,模型能看到全局。
2. 无需切分
文档切分会打断上下文,导致跨片段信息丢失。长上下文保持文档完整性。
3. 简单直接
不需要嵌入模型、向量数据库、检索流程——架构大幅简化。
4. “Lost in the Middle” 问题
虽然有此问题,但研究表明对于 <100K token 的输入,长上下文模型的准确率仍然很高:
| 输入位置 | 准确率 |
|---|---|
| 前 10% | 78% |
| 中间 40% | 56% |
| 后 10% | 75% |
缓解策略:将最相关信息放在头尾,次要信息放中间。
成本对比矩阵
假设:GPT-4o 定价 $3/M input token,1000 次查询
| 场景 | 文档总量 | RAG 成本 | 长上下文成本 | 倍数 |
|---|---|---|---|---|
| 小文档 FAQ | 50页 | $6 | $6 | 1x |
| 中等文档库 | 500页 | $6 | $60 | 10x |
| 大型知识库 | 5000页 | $6 | $600 | 100x |
| 企业级 | 50000页 | $6 | $6000 | 1000x |
准确率对比
基于多个 benchmark 的综合数据:
| 任务类型 | RAG | 长上下文 | 胜者 |
|---|---|---|---|
| 单文档 QA | 82% | 89% | 长上下文 |
| 多文档 QA | 76% | 71% | RAG |
| 事实查询 | 85% | 88% | 长上下文 |
| 多跳推理 | 58% | 64% | 长上下文 |
| 时效性问题 | 79% | 45% | RAG |
| 大规模检索 | 73% | N/A | RAG |
关键发现:文档量 <100 页时长上下文更准;>500 页时 RAG 更可靠。
混合方案:RAG + 长上下文
最佳实践是两者结合:
class HybridRAGLongContext:
"""
1. 用 RAG 检索 top-K 相关文档
2. 将检索结果 + 原始查询一起放入长上下文模型
3. 模型有充足上下文做全局推理
"""
def __init__(self, embed_fn, vector_store, llm_call,
top_k=20, max_context_tokens=50000):
self.embed = embed_fn
self.store = vector_store
self.llm = llm_call
self.top_k = top_k
self.max_tokens = max_context_tokens
def retrieve(self, query: str) -> list[dict]:
embedding = self.embed(query)
results = self.store.search(embedding, top_k=self.top_k)
# 按 token 预算裁剪
total_tokens = 0
selected = []
for r in results:
doc_tokens = len(r["text"]) // 4 # 粗估
if total_tokens + doc_tokens > self.max_tokens:
break
selected.append(r)
total_tokens += doc_tokens
return selected
def answer(self, query: str) -> str:
docs = self.retrieve(query)
# 构建长上下文 prompt
context_parts = []
for i, doc in enumerate(docs):
context_parts.append(
f"=== 文档 {i+1} (来源: {doc.get('source','未知')}) ===\n{doc['text']}"
)
context = "\n\n".join(context_parts)
prompt = f"""你是一个精确的信息检索助手。基于以下检索到的文档回答问题。
要求:
1. 只基于提供的文档回答,不要编造
2. 如果文档中没有相关信息,明确说明
3. 引用来源文档编号
检索文档:
{context}
问题:{query}
回答:"""
return self.llm(prompt)
混合方案的 token 分配
128K 上下文窗口分配建议:
系统 prompt: 2K token (1.5%)
用户查询: 1K token (0.8%)
RAG 检索结果: 80K token (62.5%) ← top-20 文档
历史对话: 10K token (7.8%)
模型回答预留: 35K token (27.3%)
决策矩阵
| 条件 | 推荐方案 | 理由 |
|---|---|---|
| 文档 < 50 页 | 长上下文 | 检索可能遗漏,直接塞入更准 |
| 文档 50-500 页 | 混合 (RAG + 长上下文) | RAG 检索 + 长上下文推理 |
| 文档 > 500 页 | RAG | 成本和延迟优势显著 |
| 需要引用来源 | RAG 或混合 | 精确来源追踪 |
| 实时对话 | RAG | 延迟低 |
| 离线分析 | 长上下文 | 延迟不敏感,准确率优先 |
| 预算充裕 | 长上下文 | 架构简单,维护成本低 |
| 预算紧张 | RAG | 成本可控 |
| 文档频繁更新 | RAG | 只需更新向量库 |
| 多语言文档 | 长上下文 | 跨语言检索准确率低 |
实战建议
- 先试长上下文:如果文档量在窗口内,先试长上下文方案,简单且准
- RAG 的检索质量是上限:检索不到的,模型再强也答不出来
- 混合方案最优:RAG 负责精准召回,长上下文负责深度推理
- 监控 token 消耗:长上下文的成本可能悄悄爆炸
- 缓存策略:对相同文档的重复查询,缓存首次的长上下文结果
- 逐步扩展:从 RAG → 混合 → 长上下文,根据效果和成本逐步调整
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
