Junjie
2026-04-27 06602ee7fbcbb43c078d8ebb0029399c79424307
fix: audit auto tune agent runs
2个文件已修改
104 ■■■■■ 已修改文件
src/main/java/com/zy/ai/service/impl/AutoTuneCoordinatorServiceImpl.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/zy/ai/service/AutoTuneCoordinatorServiceImplTest.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/ai/service/impl/AutoTuneCoordinatorServiceImpl.java
@@ -5,6 +5,7 @@
import com.zy.ai.domain.autotune.AutoTuneJobStatus;
import com.zy.ai.domain.autotune.AutoTuneTriggerType;
import com.zy.ai.entity.AiAutoTuneJob;
import com.zy.ai.enums.AiPromptScene;
import com.zy.ai.service.AiAutoTuneJobService;
import com.zy.ai.service.AutoTuneAgentService;
import com.zy.ai.service.AutoTuneCoordinatorService;
@@ -40,6 +41,8 @@
    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 int SUMMARY_MAX_LENGTH = 512;
    private static final int ERROR_MESSAGE_MAX_LENGTH = 1024;
    private static final List<Long> FINAL_WRK_STS_LIST = Arrays.asList(
            WrkStsType.COMPLETE_INBOUND.sts,
            WrkStsType.SETTLE_INBOUND.sts,
@@ -91,17 +94,20 @@
        }
        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);
@@ -198,6 +204,80 @@
        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(Boolean.TRUE.equals(agentResult.getSuccess())
                ? AutoTuneJobStatus.SUCCESS.getCode()
                : AutoTuneJobStatus.FAILED.getCode());
        job.setStartTime(startTime == null ? finishTime : startTime);
        job.setFinishTime(finishTime);
        job.setHasActiveTasks(resolveHasActiveTasksForAudit());
        job.setPromptSceneCode(AiPromptScene.AUTO_TUNE_DISPATCH.getCode());
        job.setSummary(limitText(agentResult.getSummary(), SUMMARY_MAX_LENGTH));
        job.setIntervalBefore(intervalMinutes);
        job.setIntervalAfter(intervalMinutes);
        job.setSuccessCount(0);
        job.setRejectCount(0);
        if (!Boolean.TRUE.equals(agentResult.getSuccess())) {
            job.setErrorMessage(limitText(agentResult.getSummary(), ERROR_MESSAGE_MAX_LENGTH));
        }
        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");
        }
    }
    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 String limitText(String value, int maxLength) {
        if (value == null || value.length() <= maxLength) {
            return value;
        }
        return value.substring(0, maxLength);
    }
    private void safeWriteOperateLog(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
        try {
            writeOperateLog(agentResult);
src/test/java/com/zy/ai/service/AutoTuneCoordinatorServiceImplTest.java
@@ -6,12 +6,14 @@
import com.zy.ai.domain.autotune.AutoTuneApplyResult;
import com.zy.ai.domain.autotune.AutoTuneChangeCommand;
import com.zy.ai.domain.autotune.AutoTuneSnapshot;
import com.zy.ai.domain.autotune.AutoTuneJobStatus;
import com.zy.ai.domain.autotune.AutoTuneTriggerType;
import com.zy.ai.entity.AiAutoTuneChange;
import com.zy.ai.entity.AiAutoTuneJob;
import com.zy.ai.entity.AiPromptTemplate;
import com.zy.ai.entity.ChatCompletionRequest;
import com.zy.ai.entity.ChatCompletionResponse;
import com.zy.ai.enums.AiPromptScene;
import com.zy.ai.mcp.service.SpringAiMcpToolManager;
import com.zy.ai.mcp.tool.AutoTuneMcpTools;
import com.zy.ai.service.impl.AutoTuneAgentServiceImpl;
@@ -426,6 +428,8 @@
    void manualTriggerRunsAgentWithRunningLockAndDoesNotWriteSchedulerGuard() {
        AutoTuneAgentService.AutoTuneAgentResult agentResult = successfulAgentResult();
        agentResult.setTriggerType(AutoTuneTriggerType.MANUAL.getCode());
        when(configService.getConfigValue("aiAutoTuneIntervalMinutes", "10")).thenReturn("10");
        when(wrkMastService.count(any(Wrapper.class))).thenReturn(1L);
        when(redisUtil.trySetStringIfAbsent(anyString(), anyString(), anyLong())).thenReturn(true);
        when(autoTuneAgentService.runAutoTune(AutoTuneTriggerType.MANUAL.getCode())).thenReturn(agentResult);
@@ -435,13 +439,31 @@
        assertTrue(result.getTriggered());
        assertSame(agentResult, result.getAgentResult());
        verify(autoTuneAgentService).runAutoTune(AutoTuneTriggerType.MANUAL.getCode());
        verify(wrkMastService, never()).count(any(Wrapper.class));
        verify(redisUtil, never()).set(eq(RedisKeyType.AI_AUTO_TUNE_LAST_TRIGGER_GUARD.key), any(), anyLong());
        verify(redisUtil).compareAndDelete(anyString(), anyString());
        ArgumentCaptor<OperateLog> operateLogCaptor = ArgumentCaptor.forClass(OperateLog.class);
        verify(operateLogService).save(operateLogCaptor.capture());
        assertEquals("ai_auto_tune_manual_trigger", operateLogCaptor.getValue().getAction());
        ArgumentCaptor<AiAutoTuneJob> jobCaptor = ArgumentCaptor.forClass(AiAutoTuneJob.class);
        verify(aiAutoTuneJobService).save(jobCaptor.capture());
        AiAutoTuneJob auditJob = jobCaptor.getValue();
        assertEquals(AutoTuneTriggerType.MANUAL.getCode(), auditJob.getTriggerType());
        assertEquals(AutoTuneJobStatus.SUCCESS.getCode(), auditJob.getStatus());
        assertNotNull(auditJob.getStartTime());
        assertNotNull(auditJob.getFinishTime());
        assertEquals(1, auditJob.getHasActiveTasks());
        assertEquals(AiPromptScene.AUTO_TUNE_DISPATCH.getCode(), auditJob.getPromptSceneCode());
        assertEquals("no changes needed", auditJob.getSummary());
        assertEquals(10, auditJob.getIntervalBefore());
        assertEquals(10, auditJob.getIntervalAfter());
        assertEquals(0, auditJob.getSuccessCount());
        assertEquals(0, auditJob.getRejectCount());
        assertEquals(1, auditJob.getLlmCallCount());
        assertEquals(10, auditJob.getPromptTokens());
        assertEquals(5, auditJob.getCompletionTokens());
        assertEquals(15, auditJob.getTotalTokens());
    }
    @Test