Spring AI Alibaba-03- Spring AI + DeepSeek-R1 + ES/Milvus + RAG 智能对话应用开发全流程
Spring AI Alibaba-03- Spring AI + DeepSeek-R1 + ES/Milvus + RAG 智能对话应用开发全流程
在[人工智能](AI)应用中,模型通常需要访问外部资源或执行特定操作,例如数据库查询、调用外部API或执行计算任务。Spring AI,作为Spring官方开源的[AI应用]开发框架,旨在简化将AI功能集成到应用程序的过程,避免不必要的复杂性,
文章目录
- Spring AI Alibaba-03- Spring AI + DeepSeek-R1 + ES/Milvus + RAG 智能对话应用开发全流程
- Spring AI 中 Function Calling、Tool Calling 和 Agent 是什么关系
- Spring AI 介绍
- Spring AI的版本演进
- Spring AI的主要功能
- **函数调用(Function Calling)**
- 功能与作用
- 实现方式
- **工具调用(Tool Calling)**
- **功能与作用**
- 实现方式
- 智能体(Agent)
- 功能与作用
- 实现方式
- 从函数调用(FunctionCallbac)到工具调用(ToolCallback)
- Spring AI + DeepSeek-R1 + ES/Milvus + RAG + Tool Calling:智能对话应用开发
- 环境准备
- 功能介绍
- 代码实现
- 测试验证
- 附:向量数据库milvus 存储数据
- Milvus安装
Spring AI 中 Function Calling、Tool Calling 和 Agent 是什么关系
Spring AI 介绍
Spring AI的版本演进
Spring AI自发布以来,经过了多个版本的迭代更新,每个版本都带来了新的特性和改进,以下是一些重要版本的发布情况:
- 2024年5月30日:发布了1.0.0 Milestone 1版本,标志着API的基本确定,功能逐步完善。
- 2024年8月23日:发布了1.0.0 Milestone 2版本,增强了可观察性功能,提高了监控和调试能力。
- 2024年10月8日:发布了1.0.0 Milestone 3版本,进一步扩展了对多种AI模型的支持。
- 2024年11月20日:发布了1.0.0 Milestone 4版本,加入了对Amazon Bedrock Converse API的支持,提升了与Amazon模型的兼容性。
- 2025年2月14日:发布了1.0.0 Milestone 6版本,带来了多项新特性和改进,进一步巩固了框架的稳定性
Spring AI的主要功能
Spring AI为开发者提供了多种AI功能的支持,包括:
- 智能聊天:集成大型语言模型(LLM),实现自然语言处理和对话生成。
- 文本生成:利用AI模型生成创意文本内容,例如文章、故事等。
- 图像生成:通过文本提示生成图像,支持多种图像生成模型。
- 音频处理:实现语音转文本和文本转语音功能,增强应用的交互性。
- 功能调用:允许模型请求执行客户端工具和函数,以访问实时信息。
函数调用(Function Calling)
功能与作用
在AI应用中,模型常常需要访问外部资源或执行特定操作(如数据库查询、调用API等)。函数调用功能允许AI模型在生成响应时,调用预定义的外部函数或服务,以获取模型无法直接生成的信息。举个例子,模型可以通过调用天气查询函数,获取实时天气数据。
实现方式
在Spring AI中,函数调用的实现步骤如下:
1、定义函数:开发者创建一个实现Function
接口的Java类,明确函数的输入输出类型。例如,定义一个获取天气信息的函数:
@Component
public class WeatherFunction implements Function<WeatherFunction.WeatherRequest, String> {@Overridepublic String apply(WeatherRequest request) {return "The weather in " + request.getCity() + " is sunny.";}public static class WeatherRequest {private String city;public String getCity() { return city; }public void setCity(String city) { this.city = city; }}
}
2、注册函数:将函数注册为Spring Bean,使其能被Spring AI识别和调用
@Configuration
public class FunctionConfig {@Bean@Description("获取指定城市的天气信息")public Function<WeatherFunction.WeatherRequest, String> weatherFunction() {return new WeatherFunction();}
}
3、配置模型:在Spring AI的聊天客户端配置中,指定模型可以调用的函数:
@Configuration
public class ChatConfig {@Autowiredprivate ChatClient.Builder chatClientBuilder;@Beanpublic ChatClient chatClient() {return chatClientBuilder.withOptions(ChatOptions.builder().withFunction("weatherFunction").build()).build();}
}
4、模型调用函数:模型在生成响应时,可以通过请求调用外部函数,得到结果:
@RestController
public class ChatController {@Autowiredprivate ChatClient chatClient;@GetMapping("/chat")public String chat(@RequestParam String input) {WeatherFunction.WeatherRequest request = new WeatherFunction.WeatherRequest();request.setCity(input);String response = chatClient.prompt().user(input).functions("weatherFunction").call().content();return response;}
}
注意:需要注意的是,Spring AI中的函数调用功能已被弃用,建议使用工具调用(Tool Calling)来实现类似功能。
工具调用(Tool Calling)
功能与作用
工具调用允许AI模型在生成响应时调用外部工具或服务。这些工具能够帮助模型获取实时数据、执行特定操作或增强其功能。例如,模型可以调用天气查询工具来获取实时天气信息,或调用计算工具进行复杂的数学运算。
实现方式
工具调用的实现步骤与函数调用类似,但使用的是外部工具而非预定义的函数。以下是具体步骤:
1、定义工具接口:开发者定义一个接口,声明工具的方法:
public interface WeatherTool {String getWeather(String city);
}
2、实现工具接口:编写具体的工具实现类,处理工具的业务逻辑:
@Component
public class WeatherToolImpl implements WeatherTool {@Overridepublic String getWeather(String city) {return "The weather in " + city + " is sunny.";}
}
3、注册工具为Spring Bean:将工具实现类注册为Spring Bean,以便Spring容器管理:
@Configuration
public class ToolConfig {@Beanpublic WeatherTool weatherTool() {return new WeatherToolImpl();}
}
4、配置模型使用工具:在Spring AI配置中,指定模型使用的工具:
@Configuration
public class ChatConfig {@Autowiredprivate ChatClient.Builder chatClientBuilder;@Beanpublic ChatClient chatClient() {return chatClientBuilder.withOptions(ChatOptions.builder().withTool("weatherTool").build()).build();}
}
5、模型调用工具:模型在与用户交互时,通过工具调用获取所需的响应:
@RestController
public class ChatController {@Autowiredprivate ChatClient chatClient;@GetMapping("/chat")public String chat(@RequestParam String input) {String response = chatClient.prompt().user(input).tools("weatherTool").call().content();return response;}
}
智能体(Agent)
功能与作用
智能体(Agent)是能够自主决策并采取行动的软件实体。不同于函数调用和工具调用,智能体不仅能够执行预定义的操作,还能够根据环境变化和任务需求,动态选择并调用适当的工具或服务来完成复杂任务。
实现方式
智能体的实现涉及多个关键组件,主要包括:
模型上下文协议(MCP):用于规范如何向大型语言模型(LLM)提供上下文信息。通过MCP,智能体可以与不同的数据源和工具交互,实现数据获取和处理。
智能体配置与实现:创建智能体并定义其行为。例如,可以创建一个与文件系统交互的智能体。
智能体与模型集成:在Spring AI配置中,定义智能体,并与AI模型进行集成,使其能够在生成响应时调用智能体的功能。
注意:在最新版本的Spring AI中,智能体功能已得到优化,建议开发者参考官方文档,了解最新实现方式和最佳实践。
从函数调用(FunctionCallbac)到工具调用(ToolCallback)
随着Spring AI的持续发展,函数调用(Function Callback)功能已被标记为过时(Deprecated),未来的趋势是采用工具调用(Tool Callback)机制。这种转变旨在提供更高的灵活性和扩展性,使开发者能够更方便地将外部工具和服务集成到AI应用中。
为什么从FunctionCallback迁移到ToolCallback
Spring AI团队决定将函数调用功能从FunctionCallback迁移到ToolCallback,主要基于以下考虑:
- 功能增强:ToolCallback提供了比FunctionCallback更丰富的功能,包括更灵活的工具定义和调用方式,使开发者能够更精确地控制工具的行为。
- 一致性和标准化:采用ToolCallback有助于统一工具调用的接口和规范,提升代码的可读性和维护性,减少因使用过时接口而可能引发的问题。
- 未来兼容性:由于FunctionCallback已被标记为过时,未来版本的Spring AI可能会移除对其的支持。为了确保应用的长期兼容性和稳定性,建议开发者尽早迁移至ToolCallback。
因此,Spring AI强烈建议开发者将现有的FunctionCallback实现迁移到ToolCallback,以充分利用新版本带来的改进和优势,确保应用的可维护性和可扩展性。
Spring AI + DeepSeek-R1 + ES/Milvus + RAG + Tool Calling:智能对话应用开发
环境准备
- 操作系统:Windows 11
- Java 版本:JDK 17+(请注意 Spring Boot 3.4.4 的兼容性),Redis 、Elasticsearch、Milvus向量数据库
- 依赖管理:Maven 3.8.3+
- 阿里云百炼平台: 账号申请 后,可以查看到以下模型的选择 Deepseek接入
Elasticsearch安装
# base 基础组件服务
version: '3.8'
services:elasticsearch1:image: docker.elastic.co/elasticsearch/elasticsearch:8.13.4hostname: "elasticsearch1"environment:- TZ=Asia/Shanghai- "ES_JAVA_OPTS=-Xms84m -Xmx512m"ports:- 9200:9200- 9300:9300volumes:- /Users/lison/work/data/dockerData/elasticsearch1/data:/usr/share/elasticsearch/data - /Users/lison/work/data/dockerData/elasticsearch1/plugins:/usr/share/elasticsearch/plugins - /Users/lison/work/data/dockerData/elasticsearch1/config:/usr/share/elasticsearch/config - /Users/lison/work/data/dockerData/elasticsearch1/logs:/usr/share/elasticsearch/logs privileged: true#restart: alwaysnetworks:nt_dev:ipv4_address: 172.18.0.7vsftpd:image: "fauria/vsftpd"hostname: "rabbitmq1"environment:- FTP_USER=admin - FTP_PASS=123456 - PASV_MIN_PORT=21100- PASV_MAX_PORT=21110 - PASV_ADDRESS=172.18.0.8ports:- 21:21- 21100-21110:21100-21110volumes:- /Users/lison/work/data/dockerData/vsftpd/file:/home/vsftpd- /Users/lison/work/data/dockerData/vsftpd/log/:/var/log/vsftpdprivileged: true#restart: alwaysnetworks:- nt_dev
docker compose up -d
功能介绍
- 集成 Spring AI 提供基础框架支持:Spring AI 作为对话系统开发的基础框架,提供了丰富的工具和库,帮助开发者快速搭建、训练和部署基于 AI 模型的应用程序,为整个项目提供了坚实的技术支撑。
- 集成 Spring AI Alibaba,无缝对接阿里百炼平台:通过 Spring AI Alibaba,我们能够轻松调用阿里百炼平台提供的多种 AI 模型,包括高性能的大模型 DeepSeek-R1 和文本嵌入模型 text-embedding-v2。这些模型为对话系统提供了强大的生成能力和精准的向量表示能力。
- 基于 Redis 实现记忆对话功能,并支持多种持久化方案:项目通过 Redis 实现了对话记忆功能,确保对话的连贯性和上下文一致性。同时,我们还提供了基于 Redis 的持久化方案,开发者可以根据需求灵活改造为 MySQL 持久化,或者采用 Redis + MySQL 的混合持久化方案,满足不同场景的需求。
- 通过向量模型与 ElasticSearch 实现本地知识库功能(RAG 技术):本项目采用阿里百炼平台提供的 text-embedding-v2 向量模型和 ElasticSearch 数据库,实现了本地知识库功能。通过 RAG 技术,系统能够从知识库中检索相关信息并融入对话生成过程。为了验证 RAG 的功能,我们通过工具生成了一份虚拟的医院介绍(“小智口腔医院”),以及 3 个科室介绍(“口腔修复科”、“口腔正畸科”、“儿童口腔科”)和 30 位医生的介绍。
- Tool Calling:通过串联场景实现复杂任务的自动化:项目通过模拟以下三个场景,展示了 Tool Calling 技术如何串联整个技术栈的能力:
- 场景 1:通过 RAG 找到医生后,进行挂号
- 场景 2:挂号当日请假
- 场景 3:挂号当日在医院附近找饭店
通过以上功能,本项目实现了从基础框架支持到复杂任务自动化的完整技术链条,包括 Spring AI + Spring AI Alibaba 调用阿里百炼平台、DeepSeek-R1 大模型、连续对话(Redis 实现)、RAG(向量模型 + ElasticSearch 实现)以及 Tool Calling(串联复杂场景)。这些技术的结合为开发者提供了一个强大的 AI 应用开发范例
代码实现
Maven 核心依赖
<?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.4.3</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.lison.ai</groupId><artifactId>spring_ai_alibaba_demo</artifactId><version>0.0.1-SNAPSHOT</version><name>spring_ai_alibaba_demo</name><description>第一个 Spring AI Alibaba 项目</description><properties><java.version>23</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>3.4.3</spring-boot.version><spring-ai.version>1.0.0-M6</spring-ai.version><alibaba.ai.version>1.0.0-M6.1</alibaba.ai.version><spring.ai.ollama.version>1.0.0-M6</spring.ai.ollama.version></properties><dependencies><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId><version>${alibaba.ai.version}</version></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-elasticsearch-store-spring-boot-starter</artifactId><version>${spring-ai.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>${spring-boot.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><repositories><repository><id>alimaven</id><name>aliyun maven</name><url>https://maven.aliyun.com/repository/public</url></repository><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>false</enabled></releases></repository></repositories><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><annotationProcessorPaths><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.36</version></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>
上述配置文件中,我们引入了Spring Boot、Spring AI、Alibaba Cloud AI等核心依赖,确保项目能够顺利构建和运行。同时,通过配置Maven插件和仓库,优化项目的构建流程和依赖管理。
核心配置 (application.yml
)
这里一定要注意的是文本推理模型 model: deepseek-r1
, 嵌入模型model: text-embedding-v2
用来生成文本的向量数据
server:port: 8080
spring:application:name: AI Demodata:redis:host: 127.0.0.1port: 6379database: 0ai:dashscope:api-key: sk-xxxxxmodel: deepseek-r1embedding:options:model: text-embedding-v2vectorstore:elasticsearch:initialize-schema: trueindex-name: spring-ai-tag# 这里一定要注意,维度是默认1536,需要和向量模型的维度一致dimensions: 1536similarity: cosinebatching-strategy: TOKEN_COUNT
在配置文件中,我们设置了服务器端口、Redis连接信息、Elasticsearch地址以及AI模型的相关参数。通过这些配置,项目能够正确连接到各个服务,并使用指定的模型进行对话处理和数据存储。 后面可以使用向量数据库进行存储
Redis配置类(RedisConfig.java)
package com.lison.ai.spring_ai_alibaba_demo.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(factory);redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));redisTemplate.afterPropertiesSet();return redisTemplate;}
}
配置了 RedisTemplate,使用 JSON 序列化器将对象存储为 JSON 格式,方便后续的存储和读取。
创建消息实体(ChatEntity.java)
package com.lison.ai.spring_ai_alibaba_demo.config;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@NoArgsConstructor
@AllArgsConstructor
@Data
public class ChatEntity implements Serializable {String chatId;String type;String text;
}
定义了消息实体类,用于存储对话的 ID、类型和内容,实现了序列化接口以便在 Redis 中存储。
实现 Redis 聊天记忆模型(ChatStorageMemory.java)
package com.lison.ai.spring_ai_alibaba_demo.config.chat;import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.*;
import org.springframework.stereotype.Component;
import org.springframework.data.redis.core.RedisTemplate;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;@Slf4j
@Component
public class ChatStorageMemory implements ChatMemory {private static final String KEY_PREFIX = "chat:history:";private final RedisTemplate<String, Object> redisTemplate;public ChatStorageMemory(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}@Overridepublic void add(String conversationId, List<Message> messages) {String key = KEY_PREFIX + conversationId;List<ChatEntity> listIn = new ArrayList<>();for (Message msg : messages) {String[] strs = msg.getText().split("</think>");String text = strs.length == 2 ? strs[1] : strs[0];ChatEntity ent = new ChatEntity();ent.setChatId(conversationId);ent.setType(msg.getMessageType().getValue());ent.setText(text);listIn.add(ent);}redisTemplate.opsForList().rightPushAll(key, listIn.toArray());redisTemplate.expire(key, 30, TimeUnit.MINUTES);}@Overridepublic List<Message> get(String conversationId, int lastN) {String key = KEY_PREFIX + conversationId;Long size = redisTemplate.opsForList().size(key);if (size == null || size == 0) {return Collections.emptyList();}int start = Math.max(0, (int) (size - lastN));List<Object> listTmp = redisTemplate.opsForList().range(key, start, -1);List<Message> listOut = new ArrayList<>();ObjectMapper objectMapper = new ObjectMapper();for (Object obj : listTmp) {ChatEntity chat = objectMapper.convertValue(obj, ChatEntity.class);if (MessageType.USER.getValue().equals(chat.getType())) {listOut.add(new UserMessage(chat.getText()));} else if (MessageType.ASSISTANT.getValue().equals(chat.getType())) {listOut.add(new AssistantMessage(chat.getText()));} else if (MessageType.SYSTEM.getValue().equals(chat.getType())) {listOut.add(new SystemMessage(chat.getText()));}}return listOut;}@Overridepublic void clear(String conversationId) {redisTemplate.delete(KEY_PREFIX + conversationId);}
}
实现了 Redis 中的对话记忆功能,包括添加对话、获取对话历史和清除对话记录。
SpringAiChatConfig 注入类
package com.lison.ai.spring_ai_alibaba_demo.config.chat;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;@Configuration
public class SpringAiChatConfig {@Autowiredprivate ChatModel chatModel;@Beanpublic ChatClient chatClient(ChatMemory chatMemory) {return ChatClient.builder(chatModel)// 设置系统的默认行为和风格.defaultSystem("你是一个专业的助手,遇到不确定或不明确的信息时,会主动询问用户以获取更多信息。在回答问题时,你倾向于使用条理清晰的格式,例如分点列举的方式,以便用户更容易理解和参考。")// 注册向量检索顾问,用于从向量存储中检索相关信息.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore,SearchRequest.builder().similarityThreshold(0.1) // 设置相似度阈值.topK(10) // 设置返回最多 10 条相关结果.build())).build();}@Beanpublic ChatMemory chatMemory(RedisTemplate<String, Object> redisTemplate) {return new ChatStorageMemory(redisTemplate);}
}
编写核心控制器(DeepseekChatController.java)
package com.lison.ai.spring_ai_alibaba_demo.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentReader;
import org.springframework.ai.rag.Query;
import org.springframework.ai.rag.preretrieval.query.transformation.CompressionQueryTransformer;
import org.springframework.ai.rag.preretrieval.query.transformation.QueryTransformer;
import org.springframework.ai.rag.preretrieval.query.transformation.RewriteQueryTransformer;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** @ClassName DeepseekChatController //类名称* @Description:* @Author: 200900681 李村* @CreateDate: 2025/4/25* @Version: 1.0 //版本号*/
@Slf4j
@RestController
@RequestMapping("/ai/v1")
public class DeepseekChatController {@Autowiredprivate ChatClient chatClient;@Autowiredprivate VectorStore vectorStore;@Autowiredprivate ChatMemory chatMemory;// 对话记忆长度private final Integer CHAT_HISTORY_SIZE = 10;@GetMapping("/data/load")public String loadData() throws IOException {// 1. 读取文件DocumentReader reader = new TextReader("static/hospitalInfo.txt");List<Document> documents = reader.get();// 2.切分文件:根据空白行进行分割List<Document> splitDocuments = new ArrayList<>();for (Document document : documents) {// 获取文档内容String content = document.getText();// 使用正则表达式按空白行分割String[] parts = content.split("\\n\\s*\\n");// 将分割后的部分封装为新的 Document 对象for (String part : parts) {splitDocuments.add(new Document(part.trim()));}}log.info("文件切分为 [{}]", splitDocuments.size());// 3.写入数据vectorStore.add(splitDocuments);return "success";}@GetMapping("/select")public String search(@RequestParam("query") String query) {log.info("query is [{}]", query);List<Document> results = vectorStore.similaritySearch(
// SearchRequest.builder().query(query).similarityThreshold(0.1).topK(10).build()SearchRequest.builder().query(query).similarityThreshold(0.1).topK(10).build());log.info("results is [{}]", results);return results.toString();}/*** 查询重写* @param userId* @param inputMsg* @return*/@GetMapping(value = "/rag/rewrite/chat", produces = "text/plain; charset=UTF-8")public String ragChatByRewrite(@RequestParam String userId, @RequestParam String inputMsg) {// 1、构建查询重写转换器QueryTransformer queryTransformer = RewriteQueryTransformer.builder().chatClientBuilder(chatClient.mutate()).build();// 2、执行查询重写Query queryByTransform = queryTransformer.transform(new Query(inputMsg));// 3、输出重写后的查询文案return queryByTransform.text();}/*** 上下文感知查询* @param userId* @param inputMsg* @return*/@GetMapping(value = "/rag/history/chat", produces = "text/plain; charset=UTF-8")public String ragChatByHistory(@RequestParam String userId, @RequestParam String inputMsg) {// 1. 获取历史消息List<Message> history = chatMemory.get(userId, CHAT_HISTORY_SIZE);// 2. 构建查询对象Query query = Query.builder().text(inputMsg).history(history).build();// 3. 创建 CompressionQueryTransformerCompressionQueryTransformer queryTransformerByHis = CompressionQueryTransformer.builder().chatClientBuilder(chatClient.mutate()).build();// 4、执行历史信息后查询Query queryByHistory = queryTransformerByHis.transform(query);// 5、输出重写后的查询文案return queryByHistory.text();}}
数据准备
我让kimi帮我生成了一个医院的介绍,包含
- 医院
小智口腔医院
的介绍, - 3个科室的介绍,分别是
口腔修复科
,口腔正畸科
,儿童口腔科
- 每个科室也有对应的20位医生信息
1. 医院介绍
小智口腔医院坐落在风景如画的苍云市,一所集临床、科研、教学、预防为一体的三级医院。医院是苍云市慈善医院、苍云市省老年友善医院、南阳市光荣院、南阳市牙病防治所所在地、南阳市中小学口腔保健教育基地。目前,医院已发展为“一院两区多门诊”,即两相路院区、光武路院区、新华路门诊、卧龙路门诊、关帝庙路门诊、工业路门诊等同步发展、同步运营、同质化服务的发展格局。两相路院区于2023年1月投入使用,建筑面积12.2万平米,规划床位1000张,口腔综合治疗台200台,耳鼻喉多功能治疗台20台,是以口腔、眼、耳鼻喉、整形美容为特色,内、外、妇、儿、中医、康复、肿瘤、放疗、体检等功能齐全的“大专科 小综合”三级医院。医院下设60个临床、医技科室,其中,口腔专业是苍云市省临床重点培育专科,耳鼻咽喉头颈外科是南阳市级临床重点专科。医院拥有计算机体层摄像系统、磁共振成像系统、彩色多普勒超声诊断仪、日本奥林巴斯NBI电子鼻咽喉镜、高清内窥镜摄像系统、全自动生化免疫流水线、口腔CBCT、多导睡眠诊断分析系统、日本奥林巴斯CV-290电子胃肠镜、眼科光学相干断层扫描仪OCT、蔡司700眼科手术显微镜、蔡司VisuMax SMILE 3.0微创全飞秒手术系统、CAD/CAM牙科扫描仪等先进设备。目前医院已开设内科、外科、妇产科、儿科、中医科、康复医学科、眼科、皮肤科、体检科、肿瘤放疗科、医养结合(无陪护康养)等综合医疗服务,先进的设备,优美的环境,精湛的技术,可满足患者优质医疗服务的需求。医院全体干部职工以“争创三级甲等口腔医院和省级区域性口腔医疗中心”为愿景,不断提高医疗技术和服务水平,更好地为人民群众提供更加安全、高效、高品质的口腔医疗保健服务和综合医疗服务。
2. 科室介绍
(1)口腔修复科:
口腔修复科是医院的核心科室之一,口腔修复科是专注于修复牙齿缺损、缺失,以及改善牙齿外观和功能的科室。主要通过人工修复体(如假牙、贴面、嵌体等)恢复牙齿的形态、咬合和美观,解决因龋齿、外伤、磨损或先天因素导致的牙齿问题,帮助患者重获正常的咀嚼、发音和笑容自信
(2)口腔正畸科:
是口腔科中专门负责治疗错𬌗畸形的部门。该科室主要收治因牙齿排列不齐、牙齿扭转错位、反𬌗、上颌前突、下颌后缩等牙颌面畸形问题前来就诊的患者。在专业范围上,科室通过运用矫治技术和设备,如固定矫治器、功能矫治器、隐形矫治器等,帮助患者恢复正常的牙齿排列和咬合关系,同时改善面部美观和口腔功能。在具体的科室设置上,口腔正畸科通常配备有专业的医护人员和先进的医疗设备,正畸专业医生多为本科毕业后教育,一般在本科阶段不涉及临床实习,都是在毕业后,通过研究生、进修或上级医师专业指导等方式获得正畸临床实践经验,确保患者能够得到全面、专业的治疗服务。
(3)儿童口腔科:
儿童口腔科是医院的新兴科室,提供儿童口腔疾病的预防与治疗,如龋齿、牙髓炎、牙周病等。我们还为儿童提供专门的牙科治疗设备和技术,以减轻他们的紧张情绪和疼痛感。3. 医生介绍
(1) 李明轩
性别 :男
出生年月 :1985年3月
医生职称 :副主任医师
所在科室 :口腔修复科
毕业院校 :本科毕业于“华西医科大学”,硕士毕业于“北京大学口腔医学院”,博士毕业于“上海交通大学医学院”。
介绍 :李明轩医生是口腔修复科的骨干力量,临床经验丰富。他专注于复杂牙体修复和全瓷冠桥技术,熟练掌握数字化修复设备的应用。李医生多次参与国际学术交流,发表多篇高水平论文。他还积极参与社区义诊活动,普及口腔健康知识,为提高公众口腔保健意识做出了积极贡献。(2) 王晓彤
性别 :女
出生年月 :1990年7月
医生职称 :主治医师
所在科室 :口腔正畸科
毕业院校 :本科毕业于“中山大学光华口腔医学院”,硕士毕业于“复旦大学附属口腔医院”。
介绍 :王晓彤医生是口腔正畸领域的新生力量,擅长儿童及成人错颌畸形的矫正治疗。她熟练掌握传统固定矫治器、隐形矫治器及功能性矫治器的应用,并注重个性化治疗方案的设计。王医生积极参与科研项目,发表了多篇关于正畸治疗效果的研究论文。她还多次参与社区公益活动,为青少年普及口腔健康知识。(3) 张伟杰
性别 :男
出生年月 :1982年11月
医生职称 :主任医师
所在科室 :口腔修复科
毕业院校 :本科毕业于“四川大学华西口腔医学院”,硕士毕业于“德国法兰克福大学”,博士毕业于“美国哈佛大学牙学院”。
介绍 :张伟杰医生是口腔修复领域的权威专家,拥有丰富的临床经验。他专注于美学修复及咬合重建技术,尤其擅长复杂病例的综合设计与实施。张医生在国际学术界享有盛誉,曾多次受邀参加国际修复大会并作专题报告。他还致力于培养年轻医生,推动国内修复技术的发展。(4) 陈静怡
性别 :女
出生年月 :1988年4月
医生职称 :住院医师
所在科室 :儿童口腔科
毕业院校 :本科毕业于“武汉大学口腔医学院”,硕士毕业于“浙江大学医学院”。
介绍 :陈静怡医生是儿童口腔科的新锐力量,专注于儿童龋病预防及早期干预。她熟练掌握乳牙及恒牙的治疗技术,并善于与孩子沟通,缓解他们的紧张情绪。陈医生积极参与科研工作,研究方向包括儿童口腔健康管理及行为引导策略。她还经常参与幼儿园及学校的口腔健康讲座,为家长和孩子普及护牙知识。(5) 刘志远
性别 :男
出生年月 :1980年9月
医生职称 :副主任医师
所在科室 :口腔正畸科
毕业院校 :本科毕业于“同济医科大学”,硕士毕业于“北京协和医学院”,博士毕业于“日本东京医科齿科大学”。
介绍 :刘志远医生是口腔正畸领域的资深专家,擅长复杂错颌畸形的矫正治疗。他熟练掌握隐形矫治技术,并注重患者的舒适体验及术后效果。刘医生在国内外发表了多篇学术论文,研究成果受到同行高度认可。他还积极参与公益事业,为偏远地区患者提供免费咨询服务。(6) 孙悦宁
性别 :女
出生年月 :1993年1月
医生职称 :住院医师
所在科室 :儿童口腔科
毕业院校 :本科毕业于“武当医学学院”,硕士毕业于“青城医学研究所”,博士毕业于“昆仑医学大学”。
介绍 :孙悦宁医生是儿童口腔科的新生力量,专业基础扎实。她专注于帮助儿童克服牙齿发育障碍,熟练掌握多种牙科评估工具和治疗方法。孙医生积极参与科室的科研工作,发表多篇学术论文。她多次参与社区义诊活动,普及儿童口腔健康知识,为提高公众健康意识做出了积极贡献。(7) 赵天宇
性别 :男
出生年月 :1987年5月
医生职称 :主治医师
所在科室 :口腔修复科
毕业院校 :本科毕业于“南京医科大学”,硕士毕业于“中国医科大学”。
介绍 :赵天宇医生是口腔修复科的中坚力量,擅长全口义齿修复及种植修复技术。他注重美学设计,能够根据患者需求制定个性化的修复方案。赵医生积极参与学术交流,不断提升自身技术水平。他还通过线上科普视频,向患者普及修复相关知识。(8) 林晓慧
性别 :女
出生年月 :1991年8月
医生职称 :住院医师
所在科室 :儿童口腔科
毕业院校 :本科毕业于“山东大学口腔医学院”,硕士毕业于“西安交通大学医学院”。
介绍 :林晓慧医生是儿童口腔科的新锐医生,专注于儿童龋病的早期诊断与治疗。她熟练掌握微创治疗技术,并善于用生动的方式与孩子互动。林医生积极参与科研项目,研究方向包括低龄儿童龋病的预防策略。她还通过社区活动,向家长普及儿童口腔护理知识。(9) 黄志强
性别 :男
出生年月 :1979年12月
医生职称 :主任医师
所在科室 :口腔正畸科
毕业院校 :本科毕业于“复旦大学医学院”,硕士毕业于“美国密歇根大学牙学院”。
介绍 :黄志强医生是口腔正畸领域的权威专家,擅长复杂病例的矫正治疗。他熟练掌握隐形矫治技术,并注重患者的舒适体验及术后效果。黄医生在国内外发表了多篇学术论文,研究成果受到同行高度认可。他还积极参与公益事业,为偏远地区患者提供免费咨询服务(10) 高雅婷
性别 :女
出生年月 :1986年2月
医生职称 :主治医师
所在科室 :口腔修复科
毕业院校 :本科毕业于“首都医科大学”,硕士毕业于“北京大学口腔医学院”。
介绍 :高雅婷医生是口腔修复科的中坚力量,擅长美学修复及咬合重建技术。她熟练掌握数字化修复设备的应用,并注重患者的个性化需求。高医生积极参与学术交流,不断提升自身技术水平。她还通过线上科普视频,向患者普及修复相关知识,帮助更多人了解牙齿健康的重要性。(11) 王浩然
性别 :男
出生年月 :1983年7月
医生职称 :副主任医师
所在科室 :口腔正畸科
毕业院校 :本科毕业于“浙江大学医学院”,硕士毕业于“英国伦敦大学学院牙学院”。
介绍 :王浩然医生是口腔正畸领域的资深专家,擅长成人复杂错颌畸形的矫正治疗。他熟练掌握隐形矫治技术,并注重患者的舒适体验及术后效果。王医生在国内外发表了多篇学术论文,研究成果受到同行高度认可。他还积极参与公益事业,为偏远地区患者提供免费咨询服务。(12) 李佳琪
性别 :女
出生年月 :1992年4月
医生职称 :住院医师
所在科室 :儿童口腔科
毕业院校 :本科毕业于“天津医科大学”,硕士毕业于“南京大学医学院”。
介绍 :李佳琪医生是儿童口腔科的新锐力量,专注于儿童龋病预防及早期干预。她熟练掌握乳牙及恒牙的治疗技术,并善于与孩子沟通,缓解他们的紧张情绪。李医生积极参与科研工作,研究方向包括儿童口腔健康管理及行为引导策略。她还经常参与幼儿园及学校的口腔健康讲座,为家长和孩子普及护牙知识。(13) 周文博
性别 :男
出生年月 :1981年10月
医生职称 :主任医师
所在科室 :口腔修复科
毕业院校 :本科毕业于“上海交通大学医学院”,硕士毕业于“德国慕尼黑大学牙学院”。
介绍 :周文博医生是口腔修复领域的权威专家,拥有丰富的临床经验。他专注于复杂牙体修复及全口义齿修复技术,尤其擅长美学修复设计。周医生在国际学术界享有盛誉,曾多次受邀参加国际修复大会并作专题报告。他还致力于培养年轻医生,推动国内修复技术的发展。(14) 张馨月
性别 :女
出生年月 :1989年6月
医生职称 :主治医师
所在科室 :口腔正畸科
毕业院校 :本科毕业于“中山大学光华口腔医学院”,硕士毕业于“复旦大学附属口腔医院”。
介绍 :张馨月医生是口腔正畸领域的新生力量,擅长儿童及成人错颌畸形的矫正治疗。她熟练掌握传统固定矫治器、隐形矫治器及功能性矫治器的应用,并注重个性化治疗方案的设计。张医生积极参与科研项目,发表了多篇关于正畸治疗效果的研究论文。她还多次参与社区公益活动,为青少年普及口腔健康知识。(15) 王志鹏
性别 :男
出生年月 :1984年11月
医生职称 :副主任医师
所在科室 :口腔修复科
毕业院校 :本科毕业于“华西医科大学”,硕士毕业于“北京大学口腔医学院”。
介绍 :王志鹏医生是口腔修复科的骨干力量,擅长复杂牙体修复及种植修复技术。他熟练掌握显微修复技术,并注重患者的个性化需求。王医生多次参与国际学术交流,发表多篇高水平论文。他还积极参与社区义诊活动,普及口腔健康知识,为提高公众口腔保健意识做出了积极贡献。(16) 赵雪梅
性别 :女
出生年月 :1990年3月
医生职称 :住院医师
所在科室 :儿童口腔科
毕业院校 :本科毕业于“武汉大学口腔医学院”,硕士毕业于“浙江大学医学院”。
介绍 :赵雪梅医生是儿童口腔科的新锐医生,专注于儿童龋病的早期诊断与治疗。她熟练掌握微创治疗技术,并善于用生动的方式与孩子互动。赵医生积极参与科研项目,研究方向包括低龄儿童龋病的预防策略。她还通过社区活动,向家长普及儿童口腔护理知识。(17) 陈子豪
性别 :男
出生年月 :1988年9月
医生职称 :主治医师
所在科室 :口腔正畸科
毕业院校 :本科毕业于“同济医科大学”,硕士毕业于“北京协和医学院”。
介绍 :陈子豪医生是口腔正畸领域的资深专家,擅长复杂错颌畸形的矫正治疗。他熟练掌握隐形矫治技术,并注重患者的舒适体验及术后效果。陈医生在国内外发表了多篇学术论文,研究成果受到同行高度认可。他还积极参与公益事业,为偏远地区患者提供免费咨询服务。(18) 李梦瑶
性别 :女
出生年月 :1994年1月
医生职称 :住院医师
所在科室 :儿童口腔科
毕业院校 :本科毕业于“山东大学口腔医学院”,硕士毕业于“西安交通大学医学院”。
介绍 :李梦瑶医生是儿童口腔科的新生力量,专业基础扎实。她专注于帮助儿童克服牙齿发育障碍,熟练掌握多种牙科评估工具和治疗方法。李医生积极参与科室的科研工作,发表多篇学术论文。她多次参与社区义诊活动,普及儿童口腔健康知识,为提高公众健康意识做出了积极贡献。(19) 黄晓峰
性别 :男
出生年月 :1985年8月
医生职称 :副主任医师
所在科室 :口腔修复科
毕业院校 :本科毕业于“南京医科大学”,硕士毕业于“中国医科大学”。
介绍 :黄晓峰医生是口腔修复科的中坚力量,擅长全口义齿修复及种植修复技术。他注重美学设计,能够根据患者需求制定个性化的修复方案。黄医生积极参与学术交流,不断提升自身技术水平。他还通过线上科普视频,向患者普及修复相关知识。(20) 刘欣怡
性别 :女
出生年月 :1991年5月
医生职称 :主治医师
所在科室 :口腔正畸科
毕业院校 :本科毕业于“中山大学光华口腔医学院”,硕士毕业于“复旦大学附属口腔医院”。
介绍 :刘欣怡医生是口腔正畸领域的新生力量,擅长儿童及成人错颌畸形的矫正治疗。她熟练掌握传统固定矫治器、隐形矫治器及功能性矫治器的应用,并注重个性化治疗方案的设计。刘医生积极参与科研项目,发表了多篇关于正畸治疗效果的研究论文。她还多次参与社区公益活动,为青少年普及口腔健康知识。
测试验证
数据写入的接口
http://127.0.0.1:8080/ai/v1/data/load
获取数据向量的接口
http://127.0.0.1:8080/ai/v1/select
连读对话的接口
http://127.0.0.1:8080/ai/v1/rag/chat
数据写入
数据写入的接口
http://127.0.0.1:8080/ai/v1/data/load
获取数据向量的接口
http://127.0.0.1:8080/ai/v1/select?query=医院介绍
可以通过elasticsearch 搜索到医院信息,也就是这些信息会通过聊天接口上传给deepseek
接下来我们就准备验证RAG的能力,也就是检索+连续对话的能力了,我们设计了几个问题
- 请介绍一下医院
- 请介绍一下医院都有哪些科室
- 医院有哪些主任医师
- 张馨月医生怎么样
- 我刚才都问了哪些问题
附:向量数据库milvus 存储数据
Milvus安装
Docker-compose 配置
# 配置 etcd 服务etcd:container_name: milvus-etcd # 容器名称image: quay.io/coreos/etcd:v3.5.16 # 使用的镜像environment:- ETCD_AUTO_COMPACTION_MODE=revision # 自动压缩模式- ETCD_AUTO_COMPACTION_RETENTION=1000 # 保留的修订版本数- ETCD_QUOTA_BACKEND_BYTES=4294967296 # 后端存储限制- ETCD_SNAPSHOT_COUNT=50000 # 快照计数volumes:- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd # 数据卷挂载command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls=http://0.0.0.0:2379 --data-dir /etcd # 启动命令healthcheck:test: ["CMD", "etcdctl", "endpoint", "health"] # 健康检查命令interval: 30s # 检查间隔timeout: 20s # 超时时间retries: 3 # 重试次数# 配置 MinIO 服务minio:container_name: milvus-minio # 容器名称image: minio/minio:RELEASE.2023-03-20T20-16-18Z # 使用的镜像environment:MINIO_ACCESS_KEY: minioadmin # MinIO 访问密钥MINIO_SECRET_KEY: minioadmin # MinIO 密钥ports:- "9001:9001" # 控制台端口映射- "9000:9000" # 服务端口映射volumes:- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data # 数据卷挂载command: minio server /minio_data --console-address ":9001" # 启动命令healthcheck:test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] # 健康检查命令interval: 30s # 检查间隔timeout: 20s # 超时时间retries: 3 # 重试次数networks:nt_dev:ipv4_address: 172.18.0.8 # 配置 Milvus 服务standalone:container_name: milvus-standalone # 容器名称image: milvusdb/milvus:v2.5.4 # 使用的镜像command: ["milvus", "run", "standalone"] # 启动命令security_opt:- seccomp:unconfined # 安全选项environment:ETCD_ENDPOINTS: etcd:2379 # etcd 服务地址MINIO_ADDRESS: minio:9000 # MinIO 服务地址volumes:- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus # 数据卷挂载healthcheck:test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"] # 健康检查命令interval: 30s # 检查间隔start_period: 90s # 启动等待时间timeout: 20s # 超时时间retries: 3 # 重试次数ports:- "19530:19530" # 服务端口映射- "9091:9091" # 管理端口映射depends_on:- "etcd" # 依赖 etcd 服务- "minio" # 依赖 MinIO 服务# 配置 Milvus Dashboard 服务dashboard:image: zilliz/attu:v2.5 # 使用的镜像container_name: milvus-dashboard # 容器名称environment:MILVUS_URL: http://standalone:19530 # Milvus 服务地址ports:- "8000:3000" # 端口映射depends_on:- standalone # 依赖 Milvus 服务- etcd # 依赖 etcd 服务- minio # 依赖 MinIO 服务# 定义网络配置
networks:nt_dev:external: true
docker compose up -d
查看客户端, 输入地址 http://localhost:8000/
进入如下界面:此时什么都不要填写,直接点【连接】按钮
进入系统后
创建用户:用户名:``milvus, 密码:
milvus-4321(这里要和我们spring工程里的配置一致),角色我们选
admin`,
修改依赖:
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-milvus-store-spring-boot-starter</artifactId></dependency>
修改配置
server:port: 8080
spring:application:name: AI Demodata:redis:host: 127.0.0.1port: 6379database: 0elasticsearch:uris: http://127.0.0.1:9200username:password:ai:dashscope:api-key: sk-xxxxmodel: deepseek-r1embedding:options:model: text-embedding-v2vectorstore:milvus:client:host: "localhost"port: 19530username: "milvus"password: "milvus-4321"databaseName: "default"collectionName: "vector_store"embeddingDimension: 1536indexType: IVF_FLATmetricType: COSINEinitialize-schema: true
在配置文件中,我们设置了服务器端口、Redis连接信息、Milvus地址以及AI模型的相关参数。通过这些配置,项目能够正确连接到各个服务,并使用指定的模型进行对话处理和数据存储。
使用加载接口