在开发基于Spring AI的项目时,如何配置Postgres PgVectorStore来存储向量数据是一项关键技能。本文将围绕这一主题展开,详细介绍如何在Spring AI项目中配置Postgres PgVectorStore,以存储使用OpenAIOllama嵌入模型生成的向量,这些向量可用于构建检索增强生成(RAG)风格的应用程序,如聊天机器人。

一、安装Postgres PgVector数据库

pgvector是一款为PostgreSQL打造的开源扩展,它能在数据库中高效存储向量数据,并执行相似性搜索,还能与PostgreSQL的其他功能,如索引和查询,无缝协作。

(一)使用Docker安装

在机器上安装pgvector,最简单的方法就是借助Docker。执行下面的命令:

docker run -it --rm --name postgres -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e VECTOR_STORE_TYPE=pgVector pgvector/pgvector:pg16 

要是使用Docker Compose,就添加以下服务,并可根据需求进行额外的自定义配置:

services: postgres: image: pgvector/pgvector:pg16 container_name: postgres ports: - "5432:5432" environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres VECTOR_STORE_TYPE: pgVector 

如果你已经安装了Postgres,那就必须启用vectorhstoreuuid-ossp这几个扩展。

(二)在Spring AI中配置PgVector

Spring AI能在启动时自动帮你启用所需的扩展。当spring.ai.vectorstore.pgvector.initialize-schema属性被设为true时,Spring AI会运行以下模式文件:

-- 若vector扩展不存在,则创建它 CREATE EXTENSION IF NOT EXISTS vector; -- 若hstore扩展不存在,则创建它 CREATE EXTENSION IF NOT EXISTS hstore; -- 若uuid-ossp扩展不存在,则创建它 CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- 创建名为vector_store的表,若该表不存在 CREATE TABLE IF NOT EXISTS vector_store ( id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, content text, metadata json, -- embedding字段存储向量,默认维度为1536 embedding vector(1536) ); -- 基于embedding字段创建HNSW索引,使用向量余弦操作 CREATE INDEX ON vector_store USING HNSW (embedding vector_cosine_ops); 

要是想自定义index-type(索引类型)、distance-type(距离类型)和dimensions(维度),可以在application.properties文件里修改相应的属性:

spring.datasource.url=jdbc:postgresql://localhost:5432/postgres spring.datasource.username=postgres spring.datasource.password=postgres spring.ai.vectorstore.pgvector.initialize-schema=true spring.ai.vectorstore.pgvector.index-type=HNSW spring.ai.vectorstore.pgvector.distance-type=COSINE_DISTANCE spring.ai.vectorstore.pgvector.dimensions=1536 spring.ai.vectorstore.pgvector.schema-validation=true spring.ai.vectorstore.pgvector.remove-existing-vector-store-table=false 

二、结合OpenAI嵌入模型配置PgVectorStore

下面来构建一个简单的演示应用,使用OpenAI的text-embedding-ada-002模型生成嵌入向量,用gpt-4模型实现聊天功能。

(一)Maven依赖配置

首先添加如下依赖:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId> </dependency> 

(二)属性配置

接下来,要指定用于生成向量和进行聊天对话的模型,同时还要设置PgVector的连接信息以及其他可选配置:

spring.ai.openai.api-key=${OPENAI_API_KEY} spring.ai.openai.chat.model=gpt-4 spring.ai.openai.embedding.model=text-embedding-ada-002 spring.datasource.url=jdbc:postgresql://localhost:5432/postgres spring.datasource.username=postgres spring.datasource.password=postgres spring.ai.vectorstore.pgvector.initialize-schema=true spring.ai.vectorstore.pgvector.index-type=HNSW spring.ai.vectorstore.pgvector.distance-type=COSINE_DISTANCE spring.ai.vectorstore.pgvector.dimensions=1536 spring.ai.vectorstore.pgvector.schema-validation=true spring.ai.vectorstore.pgvector.remove-existing-vector-store-table=false 

(三)演示应用代码

应用启动时,Spring Boot的自动配置会依据配置属性创建ChatClientEmbeddingModelVectorStore这几个bean,其中VectorStore bean的类型是PgVectorStore类。我们可以利用配置好的VectorStore bean,存储由OpenAI模型生成的嵌入向量,并在Postgres数据库中进行相似性搜索。代码如下:

import java.util.List; import java.util.Map; import org.springframework.ai.document.Document; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App implements CommandLineRunner { public static void main(String[] args) { // 启动Spring Boot应用 SpringApplication.run(App.class); } // 注入VectorStore实例 private final VectorStore vectorStore; public App(VectorStore vectorStore) { this.vectorStore = vectorStore; } @Override public void run(String... args) { // 创建包含多个Document对象的列表,每个Document对象包含不同的文本内容和元数据 List<Document> documents = List.of( new Document("Java is a high-level, object-oriented programming language known for its platform independence."), new Document("It is widely used for developing enterprise applications, Android apps, and big data processing systems."), new Document("Java's strong typing, automatic memory management, and extensive libraries contribute to its popularity.", Map.of("reason", "popularity"))); // 将文档添加到PGVector数据库 vectorStore.add(documents); // 根据查询条件“programming language”进行相似性搜索,获取最相似的1个文档 List<Document> results = vectorStore .similaritySearch(SearchRequest.query("programming language").withTopK(1)); // 遍历搜索结果,打印文档内容 results.stream() .map(Document::getContent) .forEach(System.out::println); } } 

程序输出结果为:

Java is a high-level, object-oriented programming language known for its platform independence. 

三、结合Ollama嵌入模型配置PgVectorStore

在使用PgVectorStore与本地部署的大语言模型(LLM)交互时,Ollama是个不错的选择,它能让模型的下载和运行变得十分轻松。关于Ollama的安装,你可以参考《Ollama本地设置和Spring AI集成示例》获取详细说明。在本次演示中,使用Docker在本地运行Ollama,命令如下:

docker run -d --gpus all -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama 

Ollama运行起来后,用run命令就能下载并运行模型,比如运行mistral模型:

ollama run mistral 

(一)Maven依赖配置

先添加如下依赖,这些启动器会导入所有必要的依赖:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId> </dependency> 

(二)属性配置

运行模型并设置好依赖后,就可以配置应用属性,指定生成嵌入向量的模型,并连接到PgVector数据库:

spring.ai.ollama.embedding.options.model=mistral spring.ai.ollama.chat.options.model=mistral spring.datasource.url=jdbc:postgresql://localhost:5432/postgres spring.datasource.username=postgres spring.datasource.password=postgres spring.ai.vectorstore.pgvector.initialize-schema=true spring.ai.vectorstore.pgvector.index-type=HNSW spring.ai.vectorstore.pgvector.distance-type=COSINE_DISTANCE spring.ai.vectorstore.pgvector.dimensions=4096 #spring.ai.vectorstore.pgvector.schema-validation=true #spring.ai.vectorstore.pgvector.remove-existing-vector-store-table=false 

(三)演示应用代码

Spring Boot在应用启动时会初始化并自动配置EmbeddingModelVectorStore这两个bean。我们可以像前面的演示应用那样,用这些bean生成嵌入向量并进行相似性搜索。代码如下:

import java.util.List; import java.util.Map; import org.springframework.ai.document.Document; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class PgVectorOllamaApp implements CommandLineRunner { public static void main(String[] args) { // 启动Spring Boot应用 SpringApplication.run(PgVectorOllamaApp.class); } // 注入VectorStore实例 private final VectorStore vectorStore; public PgVectorOllamaApp(VectorStore vectorStore) { this.vectorStore = vectorStore; } @Override public void run(String... args) { // 创建包含多个Document对象的列表,每个Document对象包含不同的文本内容和元数据 List<Document> documents = List.of( new Document("Java is a high-level, object-oriented programming language known for its platform independence."), new Document("It is widely used for developing enterprise applications, Android apps, and big data processing systems."), new Document("Java's strong typing, automatic memory management, and extensive libraries contribute to its popularity.", Map.of("reason", "popularity"))); // 将文档添加到PGVector数据库 vectorStore.add(documents); // 根据查询条件“programming language”进行相似性搜索,获取最相似的1个文档 List<Document> results = vectorStore .similaritySearch(SearchRequest.query("programming language").withTopK(1)); // 遍历搜索结果,打印文档内容 results.stream() .map(Document::getContent) .forEach(System.out::println); } } 

程序输出结果为:

Java is a high-level, object-oriented programming language known for its platform independence. 

四、解决“column cannot have more than 2000 dimensions”错误

需要注意的是,PgVector的嵌入列在创建索引时,维度不能超过2000。但大多数大语言模型生成的向量维度都大于2000。所以,如果使用HNSWivfflat这类索引类型,在存储生成的嵌入向量时就会遇到运行时错误。

比如,下面这种索引类型和维度的错误组合:

spring.ai.vectorstore.pgvector.index-type=ivfflat spring.ai.vectorstore.pgvector.dimensions=4096 

会导致如下运行时错误:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'pgVectorOllamaApp': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'vectorStore' defined in class path resource [org/springframework/ai/autoconfigure/vectorstore/pgvector/ PgVectorStoreAutoConfiguration.class]: StatementCallback; uncategorized SQLException for SQL [CREATE INDEX IF NOT EXISTS spring_ai_vector_index ON public.vector_store USING IVFFLAT (embedding vector_cosine_ops) ]; SQL state [XX000]; error code [0]; ERROR: column cannot have more than 2000 dimensions for ivfflat index 

解决这个错误有以下两种办法:

  1. 换用其他向量存储:比如使用ChromaDB。
  2. 将索引类型设为none:不过这样会影响性能,它只适合在开发环境中用于测试,生产环境千万别用。
spring.ai.vectorstore.pgvector.index-type=none # 不要在生产环境使用,仅适用于演示目的 spring.ai.vectorstore.pgvector.dimensions=4096 

修改完后,重新生成模式,就不会再遇到“column cannot have more than 2000 dimensions”这个错误了。

spring.ai.vectorstore.pgvector.initialize-schema=true spring.ai.vectorstore.pgvector.remove-existing-vector-store-table=true 

五、总结

本文围绕Spring AI集成PgVectorStore展开,具体讨论了以下内容:

  1. 借助Docker安装Postgres向量数据库的方法。
  2. 在Postgres数据库中启用PgVector扩展的操作。
  3. Spring AI连接pgvector的自动配置选项和自定义属性。
  4. 在RAG风格的应用中,如何配置PgVector和OpenAI大语言模型。
  5. 如何配置PgVector和Ollama大语言模型。
  6. 当使用HNSWivfflat索引类型时,维度不匹配错误的解决办法。

希望通过本文的学习,大家能在Spring AI项目中熟练配置和使用PgVectorStore。