使用LangChain实现RAG流程:从数据读取到效果评估的全流程解析

发布时间:2026/11/6 4:54:22

使用LangChain实现RAG流程:从数据读取到效果评估的全流程解析 引言随着自然语言处理NLP和大模型技术的发展构建基于大模型的知识库检索增强生成RAG, Retrieval-Augmented Generation系统成为许多项目的重要组成部分。本文将详细介绍我如何结合LangChain实现一个完整的RAG流程涵盖从数据读取到效果评估的具体步骤并分享一些遇到的问题及解决方案。1. LangChain与RAG简介为什么使用外挂知识库尽管大模型在自然语言处理任务中表现出色但它们仍然存在一定的局限性如对最新信息的获取不足、特定领域知识的欠缺等。为了弥补这些不足我们采用了RAG技术通过给大模型外挂知识库提升其回答问题的准确性和时效性。LangChain的作用LangChain是一个强大的工具集提供了丰富的函数和模块帮助我们轻松实现RAG流程中的各个环节。它不仅简化了开发过程还提高了系统的灵活性和可扩展性。本文将详细介绍如何利用LangChain实现一个完整的RAG系统。上图展示了一个复杂的语言处理系统其中包含模型、提示、链、代理和嵌入与向量存储。Models 负责理解和生成语言提示用于引导模型输出Chains 代表将多个步骤串联起来完成复杂任务的过程Agents 则用于让模型与外部环境互动比如执行API调用。Embedding 嵌入与向量存储 VectorStore 是数据表示和检索的手段为模型提供必要的语言理解基础。2. RAG开发流程第一步数据读取首先我们需要读取本地的数据文件。LangChain提供了TextLoader函数可以方便地读取txt文件。除此之外我还实现了对PDF和Word文件的支持PDF文件读取使用PyPDF2时会将普通文字和表格中的文字一起读出来无法区分开。为了解决这个问题我采用了fitz库。fitz库能够分块读取PDF内容每一块都是一个元组其中第5个位置如果是0则代表普通文本如果不是0则代表表格中的数据。利用这个特性我通过if-else判断提取出所需的文本剔除表格数据。Word文件读取使用python-docx库可以方便地提取Word文档中的文本和表格。from langchain.document_loaders import TextLoader import fitz from docx import Document def load_pdf(file_path): doc fitz.open(file_path) text for page_num in range(len(doc)): page doc.load_page(page_num) blocks page.get_text(blocks) for block in blocks: if block[4] 0: # 普通文本 text block[4] return text def load_word(file_path): doc Document(file_path) text \n.join([para.text for para in doc.paragraphs]) return text背景信息补充不同文件格式的读取方法各有优劣。例如PDF文件通常包含丰富的排版信息而Word文件则可能包含复杂的表格和图表。选择合适的库和方法可以确保数据的完整性和准确性。第二步数据清洗和预处理数据清洗主要包括四个部分的工作指代消解将原文中的代词替换成真实值。例如将“他”替换为具体的对象名称。这一步可以通过大模型提示词来实现def resolve_coreferences(text, llm): prompt f请将文本块中的代词替换成真实值{text} response llm(prompt) return response名称替换将知识库中的不同名称用统一的名称进行替换。例如“大模型”、“llm”、“大语言模型”统一替换为“大模型”。剔除杂乱数据去除特殊符号和没有意义的信息。标题匹配对于层级较多的文本给每个切分后的段落匹配上对应的一级和二级标题。def clean_and_preprocess(text, llm): text resolve_coreferences(text, llm) text replace_names(text) text remove_noise(text) text match_titles(text) return text背景信息补充数据清洗是确保后续处理质量的关键步骤。不干净的数据可能导致错误的分析结果或低效的模型训练。通过细致的数据清洗我们可以显著提高系统的性能和可靠性。第三步文本切分处理好的数据需要转换成Document类型然后送入TextSplitter进行文本切分。我采取了两种切分方式固定字数切分设置一定的重叠字数缓解切分带来的语义破坏。递归切分先按段落切如果段落太大再按句子切最后按标点符号或字数切。from langchain.text_splitter import TextSplitter, ChineseRecursiveTextSplitter def split_text(text, methodfixed): if method fixed: splitter TextSplitter(chunk_size100, chunk_overlap20) elif method recursive: splitter ChineseRecursiveTextSplitter() else: raise ValueError(Invalid splitting method) documents splitter.split_documents(text) return documents背景信息补充文本切分的方式直接影响到后续向量化的质量和检索效率。合理选择切分策略可以在保持语义完整性的同时最大化检索的准确性。第四步文本转向量与存储文本切分完成后需要将文本块转成向量并存储。我选择了m3e模型进行文本转向量并使用faiss作为向量库。具体做法如下下载并部署m3e模型根据GitHub上的README安装依赖并测试模型。向量存储与检索优化检测存储结果避免重复导入数据提升问答速度。设置聚类数量以加速检索。调用faiss时需加入allow_dangerous_deserializationTrue参数。from sentence_transformers import SentenceTransformer import faiss def vectorize_and_store(text_chunks): model SentenceTransformer(m3e-base) vectors model.encode(text_chunks) index faiss.IndexFlatL2(vectors.shape[1]) index.add(vectors) return index背景信息补充向量化和存储是RAG系统的核心环节之一。高效的向量库可以显著提升检索速度和精度从而改善用户体验。第五步多路召回策略我采用了三种召回策略正常分割 m3e转向量每段添加主题词和上下文摘要选出10块。正常分割 tf-idf转向量同样添加主题词和上下文摘要选出10块。子块召回父块先将文档分成大块再细分成小块标记清楚每个小块属于哪个大块。用户提问时先匹配小块返回对应的大块以包含更多上下文信息。def multi_recall(query, chunks): results [] results.extend(normal_recall(query, chunks, m3e)) results.extend(normal_recall(query, chunks, tf-idf)) results.extend(parent_chunk_recall(query, chunks)) return results背景信息补充多路召回策略可以提高检索结果的多样性和准确性。不同的召回方法各有优势综合使用可以更好地覆盖用户的查询需求。第六步精排策略多路召回后对几十个文本块进行进一步筛选选出最相关的5个文本块。具体方法是采用余弦相似性和BM25模型进行重排。from sklearn.metrics.pairwise import cosine_similarity from rank_bm25 import BM25Okapi def rerank(results, query): cos_sim cosine_similarity(results, [query]) bm25_scores BM25Okapi(results).get_scores(query) combined_scores (cos_sim bm25_scores) / 2 top_5 sorted(combined_scores, reverseTrue)[:5] return top_5背景信息补充精排策略确保最终呈现给用户的结果是最相关和最有用的。合理的排序算法可以显著提升用户的满意度。第七步Query转写针对用户提问不明确或信息不全的情况提出了多种Query转写方案换表达方式保持语义不变的情况下换一种表达方式。关键词匹配提取关键实体词与召回结果进行匹配。结合上下文改写例如将“Langchain 如何工作”转写为“Langchain 的工作流程有几个步骤”。任务拆分将复合问题拆分为多个简单问题分别检索。宽泛问题将具体问题改为宽泛问题如“Langchain到底是什么”。大模型自答让大模型先回答问题再将答案连同原问题一起送入RAG检索。def rewrite_query(query, llm): rewritten_queries [ llm(f请换一种表达方式{query}), extract_keywords(query), combine_context(query), split_tasks(query), broaden_question(query), llm_answer(query) ] return rewritten_queries背景信息补充Query转写可以帮助系统更好地理解用户的意图从而提高检索的准确性和覆盖面。灵活的转写策略可以应对各种复杂情况。第八步提示词模板设计提示词模板的设计思想是告诉大模型严格按照检索文本块中的内容进行回答不允许大模型自己编造答案。这样可以保证问答结果的可控性和准确性。def create_prompt(context, query): return f根据以下内容回答问题\n{context}\n\n问题{query}\n背景信息补充良好的提示词设计可以引导大模型生成更符合预期的回答减少不必要的误差和误解。第九步Web界面展示最后我使用Gradio创建了一个简单的Web页面方便展示和测试。import gradio as gr def demo_interface(): interface gr.Interface(fnrun_rag_pipeline, inputstext, outputstext) interface.launch()背景信息补充用户友好的界面可以极大地提升用户体验使系统更易于操作和展示。第十步效果评估主要用两种方法进行评估技术指标评估使用ROUGE和BLEU指标。人工盲测设置一百个问题由人生成相关答案再让机器生成答案。找第三方评估两个答案哪个更好统计机器比人好、人比机器好、两者相等的比例。最终结果显示76%的结果机器和人一样好14%人比机器好10%机器比人好。86%的结果表明机器至少接近人类水平满足业务需求。背景信息补充评估是确保系统性能和效果的关键步骤。通过多种评估方法可以全面了解系统的优点和不足为进一步优化提供依据。结语通过以上步骤我成功构建了一个基于LangChain的RAG系统不仅提升了大模型的回答准确性还增强了系统的灵活性和可控性。希望这篇文章能为你提供一些有价值的参考和启发。参考资料LangChain DocumentationM3E Model on GitHubFAISS DocumentationSentence TransformersGradio Documentation
延伸阅读

