首页 / 科技 / 基于 RAG + LangChain 搭建企业级私有知识库问答系统(2026 实战版)

基于 RAG + LangChain 搭建企业级私有知识库问答系统(2026 实战版)

摸鱼不慌
摸鱼不慌
💡 作者按:这是我在多个企业知识库项目落地后的实战总结。RAG 看似简单——"文档切片、向量化、检索、生成",但要真正达到生产可用,里面的坑比想象中深得多。本文给出完整可运行的代码,并穿插十年开发经验中总结的架构决策与避坑指南。

一、为什么企业要自建 RAG 知识库

通用大模型有两个致命问题:知识滞后幻觉。企业内部的制度文档、技术手册、客户资料,ChatGPT/DeepSeek 统统不知道。RAG(Retrieval-Augmented Generation,检索增强生成)的核心思路是:
不让大模型只依赖训练知识,而是回答前先从企业知识库检索相关资料,再基于真实资料生成答案。
一个真正能上线的 RAG 系统,远不止"文档切块+向量化+调 LLM"这么简单,还要处理:文档解析与版本更新、关键词与向量混合检索、权限过滤、结果重排、引用来源、资料不足时拒答、效果评测与监控。
本文带你从零搭建一套本地可运行、生产可扩展的 RAG 知识库系统。

二、系统架构

┌─────────────┐   ┌──────────────┐   ┌─────────────┐
│ 文档上传     │ → │ 文本分割      │ → │ 向量化存储   │
│ PDF/Word/MD  │   │ Chunk Split  │   │ ChromaDB    │
└─────────────┘   └──────────────┘   └─────────────┘
                                                ↓
┌─────────────┐   ┌──────────────┐   ┌─────────────┐
│ 最终回答     │ ← │ DeepSeek/LLM │ ← │ 向量检索     │
│ 含来源引用   │   │ 生成回答      │   │ Top-K 结果  │
└─────────────┘   └──────────────┘   └─────────────┘
技术栈选型(2026 主流组合):
  • 编排框架:LangChain 0.3.x

  • 向量数据库:ChromaDB(开发)/ Milvus(生产)

  • Embedding:BAAI/bge-small-zh-v1.5(中文效果好的本地小模型)

  • LLM:DeepSeek(OpenAI 兼容接口)或 Ollama 本地模型


三、环境准备

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

四、核心代码实战

4.1 文档加载与分割

大模型有上下文窗口限制,必须把长文档切成小块(Chunk)。
from langchain.document_loaders import PyPDFLoader, Docx2txtLoader, TextLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitterimport osdef load_documents(file_path: str):    """根据文件类型加载文档"""
    ext = os.path.splitext(file_path)[1].lower()    if ext == '.pdf':
        loader = PyPDFLoader(file_path)    elif ext in ['.docx', '.doc']:
        loader = Docx2txtLoader(file_path)    elif ext in ['.txt', '.md']:
        loader = TextLoader(file_path, encoding='utf-8')    else:        raise ValueError(f"不支持的文件类型: {ext}")    return loader.load()def split_documents(documents, chunk_size=500, chunk_overlap=50):    """将文档切割成小块"""
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        separators=["\n\n", "\n", "。", "!", "?", " ", ""],
        length_function=len
    )    return splitter.split_documents(documents)# 使用示例docs = load_documents("company_manual.pdf")
chunks = split_documents(docs)print(f"共切割为 {len(chunks)} 个文本块")
⚠️ 十年经验提示chunk_size 不是越小越精准。太小会切断语义完整性,太大则检索精度下降。中文场景建议 500-800 字符,overlap 取 10%-20%。

4.2 向量化与存储

使用本地 Embedding 模型(免费,不调用 API):
from langchain_community.vectorstores import Chromafrom langchain_community.embeddings import HuggingFaceEmbeddingsdef create_vector_store(chunks, persist_dir="./chroma_db"):    """创建向量数据库"""
    embeddings = HuggingFaceEmbeddings(
        model_name="BAAI/bge-small-zh-v1.5",
        model_kwargs={'device': 'cpu'},
        encode_kwargs={'normalize_embeddings': True}
    )
    vectorstore = Chroma.from_documents(
        documents=chunks,
        embedding=embeddings,
        persist_directory=persist_dir
    )
    vectorstore.persist()    print(f"向量库已保存至 {persist_dir}")    return vectorstore

4.3 接入 DeepSeek 大模型

