使用 Python + LangChain 实现本地文档智能问答

你有多少份文档需要处理?十份?一百份?当数量少的时候,搜索一下或者人工看看就能找到需要的信息。但文档一多,特别是达到几百上千份的时候,这个问题就变得棘手了。

传统的关键词搜索有多烂,我相信你一定有数。它只能匹配字面上的词,无法理解你真正想问什么。比如搜”如何安装 Python”,它可能完全忽略”Python 安装教程”这种相关结果。你也无法表达模糊的意图,比如”昨天开会说的那个新功能在哪”。

这就是 RAG(检索增强生成)能帮上忙的地方。它结合了语义搜索和大语言模型的能力,能理解你的真实问题,从大量文档里找出最相关的内容,再生成准确的回答。

实际问题

我们的需求通常是这样的:

  • 文档格式各异:PDF、Word、Markdown、纯文本,每种格式处理方式都不一样
  • 文档内容可能有重复、冗余,甚至相互矛盾
  • 需要在海量文档里快速找到相关内容
  • 确保 LLM 生成的回答有据可查,而不是凭空捏造

以前要解决这些问题,得写大量的预处理代码来处理各种格式,建索引系统来支持快速检索,还要设计提示词来约束输出。这些工作非常繁琐,容易出错。

实现步骤

我们用 Python + LangChain 来快速搭建一个本地文档问答系统。整个过程分几步:

第一步是环境准备。 需要装这些包:langchain、langchain-community、chromadb、python-docx、pypdf。分别管大语言模型集成、向量数据库、文档解析。

第二步是文档加载。 LangChain 提供了多种文档加载器,处理不同格式的文件。我们用 DirectoryLoader 来加载目录里的所有文档,支持 PDF、Word、Markdown、TXT这些常见格式。

第三步是文本分词。 向量数据库对文本长度有限制,需要把长文档分割成较短的片段。选什么分割策略,看你文档的具体结构。

第四步是向量化存储。 用嵌入模型把文本片段转换成向量存到 Chroma 向量数据库。Chroma 是轻量级的,本地部署很适合个人或小团队用。

第五步是构建问答链。 把检索系统和大语言模型结合起来。用户提问时,系统先从向量数据库检索相关文档,然后把用户问题和检索到的文档一起发给 LLM 生成回答。

完整代码

直接上代码,可以复制到一个 Python 文件里运行:

import os
from langchain_community.document_loaders import DirectoryLoader
from langchain_community.document_loaders import TextLoader
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.document_loaders import UnstructuredWordDocumentLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA

# 配置参数
DOCS_DIR = "./docs"  # 文档目录
PERSIST_DIR = "./chroma_db"  # 向量数据库持久化目录
EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"  # 嵌入模型

def load_documents():
    """加载文档目录中的所有文件"""
    # 根据文件类型选择不同的加载器
    loaders = {
        '.txt': TextLoader,
        '.md': TextLoader,
        '.pdf': PyPDFLoader,
        '.docx': UnstructuredWordDocumentLoader,
    }

    documents = []
    for ext, loader_class in loaders.items():
        loader = DirectoryLoader(
            DOCS_DIR,
            glob=f"**/*{ext}",
            loader_cls=loader_class,
            show_progress_bar=True
        )
        docs = loader.load()
        documents.extend(docs)

    print(f"共加载 {len(documents)} 个文档")
    return documents

def split_documents(documents):
    """将文档分割成较小的片段"""
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=500,
        chunk_overlap=50,
        separators=["\n\n", "\n", "。", ".", " ", ""]
    )

    texts = text_splitter.split_documents(documents)
    print(f"共分割成 {len(texts)} 个文本片段")
    return texts

def create_vectorstore(texts):
    """创建向量数据库"""
    embeddings = HuggingFaceEmbeddings(
        model_name=EMBEDDING_MODEL,
        model_kwargs={'device': 'cpu'}
    )

    vectorstore = Chroma.from_documents(
        documents=texts,
        embedding=embeddings,
        persist_directory=PERSIST_DIR
    )

    print("向量数据库创建成功")
    return vectorstore

def build_qa_chain(vectorstore):
    """构建问答链"""
    llm = ChatOpenAI(
        base_url="http://localhost:11434/v1",
        model="llama3",
        api_key="ollama",
        temperature=0
    )

    qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=vectorstore.as_retriever(
            search_kwargs={"k": 3}
        ),
        return_source_documents=True
    )

    return qa_chain

def main():
    documents = load_documents()
    texts = split_documents(documents)
    vectorstore = create_vectorstore(texts)
    qa_chain = build_qa_chain(vectorstore)

    print("\n=== 文档问答系统 ===")
    print("输入问题进行查询,输入 'quit' 退出\n")

    while True:
        query = input("请输入问题: ")
        if query.lower() == 'quit':
            break

        result = qa_chain({"query": query})

        print("\n回答:")
        print(result["result"])

        print("\n参考来源:")
        for i, doc in enumerate(result["source_documents"], 1):
            print(f"  {i}. {doc.page_content[:200]}...")
        print()

if __name__ == "__main__":
    main()

运行前先装必要的包:

pip install langchain langchain-community langchain-openai
pip install chromadb python-docx pypdf sentence-transformers
pip install huggingface-hub

还需要安装 Ollama 来运行本地大模型。去 https://ollama.ai 下载。

效果

代码跑起来后,系统会先加载指定目录里的所有文档,然后分割、向量化。这个过程需要几分钟,看文档数量和大小。

处理完就进入交互模式。你可以问任何关于文档内容的问题,系统会检索相关片段并生成回答。

比如文档里有 Python 安装教程,你问”如何在 Windows 上安装 Python?”系统会找到相关文档,生成详细的安装步骤。

系统还会显示回答的参考来源。每个来源显示文档片段的前 200 个字符,方便你追溯到原文。

总结

这篇文章介绍了怎么用 Python + LangChain 搭建本地文档问答系统。流程是:文档加载 -> 文本分割 -> 向量存储 -> 问答链。

这个方案的优点:完全本地化部署,不依赖外部 API,数据安全;用开源的嵌入模型和大模型,免费使用;代码结构清晰,扩展方便。

局限是:运行需要硬件资源,处理大量文档耗时长;回答质量取决于文档本身的质量和结构化程度。

可以优化的方向:按章节或段落分割文档,更精确;尝试不同的嵌入模型;添加��话历史,支持多轮对话;添加过滤排序功能。

希望这篇文章能帮你快速搭建自己的文档问答系统。有问题评论区见。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