🧠AI RAG

长上下文与递归 RAG

难度:⭐⭐⭐ | 高频指数:🔥🔥 | 应用岗相关度:★★

面试回答

常见问法

  • 既然模型支持 128k / 1M 长上下文,RAG 是不是要被淘汰了?
  • 长上下文的 Lost-in-the-Middle 是怎么回事,怎么缓解?
  • 递归 RAG / Self-RAG / RAPTOR 分别在解决什么问题?
  • 长文档摘要时 Map-Reduce、Refine、Tree-of-Summaries 怎么选?
  • 长上下文 + RAG 怎么混合用?哪些场景必须混合?
  • 你们项目里上下文打多少 token?怎么权衡成本和召回?

回答

简短答案是:长上下文没有取代 RAG,反而让 RAG 的取舍更复杂

工程上要分两层看。第一层是物理代价——Transformer 注意力是 O(n²),KV cache 随上下文线性膨胀;一个 1M context 的请求,单次推理成本和延迟可能是 8k 请求的几十倍。线上根本不可能把所有用户的全量知识库都塞进 prompt,成本和延迟都不允许

第二层是效果代价——也就是 Lost-in-the-Middle。模型对 prompt 首尾的注意力远高于中段,把 100 个 chunk 平铺进去时,关键证据如果落在中间位置,召回了也用不上。Needle-in-a-Haystack 测试在 8k 以内还行,到 100k 以后命中曲线就出现明显的 U 型塌陷。

所以现在主流是混合方案:RAG 做粗筛,长上下文做精读。检索把候选从十万级压到几十个 chunk,再用长上下文模型一次性消化,避免传统 RAG 上下文窗口太小只能塞 top-3 的问题。复杂问题再叠一层递归——先检索→判断证据是否够→不够就改写 query 再检索,直到模型自己说”我能答了”。

面试时要表现出的判断力是:长上下文是新工具,不是新答案——它改变了 RAG 各环节的最优参数(top-k 可以大一些、chunk 可以粗一些),但没有改变”先粗筛后精读”这个基本架构。

追问

  • Sliding Window Attention、Ring Attention、Flash Attention 各自优化什么?
  • Needle-in-a-Haystack 测试怎么解读,为什么不能只看平均分?
  • 分层索引(父子块、摘要索引)是怎么组织的?
  • 多跳推理用 Agentic RAG 还是 pipeline RAG?怎么决定?
  • 递归 RAG 的终止条件怎么设计?
  • prompt caching / KV cache 复用对长上下文成本影响有多大?

原理展开

1. 长上下文不是免费的:成本曲线长什么样

很多人对长上下文的第一反应是”那就都塞进去呗”,但要算账。

计算成本:注意力机制是 O(n²),prefill 阶段的 FLOPs 随上下文长度平方增长。10k → 100k 上下文,单次 prefill 算力涨 100 倍,这是物理规律,再优化也跑不掉指数。

内存成本:KV cache 是 O(n),一个 70B 模型在 100k 上下文下 KV cache 能占几十 GB 显存,直接决定单卡能并发多少请求。

金钱成本:以主流 API 为例,128k 上下文比 8k 单价没变,但你真往里塞 100k token,单次成本就是 8k 请求的 12 倍。线上一天百万次请求时,这个差距是几个数量级的开销

延迟成本:prefill 时间线性增加,用户感知的”首字延迟”(TTFT)从几百 ms 涨到几秒,体验直接崩盘。

所以工程判断永远是:长上下文是有上限的奢侈品,不是”反正能塞就都塞”。

2. Lost-in-the-Middle:长上下文的效果天花板

斯坦福 2023 年的论文给出了一个反直觉结论:把关键信息放在 prompt 不同位置,模型回答正确率呈 U 型曲线——首尾高,中段低

位置:[开头] [中段]  [中段]  [中段] [末尾]
准确:  75%   55%    45%    50%   72%

为什么?训练阶段的位置编码、attention pattern、SFT 数据分布共同决定的——模型见过更多”开头有指令、末尾有问题”的样本,对中段 token 的检索能力训练不足。

工程含义有两条:

  • 塞 100 个 chunk 进 prompt,命中的不一定能被用上——召回率高 ≠ 回答准
  • 关键 chunk 要排到首尾——Rerank 之后顺序很重要,最相关的放最前面或最后面,别放中间

Needle-in-a-Haystack 测试就是为了量化这个现象——往长文档里插一句”无关的关键事实”,看模型在不同位置和不同长度下能否找回。只看平均分会被骗,要看分位数和位置热力图。

3. 递归 RAG / Self-RAG:什么时候要”再来一轮”

