LLM 面试备考
首页/RAG

上下文感知检索:在索引前补回被切掉的上下文(Contextual Retrieval)

所属:01-fundamentals / 03-rag
面试权重:★★★
来源:《深入理解 AI Agent:设计原理与工程实践》第 3 章(李博杰,github.com/bojieli/ai-agent-book);非一手实测
相关知识点:pipeline.md(失败分类与决策树)、chunking.md(分块切断上下文的伏笔)、hybrid-rerank.md(BM25 + reranker 是它的两个搭档)、05-agent-evaluation/rag-eval.md(Recall@k / Hit Rate@k 的口径)、04-agent-architecture/memory.md(双层记忆架构)

一句话#

分块必然把片段与原文的联系切开(chunking.md 末尾点出、但只给了切分时的手段),上下文感知检索(Contextual Retrieval)就是在入库之前用一次 LLM 调用给每个 chunk 补一段「前缀摘要」,把「这块在原文里是谁、属于哪一节、在说什么」重新锚回去 —— 它对 BM25 加的是可精确匹配的关键词,对向量加的是消歧的语义背景,所以两路检索同时变好。

为什么重要(面试角度)#

  • 它是 chunking.md 那条悬线的收口。 那篇说「分块会切断答案」,但列出的手段(递归 / 结构感知 / 父子分块 / overlap)全是切分时的动作 —— 它们只能把切点选得更自然,补不回「这一块属于哪一节、这一节在说什么」这类背景(overlap 只是把上下文复制到邻块,不是生成一段关于该块的描述)。本文件补上的正是索引期的第二类解法;缺了它,那条线是断的。
  • 它有可以背的硬数字(49% / 67% / 约 1 美元 / top-20),是 RAG 里少见的「方法极简、收益被量化」的一处 —— 答得出来立刻显得不止读过概念。
  • 它是很好的「概念分界」题:上下文感知检索 vs 上下文压缩,名字像、作用时机与方向恰好相反,是面试官最爱设的混淆点。
  • 它横跨两个尺度:既服务共享知识库(文档 RAG),也服务用户记忆(对话历史检索),并天然接到 04-agent-architecture/memory.md 的双层记忆架构。
  • 它的定位容易说错:书中反复强调,它不是架在智能体化 RAG 之上的更高一层,而是回过头去修补最基础的分块环节、提升每个分块自身的可检索性 —— 属于「改变知识的切分方式」,与 RAPTOR / GraphRAG(改变组织)、智能体化 RAG(改变检索控制)并列,而不是它的上层。

核心内容#

1. 问题:孤立地看一个 chunk,丢的是四类「指代与背景」#

书中给的例子是「该公司第二季度的收入增长了 3%」。脱离原文后它同时丢掉了:

丢的东西 例子 后果
代词指代 「它的价格是 300 元」里的「它」 检索器不知道找什么实体
主语 / 实体 「该公司收入增长 3%」 「该公司」是 ACME 还是别的公司?
时间与条件 「2025 Q2」「若下周仍下雨」 时间参照、前提条件丢失
章节归属 这段出自哪份报告的哪一节 无法回答「这块在原文里属于哪一部分」

这四类信息在嵌入阶段就损失掉了,直接拉低后续检索准确率。注意关键点:现有手段修不了它。

现有手段 在哪一步做 为什么不够
固定 / 递归切分 切分时 只优化切点位置,块本身仍缺背景
结构感知切分 切分时 若块能带上完整标题路径可缓解;但标题路径不是「这节在说什么」
父子分块(small-to-big) 切分时 缓解生成时的完整性;检索用的仍是有歧义的小块
overlap 切分时 把上下文复制到邻块,不生成描述;还带来索引膨胀与近重复

一句话:切分时的手段可以让块切得更漂亮,但补不回一块「它从未在原文里出现过」的背景。 这需要一次生成。

2. 方法:索引前,用 LLM 给每个 chunk 生成前缀摘要#

做法极简 —— 在向量化入库之前,为每个 chunk 生成一段简短的、包含核心上下文的「前缀摘要」,再把前缀 + 原文拼接后去索引:

