两种范式

随着模型上下文窗口从 4K 扩展到 1M+ token,出现了一个根本性问题:

  • RAG 方案:检索相关片段,只放入少量上下文
  • 长上下文方案:把所有文档直接塞进上下文窗口

两种方案各有优劣,选择哪种取决于具体场景。

长上下文模型现状

模型上下文长度约等于定价倍数(vs 4K)
GPT-4o128K~300页文档2x
Claude 3.5 Sonnet200K~500页文档2x
Gemini 1.5 Pro1M~2500页文档1.5x
Gemini 1.5 Flash1M~2500页文档0.5x
GPT-4 Turbo128K~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 成本长上下文成本倍数
小文档 FAQ50页$6$61x
中等文档库500页$6$6010x
大型知识库5000页$6$600100x
企业级50000页$6$60001000x

准确率对比

基于多个 benchmark 的综合数据:

任务类型RAG长上下文胜者
单文档 QA82%89%长上下文
多文档 QA76%71%RAG
事实查询85%88%长上下文
多跳推理58%64%长上下文
时效性问题79%45%RAG
大规模检索73%N/ARAG

关键发现:文档量 <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只需更新向量库
多语言文档长上下文跨语言检索准确率低

实战建议

  1. 先试长上下文:如果文档量在窗口内,先试长上下文方案,简单且准
  2. RAG 的检索质量是上限:检索不到的,模型再强也答不出来
  3. 混合方案最优:RAG 负责精准召回,长上下文负责深度推理
  4. 监控 token 消耗:长上下文的成本可能悄悄爆炸
  5. 缓存策略:对相同文档的重复查询,缓存首次的长上下文结果
  6. 逐步扩展:从 RAG → 混合 → 长上下文,根据效果和成本逐步调整

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。