大模型的知识冻结在训练截止日,也看不到你的私有数据——公司文档、产品手册、个人笔记。微调注入知识又贵又容易幻觉。RAG(Retrieval-Augmented Generation)是当前最实用的方案:把相关文档片段检索出来塞进上下文,让模型"开卷考试"。这篇把整条链路拆到算法级:切块的边界问题、embedding 的对比学习训练、BM25 公式逐项、HNSW 的跳表式检索、RRF 融合数学、cross-encoder 重排、引用约束生成,以及 GraphRAG 与评估体系。

RAG 系统全链路
RAG 系统全链路

1. 知识注入的三条路线

路线 知识更新 可溯源 成本 适合
微调 重新训练 不可解释 高(GPU + 标注) 教"行为/风格"
RAG 重建索引(分钟级) 可引用原文 中(向量库+检索) 给"事实/知识"
长上下文 直接贴文档 引用整段 每 token 计费 一次性小文档

三者不是互斥:用 RAG 检索 + 微调调整回答风格、长上下文兜底处理超长单文档,是常见的组合拳。判断标准一句话:知识以"更新"为主的用 RAG,能力以"怎么做"为主的用微调。

2. 切块(Chunking):检索质量的地基

切块策略对比
切块策略对比

切块决定了"检索单元"的粒度,它直接设定了系统上限:

  • 块太大:一个块混杂多个主题,embedding 向量是全部内容的"平均",什么都能匹配一点、什么都不精确——检索排序失真;上下文窗口也被浪费;
  • 块太小:句子残缺,模型拿到片段拼不出完整语义。

工程起点参数:块 300~800 token,相邻块重叠 10%~20%(如 512 块 + 64 重叠)。重叠的意义:关键句恰好跨在边界上时,至少有一个块包含它的完整上下文。

按优先级选策略:

  1. 结构感知切分:Markdown 按 #/## 层级、代码按函数/类边界、HTML 按标签。语义边界处切块几乎无损——有结构就用结构;
  2. 递归字符切分:先按 \n\n(段落)切,超长的再按 \n、。 递归细分,最后才硬切。无结构文本的默认选择;
  3. 重叠滑动窗口:兜底方案。

切块之后立刻做两件事:带元数据(来源文件、标题路径、页码、权限标签)——引用展示和权限过滤全靠它;小而不碎——一个块最好能独立成义(有主语、有结论),残句块的检索价值大打折扣。

进阶思路 late chunking:先让长文档过一遍 embedding 模型拿到 token 级表示、再切块池化——块向量天然带着全篇上下文,代价是实现要求模型支持长输入。

3. Embedding:向量化与它的训练方式

Embedding 模型把文本映射到稠密向量(常见 512~1024 维),语义相近 → 余弦相似度高。它不是"天生的",而是对比学习训出来的——理解训练方式才能理解它的偏好与短板:

InfoNCE 损失: 同一问题与它的正确文档为正例对,
             随机/难负例文档为负例对,
             最大化正例相似度、压低负例相似度 (温度缩放的 softmax 交叉熵)

难负例挖掘是效果的关键:用"看起来相关但答案不对"的段落(比如同一产品的不同型号章节)当负例,模型的分辨力才上得去。这解释了一个实践现象:通用 embedding 在专业领域(医疗、法律)拉不开区分度——负例分布没覆盖到你的领域,在自己的数据上微调 embedding 往往是 RAG 提效最大的单点。

选型与使用要点:

  • 中文/中英混合:BGE、Qwen-Embedding、m3e;
  • 非对称检索要加前缀:问题和文档是两种文体,很多模型要求给 query 加指令前缀(BGE 的"为这个句子生成表示以用于检索相关文章:"),漏了前缀召回率掉十几个点;
  • MTEB 榜单只是初筛,拿自己的"问题→正确段落"对测 Recall@K 才是准的。

向量库与 HNSW 索引

百万级向量做暴力检索要 O(N·d) 次乘加,必须上近似索引。主流是 HNSW(分层可导航小世界图):

HNSW 的多层图结构
HNSW 的多层图结构

把跳表思想搬到近邻图:顶层图稀疏(长边,快速跨越)、底层图稠密(短边,精确定位)。查询从顶层入口贪心走向更近的点,无法更近就下降一层,到底层输出近邻。复杂度 O(log N),代价是建图慢、内存大(每点存 M 条邻边)。关键参数:M(每点边数,越大越准越占内存)、ef_construction(建图搜索宽度)、ef_search(查询搜索宽度——在线调精度与速度的旋钮)。Milvus/Qdrant/pgvector/FAISS 都内置了 HNSW。

4. 在线检索:BM25 与混合检索

4.1 BM25:47 年前的方法仍是向量检索的对照组

BM25 是基于词频的打分函数,对查询 Q 和文档 D:

score(Q, D) = Σ_t IDF(t) · tf(t,D)·(k₁+1) / ( tf(t,D) + k₁·(1 − b + b·|D|/avgdl) )

IDF(t)  = log((N − df(t) + 0.5) / (df(t) + 0.5))   词越稀有权重越高
tf 项   : 词频饱和 —— 出现 10 次不是 1 次的 10 倍 (k₁ 控制, 常取 1.2)
b 项    : 文档长度归一 (b=0.75, 抑制长文档靠篇幅堆分)

