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 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 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 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 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 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 buildRequestSummary(AutoTuneAgentService.AutoTuneAgentResult agentResult) { Map 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 buildResponseSummary(AutoTuneAgentService.AutoTuneAgentResult agentResult) { Map 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; } }