Compare commits

...

6 Commits

Author SHA1 Message Date
Ttt
5267d29d97 feat:智能体创建 2026-03-17 22:49:00 +08:00
Ttt
24d01bc3e1 feat:智能体创建 2026-03-13 23:40:31 +08:00
Ttt
5dbd0b3a36 feat:智能体创建 2026-03-13 23:38:25 +08:00
Ttt
35dca0a435 feat:隔离用户空间 2026-03-13 23:19:15 +08:00
Ttt
fa93d4ffe0 feat:数据库持久化 2026-03-13 22:56:19 +08:00
Ttt
d00e3ce11e feat:带记忆体的简单聊天AI智能体 2026-03-13 21:25:23 +08:00
27 changed files with 480 additions and 74 deletions

14
pom.xml
View File

@@ -29,7 +29,7 @@
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<guava.version>16.0.1</guava.version> <guava.version>16.0.1</guava.version>
<mybatis-spring-boot.version>2.3.1</mybatis-spring-boot.version> <mybatis-spring-boot.version>2.3.1</mybatis-spring-boot.version>
<mybatis-plus-spring-boot.version>3.5.14</mybatis-plus-spring-boot.version> <mybatis-plus-spring-boot.version>3.5.5</mybatis-plus-spring-boot.version>
<mysql.version>8.0.33</mysql.version> <mysql.version>8.0.33</mysql.version>
<lombok.version>1.18.30</lombok.version> <lombok.version>1.18.30</lombok.version>
<spring-data-redis.version>3.23.6</spring-data-redis.version> <spring-data-redis.version>3.23.6</spring-data-redis.version>
@@ -118,6 +118,11 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus-spring-boot.version}</version>
</dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
@@ -141,6 +146,13 @@
<version>3.15.0</version> <version>3.15.0</version>
</dependency> </dependency>
<!-- JSON -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.51</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>

View File

