RAG 入门第三课:给你的知识库装上大脑(基于LangChain与Qwen3.5的本地RAG系统搭建)

概览

在上一篇文章中,我们成功把 PDF 文档变成了向量,存进了 ChromaDB 这个"图书馆"。但这时候的数据库是"死"的------它只是一堆数字,不会说话。

今天,我们要给这个图书馆装上一颗"大脑"(Ollama + Qwen3.5),并聘请一位"图书管理员"(LangChain)。我们要完成 RAG 流程的最后一步:检索增强生成

简单来说,我们要实现这样一个流程:

  1. 你提问。
  2. 管理员去图书馆找资料。
  3. 把资料递给大脑。
  4. 大脑读懂资料,给你写答案。

准备好了吗?我们要开始写代码了!

📦 准备工作:安装必要的库

在开始之前,确保你的 Python 环境中已经安装了所有需要的库。打开终端,运行以下命令:

bash 复制代码
# 安装 LangChain 核心库和社区贡献的组件
pip install langchain langchain-community

# 安装 ChromaDB 向量数据库
pip install chromadb

# 安装 HuggingFace 的嵌入模型库
# 注意:虽然代码中用的是 langchain_community.embeddings,但底层依赖 sentence-transformers
pip install sentence-transformers

📜 完整代码:一键复制测试

为了方便大家直接运行,这里先附上完整代码 。你可以直接复制到一个 .py 文件中(例如 rag_app.py),确保 Ollama 已经启动且 ./rag_db 文件夹存在,即可直接运行。

python 复制代码
import os
# 设置国内镜像,防止下载模型超时(国内开发必备!)
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'

from langchain_community.llms import Ollama
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings

# ======================
# 1. 初始化配置
# ======================
print("🚀 正在启动 RAG 系统...")

# 初始化 Embedding 模型(翻译官)
# 推荐使用 BGE 中文模型,效果比多语言模型更好!
embeddings = HuggingFaceEmbeddings(
    model_name="BAAI/bge-base-zh-v1.5", 
    model_kwargs={'device': 'cpu'},
    encode_kwargs={'normalize_embeddings': True}
)

# 连接向量数据库(图书管理员)
vectordb = Chroma(
    persist_directory="./rag_db",
    embedding_function=embeddings,
    collection_name="employee_handbook"
)

# 初始化本地大模型(大脑)
llm = Ollama(
    model="qwen3.5:9b",
    base_url="http://localhost:11434",
    temperature=0.1
)

# ======================
# 2. 构建 Prompt 模板
# ======================
prompt_template = """
你是专业的问答助手。请根据下方的【参考资料】回答用户的问题。

规则:
1. 只根据【参考资料】回答,不要利用你的外部知识。
2. 如果资料里没有答案,请直接回答"根据现有资料无法回答"。
3. 回答要简洁、准确。

【参考资料】:
{context}

【用户问题】:{question}
【助手回答】:
"""

prompt = PromptTemplate(
    template=prompt_template,
    input_variables=["context", "question"]
)

# ======================
# 3. 组装 RAG 流水线
# ======================
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vectordb.as_retriever(search_kwargs={"k": 3}),
    chain_type_kwargs={"prompt": prompt},
    return_source_documents=True
)

print("✅ 系统组装完成,准备就绪!\n")

# ======================
# 4. 开始对话
# ======================
while True:
    query = input("👤 我: ")
    if query.lower() in ["exit", "quit", "退出"]:
        print("👋 再见!")
        break
    
    try:
        result = qa_chain.invoke({"query": query})
        print(f"🤖 助手: {result['result']}")
        # 打印来源(可选)
        # print(f"📄 来源: {[doc.metadata.get('source') for doc in result['source_documents']]}")
    except Exception as e:
        print(f"❌ 出错了: {e}")

🛠️ 第一步:准备工具箱(导入模块)

代码虽然可以直接跑,但我们得知道手里拿的是什么工具。别被这些英文名吓到,我给它们每个人都安排了"职位":

python 复制代码
from langchain_community.llms import Ollama
# 【职位:大脑接口】负责连接本地的 Ollama 软件,把问题发给 Qwen3.5。

from langchain.prompts import PromptTemplate
# 【职位:话术导演】负责把"资料"和"问题"打包成模型能听懂的指令。

from langchain.chains import RetrievalQA
# 【职位:项目经理】负责统筹全局,自动安排"检索-组装-回答"的流水线。

