← 返回博客
2026-06-18 14:00:00

从零搭建你的第一个 RAG 应用(开源模型篇):零成本、全本地、可商用

从零搭建你的第一个 RAG 应用(开源模型篇):零成本、全本地、可商用

2026-06-18 14:00:00 · 标签:RAG、开源模型、Ollama、LangChain、BGE、DeepSeek、Qwen、教程

阅读指南

这篇文章是《从零搭建你的第一个 RAG 应用:手把手教程 + 深度理解》的开源模型替代篇。那篇文章使用了 OpenAI 的 GPT-4o-mini + text-embedding-3-small,跑通很快,但有三笔账绕不开:

本文用完全开源的模型把整套 RAG 重新实现一遍:LLM 用 Qwen3(通义千问)或 DeepSeek,Embedding 用 BGE-M3。所有模型都跑在你自己的机器上,不需要联网,不需要 API Key,数据不出你的电脑。

文章同样分为两部分:


上篇:手把手教程

前提条件

开始之前,确保你的环境满足以下条件:

没有显卡? 用 CPU 也能跑。Qwen3 的 1.5B 量化版在 CPU 上大概每秒 10-20 个 token,够你学习和验证用了。BGE-M3 的 Embedding 计算量不大,CPU 完全能胜任。本文会同时给出 GPU 和 CPU 方案。

第 0 步:安装 Ollama 并下载模型

Ollama 是现在部署开源模型最方便的工具——一条命令下载模型、一条命令启动服务,比配 Python 环境还简单。

安装 Ollama

ollama.com 下载对应系统版本,安装后打开终端验证:

ollama --version
# 输出:ollama version 0.11.x

下载模型

LLM(大语言模型,负责回答)

# 推荐:Qwen3 8B 量化版(4.7GB),中文能力强,8GB 显存可跑
ollama pull qwen3:8b

# 如果你的显卡 >= 16GB 显存,可以用更强的 14B 版
ollama pull qwen3:14b

# CPU 首选:DeepSeek-R1 1.5B 蒸馏版(不到 1GB),轻量但够用
ollama pull deepseek-r1:1.5b

# 或者 Qwen3 的迷你版(~1GB),CPU 友好
ollama pull qwen3:1.8b

下载完成后验证:

ollama run qwen3:8b "你好,请用一句话介绍你自己"

Embedding 模型(负责把文本变成向量)

# BGE-M3(1024 维),中文 Embedding 的标杆,CPU 也能跑
ollama pull bge-m3

# 也可以用 GTE-Qwen2,Qwen 家族的中文 Embedding 模型
ollama pull gte-qwen2:1.5b

验证 Embedding:

ollama run bge-m3 "这是一段测试文本"
# 会输出一串数字(向量)
为什么不用 OpenAI? Ollama 的 Embedding API 和 OpenAI 完全兼容——代码几乎不用改,只是改了 URL 和模型名。

第 1 步:安装依赖

打开终端,创建项目文件夹并安装所需库:

mkdir my-rag-app-oss && cd my-rag-app-oss
pip install langchain langchain-community langchain-ollama faiss-cpu pypdf

# 或者国内镜像
pip install langchain langchain-community langchain-ollama faiss-cpu pypdf \
  -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn

和原版教程相比,唯一的区别是 langchain-openai 换成了 langchain-ollama。其他完全一样。

装了什么?

作用
langchainRAG 流程的核心框架
langchain-community社区贡献的文档加载器、向量库集成
langchain-ollamaOllama 的 LLM 和 Embedding 接口(替代 langchain-openai)
faiss-cpuMeta 开源的向量检索库(CPU 版,免配)
pypdf读取 PDF 文件

第 2 步:准备你的文档

和原版一样,项目文件夹里新建 data/ 目录,把想"问"的文档丢进去:

mkdir data
# 把你的 PDF、MD、TXT 文件复制到 data/ 里

先从 2-3 份文件开始,跑通了再加。


第 3 步:加载文档 → 切成小块

这一步和原版一模一样——加载文档靠的是 langchain-community,分割靠的是 langchain 核心库,都不涉及 API 调用。

新建 main.py,写入:

