第 8 章 检索增强生成
- 理解 RAG 的工作原理,区分 RAG 与微调的适用场景
- 理解 Tokenizer 的分词原理,掌握 BPE 算法的核心思想
- 理解 词嵌入与文本嵌入的理论基础,建立向量语义空间的直觉认识
- 理解 向量数据库的索引机制,了解 HNSW 算法的工作原理
- 应用 文档分块的基本原则,判断不同金融文档的分块策略
- 应用 Claude Code + MCP 工具构建简易 RAG 系统
- 分析 RAG 系统各环节的质量瓶颈与优化方向
8.1 RAG 系统概述

检索增强生成(Retrieval-Augmented Generation,RAG)是当前大语言模型应用中最重要的架构范式之一。本节从理论层面剖析 RAG 的核心机制、与传统方法的区别,以及其在金融领域的独特价值。
8.1.1 RAG 的定义与动机
定义
RAG 是一种将信息检索与文本生成相结合的技术架构。其核心思想是:在大语言模型生成回答之前,先从外部知识库中检索相关信息,将检索结果作为上下文输入模型,从而生成更准确、更有依据的回答。
大语言模型的知识局限性
大语言模型的知识来源于训练数据。这一特性带来两个根本性限制:
第一,知识截止问题(Knowledge Cutoff)。模型的知识停留在训练数据的截止日期。对于金融领域而言,2024 年训练的模型无法了解 2025 年的货币政策调整、最新的监管规定或市场动态。当用户询问「最新的 LPR 报价是多少」时,模型只能给出过时的数据。
第二,幻觉问题(Hallucination)。当模型缺乏某一领域的知识时,它往往不会承认不知道,而是生成看似合理但实际错误的内容。在金融场景中,这种幻觉尤其危险——一个错误的政策解读或数据引用可能导致投资决策失误或合规风险。
幻觉(Hallucination)指大语言模型生成与事实不符的内容,但表述方式却自信流畅。幻觉产生的根源在于模型本质上是一个概率预测系统——它预测下一个最可能出现的词,而非检索已知事实。
RAG 如何解决这些问题
RAG 通过引入外部知识库,从根本上改变了模型的回答机制:
| 传统模式 | RAG 模式 |
|---|---|
| 完全依赖模型内部参数存储的知识 | 实时检索外部知识库 |
| 知识固定在训练时刻 | 知识库可随时更新 |
| 无法追溯信息来源 | 可标注每个回答的文档依据 |
| 对领域知识覆盖不均 | 可针对特定领域构建专属知识库 |
对于金融应用而言,RAG 的价值尤为突出。金融信息具有高度时效性——央行政策、市场行情、监管规定每天都在变化。同时,金融行业对信息准确性有严格要求,任何数据引用都需要可追溯、可验证。RAG 恰好满足这两项核心需求。
8.1.2 RAG 的三阶段架构
RAG 系统的工作流程可分为三个阶段:检索(Retrieval)、增强(Augmentation)、生成(Generation)。
第一阶段:检索
检索阶段的任务是从外部知识库中找出与用户查询最相关的文档片段。
这一阶段涉及两个核心技术:
查询编码:将用户的自然语言问题转换为向量表示(Vector Representation),这一过程由嵌入模型(Embedding Model)完成。
相似度匹配:在向量数据库中搜索与查询向量最相似的文档向量,返回 Top-K 个最相关的文档片段。
检索质量直接决定了整个系统的上限。如果这一阶段无法找到正确的信息源,后续的生成阶段再优秀也无济于事。
第二阶段:增强
增强阶段的任务是将检索到的文档片段与用户的原始问题整合为一个结构化的提示词(Prompt)。
这一阶段的关键在于上下文构建策略:
- 如何排列多个检索结果的顺序
- 是否需要对检索内容进行摘要或压缩
- 如何在提示词中明确指示模型使用检索内容
一个典型的增强后提示词结构如下:
系统指令:请基于以下参考资料回答用户问题。如果资料中没有相关信息,请明确告知。
参考资料:
[文档片段 1]
[文档片段 2]
[文档片段 3]
用户问题:{用户的原始问题}第三阶段:生成
生成阶段由大语言模型完成。模型接收增强后的提示词,基于其中的参考资料生成回答。
与普通的文本生成不同,RAG 模式下的生成具有以下特征:
- 回答应当基于检索到的文档内容,而非模型的内部知识
- 回答中应标注信息来源,便于用户验证
- 当检索内容不足以回答问题时,模型应承认信息不足
RAG 的三阶段可以用经济学中的生产流程类比:检索阶段是「原材料采购」,增强阶段是「原材料加工」,生成阶段是「成品制造」。原材料的质量(检索准确性)和加工工艺(增强策略)共同决定了最终产品(回答)的品质。
8.1.3 RAG 与微调的理论对比
给大语言模型补充专业知识,存在两条技术路径:RAG 和微调(Fine-tuning)。理解两者的理论差异,有助于在实际项目中做出正确选择。
微调的原理
微调是指在预训练模型的基础上,使用特定领域的数据继续训练,从而调整模型的参数。经过微调后,领域知识被「内化」到模型的权重中,模型在该领域的表现会显著提升。
这一过程可以类比为「深度学习」——模型通过反复训练,将知识转化为自身的「直觉」。
RAG 的原理
RAG 不修改模型参数,而是在推理时为模型提供外部知识。模型的核心能力保持不变,但通过检索增强,它可以回答训练数据中未曾覆盖的问题。
这一过程可以类比为「开卷考试」——模型的能力不变,但允许查阅参考资料。
理论权衡分析
| 维度 | 微调 | RAG |
|---|---|---|
| 知识存储位置 | 模型参数(内化) | 外部数据库(外挂) |
| 知识更新成本 | 高(需重新训练) | 低(更新文档即可) |
| 计算资源需求 | 高(需 GPU 训练) | 中(需向量数据库) |
| 可追溯性 | 弱(无法标注来源) | 强(可标注文档来源) |
| 对模型的改变 | 永久改变参数 | 不改变参数 |
| 适合的知识类型 | 稳定、结构化的领域知识 | 动态、需频繁更新的信息 |
对于金融场景,推荐采用「RAG 为主、微调为辅」的混合策略:
- RAG 负责:实时政策信息、市场数据、监管规定等需要频繁更新且要求可追溯的内容
- 微调负责:金融领域的专业表达风格、特定任务(如财报摘要格式)的输出规范
对于本书的学习者而言,掌握 RAG 技术足以应对绑大多数应用场景。
8.1.4 RAG 的理论基础
RAG 技术建立在多个学科领域的理论基础之上。理解这些基础有助于把握 RAG 的本质,并在实践中做出更合理的设计决策。
信息检索传统
信息检索(Information Retrieval,IR)是计算机科学中一个历史悠久的研究领域,其核心问题是:如何从大规模文档集合中找到与用户需求相关的内容。
传统信息检索主要依赖词频统计方法。经典的 TF-IDF(词频-逆文档频率)和 BM25 算法通过统计词语在文档中出现的频率来衡量相关性。这类方法的优点是计算高效、可解释性强,缺点是无法理解语义——「降低存款准备金率」和「降准」在词汇层面毫无关联,但它们表达的是同一概念。
RAG 中的检索模块继承了信息检索领域的核心思想,但用向量语义检索替代了传统的关键词匹配,从而克服了语义鸿沟问题。
语义匹配与表示学习
语义匹配的关键在于将文本转换为能够捕捉其含义的数学表示。这一任务由表示学习(Representation Learning)技术解决。
表示学习的核心思想是:将高维、离散的符号(如文字)映射到低维、连续的向量空间。在这个空间中,语义相近的文本对应的向量位置也相近。
向量嵌入模型(Embedding Model)是实现这一映射的工具。它接收一段文本作为输入,输出一个固定长度的浮点数向量(如 1024 维)。这个向量可以理解为文本在「语义空间」中的坐标。
向量嵌入的理论根源可追溯到分布式语义假说(Distributional Semantics Hypothesis):语义相似的词语倾向于出现在相似的上下文中。现代嵌入模型通过在大规模语料上训练神经网络,学习到了这种上下文分布规律,从而能够将语义信息编码为向量。
知识增强生成范式
RAG 代表了一种新的生成范式:知识增强生成(Knowledge-Augmented Generation)。与纯粹依赖模型内部参数的生成方式不同,这一范式将外部知识显式地引入生成过程。
这一范式具有以下理论优势:
知识与推理的分离:模型负责推理和生成,知识库负责存储事实。这种分离使得两者可以独立优化和更新。
可扩展性:知识库的规模可以任意扩展,而不需要增加模型的参数量。这解决了「将所有知识塞入模型参数」的根本性局限。
可验证性:生成的内容可以追溯到具体的知识条目,便于人工审核和错误排查。
8.1.5 RAG 在金融领域的应用价值
金融行业的三个显著特征,使其成为 RAG 技术的天然应用场景。
信息密集性
金融从业者每天面对海量文档:财务报表、研究报告、政策文件、市场公告、新闻资讯。传统的信息获取方式依赖人工搜索和阅读,效率低下且容易遗漏关键信息。
RAG 系统可以将这些文档纳入知识库,支持自然语言查询。分析师可以用日常语言提问,系统自动从数万份文档中检索相关内容。这大幅提升了信息获取效率。
高时效性要求
金融市场的信息窗口极短。央行今天发布的政策,明天就需要反映在投资决策中。监管规定的变化可能即时影响业务合规性。
RAG 的增量更新特性完美匹配这一需求。新文档可以在发布后立即索引入库,无需重新训练模型。上午发布的政策,下午就可以被检索和引用。
合规与审计要求
金融行业受到严格的监管。投资建议、风险评估、合规判断都需要有据可查。如果 AI 系统给出一个结论,审计人员需要能够验证这个结论的依据是什么。
RAG 的来源标注机制直接满足了这一要求。每个回答都可以附带其引用的文档来源、具体段落,甚至原文引述。这种可追溯性是纯粹依赖模型内部知识的系统无法提供的。
典型应用场景
| 应用场景 | RAG 的价值 |
|---|---|
| 政策文档问答 | 自然语言查询央行报告、监管规定,秒级返回相关条款 |
| 投研报告辅助 | 自动检索财报数据、行业信息,为分析师提供素材 |
| 智能客服 | 从产品说明书、FAQ 中检索信息,准确回答客户咨询 |
| 合规审查 | 检索历史案例和法规条款,辅助判断合规性 |
| 舆情分析 | 从新闻库中检索相关报道,支撑舆情判断 |
8.2 文本的数字化表示:Tokenizer 原理

