新增系统仿真参数优化 Skill
This commit is contained in:
1 parent
a87d462e94
commit
42ffdfff7d
6 files changed
+5935
-14
No files matched your search
@@ -1,11 +1,15 @@
|
||||
---
|
||||
name: system-simulation
|
||||
description: 读取、校验并简要解释 SystemSimulationApp 工程 JSON v1 或 System XML v3,安全规范化文件文本,并在用户选定结果曲线后启动、监视或取消仿真及导出 CSV。适用于检查模型文件、修复编码或换行、运行仿真和获取结果;不用于旧格式迁移、任意语义修复、自动迭代或网页自动预装。
|
||||
description: 读取、校验并简要解释 SystemSimulationApp 工程 JSON v1 或 System XML v3,安全规范化文件文本,运行并监视仿真、导出结果,以及在用户确认计划后对 JSON v1 执行单目标、有界连续 SI 参数优化。适用于检查模型、修复编码或换行、运行仿真、获取结果和优化结果统计量;不用于旧格式迁移、任意语义修复、离散或拓扑优化、多目标优化或网页自动预装。
|
||||
metadata:
|
||||
openclaw:
|
||||
requires:
|
||||
bins: [python3.12]
|
||||
---
|
||||
|
||||
# 系统仿真
|
||||
|
||||
使用本 Skill 随附的确定性脚本检查模型、调用现有后端并保存结果;不要让语言模型自行重写模型或猜测求解数据。
|
||||
使用本 Skill 随附的确定性脚本检查模型、调用现有后端并保存结果;不要让语言模型自行重写模型或猜测求解数据。`simulation_skill.py` 处理文件和单次仿真,同一 Skill 内的独立入口 `optimization_skill.py` 处理优化计划与执行。
|
||||
|
||||
## 基本边界
|
||||
|
||||
@@ -13,29 +17,39 @@ description: 读取、校验并简要解释 SystemSimulationApp 工程 JSON v1
|
||||
- 组件参数是仿真前设定的固定输入;结果变量才是可随时间绘制的量。不要把“参数”当成结果曲线。
|
||||
- 工程 JSON 可在连续数值参数中保存受限算术表达式。检查、编译或生成 XML 时由后端安全求值并换算为 SI;不得把计算结果回写到源 JSON。
|
||||
- 文件通过格式校验不等于物理系统一定可求解。不要隐瞒编译或运行阶段的诊断。
|
||||
- 不直接覆盖源文件,不自行修改参数、连接、组件类型、模型版本或求解设置。
|
||||
- 本版不支持把模型自动注入网页、生成可直接打开的预装页面、任意损坏文件修复、模型迁移或自动调参迭代。明确告知用户这些能力尚未实现,不要用手工网页操作冒充支持。
|
||||
- 不直接覆盖源文件。普通检查或仿真不自行修改参数、连接、组件类型、模型版本或求解设置;优化也只能在用户确认的派生副本中改变明确选定的参数。
|
||||
- 优化仅面向 ReactFlow 工程 JSON v1,设计变量必须由用户指定,或由用户明确授权 Skill 提议后再纳入计划;它们必须是连续、线性 SI 参数。现有后端不负责证明参数连续性,不能只因字段是数字就自动选作设计变量。带编辑器、离散选项或后端显式否决的参数必须拒绝,整数、条件显示控制量及会改变活动端口、模式或拓扑的参数不得进入连续优化。
|
||||
- 本版不支持把模型自动注入网页、生成可直接打开的预装页面、任意损坏文件修复、模型迁移、离散或拓扑优化以及多目标优化。不要用手工网页操作冒充支持。
|
||||
|
||||
处理文件、解释格式或选择结果变量时,读取 [references/file-contracts.md](references/file-contracts.md)。请求文件修复时,再读取 [references/repair-policy.md](references/repair-policy.md)。需要运行、监视、取消仿真或交付结果时,读取 [references/workflows.md](references/workflows.md)。
|
||||
处理文件、解释格式或选择结果变量时,读取 [references/file-contracts.md](references/file-contracts.md)。请求文件修复时,再读取 [references/repair-policy.md](references/repair-policy.md)。需要运行、监视、取消仿真或交付结果时,读取 [references/workflows.md](references/workflows.md)。用户请求按仿真结果优化参数时,必须读取 [references/optimization-workflow.md](references/optimization-workflow.md)。
|
||||
|
||||
## 工作原则
|
||||
|
||||
1. 先用 `inspect` 确认输入格式、版本、结构和诊断,再基于检查结果简要解释组件、连接与仿真设置。
|
||||
2. 如果用户要求修复,只能执行文本规范化。先展示预览和源文件 SHA-256,获得针对该预览的明确确认后,才可写入另一个输出路径;随后重新 `inspect`。
|
||||
3. 仿真前必须让用户选择直接曲线查看方式,并解析具体结果变量:
|
||||
3. 单次仿真前必须让用户选择直接曲线查看方式,并解析具体结果变量:
|
||||
- 分别查看所选变量;
|
||||
- 将多个同单位、可比较的变量叠加;
|
||||
- 将不同物理量或单位的变量上下排列。
|
||||
4. 用户用显示名称描述组件或变量时,利用检查结果中的稳定 ID、结果 `key`、物理量和单位消歧。存在重名、多个候选或“参数/结果变量”含义不清时,先询问,不能替用户猜。
|
||||
5. 使用 `simulate` 的事件流持续判断 queued、validating、compiling、integrating 和结束状态。仿真时间暂时不变但内部活动仍增长时,只说明正在处理慢步,不能宣称卡死。
|
||||
6. 成功运行后交付用户选择的 SVG 曲线和完整 `results.csv`,并简要说明完成状态、实际仿真终点和重要诊断。失败或取消时交付能够安全生成的部分结果;若运行前即失败而没有 CSV,要明确说明原因。
|
||||
7. 优化需求优先按自然语言理解:从检查结果补齐稳定结果 `key`、单位和当前参数值,未指定的算法、预算、容差和输出目录采用参考文档中的推荐默认值。不要要求用户填写规格 JSON,也不要追问随机种子、变异因子等已有默认值。
|
||||
8. 信息足以形成规格后,直接在内部写入规格并执行无候选仿真的 `plan`,无需先征求生成计划的许可。计划阶段用普通语言只展示目标、可调参数及范围、约束、最多仿真次数、输出位置和重要假设。参数明显是连续物理标量时,把连续性作为计划假设,一次整体执行确认即可覆盖;只有语义确有歧义时才自然地追问。内部声明代码、SHA、`planHash` 和 `confirmationToken` 默认不展示。
|
||||
9. 生成计划不等于获准执行。只有用户看过计划摘要并明确表示开始后才能传入 `--confirmed`;用户只要求计划时必须停在计划阶段。计划任一实质内容变化都要重新确认。
|
||||
10. 优化中的失败、取消、停滞或不完整仿真不计算目标分数。最终只对预算内找到的最佳可行候选做一次绕过缓存的完整复验;复验通过前不把候选称为已验证方案。
|
||||
11. 严格区分搜索停止与候选复验:`verified` 只说明最佳搜索候选的新鲜复验通过,不等于搜索收敛、系统达到稳态或全局最优。报告时分别说明搜索候选评估、按阶段拆分的仿真预算槽位占用及完成/失败记录、未形成试验记录的槽位、缓存命中、独立复验、未用预算和真实停止原因;槽位占用不能说成后端已接收或已完成。内部防死循环上限及重复停滞后的种群塌缩都不得解释成“已收敛”。
|
||||
12. 目标使用 `final` 时必须报告脚本给出的末段趋势诊断状态;只有完整且覆盖计划终点的新鲜序列才能分析趋势,诊断不可用或样本不足时明确说明且不自行推断。检测到明显变化时,说明终点值只是快照。新鲜复验已经通过后不再建议重复同一复验;最佳点落在边界时,只能说明已采样点的趋势,未经工程可行性确认不得建议放宽边界。
|
||||
|
||||
脚本命令统一从仓库根目录运行:
|
||||
|
||||
```powershell
|
||||
py -3.12 skills/system-simulation/scripts/simulation_skill.py --help
|
||||
py -3.12 skills/system-simulation/scripts/optimization_skill.py --help
|
||||
```
|
||||
|
||||
在 OpenClaw 中不要假设当前目录是仓库根目录。使用 `"{baseDir}/scripts/simulation_skill.py"` 或 `"{baseDir}/scripts/optimization_skill.py"`,也可以先进入本 Skill 目录再从 `scripts/` 运行;不要把 `{baseDir}` 当成需要手工猜测的仓库路径。
|
||||
|
||||
Windows 优先使用仓库 `.venv-win\Scripts\python.exe`(若存在),否则使用 `py -3.12`;Linux 优先使用 `.venv/bin/python`,否则使用 `python3.12`。不要调用未经版本确认的 `python`,本项目要求 Python 3.12。
|
||||
|
||||
优先依赖脚本返回的结构化 JSON/JSONL、稳定错误码和退出码做判断,不解析中文提示文本来驱动下一步。
|
||||
@@ -1,7 +1,7 @@
|
||||
interface:
|
||||
display_name: "系统仿真文件助手"
|
||||
short_description: "读取与校验模型文件,监视仿真并导出结果曲线和 CSV 文件"
|
||||
default_prompt: "使用 $system-simulation 检查我的模型文件,并在我选定结果曲线后运行和监视仿真。"
|
||||
display_name: "系统仿真与参数优化助手"
|
||||
short_description: "用自然语言检查、仿真模型,并按安全默认值规划连续参数优化"
|
||||
default_prompt: "使用 $system-simulation 按我描述的目标优化模型参数;请自动补齐安全默认值,先给出易读计划,等我一次确认后执行。"
|
||||
|
||||
policy:
|
||||
allow_implicit_invocation: true
|
||||
@@ -0,0 +1,307 @@
|
||||
# 单目标参数优化工作流
|
||||
|
||||
## 执行入口与范围
|
||||
|
||||
本 Skill 内的 `scripts/optimization_skill.py` 是独立外层优化入口,它复用同目录的 `simulation_skill.py` 访问现有 FastAPI 后端。优化算法、候选生成、目标统计和报告都在 Skill 进程中完成,不要求后端提供原生优化端点或优化参数准入字段。优化入口只接受已通过检查和编译的 ReactFlow 工程 JSON v1,不接受 System XML 作为优化源文件。
|
||||
|
||||
支持范围固定为:
|
||||
|
||||
- 一个结果变量统计量构成的单目标;
|
||||
- 1–16 个有限上下界内的线性尺度连续 SI 参数;
|
||||
- 0–16 个结果响应约束;
|
||||
- 最多占用 200 个仿真预算槽位;
|
||||
- 顺序执行的有界 DE/rand/1/bin 差分进化。
|
||||
|
||||
不支持离散、整数、介质引用、条件显示控制量、活动端口数、组件类型、连接、拓扑、无界、对数尺度或多目标优化。不执行用户文本中的任意 Python 回调或表达式,也不在优化中改变仿真时段、采样间隔、最大内部步长、求解方法或物理拓扑。用户给出多个愿望时,必须选定一个目标,再将可用上下限表达的其余要求定义为响应约束;不自行设计权重合成多目标。
|
||||
|
||||
## 自然语言交互与推荐默认值
|
||||
|
||||
用户不需要了解优化规格 JSON。通常只需说明:想改善哪个结果、让它变大/变小或接近什么值,以及允许调整哪个参数和范围。若用户明确说“你先假定一个”或同等授权,可以提出一个设计变量和工程范围,但必须标成待确认的假设,不能伪装成后端验证结论。
|
||||
|
||||
先运行 `inspect` 和必要的编译检查,再把用户说法解析为严格规格:
|
||||
|
||||
- “结束时”映射为 `final`;“整个仿真中的最大/最小值”映射为 `maximum` / `minimum`;未指定窗口时使用完整结果时间范围。
|
||||
- 从结果和参数合同补齐稳定 ID、`resultKey`、SI 单位和当前值;能唯一解析时不要反问用户这些机器字段。
|
||||
- 用户未提响应约束时使用空列表,不逐项询问“是否需要约束”。
|
||||
- 目标、参数、范围或方向无法唯一确定时必须询问;不要为了可默认的技术字段打断用户。
|
||||
|
||||
用户未指定高级设置时,把下列推荐配置显式写入规格文件,保证计划可复现:
|
||||
|
||||
```json
|
||||
{
|
||||
"algorithm": {
|
||||
"name": "differentialEvolution",
|
||||
"seed": 0,
|
||||
"populationSize": 8,
|
||||
"mutationFactor": 0.8,
|
||||
"crossoverProbability": 0.7
|
||||
},
|
||||
"budget": {
|
||||
"maxSimulationRuns": 25,
|
||||
"maxWallSeconds": 3600
|
||||
},
|
||||
"validation": {
|
||||
"relativeTolerance": 1e-08,
|
||||
"absoluteTolerance": 0.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
用户描述了响应约束但未指定容差时,默认 `tolerance = 0`;未指定 `scale` 时,取该约束所有非空边界绝对值的最大值,若结果为零则取 `1`。目标或约束接近零、后端存在可观察的不确定性,或用户给出安全裕量时,应提出有物理意义的容差建议,不能用一个跨量纲的非零绝对容差替代判断。
|
||||
|
||||
默认输出目录使用项目文件所在目录下尚不存在的 `optimization-runs/<项目名>-<UTC时间戳>`。源目录不可写时,改用当前可写工作区中的同名新目录,并在计划摘要中说明实际位置。不要让用户命名目录,也不要覆盖既有目录。默认计划可对用户概括为“差分进化、最多 25 次仿真、最长 1 小时,并预留一次独立复验”;除非用户询问或计划产生覆盖不足警告,不主动讲解种群、变异因子、交叉概率、随机种子或代数公式。
|
||||
|
||||
## 优化规格 JSON
|
||||
|
||||
规格是独立 JSON 文件,顶层必须且只能包含 `optimizationSchemaVersion`、`objective`、`designVariables`、`constraints`、`algorithm`、`budget` 和 `validation`。完整示例如下;其中组件 ID、参数名、结果 `key` 和单位必须换成实际 `plan` 所检查的合同:
|
||||
|
||||
```json
|
||||
{
|
||||
"optimizationSchemaVersion": 1,
|
||||
"objective": {
|
||||
"resultKey": "pressure.chamber_1.absolute",
|
||||
"expectedUnit": "Pa",
|
||||
"statistic": {
|
||||
"kind": "maximum",
|
||||
"window": {"start": 0.0, "end": 1.0}
|
||||
},
|
||||
"goal": {"kind": "minimize"}
|
||||
},
|
||||
"designVariables": [
|
||||
{
|
||||
"id": "orifice_area",
|
||||
"componentId": "valve_1",
|
||||
"parameter": "area0",
|
||||
"unit": "m2",
|
||||
"lower": 1e-06,
|
||||
"upper": 0.0001
|
||||
}
|
||||
],
|
||||
"constraints": [
|
||||
{
|
||||
"id": "mass_flow_limit",
|
||||
"resultKey": "massFlow.valve_1.port_2.intoComponent",
|
||||
"expectedUnit": "kg/s",
|
||||
"statistic": {"kind": "peakAbsolute", "window": null},
|
||||
"lower": null,
|
||||
"upper": 0.25,
|
||||
"tolerance": 0.0,
|
||||
"scale": 0.25
|
||||
}
|
||||
],
|
||||
"algorithm": {
|
||||
"name": "differentialEvolution",
|
||||
"seed": 0,
|
||||
"populationSize": 8,
|
||||
"mutationFactor": 0.8,
|
||||
"crossoverProbability": 0.7
|
||||
},
|
||||
"budget": {
|
||||
"maxSimulationRuns": 25,
|
||||
"maxWallSeconds": 3600
|
||||
},
|
||||
"validation": {
|
||||
"relativeTolerance": 1e-08,
|
||||
"absoluteTolerance": 0.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
规格使用严格字段集。未知字段、缺失字段、重复键、布尔型伪装的数字、`NaN` 和无穷值均不能进入优化;`optimizationSchemaVersion` 固定为 `1`。
|
||||
|
||||
`plan` 的 `spec.resolved` 会额外显示只读派生字段,例如统计量 `metricUnit`、算法 `strategy`/`workers`/`updating`、搜索策略、边界处理、一维端点播种策略、停滞代数、预算预留量和固定复验次数;这些字段不是输入规格字段,不能复制回 schema v1 规格文件。输入优化规格仍是 schema v1;最终结果的 `optimizationResultSchemaVersion` 为 `2`,两者不要混淆。
|
||||
|
||||
### 目标
|
||||
|
||||
`objective.resultKey` 必须是编译模型声明的稳定结果键,`expectedUnit` 必须与结果元数据单位完全一致,无量纲时使用空字符串。`goal.kind` 可为:
|
||||
|
||||
- `minimize`:最小化统计值;
|
||||
- `maximize`:最大化统计值;
|
||||
- `target`:最小化 `abs(statistic - value)`,此时必须增加有限数字 `goal.value`。
|
||||
|
||||
`minimize` 和 `maximize` 不接受 `goal.value`。`target` 只定义目标损失,不是提前停止阈值。
|
||||
|
||||
`expectedUnit` 是原始时间序列单位。`final`、`minimum`、`maximum`、`timeMean`、`rms` 和 `peakAbsolute` 的统计量单位与它相同;`integral` 与 `absoluteIntegral` 的统计量单位为原单位乘秒(结构化输出的 `metricUnit` 使用 `(<seriesUnit>)*s`,无量纲序列积分为 `s`)。`target` 的 `value` 使用统计量单位。
|
||||
|
||||
### 设计变量
|
||||
|
||||
`designVariables` 必须包含 1–16 项,ID 和 `(componentId, parameter)` 均不能重复。每项必须:
|
||||
|
||||
- 由用户明确指定,或在用户授权 Skill 提议后纳入计划;执行时它必须是连续、线性 SI 参数,且改变它不会切换模式、改变活动端口或拓扑;
|
||||
- 不带组件合同中的 `editor` 或离散 `options`;若未来合同显式提供 `optimizationEligible: false`,该否决不可由用户确认覆盖;
|
||||
- `unit` 与参数合同的 SI 单位完全一致;
|
||||
- 使用有限数字 `lower < upper`,同时满足目录最小值、最大值和排他下界;
|
||||
- 使源模型编译后的当前 SI 值位于闭区间 `[lower, upper]` 内。
|
||||
|
||||
当前后端目录没有能够单独证明“连续量、整数、模式控制量、活动端口数”的机器字段;字段缺失本身不是连续性证据。不得仅凭“值是数字”自动挑选设计变量。若用户已选定参数,且参数合同、物理量与单位、组件语义和用户给出的连续区间一致表明它是普通物理标量,同时不存在 `editor`、`options` 或显式否决,则可把“按连续线性 SI 参数处理且不改变结构”写成计划假设,不必在生成计划前要求用户复述声明。用户对该计划的一次整体执行确认同时接受这项假设。
|
||||
|
||||
若参数像整数、计数、无量纲模式量、条件控制量,元数据彼此矛盾,或无法判断改变它是否影响端口和拓扑,必须先用自然语言询问,例如:“这个参数可以取任意小数,并且调整时不会切换组件模式或端口吗?”不要向用户显示内部声明代码。`editor`、`options` 和未来可能出现的显式否决只用于拒绝明显不适用的参数,不能证明其余参数连续。
|
||||
|
||||
参数值、初值和边界一律使用线性 SI 合同。如果用户用显示单位给出边界,先换算为 SI 并在计划中展示。后端在计划阶段将源 JSON 转换为基准 System XML v3;每个候选都从该 XML 重新生成,只替换选中 `Parameter/@value` 的 SI 数字,不在前一个候选上累积修改,源 JSON 永不被覆盖。
|
||||
|
||||
### 统计量与响应约束
|
||||
|
||||
`statistic` 必须同时包含 `kind` 和 `window`。支持的 `kind` 为 `final`、`minimum`、`maximum`、`timeMean`、`rms`、`integral`、`absoluteIntegral` 和 `peakAbsolute`。`window` 可为 `null`,表示使用完整返回时间序列;也可为有限数字的 `{start, end}`,且 `start < end`。
|
||||
|
||||
窗口边界不在采样点时使用线性插值,不为超出结果范围的窗口外推。时间必须严格递增,时间和数值必须有限且等长。`timeMean`、`rms`、`integral` 和 `absoluteIntegral` 使用梯形时间积分。`minimum`、`maximum` 和 `peakAbsolute` 只是样本及插值边界上的统计,不证明连续时间真实峰值;安全关键峰值可能比 `sampleStep` 更窄时,必须报告采样风险。
|
||||
|
||||
`constraints` 只支持响应约束。每项必须包含唯一 `id`、`resultKey`、`expectedUnit`、`statistic`、`lower`、`upper`、`tolerance` 和 `scale`。`lower` 或 `upper` 可为 `null`,但至少一个必须是有限数字;两者都存在时必须 `lower <= upper`。`tolerance >= 0`,`scale > 0`。下界在 `value >= lower - tolerance` 时满足,上界在 `value <= upper + tolerance` 时满足。
|
||||
|
||||
`scale` 只用于将违反量归一化为 `rawViolation / scale`,以便对完整但不可行的候选排序;它不改变可行边界,也不是软约束权重。需要等式时用显式容差带,不使用浮点精确相等。
|
||||
|
||||
约束的 `lower`、`upper`、`tolerance` 和 `scale` 都使用该约束统计量的单位,而不是一律使用原始序列单位。复验中的单个 `absoluteTolerance` 数值分别按每项统计量自己的单位解释;目标与约束量纲差异很大时优先把它设为 `0` 并使用相对容差,或明确接受这一 schema v1 限制。
|
||||
|
||||
## 计划确认
|
||||
|
||||
信息足以形成规格后直接执行 `plan`,不需要用户先批准计划生成。它校验源 JSON、规格、结果键/单位、设计变量合同和边界,并请求后端生成基准 XML,但不开始优化候选仿真。其结构化输出包含源 JSON 与规格 JSON 的绝对路径/SHA-256、解析后规格、基准 XML SHA-256、目标/约束元数据、解析后设计变量、执行上限、绝对输出目录、`parameterContinuity`、`requiredAssertions`,以及值相同的 `planHash` 与 `confirmationToken`。
|
||||
|
||||
计划中的 `OPTIMIZATION_CONTINUITY_USER_ASSERTION` 是给脚本和审计使用的内部标识,不是要求用户照抄的口令。后端不会验证参数的物理/语义连续性,因此面向用户的计划摘要必须用普通语言列出相关假设。若参数语义清楚,用户在看到摘要后明确同意开始运行,即视为同时接受完整计划和这些假设;未得到这次整体确认时不得传入 `--confirmed`。若参数语义不清,则应在执行确认之前先完成自然语言消歧。
|
||||
|
||||
`confirmationToken` 绑定源 JSON SHA-256、规格文件 SHA-256、基准 XML SHA-256、解析后的输出目录、后端 base URL、流读取超时、连续性确认策略和搜索策略。向用户展示计划摘要并获得明确确认后,才能传入 `--confirmed`。`optimize` 还必须提供 `plan` 返回的源 SHA-256、规格 SHA-256 和 token;脚本会重建当前计划并拒绝旧策略或其他过期确认。任一绑定项改变,包括搜索策略升级,都必须重新 `plan` 和确认。输出目录必须是新路径或空目录。
|
||||
|
||||
`requiredAssertions[].code`、各类 SHA、`planHash` 和 `confirmationToken` 是代理执行命令时保存和回传的机器字段。普通对话中应由 Skill 内部保管,不向用户倾倒;只有用户主动要求审计细节,或排查计划过期/文件变化时才展示。
|
||||
|
||||
普通计划摘要只需要回答:
|
||||
|
||||
- 要改善哪个结果,用什么统计口径;
|
||||
- 调整哪些参数,各自在什么范围;
|
||||
- 有哪些响应约束;
|
||||
- 采用默认还是用户指定的搜索配置,最多提交多少次仿真、最长多久;
|
||||
- 哪些参数连续性或工程边界属于假设;
|
||||
- 输出写到哪里,并明确源模型不变。
|
||||
|
||||
摘要后只问一次自然问题,例如:“就按这个方案开始吗?”用户明确同意后直接执行,不再追加连续性声明、算法参数或 token 确认。若用户明确只要计划,则交付摘要后停止,不把问题措辞成已经准备执行;等用户之后主动要求开始。
|
||||
|
||||
## DE/rand/1/bin 搜索
|
||||
|
||||
外层优化由 `optimization_skill.py` 使用 Python 标准库自行实现,不调用 SciPy 优化器,也不安装额外优化依赖。`algorithm` 严格包含:
|
||||
|
||||
```text
|
||||
name = differentialEvolution
|
||||
seed = 0 .. 2^32-1 的整数
|
||||
populationSize = 4 .. 50
|
||||
mutationFactor = (0, 2]
|
||||
crossoverProbability = [0, 1]
|
||||
```
|
||||
|
||||
`populationSize` 是实际种群个体数,不是乘以设计变量数的倍数。初始候选先在每个线性归一化坐标上做拉丁超立方分层,再用工程当前参数替换第一行,所以替换后的最终种群不承诺保持严格拉丁超立方的每层唯一性。只有一个设计变量时,初始种群还会强制包含归一化坐标 `0` 和 `1`,也就是精确测试用户确认的 SI 下界和上界;若基准点已经等于某个端点,不再为该端点制造重复行。第一次提交仍是基准仿真。基准仿真必须完整成功;基准可以不满足响应约束,此时仍可继续搜索。
|
||||
|
||||
每个完整代开始时冻结当前种群和排名;代内所有目标个体都只从这份冻结种群中选三个不同且不是自己的个体,按 `a + mutationFactor * (b - c)` 生成变异向量,接受结果在完整代结束后统一成为下一代。这是 `updating = deferred`,不会让同一代后面的候选使用刚被接受的新个体。越出归一化区间的坐标通过周期为 `2` 的镜像反射折回 `[0, 1]`,不再硬裁剪到端点;随后做 binomial crossover,并强制至少一个坐标来自变异向量。候选排名顺序是:完整可行点按目标损失;其次是完整但不可行点,按约束归一化违反量总和再按目标损失;失败点最后。平局用最早评估 ID 确定性打破。
|
||||
|
||||
`seed` 由标准库 `random.Random` 使用。固定种子可使同一 Python 实现和同一后端环境中的候选顺序可重放,但不证明跨 Python 版本、后端代码、操作系统或硬件位级一致。
|
||||
|
||||
当前搜索策略标识为 `de-rand-1-bin-deferred-reflection-1d-endpoints-stagnation-v3`。该标识随解析后算法配置进入计划,并绑定到确认 token;不能拿旧搜索策略产生的确认 token 启动新版搜索。
|
||||
|
||||
## 预算、停止、缓存与失败
|
||||
|
||||
`maxSimulationRuns` 必须至少是 `populationSize + 1`,且不得超过 200。它是仿真预算槽位的严格总上限。每次准备进度记录和打开后端流之前先保守占用一个槽位,因此即使本地进度文件创建或连接失败,该槽位也不会重新使用。搜索最多使用 `maxSimulationRuns - 1` 个槽位,始终为最佳可行候选保留一个绕过缓存的新鲜复验槽位。
|
||||
|
||||
计划会给出 `fullGenerationsWithUniqueCandidates = floor((searchRunLimit - populationSize) / populationSize)`。它表示在候选都不重复时,初始种群之外预算还能完整覆盖多少代。值为 `0` 时会返回 `OPTIMIZATION_BUDGET_INITIAL_POPULATION_ONLY` 警告;此时仍可按用户确认运行,但必须明确说明搜索覆盖很弱。若希望至少完整执行两代,预算至少应为 `3 * populationSize + 1`。
|
||||
|
||||
`maxWallSeconds` 必须在 10 秒至 7 天之间。它只在启动下一个搜索候选前检查:达到后不再启动新搜索,不取消正在处理慢步的健康仿真。已存在最佳可行候选时,即使搜索墙钟已到,预留的一次新鲜复验仍会运行。
|
||||
|
||||
搜索没有目标阈值、目标收敛容差或局部抛光阶段。每个完整 DE 代结束后会统计该代是否产生过新的后端仿真提交;连续 `3` 个完整代没有新提交时提前停止:
|
||||
|
||||
- 若当前种群映射为同一个精确 SI 候选,停止原因为 `populationCollapsedAfterDuplicateStagnation`。这只表示重复停滞发生时种群已经塌缩为一个精确候选,不是数值收敛判定,也不证明全局最优;
|
||||
- 若种群仍映射为多个精确候选,停止原因为 `duplicateProposalStagnation`。这表示候选生成持续重复缓存中的点,不是收敛判定。
|
||||
|
||||
仿真预算用完时为 `simulationBudgetExhausted`,搜索墙钟到达时为 `searchWallTimeReached`。为防止任何未预见的重复循环,候选请求另有 `max(100, searchRunLimit * 20)` 的内部防死循环上限;`optimizerCallLimitReached` 仅表示该安全保护触发,绝不能解释成搜索已经收敛。用户中断或结构化错误也会中止运行。
|
||||
|
||||
结果中的 `generations` 只统计完整完成的 DE 代;如果预算、墙钟或候选请求保护在一代中途阻止下一个候选,该部分代不会增加计数,也不会产生 `optimization-generation-completed` 事件。完整代进一步分成 `generationsWithNewBackendSubmissions` 和 `generationsWithoutNewBackendSubmissions`;初始种群不算一个 DE 代。“候选都唯一时预算可覆盖的完整代数”只是计划容量,不得当作实际完成或有效搜索代数。
|
||||
|
||||
缓存仅在当前 `optimize` 进程内有效。它根据固定参数 ID 顺序和映射后的精确 SI 数值识别重复候选;目标和所有响应约束共享一次仿真。缓存命中不增加后端提交数,最终复验始终绕过缓存。
|
||||
|
||||
最终结果使用 schema v2 分开记录搜索、复验、缓存和预算:
|
||||
|
||||
- 顶层 `search` 给出停止原因及类别、恒为 `false` 的 `searchConvergenceEstablished`、`populationCollapsedToSingleCandidate`、搜索候选评估数、`submissionSlotsConsumed`、缓存命中率、搜索预算的上限/已用/未用/是否耗尽、完整代及有新提交/无新提交代数、停滞连续代数、最终种群精确候选数和防死循环上限;
|
||||
- 顶层 `verification` 给出独立复验是否通过、源文件是否未变、比较容差和 `submissionSlotsConsumed`,并明确其含义是候选可复现且可行,而不是搜索收敛;
|
||||
- `counts.searchProposals`、`searchSubmissionSlotsConsumed`、`verificationSubmissionSlotsConsumed`、`backendSubmissionSlotsConsumed`、分阶段完成/失败记录、`unrecordedSubmissionSlotsConsumed`、`cacheHits` 和 `remainingSearchRunBudget` 提供可直接核对的分项统计。`optimizerCalls` 仅为初版兼容别名,新字段 `candidateRequestsAllStages` 明确包含搜索、缓存命中和复验请求;不带 `SlotsConsumed` 的 `backendSubmissions` 系列也仅是初版兼容别名,不能解释为后端已经接收。`timing` 使用全部已形成的试验记录计算墙钟、仿真耗时总计、最短、最长和平均值。
|
||||
- `bestSearch` 只保存完整可行的最佳搜索点。没有可行点时它必须为 `null`,约束违反最小的完整不可行点只放入 `bestDiagnosticSearch`,报告必须明确称其为诊断点并展示归一化约束违反总量,不能称为方案或最佳可行点。
|
||||
|
||||
面向用户汇报时分别写“搜索停止”和“候选复验状态”,并同时报告搜索候选评估数、按阶段拆分的预算槽位占用与完成/失败记录、没有形成试验记录的槽位、缓存命中、未用搜索额度、完整代中有新仿真的代数与纯重复代数。不得把槽位占用称为后端已接收或已完成的仿真,也不得把 `solutionStatus: verified`、缓存命中次数、纯重复代、种群塌缩或理论预算容量解释为算法收敛证据。
|
||||
|
||||
本版没有持久缓存或 resume 命令。`checkpoint.json` 和 `optimization-events.jsonl` 仅用于审计已完成工作,不承诺中断后恢复同一种群。也没有自动重试:后端、网络或产物写入错误会按结构化错误中止,不悄悄再发起一次仿真。
|
||||
|
||||
只有 `status == completed`、`success == true`、结果变量键与单位仍匹配计划、所需序列存在、时间窗完整被覆盖且时间/数值结构正确时,才计算目标和约束。`failed`、`stalled`、`stopped`、取消或部分结果作为失败候选且不计分,`objectiveValue` 和 `objectiveLoss` 保持为空。若后端把缺失变量、错误单位或畸形序列标成成功完成,则视为结果合同错误并中止本次优化,避免继续解释不可靠数据。完整但不可行的点仍保留统计值,但不能优先于任何完整可行点。
|
||||
|
||||
## 最终复验、产物与措辞
|
||||
|
||||
没有完整可行候选时,`solutionStatus` 为 `noFeasibleCandidate`,`bestSearch` 为 `null`;可在 `bestDiagnosticSearch` 中报告归一化约束违反更小的完整不可行候选作诊断,但不生成 `best-*` 产物。存在最佳可行搜索候选时,脚本重新核对源 JSON SHA-256,从基准 XML 生成候选,使用新 simulation ID 并绕过缓存做一次完整新鲜仿真。
|
||||
|
||||
复验必须仍完整可行,复验结束时源 JSON SHA-256 仍与计划一致,且目标和每个约束原值均满足 `abs(search - verification) <= absoluteTolerance + relativeTolerance * max(abs(search), abs(verification))`。`validation` 只包含非负的 `relativeTolerance` 和 `absoluteTolerance`;复验次数固定为一,不是规格字段。通过时状态为 `verified`,否则为 `verificationFailed`,不用多次平均掩盖差异。`verified` 只表示最佳搜索候选通过了这次绕过缓存的新鲜复验;它不表示搜索收敛,不证明达到稳态,也不是全局最优证明。复验已经通过时,不再默认建议重跑同一项复验。
|
||||
|
||||
若目标统计量是 `final`,schema v2 的 `objectiveEndpointTrend` 会报告一次不影响复验状态的末段趋势诊断。只有新鲜复验本身完整成功,而且目标序列覆盖统计窗口终点或计划仿真终点时才分析;复验未完成、序列无效或未覆盖计划终点时标为 `unavailable` 并给出原因,不能把部分曲线末尾当成计划终点。它优先检查目标统计时段最后 `5%`,为取得至少 `6` 个样本可向前扩展,但最多使用最后 `20%`;仍不足时标为 `insufficientData`。相对量的尺度取“末段最大绝对值、完整时段最大绝对值的 `1e-6` 倍、最小正正规浮点数”三者的最大值。
|
||||
|
||||
末段净相对变化至少 `1%` 且非零相邻变化的方向一致率至少 `80%` 时记录 `directionalChange`;零增量不稀释方向一致率。末段相对峰峰范围至少 `2%` 时记录 `tailVariability`。任一条件成立就标为 `materialChangeDetected`。只有方向变化条件成立时才称为上升或下降;仅由范围条件触发时方向为 `fluctuating`,报告明显波动,并分别展示首尾净变化与峰峰范围,不能把振荡描述成单向趋势。结构化结果同时记录末段起止时刻、样本数、起止值、变化量、平均变化率、相对变化、相对范围、方向和检测原因。
|
||||
|
||||
这项检查只用于提醒“终点快照可能仍处于动态过程”。`steadyStateProven` 始终为 `false`;`noMaterialChangeDetected` 只能表述为“该启发式检查未发现明显末端变化”,不能写成“系统已达到稳态”。检查发现明显变化时,必须指出 `final` 结果只支持所选终点时刻的比较,不能外推成稳态性能更优。
|
||||
|
||||
优化输出包括 `optimization-plan.json`、`optimization-events.jsonl`、`simulation-progress/evaluation-NNNN.jsonl`、`checkpoint.json`、`evaluations.csv`、`optimization-result.json` 和 `report.md`。`evaluations.csv` 对每次形成试验记录的预算请求写一行;若本地准备或连接在形成试验记录前抛错,预算槽位占用可能比 CSV 行数多。`checkpoint.json` 是审计快照而不是 resume 状态。
|
||||
|
||||
只有 `solutionStatus == verified` 时才生成 `best-parameters.json`、`best-system.xml`、`best-project.json`、`result.json`、完整 `results.csv` 和目标/响应约束的独立 SVG 曲线。`best-project.json` 将被优化参数的原表达式替换为普通 SI 数值,源 JSON 不变。`optimize` 仅在状态为 `verified` 时返回退出码 `0`,其他结果返回 `4`。
|
||||
|
||||
最终汇报必须使用这一口径:
|
||||
|
||||
> 这是实际完成仿真的搜索点中表现最好的可行候选,并已通过一次独立复验;复验不证明搜索收敛、系统达到稳态或全局最优。
|
||||
|
||||
不使用“已找到全局最优”、“必然最优”或其他超出有限搜索证据的措辞。最佳候选位于用户确认的参数边界时,只能报告它是当前边界内实际搜索得到的边界点;在用户确认更宽范围符合物理、安全和组件合同前,不建议直接放宽边界或启动扩边界搜索,也不把有限采样点概括成整个连续区间上的严格单调规律。
|
||||
|
||||
## 内部命令与 OpenClaw 路径
|
||||
|
||||
以下命令供 Skill 实现和故障排查使用;正常交互不得要求用户手工运行命令、创建规格文件或复制确认参数。
|
||||
|
||||
从仓库根目录先预览计划:
|
||||
|
||||
```powershell
|
||||
py -3.12 skills/system-simulation/scripts/optimization_skill.py plan PROJECT.json `
|
||||
--spec optimization-spec.json `
|
||||
--output-dir OUTPUT_DIR
|
||||
```
|
||||
|
||||
向用户展示计划并获得明确确认后,原样使用 `plan` 返回的三个值:
|
||||
|
||||
```powershell
|
||||
py -3.12 skills/system-simulation/scripts/optimization_skill.py optimize PROJECT.json `
|
||||
--spec optimization-spec.json `
|
||||
--output-dir OUTPUT_DIR `
|
||||
--expected-source-sha256 SOURCE_SHA256 `
|
||||
--expected-spec-sha256 SPEC_SHA256 `
|
||||
--confirmation-token CONFIRMATION_TOKEN `
|
||||
--confirmed
|
||||
```
|
||||
|
||||
Linux 使用已确认的 Python 3.12 解释器和相同参数:
|
||||
|
||||
```bash
|
||||
python3.12 skills/system-simulation/scripts/optimization_skill.py plan PROJECT.json \
|
||||
--spec optimization-spec.json \
|
||||
--output-dir OUTPUT_DIR
|
||||
|
||||
python3.12 skills/system-simulation/scripts/optimization_skill.py optimize PROJECT.json \
|
||||
--spec optimization-spec.json \
|
||||
--output-dir OUTPUT_DIR \
|
||||
--expected-source-sha256 SOURCE_SHA256 \
|
||||
--expected-spec-sha256 SPEC_SHA256 \
|
||||
--confirmation-token CONFIRMATION_TOKEN \
|
||||
--confirmed
|
||||
```
|
||||
|
||||
需要非默认后端或读取超时时,全局选项必须放在 `plan` / `optimize` 子命令之前,例如:
|
||||
|
||||
```bash
|
||||
python3.12 skills/system-simulation/scripts/optimization_skill.py \
|
||||
--base-url http://127.0.0.1:18082 --timeout 60 \
|
||||
plan PROJECT.json --spec optimization-spec.json --output-dir OUTPUT_DIR
|
||||
```
|
||||
|
||||
OpenClaw 中不假设当前目录是仓库根目录,使用 Skill 根目录占位符:
|
||||
|
||||
```bash
|
||||
python3.12 "{baseDir}/scripts/optimization_skill.py" plan PROJECT.json \
|
||||
--spec optimization-spec.json \
|
||||
--output-dir OUTPUT_DIR
|
||||
```
|
||||
|
||||
也可以先进入本 Skill 目录,再使用 `scripts/optimization_skill.py plan ...` 和 `scripts/optimization_skill.py optimize ...`。不根据用户主目录、OpenClaw 数据目录或仓库名称猜测脚本路径。持续消费 JSONL 进展,定期报告已使用/最大后端提交数、当前代数、最佳可行目标、失败数、缓存命中和内层仿真阶段;不因仿真时间短暂停滞而声称卡死。
|
||||
|
||||
通常省略 `--optimization-id` 让脚本生成唯一 ID。若显式指定,同一后端任务保留窗口内必须使用新的 ID;快速复用旧 ID 会被后端按冲突拒绝。
|
||||
File diff suppressed because it is too large.
Load diff
@@ -27,7 +27,7 @@ import uuid
|
||||
import xml.etree.ElementTree as ET
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable, Mapping, Sequence
|
||||
from typing import Any, Callable, Iterable, Mapping, Sequence
|
||||
|
||||
|
||||
MAX_INPUT_BYTES = 5 * 1024 * 1024
|
||||
@@ -1294,12 +1294,16 @@ def _read_simulation_stream(
|
||||
simulation_id: str,
|
||||
timeout: float,
|
||||
progress_path: Path,
|
||||
*,
|
||||
event_sink: Callable[[object], None] | None = None,
|
||||
full_result: str | None = "result.json",
|
||||
) -> tuple[dict[str, object] | None, dict[str, object] | None]:
|
||||
final_result: dict[str, object] | None = None
|
||||
final_error: dict[str, object] | None = None
|
||||
public_phase: str | None = None
|
||||
public_progress: float | None = None
|
||||
public_emit_time = 0.0
|
||||
sink = event_sink or emit_json
|
||||
try:
|
||||
progress = progress_path.open("x", encoding="utf-8", newline="\n")
|
||||
except OSError as exc:
|
||||
@@ -1333,7 +1337,11 @@ def _read_simulation_stream(
|
||||
)
|
||||
event_kind = event.get("event")
|
||||
logged_event = (
|
||||
_public_result_event(event, simulation_id)
|
||||
_public_result_event(
|
||||
event,
|
||||
simulation_id,
|
||||
full_result=full_result,
|
||||
)
|
||||
if event_kind == "result"
|
||||
else event
|
||||
)
|
||||
@@ -1348,16 +1356,16 @@ def _read_simulation_stream(
|
||||
previous_progress=public_progress,
|
||||
seconds_since_emit=now - public_emit_time,
|
||||
):
|
||||
emit_json(event)
|
||||
sink(event)
|
||||
public_phase = str(event.get("phase") or "")
|
||||
raw_progress = event.get("progress")
|
||||
if isinstance(raw_progress, (int, float)) and not isinstance(raw_progress, bool):
|
||||
public_progress = float(raw_progress)
|
||||
public_emit_time = now
|
||||
elif event_kind == "result":
|
||||
emit_json(logged_event)
|
||||
sink(logged_event)
|
||||
else:
|
||||
emit_json(event)
|
||||
sink(event)
|
||||
if event_kind == "result":
|
||||
result = event.get("result")
|
||||
if isinstance(result, dict):
|
||||
|
||||
Reference in new issue
Block a user