import os
from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# ========== 第 3 步:加载与分割文档 ==========

def load_and_split(data_dir="./data"):
    """把 data/ 里的所有文档读进来,切成小块"""

    # --- 3.1 加载 PDF ---
    pdf_loader = DirectoryLoader(
        data_dir,
        glob="**/*.pdf",
        loader_cls=PyPDFLoader
    )
    documents = pdf_loader.load()

    # --- 3.2 加载 TXT 和 Markdown ---
    text_loader = DirectoryLoader(
        data_dir,
        glob="**/*.{txt,md}",
        loader_cls=TextLoader
    )
    documents += text_loader.load()

    if not documents:
        raise FileNotFoundError(f"data/ 目录下没找到任何文档,请放入 PDF/TXT/MD 文件。")

    # --- 3.3 分割 ---
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=600,        # 每块最多 600 个字符
        chunk_overlap=100,     # 相邻两块重叠 100 个字符
        separators=["\n\n", "\n", "。", ".", " ", ""]
    )
    chunks = splitter.split_documents(documents)

    print(f"加载了 {len(documents)} 个文档,切成 {len(chunks)} 个文本块")
    print(f"\n第一个文本块预览:\n{chunks[0].page_content[:300]}...")
    return chunks


if __name__ == "__main__":
    load_and_split()

验证一下:

python main.py

你应该看到类似输出:

加载了 3 个文档,切成 47 个文本块
第一个文本块预览:
第3章 请假制度
员工每年享有5天带薪年假...

第 4 步:用开源 Embedding 模型构建向量库

这是第一个关键替换点——原版用的是 OpenAI 的 text-embedding-3-small,我们换成 BGE-M3(Ollama 本地部署)。

main.py 中追加:

from langchain_ollama import OllamaEmbeddings
from langchain_community.vectorstores import FAISS

# ========== 第 4 步:构建向量库 ==========

def build_index(chunks, persist_dir="./vector_store"):
    """把文本块转为向量,存入 FAISS 向量库"""

    # 使用 BGE-M3(本地 Ollama 运行,不调任何外部 API)
    embeddings = OllamaEmbeddings(
        model="bge-m3",           # Ollama 上已下载的 Embedding 模型
        base_url="http://localhost:11434"  # Ollama 默认地址
    )

    # 逐块计算向量,构建索引
    vector_store = FAISS.from_documents(chunks, embeddings)

    # 保存到磁盘
    vector_store.save_local(persist_dir)
    print(f"向量库已保存到 {persist_dir}/")
    return vector_store

对比一下两者的差异:

OpenAI 原版开源版
Embedding 模型text-embedding-3-smallbge-m3
运行位置OpenAI 云端你的本地机器
网络要求需要科学上网无需联网
成本$0.02/1M tokens免费
维度512(可调)1024 固定
延迟~100ms~20ms(本地 GPU)
BGE-M3 为什么是中文首选? BGE-M3 由智源研究院(BAAI)开源,在中文检索基准测试中全面领先。它支持 100+ 种语言,同时输出稠密向量和稀疏向量(BM25 风格的 token 权重),一条龙解决"语义检索"和"关键词匹配"两件事。

第 5 步:用开源 LLM 实现问答

第二个关键替换点——把 ChatOpenAI(gpt-4o-mini) 换成本地 Ollama 运行的 Qwen3 或 DeepSeek。

追加到 main.py

from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# ========== 第 5 步:问答 ==========

def ask(question, vector_store, model_name="qwen3:8b"):
    """根据向量库中的文档回答问题"""

    # 使用本地开源模型
    llm = ChatOllama(
        model=model_name,        # qwen3:8b / deepseek-r1:1.5b / qwen3:14b
        temperature=0,
        base_url="http://localhost:11434"
    )

    # 检索最相关的 4 个文本块
    docs = vector_store.similarity_search(question, k=4)

    # 拼接成上下文
    context = "\n\n---\n\n".join(
        f"[来源: {d.metadata.get('source', '未知')}]\n{d.page_content}"
        for d in docs
    )

    # 构造提示词(完全一样)
    prompt = ChatPromptTemplate.from_template("""
你是一个知识助手。请根据以下参考资料回答问题。
如果资料不足以回答,请诚实地说"根据现有资料无法确定"。

参考资料:
{context}

问题:{question}

回答:
""".strip())

    chain = prompt | llm | StrOutputParser()
    answer = chain.invoke({"context": context, "question": question})

    # 打印结果
    print(f"\n检索到 {len(docs)} 个相关片段:")
    for i, d in enumerate(docs):
        print(f"  [{i+1}] {d.metadata.get('source', '?')}: {d.page_content[:80]}...")

    print(f"\n回答:\n{answer}")
    return answer

