为什么企业需要 RAG?
大模型很聪明,但它不知道你公司的内部资料、产品文档、历史工单。RAG(Retrieval-Augmented Generation) 就是解决这个问题的——先检索相关知识,再让大模型基于这些知识生成回答。
简单来说:RAG = 搜索引擎 + 大模型。
相比微调(Fine-tuning),RAG 的优势在于: - 不需要重新训练模型,省时省钱 - 知识可以实时更新,改了文档立刻生效 - 可追溯来源,每个回答都能找到出处 - 适合企业场景,大量内部文档的最佳利用方式
RAG 系统架构
一个完整的 RAG 系统包含以下核心模块:
用户提问
↓
[查询改写] ← 优化用户问题
↓
[向量检索] ← 从知识库中找到相关片段
↓
[重排序] ← 对检索结果精排
↓
[上下文组装] ← 把相关内容塞进 Prompt
↓
[大模型生成] ← 基于上下文生成回答
↓
返回答案 + 引用来源
Step 1:文档处理与分块
支持的文档格式
企业文档格式多样,需要统一处理:
| 格式 | 处理工具 | 注意事项 |
|---|---|---|
| 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:检索策略优化
混合检索(Hybrid Search)
单纯向量检索有时会漏掉关键词精确匹配的结果。混合检索结合向量检索和关键词检索:
# 向量检索 + 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"]])
优化经验总结
- 数据质量 > 模型大小:清理文档中的噪声比换更大的模型更有效
- 分块策略最关键:好的分块能提升 30%+ 的检索准确率
- 混合检索是标配:向量 + BM25 几乎总是优于单一检索
- 重排序显著提升效果:多一步 reranking,效果提升明显
- 评估驱动优化:准备 50-100 个测试问答对,量化评估效果
RAG 系统搭建好之后,可以考虑接入 OpenClaw Agent,实现更智能的多轮对话和任务执行。