Files

336 lines
32 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 单目标参数优化工作流
## 执行入口与范围
本 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 合同。ReactFlow JSON 中普通数值参数已经是 SI 值,`parameterUnits` 只是编辑器显示信息,不能据此把普通数值再次换算;表达式所需的显示单位换算由后端完成。如果用户用显示单位给出边界,才把用户输入换算为 SI。普通 `plan --present` 成功后,`presentation.designVariables[].current` 和 `unit` 是计划摘要中当前值与单位的唯一依据;完整审计计划中的对应字段为 `designVariables[].initial` 和 `unit`。不要从源 JSON 重新计算显示值,若其他信息与它矛盾则先排查而不是向用户展示两套数值。用户给出的边界已经使用该 SI 单位且没有矛盾时,不主动解释 `parameterUnits` 或添加显示单位换算旁注。后端在计划阶段将源 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`;普通流程的 stdout 只返回严格白名单的展示视图,不包含这些执行凭据和搜索内部字段。
计划中的 `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 内部保管,不向用户倾倒;只有用户主动要求审计细节,或排查计划过期/文件变化时才展示。
普通计划摘要只需要回答:
- 要改善哪个结果,用什么统计口径;
- 调整哪些参数,各自在什么范围;
- 有哪些响应约束;
- 采用默认还是用户指定的搜索配置、最多占用多少仿真预算槽位、停止启动新搜索候选的时限,以及可使总耗时超过该时限的在途仿真和预留复验;
- 哪些参数连续性或工程边界属于假设;
- 输出写到哪里,并明确源模型不变。
计划正文只描述即将执行的运行,不把历史结果回顾、搜索审计明细或对本次结果的预测混入计划。默认设置不存在警告时,“采用默认搜索设置”已足够,不再把内部配置、端点播种或合同判定字段展开成技术清单。若安全展示视图包含 `nonDefaultSettings`,只列出其中实际偏离推荐默认值、并会随本计划一起确认的设置;不要反过来读取完整审计计划扩展技术细节。
用户尚未限制本轮只做计划、且接下来是否执行需要确认时,摘要后只问一次中性的自然问题,例如:“就按这个方案开始吗?”不要主动把换目标、放宽参数边界或其他扩展范围列成备选项。用户明确同意后直接执行,不再追加连续性声明、算法参数或 token 确认。若用户说“只要计划”“暂时不要运行”或同等意思,则交付摘要后直接陈述会停在计划阶段,不在本轮询问是否开始,等用户之后主动要求。用户主动提出调整时再讨论;涉及放宽工程边界时,必须先确认新的范围符合物理、安全和组件合同。
无警告且使用默认设置时,按下列内容边界组织计划回复;可以顺应用户语言调整措辞,但不要增加其他技术段落:
```text
优化目标:让哪个结果按什么统计口径变大、变小或接近目标值。
调整参数:参数名称、脚本 plan 返回的当前 SI 值、用户确认的 SI 范围。
响应约束:列出约束;没有就说无。
运行上限:采用默认搜索设置,最多占用多少仿真预算槽位;搜索到时后不再启动新候选,但会等在途仿真结束,并为找到的最佳可行候选预留一次独立复验。
重要假设:用一句普通语言说明参数按连续物理量处理且不改变模式或结构。
输出:plan 返回的新目录完整绝对路径,源模型不变。
结束语:若本轮可以询问执行,则问“就按这个方案开始吗?”;若用户说暂时不要运行,则说“计划已准备好,我会停在这里,等你之后明确说开始。”
```
正式计划回复从第一个字起使用用户当前语言并直接进入计划内容;不加过程旁白,不显示 `warnings: []` 等内部状态,不复述内部枚举名,也不在计划后追加单位科普、算法原理、历史回顾、结果预测或调整建议。输出目录照抄 `plan` 返回的完整绝对路径,不用省略号或相对路径。只有真实警告、无法消除的单位歧义或其他需要用户决策的问题,才在相应条目中简短说明。
## 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`。
只有 `solutionStatus == verified` 时,最终汇报才使用这一口径:
> 这是实际完成仿真的搜索点中表现最好的可行候选,并已通过一次独立复验;复验不证明搜索收敛、系统达到稳态或全局最优。
不使用“已找到全局最优”、“必然最优”或其他超出有限搜索证据的措辞。最佳候选位于用户确认的参数边界时,只能报告它是当前边界内实际搜索得到的边界点;在用户确认更宽范围符合物理、安全和组件合同前,不建议直接放宽边界或启动扩边界搜索,也不把有限采样点概括成整个连续区间上的严格单调规律。
## 内部命令与 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-file PLAN_FILE `
--present
```
普通 Skill 流程必须同时使用 `--plan-file` 和 `--present`:完整审计计划以 `0600` 权限独占写入 `PLAN_FILE`,stdout 只返回用户计划所需的白名单字段。`PLAN_FILE` 必须是位于 `OUTPUT_DIR` 外的新文件,不能覆盖既有文件,也不能与输出目录互为祖先或后代;默认用本次输出目录名加 UTC 时间戳或随机后缀生成同级文件,不复用固定的临时文件名。省略这两个选项的旧式完整 stdout 只用于兼容测试或显式审计排障,不用于普通对话。
向用户展示计划并获得明确确认后,在内部从 `PLAN_FILE` 读取并原样使用源 SHA、规格 SHA 和 `confirmationToken`,不要向用户展示:
```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 \
--plan-file PLAN_FILE \
--present
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 \
--plan-file PLAN_FILE --present
```
OpenClaw 中不假设当前目录是仓库根目录,使用 Skill 根目录占位符:
```bash
python3.12 "{baseDir}/scripts/optimization_skill.py" plan PROJECT.json \
--spec optimization-spec.json \
--output-dir OUTPUT_DIR \
--plan-file PLAN_FILE \
--present
```
也可以先进入本 Skill 目录,再使用 `scripts/optimization_skill.py plan ...` 和 `scripts/optimization_skill.py optimize ...`。不根据用户主目录、OpenClaw 数据目录或仓库名称猜测脚本路径。持续消费 JSONL 进展,定期报告已占用/最大仿真预算槽位、已完成和失败记录、当前代数、最佳可行目标、缓存命中和内层仿真阶段;不把槽位占用说成后端已接收或已完成,也不因仿真时间短暂停滞而声称卡死。
通常省略 `--optimization-id` 让脚本生成唯一 ID。若显式指定,同一后端任务保留窗口内必须使用新的 ID;快速复用旧 ID 会被后端按冲突拒绝。