chunk 原文 :  "其收入增长了 3%。"

前缀摘要   :  [本段内容节选自 ACME 公司 2025 年 Q2 财务报告的「关键业绩指标」章节]

落库/索引文本: [本段内容节选自 ACME 公司 2025 年 Q2 财务报告的「关键业绩指标」章节]
              其收入增长了 3%。

→ 再把这段拼接文本同时喂给 稀疏索引(BM25) 与 稠密索引(向量)

原本模糊的「其收入增长了 3%」被重新锚定回它原始的语义环境。检索后注入生成时,可以用「前缀 + 原文」,也可以剥掉前缀只留原文 —— 前缀的作用是让块被找到,不必一定进最终答案。

为什么它同时提升两路检索(这是全篇最该讲清的一点):

检索路 前缀加了什么 效果
稀疏(BM25) 原本不在 chunk 里的关键词:实体名(「ACME」)、时间(「2025 年第二季度」)、章节名(「关键业绩指标」) 查询里的专有名词 / 时间现在能精确词面命中,而原来这些词根本不在块里
稠密(向量) 语义背景,把块锚回它的真实主题 降低 chunk 之间的语义歧义:一堆「某公司收入增长 X%」的块不再挤在同一片向量邻域里互相干扰

这也解释了为什么它天然和 hybrid-rerank.md 里的 BM25 是一对:上下文感知检索补的是 BM25 最擅长的关键词,正好喂到它的强项上。

3. 与「上下文压缩」划清界限(最容易被混的一处)#

两者名字都带「上下文」,但时机、对象、方向全相反。书中点明:名字相近,作用的时机和对象完全不同。

上下文感知检索 上下文压缩
英文 Contextual Retrieval Context Compression(书中称「上下文感知压缩」)
何时 索引期(入库前) 运行期(注入前 / 出库后)
对象 知识库里的文本块(chunk) 注入上下文的检索结果 / 会话历史
做什么 加法 —— 补前缀、加背景,提升可检索性 减法 —— 按当前任务裁剪、丢弃无关内容,节省窗口
改变了什么 「能检索到什么」 「给模型看多少」
一句话 加在入库前 减在出库后
  • 上下文感知检索的目的是让块更容易被找到(recall 侧)。
  • 上下文压缩的目的是让注入的上下文更少、更干净(成本与噪声侧),仓库在 04-agent-architecture/context-engineering.md 的「压缩与摘要」一节讲了它(截断 / 滑动窗口 / 滚动摘要 / 工具输出裁剪等),并且强调压缩是有损且不可逆、必须配评测。
  • 混淆的代价:一个说「我要给块补上下文」,另一个说「我要把上下文减掉」—— 答混了会让人以为你在同一层里自相矛盾。

4. 成本与收益的硬数字(面试可直接引用)#

维度 数字 口径 / 前提
前缀生成成本 约降到 1/10 借助 prompt caching(把整篇文档当跨请求共享前缀缓存,同一个文档的每个 chunk 调用只有「当前 chunk」这一小段是变量,所以第 1 个块之后的调用成本骤降)
每百万文档 token 成本 约 1 美元 量级 Anthropic 报告口径
结合 BM25 后 检索失败率下降约 49% 见下方「检索失败率」定义
再叠加 reranking 后 检索失败率下降约 67% 同一口径;两级是叠加关系,不是并列

「检索失败率」这个工业口径必须先定义清楚:它指「正确信息未出现在 top-20 检索结果中的查询比例」。⚠️ 它与学术意义上的 recall@k 不是同一个东西:

  • 学术 recall@k = 前 k 条里相关文档数 ÷ 该查询全部相关文档数(一个查询有多篇相关文档时,二者不相等);
  • 这里的「检索失败率」对应的其实是命中率(hit rate / success@k)——只要前 k 条里有一篇相关文档就算命中。

