SpringAI Redis RAG 搜索

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)


相关推荐
小沈同学呀2 天前
SpringAI+MCPServer实战-StreamableHTTP协议打造企业级AI工具服务
人工智能·微服务架构·springai·mcpserver·javaai·streamablehttp
莫逸风2 天前
【AgentScope】6.文件系统(Filesystem)详解
开发语言·windows·springai·agentscope·agnet
中草药z2 天前
【RAG】工程化实战:全链路原理复盘 + 方案选型 + 实战高阶玩法
java·深度学习·机器学习·阿里云·rag·springai
阿昌喜欢吃黄桃6 天前
Java优质开源AI项目
java·ai·langchain·开源·rag·springai·langchain4j
流放深圳6 天前
抓住 AI 人工智能的风口之第 5 章 —— 使用视觉大模型(Vision-Language Model)支持图片识别,完善电商智能客服项目
人工智能·视觉大模型·图片识别·springai·vision-language
莫逸风7 天前
【AgentScope】3. 工作空间(Workspace)详解
java·ai·agent·springai·agentscope
莫逸风9 天前
【AgentScope】1. HarnessAgent 总览详解
springai·agentscope·agnet
Maiko Star10 天前
理解 RAG 的“为什么”与 Spring AI 实战初体验
人工智能·rag·springai
Maiko Star11 天前
SpringAI 模型 API 调用中的错误处理、重试与熔断降级实战
错误处理·springai
装不满的克莱因瓶14 天前
SpringAI Alibaba Tool工具调用机制实战-注解注册与函数调用全流程
人工智能·ai·tools·智能体·springai·tool