java 使用 spring AI 实战 RAG (Chroma 向量数据库+Advisor)

一、环境准备

Ollama

我这里用的 :ollama+ qwen 大家可以根据自己的环境自行更改
Windows版本(qwen 模型):https://blog.csdn.net/YXWik/article/details/143871588
Linux版本(deepseek模型 含安装脚本):https://blog.csdn.net/YXWik/article/details/149497501

Chroma 向量数据库

python版的RAG中装过chromahttps://blog.csdn.net/YXWik/article/details/147392086

这里直接启动chroma就行:

cmd 复制代码
chroma run 

默认是 8000 端口 以下命令更改端口启动

cpp 复制代码
chroma run --host 0.0.0.0 --port 8000

二、java开发

POM文件

Ollama 依赖

cpp 复制代码
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-model-ollama</artifactId>
    </dependency>

Chroma 向量数据库依赖

cpp 复制代码
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-vector-store-chroma</artifactId>
    </dependency>

Advisor API 依赖

cpp 复制代码
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-advisors-vector-store</artifactId>
    </dependency>

版本

cpp 复制代码
 <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-bom</artifactId>
        <version>1.0.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

整合

cpp 复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.yxy</groupId>
  <artifactId>spring-ai-vectorStore</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>spring-ai-vectorStore</name>
  <url>http://maven.apache.org</url>

  <properties>
    <java.version>17</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>3.2.5</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-model-ollama</artifactId>
    </dependency>



    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-vector-store-chroma</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-advisors-vector-store</artifactId>
    </dependency>

	<dependency>
	  <groupId>org.springframework.ai</groupId>
	  <artifactId>spring-ai-tika-document-reader</artifactId>
	</dependency>


    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <version>3.2.4</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>17</source>
          <target>17</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-bom</artifactId>
        <version>1.0.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>



</project>

yaml配置文件

yml 复制代码
spring:
  application:
    name: spring-ai-vectorStore

  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        options:
          model: qwen2.5-coder:7b

    vectorstore:
      chroma:
        host: localhost
        port: 8000
        collection-name: TestCollection
        tenant-name: default_tenant          # Chroma 默认 tenant
        database-name: default_database      # Chroma 默认 database
        initialize-schema: true             # 自动创建 collection

使用向量库

引入向量库实体

cpp 复制代码
   @Autowired
    private VectorStore vectorStore;

添加 QuestionAnswerAdvisor 实例以启用 RAG 功能
QuestionAnswerAdvisor基于向量相似度的文档检索和答案生成

cpp 复制代码
.advisors(new QuestionAnswerAdvisor(vectorStore))

向量库插入文本数据(追加版)

cpp 复制代码
    @GetMapping("/ai/vector/add")
    public String addVector(@RequestParam(value = "text") String text) {
        /**
         * Document 文档类型
         *  text 文档内容
         *  metadata 元数据,用于增强检索能力,标注文档额外数据,例如来源,时间等
         * Map.of("source", "manual") 标识这些文档的来源是手动添加的
         */
        List<Document> documents = List.of(
                new Document(text, Map.of("source", "manual")));

        vectorStore.add(documents);
        return "success";
    }

测试写入

对话

cpp 复制代码
    @GetMapping("/ai/ollama")
    public String ollama(@RequestParam(value = "msg") String msg) {
        ChatClient chatClient = ChatClient.builder(ollamaChatModel)
                .build();
        String content = chatClient.prompt(msg).advisors(new QuestionAnswerAdvisor(vectorStore)).call().content();
        System.out.println(content);
        return content;
    }

向量库插入文档数据(追加版)

测试.docx

java 复制代码
YXWik是一个java开发者,喜欢探索AI。
技能有java,mysql
平时最喜欢的游戏是穿越火线

依赖

java 复制代码
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-tika-document-reader</artifactId>
</dependency>

接口