它至今难被完全取代的原因:精确词匹配。型号 RK3566、错误码 0x80070057、函数名 malloc_init——这些低频精确串,向量的语义空间反而容易"模糊掉",BM25 的 IDF 机制给它们极高权重。开源 RAG 评测(如 BEIR)里 BM25 至今是半数数据集上的强者。

4.2 混合检索与 RRF 融合

生产系统的标配是两路召回 + 融合。分数融合有个麻烦:余弦相似度 ∈ [−1,1]、BM25 ∈ [0, ∞),量纲不可比,加权融合要反复调权。RRF(Reciprocal Rank Fusion)绕开了分数,只用排名:

score(d) = Σ_r 1 / (k + rank_r(d)),   k ≈ 60
RRF 融合示例
RRF 融合示例

实现几行、无需调参、对异常排名鲁棒——两路各取 Top-50,RRF 融合去重后交给重排。向量语义泛化 + BM25 精确打击,互补性极强。

4.3 查询改写

用户的问题往往不是好的检索 query:

  • 多查询改写:让 LLM 生成 2~3 个不同角度的改写,分别检索再融合——覆盖面换成本;
  • HyDE:先让 LLM 生成一段"假设性答案",用它去做向量检索。原理:问题(短、疑问句)与文档(长、陈述句)文体不对称,而"假设答案"与正确文档文体一致,向量空间里更近;
  • 对话改写:多轮对话里把"那怎么配置?"补全成独立完整的查询("K8s 的 Service 怎么配置?"),否则检索拿到的指代残句什么都查不到。

5. 重排:漏斗的最后一层

双塔与交叉编码器对比
双塔与交叉编码器对比
检索漏斗
检索漏斗

召回(双塔)为了快牺牲精度:query 和文档独立编码,只能在最后算一次余弦——两者的交互被压缩到点积一个数。重排(cross-encoder)把 (query, doc) 拼成一条序列过 Transformer,每个 token 看得见彼此,交互发生在每一层注意力里——精度高一个档次,但每个候选对都要跑一次完整前向,只能用于几十条候选。

于是两级漏斗定型:召回 50~100(毫秒级)→ 重排留 3~8(百毫秒级)。加上 cross-encoder 重排,端到端命中率的提升普遍 5~15 个百分点,是 RAG 里投入产出比最高的组件。开源用 BGE-reranker(可中文微调),商用 Cohere Rerank;预算紧张时可以蒸馏一个小重排模型。

6. 生成:把检索结果变成可信回答

Prompt 模板的核心结构:

请根据以下参考资料回答问题。若资料不足以回答, 请明确说明"根据现有资料无法回答", 不要编造。

[1] {chunk_1}
[2] {chunk_2}
[3] {chunk_3}

问题: {query}
要求: 回答中标注引用来源 (如 [1][2]); 与资料冲突的内容不要输出。

四个关键约束:

  1. "不足以回答就说无法回答"——显式给模型"认输"的出口,是压幻觉的第一道闸;
  2. 引用编号既方便用户核对,也倒逼模型贴着资料说话——生成时注意力有明确锚点;
  3. temperature 压到 0.1~0.3——RAG 要忠实不要发散;
  4. 拒答率是健康指标:RAG 系统永远答问题的能力反而危险,"查无此料"的诚实回复占比应保持合理水平。

7. 进阶架构

  • 父块检索:用小块(精确)检索,返回大块(完整上下文)给 LLM——检索精度与生成上下文兼得;
  • RAPTOR:对文档做层次化摘要树,不同粒度的节点都进索引——细节问题查叶子、综述问题查上层;
  • GraphRAG:用 LLM 抽取实体与关系建知识图谱 + 社区层级摘要,检索沿图扩展——擅长"跨文档综合"的全局性问题("这批投诉的共同根因是什么"),构建成本高,按需引入;
  • Agentic RAG:把检索变成 Agent 的一个工具(见下一篇),模型自己决定查什么、查几轮、何时停止——多跳问题("A 的竞争对手的 CEO 是谁")的正确率显著高于一次性检索。

8. 评估:没有度量的优化都是玄学

  • 检索层:标注 100~500 条"问题 → 正确段落",算 Recall@K(前 K 里有没有正确块——地基指标,K 取进上下文的数量)、MRR(正确块排名的倒数均值)、nDCG(带位置折损的排序质量);
  • 生成层:Faithfulness(回答的每个论断能否被检索内容支持——RAGAS 用 LLM 分解论断再逐条核对)、Answer Relevancy(回答与问题的相关度);
  • 端到端:人工标注小样本 + LLM-as-judge 批量评,双轨校准。

排障顺序永远是:先查检索(Recall 不行就别动生成端)→ 再查重排 → 再调 prompt。实践中六成的"RAG 幻觉"其实是检索没召回正确块,模型在拿错误资料硬答。

9. 小结

  • 切块按语义边界、带元数据、重叠兜底,块的质量决定系统上限;
  • embedding 靠对比学习训练,难负例决定领域分辨力,非对称检索记得加前缀;HNSW 用分层图实现 O(log N) 检索;
  • BM25 的精确词匹配与向量语义互补,RRF 只用排名就完成无量纲融合;
  • cross-encoder 重排是性价比之王,漏斗"召回 100 → 重排 5"是标准形;
  • 引用约束 + 低温 + 认输出口压幻觉;先建评估集再调参,检索层永远优先于生成层排查。

最后一篇:把模型从"开卷答题"升级为"动手干活"——Agent 的循环、记忆、编排与安全。