Day21:(实战篇)从零搭建 RAG 系统(附通义千问 / Deepseek / 本地大模型完整代码)

前言:

上一篇我们讲透了 RAG 的核心原理,很多朋友在评论区催更代码。这篇直接上硬菜!全程保姆级,逐行注释,用 Python + LangChain + Chroma 搭建一个能精准回答 “2026 年 LPR 利率” 的 RAG 系统,通义千问、Deepseek、本地模型三种方案任选,新手复制粘贴就能跑!

开篇:别光看了,跑通代码才是自己的

上一篇发布后,有个朋友私信我说:“原理我都懂了,但一写代码就懵,能不能给个‘开箱即用’的?”

安排!这篇博客没有废话,全是干货。你只需要准备:

  1. 一台能上网的电脑(Windows/Mac/Linux 都行)

  2. 一个 Python 环境(3.8-3.11 版本最佳)

  3. 5 分钟申请的 API Key(不想用 API 也没关系,有纯本地方案)

跟着我走,30 分钟内,你就能看到自己的 RAG 系统精准输出答案,那种成就感,绝对比看 10 篇原理文都爽!

前情提要:

为了让我们能看出效果,我们先用千问、deepseek、本地qwen-7B模型,对于他们没有学习到的问题进行回答的结果:在不联网的情况下,在线的相关模型在2024年就截止更新数据了

qwen:qwen-plus模型

截至**20246****20263月的1年期LPR利率尚未公布,也无法提前确定**。

原因如下:

✅ LPR(贷款市场报价利率)是由全国银行间同业拆借中心(NIFC)在**每月20日(遇节假日顺延)上午9:15**公布的**当月有效利率**,反映的是**当前市场资金供需和政策导向**,仅对未来一个月内新发放的贷款具有参考意义。

❌ LPR **不是长期固定利率**,也不会提前数年公布。因此,**20263月的LPR目前(2024年中)完全未知**,其具体数值将取决于届时的宏观经济形势、通胀水平、货币政策(如MLF操作利率调整)、银行资金成本及信贷需求等多重因素。

📌 当前最新数据(截至2024620日公布):
- **1年期LPR为:3.45%**(自20238月起连续10个月维持不变)  
- 5年期LPR为:3.95%

🔍 提示:若需预测2026年LPR趋势,可关注:
- 中国人民银行后续MLF利率调整;
- 国家统计局发布的CPI、PPI、GDP等宏观数据;
- 央行季度《货币政策执行报告》中的前瞻性指引;
- 市场对降准/降息的预期(如通过国债收益率、利率互换IRS定价推断)。

如您有特定用途(如贷款预算、财务建模),可基于当前利率+合理假设(如每年下调10–20BP)做情景分析,但需注明“假设性预测,非官方承诺”。

需要我帮您制作一个LPR敏感性分析表或未来两年的基准利率情景预测模板吗?

deepseek:deepseek-chat模型

截至20263月,中国最新的1年期贷款市场报价利率(LPR)尚未公布,因为当前时间仍为2024年。LPR由中国人民银行授权全国银行间同业拆借中心每月20日(遇节假日顺延)公布,具体数据需以官方发布为准。

如果您需要查询历史或未来的LPR数据,建议关注中国人民银行或全国银行间同业拆借中心的官方公告。对于当前(2024年)的LPR信息,我可以为您提供最新数据。

本地ollama:qwen:7b模型

提问:20263 月最新的 1 年期 LPR 利率是多少?
本地模型 (qwen:7b) 思考中...
💬 回答:对不起,但是作为一个AI,我无法提供实时的金融资讯,包括LPR利率的变化。建议您查询权威金融机构或使用相关的财经信息APP获取最准确的数据。
⏱️ 耗时:21.15

环境准备:先把 “武器库” 配齐

在开始写代码前,我们需要先安装必要的 Python 库。打开你的终端(PyCharm 的 Terminal 或者 CMD 都行),复制粘贴以下命令:

# 一键安装所有依赖库(推荐用国内镜像源,速度飞快)
pip install langchain langchain-community chromadb dashscope openai sentence-transformers -i https://pypi.tuna.tsinghua.edu.cn/simple

第一步:准备 “弹药”—— 文档加载与预处理

我们先造一个简单的测试场景:假设我们有一份 “2026 年 3 月 LPR 利率公告”,我们要让 RAG 系统基于这份公告回答问题。

