企业文档智能问答系统
基于 LangChain + Chroma 构建的企业级文档问答系统,支持PDF/Word/网页等多格式文档,实现精准的语义检索和智能问答。
项目概述
企业在日常运营中积累了大量的文档资源,包括产品手册、技术规范、FAQ、合同模板等。如何让员工快速、准确地从海量文档中找到所需信息,是一个普遍痛点。
本案例展示了一个基于 RAG(Retrieval-Augmented Generation)架构的企业文档智能问答系统。系统支持 PDF、Word、网页等多种文档格式的自动解析和索引,用户可以通过自然语言提问,系统从知识库中检索最相关的内容并生成精准回答,同时标注信息来源。
关键指标
系统架构
系统采用经典的 RAG 四层架构:文档处理层 → 向量索引层 → 检索层 → 生成层。
┌─────────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────────┐ │ 文档处理层 │ │ 向量索引层 │ │ 检索层 │ │ 生成层 │ │ │ │ │ │ │ │ │ │ PDF解析 │───▶│ 文本分块 │───▶│ 语义检索 │───▶│ LLM 回答生成 │ │ Word解析 │ │ Embedding │ │ 混合检索 │ │ 上下文注入 │ │ 网页爬取 │ │ Chroma 存储 │ │ 重排序 │ │ 引用标注 │ │ 自动清洗 │ │ 增量更新 │ │ 过滤 │ │ 格式输出 │ └─────────────┘ └──────────────┘ └────────────┘ └──────────────┘
实现细节
文档处理管道
多格式解析
使用 Unstructured 库支持 PDF、Word、Excel、Markdown 等多种格式的文档解析,保留文档结构(标题、段落、表格、列表)。
智能分块
基于文档结构进行语义分块(Semantic Chunking),而非简单的固定长度截断。保持段落、章节的完整性,确保每个块有独立的语义。
数据清洗
自动去除页眉页脚、水印、无关元数据,识别并修复乱码、表格形变等常见文档问题。
向量索引
Embedding 策略
采用 OpenAI text-embedding-3-small 模型(1536维),兼顾准确率和成本。对中文文档使用专门优化,处理中日韩文本的一致性问题。
Chroma 部署
作为嵌入式向量数据库,Chroma 直接集成在 FastAPI 应用中运行,零外部依赖。支持 HNSW 索引实现毫秒级检索。
增量更新
支持文档的增量添加和更新,每次更新只重新索引变化的文档块,避免全量重建的代价。
检索与生成
混合检索
结合语义检索(向量相似度)和关键词检索(BM25),在召回率和精确率之间取得平衡。支持自定义权重配置。
重排序
对初步检索结果使用 Cross-Encoder 重排序模型,将最相关的结果排到前面,提升 Top-K 准确率约 15-20%。
回答生成
LangChain 将检索到的文档块作为上下文注入 prompt,LLM 基于此生成回答。自动标注引用来源,支持多轮对话。
核心 RAG 实现
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import UnstructuredPDFLoader
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CrossEncoderReranker
# 1. 文档加载与分块
loader = UnstructuredPDFLoader("manual.pdf")
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", "。", "!", "?", " ", ""]
)
chunks = text_splitter.split_documents(documents)
# 2. 构建向量索引
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(chunks, embeddings)
# 3. 混合检索 + 重排序
retriever = vectorstore.as_retriever(search_kwargs={"k": 10})
reranker = CrossEncoderReranker(
model_name="BAAI/bge-reranker-v2-m3",
top_n=5
)
compression_retriever = ContextualCompressionRetriever(
base_compressor=reranker,
base_retriever=retriever
)
# 4. 问答
llm = ChatOpenAI(model="gpt-4o", temperature=0.1)
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=compression_retriever,
return_source_documents=True
)
result = qa_chain.invoke({"query": "产品的退货政策是什么?"})
print(f"回答: {result['result']}")
print(f"来源: {[d.metadata['source'] for d in result['source_documents']]}")
经验教训
- 文档质量直接影响检索效果 — OCR识别不准确或格式混乱的文档会导致嵌入向量质量下降,建议在预处理阶段投入更多精力
- 分块策略需要针对文档类型调整 — 技术文档适合按章节分块,FAQ适合按问答对分块,混合文档需要分层分块策略
- 重排序是性价比最高的优化 — 从10个候选中重排序取Top-5,效果提升显著且计算成本低
- 上下文注入需要平衡 — 注入太多文档块会稀释注意力,建议控制在3-5个,且根据问题类型动态调整
- 生产环境需要兜底策略 — 当检索结果置信度低时,应该友好地告知用户"未找到相关信息"而非勉强作答