在企业运营中,我们经常面临复杂的资源分配和调度问题:如何为数百名员工安排最优排班?如何规划物流车辆的最短配送路径?如何合理安排课程表避免时间冲突?这些问题都属于
NP-hard 优化问题,传统算法难以在合理时间内找到最优解。
Timefold Solver 作为一款开源的 AI 约束求解器,正在帮助企业高效解决这类复杂规划问题。本文将深入介绍 Timefold 的背景、核心概念、应用场景,并通过实战示例展示其强大能力。
一、Timefold 背景介绍
1.1 从 OptaPlanner 到 Timefold
Timefold 并非凭空诞生,而是 OptaPlanner 的合法继承者与全面升级版1。
OptaPlanner 的辉煌历史:
- OptaPlanner 是全球广泛使用的开源运筹优化引擎
- 每天为成千上万的组织节省时间、金钱和资源
- 创始人:Geoffrey De Smet
转折点:
- 2022 年,Red Hat 进行战略调整,业务自动化不再是公司重点
- OptaPlanner 团队的支持减少,项目面临停滞风险
Timefold 的诞生:
- 2023 年 5 月,Geoffrey De Smet 决定自立门户,成立 Timefold BV
- 原班核心团队打造:Geoffrey De Smet、Lukáš Petrovický、Radovan Synek、Christopher Chianelli
- 获得 Smartfin 等开源友好型风投支持

1.2 Timefold 的核心优势
相比 OptaPlanner,Timefold 在性能、体积和功能上均有显著提升1:
| 指标 | Timefold vs OptaPlanner | 提升幅度 |
|---|---|---|
| 运行速度 | Timefold 更快 | 2 倍 |
| Jar 包体积 | Hello World 程序 | 小 41% |
| 文档与支持 | 社区响应更及时 | 显著改善 |
| 功能与修复 | Bug 修复和新特性 | 持续增加 |
1.3 开源协议
Timefold 采用 开放核心(Open Core)模式1:
- 社区版(Community Edition):Apache 2.0 协议,完全开源,功能全面
- 企业版(Enterprise Edition):商业许可,增加高可扩展性特性(多线程求解、Nearby Selection)
1.4 Timefold 是什么?
Timefold 是一个约束求解引擎2,用于解决 NP-hard 的排产、路径规划等问题。
核心原理:
你向 Timefold 描述:
- 待安排的任务(订单、工件、车辆)
- 可用资源(机床、灶台、司机)
- 绝对不能违反的规则(硬约束)
- 希望尽量满足的目标(软约束)
Timefold 会通过启发式搜索 (非穷举)自动尝试大量组合,找到扣分最少的方案。
求解过程分两阶段:
| 阶段 | 目标 | 特点 |
|---|---|---|
| 构造启发式(Construction Heuristic) | 快速生成可行解 | 速度快,满足所有硬约束 |
| 局部搜索(Local Search) | 优化软约束 | 逐步改进,可能短暂变差以跳出局部最优 |
二、Timefold 核心概念
理解 Timefold 的关键是建立 「业务世界 → 数学模型 → 代码实体」 的映射关系2。
2.1 三层映射模型
| 业务概念 | 数学概念 | 代码实体 | Timefold 注解 |
|---|---|---|---|
| 今天的全部工作安排 | 规划解 | Schedule |
@PlanningSolution |
| 一道待炒的菜 | 规划实体 | Task |
@PlanningEntity |
| 菜放哪个灶台、几点炒 | 规划变量 | resource、startTime |
@PlanningVariable |
| 灶台本身(固定不变) | 问题事实 | Stove |
@ProblemFact |
| 一个灶台不能炒两道菜 | 硬约束 | resourceConflict |
ConstraintProvider |
| VIP 菜尽量先做 | 软约束 | vipPriority |
penalize(ONE_SOFT) |
| 方案好坏的量化结果 | 得分 | HardSoftScore |
@PlanningScore |
2.2 关键设计原则
- PlanningEntity (如
Task):Timefold 会不断修改其内部变量 --- 它是被安排的对象 - ProblemFact (如
Stove):Timefold 只读取,不会修改 --- 它是环境条件 - PlanningVariable 初始必须为 null :因为 Timefold 的求解就是填空过程
2.3 约束评分机制
Timefold 使用 Hard/Soft 约束评分:
- Hard Score(硬约束):绝对不能违反(如:一个灶台不能同时炒两道菜)
- Soft Score(软约束):希望尽量满足(如:VIP 订单优先处理)
目标:最小化总扣分(Hard Score 优先于 Soft Score)
三、Timefold 应用场景
Timefold 的应用场景非常广泛,覆盖多个行业和领域3。

