RAG + 大模型私有知识库实战(附完整代码)

3 阅读5分钟

从零搭建企业私有知识库:RAG + 大模型实战(附完整代码)

本文详细介绍如何使用 RAG(检索增强生成)技术搭建企业私有知识库系统,支持上传文档、智能问答,让大模型"懂"你的业务数据。

一、为什么企业需要私有知识库?

很多企业在引入大模型后,发现了一个尴尬的问题:通用大模型不懂你的业务

比如你问 ChatGPT "我们公司去年的营收是多少",它肯定答不上来。这是因为:

  1. 数据隐私:企业核心数据不能上传到第三方 API
  2. 领域知识:通用模型缺乏垂直行业的专业知识
  3. 实时性:无法回答关于你最新产品、政策的实时问题

RAG(Retrieval-Augmented Generation) 正是解决这个问题的最佳方案。

二、RAG 是什么?

RAG = 检索(Retrieval) + 增强生成(Augmented Generation)

核心流程:

用户问题 → 检索相关文档 → 将文档作为上下文 → 大模型生成答案

简单来说,就是先把你的文档"喂"给向量数据库,用户提问时先检索相关文档,再让大模型基于这些文档回答。

三、实战:搭建私有知识库问答系统

3.1 技术架构

┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│  文档上传   │ ──▶ │  文本分块    │ ──▶ │  向量嵌入   │
└─────────────┘     └──────────────┘     └─────────────┘
                                              │
                                              ▼
┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│  用户提问   │ ──▶ │  检索相关文档 │ ◀── │  向量数据库 │
└─────────────┘     └──────────────┘     └─────────────┘
                           │
                           ▼
                    ┌──────────────┐
                    │  大模型生成  │
                    └──────────────┘

3.2 环境准备

# Python 3.10+
pip install langchain openai chromadb pypdf python-dotenv streamlit

3.3 完整代码实现

"""
企业私有知识库问答系统
支持 PDF、TXT、DOCX 文档,自动向量化存储
"""

import os
from dotenv import load_dotenv
from langchain.document_loaders import PyPDFLoader, TextLoader, UnstructuredWordDocumentLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA

# 加载环境变量
load_dotenv()

class PrivateKnowledgeBase:
    def __init__(self, persist_directory="./chroma_db"):
        self.persist_directory = persist_directory
        self.embeddings = OpenAIEmbeddings()
        self.vectordb = None
        self.qa_chain = None
        
        # 初始化 LLM
        self.llm = ChatOpenAI(
            model="gpt-4",
            temperature=0.3,  # 降低随机性,提高准确性
            max_tokens=1000
        )
    
    def load_documents(self, folder_path):
        """加载文件夹中的所有文档"""
        documents = []
        
        for root, dirs, files in os.walk(folder_path):
            for file in files:
                file_path = os.path.join(root, file)
                try:
                    if file.endswith('.pdf'):
                        loader = PyPDFLoader(file_path)
                    elif file.endswith('.txt'):
                        loader = TextLoader(file_path)
                    elif file.endswith(('.docx', '.doc')):
                        loader = UnstructuredWordDocumentLoader(file_path)
                    else:
                        continue
                    
                    documents.extend(loader.load())
                    print(f"✅ 已加载: {file}")
                except Exception as e:
                    print(f"❌ 加载失败 {file}: {e}")
        
        return documents
    
    def split_documents(self, documents, chunk_size=500, chunk_overlap=50):
        """将文档分割成小块"""
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=chunk_size,
            chunk_overlap=chunk_overlap,
            length_function=len
        )
        return text_splitter.split_documents(documents)
    
    def build_vectorstore(self, chunks):
        """构建向量数据库"""
        print("🔄 正在生成向量嵌入...")
        self.vectordb = Chroma.from_documents(
            documents=chunks,
            embedding=self.embeddings,
            persist_directory=self.persist_directory
        )
        self.vectordb.persist()
        print(f"✅ 向量数据库已保存到: {self.persist_directory}")
        return self.vectordb
    
    def load_vectorstore(self):
        """加载已有的向量数据库"""
        if os.path.exists(self.persist_directory):
            self.vectordb = Chroma(
                persist_directory=self.persist_directory,
                embedding_function=self.embeddings
            )
            print("✅ 向量数据库加载成功")
            return True
        return False
    
    def setup_qa_chain(self, top_k=4):
        """设置问答链"""
        if not self.vectordb:
            raise ValueError("请先构建或加载向量数据库")
        
        retriever = self.vectordb.as_retriever(
            search_kwargs={"k": top_k}  # 检索最相关的 4 个文档块
        )
        
        self.qa_chain = RetrievalQA.from_chain_type(
            llm=self.llm,
            chain_type="stuff",  # 将检索到的文档"塞进"上下文
            retriever=retriever,
            return_source_documents=True
        )
        print("✅ 问答系统已就绪")
    
    def ask(self, question):
        """向知识库提问"""
        if not self.qa_chain:
            raise ValueError("请先设置问答链")
        
        result = self.qa_chain({"query": question})
        
        return {
            "answer": result["result"],
            "sources": [doc.page_content[:200] + "..." for doc in result["source_documents"]]
        }


