深度剖析Spring AI源码(四):RAG的基石,解密VectorStore的统一抽象
深度剖析Spring AI源码(四):RAG的基石,解密VectorStore的统一抽象
“Data is the new oil, but vectors are the new gold.” —— 某个不知名的AI大佬
在AI时代,向量化数据成为了企业最宝贵的数字资产。Spring AI的
VectorStore
抽象层就像是一个高度安全的“数字黄金储备库”,它不仅负责安全地存储这些“黄金”,还提供了一套高效、标准的检索和管理服务。今天,就让我们一起深入这个向量世界的核心,看看Spring AI是如何管理这些“数字财富”的。
引子:向量存储的重要性
在RAG(检索增强生成)应用中,向量存储是绝对的核心引擎。如果没有它,RAG就是无源之水、无本之木。想象一下这个场景:
- 你有一个庞大的企业内部知识库,包含数万份PDF、Word文档和网页
- 用户在聊天机器人中问了一个问题:“我们公司的年度休假政策是什么?”
- AI需要像一个经验丰富图书管理员一样,从浩如烟海的文档中迅速找到最相关的几页内容,然后基于这些“开卷”信息,生成精准的答案。
这个“迅速找到相关内容”的过程,正是向量检索大显身手的魔法时刻。Spring AI的VectorStore
抽象层,就是为了让这个原本复杂的过程变得像调用一个普通Java接口一样简单、优雅。
VectorStore接口体系全景
在深入代码之前,我们先通过一张类图来鸟瞰VectorStore
的整体设计。它通过接口组合和抽象类,构建了一个清晰、可扩展的体系结构。
核心接口深度解析
1. VectorStore主接口
VectorStore
是所有操作的核心入口。让我们深入其接口设计(位于spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/VectorStore.java
):
/*** The VectorStore interface defines the operations for managing and querying* documents in a vector database. It extends DocumentWriter to support document* writing operations. Vector databases are specialized for AI applications, performing* similarity searches based on vector representations of data rather than exact matches.*/
public interface VectorStore extends DocumentWriter, VectorStoreRetriever {default String getName() {return this.getClass().getSimpleName();}/*** Adds list of Documents to the vector store.* @param documents the list of documents to store. Throws an exception if the* underlying provider checks for duplicate IDs.*/void add(List<Document> documents);@Overridedefault void accept(List<Document> documents) {add(documents); // DocumentWriter接口的实现}/*** Deletes documents from the vector store.* @param idList list of document ids for which documents will be removed.*/void delete(List<String> idList);/*** Deletes documents from the vector store based on filter criteria.* @param filterExpression Filter expression to identify documents to delete*/void delete(Filter.Expression filterExpression);/*** Deletes documents using a string filter expression.*/default void delete(String filterExpression) {SearchRequest searchRequest = SearchRequest.builder().filterExpression(filterExpression).build();Filter.Expression textExpression = searchRequest.getFilterExpression();Assert.notNull(textExpression, "Filter expression must not be null");this.delete(textExpression);}/*** Returns the native client if available in this vector store implementation.*/default <T> Optional<T> getNativeClient() {return Optional.empty();}
}
这个设计的精妙之处:
- 多重继承:同时继承了
DocumentWriter
和VectorStoreRetriever
- 职责分离:读写操作分离,符合接口隔离原则
- 灵活删除:支持ID删除和条件删除两种方式
- 原生客户端访问:为高级用法预留了后门
2. VectorStoreRetriever:只读接口
// spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/VectorStoreRetriever.java
@FunctionalInterface
public interface VectorStoreRetriever {/*** Retrieves documents based on a search request.* @param request the search request containing query and filtering criteria* @return list of documents matching the search criteria*/List<Document> retrieve(SearchRequest request);// 默认方法,提供便捷的相似性搜索default List<Document> similaritySearch(String query) {return retrieve(SearchRequest.query(query));}default List<Document> similaritySearch(String query, int topK) {return retrieve(SearchRequest.query(query).topK(topK));}default List<Document> similaritySearch(String query, int topK, double threshold) {return retrieve(SearchRequest.query(query).topK(topK).similarityThreshold(threshold));}
}
这种设计体现了经典的接口隔离原则和最小权限原则:
- 只需要检索功能的组件(例如,一个RAG服务的检索模块),可以只依赖
VectorStoreRetriever
接口,根本不知道还有写入和删除操作的存在。 - 需要完整增删改查功能的组件(例如,一个数据ETL管道),则依赖
VectorStore
接口。 - 清晰的职责边界,极大地降低了系统各模块间的耦合度。
3. SearchRequest:查询请求的封装
SearchRequest
是一个精心设计的DTO,它将一次复杂的相似性搜索所需的所有参数都封装了起来,并通过建造者模式提供了流畅的构建体验。
// spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/SearchRequest.java
public class SearchRequest {private final String query;private final int topK;private final double similarityThreshold;private final Filter.Expression filterExpression;// Builder模式构建public static Builder query(String query) {return new Builder(query);}public static class Builder {private String query;private int topK = 4; // 默认返回4个结果private double similarityThreshold = 0.0;private Filter.Expression filterExpression;public Builder topK(int topK) {this.topK = topK;return this;}public Builder similarityThreshold(double threshold) {this.similarityThreshold = threshold;return this;}public Builder filterExpression(String textExpression) {this.filterExpression = new FilterExpressionTextParser().parse(textExpression);return this;}public Builder filterExpression(Filter.Expression expression) {this.filterExpression = expression;return this;}public SearchRequest build() {return new SearchRequest(query, topK, similarityThreshold, filterExpression);}}
}
元数据过滤:类SQL的查询语法
Spring AI最令人称道的设计之一,就是这套可移植的元数据过滤API。在RAG场景中,我们常常需要进行混合搜索,即不仅要看向量的相似度,还要根据文档的元数据(如作者、年份、标签等)进行精确筛选。这套API让这一切变得异常简单。让我们看看这个“类SQL”语法是如何工作的:
1. Filter表达式体系
// spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/Filter.java
public class Filter {public interface Expression {ExpressionType type();}public enum ExpressionType {AND, OR, NOT, EQ, NE, LT, LTE, GT, GTE, IN, NIN}// 比较表达式public static class Compare implements Expression {private final String key;private final Object value;private final ExpressionType type;public Compare(String key, ExpressionType type, Object value) {this.key = key;this.type = type;this.value = value;}}// 逻辑表达式public static class Group implements Expression {private final ExpressionType type;private final List<Expression> expressions;public Group(ExpressionType type, List<Expression> expressions) {this.type = type;this.expressions = expressions;}}
}
2. 使用示例
// 文本形式的过滤表达式
String filterExpression = "author == 'John Doe' AND category IN ['tech', 'ai'] AND score > 0.8";List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring AI tutorial").topK(10).similarityThreshold(0.7).filterExpression(filterExpression)
);// 程序化构建过滤表达式
Filter.Expression filter = Filter.builder().eq("author", "John Doe").and().in("category", List.of("tech", "ai")).and().gt("score", 0.8).build();List<Document> results2 = vectorStore.retrieve(SearchRequest.query("Spring AI tutorial").filterExpression(filter)
);
这种设计的巨大优势在于:
- 可移植性:同一套过滤语法和API,无论底层对接的是PGVector、Chroma还是Elasticsearch,都能正常工作。业务代码无需为不同数据库的“方言”做适配。
- 类型安全与易读性:通过Builder API以编程方式构建表达式,可以在编译期就发现错误,并且代码逻辑清晰,可读性远超拼接字符串。
- 易于理解:对于熟悉SQL的开发者来说,这套语法的学习成本几乎为零。
具体实现案例分析
1. SimpleVectorStore:内存实现
SimpleVectorStore
是Spring AI提供的一个基于内存的简单实现,非常适合在开发、测试或者小型应用场景中使用。让我们看看它的核心代码(位于spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/SimpleVectorStore.java
):
public class SimpleVectorStore extends AbstractObservationVectorStore {private static final Logger logger = LoggerFactory.getLogger(SimpleVectorStore.class);private final ObjectMapper objectMapper;private final ExpressionParser expressionParser;private final FilterExpressionConverter filterExpressionConverter;// 内存存储 - 线程安全的ConcurrentHashMapprotected Map<String, SimpleVectorStoreContent> store = new ConcurrentHashMap<>();protected SimpleVectorStore(SimpleVectorStoreBuilder builder) {super(builder);this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();this.expressionParser = new SpelExpressionParser();this.filterExpressionConverter = new SimpleVectorStoreFilterExpressionConverter();}@Overridepublic void doAdd(List<Document> documents) {Objects.requireNonNull(documents, "Documents list cannot be null");if (documents.isEmpty()) {throw new IllegalArgumentException("Documents list cannot be empty");}for (Document document : documents) {logger.info("Calling EmbeddingModel for document id = {}", document.getId());// 生成向量嵌入float[] embedding = this.embeddingModel.embed(document);SimpleVectorStoreContent storeContent = new SimpleVectorStoreContent(document.getId(), document.getText(),document.getMetadata(), embedding);this.store.put(document.getId(), storeContent);}}@Overridepublic void doDelete(List<String> idList) {for (String id : idList) {this.store.remove(id);}}@Overridepublic List<Document> doSimilaritySearch(SearchRequest request) {String query = request.getQuery();int topK = request.getTopK();double threshold = request.getSimilarityThreshold();Filter.Expression filterExpression = request.getFilterExpression();// 1. 生成查询向量float[] queryEmbedding = this.embeddingModel.embed(query);// 2. 计算相似度并排序List<SimilarityResult> similarities = this.store.values().stream().filter(content -> applyFilter(content, filterExpression)) // 应用过滤器.map(content -> {double similarity = cosineSimilarity(queryEmbedding, content.getEmbedding());return new SimilarityResult(content, similarity);}).filter(result -> result.similarity >= threshold) // 应用相似度阈值.sorted((r1, r2) -> Double.compare(r2.similarity, r1.similarity)) // 降序排序.limit(topK).toList();// 3. 转换为Document对象return similarities.stream().map(result -> {SimpleVectorStoreContent content = result.content;return Document.builder().id(content.getId()).text(content.getText()).metadata(content.getMetadata()).metadata("distance", 1.0 - result.similarity) // 添加距离元数据.build();}).toList();}// 余弦相似度计算private double cosineSimilarity(float[] vectorA, float[] vectorB) {if (vectorA.length != vectorB.length) {throw new IllegalArgumentException("Vectors must have the same length");}double dotProduct = 0.0;double normA = 0.0;double normB = 0.0;for (int i = 0; i < vectorA.length; i++) {dotProduct += vectorA[i] * vectorB[i];normA += Math.pow(vectorA[i], 2);normB += Math.pow(vectorB[i], 2);}return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));}// 应用元数据过滤器private boolean applyFilter(SimpleVectorStoreContent content, Filter.Expression filterExpression) {if (filterExpression == null) {return true;}try {// 使用SpEL表达式计算过滤条件Expression expression = this.filterExpressionConverter.convertExpression(filterExpression);EvaluationContext context = new StandardEvaluationContext(content.getMetadata());return Boolean.TRUE.equals(expression.getValue(context, Boolean.class));} catch (Exception e) {logger.warn("Failed to apply filter: {}", filterExpression, e);return true; // 过滤失败时默认包含}}// 相似度结果内部类private record SimilarityResult(SimpleVectorStoreContent content, double similarity) {}
}
2. PgVectorStore:PostgreSQL实现
PostgreSQL配合pgvector
扩展是目前最流行、最成熟的开源向量数据库解决方案之一。Spring AI为其提供了完善的支持。
// vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java
public class PgVectorStore extends AbstractObservationVectorStore {private final JdbcTemplate jdbcTemplate;private final String vectorTableName;private final String vectorTableValidatedName;private final boolean initializeSchema;@Overridepublic void doAdd(List<Document> documents) {List<Map<String, Object>> batch = documents.stream().map(document -> {// 生成向量嵌入float[] embedding = this.embeddingModel.embed(document);String embeddingString = Arrays.toString(embedding);Map<String, Object> map = new HashMap<>();map.put("id", document.getId());map.put("content", document.getText());map.put("metadata", toJson(document.getMetadata()));map.put("embedding", embeddingString);return map;}).toList();// 批量插入String sql = String.format("""INSERT INTO %s (id, content, metadata, embedding) VALUES (:id, :content, :metadata::jsonb, :embedding::vector)ON CONFLICT (id) DO UPDATE SET content = EXCLUDED.content,metadata = EXCLUDED.metadata,embedding = EXCLUDED.embedding""", this.vectorTableValidatedName);this.jdbcTemplate.batchUpdate(sql, batch.toArray(new Map[0]));}@Overridepublic List<Document> doSimilaritySearch(SearchRequest request) {String query = request.getQuery();int topK = request.getTopK();double threshold = request.getSimilarityThreshold();// 生成查询向量float[] queryEmbedding = this.embeddingModel.embed(query);String embeddingString = Arrays.toString(queryEmbedding);// 构建SQL查询StringBuilder sqlBuilder = new StringBuilder();sqlBuilder.append("SELECT id, content, metadata, ");// 根据距离类型选择相似度计算方法switch (this.distanceType) {case COSINE_DISTANCE -> sqlBuilder.append("1 - (embedding <=> ?::vector) AS distance ");case EUCLIDEAN_DISTANCE -> sqlBuilder.append("embedding <-> ?::vector AS distance ");case INNER_PRODUCT -> sqlBuilder.append("(embedding <#> ?::vector) * -1 AS distance ");}sqlBuilder.append("FROM ").append(this.vectorTableValidatedName);// 添加元数据过滤条件if (request.getFilterExpression() != null) {String whereClause = this.filterExpressionConverter.convertExpression(request.getFilterExpression());sqlBuilder.append(" WHERE ").append(whereClause);}// 添加相似度阈值过滤if (threshold > 0) {String thresholdCondition = switch (this.distanceType) {case COSINE_DISTANCE -> "1 - (embedding <=> ?::vector) >= " + threshold;case EUCLIDEAN_DISTANCE -> "embedding <-> ?::vector <= " + (1.0 - threshold);case INNER_PRODUCT -> "(embedding <#> ?::vector) * -1 >= " + threshold;};if (request.getFilterExpression() != null) {sqlBuilder.append(" AND ").append(thresholdCondition);} else {sqlBuilder.append(" WHERE ").append(thresholdCondition);}}sqlBuilder.append(" ORDER BY distance DESC LIMIT ?");// 执行查询return this.jdbcTemplate.query(sqlBuilder.toString(),new Object[]{embeddingString, topK},(rs, rowNum) -> {String id = rs.getString("id");String content = rs.getString("content");String metadataJson = rs.getString("metadata");double distance = rs.getDouble("distance");Map<String, Object> metadata = fromJson(metadataJson);metadata.put("distance", distance);return Document.builder().id(id).text(content).metadata(metadata).build();});}
}
设计模式的精妙运用
1. 模板方法模式:AbstractObservationVectorStore
AbstractObservationVectorStore
是所有VectorStore
实现的基类,它优雅地运用了模板方法模式,将通用的监控、日志等逻辑固化在父类,而将具体的数据库操作(doAdd
, doDelete
, doSimilaritySearch
)交由子类实现。
public abstract class AbstractObservationVectorStore implements VectorStore {protected final EmbeddingModel embeddingModel;protected final ObservationRegistry observationRegistry;protected final VectorStoreObservationConvention observationConvention;@Overridepublic final void add(List<Document> documents) {// 模板方法:观察 -> 执行 -> 记录VectorStoreObservationContext context = VectorStoreObservationContext.builder().collectionName(getName()).dimensions(embeddingModel.dimensions()).namespace("default").operation(VectorStoreObservationContext.Operation.ADD).build();VectorStoreObservationDocumentation.VECTOR_STORE_ADD.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> context, this.observationRegistry).observe(() -> {doAdd(documents); // 子类实现具体逻辑context.setDocumentCount(documents.size());return null;});}// 子类实现具体的添加逻辑protected abstract void doAdd(List<Document> documents);// 类似的模板方法用于delete和similaritySearchprotected abstract void doDelete(List<String> idList);protected abstract List<Document> doSimilaritySearch(SearchRequest request);
}
2. 建造者模式:灵活的配置
几乎所有的VectorStore
实现都提供了Builder API,用于灵活、链式地构建实例。以PgVectorStore
为例:
public class PgVectorStore {public static PgVectorStoreBuilder builder(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) {return new PgVectorStoreBuilder(jdbcTemplate, embeddingModel);}public static class PgVectorStoreBuilder extends AbstractVectorStoreBuilder<PgVectorStoreBuilder> {private String tableName = DEFAULT_TABLE_NAME;private String schemaName = DEFAULT_SCHEMA_NAME;private PgDistanceType distanceType = PgDistanceType.COSINE_DISTANCE;private boolean initializeSchema = false;private PgIndexType indexType = PgIndexType.HNSW;public PgVectorStoreBuilder tableName(String tableName) {this.tableName = tableName;return this;}public PgVectorStoreBuilder schemaName(String schemaName) {this.schemaName = schemaName;return this;}public PgVectorStoreBuilder distanceType(PgDistanceType distanceType) {this.distanceType = distanceType;return this;}public PgVectorStoreBuilder initializeSchema(boolean initializeSchema) {this.initializeSchema = initializeSchema;return this;}public PgVectorStore build() {return new PgVectorStore(this);}}
}
3. 策略模式:不同的距离计算
在向量检索中,距离计算算法(如余弦距离、欧氏距离)的选择对结果影响很大。PgVectorStore
通过枚举和策略模式,将不同的算法封装起来,使得切换算法变得非常简单。
public enum PgDistanceType {COSINE_DISTANCE("cosine", "<=>"),EUCLIDEAN_DISTANCE("l2", "<->"),INNER_PRODUCT("inner", "<#>");private final String name;private final String operator;PgDistanceType(String name, String operator) {this.name = name;this.operator = operator;}public String getOperator() {return operator;}// 根据距离类型生成不同的SQLpublic String buildSimilarityQuery(String embeddingColumn, String queryVector) {return switch (this) {case COSINE_DISTANCE -> String.format("1 - (%s %s %s)", embeddingColumn, operator, queryVector);case EUCLIDEAN_DISTANCE -> String.format("%s %s %s", embeddingColumn, operator, queryVector);case INNER_PRODUCT -> String.format("(%s %s %s) * -1", embeddingColumn, operator, queryVector);};}
}
高级特性与优化
1. 批处理策略
在向向量数据库写入大量文档时,智能的批处理至关重要。Spring AI提供了BatchingStrategy
接口,允许开发者自定义批处理策略,例如根据Token数量进行批处理,以优化性能并避免超出API限制。
public interface BatchingStrategy {List<List<Document>> batch(List<Document> documents);
}public class TokenCountBatchingStrategy implements BatchingStrategy {private final int maxTokensPerBatch;private final TokenCountEstimator tokenCountEstimator;@Overridepublic List<List<Document>> batch(List<Document> documents) {List<List<Document>> batches = new ArrayList<>();List<Document> currentBatch = new ArrayList<>();int currentTokenCount = 0;for (Document document : documents) {int documentTokens = tokenCountEstimator.estimate(document.getText());if (currentTokenCount + documentTokens > maxTokensPerBatch && !currentBatch.isEmpty()) {batches.add(new ArrayList<>(currentBatch));currentBatch.clear();currentTokenCount = 0;}currentBatch.add(document);currentTokenCount += documentTokens;}if (!currentBatch.isEmpty()) {batches.add(currentBatch);}return batches;}
}
2. 异步处理支持
对于耗时的ETL或检索任务,可以结合Spring的@Async
注解实现异步处理,避免阻塞主线程。
@Service
public class AsyncVectorStoreService {private final VectorStore vectorStore;private final TaskExecutor taskExecutor;@Asyncpublic CompletableFuture<Void> addDocumentsAsync(List<Document> documents) {return CompletableFuture.runAsync(() -> {vectorStore.add(documents);}, taskExecutor);}@Asyncpublic CompletableFuture<List<Document>> searchAsync(String query, int topK) {return CompletableFuture.supplyAsync(() -> {return vectorStore.similaritySearch(query, topK);}, taskExecutor);}
}
3. 缓存优化
对于高频、重复的查询,可以利用Spring Cache轻松添加缓存层,显著降低延迟和成本。
@Service
public class CachedVectorStoreService {private final VectorStore vectorStore;private final Cache<String, List<Document>> searchCache;@Cacheable(value = "vectorSearch", key = "#query + '_' + #topK")public List<Document> cachedSimilaritySearch(String query, int topK) {return vectorStore.similaritySearch(query, topK);}@CacheEvict(value = "vectorSearch", allEntries = true)public void addDocuments(List<Document> documents) {vectorStore.add(documents);}
}
最佳实践与使用技巧
1. 选择合适的向量数据库
Spring AI的抽象层让你可以在不同环境中轻松切换VectorStore
实现。
// 开发和测试环境 - 使用SimpleVectorStore
@Profile("dev")
@Bean
public VectorStore devVectorStore(EmbeddingModel embeddingModel) {return SimpleVectorStore.builder(embeddingModel).build();
}// 生产环境 - 使用PgVector
@Profile("prod")
@Bean
public VectorStore prodVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) {return PgVectorStore.builder(jdbcTemplate, embeddingModel).tableName("knowledge_vectors").distanceType(PgDistanceType.COSINE_DISTANCE).indexType(PgIndexType.HNSW).initializeSchema(true).build();
}
2. 文档预处理
“Garbage in, garbage out.” 这句名言在AI领域同样适用。高质量的文档预处理是保证RAG效果的基石。
@Component
public class DocumentPreprocessor {private final TextSplitter textSplitter;private final MetadataEnhancer metadataEnhancer;public List<Document> preprocess(String content, Map<String, Object> metadata) {// 1. 文本分割List<String> chunks = textSplitter.split(content);// 2. 创建文档List<Document> documents = chunks.stream().map(chunk -> Document.builder().text(chunk).metadata(new HashMap<>(metadata)).build()).toList();// 3. 增强元数据return documents.stream().map(metadataEnhancer::enhance).toList();}
}@Component
public class MetadataEnhancer {public Document enhance(Document document) {Map<String, Object> metadata = new HashMap<>(document.getMetadata());// 添加文档统计信息metadata.put("word_count", countWords(document.getText()));metadata.put("char_count", document.getText().length());metadata.put("created_at", Instant.now().toString());// 添加内容特征metadata.put("has_code", containsCode(document.getText()));metadata.put("language", detectLanguage(document.getText()));return Document.builder().id(document.getId()).text(document.getText()).metadata(metadata).build();}
}
3. 智能检索策略
单一的向量检索有时会遇到瓶颈。可以结合多种策略来提升检索效果。
@Service
public class IntelligentRetriever {private final VectorStore vectorStore;private final ReRanker reRanker;public List<Document> intelligentSearch(String query, int topK) {// 1. 初始向量检索 - 获取更多候选List<Document> candidates = vectorStore.similaritySearch(query, topK * 2);// 2. 重排序 - 使用更精确的模型List<Document> reRanked = reRanker.rerank(query, candidates);// 3. 返回top-k结果return reRanked.stream().limit(topK).toList();}public List<Document> hybridSearch(String query, int topK) {// 1. 向量检索List<Document> vectorResults = vectorStore.similaritySearch(query, topK);// 2. 关键词检索(如果支持)List<Document> keywordResults = keywordSearch(query, topK);// 3. 结果融合return fuseResults(vectorResults, keywordResults, topK);}
}
小结
Spring AI的VectorStore
抽象层是一个设计精良、功能强大的向量数据库统一接口,其核心优势在于:
- 统一抽象:屏蔽了不同向量数据库的差异
- 可移植过滤:独创的类SQL元数据过滤语法,屏蔽了底层数据库的“方言”。
- 观察者模式:通过与Micrometer集成,为所有操作内置了监控和指标收集能力。
- 模板方法:通过抽象基类固化了通用流程(如监控、日志),让具体实现更专注于核心逻辑。
- 建造者模式:提供了灵活、链式的实例配置方式。
这套设计不仅让向量数据库的集成和使用变得前所未有的简单,更为构建复杂、高效的RAG应用提供了坚实的基础。无论你选择的是PostgreSQL的pgvector、专业的Pinecone,还是开源的Qdrant,Spring AI都能提供一致、流畅的开发体验。
下一章,我们将探索Spring AI最激动人心的特性之一——工具调用(Function Calling)机制。这是让AI能够与外部世界交互、从“知识库”进化为“行动者”的桥梁,也是构建真正智能应用的关键技术。
思考题:如果让你设计一个向量数据库的抽象层,你会如何处理不同数据库之间的性能差异和特性差异?Spring AI的方案给了你什么启发?