朴素 RAG 是单次检索 → 生成。问题是:

  • 用户问题模糊时,第一次检索可能拿不到关键证据
  • 多跳问题(“X 公司 CTO 的前公司在哪上市的?“)需要先查 X 公司 CTO,再查那家前公司
  • 复杂问题可能需要多个证据片段拼起来

递归 RAG 的核心模式:

loop:
    检索(query)
    模型判断: 证据够吗?
        够 → 生成回答,结束
        不够 → 改写 query / 拆子问题 → 继续检索
    超过 max_iter → 强制收尾

Self-RAG 在这个基础上加了”反思 token”——模型在生成时主动判断”我需要再检索吗”、“这段证据可信吗”、“这段回答有依据吗”,让检索决策内化到生成过程里。

实战里要警惕:递归 RAG 的延迟是朴素 RAG 的 N 倍,每多一轮就多一次 LLM + 检索往返。简单问题用递归 RAG 是浪费,要做问题分流——能一次答的就一次答。

4. RAPTOR:树状摘要 + 多粒度检索

RAPTOR 是 2024 年提出的递归索引方案,思路是构建摘要树

Level 0: 原始 chunks(100 字粒度)
Level 1: 对 chunks 聚类,每类生成摘要(500 字)
Level 2: 对 Level 1 摘要再聚类,再摘要(2000 字)
...
Root:    全文摘要

检索时同时查多层,粗粒度摘要回答”在哪里”,细粒度 chunk 回答”具体内容”。这对长文档问答(论文、技术手册、法律条款)特别有效——单纯切 chunk 会丢全局语义,单纯做全文摘要又丢细节。

工程上 RAPTOR 的代价是离线构建成本——每层聚类 + LLM 摘要,索引一次几十万 token,更新不友好。所以适合相对静态的语料,不适合每天大量新增的场景。

5. Map-Reduce / Refine / Tree-of-Summaries:长文档处理三种范式

不止索引,对长文档做摘要 / QA 时也有三种经典模式

Map-Reduce:
  把文档切成 N 块 → 每块独立处理(map)→ 汇总(reduce)
  优点:可并行,吞吐高
  缺点:每块看不到全局,跨块逻辑会丢

Refine:
  从第一块开始,每次把"当前答案 + 下一块"喂给模型,迭代精修
  优点:上下文连续,跨块语义保留
  缺点:串行,延迟随块数线性增长

Tree-of-Summaries:
  Map-Reduce 的递归版本,多层 reduce
  优点:保留层次信息,处理超长文档稳定
  缺点:实现复杂,调用次数最多

工程选型逻辑:

  • 要快、跨块依赖弱 → Map-Reduce(如批量摘要新闻列表)
  • 要细、跨块依赖强 → Refine(如小说翻译保持人物口吻)
  • 超长、要分层 → Tree(如百页技术文档问答)

6. 长上下文 + RAG 混合方案

主流落地形态有三种:

方案 A:RAG 粗筛 + 长上下文精读

检索 top-50 chunk(粗筛)→ 全部塞入 128k 上下文 → 生成

适合:知识密集型 QA、需要交叉引用多个证据片段。长上下文承担了”在召回结果里精读”这一步,比传统”只塞 top-3”召回完整很多。

方案 B:长上下文容纳完整文档 + RAG 做文档级路由

向量检索定位到 top-1 文档 → 整篇塞进 128k → 生成

适合:每篇文档可独立回答的场景(产品手册、论文 QA)。比 chunk 级检索少一次拼接误差。

方案 C:分层——粗粒度长上下文 + 细粒度 RAG

长上下文承载顶层摘要(全局视野)→ RAG 检索细节 chunk → 拼接生成

适合:用户问题既需要全局判断又需要细节支撑(比如”总结这本书的核心论点并给出原文引用”)。

判断标准:候选规模能否塞进 context?塞进去能否回本?回不了本就 RAG,能回本就长上下文。

7. 长上下文的工程优化:让昂贵变可承受

线上要把长上下文用起来,必须做几件事:

  • Prompt Caching:把不变的部分(system prompt、知识库 chunk)缓存 KV cache,下次只算 query。OpenAI、Anthropic、DeepSeek 都支持,命中时延迟和成本都能降 50%+
  • Context 压缩:用 LLMLingua 之类的工具对召回的 chunk 做有损压缩,去掉冗余 token,能把 100k 压到 30k 而效果损失可控
  • 流式拼装:边检索边喂给模型,不等召回全完成
  • KV cache 复用:同一文档多次问答时复用 prefill
  • 分级模型:粗筛用小模型、精排用长上下文大模型

