| | |
| | | |
| | | public final class AutoTuneRuleDefinition { |
| | | |
| | | private static final String DEFAULT_RULE_NOTE = "单次调整幅度不能超过 maxStep。"; |
| | | private static final String STATION_OUT_TASK_LIMIT_DYNAMIC_MAX_SOURCE = |
| | | "currentParameterSnapshot.stationOutBufferCapacities[targetId]"; |
| | | private static final String STATION_OUT_TASK_LIMIT_NOTE = |
| | | "单次调整幅度不能超过 maxStep;增大时不得超过对应站点 outBufferCapacity。"; |
| | | |
| | | private static final Map<String, Rule> RULE_MAP = buildRuleMap(); |
| | | |
| | | private AutoTuneRuleDefinition() { |
| | |
| | | |
| | | private static Map<String, Rule> buildRuleMap() { |
| | | 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, 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, 3, 10, false); |
| | | add(ruleMap, AutoTuneTargetType.DUAL_CRN, "maxOutTask", 0, 10, 3, 10, false); |
| | | add(ruleMap, AutoTuneTargetType.DUAL_CRN, "maxInTask", 0, 10, 3, 10, false); |
| | | add(ruleMap, rule(AutoTuneTargetType.SYS_CONFIG, "aiAutoTuneIntervalMinutes") |
| | | .minValue(5) |
| | | .maxValue(60) |
| | | .maxStep(5) |
| | | .cooldownMinutes(30)); |
| | | add(ruleMap, rule(AutoTuneTargetType.SYS_CONFIG, "conveyorStationTaskLimit") |
| | | .minValue(5) |
| | | .maxValue(200) |
| | | .maxStep(5) |
| | | .cooldownMinutes(20)); |
| | | add(ruleMap, rule(AutoTuneTargetType.SYS_CONFIG, "crnOutBatchRunningLimit") |
| | | .minValue(1) |
| | | .maxValue(20) |
| | | .maxStep(3) |
| | | .cooldownMinutes(20)); |
| | | add(ruleMap, rule(AutoTuneTargetType.STATION, "outTaskLimit") |
| | | .minValue(0) |
| | | .maxStep(3) |
| | | .cooldownMinutes(10) |
| | | .dynamicMaxSource(STATION_OUT_TASK_LIMIT_DYNAMIC_MAX_SOURCE) |
| | | .note(STATION_OUT_TASK_LIMIT_NOTE)); |
| | | add(ruleMap, rule(AutoTuneTargetType.CRN, "maxOutTask") |
| | | .minValue(0) |
| | | .maxValue(10) |
| | | .maxStep(3) |
| | | .cooldownMinutes(10)); |
| | | add(ruleMap, rule(AutoTuneTargetType.CRN, "maxInTask") |
| | | .minValue(0) |
| | | .maxValue(10) |
| | | .maxStep(3) |
| | | .cooldownMinutes(10)); |
| | | add(ruleMap, rule(AutoTuneTargetType.DUAL_CRN, "maxOutTask") |
| | | .minValue(0) |
| | | .maxValue(10) |
| | | .maxStep(3) |
| | | .cooldownMinutes(10)); |
| | | add(ruleMap, rule(AutoTuneTargetType.DUAL_CRN, "maxInTask") |
| | | .minValue(0) |
| | | .maxValue(10) |
| | | .maxStep(3) |
| | | .cooldownMinutes(10)); |
| | | |
| | | return Collections.unmodifiableMap(ruleMap); |
| | | } |
| | | |
| | | private static void add(LinkedHashMap<String, Rule> ruleMap, |
| | | AutoTuneTargetType targetType, |
| | | String targetKey, |
| | | Integer minValue, |
| | | Integer maxValue, |
| | | int maxStep, |
| | | int cooldownMinutes, |
| | | boolean dynamicMaxValue) { |
| | | Rule rule = new Rule(targetType, targetKey, minValue, maxValue, maxStep, cooldownMinutes, dynamicMaxValue); |
| | | ruleMap.put(buildKey(targetType.getCode(), targetKey), rule); |
| | | private static RuleSpec rule(AutoTuneTargetType targetType, String targetKey) { |
| | | return new RuleSpec(targetType, targetKey); |
| | | } |
| | | |
| | | private static void add(LinkedHashMap<String, Rule> ruleMap, RuleSpec ruleSpec) { |
| | | Rule rule = ruleSpec.toRule(); |
| | | ruleMap.put(buildKey(rule.getTargetType().getCode(), rule.getTargetKey()), rule); |
| | | } |
| | | |
| | | private static String buildKey(String targetType, String targetKey) { |
| | |
| | | private final int maxStep; |
| | | private final int cooldownMinutes; |
| | | private final boolean dynamicMaxValue; |
| | | private final String dynamicMaxSource; |
| | | private final String note; |
| | | |
| | | private Rule(AutoTuneTargetType targetType, |
| | | String targetKey, |
| | |
| | | Integer maxValue, |
| | | int maxStep, |
| | | int cooldownMinutes, |
| | | boolean dynamicMaxValue) { |
| | | boolean dynamicMaxValue, |
| | | String dynamicMaxSource, |
| | | String note) { |
| | | this.targetType = targetType; |
| | | this.targetKey = targetKey; |
| | | this.minValue = minValue; |
| | |
| | | this.maxStep = maxStep; |
| | | this.cooldownMinutes = cooldownMinutes; |
| | | this.dynamicMaxValue = dynamicMaxValue; |
| | | this.dynamicMaxSource = dynamicMaxSource; |
| | | this.note = note; |
| | | } |
| | | |
| | | public AutoTuneTargetType getTargetType() { |
| | |
| | | public boolean isDynamicMaxValue() { |
| | | return dynamicMaxValue; |
| | | } |
| | | |
| | | public String getDynamicMaxSource() { |
| | | return dynamicMaxSource; |
| | | } |
| | | |
| | | public String getNote() { |
| | | return note; |
| | | } |
| | | } |
| | | |
| | | private static final class RuleSpec { |
| | | private final AutoTuneTargetType targetType; |
| | | private final String targetKey; |
| | | private Integer minValue; |
| | | private Integer maxValue; |
| | | private int maxStep; |
| | | private int cooldownMinutes; |
| | | private String dynamicMaxSource; |
| | | private String note = DEFAULT_RULE_NOTE; |
| | | |
| | | private RuleSpec(AutoTuneTargetType targetType, String targetKey) { |
| | | this.targetType = targetType; |
| | | this.targetKey = targetKey; |
| | | } |
| | | |
| | | private RuleSpec minValue(Integer minValue) { |
| | | this.minValue = minValue; |
| | | return this; |
| | | } |
| | | |
| | | private RuleSpec maxValue(Integer maxValue) { |
| | | this.maxValue = maxValue; |
| | | return this; |
| | | } |
| | | |
| | | private RuleSpec maxStep(int maxStep) { |
| | | this.maxStep = maxStep; |
| | | return this; |
| | | } |
| | | |
| | | private RuleSpec cooldownMinutes(int cooldownMinutes) { |
| | | this.cooldownMinutes = cooldownMinutes; |
| | | return this; |
| | | } |
| | | |
| | | private RuleSpec dynamicMaxSource(String dynamicMaxSource) { |
| | | this.dynamicMaxSource = dynamicMaxSource; |
| | | return this; |
| | | } |
| | | |
| | | private RuleSpec note(String note) { |
| | | this.note = note; |
| | | return this; |
| | | } |
| | | |
| | | private Rule toRule() { |
| | | return new Rule( |
| | | targetType, |
| | | targetKey, |
| | | minValue, |
| | | maxValue, |
| | | maxStep, |
| | | cooldownMinutes, |
| | | dynamicMaxSource != null, |
| | | dynamicMaxSource, |
| | | note |
| | | ); |
| | | } |
| | | } |
| | | } |
| | |
| | | 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; |
| | |
| | | item.setMaxStep(rule.getMaxStep()); |
| | | item.setCooldownMinutes(rule.getCooldownMinutes()); |
| | | item.setDynamicMaxValue(rule.isDynamicMaxValue()); |
| | | item.setDynamicMaxSource(resolveDynamicMaxSource(rule)); |
| | | item.setNote(resolveRuleNote(rule)); |
| | | item.setDynamicMaxSource(rule.getDynamicMaxSource()); |
| | | item.setNote(rule.getNote()); |
| | | 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() { |
| | |
| | | @Component |
| | | public class AiPromptUtils { |
| | | |
| | | private static final String AUTO_TUNE_RULE_SNAPSHOT_INSTRUCTIONS = |
| | | "Step 4 读取调参规则\n" + |
| | | "- 必须读取 snapshot.ruleSnapshot 中的 minValue、maxValue、maxStep、cooldownMinutes、dynamicMaxValue 和 dynamicMaxSource。\n" + |
| | | "- 每个目标参数的新值与当前值差值不能超过对应 maxStep;outTaskLimit 的上调还必须受 stationOutBufferCapacities[targetId] 约束。"; |
| | | |
| | | public String getDefaultPrompt(String sceneCode) { |
| | | AiPromptScene scene = AiPromptScene.ofCode(sceneCode); |
| | | if (scene == null) { |
| | |
| | | "- 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" + |
| | | "- 必须读取 snapshot.ruleSnapshot 中的 minValue、maxValue、maxStep、cooldownMinutes、dynamicMaxValue 和 dynamicMaxSource。\n" + |
| | | "- 每个目标参数的新值与当前值差值不能超过对应 maxStep;outTaskLimit 的上调还必须受 stationOutBufferCapacities[targetId] 约束。\n\n" + |
| | | AUTO_TUNE_RULE_SNAPSHOT_INSTRUCTIONS + "\n\n" + |
| | | "Step 5 提交变更\n" + |
| | | "- 先通过 wcs_local_dispatch_apply_auto_tune_changes 执行 dry-run。\n" + |
| | | "- dry-run 通过后才允许通过同一工具实际应用。\n" + |
| New file |
| | |
| | | -- AI自动调参合并增量脚本 |
| | | -- 合并范围:2026-04-27 至 2026-04-28 自动调参相关 SQL 变更。 |
| | | -- 执行说明:在目标库中执行本文件一次即可;脚本按幂等方式编写,已存在对象会跳过或更新。 |
| | | -- 不包含已废弃的 asr_station_flow_capacity 建表脚本;如历史库存在该表,会迁移 OUT 容量到 asr_bas_station.out_buffer_capacity 后删除旧表。 |
| | | -- 来源脚本: |
| | | -- - 20260427_create_ai_auto_tune_tables.sql |
| | | -- - 20260427_add_ai_auto_tune_sys_configs.sql |
| | | -- - 20260427_add_out_buffer_capacity_to_asr_bas_station.sql |
| | | -- - 20260427_add_ai_auto_tune_console_menu.sql |
| | | -- - 20260427_update_auto_tune_prompt_out_buffer_capacity.sql |
| | | -- - 20260427_update_auto_tune_prompt_rule_snapshot.sql |
| | | |
| | | -- 1. AI自动调参审计表 |
| | | CREATE TABLE IF NOT EXISTS `sys_ai_auto_tune_job` ( |
| | | `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', |
| | | `trigger_type` VARCHAR(32) NOT NULL COMMENT '触发类型:auto/manual/revert', |
| | | `status` VARCHAR(32) NOT NULL COMMENT '执行状态:running/success/failed/rejected', |
| | | `start_time` DATETIME NOT NULL COMMENT '开始时间', |
| | | `finish_time` DATETIME DEFAULT NULL COMMENT '结束时间', |
| | | `has_active_tasks` TINYINT NOT NULL DEFAULT 0 COMMENT '执行时是否存在活动任务:1是0否', |
| | | `prompt_scene_code` VARCHAR(64) NOT NULL COMMENT 'Prompt场景编码', |
| | | `summary` VARCHAR(512) DEFAULT NULL COMMENT '执行摘要', |
| | | `reasoning_digest` MEDIUMTEXT COMMENT '推理摘要', |
| | | `snapshot_digest` MEDIUMTEXT COMMENT '快照摘要', |
| | | `interval_before` INT DEFAULT NULL COMMENT '调参前自动调参间隔分钟', |
| | | `interval_after` INT DEFAULT NULL COMMENT '调参后自动调参间隔分钟', |
| | | `success_count` INT NOT NULL DEFAULT 0 COMMENT '成功变更数', |
| | | `reject_count` INT NOT NULL DEFAULT 0 COMMENT '拒绝变更数', |
| | | `error_message` VARCHAR(1024) DEFAULT NULL COMMENT '错误信息', |
| | | `llm_call_count` INT NOT NULL DEFAULT 0 COMMENT 'LLM调用次数', |
| | | `prompt_tokens` INT NOT NULL DEFAULT 0 COMMENT 'Prompt tokens', |
| | | `completion_tokens` INT NOT NULL DEFAULT 0 COMMENT 'Completion tokens', |
| | | `total_tokens` INT NOT NULL DEFAULT 0 COMMENT '总tokens', |
| | | `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_sys_ai_auto_tune_job_status` (`status`), |
| | | KEY `idx_sys_ai_auto_tune_job_start_time` (`start_time`), |
| | | KEY `idx_sys_ai_auto_tune_job_finish_time` (`finish_time`), |
| | | KEY `idx_sys_ai_auto_tune_job_create_time` (`create_time`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI自动调参任务审计表'; |
| | | |
| | | CREATE TABLE IF NOT EXISTS `sys_ai_auto_tune_change` ( |
| | | `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', |
| | | `job_id` BIGINT NOT NULL COMMENT '自动调参任务ID', |
| | | `target_type` VARCHAR(64) NOT NULL COMMENT '目标类型:sys_config/station/crnp/dual_crnp', |
| | | `target_id` VARCHAR(64) DEFAULT NULL COMMENT '目标ID', |
| | | `target_key` VARCHAR(128) NOT NULL COMMENT '目标参数键', |
| | | `old_value` VARCHAR(255) DEFAULT NULL COMMENT '原值', |
| | | `requested_value` VARCHAR(255) DEFAULT NULL COMMENT '申请值', |
| | | `applied_value` VARCHAR(255) DEFAULT NULL COMMENT '实际应用值', |
| | | `result_status` VARCHAR(32) NOT NULL COMMENT '结果状态:success/rejected/failed/dry_run', |
| | | `reject_reason` VARCHAR(512) DEFAULT NULL COMMENT '拒绝原因', |
| | | `cooldown_expire_time` DATETIME DEFAULT NULL COMMENT '冷却截止时间', |
| | | `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_sys_ai_auto_tune_change_job_id` (`job_id`), |
| | | KEY `idx_sys_ai_auto_tune_change_result_status` (`result_status`), |
| | | KEY `idx_sys_ai_auto_tune_change_cooldown` (`cooldown_expire_time`), |
| | | KEY `idx_sys_ai_auto_tune_change_create_time` (`create_time`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI自动调参变更审计表'; |
| | | |
| | | -- 2. AI自动调参系统配置 |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT 'AI自动调参开关', 'aiAutoTuneEnabled', 'N', 1, 1, 'system' |
| | | FROM dual |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_config |
| | | WHERE code = 'aiAutoTuneEnabled' |
| | | ); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT 'AI自动调参间隔(分钟)', 'aiAutoTuneIntervalMinutes', '10', 1, 1, 'system' |
| | | FROM dual |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_config |
| | | WHERE code = 'aiAutoTuneIntervalMinutes' |
| | | ); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT 'AI自动调参Prompt日志保留上限', 'aiAutoTunePromptLogLimit', '500', 1, 1, 'system' |
| | | FROM dual |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_config |
| | | WHERE code = 'aiAutoTunePromptLogLimit' |
| | | ); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '输送站出库任务全局上限', 'conveyorStationTaskLimit', '30', 1, 1, 'system' |
| | | FROM dual |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_config |
| | | WHERE code = 'conveyorStationTaskLimit' |
| | | ); |
| | | |
| | | -- 3. asr_bas_station 增加出库缓存容量配置,替代 asr_station_flow_capacity |
| | | SET @current_db := DATABASE(); |
| | | |
| | | SET @out_buffer_capacity_exists := ( |
| | | SELECT COUNT(1) |
| | | FROM information_schema.COLUMNS |
| | | WHERE TABLE_SCHEMA = @current_db |
| | | AND TABLE_NAME = 'asr_bas_station' |
| | | AND COLUMN_NAME = 'out_buffer_capacity' |
| | | ); |
| | | |
| | | SET @add_out_buffer_capacity_sql := IF( |
| | | @out_buffer_capacity_exists = 0, |
| | | 'ALTER TABLE asr_bas_station ADD COLUMN out_buffer_capacity INT NULL COMMENT ''出库缓存容量,用于证明 out_task_limit 上限'' AFTER out_task_limit', |
| | | 'SELECT ''column out_buffer_capacity already exists'' ' |
| | | ); |
| | | PREPARE stmt_out_buffer_capacity FROM @add_out_buffer_capacity_sql; |
| | | EXECUTE stmt_out_buffer_capacity; |
| | | DEALLOCATE PREPARE stmt_out_buffer_capacity; |
| | | |
| | | SET @station_flow_capacity_exists := ( |
| | | SELECT COUNT(1) |
| | | FROM information_schema.TABLES |
| | | WHERE TABLE_SCHEMA = @current_db |
| | | AND TABLE_NAME = 'asr_station_flow_capacity' |
| | | ); |
| | | |
| | | SET @migrate_station_flow_capacity_sql := IF( |
| | | @station_flow_capacity_exists > 0, |
| | | 'UPDATE asr_bas_station station |
| | | JOIN asr_station_flow_capacity capacity |
| | | ON capacity.station_id = station.station_id |
| | | AND capacity.direction_code = ''OUT'' |
| | | AND capacity.buffer_capacity IS NOT NULL |
| | | SET station.out_buffer_capacity = capacity.buffer_capacity |
| | | WHERE station.out_buffer_capacity IS NULL', |
| | | 'SELECT ''table asr_station_flow_capacity not exists'' ' |
| | | ); |
| | | PREPARE stmt_migrate_station_flow_capacity FROM @migrate_station_flow_capacity_sql; |
| | | EXECUTE stmt_migrate_station_flow_capacity; |
| | | DEALLOCATE PREPARE stmt_migrate_station_flow_capacity; |
| | | |
| | | SET @drop_station_flow_capacity_sql := IF( |
| | | @station_flow_capacity_exists > 0, |
| | | 'DROP TABLE asr_station_flow_capacity', |
| | | 'SELECT ''table asr_station_flow_capacity already removed'' ' |
| | | ); |
| | | PREPARE stmt_drop_station_flow_capacity FROM @drop_station_flow_capacity_sql; |
| | | EXECUTE stmt_drop_station_flow_capacity; |
| | | DEALLOCATE PREPARE stmt_drop_station_flow_capacity; |
| | | |
| | | -- 4. AI自动调参控制台菜单 |
| | | -- 执行后请在“角色授权”里给对应角色勾选 AI管理 -> AI自动调参控制台。 |
| | | SET @ai_manage_id := COALESCE( |
| | | ( |
| | | SELECT id |
| | | FROM sys_resource |
| | | WHERE code = 'aiManage' AND level = 1 |
| | | ORDER BY id |
| | | LIMIT 1 |
| | | ), |
| | | ( |
| | | SELECT id |
| | | FROM sys_resource |
| | | WHERE name = 'AI管理' AND level = 1 |
| | | ORDER BY id |
| | | LIMIT 1 |
| | | ) |
| | | ); |
| | | |
| | | INSERT INTO sys_resource(code, name, resource_id, level, sort, status) |
| | | SELECT 'ai/auto_tune.html', 'AI自动调参控制台', @ai_manage_id, 2, 4, 1 |
| | | FROM dual |
| | | WHERE @ai_manage_id IS NOT NULL |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_resource |
| | | WHERE code = 'ai/auto_tune.html' AND level = 2 |
| | | ); |
| | | |
| | | UPDATE sys_resource |
| | | SET name = 'AI自动调参控制台', |
| | | resource_id = @ai_manage_id, |
| | | level = 2, |
| | | sort = 4, |
| | | status = 1 |
| | | WHERE code = 'ai/auto_tune.html' AND level = 2; |
| | | |
| | | SET @ai_auto_tune_id := ( |
| | | SELECT id |
| | | FROM sys_resource |
| | | WHERE code = 'ai/auto_tune.html' AND level = 2 |
| | | ORDER BY id |
| | | LIMIT 1 |
| | | ); |
| | | |
| | | INSERT INTO sys_resource(code, name, resource_id, level, sort, status) |
| | | SELECT 'ai/auto_tune.html#view', '查看', @ai_auto_tune_id, 3, 1, 1 |
| | | FROM dual |
| | | WHERE @ai_auto_tune_id IS NOT NULL |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_resource |
| | | WHERE code = 'ai/auto_tune.html#view' AND level = 3 |
| | | ); |
| | | |
| | | UPDATE sys_resource |
| | | SET name = '查看', |
| | | resource_id = @ai_auto_tune_id, |
| | | level = 3, |
| | | sort = 1, |
| | | status = 1 |
| | | WHERE code = 'ai/auto_tune.html#view' AND level = 3; |
| | | |
| | | -- 5. 历史已发布 Prompt 补丁:outBufferCapacity 约束说明 |
| | | 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注意:asr_bas_station.out_buffer_capacity 是人工维护的出库缓存容量,只用于证明 outTaskLimit 可上调上限,Agent 不允许修改该字段;增大 outTaskLimit 时建议值不得超过对应站点 outBufferCapacity。' |
| | | ) |
| | | WHERE template.scene_code = 'wcs_auto_tune_dispatch' |
| | | AND block.block_type = 'scene_playbook' |
| | | AND block.content NOT LIKE '%out_buffer_capacity 是人工维护的出库缓存容量%'; |
| | | |
| | | -- 6. 历史已发布 Prompt 补丁:ruleSnapshot 规则说明 |
| | | -- 追加文本需与 AiPromptUtils 的默认规则说明保持一致。 |
| | | UPDATE sys_ai_prompt_block block |
| | | JOIN sys_ai_prompt_template template ON template.id = block.template_id |
| | | SET block.content = CONCAT( |
| | | block.content, |
| | | '\nStep 4 读取调参规则\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%'; |
| | | |
| | | -- 7. 执行后检查 |
| | | SELECT id, name, code, value, type, status, select_type |
| | | FROM sys_config |
| | | WHERE code IN ( |
| | | 'aiAutoTuneEnabled', |
| | | 'aiAutoTuneIntervalMinutes', |
| | | 'aiAutoTunePromptLogLimit', |
| | | 'conveyorStationTaskLimit' |
| | | ) |
| | | ORDER BY code; |
| | | |
| | | SHOW COLUMNS FROM asr_bas_station LIKE 'out_buffer_capacity'; |
| | | |
| | | SELECT id, code, name, resource_id, level, sort, status |
| | | FROM sys_resource |
| | | WHERE code IN ( |
| | | 'aiManage', |
| | | 'ai/auto_tune.html', |
| | | 'ai/auto_tune.html#view' |
| | | ) |
| | | ORDER BY level, sort, id; |
| | |
| | | </div> |
| | | </div> |
| | | <div class="map-box"> |
| | | <div class="map-title">调参规则 maxStep/cooldown</div> |
| | | <div class="map-title"> |
| | | <span>调参规则 ruleSnapshot</span> |
| | | <el-button type="text" size="mini" @click="openJsonDialog('调参规则 ruleSnapshot', ruleSnapshot)">JSON</el-button> |
| | | </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 |
| | | {{ formatRuleSnapshotText(item) }} |
| | | </span> |
| | | <span class="small-muted" v-if="ruleSnapshot.length === 0">暂无数据</span> |
| | | </div> |
| | |
| | | } |
| | | return result; |
| | | }, |
| | | formatRuleSnapshotText: function(rule) { |
| | | var safeRule = rule || {}; |
| | | var targetName = this.valueOrDash(safeRule.targetType) + '/' + this.valueOrDash(safeRule.targetKey); |
| | | var minValue = this.valueOrDash(safeRule.minValue); |
| | | var maxValue = this.valueOrDash(safeRule.maxValue); |
| | | var dynamicMaxValue = this.valueOrDash(safeRule.dynamicMaxValue); |
| | | var maxStep = this.valueOrDash(safeRule.maxStep); |
| | | var cooldownMinutes = this.valueOrDash(safeRule.cooldownMinutes); |
| | | var dynamicMaxSource = this.valueOrDash(safeRule.dynamicMaxSource); |
| | | |
| | | return targetName |
| | | + ': min=' + minValue |
| | | + ' / max=' + maxValue |
| | | + ' / dynamicMax=' + dynamicMaxValue |
| | | + ' / maxStep=' + maxStep |
| | | + ' / cooldown=' + cooldownMinutes + 'm' |
| | | + ' / dynamicMaxSource=' + dynamicMaxSource; |
| | | }, |
| | | statusType: function(status) { |
| | | if (status === 'success') { |
| | | return 'success'; |
| | |
| | | } |
| | | |
| | | @Test |
| | | void rejectCrnOutBatchRunningLimitRangeAndStepCases() { |
| | | void crnOutBatchRunningLimitAllowsStepThreeAndRejectsRangeAndStepCases() { |
| | | when(configService.getOne(any(Wrapper.class))).thenReturn(config("crnOutBatchRunningLimit", "10")); |
| | | |
| | | service.apply(request(true, |
| | | command("sys_config", null, "crnOutBatchRunningLimit", "13"), |
| | | command("sys_config", null, "crnOutBatchRunningLimit", "14"), |
| | | command("sys_config", null, "crnOutBatchRunningLimit", "21") |
| | | )); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | assertEquals("rejected", changes.get(0).getResultStatus()); |
| | | assertTrue(changes.get(0).getRejectReason().contains("步长不能超过 2")); |
| | | assertEquals("dry_run", changes.get(0).getResultStatus()); |
| | | assertEquals("13", changes.get(0).getRequestedValue()); |
| | | assertEquals("rejected", changes.get(1).getResultStatus()); |
| | | assertTrue(changes.get(1).getRejectReason().contains("1~20")); |
| | | assertTrue(changes.get(1).getRejectReason().contains("步长不能超过 3")); |
| | | assertEquals("rejected", changes.get(2).getResultStatus()); |
| | | assertTrue(changes.get(2).getRejectReason().contains("1~20")); |
| | | } |
| | | |
| | | @Test |
| | | void rejectMaxInTaskRangeAndStepCases() { |
| | | void maxInTaskAllowsStepThreeAndRejectsRangeAndStepCases() { |
| | | when(basCrnpService.getById(1)).thenReturn(crn(1, 1, 5)); |
| | | |
| | | service.apply(request(true, |
| | | command("crn", "1", "maxInTask", "7"), |
| | | command("crn", "1", "maxInTask", "8"), |
| | | command("crn", "1", "maxInTask", "9"), |
| | | command("crn", "1", "maxInTask", "11") |
| | | )); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | assertEquals("rejected", changes.get(0).getResultStatus()); |
| | | assertTrue(changes.get(0).getRejectReason().contains("步长不能超过 1")); |
| | | assertEquals("dry_run", changes.get(0).getResultStatus()); |
| | | assertEquals("8", changes.get(0).getRequestedValue()); |
| | | assertEquals("rejected", changes.get(1).getResultStatus()); |
| | | assertTrue(changes.get(1).getRejectReason().contains("0~10")); |
| | | assertTrue(changes.get(1).getRejectReason().contains("步长不能超过 3")); |
| | | assertEquals("rejected", changes.get(2).getResultStatus()); |
| | | assertTrue(changes.get(2).getRejectReason().contains("0~10")); |
| | | } |
| | | |
| | | @Test |
| | |
| | | AutoTuneRuleSnapshotItem stationOutTaskRule = findRule(result, "station", "outTaskLimit"); |
| | | assertEquals(0, stationOutTaskRule.getMinValue()); |
| | | assertNull(stationOutTaskRule.getMaxValue()); |
| | | assertEquals(1, stationOutTaskRule.getMaxStep()); |
| | | assertEquals(3, stationOutTaskRule.getMaxStep()); |
| | | assertEquals(10, stationOutTaskRule.getCooldownMinutes()); |
| | | assertEquals(Boolean.TRUE, stationOutTaskRule.getDynamicMaxValue()); |
| | | assertEquals("currentParameterSnapshot.stationOutBufferCapacities[targetId]", |
| | | stationOutTaskRule.getDynamicMaxSource()); |
| | | assertEquals("单次调整幅度不能超过 maxStep;增大时不得超过对应站点 outBufferCapacity。", |
| | | stationOutTaskRule.getNote()); |
| | | |
| | | AutoTuneRuleSnapshotItem crnMaxOutRule = findRule(result, "crn", "maxOutTask"); |
| | | assertEquals(3, crnMaxOutRule.getMaxStep()); |
| | | assertNull(crnMaxOutRule.getDynamicMaxSource()); |
| | | assertEquals("单次调整幅度不能超过 maxStep。", crnMaxOutRule.getNote()); |
| | | |
| | | AutoTuneRuleSnapshotItem crnMaxInRule = findRule(result, "crn", "maxInTask"); |
| | | assertEquals(1, crnMaxInRule.getMaxStep()); |
| | | assertEquals(3, crnMaxInRule.getMaxStep()); |
| | | |
| | | assertRuleMaxStep(result, "sys_config", "crnOutBatchRunningLimit", 3); |
| | | assertRuleMaxStep(result, "dual_crn", "maxOutTask", 3); |
| | | assertRuleMaxStep(result, "dual_crn", "maxInTask", 3); |
| | | } |
| | | |
| | | @Test |
| | |
| | | } |
| | | throw new AssertionError("rule not found: " + targetType + "/" + targetKey); |
| | | } |
| | | |
| | | private void assertRuleMaxStep(List<AutoTuneRuleSnapshotItem> rules, |
| | | String targetType, |
| | | String targetKey, |
| | | int expectedMaxStep) { |
| | | AutoTuneRuleSnapshotItem rule = findRule(rules, targetType, targetKey); |
| | | assertEquals(expectedMaxStep, rule.getMaxStep()); |
| | | } |
| | | } |