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

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


参考资料

相关推荐
王小义笔记11 分钟前
CUDA 版本下 Transformers 报错排查与解决办法
llm·transformer·cuda
逸模5 小时前
告别熬夜手工整理台账,逸模智能归集实现项目数据自动化存档
大数据·运维·人工智能·笔记·其他·信息可视化·自动化
weixin_397574096 小时前
生产管理和设备管理:制造执行层的AI痛点
人工智能·制造
冬奇Lab6 小时前
Agent 系列(16):工具链设计——让 LLM 用对工具的五个原则
人工智能·llm·agent
冬奇Lab6 小时前
每日一个开源项目(第125篇):taste-skill - 给 AI 装上审美,让前端不再千篇一律
人工智能·开源·agent
Ajie'Blog6 小时前
Copilot Agent Tasks API 开放:AI 编程开始进入后台任务时代
服务器·前端·javascript·人工智能·copilot·ai编程
SEONIB_Explorer6 小时前
AI SEO 与传统SEO成本对比:哪种更划算?
人工智能
一次旅行6 小时前
AI领域每日资讯报告
人工智能
Python私教6 小时前
Cursor + Claude Code 全流程实战:搭一套生产级 AI 编程工作流(2026 最新版)
人工智能·语言模型·qwen·ollama·本地大模型·大模型部署·deepseek