切换语言
切换主题

Ollama Embedding 实战:本地向量检索与 RAG 搭建

上周我翻遍了电脑里 200 多个 PDF 文档,想找一份半年前看过的技术方案细节。关键词搜索?没戏——我记得的是内容的意思,不是原话。花了快一小时才找到,当时就想:要是有个能”理解语义”的搜索工具就好了。

更麻烦的是,这些文档涉及公司内部架构,传到云端做向量检索?想都别想。隐私红线划得死死的。

后来我发现 Ollama 的 Embedding 功能恰好能解决这个问题。本地跑,数据不出门,还能做语义检索。折腾了几天,把 mxbai、nomic、Qwen3 三种模型都试了一遍,顺便把向量数据库选型也摸了个透。说实话,坑还真不少——模型选错了检索效果差,数据库选小了后面扩容麻烦。

这篇文章就把我的踩坑经验整理出来。读完你就能自己搭一个本地 RAG 系统,从文档处理到语义检索,全流程代码都有。

Ollama Embedding 模型全家桶

Ollama 现在支持好几种 Embedding 模型,一开始我也不知道该选哪个。官方文档说 mxbai 超过了 OpenAI 的 text-embedding-3-large——嗯,听起来挺厉害,但实际用起来效果咋样?我试了一圈,给你个直接答案。

先看这张对比表,心里有个数:

模型向量维度上下文长度模型大小特点
mxbai-embed-large1024512 tokens670M通用首选,MTEB 榜单前几
nomic-embed-text7688192 tokens274M长文本支持,支持上下文扩展
Qwen3 Embedding10248192 tokens约 600M2026 新发布,中文友好

mxbai-embed-large 是我用得最多的。为啥?简单好用,效果稳定。它的 1024 维向量在大多数场景下够用了,MTEB(Massive Text Embedding Benchmark)榜单上排名很靠前,确实比 OpenAI 的 text-embedding-3-large 分数高那么一点。日常文档检索、代码搜索这类任务,选它准没错。

nomic-embed-text 的亮点是 8192 tokens 的上下文长度。如果你要处理整篇文章或者长对话记录,这就有用了。体积也小,274M 参数,跑起来比 mxbai 快一些。不过向量维度降到 768,语义表达能力理论上会弱一点——实际测试下来,短文本检索差异不大,长文本场景 nomic 更合适。

Qwen3 Embedding 是阿里 2026 年 4 月刚放出来的。中文效果确实好,我拿几篇技术文章测试了下,“分布式系统容错机制”和”容错设计”能匹配上,mxbai 就差点意思。如果你主要处理中文内容,这个值得试试。

选型建议?新手入门直接 mxbai,不用纠结。长文本场景上 nomic,中文内容优先 Qwen3。说白了,三个都试试也就半小时的事,效果才是硬道理。

向量数据库选型指南

模型选好了,数据存哪儿?向量数据库的选择比模型还容易踩坑。选小了后面扩容痛苦,选大了资源浪费。我对比了三种主流方案:

数据库适用场景数据规模特点
ChromaDB入门开发、个人项目< 10万条开箱即用,零配置
FAISS单机高性能、研究实验10-100万条Meta开源,速度极快
Milvus生产部署、企业级百万+条级分布式、可扩展、功能全

ChromaDB 是我最推荐的入门选择。安装一条命令搞定:pip install chromadb。API 设计得很友好,存数据、查数据几行代码就完事。它用 HNSW(Hierarchical Navigable Small World)索引,检索速度对小数据量完全够用。缺点是单机部署,数据量超过 10 万条性能就开始下降。

FAISS 是 Meta 开源的老牌工具。纯 C++ 实现,速度是真的快。我测试过 50 万条数据,检索延迟稳定在毫秒级。不过它更像一个向量检索库,不是完整的数据库——你得自己管存储、索引文件。适合折腾党,或者对性能要求特别高的场景。

Milvus 就不一样了,真正面向生产环境。支持分布式部署、持久化存储、多索引类型,还有云服务版本(Zilliz Cloud)。但配置复杂,部署成本高。百万级数据、需要高可用、团队协作——这种场景才值得投入。

我的选择策略:个人折腾用 ChromaDB,快速能跑起来;研究项目、性能敏感用 FAISS;真要上生产,直接 Milvus 或者云服务。别想着 ChromaDB 后面再迁移到 Milvus——数据格式、API 都不一样,迁移成本不小。

完整 RAG 流程实战

说再多理论不如直接上代码。下面是一个完整的本地 RAG 实现,从 PDF 文档到语义检索,用 Ollama + ChromaDB 搭建。

环境准备

先装依赖:

pip install ollama chromadb langchain langchain-community pypdf

确保 Ollama 已经跑起来了,模型也拉好:

ollama pull mxbai-embed-large
ollama pull qwen2.5:7b  # 用于生成回答

代码实现

import ollama
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import chromadb

# 1. 加载 PDF 文档
loader = PyPDFLoader("./your_document.pdf")
docs = loader.load()

# 2. 文档分块——这点不能马虎,chunk 太大检索不准,太小信息丢失
splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,      # 每块 800 字符
    chunk_overlap=100,   # 重叠 100 字符,避免边界信息丢失
)
chunks = splitter.split_documents(docs)

# 3. 生成 Embedding 并存入 ChromaDB
client = chromadb.Client()
collection = client.create_collection("my_docs")

for i, chunk in enumerate(chunks):
    # 调用 Ollama API 生成向量
    response = ollama.embed(
        model="mxbai-embed-large",
        input=chunk.page_content,
    )
    embedding = response["embeddings"][0]

    # 存入向量数据库
    collection.add(
        ids=[str(i)],
        embeddings=[embedding],
        documents=[chunk.page_content],
        metadatas=[&#123;"source": chunk.metadata.get("source", "unknown")&#125;],
    )

print(f"已入库 &#123;len(chunks)&#125; 个文档片段")

# 4. 语义检索
query = "分布式系统的容错机制是什么?"
query_embedding = ollama.embed(
    model="mxbai-embed-large",
    input=query,
)["embeddings"][0]

results = collection.query(
    query_embeddings=[query_embedding],
    n_results=3,  # 返回最相关的 3 个片段
)

# 5. 用检索结果生成回答
context = "\n\n".join(results["documents"][0])
response = ollama.chat(
    model="qwen2.5:7b",
    messages=[
        &#123;
            "role": "system",
            "content": "基于以下文档内容回答问题,如果文档中没有相关信息,请诚实说明。",
        &#125;,
        &#123;"role": "user", "content": f"文档内容:&#123;context&#125;\n\n问题:&#123;query&#125;"&#125;,
    ],
)

print(f"回答:&#123;response['message']['content']&#125;")

跑起来试试。整体流程其实不复杂:文档分块 → 生成向量 → 存库 → 查询 → 组装回答。

几个坑点提醒

一是 chunk_size 别随便设。我试过 200 字符一块,结果检索出来都是碎片信息,拼不成完整答案。500-1000 这个范围比较稳。

二是 batch 处理。文档量大的时候,一条条调用 Ollama API 会很慢。可以攒一批一起处理:

# 批量生成 Embedding,提速明显
batch_texts = [chunk.page_content for chunk in chunks[:50]]
batch_embeddings = ollama.embed(
    model="mxbai-embed-large",
    input=batch_texts,
)["embeddings"]

三是相似度阈值。ChromaDB 默认返回 n_results 个结果,不管相关不相关。有些场景需要过滤掉不相关的:

# 自定义距离阈值过滤
results = collection.query(
    query_embeddings=[query_embedding],
    n_results=10,
)
# 只保留距离小于 0.3 的结果(距离越小越相似)
filtered = [
    doc for doc, dist in zip(results["documents"][0], results["distances"][0])
    if dist < 0.3
]

这套代码跑完,你就有了自己的本地 RAG。文档换成你的 PDF,query 改成你想问的问题,其他不用动。

性能调优与实践建议

系统跑起来了,接下来就是调优。几个参数直接影响效果,我踩过的坑都告诉你。

chunk_size 选多少

500-1000 字符是我的经验值。太小的坑是语义不完整——一句话被切成两半,检索出来跟问题匹配不上。太大的坑是检索噪音——一个 chunk 包含多个话题,模糊了边界。

不同文档类型也有差异。技术文档结构清晰,可以按段落切;对话记录这种碎片化的,500 字符一块更合适。实际场景多试试,没有通用答案。

batch 批量处理提速

单条调用 Ollama API,每条都要等网络往返。攒成一批 50-100 条一起发,速度能翻好几倍。注意别太大——embedding 模型对输入长度有限制,超出会报错。

相似度阈值怎么定

0.7-0.85 这个范围,看你对准确性的要求。阈值高(比如 0.85),只保留非常相关的结果,召回率低但准确;阈值低(0.7),召回多但可能有噪音。文档库干净、问题明确,阈值设高点;问题模糊、需要更多信息,阈值放低。

一个小建议:先拿 50-100 条数据跑一遍,看检索效果再决定参数。全量数据进去再调,时间成本太大。迭代式调优,边跑边改。

总结

说了这么多,核心就几件事:

模型选择看场景——通用选 mxbai,长文本用 nomic,中文优先 Qwen3。数据库入门 ChromaDB,性能敏感 FAISS,生产环境 Milvus。完整流程代码都有,拿去改改就能跑。

这套方案的优点很明显:本地部署,数据隐私可控;Ollama 模型免费,成本为零;ChromaDB 简单易用,上手门槛低。缺点也有——单机性能上限有限,数据量超过百万就得考虑升级方案。

建议你先用文章里的代码跑一遍,把 50 个文档丢进去试试效果。检索质量好不好、回答准确不准确,实测才知道。参数调好了再扩展到全量数据。

想进一步了解 LangChain 怎么和 Ollama 结合做更复杂的应用,可以看看我之前写的《LangChain + Ollama 集成实战》——那篇讲的是对话链和工具调用,这篇聚焦向量检索,两篇连起来就是完整的本地 LLM 应用开发路线。

搭建本地 RAG 系统

使用 Ollama + ChromaDB 搭建本地向量检索系统

⏱️ 预计耗时: 30 分钟

  1. 1

    步骤1: 安装依赖和准备模型

    执行以下命令安装依赖:

    ```bash
    pip install ollama chromadb langchain langchain-community pypdf
    ollama pull mxbai-embed-large
    ollama pull qwen2.5:7b
    ```

    确保 Ollama 服务已启动。
  2. 2

    步骤2: 加载和分块文档

    使用 PyPDFLoader 加载 PDF,RecursiveCharacterTextSplitter 分块:

    ```python
    loader = PyPDFLoader("./your_document.pdf")
    docs = loader.load()
    splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,
    chunk_overlap=100,
    )
    chunks = splitter.split_documents(docs)
    ```

    chunk_size 建议 500-1000 字符。
  3. 3

    步骤3: 生成向量并存入数据库

    调用 Ollama API 生成 Embedding,存入 ChromaDB:

    ```python
    client = chromadb.Client()
    collection = client.create_collection("my_docs")

    for i, chunk in enumerate(chunks):
    response = ollama.embed(
    model="mxbai-embed-large",
    input=chunk.page_content,
    )
    embedding = response["embeddings"][0]
    collection.add(
    ids=[str(i)],
    embeddings=[embedding],
    documents=[chunk.page_content],
    )
    ```

    文档量大时使用批量处理。
  4. 4

    步骤4: 语义检索和生成回答

    将查询转换为向量,检索相关文档,再用 LLM 生成回答:

    ```python
    query_embedding = ollama.embed(
    model="mxbai-embed-large",
    input=query,
    )["embeddings"][0]

    results = collection.query(
    query_embeddings=[query_embedding],
    n_results=3,
    )

    context = "\n\n".join(results["documents"][0])
    response = ollama.chat(
    model="qwen2.5:7b",
    messages=[
    {"role": "system", "content": "基于文档内容回答问题"},
    {"role": "user", "content": f"文档:{context}\n问题:{query}"},
    ],
    )
    ```

    根据需要调整相似度阈值过滤结果。

常见问题

Ollama 的 Embedding 模型哪个最好用?
没有绝对最好,看场景选择:

• mxbai-embed-large:通用首选,效果稳定,适合大多数场景
• nomic-embed-text:长文本场景,支持 8192 tokens
• Qwen3 Embedding:中文友好,2026 新发布

建议三个都试试,实测效果为准。
ChromaDB、FAISS、Milvus 该选哪个?
根据数据规模和使用场景选择:

• ChromaDB:入门首选,零配置,适合 10 万条以内
• FAISS:性能优先,单机百万级,需要自己管理存储
• Milvus:生产环境,分布式部署,百万级以上数据

个人项目用 ChromaDB,生产环境用 Milvus。
chunk_size 应该设置多大?
建议 500-1000 字符。太小语义不完整,太大检索噪音多。技术文档按段落切,对话记录 500 字符一块。实际场景多测试调整。
如何提高 Embedding 生成速度?
使用批量处理,攒 50-100 条一起调用 Ollama API,比单条调用快好几倍。注意控制批次大小,避免超出模型输入长度限制。
相似度阈值应该设多少?
0.7-0.85 范围内调整。高阈值(0.85)准确但召回少,低阈值(0.7)召回多但可能有噪音。文档库干净、问题明确时设高点,问题模糊时设低点。
本地 RAG 的主要优点和缺点是什么?
优点:数据隐私可控,模型免费零成本,部署简单上手快。

缺点:单机性能有限,百万级以上数据需要升级方案,需要自己维护服务。

9 分钟阅读 · 发布于: 2026年4月8日 · 修改于: 2026年4月8日

评论

使用 GitHub 账号登录后即可评论

相关文章