@@ -2,6 +2,8 @@ package com.xiang.xsa.xservice.ai.server;
import com.xiang.xservice.ai.agent.BaseAgent; import com.xiang.xservice.ai.agent.BaseAgent;
import com.xiang.xservice.ai.core.enums.ModelTypeEnum; import com.xiang.xservice.ai.core.enums.ModelTypeEnum;
import com.xiang.xservice.ai.pojo.enums.AgentEnums;
import com.xiang.xservice.ai.service.AgentService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@@ -11,11 +13,12 @@ import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor @RequiredArgsConstructor
public class ChatController { public class ChatController {
private final BaseAgent baseAgent; private final AgentService agentService;
@GetMapping("/chat") @GetMapping("/chat")
public String chatDemo(@RequestParam("question") String question) { public String chatDemo(@RequestParam("question") String question, @RequestParam("memoryId") Long memoryId, @RequestParam("userId") Long userId) {
baseAgent.chat(ModelTypeEnum.OPEN_AI, question); BaseAgent agent = agentService.createAgent(AgentEnums.SIMPLE_CHAT_AGENT);
agent.chat(ModelTypeEnum.OPEN_AI, memoryId, userId, question);
return "321"; return "321";
} }

View File

@@ -1,8 +1,8 @@
spring: spring:
datasource: datasource:
url: jdbc:mysql://rm-bp15t34gqx62jm069ro.mysql.rds.aliyuncs.com:3306/xservice-ai-center?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true url: jdbc:mysql://120.27.153.87:3306/xe-service-ai?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root username: root
password: xb#UWqnhH24&XpX password: sdkljfikdfn@123
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
ai: ai:
@@ -12,4 +12,9 @@ ai:
apiKey: sk-70cb426d7d1e4b54b4ffe71022e7d815 apiKey: sk-70cb426d7d1e4b54b4ffe71022e7d815
modelName: qwen3.5-plus modelName: qwen3.5-plus
baseUrl: https://dashscope.aliyuncs.com/compatible-mode/v1 baseUrl: https://dashscope.aliyuncs.com/compatible-mode/v1
ollama:
configs:
qwen3:
modelName: qwen3
baseUrl: http://192.168.1.12:11434/api/chat

View File

@@ -1,16 +1,14 @@
spring: spring:
datasource: datasource:
url: jdbc:mysql://rm-bp15t34gqx62jm069ro.mysql.rds.aliyuncs.com:3306/xservice-ai-center?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true url: jdbc:mysql://rm-bp15t34gqx62jm069ro.mysql.rds.aliyuncs.com:3306/xservice-ai-center?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root username: root
password: xb#UWqnhH24&XpX password: sdkljfikdfn@123
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
ai:
dashscope: ai:
api-key: sk-07353fd191074c9c930b134230ba88ea openai:
chat: configs:
options: bai-lian:
model: qwen-plus apiKey: sk-70cb426d7d1e4b54b4ffe71022e7d815
http: modelName: qwen3.5-plus
connect-timeout: 30s baseUrl: https://dashscope.aliyuncs.com/compatible-mode/v1
read-timeout: 60s

View File

@@ -1,16 +1,14 @@
spring: spring:
datasource: datasource:
url: jdbc:mysql://rm-bp15t34gqx62jm069ro.mysql.rds.aliyuncs.com:3306/xservice-ai-center?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true url: jdbc:mysql://120.27.153.87:3306/xe-service-ai?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root username: root
password: xb#UWqnhH24&XpX password: sdkljfikdfn@123
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
ai:
dashscope: ai:
api-key: sk-07353fd191074c9c930b134230ba88ea openai:
chat: configs:
options: bai-lian:
model: qwen-plus apiKey: sk-70cb426d7d1e4b54b4ffe71022e7d815
http: modelName: qwen3.5-plus
connect-timeout: 30s baseUrl: https://dashscope.aliyuncs.com/compatible-mode/v1
read-timeout: 60s

View File

@@ -1,10 +1,14 @@
spring: spring:
application: application:
name: xservice-ai-center name: xservice-ai-center
profiles: profiles:
active: local active: local
cloud:
nacos:
config:
import-check:
enabled: false
server: server:
port: 38020 port: 38020
@@ -14,4 +18,9 @@ server:
charset: UTF-8 charset: UTF-8
enabled: true enabled: true
force: true force: true
mybatis:
mapper-locations:
- classpath*:mapper/*/*.xml
configuration:
map-underscore-to-camel-case: true

View File

@@ -1,7 +1,17 @@
package com.xiang.xservice.ai.agent; package com.xiang.xservice.ai.agent;
import com.xiang.xservice.ai.core.enums.ModelTypeEnum; import com.xiang.xservice.ai.core.enums.ModelTypeEnum;
import com.xiang.xservice.ai.pojo.enums.AgentEnums;
public interface BaseAgent { public interface BaseAgent {
void chat(ModelTypeEnum modelType, String message); /**
* chat
* @param modelType 模型枚举
* @param memoryId
* @param userId
* @param message
*/
void chat(ModelTypeEnum modelType, Long memoryId, Long userId, String message);
AgentEnums agent();
} }

View File