计算机只能处理数字。无论是加法运算还是图像识别,底层都是 0 和 1 的计算。大语言模型也不例外——它无法直接理解「央行宣布降准」这几个汉字,必须先把文字转换成数字序列,才能进行后续处理。
这个转换过程的核心工具叫 Tokenizer(分词器)。它决定了模型如何「看待」文本,是理解大语言模型工作原理的基础概念。
8.2.1 为什么需要 Tokenizer
文本到数字的桥梁
Tokenizer 的核心任务是建立文本和数字之间的映射关系。这个过程分三步:
- 分词:把连续的文本切分成离散的片段(Token)
- 编码:把每个 Token 映射到一个唯一的数字 ID
- 嵌入:把数字 ID 转换成模型可以运算的向量
用一个具体例子说明:
原始文本: "央行降准0.5个百分点"
第一步 分词:
["央行", "降", "准", "0", ".", "5", "个", "百分点"]
第二步 编码:
[15234, 892, 1456, 15, 13, 20, 156, 28934]
第三步 嵌入:
[[0.12, -0.34, ...], [0.56, 0.23, ...], ...] # 每个 ID 对应一个向量Token(词元)是模型处理文本的基本单位。它不一定是一个完整的词,可能是一个字、一个词的一部分,甚至是一个标点符号。Token 的划分方式直接影响模型的理解能力和处理效率。
为什么不直接按字符处理
最简单的方案是把每个字符作为一个 Token。对于英文,就是 26 个字母加标点;对于中文,就是成千上万个汉字。这种方案看似直观,但存在两个问题:
第一,序列太长。一篇 1000 字的财报摘要,按字符切分就是 1000 个 Token。模型处理序列的计算量与长度的平方成正比,序列越长,计算越慢。
第二,语义被切碎。「百分点」是一个完整的金融术语,切成「百」「分」「点」三个字符后,模型需要从上下文重新推断它们合在一起的含义,增加了理解难度。
为什么不直接按词处理
另一个方案是按完整的词切分。英文天然有空格分隔,中文可以用分词工具。这种方案保留了语义完整性,但也有问题:
第一,词表太大。金融领域有大量专业术语:「逆回购」「MLF」「LPR」「社融」「同业拆借」……加上各类公司名、产品名,词表规模会膨胀到几十万甚至上百万。词表越大,模型参数越多,训练和推理成本越高。
第二,无法处理新词。如果训练时没见过某个词,模型就完全不认识。这在金融领域尤其麻烦——每年都有新的政策术语、金融产品名称出现。
8.2.2 分词的三种粒度
根据切分的精细程度,分词策略分为三类:
| 粒度 | 示例 | 词表大小 | 序列长度 | OOV 问题 |
|---|---|---|---|---|
| 字符级 | 「央」「行」「降」「准」 | 极小(几千) | 极长 | 无 |
| 词级 | 「央行」「降准」 | 极大(几十万) | 较短 | 严重 |
| 子词级 | 「央行」「降」「准」 | 适中(3-10 万) | 适中 | 轻微 |
OOV(Out-of-Vocabulary,词表外词)指模型词表中没有收录的词。遇到 OOV 时,词级分词只能用一个特殊的「未知词」符号代替,丢失全部语义信息。这在金融领域问题尤其突出——新股代码、新产品名称、新政策缩写都可能触发 OOV。
字符级分词(Character-level)
把每个字符作为独立的 Token。
优点:词表小,永远不会遇到 OOV。 缺点:序列太长,语义碎片化。
对于中文,一个汉字就是一个 Token;对于英文,每个字母是一个 Token。「interest rate」这个词在字符级下会被切成 13 个 Token(含空格),模型需要学会把这些字母组合起来理解「利率」的含义。
词级分词(Word-level)
按完整词切分。英文依靠空格,中文需要分词工具(如 jieba)。
优点:语义完整,序列短。 缺点:词表巨大,OOV 严重。
金融领域的专业术语特别多,词级分词的词表会非常庞大。更麻烦的是,一个新出现的术语(如某只新股的名称)会直接变成未知词。
子词级分词(Subword-level)
现代大语言模型普遍采用的方案。它的核心思想是:高频词保留完整,低频词拆成更小的片段。
例如,「央行」是高频词,保持完整;「逆周期」可能拆成「逆」「周期」;一个罕见的公司名可能被拆成若干个字符组合。
这种方案在词表大小、序列长度和 OOV 处理之间取得了平衡。即使遇到训练时没见过的词,子词分词也能把它拆成见过的片段,保留部分语义信息。
8.2.3 BPE 算法原理
BPE(Byte Pair Encoding,字节对编码)是最流行的子词分词算法,GPT 系列模型使用的就是 BPE 的变体。它的核心思想很直观:从字符开始,反复合并最常一起出现的字符对,直到达到预设的词表大小。
算法步骤
以一个简化的例子说明。假设训练语料只有三句话:
"央行降准"(出现 5 次)
"央行加息"(出现 3 次)
"降准预期"(出现 2 次)第一步:初始化
把所有文本拆成字符,统计每个字符的出现频率:
初始词表: {"央", "行", "降", "准", "加", "息", "预", "期"}
字符频率:
央: 8 次, 行: 8 次, 降: 7 次, 准: 7 次
加: 3 次, 息: 3 次, 预: 2 次, 期: 2 次第二步:找出最高频字符对
扫描语料,统计相邻字符对的出现频率:
字符对频率:
(央, 行): 8 次 ← 最高频
(降, 准): 7 次
(加, 息): 3 次
(预, 期): 2 次第三步:合并最高频对
把「央」和「行」合并成一个新 Token「央行」,加入词表:
更新后词表: {"央行", "央", "行", "降", "准", "加", "息", "预", "期"}第四步:重复合并
重新统计字符对频率,继续合并次高频的「降准」:
更新后词表: {"央行", "降准", "央", "行", "降", "准", "加", "息", "预", "期"}如此反复,直到词表达到预设大小(如 50,000 个 Token)。
为什么 BPE 有效
BPE 的巧妙之处在于它自动学习了语言的统计规律:
- 高频组合会被合并成完整 Token,如「央行」「降准」
- 低频组合保留字符级表示,保证任何新词都能处理
- 词表大小可控,不会无限膨胀
对于金融文本,BPE 会自动识别并合并常见术语。「存款准备金率」可能被分成「存款」「准备」「金」「率」四个 Token——虽然不是完美的切分,但每个片段都保留了部分语义,模型可以从上下文理解完整含义。
不同模型的 Tokenizer 切分结果可能不同。同样一句话,GPT-4 可能切成 10 个 Token,Claude 可能切成 12 个。这是因为它们的训练语料和词表不同。评估模型成本时,需要使用对应模型的 Tokenizer 计算 Token 数量。
8.2.4 其他分词算法
除了 BPE,还有几种常见的子词分词算法:
WordPiece
由 Google 开发,BERT 系列模型使用。与 BPE 的主要区别在于合并策略:
- BPE 合并频率最高的字符对
- WordPiece 合并能最大化语言模型似然的字符对
实践中两者效果接近。WordPiece 的一个特点是用「##」前缀标记非词首的子词。例如「playing」会被切成「play」和「##ing」。
Unigram Language Model
由 Google 提出的另一种方法。它从一个较大的初始词表开始,逐步删除对语言模型影响最小的词,直到词表缩小到目标大小。与 BPE 的「从小到大」相反,Unigram 采用「从大到小」的策略。
SentencePiece
严格来说,SentencePiece 不是一种分词算法,而是一个分词工具库。它的特点是:
- 直接在原始文本上训练,不依赖预分词
- 把空格也作为普通字符处理(用特殊符号「▁」表示)
- 支持 BPE 和 Unigram 两种算法
这使得 SentencePiece 能统一处理各种语言,不需要针对每种语言设计预处理规则。
| 算法 | 使用模型 | 特点 |
|---|---|---|
| BPE | GPT 系列、LLaMA | 简单高效,工业标准 |
| WordPiece | BERT、DistilBERT | 基于似然合并 |
| Unigram | T5、ALBERT | 从大词表删减 |
| SentencePiece | 多语言模型 | 语言无关,统一处理 |
8.2.5 Token 与模型能力的关系
Tokenizer 的设计直接影响模型的多项能力指标。
上下文窗口的真实容量
模型的上下文窗口以 Token 数量计算,而非字符或字数。Claude 3.5 Sonnet 的上下文窗口是 200K Token,这个数字对应多少实际文字,取决于 Tokenizer 的切分效率。
对于英文,1 个 Token 大约对应 4 个字符,即 0.75 个单词。一篇 1000 词的英文文章约占用 1300 个 Token。
对于中文,1 个 Token 大约对应 1.5-2 个汉字。一篇 1000 字的中文文章约占用 500-700 个 Token。
上下文容量估算:
Claude 3.5 Sonnet (200K Token):
- 英文: 约 15 万词 / 约 50 万字符
- 中文: 约 30-40 万字
GPT-4 Turbo (128K Token):
- 英文: 约 10 万词
- 中文: 约 20-25 万字中文的 Token 效率通常高于英文,这与中文的信息密度有关。一个汉字承载的语义信息通常比一个英文字母多。在处理同等信息量的文本时,中文占用的 Token 数更少。这意味着中文用户在相同的上下文窗口限制下,可以输入更多的内容。
Token 计费与成本控制
API 调用按 Token 计费。输入和输出分别计价,输出通常比输入贵 2-4 倍。
| 模型 | 输入价格 | 输出价格 |
|---|---|---|
| Claude 3.5 Sonnet | $3 / 百万 Token | $15 / 百万 Token |
| GPT-4o | $2.5 / 百万 Token | $10 / 百万 Token |
| Claude 3 Opus | $15 / 百万 Token | $75 / 百万 Token |
一个金融问答系统,假设每次请求平均:
- 系统提示:500 Token
- 用户问题:100 Token
- 检索文档:2000 Token
- 模型回答:500 Token
则每次请求消耗 2600 输入 Token + 500 输出 Token。使用 Claude 3.5 Sonnet,每次成本约:
输入成本: 2600 / 1,000,000 × $3 = $0.0078
输出成本: 500 / 1,000,000 × $15 = $0.0075
单次总成本: $0.0153(约 0.11 元人民币)日均 1000 次请求,月成本约 3300 元。
在设计 RAG 系统时,检索返回的文档片段是成本的主要来源。返回 5 个 500 Token 的片段意味着每次请求增加 2500 输入 Token。平衡检索数量和成本是系统设计的重要考量。
8.2.6 Tokenizer 的局限性
尽管子词分词已经是当前的最佳实践,它仍有一些固有局限:
对数字和代码不友好
Tokenizer 通常把每个数字单独切分。「2024」可能被切成「20」「24」或「2」「0」「2」「4」。这导致模型对数字的理解不如对文字那么精确——它很难把「2024」理解为一个完整的年份。
在金融场景中,数字至关重要。股价、汇率、收益率都是精确到小数点的数字。模型在处理这些数字时,偶尔会出现计算错误或数字混淆。
对专业术语切分不理想
即使是高频金融术语,也可能被切分得不够理想。「逆回购利率」可能被切成「逆」「回」「购」「利率」四个 Token,而非「逆回购」「利率」两个 Token。模型需要从上下文学习这些片段的组合含义。
跨语言一致性问题
多语言模型的 Tokenizer 需要平衡各语言的表达效率。有些模型对英文优化更多,导致其他语言的 Token 效率较低。选择模型时,可以测试一下目标语言的 Token 效率。
8.3 词嵌入与语义空间