看到没?除了 ChatOpenAI 改成 ChatOllama,多了个 base_url 参数,其他代码完全一样。这就是 LangChain 的好处——统一抽象,换模型跟换电池一样。


第 6 步:跑起来!

把前面几步串起来。修改 main.py 最后的 if __name__ == "__main__":

if __name__ == "__main__":
    import os

    # 检查 Ollama 是否在运行
    import requests
    try:
        r = requests.get("http://localhost:11434/api/tags", timeout=3)
        if r.status_code != 200:
            raise ConnectionError()
    except:
        print("Ollama 未运行!请先启动 Ollama:")
        print("  - Windows/Mac: 打开 Ollama 桌面应用")
        print("  - Linux: 终端运行 ollama serve")
        exit(1)

    # 如果已有向量库就直接加载,否则先构建
    if os.path.exists("./vector_store/index.faiss"):
        print("加载已有向量库...")
        embeddings = OllamaEmbeddings(
            model="bge-m3",
            base_url="http://localhost:11434"
        )
        vector_store = FAISS.load_local(
            "./vector_store", embeddings,
            allow_dangerous_deserialization=True
        )
    else:
        print("首次运行,构建向量库...")
        chunks = load_and_split("./data")
        vector_store = build_index(chunks)

    # 让用户选择模型
    print("\n可用模型:")
    models = ["qwen3:8b", "qwen3:14b", "deepseek-r1:1.5b", "qwen3:1.8b"]
    for i, m in enumerate(models):
        print(f"  [{i+1}] {m}")
    choice = input("选择模型 (直接回车默认 qwen3:8b):").strip()
    model = models[int(choice)-1] if choice.isdigit() and 1 <= int(choice) <= len(models) else "qwen3:8b"

    # 交互式问答
    print("\n" + "="*50)
    print(f"  你的开源 RAG 助手已就绪!模型:{model}")
    print("  输入问题开始对话,输入 q 退出")
    print("="*50 + "\n")

    while True:
        question = input("你的问题:").strip()
        if question.lower() == "q":
            print("再见!")
            break
        if not question:
            continue
        ask(question, vector_store, model_name=model)
        print("\n" + "-"*50)

完整运行:

首先确保 Ollama 在运行(打开 Ollama 桌面应用,或者终端执行 ollama serve),然后:

python main.py

你会进入一个交互式对话界面:

首次运行,构建向量库...
加载了 3 个文档,切成 47 个文本块
向量库已保存到 ./vector_store/

可用模型:
  [1] qwen3:8b
  [2] qwen3:14b
  [3] deepseek-r1:1.5b
  [4] qwen3:1.8b
选择模型 (直接回车默认 qwen3:8b):

==================================================
  你的开源 RAG 助手已就绪!模型:qwen3:8b
  输入问题开始对话,输入 q 退出
==================================================

你的问题:年假有几天?

检索到 4 个相关片段:
  [1] 员工手册.pdf: 第3章 请假制度。员工每年享有5天带薪年假,工作满1年后可申请...
  [2] 考勤制度.pdf: 年假申请需提前3个工作日提交OA审批...
  ...

回答:
根据《员工手册》的规定,员工每年享有5天带薪年假,工作满1年后可申请。
申请需要提前3个工作日通过OA系统提交。

你的问题:q
再见!

恭喜! 🎉 你用完全开源的技术栈搭建了一个智能文档问答系统。不需要 API Key,不需要联网,数据全在你的电脑上。


第 6.5 步:你刚刚做了什么

停下来看一下架构。和原版唯一的区别在于模型跑在哪