from langchain_community.vectorstores import Chroma
# 【职位:图书管理员】负责去 ChromaDB 数据库里把相关的文档片段找出来。

from langchain_community.embeddings import HuggingFaceEmbeddings
# 【职位:翻译官】负责把你的中文问题翻译成向量(数字),让数据库能听懂。

🗺️ 系统架构图:各模块如何协作

为了更直观地理解这些模块是如何串联起来的,我们可以画一个简单的架构图。想象一下,当用户提出一个问题时,数据是如何在各个组件之间流动的:

复制代码
用户提问
    │
    ▼
┌─────────────┐
│  翻译官     │ (HuggingFaceEmbeddings)
│ (将问题转为向量) │
└─────────────┘
    │
    ▼
┌─────────────┐
│  图书管理员  │ (ChromaDB)
│ (根据向量检索资料) │
└─────────────┘
    │
    ▼
┌─────────────┐
│  话术导演    │ (PromptTemplate)
│ (组装资料和指令) │
└─────────────┘
    │
    ▼
┌─────────────┐
│  项目经理    │ (RetrievalQA Chain)
│ (协调整个流程)  │
└─────────────┘
    │
    ▼
┌─────────────┐
│  大脑接口    │ (Ollama)
│ (生成最终答案)  │
└─────────────┘
    │
    ▼
用户得到回答

这个闭环清晰地展示了从用户提问到得到回答的全过程。每个模块各司其职,协同工作,最终实现了智能问答。

🧠 第二步:加载"大脑"和"图书馆"

这一步我们要初始化两个核心对象:一个是负责思考的 Ollama,一个是负责存资料的 Chroma。

python 复制代码
# 1. 初始化 Embedding 模型(翻译官)
embeddings = HuggingFaceEmbeddings(
    model_name="BAAI/bge-base-zh-v1.5", 
    model_kwargs={'device': 'cpu'},
    encode_kwargs={'normalize_embeddings': True} # 归一化,提升相似度准确度
)

# 2. 连接向量数据库(图书管理员)
vectordb = Chroma(
    persist_directory="./rag_db",       # 你的数据库文件夹路径
    embedding_function=embeddings,      # 把刚才的翻译官交给他
    collection_name="employee_handbook" # 集合名称,要和入库时一致
)

# 3. 初始化本地大模型(大脑)
llm = Ollama(
    model="qwen3.5:9b",
    base_url="http://localhost:11434",
    temperature=0.1  # 温度设低一点,让模型严谨点,别乱编
)

这里有个细节:我们在 HuggingFaceEmbeddings 里加了 normalize_embeddings=True。这就像给向量做了个"标准化整容",能让后续的相似度计算更精准,这是一个提升检索效果的小技巧。

🎬 第三步:构建"话术导演"(Prompt 模板)

这是 RAG 的灵魂!如果 Prompt 写得不好,模型就会开始"自由发挥"(产生幻觉)。我们需要明确告诉它:只看资料,不许瞎编

python 复制代码
prompt_template = """
你是专业的问答助手。请根据下方的【参考资料】回答用户的问题。

规则:
1. 只根据【参考资料】回答,不要利用你的外部知识。
2. 如果资料里没有答案,请直接回答"根据现有资料无法回答"。
3. 回答要简洁、准确。

【参考资料】:
{context}

【用户问题】:{question}
【助手回答】:
"""

prompt = PromptTemplate(
    template=prompt_template,
    input_variables=["context", "question"]
)

这里的 {context}{question} 是两个"坑位"。

  • {context}:LangChain 会自动把从数据库里搜到的资料填在这里。
  • {question}:会自动把你输入的问题填在这里。

🔗 第四步:组装流水线(RetrievalQA)

万事俱备,现在要把它们串起来了。我们使用 LangChain 的 RetrievalQA 链,它就像一个项目经理,自动帮我们处理复杂的流程。

python 复制代码
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,                          # 指定大脑
    chain_type="stuff",               # 模式:把所有搜到的资料"塞"进提示词里
    retriever=vectordb.as_retriever(search_kwargs={"k": 3}), # 指定图书管理员,每次找3个最相关的片段
    chain_type_kwargs={"prompt": prompt}, # 把写好的话术模板交给项目经理
    return_source_documents=True      # 开启这个,能看到答案出自哪篇文档(调试神器)
)

这里有一个关键参数 chain_type_kwargs={"prompt": prompt}。它的作用就是把刚才定义的"规矩"(Prompt 模板)真正安装到流水线上。如果不加这一行,模型就会用默认的方式回答,可能就不会遵守"不许瞎编"的指令了。