上一节介绍了 Tokenizer 如何将文本切分成 Token 序列。本节深入探讨下一步:如何将这些 Token 转换成能够捕捉语义的数学向量——词嵌入(Word Embedding)技术是如何学会「理解」语言的。
8.3.1 从离散到连续:表示学习的动机
计算机处理文本的第一步是把文字转换成数字。最直观的方法是独热编码(One-hot Encoding)。
独热编码的工作方式
假设我们的词汇表只有五个词:「股票」「债券」「利率」「银行」「市场」。独热编码为每个词分配一个位置,用 0 和 1 表示:
股票 → [1, 0, 0, 0, 0]
债券 → [0, 1, 0, 0, 0]
利率 → [0, 0, 1, 0, 0]
银行 → [0, 0, 0, 1, 0]
市场 → [0, 0, 0, 0, 1]这种表示有三个严重问题:
维度灾难。真实的中文词汇表可能有几十万个词。每个词的向量长度就是词汇表大小——几十万维,绝大多数位置都是 0。这种稀疏表示存储效率极低,计算成本极高。
语义鸿沟。在独热编码中,「股票」和「债券」的距离与「股票」和「香蕉」的距离完全相同——都是两个不同的位置。编码没有捕捉到「股票」和「债券」都是金融资产这一语义关系。
泛化困难。如果模型学会了「股票价格上涨」这个模式,它无法自动推广到「债券价格上涨」,因为「股票」和「债券」在独热编码中毫无关联。
独热编码把每个词当作孤立的符号,丢失了词与词之间的语义关系。我们需要一种表示方法,让语义相近的词在数学空间中也彼此接近。
分布式表示的思路
与独热编码相反,分布式表示(Distributed Representation)用一组连续的实数来表示每个词。这组数字不再是「只有一个位置为 1」的稀疏向量,而是每个维度都携带信息的稠密向量。
股票 → [0.82, -0.15, 0.43, ..., 0.21] (假设 300 维)
债券 → [0.79, -0.12, 0.38, ..., 0.18] (和股票很接近)
香蕉 → [-0.31, 0.67, -0.22, ..., 0.54] (和股票很远)分布式表示的核心优势在于:语义相似的词会被映射到向量空间中相近的位置。「股票」和「债券」的向量接近,因为它们在语义上相关;「股票」和「香蕉」的向量相距甚远,因为它们几乎没有语义联系。
表示学习的目标
表示学习(Representation Learning)的任务是:从大量文本数据中自动学习出这种有意义的向量表示。我们不需要手工定义「股票应该和债券接近」——让模型自己从语料中发现这种关系。
这正是词嵌入技术要解决的问题。
8.3.2 分布式假设:语义来自上下文
词嵌入的理论基础是一个简洁而深刻的假设——分布式假设(Distributional Hypothesis)。
Firth 的经典论断
1957 年,英国语言学家 John Rupert Firth 提出了一个著名观点:
“You shall know a word by the company it keeps.” (通过一个词的伴随词,你就能了解这个词。)
这句话的含义是:一个词的意思由它经常出现的上下文决定。如果两个词总是出现在相似的上下文中,它们的意思就相近。
共现统计与语义相似性
考虑以下句子:
央行宣布下调存款准备金率
央行决定降低贷款利率
央行表示将保持流动性合理充裕「央行」这个词频繁与「宣布」「决定」「利率」「准备金」等词共同出现。另一个词「美联储」可能出现在类似的上下文中:
美联储宣布加息 25 个基点
美联储决定维持利率不变
美联储表示将继续缩表尽管「央行」和「美联储」是不同的词,但它们的上下文模式高度相似:都与「宣布」「决定」「利率」等词共现。根据分布式假设,这意味着它们在语义上相近——都是货币政策制定机构。
分布式假设的理论根源可以追溯到结构主义语言学。索绪尔(Ferdinand de Saussure)在 20 世纪初就提出:语言符号的意义来自它与其他符号的关系,而非符号本身。分布式假设是这一思想在计算语言学中的具体体现。
从假设到算法
分布式假设为词嵌入学习提供了方向:
- 统计每个词出现的上下文模式
- 上下文模式相似的词,赋予相似的向量表示
- 上下文模式不同的词,向量表示应当不同
接下来介绍的 Word2Vec 正是这一思想的经典实现。
8.3.3 Word2Vec:词嵌入的里程碑
2013 年,Google 的 Tomas Mikolov 等人发布了 Word2Vec,这是词嵌入技术的里程碑。它用一种高效的方式把分布式假设转化为可学习的模型。
两种训练架构
Word2Vec 提供了两种训练方式,思路互补:
CBOW(Continuous Bag of Words):用上下文预测中心词
给定一个词的周围词,预测这个词本身。例如:
上下文: [央行, 宣布, 存款, 准备金率]
目标词: 下调模型的任务是:看到「央行」「宣布」「存款」「准备金率」这些上下文词,预测中间空缺的词是「下调」。
Skip-gram:用中心词预测上下文
思路相反——给定中心词,预测它周围可能出现的词:
中心词: 下调
目标: 预测周围词是 [央行, 宣布, 存款, 准备金率]模型的任务是:看到「下调」这个词,预测它的上下文可能包含「央行」「准备金」等词。
两种方法各有优势:CBOW 训练更快,适合大规模语料;Skip-gram 对低频词的表示更准确,在较小语料上效果更好。
训练过程的直观理解
Word2Vec 的训练过程可以这样理解:
- 初始化:为词汇表中的每个词随机分配一个向量(比如 300 维)
- 预测:用当前向量进行预测任务(CBOW 或 Skip-gram)
- 对比:比较预测结果和真实答案
- 调整:如果预测错误,微调相关词的向量,使预测更准确
- 迭代:在大量文本上重复步骤 2-4
经过数十亿词的训练,神奇的事情发生了:语义相近的词,其向量自然而然地聚集在一起。模型并不知道「股票」和「债券」都是金融资产——它只是发现这两个词总是出现在相似的上下文中,于是把它们的向量调整到相近的位置。
负采样:提升训练效率
Word2Vec 面临一个计算瓶颈:词汇表可能有几十万词,每次预测都要计算所有词的概率,计算量巨大。
负采样(Negative Sampling)解决了这个问题。它的思路是:不需要计算所有词的概率,只需要区分「正确答案」和「随机选的错误答案」。
例如,在 Skip-gram 中预测「下调」的上下文:
- 正样本:「准备金」确实出现在「下调」附近(应该预测为「是」)
- 负样本:随机选几个词如「香蕉」「电影」(应该预测为「否」)
模型只需要学会区分真正的上下文词和随机词,不需要在整个词汇表上做计算。这让 Word2Vec 能够在普通计算机上高效训练。
对于经管类学生,理解 Word2Vec 的核心思想比掌握数学细节更重要。关键点是:(1)语义来自上下文共现;(2)通过预测任务学习向量;(3)负采样让训练变得高效。不需要推导梯度公式或理解 softmax 归一化。
8.3.4 词向量的语义性质
训练好的词向量展现出令人惊讶的语义性质。
相似词的向量接近
在向量空间中,语义相关的词会聚集在一起:
| 查询词 | 最近邻词(按余弦相似度) |
|---|---|
| 股票 | 股份、证券、股市、A股、港股 |
| 利率 | 利息、基准利率、贷款利率、存款利率 |
| 通胀 | 通货膨胀、物价、CPI、涨价 |
| 央行 | 中央银行、人民银行、美联储、欧央行 |
这种聚类完全是从数据中自动学习的,没有人工标注「股票」和「股份」是近义词。
向量算术:语义关系的编码
Word2Vec 最著名的发现是向量算术(Vector Arithmetic)。词向量之间的加减运算能够捕捉语义关系:
king - man + woman ≈ queen
国王 - 男人 + 女人 ≈ 女王这个等式的含义是:从「国王」的向量中减去「男人」的向量,再加上「女人」的向量,得到的结果最接近「女王」的向量。
这说明词向量不仅捕捉了词的「位置」,还编码了词之间的「关系」。「国王」和「女王」的关系,与「男人」和「女人」的关系,在向量空间中呈现为相似的方向。
金融领域也存在类似的关系:
股票 - 买入 + 卖出 ≈ 做空
央行 - 中国 + 美国 ≈ 美联储
GDP - 总量 + 增速 ≈ 经济增长率词向量捕捉的语义关系类型
研究表明,词向量能够编码多种语义关系:
| 关系类型 | 示例 |
|---|---|
| 同义关系 | 利润 ≈ 盈利 |
| 上下位关系 | 银行 → 金融机构 |
| 部分整体 | 利息 → 贷款 |
| 属性关系 | 中国 : 北京 = 美国 : 华盛顿 |
| 动作对象 | 降息 : 利率 = 降准 : 准备金 |
词向量的局限性
静态词向量也有明显的局限:
多义词问题。「银行」既可以指金融机构,也可以指河岸。但在 Word2Vec 中,「银行」只有一个向量,无法区分不同含义。
上下文无关。词向量一旦训练完成就固定不变。无论「苹果」出现在「苹果公司发布新产品」还是「苹果富含维生素」中,它的向量都相同。
稀有词表示差。低频词的上下文样本少,学到的向量质量较差。专业术语、新词往往是低频词。
无法处理未登录词。词汇表之外的词(Out-of-Vocabulary,OOV)没有向量表示。
GloVe(Global Vectors for Word Representation)是斯坦福大学 2014 年提出的另一种词嵌入方法。与 Word2Vec 的「预测」思路不同,GloVe 直接对词共现矩阵进行分解。两者效果相近,但 GloVe 的训练更稳定。在实践中,两者常作为词嵌入的基准选择。
8.3.5 从静态词向量到上下文相关嵌入
Word2Vec 和 GloVe 都属于静态词向量——每个词有且只有一个固定的向量表示。这限制了它们处理多义词和上下文相关语义的能力。
上下文相关嵌入的思路
2018 年之后,一种新的思路兴起:不再为每个词分配固定向量,而是根据词所在的具体句子动态生成向量。
句子 1: "我去银行取钱"
句子 2: "河的银行长满了芦苇"
静态词向量:
银行 → [0.5, 0.3, ...] (两个句子相同)
上下文相关嵌入:
句子 1 中的"银行" → [0.8, 0.2, ...] (金融机构语义)
句子 2 中的"银行" → [-0.2, 0.7, ...] (河岸语义)同一个词「银行」,在不同句子中获得不同的向量。向量不再是词的属性,而是词在特定上下文中的语义表示。
技术演进脉络
上下文相关嵌入的发展经历了几个阶段:
| 阶段 | 代表模型 | 关键创新 |
|---|---|---|
| 静态词向量 | Word2Vec、GloVe | 分布式表示、向量算术 |
| 上下文嵌入初期 | ELMo(2018) | 双向 LSTM 生成上下文向量 |
| Transformer 时代 | BERT(2018) | 自注意力机制、预训练-微调范式 |
| 大语言模型 | GPT 系列、Claude | 大规模预训练、涌现能力 |
与 RAG 的关系
本章介绍的 RAG 系统使用的嵌入模型(如 bge-small-zh-v1.5、text-embedding-3-small)都是上下文相关嵌入模型。它们继承了 Word2Vec 的核心思想——语义相似则向量接近——但克服了静态词向量的局限。
当你把一段金融政策文本送入嵌入模型时,模型会:
- 读取整段文本,理解上下文
- 为这段文本生成一个向量,编码其语义信息
- 这个向量反映的是文本在当前上下文中的含义
这就是为什么向量检索能够实现语义搜索:查询「降准政策」和文档「下调存款准备金率」虽然字面不同,但它们的语义向量接近,因此能被检索到。
8.4 文本嵌入与语义检索