java 复制代码
@GetMapping("/ai/vector/addDocument")
    public String addDocument() {
        String path = "G:\\桌面\\测试.docx";
        String label = "document";
        // 读取文件
        FileSystemResource resource = new FileSystemResource(path);
        TikaDocumentReader tikaReader = new TikaDocumentReader(resource);
        List<Document> docbatch = tikaReader.read();
        // 文件发送给适量存储  将文档切分为512字符大小的块
        docbatch = TokenTextSplitter.builder().withChunkSize(512).withMaxNumChunks(100).build().apply(docbatch);
        System.out.println("添加的文档大小:" + docbatch.size());
        // 最后为每块文档添加标签并存入向量数据库
        docbatch.forEach(doc -> {
            System.out.println("添加的文档内容:"+doc.getText());
            doc.getMetadata().put("label",label);
            vectorStore.add(List.of(doc));
        });
        return "success";
    }

对话

我将文本内容进行了变更

重新将文件内容写入chroma

再次对话

虽然还是一个文件但是,内容是追加而不是替换的

要实现 "修改文件后重新上传会覆盖旧内容",需要手动添加 "删除旧数据 + 新增新数据" 的逻辑

向量库插入文档数据(替换版)

测试文件

接口

java 复制代码
@GetMapping("/ai/vector/addDocumentOverRide")
    public String addDocumentOverRide() {
        String path = "G:\\桌面\\测试替换.docx";
        String label = "document";
        // 1. 生成该文件旧文档块的所有可能ID(格式:文件路径+序号)
        List<String> oldDocumentIds = new ArrayList<>();
        // 假设最大块数为100(与拆分时的maxNumChunks一致)
        for (int i = 0; i < 100; i++) {
            oldDocumentIds.add(path + "_" + i);
        }
        // 2. 批量删除该文件的旧文档块(按ID删除)
        vectorStore.delete(oldDocumentIds); // 按ID列表删除
        // 3. 读取新文件并拆分
        FileSystemResource resource = new FileSystemResource(path);
        TikaDocumentReader tikaReader = new TikaDocumentReader(resource);
        List<Document> docbatch = tikaReader.read();
        docbatch = TokenTextSplitter.builder()
                .withChunkSize(512)
                .withMaxNumChunks(100)
                .build()
                .apply(docbatch);

        // 4. 为新文档块设置唯一ID(文件路径+块索引)
        List<Document> newDocuments = new ArrayList<>();
        for (int i = 0; i < docbatch.size(); i++) {
            Document originalDoc = docbatch.get(i);
            // 构建新文档,指定唯一ID
            Document newDoc = new Document(
                    path + "_" + i, // 自定义ID:文件路径+块索引
                    originalDoc.getText(),
                    originalDoc.getMetadata()
            );
            newDoc.getMetadata().put("label", label);
            newDoc.getMetadata().put("filePath", path);
            newDocuments.add(newDoc);
        }
        // 5. 添加新文档
        vectorStore.add(newDocuments);
        return "success";
    }

调用

对话

更改文件

调用

对话

查询向量库所有内容

cpp 复制代码
    @GetMapping("/ai/vector/list")
    public List<String> listAllDocs() {

        List<Document> allDocs = vectorStore.similaritySearch(""); // 用空文本查所有文档
        System.out.println("所有文档数量:" + allDocs.size());
        System.out.println("所有文档内容:");
        allDocs.forEach(System.out::println);
        return allDocs.stream().map(Document::getText).collect(Collectors.toList());
    }


踩坑记录

如果项目启动报错如下:

cpp 复制代码
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2025-09-04T13:49:08.939+08:00 ERROR 30180 --- [spring-ai-vectorStore] [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ollamaController': Unsatisfied dependency expressed through field 'vectorStore': Error creating bean with name 'vectorStore' defined in class path resource [org/springframework/ai/vectorstore/chroma/autoconfigure/ChromaVectorStoreAutoConfiguration.class]: I/O error on GET request for "http://localhost:8000/api/v2/tenants/SpringAiTenant/databases/SpringAiDatabase/collections/TestCollection": Connection refused: connect
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:787) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:767) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:508) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1419) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.6.jar:6.1.6]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.6.jar:6.1.6]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.5.jar:3.2.5]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.5.jar:3.2.5]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.5.jar:3.2.5]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[spring-boot-3.2.5.jar:3.2.5]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-3.2.5.jar:3.2.5]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-3.2.5.jar:3.2.5]
	at org.yxy.OllamaApplication.main(OllamaApplication.java:14) ~[classes/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vectorStore' defined in class path resource [org/springframework/ai/vectorstore/chroma/autoconfigure/ChromaVectorStoreAutoConfiguration.class]: I/O error on GET request for "http://localhost:8000/api/v2/tenants/SpringAiTenant/databases/SpringAiDatabase/collections/TestCollection": Connection refused: connect
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:784) ~[spring-beans-6.1.6.jar:6.1.6]
	... 20 common frames omitted
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8000/api/v2/tenants/SpringAiTenant/databases/SpringAiDatabase/collections/TestCollection": Connection refused: connect
	at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.createResourceAccessException(DefaultRestClient.java:557) ~[spring-web-6.1.6.jar:6.1.6]
	at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:482) ~[spring-web-6.1.6.jar:6.1.6]
	at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.retrieve(DefaultRestClient.java:444) ~[spring-web-6.1.6.jar:6.1.6]
	at org.springframework.ai.chroma.vectorstore.ChromaApi.getCollection(ChromaApi.java:226) ~[spring-ai-chroma-store-1.0.0.jar:1.0.0]
	at org.springframework.ai.chroma.vectorstore.ChromaVectorStore.afterPropertiesSet(ChromaVectorStore.java:121) ~[spring-ai-chroma-store-1.0.0.jar:1.0.0]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1833) ~[spring-beans-6.1.6.jar:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782) ~[spring-beans-6.1.6.jar:6.1.6]
	... 30 common frames omitted
Caused by: java.net.ConnectException: Connection refused: connect
	at java.base/sun.nio.ch.Net.connect0(Native Method) ~[na:na]
	at java.base/sun.nio.ch.Net.connect(Net.java:579) ~[na:na]
	at java.base/sun.nio.ch.Net.connect(Net.java:568) ~[na:na]
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:588) ~[na:na]
	at java.base/java.net.Socket.connect(Socket.java:633) ~[na:na]
	at java.base/java.net.Socket.connect(Socket.java:583) ~[na:na]
	at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:183) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:498) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:603) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:246) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:351) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:373) ~[na:na]
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1309) ~[na:na]
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1242) ~[na:na]
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1128) ~[na:na]
	at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:1057) ~[na:na]
	at org.springframework.http.client.SimpleClientHttpRequest.executeInternal(SimpleClientHttpRequest.java:79) ~[spring-web-6.1.6.jar:6.1.6]
	at org.springframework.http.client.AbstractStreamingClientHttpRequest.executeInternal(AbstractStreamingClientHttpRequest.java:70) ~[spring-web-6.1.6.jar:6.1.6]
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) ~[spring-web-6.1.6.jar:6.1.6]
	at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:476) ~[spring-web-6.1.6.jar:6.1.6]
	... 35 common frames omitted

说明chroma是 通过 chroma run启动的 停止后采用chroma run --host 0.0.0.0 --port 8000启动即可解决

相关推荐
间彧6 小时前
Stream.collect(Collectors.toList())和Stream.toList()
java
今天也要学习吖6 小时前
OpenAI开放ChatGPT Projects功能,免费用户也能用了!
人工智能·chatgpt·aigc·openai·chatgptprojects
We....6 小时前
Java集合---Collection接口和Map接口
java·开发语言
聚客AI6 小时前
👀10分钟搞懂RAG架构:离线索引+在线检索的闭环秘密
人工智能·llm·agent
海天瑞声AI7 小时前
“AI 正回应时,也可随时打断?”揭秘 GPT Realtime × Gemini 的“全双工魔力”,都离不开它!
数据库·人工智能·语音识别
程序员鱼皮7 小时前
Java 8 终于要被淘汰了!带你速通 Java 8~24 新特性 | 又能跟面试官吹牛皮了
java·后端·程序员
机器之心7 小时前
Nano Banana爆火之后,一个神秘的「胡萝卜」代码模型又上线了
人工智能·openai
whh119whh7 小时前
从关键词到语义理解:小陌引擎如何重构AI搜索优化逻辑?
人工智能
蓝倾9767 小时前
1688拍立淘接口对接实战案例
java·开发语言·数据库·python·电商开放平台·开放api接口