💬 第五步:开始对话

最后,我们写一个简单的循环,就可以像聊天一样问问题了。

python 复制代码
print(" RAG 系统已就绪,请输入问题(输入 'exit' 退出):")

while True:
    query = input("\n 我: ")
    if query.lower() in ["exit", "quit", "退出"]:
        break
    
    try:
        result = qa_chain.invoke({"query": query})
        print(f" 助手: {result['result']}")
        
        # 打印来源(可选)
        # print(f" 来源: {[doc.metadata.get('source') for doc in result['source_documents']]}")
    except Exception as e:
        print(f" 出错了: {e}")

🕵️‍♂️ 深度答疑:关于 sentence-transformers 的疑问

疑问: 代码里用的是 HuggingFaceEmbeddings,为什么安装命令里要装 sentence-transformers?代码里好像没用到它啊?

解答: 这是一个非常棒的观察!这涉及到 Python 库的**"套娃"关系**。

  • 表层(LangChain): 我们在代码里用的是 HuggingFaceEmbeddings,这是 LangChain 提供的一个**"外壳"**(接口)。它的作用是让 LangChain 能统一调用各种模型。
  • 里层(实际干活): HuggingFaceEmbeddings 这个外壳里面,实际上调用的就是 sentence-transformers 这个库

打个比方:

  • 你买了一台**"智能音箱"**(LangChain)。
  • 你想听音乐,你对音箱说"播放音乐"(调用 HuggingFaceEmbeddings)。
  • 音箱内部其实是连接了**"网易云音乐"**(sentence-transformers)来播放的。
  • 你虽然没直接操作网易云音乐的 App,但如果没有安装它,音箱就放不出歌。

所以,虽然我们代码里没 import sentence_transformers,但它是 HuggingFaceEmbeddings底层依赖,必须安装,否则程序会报错提示找不到模块。

🚫 避坑指南:为什么你的代码会报错?

除了上面的依赖问题,大家最容易踩的另一个坑就是**"库混用"**。

错误现象:

很多教程会教你直接用 from sentence_transformers import SentenceTransformer

如果你直接用这个模型对象传给 LangChain 的 Chroma,会报错!

原因:

  • ChromaDB 原生库:它只认"数字列表"(向量)。你给它什么它存什么,所以直接跑入库脚本没问题。
  • LangChain :它是一个框架,它要求 embedding_function 必须是一个**"懂 LangChain 规矩"**的对象(即必须有 embed_documents 等方法)。
  • SentenceTransformer :底层的模型对象只有 encode 方法,听不懂 LangChain 的指令。

解决方案:

一定要用 LangChain 封装好的类!

  • 推荐用:from langchain_community.embeddings import HuggingFaceEmbeddings

这就好比 LangChain 给了你一个"三孔插座",你必须插"三脚插头"(封装类),不能直接拿"螺丝刀"(底层模型)硬捅。

📌 总结

恭喜你!跑通这段代码,你就真正理解了 RAG 的核心闭环:

  1. Embedding 把文字变成向量。
  2. VectorStore 存储并检索向量。
  3. Prompt 约束模型的行为。
  4. Chain 把一切自动化。

现在,你拥有了一个完全本地化、保护隐私、且能基于私有数据回答问题的 AI 助手!快去试试问它几个刁钻的问题吧!

相关推荐
大模型真好玩4 小时前
大模型训练全流程实战指南工具篇(十一)—— 大模型训练参数调优实战:从小白到调参高手
人工智能·langchain·deepseek
星浩AI4 小时前
手把手带你跑通智能体 A2A 实战案例
后端·langchain·agent
繁星星繁6 小时前
【AI】LangChain与LangGraph展望
人工智能·langchain
yanghuashuiyue19 小时前
langchain AI应用框架研究【开发部署-篇四】
python·langchain
Where-19 小时前
LangChain核心组件——Model I/O
langchain
庄小焱1 天前
【AI模型】——RAG技术简介与实战示例
大模型·rag·ai模型·ai系统·ai算法
Bug终结者_1 天前
别只会写 Java 了!LangChain4J 带你弯道超车 AI 赛道
后端·langchain·ai编程
GISer_Jing1 天前
LangChain.js + LangGraph.js 前端AI开发实战指南
前端·javascript·langchain
凌奕1 天前
LangChain + RAG 实战
langchain