You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Chained through time模式中计算休息与停顿?TimeFold技术问询

TimeFold连续规划中休息与额外工作时间的处理方案

核心考量逻辑

在TimeFold的连续规划场景里,休息时间和额外工作时间一般靠约束规则加影子变量配合处理:

  • 休息时间:可设为硬约束(必须满足,比如连续工作4小时后必须休息30分钟)或软约束(违反会触发惩罚,比如尽量避免短于规定时长的休息)。同时通过影子变量跟踪员工上一个任务的结束时间,自动计算下一个任务的最早可开始时间,确保休息间隔达标。
  • 额外工作时间:大多作为软约束处理,比如超出每日8小时标准时长的部分,每小时扣一定分数,引导求解器尽量少安排加班;也可设置允许少量加班,但超出阈值的部分惩罚权重更高。

代码实现示例

1. 实体类与影子变量定义

在员工实体中定义影子变量,跟踪其最后任务的结束时间,作为休息时间计算的基础:

@PlanningEntity
public class Employee {
    private Long id;
    private String name;
    private LocalTime dailyStartTime;
    private LocalTime dailyEndTime;

    // 影子变量:跟踪员工最后一个任务的结束时间
    @ShadowVariable(sourceVariableName = "endTime", variableListenerClass = EmployeeLastEndTimeUpdater.class)
    private LocalDateTime lastTaskEndTime;

    // getter、setter 省略
}

@PlanningEntity
public class Shift {
    @PlanningId
    private Long id;
    private LocalDateTime startTime;
    private LocalDateTime endTime;

    @PlanningVariable(valueRangeProviderRefs = "employeeRange")
    private Employee employee;

    // getter、setter 省略
}

2. 影子变量更新监听器

实现监听器维护员工的lastTaskEndTime,确保该值随任务分配动态更新:

public class EmployeeLastEndTimeUpdater extends VariableListenerSupport<Shift> {
    @Override
    public void afterEntityAdded(ScoreDirector scoreDirector, Shift shift) {
        updateEmployeeLastEndTime(scoreDirector, shift);
    }

    @Override
    public void afterVariableChanged(ScoreDirector scoreDirector, Shift shift) {
        updateEmployeeLastEndTime(scoreDirector, shift);
    }

    private void updateEmployeeLastEndTime(ScoreDirector scoreDirector, Shift shift) {
        Employee employee = shift.getEmployee();
        if (employee == null) {
            return;
        }
        // 找出该员工所有已分配班次的最晚结束时间
        LocalDateTime newLastEndTime = scoreDirector.getWorkingSolution().getShiftList().stream()
                .filter(s -> employee.equals(s.getEmployee()))
                .map(Shift::getEndTime)
                .max(Comparator.naturalOrder())
                .orElse(null);
        scoreDirector.beforeVariableChanged(employee, "lastTaskEndTime");
        employee.setLastTaskEndTime(newLastEndTime);
        scoreDirector.afterVariableChanged(employee, "lastTaskEndTime");
    }
}

3. 约束规则定义

通过约束流添加休息时间校验和额外工作时间惩罚规则:

public class ShiftConstraints {
    // 硬约束:连续工作后必须休息至少30分钟
    @Constraint(constraintPackage = "shift", name = "MinimumRestBetweenShifts")
    public Constraint minimumRestBetweenShifts(ConstraintFactory factory) {
        return factory.forEach(Shift.class)
                .join(Shift.class,
                        Joiners.equal(Shift::getEmployee),
                        Joiners.lessThan(Shift::getStartTime, Shift::getEndTime),
                        Joiners.greaterThanOrEqual(Shift::getStartTime, other -> other.getEndTime().plusMinutes(30).minusSeconds(1)))
                .penalize(HardSoftScore.ONE_HARD, (shift1, shift2) -> 1)
                .asConstraint();
    }

    // 软约束:超出每日8小时标准时长的部分,每小时惩罚10分
    @Constraint(constraintPackage = "shift", name = "OvertimePenalty")
    public Constraint overtimePenalty(ConstraintFactory factory) {
        return factory.forEach(Employee.class)
                .join(Shift.class, Joiners.equal(Shift::getEmployee))
                .groupBy(Employee::getId,
                        sum(shift -> Duration.between(
                                shift.getStartTime().toLocalTime(), shift.getEndTime().toLocalTime()).toHours()))
                .filter((employeeId, totalHours) -> totalHours > 8)
                .penalize(HardSoftScore.ofSoft(10), (employeeId, totalHours) -> totalHours - 8)
                .asConstraint();
    }
}

关于休息时间计算的位置

休息时间的计算分为两个核心环节:

  • 影子变量阶段:通过监听器维护员工的最后任务结束时间,这是判断是否满足休息间隔的基础数据来源;
  • 约束规则阶段:基于影子变量提供的时间数据,定义约束来校验休息间隔是否合规,或计算休息不足的惩罚。

简单来说,影子变量负责动态跟踪时间状态,约束规则负责利用这些状态执行休息时间的考量逻辑。

内容的提问来源于stack exchange,提问作者BrayanMa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 10:27:23