
LangChain 中的 Document 对象RAG 数据流的核心枢纽本文是 LangChain 系列文章之一重点剖析Document对象的数据结构、承上启下的枢纽作用以及在 RAG 全流程中的实际应用。一、为什么需要 Document在构建 RAG检索增强生成应用时我们面临一个根本问题数据来源极其多样——PDF、Word、网页、数据库、Markdown、纯文本……每种格式都有自己的解析方式和数据结构。如果下游的嵌入模型、向量数据库、检索器、LLM 都要为每种格式写适配逻辑代码将变得异常复杂且难以维护。Document 对象就是 LangChain 给出的统一答案无论数据从哪来最终都转换为同一种标准结构下游组件只需面对这一种语言。正如官方文档所述Document是用于检索工作流retrieval workflows的类专门存储一段文本及其关联的元数据而不是用于聊天 I/O。二、Document 的数据结构2.1 核心属性一览Document继承自BaseMedia后者又继承自Serializable。其数据结构非常简洁属性类型说明来源page_contentstr文本内容这是 Document 的核心载体自身定义metadatadict任意元数据如来源、页码、标题等继承自BaseMediatypeLiteral[Document]类型标识固定为Document自身定义idstr | None可选的唯一标识符继承自BaseMedia2.2 继承链Serializable (序列化基类) ↓ BaseMedia (媒体基类: id, metadata) ↓ Document (文档对象: page_content, type)这种继承设计让 Document 具备了序列化能力可以安全地转为 JSON 传输或持久化和媒体抽象能力统一的元数据管理。2.3 序列化后的 JSON 结构当调用document.to_json()时输出结构如下{lc:1,type:constructor,id:[langchain_core,documents,base,Document],kwargs:{page_content:Hello, world!,metadata:{source:https://example.com,page:1}}}lc: 序列化版本号type: 固定为constructorid: 类的唯一标识路径用于反序列化时定位类kwargs: 构造参数包含page_content和metadata三、承上启下Document 的枢纽作用Document 对象在 LangChain 数据流中处于正中心位置承担承上启下的关键角色。3.1 承上统一上游输入无论数据来自哪里最终都通过Loader或Splitter转换为 Document数据源接入方式输出PDFPyPDFLoaderList[Document]WordDocx2txtLoaderList[Document]网页WebBaseLoaderList[Document]数据库自定义 LoaderList[Document]纯文本TextSplitter.split_text()List[Document]核心意义上游百花齐放各种格式Document 将其统一为一种标准接口。3.2 启下驱动下游流程Document 对象生成后流向三个主要方向向量存储通过Embeddings模型将page_content转换为向量存入 Chroma、FAISS 等向量数据库LLM 上下文在 RAG 流程中检索到的 Document 被格式化为 Prompt 的context部分检索器向量数据库基于 Document 的向量进行相似度搜索返回相关 Document核心意义下游所有操作都基于标准化的 Document 接口无需关心数据最初是 PDF 还是网页。四、实战代码示例4.1 创建 Document 对象fromlangchain_core.documentsimportDocument# 手动创建docDocument(page_contentLangChain 是一个用于开发大语言模型应用的框架。,metadata{source:https://python.langchain.com,title:LangChain 官方文档,page:1})print(doc.page_content)# LangChain 是一个用于开发大语言模型应用的框架。print(doc.metadata)# {source: https://python.langchain.com, title: LangChain 官方文档, page: 1}4.2 从文件加载为 Documentfromlangchain_community.document_loadersimportPyPDFLoader loaderPyPDFLoader(report.pdf)docsloader.load()# 直接返回 List[Document]fordocindocs:print(f内容:{doc.page_content[:50]}...)print(f来源:{doc.metadata[source]}, 页码:{doc.metadata[page]})print(---)4.3 切割后重新封装为 Documentfromlangchain_text_splittersimportRecursiveCharacterTextSplitter# 假设已有长文档long_docDocument(page_content这是一段非常长的文本...*1000,metadata{source:long_article.txt})# 切割splitterRecursiveCharacterTextSplitter(chunk_size500,chunk_overlap50)# split_documents 返回新的 List[Document]每个都保留原始 metadatasplit_docssplitter.split_documents([long_doc])print(f切割为{len(split_docs)}个 Document)fori,dinenumerate(split_docs[:3]):print(f 块{i}:{len(d.page_content)}字符, metadata{d.metadata})4.4 写入向量存储fromlangchain_chromaimportChromafromlangchain_openaiimportOpenAIEmbeddings# Document 直接流入向量存储vectorstoreChroma.from_documents(documentssplit_docs,embeddingOpenAIEmbeddings(),collection_namemy_docs)# 检索时返回的仍然是 Document 对象resultsvectorstore.similarity_search(LangChain 是什么,k3)fordocinresults:print(doc.page_content)# 文本内容print(doc.metadata[source])# 元数据中的来源信息4.5 在 RAG 链中使用fromlangchain_core.promptsimportChatPromptTemplatefromlangchain_openaiimportChatOpenAI# 格式化函数将 Document 列表转为字符串defformat_docs(docs):return\n\n.join([f内容:{d.page_content}\n[来源:{d.metadata[source]}]fordindocs])# 构建 RAG 链retrievervectorstore.as_retriever()promptChatPromptTemplate.from_template( 基于以下上下文回答问题 {context} 问题{question} )rag_chain({context:retriever|format_docs,question:lambdax:x}|prompt|ChatOpenAI())responserag_chain.invoke(LangChain 的核心概念是什么)print(response.content)五、metadata 的妙用超越纯文本检索metadata字典虽然简单但功能强大。它不参与向量嵌入默认情况下却能在检索阶段发挥关键作用5.1 权限过滤# 存储时标记权限等级docs[Document(page_content2026年Q1财务预算为 5000 万元。,metadata{level:L9,source:Finance_Q1.pdf}),Document(page_content公司班车出发时间为 18:30。,metadata{level:L1,source:Admin_Guide.docx})]# 检索时按权限过滤vectorstoreChroma.from_documents(docs,OpenAIEmbeddings())retrievervectorstore.as_retriever(search_kwargs{filter:{level:{$in:[L1,L5]}}})5.2 时间范围过滤# 存储过期时间docDocument(page_content企业知识库需定期清理过期文档,metadata{source:business,expire_time:2025-12-31})# 删除过期文档vectorstore.delete(filter{expire_time:{$lt:2026-01-01}})5.3 来源追溯在 RAG 回答中标注引用来源提升可信度defformat_with_citation(docs):return\n\n.join([f{i1}.{d.page_content}\n (来源:{d.metadata[source]}, 第{d.metadata[page]}页)fori,dinenumerate(docs)])六、总结Document 对象的设计体现了 LangChain 的核心哲学通过标准化接口解耦复杂流程。维度要点数据结构page_contentmetadatatypeid简洁而完备承上统一 PDF、Word、网页等多样数据源启下驱动嵌入、存储、检索、生成全流程序列化基于Serializable继承链支持 JSON 序列化metadata不参与嵌入但支持过滤、溯源、权限控制理解 Document 对象就是理解了 LangChain RAG pipeline 的数据血液如何流动。它是连接原始数据与大模型能力的桥梁也是构建生产级 RAG 应用时最需要精细设计的环节之一。 本文代码基于langchain-core最新版本Document 类定义位于langchain_core.documents.base模块。