跳到主内容

长上下文 vs RAG 实测对比:百万 token 时代,检索增强还有必要吗? ​

上下文窗口的军备竞赛这两年快得惊人:128K 已是主流配置,部分旗舰模型标称百万 token。于是团队里出现了新声音——「还搞什么向量库和切片,把全部文档直接塞进上下文不就完了?」我们拿同一批内部文档做了对比实测,结论先说:长上下文确实吃掉了一部分 RAG 的传统领地,但「检索增强已死」是误读。这篇文章给出三个维度的实测数据与最终采用的混合策略。

实测对比:质量、成本、工程三个维度 ​

质量维度:窗口大不等于注意力均匀。我们用 20 万 token 的文档集测了两类问题——「头尾附近的信息」(如文档开头的总则)和「中段细节」(如第 800 页的某个条款数值)。长上下文直塞在头部问题上与 RAG 打平,但中段细节的准确率明显下滑,这正是 Lost in the Middle 现象:模型对超长上下文中部信息的召回可靠性显著低于头尾。RAG 加重排序后,中段问题的表现稳定,因为模型每次只看被精准选出的四段。另一个质量差异是幻觉倾向:直塞 20 万 token 时,模型偶尔会「脑补」文档间不存在的关联;带引用编号的 RAG 回答则更容易核查来源。

补充一下测试方法,避免误读:我们固定 60 道题(30 道头部信息、30 道中段细节),两种方案各跑三遍取多数正确,温度设为 0 保证可复现。中段衰减在不同模型上表现不一,且窗口越接近标称上限衰减越明显——「塞到八成满」和「只塞 20 万」的体验完全不同,容量规划要按「舒适区」而不是标称值来。

成本维度:这是长上下文最容易被低估的坑。输入 token 是按次计费的,直塞方案等于每次提问都重复支付全量文档的钱。我们写了个脚本算总账:

python
def cost_compare(docs_tokens: int, queries_per_day: int, days: int,
                 input_price: float, rag_ctx_tokens: int = 4000):
    """对比两种方案一个月的输入 token 成本:直塞 vs RAG"""
    # 直塞:每次请求都携带全量文档
    raw = docs_tokens * queries_per_day * days * input_price
    # RAG:每次只带检索出的片段(约 4K token)+ 向量库摊销成本(忽略不计)
    rag = rag_ctx_tokens * queries_per_day * days * input_price
    print(f"直塞方案:{raw:,.0f} 元;RAG 方案:{rag:,.0f} 元;差距 {raw / rag:.0f} 倍")
    return raw, rag

# 20 万 token 文档,每天 500 次提问,输入单价按 0.000003 元/token 估算
cost_compare(200_000, 500, 30, 0.000003)   # 直塞约 9000 元/月,RAG 约 180 元/月

50 倍的差距在低频场景可以忽略,在日均千次以上的生产系统里就是架构决策本身。延迟同理:直塞的首 token 延迟随文档量线性恶化,而 RAG 的检索耗时可控制在几十毫秒。

工程维度:长上下文在权限和新鲜度上有结构性短板。百万 token 的语料库做不到文档级权限过滤——塞进去就人人可见;文档更新后也没有增量机制,只能整体重塞。RAG 的检索层天然按文档隔离权限、按变更增量更新,这两个能力在多部门企业场景里不是优化项,是准入项。

还有两个工程细节常被漏算。一是提示缓存:主流厂商对相同前缀的输入 token 有缓存折扣,长上下文直塞配合提示缓存能把重复成本压掉一大截,但前提是前缀稳定,多轮对话场景并不总是成立。二是响应可溯性:直塞模式下用户追问「你从哪份文档得出的结论」,模型只能凭记忆复述;RAG 模式则能精确回指文档与段落,审计与复核的成本天差地别。

实操:按场景选路的混合策略 ​

生产系统的正确形态不是二选一,而是给请求分流。我们的路由逻辑很简单,十几行代码:

python
def route(question: str, scope: dict) -> str:
    """按任务特征选路:单文档精读走长上下文,跨库问答走 RAG"""
    # 场景 1:明确指向单份文档且总量可控(< 100K token)→ 直塞
    # 典型如「帮我总结这份合同的风险条款」,上下文完整性与跨段关联最重要
    if scope.get("doc_id") and scope["doc_tokens"] < 100_000:
        return "long-context"

    # 场景 2:跨文档检索、高频问答、涉及权限隔离 → RAG
    # 典型如「对比三家供应商的报价条款」,必须检索 + 权限过滤 + 引用溯源
    if scope.get("multi_doc") or scope.get("need_acl") or scope.get("high_qps"):
        return "rag"

    # 场景 3:兜底默认走 RAG,成本与可控性更稳
    return "rag"

配套两个实践要点:其一,走长上下文的请求也要「排兵布阵」——把最可能相关的内容放在上下文头部和尾部,规避中段衰减;其二,走 RAG 的链路在召回后务必加重排序,我们的实测里 rerank 让中段问题的准确率提升最明显。

分流上线后还有一步容易忽略:两条链路的指标要分开看。长上下文链路关注上下文利用率与超长请求的 P95 延迟,RAG 链路关注召回命中率与引用准确率,混在一起统计会把问题互相掩盖。我们就吃过亏——整体准确率没变化,拆开才发现是长上下文变好了、RAG 检索变差了,混合报表让检索的问题多藏了两周。

避坑总结 ​

  1. 把窗口长度当可靠容量:标称 200K 不代表 200K 内处处可靠,中段信息召回明显弱于头尾。关键信息放头尾,或先做要点化预处理再进上下文。
  2. 成本按单次请求估算:直塞方案的钱花在「每次都重复带全量文档」,高频场景下成本与延迟随文档量线性恶化。上线前用上面的脚本算清月度总账再定架构。
  3. 无权限差异的语料混塞上下文:长上下文没有天然的文档级隔离,A 部门的合同和 B 部门的薪酬制度塞进同一个窗口就是越权事故。涉及权限隔离的场景只能走检索层过滤。
  4. RAG 效果差就全盘否定:先排查切分粒度、Embedding 模型与 rerank,多数「检索不准」是工程问题而非路线问题。检索质量没调优前,「不如直塞」的对比结论不可信。
  5. 架构上互斥二选一:单文档深度分析与跨库高频问答是两类任务,硬套同一种方案必然顾此失彼。按请求特征路由,两条链路各守各的优势区。

长上下文确实让一部分「小而全」的场景变简单了——单文档问答不再需要搭检索链路。但语料在增长、权限在收紧、成本在累计,这些约束都站在检索这一边。百万 token 时代的答案不是「检索已死」,而是「先问清楚任务是什么,再决定塞还是查」。

免责声明

本工具与文章内容仅用于学习测试场景,优化建议仅供参考,请勿直接用于生产环境,由此产生的风险自行承担。