2026/07/07

Timefold Solver 教程:把排班、路径和调度做成可解释的优化系统

深入介绍 Timefold Solver 的约束求解思想、硬/中/软约束、Constraint Streams、服务化架构和生产落地路径。

TimefoldConstraint SolverPlanning AIJavaOptimization

很多业务系统真正难的地方,不是 CRUD,也不是再接一个大模型 API,而是每天都要把有限资源分给互相冲突的需求:车辆要跑哪些点、护士怎么排班、机台什么时候维修、订单由谁拣货、课程排在哪个教室。规则一多,人工排得慢;写死贪心算法,又很快被例外击穿。

Timefold Solver 值得认真写,是因为它解决的是这类“计划优化”问题。它不是聊天式 AI,也不是让模型替你拍脑袋排班;它是一个约束求解器:把业务对象、可变决策、硬约束、软偏好和评分函数明确建模,然后在巨大的组合空间里搜索更好的方案。

先判断:你的问题是不是 Timefold 问题

Timefold 适合的问题通常有三个特征。第一,有一批资源:人、车、房间、机器、床位、时间窗。第二,有一批待安排事项:班次、订单、课程、访问点、维修任务。第三,安排结果要同时满足很多规则,而且规则之间会冲突。

如果问题只是“按创建时间排序”“离我最近的门店是哪家”,普通 SQL 或规则代码就够了。Timefold 处理的是组合爆炸:4 辆车、8 个访问点就可能产生上千万种分配;真实企业场景里,一天几百个班次、几千个订单、几十条劳动法规和偏好条件,已经不可能靠全量枚举。

项目基本信息

  • 仓库:TimefoldAI/timefold-solver
  • 定位:Java / Kotlin 的开源 Solver AI,用于 scheduling、routing、resource allocation。
  • 来源:由原 OptaPlanner 团队开发,Timefold 于 2023 年从 OptaPlanner 分叉。
  • 许可证:Community Edition 为 Apache-2.0。
  • 最新版本:GitHub release v2.2.0,发布时间 2026-06-19。
  • 运行要求:官方文档写明 Timefold Solver 是纯 Java,运行在 Java 21 或更高版本。
  • 集成方式:可作为独立优化服务,也可嵌入 Spring Boot、Quarkus 或普通 JVM 应用。
  • 典型场景:Vehicle Routing、Employee Rostering、Maintenance Scheduling、Task Assignment、School Timetabling、Cloud Optimization、Conference Scheduling、Job Shop Scheduling。

核心建模:别先写算法,先定义业务语言

Timefold 的正确打开方式不是“我该用什么启发式算法”,而是先把业务拆成四类东西:问题事实、规划实体、规划变量和解。

  • 问题事实:求解过程中不变的数据,例如员工、车辆、教室、客户位置、时间段。
  • 规划实体:需要被安排的对象,例如班次、课程、访问点、维修任务。
  • 规划变量:实体上可以变化的字段,例如某个班次分给哪个员工、某节课排到哪个时间和教室、某个访问点插入哪辆车路线。
  • 规划解:包含所有事实、实体、可选参数和最终 score 的整体对象。

这个拆分很关键。很多团队做排班失败,是因为一开始就写过程代码:先按优先级塞,再修冲突,再补边界,再加例外。两个月后没人能说清哪个规则比哪个规则重要。Timefold 强迫你把“什么可变、什么不可变、什么不能违反、什么只是偏好”写成模型。

Score:业务优先级必须显式化

Timefold 用 score 表达解的质量。最常见的是 HardSoftScore 或 HardMediumSoftScore。硬约束不能违反,比如同一老师同一时间不能上两门课、车辆容量不能超、护士不能同时在两个地点。软约束是偏好,比如少走路、老师尽量固定教室、员工尽量拿到偏好的班次。

硬约束永远压过软约束。一个没有硬约束违规但路程多 10 公里的方案,通常比一个路程短但车辆超载的方案好。这个设计让业务规则不再混成一个神秘权重总和,而是有层级:先可行,再尽量好。

Constraint Streams:用业务规则写增量评分

Timefold 最值得学的是 Constraint Streams。它看起来像 Java Streams 或 SQL,但目标不是一次性遍历集合,而是做增量评分。求解过程中只改动一个规划变量时,普通 stream 往往要重新计算整套规则;Constraint Streams 能只更新受影响的匹配部分,这对排班和路径优化非常重要。

public class TimetableConstraintProvider implements ConstraintProvider {

    @Override
    public Constraint[] defineConstraints(ConstraintFactory factory) {
        return new Constraint[] {
                roomConflict(factory),
                teacherConflict(factory),
                teacherRoomStability(factory)
        };
    }

    Constraint roomConflict(ConstraintFactory factory) {
        return factory.forEachUniquePair(Lesson.class,
                Joiners.equal(Lesson::getTimeslot),
                Joiners.equal(Lesson::getRoom))
            .penalize(HardSoftScore.ONE_HARD)
            .asConstraint("Room conflict");
    }

    Constraint teacherRoomStability(ConstraintFactory factory) {
        return factory.forEachUniquePair(Lesson.class,
                Joiners.equal(Lesson::getTeacher))
            .filter((a, b) -> a.getRoom() != b.getRoom())
            .penalize(HardSoftScore.ONE_SOFT)
            .asConstraint("Teacher room stability");
    }
}

这段规则已经能体现 Timefold 的思想:房间冲突是硬约束,老师教室稳定性是软约束。前者决定方案是否可行,后者决定可行方案之间谁更好。代码没有写“如何排”,只写“什么是好排法”。

车辆路径:为什么 Timefold 不只是排班工具

