跳过正文
  1. Ais/

RAG检索增强生成:原理、数据切片与向量化详解

x
作者
x
熟练掌握Spring Boot、Spring Cloud等Java技术栈,专注于分布式系统设计与微服务架构。热爱技术分享,探索编程之美。
目录

RAG检索增强生成:原理、数据切片与向量化详解
#

大语言模型(LLM)虽然拥有海量的通用知识,但它的知识边界止于训练数据的截止日期,也无法访问企业内部文档、私有数据库这类"没见过"的信息。想让模型回答这些问题,通常有两条路:**微调(Fine-tuning)把新知识"炼"进参数里,或者RAG(Retrieval-Augmented Generation,检索增强生成)**在回答前先把相关资料"喂"给模型。本文重点讲后者——它的基本原理、数据切片(Chunking)与向量化(Embedding)的具体做法,以及一个经常被混淆的问题:向量化和大模型训练到底是不是一回事

一、为什么需要RAG
#

大模型的知识本质上是训练阶段被压缩进参数矩阵里的统计规律,这带来两个绕不开的限制:

  • 知识过时:模型上线后,世界仍在变化,但参数不会自动更新。
  • 知识盲区:私有代码库、内部Wiki、最新的产品文档,模型从未见过。
  • 幻觉问题:面对未知问题,模型倾向于"编"一个看似合理的答案,而不是承认不知道。

RAG的思路很朴素:既然模型脑子里没有,那就在提问时把答案相关的资料一并递给它,让它"开卷考试"。 这样既不需要重新训练模型(成本低、迭代快),又能让答案有据可查(可追溯到原始文档),是目前企业知识库、智能客服、文档问答类应用最主流的技术方案。

二、RAG的核心原理
#

RAG系统通常分为两个阶段:离线的知识库构建在线的检索生成

2.1 整体流程
#

离线阶段(构建索引):
原始文档 → 数据切片(Chunking) → 向量化(Embedding) → 存入向量数据库

在线阶段(问答):
用户提问 → 向量化(Embedding) → 向量数据库中检索相似片段 → 拼接Prompt → LLM生成答案

用一张更具体的图来看在线问答的过程:

用户提问:"我们系统的订单超时是怎么处理的?"
   Embedding模型把问题转成向量 q
   在向量数据库中做相似度检索(如余弦相似度、ANN近邻搜索)
   召回Top-K个最相关的文档片段(如订单超时相关的3段代码注释/文档)
   把这些片段拼进Prompt:"根据以下资料回答问题:[片段1][片段2][片段3]\n问题:xxx"
   LLM基于给定资料生成回答(而不是凭参数记忆瞎编)

可以看到,RAG本质上是**信息检索(IR)+ 生成(Generation)**的组合,模型的角色从"知识的存储者"变成了"资料的阅读理解者",这也是它能大幅缓解幻觉问题的原因——回答有据可依,而不是靠参数里模糊的统计记忆。

2.2 RAG vs 微调
#

维度RAG微调(Fine-tuning)
更新知识的方式更新向量库中的文档,无需改模型需要重新训练/微调模型参数
成本低,主要是Embedding和检索的算力高,需要标注数据和训练算力
时效性高,文档改了检索结果立刻生效低,每次更新知识都要重新训练
可解释性强,可以指出答案来自哪个文档片段弱,答案来自参数,无法溯源
适用场景知识频繁变化、需要溯源、私有数据需要改变模型的语气/风格/推理能力

两者并不互斥,实践中也常常结合使用:用微调让模型更好地理解某个领域的术语和回答风格,用RAG保证具体事实的准确性和时效性。

三、数据切片(Chunking)
#

在把文档灌进向量库之前,第一步是切片:把一篇长文档切成一个个较小的片段。这一步看似简单,实际上直接决定了检索质量的上限。