准备测试文档

在你的代码同级目录下,新建一个文本文件,命名为 lpr_2026.txt,内容如下:

中国人民银行授权全国银行间同业拆借中心公布,2026年3月20日贷款市场报价利率(LPR)为:1年期LPR为3.00%,5年期以上LPR为3.50%。以上LPR在下一次发布LPR之前有效。
该利率已连续10个月维持不变(自2025年6月以来未调整)。
写代码拆分文档

大模型没法直接啃下整份文档,我们要把它拆成 “小饼干”—— 文本块(Chunk)。新建一个 Python 文件 rag_demo.py,开始写代码:

from langchain.text_splitter import RecursiveCharacterTextSplitter

# ==========================================
# 第一步:文档加载与预处理
# ==========================================
print("📄 正在加载并拆分文档...")

# 1. 读取我们准备好的LPR公告
with open("lpr_2026.txt", "r", encoding="utf-8") as f:
    raw_text = f.read()

# 2. 初始化文本拆分器(中文场景专属配置)
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=300,    # 每个文本块最多300个字符(大概一段话)
    chunk_overlap=50,   # 块与块之间重叠50个字符,防止语义断裂
    separators=["\n\n", "\n", "。", "!", "?", " ", ""]  # 中文优先按标点符号拆分
)

# 3. 开始拆分!
chunks = text_splitter.split_text(raw_text)

# 打印看看效果
print(f"✅ 文档拆分完成!共拆成 {len(chunks)} 个文本块:")
for i, chunk in enumerate(chunks):
    print(f"\n--- 第 {i+1} 块 ---\n{chunk}")

运行一下,你会看到文档被干净利落地拆成了小文本块,这一步就搞定了!

第二步:建立 “情报库”—— 向量化与存储

这是 RAG 最核心的一步:把文字变成计算机能懂的数字(向量),并存进向量数据库。

我给大家准备了两种方案,新手推荐用方案 A(省事),想完全离线的用方案 B(极客)。

方案 A:通义千问 Embedding(推荐新手首选)

首先你需要去阿里云申请一个通义千问的 API Key(百度 “通义千问 API Key 申请”,5 分钟搞定,新用户

有免费额度)。

在刚才的代码后面继续加:

import os
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import DashScopeEmbeddings

# ==========================================
# 第二步:向量化与存储(通义千问版)
# ==========================================
print("\n🔢 正在向量化并存入数据库...")

# 【重要】这里替换成你自己的通义千问API Key
os.environ["DASHSCOPE_API_KEY"] = "sk-你的API Key在这里"

# 1. 初始化通义千问Embedding模型
embedding_model = DashScopeEmbeddings(
    model="text-embedding-v3"  # 这是通义千问最新的Embedding模型,中文效果贼好
)

# 2. 初始化Chroma向量数据库
# 这会在当前目录下创建一个 chroma_db 文件夹,数据就存在里面
vector_db = Chroma.from_texts(
    texts=chunks,
    embedding=embedding_model,
    persist_directory="./chroma_db"
)

# 3. 持久化保存(存到硬盘,下次不用重新向量化)
vector_db.persist()

print("✅ 向量化完成!数据已存入 chroma_db 文件夹")

方案 B:本地 Embedding 模型(bge-small-zh,完全免费)

如果你不想用 API,或者想在没网的环境下跑,就用这个方案。bge-small-zh 是中文场景最轻量最好用的 Embedding 模型,普通电脑就能跑。

把上面方案 A 的代码替换成:

from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings

# ==========================================
# 第二步:向量化与存储(本地模型版)
# ==========================================
print("\n🔢 正在加载本地Embedding模型并向量化...")

# 1. 初始化本地Embedding模型
# 第一次运行会自动下载模型(约100MB),之后就可以离线用了
embedding_model = HuggingFaceEmbeddings(
    model_name="BAAI/bge-small-zh-v1.5",
    model_kwargs={'device': 'cpu'}  # 有N卡的同学可以把 'cpu' 改成 'cuda',速度更快
)

# 2. 初始化Chroma向量数据库
vector_db = Chroma.from_texts(
    texts=chunks,
    embedding=embedding_model,
    persist_directory="./chroma_db_local"  # 换个路径,和在线版区分开
)

vector_db.persist()
print("✅ 本地向量化完成!数据已存入 chroma_db_local 文件夹")

