aps-agent/docs/algorithm/scheduling-v1.md

127 lines
7.3 KiB
Markdown
Raw 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.

# 排产算法 v1(已落地 · RULE)
> 代码:`server/engines/rule_engine.py`
> 测试:`tests/golden/test_rule_engine.py`
> 定位:构造启发式(策略排序 + 顺排占槽),毫秒级;**不是**最优解求解器。
---
## 1. 输入 / 输出
| | 内容 |
| --- | --- |
| 输入 | 世界状态 `world` + `EngineParams`(策略、展望期、起算日、订单子集、引擎类型名;可选 `deliveryBufferRatio` / `freezeWindowHours`) |
| 写出 | DRAFT `scheduleVersion`、生产订单 PO、工单 WO、`conflicts`、版本 KPI 字段 |
| 权力 | 由工作流以 P1 调用;本引擎只改传入的内存 `world` |
请求的 `engineType`:`RULE`→规则引擎;`CP`→OR-Tools CP-SAT(见 [scheduling-cp-v1.md](./scheduling-cp-v1.md));`GA`→遗传算法搜索订单顺序与可行产线;`HYBRID`→RULE 热启动 CP-SAT。三类高级引擎最终都复用本引擎的班次占槽与硬约束物化。
---
## 2. 主流程
```text
① 收集待排订单项(过滤 CANCELLED/COMPLETED 订单与 COMPLETED 明细)
② 按 strategyTemplate 排序(OR-02:综合/均衡以 `-customerLevelWeight` 为第一键;交期优先仍以交期为主)
③ 创建 DRAFT 版本(版本链 parentVersionId)
④ 逐项:选产线 → 按工艺步骤拆 WO → 在班次窗内找槽(可跨天)→ 齐套检查
⑤ 版本内冲突扫描(产能/维保等只扫本版新 WO,避免历史污染)
⑥ 汇总 KPI(延期、利用率等)
```
与 legacy `aps-frontend` 的刻意偏差(已测固化):
1. 容量/维保冲突只扫**本版**新工单
2. 利用率只按本版工单统计
---
## 3. 策略模板(排序键)
| strategyTemplate | 排序逻辑(实现) |
| --- | --- |
| `DELIVERY_FIRST` | `(deliveryDate, -levelWeight, priority)` ≈ EDD 为主 |
| `FIFO` | `(orderDate, -levelWeight, priority)` |
| `CAPACITY_BALANCE` | `(-levelWeight, priority, deliveryDate)`;选线时偏好累计占用少的线 |
| `CHANGEOVER_MIN` | 贪心最近邻:按换型矩阵选下一单;选线偏好同族续排(SC-07) |
| `CAMPAIGN` | 同产品交期窗口合并为战役 PO,再走换型最小化序(SC-08) |
| `COST_FIRST` | 同 `CHANGEOVER_MIN`(以换型分钟为成本代理) |
| 其他 / `COMPREHENSIVE` | `(-levelWeight, priority, deliveryDate)`(OR-02 客户等级权重) |
`levelWeight` 来自 `scheduleParams.customerLevelWeights`(默认 VIP=3/A=2/B=1/C=1)。
**纠偏**:`COST_FIRST` 仍**不是**财务成本最优;当前以 MD-06 换型矩阵分钟为代理。真成本目标待后续切片。
---
## 4. 占槽与资源
- 产线:由产品-产线关系 `find_product_lines`;无可用线 → `NO_LINE`
- 工位:工序资格 `find_workstation_for_operation`;无 → `NO_WORKSTATION`
- 时间:班次日历内找连续可用分钟;长工单可跨天拼接
- 展望期:默认 `planningHorizonDays`(种子参数 14)
- 齐套:BOM vs 库存/在途逻辑;不足 → `MATERIAL_SHORTAGE`(可推迟或标冲突,见代码分支)
---
## 5. 冲突类型(引擎实际产出)
| conflictType | 含义 |
| --- | --- |
| `NO_LINE` | 产品无可用产线 |
| `NO_WORKSTATION` | 工序无合格工位 |
| `MATERIAL_SHORTAGE` | 齐套不足 |
| `DELAY` | 相对交期延误 |
| `CAPACITY` | 产线日产能/占用冲突 |
| `EQUIPMENT` | 维保等设备不可用 |
前端可高亮超期/冲突;**一键修复工作流未落地**。
---
## 6. 明确未做(勿在对外材料写成已有)
- 工序级 CP Interval 全模型 / NSGA-II/Pareto / LNS(产线级 CP、GA 与 HYBRID 首切片已落地)
- 换型全局最优排序(当前为贪心最近邻)
- 战役可配窗口/最大批量与拆批回退
- 插单 LNS 局部修复 / 全量重排决策表(plan §9.11)
- 蒙特卡洛鲁棒性 / 参数推荐(Tornado one-at-a-time 已落地,见 SC-06)
见 [roadmap.md](./roadmap.md)。
## Closed-loop Scheduling Kernel v1 / Contract V2
- 统一输入:Requirement、SupplyEvent、OperationActivity、Resource、日历/维保、目标与硬约束策略。
- 统一输出:ScheduledActivity、SupplyDecision、Pegging、UnscheduledRequirement、HardViolation、Assumption与 Provenance。
- 必须先通过独立 Validator 才可物化;检查工序顺序、资源重叠/能力、日历/维保、物料时间、pegging 守恒、activity identity 和源哈希漂移。
- RULE/Pool/外部 Skill 保留 V1 兼容层,但真实主路径必须映射 V2 并失败关闭。
### Round 65 审计补强:多资源与确定性
- `OperationActivity.resourceRequirements` 使用 `ActivityResourceRequirement` 同时表达 EQUIPMENT / TEAM / TOOLING 候选、能力、容量单位和模具寿命消耗。
- `ScheduledActivity.resourceAllocations` 记录每类资源的实际分配;缺少必需 TEAM/TOOLING、种类不匹配、能力不匹配、容量不足或模具寿命不足均为硬违规。
- 设备分配沿 WORKSTATION -> LINE -> WORKSHOP -> FACTORY 父链汇总负荷;班组和模具分别计算累计容量与寿命,避免重复计费。
- 活动身份哈希纳入多资源要求;planning sourceHash 对自动生成 DRAFT 建议的技术 ID/时间去噪,但保留物料、数量、日期、供应类型和状态等业务语义。
### Round 66 求解器执行隔离与失败关闭
CP/HYBRID 的优化调用采用独立进程协议 `aps.solver-process.v1`。父进程仍负责业务问题构造、门禁与结果物化;child 只接收可序列化的 world/entries/params,执行优化并返回带 requestId/digest、运行时身份和 success/error marker 的版本化 JSON。
父进程必须同时验证:
1. 进程在 OS 级时限内完成,且 stdout/stderr 不含 fatal marker;即使 exit code 为 0,fatal marker 仍判失败。
2. response 协议、requestId、摘要和 schema 与请求匹配。
3. Python executable/base executable、`site.ENABLE_USER_SITE`、OR-Tools/NumPy/Pandas/Protobuf 来源位于受信任 runtime prefix。
4. 只有校验通过的 ordered entries 才进入父进程 `materialize_schedule()`。
失败语义固定为 fail-closed:CP 与 HYBRID 均返回 `UNAVAILABLE`,不创建 PO/WO,不产生可发布版本;HYBRID 不静默退回 RULE。timeout 必须终止完整进程树,父进程与 API 服务继续存活,业务 world 保持原子。
真实验收:复制 `world.json` 的 CP/HYBRID 均为 `OPTIMAL`、1 PO / 10 WO / 10 operationSlots、`runtimeSafe=true`;fatal 与 timeout 注入均满足上述失败语义。复制 MOM 数据虽然求解器为 `OPTIMAL`,但现有业务主数据阻断仍为 0 PO/WO、1 slot/1 conflict,这不是现场主数据完成证明。
源码 Sidecar 在 Python `-I` 下不会信任任意 `PYTHONPATH`。真实冒烟发现隔离模式会移除源码 checkout import root 后,只允许 bootstrap 已解析的 trusted source root;`test_source_solver_child_entry_runs_under_isolated_python` 固化该边界。现有冻结 EXE 尚未重建,因此新 `--solver-child` 的打包验证仍是构建后验收,不能由源码冒烟替代。
- ???? success response ???? `responseDigest`??????/????????????????pipeline/status?objective/gap ? operationSlots ???????? `SOLVER_RESPONSE_INVALID`??????
- 父进程对 success response 强制重算 `responseDigest`,并校验请求/响应条目完整排列、关键业务字段、pipeline/status、objective/gap 与 operationSlots 覆盖;不一致统一 `SOLVER_RESPONSE_INVALID`,禁止物化。