Timefold 的 quickstarts 里还有 vehicle-routing。车辆路径问题比课表更能说明约束求解的价值:车辆有容量,访问点有服务时间和截止时间,客户可能允许未分配,路线要尽量短。官方示例使用 HardMediumSoftScore:容量和超时是 hard,最大化已分配访问是 medium,最小化行驶时间是 soft。

protected Constraint vehicleCapacity(ConstraintFactory factory) {
    return factory.forEach(Vehicle.class)
        .filter(vehicle -> vehicle.getTotalDemand() > vehicle.getCapacity())
        .penalize(HardMediumSoftScore.ONE_HARD,
            vehicle -> vehicle.getTotalDemand() - vehicle.getCapacity())
        .asConstraint("vehicleCapacity");
}

protected Constraint maximizeVisitsAssigned(ConstraintFactory factory) {
    return factory.forEachIncludingUnassigned(Visit.class)
        .filter(visit -> visit.getVehicle() == null)
        .penalize(HardMediumSoftScore.ONE_MEDIUM,
            visit -> visit.getServiceDuration().toMinutes())
        .asConstraint("maximizeVisitsAssigned");
}

protected Constraint minimizeTravelTime(ConstraintFactory factory) {
    return factory.forEach(Vehicle.class)
        .penalize(HardMediumSoftScore.ONE_SOFT,
            Vehicle::getTotalDrivingTimeSeconds)
        .asConstraint("minimizeTravelTime");
}

这套表达方式很适合真实业务。因为真实业务经常不是“所有订单必须完成”,而是“资源不足时尽量服务高价值订单,同时别违法、别超容量、别超时”。Medium 层让“尽量分配更多任务”能压过普通软偏好,但仍然低于硬约束。

两种落地架构:服务化或嵌入式

官方 getting started 把 Timefold 的落地方式拆成两类:run as a service 和 embed as a library。这个选择比语言细节更重要。

  • 服务化:把优化器做成单一职责服务。业务系统提交 dataset,服务求解后返回结果。它不拥有数据库,不处理用户状态,不管持久化。优点是边界清楚、易扩展、易替换。
  • 嵌入式:把 solver core 作为依赖放进现有应用。优点是集成灵活,适合已有 Spring Boot / Quarkus 系统内部直接求解;缺点是生命周期、并发、资源控制要自己治理。

我更建议大多数生产系统先按服务化思路设计,即使初期部署在同一个仓库里,也要保持 API 边界。优化任务常常耗 CPU、需要异步返回、需要取消和进度查询;把它和普通业务请求混在一个同步调用里,后期会很难调度。

一条可靠的学习路线

  • 第一步:从 quickstarts 里的 school-timetabling 入手,理解 planning entity、planning variable、solution、ConstraintProvider。
  • 第二步:把硬约束和软约束分开写,不要急着调权重。先保证 score 能解释业务常识。
  • 第三步:给每条约束写单元测试。Timefold 2.2.0 release 提到改进了 constraint 的 score/impact 直接测试能力,这类测试比端到端跑一次求解更稳定。
  • 第四步:再看 vehicle-routing,理解 chained planning variable、shadow variable、unassigned visits、HardMediumSoftScore。
  • 第五步:把真实业务数据缩成一个小样本,要求领域专家逐条解释输出为什么合理或不合理。

生产系统最容易踩的坑

  • 把所有偏好都写成硬约束。结果是 solver 找不到可行解,或者现实里明明可以妥协的条件被系统判死。
  • 权重靠拍脑袋。软约束权重要能解释:少 10 分钟路程和员工偏好之间如何取舍,必须有业务共识。
  • 缺少 score explanation。排班系统不是推荐系统,业务方会问“为什么这样排”。约束命名、justification、约束测试都要提前做。
  • 数据清洗不足。求解器不能替你修错误的班次、重复资源、非法时间窗和缺失坐标。
  • 把求解时间当成固定 SLA。组合优化是 anytime 思路:时间越久通常越好,但生产上要设置 termination、异步任务和可接受的结果阈值。

Timefold 和大模型的关系

Timefold 不是 LLM 的替代品,也不应该被 LLM 替代。大模型可以帮助解释规则、生成初版 ConstraintProvider、根据自然语言整理需求;但最终排班和路径选择不能靠模型“感觉”。原因很简单:排班需要可重复、可解释、可测试、可审计。

更合理的组合是:LLM 负责需求澄清和人机交互,Timefold 负责确定性求解和评分。用户说“我希望护士尽量连休两天”,模型可以把需求转成候选规则;工程师和业务方确认后,把它沉淀成 Constraint Streams 代码和约束测试。

什么时候不该用 Timefold

  • 问题规模很小,人工规则足够稳定。
  • 结果必须全局最优且规模可用数学规划直接求精确解。
  • 业务规则还没有稳定到可以被建模,今天一种例外、明天一种例外。
  • 团队没有 Java/Kotlin 运行和测试能力,却要把求解器直接塞进核心生产链路。
  • 组织不愿意为“约束是什么、权重怎么定、结果如何解释”投入业务讨论。

结论

Timefold 的深层价值,不是“自动排班”四个字,而是把计划问题从经验活变成工程模型。它让团队把资源、任务、约束、偏好和评分显式化,再用成熟求解算法在组合空间里找更好的方案。

对企业系统来说,这比再接一个聊天机器人更有意义。因为排班、路径、调度、分配是直接影响成本、体验和公平性的核心决策。Timefold 适合那些愿意认真建模、认真测试、认真解释业务规则的团队;如果只是想让 AI 随便给一个“看起来不错”的方案,那反而不是它的用武之地。