05-agent-evaluation/rag-eval.md 已经把 Recall@k 与 Hit Rate@k 的区别列清了(Hit Rate@k 是 Recall 的宽松版)。两边指向的是同一件事:跨来源比较数字前,先对齐口径。本文件的 49% / 67% 是 Anthropic 报告的 hit-rate@20 口径,别拿去和学术 recall@20 直接比。

还要记住边界:49% / 67% 是检索这一环的降幅,不等于端到端正确率也涨同样多 —— 检索改善只是抬高了端到端正确率的上限,生成侧仍可能丢分(pipeline.md 的决策树正是为区分这两侧)。

5. 延伸到用户记忆:同一手法 + 双层记忆架构#

同一手法反过来用在对话历史上 —— 这就是从「知识库 RAG」到「用户记忆检索」的迁移。书中 agentic-rag-for-user-memory 的对比实验:

  • 孤立对话块的问题:一句「好的,就订这个吧」毫无信息量,只有知道上文是「从上海到西雅图的 500 美元单程机票」才有意义。

  • 索引对话历史前,对每个对话块调用 LLM 生成含人物、时间、意图的前缀摘要。在「矛盾财务指令」场景里,三个原本各自独立、相互矛盾的转账指令块,被加上前缀后变成:

    • [妻子 Patricia Thompson 正在设立初始电汇]
    • [丈夫 James Thompson 正在修改之前的电汇]
    • [妻子在丈夫修改后再次修改电汇]

    这些前缀给 Agent 提供了判断指令优先级与最终有效性的关键线索 —— 原来它只看到三块矛盾文本,无从判断哪个生效。

由此得出双层记忆架构(书中两条线索的交汇点):

层 是什么 提供什么 对应技术
结构化卡片 / 事实(常驻上下文) 少量关键事实,如「用户 Jessica 的护照将于 2025 年 2 月 18 日过期」(书中用 Advanced JSON Cards) 概览 —— 随时可见、精确、稳定 常驻注入
上下文感知检索(按需) 从海量原始对话中取回相关细节 细节 —— 精确、可扩展 上下文感知检索

为什么必须两层:只靠常驻上下文会因容量受限而丢细节;只靠检索又会因缺乏全局视野而发现不了跨会话的隐藏关联(如「机票在一月、护照二月过期」这种需要同时握着两件事才能发现的关联)。这与 04-agent-architecture/memory.md 的结论同构 —— 该精确的用结构化(每轮注入),该模糊的用向量(按需检索),而且该文件还强调「程序性记忆不该做成检索」(怎么做事的规则要常驻、要稳定生效),双层架构里这一点同样成立。

6. 什么时候值得做(取舍意识,面试加分项)#

代价:① 索引期多一遍 LLM 调用(每个 chunk 一次,成本靠 prompt caching 压);② 索引体积变大(每个块都被前缀撑长,向量库与 BM25 索引都变大);③ 重建更贵(重索引时要重跑前缀生成)。

信号 值得做 不值得做
语料 专业性强(术语密集、实体多、跨文档引用多) 本身自包含、句句独立
chunk 块之间术语 / 实体重叠高(同一批词反复出现,语义易混) 块已带完整标题路径(前缀能加的信息它已经有了)
查询 经常需要「这一条属于哪个实体 / 哪一节」 查询与块是直接词面 / 语义匹配,不依赖背景
成本 索引预算可接受(一次性 + 增量) 成本极敏感、且收益未验证

工程判断的话术:先按 pipeline.md 的决策树确认瓶颈确实在检索未召回(而不是排序或生成),再确认根因是块缺背景(而不是 chunk 太大、embedding 域不匹配、查询该改写)—— 是它才上;并且用「有 / 无上下文感知检索」两个知识库做离线 Recall@k / hit-rate@20 对照,收益为正才保留。全书对这类优化的一贯口径是:先证伪便宜的环节,再上贵的。

常见面试问法#

Q:分块会切断上下文,你有哪些办法? A:分两类、两个阶段。切分时的手段(chunking.md):递归 / 结构感知切分、父子分块、overlap —— 它们优化切点位置,但补不回「这块属于哪一节、在说什么」这类背景。索引期的手段(本文件):上下文感知检索 —— 入库前用 LLM 给每个 chunk 生成前缀摘要,把实体、时间、章节这些背景生成出来拼进去。二者互补:先切得对,再补得全。

