你有多少份文档需要处理?十份?一百份?当数量少的时候,搜索一下或者人工看看就能找到需要的信息。但文档一多,特别是达到几百上千份的时候,这个问题就变得棘手了。
传统的关键词搜索有多烂,我相信你一定有数。它只能匹配字面上的词,无法理解你真正想问什么。比如搜”如何安装 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,数据安全;用开源的嵌入模型和大模型,免费使用;代码结构清晰,扩展方便。
局限是:运行需要硬件资源,处理大量文档耗时长;回答质量取决于文档本身的质量和结构化程度。
可以优化的方向:按章节或段落分割文档,更精确;尝试不同的嵌入模型;添加��话历史,支持多轮对话;添加过滤排序功能。
希望这篇文章能帮你快速搭建自己的文档问答系统。有问题评论区见。