SpringAI Redis RAG 搜索
- [1 依赖](#1 依赖)
- [2 配置](#2 配置)
- [3 代码](#3 代码)
-
- [1 OllamaController .java](#1 OllamaController .java)
- [2 RagController.java](#2 RagController.java)
- [3 MemConf.java](#3 MemConf.java)
- [4 TestController.java](#4 TestController.java)
- [5 SpringaiRagApplication.java](#5 SpringaiRagApplication.java)
- [4 结果](#4 结果)
1 依赖
xml
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xu</groupId>
<artifactId>springai-rag</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springai-rag</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>25</java.version>
<spring-ai.version>1.0.3</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-transformers</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2 配置
yml
servlet:
port: 8080
context-path: /
spring:
application:
name: spring-onnx
ai:
vectorstore:
redis:
initialize-schema: true
index-name: custom-index
prefix: custom-prefix
embedding:
transformer:
onnx:
modelUri: classpath:/onnx/all-MiniLM-L6-v2/model.onnx
modelOutputName: last_hidden_state
gpuDeviceId: -1
tokenizer:
uri: classpath:/onnx/all-MiniLM-L6-v2/tokenizer.json
cache:
enabled: true
directory: cache/onnx/all-MiniLM-L6-v2/
ollama:
base-url: http://localhost:11434
chat:
model: gemma3:latest
data:
redis:
host: 127.0.0.1
3 代码
1 OllamaController .java
java
package com.xu.controller;
import lombok.AllArgsConstructor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.content.Media;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.http.MediaType;
import org.springframework.util.MimeType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;
@RestController
@AllArgsConstructor
@RequestMapping("/ollama")
public class OllamaController {
private final ChatMemory chatMemory;
private final OllamaChatModel model;
/**
* 聊天
*
* @param content 聊天内容
* @return 聊天结果
*/
@RequestMapping("/common/chat")
public Object chat(String content) {
return model.call(content);
}
/**
* 聊天记忆
*
* @param content 聊天内容
* @return 聊天结果
*/
@RequestMapping("/common/memory")
public Object memory(String content, String msgId) {
UserMessage message = UserMessage.builder().text(content).build();
chatMemory.add(msgId, message);
ChatResponse response = model.call(new Prompt(message));
chatMemory.add(msgId, response.getResult().getOutput());
return response.getResult().getOutput().getText();
}
/**
* 聊天图片
*
* @param content 聊天内容
* @return 聊天结果
*/
@RequestMapping("/common/image")
public Object image(@RequestParam("content") String content, @RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return model.call(content);
}
var media = new Media(new MimeType("image", "png"), file.getResource());
return model.call(new Prompt(UserMessage.builder().media(media).text(content).build()));
}
/**
* 聊天流式
*
* @param content 聊天内容
* @return 聊天结果
*/
@RequestMapping(value = "/stream/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(String content) {
return model.stream(content);
}
}
2 RagController.java
java
package com.xu.controller;
import lombok.AllArgsConstructor;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.ExtractedTextFormatter;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@AllArgsConstructor
@RequestMapping("/rag")
public class RagController {
private final VectorStore vectorStore;
/**
* 聊天
*
* @param content 聊天内容
* @return 聊天结果
*/
@RequestMapping("/save")
public Object save(String content) {
Resource resource = new FileSystemResource("E:\\我的照片\\我的简历");
PagePdfDocumentReader reader = new PagePdfDocumentReader(resource,
PdfDocumentReaderConfig.builder()
.withPageExtractedTextFormatter(ExtractedTextFormatter.defaults())
.withPagesPerDocument(1)
.build());
List<Document> documents = reader.read();
vectorStore.add(documents);
return documents.size();
}
/**
* rag 搜索
*
* @param content 聊天内容
* @return 搜索结果
*/
@RequestMapping("/search1")
public Object search1(String content) {
SearchRequest request = SearchRequest.builder()
.query(content) // 查询
.topK(5) // 返回的相似文档数量
.similarityThreshold(0.9) // 相似度阈值
//.filterExpression("file_name == '中二知识笔记.pdf'") // 过滤条件
.build();
List<Document> docs = vectorStore.similaritySearch(request);
return docs.stream().map(doc -> {
Map<String, Object> map = new HashMap<>();
map.put("id", doc.getId());
map.put("score", doc.getScore());
map.put("text", doc.getText());
return map;
}).toList();
}
/**
* rag 搜索
*
* @param content 聊天内容
* @return 搜索结果
*/
@RequestMapping("/search2")
public Object search2(String content) {
List<Document> docs = vectorStore.similaritySearch("content");
return docs.stream().map(doc -> {
Map<String, Object> map = new HashMap<>();
map.put("id", doc.getId());
map.put("score", doc.getScore());
map.put("text", doc.getText());
return map;
}).toList();
}
}
3 MemConf.java
java
package com.xu.conf;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MemConf {
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.maxMessages(20)
.build();
}
}
4 TestController.java
java
package com.xu.controller;
import lombok.AllArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AllArgsConstructor
@RequestMapping("/test")
public class TestController {
private final OllamaChatModel chatModel;
private final VectorStore vectorStore;
/**
* 聊天
*
* @param content 聊天内容
* @return 聊天结果
*/
@RequestMapping("/common/rag1")
public Object rag1(String content) {
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().build())
.build())
.build();
return chatClient.prompt()
.user(content)
.call().chatResponse();
}
/**
* 聊天
*
* @param content 聊天内容
* @return 聊天结果
*/
@RequestMapping("/common/rag2")
public Object rag2(String content) {
return ChatClient.builder(chatModel)
.build().prompt()
.advisors(QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().similarityThreshold(0.8d).topK(6).build())
.build())
.user(content)
.call()
.chatResponse();
}
}
5 SpringaiRagApplication.java
java
package com.xu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringaiRagApplication {
static void main(String[] args) {
SpringApplication.run(SpringaiRagApplication.class, args);
}
}
4 结果
java
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.5.6)
2025-10-23T22:33:18.966+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] com.xu.SpringaiRagApplication : Starting SpringaiRagApplication using Java 25 with PID 29416 (D:\SourceCode\JavaLearn\springai-rag\target\classes started by xuhya in D:\SourceCode\JavaLearn\springai-rag)
2025-10-23T22:33:18.971+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] com.xu.SpringaiRagApplication : No active profile set, falling back to 1 default profile: "default"
2025-10-23T22:33:19.039+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2025-10-23T22:33:19.039+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2025-10-23T22:33:19.951+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
2025-10-23T22:33:19.954+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2025-10-23T22:33:19.986+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 15 ms. Found 0 Redis repository interfaces.
2025-10-23T22:33:20.553+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-10-23T22:33:20.573+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-10-23T22:33:20.573+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.46]
2025-10-23T22:33:20.643+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-10-23T22:33:20.644+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1604 ms
2025-10-23T22:33:21.204+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.s.a.transformers.ResourceCacheService : The class path resource [onnx/all-MiniLM-L6-v2/tokenizer.json] resource with URI schema [file] is excluded from caching
2025-10-23T22:33:21.415+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] ai.djl.util.Platform : Found matching platform from: jar:file:/E:/Maven/ai/djl/huggingface/tokenizers/0.32.0/tokenizers-0.32.0.jar!/native/lib/tokenizers.properties
2025-10-23T22:33:21.757+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.s.a.transformers.ResourceCacheService : The class path resource [onnx/all-MiniLM-L6-v2/model.onnx] resource with URI schema [file] is excluded from caching
2025-10-23T22:33:22.167+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.s.a.t.TransformersEmbeddingModel : Model input names: input_ids, attention_mask, token_type_ids
2025-10-23T22:33:22.168+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.s.a.t.TransformersEmbeddingModel : Model output names: last_hidden_state
2025-10-23T22:33:23.227+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2025-10-23T22:33:23.271+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2025-10-23T22:33:23.289+08:00 INFO 29416 --- [spring-onnx] [ restartedMain] com.xu.SpringaiRagApplication : Started SpringaiRagApplication in 4.907 seconds (process running for 5.543)

