| | |
| | | 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); |
| | | } |
| | | |
| New file |
| | |
| | | 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; |
| | | } |
| | |
| | | |
| | | private AutoTuneParameterSnapshot currentParameterSnapshot; |
| | | |
| | | private List<AutoTuneRuleSnapshotItem> ruleSnapshot; |
| | | |
| | | private Date snapshotTime; |
| | | } |
| | |
| | | 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(); |
| | | } |
| | |
| | | |
| | | 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; |
| | |
| | | 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(); |
| | | |
| | |
| | | "- 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; |
| New file |
| | |
| | | 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%'; |
| | |
| | | <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> |
| | |
| | | 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++) { |
| | |
| | | |
| | | 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; |
| | |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | |
| | | 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 |
| | |
| | | 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); |
| | | } |
| | | } |