Q:上下文感知检索是什么?为什么它同时提升稀疏和稠密两路? A:在向量化入库之前,为每个 chunk 生成一段含核心上下文的前缀摘要,前缀 + 原文拼接后再索引。对 BM25,前缀补进了原本不在块里的关键词(实体名、时间、章节名),使查询里的专有名词能精确词面命中;对稠密向量,前缀注入了语义背景,降低了 chunk 之间的语义歧义。所以它和 hybrid-rerank.md 里的 BM25 是一对 —— 补的正是 BM25 最擅长的关键词。

Q:它和上下文压缩有什么区别?(陷阱题) A:方向和时机都相反。 上下文感知检索在索引期、对文本块做加法(补前缀、加背景),改变「能检索到什么」;上下文压缩在运行期、对注入的检索结果 / 会话历史做减法(裁剪、去冗余),改变「给模型看多少」。一个加在入库前,一个减在出库后。仓库在 04-agent-architecture/context-engineering.md 讲压缩(截断 / 滚动摘要等),且强调压缩有损且不可逆、必须配评测。

Q:它的成本和收益? A:成本是索引期每个 chunk 多一次 LLM 调用,靠 prompt caching(整篇文档作共享前缀)可把成本压到约 1/10,量级约每百万文档 token 1 美元;索引体积变大、重建更贵。收益(Anthropic 报告口径):结合 BM25 后检索失败率下降约 49%,再叠加重排序后下降约 67%。注意「检索失败率」是「正确信息未出现在 top-20 的比例」(hit-rate 口径,不是学术 recall@k),而且这只是检索环节的降幅,不等于端到端。

Q:什么时候你会做、什么时候不做? A:值得做:语料专业性强、chunk 之间术语重叠高、查询经常需要「这一条属于哪个实体 / 哪一节」。不做:语料自包含、chunk 已带完整标题路径、成本极敏感。做法上先按决策树确认瓶颈在检索未召回且根因是「块缺背景」,再用有 / 无两个知识库做离线对照验证收益。

Q:用户记忆检索用得着它吗? A:用得上,而且是双层记忆架构的一半。对对话历史同样在索引前加前缀(如「[妻子 Patricia Thompson 正在设立初始电汇]」),用来消解跨会话的冲突与指代。双层是:结构化的关键事实常驻上下文提供「概览」(精确、稳定),上下文感知检索按需取「细节」(可扩展);只靠一层都不行 —— 见 04-agent-architecture/memory.md。

易错点#

易错说法 问题 应该说
把「上下文感知检索」和「上下文压缩」当成一回事 时机与方向完全相反 索引期 / 加法 / 改「能检索到什么」vs 运行期 / 减法 / 改「给模型看多少」
说「overlap / 父子分块 也能补回上下文」 混淆了「切点优化」与「背景生成」 切分时手段只优化切点、或复制上下文;补「这块属于哪一节」需要生成前缀
说 overlap 就是上下文感知检索 一个复制、一个生成 overlap 是冗余复制;上下文感知检索是 LLM 生成的语义前缀
拿 49% / 67% 当「端到端正确率提升」 混淆环节 那是检索失败率的降幅;端到端还受生成侧约束
把「检索失败率」和 recall@k 混着比 口径不同 它是 hit-rate@20 口径(前 20 有 1 篇相关即命中),学术 recall@k 是「相关文档召回比例」
说它是「智能体化 RAG 之上的更高一层」 定位错 它是回到最基础的分块环节去修补,与结构化索引 / 智能体化 RAG 并列而非其上层
只讲方法不讲代价 缺取舍意识 代价是索引期多一遍 LLM 调用、索引变大、重建变贵
把它当成「必开」的优化 不给判断依据 先走决策树确认瓶颈与根因,再做有 / 无对照验证收益

自测#

1. chunking.md 说「分块会切断答案」,但给的四种手段都是切分时的。请说出它缺的那一类解法,并解释为什么切分时的手段补不上。

