本文详细介绍如何使用 RAG(检索增强生成)技术搭建企业私有知识库系统,支持上传文档、智能问答,让大模型"懂"你的业务数据。
一、为什么企业需要私有知识库?
很多企业在引入大模型后,发现了一个尴尬的问题:通用大模型不懂你的业务。
比如你问 大模型 "我们公司去年的营收是多少",它肯定答不上来。这是因为:
- 数据隐私:企业核心数据不能上传到第三方 API
- 领域知识:通用模型缺乏垂直行业的专业知识
- 实时性:无法回答关于你最新产品、政策的实时问题
RAG(Retrieval-Augmented Generation) 正是解决这个问题的最佳方案。
参考阿里云知识库官网文档:https://help.aliyun.com/zh/model-studio/rag-knowledge-base
二、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 模型
- 启用缓存机制
总结
通过本文,你学会了:
- ✅ RAG 的基本原理
- ✅ 使用 LangChain + Chroma 构建向量知识库
- ✅ 实现文档问答系统
- ✅ 优化检索和生成效果
- ✅ 快速部署上线