从幻觉到检索增强:RAG 向量化语义搜索的工程启蒙
v037 构建了 Agent 的工具调用与上下文编排体系——MCP Server 既能提供动作(Tool),也能发布知识(Resource)。但这个体系隐含了一个前提:Agent 知道该调用哪个工具,模型理解当前任务需要什么背景。
第三十四天要回答一个更根本的问题:当模型"不知道"时怎么办?
大模型的知识取决于训练时喂给它的数据集。问它最近发生的事情,或者企业内部私有文档,它不知道——但不会说不知道,而是会"一本正经地胡说八道"。这就是幻觉(Hallucination)。
解决思路很朴素:用户要查的内容,先去内部知识库里找,找到后放进 Prompt 再给大模型。这套流程就叫 RAG——Retrieval-Augmented Generation,检索增强生成。
用户提问 → 向量化 → 向量数据库相似度检索 → 相关文档片段 → 拼入 Prompt → 大模型生成回答
本文以第三十四天的学习笔记与代码实践为基础,从幻觉的成因出发,逐层展开 RAG 的工程原理。
一、LLM 幻觉的本质:不是 bug,是训练数据的边界
大模型的知识从哪来
大模型的知识完全来自训练数据集。训练数据有截止日期,有覆盖范围的局限。企业的内部文档、昨天刚发布的新闻、你电脑上的私有笔记——这些都不在训练集里。
关键问题在于:模型不会说"我不知道"。
这不是模型故意骗你,而是它的训练目标决定了行为模式——它学到的总是尽力根据已有模式生成最像的答案,而不是诚实地评估自己是否拥有足够信息。当你问一个超出它知识范围的问题时,它仍然会基于语言模式拼出一个"看起来合理"的回答,而这个回答可能与事实毫无关系。
工程角度的定义
从工程角度看,幻觉不是模型的道德缺陷,而是信息检索问题:模型缺少回答问题所需的关键信息,但调用链中没有"先查资料"这一步。
因此,解决方案不是在模型层面消除幻觉,而是在 Prompt 进入模型之前,把相关资料找出来一起送进去。这就是 RAG 要解决的问题。
二、RAG 核心原理:先检索,再增强,最后生成
三个词拆解
| 阶段 | 英文 | 做什么 | 类比 |
|---|---|---|---|
| 检索 | Retrieval | 从知识库中找到与用户问题最相关的文档片段 | 图书馆管理员帮你找书 |
| 增强 | Augmented | 把检索到的片段作为背景知识加入 Prompt | 把找到的书摊开在你面前 |
| 生成 | Generation | 大模型基于增强后的 Prompt 生成回答 | 你读完资料后回答问题 |
朴素流程
用户问:"公司今年的休假政策是什么?"
1. 把问题向量化
2. 去向量数据库里查最相关的文档片段
3. 找到 3 段相关片段:
- "年假每年 10 天,可累积至次年 3 月"
- "病假需提供医院证明"
- "调休需提前 3 个工作日申请"
4. 把这些片段拼到 Prompt 里:
---
根据以下公司政策回答用户问题:
政策 1:年假每年 10 天,可累积至次年 3 月
政策 2:病假需提供医院证明
政策 3:调休需提前 3 个工作日申请
用户问题:公司今年的休假政策是什么?
---
5. 大模型根据这些背景知识生成准确回答
没有第 2~4 步,模型就只能靠训练数据里的"一般公司政策"来猜测,大概率给出不准确甚至完全错误的答案。
RAG 的两个关键工程决策
整个 RAG 链路中有两个环节最影响效果:
- 文档怎么切分——切太粗,检索精度差;切太细,丢失上下文
- 怎么把相关文档查出来——关键词匹配太粗糙,向量语义搜索才是正解
三、从关键词到向量:语义搜索的核心突破
为什么关键词搜索不够
如果用户问"这东西能吃吗",而知识库里写的是"本品可食用"——
- 关键词匹配:在"本品可食用"里搜不到"能吃"二字,返回空
- 向量语义搜索:两个表达的向量非常接近,精准命中
关键词搜索做的是字符串匹配,向量搜索做的是语义匹配。前者只能找到"说法一样"的内容,后者能找到"意思一样"的内容。
向量如何表达语义
想象一个简化的二维坐标系:
- 横轴:可食用性,0 = 不可食用,1 = 高可食用
- 纵轴:硬度,0 = 液体,1 = 高硬度
苹果 [0.9, 0.5] ← 可食用性高,中等硬度
香蕉 [0.9, 0.2] ← 可食用性高,较软
石头 [0.1, 0.8] ← 不可食用,很硬
苹果和香蕉的向量很接近——它们都是水果,语义相关。石头离它们很远——语义无关。
真实世界的嵌入模型用的是几百到上千维的向量,能表达远比"可食用性+硬度"丰富得多的语义特征。两个向量之间的夹角越小,语义越相似——这就是余弦相似度。
从二维到高维的工程直觉
二维只是方便理解。真实模型(如 text-embedding-v4)输出的向量通常是 1024 维甚至更多。这些维度不是人为定义的(没人去标注"第 37 维代表动词时态"),而是模型在大量文本上训练后自动学到的语义编码。
工程上你不需要理解每一维的含义——你只需要知道:相似的文本会产生相似的向量,而向量数据库能高效地找到它们。
四、嵌入模型与向量数据库:RAG 的基础设施
嵌入模型(Embedding Model)
嵌入模型是专门用来把文本转成向量的模型。它和生成式大模型(如 DeepSeek、GPT)是两种不同的模型:
| 生成式大模型 | 嵌入模型 | |
|---|---|---|
| 任务 | 根据输入生成文本 | 把文本转成向量 |
| 输出 | 一段文字 | 一个高维向量(如 1024 维) |
| 用途 | 对话、写作、推理 | 语义搜索、聚类、分类 |
| 例子 | DeepSeek-v4, GPT-4 | text-embedding-v4, ada-002 |
嵌入模型可以把文本、图片、PDF、语音等多种模态的内容统一向量化,这意味着你可以在同一个向量数据库里跨模态做语义搜索。
文档切分(Chunking)
在把文档写入向量数据库之前,需要把长文档切成小片段。切分策略直接影响检索质量:
| 策略 | 适用场景 | 优缺点 |
|---|---|---|
| 按章节切 | 结构化文档 | 上下文完整,但粒度可能偏粗 |
| 按段落切 | 通用文档 | 粒度适中,但可能切断跨段落的论述 |
| 按固定 token 数 | 无结构文本 | 粒度可控,但可能在语义中间切断 |
| 按自然语义边界 | 高质量要求 | 语义完整,但实现更复杂 |
切分没有银弹——需要根据文档类型和业务场景选择策略,并在检索效果上做验证。
向量数据库
传统数据库(MySQL、PostgreSQL)擅长精确匹配和范围查询,但做不了"找与这个向量最相似的前 10 条记录"这种操作。向量数据库专为高维向量的相似度检索设计:
- 存储:持久化保存文档向量及其元数据
- 检索:基于余弦相似度、欧氏距离等指标,快速找到最相似的向量
- 规模:支持百万甚至亿级向量的近似最近邻搜索(ANN)
市面上常见的向量数据库包括 Milvus、Pinecone、Weaviate、Qdrant,以及 PostgreSQL 的 pgvector 扩展。
五、代码实践:用 LangChain 构建可检索的文档知识库
第三十四天的代码实践使用 LangChain 搭建了一个最小 RAG 的文档端。虽然还没有完成向量存储和检索的闭环,但已经建立了最关键的工程直觉:文档不是随便写的字符串,而是带有结构化 metadata 的可检索单元。
嵌入模型配置
import { OpenAIEmbeddings } from '@langchain/openai'
const embeddings = new OpenAIEmbeddings({
modelName: 'text-embedding-v4',
apiKey: process.env.QENWEN_API_KEY,
configuration: {
baseURL: 'https://ws-qxij4z1ad9qamacb.cn-beijing.maas.aliyuncs.com/compatible-mode/v1',
},
})
OpenAIEmbeddings 是 LangChain 提供的嵌入模型封装。虽然叫 "OpenAI",但它兼容任何 OpenAI 格式的 embedding API——这里实际用的是阿里云 MAAS 上的通义千问嵌入模型。这里体现的是一个重要的工程习惯:通过标准接口隔离具体实现,方便后续切换嵌入模型提供商。
Document 的 metadata 设计
import { Document } from '@langchain/core/documents'
const documents = [
new Document({
pageContent: `光光是一个活泼开朗的小男孩,他有一双明亮的大眼睛……`,
metadata: {
chapter: 1,
character: "光光",
type: "角色介绍",
mood: "活泼"
},
}),
new Document({
pageContent: `东东是光光最好的朋友,他是一个安静而聪明的男孩……`,
metadata: {
chapter: 2,
character: "东东",
type: "角色介绍",
mood: "温馨"
},
}),
// ... 共 7 个 Document,构成一个完整的儿童故事
]
几个工程要点:
-
pageContent是向量化的主体——只有它会被送入嵌入模型生成向量,metadata不参与向量化计算。这意味着你应该把最需要语义匹配的内容放在pageContent里。 -
metadata用于过滤和溯源——检索命中后,你可以通过 metadata 过滤("只要 mood=温馨 的片段")、排序("优先返回 chapter 靠前的")或展示来源。但它不会影响语义搜索的匹配结果。 -
metadata 结构体现领域建模——这里定义了
chapter(位置)、character(人物)、type(情节类型)、mood(情绪)四个维度。这些不是随意起的字段名,而是对这个儿童故事内容特征的抽象。在真实业务中,metadata 的设计决定了后续检索、过滤和展示的质量上限。
与 v037 Resource 的对比
| 维度 | MCP Resource(v037) | RAG Document(v038) |
|---|---|---|
| 注入方式 | 直接注入 SystemMessage | 先检索,命中后再注入 |
| 适用规模 | 少量静态指南/规则 | 大规模文档集合 |
| 内容来源 | 开发者手动编写并审查 | 从文档自动切分生成 |
| 信任模型 | 高信任,来源可控 | 需独立验证,保留来源信息 |
| 检索能力 | 无,全量注入 | 向量语义搜索,精准命中 |
| 更新频率 | 低,随代码发布 | 高,可实时入库 |
两者不是替代关系,而是互补关系:少量、高信任、高频使用的规则走 Resource 直接注入;大规模、动态、需要精准匹配的知识走 RAG 检索。
六、AI 工程化认知升级
第三十四天的增量可以收束为三件事:
-
幻觉不是要消除的 bug,而是信息检索流程要补齐的缺口。 模型不知道就是不知道,你不能让它变知道——但你可以让它"先查再答"。这改变了我们对 AI 能力的预期:不再期待模型全能,而是设计好它获取信息的路径。
-
向量化是连接自然语言与机器检索的桥梁。 关键词搜索停留在"字符串匹配"层面,向量搜索进入了"语义匹配"层面。嵌入模型把文字变成数字,向量数据库让数字变得可检索——这套基础设施是 RAG 能成立的前提。
-
文档不是数据,是知识单元。 一个
Document怎么切分、metadata 设计什么字段、哪些内容参与向量化——这些决策决定了检索质量。写代码之前先想清楚"用户会怎么搜,搜到什么才算有用"。
结语
第三十四天没有在 Agent 的工具链上继续叠加,而是回到了一个更底层的问题:模型的知识从哪里来。
LLM 的训练数据是固定的,但真实世界的知识是流动的。RAG 给这种流动性提供了一套工程方案——把知识存在向量数据库里,用语义搜索找到相关片段,在 Prompt 里增强模型。它不改变模型本身,但改变了模型获取信息的方式。
从 v036 的工具调用,到 v037 的上下文编排,再到 v038 的检索增强——这三步串在一起,一个 Agent 的信息处理闭环才算完整:
知道做什么(Tool)
→ 知道需要什么背景(Resource/RAG)
→ 知道如何组合(多 Server / 检索策略)
→ 知道出问题时怎么追查(可观测性 / 排障证据)
RAG 补上了"知识注入"这块拼图,而它与 Resource 的边界划分,正是工程师在"简单直接"和"精准高效"之间做权衡的典型场景。
本篇内容基于第三十四天学习笔记整理,重点记录 RAG 的核心原理、向量语义搜索的工程基础,以及与 MCP Resource 在知识注入方式上的边界对比。