mirror of
https://github.com/ccmjga/zhilu-admin
synced 2026-03-14 05:33:42 +08:00
init command execute mode
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
package com.zl.mjga.config.ai;
|
||||
|
||||
import com.zl.mjga.exception.BusinessException;
|
||||
import com.zl.mjga.repository.DepartmentRepository;
|
||||
import com.zl.mjga.service.DepartmentService;
|
||||
import dev.langchain4j.agent.tool.P;
|
||||
import dev.langchain4j.agent.tool.Tool;
|
||||
import dev.langchain4j.model.output.structured.Description;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jooq.generated.mjga.tables.pojos.Department;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Description("和部门管理有关的操作工具")
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class DepartmentOperatorTool {
|
||||
|
||||
private final DepartmentService departmentService;
|
||||
private final DepartmentRepository departmentRepository;
|
||||
|
||||
@Tool(value = "创建部门")
|
||||
void createDepartment(
|
||||
@P(value = "部门名称") String departmentName,
|
||||
@P(value = "上级部门名称", required = false) String parentDepartmentName) {
|
||||
Department exist = departmentRepository.fetchOneByName(departmentName);
|
||||
if (exist != null) {
|
||||
throw new BusinessException("当前部门已存在");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(parentDepartmentName)) {
|
||||
Department parent = departmentRepository.fetchOneByName(parentDepartmentName);
|
||||
if (parent == null) {
|
||||
throw new BusinessException("上级部门不存在");
|
||||
}
|
||||
}
|
||||
departmentService.upsertDepartment(new Department(null, departmentName, null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.zl.mjga.config.ai;
|
||||
|
||||
import dev.langchain4j.service.MemoryId;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.memory.ChatMemoryAccess;
|
||||
|
||||
public interface SystemToolAssistant extends ChatMemoryAccess {
|
||||
TokenStream ask(@MemoryId String memoryId, @UserMessage String question);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zl.mjga.config.ai;
|
||||
|
||||
import dev.langchain4j.community.model.zhipu.ZhipuAiStreamingChatModel;
|
||||
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
|
||||
import dev.langchain4j.service.AiServices;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class ToolsInitializer {
|
||||
|
||||
private final UserOperatorTool userOperatorTool;
|
||||
private final DepartmentOperatorTool departmentOperatorTool;
|
||||
|
||||
@Bean
|
||||
@DependsOn("flywayInitializer")
|
||||
public SystemToolAssistant zhiPuToolAssistant(ZhipuAiStreamingChatModel zhipuChatModel) {
|
||||
return AiServices.builder(SystemToolAssistant.class)
|
||||
.streamingChatModel(zhipuChatModel)
|
||||
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10))
|
||||
.tools(userOperatorTool, departmentOperatorTool)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.zl.mjga.config.ai;
|
||||
|
||||
import com.zl.mjga.dto.urp.UserUpsertDto;
|
||||
import com.zl.mjga.exception.BusinessException;
|
||||
import com.zl.mjga.repository.UserRepository;
|
||||
import com.zl.mjga.service.IdentityAccessService;
|
||||
import dev.langchain4j.agent.tool.P;
|
||||
import dev.langchain4j.agent.tool.Tool;
|
||||
import dev.langchain4j.model.output.structured.Description;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jooq.generated.mjga.tables.pojos.User;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Description("和用户管理有关的操作工具")
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class UserOperatorTool {
|
||||
|
||||
private final IdentityAccessService identityAccessService;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@Tool(value = "创建用户或注册用户")
|
||||
void createUser(@P(value = "用户名") String username) {
|
||||
User user = userRepository.fetchOneByUsername(username);
|
||||
if (user != null) {
|
||||
throw new BusinessException("用户已存在");
|
||||
}
|
||||
identityAccessService.upsertUser(new UserUpsertDto(null, username, username, true));
|
||||
}
|
||||
|
||||
@Tool(value = "删除用户")
|
||||
void deleteUser(@P(value = "用户名") String username) {
|
||||
userRepository.deleteByUsername(username);
|
||||
}
|
||||
|
||||
@Tool(value = "编辑/更新/更改用户")
|
||||
void updateUser(
|
||||
@P(value = "用户名") String username,
|
||||
@P(value = "密码", required = false) String password,
|
||||
@P(value = "是否开启", required = false) Boolean enable) {
|
||||
identityAccessService.upsertUser(new UserUpsertDto(null, username, password, enable));
|
||||
}
|
||||
}
|
||||
@@ -42,11 +42,34 @@ public class AiController {
|
||||
private final UserRepository userRepository;
|
||||
private final DepartmentRepository departmentRepository;
|
||||
|
||||
@PostMapping(value = "/action/execute", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public Flux<String> actionExecute(Principal principal, @RequestBody String userMessage) {
|
||||
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
|
||||
TokenStream chat = aiChatService.actionExecuteWithZhiPu(principal.getName(), userMessage);
|
||||
chat.onPartialResponse(
|
||||
text ->
|
||||
sink.tryEmitNext(
|
||||
StringUtils.isNotEmpty(text) ? text.replace(" ", "␣").replace("\t", "⇥") : ""))
|
||||
.onToolExecuted(
|
||||
toolExecution -> log.debug("当前请求 {} 成功执行函数调用: {}", userMessage, toolExecution))
|
||||
.onCompleteResponse(
|
||||
r -> {
|
||||
sink.tryEmitComplete();
|
||||
sink.emitComplete(Sinks.EmitFailureHandler.FAIL_FAST);
|
||||
})
|
||||
.onError(sink::tryEmitError)
|
||||
.start();
|
||||
return sink.asFlux().timeout(Duration.ofSeconds(120));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public Flux<String> chat(Principal principal, @RequestBody String userMessage) {
|
||||
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
|
||||
TokenStream chat = aiChatService.chatPrecedenceLlmWith(principal.getName(), userMessage);
|
||||
chat.onPartialResponse(text -> sink.tryEmitNext(text.replace(" ", "␣").replace("\t", "⇥")))
|
||||
chat.onPartialResponse(
|
||||
text ->
|
||||
sink.tryEmitNext(
|
||||
StringUtils.isNotEmpty(text) ? text.replace(" ", "␣").replace("\t", "⇥") : ""))
|
||||
.onCompleteResponse(
|
||||
r -> {
|
||||
sink.tryEmitComplete();
|
||||
@@ -71,8 +94,8 @@ public class AiController {
|
||||
return llmService.pageQueryLlm(pageRequestDto, llmQueryDto);
|
||||
}
|
||||
|
||||
@PostMapping("/action/chat")
|
||||
public Map<String, String> actionChat(@RequestBody String message) {
|
||||
@PostMapping("/action/search")
|
||||
public Map<String, String> searchAction(@RequestBody String message) {
|
||||
AiLlmConfig aiLlmConfig = llmService.loadConfig(LlmCodeEnum.ZHI_PU);
|
||||
if (!aiLlmConfig.getEnable()) {
|
||||
throw new BusinessException("命令模型未启用,请开启后再试。");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.zl.mjga.service;
|
||||
|
||||
import com.zl.mjga.config.ai.AiChatAssistant;
|
||||
import com.zl.mjga.config.ai.SystemToolAssistant;
|
||||
import com.zl.mjga.exception.BusinessException;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import java.util.Optional;
|
||||
@@ -17,6 +18,7 @@ public class AiChatService {
|
||||
|
||||
private final AiChatAssistant deepSeekChatAssistant;
|
||||
private final AiChatAssistant zhiPuChatAssistant;
|
||||
private final SystemToolAssistant zhiPuToolAssistant;
|
||||
private final LlmService llmService;
|
||||
|
||||
public TokenStream chatWithDeepSeek(String sessionIdentifier, String userMessage) {
|
||||
@@ -27,14 +29,22 @@ public class AiChatService {
|
||||
return zhiPuChatAssistant.chat(sessionIdentifier, userMessage);
|
||||
}
|
||||
|
||||
public TokenStream actionExecuteWithZhiPu(String sessionIdentifier, String userMessage) {
|
||||
return zhiPuToolAssistant.ask(sessionIdentifier, userMessage);
|
||||
}
|
||||
|
||||
public TokenStream chatPrecedenceLlmWith(String sessionIdentifier, String userMessage) {
|
||||
Optional<AiLlmConfig> precedenceLlmBy = llmService.getPrecedenceChatLlmBy(true);
|
||||
AiLlmConfig aiLlmConfig = precedenceLlmBy.orElseThrow(() -> new BusinessException("没有开启的大模型"));
|
||||
LlmCodeEnum code = aiLlmConfig.getCode();
|
||||
LlmCodeEnum code = getPrecedenceLlmCode();
|
||||
return switch (code) {
|
||||
case ZHI_PU -> zhiPuChatAssistant.chat(sessionIdentifier, userMessage);
|
||||
case DEEP_SEEK -> deepSeekChatAssistant.chat(sessionIdentifier, userMessage);
|
||||
default -> throw new BusinessException(String.format("无效的模型代码 %s", code));
|
||||
};
|
||||
}
|
||||
|
||||
private LlmCodeEnum getPrecedenceLlmCode() {
|
||||
Optional<AiLlmConfig> precedenceLlmBy = llmService.getPrecedenceChatLlmBy(true);
|
||||
AiLlmConfig aiLlmConfig = precedenceLlmBy.orElseThrow(() -> new BusinessException("没有开启的大模型"));
|
||||
return aiLlmConfig.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.zl.mjga.dto.PageResponseDto;
|
||||
import com.zl.mjga.dto.ai.LlmQueryDto;
|
||||
import com.zl.mjga.dto.ai.LlmVm;
|
||||
import com.zl.mjga.repository.LlmRepository;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -37,7 +36,7 @@ public class LlmService {
|
||||
List<AiLlmConfig> aiLlmConfigs = llmRepository.fetchByEnable(enable);
|
||||
return aiLlmConfigs.stream()
|
||||
.filter(aiLlmConfig -> LlmTypeEnum.CHAT.equals(aiLlmConfig.getType()))
|
||||
.max(Comparator.comparingInt(AiLlmConfig::getPriority));
|
||||
.max(Comparator.comparingInt(AiLlmConfig::getPriority));
|
||||
}
|
||||
|
||||
public PageResponseDto<List<LlmVm>> pageQueryLlm(
|
||||
|
||||
Reference in New Issue
Block a user