RAG 系列(二十二):长上下文 vs RAG——要不要 RAG

一个看似合理的问题

Gemini 1.5 Pro 支持 100 万 token 上下文,Claude 3.5 支持 20 万 token,GPT-4 Turbo 12.8 万 token。一部小说大约 15 万字,约 20 万 token,直接塞进去就能问。有人问:RAG 还有必要吗?

这个问题值得认真回答,因为它背后藏着一个真实的决策:给一个生产系统,我应该用 RAG 还是长上下文?


先把数字摆出来

大语言模型的上下文窗口(2024--2025):

模型 上下文窗口 约合文本量
Gemini 1.5 Pro 1,000,000 tokens ~750,000 词,约 1500 页
Claude 3.5 Sonnet 200,000 tokens ~150,000 词,约 300 页
GPT-4 Turbo 128,000 tokens ~96,000 词,约 190 页
GPT-4o 128,000 tokens ~96,000 词,约 190 页

看起来很多。但一个企业知识库有多少内容?

  • 中等规模公司的内部文档:数千篇,数百万字
  • 大型代码库:数万个文件,十亿 token+
  • 新闻/研究数据库:数百万篇文章

所有这些都超出了任何模型的上下文窗口。这是长上下文能力的物理上限。


长上下文的实际代价

"窗口大"不等于"免费"。每次请求都要处理所有 token,代价是真实的。

代价一:钱

按 2024 年末的价格粗估(输入 token):

模型 每百万 token 价格 100 万 token 一次请求
Gemini 1.5 Pro $1.25 $1.25
Claude 3.5 Sonnet $3.00 $3.00
GPT-4 Turbo $10.00 $10.00

对比 RAG 的成本:

  • 检索阶段:只调用 Embedding API(< $0.001)
  • 生成阶段:只发送 2,000--5,000 token 的检索结果 + 问题(< $0.05)

同样的问题,RAG 的成本可以比长上下文低 20--200 倍。

如果一天有 1,000 个用户查询企业知识库:

  • 长上下文(1M token):约 $1,250/天
  • RAG(3K token 上下文):约 $3--15/天

代价二:延迟

处理更多 token = 更慢的响应。首 token 延迟(TTFT)随输入长度线性增长:

复制代码
100K token 输入 → TTFT ~2--5 秒
1M token 输入   → TTFT ~15--30 秒(视模型和基础设施)

对话类应用 30 秒才开始输出,用户体验基本无法接受。

代价三:中间丢失问题

2023 年 Stanford 的研究 "Lost in the Middle"(Liu et al.)发现:当相关信息放在长上下文的中间时,LLM 的召回表现显著下降。信息在开头或结尾时表现最好,在中间时表现最差。

erlang 复制代码
位置 vs 召回率(近似趋势):
开头(0-10%)   ████████████████ 高
中间(40-60%)  ██████           低
结尾(90-100%) ████████████     较高

这意味着你把 100 篇文档全塞进去,模型不一定能找到放在 50 号位置的那篇。


RAG 的实际代价

RAG 不是没有代价的。

代价一:检索不完美

向量检索是近似匹配,会出错:

  • 漏检(False Negative):相关文档没被召回。用户问了一个问题,但对应的段落语义上和问题距离较远,被排在了 top-k 之外。
  • 误检(False Positive):无关文档被召回。LLM 拿到噪声上下文,可能产生混淆或幻觉。

这是 RAG 系列前几篇一直在解决的问题:混合检索、Rerank、HyDE......本质上都是在修补检索的不完美。

代价二:分块破坏上下文

分块(Chunking)把文档切碎,相关信息可能分散在不同 chunk。一篇 10 页的研究报告,结论依赖第 3 页的假设,但被切成了两个 chunk,检索时只拿到了结论那块,LLM 缺少背景信息。

代价三:系统复杂度

RAG 是一个完整的工程系统:向量库 + Embedding 模型 + 检索链路 + 更新机制 + 评估框架。相比"直接把文档发给 LLM",它的维护成本更高。


五个维度的对比

维度 长上下文 RAG
文档量上限 ~10--100 篇(受窗口和成本限制) 无上限(向量库可扩展)
成本 高(所有 token 每次都计费) 低(只发相关片段)
延迟 高(大输入慢) 低(小输入快)
召回完整性 完美(全部内容都在) 不完整(依赖检索质量)
知识更新 需要重新发送所有内容 只更新变化的文档
工程复杂度 低(直接调用 API) 高(需要维护检索链路)
单文档理解 强(跨全文的推理) 弱(受分块影响)

没有哪一方全赢。


决策框架:用哪个?

四个维度定位你的场景:

维度一:文档量

markdown 复制代码
< 50 篇,总计 < 100K token    → 考虑长上下文
50--1000 篇                   → 评估成本后决定
> 1000 篇,或总量 > 1M token  → RAG

维度二:更新频率

复制代码
静态内容(月级更新以上)         → 长上下文可接受
动态内容(日级/小时级更新)       → RAG(增量索引成本低)
实时数据                        → RAG(或直接 API 集成)

维度三:查询次数

yaml 复制代码
一次性分析(研究、报告生成)      → 长上下文
低频查询(< 100 次/天)          → 两者都可以
高频查询(> 1000 次/天)         → RAG(成本差异会累积到不可忽视)

维度四:延迟要求

复制代码
交互式问答(< 3 秒响应)         → RAG
报告生成、离线分析               → 长上下文可接受

