Route optimisation for field service technicians is a classic operations-research problem — the vehicle routing problem, essentially — and it's been solved well by non-AI algorithms for decades. The interesting question isn't whether an LLM can compute an optimal route (it can't, reliably); it's where an agent adds value around a solver that's already doing the actual optimisation.
The optimiser is not the LLM
Route sequencing with time windows, technician skills, and travel time is a constraint satisfaction problem with well-established solvers — OR-Tools, or a dedicated routing engine, applied to the day's Acumatica Field Service appointments (FS300100 service orders with their scheduled time windows and required skill sets). Asking an LLM to directly output an optimal sequence of stops is asking it to do arithmetic it's bad at; LLMs don't reliably reason about minimizing total travel distance across a dozen constrained stops, and a wrong route costs a technician real hours on the road. Keep the actual optimisation in a real solver and give the LLM a narrower job around it.
Where the agent actually helps
The genuinely useful agent work is upstream and downstream of the solver: turning a dispatcher's natural-language request ("swap technician B onto the downtown jobs, they know that customer") into a constraint change the solver can consume, and turning the solver's output — a list of stop sequences and ETAs — into a plain-language summary a dispatcher or technician can scan in five seconds instead of parsing a table. This is a translation layer, not a computation layer, and it's where the LLM's actual strength (language, not arithmetic) applies.
If a dispatcher's natural-language instruction conflicts with a hard constraint (a technician lacking a required certification, a time window that's already full), the agent should surface the conflict back to the dispatcher explicitly, not quietly produce a route that ignores it. A route that looks reasonable but silently violates a skill requirement is worse than an error message.
Handling same-day disruption
The case where an agent earns its keep operationally is mid-day disruption: a job runs long, a technician calls in sick, an emergency service request comes in. Re-running the full solver from scratch for every disruption is expensive and slow; a well-scoped agent can decide when a full re-optimisation is warranted versus when a local patch (reassign the one affected stop, extend the one time window) is enough, and hand off to the solver either way rather than improvising a route itself.
def handle_disruption(event, current_routes):
if event.type == "single_job_delay" and event.delay_minutes < 45:
# local patch: push downstream stops for one technician
return patch_single_route(current_routes, event.technician_id, event.delay_minutes)
# technician unavailable, or major delay: re-run the real solver
remaining_jobs = extract_unstarted_jobs(current_routes)
return routing_solver.solve(remaining_jobs, constraints=current_constraints())
Feeding the solver good inputs
Route quality depends more on input data quality than on solver sophistication. Stale service-address geocoding, missing skill tags on technicians, or optimistic service-duration estimates in Acumatica will produce a technically optimal route around bad data. Before investing in a fancier optimisation layer, audit whether FS300100 appointment durations reflect actual historical job lengths and whether technician skill assignments are actually maintained — this is unglamorous data hygiene work, but it moves route quality more than model choice does.
Measure against the manual baseline
Before rolling this out broadly, compare solver-generated routes against what an experienced dispatcher already produces manually for a representative week. In mature field service operations, a good dispatcher's routes are often close to optimal already, and the win is less about beating their route quality and more about doing it in seconds instead of forty minutes, and handling disruption without dropping other technicians' schedules.
| Layer | Responsibility |
|---|---|
| Routing solver (OR-Tools etc.) | Actual constraint-satisfying route optimisation |
| Agent | Natural-language input/output translation, disruption triage |
| Data hygiene | Accurate durations, addresses, skill tags — biggest quality lever |
Wrapping up
Route optimisation is a solved problem for the actual math — lean on a real constraint solver, not an LLM, for sequencing. The agent's job is translating dispatcher intent into solver constraints, summarizing output in plain language, and deciding when a disruption needs a full re-solve versus a quick patch. Data hygiene in Acumatica's Field Service module will move the needle more than any model upgrade.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.