首页 / 技术博客 / RAG系统搭建:企业知识库最佳实践
最佳实践 2024-01-10

RAG系统搭建:企业知识库最佳实践

如何构建高效的企业知识库?本文分享RAG系统的搭建经验和优化技巧,从向量数据库选择到检索策略调优。

为什么企业需要 RAG?

大模型很聪明,但它不知道你公司的内部资料、产品文档、历史工单。RAG(Retrieval-Augmented Generation) 就是解决这个问题的——先检索相关知识,再让大模型基于这些知识生成回答。

简单来说:RAG = 搜索引擎 + 大模型

相比微调(Fine-tuning),RAG 的优势在于: - 不需要重新训练模型,省时省钱 - 知识可以实时更新,改了文档立刻生效 - 可追溯来源,每个回答都能找到出处 - 适合企业场景,大量内部文档的最佳利用方式


RAG 系统架构

一个完整的 RAG 系统包含以下核心模块:

用户提问
    ↓
[查询改写] ← 优化用户问题
    ↓
[向量检索] ← 从知识库中找到相关片段
    ↓
[重排序] ← 对检索结果精排
    ↓
[上下文组装] ← 把相关内容塞进 Prompt
    ↓
[大模型生成] ← 基于上下文生成回答
    ↓
返回答案 + 引用来源

Step 1:文档处理与分块

支持的文档格式

企业文档格式多样,需要统一处理:

格式 处理工具 注意事项
PDF PyMuPDF / Marker 表格、图片需要特殊处理
Word python-docx 保留标题层级
Excel pandas 每行转为独立文本块
Markdown 原生解析 最友好,推荐内部文档用MD
网页 BeautifulSoup 注意去除导航、广告

分块策略(Chunking)

分块是 RAG 的核心环节,直接影响检索质量:

# 方案1:按固定长度分块(简单但效果一般)
from langchain.text_splitter import CharacterTextSplitter

splitter = CharacterTextSplitter(
    chunk_size=500,      # 每块500字符
    chunk_overlap=50,    # 块间重叠50字符
    separator="\n"       # 优先在换行处切分
)

# 方案2:递归分块(推荐)
from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separators=["\n\n", "\n", "。", "!", "?", ".", " "]
)

# 方案3:语义分块(效果最好但更慢)
# 使用 embedding 相似度来决定切分点

最佳实践建议: - 中文文档 chunk_size 设为 300-600 字符 - 保留 50-100 字符的重叠,避免上下文断裂 - 表格和代码块保持完整,不要拆分 - 保留标题层级信息(在每个块前面加上所属章节)


Step 2:向量数据库选择

主流方案对比

数据库 特点 适用场景
Milvus 分布式、高性能、云原生 大规模生产环境(百万级+)
Chroma 轻量、嵌入式、Python友好 原型验证、小规模部署
FAISS Facebook开源、纯内存、极快 研究场景、中小规模
Weaviate 自带向量化、GraphQL接口 需要一体化方案
Qdrant Rust写的、性能优秀 对性能要求高的场景

快速上手 Chroma(推荐入门)

import chromadb
from sentence_transformers import SentenceTransformer

# 初始化
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection("company_docs")

# 加载 embedding 模型
model = SentenceTransformer("BAAI/bge-large-zh-v1.5")  # 中文推荐

# 添加文档
docs = ["产品A的使用说明...", "售后服务政策..."]
embeddings = model.encode(docs).tolist()

collection.add(
    documents=docs,
    embeddings=embeddings,
    ids=["doc_1", "doc_2"],
    metadatas=[{"source": "产品手册"}, {"source": "售后文档"}]
)

# 检索
query = "产品怎么用?"
query_emb = model.encode([query]).tolist()
results = collection.query(query_embeddings=query_emb, n_results=3)

生产环境推荐 Milvus

# Docker 部署 Milvus
docker compose -f milvus-docker-compose.yml up -d