工程上的关键认知是:长上下文不是开关,是滑动条——根据 query 复杂度动态决定上下文长度,不要一刀切。

8. 什么时候递归 RAG 反而是坑

递归 RAG 被吹得很神,但实战里要警惕:

  • 没设终止条件:模型反复说”再查一下”,无限循环,token 烧光
  • 判断不准:模型经常误判”我有足够证据了”,提前止损或过度检索
  • 延迟爆炸:每轮 2-3 秒,三轮 10 秒,用户已经走了
  • 错误放大:第一轮检索错了,改写的 query 也错,越走越偏
  • 可观测性差:用户问一次,背后跑了 5 次检索 + 3 次 LLM,trace 一长串

判断标准是:问题是否真的需要多跳。能一次答的不要递归。常见做法是先用小模型对 query 做复杂度分类,简单问题走朴素 RAG,复杂问题才进递归。

9. 实战里的混合架构长什么样

我们项目里典型的处理链是这样的:

用户 query

意图分类(小模型,分流)
  ├─ 简单事实问答 → 单次 RAG,top-5 chunk → 生成
  ├─ 复杂多跳推理 → 递归 RAG,最多 3 轮
  └─ 长文档总结  → Map-Reduce + 长上下文精读

Rerank(Cross-Encoder)

Prompt 组装(最相关的放首尾,避免 Lost-in-the-Middle)

长上下文 LLM(开 prompt caching)

后处理(引用标注、置信度)

这套架构的精髓不在每个组件多先进,而在分流——根据 query 类型走不同的链路,避免简单问题用复杂方案,复杂问题被简单方案敷衍。

对比总结

方案适用场景成本延迟召回上限
长上下文直塞全量候选小、追求极致召回极高极高
朴素 RAG简单事实问答
RAG + 长上下文精读主流知识 QA
递归 RAG / Self-RAG多跳推理、模糊查询
RAPTOR 摘要树长文档分层问答离线高、在线中高(多粒度)
Map-Reduce批量长文档摘要中(可并行)
Refine跨块连续语义高(串行)

易错点

  • 以为”上下文变长 = 答得更准”——没测 Needle-in-a-Haystack,被 Lost-in-the-Middle 暗杀
  • 长上下文直塞导致单次成本暴涨——线上一天百万次请求时财务部会找你
  • 递归 RAG 没设终止条件——陷入死循环或反复检索同一组结果
  • 把 Agentic RAG 用在简单问题上——延迟翻数倍,体验崩盘
  • Map-Reduce 用在跨块依赖强的任务——每块独立处理把上下文打散了
  • 忘开 prompt caching——长上下文场景不开缓存等于自杀
  • 把”召回了”等同于”用上了”——top-50 里关键 chunk 在中段,模型可能根本没注意

记忆技巧

记三组锚点:

  1. 三条路线:长塞 / 朴素 RAG / 递归 RAG,按「问题复杂度 × 成本预算」二维选
  2. 两个天花板:物理(O(n²) 算力 + KV cache)+ 效果(Lost-in-the-Middle)
  3. 一个底层判断:上下文长不等于有效上下文;索引粒度 ≠ 召回粒度 ≠ 入 prompt 粒度

面试速答版

长上下文没取代 RAG,反而让 RAG 取舍更复杂。两个天花板逃不掉:注意力 O(n²) 决定成本和延迟随上下文平方增长,Lost-in-the-Middle 决定中段 chunk 命中也用不上。

主流是混合方案——RAG 粗筛(把候选从十万压到几十个 chunk)+ 长上下文精读(一次消化所有候选)。复杂多跳问题再叠递归 RAG / Self-RAG,但要有终止条件避免延迟爆炸。

工程上必须做的优化:prompt caching、context 压缩、query 复杂度分流。判断标准是”问题是否真的需要更长上下文 / 更多轮检索”,不要给简单问题加复杂方案。

面试加分版

如果想多讲一层,可以补:

  • 长上下文改变了 RAG 各环节的最优参数:top-k 可以更大、chunk 可以更粗、rerank 的 top-n 可以放宽——但没有改变”先粗筛后精读”的基本架构
  • RAPTOR / 分层索引的价值在于”多粒度”:单一粒度切 chunk 永远在”语义完整”和”检索精度”之间妥协,分层是绕过这个二选一
  • Agentic RAG 真正的成本不是 token,是延迟可观测性变差——一次用户请求背后跑了几次 LLM、几次检索,trace 不清楚的话出问题根本查不到
  • Cross-ref:RAG 系统设计的完整数据流参考 RAG系统设计面试题.md,本篇聚焦”长上下文出现后取舍怎么变”
Related · RAG