如果无法下载,可以使用ModelScope进行下载:

首先导入:pip install modelscope

然后代码改成这样:

# 1. 使用 ModelScope 下载模型(自动下载到本地缓存,下次直接用)
# 这是 bge-small-zh-v1.5 在 ModelScope 上的模型ID
model_dir = snapshot_download('AI-ModelScope/bge-small-zh-v1.5', cache_dir='./')
# 2. 初始化 Embedding 模型,指向刚才下载的本地路径
embedding_model = HuggingFaceEmbeddings(
    model_name=model_dir,  # 这里填本地路径,不再去 Hugging Face 下载
    model_kwargs={'device': 'cpu'}
)

第三步:“检索员” 上线 —— 相似度检索

现在我们的 “情报库” 建好了,用户提问时,我们先让 “检索员” 去把最相关的资料找出来。

继续在代码后面加:

# ==========================================
# 第三步:相似度检索
# ==========================================
print("\n🔍 正在检索相关资料...")

# 用户的问题
user_question = "2026年3月最新的1年期LPR利率是多少?"

# 把向量数据库变成一个“检索器”,找最相关的3个文本块
retriever = vector_db.as_retriever(search_kwargs={"k": 3})
relevant_docs = retriever.get_relevant_documents(user_question)

# 打印检索结果
print(f"✅ 找到 {len(relevant_docs)} 条相关资料:")
for i, doc in enumerate(relevant_docs):
    print(f"\n--- 资料 {i+1} ---\n{doc.page_content}")

运行到这里,你会看到系统精准地找到了我们存进去的 LPR 公告内容,是不是很神奇?

第四步:“智囊团” 作答 —— 增强生成(三种大模型任选)

最后一步,也是最激动人心的一步:把检索到的资料喂给大模型,让它基于资料回答。

这里我准备了通义千问Deepseek、本地 Ollama三种方案,你想用哪个用哪个!

方案 A:通义千问(稳定靠谱,新手首选)

继续加代码:

from langchain_community.llms import Tongyi
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA

# ==========================================
# 第四步:增强生成(通义千问版)
# ==========================================
print("\n🤖 正在调用大模型生成答案...")

# 1. 初始化通义千问大模型
llm = Tongyi(
    model="qwen-max",          # 预算有限用 qwen-plus,追求效果用 qwen-max
    temperature=0.1,           # 温度设低一点,让大模型更严谨,少瞎编
    dashscope_api_key="sk-你的API Key在这里"  # 还是刚才那个API Key
)

# 2. 构建“防瞎编”Prompt模板(核心!)
prompt_template = """
你是一个专业、严谨的金融答疑助手。请严格遵守以下规则:
1. 只能使用下面【参考资料】里的内容回答问题,绝对不允许编造。
2. 如果参考资料里没有答案,直接说“抱歉,参考资料中没有相关内容”。
3. 回答要简洁明了,直接给答案,不要说废话。

【参考资料】
{context}

【用户问题】
{question}
"""

# 3. 初始化Prompt
PROMPT = PromptTemplate(
    template=prompt_template,
    input_variables=["context", "question"]
)

# 4. 构建RAG链(把检索和生成串起来)
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",      # 新手就用"stuff",简单直接
    retriever=retriever,
    return_source_documents=True,  # 返回检索到的资料,方便我们验证
    chain_type_kwargs={"prompt": PROMPT}
)

# 5. 运行!得到最终答案
result = qa_chain.invoke({"query": user_question})

# 6. 打印最终结果
print("\n" + "="*50)
print("🎯 【RAG系统最终回答】")
print(result["result"])
print("="*50)

回答结果:

💾 正在从硬盘加载向量库...
✅ 向量库加载成功!

🤖 正在查询: 2026年3月的1年期LPR是多少?

==================================================
🎯 【最终回答】
3.00%
==================================================

可以看出来,没有任何废话,直接给出答案

**换一种问法:**线上的API严格遵守了RAG的约束,我的资料中有5年以上是3.50%,理论上10年以上也是3.50%,但是线上的模型回答没有

💾 正在从硬盘加载向量库...
✅ 向量库加载成功!

🤖 正在查询: 2026年3月的10年期LPR是多少?