更多相关文章

2026/11/6 4:54:22

慈様や 前端学习导航‍

慈様や 前端学习导航👩🏻‍🚀🚀 ✍忙里偷闲,来写一下Blog导航—— 一直都想做这个,但因为前端的文章还没写多少,不够撑起来…就一直没写, 加上前两天天天加班、也没有&#x1f55…

2026/11/6 4:54:22

uni-app 定位 SDK:维智全方位赋能开发者的位置服务

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/6 4:49:21

MyBatis(随笔2 : Sql映射文件 )

Sql文件映射 这里只是笔记,属性详情,关键还是要多练 ——实例代码 MyBatisDemo MyBatis 真正的强大在于映射语句,专注于SQL,功能强大,SQL映射的配置却是相当简单 SQL映射文件的几个顶级元素 name详情mappernamespace: 命名空间cache配置给…

2026/11/6 6:09:25

GitHub热榜精选:五款易上手的效率工具与实操避坑指南

每周翻 GitHub Trending,差不多已经成了我的固定动作。9月16日这一期的榜单给我的感觉特别明显:AI 应用还在霸榜,但真正涨得猛的,反而是一批“看起来不性感”的本地效率工具——生活管理、画质替换、内存清理、论文转代码&#xf…

2026/11/6 6:09:25

