This commit is contained in:
liukai
2025-09-03 18:02:40 +08:00
parent a02be6d1ea
commit cd3cfe4e64
7 changed files with 219 additions and 65 deletions

14
pom.xml
View File

@@ -34,7 +34,11 @@
<groupId>org.springframework.ai</groupId> <groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId> <artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency> </dependency>
<!-- 阿里百炼大模型自动配置-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId> <artifactId>spring-boot-starter-data-redis</artifactId>
@@ -89,6 +93,14 @@
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<!-- 阿里百炼版本管理-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>1.0.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@@ -1,5 +1,6 @@
package com.ai.app.config; package com.ai.app.config;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
@@ -54,9 +55,9 @@ public class AIConfig {
} }
/** /**
* 配置AI对话客户端 * dashScopeChatClient对话客户端
* *
* @param deepSeekChatModel deepseek大模型 * @param dashScopeChatModel 千问max大模型
* @param simpleLoggerAdvisor 日志Advisor * @param simpleLoggerAdvisor 日志Advisor
* @param messageChatMemoryAdvisor 会话记忆Advisor * @param messageChatMemoryAdvisor 会话记忆Advisor
* @return: org.springframework.ai.chat.client.ChatClient * @return: org.springframework.ai.chat.client.ChatClient
@@ -64,9 +65,33 @@ public class AIConfig {
* @date: 2025/8/29 9:06 * @date: 2025/8/29 9:06
*/ */
@Bean @Bean
public ChatClient chatClient(DeepSeekChatModel deepSeekChatModel, public ChatClient dashScopeChatClient(DashScopeChatModel dashScopeChatModel,
SimpleLoggerAdvisor simpleLoggerAdvisor, SimpleLoggerAdvisor simpleLoggerAdvisor,
MessageChatMemoryAdvisor messageChatMemoryAdvisor) { MessageChatMemoryAdvisor messageChatMemoryAdvisor) {
return ChatClient
//模型
.builder(dashScopeChatModel)
//系统角色
.defaultSystem("你是一个小团团")
//环绕增强
.defaultAdvisors(simpleLoggerAdvisor, messageChatMemoryAdvisor)
.build();
}
/**
* deepseek 对话客户端
*
* @param deepSeekChatModel deepseek大模型
* @param simpleLoggerAdvisor 日志增强
* @param messageChatMemoryAdvisor 会话记忆增强
* @return: org.springframework.ai.chat.client.ChatClient
* @author kai.liu
* @date: 2025/9/3 16:45
*/
@Bean
public ChatClient deepSeekChatClient(DeepSeekChatModel deepSeekChatModel,
SimpleLoggerAdvisor simpleLoggerAdvisor,
MessageChatMemoryAdvisor messageChatMemoryAdvisor) {
return ChatClient return ChatClient
//模型 //模型
.builder(deepSeekChatModel) .builder(deepSeekChatModel)

View File

@@ -1,55 +0,0 @@
package com.ai.app.controller;
import com.ai.app.dto.MessageDTO;
import com.ai.app.service.AIChatMessageService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.messages.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.stream.Collectors;
/**
* @describe AI对话控制器
* @Author kai.liu
* @Date 2025/8/28 10:06
*/
@RestController
@RequestMapping("/ai")
public class ChatController {
@Autowired
private ChatClient chatClient;
@Autowired
private InMemoryChatMemoryRepository inMemoryChatMemoryRepository;
@RequestMapping(value = "/chat", produces = "text/stream;charset=UTF-8")
public Flux<String> chat(String prompt, String chatId) {
return chatClient
.prompt()
.user(prompt)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
.stream().content();
}
@RequestMapping("/history/chat")
public List<String> history() {
return inMemoryChatMemoryRepository.findConversationIds();
}
@RequestMapping("/history/chat/{chatId}")
public List<MessageDTO> history(@PathVariable("chatId") String chatId) {
List<Message> messages = inMemoryChatMemoryRepository.findByConversationId(chatId);
return messages.stream().map(e -> {
return new MessageDTO(e.getMessageType().getValue(), e.getText());
}).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,85 @@
package com.ai.app.controller;
import com.ai.app.dto.MessageDTO;
import com.ai.app.service.ChatHistoryService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.messages.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.stream.Collectors;
/**
* @describe AI对话控制器
* @Author kai.liu
* @Date 2025/8/28 10:06
*/
@RestController
@RequestMapping("/dashscope")
public class DashScopeController {
@Autowired
@Qualifier("dashScopeChatClient")
private ChatClient dashScopeChatClient;
@Autowired
private ChatHistoryService chatHistoryService;
@Autowired
private InMemoryChatMemoryRepository inMemoryChatMemoryRepository;
/**
* 对话聊天
*
* @param prompt 用户提示词
* @param chatId 对话ID
* @return: reactor.core.publisher.Flux<java.lang.String>
* @author kai.liu
* @date: 2025/9/3 17:30
*/
@RequestMapping(value = "/chat", produces = "text/stream;charset=UTF-8")
public Flux<String> chat(String prompt, String chatId) {
return dashScopeChatClient
.prompt()
.user(prompt)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
.stream().content();
}
/**
* 会话ID列表
*
* @param type 类型
* @return: java.util.List<java.lang.String>
* @author kai.liu
* @date: 2025/9/3 17:30
*/
@GetMapping("/getHistoryChatIdList/{type}")
public List<String> getHistoryChatIdList(@PathVariable("type") String type) {
return chatHistoryService.getHistoryChatIds(type);
}
/**
* 会话历史
*
* @param chatId 会话ID
* @return: java.util.List<com.ai.app.dto.MessageDTO>
* @author kai.liu
* @date: 2025/9/3 17:31
*/
@RequestMapping("/getChatHistory/{type}/{chatId}")
public List<MessageDTO> getChatHistory(@PathVariable("type") String type, @PathVariable("chatId") String chatId) {
chatHistoryService.saveHistoryChatId(type, chatId);
List<Message> messages = inMemoryChatMemoryRepository.findByConversationId(chatId);
return messages.stream().map(e -> new MessageDTO(e.getMessageType().getValue(), e.getText())).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,15 @@
package com.ai.app.service;
import java.util.List;
/**
* @describe 对话历史接口定义
* @Author kai.liu
* @Date 2025/9/3 17:05
*/
public interface ChatHistoryService {
void saveHistoryChatId(String type, String chatId);
List<String> getHistoryChatIds(String type);
}

View File

@@ -0,0 +1,67 @@
package com.ai.app.service.impl;
import com.ai.app.service.ChatHistoryService;
import com.ai.app.service.RedisTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @describe
* @Author kai.liu
* @Date 2025/9/3 17:05
*/
@Slf4j
@Service
public class ChatHistoryServiceImpl implements ChatHistoryService {
@Autowired
private RedisTemplateService redisTemplateService;
public static final String CHAT_TYPE_KEY = "ai:history:type:";
/**
* 保存会话ID
*
* @param type 类型
* @param chatId 会话ID
* @return: void
* @author kai.liu
* @date: 2025/9/3 17:14
*/
@Override
public void saveHistoryChatId(String type, String chatId) {
String key = CHAT_TYPE_KEY + type;
List<String> chatIds;
if (redisTemplateService.hasKey(key)) {
chatIds = (List<String>) redisTemplateService.get(key);
} else {
chatIds = new ArrayList<>();
}
if (chatIds.contains(chatId)) {
return;
}
chatIds.add(chatId);
redisTemplateService.set(key, chatIds);
}
/**
* 获取历史会话id列表
*
* @param type 类型
* @return: java.util.List<java.lang.String>
* @author kai.liu
* @date: 2025/9/3 17:08
*/
@Override
public List<String> getHistoryChatIds(String type) {
String key = CHAT_TYPE_KEY + type;
if (redisTemplateService.hasKey(key)) {
return (List<String>) redisTemplateService.get(key);
}
return List.of();
}
}

View File

@@ -1,7 +1,7 @@
server: server:
port: 8080 port: 8080
# servlet: servlet:
# context-path: /ai context-path: /ai
tomcat: tomcat:
# 等待队列长度 (所有线程忙碌时,排队等待的连接数, 默认: 100) # 等待队列长度 (所有线程忙碌时,排队等待的连接数, 默认: 100)
accept-count: 1000 accept-count: 1000
@@ -40,10 +40,15 @@ spring:
max-request-size: 100MB max-request-size: 100MB
ai: ai:
deepseek: deepseek:
api-key: sk-f970111b34c84a8c812919fb4fad8b65 api-key: ${DEEPSEEK_API_KEY}
chat: chat:
options: options:
model: deepseek-reasoner model: deepseek-r1
dashscope:
api-key: ${DASHSCOPE_API_KEY}
chat:
options:
model: qwen-max
data: data:
redis: redis:
host: 180.76.119.46 host: 180.76.119.46