3.1 为什么不能整篇文档直接向量化
#

  • Embedding模型有输入长度限制:大多数Embedding模型的上下文窗口在512~8192 token之间,超长文档会被截断。
  • 粒度太粗,检索精度差:如果把一整篇几千字的文档编码成一个向量,那这个向量表达的是"全文大意",当用户只问一个具体细节时,这个向量和问题向量的相似度会被大量无关内容"稀释",检索不准。
  • 拼接进Prompt的成本:即使检索到了,把一整篇文档塞进Prompt也会占用大量上下文窗口,挤占LLM推理的"预算",还会增加token成本。

所以合理的粒度是:每个片段尽量只包含一个完整、独立的语义单元——太大则精度下降、太小则丢失上下文,两者都会拉低最终答案的质量。

3.2 常见的切片策略
#

1. 固定长度切片(Fixed-size Chunking)

按字符数或token数切分,通常会保留一定的重叠(overlap)以避免关键信息刚好被切在两段的交界处丢失语义连贯性。

def fixed_size_chunk(text, chunk_size=500, overlap=50):
    chunks = []
    start = 0
    while start < len(text):
        end = start + chunk_size
        chunks.append(text[start:end])
        start += chunk_size - overlap  # 保留overlap长度的重叠
    return chunks

优点是实现简单、速度快;缺点是完全不考虑语义边界,可能把一句话、一个代码块硬生生切成两半。

2. 按语义结构切片(Structure-aware Chunking)

优先沿着文档天然的结构切分——按段落、按Markdown的标题层级、按代码的函数/类边界,尽量保证每个片段是一个完整的语义单元。

def markdown_chunk(text):
    # 按二级标题切分,每个chunk保留自己的标题作为上下文
    sections = text.split("\n## ")
    chunks = []
    for i, sec in enumerate(sections):
        prefix = "## " if i > 0 else ""
        chunks.append(prefix + sec.strip())
    return chunks

这也是本站这类技术博客最适合的方式:一篇文章天然按##/###分节,沿着标题切片能最大程度保留语义完整性。

3. 递归切片(Recursive Chunking)

先按最大的结构单元(如章节)切,如果切出来的片段仍然超过长度限制,再依次按段落、句子递归细分,是LangChain等框架里最常用的默认策略,兼顾了语义完整性和长度可控性。

4. 语义切片(Semantic Chunking)

用Embedding模型逐句计算向量,在相邻句子的语义相似度出现明显下降的地方切分——本质上是"哪里意思变了,就在哪里切一刀"。效果通常更好,但需要额外的Embedding计算,成本更高,适合对检索精度要求高的场景。

3.3 切片粒度的权衡
#

片段大小优点缺点
较小(如100~200 token)检索精度高,命中片段更聚焦容易丢失上下文,需要更多Top-K才能覆盖完整信息
较大(如500~1000 token)保留更多上下文,语义更完整检索精度下降,Prompt占用更多token

实践中一般会配合**overlap(重叠窗口)元数据(如标题、来源、章节路径)**一起存储,检索到片段后还能带着元数据一起拼进Prompt,帮助模型理解这段内容的上下文出处。

四、向量化(Embedding)
#

切好片段后,下一步是把每个文本片段转换成一个固定维度的数值向量——这就是向量化,也叫Embedding。

4.1 向量化的核心思想
#

向量化的目标是把"语义"映射到几何空间里:语义相近的文本,向量在空间中的距离也相近。比如"订单超时怎么处理"和"超时的订单如何应对"这两句话字面上差异不小,但语义几乎一致,一个好的Embedding模型会把它们映射到空间中很接近的两个点。

有了这个性质,检索问题就变成了一个几何问题——给定一个查询向量,找出向量库里离它最近的K个向量,常用的相似度度量是余弦相似度(Cosine Similarity):

similarity(A, B) = (A · B) / (|A| × |B|)

值越接近1,代表两个向量方向越一致,语义越相似。

4.2 向量数据库中的检索
#

真正的向量库不会对每个查询做暴力全量比对(数据量大了之后代价太高),而是用**近似最近邻(ANN, Approximate Nearest Neighbor)**算法,比如HNSW(分层可导航小世界图)、IVF(倒排文件索引)等,用可接受的精度损失换取数量级的检索速度提升。常见的向量数据库有Milvus、Pinecone、Weaviate、Qdrant,也有像PostgreSQL的pgvector这种插件式方案。