FX系列PLC编程口通信协议解析与Qt串口助手实战

简介:本资源是面向工业自动化初学者与QT嵌入式开发者的三菱FX3U系列PLC编程口通信实践项目,聚焦上位机与PLC的串行通信实现,解决QT环境下FINS协议解析、串口参数配置、寄存器读写及基础错误处理等核心问题。压缩包共7个文件,含2个…

2026/11/6 6:09:25

Hadoop序列化机制与Writable接口深度解析

1. Hadoop序列化机制深度解析在大数据分布式计算框架中,序列化机制是影响系统性能的关键因素之一。Hadoop作为经典的大数据处理框架,其独特的序列化设计与Java原生序列化有着本质区别。理解这套机制的工作原理,对于优化MapReduce作业性能、减…

2026/11/6 6:09:25

从代码补全到工程化协作:AI编程代理趋势解析

最近我养成了一个习惯:每天睡前刷一遍 GitHub Trending,看看又有什么项目在涨。坦白说,AI 编程代理(coding agent)类项目已经不是第一次占据榜单高位,但这一次和之前不一样,热度不是冲高回落&am…

2026/11/6 6:09:25

类与对象:一张蓝图和它造出来的产品

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/6 6:04:24

Checklist设计编写规范及模板:条目规则、试跑与落地治理

清单这东西,几乎人人都写过,但真正写到“能被人照着执行、执行完真能挡住问题”的,比例低得吓人。我见过太多这样的文件:二十页的文档,通篇写着“注意”“确保”“严格检查”,执行的人扫一眼、签个字、归档…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/6 1:03:39

MCU晶振电路设计全解析:从负载电容计算到PCB布局调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