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)


相关推荐
006_2 天前
Python工具将本地Embedding转换成onnx格式
springai
程序员小赵同学3 天前
Spring AI Alibaba文生图实战:从零开始编写AI图片生成Demo
阿里云·ai·springboot·springai
BeingACoder7 天前
【SAA】SpringAI Alibaba学习笔记(二):提示词Prompt
java·人工智能·spring boot·笔记·prompt·saa·springai
BeingACoder9 天前
【SAA】SpringAI Alibaba学习笔记(一):SSE与WS的区别以及如何注入多个AI模型
java·笔记·学习·saa·springai
FightingITPanda12 天前
Spring AI 搭建 RAG 个人知识库
人工智能·知识库·rag·springai·向量库
程序员小赵同学12 天前
Spring AI 极简入门:15分钟集成AI到SpringBoot应用
人工智能·spring boot·spring·springai
FightingITPanda15 天前
springAI +openAI 接入阿里云百炼大模型-通义千问
openai·通义千问·springai·阿里云百炼
曾经的三心草18 天前
SpringAI5-智能聊天机器⼈
java·springai
深色風信子22 天前
SpringAI 内嵌模型 ONNX
springai·springai onnx·embedding onnx·java springai·java onnx·java embedding