答案

缺的是索引期的解法 —— 上下文感知检索:入库前用 LLM 为每个 chunk 生成前缀摘要,把实体、时间、章节归属这些背景生成出来,再前缀 + 原文一起索引。切分时的手段(递归 / 结构感知 / 父子分块 / overlap)只能做两件事:把切点选得更自然,或者把上下文复制 / 放大(overlap、父块)。它们都无法提供一块它从未在原文里出现过的信息 —— 「这段出自 ACME 2025 Q2 财报的关键业绩指标章节」不是切点问题,而是需要一次生成才能得到。overlap 尤其容易被误解:它复制的是相邻块已有的文字,不是关于本块的描述。

2. 上下文感知检索为什么能同时提升 BM25 和向量两路?分别补了什么?

答案

前缀对两路补的是不同性质的东西。对稀疏(BM25):补进原本不在 chunk 里的关键词 —— 实体名(ACME)、时间(2025 年第二季度)、章节名(关键业绩指标)。BM25 靠词面精确匹配,这些词进去后,查询里的专有名词 / 时间才能命中,正好喂到 BM25 的强项上。对稠密(向量):补进语义背景,把块锚回真实主题,降低 chunk 之间的语义歧义 —— 一堆「某公司收入增长 X%」的块不再挤在同一片向量邻域里彼此干扰。所以它和 hybrid-rerank.md 的 BM25 是天然一对。

3. 面试官问「上下文感知检索和上下文压缩有什么不同」,这两个词哪里容易混?

答案

名字都带「上下文」,但时机、对象、方向三处全相反。上下文感知检索:索引期、对文本块、做加法(补前缀 / 加背景),目的是提升可检索性,改变「能检索到什么」。上下文压缩:运行期、对注入的检索结果 / 会话历史、做减法(裁剪 / 去冗余),目的是省窗口与降噪,改变「给模型看多少」。记忆点:一个加在入库前,一个减在出库后。 仓库在 04-agent-architecture/context-engineering.md 讲压缩,且强调压缩有损、不可逆、必须配评测 —— 恰好和「加法、提升召回的索引期手段」形成对照。

4. 「结合 BM25 后检索失败率下降约 49%,再叠加重排序后约 67%」——这句话里有哪些坑?

答案

三个坑。①口径:「检索失败率」=「正确信息未出现在 top-20 的比例」,这对应的是命中率(hit-rate / success@20)口径,不是学术 recall@k(后者是相关文档被召回的比例);跨来源比较前必须先对齐定义。②叠加关系:49% 是「上下文感知检索 + BM25」的降幅,67% 是「再 + reranking」的降幅,是逐级叠加,不是两个并列的独立数字。③只是检索环节:它提升的是端到端正确率的上限,不等于端到端也涨这么多 —— 生成侧仍可能丢分(见 pipeline.md 决策树)。成本侧别忘了:前缀生成靠 prompt caching 压到约 1/10,量级约每百万文档 token 1 美元。

5. 用户记忆里怎么用它?为什么需要「双层」而不是只用一层?

答案

对对话历史同样在索引前加前缀:一句孤立的「好的,就订这个吧」加前缀后变成「对『上海→西雅图 500 美元单程机票』的确认」;在矛盾指令场景里,三块冲突的转账指令分别带上「[妻子 Patricia Thompson 正在设立初始电汇]」「[丈夫 James Thompson 正在修改之前的电汇]」「[妻子在丈夫修改后再次修改电汇]」,Agent 才有线索判断哪个最终生效。双层的必要性:结构化卡片 / 事实常驻上下文提供随时可见的概览(精确、稳定,如护照过期日),上下文感知检索按需取回海量对话里的细节(可扩展)。只靠常驻会因容量丢细节;只靠检索会因缺全局视野而发现不了跨会话关联(如「机票在一月、护照二月过期」)。这与 04-agent-architecture/memory.md 的「该精确的用结构化(每轮注入)、该模糊的用向量(按需检索)」是同一结论。