@@ -3,15 +3,20 @@ package com.xiang.xservice.ai.agent;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.xiang.xservice.ai.config.OpenAIBaseConfig; import com.xiang.xservice.ai.config.OpenAIBaseConfig;
import com.xiang.xservice.ai.config.OpenAIConfig; import com.xiang.xservice.ai.config.OpenAIConfig;
import com.xiang.xservice.ai.core.assistant.Assistant;
import com.xiang.xservice.ai.core.entity.ModelConfig; import com.xiang.xservice.ai.core.entity.ModelConfig;
import com.xiang.xservice.ai.core.enums.ModelStrategyEnum; import com.xiang.xservice.ai.core.enums.ModelStrategyEnum;
import com.xiang.xservice.ai.core.enums.ModelTypeEnum; import com.xiang.xservice.ai.core.enums.ModelTypeEnum;
import com.xiang.xservice.ai.core.handler.MyStreamingHandler;
import com.xiang.xservice.ai.core.route.TaskRouter; import com.xiang.xservice.ai.core.route.TaskRouter;
import com.xiang.xservice.ai.core.storage.DbPersistentStore;
import com.xiang.xservice.ai.pojo.enums.AgentEnums;
import com.xiang.xservice.ai.repository.manage.IAiSimpleChatMessageManage;
import dev.langchain4j.data.message.SystemMessage; import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.StreamingChatModel; import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.service.AiServices;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -21,11 +26,21 @@ public class SimpleChatAgent implements BaseAgent {
private final TaskRouter router; private final TaskRouter router;
private final OpenAIBaseConfig openAIBaseConfig; private final OpenAIBaseConfig openAIBaseConfig;
private final MyStreamingHandler streamingHandler; private final IAiSimpleChatMessageManage aiSimpleChatMessageManage;
@Override @Override
public void chat(ModelTypeEnum modelType, String message) { public void chat(ModelTypeEnum modelType, Long memoryId, Long userId, String message) {
DbPersistentStore store = new DbPersistentStore(aiSimpleChatMessageManage);
ChatMemoryProvider chatMemoryProvider = args -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(10)
.chatMemoryStore(store)
.build();
OpenAIConfig openAIConfig = openAIBaseConfig.getConfigs().get("bai-lian"); OpenAIConfig openAIConfig = openAIBaseConfig.getConfigs().get("bai-lian");
ModelConfig modelConfig = ModelConfig.builder() ModelConfig modelConfig = ModelConfig.builder()
.baseUrl(openAIConfig.getBaseUrl()) .baseUrl(openAIConfig.getBaseUrl())
@@ -34,9 +49,27 @@ public class SimpleChatAgent implements BaseAgent {
.modelName(openAIConfig.getModelName()) .modelName(openAIConfig.getModelName())
.temperature(openAIConfig.getTemperature()) .temperature(openAIConfig.getTemperature())
.build(); .build();
StreamingChatModel chat = router.route(ModelStrategyEnum.CHAT, modelType.getModelType(), modelConfig); StreamingChatModel chat = router.routeStream(ModelStrategyEnum.CHAT, modelType.getModelType(), modelConfig);
if (chat == null) {
throw new RuntimeException("chat model route failed");
}
Assistant assistant = AiServices.builder(Assistant.class)
.streamingChatModel(chat)
.chatMemoryProvider(chatMemoryProvider)
.build();
UserMessage userMessage = new UserMessage(message); UserMessage userMessage = new UserMessage(message);
SystemMessage systemMessage = new SystemMessage("你是一个能够与人聊天的AI智能助手名字叫龙虾"); SystemMessage systemMessage = new SystemMessage("你是一个能够与人聊天的AI智能助手名字叫龙虾");
chat.chat(Lists.newArrayList(systemMessage, userMessage), streamingHandler); String id = userId + "-" + memoryId;
assistant.chat(id, Lists.newArrayList(systemMessage, userMessage))
.onPartialResponse(System.out::print)
.onCompleteResponse(res -> System.out.println("\n完成"))
.onToolExecuted(error -> System.out.println("\n错误"))
.ignoreErrors()
.start();
}
@Override
public AgentEnums agent() {
return AgentEnums.SIMPLE_CHAT_AGENT;
} }
} }

View File

@@ -0,0 +1,18 @@
package com.xiang.xservice.ai.agent;
import com.xiang.xservice.ai.core.enums.ModelTypeEnum;
import com.xiang.xservice.ai.pojo.enums.AgentEnums;
import org.springframework.stereotype.Service;
@Service
public class StockAnalysisAgent implements BaseAgent{
@Override
public void chat(ModelTypeEnum modelType, Long memoryId, Long userId, String message) {
}
@Override
public AgentEnums agent() {
return AgentEnums.STOCK_ANALYZER_AGENT;
}
}

View File

@@ -0,0 +1,12 @@
package com.xiang.xservice.ai.core.assistant;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.service.UserMessage;
import java.util.List;
public interface Assistant {
TokenStream chat(@MemoryId Object memoryId, @UserMessage List<ChatMessage> messages);
}

View File

@@ -3,7 +3,6 @@ package com.xiang.xservice.ai.core.handler;
import dev.langchain4j.model.StreamingResponseHandler; import dev.langchain4j.model.StreamingResponseHandler;
import dev.langchain4j.model.chat.response.ChatResponse; import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import org.checkerframework.checker.units.qual.C;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component