# ==================== 使用示例 ====================

if __name__ == "__main__":
    # 初始化知识库
    kb = PrivateKnowledgeBase(persist_directory="./company_knowledge")
    
    # 方式一:从文档构建(首次使用)
    print("\n📂 开始构建知识库...")
    docs = kb.load_documents("./documents")  # 放入你的文档目录
    chunks = kb.split_documents(docs)
    kb.build_vectorstore(chunks)
    kb.setup_qa_chain()
    
    # 方式二:直接加载已有知识库
    # kb.load_vectorstore()
    # kb.setup_qa_chain()
    
    # 开始问答
    print("\n💬 知识库问答系统已启动,输入 'exit' 退出\n")
    
    while True:
        question = input("请输入问题: ")
        if question.lower() == "exit":
            break
        
        result = kb.ask(question)
        print(f"\n📖 回答:\n{result['answer']}\n")
        print(f"📌 参考来源 ({len(result['sources'])} 条):")
        for i, src in enumerate(result['sources'], 1):
            print(f"  {i}. {src}\n")

3.4 添加更多数据源

除了本地文档,还可以接入:

# 接入网站内容
from langchain.document_loaders import UnstructuredURLLoader

urls = ["https://your-company.com/about.html"]
loader = UnstructuredURLLoader(urls=urls)
web_docs = loader.load()

# 接入 Notion
from langchain.document_loaders import NotionLoader

loader = NotionLoader(notion_url="https://notion.so/...")
notion_docs = loader.load()

四、使用国产大模型(节省成本)

如果觉得 OpenAI API 太贵,可以用国内模型替代:

from langchain.chat_models import Tongyi

# 阿里通义千问
llm = Tongyi(temperature=0.3)

# 或者使用本地部署的 Ollama
from langchain.llms import Ollama
llm = Ollama(model="llama3")

五、效果优化技巧

5.1 文档分块策略

# 针对不同类型内容使用不同策略

# 技术文档:较小分块,保持代码完整性
tech_splitter = RecursiveCharacterTextSplitter(
    chunk_size=300,
    separators=["\n\n", "\n", "```\n", "```", " "]
)

# 政策文档:较大分块,保持段落完整
policy_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    separators=["\n\n\n", "\n\n", "\n", "。", "!", "?"]
)

5.2 混合检索

# 结合关键词搜索和向量搜索
from langchain.retrievers import EnsembleRetriever

# 向量检索
vector_retriever = vectordb.as_retriever(search_kwargs={"k": 5})

# 关键词检索
bm25_retriever = BM25Retriever.from_documents(chunks)

# 混合检索
ensemble_retriever = EnsembleRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    weights=[0.7, 0.3]
)

5.3 重排序(Rerank)

使用 Coherence Rerank 可以进一步提升检索质量:

from cohere import Client as CohereClient

cohere_client = CohereClient()

def rerank_documents(query, documents, top_n=3):
    results = cohere_client.rerank(
        query=query,
        documents=documents,
        top_n=top_n
    )
    return [results[i].document for i in range(len(results))]

六、部署上线

6.1 使用 Streamlit 快速搭建界面

import streamlit as st

st.title("🏢 企业私有知识库")

if "qa_chain" not in st.session_state:
    st.session_state.kb = PrivateKnowledgeBase()
    if st.session_state.kb.load_vectorstore():
        st.session_state.kb.setup_qa_chain()

question = st.text_input("请输入您的问题:")

if question:
    with st.spinner("正在思考..."):
        result = st.session_state.kb.ask(question)
    st.success(result["answer"])
    
    with st.expander("查看参考来源"):
        for i, src in enumerate(result["sources"], 1):
            st.text(f"{i}. {src}")

6.2 一键启动

streamlit run app.py --server.port 8501

现在你可以通过浏览器访问 http://localhost:8501 使用知识库了。

七、常见问题

Q: 检索不到相关内容?

  • 检查文档是否正确加载
  • 尝试调整 chunk_size(太小或太大都不行)
  • 增加检索数量 top_k

Q: 回答不准确?

  • 在 prompt 中添加角色设定:"你是一个专业的XXX顾问..."
  • 降低 temperature 参数
  • 检查参考文档的相关性

Q: 响应太慢?

  • 使用流式输出 stream=True
  • 考虑使用本地部署的 Embedding 模型
  • 启用缓存机制

总结

通过本文,你学会了:

  1. ✅ RAG 的基本原理
  2. ✅ 使用 LangChain + Chroma 构建向量知识库
  3. ✅ 实现文档问答系统
  4. ✅ 优化检索和生成效果
  5. ✅ 快速部署上线

完整代码已整理好,复制粘贴即可运行。搭建企业私有知识库,让大模型真正"懂"你的业务!


关于作者

长期关注大模型应用落地与云服务器实战,专注技术在企业场景中的落地实践。

个人博客:yunduancloud.icu —— 持续更新云计算、AI大模型实战教程,欢迎访问交流。