#
Junjie
2026-04-27 b83bc2ee89d5826b3ab5fe42ac3af5972360b55c
#
2个文件已添加
7个文件已修改
141 ■■■■■ 已修改文件
src/main/java/com/zy/ai/domain/autotune/AutoTuneRuleDefinition.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/ai/domain/autotune/AutoTuneRuleSnapshotItem.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/ai/domain/autotune/AutoTuneSnapshot.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/ai/mcp/tool/AutoTuneMcpTools.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/ai/service/impl/AutoTuneSnapshotServiceImpl.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/ai/utils/AiPromptUtils.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260427_update_auto_tune_prompt_rule_snapshot.sql 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/ai/auto_tune.html 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/zy/ai/service/impl/AutoTuneSnapshotServiceImplTest.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/ai/domain/autotune/AutoTuneRuleDefinition.java
@@ -26,12 +26,12 @@
        LinkedHashMap<String, Rule> ruleMap = new LinkedHashMap<>();
        add(ruleMap, AutoTuneTargetType.SYS_CONFIG, "aiAutoTuneIntervalMinutes", 5, 60, 5, 30, false);
        add(ruleMap, AutoTuneTargetType.SYS_CONFIG, "conveyorStationTaskLimit", 5, 200, 5, 20, false);
        add(ruleMap, AutoTuneTargetType.SYS_CONFIG, "crnOutBatchRunningLimit", 1, 20, 2, 20, false);
        add(ruleMap, AutoTuneTargetType.STATION, "outTaskLimit", 0, null, 1, 10, true);
        add(ruleMap, AutoTuneTargetType.SYS_CONFIG, "crnOutBatchRunningLimit", 1, 20, 3, 20, false);
        add(ruleMap, AutoTuneTargetType.STATION, "outTaskLimit", 0, null, 3, 10, true);
        add(ruleMap, AutoTuneTargetType.CRN, "maxOutTask", 0, 10, 3, 10, false);
        add(ruleMap, AutoTuneTargetType.CRN, "maxInTask", 0, 10, 1, 10, false);
        add(ruleMap, AutoTuneTargetType.CRN, "maxInTask", 0, 10, 3, 10, false);
        add(ruleMap, AutoTuneTargetType.DUAL_CRN, "maxOutTask", 0, 10, 3, 10, false);
        add(ruleMap, AutoTuneTargetType.DUAL_CRN, "maxInTask", 0, 10, 1, 10, false);
        add(ruleMap, AutoTuneTargetType.DUAL_CRN, "maxInTask", 0, 10, 3, 10, false);
        return Collections.unmodifiableMap(ruleMap);
    }
src/main/java/com/zy/ai/domain/autotune/AutoTuneRuleSnapshotItem.java
New file
@@ -0,0 +1,28 @@
package com.zy.ai.domain.autotune;
import lombok.Data;
import java.io.Serializable;
@Data
public class AutoTuneRuleSnapshotItem implements Serializable {
    private static final long serialVersionUID = 1L;
    private String targetType;
    private String targetKey;
    private Integer minValue;
    private Integer maxValue;
    private Integer maxStep;
    private Integer cooldownMinutes;
    private Boolean dynamicMaxValue;
    private String dynamicMaxSource;
    private String note;
}
src/main/java/com/zy/ai/domain/autotune/AutoTuneSnapshot.java
@@ -21,5 +21,7 @@
    private AutoTuneParameterSnapshot currentParameterSnapshot;
    private List<AutoTuneRuleSnapshotItem> ruleSnapshot;
    private Date snapshotTime;
}
src/main/java/com/zy/ai/mcp/tool/AutoTuneMcpTools.java
@@ -43,7 +43,7 @@
    private final ConcurrentMap<String, DryRunPreview> dryRunPreviews = new ConcurrentHashMap<>();
    private LongSupplier currentTimeMillisSupplier = System::currentTimeMillis;
    @Tool(name = "dispatch_get_auto_tune_snapshot", description = "获取WCS自动调参所需的调度快照、站点运行态、拓扑容量和当前可写参数")
    @Tool(name = "dispatch_get_auto_tune_snapshot", description = "获取WCS自动调参所需的调度快照、站点运行态、拓扑容量、当前可写参数和调参规则约束")
    public AutoTuneSnapshot getAutoTuneSnapshot() {
        return autoTuneSnapshotService.buildSnapshot();
    }
