Ollama Embedding 实战:本地向量检索与 RAG 搭建
上周我翻遍了电脑里 200 多个 PDF 文档,想找一份半年前看过的技术方案细节。关键词搜索?没戏——我记得的是内容的意思,不是原话。花了快一小时才找到,当时就想:要是有个能”理解语义”的搜索工具就好了。
更麻烦的是,这些文档涉及公司内部架构,传到云端做向量检索?想都别想。隐私红线划得死死的。
后来我发现 Ollama 的 Embedding 功能恰好能解决这个问题。本地跑,数据不出门,还能做语义检索。折腾了几天,把 mxbai、nomic、Qwen3 三种模型都试了一遍,顺便把向量数据库选型也摸了个透。说实话,坑还真不少——模型选错了检索效果差,数据库选小了后面扩容麻烦。
这篇文章就把我的踩坑经验整理出来。读完你就能自己搭一个本地 RAG 系统,从文档处理到语义检索,全流程代码都有。
Ollama Embedding 模型全家桶
Ollama 现在支持好几种 Embedding 模型,一开始我也不知道该选哪个。官方文档说 mxbai 超过了 OpenAI 的 text-embedding-3-large——嗯,听起来挺厉害,但实际用起来效果咋样?我试了一圈,给你个直接答案。
先看这张对比表,心里有个数:
| 模型 | 向量维度 | 上下文长度 | 模型大小 | 特点 |
|---|---|---|---|---|
| mxbai-embed-large | 1024 | 512 tokens | 670M | 通用首选,MTEB 榜单前几 |
| nomic-embed-text | 768 | 8192 tokens | 274M | 长文本支持,支持上下文扩展 |
| Qwen3 Embedding | 1024 | 8192 tokens | 约 600M | 2026 新发布,中文友好 |
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=[{"source": chunk.metadata.get("source", "unknown")}],
)
print(f"已入库 {len(chunks)} 个文档片段")
# 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=[
{
"role": "system",
"content": "基于以下文档内容回答问题,如果文档中没有相关信息,请诚实说明。",
},
{"role": "user", "content": f"文档内容:{context}\n\n问题:{query}"},
],
)
print(f"回答:{response['message']['content']}")
跑起来试试。整体流程其实不复杂:文档分块 → 生成向量 → 存库 → 查询 → 组装回答。
几个坑点提醒:
一是 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: 安装依赖和准备模型
执行以下命令安装依赖:
```bash
pip install ollama chromadb langchain langchain-community pypdf
ollama pull mxbai-embed-large
ollama pull qwen2.5:7b
```
确保 Ollama 服务已启动。 - 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: 生成向量并存入数据库
调用 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: 语义检索和生成回答
将查询转换为向量,检索相关文档,再用 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 应该设置多大?
如何提高 Embedding 生成速度?
相似度阈值应该设多少?
本地 RAG 的主要优点和缺点是什么?
缺点:单机性能有限,百万级以上数据需要升级方案,需要自己维护服务。
9 分钟阅读 · 发布于: 2026年4月8日 · 修改于: 2026年4月8日
相关文章
LangChain + Ollama 集成实战:本地 LLM 应用开发完全指南
LangChain + Ollama 集成实战:本地 LLM 应用开发完全指南
Ollama 多模型并行运行:Qwen、Llama、DeepSeek 配置实战
Ollama 多模型并行运行:Qwen、Llama、DeepSeek 配置实战
Ollama Modelfile 参数详解:创建专属定制模型的完整指南

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