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

简介: 本文详解如何用RAG技术构建企业私有知识库:支持PDF/TXT/DOCX等文档上传、向量化存储与智能问答,让大模型精准理解业务数据,兼顾数据隐私、领域专业性与实时性,附完整代码与部署方案。

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

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

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

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

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

RAG(Retrieval-Augmented Generation) 正是解决这个问题的最佳方案。
参考阿里云知识库官网文档:https://help.aliyun.com/zh/model-studio/rag-knowledge-base
image.png

二、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. ✅ 快速部署上线
相关文章
|
存储 缓存 文件存储
如何保证分布式文件系统的数据一致性
分布式文件系统需要向上层应用提供透明的客户端缓存,从而缓解网络延时现象,更好地支持客户端性能水平扩展,同时也降低对文件服务器的访问压力。当考虑客户端缓存的时候,由于在客户端上引入了多个本地数据副本(Replica),就相应地需要提供客户端对数据访问的全局数据一致性。
32711 80
如何保证分布式文件系统的数据一致性
|
前端开发 容器
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局(上)
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局
17766 21
|
设计模式 存储 监控
设计模式(C++版)
看懂UML类图和时序图30分钟学会UML类图设计原则单一职责原则定义:单一职责原则,所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。bad case:IPhone类承担了协议管理(Dial、HangUp)、数据传送(Chat)。good case:里式替换原则定义:里氏代换原则(Liskov 
36696 21
设计模式(C++版)
|
存储 编译器 C语言
抽丝剥茧C语言(初阶 下)(下)
抽丝剥茧C语言(初阶 下)
|
机器学习/深度学习 人工智能 自然语言处理
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
24771 14
|
机器学习/深度学习 弹性计算 监控
重生之---我测阿里云U1实例(通用算力型)
阿里云产品全线降价的一力作,2023年4月阿里云推出新款通用算力型ECS云服务器Universal实例,该款服务器的真实表现如何?让我先测为敬!
36677 15
重生之---我测阿里云U1实例(通用算力型)
|
SQL 存储 弹性计算
Redis性能高30%,阿里云倚天ECS性能摸底和迁移实践
Redis在倚天ECS环境下与同规格的基于 x86 的 ECS 实例相比,Redis 部署在基于 Yitian 710 的 ECS 上可获得高达 30% 的吞吐量优势。成本方面基于倚天710的G8y实例售价比G7实例低23%,总性价比提高50%;按照相同算法,相对G8a,性价比为1.4倍左右。
|
存储 算法 Java
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的限流器RateLimiter功能服务
随着互联网的快速发展,越来越多的应用程序需要处理大量的请求。如果没有限制,这些请求可能会导致应用程序崩溃或变得不可用。因此,限流器是一种非常重要的技术,可以帮助应用程序控制请求的数量和速率,以保持稳定和可靠的运行。
29848 52

热门文章

最新文章

下一篇
开通oss服务