综合判断

复制代码
场景匹配表:

用例                    文档量    更新      查询     建议
────────────────────────────────────────────────────────
法律合同审查(单份)     小        无        一次     长上下文
企业知识库问答          大        频繁      高频     RAG
PDF 财务报告分析        中        无        一次     长上下文
产品文档问答系统        大        中频      高频     RAG
代码库理解              极大      频繁      高频     RAG
会议纪要摘要(单次)     小        无        一次     长上下文

混合策略:两者兼用

长上下文和 RAG 并不互斥,有时候最好的选择是组合:

策略一:RAG 选文档,长上下文读全文

python 复制代码
# 第一步:用 RAG 找到最相关的 3 篇文档
relevant_docs = retriever.invoke(query)  # top-3 文档

# 第二步:把完整文档(而不是 chunk)发给 LLM
full_docs = [load_full_doc(doc.metadata["source"]) for doc in relevant_docs]
full_context = "\n\n".join([doc.page_content for doc in full_docs])

# 第三步:LLM 基于完整文档回答
answer = llm.invoke(f"基于以下文档回答:{full_context}\n\n问题:{query}")

适用场景:文档数量大(不能全发),但每篇文档内部有复杂的跨段推理需求。

策略二:粗粒度 RAG + 大块上下文

传统 RAG 的 chunk 大小是 512--1024 tokens。现在窗口大了,可以用 3000--10000 token 的大块,保留更多上下文,同时仍然做检索过滤。

python 复制代码
# 分块时用大块(保留更多上下文)
splitter = RecursiveCharacterTextSplitter(
    chunk_size=4000,    # 传统 512 → 现在可以用 4000
    chunk_overlap=400,
)

# 检索时 top-k 相应减小
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 3 × 4000 = 12,000 tokens,足够精准,比传统 RAG 上下文丰富得多

策略三:摘要缓存 + 精准检索

对大型文档库,先用 LLM 生成每篇文档的结构化摘要,存入向量库;检索时先找摘要,再按需拉取原文对应段落。

python 复制代码
# 预处理:生成摘要(一次性)
for doc in all_documents:
    summary = llm.invoke(f"总结这篇文档的核心要点(200字以内):{doc.page_content}")
    # 把摘要作为检索单元
    summary_doc = Document(page_content=summary, metadata={
        "source": doc.metadata["source"],
        "original": doc.page_content,
    })
    summary_vectorstore.add_documents([summary_doc])

# 查询时:检索摘要,返回原文段落
def query_with_summary(question):
    summaries = summary_vectorstore.similarity_search(question, k=5)
    # 从原文中精确提取相关段落
    relevant_chunks = [
        extract_relevant_passage(s.metadata["original"], question)
        for s in summaries
    ]
    return llm.invoke(build_prompt(question, relevant_chunks))

现实中的选择

大窗口模型的出现确实改变了一些决策:

以前需要 RAG,现在可以不用的场景

  • 50 页以内的文档理解(直接塞进去,更简单)
  • 一次性的文档分析任务(不值得搭 RAG 系统)
  • 原型验证阶段(快速验证想法,不需要生产级 RAG)

仍然需要 RAG 的场景(大多数生产系统):

  • 知识库 > 1000 篇文档
  • 需要实时/频繁更新
  • 高并发,成本敏感
  • 需要引用溯源(RAG 天然知道答案来自哪篇文档)

大窗口模型让"简单场景不用 RAG"变得合理了。但它没有让 RAG 过时------它只是让 RAG 的适用场景变得更清晰:当文档量、更新频率、或成本让"全量上下文"不可行时,RAG 是无可替代的。


小结

长上下文 RAG
核心优势 完整上下文,跨文档推理 可扩展,成本低,实时更新
核心局限 成本高,延迟大,文档量有上限 检索不完美,工程复杂
最适合 小量文档的一次性深度分析 大规模生产系统
趋势 窗口继续变大,成本继续降低 检索质量继续提升

两者不是竞争关系,而是互补的工具箱。理解各自的代价,选对了工具,才是工程判断力。


参考资料

相关推荐
福客AI智能客服3 小时前
电商AI客服进入物流场景,服务响应开始靠近履约环节
人工智能·ai智能客服机器人
闵孚龙4 小时前
Claude Code Ultraplan 远程多代理规划全解析:AI Agent、CCR远程容器、异步规划、状态机、计划传送与企业级自动化治理
运维·人工智能·自动化
冬奇Lab4 小时前
一天一个开源项目(第105篇):Academic Research Skills - 学术研究全流程 AI 代理套件,及其工作流设计的启示
人工智能·开源·资讯
冬奇Lab4 小时前
RAG 系列(二十一):性能优化——又快又省钱
人工智能·llm
Robot_Nav4 小时前
深度学习与强化学习面试八股文知识点汇总
人工智能·深度学习·强化学习
Z1Y492Vn3ZYD9et3B064 小时前
李彦宏:今年小龙虾明年可能螃蟹,AI的杀手级产品还没定型
人工智能
啊哈哈121385 小时前
系统设计复盘:为什么 Agent 的 ReAct 循环必须内嵌确定性保护层——以 FitMind 健康助手的路由与步骤控制为例
人工智能·python·react
@蔓蔓喜欢你5 小时前
数据可视化入门:让你的数据说话
人工智能·ai
2401_832298105 小时前
破解智能体幻觉难题,OpenClaw思维链重构,夯实工业级执行可靠性
人工智能