aps-kernel — Metis 谋序(APS 标准版)
运筹帷幄,谋定后动 —— 同衡·谋序·同枢,智造同频。 One Truth. One Plan. One Rhythm.
可嵌入的确定性排产内核:CP-SAT 求解器、独立复核器、 工序-资源适配数据模型、设备槽位与班次日历、headless CLI。
标准版 vs 专业版:本包只含可嵌入内核(无 UI / 无服务 / 无 LLM)。 甘特图前端、Web 控制台、AI 编排层(Copilot)、多租户 RLS、审批与 IM 通道 属专业版(Docker 镜像 + docker compose)。
🌐 官网:https://aps.zhinengti.online | 家族:Nereus 同衡(数字孪生)· Hermes 同枢(物流调度)
当前状态
v0.1.0.dev0 — P0 阶段(骨架 + 数据模型 + 校验 + 演示)
| 模块 | 状态 |
|---|---|
| 数据模型(6 个核心 dataclass) | ✅ 完成 |
| 输入校验(三项 P0 准则 + 5 类检查) | ✅ 完成 |
| Provenance(快照 hash + 配置/源码/求解器版本) | ✅ 完成 |
| 独立复核器(7 项检查) | ✅ 完成 |
| 演示(20 工单 / 6 槽位 / 2 班制) | ✅ 7/7 通过 |
| 规则引擎(P1) | ⬜ 未开始 |
| CP-SAT 求解(P3) | ⬜ 未开始(当前为贪心,仅证明链路可行) |
⚠️ 当前排产用贪心实现,只给可行解,gap=None 表示未证明最优。
CP-SAT 求解器是 P3 阶段的工作。
快速开始
pip install -e .
python -m aps_kernel.demo # 完整演示
python -m pytest tests/ -q # 37 个测试
演示输出(实测):
aps-kernel 演示
数据集:20 工单 · 160 道工序 · 4 资源(含 1 台调机技师)· 2 班制工作日
── 输入校验 ─
错误 0 · 警告 0 · 提示 3
── 排产结果 ─
状态 FEASIBLE
makespan 1600 分钟(26.7 小时)
未排 0 道工序
── Provenance ─
数据快照 sha256:7bcded5ff679
配置版本 fdb29c7bf7d7
源码版本 src-20b46ecf6df5
OR-Tools 9.15.6755
求解状态 FEASIBLE(gap —)
── 独立复核器(不信任求解器自报时长)──
✓ 任务守恒 160 已排 / 0 未排 / 共 160
✓ 设备资格 160/160 工序在候选资源内
✓ 时长重算 160/160 与输入工时一致
✓ 槽位不重叠 6 槽位 · 160 区间 · 0 重叠
✓ 工序先后 20 工单 · 0 违序
✓ 换型可行 160 工序 · 累计换型 0 分钟
✓ 无工序丢失 0 工序既未排也未标记未排
复核结论 ✅ 7/7 通过
三条不可妥协的建模准则
这三条来自 frePPLe 与 device-aps 的实战教训,已用测试锁定:
1. 空适配 ≠ 全设备可用
Operation(resources=()) # ← ERROR,不是"所有设备都能做"
Aethon 现状:RouteStep 只有单值 work_center_id,无法表达 FJSP。
没有它,求解器只能做单资源分配。
2. 容量是池不是槽位
Resource(slots=(EquipmentSlot("S1", ...), EquipmentSlot("S2", ...)))
# 一台 3 腔注塑机 = 3 个 slot,可同时跑 3 个部分工单
若直接把设备当槽位,数据库层的排除约束会错误地限制成"一台设备同时只能跑一个工单"。
3. 换型本身要占用资源
Resource(setup_resource="RES-SETUP") # 调机技师
换模要调机技师,技师不空闲就换不了模。 这是工业现场最常被漏掉的约束。
独立复核器
校验器不调用求解器,也不信任排程结果自带的加工时间。 它使用输入数据重新核对,因此可以发现导出或后处理阶段篡改结果的问题。 —— 抄自 rtd
validation.py的设计原则
7 项检查:
| 检查 | 内容 |
|---|---|
| 任务守恒 | scheduled ∪ unscheduled == pending 且交集为空 |
| 设备资格 | 每个工序的资源必须在其候选集内 |
| 时长重算 | 用输入的 duration 重算,不信求解器自报 |
| 槽位不重叠 | 同 slot 区间不重叠([)' 半开,相接不算) |
| 工序先后 | 同一工单内工序不逆序 |
| 换型可行 | 相邻任务的换型有 SetupRule 匹配 |
| 无工序丢失 | 不允许工序"消失" |
复核器真的有用:它抓出了两个真实 bug
本项目的第一版演示就抓出了自己的两个 bug:
| Bug | 现象 | 根因 | 修复 |
|---|---|---|---|
| 浮点工时截断 | 6 处 1 分钟重叠 | int(22.5) = 22 排程用截断值,复核用真实值 |
Assignment.end_minute 改向上取整 |
| 多槽位未全遍历 | makespan 2551 → 1580 | 每个资源只取第一个槽位 | 遍历全部 slot |
如果没有独立复核器,这两个 bug 会一直留到客户现场。
确定性的机制(为什么可以信)
抄 cps-twin 的确定性文化:
| 机制 | 本项目实现 |
|---|---|
| 固定时间栅格 | 分钟偏移 start_minute,墙钟不进结果 |
| 具名 RNG | 演示数据用固定 seed,同 seed 同数据 |
| 快照 hash | data_snapshot_hash = sha256(规范化输入) |
| 配置版本 | config_version = sha256(规则配置) |
| 源码版本 | code_version(git commit 或源码 hash) |
| 求解器版本 | solver_version(ortools.__version__) |
目标:同一数据快照 + 同一配置版本 → 同一输出。 解决 cp_sat_demo 自陈的 "同一排程编号重开会得到不同结果"缺陷。
求解状态诚实报告
solver_status = "FEASIBLE" # 找到可行解
gap = None # ⚠️ 未证明最优
绝不伪称 OPTIMAL。 这是与市面黑箱求解器的核心差异。
行业参数必须标注来源
SetupRule(
priority=100, from_pattern=".*", to_pattern=".*",
duration_minutes=35.0,
source="行业基准:注塑跨族换型 30-60 分钟(symestic 2026)", # ⚠️ 必填
)
校验器会为未标注来源的换型规则报 WARNING。
当前已引用的基准值(见 docs/规则与权重KnowHow来源.md):
| 参数 | 值 | 来源 |
|---|---|---|
| 注塑跨族换型 | 30-60 min | symestic(引 ISO 22400) |
| 注塑手工螺栓下限 | 10-20 min | kinghou.net |
| 两班设备利用率 | 36-45% | symestic |
| 单班设备利用率 | 18-22% | symestic |
项目结构
src/aps_kernel/
├── models.py # 数据模型(6 个核心 dataclass)
├── validator.py # 输入校验(三项 P0 准则)
├── provenance.py # 快照 hash + 版本绑定
└── demo.py # 演示(含独立复核器)
tests/
├── test_models.py # 模型与校验器测试
└── test_validator.py # 复核器与确定性测试
docs/
└── aps详细实施计划.md # P0-P7 阶段计划
License
MIT
⚠️ 行业规则库与默认权重表属专业版 —— 见 docs/aps免费版与专业版功能对比.md
Metadata
Release files for aps-kernel 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aps_kernel-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Release files / aps_kernel-0.4.0-py3-none-any.whl
| Download URL | aps_kernel-0.4.0-py3-none-any.whl |
|---|---|
| Size | 99.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
69ba20a95be5fc3101b1c4a986286cf6498fa812aa426a13a821081ac73e7833
|
|
BLAKE2b-256 checksum How to use checksums |
c0d7f627ec67e628b70221605f00a0e74252742729fb78e91a707cbda3744452
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|