src/main/java/com/zy/ai/service/impl/AutoTuneSnapshotServiceImpl.java
@@ -2,8 +2,11 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zy.ai.domain.autotune.AutoTuneParameterSnapshot;
import com.zy.ai.domain.autotune.AutoTuneRuleDefinition;
import com.zy.ai.domain.autotune.AutoTuneRuleSnapshotItem;
import com.zy.ai.domain.autotune.AutoTuneSnapshot;
import com.zy.ai.domain.autotune.AutoTuneStationRuntimeItem;
import com.zy.ai.domain.autotune.AutoTuneTargetType;
import com.zy.ai.domain.autotune.AutoTuneTaskSnapshot;
import com.zy.ai.service.AutoTuneSnapshotService;
import com.zy.ai.service.FlowTopologySnapshotService;
@@ -86,10 +89,47 @@
        snapshot.setCycleLoadSnapshot(buildCycleLoadSnapshot());
        snapshot.setFlowTopologySnapshot(flowTopologySnapshotService.buildSnapshot(stationRuntimeSnapshot));
        snapshot.setCurrentParameterSnapshot(buildCurrentParameterSnapshot());
        snapshot.setRuleSnapshot(buildRuleSnapshot());
        snapshot.setSnapshotTime(new Date());
        return snapshot;
    }
    List<AutoTuneRuleSnapshotItem> buildRuleSnapshot() {
        List<AutoTuneRuleSnapshotItem> result = new ArrayList<>();
        for (AutoTuneRuleDefinition.Rule rule : AutoTuneRuleDefinition.rules().values()) {
            AutoTuneRuleSnapshotItem item = new AutoTuneRuleSnapshotItem();
            item.setTargetType(rule.getTargetType().getCode());
            item.setTargetKey(rule.getTargetKey());
            item.setMinValue(rule.getMinValue());
            item.setMaxValue(rule.getMaxValue());
            item.setMaxStep(rule.getMaxStep());
            item.setCooldownMinutes(rule.getCooldownMinutes());
            item.setDynamicMaxValue(rule.isDynamicMaxValue());
            item.setDynamicMaxSource(resolveDynamicMaxSource(rule));
            item.setNote(resolveRuleNote(rule));
            result.add(item);
        }
        return result;
    }
    private String resolveDynamicMaxSource(AutoTuneRuleDefinition.Rule rule) {
        if (!AutoTuneTargetType.STATION.equals(rule.getTargetType())) {
            return null;
        }
        if (!"outTaskLimit".equals(rule.getTargetKey())) {
            return null;
        }
        return "currentParameterSnapshot.stationOutBufferCapacities[targetId]";
    }
    private String resolveRuleNote(AutoTuneRuleDefinition.Rule rule) {
        if (AutoTuneTargetType.STATION.equals(rule.getTargetType())
                && "outTaskLimit".equals(rule.getTargetKey())) {
            return "单次调整幅度不能超过 maxStep;增大时不得超过对应站点 outBufferCapacity。";
        }
        return "单次调整幅度不能超过 maxStep。";
    }
    private AutoTuneTaskSnapshot buildTaskSnapshot() {
        List<WrkMast> activeTasks = loadActiveTasks();
src/main/java/com/zy/ai/utils/AiPromptUtils.java
@@ -158,11 +158,14 @@
                            "- maxOutTask:对应 asr_bas_crnp.max_out_task / asr_bas_dual_crnp.max_out_task\n" +
                            "- maxInTask:对应 asr_bas_crnp.max_in_task / asr_bas_dual_crnp.max_in_task\n\n" +
                            "注意:asr_bas_station.out_buffer_capacity 是人工维护的出库缓存容量,只用于证明 outTaskLimit 可上调上限,Agent 不允许修改该字段;增大 outTaskLimit 时建议值不得超过对应站点 outBufferCapacity。\n\n" +
                            "Step 4 提交变更\n" +
                            "Step 4 读取调参规则\n" +
                            "- 必须读取 snapshot.ruleSnapshot 中的 minValue、maxValue、maxStep、cooldownMinutes、dynamicMaxValue 和 dynamicMaxSource。\n" +
                            "- 每个目标参数的新值与当前值差值不能超过对应 maxStep;outTaskLimit 的上调还必须受 stationOutBufferCapacities[targetId] 约束。\n\n" +
                            "Step 5 提交变更\n" +
                            "- 先通过 wcs_local_dispatch_apply_auto_tune_changes 执行 dry-run。\n" +
                            "- dry-run 通过后才允许通过同一工具实际应用。\n" +
                            "- 如果工具返回拒绝、冷却中、存在活动任务风险或参数不在白名单内,必须停止实际应用。\n\n" +
                            "Step 5 回滚边界\n" +
                            "Step 6 回滚边界\n" +
                            "- 只有当最近一次自动调参被 MCP facts 明确证明造成异常,才允许调用 wcs_local_dispatch_revert_last_auto_tune_job。\n" +
                            "- 不得臆测回滚原因。");
            return blocks;
