Mobility: one agent per car
Track: Mobility · In the room: pre-assigned teams · Seed use case: telemetry anomaly — diagnose, then book service.
Who this is for
Teams from a connected-vehicle manufacturer already running Workers and KV in production: a connected fleet, OTA updates, a direct-to-consumer relationship, no dealer network to hide behind. A legacy manufacturer's fault data stops at the workshop door; yours streams home. The constraint: telemetry isn't a diagnosis, and a diagnosis isn't a booked appointment — that gap is where the agent goes.
Invented VINs, fault codes, and owners. Vehicle telemetry is personal data in the EU — nothing real goes into a shared account today.
Three agent use cases
1. Fault-to-appointment agent — the seed use case
- Trigger. A diagnostic trouble code, or a derived signal crossing a threshold — coolant drift over a week, an unbalanced battery cell, charge sessions terminating early.
- Decides. Known pattern or not; urgency (drive it, or don't); fixable via software update or needs the car present; if physical, parts and bay time.
- Tools.
diagnose_faultretrieves the bulletin and produces a cited diagnosis.propose_appointmentfinds slots fitting duration and location, drafts the message. - Human in the loop? Two gates. The owner confirms the appointment. Any remote remediation needs an engineer's approval, always, no exceptions — an agent silently altering a moving vehicle is a recall waiting to happen.
2. Charging-experience investigator
- Trigger. A charge session fails, or sessions at one location degrade across multiple vehicles.
- Decides. Whether the fault is the car, cable, charge point, or network operator — the question every EV owner asks and nobody can answer.
- Tool.
correlate_sessionscompares this vehicle's history elsewhere against other vehicles here, producing an attribution with a confidence level. - Human in the loop? Not for the diagnosis — "this charge point failed for eleven other cars this week, it isn't you" is pure gain. Yes for the consequence: raising a formal fault with a network is a commercial act.
3. Pre-delivery and handover briefer
- Trigger. A vehicle is allocated to a customer order and enters the delivery pipeline.
- Decides. What's specific to this car and owner — options fitted, software version, known quirks, home charging setup.
- Tool.
draft_handover_briefassembles a briefing for the delivery specialist and a shorter one for the owner. - Human in the loop? Yes, as an editor. The specialist must correct the agent before it reaches the owner — same shape as the retail content queue: agent drafts, human ships.
The 60-minute cut
Build use case 1: per-vehicle memory, a grounded diagnosis, a gated action.
In scope:
- Four synthetic vehicles (VIN, model, timestamped telemetry readings) — one clear fault, one slow drift visible only across several readings, two healthy cars.
- One agent instance per VIN — the design decision that makes the demo land. The agent
for
SYN-VIN-0002knows that car's history and nothing else. - A short synthetic "service bulletin" corpus (four or five paragraphs mapping fault patterns to remedies) that the diagnosis must cite.
- A Workflow proposing an appointment and waiting for owner confirmation.
- A hard-blocked remote-update path: the agent may recommend an OTA fix; the endpoint that would apply it refuses without explicit engineer approval.
Out of scope, and say so: real telemetry ingestion, a real workshop calendar, geospatial slot optimisation, auth, anything resembling a fleet dashboard.
The demo that wins. Feed a reading into one car, then ask a different car's agent about it — correctly clueless. Show the diagnosis quoting the bulletin, the appointment sitting unconfirmed, then call the OTA endpoint and let it refuse you. Being told "no" by your own demo is the most memorable thing on that stage.
Primitives — exactly three
- Persistent Durable Object state (one
Agentinstance per VIN). A vehicle is long-lived; diagnosis depends on comparing today against last month. Addressing by VIN gives a strongly-consistent, single-threaded actor per car with its own SQLite viathis.sql, hibernating between readings — a shared table with avincolumn gets the same rows, none of the isolation. - AI Search over your own documents. A fault code is meaningless without the bulletin explaining it. Retrieval turns "the model thinks it's the coolant pump" into "bulletin TSB-114 says so" — a technician acts on the second, not the first.
- Workflows. Waiting for an owner to confirm is waiting on someone probably asleep.
step.waitForEvent()holds the run open at no cost; checkpointing stops a retry booking two bays for one car.
Skip Browser Rendering (nothing to render) and schedules — your trigger is a telemetry event, not a clock.
Paste-ready starter prompt
Scaffold the starter first:
npm create cloudflare@latest -- team-mobility-fault-triage --template cloudflare/agents-starter
cd team-mobility-fault-triage
Then paste this into your coding agent:
I'm working in a fresh clone of Cloudflare's agents-starter template. The agent code is in
src/server.ts and the React client is in src/client.tsx. I have 60 minutes. Keep the
existing chat agent working — extend it, don't replace it.
Build a vehicle fault-triage agent with one agent instance per vehicle.
1. Route requests so that each VIN gets its own Agent instance, addressed by name. Use
routeAgentRequest from the "agents" package, or resolve the instance by VIN explicitly.
Vehicle SYN-VIN-0002 must not be able to see SYN-VIN-0001's history.
2. Add a POST /telemetry/:vin route that accepts a JSON reading: timestamp, a metric name,
a numeric value, and an optional diagnostic trouble code. Append it to that vehicle's
agent state using this.sql. Add GET /history/:vin that returns only that vehicle's
readings.
3. Add a hardcoded synthetic service-bulletin corpus in a separate file: 5 short invented
bulletins, each with an id like TSB-114, a fault signature description, an urgency, a
remedy, and whether the remedy can be applied over the air. Invented content only.
4. Add a tool called diagnose_fault. Given a VIN, it reads that vehicle's stored readings,
picks the most relevant bulletin from the corpus, and uses a Workers AI model to return
JSON: bulletinId, diagnosis (two sentences), urgency (low, medium, high), driveable
(boolean), remediation (one of ota_update, workshop_visit, monitor), and estimatedHours.
The diagnosis text MUST quote the bulletin id it relied on. If no bulletin matches, say
so honestly and return bulletinId null — do not invent a bulletin.
5. Add a Cloudflare Workflow called ServiceBooking in a new file, with steps as step.do():
a) propose_slot — pick a slot from a hardcoded array of 6 synthetic workshop slots that
fits estimatedHours, and draft a message to the owner
b) await_owner_confirmation — step.waitForEvent() with a generous timeout, waiting for
an event of type "owner-response"
c) confirm — record the booking, or record that the owner declined
Import WorkflowEntrypoint, WorkflowStep and WorkflowEvent from "cloudflare:workers", and
add the workflow binding to wrangler.jsonc.
6. Add a POST /ota/:vin route that represents applying a remote software update. It must
REFUSE with HTTP 403 and a clear JSON explanation unless the request carries a header
X-Engineer-Approval with a non-empty value. Log every attempt, approved or refused. Put a
comment above it explaining that this is a deliberate human approval gate for an
irreversible action on a physical vehicle.
7. Add routes:
POST /triage/:vin runs diagnose_fault, then starts a ServiceBooking workflow if
remediation is workshop_visit
POST /owner/:vin sends the "owner-response" event with accepted true or false
GET /fleet HTML page: every VIN, latest diagnosis, cited bulletin id,
booking state
Requirements: TypeScript. Route every Workers AI call through AI Gateway by passing
{ gateway: { id: "agenthack" } } as the options argument. Run `npx wrangler types` after
changing wrangler.jsonc. Then deploy with `npx wrangler deploy` and give me curl commands
that prove vehicle isolation and prove the OTA route refuses.
The Worker name team-mobility-fault-triage follows the shared-account rule
<team_prefix>-<slug>. Eight teams deploy into one account today, so if the Scoping agent
issued you a different prefix, use that one instead.
What "production" looks like
- Feed it the real bulletin corpus and measure citation quality. Check, on a sample, whether the cited bulletin is the one a technician would choose. That accuracy number, not a demo, is what earns trust — and flags when documentation, not the model, is weak.
- Make the approval gate a real signed authorisation. Put your identity provider in front of the remediation path so an OTA approval carries a named engineer, timestamp, and the diagnosis shown. Store the recommendation next to the decision — divergence is your best evidence for widening automation, or not.
- Turn per-vehicle agents into fleet intelligence. Once every car has its own stateful agent, patterns across them are a query away: a fault signature appearing in one production week, one charge-point vendor, one climate — a quality engineering capability from the same architecture, no second platform needed.
Next: Level Up explains each primitive, the cheat sheet has the snippets.