View File

@@ -2,7 +2,6 @@ package com.xiang.xservice.ai.core.provider;
import com.xiang.xservice.ai.core.entity.ModelConfig; import com.xiang.xservice.ai.core.entity.ModelConfig;
import com.xiang.xservice.ai.core.enums.ModelStrategyEnum; import com.xiang.xservice.ai.core.enums.ModelStrategyEnum;
import com.xiang.xservice.ai.core.enums.ModelTypeEnum;
import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel; import dev.langchain4j.model.chat.StreamingChatModel;
@@ -14,11 +13,18 @@ public interface BaseProvider {
*/ */
String providerName(); String providerName();
/** /**
* 创建model * 创建流式model
* @param config model配置文件 * @param config model配置文件
* @return ChatModel * @return ChatModel
*/ */
StreamingChatModel build(ModelConfig config); StreamingChatModel build(ModelConfig config);
/**
* 同步聊天模型
* @param config
* @return
*/
ChatModel buildChatModel(ModelConfig config);
/** /**
* 用于标记这个 Provider 是否适合某个 TaskType * 用于标记这个 Provider 是否适合某个 TaskType
* @param taskType ModelStrategyEnum * @param taskType ModelStrategyEnum

View File

@@ -26,6 +26,15 @@ public class OllamaLlmProvider implements BaseProvider{
.build(); .build();
} }
@Override
public ChatModel buildChatModel(ModelConfig config) {
return OllamaChatModel.builder()
.baseUrl(config.getBaseUrl())
.modelName(config.getModelName())
.temperature(config.getTemperature())
.build();
}
@Override @Override
public boolean support(ModelStrategyEnum taskType) { public boolean support(ModelStrategyEnum taskType) {
return true; return true;

View File

@@ -3,10 +3,14 @@ package com.xiang.xservice.ai.core.provider;
import com.xiang.xservice.ai.core.entity.ModelConfig; import com.xiang.xservice.ai.core.entity.ModelConfig;
import com.xiang.xservice.ai.core.enums.ModelStrategyEnum; import com.xiang.xservice.ai.core.enums.ModelStrategyEnum;
import com.xiang.xservice.ai.core.enums.ModelTypeEnum; import com.xiang.xservice.ai.core.enums.ModelTypeEnum;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel; import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel; import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Duration;
@Component @Component
public class OpenAILlmProvider implements BaseProvider { public class OpenAILlmProvider implements BaseProvider {
@@ -30,6 +34,18 @@ public class OpenAILlmProvider implements BaseProvider {
.build(); .build();
} }
@Override
public ChatModel buildChatModel(ModelConfig config) {
return OpenAiChatModel.builder()
.baseUrl(config.getBaseUrl())
.apiKey(config.getApiKey())
.modelName(config.getModelName())
.temperature(config.getTemperature())
.maxTokens(config.getMaxTokens())
.timeout(Duration.ofSeconds(300))
.build();
}
@Override @Override
public boolean support(ModelStrategyEnum taskType) { public boolean support(ModelStrategyEnum taskType) {
return true; return true;

View File

@@ -3,10 +3,8 @@ package com.xiang.xservice.ai.core.route;
import com.xiang.xservice.ai.core.entity.ModelConfig; import com.xiang.xservice.ai.core.entity.ModelConfig;
import com.xiang.xservice.ai.core.enums.ModelStrategyEnum; import com.xiang.xservice.ai.core.enums.ModelStrategyEnum;
import com.xiang.xservice.ai.core.provider.BaseProvider; import com.xiang.xservice.ai.core.provider.BaseProvider;
import com.xiang.xservice.ai.core.strategy.BaseStrategy;
import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel; import dev.langchain4j.model.chat.StreamingChatModel;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.List; import java.util.List;
@@ -29,7 +27,7 @@ public class ModelRouter {
/** /**
* 根据 providerName 选择 Provider并用 config 构建模型 * 根据 providerName 选择 Provider并用 config 构建模型
*/ */
public StreamingChatModel route(String providerName, ModelConfig config) { public StreamingChatModel routeStream(String providerName, ModelConfig config) {
BaseProvider provider = providerMap.get(providerName); BaseProvider provider = providerMap.get(providerName);
if (provider == null) { if (provider == null) {
throw new RuntimeException("Provider " + providerName + " not found"); throw new RuntimeException("Provider " + providerName + " not found");
@@ -37,6 +35,14 @@ public class ModelRouter {
return provider.build(config); return provider.build(config);
} }
public ChatModel route(String providerName, ModelConfig config) {
BaseProvider provider = providerMap.get(providerName);
if (provider == null) {
throw new RuntimeException("Provider " + providerName + " not found");
}
return provider.buildChatModel(config);
}
/** /**
* 获取某个 TaskType 支持的所有 Provider 名称 * 获取某个 TaskType 支持的所有 Provider 名称
*/ */

View File

@@ -5,13 +5,11 @@ import com.xiang.xservice.ai.core.enums.ModelStrategyEnum;
import com.xiang.xservice.ai.core.provider.BaseProvider; import com.xiang.xservice.ai.core.provider.BaseProvider;
import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel; import dev.langchain4j.model.chat.StreamingChatModel;
import org.checkerframework.checker.units.qual.C;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
@Component @Component
public class TaskRouter { public class TaskRouter {
@@ -32,7 +30,17 @@ public class TaskRouter {
/** /**
* 第一层路由 + 第二层 ProviderRouter * 第一层路由 + 第二层 ProviderRouter
*/ */
public StreamingChatModel route(ModelStrategyEnum taskType, String providerName, ModelConfig config) { public StreamingChatModel routeStream(ModelStrategyEnum taskType, String providerName, ModelConfig config) {
// 如果没有传 providerName使用默认 Provider
if (providerName == null || providerName.isEmpty()) {
providerName = taskDefaultProviderMap.get(taskType).get(0);
}
// 第二层路由
return providerRouter.routeStream(providerName, config);
}
public ChatModel route(ModelStrategyEnum taskType, String providerName, ModelConfig config) {
// 如果没有传 providerName使用默认 Provider // 如果没有传 providerName使用默认 Provider
if (providerName == null || providerName.isEmpty()) { if (providerName == null || providerName.isEmpty()) {
providerName = taskDefaultProviderMap.get(taskType).get(0); providerName = taskDefaultProviderMap.get(taskType).get(0);

View File

@@ -0,0 +1,109 @@
package com.xiang.xservice.ai.core.storage;
import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.xiang.xservice.ai.pojo.entity.AiSimpleChatMessageDO;
import com.xiang.xservice.ai.repository.manage.IAiSimpleChatMessageManage;
import dev.langchain4j.data.message.*;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
/**
* 数据库持久化
*/
@RequiredArgsConstructor
public class DbPersistentStore implements ChatMemoryStore {
private final IAiSimpleChatMessageManage aiSimpleChatMessageManage;
@Override
public List<ChatMessage> getMessages(Object args) {
String argsStr = String.valueOf(args);
String[] split = argsStr.split("-");
Long userId = Long.valueOf(split[0]);
Long memoryId = Long.valueOf(split[1]);
List<AiSimpleChatMessageDO> msg = aiSimpleChatMessageManage.getMsgByMemoryId(userId, memoryId);
if (CollectionUtils.isEmpty(msg)) {
return Lists.newArrayList();
}
List<ChatMessage> result = Lists.newArrayList();
for (AiSimpleChatMessageDO chatMessageDO : msg) {
String role = chatMessageDO.getRole();
switch (role) {
case "SYSTEM":
result.add(SystemMessage.from(chatMessageDO.getMessage()));
break;
case "USER":
result.add(UserMessage.from(chatMessageDO.getMessage()));
break;
case "AI":
result.add(AiMessage.from(chatMessageDO.getMessage()));
break;
default:
break;
}
}
return result;
}
@Override
public void updateMessages(Object args, List<ChatMessage> list) {
String argsStr = String.valueOf(args);
String[] split = argsStr.split("-");
Long userId = Long.valueOf(split[0]);
Long memoryId = Long.valueOf(split[1]);
List<AiSimpleChatMessageDO> result = Lists.newArrayList();
for (ChatMessage chatMessage : list) {
StringBuilder str = new StringBuilder();
String type = "";
if (chatMessage instanceof SystemMessage systemMessage) {
str = new StringBuilder(systemMessage.text());
type = "SYSTEM";
}
if (chatMessage instanceof UserMessage userMessage) {
for (Content content : userMessage.contents()) {
if (content instanceof TextContent textContent) {
str.append(textContent.text());
type = "USER";
}
}
}
if (chatMessage instanceof AiMessage aiMessage) {
str = new StringBuilder(aiMessage.text());
type = "AI";
}
AiSimpleChatMessageDO message = AiSimpleChatMessageDO.builder()
.userId(userId)
.memoryId(memoryId)
.message(str.toString())
.createTime(LocalDateTime.now())
.delFlag(0)
.role(type)
.build();
result.add(message);
}
aiSimpleChatMessageManage.saveBatch(result);
}
@Override
public void deleteMessages(Object args) {
String argsStr = String.valueOf(args);
String[] split = argsStr.split("-");
Long userId = Long.valueOf(split[0]);
Long memoryId = Long.valueOf(split[1]);
aiSimpleChatMessageManage.deleteByMemoryId(userId, memoryId);
}
}

View File

@@ -0,0 +1,42 @@
package com.xiang.xservice.ai.core.storage;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import java.util.List;
import java.util.Map;
/**
* 内存持久化
*/
public class MemoryPersistentStore implements ChatMemoryStore {
private final static Map<Long, List<ChatMessage>> MAP = Maps.newHashMap();
@Override
public List<ChatMessage> getMessages(Object memoryId) {
Long id = (Long) memoryId;
if (MAP.containsKey(id)) {
return MAP.get(id);
}
return Lists.newArrayList();
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> list) {
Long id = (Long) memoryId;
if (MAP.containsKey(id)) {
MAP.get(id).addAll(list);
} else {
MAP.put(id, list);
}
}
@Override
public void deleteMessages(Object memoryId) {
MAP.remove((Long) memoryId);
}
}

View File

@@ -2,9 +2,7 @@ package com.xiang.xservice.ai.core.strategy;
import com.xiang.xservice.ai.core.entity.ModelConfig; import com.xiang.xservice.ai.core.entity.ModelConfig;
import com.xiang.xservice.ai.core.enums.ModelStrategyEnum; import com.xiang.xservice.ai.core.enums.ModelStrategyEnum;
import com.xiang.xservice.ai.core.provider.BaseProvider;
import com.xiang.xservice.ai.core.route.TaskRouter; import com.xiang.xservice.ai.core.route.TaskRouter;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel; import dev.langchain4j.model.chat.StreamingChatModel;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -22,6 +20,6 @@ public class ChatStrategy implements BaseStrategy {
@Override @Override
public StreamingChatModel createProvider(String provider, ModelConfig config) { public StreamingChatModel createProvider(String provider, ModelConfig config) {
return taskRouter.route(ModelStrategyEnum.CHAT, provider, config); return taskRouter.routeStream(ModelStrategyEnum.CHAT, provider, config);
} }
} }

View File

@@ -0,0 +1,24 @@
package com.xiang.xservice.ai.pojo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("ai_simple_chat_message")
@Builder
public class AiSimpleChatMessageDO {
private Long id;
private Long userId;
private Long memoryId;
private String role;
private String message;
private LocalDateTime createTime;
private Integer delFlag;
}

View File

@@ -0,0 +1,13 @@
package com.xiang.xservice.ai.pojo.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum AgentEnums {
SIMPLE_CHAT_AGENT("SimpleChatAgent"),
STOCK_ANALYZER_AGENT("StockAnalysisAgent"),
;
private final String name;
}

View File

@@ -0,0 +1,27 @@
package com.xiang.xservice.ai.repository.manage;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiang.xservice.ai.pojo.entity.AiSimpleChatMessageDO;
import com.xiang.xservice.ai.repository.mapper.IAiSimpleChatMessageMapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AiSimpleChatMessageManageImpl extends ServiceImpl<IAiSimpleChatMessageMapper, AiSimpleChatMessageDO> implements IAiSimpleChatMessageManage {
@Override
public List<AiSimpleChatMessageDO> getMsgByMemoryId(Long userId, Long memoryId) {
LambdaQueryWrapper<AiSimpleChatMessageDO> lambdaQueryWrapper = Wrappers.lambdaQuery();
lambdaQueryWrapper.eq(AiSimpleChatMessageDO::getUserId, userId);
lambdaQueryWrapper.eq(AiSimpleChatMessageDO::getMemoryId, memoryId);
lambdaQueryWrapper.eq(AiSimpleChatMessageDO::getDelFlag, 0);
return baseMapper.selectList(lambdaQueryWrapper);
}
@Override
public boolean deleteByMemoryId(Long userId, Long memoryId) {
return baseMapper.deleteByMemoryId(userId, memoryId) > 0;
}
}

View File

@@ -0,0 +1,11 @@
package com.xiang.xservice.ai.repository.manage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiang.xservice.ai.pojo.entity.AiSimpleChatMessageDO;
import java.util.List;
public interface IAiSimpleChatMessageManage extends IService<AiSimpleChatMessageDO> {
List<AiSimpleChatMessageDO> getMsgByMemoryId(Long userId, Long memoryId);
boolean deleteByMemoryId(Long userId, Long memoryId);
}

View File

@@ -0,0 +1,14 @@
package com.xiang.xservice.ai.repository.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiang.xservice.ai.pojo.entity.AiSimpleChatMessageDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface IAiSimpleChatMessageMapper extends BaseMapper<AiSimpleChatMessageDO> {
int deleteByMemoryId(@Param("userId") Long userId, @Param("memoryId") Long memoryId);
}

View File

@@ -1,16 +1,31 @@
package com.xiang.xservice.ai.service; package com.xiang.xservice.ai.service;
import com.xiang.xservice.ai.core.entity.ModelConfig; import com.google.common.collect.Maps;
import com.xiang.xservice.ai.core.enums.ModelStrategyEnum; import com.xiang.xservice.ai.agent.BaseAgent;
import com.xiang.xservice.ai.core.route.TaskRouter; import com.xiang.xservice.ai.pojo.enums.AgentEnums;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.service.AiServices;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class AgentService { public class AgentService {
private final static Map<AgentEnums, BaseAgent> agents = Maps.newHashMap();
public AgentService(List<BaseAgent> agentList) {
agents.putAll(agentList.stream()
.collect(Collectors.toMap(BaseAgent::agent, Function.identity())));
}
public BaseAgent createAgent(AgentEnums name) {
BaseAgent agent = agents.get(name);
if (Objects.isNull(agent)) throw new RuntimeException("Agent not found: " + name);
return agent;
}
} }

View File

@@ -1,19 +0,0 @@
package com.xiang.xservice.ai.service;
import com.xiang.xservice.ai.core.entity.ModelConfig;
import com.xiang.xservice.ai.core.enums.ModelTypeEnum;
import com.xiang.xservice.ai.core.route.ModelRouter;
import dev.langchain4j.model.chat.ChatModel;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class AiService {
// private final ModelRouter modelRouter;
//
// private ChatModel createChat(ModelTypeEnum modelType, ModelConfig modelConfig) {
// return modelRouter.create(modelType.getModelType(), modelConfig);
// }
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiang.dao.AiSimpleChatMessageMapper">
<resultMap id="BaseResultMap" type="com.xiang.xservice.ai.pojo.entity.AiSimpleChatMessageDO">
<result column="id" property="id"/>
<result column="memory_id" property="memoryId"/>
<result column="role" property="role"/>
<result column="message" property="message"/>
<result column="create_time" property="createTime"/>
<result column="user_id" property="userId"/>
<result column="del_flag" property="delFlag"/>
</resultMap>
<sql id="Base_Column_List">
id,
memory_id,
role,
message,
create_time,
user_id,
del_flag
</sql>
<update id="deleteByMemoryId">
update ai_simple_chat_message set del_flag = 1 where memory_id = #{memoryId} and user_id = #{userId}
</update>
</mapper>