Dispatch Optimizer
A constraint-based field-service dispatch optimizer for Atlas Field Services, a synthetic commercial-facilities maintenance company. It uses a synthetic backlog, technician capacity, skill matching, SLA rules, travel penalties, and scenario controls to recommend operationally feasible job assignments, then benchmarks an OR-Tools CP-SAT plan against naive and greedy plus 2-opt baselines to measure planning performance under controlled synthetic scenarios.





Problem
On a typical day a field-service dispatcher faces more work than the crews can finish: twelve technicians, more than a hundred jobs, six skills, hard SLA deadlines, travel between sites, fixed shifts, limited overtime, and some jobs blocked on parts. The common-sense approach, highest priority first to the nearest qualified technician, makes locally reasonable choices that are globally poor: it strands specialists, books avoidable travel, and breaches SLAs that better sequencing would have saved.
The question is not what happened. It is what should we do next, given limited people, time, travel, skills, and SLA risk. That is an optimization problem, not a reporting problem, and it is the gap this project fills: operational decision-making under constraints rather than another dashboard.
Users and decisions
Dispatchers and field-service operations managers are the intended users. They use the plan and scenario controls to decide which technician should take each job, which work must be deferred, and whether added capacity or overtime is justified under SLA, skill, travel, and shift constraints.
The interactive demo is built for portfolio demonstration on a fully synthetic, seeded dataset. No proprietary or employer data is used. It is not a production dispatch system for a real field-service operation.
Dispatch as routing plus assignment, solved with CP-SAT
Technician dispatch is modeled as a vehicle-routing problem with time windows plus assignment and solved with Google OR-Tools CP-SAT. Per technician, a routing circuit is built over home plus candidate jobs using AddCircuit, with self-loop arcs making each job optional and arc literals carrying travel time. Start times respect travel and service sequencing, skills and certifications restrict which assignments can exist, parts-blocked jobs are never candidates, and the objective maximizes priority-weighted completion minus travel, SLA-breach, and overtime penalties. SLAs are soft by default and become hard for top-priority jobs under strict mode.
The comparison is the point. A deliberately naive manual baseline obeys the same feasibility rules, so the delta is planning quality, not different assumptions. Two techniques keep an eight-second live solve reliable: per-technician candidate capping keeps routing circuits small, and the baseline is warm-started into CP-SAT as a complete feasible incumbent, so the optimized plan never loses to the baseline even when optimality cannot be proven in the time budget. The solver also reports its optimality gap so the user knows how close the incumbent is to the proven bound.
Architecture
Greedy plus 2-opt takes 12 breaches off the baseline
On the canonical day (seed 42, default settings), the optimized plan beats the naive warm-start baseline on jobs completed, SLA breaches, and overtime with the same crew. The stronger 24-scenario comparison against greedy plus 2-opt shows that the result changes with the solver time budget.
Evidence for
12 fewerSLA breaches per day, median across 200 randomized scenarios, for a greedy nearest-qualified dispatcher with a 2-opt improvement pass against the naive priority-first baseline (5th to 95th percentile: 3 to 22 fewer). Better on 199 of 200 scenarios. Fully deterministic and reproducible.Evidence for
3.35 hrsOvertime removed per day by that same improvement pass, median across the same 200 scenarios (5th to 95th percentile: 0.3 to 7.7 hours).Evidence against
5 behindSLA breaches for CP-SAT at the eight-second budget the product ships, against greedy plus 2-opt, median across 24 scenarios. At 30 and 60 seconds it moves to 3 and 5 breaches ahead, so the shipped time budget is the binding constraint, not the model.Evidence against
45.6%Share of the deadlines the naive baseline misses among the jobs it schedules, median across 200 scenarios. That rate is why beating it proves very little.Tools used
Key features
- A real optimizer, not a heuristic dressed up as one: a VRPTW plus assignment model in OR-Tools CP-SAT with a bounded live solve.
- An honest baseline foil that obeys the same feasibility rules, so the measured delta is planning quality rather than different assumptions.
- Warm start plus per-technician candidate capping, which keeps the live solve tractable and guarantees the optimized plan never loses to the naive warm-start baseline.
- Optimality-gap reporting so the operator knows how close the returned incumbent is to CP-SAT's proven bound.
- Decision support, not just assignment: bottleneck-skill detection, the overtime-versus-SLA trade-off, and explicit reason-coded deferrals on every unassigned job.
- Marginal Value of Capacity: a crew-size sweep through the optimizer that charts the hire-versus-overtime decision frontier.
- Scenario Simulator: pull technicians, raise the traffic penalty, create a skill shortage, tighten SLA strictness, or spike emergencies, and re-solve live.
- A pluggable travel provider behind one seam: offline haversine by default, with real road durations from OpenRouteService or OSRM, set server-side or per visitor with no key stored.
- Full stack with a clean seam: a pure-Python, database-free optimizer core, SQLAlchemy persistence, portable analytical SQL views, and a seven-page dashboard.
Tradeoffs and constraints
A live, anytime solver on an eight-second budget cannot always prove optimality on the full instance. Candidate capping and the naive warm-start baseline are the deliberate response: they shrink the search and guarantee a usable plan at least as good as that baseline on every solve, at the cost of occasionally leaving a small, reported optimality gap. The throughput floor keeps the optimizer from ever finishing fewer jobs than the naive warm-start baseline while it trims breaches, travel, and overtime above that floor.
The default travel model is offline haversine so the public demo is free, reproducible, and quota-free. Real road durations drop in through OpenRouteService or OSRM with no model change, because travel is resolved behind a single seam and the CP-SAT model already uses directional arcs that handle asymmetric road times.
Methodology
Appropriate use: portfolio demonstration of constraint-based optimization and decision support on a fully synthetic, seeded dataset.
Inappropriate use: as a production dispatch system, or as ground truth for staffing, routing, or service-level decisions in a real operation. The live demo begins with one canonical seeded day. The baseline benchmark spans 200 randomized synthetic scenarios, and the solver time-budget sweep spans 24.
Limitations
The optimizer plans a single canonical day from a seeded synthetic generator. It is deterministic and reproducible by design, not a forecast of real demand. SLA deadlines, durations, skills, and parts availability are modeled, but real-world frictions such as no-shows, re-work, mid-day reprioritization, and stochastic travel are not.
The live solve is intentionally time-boxed, so on the hardest scenarios the returned plan can carry a small optimality gap rather than a proven optimum. The objective weights are sensible defaults exposed through the UI sliders; a real deployment would tune them against that operation's actual cost of a breach, an overtime hour, and a mile.
Decisions and rejected alternatives
CP-SAT over simulated annealing or a genetic algorithm. Metaheuristics would have found comparable schedules faster to build. I chose a declarative constraint model because a dispatcher has to be told why a job was deferred. The application derives reason codes from modeled constraints, while CP-SAT provides an objective bound. The cost is real: an 8-second solve budget, a model that gets harder to change as constraints accumulate, and runs that terminate on the time limit rather than at proven optimality.
Warm-starting the baseline as a feasible incumbent. This guarantees the optimizer never returns a worse plan than the manual one, which is the right engineering choice for an operator-facing tool. It also means the direction of the result is decided before the solver runs, and only the magnitude is empirical. I would rather state that plainly than present a guaranteed outcome as a discovery.
A deliberately naive baseline, which was the weakest choice here. Priority-first dispatch misses 45.6 percent of the deadlines it schedules, median across 200 randomized scenarios. No real operation tolerates that, so beating it flattered the optimizer. I have since built the comparison that should have been there from the start: a greedy nearest-qualified dispatcher with a 2-opt improvement pass, which is roughly what a competent engineer writes in an afternoon without a solver. It takes a median of 12 breaches off the naive baseline on its own.
The finding that came out of fixing it. Against that stronger baseline, CP-SAT at the eight-second budget the product actually ships lands five breaches behind, not comfortably ahead. Raising the budget changes the answer: across 24 scenarios run at 2, 8, 30, and 60 seconds, the median SLA difference against greedy-plus-2-opt moves from +9 and +5 breaches worse at 2 and 8 seconds to 3 and 5 breaches better at 30 and 60. The model is not wrong. The shipped time budget sits below the point where constraint programming starts paying for itself, and the original comparison hid that because the strawman baseline was easy enough that eight seconds looked sufficient.
What is not reproducible, and why I am not quoting per-scenario win rates. The solver runs with eight search workers against a wall-clock limit, so setting a random seed does not make it deterministic; re-running the same scenarios shifts individual results and flips the verdict on roughly a fifth of them. The greedy and naive arms are bit-identical across runs and their numbers can be trusted directly. The CP-SAT numbers are honest as distributions and unreliable per scenario, which is a property of racing a portfolio search against a clock and worth stating rather than papering over.
Still open. Real road-network travel as the default with a cached matrix. Rolling re-optimization so the plan absorbs no-shows and emergencies instead of solving one static instance. Operator-tuned weights fit to a specific operation's costs. Multi-day and overtime-budget planning.