调用 Ollama 的 /api/embeddings,为什么返回空向量
Ollama 的 embed 与 embeddings 使用不同路径、输入字段和向量形状。返回空数组时核对实际请求,区分新旧 SDK 方法与模型迁移。
向 Ollama 发送了文本,响应却是 {"embedding": []},甚至没有 HTTP 错误。若请求地址是 /api/embeddings,正文里写的却是 input,先改正接口与字段的配对。
Ollama 原生接口中,/api/embed 与 /api/embeddings 是不同契约。下面按服务端 0.11.4、Python 客户端 0.5.3 核对;旧接口仍存在,但已由新接口取代,不应说成已经删除。
三处要一起对应
/api/embed:文本字段是input,接受字符串或字符串列表;响应的向量字段是embeddings,为二维列表。/api/embeddings:文本字段是prompt,接受单个字符串;响应的向量字段是embedding,为一维列表。
在 0.11.4 的旧接口中,缺少或为空的 prompt 会走加载模型的分支,返回空向量。把 input 发到旧地址时,并没有把文本写进它读取的 prompt 字段;HTTP 200 不能证明完成了文本编码。
反过来,把 prompt 发到新地址,同样不符合新接口的 input 约定。别只改一个字段后继续沿用另一套响应读取代码,例如迁移到 embed 后仍读 data["embedding"]。
使用新接口,核对每个输入的向量
下例向本机服务发送编码请求。模型名为占位值,应换成已经安装并确认适用于 embedding 的模型;示例不负责下载模型:
import requests
texts = ["向量检索", "全文搜索"]
response = requests.post(
"http://localhost:11434/api/embed",
json={
"model": "your-installed-embedding-model",
"input": texts,
"truncate": False,
},
timeout=(5, 120),
)
response.raise_for_status()
data = response.json()
if "error" in data:
raise RuntimeError(data["error"])
vectors = data["embeddings"]
if len(vectors) != len(texts) or any(len(vector) == 0 for vector in vectors):
raise ValueError("embedding count or dimension is invalid")
即使只传一个字符串,新接口响应的 embeddings 仍是向量列表,需要先取对应那一行,再把一维向量交给下游。truncate=False 让超出当前上下文的输入明确失败;它不会扩大上下文,也不代表任意模型都支持同样长度。
有意维持旧接口的代码,则发送 {"model": "...", "prompt": "实际文本"},读取 embedding。把接口地址、请求模型、字段名和响应形状记录在同一配置或封装中,可以避免一半新、一半旧。
Python SDK 的方法也各自配对
客户端 0.5.3 的 embed(model=..., input=...) 对应 /api/embed;embeddings(model=..., prompt=...) 对应 /api/embeddings,后者标注弃用。不能因一次手写 HTTP 请求用了错误字段,就断言 SDK 把 input 发到了旧地址。
如果直接调用 SDK 正常,经过自己的代理后为空,检查代理的路径重写和 JSON 字段转换。日志避免记录原始业务文本;诊断只需要确认路径、字段是否存在、输入条数和输出维度。
字段配对正确却仍为空,还要检查是否实际传入空字符串或空列表,以及模型和错误响应。新接口没有有效输入时也可能返回空 embeddings;不能用空列表填充成零向量继续写入索引。
迁移接口或更换模型后,核对向量维度与下游索引要求。维度一致也不能证明两个模型的语义空间一致;已经有 Chroma 索引时,见 模型与集合向量维度不匹配的处理。保留原接口配置和旧索引,确认编码与代表性检索结果后再切换。


