
本文已同步发布到微信公众号「人言兑」
👈 扫描二维码关注,第一时间获取更新!
在 Ollama 使用教程:本地大模型部署工具完全指南 中提到过 Embeddings,但写得比较简略。这篇文章单独展开说一下,因为我发现很多人在做 RAG(检索增强生成)时卡住的点其实并不在模型本身,而是对 Embeddings 的理解不够透,导致后面检索出来的东西质量很差。本文从Ollama Embeddings嵌入向量基础概念入手,详解其用法、场景及实操步骤,破解常见踩坑点,搭配 Ollama 命令速查手册 ,新手也能轻松上手。
先讲一个简单概念。
计算机不认识「意思」这一说。你给它一句「猫趴在垫子上」,它看到的只是一堆字符。但如果你把这句话转成一个由几百个浮点数组成的数组(比如长度 384 或 768),两个句子的向量在空间里距离近,说明它们的语义相似。嵌入向量就是干这件事的。
Ollama 里跑 Embeddings 模型的时候,模型不生成自然语言回复,只吐出向量。输入「Man 爱吃 pizza」,输出就是类似 [0.023, -0.457, 0.112, ...] 这样的数组。后续你可以拿这个数组和别的句子向量做余弦相似度计算,找语义最接近的文本。
和普通的 LLM(重点是生成)不一样,Embeddings 模型只做「编码」这一半工作。
1. 搜索引擎 / RAG(检索增强生成)
假设你有一堆文档(比如你的笔记、公司内部 Wiki、法律条款),用户问一个问题。你不能把整篇文档都塞进模型的上下文(太长了而且贵),但你可以:先把所有文档切块,每块生成一个 embedding → 存到向量数据库;用户问问题时,把问题也转成 embedding → 在数据库里找最相似的几块 → 把那几块原文 + 用户问题一起发给大模型,让模型基于资料回答。这个流程就是 RAG 的基础。
2. 去重 / 聚类
你想把一堆用户评论按主题分组,或者找重复的客服工单,靠人眼看不过来。可以把每条评论转成 embedding,然后用聚类算法(比如 k-means)自动分堆,或者两两算相似度,超过阈值就认为是重复。
3. 推荐系统 / 搜索排序
用户点击了一篇文章,你就用那篇文章的 embedding 去库里找「看过这篇文章的人还看了什么」——本质就是找最相似的向量。
命令行
最简单的用法:
ollama run embeddinggemma "Hello world"
这个会给出一长串浮点数,你可以直接重定向到文件或者用管道处理。
embeddinggemma 只是其中一种模型,也可以换成 nomic-embed-text、all-minilm 等。
REST API
批量生成(比如你要一次性给 100 段文字做向量):
curl -X POST http://localhost:11434/api/embed \
-H "Content-Type: application/json" \
-d '{
"model": "embeddinggemma",
"input": ["第一段文本", "第二段文本"]
}'
返回的 JSON 里 embeddings 字段是一个二维数组,每个元素对应一个输入的向量。
返回的 embeddings 是一个多维浮点数数组。注意不是所有模型都支持 embedding 功能,需要模型本身是专门训练来做 embedding 的,比如 all-minilm、nomic-embed-text、bge-m3 等。用普通的对话模型(像 qwen3.5)做 embedding 也不是不行,但效果一般,因为训练目标不同。
Python SDK
import ollama
response = ollama.embed(
model='embeddinggemma',
input=['天空是蓝色的', '太阳从东边升起']
)
vectors = response.embeddings
print(len(vectors[0])) # 向量的维度,比如 384
需要注意:embed 函数是同步的,输入太多文本会等很久。如果你的文档集合很大,建议分批调用,或者换用异步方式自己控制并发。
一个易混淆的点:Ollama 有两个 embedding 相关的 API:
两个都能用,但新项目直接用 /api/embed 就行。
和推理模型的关系:embedding 模型往往比对话模型小很多(几十 MB 到一两 GB),因为它们的任务简单——不需要生成自然语言,只需要输出固定维度的向量。所以跑 embedding 的硬件门槛低得多,甚至树莓派都能跑。
Ollama 官方库( https://ollama.com/library?q=embed )里带 embedding 标签的模型不少。我自己试过几个,简单做个对比:
| 模型 | 维度 | 中文支持 | 速度 | 推荐场景 |
|---|---|---|---|---|
| embeddinggemma | 768 | 一般 | 较快 | 通用英文,小项目 |
| nomic-embed-text | 768 | 还凑合 | 中等 | 长文档(8192 token 上下文) |
| all-minilm | 384 | 差 | 极快 | 快速原型,对精度要求不高 |
| bge-m3 | 1024 | 好 | 较慢 | 多语言 + 稠密+稀疏混合检索 |
中文内容多的话,bge-m3 虽然慢但效果好。如果只处理技术文档(术语偏英文),nomic-embed-text 性价比不错。all-minilm 维度低,存储和计算都快,但语义区分度弱一些。
假设你有一堆自己的笔记或者博客文章,想通过问题找到最相关的那几段。
步骤 1:预计算所有笔记内容的向量
import ollama
import json
notes = [
"今天用 Ollama 跑了下 qwen3,速度还行",
"买了新的机械键盘,青轴声音太大了",
"Embeddings 可以用来做相似度计算",
"RAG 需要先把文档切成块,再生成向量",
]
# 存储 (文本, 向量)
corpus = []
for text in notes:
resp = ollama.embed(model='nomic-embed-text', input=[text])
vec = resp.embeddings[0]
corpus.append((text, vec))
步骤 2:用户输入 query,转为向量
query = "怎么用 Ollama 做向量搜索"
query_vec = ollama.embed(model='nomic-embed-text', input=[query]).embeddings[0]
步骤 3:计算余弦相似度,排序
import numpy as np
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
results = []
for text, vec in corpus:
sim = cosine_similarity(query_vec, vec)
results.append((sim, text))
results.sort(reverse=True)
for sim, text in results[:2]:
print(f"{sim:.4f}: {text}")
你会看到和「向量搜索」语义最接近的那条笔记排在前面。这套东西跑通了,后面就能搭 RAG。
RAG 的基本流程:
伪代码:
# 假设已经有了 corpus 向量库(上面 notes 的例子)
def rag_answer(question):
q_vec = ollama.embed(model='nomic-embed-text', input=[question]).embeddings[0]
# 找到最相似的 top-2 文本
scores = [(cosine_similarity(q_vec, vec), text) for text, vec in corpus]
scores.sort(reverse=True)
context = "\n".join([text for _, text in scores[:2]])
prompt = f"基于以下信息回答用户的问题:\n{context}\n\n问题:{question}\n回答:"
response = ollama.generate(model='qwen3:4b', prompt=prompt)
return response['response']
注意:这里为了简单演示,每来一个问题都会重新算一次相似度。工程上应该把向量存到数据库里(如 Chroma、Qdrant、FAISS),然后建索引,才能支撑大量文档。
维度不匹配
不同模型输出的向量维度不一样。比如 all-minilm 是 384 维,bge-m3 是 1024 维。你把两种模型生成的向量混在一个库里,相似度计算会出错(即使强行 dot 也能算,但语义空间不一致)。要么统一用一个模型,要么做模型对齐(很麻烦,不推荐)。
上下文长度
Embeddings 模型也有输入长度限制。nomic-embed-text 支持 8192 token,embeddinggemma 只有 2048。如果某个文本块超过限制,要么截断,要么切得更碎。一般做法是把文档按句子或段落切块,每块控制在 500 token 以内比较安全。
归一化
余弦相似度计算前,很多实现会先把向量 L2 归一化。ollama 返回的原始向量已经是归一化过的吗?我没在文档里找到明确说明。保险的做法还是自己 normalise 一下,或者直接用 np.dot(vec1, vec2) / (norm1 * norm2)。
中文 token 计数
Ollama 的 token izer 对不同模型不同。一个中文字可能占 2-3 token,自己估算长度的时候别按字符数来,最好实际 ollama show <embedding-model> --parameters 看看详细信息。稳妥做法是写一小段代码测试超长输入会报什么错误。
ollama run 就行,别绕一圈嵌入实践中很多 RAG 系统会把 Embedding 检索和关键词检索混合(HyDE、RRF 等)。初期先把纯 Embedding 跑通,后面再慢慢优化。
微信公众号后台自动回复中AI回复其实就是一种RAG,根据你的文章来回复粉丝的私信。相信你看完这篇文章之后也懂了它的实现原理。
如果是自己跑数据,要处理大规模数据(百万级以上),建议上专门的向量数据库 + GPU 加速版 FAISS。Ollama 这边负责生成向量,剩下的检索部分交给别的工具。
以上就是Ollama Embeddings的完整解析,从概念到实操,从场景到避坑,覆盖RAG落地核心需求。如需搭建本地模型基础环境,可参考 本地部署AI大模型完全指南 ;想优化模型性能,可查看 Ollama量化全指南 ,也可通过 Ollama HTTP API实操手册 拓展开发能力,用 Ollama Modelfile 完全指南 定制模型,结合 Ollama launch用法详解 实现多场景复用。