上一节介绍了词嵌入的理论基础。本节进一步探讨如何将这些原理应用到实际的文本检索中——文本嵌入(Text Embedding)技术如何支撑 RAG 系统的语义检索能力。
8.4.1 从词向量到文档向量
词袋模型的局限
早期的文本表示方法是词袋模型(Bag of Words, BoW)。它把一段文字看作词语的集合,统计每个词出现的频率,忽略词序和语法结构。
词袋模型的一个变体是简单平均法:先为每个词分配一个向量(词向量),然后把句子中所有词向量取平均,作为句子的表示。
这种方法存在明显缺陷:
- 语序丢失:「央行加息」和「加息央行」得到相同的向量
- 语义稀释:句子越长,平均后的向量越接近「通用」含义,区分度下降
- 无法处理多义词:「苹果」在「吃苹果」和「苹果公司」中的含义完全不同,但词向量相同
文本嵌入的目标
文本嵌入(Text Embedding)要解决的核心问题是:如何将任意长度的文本映射到固定维度的向量空间,使得语义相近的文本在向量空间中距离相近。
与简单平均不同,现代文本嵌入模型能够:
- 捕捉词序信息:「央行加息」和「加息预期」表达不同含义
- 理解上下文:同一个词在不同语境中获得不同表示
- 保持语义一致性:「降低利率」和「降息」应映射到相近位置
这正是 Transformer 架构带来的突破。
8.4.2 Transformer 编码器与文本表示
Self-Attention 的直观理解
Transformer 是 2017 年提出的神经网络架构,如今已成为自然语言处理的基础设施。其核心机制是自注意力(Self-Attention)。
自注意力的作用可以这样理解:在处理一个句子时,模型会让每个词「关注」句子中的其他词,根据相关性加权汇总信息。
以句子「央行宣布下调存款准备金率」为例:
- 「下调」会重点关注「央行」(谁下调)和「准备金率」(下调什么)
- 「准备金率」会关注「存款」(哪种准备金)和「下调」(发生什么变化)
- 每个词的最终表示融合了它与其他词的语义关联
这种机制使模型能够理解长距离依赖关系。在传统的循环神经网络中,句首和句尾的词很难建立直接联系;而在 Transformer 中,任意两个词都能直接「对话」。
Transformer 架构由 Vaswani 等人在论文「Attention Is All You Need」(2017)中提出。这篇论文的核心洞见是:仅用注意力机制就能构建强大的序列模型,不需要循环或卷积结构。如今,几乎所有主流大语言模型(GPT、Claude、LLaMA)都基于 Transformer 架构。
BERT 与句子表示
BERT(Bidirectional Encoder Representations from Transformers,双向编码器表示)是 Google 于 2018 年推出的预训练语言模型。它专门用于生成文本的向量表示。
BERT 的核心设计是双向上下文建模。在处理一个词时,它同时考虑左侧和右侧的上下文。这与 GPT 类模型不同——GPT 只能看到左侧的词(用于生成下一个词),而 BERT 能综合利用完整的句子信息。
[CLS] Token 的作用
BERT 在输入文本的开头添加一个特殊标记 [CLS](Classification)。这个标记不代表任何具体的词,而是用于汇聚整个句子的语义信息。
处理流程如下:
输入: [CLS] 央行 宣布 降准 0.5 个 百分点 [SEP]
↓
Transformer 编码器(多层 Self-Attention)
↓
输出: [CLS]向量 央行向量 宣布向量 降准向量 ...
[CLS] 向量即为整个句子的语义表示[CLS] 位置的输出向量浓缩了句子的整体含义,可直接用于下游任务:文本分类、语义相似度计算、信息检索等。
编码器与生成模型的区别
理解 BERT 类编码器与 GPT 类生成模型的区别很重要:
| 特征 | 编码器(BERT 类) | 生成模型(GPT 类) |
|---|---|---|
| 注意力方向 | 双向(看完整句子) | 单向(只看左侧) |
| 主要任务 | 理解、分类、检索 | 生成、续写、对话 |
| 输出形式 | 固定维度向量 | 下一个 token 的概率分布 |
| 典型应用 | 文本嵌入、语义搜索 | 聊天机器人、内容生成 |
在 RAG 系统中,编码器负责将文档和查询转换为向量,用于相似度计算;生成模型负责基于检索结果生成最终回答。两者各司其职。
8.4.3 双编码器架构
查询与文档分别编码
在实际的语义检索系统中,通常采用双编码器架构(Bi-Encoder)。其核心思想是:用同一个编码器分别处理查询和文档,将它们映射到同一个向量空间。
查询编码:
"降准政策的影响" → 编码器 → 查询向量 (1024维)
文档编码:
"央行降准有助于释放流动性..." → 编码器 → 文档向量 (1024维)
相似度计算:
查询向量 · 文档向量 = 相似度分数这种架构的关键优势在于:文档向量可以离线预计算。知识库中的所有文档在索引阶段就完成向量化,存入向量数据库。检索时只需计算查询向量,然后与预存的文档向量比较,速度极快。
双编码器的权衡
双编码器架构有明显的优缺点:
| 优点 | 缺点 |
|---|---|
| 文档可离线索引,检索速度快 | 查询与文档独立编码,交互信息损失 |
| 支持大规模向量库(百万级) | 细粒度语义匹配能力有限 |
| 计算成本可控 | 对复杂查询(多条件、否定句)效果较差 |
这些缺点可以通过重排序(Reranking)来弥补。初检使用双编码器快速筛选候选文档,精排使用交叉编码器(Cross-Encoder)对查询-文档对进行联合建模,获得更精确的相关性判断。
8.4.4 余弦相似度的数学与直觉
定义与计算
余弦相似度(Cosine Similarity)衡量两个向量方向的一致程度。分子是两个向量的点积,分母是两个向量长度的乘积。这个比值等于两向量夹角的余弦值。
余弦函数的性质决定了相似度的取值范围:当两向量方向完全相同时,夹角为 0°,余弦值为 1;当两向量垂直时,夹角为 90°,余弦值为 0;当两向量方向完全相反时,夹角为 180°,余弦值为 -1。
为何用方向而非距离
一个自然的问题是:为什么不直接用欧氏距离衡量相似性?
欧氏距离计算两点之间的直线距离。问题在于,它对向量的长度敏感。假设两段文本语义相同,但其中一段更长,其向量的模长(长度)可能更大。用欧氏距离衡量,这两段文本反而显得不相似。
余弦相似度只关注方向,忽略长度。这意味着:
- 一篇 1000 字的政策解读和一段 100 字的摘要,只要表达的核心含义相同,余弦相似度就会很高
- 文本长度的差异不会干扰语义相似度的判断
这正是文本检索所需要的特性。用户的查询通常很短(一句话),而文档片段可能有几百字,但只要语义匹配,就应该被检索到。
数值范围与实际含义
余弦相似度的取值范围是 [-1, 1]:
| 相似度范围 | 含义 | 文本检索中的例子 |
|---|---|---|
| 0.85 - 1.0 | 高度相似 | 同义改写、精确匹配 |
| 0.65 - 0.85 | 相关 | 讨论同一话题的不同表述 |
| 0.4 - 0.65 | 弱相关 | 同一领域但话题不同 |
| 0 - 0.4 | 基本无关 | 不同领域的内容 |
| < 0 | 语义相反 | 在实际应用中较少见 |
在 RAG 系统中,通常设置相似度阈值(如 0.6),低于阈值的检索结果不予采纳。这个阈值需要根据具体场景调整——金融政策查询对精确度要求高,可以设置较高阈值;一般性问答可以适当放宽。
8.4.5 嵌入模型的选择
维度的权衡
嵌入模型输出的向量维度是一个重要的设计参数。维度越高,能够编码的语义信息越丰富,但计算和存储成本也越大。
| 维度 | 存储成本 | 检索速度 | 语义精度 | 适用场景 |
|---|---|---|---|---|
| 256-384 | 低 | 快 | 一般 | 原型开发、资源受限 |
| 512-768 | 中等 | 较快 | 良好 | 大多数生产应用 |
| 1024 | 中等偏高 | 中等 | 较高 | 高精度检索需求 |
| 3072-4096 | 高 | 较慢 | 最高 | 极致精度场景 |
对于金融文档检索,768-1024 维通常是合适的选择。维度过低会损失细微的语义差异(如区分「加息」和「降息」的政策取向),维度过高则带来不必要的计算开销。
多语言与中文优化
嵌入模型的训练数据决定了它对不同语言的处理能力。选择模型时需考虑:
- 英文为主的模型(如 all-MiniLM-L6-v2):英文效果好,中文能力有限
- 多语言模型(如 OpenAI text-embedding-3):覆盖多种语言,中文表现尚可
- 中文优化模型(如 bge-small-zh、text2vec-chinese):专门针对中文训练,中文效果最佳
金融领域存在大量中文专业术语(如「逆回购」「MLF」「LPR」),选用中文优化模型能更准确地理解这些概念。
主流嵌入模型概览
| 模型 | 提供方 | 维度 | 特点 | 推荐场景 |
|---|---|---|---|---|
| text-embedding-3-small | OpenAI | 1536 | 多语言、质量高 | 快速起步、API 调用 |
| text-embedding-3-large | OpenAI | 3072 | 最高精度 | 对精度要求极高 |
| bge-small-zh-v1.5 | BAAI | 512 | 中文优化、轻量 | 中文场景、本地部署 |
| bge-large-zh-v1.5 | BAAI | 1024 | 中文优化、高精度 | 高精度中文检索 |
| bge-m3 | BAAI | 1024 | 多检索模式 | 混合检索场景 |
| Qwen2.5-Embedding-0.6B | 阿里云 | 1024 | 中英双语、平衡 | 双语场景、本地部署 |
对于本书的学习者,推荐以下选择路径:
- 快速上手:使用 OpenAI text-embedding-3-small,通过 API 调用,无需本地配置
- 本地部署:使用 bge-small-zh-v1.5 或 Qwen2.5-Embedding-0.6B,免费且中文效果好
- 生产环境:根据实际测试结果选择,通常 bge-large-zh 或 text-embedding-3-large 表现最佳
8.5 向量数据库与索引机制

