DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

Chroma 报 Embedding dimension 不匹配

Chroma 的请求向量与集合维度不一致,先核对模型、客户端和集合。区分 0.6.3 加载时的函数回退与当前配置持久化行为,保留旧索引后再迁移。

重新打开持久化的 Chroma 集合,查询却报下面的错误:

InvalidDimensionException: Embedding dimension 384 does not match collection dimensionality 768

这表示本次请求送入了 384 维向量,而集合要求 768 维。错误也可能出现在 add、update 或 upsert。两个数字以实际报错为准;它们不是文档数量,也不是 token 长度。

常见的变化发生在加载路径:建库时使用一种模型,另一个进程打开集合时却用了不同的 embedding function。也可能是读到了另一份持久化目录或同名集合。先确认请求中的向量从哪里来,再决定是否需要重建索引。

下面的 Python API 示例按 chromadb 0.6.3 核对。当前版本已支持保存 embedding function 配置,并在其他客户端重构函数;下面关于显式传入自定义函数的旧版处理,不应直接套到所有版本。无论如何,仍需核对实际模型配置与向量来源。

报错的两边分别是什么

Chroma 集合起初没有确定的向量维度,第一次写入向量后便确定维度。后续写入和查询都要匹配这个长度。把集合重新打开,或再次调用 get_or_create_collection,不会把已有的 768 维向量改成 384 维。

先记录实际使用的客户端地址或持久化目录、集合名,以及生成请求向量的模型标识、模型修订和输出维度。远程客户端还要确认 tenant、database 与部署环境。开发机和服务端都叫同一个集合名,不能证明它们访问的是同一份数据。

对于非空集合,可以读取一条已有向量看长度。先备份原持久化目录,使用与建库相容的版本,在隔离副本中诊断。PersistentClient 启动可能初始化或迁移数据库;下面的 get 不增删业务记录,并不表示客户端打开过程是只读的。

目录和名称都是占位值,应换成已经确认的隔离副本位置。目录不存在时不要继续用它排查,以免打开一份新的空库:

from pathlib import Path
import chromadb

persist_dir = Path("/srv/example/chroma-diagnostic-copy")
if not persist_dir.is_dir():
    raise FileNotFoundError(persist_dir)

client = chromadb.PersistentClient(path=str(persist_dir))
collection = client.get_collection(
    name="documents_v1",
    embedding_function=None,
)
rows = collection.get(limit=1, include=["embeddings"])
embeddings = rows["embeddings"]
if embeddings is not None and len(embeddings) > 0:
    print("stored dimension:", len(embeddings[0]))
else:
    print("no stored embedding available")

这里显式禁用自动生成向量,避免诊断时又调用另一种模型。空集合没有可用样本时,不能从这段输出推断历史维度。也不要用 if embeddings 判断 NumPy 数组是否存在;这类判断可能触发 数组真值不明确的错误。

查询文本和查询向量要分开检查

调用 query_texts 时,客户端需要先把文本变成向量。在 0.6.3 中,取得集合时若省略 embedding_function,可能落入默认函数,而不是应用原先使用的自定义函数。默认函数产生的维度与旧库不同,就会在查询阶段报错。

若要继续使用旧索引,应在所有相关的 create、get 和 get_or_create 路径绑定原来适用的函数,并保留其模型与文档、查询编码约定。不能只改建库脚本,遗漏服务启动后的加载入口。

调用 query_embeddings 时,向量由调用方提供,应直接检查那份数组的每行长度。下例假设文件保存了由旧库对应模型生成的一条查询向量;不是随意凑出的 768 个数字:

import json

with open("query-vector.json", encoding="utf-8") as stream:
    query_vectors = json.load(stream)

if len(query_vectors) != 1 or len(query_vectors[0]) != 768:
    raise ValueError("expected one 768-dimensional query vector")

result = collection.query(
    query_embeddings=query_vectors,
    n_results=1,
)

文件名和 768 都需按真实模型调整,前提是目标集合非空。这个例子用于隔离“自动生成向量”这一步,不会修复模型选错的问题。即使两个模型都输出 768 维,也不代表它们的向量在同一个空间里;维度检查通过只能说明形状匹配。

确实更换模型时,新建一份索引

如果应用有意换模型,应保留旧集合,把原始文档按新模型重新编码到一个新名称的集合。仅给已有向量补零、截掉几列,或者修改数据库中的维度字段,都不能把旧模型的向量转换成新模型的表示。

迁移时同时保存文档 ID、文本、元数据和模型配置。确认新集合的条数、查询向量来源及代表性查询结果后,再切换应用读取目标。两套集合并存会增加存储占用,但允许应用在新索引异常时切回旧版本。不要在排查阶段先删除旧集合。

恢复原模型的场景,验收应从发生故障的那个服务进程执行查询,确认它读取原集合、使用正确函数,且不再出现维度错误。有意更换模型的场景,还要检查检索结果是否符合业务预期;“查询不报错”并不能证明语义检索正确。