你是否曾遇到过这样的情形:向大模型询问公司内部文档的相关信息,它给出的回答看似有理有据,仔细推敲却全是虚构?

大模型并非有意欺骗你,而是它确实不具备相关知识。它仅掌握训练时输入的数据,对于企业内部的私有文档或最新资讯,完全一无所知。然而,它并不会承认自己的无知,反而会“煞有其事地编造答案”——这便是所谓的大模型幻觉。
如何有效应对这一棘手问题?
最直观的解决方案是微调(Finetune)。然而,这条路径却难以付诸实践——成本过于高昂,需要大量的算力和专业数据集,普通企业往往难以承受。
不妨换个思路:既然大模型缺乏相关知识,那我们直接提供给它不就可以了吗?
这正是RAG(检索增强生成)的核心理念:
这样一来,大模型便拥有了“参考依据”,其回答自然更加准确可靠。
然而,新的问题随之而来:如何实现高效检索?
最直接的想法是使用关键词搜索。但这种方法过于粗糙——例如,用户询问“如何请假”,而文档中写的是“休假申请流程”,关键词无法准确匹配。
于是就有了向量化(Vectorization) 的方案。
简单而言,就是将文本转换为一组数字(即向量),通过计算向量间的“距离”来评估语义相似度。例如:
- 苹果 → [0.9, 0.5]
- 香蕉 → [0.9, 0.1]
- 石头 → [0.1, 0.9]
苹果和香蕉的向量距离更近,表明它们在语义上具有关联性;而苹果与石头的向量距离则较远。
这正是嵌入模型(Embedding Model) 的功能所在。它能够将文本、图片、PDF、语音等多种内容形式转化为向量,从而实现真正的语义搜索。此外,嵌入模型相比生成式大模型成本低廉许多,堪称小团队的福音。
动手实践:搭建一个RAG演示系统
纸上谈兵终觉浅,不如使用LangChain构建一个简单的RAG演示系统。
首先准备数据——编写一个关于“光光与东东”的小故事,将其划分为7个片段,每个片段携带元数据(章节、角色、类型、情绪):
ja vascript
复制代码import { Document } from '@langchain/core/documents';const documents = [
new Document({
pageContent: '光光是一个活泼开朗的小男孩,他有一双明亮的大眼睛,总是带着灿烂的笑容。光光最喜欢的事情就是和朋友们一起玩耍,他特别擅长踢足球...',
metadata: {
chapter: 1,
character: '光光',
type: '角色介绍',
mood: '活泼'
}
}),
// ... 更多文档片段
];
接着初始化生成模型与嵌入模型:
ja vascript
复制代码import 'dotenv/config';
import { ChatOpenAI, OpenAIEmbeddings } from 'langchain/openai';// 生成模型 - 用来回答问题
const model = new ChatOpenAI({
temperature: 0,
model: process.env.MODEL_NAME,
apiKey: process.env.OPENAI_API_KEY,
configuration: {
baseURL: process.env.OPENAI_API_BASE_URL,
}
});// 嵌入模型 - 用来做语义检索
const embeddings = new OpenAIEmbeddings({
model: process.env.EMBEDDING_MODEL_NAME,
apiKey: process.env.OPENAI_API_KEY,
configuration: {
baseURL: process.env.OPENAI_API_BASE_URL,
}
});
这里需要留意两个模型的区别:
- 生成模型(Generative Model):用于回答用户问题,体积较大、成本较高
- 嵌入模型(Embedding Model):负责将文本转化为向量,体积较小、成本较低
向量数据库:存储什么?如何查询?
在拥有文档和嵌入模型后,下一步便是将这些文档向量化并存储起来。
传统的关系型数据库(如MySQL)无法胜任此项任务,因为向量间的相似度计算(例如余弦相似度)需要专门的索引与查询机制。此时便需要引入向量数据库。
目前主流的向量数据库包括:
- Pinecone(云服务)
- Milvus(开源)
- Weaviate(开源)
- Chroma(轻量级,适合本地开发)
具体流程如下:
text
复制代码1. 把文档切成段落(按章节、按页、按语义段落)
2. 用嵌入模型把每个段落转成向量
3. 把向量存到向量数据库
4. 用户提问时,也把问题转成向量
5. 在向量数据库中搜索最相似的文档片段
6. 把检索到的片段作为上下文,让大模型生成回答
实战效果展示
使用准备好的7个故事片段,提出一个问题:
如果采用关键词搜索,可能仅能匹配到“朋友”、“东东”、“光光”等词汇,但对于具体情节的匹配准确度较低。
而使用向量检索,模型能够理解“成为朋友”这一语义,从而找到相关片段——例如光光邀请东东踢球、光光教东东踢球、东东通过画画回报光光等情节。
随后将检索到的内容放入提示词(Prompt)中:
text
复制代码背景知识:
光光看出了东东的担忧,他拍着东东的肩膀说:“没关系,我们一起练习,我相信你一定能行的!”
接下来的日子里,光光每天放学后都会教东东踢足球...
东东也用自己的方式回报光光,他画了一幅画送给光光...问题:东东和光光怎么成为朋友的?
如此一来,大模型便能基于真实上下文进行回答,而非凭空编造。
两个关键突破点
在实施RAG的过程中,有两个核心问题亟待解决:
1. 文档如何切分?
切分粒度过粗,检索到的片段可能混杂大量无关信息;切分过细则可能导致上下文丢失。通常建议按章节、按页或按语义段落进行切分,以确保每个片段具备完整的语义。
2. 如何通过向量检索相关文档?
这依赖于向量数据库的相似度检索功能。通过计算问题向量与文档向量之间的余弦相似度,夹角越小,相似度越高。
总结
RAG并非高深莫测的技术,其核心只包含三个步骤:
- 检索:利用嵌入模型将文档向量化,并存储至向量数据库
- 增强:当用户提问时,从向量数据库中检索相关文档,将其注入Prompt
- 生成:大模型基于增强后的Prompt生成回答
该方案成本远低于微调,准确度远超关键词搜索,并且支持文本、图片、PDF、语音等多种格式。如果你也正受大模型幻觉问题的困扰,不妨尝试RAG。
