行业资讯
Langchain核心组件---文档加载器
Langchain一、Langchain核心组件Components1. 文档加载器Document loaders1.1 RAG 介绍1.1.1 RAG 概念1.1.2 RAG 流程1.1.3 RAG 示例1.2 Document 文档类1.3 加载 PDF 文档1.4 加载 Markdown 文件一、Langchain核心组件Components1. 文档加载器Document loaders1.1 RAG 介绍1.1.1 RAG 概念从本小节开始我们将重点放在 RAG 阶段Retrieval-Augmented Generation检索增强生成。这是当前大语言模型应用的核心模式。RAG 的流程相对复杂为了更好的理解 RAG我们先用 AI 搜索来引出 RAG。对于【AI 大模型】来说它最擅长的是语义理解和文本总结最不擅长的就是获取实时的信息。因为大模型的训练数据是有截止日期的对于【搜索引擎】来说它最擅长的就是获取实时的信息缺点是信息分散每次都需要人为进行总结。大模型与搜索引擎的结合就是给 AI 配备了一个活字典让 AI 可以随时进行查阅。下图展示了一个最简单的 AI 搜索工作流程搜索引擎在这里充当知识库结合我们的查询语句大模型便可以从知识库中获取相应的查询结果有了以上流程的铺垫接下来正式进入 RAG 的学习。首先先来思考一个问题搜索引擎可以帮我们解决实时数据的获取但获取到的数据也是受限的。它只能获取到公开在网络中的数据而无法获取到一些本地数据或企业内部的私有数据等此时该如何答案是使用 RAG检索增强生成技术当用户向 LLM 提问时系统首先在知识库如公司内部文档中进行语义搜索找到最相关的内容然后将这些内容和问题一起交给 LLM 来生成答案。与 AI 搜索类比本质是知识库改变了从搜索引擎线上搜索改为了本地或私有知识库中搜索。1.1.2 RAG 流程RAG 的流程分为【离线数据处理】和【在线检索】两个过程。上面提到RAG 知识库可以是本地文档、公司内部文档等一些私有化数据。但这些私有数据或文档实际上并不能很好地被直接进行检索访问。因此需要将这些私有化数据构建成可以被检索的知识库这就是离线数据处理要干的事情。经过离线数据后知识则会按照某种格式以及排列方式存储在知识库中等待被使用。而在线检索则是我们依赖知识库查询通过大模型生成结果的过程。过程如下图所示这张图将会是我们后续要学习 LangChain 组件知识地图所有的组件都会一 进行讲解现在我们只需掌握其流程接触相关概念即可。文档加载 (Document Loading)加载多种不同来源加载文档。LangChain 提供了 100 多种不同的文档加载器包括 PDF 在内的非结构化的数据、SQL 在内的结构化的数据以及 Python、Java之类的代码等。文本分割 (Splitting)文本分割器把 Documents 切分为指定大小的块。存储 (Storage)存储涉及到两个环节分别是将切分好的文档块进行嵌入Embedding即将文档块转换成向量的形式。将 Embedding 后的向量数据存储到向量数据库中。检索 (Retrieval)数据存入向量数据库后。当我们需要进行数据检索时会通过某种检索算法找到与输入问题相似的文档块。输出 (Output)把问题以及检索出来的文档块一起提交给 LLMLLM 会通过问题和检索出来的提示一起来生成更加合理的答案。我们现在已经知道了 RAG 的完整流程但也仅是知道 RAG 是什么至于流程中为什么要进行文档加载、文本分割、存储我们还无从了解。因此后续的各个 LangChain 组件讲解时都会涉及每个步骤被设计出来的原因。1.1.3 RAG 示例讲这么多不如一见。接下来就来演示一个 RAG 系统。在这个示例中我们提供了《租房项目QA》文档希望通过聊天方式提问关于项目的任何问题最终得到答案。要求最多只用三句话回答要简明扼要。代码如下不用理解代码含义只需要看结果即可fromlangchain_openaiimportOpenAIEmbeddings,ChatOpenAIfromlangchain_redisimportRedisConfig,RedisVectorStorefromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.runnablesimportRunnablePassthrough# 定义聊天模型modelChatOpenAI(modelgpt-4o-mini)# 定义嵌入模型embeddingsOpenAIEmbeddings(modeltext-embedding-3-large)# 配置 Redis 客户端redis_urlredis://192.168.100.238:6379configRedisConfig(index_nameqa,redis_urlredis_url,metadata_schema[{name:category,type:tag},{name:num,type:numeric},],)# 定义 Redis 向量存储vector_storeRedisVectorStore(embeddings,configconfig)# 生成检索器retrievervector_store.as_retriever()# 定义提示词模板promptChatPromptTemplate.from_messages([(human,你是负责回答问题的助手。使用以下检索到的上下文片段来回答问题。如果你不知 道答案就说你不知道。最多只用三句话回答要简明扼要。 Question: {question} Context: {context} Answer:,),])# 将文档转换为字符串defformat_docs(docs):return\n\n.join(doc.page_contentfordocindocs)# 定义链rag_chain({context:retriever|format_docs,question:RunnablePassthrough()}|prompt|model|StrOutputParser())# 循环输入问题whileTrue:# 获取用户输入questioninput(\n请输入您的问题输入退出或quit结束程序: ).strip()# 检查是否退出ifquestion.lower()in[退出,quit]:print(程序已结束再见)break# 检查输入是否为空ifnotquestion:print(问题不能为空请重新输入。)continue# 执行链流式输出print(回答: ,end,flushTrue)chunks[]forchunkinrag_chain.stream(question):chunks.append(chunk)print(chunk,end,flushTrue)print()# 换行运行代码请输入您的问题输入退出或quit结束程序: 介绍一下这个项目 回答: 这个项目是一个基于脚手架的微服务在线租房系统旨在模仿贝壳、安居客等流行应用具备真 实的交互体验和架构设计。通过结合理论和实践我在这个项目中加深了对 Java 编程语言的理解。这 个项目的灵感来源于我在大学时和同学合租的经历。 请输入您的问题输入退出或quit结束程序: 项目设计难点有哪些 回答: 项目设计的难点主要包括非技术方面的需求把控和技术方面的明确项目范围与目标。缺乏产品经 理的介入使得设计过程中需自行绘制原型图并拆分功能这增加了设计的复杂性。同时测试和质量保 证也是项目中的重要挑战需要合理安排时间以确保项目质量。 请输入您的问题输入退出或quit结束程序: 介绍数据存储的相关设计 回答: 数据存储的设计中使用MySQL作为关系数据库管理系统并结合MyBatis简化SQL操作。为了 更好地应对高并发场景设计了Redis缓存方案来优化内存利用和减轻数据库压力。引入OSS对象存储 是为了实现无限扩展性相对于本地存储和MySQL它具备更高的性能和成本效益。 请输入您的问题输入退出或quit结束程序: 详细介绍 Redis 与 Mysql 数据一致性方案 回答: Redis与MySQL的数据一致性方案通常采用“双写一致性”模式通过Cache-Aside方法实现。在 这一方案中应用程序在读取数据时先查询Redis缓存如果不存在再查询MySQL数据库并将结果缓 存到Redis中在更新数据时需同时更新MySQL和Redis以确保两者状态一致。此方法也包括设置 合理的缓存过期时间和使用布隆过滤器以解决缓存穿透等问题。 请输入您的问题输入退出或quit结束程序:我们提供的文档越详细生成的结果越符合预期。1.2 Document 文档类要想实现 RAG首先就需要从源中获取数据即加载数据或文档。这是通过 LangChain 的文档加载器完成的。LangChain 文档加载器可以将各种数据源加载成一系列的文档对象Document。class langchain_core.documents.base.Document用于存储一段文本和相关元数据的类我们可以直接定义LangChain 文档列表如下所示fromlangchain_core.documentsimportDocument documents[# 单个Document对象通常表示较大文档的一个块Document(# 内容字符串page_content狗是很好的伴侣以忠诚和友好而闻名。,# 元数据字典# 元数据属性可以捕获有关文档源、与其他文档的关系以及其他信息的信息。metadata{source:mammal-pets-doc},),Document(page_content猫是独立的宠物经常享受自己的空间。,metadata{source:mammal-pets-doc},),]这里我们定义了一个documents文档列表其内包含了两个Document文档对象。通常单个Document对象表示较大文档的一个块/页。每个Document对象包含了以下参数id可选的文档标识符。理想情况下这应该在整个文档集合中是唯一的并格式化为UUID但不会强制执行。page_content字符串文本metadata与内容关联的任意元数据。类型为dict [Optional]1.3 加载 PDF 文档将本地的 PDF 文档加载到 LangChain 中其实就是将 PDF 文档转换为一个个Document对象。这时就需要我们使用PyPDFLoader文档加载器完成这一功能。class langchain_community.document_loaders.pdf.PyPDFLoader类有以下关键函数init()初始化函数入参file_path表示要加载的 PDF 文件的路径。load() - list[Document]将数据加载到文档对象中。返回文档对象列表。现在让我们加载一个本地 PDF 文档看下效果。fromlangchain_community.document_loadersimportPyPDFLoader file_path../Docs/PDF/脚手架级微服务租房平台QA.pdfloaderPyPDFLoader(file_path)# 将 PDF 文件的每一页转换为一个独立的 Document 对象并存储在列表 docs 中。docsloader.load()接下来来看看Document重要的一些参数print(f问PDF 文件的总页数为\n{len(docs)}\n)print(f问第一页文本内容的前200个字符是\n{docs[0].page_content[:200]}\n)print(f问第一页元数据\n{docs[0].metadata})打印结果如下问PDF 文件的总页数为 32 问第一页文本内容的前200个字符是 脚⼿架级微服务租房平台 通⽤问题 1. 为什么做这个项⽬ • 回答1出于兴趣爱好开发 大学期间我和同学在外合租过⼀段时间使⽤了⼀些租房平台于是我有个想法⾃⼰能不能开发 ⼀个租房平台可以让我将理论知识与实践相结合。我希望通过实际项⽬来加深对Java编程语⾔和相 关技术的理解。于是我便查找了⼀些资料看了⼀些开源项⽬进⾏了⼀些改进。 • 回答2开源项⽬的解释 这个 问第一页元数据 {producer: pdfcpu v0.8.1 dev, creator: Chromium, creationdate: 2025-08-28T17:52:3408:00, moddate: 2025-08-28T17:52:3408:00, source: ../PDF/脚手架级微服务租房平台QA.pdf, total_pages: 32, page: 0, page_label: 1}现在许多 LLM 支持对多模态输入例如图像进行推理。在某些应用程序中例如对具有复杂布局、图表或扫描的 PDF 进行问答可以跳过 PDF 解析直接将 PDF 页面转换为图像并将其直接传递给模型可能是更准确的。1.4 加载 Markdown 文件将本地的 Markdown 文档加载到 LangChain 中需要我们使用UnstructuredMarkdownLoader文档加载器完成这一功能。class langchain_community.document_loaders.markdown.UnstructuredMarkdownLoader类有以下关键函数init()初始化函数所需参数file_path表示要加载的 Markdown 文件的路径。mode加载文件时要使用的模式。可以是single或elements。默认为single。single文档将作为单个Document对象返回elements会将文档拆分为 Title 和 NarrativeText 等不同类型的元素。load() - list[Document]将数据加载到文档对象中。返回文档对象列表。LangChain 实现的UnstructuredMarkdownLoader需要依赖Unstructured包。因此在使用前我们需要先安装它pipinstallunstructured[md]nltk现在让我们使用single模式加载一个本地 Markdown 文档看下效果。fromlangchain_community.document_loadersimportUnstructuredMarkdownLoaderfromlangchain_core.documentsimportDocument markdown_path../Docs/Markdown/脚手架级微服务租房平台QA.md# single 模式加载后默认只有一个 Document 对象loaderUnstructuredMarkdownLoader(markdown_path)dataloader.load()assertlen(data)1assertisinstance(data[0],Document)print(data[0].page_content[:200])print(data[0].metadata)打印结果如下通用问题 为什么做这个项目 回答1 {source: ../Docs/Markdown/脚手架级微服务租房平台QA.md}接下来再看下elements模式下加载本地 Markdown 文档的效果fromlangchain_community.document_loadersimportUnstructuredMarkdownLoaderfromlangchain_core.documentsimportDocument markdown_path../Docs/Markdown/脚手架级微服务租房平台QA.md# single 模式加载后默认只有一个 Document 对象loaderUnstructuredMarkdownLoader(markdown_path,modeelements)dataloader.load()print(f问文档个数为\n{len(data)}\n)print(问前三个文档数据)fordocumentindata[:3]:print(f{document}\n)打印结果如下问文档个数为 441 问前三个文档数据 page_content通用问题 metadata{source: ../Docs/Markdown/脚手架级微服务租房平 台QA.md, category_depth: 0, languages: [zho], file_directory: ../Docs/Markdown, filename: 脚手架级微服务租房平台QA.md, filetype: text/markdown, last_modified: 2025-08-29T10:56:36, category: Title, element_id: 3a0670f9bfd58576e430ef11def41593} page_content为什么做这个项目 metadata{source: ../Docs/Markdown/脚手架级微 服务租房平台QA.md, category_depth: 2, emphasized_text_contents: [为什么做这 个项目], emphasized_text_tags: [b], languages: [zho], file_directory: ../Docs/Markdown, filename: 脚手架级微服务租房平台QA.md, filetype: text/markdown, last_modified: 2025-08-29T10:56:36, parent_id: 3a0670f9bfd58576e430ef11def41593, category: Title, element_id: fcb08b2a85942455eecebb9467ffca4c} page_content回答1出于兴趣爱好开发 metadata{source: ../Docs/Markdown/脚 手架级微服务租房平台QA.md, emphasized_text_contents: [回答1出于兴趣爱好开 发], emphasized_text_tags: [b], languages: [zho], file_directory: ../Docs/Markdown, filename: 脚手架级微服务租房平台QA.md, filetype: text/markdown, last_modified: 2025-08-29T10:56:36, parent_id: fcb08b2a85942455eecebb9467ffca4c, category: UncategorizedText, element_id: a6fc0b5a457d21234bf1c4a6ae0a18db}可以看见文档被分成了 441 个。根据什么规则拆分呢答案是根据类型拆分。在元数据中有一个表示类型的字段category。这些类型都是现代文档解析库如Unstructured.io中用于分类 MarkdownMD文档或从其他格式如 PDF, Word, HTML解析后转换为 Markdown 的常见元素类型。让我们把当前文档包含的所有类型拿出来看看print(set(document.metadata[category]fordocumentindata))结果如下{Image, Title, ListItem, Table, NarrativeText, UncategorizedText}含义如下Image图像。使用语法插入的图片。Title标题。这里包含了一级、二级、三级等标题。ListItem列表项。以-,*,开头的无序列表项或以1.,2.等开头的有序列表项。Table表格。使用|和-语法创建的表格。NarrativeText叙述性文本。一个或多个连续的段落。UncategorizedText未分类文本。通常是表格中的脚注或注释、图片下方的简短说明、项目符号中非常简短的词组、文档页眉/页脚中的日期或页码等元数据。这里的标题都是用Title表示那么如何区分到底是几级标题呢首先每个文档对象的元数据中都包含一个element_id用来识别其唯一性除此之外还有一个parent_id元素用来表示其归属于那个文档对象下可以用来表示层级关系。如示例中的前三个文档对象就有明显的层级关系page_content通用问题 metadata{ category: Title, element_id: 3a0670f9bfd58576e430ef11def41593 } page_content为什么做这个项目 metadata{ parent_id: 3a0670f9bfd58576e430ef11def41593, category: Title, element_id: fcb08b2a85942455eecebb9467ffca4c } page_content回答1出于兴趣爱好开发 metadata{ parent_id: fcb08b2a85942455eecebb9467ffca4c, category: UncategorizedText, element_id: a6fc0b5a457d21234bf1c4a6ae0a18db }经过这种设计就可以还原出整个 MD 文档的内容了。对于 LangChain 来说能加载的文档类型远不止这些它还能加载网页、一些云提供商文件、社交媒体平台文档等更多文档加载器见这里。
郑州网站建设
网页设计
企业官网