知识库工程(一):从信息检索到 RAG
今天谈知识库,常把向量数据库、Embedding 和大模型放在一起。知识库的历史远早于大模型,并且有两条长期并行的技术路线。一条来自专家系统,把事实、规则和推理程序分开;另一条来自信息检索,研究如何从大量文本中找出与查询相关的内容。RAG 延续了第二条路线,同时吸收了第一条路线对“外部知识”的处理方式。
厘清这段历史很重要。向量检索没有废除关键词检索,长上下文也没有消除索引。当前知识库产品中的切片、倒排索引、向量召回、重排和引用,分别来自不同阶段积累的方法。
两条知识库路线在生成模型前已经形成
早期专家系统把领域知识保存为事实和规则,再由推理引擎执行匹配与演绎。修改知识时可以调整规则库,无需重写推理器。它适合边界清楚、规则稳定的任务,却难以直接容纳大量自然语言文档。规则数量增长后,冲突处理和维护成本也会迅速上升。
信息检索处理的是另一类问题:文档已经存在,系统需要计算查询与文档的相关程度。图书目录依靠人工主题词,数字化文档推动了自动索引。倒排索引、词项权重和相关性排序由此成为搜索系统的基础。企业文档问答沿用的正是这套链路,只在候选文档后增加了生成模型。
倒排索引解决候选集规模
顺序扫描每篇文档的代价随文档总量增长。倒排索引反过来记录“词出现在哪些文档中”。查询包含“迁移 校验”时,检索器先取得两个词对应的文档列表,再求交集、并集或带权组合。文档频率、词频和位置信息都可以保存在 posting list 中。
倒排索引擅长精确标识符、人名、错误码和专有术语。它也有明显限制:用户搜索“服务无法启动”,文档只写“进程初始化失败”,词面重合可能很少。分词、同义词和查询扩展可以缓解问题,仍无法完整表达句子语义。
TF-IDF 给词项分配区分度
词频 TF 描述词项 $t$ 在文档 $d$ 中出现的程度,逆文档频率 IDF 降低常见词的权重。一种常见写法是:
$$ \operatorname{tfidf}(t,d)=\operatorname{tf}(t,d)\cdot \log\frac{N}{\operatorname{df}(t)+1} $$
$N$ 是文档总数,$\operatorname{df}(t)$ 是包含词项 $t$ 的文档数。工程实现会采用对数词频、平滑项和向量归一化等变体,因此不同搜索库给出的分数不能脱离实现直接比较。
将文档与查询表示成词项权重向量后,可以计算余弦相似度:
$$ \cos(q,d)=\frac{q\cdot d}{\lVert q\rVert\lVert d\rVert} $$
TF-IDF 建立了可计算的相关性模型,但长文档容易因为词出现次数更多而占优。词频的收益也并非线性增长,一个词出现二十次通常不会比出现十次多一倍信息。
BM25 处理词频饱和与文档长度
BM25 在概率相关性框架下引入词频饱和和长度归一。忽略查询词频后的常见形式为:
$$ \operatorname{BM25}(q,d)=\sum_{t\in q}\operatorname{IDF}(t) \frac{f(t,d)(k_1+1)} {f(t,d)+k_1\left(1-b+b\frac{|d|}{\operatorname{avgdl}}\right)} $$
$f(t,d)$ 是词项在文档中的次数,$|d|$ 是文档长度,$\operatorname{avgdl}$ 是语料平均长度。$k_1$ 控制词频多快进入饱和区,$b$ 控制长度归一强度。当 $b=0$ 时不校正文档长度;$b$ 接近 1 时长度影响更强。
BM25 至今仍适合代码、规章编号、产品型号和报错文本。它的分数依赖当前语料统计,换一批文档后 IDF 会变化。知识库做增量更新时,索引统计与分片策略也会影响排序,不能把某个阈值当成跨语料常数。
稠密检索开始直接比较语义表示
神经检索器把查询和段落编码成低维稠密向量。双编码器分别计算 $E_q(q)$ 和 $E_d(d)$,在线查询只需编码一次问题,再到预先构建的向量索引中搜索:
$$ s(q,d)=E_q(q)^\mathsf{T}E_d(d) $$
若向量已经归一化,点积等价于余弦相似度。DPR 在 2020 年展示了双编码器用于开放域问答的效果,稠密检索由此成为 RAG 的重要组件。近似近邻索引可以在大规模向量中压缩查询时间,代价是召回率、内存和构建时间之间需要取舍。
稠密检索能找到词面不同但语义接近的段落,也可能忽略一个字符就能决定结果的版本号。Embedding 模型升级后,旧向量与新向量通常不能混用。生产系统因此常保留 BM25 与向量两路候选,再通过排名融合或重排器合并。
RAG 把外部文档接到生成概率上
2020 年的 RAG 论文将预训练生成模型称为参数化记忆,将稠密向量索引称为非参数化记忆。给定输入 $x$ 和候选文档 $z$,生成结果 $y$ 的概率对检索结果求和:
$$ p(y\mid x)=\sum_{z\in \operatorname{top}k}p_{\eta}(z\mid x),p_{\theta}(y\mid x,z) $$
$p_{\eta}$ 由检索器给出文档相关概率,$p_{\theta}$ 由生成模型在问题和证据条件下生成答案。论文区分了整段输出共享同一批文档的 RAG-Sequence,以及每个 token 可以依赖不同文档的 RAG-Token。今天常见的工程实现更直接:先取候选段落,经过过滤和重排后拼进上下文,再调用通用 LLM。
这套结构解决了三个现实问题。知识可以在不重新训练模型的情况下更新;回答可以携带来源;私有资料可以放在受控索引中按需读取。它没有自动保证事实正确。相关段落未被召回,错误版本排在前面,或者模型没有遵守证据,答案仍会出错。
长上下文没有结束检索
上下文窗口变大后,直接塞入整本文档看似省去索引,实际仍受成本、延迟和注意力利用率约束。Lost in the Middle 的实验显示,相关信息处在长上下文中部时,模型表现可能显著下降。文档越多,版本冲突、权限过滤和引用定位也越难处理。
检索的价值因此从“突破 token 上限”扩展到“选择证据”。好的知识库需要回答四个问题:哪些资料可以参与本次查询,哪一版有效,哪几段支持答案,用户能否核对来源。Embedding 参与语义选择,LLM 负责组织文本,两者无法单独处理资料权限、版本和来源核对。
从检索史回看 RAG,技术边界会清楚许多。倒排索引负责精确词项,向量负责语义候选,重排器比较问题与段落,生成模型组织答案。后续的 Agent 会多次调用这些能力,但底层仍依赖可维护、可过滤、可评测的知识集合。