Python RAG应用怎么搭建:从零构建企业级知识问答系统
📖 目录导读
- 什么是RAG?为什么需要它?
- RAG应用的核心架构拆解
- 开发环境与依赖库准备
- 文档加载与文本分割实战
- 向量化与向量数据库搭建
- 检索器设计与相似度匹配
- 大模型集成与答案生成
- 完整代码示例与调优技巧
- 常见问题与问答(FAQ)
什么是RAG?为什么需要它?
Q: 什么是RAG?
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合信息检索与文本生成的技术,它先从一个知识库中检索与用户查询最相关的文档片段,再将这些片段作为上下文输入给大语言模型(LLM),让模型生成更准确、更具时效性的答案,从而解决大模型“幻觉”和知识过时问题。

Q: 为什么企业要用RAG而不是微调?
微调需要大量标注数据、高昂算力,且模型知识更新困难,RAG只需更新向量数据库中的文档,即可实时扩展知识范围,成本低、灵活性强,特别适合内部知识库、产品文档、客服系统等场景。
RAG应用的核心架构拆解
一个完整的Python RAG应用包含以下流水线:
- 数据预处理阶段:加载PDF、Word、网页等源文档 → 文本分块(Chunking)→ 向量化(Embedding)→ 存入向量数据库。
- 检索阶段:用户提问 → 向量化查询 → 向量数据库相似度搜索 → 返回Top-K相关片段。
- 生成阶段:将查询+检索片段拼接成Prompt → 调用LLM生成最终答案。
关键技术选择:Text Embedding模型(如text-embedding-ada-002)、向量数据库(如Chroma、Qdrant、Pinecone)、LLM(如GPT-4、Claude、开源模型)。
开发环境与依赖库准备
首先创建Python虚拟环境(推荐Python 3.10+):
python -m venv rag_env source rag_env/bin/activate # Windows: rag_env\Scripts\activate
安装核心依赖:
pip install langchain chromadb openai tiktoken pypdf python-docx
- langchain:编排RAG流程的框架
- chromadb:轻量级向量数据库,无需单独部署
- openai:调用Embedding与Chat模型
- pypdf/python-docx:解析PDF和Word文档
文档加载与文本分割实战
代码示例:加载PDF并分块
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 加载文档
loader = PyPDFLoader("./企业制度手册.pdf")
documents = loader.load()
# 文本分割(块大小500字符,重叠50字符)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", " ", ""]
)
chunks = text_splitter.split_documents(documents)
print(f"共分割为{len(chunks)}个文本块")
分割策略关键点:
- 块大小需平衡LLM上下文窗口与检索粒度,建议256-1024 tokens。
- 重叠字符确保跨块语义不丢失。
向量化与向量数据库搭建
初始化Embedding模型
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(
model="text-embedding-ada-002",
openai_api_key="your-api-key-here" # 替换为真实API Key
)
创建Chroma向量库并存入数据
from langchain_community.vectorstores import Chroma
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db" # 持久化到磁盘
)
vectorstore.persist() # 显式保存
进阶建议:对于百万级文档,改用Qdrant或Pinecone获得更优性能。
检索器设计与相似度匹配
# 创建相似度检索器,返回Top-3
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
# 测试检索
query = "请假流程是什么?"
results = retriever.invoke(query)
for doc in results:
print(f"→ {doc.page_content[:100]}...")
检索优化技巧:
- 使用
MMR(最大边际相关性)避免返回过于相似的片段。 - 加入元数据过滤(如按文档来源、日期过滤)。
大模型集成与答案生成
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
llm = ChatOpenAI(
model="gpt-4", # 或 gpt-3.5-turbo
temperature=0.2, # 控制创造性
openai_api_key="your-api-key-here"
)
# 构建Prompt模板
prompt = ChatPromptTemplate.from_template("""
基于以下上下文回答问题,如果无法从上下文中找到答案,请说“未找到相关信息”。
Context: {context}
Question: {question}
Answer:
""")
# 组装RAG链
from langchain.schema.runnable import RunnablePassthrough
def format_docs(docs):
return "\n\n".join([doc.page_content for doc in docs])
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
)
# 执行查询
response = rag_chain.invoke("年假天数怎么计算?")
print(response.content)
输出示例:
根据公司制度,员工入职满一年后享有5天年假,每增加一年工龄增加1天,上限15天,具体可查看第3.2节。
完整代码示例与调优技巧
端到端脚本(仅需替换API Key)
import os
from langchain_community.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.prompts import ChatPromptTemplate
# 1. 加载文档目录
loader = DirectoryLoader("./docs", glob="**/*.pdf")
docs = loader.load()
# 2. 分割
splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=50)
chunks = splitter.split_documents(docs)
# 3. 向量化与存储
embeddings = OpenAIEmbeddings(openai_api_key=os.getenv("OPENAI_API_KEY"))
vectorstore = Chroma.from_documents(chunks, embeddings, persist_dir="./rag_db")
# 4. 检索器
retriever = vectorstore.as_retriever(k=3)
# 5. 生成链
llm = ChatOpenAI(model="gpt-4", openai_api_key=os.getenv("OPENAI_API_KEY"))
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个知识库助手,请基于上下文回答。"),
("human", "上下文:{context}\n问题:{question}")
])
chain = prompt | llm
# 6. 问答函数
def ask(question):
context = "\n".join([d.page_content for d in retriever.invoke(question)])
return chain.invoke({"context": context, "question": question}).content
print(ask("远程办公的申请条件?"))
调优清单
| 调优点 | 建议 |
|---|---|
| 分块大小 | 对技术文档用500字符,对对话类用256字符 |
| 检索数量k | 一般3-5个,长文档可增加到7 |
| LLM温度 | 答案要求事实性时设为0.1,创意生成时0.7 |
| Prompt模板 | 明确要求“只根据上下文回答”降低幻觉 |
常见问题与问答(FAQ)
Q1: 为什么检索结果不相关?
A: 检查文本分块是否过大导致内容混杂,或Embedding模型选择不当,可尝试换用BGE、E5等中文Embedding模型。
Q2: 如何支持中文文档?
A: 使用langchain.text_splitter的ChineseTextSplitter,或直接用RecursiveCharacterTextSplitter(默认支持中文),Embedding模型推荐maidalun1020/bce-embedding-base_v1。
Q3: 系统响应太慢怎么办?
A: 使用异步检索(asyncio)、缓存常见查询、或对向量数据库建立索引。
Q4: 需要处理100万+文档如何架构?
A: 用Elasticsearch做全文搜索+向量检索双路召回,部署容器化服务(FastAPI+Celery)。
Q5: 如何避免泄露敏感信息?
A: 在Prompt中加入“不要输出原始文档内容”,并配置LLM的内容过滤层。
延伸资源:
- LangChain官方文档:python.langchain.com
- OpenAI Embeddings官方指南
- ChromaDB GitHub仓库
(声明:本文仅作技术教学,所有API Key需自行申请。)