概述
在RAG应用中使用 redis 作为向量数据库。
当使用results = vector_store.similarity_search_with_score(query="测试", k=10, score_threshold=0.1)
查询相似的文本内容时报错Property "vector_distance" not loaded nor in schema
,本文记录一下排查该问题的过程,希望对遇到同样问题的小伙伴提供一些帮助。
复现代码
redisvl使用的schema如下,关于schema的定义说明查看文档 Schema in RedisVL:
python
CHUNK_INDEX_NAME = "{kb_id}:doc_chunk"
chunk_index_schema = {
"index": {
"name": f"{CHUNK_INDEX_NAME}",
"prefix": f"{CHUNK_INDEX_NAME}",
"storage_type": "hash" # 索引存储类型,可选值:hash、json
},
"fields": [
{"name": "doc_id", "type": "tag"},
{"name": "chunk_id", "type": "tag"},
{"name": "kb_id", "type": "tag"},
{"name": "page", "type": "numeric", "attrs": {"sortable": True}},
{"name": "content", "type": "text"},
{
"name": "content_vector",
"type": "vector",
"attrs": {
"dims": 1024, # 向量维度, 在创建索引时必填
"algorithm": "flat", # 向量检索算法, 在创建索引时必填,实际在使用时如果没有设置会默认为flat,可填写:"flat"(精确搜索)"hnsw"(近似搜索)
"distance_metric": "cosine",
"datatype": "float32"
}
}
]
}
使用的代码如下,这里为了方便使用自定义一些方法,继承了RedisVectorStore
:
下面的代码已经修改过了,如果要复现该错误,需要将 schema 中 vector 字段对应的 name 和下面类中的 embedding_field的值修改为其他名称。
python
from typing import Dict, Any, List
from langchain_core.documents import Document
from langchain_core.embeddings import Embeddings
from langchain_redis import RedisVectorStore, RedisConfig
from redis.commands.search.query import Query
from redisvl.query import FilterQuery
from redisvl.query.filter import Tag
from redisvl.schema import IndexSchema
from logging import getLogger
logger = getLogger(__name__)
def _update_dimensions(embeddings: Embeddings, index_schema: Dict[str, Any]):
"""
Update the dimensions of the vector index schema.
:param embeddings:
:param index_schema:
:return:
"""
embedding_dimensions = len(embeddings.embed_query('get vector dimensions'))
for field in index_schema.get('fields'):
if field['type'] == 'vector':
if 'attrs' in field:
field['attrs'].update({'dims': embedding_dimensions})
else:
field['attrs'] = {'dims': embedding_dimensions, 'algorithm': 'flat'}
class RedisSearch(RedisVectorStore):
def __init__(self, embeddings: Embeddings, index_schema: Dict[str, Any]):
# update the dimensions of the index schema
_update_dimensions(embeddings, index_schema)
schema = IndexSchema.from_dict(index_schema)
# 获取redis客户端
config = RedisConfig.from_schema(
schema,
redis_client=redis_client,
content_field="content",
embedding_field="content_vector"
)
super().__init__(embeddings=embeddings, config=config)
@classmethod
def from_kb(cls, embeddings: Embeddings, index_schema: Dict[str, Any], kb_id: str) -> 'RedisSearch':
index_schema['index']['name'] = index_schema['index']['name'].format(kb_id=kb_id)
index_schema['index']['prefix'] = index_schema['index']['prefix'].format(kb_id=kb_id)
return cls(embeddings=embeddings, index_schema=index_schema)
def get_by_doc_id(self,
doc_id: str,
with_metadata: bool = True,
offset: int = None,
num: int = None) -> list[Document]:
"""
根据doc_id查询文档
Args:
doc_id: 文档id
with_metadata: 是否返回元数据
offset: 分页偏移量
num: 分页大小
Returns:
List[Document]: 文档列表
"""
return_fields = [self.config.content_field]
if with_metadata:
return_fields = self.config.index_schema.field_names
query = Tag("doc_id") == doc_id
query_string = str(query)
return self.search_by_query(offset=offset, num=num, query_string=query_string, return_fields=return_fields)
def get_by_kb_id(self, kb_id: str, with_metadata: bool = True, offset: int = 0, num: int = 10) -> List[Document]:
return_fields = [self.config.content_field]
if with_metadata:
return_fields = self.config.index_schema.field_names
query = Tag("kb_id") == kb_id
query_string = str(query)
return self.search_by_query(offset=offset, num=num, query_string=query_string, return_fields=return_fields)
def get_by_tag_name(self, tag_name: str, tag_value: Any, with_metadata: bool = True) -> List[Document]:
return_fields = [self.config.content_field]
if with_metadata:
return_fields = self.config.index_schema.field_names
query = Tag(tag_name) == tag_value
query_string = str(query)
query = (
FilterQuery(query_string)
.return_fields(*return_fields)
.in_order()
.paging(offset=0, num=10000) # 默认num为10000,表示查询所有文档
.dialect(2)
)
logger.info(f'redis查询语句: {query.get_args()}')
results = self.index.query(query)
# Prepare document results
docs = []
for doc in results:
docs.append(
Document(
id=doc.get(self.config.id_field),
page_content=doc[self.config.content_field],
metadata={
k: v
for k, v in doc.items()
if k != self.config.content_field and k != "embedding"
},
)
)
return docs
def delete_by_tag_name(self, tag_name: str, tag_value: Any):
"""
根据tag删除文档
:param tag_name:
:param tag_value:
:return:
"""
query_results = self.get_by_tag_name(tag_name, tag_value, with_metadata=False)
ids = [doc.id for doc in query_results]
if not ids:
logger.info(f"未查询到{tag_name}={tag_value}对应的数据,无需删除")
return
result = self.index.drop_keys(ids)
logger.info(f"删除{tag_name}={tag_value}对应的数据,数量:{result}")
if result != len(ids):
# 删除数据失败
raise Exception(f"删除{tag_name}={tag_value}对应的数据失败,数量:{result},实际删除数量:{len(ids)}")
def delete_by_doc_id(self, doc_id: str):
"""
根据doc_id删除文档
:param doc_id:
:return:
"""
self.delete_by_tag_name("doc_id", doc_id)
def search_by_query(self, query_string: str, return_fields: List[str], offset: int = None, num: int = None):
if num is None or offset is None:
query = (
FilterQuery(query_string)
.return_fields(*return_fields)
.paging(offset=0, num=10000) # 默认num为10000,表示查询所有文档
.in_order()
.dialect(2)
)
else:
query = (
FilterQuery(query_string)
.return_fields(*return_fields)
.paging(offset=offset, num=num)
.in_order()
.dialect(2)
)
results = self.index.query(query)
# Prepare document results
docs = []
for result in results:
content_key = self.config.content_field
page_content = result.pop(content_key)
docs.append(
Document(id=result.get('id'), page_content=page_content, metadata=result)
)
return docs
def count_by_doc_id(self, doc_id: str):
"""
根据doc_id查询数量
:param doc_id:
:return:
"""
query = Tag("doc_id") == doc_id
query = (
Query(str(query))
.paging(offset=0, num=0) # limit 0 0 表示统计数量
.dialect(2)
)
print(query.get_args())
results = self.config.redis().ft(self.config.index_name).search(query)
return results.__dict__
报错信息
运行代码
python
if __name__ == '__main__':
embedding_name = EmbeddingType.M3E_BASE.value
embeddings = init_embedding(embedding_name)
vector_store = RedisSearch.from_kb(embeddings=embeddings, index_schema=chunk_index_schema, kb_id="LAW_LIBRARY")
result = vector_store.similarity_search_with_score(query="测试", k=10, score_threshold=0.1)
print(result)
报错信息如下Property
vector_distance not loaded nor in schema
:
python
raise RedisSearchError(f"Error while searching: {str(e)}") from e
redisvl.exceptions.RedisSearchError: Error while searching: Property `vector_distance` not loaded nor in schema
报错原因分析
最开始使用的是 langchain_community
封装的 Redis
来操作向量数据库,但是从 langchain-community==0.3.13
开始,使用from langchain_community.vectorstores.redis import Redis
被标记为过时,推荐使用langchain_redis.RedisVectorStore
。