前几节介绍了向量嵌入的概念——把文本转换成高维空间中的坐标。本节深入探讨:这些向量存储在哪里?如何高效检索?
8.5.1 向量数据库的定义与功能
向量数据库(Vector Database)是专门为存储和检索高维向量而设计的数据库系统。它与传统关系型数据库有根本性区别。
传统数据库(如 MySQL、PostgreSQL)擅长处理结构化数据:姓名、日期、金额等字段,通过精确匹配或范围查询定位记录。当你执行 SELECT * FROM stocks WHERE price > 100 时,数据库按字段值筛选,返回符合条件的行。
向量数据库解决的是另一类问题:给定一个查询向量,找出数据库中与它最相似的 K 个向量。这里没有精确匹配,只有相似度排序。
| 查询类型 | 传统数据库 | 向量数据库 |
|---|---|---|
| 精确查找 | 查询股票代码 600519 的收盘价 | 不适用 |
| 范围查询 | 查询市盈率在 10-20 之间的股票 | 不适用 |
| 语义搜索 | 不适用 | 找出和「央行降息影响」语义最相近的研报段落 |
| 相似推荐 | 不适用 | 根据某篇研报,推荐内容相似的其他研报 |
向量数据库承担三项核心功能:
存储(Storage)
向量数据库存储的是高维浮点数组。一篇文档经过嵌入模型处理后,变成一个包含数百到数千个浮点数的向量。数据库需要高效地将这些向量持久化到磁盘,同时支持快速加载到内存。
索引(Indexing)
索引是向量数据库的核心技术。原始向量存储后,数据库会构建索引结构,使检索时无需遍历全部数据。索引算法决定了检索速度和召回率的平衡。
检索(Retrieval)
检索是向量数据库的最终目的。给定查询向量,数据库利用索引结构快速定位最相似的 K 个向量,返回结果及相似度分数。
向量数据库的本质功能:在海量高维向量中,快速找到与查询向量最相似的若干条目。这是语义搜索、推荐系统、RAG 等应用的底层支撑。
8.5.2 最近邻搜索问题
向量检索的数学本质是最近邻搜索(Nearest Neighbor Search)问题。
K 最近邻(K-Nearest Neighbors,K-NN)定义
给定一个查询点 q 和一个包含 n 个点的数据集 D,K-NN 问题要求找出 D 中与 q 距离最近的 K 个点。
在向量数据库语境下,每个「点」是一个文档块的嵌入向量,「距离」通常用余弦相似度或欧氏距离衡量。
暴力搜索的复杂度问题
最直接的方法是暴力搜索(Brute Force):计算查询向量与数据库中每个向量的相似度,按分数排序,取前 K 个。
这个方法简单直观,但计算量巨大。假设数据库有 n 个向量,每个向量维度为 d,则: - 计算一次相似度需要 O(d) 次运算 - 遍历全部向量需要 O(n × d) 次运算
当 n = 1000 万、d = 1024 时,一次查询需要约 100 亿次浮点运算。即使用现代 CPU,也需要数秒才能完成,完全无法满足实时检索需求。
为什么需要近似最近邻(ANN)算法
面对暴力搜索的效率瓶颈,近似最近邻(Approximate Nearest Neighbor,ANN)算法应运而生。
ANN 算法的核心思想是:牺牲少量精度,换取数量级的速度提升。
它不保证找到绝对最近的 K 个邻居,但保证找到的结果「足够接近」真实答案。实践中,优秀的 ANN 算法能在毫秒级完成千万级向量库的检索,同时召回率超过 95%——即找到的 Top-10 结果中,有 9.5 个确实是真正的 Top-10。
对于 RAG 应用,这个精度足够了。我们并不需要找到数学意义上绝对最相似的文档,而是需要找到语义高度相关的文档。ANN 算法的微小误差几乎不影响最终生成质量。
8.5.3 HNSW 算法原理
HNSW(Hierarchical Navigable Small World,分层可导航小世界图)是目前最流行的 ANN 索引算法。理解它的原理有助于选择合适的参数配置。
小世界网络的启发
HNSW 的理论基础来自「六度分隔理论」——任意两个陌生人之间,平均只需通过六个中间人就能建立联系。这说明在某些网络结构中,节点间的连接非常高效。
将这个思想应用到向量检索:如果我们能构建一个网络,让语义相近的向量彼此连接,检索时就可以沿着连接「跳跃」,快速逼近目标,而非逐一遍历。
多层图结构的设计思想
HNSW 的核心创新是引入分层结构:
- 第 0 层(底层)包含全部向量,每个向量与其最近的若干邻居相连
- 更高层只包含部分向量(按概率随机选择),形成更稀疏的「快速通道」
- 层数越高,向量越少,节点间跨度越大
这种设计类似于地图的缩放级别: - 第 3 层是全国地图,只显示省会城市,城市间有高速公路 - 第 2 层是省级地图,显示地级市,有国道连接 - 第 1 层是市级地图,显示县区,有省道连接 - 第 0 层是街道地图,显示每栋建筑,有小路连接
检索过程:从顶层粗定位到底层精确查找
查询向量 q 的检索过程如下:
- 从顶层入口点开始。选择顶层的一个固定入口点作为起点
- 在当前层贪心搜索。沿着边移动到与 q 更相似的邻居,直到无法继续改进
- 下降到下一层。以当前位置为起点,进入更密集的下一层继续搜索
- 重复直到底层。在底层完成最终的精确定位,返回 Top-K 结果
整个过程如同用地图导航:先在全国地图上定位大致省份,再在省级地图上定位城市,最后在街道地图上定位具体地址。每一层的搜索范围都比上一层小,但定位更精确。
性能特点:速度与召回率的权衡
HNSW 的性能受两个关键参数影响:
| 参数 | 含义 | 影响 |
|---|---|---|
| M | 每个节点的最大连接数 | 越大召回率越高,但内存占用和构建时间增加 |
| ef_search | 检索时的候选队列大小 | 越大召回率越高,但检索延迟增加 |
典型配置与性能参考(百万级向量库):
| 配置 | 检索延迟 | 召回率@10 | 适用场景 |
|---|---|---|---|
| M=16, ef=50 | ~5ms | ~92% | 对延迟敏感的实时应用 |
| M=32, ef=100 | ~15ms | ~97% | 平衡选择,推荐默认值 |
| M=48, ef=200 | ~30ms | ~99% | 对召回率要求极高的场景 |
对于金融文档检索场景(通常几万到几十万条向量),HNSW 的默认参数(M=32, ef_search=100)已能满足需求,无需过度调优。优先关注嵌入模型选择和分块策略,它们对检索质量的影响更大。
8.5.4 其他索引算法简介
除 HNSW 外,还有几种常见的 ANN 索引算法,适用于不同场景。
IVF(Inverted File Index):倒排索引思想
IVF 的核心思想是「先聚类,后检索」:
- 训练阶段:用 K-Means 算法将全部向量聚成若干簇(cluster),每个簇有一个中心点
- 索引阶段:每个向量被分配到最近的簇,建立倒排索引
- 检索阶段:先找到查询向量最近的若干个簇,只在这些簇内搜索
IVF 的优缺点: - 优点:索引构建速度快,内存占用低 - 缺点:簇边界处的向量容易被漏检,召回率不如 HNSW
PQ(Product Quantization):向量压缩
PQ 是一种向量压缩技术,常与 IVF 联合使用(IVF-PQ)。
原理:将高维向量切分成若干段,每段独立量化。例如,1024 维向量切成 8 段,每段 128 维,用 256 个码本向量表示。原本需要 4KB 的向量,压缩后只需 8 字节。
PQ 大幅降低内存占用和计算量,代价是精度损失。适合内存受限但数据量巨大的场景(如十亿级向量库)。
不同算法的适用场景
| 算法 | 数据规模 | 内存 | 召回率 | 推荐场景 |
|---|---|---|---|---|
| HNSW | 百万~千万级 | 高 | 高 | 追求检索质量,内存充足 |
| IVF | 千万~亿级 | 中 | 中 | 大规模数据,平衡方案 |
| IVF-PQ | 亿级以上 | 低 | 较低 | 超大规模,内存受限 |
| Flat(暴力) | 万级以内 | 低 | 100% | 数据量小,对精度要求极高 |
对于本书涉及的金融应用场景(政策文档、研报、论文),数据量通常在十万到百万级,HNSW 是首选。
8.5.5 主流向量数据库
市场上有多种向量数据库产品,各有定位。
Chroma:轻量级,适合学习
Chroma 是 Python 原生的向量数据库,以简洁著称。
特点: - 零配置,pip install chromadb 后立即可用 - 内置多种嵌入模型支持 - 数据默认存储在内存或本地文件 - 不适合生产环境的大规模部署
适用场景:学习向量检索概念、快速原型开发、小型项目。
Qdrant:高性能,支持 MCP
Qdrant 是用 Rust 编写的高性能向量数据库,近年获得广泛采用。
特点: - 性能优异,支持百万级向量的毫秒级检索 - 丰富的过滤条件支持(按元数据筛选) - 提供官方 MCP Server,与 Claude Code 集成顺畅 - 支持 Docker 部署,也有托管云服务
适用场景:生产环境、需要与 Claude Code 集成的项目。
Pinecone:托管服务
Pinecone 是一款全托管的向量数据库云服务。
特点: - 无需运维,按用量付费 - 自动扩缩容 - 提供免费额度,适合试用 - 数据存储在云端,需考虑数据合规
适用场景:希望快速上线、不想管理基础设施的团队。
Milvus:企业级
Milvus 是 CNCF 孵化的开源向量数据库,面向企业级场景。
特点: - 支持十亿级向量 - 分布式架构,水平扩展 - 多种索引算法可选 - 部署和运维复杂度较高
适用场景:大型企业、海量数据、有专业运维团队。
选型考虑因素
| 因素 | 说明 |
|---|---|
| 数据规模 | 万级以下用 Chroma;百万级用 Qdrant/Pinecone;亿级用 Milvus |
| 部署方式 | 本地开发用 Chroma/Qdrant Docker;生产环境考虑 Pinecone 托管或 Qdrant Cloud |
| 集成需求 | 与 Claude Code 集成优先选 Qdrant(有官方 MCP) |
| 预算 | 预算有限选开源方案;追求免运维选托管服务 |
| 数据合规 | 敏感数据需本地部署,避免数据出境 |
对于本书的学习者,推荐从 Qdrant 起步。它兼具易用性和专业性:Docker 一条命令启动,MCP 集成无缝对接 Claude Code,性能满足绑大多数学习和小型生产场景。等熟悉了向量检索的完整流程,再根据实际需求评估其他选项。
8.6 文档处理与检索优化

