本页目录

RAG 与检索增强

别把整库塞进窗口赌模型自己找。RAG 把"无限知识"拆成"先检索、再把命中的几页放进上下文"——决定答案质量的不是上下文有多长,而是检索有多准。

概述

上下文工程 反复强调一条:⁠检索优于硬塞⁠。这篇把那句引子展开成可落地的工程——RAG(retrieval-augmented generation,检索增强生成)

模型的参数里压着训练时见过的知识,但它不知道私有文档、今天的数据、本项目的约定;而且窗口有限、注意力对中段"看不清"(lost in the middle)。两条死路:把整个知识库塞进 system(撑爆窗口、稀释信号、贵),或指望微调把知识焊进权重(慢、贵、更新难)。RAG 是第三条路:⁠把知识留在窗口外的检索库里,每次只把与当前问题相关的几个片段取出来,拼进上下文⁠。

一句话抓住本质:RAG 把"模型知道多少"问题,转化成"检索系统能不能把对的片段排到前面"问题。 于是答案质量的瓶颈从模型搬到了检索——这也是为什么这一篇的大部分篇幅在讲怎么检索得准,而不是怎么生成。

核心回路:retrieve → augment → generate

flowchart LR
    Q["用户问题"] --> EMB["query 向量化"]
    EMB --> SEARCH["向量库检索<br/>top-k 相似片段"]
    DOCS["知识库<br/>(切块 + 预先 embed)"] -.离线建索引.-> SEARCH
    SEARCH --> RERANK["rerank<br/>(可选, 精排)"]
    RERANK --> AUG["拼进上下文<br/>命中片段 + 问题"]
    AUG --> LLM["模型生成<br/>(带引用)"]
    LLM --> A["答案"]

分两段:⁠离线建索引⁠(把文档切块、向量化、灌进向量库,一次性)和在线检索生成⁠(每次请求把问题向量化、检索、拼接、生成)。后者直接接上 Agent 循环——检索可以是一个工具,模型自己决定何时查、查什么。

embedding 与向量检索:为什么能"按意思"找

关键词检索按字面匹配,问"怎么让显卡省显存"找不到写着"KV cache 量化"的段落。embedding 把一段文本映射成一个高维向量,语义相近的文本向量也相近,于是检索变成"在向量空间里找最近邻"——按意思找,不按字面。

  • 相似度⁠:常用余弦相似度(cos similarity)。把库里每个片段预先 embed 存好,查询时只 embed 一次 query,算它与库向量的相似度,取 top-k。
  • 本机可跑⁠:embedding 不必上云。llama.cpp 能直接跑 embedding 模型(如 Qwen3-Embedding、bge-m3),配一个本地向量库(sqlite-vec、Qdrant、Chroma)就是一套离线 RAG——参见 本地 LLM 部署。embedding 模型小、batch 跑得快,显存压力远小于生成模型。
  • 维度与规模⁠:几千到几万个片段用暴力近邻就够;上百万再上 ANN 索引(HNSW、IVF)换近似检索的速度。

embedding 模型 ≠ 生成模型,二者各自分词、互不通用⁠。给检索库 embed 用的模型,和查询时 embed 用的,⁠必须是同一个⁠——换了模型整库要重新 embed,否则向量不在同一空间,检索全乱。

chunk 策略:切不好,检索再准也没用

文档得先切成块(chunk)⁠才能 embed。切块是 RAG 里最被低估、却最影响效果的一步:

维度取舍
大小太大→一块混多个主题,向量被稀释,命中也夹带噪声;太小→语义不完整,"它"指代谁丢了。常见 200–500 token。
重叠相邻块留 10–20% 重叠,避免把一句话/一个论点从中间切断。
切分点按语义边界切(标题、段落、Markdown 章节)远胜按固定字数硬切。代码按函数/类切,别按行。
附元数据每块带上来源、标题、章节路径——既能过滤(只检索某文档),又能在答案里给引用。

