目录
- 一、从一个简单的问题开始
- 二、语言模型“闭卷考试”的困境
- 三、RAG 是什么—LLM 的现实世界“外挂”
- 四、RAG 的七步流程
- 第一步:加载数据(Load)
- 第二步:切分文本(Chunking)
- 第三步:向量化(Embedding)
- 向量化:把句子变成“可以计算距离”的坐标点
- 向量化模型做了啥?
- 相似度怎么计算?
- 总结一下
- 第四步:存入向量数据库(Vector Store)
- 第五步:接收用户问题(Query)
- 第六步:检索相关文段(Retrieve)
- 第七步:组织 Prompt,交给大模型生成回答(Generate)
- 总结
如果关注 AI 领域,那么 RAG 这个名词你肯定不陌生,这篇文章,我们就来揭开它的神秘面纱。为什么需要 RAG,它到底是什么,能解决什么问题。
一、从一个简单的问题开始
假设你在和一个 AI 聊天助手对话,你问它:
“北京到上海高铁多久?”
这看起来像个非常简单的问题,但它考验的却是 AI 模型的知识广度和知识时效性。
你希望它能回答类似这样:
“大约 4.5 到 6 小时,具体取决于车次。”
但是,假设这个 AI 模型训练得比较早,它可能回答是——
“我不知道。”
或者:
“我认为北京和上海之间目前没有高铁。”(因为它只看到了 2010 年以前的数据)
这就暴露出一个大语言模型的通病:
训练完就定格了,它不会自己更新知识。
二、语言模型“闭卷考试”的困境
所有的大语言模型(如 GPT、Claude、Gemini)在训练时都要读取大量文本,比如:
- 维基百科
- 新闻网站
- Reddit 论坛
- Github 代码
- 开放书籍、论文
训练结束后,它就像一个“背书高手”,记住了大量的知识。但这也意味着一但遇到新知识
、实时内容
、你私有的数据
,它就歇菜了。
所以问题就来了:怎么让模型既有“语言能力”,又能随时“看资料再回答”呢?
这时候就该 RAG 登场了!
三、RAG 是什么—LLM 的现实世界“外挂”
RAG,全称是 Retrieval-Augmented Generation,翻译为“检索增强生成”。
通过字面意思也能看出来它的核心作用,通过检索来增强生成(废话)
用通俗话来讲:
它让 AI 在回答之前,先“查资料”,再用大模型来“组织语言”。
就像你考试的时候如果不确定答案,那就翻课本,然后用自己的话组织一段回答。
想象一个真实的场景,比如你在一家 SaaS 公司,客户经常问你:
- “你们的产品怎么绑定企业微信?”
- “有没有 API 文档?”
- “怎么开具发票?”
这些内容,可能都写在:
- 帮助中心文档
- FAQ 文档
- 客服聊天记录
- 内部知识库
而传统的 ChatGPT 模型对这些你们内部的这些专属知识一无所知。
这时候你就可以用 RAG,它的基本流程是:
- 用户提问
- 在你的知识库里“检索”相关文档段落(比如找到 API 文档那一段)
检索
- 把这些内容和用户问题一起送进语言模型
增强
- 生成一个有针对性的、个性化的回答。
生成
这样的系统既懂你公司,又能写好回答。
所以 RAG 的核心优势显而易见:
优点 | 解释 |
---|---|
实时更新 | 你改了文档,模型就能学会新内容,不需要重新训练 |
私有知识 | 可以在不暴露给外部模型的前提下使用公司内部数据 |
可控性强 | 检索什么,传给模型什么,你可以干预整个过程 |
更少幻觉 | 模型参考真实资料后,不容易瞎编 |
所以,总的来说,大语言模型就像是通用的大脑,RAG 则让它接入你自己的知识,RAG 不是让模型更“聪明”,而是让它更“有见识”。
通过上面的描述,RAG 听起来很简单嘛。
但真正的 RAG 系统背后可是有很多技术细节:
- 文档如何分段(chunking)
- 怎样计算用户问题和文档的“语义相似度”(向量检索)
- 检索出几条内容?怎么拼接 Prompt?
- 模型是否支持多轮记忆和上下文压缩?
- 如何缓存和优化响应速度?
等等,这些都会影响最终效果。
四、RAG 的七步流程
我们已经大致了解了 RAG 的原理,现在我们就从宏观视角来看看 RAG 的全流程是怎么样的。
第一步:加载数据(Load)
RAG 的第一步,就是从你现有的资料中**“把内容读进来”**。比如我们加载一份 FAQ 文档:
import { TextLoader } from "langchain/document_loaders/fs/text";const loader = new TextLoader("docs/faq.txt");
const rawDocs = await loader.load();
输出结果是一个标准格式:
[{pageContent: "退订说明:用户如需退订,请登录控制台,点击账户管理。",metadata: { source: "faq.txt" }},...
]
这就像是把原始文档清洗成结构化文本,供后续使用。
第二步:切分文本(Chunking)
为什么要切分?因为文档太大了,大模型一次吃不下。
我们需要把文档拆成段落级别的小块(chunk),通常每块控制在几百字以内。
切分工具(在 langchain.js 中):
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";const splitter = new RecursiveCharacterTextSplitter({chunkSize: 500,chunkOverlap: 50
});
const docs = await splitter.splitDocuments(rawDocs);
第三步:向量化(Embedding)
现在我们已经把文档切分了,但是有一个问题:
计算机如何知道,两个句子意思相近?
比如下面这两个问法:
- “怎么取消订阅?”
- “我不想继续用了,怎么退?”
我们人类一看就知道是同一个意思。可对计算机来说,它只看到一串字符,完全不懂“语义”。
那怎么办?
我们需要给它一种能“看出意思相似”的方式,这就是向量化。
向量化:把句子变成“可以计算距离”的坐标点
你可以把“向量化”想象成这样的过程:
我们给每个句子分配一个“坐标点”,让相近意思的句子靠得近,差很多的句子离得远。
比如:
句子 | 向量(简化表示) |
---|---|
“怎么退订服务?” | [0.9, 0.1, 0.4] |
“不想用了怎么办?” | [0.88, 0.12, 0.45] |
“产品价格是多少?” | [0.1, 0.8, 0.9] |
你可以把每个向量想象成一个坐标点在三维空间中:
- 第一个句子和第二个句子很靠近(表示意思差不多)
- 第三个句子在远处(表示是完全不同的问题)
于是我们就可以计算两个句子之间的“距离”,这个距离越近,它们的意思就越像。
这就是所谓的**“语义相似度计算”**。
向量化模型做了啥?
现在的语言模型已经很强大,它们会从大量语料中学会如何把意思相近的词语、句子放到更接近的坐标位置上。
你只要给它一句话,它就会返回一个长长的向量,比如:
[0.12, -0.03, 0.77, ..., 0.01] // 长度可能是 1536 维
虽然我们看不懂这个向量长啥样,但这没关系——我们只需要知道:它可以拿来计算“相似度”
相似度怎么计算?
最常用的方式就是:
计算两个向量的夹角是否接近(余弦相似度)
可以理解为:
- 两个方向完全一样的箭头,表示“非常相似”
- 两个方向差很多,说明“几乎没关系”
总结一下
概念 | 通俗理解 |
---|---|
向量 | 把一个句子的意思表示成一组数字坐标 |
向量化 | 把句子转成向量(embedding 模型来做) |
相似度 | 比较两个向量距离近不近,距离越近意思越相近 |
所以,向量化的本质就是:把语言变成“可以比较距离的东西”,让计算机能看出语义像不像。
第四步:存入向量数据库(Vector Store)
我们把每段文本 + 它的向量
都存到一个数据库里,方便后续检索。
可用的数据库有:
- FAISS(本地)
- Pinecone / Weaviate / Chroma(云服务)
- Milvus(工业级)
示例(用 FAISS):
import { FaissStore } from "langchain/vectorstores/faiss";const store = await FaissStore.fromDocuments(docs, embeddings);
现在,你就有了一个可以按语义相似度查内容的数据库了。
第五步:接收用户问题(Query)
终于轮到用户提问了,比如:
“怎么退订你们的服务?”
这个问题会经过同样的向量化过程,再去数据库中查最接近的问题片段。
第六步:检索相关文段(Retrieve)
将用户问题的向量丢进数据库,找出前几段最相似的内容(通常是 top-3 或 top-5):
const results = await store.similaritySearch("怎么退订服务?", 3);
返回类似:
["退订说明:请登录控制台,点击账户管理页面...","退订功能位于控制台左侧菜单栏...",...
]
这就是查资料的部分。
第七步:组织 Prompt,交给大模型生成回答(Generate)
最后,我们把:
- 用户问题
- 检索到的文档片段
组织成一个 Prompt,交给 LLM:
const prompt = `
你是一个客服助手,请参考以下内容回答用户问题:资料:
---
1. ${results[0]}
2. ${results[1]}
3. ${results[2]}
---用户提问:
怎么退订服务?请根据资料,用简洁的语言回答:
`;
然后喂给 OpenAI 或其他模型:
const res = await llm.call(prompt);
得到最终回答:
“您好,您可以登录控制台,进入‘账户管理’,点击‘退订管理’即可操作退订。”
至此,一个完整的 RAG 流程就完成了。
当然这只是最简单笼统的流程介绍,具体的每个环节都是很复杂的,比如算法的选取以及参数的调整等等等等…
总结
RAG = 查文档 + 用大模型答题,RAG 本质上让 LLM 具备了实时访问企业知识库的能力,既规避了幻觉,又能针对性回答个性化问题。