RAG 基础
检索增强生成把 LLM 的生成能力与外部知识库的广度和时效性结合起来——检索器找出相关片段,生成器基于片段作答。这是构建知识库的主流路线;理解它的机制与局限,才明白 Zapvol 为何选了另一条(文件系统范式)。
Zapvol 的知识库没有采用向量 RAG,而是文件系统范式 + 智能体化 RAG(
grep_docs+ 知识子代理,见知识的组织与检索)。本页把 RAG 基础作为对照保留:理解这条主流路线的机制与局限,才明白我们为什么选了另一条。
构建共享知识库的核心技术是检索增强生成(Retrieval-Augmented Generation, RAG)。其核心思想是将大型语言模型的思考和生成能力,与外部知识库的广度和时效性相结合——模型本身的训练数据有截止日期,而知识库可以随时更新。
典型的 RAG 系统由两部分构成:检索器负责从知识库里找出相关片段,生成器(通常是 LLM)拿到这些片段作为上下文来生成答案。用户问“量子纠缠是什么、最新的实验进展有哪些”,基座模型的训练数据可能不包含最新进展,于是先从维基百科知识库检索出最相关的几个片段(量子纠缠定义、2022 年诺贝尔物理学奖、贝尔不等式实验),再把检索结果作为上下文让 LLM 生成答案;用户问“退款流程”,检索器从公司知识库找到退款政策(“订单签收后 7 天内可申请全额退款,需提供订单号,退款将在 3-5 个工作日内到账”)和操作步骤,同样注入上下文后生成答案。两个例子的模式完全一致:检索相关片段 → 注入上下文 → LLM 基于上下文生成答案。RAG 的核心价值在于让 LLM 能利用它训练时没见过的知识(维基百科的最新内容、公司的内部文档),而不需要重新训练模型。检索器的质量直接决定了 RAG 的效果——如果检索不到相关片段,LLM 再强也无米之炊。
文档分块(Chunking)
在能够检索之前,还有一步不可或缺的离线预处理——分块(Chunking):把长文档切成适合独立检索的片段(chunk)。分块之所以必要,原因有二。其一,嵌入模型对输入长度有限制,且一整篇文档只压缩成一个向量时,多个主题混在一起,向量无法精确表达任何一个——这与 Enhanced Notes 遇到的问题同源:段落越长,嵌入越难抓住重点。其二,检索的目标是只把相关的那部分注入上下文,片段太大会连带大量无关内容,浪费窗口、稀释注意力。常见的分块策略有三类:
固定大小切分:最简单的方法,按固定的 token 数(如 512)切分,通常在相邻块之间保留一定重叠(如 50-100 token),避免关键句子恰好在边界处被切断。实现简单、结果可预测,但完全无视文档结构——一个段落、一段代码、一张表格都可能被拦腰截断。
递归 / 结构感知切分:按文档的自然边界(章节标题、段落、句子)递归切分——先尝试按大边界切,块仍超长时再降级到更小的边界。Markdown、HTML 这类有显式结构的文档尤其适合。这是目前生产系统最常用的默认选择。
语义切分:计算相邻句子的嵌入相似度,在语义“断崖”处(相似度骤降的位置)下刀,使每个块内部主题尽量单一。切分质量更高,代价是需要额外的嵌入计算。
块大小与重叠量的选择是一对典型权衡:块太小,单块信息不完整,脱离上下文后语义模糊(“该公司收入增长了 3%”——哪家公司?哪个季度?);块太大,一个块混杂多个主题,嵌入向量被稀释,检索精度下降。实践中常见的起点是每块 256-1024 token、相邻块重叠 10%-20%,再根据检索质量实测调优。无论采用哪种策略,分块都会切断片段与其原始上下文的联系——“该公司”指代谁、这段话出自哪份报告,这些信息留在了块的外面。这是分块的固有缺陷,知识的组织与检索的“上下文感知检索”一节将正面解决它。
稠密嵌入:从词汇关联到语义理解
计算机只能处理数字,不能直接理解“苹果”和“橙子”的含义。嵌入(Embedding)的思路是:把每个词或句子转化成一串数字(称为“向量”),并且让语义相近的内容转化出来的数字串也“相近”。这些向量所在的数学空间称为“向量空间”,可以把它想象成一张高维地图,每个词或句子都是其中一个点,语义越接近的内容彼此就越靠近。经典例子是 “国王” − “男性” + “女性” ≈ “女王”,说明向量运算可以捕捉到语义关系。“稠密”是相对后面的“稀疏嵌入”而言:稠密向量的每个维度都有数值,稀疏向量大部分维度为零。
稠密嵌入用深度学习把文本映射到向量空间——语义相近的内容,向量距离也近。衡量两个向量有多“近”的常用方法是余弦相似度:它计算两个向量夹角的余弦值,值越接近 1 表示方向越一致、语义越相似。为什么用夹角而不是距离?因为我们关心的是两个向量的方向是否一致(语义是否相近),而不是它们的长度(文本的长度或频率)——两篇内容相同但长度不同的文档,向量长度不同但方向一致,余弦相似度能正确判断它们语义相同。
在稠密嵌入的早期,以 Word2Vec 为代表的技术通过分析海量文本中词汇的共现关系,为每个词生成一个固定向量,能捕捉 “king” − “man” + “woman” ≈ “queen” 这样的线性语义规律。然而静态词向量存在根本局限:无法处理一词多义——“bank” 在 “river bank”(河岸)和 “investment bank”(投资银行)中含义截然不同,Word2Vec 却赋予完全相同的向量。现代嵌入模型(如 BERT、BGE-M3)借助自注意力机制,在生成一个词的向量时会同时参考句子中所有其他词,因此同一个词“苹果”在“苹果公司发布新产品”和“买了两斤苹果”中会得到不同的向量表示,实现了从“词汇级”到“语境级”语义的飞跃。较早的 BERT 输入长度上限仅 512 个 token、并不适合长文本,BGE-M3 等新一代模型进一步支持多语言与长文本输入(并同时输出稠密、稀疏、多向量三种表示)。
稀疏嵌入:精确匹配的关键词检索
与捕捉语义相似性的稠密嵌入不同,稀疏嵌入(Sparse Embedding)根植于传统信息检索,核心是精确的关键词匹配。它将文档表示为极高维度的向量,绝大多数维度为零,只有与文档中出现的词汇对应的维度具有非零值。理论基石是经典的词袋模型(Bag of Words)——它把一段文本看作一个“装满词的袋子”,只关心哪些词出现了、出现了几次,完全忽略词序(“猫追狗”和“狗追猫”在词袋模型中完全相同)。
先用一个例子建立直觉:知识库有 100 篇技术文章,用户搜索“模型蒸馏”。“模型”在 60 篇文章中都出现(太常见、区分度低),而“蒸馏”只在 3 篇中出现(很稀有、区分度高)——好的算法应给“蒸馏”更高的权重。这就是 TF-IDF 与 BM25 的核心思想。TF-IDF 基于一个直觉:一个词在文档中的词频(TF)越高、包含它的文档数越少(即逆文档频率 IDF 越高),这个词就越重要——“模型”的 df/N = 60%、IDF 低,“蒸馏”的 df/N = 3%、IDF 高,所以“蒸馏”对排序的贡献远大于“模型”。但 TF-IDF 没有考虑文档长度,且词频增长是线性的(一个词出现 10 次的重要性真的是 5 次的 2 倍吗?)。BM25 引入两个参数来修正:k1 控制词频“饱和度”,让词频的贡献随增加而逐渐趋于平缓,避免长文档因词频堆砌而占优;b 控制文档长度归一化,更公平地处理不同长度的文档。BM25 至今仍是各大搜索引擎中不可或缺的核心组件。
稀疏检索依赖倒排索引(Inverted Index)——一个从词到文档的反向映射表:普通索引是“给定文档,列出它包含的词”,倒排索引则反过来,“给定一个词,立刻找到所有包含它的文档”,好比一本书后面的术语索引页。
本节以经典的 BM25 作为稀疏检索的代表,因为它无需训练、透明可复算。但稀疏检索本身已进入“学习型”阶段:以 SPLADE 为代表的一类模型,以及 BGE-M3 的稀疏输出分支,用神经网络为每个词项打权重——不再像 BM25 那样只按词频和文档频率算分,而是让模型判断“这个词在这段文本里到底有多重要”,甚至为原文没出现、但语义相关的词项补上非零权重(术语扩展)。这样得到的仍是一个大部分维度为零的稀疏向量,既保留了词法层面的可解释性和精确匹配能力,又借神经网络获得了一定的语义泛化,可以看作稀疏与稠密两条路线的一次中间地带的融合。
混合检索:两全其美的艺术
两种方法各有盲区:稠密检索懂语义但可能漏掉关键词(搜 “HTTP-403” 可能返回 “服务器错误” 的泛泛讨论),稀疏检索精确匹配但读不懂同义词(搜 “kitty” 找不到只写了 “cat” 的文档)。混合检索的思路很简单——两个引擎都跑、结果合并——难点在于如何把分布迥异的两组得分整合成一个有意义的排序。典型的混合检索流水线包含三个阶段。
第一阶段是并行检索,系统同时向稠密和稀疏两个引擎发送查询,各自召回一部分候选文档。第二阶段是结果融合,把两路结果合成一个统一的候选池——难点在于两路得分不可直接比较:稠密的相似度得分(归一化后通常落在 0 到 1)和稀疏的 BM25 得分(0 到几十的任意值)尺度和分布完全不同。常用的融合方法有两种:一是把各路得分分别归一化后加权求和;二是倒数排名融合(Reciprocal Rank Fusion, RRF)——完全抛开原始得分、只看排名,每个文档的综合得分是它在各路结果中排名的平滑倒数之和,即 得分 = Σ 1/(k + rank),其中 k 是平滑常数(常取 60)。RRF 简单鲁棒,但只利用了排名信息,丢失了原始得分中蕴含的相关性信号;加权归一化融合保留了得分,代价是两路尺度对齐本身不好调。第三阶段是神经重排序(Neural Reranking)——无论前一步用哪种方式融合,重排序都值得加,因为它换用了一种更强的匹配范式:对融合产生的候选池中排名靠前的 N 个(如前 50 个)逐一精细打分,产生最终排序。重排序并不替代融合:融合负责产生统一的候选池,重排序负责在候选池上精排。
重排序器采用的是“跨编码器(Cross-Encoder)”架构,与检索阶段的“双编码器(Bi-Encoder)”形成鲜明对比。打个比方:求职者把简历交给猎头快速筛选,是双编码器;面试官与每位候选人深谈,是跨编码器。双编码器为查询和文档独立生成向量、通过向量运算计算相似度——速度极快,但无法捕捉深层的匹配关系,适合从海量数据中做初步筛选;跨编码器则把查询和候选文档拼接成一段完整的文字送入模型,让模型逐词比对、输出一个综合的相关性得分——慢得多,但判断更准确(常用的重排序模型如 BAAI/bge-reranker-v2-m3 就采用这种架构)。
调优这样一条多阶段流水线需要客观的度量指标,最核心的三个(均在带标注答案的测试查询集上计算):recall@k 是包含正确答案的文档出现在前 k 个检索结果中的查询比例——回答“该找的找到了吗”,最贴近 RAG 需求(只要相关文档进入上下文,LLM 就有机会利用它);MRR(平均倒数排名)对每个查询取第一个相关文档排名的倒数再平均——回答“找得够不够靠前”(排第 1 得 1 分,排第 10 只得 0.1 分);nDCG(归一化折损累积增益)综合考虑所有相关文档的排名与相关程度,排名越靠后折扣越大——回答“整个排序列表的质量如何”。工业界报告中还常见“检索失败率”,如后文引用的 Anthropic 数据指正确信息未出现在 top-20 中的查询比例,本质上就是 1 − recall@20。
多模态信息提取:超越文本的界限
到目前为止,检索的对象都是纯文本,但现实中的知识载体远不止于此——图表、PDF 版式、语音这些非文本形式同样携带信息。多模态信息提取处于整条知识库流水线最前端的摄取与索引阶段,它决定了非文本内容以什么形态进入知识库,进而决定后续分块、嵌入和检索能利用到多少信息。架构上有三条路,核心取舍在保真度和成本之间。
原生多模态处理的核心在于,通过专门的编码器将不同类型的数据全部映射到统一的高维语义空间。以图像为例,多模态模型(如 Qwen-VL、LLaVA)通常集成基于 Vision Transformer(ViT)的视觉编码器——把图像分割为固定大小的图像块(Patches)当作“视觉单词”序列化为向量,与文本词向量共存于共享的多模态嵌入空间,自注意力机制能同等对待文本和图像 token。它直接“看到”页面布局、图表和文字,上下文保真度最高,尤其适合版式复杂、信息密度高的文档。
提取为文本(Extract to Text)是两阶段过程:先通过专门工具(OCR、音频转录)将非文本内容转为纯文本,再输入语言模型。这种方式模块化、成本低、兼容所有语言模型,提取出的文本还可缓存复用;代价是所有版式、图表、图像信息都在提取过程中被丢弃。
将多模态分析作为工具是一种混合方法:以文本提取为起点,为 agent 提供初步文本摘要,同时赋予它可对原始文件深入分析的工具(如 analyze_image、analyze_pdf)。这种“按需深入”的策略兼顾了低成本初步处理和高保真深度分析,但在需要一次性端到端深度理解的场景下不如原生模式。三种策略各有胜场,没有万能答案。