经验:⁠先按文档的天然结构切(本站点就是按 ## 章节),再在过大的块内按字数兜底。 切块质量比向量库选型重要得多。

提升命中:混合检索与 rerank

纯向量检索有盲区:精确的专有名词、错误码、ID,字面匹配反而更可靠。两个加准的手段:

  • 混合检索(hybrid):向量检索(管语义)+ 关键词检索 BM25(管字面),两路结果融合(如 RRF, reciprocal rank fusion)。问"model_context_window_exceeded 是什么"时,BM25 能精确命中那个串,向量未必。
  • rerank(精排):检索先粗召回 top-50,再用一个 cross-encoder reranker 对 query-片段逐对精打分,取真正最相关的 top-5 拼进窗口。召回管"别漏",精排管"别滥"——两段式比单纯调大 k 更省窗口、更准。

别用拉大 k 来兜底。 k 越大,拼进窗口的噪声越多,既触发 context rot 又费 token。宁可召回宽、精排狠,最后只放少而精的几页。

拼进窗口:接回上下文工程

检索出片段后怎么放进上下文,直接复用 上下文工程 的全部结论:

  • 位置⁠:把命中片段放在注意力强的位置(靠前的 system 之后、或紧贴问题之前),用清晰分隔符 / XML 标签框住,别埋在中段。
  • 缓存⁠:稳定的指令、不变的少量参考放前缀(命中 prompt caching);⁠每次变化的检索片段放靠后⁠,别插在前缀里破坏缓存。
  • 要引用⁠:让模型在答案里标注每条结论来自哪个片段(靠 chunk 元数据)。Claude 的 citations 能力可让被引用片段携带定位信息,但注意它与结构化输出 output_config.format 互斥,二选一。

评估检索:把"准不准"量化

RAG 的失败往往不在生成,而在检索——⁠模型答错,常常是因为对的片段根本没被检索到⁠。所以要分开评估两段(接 评估与可观测):

  • 检索质量(可编程,最可靠):对一组带"标准答案出处"的问题,量 recall@k(对的片段有没有进 top-k)、precision@k(top-k 里有多少真相关)、MRR。这步纯靠代码断言,零成本、无偏差。
  • 生成质量(开放式):答案是否忠于检索内容、有没有编造(hallucination)、引用对不对——用 LLM-as-judge,裁判与被测分离。

定位问题先看检索:recall 低就回去调 chunk / 换 embedding / 加 rerank;recall 高但答错,才是生成或 prompt 的问题。

RAG vs 记忆 vs 长上下文:别混

三个常被搞混的"让模型多知道点"的手段,边界要清(详见 记忆与状态):

解决什么存在哪生命周期
长上下文这一轮要看的全部资料窗口内一轮即逝
RAG海量知识里按需取相关片段窗口外的检索库库持久,每轮取一小片进窗口
记忆跨会话记住偏好/结论/踩过的坑会话外的文件/库跨会话持久

RAG 取的是"客观知识片段",记忆存的是"主观经验结论";RAG 每轮重新检索,记忆是模型主动写读。长 agent 三者常并用。

最佳实践

  • 先量检索,再调生成。 建一个带"出处标准答案"的小评估集,先把 recall@k 顶上去——检索不准,换什么模型、调什么 prompt 都是隔靴搔痒。
  • 按语义结构切块,带足元数据。 标题/段落/函数边界优先于固定字数;每块附来源+章节,既能过滤又能给引用。
  • 召回宽、精排狠、上下文少。 粗召回 top-50 → rerank → 只放 top-3~5,别靠拉大 k 硬塞。
  • query 与库用同一个 embedding 模型。 换模型必须整库重 embed;升级 embedding 走灰度,别原地替换。
  • 专名/错误码/ID 走混合检索。 纯向量对字面精确项有盲区,叠一路 BM25。
  • 检索片段一律放窗口靠后。 保护前缀缓存;稳定指令在前,易变命中在后。
  • 让答案带引用、可溯源。 用 chunk 元数据标注出处,既降幻觉又便于人工核查。

权衡与失败模式

  • 把整库塞进 system:撑爆窗口、稀释信号、贵 → 检索后只放命中片段。
  • chunk 太大/太碎⁠:大块向量被稀释、碎块语义断裂 → 按语义边界切 + 适度重叠,实测 recall 调。
  • query 与库 embedding 不一致⁠:向量不同空间,检索全乱 → 锁定同一 embedding 模型,换模型重建索引。
  • 纯向量漏掉精确项⁠:错误码/专名字面匹配失败 → 混合检索叠 BM25。
  • 拉大 k 兜底⁠:噪声进窗口、触发 context rot、费 token → 召回宽 + rerank 精排 + 少放。
  • 只看生成不看检索⁠:答错以为是模型的锅,其实片段没召回 → 先量 recall@k 再归因。
  • 检索片段插进缓存前缀⁠:每轮命中不同,前缀缓存全废 → 命中放靠后,前缀保持稳定。

参考

  • 研究⁠: "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks"(Lewis et al., 2020)、"Lost in the Middle"(Liu et al., 2023)
  • Anthropic 官方文档⁠: Citations、Structured Outputs(platform.claude.com,实现前以官方为准)
  • 本地实战⁠: 本地 LLM 部署(本机跑 embedding 与向量库)
  • 上游/下游⁠: 上下文工程记忆与状态评估与可观测

Keywords: RAG, retrieval-augmented generation, embedding, 向量检索, cosine similarity, 近邻, ANN, HNSW, chunk, chunking, overlap, 切块, BM25, hybrid search, 混合检索, RRF, rerank, cross-encoder, recall@k, precision@k, MRR, citations, hallucination, 检索优于硬塞, vector database, sqlite-vec, Qdrant