src/main/resources/sql/20260427_update_auto_tune_prompt_rule_snapshot.sql
New file
@@ -0,0 +1,9 @@
UPDATE sys_ai_prompt_block block
JOIN sys_ai_prompt_template template ON template.id = block.template_id
SET block.content = CONCAT(
    block.content,
    '\n补充:读取调参规则\n- 必须读取 snapshot.ruleSnapshot 中的 minValue、maxValue、maxStep、cooldownMinutes、dynamicMaxValue 和 dynamicMaxSource。\n- 每个目标参数的新值与当前值差值不能超过对应 maxStep;outTaskLimit 的上调还必须受 stationOutBufferCapacities[targetId] 约束。'
)
WHERE template.scene_code = 'wcs_auto_tune_dispatch'
  AND block.block_type = 'scene_playbook'
  AND block.content NOT LIKE '%snapshot.ruleSnapshot%';
src/main/webapp/views/ai/auto_tune.html
@@ -371,6 +371,15 @@
                <span class="small-muted" v-if="mapEntries(taskSnapshot.byTargetStation).length === 0 && mapEntries(taskSnapshot.byBatch).length === 0">暂无活动任务</span>
              </div>
            </div>
            <div class="map-box">
              <div class="map-title">调参规则 maxStep/cooldown</div>
              <div class="pill-row">
                <span class="kv-pill" v-for="item in ruleSnapshot" :key="'r_' + item.targetType + '_' + item.targetKey">
                  {{ item.targetType }}/{{ item.targetKey }}: 步{{ item.maxStep }} / 冷{{ item.cooldownMinutes }}m
                </span>
                <span class="small-muted" v-if="ruleSnapshot.length === 0">暂无数据</span>
              </div>
            </div>
          </div>
        </div>
      </div>
@@ -556,6 +565,9 @@
      cycleLoad: function() {
        return this.snapshot.cycleLoadSnapshot || {};
      },
      ruleSnapshot: function() {
        return this.snapshot.ruleSnapshot || [];
      },
      stationBusyCount: function() {
        var count = 0;
        for (var stationIndex = 0; stationIndex < this.stationRuntime.length; stationIndex++) {
src/test/java/com/zy/ai/service/impl/AutoTuneSnapshotServiceImplTest.java
@@ -2,6 +2,7 @@
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.zy.ai.domain.autotune.AutoTuneRuleSnapshotItem;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.BasStation;
import com.zy.asrs.entity.WrkMast;
@@ -16,6 +17,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -62,6 +64,26 @@
        assertNull(result.get("101"));
        assertEquals(0, result.get("102"));
        assertEquals(3, result.get("103"));
    }
    @Test
    void buildRuleSnapshotExposesStepRangeCooldownAndDynamicMaxSource() {
        List<AutoTuneRuleSnapshotItem> result = service.buildRuleSnapshot();
        AutoTuneRuleSnapshotItem stationOutTaskRule = findRule(result, "station", "outTaskLimit");
        assertEquals(0, stationOutTaskRule.getMinValue());
        assertNull(stationOutTaskRule.getMaxValue());
        assertEquals(1, stationOutTaskRule.getMaxStep());
        assertEquals(10, stationOutTaskRule.getCooldownMinutes());
        assertEquals(Boolean.TRUE, stationOutTaskRule.getDynamicMaxValue());
        assertEquals("currentParameterSnapshot.stationOutBufferCapacities[targetId]",
                stationOutTaskRule.getDynamicMaxSource());
        AutoTuneRuleSnapshotItem crnMaxOutRule = findRule(result, "crn", "maxOutTask");
        assertEquals(3, crnMaxOutRule.getMaxStep());
        AutoTuneRuleSnapshotItem crnMaxInRule = findRule(result, "crn", "maxInTask");
        assertEquals(1, crnMaxInRule.getMaxStep());
    }
    @Test
@@ -154,4 +176,15 @@
        stationObjModel.setStationId(stationId);
        return stationObjModel;
    }
    private AutoTuneRuleSnapshotItem findRule(List<AutoTuneRuleSnapshotItem> rules,
                                              String targetType,
                                              String targetKey) {
        for (AutoTuneRuleSnapshotItem rule : rules) {
            if (targetType.equals(rule.getTargetType()) && targetKey.equals(rule.getTargetKey())) {
                return rule;
            }
        }
        throw new AssertionError("rule not found: " + targetType + "/" + targetKey);
    }
}