Skip to main content

Mobility: one agent per car

Track: Mobility · In the room: Polestar · Seed use case: telemetry anomaly — diagnose, then book service.

Who this is for

Polestar. Workers and KV already in production, so roughly a fifth of what the developer platform could be doing for you is switched on. A connected fleet, over-the-air updates, a direct-to-consumer relationship with every owner, and no dealer network to hide behind when something goes wrong.

That last point is what makes this track different. A legacy manufacturer's fault data stops at the workshop door. Yours streams home. You are already in the position everyone else is trying to buy their way into — the constraint is that a stream of telemetry is not a diagnosis, and a diagnosis is not a booked appointment. The gap between "we have the data" and "the customer's problem is fixed" is where the agent goes.

Synthetic data only

Invented VINs, invented fault codes, invented 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 vehicle reports a diagnostic trouble code, or a derived signal crosses a threshold — coolant temperature drifting up over a week, a battery cell out of balance with its neighbours, charge sessions terminating early.
  • What the agent decides. Whether this is a known pattern with a known remedy; whether it is urgent (drive it, or don't); whether it can be fixed remotely with a software update or needs the car physically present; and, if physical, what parts and how long a bay.
  • What it does — the tools. diagnose_fault retrieves the relevant service bulletin and produces a diagnosis with citations. propose_appointment finds workshop slots that fit the estimated duration and the owner's location, and drafts the message to the owner.
  • Human in the loop? Two gates, for two different reasons. The owner must confirm the appointment, because nothing goes on someone's calendar without their say-so. And any remote remediation — pushing a software change to a car — needs an engineer's approval, always, no threshold, no exceptions. An agent that can silently alter a moving vehicle is not a product feature, it is a recall waiting to happen.

2. Charging-experience investigator

  • Trigger. An owner's charge session fails, or a pattern of sessions at one location degrades across multiple vehicles.
  • What the agent decides. Whether the fault is the car, the cable, the charge point, or the network operator — the question every EV owner asks and nobody can answer for them.
  • What it does — the tool. correlate_sessions compares this vehicle's history at other locations against other vehicles' history at this location, and produces an attribution with a confidence level.
  • Human in the loop? Not for the diagnosis — telling an owner "this charge point has failed for eleven other cars this week, it isn't you" is pure gain and needs no approval. Yes for the consequence: raising a formal fault with a charging network is a commercial act.

3. Pre-delivery and handover briefer

  • Trigger. A vehicle is allocated to a specific customer order and enters the delivery pipeline.
  • What the agent decides. What is genuinely specific about this car and this owner — the options fitted, the software version it will arrive on, the known quirks of that build, the charging setup at their home address.
  • What it does — the tool. draft_handover_brief assembles a personalised briefing for the delivery specialist, and a shorter one for the owner.
  • Human in the loop? Yes, as an editor. The delivery specialist owns the customer relationship and must be able to correct the agent before it reaches the owner. Same shape as the retail content queue: the agent produces a draft, the human ships it.

The 60-minute cut

Build use case 1, and cut it to: per-vehicle memory, a grounded diagnosis, a gated action.

In scope:

  1. Four synthetic vehicles, each with a VIN, model, and a short array of telemetry readings with timestamps. Include one clear fault, one slow drift that only looks wrong across several readings, and two healthy cars.
  2. One agent instance per VIN. This is the design decision that makes the demo land — the agent for SYN-VIN-0002 knows that car's history and nothing else.
  3. A short synthetic "service bulletin" corpus — four or five paragraphs of invented guidance mapping fault patterns to remedies — that the diagnosis must cite.
  4. A Workflow that proposes an appointment and waits for owner confirmation.
  5. A hard-blocked remote-update path: the agent may recommend an OTA fix, and the endpoint that would apply it refuses without an explicit engineer approval.

Out of scope, and say so: real telemetry ingestion, a real workshop calendar, geospatial slot optimisation, authentication, and anything resembling a fleet dashboard. If you are building a time-series database, you have lost the hour.

The demo that wins. Feed a reading into one car, then ask a different car's agent about it — it has no idea, correctly. Then show the diagnosis quoting the bulletin, the appointment sitting unconfirmed, and finally call the OTA endpoint and let it refuse you. Being told "no" by your own demo is the most memorable thing that can happen on that stage.

Primitives — exactly three

  1. Persistent Durable Object state (one Agents SDK Agent instance per VIN). A vehicle is a long-lived entity with a history, and the whole diagnosis depends on comparing today against last month. Addressing an agent by VIN gives you one strongly-consistent, single-threaded actor per car with its own SQLite via this.sql, and it hibernates between readings so a million idle cars cost nothing. One shared database table with a vin column gets you the same rows and none of the isolation, and you will spend the hour on locking.
  2. AI Search over your own documents. A fault code is meaningless without the bulletin that explains it. Retrieval turns "the model thinks it's the coolant pump" into "bulletin TSB-114 says this signature indicates the coolant pump, here is the passage" — and a technician will act on the second and ignore the first. No amount of prompt engineering substitutes for citing the source.
  3. Workflows. Waiting for an owner to confirm an appointment is waiting on a human who is probably asleep. step.waitForEvent() holds the run open for as long as that takes without burning anything, and the checkpointing means a retry never books two bays for one car.

Skip Browser Rendering (nothing to render) and skip schedules — your trigger is a telemetry event arriving, and inventing a polling loop would be architecture theatre.

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

  1. Feed it the real bulletin corpus and measure citation quality. Index your actual service documentation and then check, on a sample, whether the cited bulletin is the one a technician would have chosen. That accuracy number is the thing your engineering organisation will trust or reject the system on — not a demo, a measurement. It also tells you when your documentation, rather than your model, is the weak link.
  2. 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, a timestamp, and the exact diagnosis they were shown. Keep the agent's recommendation stored next to the human's decision — the divergence between them is your best evidence for widening automation later, or for not widening it.
  3. Turn per-vehicle agents into fleet intelligence. Once every car has its own stateful agent, patterns across them are a query away: this fault signature is appearing in cars from one production week, at one charge-point vendor, in one climate. That is a quality engineering capability, and it emerges from the same architecture rather than needing a second platform.

Next: the Level Up page explains each primitive, and the cheat sheet has the snippets. Both are in the sidebar.