你的文档(PDF/MD/TXT)
    ↓ [加载] 读入程序
    ↓ [分割] 切成几百字的小块
    ↓ [向量化] BGE-M3(本地)把每块变成 1024 维向量
    ↓ [存储] 向量 + 原文存入 FAISS
    ════════════════════════ 准备阶段 ════════════════════════
    ↓
你的问题
    ↓ [向量化] BGE-M3(本地)把问题变成向量
    ↓ [检索] FAISS 找距离最近的 K 个文本块
    ↓ [拼接] 把文本块 + 问题组合成提示词
    ↓ [生成] Qwen3/DeepSeek(本地)生成回答
    ════════════════════════ 问答阶段 ════════════════════════

所有 AI 能力都在你本地运行。你的文档从未离开你的机器。


下篇:深度理解与实战

上篇跑通了一个最朴素的开源 RAG。它能用,但你很快会碰到几个问题:

下面逐一解决。


一、开源 LLM 选型:2026 年的"三足鼎立"

选型速查表

模型中文能力推理速度最低显存适合场景
Qwen3 8B⭐⭐⭐⭐⭐快(~80 tok/s @ 4070)6GB中文 RAG 首选,性价比之王
DeepSeek-V3⭐⭐⭐⭐⭐中(~40 tok/s @ 4090)14GB(量化)复杂推理、数学、代码分析
DeepSeek-R1 1.5B⭐⭐⭐极快(~200 tok/s @ CPU)1.5GBCPU 环境、嵌入式设备
LLaMA-4 8B⭐⭐⭐6GB英文为主、多语言均衡
Phi-4 14B⭐⭐极快10GB英文为主、追求速度
Mistral 8B⭐⭐6GB英文为主、代码生成

选型决策树

你的需求是什么?
├── 中文文档问答
│   ├── 有 GPU(≥6GB 显存)→ Qwen3 8B(首选)或 DeepSeek-V3
│   └── 只有 CPU → DeepSeek-R1 1.5B 或 Qwen3 1.8B
│
├── 英文文档问答
│   ├── 有 GPU → LLaMA-4 8B 或 Mistral 8B
│   └── 只有 CPU → Phi-3 Mini(3.8B,CPU 可用)
│
└── 混合语言 → Qwen3 系列(多语言能力均衡,中文尤强)
为什么不首推 DeepSeek-V3? 因为它是 MoE(混合专家)架构,虽然总参数量 671B 但激活参数只有 37B——问题在于推理时仍然需要把全部权重加载到内存。即使量化版也需要 ~20GB 显存。对于 RAG 这类"查资料 → 回答问题"的场景,8B-14B 的模型已经足够。

快速上手命令

# Qwen3 8B (推荐)
ollama pull qwen3:8b

# DeepSeek-V3 量化版 (需要大显存)
ollama pull deepseek-v3:latest

# LLaMA-4 8B
ollama pull llama4:8b

二、开源 Embedding 模型全对比

2026 年主流开源 Embedding 模型

模型维度最大输入中文 MTEB 排名*特色
BGE-M3 (BAAI)10248192 token🥇稠密+稀疏双输出,100+语言
GTE-Qwen2-7B (阿里)358432768 token🥈Qwen 生态,长文本,高精度
multilingual-e5-large (微软)1024512 token🥉英文为主,多语言均衡
Jina embeddings v310248192 token任务特定 LoRA,灵活切换
stella-base-zh-v31024512 token纯中文,轻量级
nomic-embed-text7688192 token英文轻量,Ollama 内置

\中文 MTEB 排名基于 2025 年底公开评测数据,排名会随时间变化*

选择指南

# 中文为主 → BGE-M3
embeddings = OllamaEmbeddings(model="bge-m3")

# 超长文本(> 8000 token) → GTE-Qwen2
embeddings = OllamaEmbeddings(model="gte-qwen2:1.5b")

# 极致轻量(边缘设备) → stella-base-zh-v3
# 需要 HuggingFace 本地加载,见下方代码
from langchain_huggingface import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(
    model_name="infgrad/stella-base-zh-v3-1792d",
    model_kwargs={"device": "cpu"}
)
BGE-M3 的"隐藏技能"——稀疏向量:BGE-M3 不仅能输出稠密向量(用于语义检索),还能输出稀疏向量(类似 BM25 的词权重)。两者结合就是"混合检索"——第三章会讲怎么用。

