常用向量数据库
约 13484 字大约 45 分钟
2026-09-15
合集说明
本篇精选自参考文档「Vector Databases」章节:总览、理解向量,以及国内最常用的 Elasticsearch、Milvus、PGvector、Redis,按官方文档顺序排列:(各篇之间以 --- 分隔)
- 向量数据库(Vector Databases)总览
- 理解向量(Understanding Vectors)
- Elasticsearch 向量存储
- Milvus 向量存储
- PGvector 向量存储
- Redis 向量存储
向量数据库(Vector Databases)总览
📌 原文地址:https://docs.spring.io/java-ai/reference/api/vectordbs.html
📌 原文版本:Java AI 2.0.1(Stable)
向量数据库(Vector Database) 是一种专用数据库,在 AI 应用中扮演着至关重要的角色。
在向量数据库中,查询方式与传统关系型数据库不同。它们不进行精确匹配,而是执行相似性搜索(Similarity Search)。当给定一个向量作为查询时,向量数据库返回与查询向量"相似"的向量。关于这种相似度如何在较高层面计算的更多细节,请参见"Vector Similarity(向量相似度)"部分。
向量数据库用于将你的数据与 AI 模型集成。使用的第一步是将数据加载到向量数据库中。随后,当用户查询要发送给 AI 模型时,会先检索出一组相似的文档。这些文档随后作为用户问题的上下文,与用户查询一起发送给 AI 模型。这种技术即被称为 检索增强生成(Retrieval Augmented Generation,RAG)。
白话解读
向量数据库是 LLM 的"外接大脑"——LLM 本身知识有截止日期,向量库帮你存最新的资料;用户提问时先把"最相关的那几段"找出来,一并塞给 LLM。Java AI 把这一套抽象成统一的 VectorStore 接口,下面接了 20+ 种真实向量库实现。
一、API 概览(API Overview)
Java AI 通过 VectorStore 接口及其只读对应接口 VectorStoreRetriever,提供了与向量数据库交互的抽象 API。
1.1 VectorStoreRetriever 接口
Java AI 提供了一个名为 VectorStoreRetriever 的只读接口,仅暴露文档检索功能:
@FunctionalInterface
public interface VectorStoreRetriever {
// 相似性搜索,接受一个 SearchRequest
List<Document> similaritySearch(SearchRequest request);
// 便捷方法:直接用字符串查询构建 SearchRequest
default List<Document> similaritySearch(String query) {
return this.similaritySearch(SearchRequest.builder().query(query).build());
}
}该函数式接口专为"只需从向量存储中检索文档、而不执行任何变更操作"的场景设计。它遵循最小权限原则(Principle of Least Privilege),只暴露检索所必需的功能。
1.2 VectorStore 接口
VectorStore 接口扩展了 VectorStoreRetriever,并新增了变更(写入/删除)能力:
public interface VectorStore extends DocumentWriter, VectorStoreRetriever {
// 返回实现类的简单名称作为向量库名称
default String getName() {
return this.getClass().getSimpleName();
}
// 添加文档(写入向量库)
void add(List<Document> documents);
// 按文档 ID 列表删除
void delete(List<String> idList);
// 按过滤表达式对象删除
void delete(Filter.Expression filterExpression);
// 按字符串形式的过滤表达式删除(默认方法)
default void delete(String filterExpression) { ... }
// 获取底层原生客户端(可选)
default <T> Optional<T> getNativeClient() {
return Optional.empty();
}
}VectorStore 接口同时包含读操作与写操作,允许你向向量数据库中添加、删除和搜索文档。
1.3 SearchRequest Builder
public class SearchRequest {
// 接受所有相似度的阈值(全接受)
public static final double SIMILARITY_THRESHOLD_ACCEPT_ALL = 0.0;
// 默认返回的 topK 数量
public static final int DEFAULT_TOP_K = 4;
private String query = "";
private int topK = DEFAULT_TOP_K;
private double similarityThreshold = SIMILARITY_THRESHOLD_ACCEPT_ALL;
@Nullable
private Filter.Expression filterExpression;
// 基于已有 SearchRequest 复制构建新的 Builder
public static Builder from(SearchRequest originalSearchRequest) {
return builder().query(originalSearchRequest.getQuery())
.topK(originalSearchRequest.getTopK())
.similarityThreshold(originalSearchRequest.getSimilarityThreshold())
.filterExpression(originalSearchRequest.getFilterExpression());
}
public static class Builder {
// ... query() / topK() / similarityThreshold() / similarityThresholdAll()
// filterExpression() / build() 等方法
}
}SearchRequest.Builder 的常用方法:
query(String):设置查询字符串,不能为 nulltopK(int):返回的最大相似文档数量,必须 >= 0similarityThreshold(double):相似度阈值,必须在[0,1]区间内similarityThresholdAll():接受所有相似度结果(设为 0.0)filterExpression(Filter.Expression)/filterExpression(String):设置过滤表达式
1.4 Document 与嵌入说明
要向向量数据库插入数据,需将其封装在一个 Document 对象中。Document 类封装了来自数据源(如 PDF 或 Word 文档)的内容,其文本以字符串表示。它还包含以键值对形式存在的元数据(metadata),例如文件名等详细信息。
插入向量数据库时,文本内容会通过嵌入模型(Embedding Model)转换为数值数组(float[]),即向量嵌入(Vector Embeddings)。诸如 Word2Vec、GLoVE、BERT,或 OpenAI 的 text-embedding-ada-002 等嵌入模型,可将单词、句子或段落转换为向量嵌入。
⚠️ 向量数据库的职责是存储并支持对这些嵌入的相似性搜索。它本身不生成嵌入。要创建向量嵌入,应当使用
EmbeddingModel。
接口中的 similaritySearch 方法允许检索与给定查询字符串相似的文档。这些方法可通过以下参数进行微调:
- k:一个整数,指定返回的相似文档最大数量。通常称为 "Top K" 搜索,或 "K 近邻(KNN)"。
- threshold:一个 0 到 1 之间的双精度值,值越接近 1 表示相似度越高。默认情况下,如果你设置阈值为 0.75,则只有相似度高于该值的文档才会被返回。
- Filter.Expression:用于传递一个流式 DSL(领域特定语言)表达式的类,其作用类似 SQL 中的
where子句,但仅作用于Document的元数据键值对。 - filterExpression:一种基于 ANTLR4 的外部 DSL,接受字符串形式的过滤表达式。例如,对于元数据键
country、year和isActive,可以使用如下表达式:country == 'UK' && year >= 2020 && isActive == true。
二、模式初始化(Schema Initialization)
某些向量存储在使用前需要初始化其后端模式(schema)。默认情况下它不会为你自动初始化。你必须主动选择启用,方法是为相应的构造函数参数传入布尔值,或者在使用 Spring Boot 时,在 application.properties 或 application.yml 中将相应的 initialize-schema 属性设置为 true。具体属性名请查阅你所使用向量存储的文档。
三、批处理策略(Batching Strategy)
在使用向量存储时,经常需要嵌入大量文档。虽然一次性调用嵌入所有文档看起来很简单,但这种方式可能导致问题。嵌入模型将文本作为 token 处理,并且有最大 token 限制,通常称为上下文窗口大小(Context Window Size)。这一限制约束了单次嵌入请求可处理的文本量。尝试在单次调用中嵌入过多 token 可能导致错误或嵌入被截断。
为了解决 token 限制问题,Java AI 实现了批处理策略。该方法将大批文档拆分为更小的批次,以适配嵌入模型的最大上下文窗口。批处理不仅解决了 token 限制问题,还能提升性能、更高效地利用 API 速率限制。
Java AI 通过 BatchingStrategy 接口提供此功能,允许根据文档的 token 数量将其拆分为子批次。
public interface BatchingStrategy {
// 将文档列表拆分为多个批次
List<List<Document>> batch(List<Document> documents);
}3.1 默认实现
Java AI 提供了一个名为 TokenCountBatchingStrategy 的默认实现。该策略基于文档的 token 数量进行批处理,确保每个批次不超过计算出的最大输入 token 数。
TokenCountBatchingStrategy 的关键特性:
- 使用 OpenAI 的最大输入 token 数(8191)作为默认上限。
- 包含一个预留百分比(默认 10%),为潜在的开销提供缓冲。
- 实际最大输入 token 数计算公式为:
actualMaxInputTokenCount = originalMaxInputTokenCount * (1 - RESERVE_PERCENTAGE)
你也可以自定义 TokenCountBatchingStrategy 以更好地满足特定需求:
@Configuration
public class EmbeddingConfig {
@Bean
public BatchingStrategy customTokenCountBatchingStrategy() {
return new TokenCountBatchingStrategy(
EncodingType.CL100K_BASE, // 编码类型
8000, // 最大输入 token 数
0.1 // 预留百分比
);
}
}此外,TokenCountBatchingStrategy 还提供了灵活性,允许你传入自己的 TokenCountEstimator 接口实现:
TokenCountEstimator customEstimator = new YourCustomTokenCountEstimator();
TokenCountBatchingStrategy strategy = new TokenCountBatchingStrategy(
this.customEstimator,
8000, // maxInputTokenCount
0.1, // reservePercentage
Document.DEFAULT_CONTENT_FORMATTER,
MetadataMode.NONE
);3.2 使用自动截断(Auto-Truncation)
某些嵌入模型(如 Vertex AI text embedding)支持 auto_truncate 特性。启用后,模型会静默截断超出最大尺寸的文本输入并继续处理;禁用时,对于过大的输入会抛出显式错误。
启用自动截断时,请将批处理策略的最大输入 token 数设置得远高于模型的实际限制。这可以防止批处理策略对大型文档抛出异常,从而让嵌入模型在内部处理截断。
@Configuration
public class AutoTruncationEmbeddingConfig {
@Bean
public VertexAiTextEmbeddingModel vertexAiEmbeddingModel(
VertexAiEmbeddingConnectionDetails connectionDetails) {
VertexAiTextEmbeddingOptions options = VertexAiTextEmbeddingOptions.builder()
.model(VertexAiTextEmbeddingOptions.DEFAULT_MODEL_NAME)
.autoTruncate(true) // 启用自动截断
.build();
return new VertexAiTextEmbeddingModel(connectionDetails, options);
}
@Bean
public BatchingStrategy batchingStrategy() {
// 仅当嵌入模型启用了自动截断时,才使用较高的 token 限制
return new TokenCountBatchingStrategy(
EncodingType.CL100K_BASE,
132900, // 人为设置的高限制
0.1 // 10% 预留
);
}
@Bean
public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel, BatchingStrategy batchingStrategy) {
return PgVectorStore.builder(jdbcTemplate, embeddingModel)
.build();
}
}最佳实践:使用自动截断时:
- 将批处理策略的最大输入 token 数设置得至少比模型的实际限制大 5–10 倍,以避免批处理策略过早抛出异常。
- 监控日志中来自嵌入模型的截断警告。
- 静默截断可能导致嵌入不完整。如果你的应用要求所有内容都被嵌入,请在嵌入前将文档拆分为更小的块。
3.3 自定义实现
要自定义批处理策略,请在 Spring Boot 应用中定义一个 BatchingStrategy Bean:
@Configuration
public class EmbeddingConfig {
@Bean
public BatchingStrategy customBatchingStrategy() {
return new CustomBatchingStrategy();
}
}Java AI 支持的向量存储均已配置为使用默认的 TokenCountBatchingStrategy。
四、VectorStore 实现列表
| 实现 | 说明 |
|---|---|
| Azure Vector Search | Azure 向量存储 |
| Apache Cassandra | Apache Cassandra 向量存储 |
| Chroma Vector Store | Chroma 向量存储 |
| Elasticsearch Vector Store | Elasticsearch 向量存储 |
| GemFire Vector Store | GemFire 向量存储 |
| MariaDB Vector Store | MariaDB 向量存储 |
| Milvus Vector Store | Milvus 向量存储 |
| MongoDB Atlas Vector Store | MongoDB Atlas 向量存储 |
| Neo4j Vector Store | Neo4j 向量存储 |
| OpenSearch Vector Store | OpenSearch 向量存储 |
| Oracle Vector Store | Oracle 数据库向量存储 |
| PgVector Store | PostgreSQL/PGVector 向量存储 |
| Pinecone Vector Store | Pinecone 向量存储 |
| Qdrant Vector Store | Qdrant 向量存储 |
| Redis Vector Store | Redis 向量存储 |
| Typesense Vector Store | Typesense 向量存储 |
| Weaviate Vector Store | Weaviate 向量存储 |
| S3 Vector Store | AWS S3 向量存储 |
| SimpleVectorStore | 简单向量存储实现,仅适用于测试目的 |
- Azure Cosmos DB 向量存储支持作为一个由 Azure Cosmos DB 团队维护的外部模块提供。
SimpleVectorStore实现并非为生产环境设计,仅应用于测试或演示目的。
五、示例用法(Example Usage)
5.1 写入向量存储
@Autowired
VectorStore vectorStore;
void load(String sourceFile) {
JsonReader jsonReader = new JsonReader(new FileSystemResource(sourceFile),
"price", "name", "shortDescription", "description", "tags");
List<Document> documents = jsonReader.get();
this.vectorStore.add(documents);
}5.2 读取向量存储
@Autowired
VectorStoreRetriever retriever; // 此处也可以使用 VectorStore
String question = "<question from user>";
List<Document> similarDocuments = retriever.similaritySearch(question);
// 或者使用更具体的搜索参数
SearchRequest request = SearchRequest.builder()
.query(question)
.topK(5) // 返回前 5 条结果
.similarityThreshold(0.7) // 仅返回相似度得分 >= 0.7 的结果
.build();
List<Document> filteredDocuments = retriever.similaritySearch(request);5.3 读写操作分离
// 需要完整访问权限的服务中的写操作
@Service
class DocumentIndexer {
private final VectorStore vectorStore;
DocumentIndexer(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}
public void indexDocuments(List<Document> documents) {
vectorStore.add(documents);
}
}
// 只需要检索的只读服务
@Service
class DocumentRetriever {
private final VectorStoreRetriever retriever;
DocumentRetriever(VectorStoreRetriever retriever) {
this.retriever = retriever;
}
public List<Document> findSimilar(String query) {
return retriever.similaritySearch(query);
}
}六、使用 VectorStoreRetriever 进行检索操作
VectorStoreRetriever 接口提供向量存储的只读视图,仅暴露相似性搜索功能。这在 RAG 应用中特别有用——因为这些应用只需要检索文档,而不需要修改底层数据。
优势:
- 关注点分离:清晰区分读操作与写操作。
- 接口隔离:只需要检索功能的客户端不会暴露在变更方法之下。
- 函数式接口:可通过 lambda 表达式或方法引用实现。
- 减少依赖:只执行搜索的组件无需依赖完整的
VectorStore接口。
示例: 与 RAG 应用集成
@Service
public class RagService {
private final VectorStoreRetriever retriever;
private final ChatModel chatModel;
public RagService(VectorStoreRetriever retriever, ChatModel chatModel) {
this.retriever = retriever;
this.chatModel = chatModel;
}
public String generateResponse(String userQuery) {
// 检索相关文档
List<Document> relevantDocs = retriever.similaritySearch(userQuery);
// 从文档中提取内容用作上下文
String context = relevantDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
// 使用检索到的上下文生成响应
String prompt = "Context information:\n" + context + "\n\nUser query: " + userQuery;
return chatModel.generate(prompt);
}
}七、元数据过滤器(Metadata Filters)
7.1 过滤器字符串
你可以将类似 SQL 的过滤表达式作为字符串传递给某个 similaritySearch 重载方法。
示例:
"country == 'BG'""genre == 'drama' && year >= 2020""genre in ['comedy', 'documentary', 'drama']"
7.2 Filter.Expression DSL
你可以使用 FilterExpressionBuilder 创建一个 Filter.Expression 实例,它暴露了流式 API:
FilterExpressionBuilder b = new FilterExpressionBuilder();
Expression expression = this.b.eq("country", "BG").build();运算符:
- 比较:
==-+>>=<<=!= - 组合:
AND/and/&&,OR/or/|| - 集合:
IN/in,NIN/nin,NOT/not - 空值:
IS/is,NULL/null,NOT NULL/not null
参考示例:
// genre == 'drama' AND year >= 2020
Expression exp = b.and(b.eq("genre", "drama"), b.gte("year", 2020)).build();
// genre IN ('drama','documentary') AND NOT (year < 2020)
Expression exp = b.and(b.in("genre", "drama", "documentary"), b.not(b.lt("year", 2020))).build();
// year IS NULL / year IS NOT NULL
Expression exp = b.and(b.isNull("year")).build();
Expression exp = b.and(b.isNotNull("year")).build();注意:
IS NULL和IS NOT NULL尚未在所有向量存储中实现。
7.3 分区共享索引
单个向量存储通常被许多逻辑文档组共享——例如一个索引存放多个客户、项目或知识库的内容。VectorStore 作用于整个索引,并根据相似度和你提供的过滤器返回或删除文档。
方法是在写入时为每个文档打上标识其所属组的元数据标签,然后在搜索和删除时应用匹配的过滤器:
// 写入时,记录文档所属的组
Document document = new Document(content, Map.of("group", groupId));
vectorStore.add(List.of(document));
// 搜索时,选择该组中的文档
List<Document> results = vectorStore.similaritySearch(SearchRequest.builder()
.query(query)
.filterExpression("group == '" + groupId + "'")
.build());
// 按过滤器删除时,定位到同一个组
Filter.Expression scoped = new FilterExpressionBuilder().eq("group", groupId).build();
vectorStore.delete(scoped);八、从向量存储中删除文档
8.1 按文档 ID 删除
void delete(List<String> idList);此方法会移除所有 ID 与所提供列表匹配的文档。如果列表中的某个 ID 在存储中不存在,则会被忽略。
Document document = new Document("The World is Big",
Map.of("country", "Netherlands"));
vectorStore.add(List.of(document));
// 按 ID 删除文档
vectorStore.delete(List.of(document.getId()));8.2 按过滤表达式删除
void delete(Filter.Expression filterExpression);Document bgDocument = new Document("The World is Big",
Map.of("country", "Bulgaria"));
Document nlDocument = new Document("The World is Big",
Map.of("country", "Netherlands"));
vectorStore.add(List.of(bgDocument, nlDocument));
// 使用过滤表达式删除来自 Bulgaria 的文档
Filter.Expression filterExpression = new Filter.Expression(
Filter.ExpressionType.EQ,
new Filter.Key("country"),
new Filter.Value("Bulgaria")
);
vectorStore.delete(filterExpression);8.3 按字符串过滤表达式删除
void delete(String filterExpression);此方法会在内部将提供的字符串过滤器转换为 Filter.Expression 对象。
vectorStore.delete("country == 'Bulgaria'");8.4 调用删除 API 时的错误处理
所有删除方法在遇到错误时都可能抛出异常。最佳实践是将删除操作包裹在 try-catch 块中:
try {
vectorStore.delete("country == 'Bulgaria'");
}
catch (Exception e) {
logger.error("Invalid filter expression", e);
}8.5 文档版本管理用例
一个常见场景是管理文档版本:你需要上传文档的新版本,同时移除旧版本:
// 创建初始文档(v1),带版本元数据
Document documentV1 = new Document(
"AI and Machine Learning Best Practices",
Map.of(
"docId", "AIML-001",
"version", "1.0",
"lastUpdated", "2024-01-01"
)
);
vectorStore.add(List.of(documentV1));
Document documentV2 = new Document(
"AI and Machine Learning Best Practices - Updated",
Map.of(
"docId", "AIML-001",
"version", "2.0",
"lastUpdated", "2024-02-01"
)
);
// 使用过滤表达式删除旧版本
Filter.Expression deleteOldVersion = new Filter.Expression(
Filter.ExpressionType.AND,
new Filter.Expression(Filter.ExpressionType.EQ, new Filter.Key("docId"), new Filter.Value("AIML-001")),
new Filter.Expression(Filter.ExpressionType.EQ, new Filter.Key("version"), new Filter.Value("1.0"))
);
vectorStore.delete(deleteOldVersion);
// 添加新版本
vectorStore.add(List.of(documentV2));8.6 删除文档时的性能考虑
- 当确切知道要移除哪些文档时,按 ID 列表删除通常更快。
- 基于过滤器的删除可能需要扫描索引以查找匹配文档。
- 大型删除操作应分批执行,以避免压垮系统。
- 当基于文档属性删除时,优先考虑使用过滤表达式,而不是先收集 ID。
理解向量(Understanding Vectors)
📌 原文地址:https://docs.spring.io/java-ai/reference/api/vectordbs/understand-vectordbs.html
📌 原文版本:Java AI 2.0.1(Stable)
向量具有维度(dimensionality)和方向(direction)。
下图描绘了笛卡尔坐标系中的一个二维向量(以箭头形式呈现):
• ← 向量的头部
↗
↗
↗ 向量
────•─────────→ x 轴
y轴方向分量向量 (\vec{v}) 的头部位于点 ((x, y)):
- x 坐标值(水平分量)
- y 坐标值(垂直分量)
这些坐标也被称为向量的分量(components)。
💡 在 AI/ML 应用中,向量通常具有数百甚至数千个维度,远超 2D/3D 可视化的范围。
白话解读
这一节用纯数学的方式解释"为什么向量之间能比相似度"——核心是余弦相似度公式。读到这里你只要记住:两个向量夹角越小,就越相似。具体数学推导不需要记住,但要理解 Java AI 内置的 SimpleVectorStore 用的是这个公式。
一、相似度(Similarity)
有多种数学公式可用来判断两个向量是否相似。其中最直观、最易于可视化和理解的就是余弦相似度(Cosine Similarity)。
三种关系示意
| 向量关系 | 角度特征 | 含义 |
|---|---|---|
| 相似 | 夹角小,方向接近 | 两向量在语义空间中表达相近 |
| 不相关 | 夹角 ≈ 90°,互相垂直 | 两向量无语义关联 |
| 相反 | 夹角 ≈ 180° | 两向量在语义上对立 |
向量 (\vec{a}) 和 (\vec{b}) 之间的夹角 (\theta) 是衡量它们相似度的一个良好指标。
二、关键数学基础
(1) 勾股定理(Pythagorean Theorem)
用于直角三角形:
[ a^2 + b^2 = c^2 ]
(2) 余弦定理(Law of Cosines)
用于任意三角形(非 90° 时):
[ c^2 = a^2 + b^2 - 2ab \cos(\theta) ]
三、向量的关键运算
(1) 向量模(Magnitude)
对于二维向量 (\vec{A} = (A_x, A_y)):
[ |\vec{A}| = \sqrt{A_x^2 + A_y^2} ]
(2) 向量点积(Dot Product)
[ \vec{A} \cdot \vec{B} = A_x B_x + A_y B_y ]
四、余弦相似度的推导
步骤 1:将余弦定理改写为向量形式
[ |\vec{C}|^2 = |\vec{A}|^2 + |\vec{B}|^2 - 2 |\vec{A}| |\vec{B}| \cos(\theta) ]
其中 (\vec{C} = \vec{A} - \vec{B})(即 (\vec{C}) 是从 (\vec{B}) 末端指向 (\vec{A}) 末端的向量)。
步骤 2:仅用 (\vec{A}) 和 (\vec{B}) 表示
利用点积性质:
[ (\vec{A} - \vec{B}) \cdot (\vec{A} - \vec{B}) = |\vec{A}|^2 + |\vec{B}|^2 - 2\vec{A} \cdot \vec{B} ]
联立可得:
[ \vec{A} \cdot \vec{B} = |\vec{A}| |\vec{B}| \cos(\theta) ]
步骤 3:展开得到余弦相似度公式
[ \cos(\theta) = \frac{\vec{A} \cdot \vec{B}}{|\vec{A}| , |\vec{B}|} ]
五、高维推广:N 维余弦相似度
这一公式在 2 维、3 维乃至更高维数下都成立,只是高维时难以可视化。
N 维向量模:
[ |\vec{A}| = \sqrt{\sum_{i=1}^{N} A_i^2} ]
N 维点积:
[ \vec{A} \cdot \vec{B} = \sum_{i=1}^{N} A_i B_i ]
N 维余弦相似度:
[ \cos(\theta) = \frac {\displaystyle\sum_{i=1}^{N} A_i B_i} {\displaystyle\sqrt{\sum_{i=1}^{N} A_i^2} \cdot \sqrt{\sum_{i=1}^{N} B_i^2}} ]
六、在 Java AI 中的应用
这个公式是 Java AI 内置的 SimpleVectorStore(简单向量存储)实现中的核心公式。
| 特性 | 说明 |
|---|---|
| 类名 | org.springframework.ai.vectorstore.SimpleVectorStore |
| 用途 | 测试与演示(testing and demonstration purposes only) |
| 核心算法 | 余弦相似度(Cosine Similarity) |
| 生产替代方案 | Chroma / Milvus / PGvector / Pinecone / Weaviate 等专业向量数据库 |
七、Vector Databases 章节完整导航
Java AI 支持的全部向量数据库实现:
| 序号 | 向量数据库 / 服务 |
|---|---|
| 1 | Azure AI Service |
| 2 | Azure Cosmos DB |
| 3 | Amazon Bedrock Knowledge Base |
| 4 | Apache Cassandra Vector Store |
| 5 | Chroma |
| 6 | Couchbase |
| 7 | Elasticsearch |
| 8 | GemFire |
| 9 | MariaDB Vector Store |
| 10 | Milvus |
| 11 | MongoDB Atlas |
| 12 | Neo4j |
| 13 | OpenSearch |
| 14 | Oracle |
| 15 | PGvector |
| 16 | Pinecone |
| 17 | Qdrant |
| 18 | Redis |
| 19 | Typesense |
| 20 | Weaviate |
| 21 | S3 Vector Store |
Elasticsearch 向量存储
📌 原文地址:https://docs.spring.io/java-ai/reference/api/vectordbs/elasticsearch.html 📌 原文版本:Java AI 2.0.1
本节指导你完成 Elasticsearch VectorStore 的设置,用来存储文档 Embedding(嵌入向量)并执行相似度搜索。
Elasticsearch 是一个基于 Apache Lucene 库构建的开源搜索与分析引擎。
前提条件
需要一个运行中的 Elasticsearch 实例,可选方案:
- Docker:本地容器
- 自托管 Elasticsearch:本地安装运行
- Elastic Cloud:Elastic 官方托管服务
自动配置
破坏性变更
Java AI 的自动配置、starter 模块的构件名称发生了重大变化,更多信息请参阅升级说明。
Java AI 为 Elasticsearch 向量存储提供了 Spring Boot 自动配置。要启用它,请将以下依赖加入 Maven pom.xml:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-elasticsearch</artifactId>
</dependency>或 Gradle build.gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-vector-store-elasticsearch'
}旧版 Spring Boot 兼容
对于 3.3.0 之前的 spring-boot 版本,需要显式添加版本 ≥ 8.13.3 的 elasticsearch-java 依赖,否则旧版本会与执行的查询不兼容:
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.13.3</version>
</dependency>implementation 'co.elastic.clients:elasticsearch-java:8.13.3'向量存储实现可以为你初始化所需的 schema,但你必须显式选择启用:通过在构造函数传入 initializeSchema=true,或在 application.properties 中设置 …initialize-schema=true。你也可以跳过自动初始化,用 Elasticsearch 客户端手动创建索引——当索引需要高级映射或额外配置时这非常有用。
破坏性变更
早期版本的 Java AI 中,此 schema 初始化默认开启;本版本起需要主动 opt-in。
此外,你还需要一个已配置的 EmbeddingModel bean。更多信息请参阅 EmbeddingModel 章节。
现在,你可以在应用中将 ElasticsearchVectorStore 作为向量存储自动装配:
@Autowired VectorStore vectorStore;
// ...
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
// 把文档添加到 Elasticsearch
vectorStore.add(documents);
// 检索与查询相似的文档
List<Document> results = this.vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());配置属性
要连接 Elasticsearch 并使用 ElasticsearchVectorStore,需要提供实例的访问信息。简单的 application.yml 配置如下:
spring:
elasticsearch:
uris: <elasticsearch instance URIs>
username: <elasticsearch username>
password: <elasticsearch password>
ai:
vectorstore:
elasticsearch:
initialize-schema: true
index-name: custom-index
dimensions: 1536
similarity: cosineElasticsearch 客户端配置(spring.elasticsearch.*)
| 属性 | 描述 | 默认值 |
|---|---|---|
spring.elasticsearch.connection-timeout | 与 ES 通信的连接超时 | 1s |
spring.elasticsearch.password | ES 认证密码 | - |
spring.elasticsearch.username | ES 认证用户名 | - |
spring.elasticsearch.uris | 要使用的 ES 实例 URI(逗号分隔) | http://localhost:9200 |
spring.elasticsearch.path-prefix | 每个请求路径的前缀 | - |
spring.elasticsearch.restclient.sniffer.delay-after-failure | 失败后嗅探执行延迟 | 1m |
spring.elasticsearch.restclient.sniffer.interval | 连续普通嗅探之间的间隔 | 5m |
spring.elasticsearch.restclient.ssl.bundle | SSL bundle 名称 | - |
spring.elasticsearch.socket-keep-alive | 是否启用 socket keep-alive | false |
spring.elasticsearch.socket-timeout | socket 超时 | 30s |
向量存储配置(spring.ai.vectorstore.elasticsearch.*)
| 属性 | 描述 | 默认值 |
|---|---|---|
spring.ai.vectorstore.elasticsearch.initialize-schema | 是否初始化所需 schema | false |
spring.ai.vectorstore.elasticsearch.index-name | 存储向量的索引名称 | spring-ai-document-index |
spring.ai.vectorstore.elasticsearch.dimensions | 向量维度数 | 1536 |
spring.ai.vectorstore.elasticsearch.similarity | 相似度函数 | cosine |
spring.ai.vectorstore.elasticsearch.embedding-field-name | 搜索的向量字段名 | embedding |
可用的相似度函数:
- cosine(默认):测量向量之间的余弦相似度,适用于大多数场景。
- l2_norm:向量之间的欧几里得距离,值越低表示越相似。
- dot_product:对归一化向量(如 OpenAI Embedding)性能最佳。
更多细节可参阅 Elasticsearch 关于 dense vector(稠密向量)的官方文档。
元数据过滤
你也可以在 Elasticsearch 中使用通用、可移植的元数据过滤器。
例如,使用文本表达式语言:
vectorStore.similaritySearch(SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression("author in ['john', 'jill'] && 'article_type' == 'blog'").build());或使用编程式 Filter.Expression DSL:
FilterExpressionBuilder b = new FilterExpressionBuilder();
vectorStore.similaritySearch(SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression(b.and(
b.in("author", "john", "jill"),
b.eq("article_type", "blog")).build()).build());这些可移植的过滤器表达式会自动转换为 Elasticsearch 专有的 query string 查询。
例如,可移植表达式:
author in ['john', 'jill'] && 'article_type' == 'blog'会被翻译成 ES 专有格式:
(metadata.author:john OR jill) AND metadata.article_type:blog手动配置
你也可以不使用 Spring Boot 自动配置,而是手动配置 Elasticsearch 向量存储。把 spring-ai-elasticsearch-store 加入项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-elasticsearch-store</artifactId>
</dependency>或 Gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-elasticsearch-store'
}创建 Elasticsearch Rest5Client bean(参考 ES 文档以获得更深入的配置信息):
@Bean
public Rest5Client restClient() {
return Rest5Client.builder(new HttpHost("<host>", 9200, "http"))
.setDefaultHeaders(new Header[]{
new BasicHeader("Authorization", "Basic <encoded username and password>")
})
.build();
}然后用 Builder 模式创建 ElasticsearchVectorStore bean:
@Bean
public VectorStore vectorStore(Rest5Client restClient, EmbeddingModel embeddingModel) {
ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
options.setIndexName("custom-index"); // 可选,默认 "spring-ai-document-index"
options.setSimilarity(COSINE); // 可选,默认 COSINE
options.setDimensions(1536); // 可选,默认模型维度或 1536
return ElasticsearchVectorStore.builder(restClient, embeddingModel)
.options(options) // 可选,使用自定义 options
.initializeSchema(true) // 可选,默认 false
.batchingStrategy(new TokenCountBatchingStrategy()) // 可选,默认 TokenCountBatchingStrategy
.build();
}
// 任意 EmbeddingModel 实现
@Bean
public EmbeddingModel embeddingModel() {
return new OpenAiEmbeddingModel(OpenAiEmbeddingOptions.builder().apiKey(System.getenv("OPENAI_API_KEY")).build());
}访问原生客户端
Elasticsearch 向量存储实现通过 getNativeClient() 方法暴露底层原生 ES 客户端:
ElasticsearchVectorStore vectorStore = context.getBean(ElasticsearchVectorStore.class);
Optional<ElasticsearchClient> nativeClient = vectorStore.getNativeClient();
if (nativeClient.isPresent()) {
ElasticsearchClient client = nativeClient.get();
// 用原生客户端执行 ES 特有的操作
}原生客户端让你能访问 VectorStore 接口未公开的 ES 特有功能和操作。
Milvus 向量存储
📌 原文地址:https://docs.spring.io/java-ai/reference/api/vectordbs/milvus.html 📌 原文版本:Java AI 2.0.1
Milvus 是一个开源的向量数据库,在数据科学与机器学习领域受到广泛关注。其突出特点之一是对向量索引和查询的强大支持。Milvus 采用最先进、前沿的算法来加速搜索过程,使其在检索相似向量时异常高效,即使面对大规模数据集也是如此。
白话解读
Milvus 的核心是专为向量相似度搜索设计的索引(如 IVF_FLAT、HNSW、ANNOY 等)。它通过把高维向量切分到不同的"桶"或"图节点"中,把 O(N) 全表扫描变成近似 O(log N) 的检索,代价是牺牲一点点精度。
前提条件
- 一个正在运行的 Milvus 实例,可选方案:
- Milvus Standalone:Docker、Operator、Helm、DEB/RPM、Docker Compose。
- Milvus Cluster:Operator、Helm。
- 如需要,为 EmbeddingModel 提供 API 密钥,以生成由 MilvusVectorStore 存储的嵌入向量。
依赖项
破坏性变更
Java AI 自动配置、starter 模块的构件名称发生了重大变化,更多信息请参阅升级说明。
将 Milvus VectorStore Boot Starter 加入项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-milvus</artifactId>
</dependency>或 Gradle build.gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-vector-store-milvus'
}向量存储实现可以为你初始化所需 schema,但你必须显式启用:通过在构造函数中指定 initializeSchema=true,或在 application.properties 中设置 … initialize-schema=true。
破坏性变更
早期版本的 Java AI 中,schema 初始化默认开启。
向量存储还需要一个 EmbeddingModel 实例来计算文档的嵌入向量,可以从可用的实现中任选一个。
要连接并配置 MilvusVectorStore,需要提供实例的访问信息。简单的 application.yml:
spring:
ai:
vectorstore:
milvus:
client:
host: "localhost"
port: 19530
username: "root"
password: "milvus"
databaseName: "default"
collectionName: "vector_store"
embeddingDimension: 1536
indexType: IVF_FLAT
metricType: COSINE现在你可以在应用程序中自动装配 Milvus Vector Store 并使用:
@Autowired VectorStore vectorStore;
// ...
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
// 把文档添加到 Milvus Vector Store
vectorStore.add(documents);
// 检索与查询相似的文档
List<Document> results = this.vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());手动配置
除了 Spring Boot 自动配置,也可以手动配置 MilvusVectorStore。将以下依赖加入项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-milvus-store</artifactId>
</dependency>用 Builder 模式创建 MilvusVectorStore bean:
@Bean
public VectorStore vectorStore(MilvusServiceClient milvusClient, EmbeddingModel embeddingModel) {
return MilvusVectorStore.builder(milvusClient, embeddingModel)
.collectionName("test_vector_store")
.databaseName("default")
.indexType(IndexType.IVF_FLAT)
.metricType(MetricType.COSINE)
.batchingStrategy(new TokenCountBatchingStrategy())
.initializeSchema(true)
.build();
}
@Bean
public MilvusServiceClient milvusClient() {
return new MilvusServiceClient(ConnectParam.newBuilder()
.withAuthorization("minioadmin", "minioadmin")
.withUri(milvusContainer.getEndpoint())
.build());
}元数据过滤
你可以利用 Milvus 存储的通用、可移植的元数据过滤器。
例如,使用文本表达式语言:
vectorStore.similaritySearch(
SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression("author in ['john', 'jill'] && article_type == 'blog'").build());或者用 Filter.Expression DSL 以编程方式:
FilterExpressionBuilder b = new FilterExpressionBuilder();
vectorStore.similaritySearch(SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression(b.and(
b.in("author","john", "jill"),
b.eq("article_type", "blog")).build()).build());这些过滤器表达式会被转换为等效的 Milvus 过滤器。
提示
从搜索结果读回元数据时,数值会通过 Gson 反序列化。整数值以 Long 形式返回,小数值以 Double 形式返回。把整数元数据字段直接强转为 Double 的代码应改为 Long 或 Number 超类型。
使用 MilvusSearchRequest
MilvusSearchRequest 扩展了 SearchRequest,允许你使用 Milvus 特有的搜索参数,例如原生表达式和搜索参数 JSON:
MilvusSearchRequest request = MilvusSearchRequest.milvusBuilder()
.query("sample query")
.topK(5)
.similarityThreshold(0.7)
.nativeExpression("metadata[\"age\"] > 30") // 同时设置时优先级高于 filterExpression
.filterExpression("age <= 30") // 设了 nativeExpression 时被忽略
.searchParamsJson("{\"nprobe\":128}")
.build();
List results = vectorStore.similaritySearch(request);这在使用 Milvus 特有搜索功能时提供了更大的灵活性。
nativeExpression 与 searchParamsJson 的作用
这两个参数增强了 Milvus 搜索的精度,并保证查询性能。
nativeExpression:支持使用 Milvus 原生过滤表达式实现额外过滤。详见 Milvus Filtering。
MilvusSearchRequest request = MilvusSearchRequest.milvusBuilder()
.query("sample query")
.topK(5)
.nativeExpression("metadata['category'] == 'science'")
.build();searchParamsJson:在使用 IVF_FLAT(Milvus 的默认索引)时对调优搜索行为至关重要。详见 Milvus Vector Index。
默认 nprobe 陷阱
默认情况下,IVF_FLAT 需要设置 nprobe 才能获得准确结果。若未指定,nprobe 默认为 1,可能导致召回率极差甚至零搜索结果。
MilvusSearchRequest request = MilvusSearchRequest.milvusBuilder()
.query("sample query")
.topK(5)
.searchParamsJson("{\"nprobe\":128}")
.build();使用 nativeExpression 可确保高级过滤,而 searchParamsJson 可防止因默认 nprobe 值过低而导致无效搜索。
Milvus VectorStore 属性
| 属性 | 描述 | 默认值 |
|---|---|---|
spring.ai.vectorstore.milvus.database-name | Milvus 数据库名 | default |
spring.ai.vectorstore.milvus.collection-name | 存储向量的集合名 | vector_store |
spring.ai.vectorstore.milvus.partition-name | 已有的 Milvus 分区名,限定插入/删除/搜索范围 | - |
spring.ai.vectorstore.milvus.initialize-schema | 是否初始化 Milvus 后端 | false |
spring.ai.vectorstore.milvus.embedding-dimension | 向量维度 | 1536 |
spring.ai.vectorstore.milvus.index-type | 索引类型 | IVF_FLAT |
spring.ai.vectorstore.milvus.metric-type | 度量类型 | COSINE |
spring.ai.vectorstore.milvus.index-parameters | 索引参数 | {"nlist":1024} |
spring.ai.vectorstore.milvus.id-field-name | 集合的 ID 字段名 | doc_id |
spring.ai.vectorstore.milvus.auto-id | 是否使用自动 ID | false |
spring.ai.vectorstore.milvus.content-field-name | 集合的内容字段名 | content |
spring.ai.vectorstore.milvus.metadata-field-name | 集合的元数据字段名 | metadata |
spring.ai.vectorstore.milvus.embedding-field-name | 集合的嵌入字段名 | embedding |
spring.ai.vectorstore.milvus.client.host | 主机名或地址 | localhost |
spring.ai.vectorstore.milvus.client.port | 连接端口 | 19530 |
spring.ai.vectorstore.milvus.client.uri | Milvus 实例的 uri | - |
spring.ai.vectorstore.milvus.client.token | 鉴权令牌 | - |
spring.ai.vectorstore.milvus.client.connect-timeout-ms | 连接超时(毫秒) | 10000 |
spring.ai.vectorstore.milvus.client.keep-alive-time-ms | 保活时间(毫秒) | 55000 |
spring.ai.vectorstore.milvus.client.keep-alive-timeout-ms | 保活超时(毫秒) | 20000 |
spring.ai.vectorstore.milvus.client.rpc-deadline-ms | 等待服务端回复的截止时间 | 0 |
spring.ai.vectorstore.milvus.client.client-key-path | TLS 双向认证 client.key 路径 | - |
spring.ai.vectorstore.milvus.client.client-pem-path | TLS 双向认证 client.pem 路径 | - |
spring.ai.vectorstore.milvus.client.ca-pem-path | TLS 双向认证 ca.pem 路径 | - |
spring.ai.vectorstore.milvus.client.server-pem-path | TLS 单向认证 server.pem 路径 | - |
spring.ai.vectorstore.milvus.client.server-name | SSL 主机名检查的目标覆盖名 | - |
spring.ai.vectorstore.milvus.client.secure | 是否启用 TLS | false |
spring.ai.vectorstore.milvus.client.idle-timeout-ms | 客户端通道空闲超时 | 24h |
spring.ai.vectorstore.milvus.client.username | 用户名 | root |
spring.ai.vectorstore.milvus.client.password | 密码 | milvus |
启动 Milvus 存储
在 src/test/resources/ 目录运行:
docker-compose up清理环境:
docker-compose down; rm -Rf ./volumes然后在 http://localhost:19530 连接向量存储,或在 http://localhost:9001 进行管理(用户名 minioadmin,密码 minioadmin)。
故障排除
如果 Docker 提示资源问题,执行:
docker system prune --all --force --volumes访问原生客户端
Milvus Vector Store 通过 getNativeClient() 方法暴露底层原生 Milvus 客户端(MilvusServiceClient):
MilvusVectorStore vectorStore = context.getBean(MilvusVectorStore.class);
Optional<MilvusServiceClient> nativeClient = vectorStore.getNativeClient();
if (nativeClient.isPresent()) {
MilvusServiceClient client = nativeClient.get();
// 用原生客户端执行 Milvus 特有的操作
}原生客户端让你能访问 VectorStore 接口未公开的 Milvus 特有功能和操作。
PGvector 向量存储
📌 原文地址:https://docs.spring.io/java-ai/reference/api/vectordbs/pgvector.html 📌 原文版本:Java AI 2.0.1
本节带你设置 PGvector VectorStore,用来存储文档的 Embedding(嵌入向量)并执行相似度搜索。
白话解读
PGvector 是 PostgreSQL 的官方扩展,给 PostgreSQL 加上了向量数据类型和向量索引能力。优势是:
- 复用已有的 PostgreSQL 运维/备份/权限体系;
- 向量数据可以和业务表做 JOIN,直接做"混合检索"。
对已有 PostgreSQL 基础的团队来说门槛最低。
PGvector 是 PostgreSQL 的一个开源扩展,能够存储和检索机器学习生成的嵌入向量。它提供不同的能力,使用户能够识别精确和近似的最近邻。它旨在与 PostgreSQL 的其他功能无缝协作,包括索引和查询。
前提条件
首先,需要一个启用了 vector、hstore 和 uuid-ossp 扩展的 PostgreSQL 实例。
你可以通过 Docker Compose 或 Testcontainers 把 PGvector 数据库作为 Spring Boot 开发服务运行。或者,本地设置 Postgres/PGVector 附录演示了如何用 Docker 容器在本地启动数据库。
启动时,如果显式启用了 schema 初始化,PgVectorStore 会尝试安装所需的数据库扩展,并在不存在时创建 vector_store 表及其索引。
或者,你也可以手动执行:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE IF NOT EXISTS vector_store (
id uuid DEFAULT uuid_generate_v4() PRIMARY KEY,
content text,
metadata json,
embedding vector(1536) -- 1536 是默认嵌入维度
);
CREATE INDEX ON vector_store USING HNSW (embedding vector_cosine_ops);如果你使用其他维度,请把 1536 替换为实际的嵌入维度。PGvector 对 HNSW 索引最多支持 2000 维度。
接下来,如需要,请准备一个用于 EmbeddingModel 生成嵌入向量的 API 密钥。
自动配置
破坏性变更
Java AI 的自动配置、starter 模块的构件名称发生了重大变化,更多信息请参阅升级说明。
将 PgVectorStore Starter 加入项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
</dependency>或 Gradle build.gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-vector-store-pgvector'
}向量存储实现可以为你初始化所需 schema,但你必须通过在构造函数中指定 initializeSchema=true,或在 application.properties 中设置 … initialize-schema=true 来选择启用。
破坏性变更
早期版本的 Java AI 中,此 schema 初始化默认执行。
Vector Store 还需要一个 EmbeddingModel 实例来计算文档嵌入。例如,要使用 OpenAI EmbeddingModel,请加入:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>要连接并配置 PgVectorStore,需要提供实例的访问信息。简单的 application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/postgres
username: postgres
password: postgres
ai:
vectorstore:
pgvector:
index-type: HNSW
distance-type: COSINE_DISTANCE
dimensions: 1536
max-document-batch-size: 10000 # 可选:每个批次的最大文档数如果你通过 Docker Compose 或 Testcontainers 把 PGvector 作为 Spring Boot 开发服务运行,则无需配置 URL、用户名和密码,Spring Boot 会自动配置它们。
现在你可以在应用中自动装配 VectorStore 并使用:
@Autowired VectorStore vectorStore;
// ...
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
// 把文档添加到 PGVector
vectorStore.add(documents);
// 检索与查询相似的文档
List<Document> results = this.vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());配置属性
| 属性 | 描述 | 默认值 |
|---|---|---|
spring.ai.vectorstore.pgvector.index-type | 最近邻搜索索引类型。NONE(精确搜索);IVFFlat(构建快内存少但查询性能较低);HNSW(构建慢内存多但查询性能更好,无需训练步骤) | HNSW |
spring.ai.vectorstore.pgvector.distance-type | 距离类型。向量已归一化为长度 1 时可用 EUCLIDEAN_DISTANCE 或 NEGATIVE_INNER_PRODUCT 获得最佳性能 | COSINE_DISTANCE |
spring.ai.vectorstore.pgvector.dimensions | 嵌入维度。未显式指定时从 EmbeddingModel 获取。维度在表创建时设置,更改后必须重建 vector_store 表 | - |
spring.ai.vectorstore.pgvector.remove-existing-vector-store-table | 启动时删除现有 vector_store 表 | false |
spring.ai.vectorstore.pgvector.initialize-schema | 是否初始化所需 schema | false |
spring.ai.vectorstore.pgvector.schema-name | 向量存储的 schema 名 | public |
spring.ai.vectorstore.pgvector.table-name | 向量存储的表名 | vector_store |
spring.ai.vectorstore.pgvector.schema-validation | 启用 schema 和表名验证,确保名称有效且对象存在 | false |
spring.ai.vectorstore.pgvector.max-document-batch-size | 单个批次中处理的最大文档数 | 10000 |
安全建议
如果你配置了自定义 schema 和/或表名,建议设置 spring.ai.vectorstore.pgvector.schema-validation=true 启用验证,这能确保名称正确,并降低 SQL 注入风险。
元数据过滤
你可以在 PGvector 存储中使用通用、可移植的元数据过滤器。
例如,使用文本表达式语言:
vectorStore.similaritySearch(
SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression("author in ['john', 'jill'] && article_type == 'blog'").build());或使用编程式 Filter.Expression DSL:
FilterExpressionBuilder b = new FilterExpressionBuilder();
vectorStore.similaritySearch(SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression(b.and(
b.in("author","john", "jill"),
b.eq("article_type", "blog")).build()).build());这些过滤器表达式会被转换为 PostgreSQL JSON 路径表达式,以实现高效的元数据过滤。
手动配置
你也可以不使用 Spring Boot 自动配置,而是手动配置 PgVectorStore。把 PostgreSQL 连接和 JdbcTemplate 自动配置依赖加入项目:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store</artifactId>
</dependency>在应用中配置 PgVector:
@Bean
public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) {
return PgVectorStore.builder(jdbcTemplate, embeddingModel)
.dimensions(1536) // 可选,默认模型维度或 1536
.distanceType(COSINE_DISTANCE) // 可选,默认 COSINE_DISTANCE
.indexType(HNSW) // 可选,默认 HNSW
.initializeSchema(true) // 可选,默认 false
.schemaName("public") // 可选,默认 "public"
.vectorTableName("vector_store") // 可选,默认 "vector_store"
.maxDocumentBatchSize(10000) // 可选,默认 10000
.build();
}在本地运行 Postgres + PGVector
docker run -it --rm --name postgres -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres pgvector/pgvector可以用以下方式连接到此服务器:
psql -U postgres -h localhost -p 5432访问原生客户端
PGVector Store 通过 getNativeClient() 方法暴露底层原生 JDBC 客户端(JdbcTemplate):
PgVectorStore vectorStore = context.getBean(PgVectorStore.class);
Optional<JdbcTemplate> nativeClient = vectorStore.getNativeClient();
if (nativeClient.isPresent()) {
JdbcTemplate jdbc = nativeClient.get();
// 用原生客户端执行 PostgreSQL 特有操作
}原生客户端让你能访问 VectorStore 接口未公开的 PostgreSQL 特有功能和操作。
Redis 向量存储
📌 原文地址:https://docs.spring.io/java-ai/reference/api/vectordbs/redis.html 📌 原文版本:Java AI 2.0.1
本节带你设置 RedisVectorStore,用来存储文档 Embedding 并执行相似性搜索。
Redis 是一个开源(BSD 许可)的内存数据结构存储,可用作数据库、缓存、消息代理和流处理引擎。Redis 提供字符串、哈希、列表、集合、带范围查询的有序集合、位图、hyperloglogs、地理空间索引和流等数据结构。
Redis Search and Query 扩展了 Redis OSS 的核心能力,允许你把 Redis 用作向量数据库:
- 在哈希或 JSON 文档中存储向量及其关联元数据
- 检索向量
- 执行向量相似性搜索(KNN)
- 执行基于范围的向量搜索(带半径阈值)
- 在 TEXT 字段上执行全文搜索
- 支持多种距离度量(COSINE、L2、IP)和向量算法(HNSW、FLAT)
白话解读
Redis 本身不是向量数据库,但官方提供了 Redis Stack(内置 RediSearch 模块)让它"长出"了向量能力。最大优势是:如果你的应用已经在用 Redis 做缓存,复用 Redis Stack 就无需引入新的存储组件,把向量检索和缓存合二为一,运维更简单。
前提条件
- 一个 Redis Stack 实例
- Redis Cloud(推荐)
- Docker 镜像
redis/redis-stack:latest
- 用于计算文档嵌入的
EmbeddingModel实例。如需要,为EmbeddingModel提供一个 API 密钥。
自动配置
破坏性变更
Java AI 自动配置和 starter 模块的构件名称发生了重大变化,更多信息请参阅升级说明。
Java AI 为 Redis Vector Store 提供 Spring Boot 自动配置。要启用它,请将以下依赖加入 Maven pom.xml:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>或 Gradle build.gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-vector-store-redis'
}向量存储实现可以为你初始化所需 schema,但你必须通过在构造函数中指定 initializeSchema=true,或在 application.properties 中设置 … initialize-schema=true 来主动启用。
破坏性变更
早期版本的 Java AI 中,schema 初始化默认执行。
此外,你还需要一个已配置的 EmbeddingModel bean。
现在你可以在应用中自动装配 RedisVectorStore 作为向量存储:
@Autowired VectorStore vectorStore;
// ...
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
// 把文档添加到 Redis
vectorStore.add(documents);
// 检索与查询相似的文档
List<Document> results = this.vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());配置属性
要连接 Redis 并使用 RedisVectorStore,需要提供实例的访问信息。简单的 application.yml:
spring:
data:
redis:
url: <redis instance url>
ai:
vectorstore:
redis:
initialize-schema: true
index-name: custom-index
prefix: custom-prefixRedis 连接配置也可通过 application.properties:
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.username=default
spring.data.redis.password=向量存储配置(spring.ai.vectorstore.redis.*):
| 属性 | 描述 | 默认值 |
|---|---|---|
spring.ai.vectorstore.redis.initialize-schema | 是否初始化所需 schema | false |
spring.ai.vectorstore.redis.index-name | 存储向量的索引名 | spring-ai-index |
spring.ai.vectorstore.redis.prefix | Redis 键的前缀 | embedding: |
spring.ai.vectorstore.redis.distance-metric | 距离度量(COSINE、L2、IP) | COSINE |
spring.ai.vectorstore.redis.vector-algorithm | 向量索引算法(HNSW、FLAT) | HNSW |
spring.ai.vectorstore.redis.hnsw-m | HNSW:最大出连接数 | 16 |
spring.ai.vectorstore.redis.hnsw-ef-construction | HNSW:索引构建期间的最大连接数 | 200 |
spring.ai.vectorstore.redis.hnsw-ef-runtime | HNSW:搜索期间要考虑的连接数 | 10 |
spring.ai.vectorstore.redis.default-range-threshold | 范围搜索的默认半径阈值 | 0.8 |
spring.ai.vectorstore.redis.text-scorer | 文本评分算法(BM25、TFIDF、BM25STD、DISMAX、DOCSCORE) | BM25 |
元数据过滤
你也可以在 Redis 上使用通用、可移植的元数据过滤器。
例如,使用文本表达式语言:
vectorStore.similaritySearch(SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression("country in ['UK', 'NL'] && year >= 2020").build());或使用编程式 Filter.Expression DSL:
FilterExpressionBuilder b = new FilterExpressionBuilder();
vectorStore.similaritySearch(SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression(b.and(
b.in("country", "UK", "NL"),
b.gte("year", 2020)).build()).build());这些可移植的过滤器表达式会被自动转换为 Redis 搜索查询。
例如,可移植表达式:
country in ['UK', 'NL'] && year >= 2020被转换为 Redis 专有格式:
@country:{UK | NL} @year:[2020 inf]手动配置
除了 Spring Boot 自动配置,也可以手动配置 Redis 向量存储。把 spring-ai-redis-store 加入项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-store</artifactId>
</dependency>或 Gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-redis-store'
}创建 RedisClient bean:
@Bean
public RedisClient jedisClient() {
return RedisClient.builder().hostAndPort("<host>", 6379).build();
}然后用 Builder 模式创建 RedisVectorStore bean:
@Bean
public VectorStore vectorStore(RedisClient jedisClient, EmbeddingModel embeddingModel) {
return RedisVectorStore.builder(jedisClient, embeddingModel)
.indexName("custom-index") // 可选,默认 "spring-ai-index"
.prefix("custom-prefix") // 可选,默认 "embedding:"
.contentFieldName("content") // 可选,文档内容字段
.embeddingFieldName("embedding") // 可选,向量字段
.vectorAlgorithm(Algorithm.HNSW) // 可选,HNSW 或 FLAT(默认 HNSW)
.distanceMetric(DistanceMetric.COSINE) // 可选,COSINE/L2/IP(默认 COSINE)
.hnswM(16) // 可选,HNSW 连接数(默认 16)
.hnswEfConstruction(200) // 可选,HNSW 构建参数(默认 200)
.hnswEfRuntime(10) // 可选,HNSW 搜索参数(默认 10)
.defaultRangeThreshold(0.8) // 可选,默认范围搜索半径
.textScorer(TextScorer.BM25) // 可选,文本评分算法(默认 BM25)
.metadataFields( // 可选,定义可过滤的元数据字段
MetadataField.tag("country"),
MetadataField.numeric("year"),
MetadataField.text("description"))
.initializeSchema(true) // 可选,默认 false
.batchingStrategy(new TokenCountBatchingStrategy()) // 可选,默认 TokenCountBatchingStrategy
.build();
}
// 任意 EmbeddingModel 实现
@Bean
public EmbeddingModel embeddingModel() {
return new OpenAiEmbeddingModel(OpenAiEmbeddingOptions.builder().apiKey(System.getenv("OPENAI_API_KEY")).build());
}提示
对于过滤表达式中使用的任何元数据字段,必须显式列出字段名和类型(TAG、TEXT 或 NUMERIC)。metadataFields 注册可过滤字段:类型 TAG 的 country 和类型 NUMERIC 的 year。
访问原生客户端
Redis Vector Store 通过 getNativeClient() 方法暴露底层原生 Redis 客户端(RedisClient):
RedisVectorStore vectorStore = context.getBean(RedisVectorStore.class);
Optional<RedisClient> nativeClient = vectorStore.getNativeClient();
if (nativeClient.isPresent()) {
RedisClient jedisClient = nativeClient.get();
// 用原生客户端执行 Redis 特有操作
}原生客户端让你能访问 VectorStore 接口未公开的 Redis 特有功能和操作。
距离度量
Redis Vector Store 支持三种用于向量相似性的距离度量:
- COSINE(默认):度量向量之间夹角的余弦值
- L2:欧几里得距离,度量向量之间的直线距离
- IP:内积,度量向量之间的点积
每种度量都会自动归一化为 0-1 的相似性分数,其中 1 表示最相似。
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.distanceMetric(DistanceMetric.COSINE) // 或 L2、IP
.build();HNSW 算法配置
Redis Vector Store 默认使用 HNSW(Hierarchical Navigable Small World)算法进行高效的近似最近邻搜索。你可以针对具体用例调整 HNSW 参数:
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.vectorAlgorithm(Algorithm.HNSW)
.hnswM(32) // 每个节点的最大出连接数(默认 16)
.hnswEfConstruction(100) // 索引构建期间的连接数(默认 200)
.hnswEfRuntime(50) // 搜索期间的连接数(默认 10)
.build();参数指南:
- M:更高的值能提高召回率,但会增加内存和索引时间。典型值:12-48。
- EF_CONSTRUCTION:更高的值能提高索引质量,但会增加构建时间。典型值:100-500。
- EF_RUNTIME:更高的值能提高搜索准确度,但会增加延迟。典型值:10-100。
对于较小的数据集或需要精确结果时,可改用 FLAT 算法:
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.vectorAlgorithm(Algorithm.FLAT)
.build();文本搜索
Redis Vector Store 使用 Redis Query Engine 的全文搜索能力提供文本搜索功能,允许你根据 TEXT 字段中的关键词和短语查找文档:
// 搜索包含特定文本的文档
List<Document> textResults = vectorStore.searchByText(
"machine learning", // 搜索 query
"content", // 要搜索的字段(必须是 TEXT 类型)
10, // limit
"category == 'AI'" // 可选,过滤表达式
);文本搜索支持:
- 单词搜索
- 当
inOrder=true时,进行精确匹配的短语搜索 - 当
inOrder=false时,进行具有 OR 语义的基于词项的搜索 - 停用词过滤,忽略常见词
- 多种文本评分算法
构建时配置文本搜索行为:
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.textScorer(TextScorer.TFIDF) // 文本评分算法
.inOrder(true) // 按顺序匹配词项
.stopwords(Set.of("is", "a", "the", "and")) // 忽略常见词
.metadataFields(MetadataField.text("description")) // 定义 TEXT 字段
.build();文本评分算法
可用的文本评分算法:
- BM25(默认):带词项饱和的现代版 TF-IDF
- TFIDF:经典的词频-逆文档频率
- BM25STD:标准化的 BM25
- DISMAX:Disjunction max
- DOCSCORE:文档评分
分数会归一化到 0-1 范围,以与向量相似性分数保持一致。
范围搜索
范围搜索返回指定半径阈值内的所有文档,而不是固定数量的最近邻:
// 用显式半径搜索
List<Document> rangeResults = vectorStore.searchByRange(
"AI and machine learning", // 查询
0.8, // 半径(相似度阈值)
"category == 'AI'" // 可选,过滤表达式
);也可以在构建时设置默认范围阈值:
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.defaultRangeThreshold(0.8) // 设置默认阈值
.build();
// 使用默认阈值
List<Document> results = vectorStore.searchByRange("query");当希望检索相似度阈值以上的所有相关文档(而非限制为 topK)时,范围搜索非常有用。
语义缓存(Semantic Cache)
白话解读
语义缓存利用 Redis 的向量搜索能力,把用户查询存进 Redis 时一并存它的 Embedding。下一次出现"意思差不多但措辞不同"的查询时,可以直接命中缓存,省一次昂贵的 LLM 调用。对客服、FAQ 类应用特别有用。
语义缓存是一种强大的优化技术,利用 Redis 向量搜索能力,基于用户查询的语义相似性(而非精确字符串匹配)来缓存和检索 AI 聊天响应。这样即使使用不同措辞提出类似问题,也能智能地重用响应。
为什么使用语义缓存?
传统缓存依赖精确键匹配,当用户用不同措辞提出语义等价的问题时会失败:
- "What is the capital of France?"
- "Tell me France's capital city"
- "Which city is the capital of France?"
三个查询有相同答案,但传统缓存会把它们视为不同请求,导致冗余的 LLM API 调用。语义缓存通过使用向量嵌入比较查询含义来解决这个问题。
优势:
- 降低 API 成本:避免对昂贵的 LLM API 进行冗余调用
- 降低延迟:立即返回缓存响应,而不是等待模型推理
- 提高可扩展性:处理更高的查询量,无需 API 成本成比例增加
- 响应一致:对语义相似的问题返回相同答案
自动配置
Java AI 为 Redis 语义缓存提供 Spring Boot 自动配置。要启用它,请把以下依赖加入 Maven pom.xml:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis-semantic-cache</artifactId>
</dependency>或 Gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-vector-store-redis-semantic-cache'
}自动配置提供一个针对语义缓存优化的默认 EmbeddingModel(redis/langcache-embed-v1)。你可以通过提供自己的 EmbeddingModel bean 来覆盖它。
配置属性
spring.ai.vectorstore.redis.semantic-cache.*:
| 属性 | 描述 | 默认值 |
|---|---|---|
spring.ai.vectorstore.redis.semantic-cache.enabled | 启用或禁用语义缓存 | true |
spring.ai.vectorstore.redis.semantic-cache.host | Redis 服务器主机 | localhost |
spring.ai.vectorstore.redis.semantic-cache.port | Redis 服务器端口 | 6379 |
spring.ai.vectorstore.redis.semantic-cache.similarity-threshold | 缓存命中相似度阈值(0.0-1.0),值越高要求越接近的语义匹配 | 0.95 |
spring.ai.vectorstore.redis.semantic-cache.index-name | 缓存条目的 Redis 搜索索引名 | semantic-cache-index |
spring.ai.vectorstore.redis.semantic-cache.prefix | Redis 中缓存条目的键前缀 | semantic-cache: |
application.yml 中的示例配置:
spring:
ai:
vectorstore:
redis:
semantic-cache:
enabled: true
host: localhost
port: 6379
similarity-threshold: 0.85
index-name: my-app-cache
prefix: "my-app:semantic-cache:"使用 SemanticCacheAdvisor
SemanticCacheAdvisor 与 Java AI 的 ChatClient advisor 模式无缝集成,会自动缓存响应,并为相似查询返回缓存结果:
@Autowired
private SemanticCache semanticCache;
@Autowired
private ChatModel chatModel;
public void example() {
// 创建缓存 advisor
SemanticCacheAdvisor cacheAdvisor = SemanticCacheAdvisor.builder()
.cache(semanticCache)
.build();
// 第一次查询:调用 LLM 并缓存响应
ChatResponse response1 = ChatClient.builder(chatModel)
.build()
.prompt("What is the capital of France?")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// 相似查询:返回缓存响应(不调用 LLM)
ChatResponse response2 = ChatClient.builder(chatModel)
.build()
.prompt("Tell me the capital city of France")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// response1 和 response2 内容相同,都来自缓存
}该 advisor 会自动:
- 在调用 LLM 之前检查缓存中是否存在语义相似的查询
- 当找到高于相似度阈值的匹配时返回缓存响应
- 在成功的 LLM 调用后缓存新响应
- 同时支持同步和流式聊天操作
直接使用缓存
你也可以直接与 SemanticCache 交互以实现更细粒度的控制:
@Autowired
private SemanticCache semanticCache;
// 用 query 存储响应
semanticCache.set("What is the capital of France?", chatResponse);
// 用 TTL 存储以自动过期
semanticCache.set("What's the weather today?", weatherResponse, Duration.ofHours(1));
// 检索语义相似的响应
Optional<ChatResponse> cached = semanticCache.get("Tell me France's capital");
if (cached.isPresent()) {
// 用缓存的响应
String answer = cached.get().getResult().getOutput().getText();
}
// 清空所有缓存条目
semanticCache.clear();手动配置
为了获得更多控制,可以手动配置语义缓存组件:
@Configuration
public class SemanticCacheConfig {
@Bean
public RedisClient jedisClient() {
return RedisClient.builder().hostAndPort("localhost", 6379).build();
}
@Bean
public SemanticCache semanticCache(RedisClient jedisClient, EmbeddingModel embeddingModel) {
return DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.distanceThreshold(0.3) // 越低 = 匹配越严格
.indexName("my-semantic-cache")
.prefix("cache:")
.build();
}
@Bean
public SemanticCacheAdvisor semanticCacheAdvisor(SemanticCache cache) {
return SemanticCacheAdvisor.builder()
.cache(cache)
.build();
}
}使用命名空间进行缓存隔离
对于多租户应用程序或需要单独缓存空间的场景,可以用不同的索引名隔离缓存条目:
// 为不同用户或上下文创建隔离缓存
SemanticCache user1Cache = DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.indexName("user-1-cache")
.build();
SemanticCache user2Cache = DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.indexName("user-2-cache")
.build();
// 每个用户拥有自己的隔离缓存空间
SemanticCacheAdvisor user1Advisor = SemanticCacheAdvisor.builder()
.cache(user1Cache)
.build();系统提示隔离
SemanticCacheAdvisor 会根据系统提示自动隔离缓存响应,确保同一用户查询在不同系统提示下返回不同的缓存响应——对具有多个 AI 角色或依赖上下文的应用程序至关重要。
SemanticCacheAdvisor cacheAdvisor = SemanticCacheAdvisor.builder()
.cache(semanticCache)
.build();
// 用技术支持人设查询
ChatResponse technicalResponse = ChatClient.builder(chatModel)
.build()
.prompt()
.system("You are a technical support specialist. Provide detailed technical answers.")
.user("How do I reset my password?")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// 同一查询 + 客服人设 —— 缓存未命中(不同上下文)
ChatResponse serviceResponse = ChatClient.builder(chatModel)
.build()
.prompt()
.system("You are a friendly customer service agent. Keep responses brief and helpful.")
.user("How do I reset my password?")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// 同一查询 + 技术支持人设 —— 缓存命中
ChatResponse technicalAgain = ChatClient.builder(chatModel)
.build()
.prompt()
.system("You are a technical support specialist. Provide detailed technical answers.")
.user("How do I reset my password?")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// 返回缓存的 technical 响应工作原理:
advisor 计算系统提示的确定性哈希值,并将其用作元数据过滤器,在存储和检索缓存响应时使用:
- 相同的用户问题 + 相同的系统提示 → 缓存命中
- 相同的用户问题 + 不同的系统提示 → 缓存未命中(独立缓存条目)
- 无系统提示的查询共享一个公共缓存空间
上下文感知缓存 API
对于高级用例,可以直接使用上下文感知缓存方法:
// 用显式 contextHash 存储
String contextHash = "technical-support-context";
semanticCache.set("How do I reset my password?", response, contextHash);
// 用 context 过滤检索
Optional<ChatResponse> cached = semanticCache.get("How do I reset my password?", contextHash);
// 不同的 contextHash 返回空(无匹配)
Optional<ChatResponse> otherContext = semanticCache.get("How do I reset my password?", "billing-context");调整相似度阈值
相似度阈值决定查询与缓存条目必须多么接近才被视为命中(0.0 到 1.0):
- 更高阈值(如 0.95):要求非常接近的语义匹配。减少误报,但可能错过有效的缓存命中。
- 更低阈值(如 0.70):允许更宽泛的语义匹配。提高缓存命中率,但可能返回不太相关的缓存响应。
// 严格匹配 —— 只有非常相似的查询才能命中缓存
SemanticCache strictCache = DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.distanceThreshold(0.2) // 严格(基于距离,越低越严格)
.build();
// 宽松匹配 —— 接受更广泛的语义相似度
SemanticCache lenientCache = DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.distanceThreshold(0.5) // 宽松
.build();从更高的阈值(更严格的匹配)开始,然后根据应用对语义变化的容忍度逐渐降低。
TTL 与缓存过期
可以为缓存响应配置 TTL(生存时间)以自动过期,对时间敏感的数据至关重要:
// 把天气数据缓存 1 小时
semanticCache.set("What's the weather in New York?", weatherResponse, Duration.ofHours(1));
// 永久缓存常识(无 TTL)
semanticCache.set("What is photosynthesis?", scienceResponse);
// Redis 自动移除过期条目工作原理
语义缓存的运行流程:
- 查询嵌入:当查询到达时,使用配置的
EmbeddingModel将其转换为向量嵌入 - 向量搜索:Redis 执行基于范围的向量搜索(
VECTOR_RANGE),查找相似度阈值内的缓存条目 - 缓存命中:如果找到语义相似的查询,立即返回缓存的
ChatResponse - 缓存未命中:如果未找到匹配项,查询继续发送到 LLM,并把响应缓存以备未来使用
该实现利用 Redis 高效的向量索引(HNSW 算法)进行快速相似性搜索,即使缓存规模很大也能保持性能。