4.3 向量化模型怎么来的
#

Embedding模型本身也是一个神经网络(通常基于Transformer的Encoder结构,如BERT系列,或专门训练的Embedding模型如OpenAI的text-embedding-3、开源的BGE、GTE系列)。它的训练目标很直接:通过对比学习(Contrastive Learning),拉近语义相近文本对的向量距离,推远不相关文本对的向量距离。

训练样本:(锚点文本, 正样本, 负样本)
例:("如何处理订单超时", "订单超时的解决方案", "如何注册新用户")

训练目标:
  distance(锚点, 正样本) 尽量小
  distance(锚点, 负样本) 尽量大

五、向量化和大模型训练原理的关系
#

这是很多刚接触RAG的人容易混淆的地方:向量化用的Embedding模型和ChatGPT/Claude这类生成式大模型,是不是同一种东西训练出来的? 答案是:底层架构同源(都基于Transformer),但训练目标和产出完全不同。

5.1 共同的根:Transformer与自监督预训练
#

无论是Embedding模型还是生成式LLM,起点通常都是同一套技术——Transformer架构上做自监督预训练(Self-supervised Pretraining):拿海量无标注文本,通过"预测被遮盖/预测下一个词"这类任务,让模型学会捕捉语言的统计规律和语义结构。这一步产出的是一个理解语言、编码语义的"基座",无论后续要拿它做生成还是做向量表示,都是站在这个基座上。

区别从这里开始分叉:

5.2 生成式LLM:训练目标是"预测下一个词"
#

ChatGPT、Claude这类大模型属于Decoder-only架构,训练目标是经典的自回归语言建模(Autoregressive Language Modeling)

给定前面的词序列,预测下一个词的概率分布

P(词_n | 词_1, 词_2, ..., 词_n-1)

之后再叠加指令微调(SFT)、人类反馈强化学习(RLHF)等阶段,让模型学会"听懂指令、给出有帮助且安全的回答"。整个过程优化的核心始终是生成质量——模型要能一个词一个词地把连贯、正确、符合意图的文本"写"出来。这个模型的产物是逐词生成的文本序列

5.3 Embedding模型:训练目标是"把语义压缩成一个定长向量"
#

而Embedding模型通常是Encoder(或双向)架构,它不关心怎么"往下写",只关心怎么把一段变长的文本,压缩成一个固定维度、能代表其语义的向量(比如1536维)。训练目标不是预测下一个词,而是前面提到的对比学习:让语义相似的文本在向量空间中靠近,不相似的推远。这个模型的产物是一个静态的数值向量,本身不能"说话",只能用来做相似度计算和检索。

5.4 一张表看清区别
#

维度生成式LLM(如GPT/Claude)Embedding模型(如BGE/text-embedding-3)
架构通常是Decoder-only Transformer通常是Encoder(双向)Transformer
训练目标自回归预测下一个词 + SFT/RLHF对齐对比学习,拉近/推远语义相似度
输出形式逐词生成的文本序列固定维度的数值向量
核心能力理解意图、推理、生成连贯文本度量语义相似度
在RAG中的角色阅读检索到的资料,生成最终回答把文本/问题编码成向量,供检索用

5.5 为什么理解这个区别很重要
#

搞清楚这个区别,会直接影响RAG系统的工程决策:

  • 不要用生成式LLM直接输出的隐藏层向量去做检索——它的训练目标是"预测下一个词",向量空间的几何结构并未被显式优化为"语义相似度可比较",直接拿来做余弦相似度检索效果通常不如专门训练的Embedding模型。
  • Embedding模型和生成模型可以独立选型、独立升级——比如可以用一个轻量的开源Embedding模型做检索,同时用能力更强的大模型做最终生成,两者解耦、各司其职,这也是RAG架构天然模块化的原因之一。
  • 两者都依赖同一批"预训练常识"——这也是为什么很多Embedding模型是在通用预训练模型的基础上,用对比学习"继续训练(continue training)“出来的,而不是从零开始训练,本质上是复用了Transformer在预训练阶段学到的语言理解能力,再把优化目标"掰"向语义相似度这个方向。

