大语言模型(LLM)常因内部知识不足而引发幻觉问题 ,同时其上下文长度(Context Length)有限,难以容纳大量知识 ,这些问题一直备受诟病。
RAG 知识库的出现,在一定程度上解决了上述问题。基于检索增强生成(RAG,Retrieval Augmented Generation)的知识库方案 ,旨在将信息检索与大语言模型相结合 ,不仅有效缓解了大模型在推理过程中产生的幻觉问题,还通过仅提取与问题相关的知识,减少了大量无关信息输入上下文的情况 ,从而提升了回答的准确性和效率。
本文将会通过 LangChain 快速搭建一个简易的基于大语言模型(LLM)的检索增强生成(RAG)知识库,我们先了解相关原理,最终实现此方法。
框架与原理解析
我们将分步骤逐步搭建基于大语言模型的检索增强生成 RAG 知识库框架。
1. 知识文件嵌入与索引存储
所谓的检索增强,旨在为大语言模型提供特定专业领域或与问题相关的知识 ,以此增强大模型的在特定领域的能力。实际上,构建检索增强的过程类似于打造搜索引擎 ,用户需要通过输入问题查询(query)来获取相对精准的答案。
因此,构建 RAG 的首要步骤是提供一个支持增删查改的数据库 ,以便用户能够通过查询(query)语句高效获取所需信息。
在机器学习领域,主流的方法是将文档等不同类型的数据通过嵌入模型(Embedding Model)获得嵌入向量(Embedding Vector),并将其存入向量数据库 。
请看下图,这个流程展示了知识文件嵌入与存储流程。
知识文件 (Documents):输入的原始文档,一般是特定领域的知识。
这里所指的文档(Documents)可以指代任何形式的文件格式或多媒体等。例如:网站、文档、PDF、视频及音频文件等,即嵌入的知识可以是多模态的。
分块器 (Chunker):由于大语言模型上下文大小的限制,我们有必要将文档分割成小块。
分块知识 (Chunks):分割后的文档块。
嵌入模型 (Embedding Model):使用一个机器学习模型(如 BERT、GPT 等)对每个分块知识进行嵌入,生成对应的嵌入向量(Embedding Vectors)。
嵌入向量 (Embedding Vectors):每个分块知识对应的数值向量表示。
向量数据库 (Vector Database):存储所有嵌入向量的数据库,用于后续的查询和检索。
不过对于初学者来说,这有点太抽象了
我们将使用一个具体的例子讲解,我们将大语言模型(LLM)作为一个天气查询小助手
我们的知识文件中包含明天所有城市的天气预报,我们的第一步是将天气预报数据存储到向量数据库 。
对于大量的知识文件,我们无法将其全部喂给大模型 ,因此有必要分块儿,我们需要一个叫做分块器(Chunker) 的工具。理想的分块效果是将内容划分为若干个不同的部分(块),每个块既独立又相互关联 。
之后,我们将分好块的知识依次通过嵌入模型(Embedding Model) 获得嵌入向量(Embedding Vector) 。
不过,这里的嵌入模型究竟是什么东西呢 ,以及我们为什么要获得嵌入向量 ?
图源来自Bright AI@Shivika K Bisen
首先是向量空间(Embedding Space),向量空间被广泛用于表示数据的特征,复杂的原始数据(如文本、图像、音频等)会被映射到一个低维或固定维度的向量空间中 ,可以更方便地进行计算和分析。上图中最右侧的三维空间就是一个向量空间。
在上图中,三维空间能够提供一个三维嵌入,每一个数据被嵌入模型转化为一个三维空间中的一个点的坐标,例如第一个是 [0.6,0.3,0.1]。对于一个 1024 维的嵌入向量(Embedding),其会被转换为类似 [0.1,0.2,0.3,…] 的一个 1024 维向量。
嵌入模型(Embedding Model)可以将文本、图像、音频等多模态数据转换为向量空间中的一个点 。训练好的嵌入模型理解各个数据之间的关联,也即相似的数据在向量空间中的距离更近,而不相似的数据距离更远 ,其能够捕捉数据之间的关联性和结构化信息。在实际应用中,我们经常接触的嵌入模型的维度通常是 1024 维,这个维度数量足够准确地表示一个数据的关联性。
二维向量空间中数据间的关联性 图源来自@Sebastian Raschka
多维向量空间中数据间的关联性 图源来自@Catcolia
在获得嵌入向量后,我们将嵌入向量存入向量数据库中,也即完成了复杂数据到嵌入向量作为索引存储的转换,以便后续数据的检索(召回)。
2. 查询语句嵌入
同样的,我们需要将用户输入,也即对查询语句进行嵌入(Embedding)以获得查询的嵌入向量 ,请看下图。
用户输入的语句是"北京的天气预报",我们需要通过嵌入模型将其转化为嵌入向量,该向量可视为对向量数据库中数据的索引 。在后续步骤中,利用该索引进行检索(召回) ,从而提取与用户输入语句相关的分块知识的索引(分块的嵌入向量)。
3. 数据检索与输入合成
嵌入模型的核心能力在于挖掘数据之间的关联性 。在之前的步骤中,知识文件已被转化为索引(即嵌入向量),并存储在向量数据库(或向量空间)中。同时,查询语句也被转化为嵌入向量。接下来,只需在向量数据库(或向量空间)中找出与查询语句关联性最强的数据 ,即可完成检索任务。
此外,还需要将用户原始输入与查询到的知识分块进行合成 ,最终将输入提供给 LLM 大语言模型。
首先,我们需要一种名为检索器(Retriever)的工具,它能够分析并找出两者之间的关联性 。常用的算法包括相似度计算(Similarity)和最大边际相关性(MMR,Maximal Marginal Relevance)等。简而言之,检索器通常会根据某种排序规则,筛选出与查询最相关的数据索引 。我们会在之后取出排名靠前的 k 组数据索引,也称为 top-k。
在通过检索器(Retriever)获取到 top-k 组数据后,这些数据仍然以嵌入向量(Embedding Vector)的形式存在,例如 [0.1, 0.2, 0.3, …] 这样的向量。因此,还需要将这些嵌入向量还原为原始的知识文档分块 。例如,在上图中仅提取了排名最靠前的一组数据,其嵌入向量被转换回原始的知识文档分块 ,即"北京天气 南风 多云 15℃"。
接下来,将得到的文档分块与用户输入进行合成,这涉及到一个领域「提示词工程」(Prompt Engineering)。
「提示词工程」一词听起来很荒谬,甚至有点搞笑,像是那种售卖 AI 课程的讲师口中会频繁提到的词汇。然而,实际上,提示词工程涵盖了诸如提升模型能力、优化输出质量以及增强模型安全性等多个方面。大语言模型依赖于提示词所提供的上下文信息 ,提示词越清晰、越具体,其越能准确理解你的意图。通过设计高效提示词,能够引导模型完成预期任务的方法。
详细的提示词工程请参考这篇指南,这篇可比哪些卖课的信息密度高多了
我们将只实现一个简单的模版,让模型依据内容回答问题。
1 2 3 仅根据提供的上下文回答以下问题。<context > {context}</context > 问题: {input}
这里的 {context} 和 {input} 是一种模版 ,请看上图中此处将由获取到的文档分块和用户输入替换。
替换好的内容如下
1 2 3 仅根据提供的上下文回答以下问题。<context > 北京天气 南风 多云 15℃</context > 问题: 北京的天气预报
4. 喂给大语言模型
是的,喂给 AI 就好了
完整的流程图如下
基于 Langchain 的实践
接下来,我们将使用 Langchain 复现上述方法,LangChain 是一个应用框架,旨在简化使用大型语言模型的应用程序。
你可以参考此处的中文教程了解更多 Langchain 有关内容,(不过广告有点多,有条件最好看官方英文版的)
我们将构建一个以网页数据为知识源的简易知识库,并结合 LLM 大语言模型。
考虑到部分学习者可能无法调用外网模型,这里 采用 deepseek-v3 模型和阿里系的 text-embedding-v3 嵌入模型 作为解决方案。
1. 环境准备
前往 deepseek 的官方平台充值并获取API秘钥,可以自定义仅选择充值1元。
在此页面创建API Key,你会得到一个以 sk- 开头的秘钥,请保留备用。
开通阿里云百炼平台,详细教程请参考此处:
同样的,你会得到一个以 sk- 开头的秘钥,请保留备用。
安装好python环境,此处忽略,然后使用pip安装下面的package。
1 pip install langchain langchain-community langchain-deepseek beautifulsoup4
请注意接下来的 Python 代码应合并为一个文件,或使用 Jupyter 进行单元运行。
2. 模型准备
1 2 3 4 5 6 7 8 9 10 from langchain_deepseek import ChatDeepSeek llm = ChatDeepSeek(model="deepseek-chat" , api_key="<你的Deepseek API Key>" )for chunk in llm.stream("你好" ): print (chunk.text(), end="" )
请在 api_key 中填入你的 deepseek 平台的秘钥。
你可以通过此代码测试 API 可用性,运行它将会流式输出大模型的回复。
1 2 3 4 5 6 7 8 9 10 11 12 13 from langchain_community.embeddings import DashScopeEmbeddingsfrom langchain_community.embeddings import OpenAIEmbeddings embeddings = DashScopeEmbeddings( model="text-embedding-v3" , dashscope_api_key="<你的阿里云百炼 API Key>" , ) text = "Hello world!" query_result = embeddings.embed_query(text)print (f"嵌入模型的嵌入向量维度是 {len (query_result)} " )print (f"嵌入结果 {query_result} " )
这里使用阿里云的 DashScope 平台,它和阿里云百炼是通用的,请在 dashscope_api_key 中填入阿里云百炼的秘钥 。
你可以通过此代码测试 API 可用性,不出意外的话,运行它将会获得和此处相同的嵌入向量。
3. 爬虫搭建
我们基于的数据爬取是基于 beautifulsoup4 的。不过,langchain 已经将 beautifulsoup 的方法包装好,我们只需使用 langchain 的 WebBaseLoader 方法即可。
下面的例子爬取了微信公众号上的新闻文章 ,请注意下方代码中添加 User-Agent 的位置 ,以防止环境异常被拦截。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import os os.environ['USER_AGENT' ] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' from langchain_community.document_loaders import WebBaseLoader urls = [ "https://mp.weixin.qq.com/s/o2yVa6_aea0I-E_Dz_rDZA" , "https://mp.weixin.qq.com/s/KoCfAbv2k82qKhgdyMM0xA" , "https://mp.weixin.qq.com/s/AbUnWSl7Y1-C8qGJC5QsaQ" , "https://mp.weixin.qq.com/s/bt76B8Pp9NkRF9f15SAy6g" , "https://mp.weixin.qq.com/s/da9JGOxejDL510sNfaVNsQ" , ] docs = []for url in urls: loader = WebBaseLoader(url, verify_ssl=True ) doc = loader.load() docs.append(doc) [print (doc) for doc in docs]
这里的 loader.load() 将会调用爬虫获取网页内容 ,调用此代码你将会得到类似下面的 Document 数据。
1 2 3 4 5 [Document(metadata={'source': 'https://mp.weixin.qq.com/s/o2yVa6_aea0I-E_Dz_rDZA', 'title': '', 'description': '缅甸强震已致该国2056人遇难'... [Document(metadata={'source': 'https://mp.weixin.qq.com/s/KoCfAbv2k82qKhgdyMM0xA', 'title': '', 'description': '没有一片雪花是无辜的'... [Document(metadata={'source': 'https://mp.weixin.qq.com/s/AbUnWSl7Y1-C8qGJC5QsaQ', 'title': '', 'description': '最苦的还是老百姓。'... [Document(metadata={'source': 'https://mp.weixin.qq.com/s/bt76B8Pp9NkRF9f15SAy6g', 'title': '', 'description': '一天一个破纪录的天价融资,“天使轮”不存在了'... [Document(metadata={'source': 'https://mp.weixin.qq.com/s/da9JGOxejDL510sNfaVNsQ', 'title': '', 'description': '虎嗅独家获悉,阿里即将在4月第二周发布新模型Qwen3'...
4. 分块与向量数据库
我们将使用 FAISS 作为向量数据库。
还记得我们之前的流程吗?在入库前需要对 Document 进行分块 ,这里使用最简易的文本递归分割器 RecursiveCharacterTextSplitter,其继承自基类 TextSplitter,你可以通过调整参数 chunk_size 来调整每个分块的大小 。例如,下面设置的分块大小是 2000 字符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 documents = []for doc in docs: documents.extend(doc)from langchain_community.vectorstores import FAISSfrom langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter(chunk_size=2000 ) documents = text_splitter.split_documents(documents)print (f"Document总分块数量: {len (documents)} " ) vectorDB = FAISS.from_documents(documents, embeddings)
5. 构建模型输入模版与检索器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from langchain_core.prompts import ChatPromptTemplate prompt = ChatPromptTemplate.from_template(""" Answer the following question based only on the provided context. <context> Today is 2025.04.01. {context} </context> Question: {input} """ )from langchain.chains.combine_documents import create_stuff_documents_chain document_chain = create_stuff_documents_chain(llm, prompt) retriever = vectorDB.as_retriever(search_kwargs={"k" : 10 }) retrieval_chain = create_retrieval_chain(retriever, document_chain)
6. 模型输出
1 2 3 4 5 6 7 8 for response in retrieval_chain.stream( {"input" : "给我总结下最近的国际新闻吧" }, ): if "answer" in response: print (response["answer" ], end="" ) else : print (response)
最终的模型输出如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 {'input': '给我总结下最近的国际新闻吧'} {'context': [Document(id='a26395f0-3d0a-47ef-a1fb-7f7cdaa1f589', metadata={'source': 'https://mp.weixin.qq.com/s/o2yVa6_aea0I-E_Dz_rDZA', 'title': '', 'description': '缅甸强震已致该国2056人遇难', 'language': 'No language found.'}, page_content='大家早上好!这里是今天的早报,每天早上,我都会在这里跟你聊聊昨夜今晨发生了哪些大事儿。本栏目由虎嗅出品。热点追踪【缅甸强震已致该国2056人遇难】据央视新闻,根据缅甸国家管理委员会新闻信息组公布的最新统计数据,截至当地时间3月31日12时,缅甸全国因28日强烈地震造成的遇难人数达到2056人,另有3900人受伤,270人失踪,目前救援行动正在展开。【外媒:马斯克称“火星也将归美国”】...... 最近的国际新闻主要包括以下重要事件: 1. **缅甸强震灾难** - 缅甸于3月28日发生强烈地震,截至3月31日已造成2056人遇难、3900人受伤、270人失踪。震级达7.7-7.9级,震源深度浅(10-30公里),破坏力极强,导致大面积房屋倒塌、基础设施损毁,救援面临高温和战乱地区协调困难等挑战。 2. **马斯克争议言论** - 马斯克在3月30日的集会上宣称“火星也将归美国”,并强调对美国忠诚,引发国际关注。他此前还透露SpaceX计划2026年用“星舰”送机器人登陆火星,载人任务或于2029-2031年实施。 3. **伊朗扣押油轮事件** - 伊朗伊斯兰革命卫队于3月31日扣押两艘涉嫌走私燃油的外国油轮,指控其在波斯湾走私超300万升柴油,25名船员被拘。 4. **俄乌冲突动态** - 特朗普称对俄罗斯同意俄乌停火设“心理最后期限”,暗示若俄方拖延将采取行动,但未透露具体细节。 5. **白宫应对泄密事件** - 白宫于3月31日宣布已采取措施防止类似“信号软件群聊泄密”事件重演,此前美高官通过加密软件讨论军事计划的信息遭泄露。 6. **国际科技与环保** - 苹果与马斯克的SpaceX因卫星通信计划冲突,双方在消除手机信号盲区的技术上存在竞争。 - 缅甸地震被世卫列为最高级别(三级)紧急事件,多国展开援助,中国救援队已赴现场。 这些事件涉及自然灾害、地缘政治、科技竞争等多领域,凸显当前国际局势的复杂性与紧迫性。
再让它总结国内的新闻
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 {'input': '给我总结下最近的中国国内新闻吧'} {'context': [Document(id='2e23d5cc-35a0-4fb6-8d23-6cf866ad4ce3', metadata={'source': 'https://mp.weixin.qq.com/s/o2yVa6_aea0I-E_Dz_rDZA', 'title': '', 'description': '缅甸强震已致该国2056人遇难', 'language': 'No language found.'}, page_content='早报 | 国家标准要求4层起设电梯;钉钉创始人陈航将回归掌舵;比亚迪辟谣在印度投资建厂;马斯克称火星也归美国\n...... 根据提供的2025年4月1日新闻内容,以下是近期中国国内热点新闻的总结: ### **科技与商业动态** 1. **钉钉高层变动** - 钉钉创始人陈航(无招)将回归阿里集团出任钉钉CEO,其创立的跨境出海公司“两氢一氧”拟被阿里收购投资人股份。 2. **抖音反舞弊通报** - 抖音集团2024年共移送39名涉嫌违法犯罪的员工至司法机关,23家合作方因行贿被列入永不合作清单。 3. **苹果AI功能扩展** - Apple Intelligence全球开放,新增支持简体中文等语言,覆盖文本创作、图像生成等功能,并首次登陆Vision Pro头显。 4. **小米汽车信任危机** - 小米SU7在安徽高速智驾事故致3人死亡,引发对智驾安全性的质疑,事故原因仍在调查中。 5. **比亚迪辟谣印度建厂** - 比亚迪否认在印度投资建厂的传闻,称相关信息不实。 ### **社会与政策** 1. **电梯新国标** - 国家标准要求4层及以上住宅需设置电梯,同时提高卧室隔音标准至65分贝以下。 2. **12306回应不文明行为** - 针对高铁或飞机上乘客光脚踩座椅等问题,12306建议向工作人员求助劝阻或换座。 3. **湖南耒水铊污染处置** - 耒水流域铊浓度异常事件已解决,水质恢复正常,供水安全达标。 ### **其他热点** - **人形机器人投资争议** - 朱啸虎称退出人形机器人投资引行业辩论,新兴公司如星海图、松延动力回应称行业需包容创新。 - **阿里模型研发** - 阿里计划4月发布新模型Qwen3,重点提升推理能力,并考核开发者社区影响力(衍生模型超10万、下载量破2亿)。 ### **总结** 近期国内焦点集中在企业动态(钉钉、小米、抖音)、政策调整(电梯新规)及环境安全(铊污染)。同时,AI与机器人领域的技术竞争和投资争议持续发酵。
总结
没错,这里是运用上述代码让大模型帮我总结的。
本文详细介绍了如何利用 LangChain 框架构建基于大语言模型(LLM)的检索增强生成(RAG)知识库。通过代码实例与原理解析,系统性地演示了 RAG 如何缓解大模型的幻觉问题,提升特定领域回答的准确性。其核心价值在于通过外部知识增强与大模型的协同,优化信息检索与生成效率,为实际应用(如新闻分析、专业咨询)提供了可复用的技术方案。