# 或使用 Milvus Lite(嵌入式,适合小规模)
pip install milvus

Step 3:Embedding 模型选择

中文场景推荐:

模型 维度 特点
BAAI/bge-large-zh-v1.5 1024 中文效果最好,推荐首选
text2vec-large-chinese 1024 速度快,效果也不错
m3e-large 768 中英双语
openai text-embedding-3-small 1536 需要API,但质量高
# 使用 BGE 模型
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("BAAI/bge-large-zh-v1.5")

# 注意:BGE 模型对 query 需要加前缀
query = "为这个句子生成表示以用于检索相关文档:产品使用方法"
doc_embedding = model.encode(["产品使用方法..."])
query_embedding = model.encode([query])

Step 4:检索策略优化

单纯向量检索有时会漏掉关键词精确匹配的结果。混合检索结合向量检索和关键词检索:

# 向量检索 + BM25 关键词检索
# 权重可调:alpha=0.7 表示向量占70%,关键词占30%
def hybrid_search(query, alpha=0.7):
    # 向量检索
    vector_results = vector_search(query, top_k=20)
    # BM25 关键词检索
    bm25_results = bm25_search(query, top_k=20)

    # 融合排序 (RRF - Reciprocal Rank Fusion)
    final_results = reciprocal_rank_fusion(
        vector_results, bm25_results, 
        k=60, alpha=alpha
    )
    return final_results[:10]

重排序(Reranking)

检索出初步结果后,用 Cross-Encoder 精排:

from sentence_transformers import CrossEncoder

reranker = CrossEncoder("BAAI/bge-reranker-large")

# 对检索结果重排序
pairs = [[query, doc] for doc in retrieved_docs]
scores = reranker.predict(pairs)

# 按分数重排
ranked = sorted(zip(retrieved_docs, scores), key=lambda x: x[1], reverse=True)

Step 5:Prompt 工程

好的 Prompt 模板是 RAG 效果的保障:

RAG_PROMPT = """你是一个专业的AI助手。请基于以下参考资料回答用户的问题。

## 规则
1. 只基于提供的参考资料回答,不要编造信息
2. 如果参考资料中没有相关内容,请明确说"根据现有资料无法回答"
3. 回答时请引用来源,格式为 [来源: 文档名]

## 参考资料
{context}

## 用户问题
{question}

## 回答"""

完整 RAG 系统示例

from langchain.chains import RetrievalQA
from langchain_community.llms import Ollama
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceBgeEmbeddings

# 1. 初始化 Embedding
embeddings = HuggingFaceBgeEmbeddings(
    model_name="BAAI/bge-large-zh-v1.5",
    model_kwargs={'device': 'cuda'},
    encode_kwargs={'normalize_embeddings': True}
)

# 2. 加载向量数据库
vectorstore = Chroma(
    persist_directory="./chroma_db",
    embedding_function=embeddings
)

# 3. 初始化本地大模型
llm = Ollama(model="llama3:8b-instruct-q5_1", temperature=0.3)

# 4. 创建 RAG 链
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vectorstore.as_retriever(search_kwargs={"k": 5}),
    return_source_documents=True
)

# 5. 提问
result = qa_chain.invoke({"query": "我们的退货政策是什么?"})
print(result["result"])
print("来源:", [doc.metadata["source"] for doc in result["source_documents"]])

优化经验总结

  1. 数据质量 > 模型大小:清理文档中的噪声比换更大的模型更有效
  2. 分块策略最关键:好的分块能提升 30%+ 的检索准确率
  3. 混合检索是标配:向量 + BM25 几乎总是优于单一检索
  4. 重排序显著提升效果:多一步 reranking,效果提升明显
  5. 评估驱动优化:准备 50-100 个测试问答对,量化评估效果

RAG 系统搭建好之后,可以考虑接入 OpenClaw Agent,实现更智能的多轮对话和任务执行。

订阅更新

获取最新的AI本地化技术文章和教程