3.1 主要应用领域
| 领域 | 示例 | 典型规模 | 学术竞赛验证 |
|---|---|---|---|
| 云计算/IT | 云负载均衡、机器重分配 | 2400-50000 实体 | ROADEF 2012 |
| 物流运输 | 车辆路径规划(VRP) | 2750 访问点 | - |
| 人力资源 | 护士排班、员工排班 | 752 实体 | INRC 2010 |
| 教育排课 | 课程排课、考试排课 | 434-1096 实体 | ITC 2007 |
| 医疗保健 | 病床规划 | 2750 实体 | - |
| 项目管理 | 项目作业调度 | 640 实体 | MISTA 2013 |
| 航空运输 | 机组排班 | 4375 实体 | - |
| 运动赛程 | 网球俱乐部调度 | 72 实体 | - |
3.2 典型场景详解
场景 1:员工排班问题(Employee Shift Scheduling)
业务背景:
- 需要为员工分配班次(早班、晚班、夜班)
- 约束条件:
- 员工技能匹配(如:服务员不能做厨师)
- 劳动法限制(如:连续工作天数、最低休息时间)
- 员工偏好(如:某人周五不工作)
- 公平性(如:周末班次均匀分配)
Timefold 解决方案:
- 规划实体:
ShiftAssignment(班次分配) - 规划变量:
employee(分配的员工) - 硬约束:技能匹配、劳动法限制
- 软约束:员工偏好、公平性
场景 2:车辆路径规划(Vehicle Routing Problem)
业务背景:
- 需要为配送车辆规划最优路线
- 约束条件:
- 车辆容量限制
- 客户时间窗要求
- 司机工作时间限制
- 配送成本最小化
Timefold 解决方案:
- 规划实体:
Visit(访问点) - 规划变量:
vehicle(分配的车辆)、nextVisit(下一个访问点) - 硬约束:容量限制、时间窗
- 软约束:总距离最小化、车辆数量最小化
场景 3:课程排课(Course Timetabling)
业务背景:
- 需要安排课程到具体时间和教室
- 约束条件:
- 同一教师不能同时上两门课
- 同一学生不能同时上两门课
- 教室容量足够
- 课程时间偏好
Timefold 解决方案:
- 规划实体:
CourseSchedule(课程安排) - 规划变量:
timeslot(时间槽)、room(教室) - 硬约束:教师冲突、学生冲突、教室容量
- 软约束:时间偏好、教室偏好
四、Timefold 示例实战
下面通过一个简单的 员工排班示例 来展示 Timefold 的实际应用。
4.1 业务场景
我们需要为 5 名员工安排 10 个班次(早班、晚班),满足以下约束:
- 硬约束:员工技能必须匹配班次要求
- 软约束:尽量满足员工的排班偏好
4.2 代码实现(Java + Spring Boot)
Step 1:定义问题事实(Problem Fact)
java
// 员工实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@PlanningId
private Long id;
private String name;
private Set<Skill> skills;
private Map<DayOfWeek, ShiftPreference> preferences;
}
// 班次实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Shift {
@PlanningId
private Long id;
private String name;
private LocalDateTime start;
private LocalDateTime end;
private Skill requiredSkill;
}
Step 2:定义规划实体(Planning Entity)
java
@PlanningEntity
@Data
@NoArgsConstructor
public class ShiftAssignment {
@PlanningId
private Long id;
// 问题事实:固定不变
private Shift shift;
// 规划变量:Timefold 会不断修改
@PlanningVariable(valueRangeProviderRefs = "employeeRange")
private Employee employee;
// 得分计算辅助方法
public boolean isSkillMatch() {
return employee != null
&& employee.getSkills().contains(shift.getRequiredSkill());
}
}
Step 3:定义规划解(Planning Solution)
java
@PlanningSolution
@Data
@NoArgsConstructor
public class EmployeeSchedule {
// 问题事实列表
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "employeeRange")
private List<Employee> employees;
@ProblemFactCollectionProperty
private List<Shift> shifts;
// 规划实体列表
@PlanningEntityCollectionProperty
private List<ShiftAssignment> shiftAssignments;
// 得分
@PlanningScore
private HardSoftScore score;
}
Step 4:定义约束(Constraint Provider)
java
@ConstraintProvider
public class EmployeeSchedulingConstraintProvider
implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory factory) {
return new Constraint[] {
skillMatch(factory),
employeePreference(factory)
};
}
// 硬约束:员工技能必须匹配
private Constraint skillMatch(ConstraintFactory factory) {
return factory.forEach(ShiftAssignment.class)
.filter(assignment -> !assignment.isSkillMatch())
.penalize(HardSoftScore.ONE_HARD)
.asConstraint("Skill match");
}
// 软约束:尽量满足员工偏好
private Constraint employeePreference(ConstraintFactory factory) {
return factory.forEach(ShiftAssignment.class)
.filter(assignment -> assignment.getEmployee() != null)
.filter(assignment -> {
DayOfWeek day = assignment.getShift().getStart().getDayOfWeek();
ShiftPreference pref = assignment.getEmployee()
.getPreferences().get(day);
return pref == ShiftPreference.DO_NOT_WANT;
})
.penalize(HardSoftScore.ONE_SOFT, 5)
.asConstraint("Employee preference");
}
}
Step 5:运行求解器
java
@SpringBootTest
class EmployeeSchedulingTest {
@Autowired
private SolverManager<EmployeeSchedule, UUID> solverManager;
@Test
void testSolve() {
// 1. 准备问题数据
EmployeeSchedule problem = createProblem();
// 2. 启动求解(最长运行 30 秒)
EmployeeSchedule solution = solverManager.solve(
UUID.randomUUID(),
problem
).getFinalBestSolution();
// 3. 输出结果
System.out.println("Score: " + solution.getScore());
solution.getShiftAssignments().forEach(assignment -> {
System.out.println(
assignment.getShift().getName() + " -> "
+ assignment.getEmployee().getName()
);
});
}
}
4.3 运行结果
Score: 0hard/-15soft
========================
班次 1 (早班) -> 张三
班次 2 (早班) -> 李四
班次 3 (晚班) -> 王五
...
结果分析:
- 0hard:所有硬约束都满足(员工技能匹配)
- -15soft:有 15 个软约束扣分(部分员工偏好未满足,但可以接受)
五、Timefold 高级特性
5.1 实时规划(Real-time Planning)
Timefold 支持在数据变化时动态响应,无需重新求解整个问题3。
应用场景:
- 员工突然请假
- 订单紧急插入
- 设备故障
实现方式:
使用 ProblemFactChange API 动态修改问题数据。
5.2 持续规划(Continuous Planning)
Timefold 支持分期滚动规划,适用于长期调度场景3。
应用场景:
- 每周安排下月排班
- 每天更新未来 7 天配送计划
5.3 基准测试(Benchmarker)
Timefold 提供 Benchmarker 工具,用于比较不同算法配置的性能2。
功能:
- 自动运行多个求解器配置
- 生成性能对比报告
- 帮助选择最优算法参数
六、Timefold 最佳实践
6.1 三大防坑口诀2
| 口诀 | 检查位置 | 不遵守的后果 |
|---|---|---|
| Join 不加 lessThan,分数一定翻一番 | 约束里的 .join(...) |
冲突被计算两次,求解质量下降 |
| Filter 不判 null,求解必然炸成灰 | 约束 filter(...) 内部 |
NPE,Score 变 null,求解中断 |
| 初始必须空,给值就不动 | @PlanningVariable 字段声明 |
求解器认为已赋值,不再优化 |
6.2 约束编写检查清单2
| 检查项 | 错误后果 |
|---|---|
join() 必须带 Joiners.lessThan(id) |
冲突被计算两次,Score 翻倍 |
filter() 中所有规划变量判 null |
NullPointerException,求解中断 |
使用 penalize() 而非 reward() |
reward() 导致求解器追求最差解 |
每个约束有 asConstraint("唯一英文名") |
重名导致日志混乱 |
6.3 测试策略
- 单元测试 :使用
ConstraintVerifier测试每个约束 - 集成测试:测试完整求解流程
- 性能测试:使用 Benchmarker 比较算法配置
七、总结
7.1 Timefold 的核心价值
Timefold 作为一款开源的 AI 约束求解器,为企业解决复杂规划优化问题提供了强大工具:
- 性能卓越:运行速度是 OptaPlanner 的 2 倍,Jar 包体积更小
- 易于使用:通过注解和流式 API,降低学习曲线
- 应用广泛:覆盖云计算、物流、人力资源、教育等多个领域
- 开源友好:社区版采用 Apache 2.0 协议,完全开源
7.2 适用场景
Timefold 适用于所有需要资源优化分配的场景:
- ✅ 员工排班、课程排课
- ✅ 车辆路径规划、物流配送
- ✅ 项目调度、生产计划
- ✅ 云计算资源分配
7.3 未来展望
随着 AI 技术的发展,Timefold 正在不断进化:
- 多线程求解:提升大规模问题求解速度
- 机器学习集成:利用 ML 预测优化求解策略
- 云原生支持:更好地集成 Kubernetes、Serverless 等云平台