三、量化:用 1/4 的显存跑同样的模型

什么是量化?

把模型参数从 16 位浮点数(FP16)压缩到 4 位整数(Q4),体积缩到 1/4,速度翻倍,精度损失通常在 2-5% 以内。对于 RAG 场景——模型只需要"阅读理解 + 总结回答",量化损失几乎感知不到。

Ollama 的量化标签

# 查看模型有哪些量化版本
ollama show qwen3:8b

# 常用量化等级(从大到小,从慢到快)
qwen3:8b           # 默认 Q4_K_M(推荐,精度和速度的最佳平衡)
qwen3:8b-q8_0      # 8-bit 量化,精度更高但显存翻倍
qwen3:8b-q4_0      # 4-bit 最小,速度最快但精度损失最大
qwen3:8b-fp16      # 不量化,原版精度,需要 16GB 显存

实测数据(Qwen3 8B,RTX 4070 12GB)

量化等级显存占用生成速度回答质量(人工评分)
FP1616.4 GB 💥4.3/5
Q8_08.8 GB65 tok/s4.2/5
Q4_K_M(默认)5.2 GB82 tok/s4.1/5
Q4_04.8 GB95 tok/s4.0/5

结论:Q4_K_M 是甜点。和 FP16 比,质量差距在统计误差范围内,但显存省了 68%,速度快了 50%。


四、CPU 优化:没有显卡也能用

模型选小但别选太弱

CPU 推理的瓶颈不在"模型大不大"而在"内存带宽够不够"。关键原则:

# CPU 用户的最佳选择
ollama pull qwen3:1.8b          # 中文 RAG,CPU 可用
ollama pull deepseek-r1:1.5b    # 推理能力好,体积最小

设置 Ollama 使用全部 CPU 核心

修改 Ollama 环境变量(Windows 在系统环境变量中设置,Mac/Linux 在 ~/.bashrc~/.zshrc 中):

# 设置使用的线程数(改成你 CPU 的核心数)
export OLLAMA_NUM_PARALLEL=4
export OLLAMA_NUM_THREADS=8

# 限制内存使用
export OLLAMA_HOST_MEMORY_LIMIT=8GB

CPU vs GPU 延迟对比(Qwen3 1.8B,回答"年假有几天?")

环境Embedding(100 条)LLM 生成(200 token)总体感
RTX 4070 GPU0.8 秒2.1 秒几乎即时
M2 MacBook1.5 秒3.8 秒可用
i7-13700 CPU3.2 秒12 秒可接受
i5-8250U CPU8 秒45 秒需要耐心
CPU 不是不能用。对于"扔进去一批文档 → 过一会儿回来问"的个人场景,10-20 秒的延迟完全可以接受。

五、匹配开源生态的高级检索策略

原版教程里讲了 MMR 去重、多查询检索、Self-Querying。这些高级特性在开源模型上完全可用——LangChain 的检索器不吃模型,吃的是 LLM 接口。只要你的 LLM 实现了 invoke() 方法,就能用。

MMR 检索(原样迁移,零改动)

retriever = vector_store.as_retriever(
    search_type="mmr",
    search_kwargs={
        "k": 4,
        "fetch_k": 20,
        "lambda_mult": 0.6
    }
)

多查询检索(唯一需要改的地方:用本地模型改写查询)

from langchain.retrievers.multi_query import MultiQueryRetriever

# 注意:这里的分词 LLM 也要用本地模型
rewrite_llm = ChatOllama(
    model="qwen3:1.8b",  # 查询改写用小模型就够了
    temperature=0.3,      # 稍微加点温度,生成的变体更多样
    base_url="http://localhost:11434"
)

retriever = MultiQueryRetriever.from_llm(
    retriever=base_retriever,
    llm=rewrite_llm
)
# 会生成 3 个查询变体,合并检索结果去重

BM25 + 稠密混合检索(利用 BGE-M3 的双输出特性)

from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever

# 1. 准备 BM25 检索器(关键词匹配)
# 需要把 chunks 转成纯文本列表
texts = [chunk.page_content for chunk in chunks]
bm25_retriever = BM25Retriever.from_texts(
    texts,
    metadatas=[chunk.metadata for chunk in chunks]
)
bm25_retriever.k = 5

# 2. 稠密检索器(语义匹配)
dense_retriever = vector_store.as_retriever(search_kwargs={"k": 5})

# 3. 融合两者
ensemble = EnsembleRetriever(
    retrievers=[dense_retriever, bm25_retriever],
    weights=[0.7, 0.3]  # 语义为主,关键词为辅
)

效果:搜"订单号 20240618-XXXX"时,BM25 精确匹配到订单号;搜"怎么处理退款"时,稠密检索命中"售后流程"相关段落。两者互补。


六、自部署架构选择:Ollama / vLLM / llama.cpp

当你从 Demo 走向生产,Ollama 的"一条命令"哲学可能不够用。下面是三种主流部署方案的对比:

架构选型速查

OllamavLLMllama.cpp
适合谁个人 / 小团队生产服务嵌入式 / 边缘设备
并发能力有限(默认 1 并发)极高(支持连续批处理)有限
吞吐量中等高(10x+ Ollama)低-中
GPU 利用率~60%~95%CPU 优化
部署复杂度⭐ 极简⭐⭐⭐⭐⭐
PagedAttention
API 兼容OpenAI 兼容OpenAI 兼容需适配

什么时候升级到 vLLM?

三个信号:

  1. 并发用户 > 10:Ollama 排队严重,vLLM 的连续批处理能把吞吐量提 10 倍
  2. GPU 利用率 < 70%:vLLM 的 PagedAttention 能榨干 GPU
  3. 需要自定义采样策略:vLLM 支持 beam search、logprobs 等高级功能
# vLLM 快速起一个服务
pip install vllm
vllm serve Qwen/Qwen3-8B --host 0.0.0.0 --port 8000

然后用 LangChain 的 OpenAI 兼容接口连接:

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="Qwen/Qwen3-8B",
    base_url="http://localhost:8000/v1",
    api_key="not-needed"  # vLLM 本地运行不需要 key
)

七、成本与隐私:开源 RAG 的真正价值

成本对比(10 万条文档,日均 1000 次问答)

项目OpenAI 方案开源方案(本地 GPU)
Embedding(一次性)~$2电费 ~$0.10
LLM 调用(月度)~$150(gpt-4o-mini)电费 ~$15
GPU 硬件不需要RTX 4090 ≈ ¥14000
运维需要管理模型更新
年度总成本~$1824~$180 + 硬件折旧
数据隐私文档发到 OpenAI 服务器数据不离开机器
回本周期:如果你已经有一块 RTX 4090(打游戏的顺便跑 AI),一年能省下 ¥10000+ 的 API 费用。如果专门买一块,大约 18 个月回本——之后全是赚的。

隐私优势

不是所有场景都适合用云端 API:

对这些场景,开源本地部署不是选择,是前提


八、常见问题与排错指南

Q1:运行时报 Connection refused 错误

ConnectionError: HTTPConnectionPool(host='localhost', port=11434)

原因:Ollama 没有在运行。

解决

Q2:Ollama 下载模型太慢

# 设置镜像(如果 Ollama 支持)或者手动下载 GGUF 文件
# 也可以挂代理
set HTTPS_PROXY=http://127.0.0.1:7890  # Windows
export HTTPS_PROXY=http://127.0.0.1:7890  # Mac/Linux

# 然后重新拉取
ollama pull qwen3:8b

Q3:Embedding 计算到一半内存爆了

原因:文本块太多,一次性加载了所有向量。

解决:分批计算

def build_index_batched(chunks, persist_dir="./vector_store", batch_size=50):
    embeddings = OllamaEmbeddings(model="bge-m3")
    
    # 先处理第一批
    vector_store = FAISS.from_documents(chunks[:batch_size], embeddings)
    
    # 逐批追加
    for i in range(batch_size, len(chunks), batch_size):
        batch = chunks[i:i+batch_size]
        vector_store.add_documents(batch)
        print(f"进度:{min(i+batch_size, len(chunks))}/{len(chunks)}")
    
    vector_store.save_local(persist_dir)
    return vector_store