六、结语
#

RAG把"模型该记住什么"和"模型该怎么表达"这两件事拆开了:知识存在可以随时更新的向量库里,模型只负责基于检索到的资料做理解和表达。切片决定了知识被拆成什么粒度,向量化决定了这些知识能不能被准确地"找到”,而生成模型负责把找到的资料组织成一个连贯、可信的回答。

理解向量化模型和生成式大模型"同源但目标不同"这一点,能帮助我们在设计RAG系统时做出更合理的选型:不必迷信"参数越大越好",而是让每个组件专注于自己被训练要做的事。

通过邮件回复

相关文章

OpenClaw 使用指南:用 Skills 实现 AI 全自动操控电脑

OpenClaw 使用指南:用 Skills 实现 AI 全自动操控电脑 # 在 AI 浪潮席卷全球的今天,我们不再满足于让 AI 仅仅回答问题——我们希望 AI 能够真正帮我们干活。OpenClaw 正是这样一个框架:它让大语言模型(LLM)拥有"手脚",通过可插拔的 Skills(技能) 系统,让 AI 代理能够执行文件操作、运行命令、自动化浏览器、管理进程……几乎所有你能用鼠标键盘做的事,OpenClaw 都能让 AI 替你完成。

MCP的AI上下文管理心得

MCP的AI上下文管理心得 # 在使用 MCP(Model Context Protocol,模型上下文协议)构建 AI 应用的过程中,上下文管理是绕不开的核心话题。无论是对话系统、代码助手还是知识库问答,上下文的质量直接决定了 AI 响应的准确性和连贯性。本文结合实际使用经验,聊一聊在 MCP 体系下做好 AI 上下文管理的一些心得。

AI的发展与现状:从传统机器学习到MCP与Skills的新纪元

AI的发展与现状:从传统机器学习到MCP与Skills的新纪元 # 人工智能(Artificial Intelligence, AI)作为21世纪最具革命性的技术之一,正在深刻地改变着我们的生活方式、工作模式和社会结构。从早期的专家系统到如今的大语言模型,AI技术经历了多个发展阶段,而近期MCP(Model Context Protocol)和Skills的出现,更是将AI应用推向了新的高度。

常见排序算法

常见排序算法 # 排序算法是计算机科学中最基础也是最重要的算法之一。本文介绍几种常见的排序算法,并给出 Java 实现。

TLS非对称加密流程详解:从握手到对称加密通信

TLS非对称加密流程详解:从握手到对称加密通信 # TLS(Transport Layer Security,传输层安全协议)是互联网上最广泛使用的安全协议之一,它为客户端和服务器之间的通信提供了加密、身份验证和数据完整性保护。本文将详细介绍TLS握手过程中客户端和服务端的具体操作,包括CA证书验证、密钥交换,直到最终生成AES对称加密密钥进行正常通信的完整流程。

使用Debian作为路由器:完整配置指南

使用Debian作为路由器:完整配置指南 # 在企业环境或高级家庭网络中,使用Linux系统(特别是Debian)作为路由器可以提供更强大的功能、更高的灵活性和更好的性能。本文将详细介绍如何将Debian配置为功能完整的路由器,包括DHCP服务、DNS服务、NAT配置、IPv6中继、网桥设置等核心功能。

spring三级缓存介绍

6 分钟
Spring的三级缓存介绍 Spring框架在处理单例Bean的创建和依赖注入时,使用了三级缓存机制来管理Bean的生命周期和解决潜在的循环依赖问题。这些缓存主要定义在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry类中。下面结合源码进行详细介绍。

Java GC进化路程

4 分钟
1. 概述 # 本博客中我们将展示不同JVM垃圾回收(GC)实现的基本原理。然后我们将学习如何在应用程序中启动特定类型的垃圾回收。