package com.zy.ai.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.zy.ai.domain.autotune.AutoTuneControlModeSnapshot;
|
import com.zy.ai.domain.autotune.AutoTuneJobStatus;
|
import com.zy.ai.domain.autotune.AutoTuneTriggerType;
|
import com.zy.ai.entity.AiAutoTuneMcpCall;
|
import com.zy.ai.entity.AiAutoTuneJob;
|
import com.zy.ai.enums.AiPromptScene;
|
import com.zy.ai.service.AiAutoTuneJobService;
|
import com.zy.ai.service.AiAutoTuneMcpCallService;
|
import com.zy.ai.service.AutoTuneAgentService;
|
import com.zy.ai.service.AutoTuneControlModeService;
|
import com.zy.ai.service.AutoTuneCoordinatorService;
|
import com.zy.asrs.entity.WrkMast;
|
import com.zy.asrs.service.WrkMastService;
|
import com.zy.common.utils.RedisUtil;
|
import com.zy.core.enums.RedisKeyType;
|
import com.zy.core.enums.WrkStsType;
|
import com.zy.system.entity.OperateLog;
|
import com.zy.system.service.ConfigService;
|
import com.zy.system.service.OperateLogService;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.Date;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.UUID;
|
|
@Slf4j
|
@Service("autoTuneCoordinatorService")
|
@RequiredArgsConstructor
|
public class AutoTuneCoordinatorServiceImpl implements AutoTuneCoordinatorService {
|
|
private static final String CONFIG_INTERVAL_MINUTES = "aiAutoTuneIntervalMinutes";
|
private static final int DEFAULT_INTERVAL_MINUTES = 10;
|
private static final int MIN_INTERVAL_MINUTES = 5;
|
private static final int MAX_INTERVAL_MINUTES = 60;
|
private static final int RUNNING_LOCK_SECONDS = 20 * 60;
|
private static final long SYSTEM_USER_ID = 9527L;
|
private static final List<Long> FINAL_WRK_STS_LIST = Arrays.asList(
|
WrkStsType.COMPLETE_INBOUND.sts,
|
WrkStsType.SETTLE_INBOUND.sts,
|
WrkStsType.COMPLETE_OUTBOUND.sts,
|
WrkStsType.SETTLE_OUTBOUND.sts,
|
WrkStsType.COMPLETE_LOC_MOVE.sts,
|
WrkStsType.COMPLETE_CRN_MOVE.sts
|
);
|
|
private final ConfigService configService;
|
private final AutoTuneControlModeService autoTuneControlModeService;
|
private final WrkMastService wrkMastService;
|
private final AiAutoTuneJobService aiAutoTuneJobService;
|
private final AiAutoTuneMcpCallService aiAutoTuneMcpCallService;
|
private final AutoTuneAgentService autoTuneAgentService;
|
private final RedisUtil redisUtil;
|
private final OperateLogService operateLogService;
|
|
@Override
|
public AutoTuneCoordinatorResult runAutoTuneIfEligible() {
|
if (!isEnabled()) {
|
return AutoTuneCoordinatorResult.skipped("disabled");
|
}
|
|
int intervalMinutes = resolveIntervalMinutes();
|
if (!hasActiveTasks()) {
|
return AutoTuneCoordinatorResult.skipped("no_active_tasks");
|
}
|
if (isLastTriggerGuardActive()) {
|
return AutoTuneCoordinatorResult.skipped("last_trigger_guard_active");
|
}
|
|
AiAutoTuneJob latestSuccessfulJob = latestSuccessfulAutoJob();
|
if (!isIntervalReached(latestSuccessfulJob, intervalMinutes)) {
|
return AutoTuneCoordinatorResult.skipped("interval_not_reached");
|
}
|
|
return runAgentWithLock(AutoTuneTriggerType.AUTO.getCode(), intervalMinutes, true);
|
}
|
|
@Override
|
public AutoTuneCoordinatorResult runManualAutoTune() {
|
return runAgentWithLock(AutoTuneTriggerType.MANUAL.getCode(), null, false);
|
}
|
|
private AutoTuneCoordinatorResult runAgentWithLock(String triggerType, Integer intervalMinutes, boolean writeGuard) {
|
String lockKey = RedisKeyType.AI_AUTO_TUNE_RUNNING_LOCK.key;
|
String lockToken = UUID.randomUUID().toString();
|
if (!redisUtil.trySetStringIfAbsent(lockKey, lockToken, RUNNING_LOCK_SECONDS)) {
|
return AutoTuneCoordinatorResult.skipped("running_lock_not_acquired");
|
}
|
|
AutoTuneAgentService.AutoTuneAgentResult agentResult = null;
|
Date startTime = new Date();
|
try {
|
if (writeGuard && intervalMinutes != null) {
|
safeMarkLastTriggerGuard(intervalMinutes);
|
}
|
agentResult = autoTuneAgentService.runAutoTune(triggerType);
|
safeWriteOperateLog(agentResult);
|
safeWriteAgentRunAudit(triggerType, startTime, agentResult);
|
return AutoTuneCoordinatorResult.triggered(agentResult);
|
} catch (Exception exception) {
|
log.error("Auto tune coordinator failed to run agent", exception);
|
agentResult = failedAgentResult(triggerType, exception);
|
safeWriteOperateLog(agentResult);
|
safeWriteAgentRunAudit(triggerType, startTime, agentResult);
|
return AutoTuneCoordinatorResult.triggered(agentResult);
|
} finally {
|
redisUtil.compareAndDelete(lockKey, lockToken);
|
}
|
}
|
|
private boolean isEnabled() {
|
AutoTuneControlModeSnapshot controlMode = autoTuneControlModeService.currentMode();
|
return controlMode != null && Boolean.TRUE.equals(controlMode.getEnabled());
|
}
|
|
private int resolveIntervalMinutes() {
|
String value = configService.getConfigValue(CONFIG_INTERVAL_MINUTES, String.valueOf(DEFAULT_INTERVAL_MINUTES));
|
try {
|
int intervalMinutes = Integer.parseInt(value.trim());
|
if (intervalMinutes < MIN_INTERVAL_MINUTES || intervalMinutes > MAX_INTERVAL_MINUTES) {
|
return DEFAULT_INTERVAL_MINUTES;
|
}
|
return intervalMinutes;
|
} catch (Exception exception) {
|
return DEFAULT_INTERVAL_MINUTES;
|
}
|
}
|
|
private boolean hasActiveTasks() {
|
QueryWrapper<WrkMast> queryWrapper = new QueryWrapper<>();
|
queryWrapper.and(wrapper -> wrapper.notIn("wrk_sts", FINAL_WRK_STS_LIST).or().isNull("wrk_sts"));
|
return wrkMastService.count(queryWrapper) > 0;
|
}
|
|
private boolean isLastTriggerGuardActive() {
|
Object guardValue = redisUtil.get(RedisKeyType.AI_AUTO_TUNE_LAST_TRIGGER_GUARD.key);
|
return guardValue != null;
|
}
|
|
private AiAutoTuneJob latestSuccessfulAutoJob() {
|
QueryWrapper<AiAutoTuneJob> queryWrapper = new QueryWrapper<>();
|
queryWrapper.eq("status", AutoTuneJobStatus.SUCCESS.getCode());
|
queryWrapper.eq("trigger_type", AutoTuneTriggerType.AUTO.getCode());
|
queryWrapper.last("order by coalesce(finish_time, create_time) desc limit 1");
|
List<AiAutoTuneJob> jobs = aiAutoTuneJobService.list(queryWrapper);
|
if (jobs == null || jobs.isEmpty()) {
|
return null;
|
}
|
return jobs.get(0);
|
}
|
|
private boolean isIntervalReached(AiAutoTuneJob latestSuccessfulJob, int intervalMinutes) {
|
if (latestSuccessfulJob == null) {
|
return true;
|
}
|
Date latestFinishTime = latestSuccessfulJob.getFinishTime();
|
if (latestFinishTime == null) {
|
latestFinishTime = latestSuccessfulJob.getCreateTime();
|
}
|
if (latestFinishTime == null) {
|
return true;
|
}
|
long intervalMillis = intervalMinutes * 60L * 1000L;
|
return System.currentTimeMillis() - latestFinishTime.getTime() >= intervalMillis;
|
}
|
|
private void safeMarkLastTriggerGuard(int intervalMinutes) {
|
try {
|
markLastTriggerGuard(intervalMinutes);
|
} catch (Exception exception) {
|
log.warn("Auto tune coordinator failed to write last trigger guard", exception);
|
}
|
}
|
|
private void markLastTriggerGuard(int intervalMinutes) {
|
long expireSeconds = intervalMinutes * 60L;
|
redisUtil.set(RedisKeyType.AI_AUTO_TUNE_LAST_TRIGGER_GUARD.key,
|
String.valueOf(System.currentTimeMillis()), expireSeconds);
|
}
|
|
private AutoTuneAgentService.AutoTuneAgentResult failedAgentResult(String triggerType, Exception exception) {
|
AutoTuneControlModeSnapshot controlMode = autoTuneControlModeService.currentMode();
|
AutoTuneAgentService.AutoTuneAgentResult result = new AutoTuneAgentService.AutoTuneAgentResult();
|
result.setSuccess(false);
|
result.setTriggerType(triggerType);
|
result.setSummary("自动调参后台任务执行异常: " + exception.getMessage());
|
result.setToolCallCount(0);
|
result.setLlmCallCount(0);
|
result.setPromptTokens(0L);
|
result.setCompletionTokens(0L);
|
result.setTotalTokens(0L);
|
result.setMaxRoundsReached(false);
|
result.setAnalysisOnly(controlMode.getAnalysisOnly());
|
result.setAllowApply(controlMode.getAllowApply());
|
result.setExecutionMode(controlMode.getModeCode());
|
result.setActualApplyCalled(false);
|
result.setRollbackCalled(false);
|
result.setSuccessCount(0);
|
result.setRejectCount(0);
|
result.setMcpCalls(new ArrayList<>());
|
return result;
|
}
|
|
private void safeWriteAgentRunAudit(String triggerType,
|
Date startTime,
|
AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
try {
|
writeAgentRunAudit(triggerType, startTime, agentResult);
|
} catch (Exception exception) {
|
log.warn("Auto tune coordinator failed to write agent run audit", exception);
|
}
|
}
|
|
private void writeAgentRunAudit(String triggerType,
|
Date startTime,
|
AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
if (agentResult == null) {
|
return;
|
}
|
Date finishTime = new Date();
|
Integer intervalMinutes = resolveIntervalMinutes();
|
AiAutoTuneJob job = new AiAutoTuneJob();
|
job.setTriggerType(AutoTuneTriggerType.normalize(triggerType));
|
job.setStatus(resolveAuditStatus(agentResult));
|
job.setStartTime(startTime == null ? finishTime : startTime);
|
job.setFinishTime(finishTime);
|
job.setHasActiveTasks(resolveHasActiveTasksForAudit());
|
job.setPromptSceneCode(AiPromptScene.AUTO_TUNE_DISPATCH.getCode());
|
job.setSummary(agentResult.getSummary());
|
job.setIntervalBefore(intervalMinutes);
|
job.setIntervalAfter(intervalMinutes);
|
job.setSuccessCount(safeCount(agentResult.getSuccessCount()));
|
job.setRejectCount(safeCount(agentResult.getRejectCount()));
|
if (isErrorAuditStatus(job.getStatus())) {
|
job.setErrorMessage(agentResult.getSummary());
|
}
|
job.setLlmCallCount(agentResult.getLlmCallCount() == null ? 0 : agentResult.getLlmCallCount());
|
job.setPromptTokens(toSafeInteger(agentResult.getPromptTokens()));
|
job.setCompletionTokens(toSafeInteger(agentResult.getCompletionTokens()));
|
job.setTotalTokens(toSafeInteger(agentResult.getTotalTokens()));
|
job.setCreateTime(job.getStartTime());
|
if (!aiAutoTuneJobService.save(job)) {
|
log.warn("Auto tune coordinator failed to save agent run audit");
|
return;
|
}
|
safeWriteMcpCallAudit(job.getId(), agentResult);
|
}
|
|
private void safeWriteMcpCallAudit(Long agentJobId, AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
try {
|
writeMcpCallAudit(agentJobId, agentResult);
|
} catch (Exception exception) {
|
log.warn("Auto tune coordinator failed to write MCP call audit", exception);
|
}
|
}
|
|
private void writeMcpCallAudit(Long agentJobId, AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
if (agentJobId == null || agentResult == null || agentResult.getMcpCalls() == null
|
|| agentResult.getMcpCalls().isEmpty()) {
|
return;
|
}
|
List<AiAutoTuneMcpCall> mcpCalls = new ArrayList<>();
|
Date createTime = new Date();
|
for (AutoTuneAgentService.McpCallResult callResult : agentResult.getMcpCalls()) {
|
mcpCalls.add(toMcpCallEntity(agentJobId, callResult, createTime));
|
}
|
if (!aiAutoTuneMcpCallService.saveBatch(mcpCalls)) {
|
log.warn("Auto tune coordinator failed to save MCP call audit");
|
}
|
}
|
|
private AiAutoTuneMcpCall toMcpCallEntity(Long agentJobId,
|
AutoTuneAgentService.McpCallResult callResult,
|
Date createTime) {
|
AiAutoTuneMcpCall mcpCall = new AiAutoTuneMcpCall();
|
mcpCall.setAgentJobId(agentJobId);
|
mcpCall.setCallSeq(callResult.getCallSeq());
|
mcpCall.setToolName(callResult.getToolName());
|
mcpCall.setStatus(callResult.getStatus());
|
mcpCall.setDryRun(toTinyInt(callResult.getDryRun()));
|
mcpCall.setApplyJobId(callResult.getApplyJobId());
|
mcpCall.setSuccessCount(safeCount(callResult.getSuccessCount()));
|
mcpCall.setRejectCount(safeCount(callResult.getRejectCount()));
|
mcpCall.setDurationMs(callResult.getDurationMs());
|
mcpCall.setRequestJson(callResult.getRequestJson());
|
mcpCall.setResponseJson(callResult.getResponseJson());
|
mcpCall.setErrorMessage(callResult.getErrorMessage());
|
mcpCall.setCreateTime(createTime);
|
return mcpCall;
|
}
|
|
private Integer toTinyInt(Boolean value) {
|
if (value == null) {
|
return null;
|
}
|
return value ? 1 : 0;
|
}
|
|
private String resolveAuditStatus(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
int successCount = safeCount(agentResult.getSuccessCount());
|
int rejectCount = safeCount(agentResult.getRejectCount());
|
if (!Boolean.TRUE.equals(agentResult.getSuccess())) {
|
if (successCount > 0 && rejectCount > 0) {
|
return AutoTuneJobStatus.PARTIAL_SUCCESS.getCode();
|
}
|
if (rejectCount > 0) {
|
return AutoTuneJobStatus.REJECTED.getCode();
|
}
|
return AutoTuneJobStatus.FAILED.getCode();
|
}
|
if (successCount > 0 && rejectCount > 0) {
|
return AutoTuneJobStatus.PARTIAL_SUCCESS.getCode();
|
}
|
if (rejectCount > 0) {
|
return AutoTuneJobStatus.REJECTED.getCode();
|
}
|
if (Boolean.TRUE.equals(agentResult.getActualApplyCalled())
|
|| Boolean.TRUE.equals(agentResult.getRollbackCalled())) {
|
return AutoTuneJobStatus.SUCCESS.getCode();
|
}
|
return AutoTuneJobStatus.NO_CHANGE.getCode();
|
}
|
|
private boolean isErrorAuditStatus(String status) {
|
return AutoTuneJobStatus.FAILED.getCode().equals(status)
|
|| AutoTuneJobStatus.REJECTED.getCode().equals(status)
|
|| AutoTuneJobStatus.PARTIAL_SUCCESS.getCode().equals(status);
|
}
|
|
private int safeCount(Integer value) {
|
return value == null ? 0 : value;
|
}
|
|
private int resolveHasActiveTasksForAudit() {
|
try {
|
return hasActiveTasks() ? 1 : 0;
|
} catch (RuntimeException exception) {
|
log.warn("Auto tune coordinator failed to query active tasks for audit", exception);
|
return 0;
|
}
|
}
|
|
private Integer toSafeInteger(Long value) {
|
if (value == null) {
|
return 0;
|
}
|
if (value > Integer.MAX_VALUE) {
|
return Integer.MAX_VALUE;
|
}
|
if (value < Integer.MIN_VALUE) {
|
return Integer.MIN_VALUE;
|
}
|
return value.intValue();
|
}
|
|
private void safeWriteOperateLog(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
try {
|
writeOperateLog(agentResult);
|
} catch (Exception exception) {
|
log.warn("Auto tune coordinator failed to write operate log", exception);
|
}
|
}
|
|
private void writeOperateLog(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
if (agentResult == null) {
|
return;
|
}
|
OperateLog operateLog = new OperateLog();
|
operateLog.setAction(resolveOperateLogAction(agentResult));
|
operateLog.setUserId(SYSTEM_USER_ID);
|
operateLog.setIp("system");
|
operateLog.setRequest(JSON.toJSONString(buildRequestSummary(agentResult)));
|
operateLog.setResponse(JSON.toJSONString(buildResponseSummary(agentResult)));
|
operateLog.setCreateTime(new Date());
|
operateLogService.save(operateLog);
|
}
|
|
private String resolveOperateLogAction(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
if (agentResult != null && AutoTuneTriggerType.MANUAL.getCode().equals(agentResult.getTriggerType())) {
|
return "ai_auto_tune_manual_trigger";
|
}
|
return "ai_auto_tune_background_scheduler";
|
}
|
|
private Map<String, Object> buildRequestSummary(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
Map<String, Object> request = new LinkedHashMap<>();
|
request.put("trigger", agentResult.getTriggerType());
|
request.put("analysisOnly", agentResult.getAnalysisOnly());
|
request.put("allowApply", agentResult.getAllowApply());
|
request.put("executionMode", agentResult.getExecutionMode());
|
return request;
|
}
|
|
private Map<String, Object> buildResponseSummary(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
Map<String, Object> response = new LinkedHashMap<>();
|
response.put("success", agentResult.getSuccess());
|
response.put("summary", agentResult.getSummary());
|
response.put("analysisOnly", agentResult.getAnalysisOnly());
|
response.put("allowApply", agentResult.getAllowApply());
|
response.put("executionMode", agentResult.getExecutionMode());
|
response.put("toolCallCount", agentResult.getToolCallCount());
|
response.put("llmCallCount", agentResult.getLlmCallCount());
|
response.put("promptTokens", agentResult.getPromptTokens());
|
response.put("completionTokens", agentResult.getCompletionTokens());
|
response.put("totalTokens", agentResult.getTotalTokens());
|
response.put("maxRoundsReached", agentResult.getMaxRoundsReached());
|
response.put("actualApplyCalled", agentResult.getActualApplyCalled());
|
response.put("rollbackCalled", agentResult.getRollbackCalled());
|
response.put("successCount", agentResult.getSuccessCount());
|
response.put("rejectCount", agentResult.getRejectCount());
|
response.put("mcpCallCount", agentResult.getMcpCalls() == null ? 0 : agentResult.getMcpCalls().size());
|
return response;
|
}
|
}
|