DeepSeek 兼容 OpenAI SDK 格式,配置非常简单:
from langchain_openai import ChatOpenAIfrom langchain.chains import RetrievalQAdef build_qa_chain(vectorstore, api_key: str):    """构建检索问答链"""
    llm = ChatOpenAI(
        model_name="deepseek-chat",
        openai_api_key=api_key,
        openai_api_base="https://api.deepseek.com/v1",
        temperature=0.1
    )
    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

4.4 完整流水线整合

把上述模块串成端到端管线:
import osdef main():    # 1. 加载并分割文档
    docs = load_documents("company_manual.pdf")
    chunks = split_documents(docs, chunk_size=500, chunk_overlap=50)    
    # 2. 构建向量库
    vectorstore = create_vector_store(chunks, persist_dir="./chroma_db")    
    # 3. 构建问答链(替换为你自己的 DeepSeek API Key)
    qa_chain = build_qa_chain(vectorstore, api_key="sk-your-deepseek-api-key")    
    # 4. 问答循环
    while True:
        query = input("\n请输入问题(输入 q 退出): ")        if query.lower() == 'q':            break
        result = qa_chain({"query": query})        print(f"\n🤖 回答: {result['result']}")        print(f"\n📚 参考来源:")        for i, doc in enumerate(result['source_documents'], 1):            print(f"[{i}] {doc.page_content[:100]}...")if __name__ == "__main__":
    main()

五、进阶:生产级优化

上面是最简版本。在企业落地时,还需要考虑以下优化点:

5.1 混合检索(向量 + 关键词)

纯向量检索会漏掉精确关键词匹配,混合检索能显著提升召回率:
from langchain.retrievers import EnsembleRetrieverfrom langchain_community.retrievers import BM25Retrieverdef create_hybrid_retriever(documents, vector_store):
    vector_retriever = vector_store.as_retriever(search_kwargs={"k": 6})
    keyword_retriever = BM25Retriever.from_documents(documents)
    keyword_retriever.k = 6
    ensemble_retriever = EnsembleRetriever(
        retrievers=[vector_retriever, keyword_retriever],
        weights=[0.7, 0.3]  # 偏向语义检索
    )    return ensemble_retriever

5.2 结果重排(Rerank)

用 Cross-Encoder 对召回结果重排,把最相关的排在前面:
from langchain.retrievers import ContextualCompressionRetrieverfrom langchain.retrievers.document_compressors import CrossEncoderRerankerfrom langchain_community.cross_encoders import HuggingFaceCrossEncoderdef create_rerank_retriever(base_retriever):
    compressor = CrossEncoderReranker(
        model=HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-large"),
        top_n=5
    )    return ContextualCompressionRetriever(
        base_compressor=compressor,
        base_retriever=base_retriever
    )

5.3 带引用的自定义 Prompt

让 LLM 基于上下文回答,并在资料不足时拒答:
from langchain_core.prompts import ChatPromptTemplatefrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.runnables import RunnablePassthroughdef build_advanced_rag_chain(retriever, llm):
    prompt = ChatPromptTemplate.from_template(        "基于以下上下文回答问题。如果上下文没有相关信息,请回答"
        "'抱歉,知识库中未找到相关信息'。\n\n"
        "上下文:\n{context}\n\n问题: {question}\n\n回答:"
    )    def format_docs(docs):        return "\n\n".join(doc.page_content for doc in docs)
    
    rag_chain = (
        {"context": retriever | format_docs, "question": RunnablePassthrough()}
        | prompt
        | llm
        | StrOutputParser()
    )    return rag_chain

六、避坑指南(血泪经验)

⚠️ 六大常见误区,每一个都是我踩过的坑:
  1. 文档入库只做一次 —— 企业文档会更新,必须做增量更新机制

  2. 分片越小越精准 —— 过小会切断语义,建议 500-800 字符

  3. 向量维度越高越好 —— 维度高≠效果好,bge-small-zh 在企业场景够用

  4. 只使用向量搜索 —— 纯向量检索会漏掉精确关键词,必须混合检索

  5. 召回结果越多越好 —— k 值过大会引入噪声,一般 k=3~5

  6. 接入 RAG 后就不会产生幻觉 —— RAG 大幅降低幻觉,但不等于消除,仍需拒答机制


七、效果评估

RAG 系统上线前必须评估,否则你根本不知道它在变好还是变坏:
  • 检索评估:召回率、MRR、NDCG

  • 回答评估: faithfulness(忠实度)、answer relevancy

  • 工具推荐:Ragas

# 评估示例(伪代码)from ragas import evaluatefrom ragas.metrics import faithfulness, answer_relevancy

results = evaluate(
    dataset=test_dataset,
    metrics=[faithfulness, answer_relevancy]
)print(results)