前言
随着大语言模型(LLM)的普及,如何让模型回答基于私有知识的问题成为刚需。检索增强生成(RAG) 是一种主流方案:先检索相关文档,再让LLM基于检索结果生成答案。本教程将使用LangChain框架,结合本地部署的嵌入模型和LLM,搭建一个完全离线的知识库问答系统。
环境准备
硬件要求
- 至少8GB RAM(推荐16GB)
- 有NVIDIA GPU(可选,但能加速嵌入和推理)
软件安装
# 创建虚拟环境
python -m venv rag_env
source rag_env/bin/activate # Linux/Mac
# rag_env\Scripts\activate # Windows
# 安装依赖
pip install langchain langchain-community chromadb sentence-transformers fastapi uvicorn
步骤一:选择嵌入模型
我们使用BAAI/bge-small-zh-v1.5,这是一个轻量级的中文嵌入模型,适合本地部署。
from langchain.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-small-zh-v1.5",
model_kwargs={'device': 'cpu'}, # 如果有GPU,改为'cuda'
encode_kwargs={'normalize_embeddings': True}
)
步骤二:加载和分割文档
假设您的知识库是PDF或Markdown文件,放在./docs目录下。
from langchain.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 加载文档
loader = DirectoryLoader('./docs', glob="**/*.md", loader_cls=TextLoader)
documents = loader.load()
# 分割文档
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""]
)
splits = text_splitter.split_documents(documents)
print(f"文档被分割为 {len(splits)} 个块")
步骤三:创建向量数据库
使用Chroma作为向量数据库,将文档块嵌入并存储。
from langchain.vectorstores import Chroma
persist_directory = './chroma_db'
vectordb = Chroma.from_documents(
documents=splits,
embedding=embeddings,
persist_directory=persist_directory
)
vectordb.persist()
print(f"向量数据库已保存至 {persist_directory}")
步骤四:设置本地LLM
这里使用Ollama运行Qwen2.5-7B模型,确保已安装Ollama并拉取模型。
from langchain.llms import Ollama
llm = Ollama(model="qwen2.5:7b", temperature=0.3)
步骤五:构建RAG链
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 自定义提示模板
prompt_template = """使用以下上下文来回答用户的问题。如果你不知道答案,就说你不知道,不要编造。
上下文:{context}
问题:{question}
答案:"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
# 创建检索器
retriever = vectordb.as_retriever(search_kwargs={"k": 3})
# 创建QA链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": PROMPT},
return_source_documents=True
)
步骤六:测试问答
query = "什么是LangChain?"
result = qa_chain({"query": query})
print(f"问题:{query}")
print(f"答案:{result['result']}")
print(f"来源文档:{result['source_documents']}")
步骤七:部署为API服务
使用FastAPI将系统封装成REST API。
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Query(BaseModel):
question: str
@app.post("/ask")
def ask(query: Query):
result = qa_chain({"query": query.question})
return {"answer": result['result'], "sources": [doc.metadata for doc in result['source_documents']]}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
运行后,可通过http://localhost:8000/docs查看Swagger文档,发送POST请求即可问答。
总结
至此,您已成功搭建了一个本地知识库问答系统。所有数据均保留在本地,无需联网,保障了隐私安全。您可以根据需要替换为更强的嵌入模型或LLM,如bge-large或Qwen2.5-14B。祝您使用愉快!