一、背景介绍
企业在做 AI 应用时,常会遇到一个实际问题:怎么让大语言模型回答关于企业内部文档的问题?总不能把敏感文件上传到第三方 API 吧,微调模型又太贵,通用模型又答不对。
这时候 RAG(检索增强生成)就派上用场了。简单来说,就是先从文档里找到相关内容,然后把内容和问题一起发给大模型,让它根据找到的内容来回答。整个过程不用改模型参数,部署起来也便宜。
这篇文章就手把手教你在本地搭建这么一套系统。用的是 LangChain 框架配合 Ollama 运行的本地大模型,全程数据不出本机,从文档加载、文本切分、向量存储到问答交互,一条龙讲清楚。
二、问题描述
具体到实际项目里,挑战主要有这么几个:
- 数据安全:企业文档涉及机密,不可能发给 OpenAI 这些第三方
- 知识更新快:产品手册、代码规范、规章制度三天两头改,微调一次太折腾
- 回答要准确:通用模型容易胡编乱造,专业问题必须给出准确答案
- 成本控制:API 调用是按 token 收费的,量大扛不住
本文的方案就是奔着解决这些问题去的:本地部署、数据不外泄、可以随时更新知识库、几乎零运行成本。
三、详细步骤
3.1 环境准备
先装依赖。建议用 Python 3.10 或更高版本。
# 创建虚拟环境
python -m venv rag-env
source rag-env/bin/activate # Linux/Mac
# Windows 下用 rag-env\Scripts\activate
# 安装核心依赖
pip install langchain langchain-community langchain-chroma \
chromadb ollama pypdf sentence-transformers numpy
3.2 准备本地大模型
用 Ollama 跑本地大模型很方便,一行命令启动,支持不少开源模型。中文场景推荐 qwen2:7b 或者 phi3。
# macOS/Linux 安装 Ollama
curl -fsSL https://ollama.com/install.sh | sh
# 启动服务
ollama serve
# 拉取中文模型(新开终端)
ollama run qwen2:7b
3.3 文档处理流程
文档要转成向量才能做检索。LangChain 提供了各种加载器和分割器,直接能用。
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_chroma import Chroma
import os
# 配置路径
DOCS_PATH = "./documents"
PERSIST_DIR = "./chroma_db"
def load_documents():
documents = []
for file in os.listdir(DOCS_PATH):
file_path = os.path.join(DOCS_PATH, file)
if file.endswith(".pdf"):
loader = PyPDFLoader(file_path)
documents.extend(loader.load())
return documents
def split_documents(documents):
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
length_function=len,
separators=["\n\n", "\n", "。", " ", ""]
)
return text_splitter.split_documents(documents)
def create_vector_store(documents):
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-small-zh-v1.5",
model_kwargs={"device": "cpu"}
)
vector_store = Chroma.from_documents(
documents=documents,
embedding=embeddings,
persist_directory=PERSIST_DIR
)
return vector_store
3.4 问答系统实现
接下来是核心部分:把检索到的内容和问题一起发给大模型,让它生成答案。
from langchain_community.llms import Ollama
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
def create_qa_chain(vector_store):
llm = Ollama(
model="qwen2:7b",
temperature=0.3,
base_url="http://localhost:11434"
)
prompt_template = """用下面的上下文来回答问题。找不到相关信息就直说,别编。
上下文:
{context}
问题:{question}
回答:"""
prompt = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vector_store.as_retriever(search_kwargs={"k": 3}),
chain_type_kwargs={"prompt": prompt},
return_source_documents=True
)
return qa_chain
3.5 完整运行脚本
#!/usr/bin/env python3
"""本地 RAG 问答系统"""
import os
def main():
print("=" * 50)
print("本地 RAG 文档问答系统")
print("=" * 50)
print("\n[1/4] 加载文档...")
documents = load_documents()
print(f" 加载了 {len(documents)} 页文档")
print("[2/4] 分割文档...")
texts = split_documents(documents)
print(f" 分割为 {len(texts)} 个文本块")
print("[3/4] 创建向量数据库...")
if not os.path.exists(PERSIST_DIR):
vector_store = create_vector_store(texts)
print(f" 向量数据库已创建")
print("[4/4] 初始化问答系统...")
qa_chain = create_qa_chain(vector_store)
print("\n系统就绪!请输入问题(输入 q 退出)")
while True:
question = input("问题> ").strip()
if question.lower() == "q":
break
query_document(qa_chain, question)
if __name__ == "__main__":
main()
四、运行结果
==================================================
本地 RAG 文档问答系统
==================================================
[1/4] 加载文档...
加载了 127 页文档
[2/4] 分割文档...
分割为 342 个文本块
[3/4] 创建向量数据库...
向量数据库已创建并保存到 ./chroma_db
[4/4] 初始化问答系统...
==================================================
系统就绪!请输入问题(输入 q 退出)
问题> 公司的年假政策是什么?
回答: 根据文档显示,公司年假政策如下:
- 员工入职满1年后可享受年假
- 年假天数为 15 天
- 年假不可累积到下一年度
- 请假需提前 3 天申请
参考来源:
[1] documents/员工手册.pdf - 第 5.2 条...
五、系统优化建议
5.1 检索质量提升
- 调分块策略:不同文档用不同参数
- 换更强的 embedding:bge-large-zh-v1.5 效果更好
- 加元数据过滤:支持按文档类型筛选
5.2 生成质量提升
- 调 LLM 参数:降低 temperature 使回答更稳定
- 改提示词:根据业务需求定制
- 多轮对话:加上历史记录功能
5.3 性能优化
- 批量处理:文档批量 embedding
- 缓存优化:启用 Chroma 持久化缓存
- GPU 加速:使用 GPU 推理
六、总结
这篇文章把本地 RAG 系统的搭建流程全过了一遍。核心优势就几点:
- 数据安全:全在本地跑,不用担心泄密
- 省钱:开源模型随便用,不花 API 钱
- 灵活:想换模型换配置随时改
- 能离线:部署到内网里完全没问题
RAG 是把大模型和企业私有知识结合起来的最实用方案。按这套方法来,企业内部的知识库智能化这件事就算落地了。
后面还能玩的包括:支持图片表格等多模态文档、更复杂的问答场景、接上 Agent 实现自动化流程等等。