还在为RAG概念发愁?别再瞎找了!用LangChain从0到1构建,一篇打通你的任督二脉!
最近在准备一个算法比赛,里面用到了RAG系统,于是就想写一遍博文以此回顾总结一下。
最近在准备一个算法比赛,里面用到了RAG系统,于是就想写一遍博文以此回顾总结一下。
一、RAG的概念
1.RAG技术概述
RAG 的核心思想可概括为:在回答用户问题前,先检索外部知识库中的相关信息,再结合检索结果生成答案,从而提升模型响应的准确性与可靠性。
RAG 主要包括两个阶段:
- 检索:从大规模文档系统中查找与问题相关的文档片段;
- 生成:模型基于检索到的信息生成对应的回答。
最简单的RAG流程如下图所述:

所以,我们再精简一下,RAG 就是在模型生成答案之前,先进行一次“查资料”的过程。
思想是非常简单的,但是效果却是非常好的。
一旦发现我们的思想非常复杂,一味的堆砌工作量,往往会适得其反。
就像大模型一样,一个next token就完成了AI时代的革新。
2.为什么需要RAG
要弄明白RAG的意义,我们就得需要对比同类型技术的优缺点。
RAG的目的是让大模型对用户的回答更加精准(或者说是解决大模型幻觉问题)。
那么同样的目的,我们也可以采用大模型微调的方式进行。
大模型微调通过使用新数据训练模型,扩展其知识边界与应答能力。然而,微调方法存在以下局限:
- 需要大量高质量标注数据;
- 消耗大量计算资源;
- 训练周期长,时间成本高。
在很多实际应用场景中,上述条件难以同时满足。相比之下,RAG 提供了一种更灵活、成本更低的增强大模型能力的方式
二、RAG的构造流程
采用LangChain构建RAG系统,可将整体流程分为以下几个核心步骤:文档加载、文档分割、文档嵌入、根据问题检索答案(检索与生成)。每个步骤都有其特定的功能和作用,共同构成完整的RAG流水线。
1.文档加载
文档加载是RAG流程的初始阶段,主要负责从各类外部数据源中提取文本内容。在实际应用中,数据可能以多种格式存在,如PDF文件、JSON数据、网页内容、Word文档等。
LangChain中针对不同类型的数据格式都提供了相应的处理逻辑,我们只需要根据相应的API进行处理即可。
例如:
"""1.文档加载"""# 导入对应的包from langchain_community.document_loaders import TextLoader# 加载文件text_loader = TextLoader("./西游记.txt", encoding="gb18030")# 获取文档docs = text_loader.load()# 查看文档print(docs)
LangChain 生态系统实现了与数百个常见源集成的文档加载器。这使得将这些源的数据整合到我们的 AI 应用中变得容易。
对应的常见的文档加载API:
from langchain_community.document_loaders import # PDF文档 PyPDFLoader, UnstructuredPDFLoader, # Word文档 Docx2txtLoader, UnstructuredWordDocumentLoader, # 网页内容 UnstructuredURLLoader, WebBaseLoader, # JSON数据 JSONLoader, # CSV文件 CSVLoader
文档加载中有两个重要的属性,一个是page_count,另一个是metadata。
page_content 是正文(我们要向量化、检索的文本)
- LangChain 的
Document结构中,真正会被送去 Embedding → 向量库 → 相似度检索 的就是page_content。 - 必须是字符串。
- 推荐把最核心、需要被语义理解的自然语言放进
page_content。
metadata 是附加信息(不参与向量计算)
metadata是字典,用于存储各种结构化数据。- metadata 不参与检索向量计算,但在检索完成后可以用于过滤或展示。
2.文档分割
文档分割是RAG流程中的关键步骤,主要负责将加载的原始文档切分为适合处理的文本块。这一环节对后续的嵌入质量和检索效果具有重要影响。
为什么需要文本分割?
- 计算效率:直接处理大型文档会消耗大量计算资源,分割后的小文本块更易于模型处理。
- 检索精度:用户问题通常针对文档中的特定信息,精细分割能提高关键内容的匹配概率。
- 上下文管理:控制输入上下文的长度,确保生成阶段能有效利用相关信息。
- 语义完整性:合理的分割能保持语义单元的完整性,避免信息割裂。
文档分割的典型方式:
- 固定大小分块:按预设的token数量或字符长度进行均匀切分。
- 基于意图的分块:利用自然语言处理工具(如NLTK、spaCy)识别句子边界进行分割。
- 分隔符分块:基于句号、换行符等显式分隔符进行文本切分。
- 递归分块:通过分层迭代使用不同分隔符,逐步将文本分割为更小的管理单元,核心就是如果依次切分没有达到预期的块大小或结构,会递归地使用不同的分隔符或判定标准对结果进行再次切分。
- 特殊格式分块:针对Markdown、LaTeX等结构化文档,按其特定语法进行智能分割。
- 语义分块:基于嵌入相似性进行分割,确保每个块内的内容语义相关。
文本分割的主要功能包括:
- 句子拆分:将文本划分为独立的句子,适用于句子级别的语义分析任务。
- 段落拆分:基于段落结构进行分割,保留文本的逻辑组织方式。
- 分页处理:针对长文档按页面或章节进行分割,维持文档的原始结构。
- 语义分块:根据语义连贯性进行分割,确保每个文本块具有完整的语义表达。
例如:
"""2.文档分割"""# 导入对应的包from langchain_text_splitters import RecursiveCharacterTextSplitter# 创建文档分割器text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=20)# 分割文档docs = text_splitter.split_documents(docs)# 查看文档print(docs)
常见的文本分割API
# 导入对应的包from langchain_text_splitters import ( RecursiveCharacterTextSplitter, # 递归字符文本分割器 CharacterTextSplitter, # 字符文本分割器 TokenTextSplitter, # Token文本分割器 MarkdownTextSplitter, # Markdown文本分割器 PythonCodeTextSplitter, # Python代码分割器 SpacyTextSplitter, # 基于spaCy的分割器 NLTKTextSplitter, # 基于NLTK的分割器 SentenceTransformersTokenTextSplitter, # 基于SentenceTransformer的分割器)
3.文档嵌入
计算机无法直接理解自然语言,因此需要将文本转换为数值向量(即嵌入表示),从而在向量空间中进行数学计算。通过计算向量之间的余弦相似度(当然也可以是其它相似度衡量标准),可以衡量文本之间的语义相似性。
例如,针对给定的向量表示进行计算:
- “机器学习”向量:A = [1, 1, 2, 3]
- “联邦学习”向量:B = [5, 6, 2, 3]
- “人工智能”向量:C = [7, 9, 8, 8]
余弦相似度计算
余弦相似度公式为:

因此:
-
机器学习与联邦学习之间的余弦相似度=0.720
-
机器学习与人工智能之间的余弦相似度=0.900
目前国内外用的比较多的嵌入模型如下(让DeepSeek帮忙总结即可):

例如:
"""3.文档嵌入"""# 导入对应的包from langchain_huggingface import HuggingFaceEmbeddings# 创建嵌入模型embeddings_model = HuggingFaceEmbeddings( model_name='./resources/BAAI/bge-m3', model_kwargs={'device': 'cuda'}, encode_kwargs={'normalize_embeddings': True})# 从Document对象中提取文本内容texts = [doc.page_content for doc in split_docs]# 获取嵌入向量 - 传入文本列表而不是Document对象embeddings = embeddings_model.embed_documents(texts)# 打印嵌入向量信息print(f"生成的嵌入向量数量: {len(embeddings)}")print(f"每个向量的维度: {len(embeddings[0]) if embeddings else 0}")print("前5个向量的前10个维度示例:")for i in range(min(5, len(embeddings))): print(f"向量{i+1}: {embeddings[i][:10]}...")
向量存储
向量存储的核心思想是将非结构化数据(如文本、图像)通过嵌入模型转换为高维向量表示,并在向量空间中建立索引结构。这种转换使得原本难以直接计算的语义相似性可以通过向量间的距离度量(如余弦相似度、欧氏距离)来量化。
当然也可以使用Doris/StarRocks进行向量存储,这部分笔者还没探讨实际效果,感兴趣的可以尝试一下。目前用得比较多的应该是FAISS、Milvus、Chroma等向量数据库。
"""4.向量存储"""# 导入对应的包from langchain_community.vectorstores import FAISS# 创建向量存储vector_store = FAISS.from_texts(texts, embeddings_model)vector_store.save_local("./faiss_index")print(f"向量存储已保存到: ./faiss_index")
4.检索器
检索器的本质是在海量文档中快速定位最相关的信息片段。其核心挑战在于平衡召回率(找到所有相关文档)与精确率(返回的文档确实相关)之间的关系。
常见的检索器类型如下:
1.MultiQueryRetriever,通过LLM生成原始查询的多个变体,从不同角度理解用户意图。
2.ContextualCompressionRetriever,两阶段检索,先召回候选文档,再用LLM过滤无关内容。
3.EnsembleRetriever,结合稠密检索(向量相似度)和稀疏检索(关键词匹配)。
4.MultiVectorRetriever,为同一文档生成多个向量表示(摘要、关键句、全文等)。
例如:
"""5.检索器"""# 导入对应的包from langchain_community.vectorstores import FAISS# 加载向量存储 - 添加安全确认参数vector_store = FAISS.load_local( "./faiss_index", embeddings_model, allow_dangerous_deserialization=True # 确认信任本地文件)# 创建检索器retriever = vector_store.as_retriever(search_kwargs={"k": 3})# 测试检索query = "翠藓堆蓝,白云浮玉,光摇片片烟霞"results = retriever.invoke(query)# 打印检索结果print(f"查询: {query}")print(f"检索到 {len(results)} 个相关文档:")for i, doc in enumerate(results): print(f"\n--- 文档 {i+1} ---") print(f"内容: {doc.page_content[:200]}...") # 只显示前200个字符 print(f"元数据: {doc.metadata}")
返回结果如下:

5.生成
生成是RAG流程的最终阶段,也是将前面所有步骤成果转化为用户可理解答案的关键环节。这一步骤的核心在于将检索到的相关信息与用户原始问题进行智能整合,生成准确、连贯且有价值的回答。
例如(上文所有代码总和/最终代码):
"""1.文档加载"""import osimport dotenvdotenv.load_dotenv()# 导入对应的包from langchain_community.document_loaders import TextLoader# 加载文件text_loader = TextLoader("./西游记.txt", encoding="gb18030")# 获取文档docs = text_loader.load()# 查看文档# print(docs)"""2.文档分割"""# 导入对应的包from langchain_text_splitters import RecursiveCharacterTextSplitter# 创建文档分割器text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=20)# 分割文档split_docs = text_splitter.split_documents(docs)# 查看分割后的文档print(f"分割后文档数量: {len(split_docs)}")"""3.文档嵌入"""# 导入对应的包from langchain_huggingface import HuggingFaceEmbeddings# 创建嵌入模型embeddings_model = HuggingFaceEmbeddings( model_name='/new_home/fangpengNB/fangpeng/TecentGames/PythonProject/multi_agent_deepcode/resources/BAAI/bge-m3', model_kwargs={'device': 'cuda'}, encode_kwargs={'normalize_embeddings': True})# 从Document对象中提取文本内容texts = [doc.page_content for doc in split_docs]# 获取嵌入向量 - 传入文本列表而不是Document对象embeddings = embeddings_model.embed_documents(texts)# 打印嵌入向量信息print(f"生成的嵌入向量数量: {len(embeddings)}")print(f"每个向量的维度: {len(embeddings[0]) if embeddings else 0}")"""4.向量存储"""# 导入对应的包from langchain_community.vectorstores import FAISS# 创建向量存储vector_store = FAISS.from_texts(texts, embeddings_model)vector_store.save_local("./faiss_index")print(f"向量存储已保存到: ./faiss_index")"""5.RAG完整调用 - 检索 + 生成"""# 导入必要的包from langchain_community.vectorstores import FAISSfrom langchain_classic.chains.retrieval_qa.base import RetrievalQAfrom langchain_core.prompts import PromptTemplatefrom langchain_community.llms import Ollama # 使用Ollama本地模型# 或者使用OpenAI: from langchain_openai import ChatOpenAI# 加载向量存储vector_store = FAISS.load_local( "./faiss_index", embeddings_model, allow_dangerous_deserialization=True)# 创建检索器retriever = vector_store.as_retriever( search_kwargs={"k": 5} # 检索5个最相关的文档)# 创建LLM模型(这里使用Ollama,你可以根据实际情况选择)# 如果你使用OpenAI,取消下面的注释并设置API密钥from langchain_openai import ChatOpenAIllm = ChatOpenAI( model=os.getenv("DEEP_SEEK_REASONER_MODEL"), openai_api_key=os.getenv("DEEP_SEEK_API_KEY"), openai_api_base=os.getenv("DEEP_SEEK_API_BASE"), timeout=180 )# 创建自定义提示模板prompt_template = """基于以下提供的上下文信息,请回答用户的问题。如果上下文中没有足够的信息来回答问题,请如实告知。上下文:{context}问题: {question}请根据上下文提供准确、详细的回答:"""PROMPT = PromptTemplate( template=prompt_template, input_variables=["context", "question"])# 创建检索QA链qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", # 可以使用 "map_reduce", "refine" 等其他类型 retriever=retriever, chain_type_kwargs={"prompt": PROMPT}, return_source_documents=True # 返回源文档用于验证)# 测试多个问题test_questions = [ "翠藓堆蓝,白云浮玉,光摇片片烟霞。这句话描述的是什么地方?", "孙悟空是如何从石头里出生的?", "唐僧为什么要去西天取经?", "请描述一下猪八戒的外貌特征", "金箍棒有什么特殊能力?"]for question in test_questions: print(f"\n{'='*60}") print(f"问题: {question}") print(f"{'='*60}") # 执行RAG查询 result = qa_chain.invoke({"query": question}) # 打印答案 print(f"答案: {result['result']}") # 打印参考的源文档(前2个) print(f"\n参考文档:") for i, doc in enumerate(result['source_documents'][:2]): print(f" {i+1}. {doc.page_content[:150]}...") print(f" 元数据: {doc.metadata}") print(f"{'='*60}")
输出结果:


三、总结
RAG的流程方案:加载文档、文档分割、文档嵌入、根据问题检索答案。
而为了保证RAG的准确性,我们可以做的工作非常多,例如多文档的联合检索、嵌入模型的选择、文档检索的相似性调优等等。
我们可以借助DeepSeek,#文心一言,#百度 等一步一步完善上述流程,但是核心一定是,我们得弄清楚为什么这么干?这么干的好处是什么?
知其然,一定得知其所以然。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)