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

13 KiB
Raw Permalink Blame History

排产算法 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);GA→遗传算法搜索订单顺序与可行产线;HYBRID→RULE 热启动 CP-SAT。三类高级引擎最终都复用本引擎的班次占槽与硬约束物化。


2. 主流程

① 收集待排订单项(过滤 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。

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.v2。父进程仍负责业务问题构造、门禁与结果物化;child 只接收可序列化的 world/entries/params,执行优化并返回带 requestId/digest、运行时身份和 success/error marker 的版本化 JSON。v2 将生产 optimize_line_assignment、整约束 diagnose_constraint_baseline/removal 与 RHS 参数 diagnose_rhs_baseline/perturbation 三类 operation 严格分离,并绑定 invocationId、relaxed constraint 或单个 RHS 扰动、assumption 实例集合和 RHS 参数状态。

RHS 诊断只支持正向 one-at-a-time 有限差分:C8_due_date_allowance 给全部 fixed 待排条目的延期目标 due RHS 增加分钟,C12_team_capacity / C12_tooling_capacity 给一个真实激活的 Cumulative 资源增加 capacity;Round 82 再扩展具体 line/day 的 C7 分钟实例。C8 不改变可行域;C12 只接受 intervalCount>=2 的资源。OPTIMAL 才给精确差;FEASIBLE 只给 incumbent/bound 区间,且所有结果均不是对偶、跨参数不可相加。

Round 82 起,C7 已进入 CP-SAT 可行模型:具体产线上的候选工序完整 setup+run 分钟、激活的顺序换型分钟和既有已发布/冻结工单固定负荷,按开工自然日整笔记入 line/day,总和不超过该日 shiftCalendar 有效分钟。长工序跨午夜仍归开工日;该口径为 start-day-full-duration.v1,不是自然日 overlap 分摊。Round 84 虽已直接物化 CP timing/segments,C7 仍保持按逻辑工序开工日整笔记账,不改为按 segment 自然日分摊。效率已作用在工序时长,容量不重复乘效率;件/日 capacityPerDay 不参与分钟 RHS。

C7_line_day_capacity_minutes 只允许显式 line-day:{lineId}:{date} 实例正向增加 1~1440 分钟。非工作日不能通过该参数启用,必须修改日历。物化后 exact C7 复核写入 materializedC7Validation;超载仍以 CAPACITY 冲突阻断发布,CP solve status 保留用于区分模型求解与物化偏差。

父进程必须同时验证:

  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 与 operationSlots 才能绑定为 _cpOperationTiming 并进入父进程 materialize_schedule();身份、路线、资源、segments、前后序或容量任一不一致都在写入前拒绝。

失败语义固定为 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,禁止物化。

Round 83 C3 班次日历分段模型

CP 时间模型使用 cp-calendar-segmented.v1。每条产线的 shiftCalendar 先扣除休息段并归一化为有效加工窗口;正常求解的 selectedMode=calendar-boundary-only,工序可以跨多个窗口,但暂停和续作只能发生在窗口边界。非工作日与休息段不能加工,日历覆盖或结构非法时失败关闭。

逻辑工序与加工段分开建模:C1 约束逻辑工序的首段开始到末段结束包络;C2 工位独占和 C12 班组/工装 Cumulative 只消费实际 processing segments;C2 实例身份按逻辑工序对计数,不能把同一工序的多个段当作互相冲突的工序。C7 仍按逻辑工序开工日记完整 setup+run 分钟,保持 start-day-full-duration.v1,不在本轮改成自然日 overlap 分摊。

每个 operationSlot 输出:

  • segments[]:实际加工段的 start/end 分钟;
  • processingMinutes:加工段分钟总和;
  • elapsedSpanMinutes:首段开始到末段结束的跨度;
  • pauseMinutes:跨度减加工分钟;
  • segmentCount、calendarCompliant、calendarMode。

整约束 C3 removal 使用独立的 continuous 模式,恢复单一连续 interval;其 calendar digest、anchor/horizon/coverage、bucket/window/segment 计数和 model identity 必须与 baseline 完全一致,只有 selectedMode 与 active 可按诊断语义变化。模型 segment selector 估算超过 50,000 时直接拒绝。

主 CP 求解永不把启发式 hint 固定为硬值。第一次求解返回 UNKNOWN 后,可用已经验证可行的固定 hint 进行一次 fallback,且触发状态、首轮/fallback 时间都写入 solverMeta。

Round 83 当时最终物化仍复用 Rule 占槽器,materializedC3Validation.cpTimingApplied=false,以 C3 硬冲突关闭偏差;该历史边界已由 Round 84 的直接物化契约替代。

Round 84 经父进程验证的 CP 时间直接物化

普通 CP/HYBRID 仅在 status 为 OPTIMAL 或 FEASIBLE 时进入直接 timing 路径。父进程首先复用 _validate_operation_slots 校验稳定订单项身份、默认路线步骤双射、产线/工位/team/tooling、segment 汇总、C1 前后序、C2/C12 容量和 C3 日历拓扑;随后再次绑定 normalized calendar digest 和窗口身份,把每个 slot 转为绝对 plannedStart/End 与 plannedSegments。任一漂移在版本、PO、WO 创建前抛出 SOLVER_RESPONSE_INVALID。

通过预检后,每个 ordered entry 附带 _cpOperationTiming。RuleEngine 仍承担版本、PO、WO 和审计对象的原子持久化,但不再调用自身占槽逻辑重新计算可行 CP 时间;每个 WO 原样写入:

  • plannedStartTime / plannedEndTime 与每段 plannedSegments;
  • processingMinutes / elapsedSpanMinutes / pauseMinutes / segmentCount;
  • routingStepId / logicalOperationKey / cpOrderIndex / cpTimingSource=operationSlots;
  • CP 选定的 line/workstation/team/tooling 与 setup/changeover。

版本元数据固定为 placement=cp-calendar-segmented-direct、materializedBy=RuleEngine.validated-cp-timing、directlyConsumedByMaterializer=true、operationTimingValidation.passed=true。物化后 C3 必须 cpTimingApplied=true 并逐工序与 operationSlots 精确对齐;C7 继续使用 start-day-full-duration.v1 复核。非可行状态保持启发式排序回退边界,不把诊断 operation 物化。

诊断响应校验按请求语义区分生产与反事实:移除 C2 时允许工位重叠,移除 C12 时允许对应累计资源超基线容量;C12 RHS 扰动按“父进程世界基础容量 + 已绑定正增量”验证。未授权松弛、资源容量伪造或累计元数据漂移继续失败关闭。