Q4:Qwen3 的回答经常不准确

排查列表:

  1. 先检查检索结果:在代码里打印 docs,看检索到的片段是否相关。如果检索错了,答案不可能对
  2. 调 chunk_size:中文文档 400-600 字最合适,太大检索不精,太小语义不完整
  3. 换大一号的模型qwen3:1.8b 的指令遵循能力远不如 qwen3:8b
  4. 检查提示词:确保提示词里有"如果资料不足以回答,请诚实地说不确定"

Q5:向量库文件损坏了怎么办?

# 删除旧索引,重新构建
rm -rf ./vector_store
python main.py

九、2026 年开源 RAG 的前沿方向

轻量模型崛起

2025-2026 年,1.5B-3B 的小模型质量飞跃式提升。Qwen3 1.8B 在 RAG 场景下的表现已经接近两年前的 7B 模型。这意味着:

多模态开源 RAG

ColQwen2(阿里开源)和 ColPali(法国团队)能在一次 Embedding 中同时处理文本、表格和图片——不用先 OCR 再 Embedding。

# ColQwen2 已经在 Ollama 上可用(实验性)
ollama pull colqwen2:latest

Agentic RAG + 开源工具链

原版教程提到的 Agentic RAG,开源生态也完全支持。LangChain 的 Agent 框架 + Ollama 的本地模型 + MCP(Model Context Protocol)的工具调用,已经形成了一套"闭源级体验、全开源实现"的 Agent 栈。

# 用 Qwen3 做 Agent——支持原生 Function Calling
from langchain.agents import create_tool_calling_agent

llm = ChatOllama(model="qwen3:8b")  # Qwen3 原生支持 tool calling
agent = create_tool_calling_agent(llm, tools, prompt)

总结

这篇教程从零开始,用完全开源的技术栈搭建了一个可用的 RAG 系统。核心三件事:

  1. 选对模型:中文 → Qwen3 + BGE-M3,英文 → LLaMA-4 / Mistral + BGE-M3。量化版(Q4_K_M)是性能和速度的最佳平衡。
  2. 架构匹配需求:个人用 Ollama,生产用 vLLM,边缘设备用 llama.cpp。LangChain 让切换成本几乎为零。
  3. 数据留在本地:这是开源方案最根本的优势——不是省钱,是不妥协。

和原版 OpenAI 方案对比,开源方案的核心取舍:

维度OpenAI 方案开源方案
上手速度⭐⭐⭐⭐⭐ 即开即用⭐⭐⭐ 需要下载模型
回答质量⭐⭐⭐⭐⭐⭐⭐⭐⭐(8B 模型接近,1.5B 有差距)
推理速度⭐⭐⭐(受网络延迟影响)⭐⭐⭐⭐(GPU 本地极快)
成本(长期)⭐⭐⭐⭐⭐⭐⭐
数据隐私⭐⭐⭐⭐⭐⭐⭐
可定制性⭐⭐(只有 prompt)⭐⭐⭐⭐⭐(Fine-tune、量化、修改架构)

最好的方案往往不是"选开源还是闭源",而是"不同环节用不同方案":Embedding 和检索全用开源(反正不依赖网络),最终回答如果对质量要求极高,可以保留 OpenAI 作为降级兜底——但先用本地模型跑,90% 的情况它已经够好了。

希望这篇指南让你在 RAG 之路上不用被 API Key 和网络延迟困扰。如果只有一句话带走,那就是:从 Ollama 开始,用 Qwen3 + BGE-M3,跑通后再考虑升级到 vLLM——不要一上来就搭最复杂的架构。


参考资料 - Ollama 官方文档:https://ollama.com/docs - BGE-M3 论文:https://arxiv.org/abs/2402.03216 - Qwen3 官方博客:https://qwenlm.github.io/blog/qwen3/ - LangChain Ollama 集成:https://python.langchain.com/docs/integrations/llms/ollama/ - vLLM:https://docs.vllm.ai - RAGAS 评估框架:https://docs.ragas.io - 本文原版(OpenAI 方案):从零搭建你的第一个 RAG 应用