前面几节介绍了 RAG 系统的基本架构、嵌入原理和向量数据库。本节深入探讨文档分块的理论基础、分块策略、检索优化方法,以及金融文档的特殊处理。
8.6.1 文档分块的理论基础
为什么需要分块
大语言模型有上下文窗口限制。即便 Claude 的窗口达到 200K tokens,一次对话也无法容纳几十份完整的研报或政策文件。分块的首要目的是让文档适配这一物理限制。
但分块还有更深层的原因:语义稀释问题(Semantic Dilution)。
当一段文本过长时,嵌入模型生成的向量只能捕捉文本的「平均语义」。假设一份 5000 字的央行报告同时讨论了降准、LPR 调整、跨境资本流动三个话题,其向量就会是这三个话题的「混合体」。用户问「LPR 是多少」时,这份文档的语义匹配度会被其他两个话题稀释,检索排名可能不如一篇只讨论 LPR 的短文。
分块的本质是提高语义密度。每个分块聚焦一个相对单一的主题,向量才能精准表达该主题的含义。
分块粒度的权衡
分块大小是一个经典的权衡问题:
| 分块太小 | 分块太大 |
|---|---|
| 语义不完整,信息碎片化 | 语义被稀释,检索精度下降 |
| 丢失上下文,答案不连贯 | 无关内容多,增加噪声 |
| 检索到的块需要拼凑 | 单块信息冗余,浪费上下文 |
8.6.2 分块策略详解
固定长度分块
最简单的分块方式:按固定字数切分,如每 500 字一块。
优点: - 实现简单,代码几行搞定 - 分块大小可控,便于资源规划 - 适合格式统一的文档
缺点: - 切分位置随机,可能切断句子或段落 - 不考虑文档的逻辑结构 - 语义边界与物理边界不对齐
递归分块(Recursive Chunking)
递归分块按分隔符的优先级逐层切分:
第 1 层:章节标题(# ## ###)
第 2 层:段落分隔(空行)
第 3 层:句号、问号、感叹号
第 4 层:逗号、分号
第 5 层:空格
第 6 层:字符级切分(兜底)这种方法的优势在于尊重文档的自然结构。一份结构清晰的研报会被切成「执行摘要」「行业分析」「财务分析」等自然段落,而非被机械地每 500 字一刀。
语义分块(Semantic Chunking)
语义分块更加智能:先将文档拆成句子,为每个句子生成向量,然后计算相邻句子的语义相似度。当相似度突然下降时,说明话题发生转换,此处就是语义边界。
语义分块的优势是完全按内容的逻辑边界切分。劣势是计算成本高——每个句子都需要调用嵌入模型。
分块策略选择指南
| 文档类型 | 推荐策略 | 理由 |
|---|---|---|
| 结构化报告(有章节目录) | 递归分块 | 尊重已有结构 |
| 新闻资讯 | 固定长度 | 结构简单,段落短 |
| 学术论文 | 语义分块 | 逻辑严密,话题转换明确 |
| 法规条款 | 按条款切分 | 保持条款完整性 |
| 财务报表 | 按表格切分 | 表格不可拆分 |
重叠(Overlap)的作用
重叠是指相邻分块之间共享一部分内容。关键信息可能恰好出现在分块边界。若无重叠,边界处的句子可能被切断,或与前后文脱节。重叠确保边界附近的信息在至少两个分块中完整出现,降低信息丢失的风险。
重叠率的经验值:
| 场景 | 推荐重叠率 | 说明 |
|---|---|---|
| 通用文档 | 10%-15% | 平衡存储与覆盖 |
| 金融报告 | 15% | 保留数据上下文 |
| 技术文档 | 20%-30% | 逻辑链较长 |
| 新闻短讯 | 5%-10% | 段落独立性强 |
8.6.3 检索优化方法
分块存入向量数据库后,检索阶段的优化直接决定 RAG 系统的最终效果。
重排序(Reranking)
初步向量检索速度快但精度有限。重排序(Reranking)用更精确的模型对初步结果进行二次排序。
- 初步检索使用双编码器(Bi-Encoder):问题和文档分别编码成向量,比较相似度。速度快(毫秒级),但问题和文档没有交互,无法捕捉细粒度的语义匹配。
- 重排序使用交叉编码器(Cross-Encoder):把问题和文档拼接在一起,让模型同时「看」两段文字,输出一个相关性分数。精度高,但速度慢(几百毫秒一对)。
生产环境的标准流程:
- 向量检索 Top-20(毫秒级)
- 交叉编码器重排至 Top-5(几百毫秒)
- 将 Top-5 送入生成模型
重排序对最终效果的提升通常在 20%-35% 之间。
混合检索(Hybrid Retrieval)
向量检索擅长语义匹配:「降准」能检索到「下调存款准备金率」。但它对精确关键词匹配较弱:搜索文件编号「银发〔2024〕187 号」,可能返回一堆「银行文件」相关的内容,却漏掉精确匹配的文档。
关键词检索(如 BM25 算法)则擅长精确匹配,但不理解语义。
混合检索将两者结合:
最终得分 = α × 向量相似度 + (1-α) × BM25 得分不同场景的推荐权重:
| 场景 | 向量权重 (α) | BM25 权重 | 说明 |
|---|---|---|---|
| 概念解释类查询 | 0.7 | 0.3 | 语义理解为主 |
| 法规政策查询 | 0.4 | 0.6 | 精确匹配更重要 |
| 跨语言查询 | 0.8 | 0.2 | 向量跨语言能力强 |
| 数据查询 | 0.3 | 0.7 | 数字、日期需精确匹配 |
查询改写(Query Rewriting)
用户的提问方式往往与文档的表述不一致。查询改写通过生成多个等价表述来扩大检索覆盖面。
原始查询:降息对股市有啥影响?
改写版本:
1. 利率下调对股票市场的影响
2. 货币政策宽松与权益市场表现
3. LPR 下调后 A 股走势分析HyDE(Hypothetical Document Embeddings)
HyDE 是一种反直觉但有效的技术:先让模型生成一个假设性答案,然后用这个假设答案的向量去检索。
为什么有效?知识库中存储的都是陈述性内容(答案),而用户的查询是疑问句。两者的语义形式不同,向量匹配度受限。用答案去搜索答案,形式更一致,匹配度更高。
8.6.4 检索质量评估
召回率(Recall@K)
定义:在 Top-K 个检索结果中,包含正确答案的查询占总查询数的比例。
目标值:Recall@5 > 80%,Recall@10 > 90%。
平均倒数排名(Mean Reciprocal Rank,MRR)
MRR 衡量正确答案出现的位置。正确答案排在第 1 位时倒数排名为 1,排在第 2 位时为 0.5,排在第 5 位时为 0.2。
目标值:MRR > 0.6。
评估与优化的迭代过程
检索优化是一个迭代过程:
- 建立基线:用当前策略在测试集上计算 Recall@K 和 MRR
- 识别问题:找出检索失败的案例,分析原因
- 调整参数:修改分块大小、重叠率、检索参数
- 重新评估:在同一测试集上计算新指标
- 对比改进:若指标提升,保留修改;否则回退
8.6.5 金融文档的特殊考量
表格数据的处理
资产负债表、利润表、现金流量表等财务表格是金融文档的核心内容。表格切分会破坏数据的行列关系,导致信息丧失。
处理原则: 1. 整表存储:将整张表格作为一个独立分块,不切分 2. 添加表头摘要:在向量中存储表头和关键数据的文字描述 3. 表格 + 解读配对:将表格与其下方的分析文字绑定存储
财务数据的上下文保留
财务比率和增长数据必须保留其参照基准。「净利润率为 15%」单独看不知道是什么业务的净利润率。
处理方法: 1. 扩大分块窗口,确保数据与其描述在同一块中 2. 使用语义分块,保持分析段落的完整性 3. 为数据块添加元数据标签
政策文件的结构化分块
央行报告、监管政策通常有清晰的章节结构。利用这些结构作为天然的分块边界,比固定字数切分效果更好。递归分块策略特别适合此类文档。
时效性标记
金融信息有强时效性。建议: 1. 为每个分块添加时间元数据(发布日期、有效期) 2. 检索时支持时间过滤 3. 对过期文档标记提示,避免引用已失效的信息
金融领域对信息准确性要求极高。即使 RAG 系统给出了答案和来源,关键决策前仍需人工核查原文。RAG 是辅助工具,不能替代专业判断。
8.7 RAG 实践入门
本节为初学者提供 RAG 系统的快速上手指南。我们从 Claude Code 的两种实现路径出发,介绍最简单的配置方法、基本问答交互,以及系统评估的核心要点。
8.7.1 Claude Code 中的 RAG 实现路径
在 Claude Code 环境中构建 RAG 系统,有两条可行路径。选择哪条取决于文档规模和检索精度要求。
路径一:文件级 RAG
利用 Claude Code 内置的 Read 和 Grep 工具直接搜索项目目录中的文档。这是最简单的方案,无需额外配置。
适用场景:
| 条件 | 说明 |
|---|---|
| 文档数量 | 50 份以内 |
| 文档总量 | 500KB 以内 |
| 检索需求 | 关键词匹配足够,无需语义理解 |
| 配置要求 | 零配置,即开即用 |
局限性: - 只能做关键词匹配,无法理解语义 - 问「降准」找不到写着「下调存款准备金率」的段落
路径二:MCP 集成向量数据库
通过 MCP 协议连接 Qdrant 等向量数据库,实现真正的语义检索。
适用场景:
| 条件 | 说明 |
|---|---|
| 文档数量 | 不限,支持数万份文档 |
| 检索需求 | 需要语义理解,处理同义词、近义表达 |
| 精度要求 | 金融合规场景,要求高召回率 |
| 配置要求 | 需启动 Docker 服务,配置 MCP |
初学者从文件级 RAG 起步,理解 RAG 的基本逻辑。当文档超过 50 份或需要语义检索时,再迁移到向量数据库方案。两种方案的 CLAUDE.md 配置逻辑相同,迁移成本很低。
8.7.2 简单实践示例
以下演示文件级 RAG 的最小可行配置。
第一步:准备知识库文档
创建项目目录结构:
my-rag-project/
├── CLAUDE.md
└── knowledge_base/
└── policies/
├── 2024q3_monetary_policy.md
└── lpr_history.md第二步:配置 CLAUDE.md 检索规则
在项目根目录创建 CLAUDE.md:
## 项目说明
这是一个货币政策问答系统,知识库包含央行政策文档。
## 检索规则
当用户询问货币政策、利率、准备金等问题时:
1. 使用 Grep 工具搜索 knowledge_base/policies/ 目录
2. 按关键词定位相关段落
3. 基于搜索结果回答,不得编造
4. 回答时标注来源文件名
## 知识库内容
- 2024 年 Q3 货币政策执行报告
- LPR 历史报价记录第三步:基本问答交互
在项目目录启动 Claude Code 后即可进行问答。
CLAUDE.md 中的检索规则相当于给 Claude 一份「工作手册」。规则越清晰,Claude 的行为越可预期。好的检索规则应明确三点:何时检索、去哪里找、怎么引用。
8.7.3 系统评估方法
RAG 系统的质量需要从检索和生成两个层面评估。
检索层评估指标
| 指标 | 含义 | 目标值 |
|---|---|---|
| Recall@K | Top-K 结果中包含正确文档的比例 | > 80% |
| MRR | 正确结果的平均排名倒数 | > 0.6 |
| Precision@K | Top-K 结果中相关文档的比例 | > 60% |
生成层评估指标
| 指标 | 含义 | 目标值 |
|---|---|---|
| 答案准确率 | 回答内容与事实一致 | > 85% |
| 来源标注率 | 正确引用了文档来源 | > 90% |
| 幻觉率 | 编造了文档中没有的信息 | < 5% |
| 完整性 | 回答覆盖了问题的各个方面 | > 80% |
幻觉率是金融 RAG 系统最需要关注的指标。一个看似合理但错误的数字(如编造的利率数据)可能导致严重的合规问题。建议对关键数字类回答进行人工复核。
常见问题与排查方向
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 检索不到相关文档 | 关键词不匹配 | 检查文档用词,考虑添加同义词 |
| 检索结果与问题无关 | 分块过大或过小 | 调整分块大小(800-1000 字) |
| 回答内容不完整 | 检索结果数量不足 | 增加 Top-K 值 |
| 回答包含错误信息 | 检索噪声或模型幻觉 | 引入重排序,降低低分结果权重 |
本章小结
本章系统介绍了检索增强生成(RAG)技术的原理与实践。核心要点回顾:
RAG 解决的问题
大语言模型的知识有截止日期,且会产生幻觉。RAG 让模型在回答前检索真实文档,用外部知识弥补模型的局限性。
技术原理链条
| 环节 | 任务 | 关键技术 |
|---|---|---|
| Tokenizer | 将文本切分成 Token | BPE、WordPiece 算法 |
| 词嵌入 | 将 Token 映射到向量空间 | Word2Vec、分布式假设 |
| 文本嵌入 | 生成句子/段落的向量表示 | Transformer 编码器、BERT |
| 向量存储 | 高效存储和索引向量 | 向量数据库、HNSW 算法 |
| 语义检索 | 找到最相关的文档 | 余弦相似度、混合检索 |
| 增强生成 | 基于检索结果回答 | 上下文构建、来源标注 |
技术选型建议
| 组件 | 入门选择 | 进阶选择 |
|---|---|---|
| 检索方式 | 文件级 RAG(Grep) | 向量数据库(Qdrant) |
| 嵌入模型 | bge-small-zh-v1.5 | Qwen2.5-Embedding |
| 分块策略 | 固定大小 800 字 | 递归分块 + 语义边界 |
| 检索优化 | 单路语义检索 | 混合检索 + 重排序 |
评估要点
- 检索层:Recall@5 > 80%,确保正确文档被检索到
- 生成层:准确率 > 85%,幻觉率 < 5%
- 金融场景特别关注来源可追溯性
RAG 效果 = 检索质量 × 生成质量
检索找不到正确文档,生成再好也无用;检索准确但生成不当,同样产出低质量回答。两个环节必须协同优化。
与后续章节的衔接
RAG 是构建知识密集型智能体的基础能力。下一章「多智能体协作」将展示如何让多个智能体共享同一个 RAG 知识库,协同完成复杂任务。第 15 章「文献综述智能体」会直接复用本章的 RAG 架构,将学术论文库作为检索源。