==================================================
🎯 【最终回答】
抱歉,参考资料中没有相关内容。
==================================================
方案B:Deepseek
# rag_query_deepseek_final.py (最终可运行版)
import os
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
# 改用 langchain_community 里的 ChatOpenAI(兼容性更好)
from langchain_community.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA

# ==========================================
# 【必填配置】替换成你自己的 API Key
# ==========================================
# DeepSeek 的 API Key
DEEPSEEK_API_KEY = "你的API_KEY"

# ==========================================
# 1. 加载已有的向量库(通义千问 Embedding)
# ==========================================
print("💾 正在从硬盘加载向量库...")
# 初始化通义千问 Embedding(版本兼容)
embedding_model = DashScopeEmbeddings(
    model="text-embedding-v3",
    dashscope_api_key="你的API_KEY"
)
# 加载 Chroma 向量库
vector_db = Chroma(
    persist_directory="./chroma_db",  # 确保这个文件夹已存在(建库脚本生成的)
    embedding_function=embedding_model
)
print("✅ 向量库加载成功!")

# ==========================================
# 2. 初始化 DeepSeek 大模型(核心适配)
# ==========================================
user_question = "2026年3月的1年期LPR是多少?"
# 构建检索器(取最相关的3条)
retriever = vector_db.as_retriever(search_kwargs={"k": 3})

# 初始化 DeepSeek 聊天模型(兼容 langchain 0.1.16)
llm = ChatOpenAI(
    model="deepseek-chat",
    temperature=0.1,
    api_key=DEEPSEEK_API_KEY,
    base_url="https://api.deepseek.com/v1"  # 必须带 /v1
)

# ==========================================
# 3. 构建 Prompt 模板 + RAG 链
# ==========================================
# 防幻觉 Prompt 模板
prompt_template = """
你是一个专业、严谨的金融答疑助手。请严格遵守以下规则:
1. 只能使用下面【参考资料】里的内容回答问题,绝对不允许编造。
2. 如果参考资料里没有答案,直接说“抱歉,参考资料中没有相关内容”。
3. 回答要简洁明了,直接给答案。

【参考资料】
{context}

【用户问题】
{question}
"""
PROMPT = PromptTemplate(
    template=prompt_template,
    input_variables=["context", "question"]
)

# 构建 RAG 问答链
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=retriever,
    return_source_documents=True,
    chain_type_kwargs={"prompt": PROMPT}
)

# ==========================================
# 4. 执行查询并输出结果
# ==========================================
print(f"\n🤖 正在查询: {user_question}")
result = qa_chain.invoke({"query": user_question})

# 打印最终结果
print("\n" + "="*50)
print("🎯 【最终回答】")
print(result["result"])
print("="*50)

# 可选:打印检索到的参考资料(方便验证)
print("\n📚 检索到的参考资料:")
for i, doc in enumerate(result["source_documents"]):
    print(f"\n--- 参考资料 {i+1} ---\n{doc.page_content}")

回答结果:

🤖 正在查询: 2026年3月的1年期LPR是多少?

==================================================
🎯 【最终回答】
3.00%。
==================================================

📚 检索到的参考资料:

--- 参考资料 1 ---
中国人民银行授权全国银行间同业拆借中心公布,2026年3月20日贷款市场报价利率(LPR)为:1年期LPR为3.00%,5年期以上LPR为3.50%。以上LPR在下一次发布LPR之前有效。
该利率已连续10个月维持不变(自2025年6月以来未调整)。
方案C:本地ollama+qwen:4b
# rag_query_qwen.py (通义千问查询版)
import os
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
# 导入 Ollama 来调用本地 qwen:7b
from langchain_community.llms import Ollama
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA

# ==========================================
# 【重要配置】填入你的通义千问 API Key
# ==========================================
os.environ["DASHSCOPE_API_KEY"] = "你的API_KEY"

# ==========================================
# 1. 加载已有的向量库
# ==========================================
print("💾 正在从硬盘加载向量库...")

# 【关键】必须使用和建库时完全一样的 Embedding 模型
embedding_model = DashScopeEmbeddings(
    model="text-embedding-v3"
)

# 直接加载 Chroma 数据库
vector_db = Chroma(
    persist_directory="./chroma_db",  # 指向刚才建库的文件夹
    embedding_function=embedding_model
)

print("✅ 向量库加载成功!")

# ==========================================
# 2. 初始化通义千问大模型并进行问答
# ==========================================
user_question = "2026年3月的10年期LPR是多少?"