langchain_community
封装的Redis
和langchain_redis
的主要区别是对redis操作使用的库不同:langchain_community
基本是自己封装了操redis的各种工具(代码基本和redisvl一致)。langchain_redis
则是直接复用redisvl
来操作redis。
langchain_community
封装的Redis
默认使用的向量字段名不同:langchain_community
封装的Redis
默认使用- content:chunk的内容字段名
- content_vector:chunk的向量字段名
langchain_redis
默认使用的向量字段:- text:chunk的内容字段名
- embedding:chunk的向量字段名
当从langchain_community
封装的 Redis
切换到langchain_redis
后,由于都是使用的默认字段,原来的向量字段是content_vector
,而langchain_redis
的向量字段是embedding
,所以在查询的时候不一致,无法计算vector_distance
,最终报错Property
vector_distance not loaded nor in schema
。
debug排查
发现报错后,首先通过debug看看报错时参入的参数是什么,如下图所示:

从报错的地方可以看到请求的参数和报错的详细内容,请求参数使用 KNN 计算向量的距离,并将计算的结果保存到 vector_distance
字段。从这里可以基本猜到是计算向量字段embedding的相似度错误导致的问题。
问大模型(推荐)
通过debug排查后,一个很自然的想法就是问一下 deepseek,再了解一些排查思路,
输入给大模型的内容如下:
plain
在RAG应用中使用 redis 作为向量数据库,当使用results = vector_store.similarity_search_with_score(query="测试", k=10, score_threshold=0.1)查询相似的文本内容时报错Property "`vector_distance`" not loaded nor in schema
模型回答如下:
在 Redis 作为向量数据库的 RAG 应用中遇到 Property "vector_distance" not loaded nor in schema
错误时,通常是因为查询时未正确加载或定义 vector_distance
字段。以下是分步解决方案:
确认 Redis 索引 Schema 定义
确保在创建 Redis 索引时,明确包含向量距离字段 vector_distance
的定义。例如:
python
from redis.commands.search.field import VectorField, TextField
schema = (
TextField("content"), # 文本字段
VectorField("embedding", # 向量字段名
"HNSW", # 索引类型 (HNSW 或 FLAT)
{
"TYPE": "FLOAT32",
"DIM": 1536, # 向量维度
"DISTANCE_METRIC": "COSINE" # 或 "L2"
}
)
)
确保查询时返回 vector_distance
在查询时,需显式要求返回 vector_distance
字段。例如使用 redis-py
的 FT.SEARCH
命令:
python
from redis.commands.search.query import Query
query = (
Query("*=>[KNN 10 @embedding $query_vector AS vector_distance]")
.return_fields("content", "vector_distance")
.dialect(2)
)
result = redis_client.ft(index_name).search(query, {"query_vector": query_embedding})
检查 LangChain/第三方库的配置
若使用 LangChain 的 Redis
集成,检查 similarity_search_with_score
是否支持 score_threshold
参数。部分版本可能需要手动配置返回字段:
python
from langchain_community.vectorstores.redis import Redis
vector_store = Redis.from_existing_index(
embedding=embedding_model,
index_name="my_index",
schema=schema # 确保传入包含 vector_distance 的 schema
)
results = vector_store.similarity_search_with_score(
query="测试",
k=10,
score_threshold=0.1,
return_fields=["content", "vector_distance"] # 显式指定返回字段
)
更新 Redis 和客户端库
确保使用的 Redis 版本 >= 7.2.0(支持向量搜索),并更新 Python 客户端库:
bash
pip install --upgrade redis redis-py
验证索引状态
检查索引是否已正确创建并包含向量字段:
python
print(redis_client.ft(index_name).info())
# 输出应包含向量字段定义
手动计算距离(备选方案)
如果问题仍未解决,可手动计算相似度得分:
python
from sklearn.metrics.pairwise import cosine_similarity
query_embedding = ... # 获取查询向量
stored_embeddings = ... # 从 Redis 获取存储的向量
scores = cosine_similarity([query_embedding], stored_embeddings)[0]
results = sorted(zip(documents, scores), key=lambda x: x[1], reverse=True)
总结
此错误的核心是 Redis 索引未返回 vector_distance
字段。需确保:
- 索引 Schema 正确包含向量字段和距离度量。
- 查询时显式请求
vector_distance
。 - 使用的库版本和参数配置正确。
若问题持续,检查库的文档或提交 Issue 以获取版本兼容性支持。
报错原因总结
从debug和大模型的回答分析,可以初步判断可能是索引 Schema 未正确包含向量字段和距离度量,使用下面的代码查看 schema 的内容:
python
print(redis_client.ft(index_name).info())
# 输出应包含向量字段定义

排查到这里,我们基本可以得出前面的结论,由于两次查询使用的shema不一致导致的问题。
解决方法
排查到了原因是:由于两次查询使用的shema不一致导致的问题,所以解决该问题也就比较简单了。
解决方法:
- 删除原来的索引
可以使用下面的方法删除索引,delete_documents
参数用来控制是否删除该索引关联的key,如果为True则会删除该索引关联的所有内容。
python
from redisvl.index import SearchIndex
# initialize the index object with schema from file
index = SearchIndex.from_yaml(
"schemas/schema.yaml",
redis_url="redis://localhost:6379",
validate_on_load=True
)
# delete index and data
index.delete(drop=True)
- 重新创建索引
python
from redisvl.index import SearchIndex
# initialize the index object with schema from file
index = SearchIndex.from_yaml(
"schemas/schema.yaml",
redis_url="redis://localhost:6379",
validate_on_load=True
)
# create the index
index.create(overwrite=True, drop=False)
# data is an iterable of dictionaries
index.load(data)
参考文档