
纲要项目概述ChatDoc 文档检索对话助手核心功能加载 PDF、Word 和 Excel 文档文档切片与国产嵌入模型向量化Chroma 向量存储与智能检索多种检索调优策略查询重写、上下文压缩、MMR、相似性打分封装为流式对话链实现步骤统一文件加载器根据后缀自动选择文本预处理与切片向量化与存储BGE‑M3 Chroma基础检索与高级优化构建对话链并支持流式输出完整可运行代码整合加载、切片、嵌入、检索与对话的本地示例引言掌握了 RAG 的各个独立组件——文档加载、切片、嵌入、向量数据库和检索器——之后下一步就是将它们串联成一个完整的应用。本文将以“ChatDoc”项目为例手把手带你构建一个支持 PDF、Word、Excel 三种格式的文档对话助手。你将看到如何用国产嵌入模型 BGE‑M3 和 Chroma 向量库搭建底层设施并通过查询重写、上下文压缩等调优手段大幅提升回答质量最后封装为带流式输出的对话链。项目架构ChatDoc 的整体流程遵循经典 RAG 流水线并在检索环节加入了多种优化策略。.pdf.docx.xlsx上传文档文件类型判断PyPDFLoaderDocx2txtLoaderOpenpyxlLoader文档切片BGE-M3 向量化Chroma 向量库基础检索器检索调优对话链 流式输出实现步骤环境准备安装所需依赖pipinstalllangchain langchain-core langchain-community chromadb pypdf openpyxl docx2txt文件加载器统一入口首先实现一个get_file_loader函数根据文件后缀自动选择对应的加载器将 PDF、Word、Excel 统一转换为 LangChain 的Document对象。fromlangchain_community.document_loadersimportPyPDFLoaderfromlangchain_community.document_loadersimportDocx2txtLoaderfromlangchain_community.document_loadersimportUnstructuredExcelLoaderdefget_file_loader(file_path:str):iffile_path.endswith(.pdf):returnPyPDFLoader(file_path)eliffile_path.endswith(.docx):returnDocx2txtLoader(file_path)eliffile_path.endswith(.xlsx):returnUnstructuredExcelLoader(file_path)else:raiseValueError(f不支持的文件格式:{file_path})文档切片与向量化使用RecursiveCharacterTextSplitter按段落切分文档再通过 BGE‑M3 嵌入模型向量化后存入 Chroma。fromlangchain_text_splittersimportRecursiveCharacterTextSplitterfromlangchain_community.embeddingsimportHuggingFaceBgeEmbeddingsfromlangchain_community.vectorstoresimportChroma# 切片器splitterRecursiveCharacterTextSplitter(chunk_size500,chunk_overlap50)# 国产嵌入模型 BGE-M3需联网下载也可使用 FakeEmbeddings 替代测试embedding_modelHuggingFaceBgeEmbeddings(model_nameBAAI/bge-m3)defbuild_vectorstore(docs):split_docssplitter.split_documents(docs)vectorstoreChroma.from_documents(split_docs,embedding_model,collection_namechatdoc)returnvectorstore基础检索与多重查询优化使用MultiQueryRetriever对用户问题进行改写生成多个精准子问题后再检索从而提高召回率。fromlangchain_community.chat_modelsimportChatOpenAIfromlangchain.retrievers.multi_queryimportMultiQueryRetriever llmChatOpenAI(modelgpt-3.5-turbo)# 可替换为 DeepSeek 等国产模型base_retrievervectorstore.as_retriever(search_kwargs{k:3})multi_retrieverMultiQueryRetriever.from_llm(retrieverbase_retriever,llmllm)resultsmulti_retriever.invoke(公司名称是什么)上下文压缩使用LLMChainExtractor对检索结果进行二次压缩仅保留与问题最相关的核心内容。fromlangchain.retrieversimportContextualCompressionRetrieverfromlangchain.retrievers.document_compressorsimportLLMChainExtractor compressorLLMChainExtractor.from_llm(llm)compression_retrieverContextualCompressionRetriever(base_compressorcompressor,base_retrieverbase_retriever)compressed_resultscompression_retriever.invoke(公司名称是什么)相似性分数过滤与 MMR通过similarity_search_with_score设定阈值如 0.5过滤低分文档。MMR 则可直接通过as_retriever的search_type参数启用。# 相似性分数过滤results_with_scorevectorstore.similarity_search_with_score(公司名称,k5)relevant_docs[docfordoc,scoreinresults_with_scoreifscore0.5]# MMR 搜索mmr_retrievervectorstore.as_retriever(search_typemmr,search_kwargs{k:3,lambda_mult:0.5})mmr_resultsmmr_retriever.invoke(公司名称)封装为对话链最后将检索、压缩与 LLM 组合成一条支持流式输出的对话链。fromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.runnablesimportRunnablePassthroughfromlangchain_core.output_parsersimportStrOutputParser promptChatPromptTemplate.from_messages([(system,你是一个处理文档的秘书根据提供的上下文用中文回答问题。),(human,上下文{context}\n\n问题{question})])chain({context:compression_retriever,question:RunnablePassthrough()}|prompt|llm|StrOutputParser())forchunkinchain.stream(公司注册地址是哪儿):print(chunk,end)完整可运行代码使用模拟嵌入以下脚本整合了上述核心步骤使用FakeEmbeddings和FakeListChatModel替代真实的模型无需下载模型或 API Key 即可运行。请将sample.docx替换为实际文件路径或直接使用代码中的模拟文档。fromlangchain_community.document_loadersimportDocx2txtLoaderfromlangchain_text_splittersimportRecursiveCharacterTextSplitterfromlangchain_community.embeddings.fakeimportFakeEmbeddingsfromlangchain_community.vectorstoresimportChromafromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.output_parsersimportStrOutputParserfromlangchain_community.chat_models.fakeimportFakeListChatModelfromlangchain_core.documentsimportDocument# 1. 模拟一个文档如果实际文件存在则使用 Docx2txtLoader 加载# 为方便演示直接构造一个 Document 对象sample_text 公司名称宏图科技发展公司 注册地址江苏省南京市玄武区长江路100号 主营业务人工智能软件开发、大数据分析 docDocument(page_contentsample_text,metadata{source:公司信息})# 2. 切片splitterRecursiveCharacterTextSplitter(chunk_size100,chunk_overlap20)split_docssplitter.split_documents([doc])# 3. 向量化模拟嵌入embeddingsFakeEmbeddings(size128)vectorstoreChroma.from_documents(split_docs,embeddings,collection_namechatdoc)# 4. 基础检索器base_retrievervectorstore.as_retriever(search_kwargs{k:2})# 5. 模拟大模型预设回答fake_llmFakeListChatModel(responses[根据文档内容公司名称为宏图科技发展公司注册地址在江苏省南京市玄武区长江路100号。])# 6. 对话链promptChatPromptTemplate.from_messages([(system,你是一个文档助手根据提供的上下文回答问题。),(human,上下文{context}\n\n问题{question})])chain({context:base_retriever,question:lambdax:x}|prompt|fake_llm|StrOutputParser())# 7. 测试question公司注册地址是哪儿print(用户问题,question)print(助手回答,chain.invoke(question))运行该脚本你将看到助手根据检索到的文档片段给出了正确的注册地址。此模板可以轻松扩展到真实文档、真实嵌入模型和大语言模型并加入多重查询、上下文压缩等优化策略构建强大的文档对话应用。总结ChatDoc 项目完整展示了从文档加载、切片、向量化到检索调优和对话封装的全流程。通过组合MultiQueryRetriever、LLMChainExtractor以及相似性分数/MMR 过滤你可以灵活地提升回答质量。这个骨架稍加扩展就能演变为你自己的知识库问答系统。