# 把数据库变成检索器
retriever = vector_db.as_retriever(search_kwargs={"k": 3})

# 初始化通义千问大模型
llm = Ollama(
    model="qwen:7b",  # 对应 Ollama 拉取的 qwen:7b 模型名
    temperature=0.1,  # 低温度保证回答严谨
    num_ctx=2048      # 上下文窗口大小,按需调整
)

# Prompt 模板
prompt_template = """
你是一个专业、严谨的金融答疑助手。请严格遵守以下规则:
1. 只能使用下面【参考资料】里的内容回答问题,绝对不允许编造。
2. 如果参考资料里没有答案,直接说“抱歉,参考资料中没有相关内容”。
3. 回答要简洁明了,直接给答案。

【参考资料】
{context}

【用户问题】
{question}
"""
PROMPT = PromptTemplate(template=prompt_template, input_variables=["context", "question"])

# 构建 RAG 链
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=retriever,
    return_source_documents=True,
    chain_type_kwargs={"prompt": PROMPT}
)

# 运行查询
print(f"\n🤖 正在查询: {user_question}")
result = qa_chain.invoke({"query": user_question})

print("\n" + "="*50)
print("🎯 【最终回答】")
print(result["result"])
print("="*50)

回答结果:

💾 正在从硬盘加载向量库...
✅ 向量库加载成功!

🤖 正在查询: 2026年3月的1年期LPR是多少?

==================================================
🎯 【最终回答】
2026年3月20日的1年期LPR为3.00%。
==================================================

换一种问法:

💾 正在从硬盘加载向量库...
✅ 向量库加载成功!

🤖 正在查询: 2026年3月的10年期LPR是多少?

==================================================
🎯 【最终回答】
2026年3月20日的贷款市场报价利率(LPR)中,10年期以上的LPR为3.50%。但需要注意的是,这个利率是到下一次发布LPR之前的有效信息。
==================================================

可以看出本地还能举一反三,实际上是调用了自身通用知识库

为什么?
维度 线上 qwen-plus 本地 qwen:7b
规则执行力度 严格遵守 “仅用参考资料”,拒绝越界 宽松遵守,优先调用自身知识补全
通用知识调用权限 被限制(RAG 模式下禁用) 无限制(可自由调用训练数据中的知识)
检索结果的权重 检索结果 = 全部依据,无结果则返回无内容 检索结果 = 参考,无结果也会补全自身知识
平台合规约束 强约束(避免编造) 无约束(本地部署无合规限制)
如何让线上 qwen-plus 也能正确回答?
  1. 优化检索(让 “10 年期” 能匹配到 “5 年期以上” 的内容)

    修改检索器的配置,增加 “同义词 / 扩展匹配”,比如:

    # 给检索器增加“模糊匹配”或“扩展关键词”
    retriever = vector_db.as_retriever(
        search_kwargs={
            "k": 3,
            "score_threshold": 0.1  # 降低匹配阈值,让相似文本也能被检索到
        }
    )
    
  2. 优化 Prompt(允许模型 “合理推导”)

把 Prompt 中过于严格的规则,调整为 “允许基于参考资料推导”:

prompt_template = """
你是专业金融答疑助手,遵守以下规则:
1. 优先使用【参考资料】的内容回答问题;
2. 如果参考资料中有相关逻辑(如“5年期以上”包含更长年限),可以基于此推导回答;
3. 完全无相关内容时,才说“抱歉,参考资料中没有相关内容”。

【参考资料】
{context}

【用户问题】
{question}
"""

总结与预告

这篇博客我们从零开始,用不到 100 行代码,跑通了 RAG 的全流程:

  1. 文档拆分:把大文档拆成小文本块

  2. 向量化存储:用通义千问或本地模型把文字变成向量,存进 Chroma

  3. 相似度检索:用户提问时,先找最相关的资料

  4. 增强生成:把资料喂给大模型,得到精准靠谱的答案

下一篇预告:

很多朋友说:“我只有 PDF 文件,怎么办?”“我有 100 个文档,能一起检索吗?”

下一篇我们就讲:如何用 RAG 处理 PDF、Word 等多格式文件,以及如何构建多文档知识库

关注我,带你三个月从 AI Agent 入门到精通!如果这篇博客对你有帮助,别忘了点赞、收藏、评论三连哦!有问题评论区见~

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