开篇:供应链计划为什么是最上游的决策,以及我为什么选 frePPLe

做完 WMS 和 TMS 之后,我以为自己已经把"货"管明白了——库里的货知道在哪、路上的货知道到哪。直到有一天甲方老板盯着满仓库的呆滞库存问了我一个问题:"这些系统都很勤快,可你告诉我,下个月到底该生产什么、采购什么、往哪个仓调多少货?"
我愣了一下。因为这恰恰是 WMS 和 TMS 都回答不了的问题。它们是"执行层":计划定了,WMS 负责把货摆上货架、TMS 负责把货运到地方。可决定"生产什么、什么时候产、备多少料、留多少库存"的,是更上游的一类系统——供应链计划,行业里叫 SCP(Supply Chain Planning),其中最难、最值钱的那一块叫 APS(Advanced Planning and Scheduling,高级计划与排程)。
这一章我不写代码细节,先把一个最关键的决策讲透:供应链计划这种东西为什么难、值不值得从零自研,以及我在 GitHub、Gitee 上翻了快两周、把四个候选项目逐个下载体检之后,为什么最后选了 frePPLe 这个用 C++ 和 Python 写的开源底座——哪怕我的主语言是 Java。
先想清楚:SCP/APS 到底比 ERP 里的 MRP 强在哪
很多人第一次接触这类系统会问:我 ERP 里不是有个 MRP 物料需求计划吗?跑一下不就把该买什么、该做什么算出来了?
区别在"有没有约束"这两个字上。传统 MRP 的逻辑是这样的:你给它一张销售订单和一份 BOM(物料清单),它就把成品一层层展开成零部件,按提前期从交期往前倒推每个物料的下单时间。听着挺美,但它做了三个致命假设——产能无限、物料永远齐套、供应商绝不延迟。结果就是 MRP 算出来的计划经常自相矛盾:它让十台设备同一天都满负荷 200%,让某个还没到货的原材料今天就被领用。计划员只能拿着一张理论正确、实际执行不了的单子,再靠经验手工改。
APS 要解决的就是这个问题。它在做计划时把真实世界的约束全部塞进同一个模型里一起算:设备和人力是有限产能、物料有库存和在途、换型要花时间、供应商有提前期和批量要求、物料和设备还能互相替代。它不追求"理论最优",而是给出一张在约束下真正可执行的计划,并且诚实地告诉你:哪张订单会晚交、瓶颈卡在哪个资源、缺口到底有多大。
更完整的 SCP 还把决策往两头延伸:往上游连需求预测(根据历史销量预测未来要备多少货,而不是等订单来了才反应),往下游连库存与分销计划(网络里有多个仓库时,安全库存放哪、仓与仓之间怎么补货)。预测→库存→分销→生产排程,这是一条完整的决策链。而这条链上任何一个决策做错,都会被下游的 WMS、TMS 用十倍的工作量放大。 计划备多了是呆滞库存和占用的现金,备少了是缺货和违约。这就是我说它是"最上游决策"的原因。
选型:四个候选项目的体检现场
定了要找开源底座,我还是老规矩:不看 star、不迷信 README 的功能清单,直接下载源码、翻建表模型、读求解器实现。四个候选,过程如实摆出来。
候选一:Timefold Solver(OptaPlanner 的继任者)。 这是 Java 圈子里最主流的规划求解引擎,Apache 2.0 协议、纯 Java、社区活跃,我之前做车间机台排产那张卡片时就研究过它。但这次我把它排除了,原因有二:其一,它本质是一个求解器库,不是开箱即用的计划系统——你得自己建实体、自己写约束评分、自己做界面和数据导入,等于只给了你发动机,整车还得自己造;其二,它最擅长的是车间 job-shop 级别的排程优化,需求预测、安全库存、多仓分销这些 SCP 的另一半它完全不管,做出来会和我已有的车间排产卡高度重叠。不过我要把话说回来:如果你的目标是在 Java 系统里内嵌一个排程内核,Timefold 仍然是首选,这个我在最后一章还会讲。
候选二:Gitee 上的 peanut-aps、jvs-aps、FutureSky 等。 这几个在国内搜索里排名靠前,但 clone 下来一翻就露馅:要么是培训机构的课程配套、仓库里压根没有 LICENSE 文件(默认作者保留一切权利,商用等于裸奔),要么模型薄得只剩几张演示表,求解逻辑是写死的演示数据。当个概念演示看看可以,当生产底座不行,放弃。
候选三:Apache OFBiz、iDempiere 这类重型开源 ERP。 它们模块齐全,也都带计划功能。但它们的计划模块本质还是经典 MRP——无限产能、倒排,正是我前面说的那个短板;而且为了一段计划能力背上整套厚重的 ERP 框架,技术栈也偏老,性价比极低。ERP 是记账和流程的中心,不是高级求解器,这个定位要分清楚。
候选四:frePPLe。 GitHub 上的自我介绍是 "open source supply chain planning",主打需求预测加生产计划排程。下载、解压、翻完源码之后,我决定就是它。下面是我的体检报告。
frePPLe 体检报告:我实际验证了什么
第一,许可证——这是我这次最大的惊喜,也必须诚实地讲清楚版本问题。 我打开当前 master 分支根目录的 COPYING,开头写得很明白:frePPLe 采用双授权,你可以免费按 MIT 许可证使用,也可以向 frePPLe bv 购买商业授权获得额外功能和支持。再翻 freppledb/input/models/item.py 的文件头,也是标准的 MIT 许可全文。MIT 是最宽松的协议之一,商用、二开、私有化都行,只需保留版权声明。这里我要提醒一个容易踩的坑:frePPLe 的早期版本(6.x 那一代)社区版曾经是 AGPL v3 强传染协议,你如果从某些旧镜像下载老包,拿到的可能是 AGPL;但当前主线已经转成 MIT,商业授权卖的是企业版的增强求解器和连接器,而不是把社区版锁死。选型时认准新版本就好。
第二,技术世代。 我去看构建配置,CMakeLists.txt 里赫然写着 set(CMAKE_CXX_STANDARD 23)——计划引擎用的是 C++23,非常激进。Python 侧 pyproject.toml 标注 requires-python >= 3.8、目标版本 py312,Web 层是 Django(它用了一个自己维护的 Django 分支标签 frepple-9.18)加 Django REST Framework,数据库是 PostgreSQL,前端用 Vue 加 pnpm 构建,预测编辑器还用 Django Channels 做异步推送。整个仓库代码量我数了一下:57 个 C++ 实现文件、47 个头文件、367 个 Python 文件,是个有真材实料的中大型工程。
第三,活跃度。 我体检时最新代码推送就在前几天(2026 年 10 月中旬),版本号已经标到 10.0.0 的开发态,官方文档稳定版是 9.18,GitHub 上七百多 star。一个敢把 C++ 内核升到 23、还在按天提交的项目,说明背后有全职团队在维护,不是玩票。
第四,也是最关键的——它的业务是不是真的。 我直接去翻最难的求解器目录 src/solver/,看到的文件名让我立刻放心了:solverdemand.cpp(逐需求求解)、solveroperation.cpp(工序展开)、solverflow.cpp 和 solverbuffer.cpp(物料与库存应答)、solverload.cpp 和 solverresource.cpp(产能应答)、operatorforward.cpp 和 operatorbackward.cpp(正向 ASAP 与反向 JIT 两套排程算子)、operatordelete.cpp(计划回滚)。这套文件名和它文档里描述的"ask-reply 递归求解"严丝合缝,是真把 APS 算法从头实现了一遍,而不是套个优化器壳子。README 里还明说了它的方法论根基:约束理论(围绕瓶颈做计划)、拉动式计划(尽量晚开工、由需求直接触发)、精益(消除中间等待和多余库存)。这正是我想要的东西。
先睹为快:求解主循环里的三个分支
空说无凭,贴一段我在 src/solver/solverplan.cpp 里读到的真实代码,这是整个计划引擎的主入口 commit(),它用三个分支对应了三种计划哲学:
if (!solver->getConstraints()) {
// 完全无约束:一次反向扫描就完事,等价于经典 MRP
setPropagate(false);
setBatchGrouping(true);
if (solver->getCreateDeliveries()) createDeliveries();
buffer_solve_shortages_only = false;
backward_sweep();
scanExcess(false);
} else if (solver->getAlgorithm() == "heuristic_2") {
// 批量启发式:先反向扫描,约束模式下再反复正向修正
...
while (loops++ <= 5) {
operator_forward->solve(); // 正向扫描,把交期推到可行位置
if (!operator_forward->getMovedDeliveries()) break;
// 删掉除交期外的试探计划,再做一次反向扫描,把批量协调到可行日期
...
backward_sweep();
}
} else {
// 常规:逐需求求解。先稳定排序,再先补安全库存,然后逐张订单展开
stable_sort(demands->begin(), demands->end(), demand_comparison);
constrainedPlanning = (solver->getPlanType() == 1);
safety_stock_planning = true;
solveSafetyStock(solver, true);
...
}
这段代码信息量极大:无约束时它就是一次扫完的 MRP;约束模式下来回"反向排→正向推→删了重排"最多五轮,逼出可行日期;常规模式则先按优先级和交期对需求做稳定排序(注释特意强调用 stable_sort 是为了跨平台结果可复现),先满足安全库存,再逐张订单递归展开。读懂这三段,你就拿到了整本"计划算法地图"——后面好几章我都是在把这几十行逐个拆开讲。
我一个 Java 工程师,为什么敢用 C++/Python 的底座
这是我选型时被问得最多的一个问题,我正面回答。
首先,做这行最忌讳语言原教旨主义。SCP 系统里真正稀缺、真正值钱的不是某门语言的语法,而是领域模型和求解算法——物料怎么分层、BOM 怎么递归展开、有限产能怎么应答、安全库存怎么算。这些东西是语言无关的,读懂了用什么语言都能落地。
其次,frePPLe 官方明确支持一种用法:把它当成没有界面的后台计划引擎嵌进别的系统。它的架构文档里专门写了,引擎可以独立运行,你的系统当前端、通过 REST API、CSV/Excel 或直接读写 PostgreSQL 与它交换数据。换句话说,我完全可以让 Java/Spring Boot 做主系统,把"算计划"这件专业的事丢给 frePPLe,算完把结果取回来——这是典型的 polyglot 架构,比自己用 Java 重写一个求解器靠谱得多。等你真把算法吃透了,想在 Java 里重写排程内核,那时候再上 Timefold 也是有的放矢。
最后是工程现实:C++23 的内核听起来吓人,但官方提供了 Docker、.devcontainer 开发容器和 Ubuntu 安装包,你绝大多数时候是在 Python/Django 和数据库这一层做集成,不需要手搓 C++。我本地就优先用容器把它跑起来,真正要动求解器时才进开发容器。
动手前的环境盘点
和前两套系统一样,下载完我先打了个基线标签,再花时间盘依赖。frePPLe 比纯 Java 系统多了个原生编译的门槛,我把关键组件列一下:
| 组件 | 用途 | 我的部署建议 |
|---|---|---|
| PostgreSQL | 唯一的生产数据库,计划数据全在这,schema 简单稳定 | Docker 起单机,官方支持直连库集成 |
| Python 3.12 | Django Web 层、API、管理命令、预测服务 | 建独立 venv,用 requirements.txt 锁版本 |
| C++23 工具链 | 编译计划引擎内核(gcc 14+/clang 18+) | 别在本机硬刚,用官方 .devcontainer 最省心 |
| Node/pnpm | 构建 Vue 前端 | 只改界面时才需要,集成场景可跳过 |
| Apache(可选) | 生产静态文件和进程托管 | 开发用内置服务器即可 |
新手最容易踩的坑是上来就想在本机裸编 C++23,结果编译器版本不够、依赖缺一串,卡在环境上三天读不了一行业务代码。正确姿势是先用容器或安装包把它跑起来、在界面里建几个物料和工序点一次"生成计划",看到计划是怎么算出来的,再回头带着问题去读 C++ 内核。先建立直觉,再啃实现,这个顺序不能反。
这个系列怎么读
接下来九章,我会沿着"从预测到排程"的真实决策链,每章啃一块硬骨头,所有结论都以这个仓库的真实代码为准,原作者的实现和我的改造我都会明确区分:
- 第 2 章讲三层架构和主数据模型,物料、地点、客户、供应商这些层级实体怎么组织;
- 第 3 章讲供给网络建模:库存点、日历、资源,以及五种工序怎么搭出一张 BOM/工艺路线;
- 第 4 章讲需求与预测:销售订单、统计预测方法的自动选型、销售订单如何抵消毛预测;
- 第 5 章讲库存计划:安全库存、覆盖天数和补货批量;
- 第 6 章讲三种计划模式与正向/反向排程,也就是这一章那段
commit()的展开; - 第 7 章是全系列最硬核的一章,我会逐行讲 ask-reply 递归怎么把一张订单展开成完整计划;
- 第 8 章讲约束、问题诊断与供需追溯,让计划不仅算得出来、还说得清为什么;
- 第 9 章讲多仓分销、采购和 what-if 场景模拟;
- 第 10 章讲怎么把它接进自己的系统、MIT 协议的商业边界,以及和 Java/Timefold 怎么配合。
还是那句老话:用开源不是照抄,是先读懂原作者的设计意图,再决定哪里留、哪里改。frePPLe 里有让我拍桌子的好算法,也有因为跨语言带来的集成摩擦——我都会原样摆给你看。
下一章,我们从它的三层架构和那套层级化主数据模型开始。
