1. Cold-chain excursion triage across a milk route (device-triggered, 2 agents)
Today: the bulk milk cooler at each village chilling centre has a dial thermometer and a temperature register, and the operator writes a reading into it twice a shift — usually both readings at once, at the end. The insulated tanker has a data logger whose chart nobody looks at until there is an argument. When the plant's chilling supervisor suspects a load ran warm, he phones the centre secretary, who reads him the register over the phone, and the question 'can we tip this tanker into the silo?' gets answered at the dock, from memory, by whoever is senior and awake. The 3am excursion is the one nobody hears about until the platform test at 7am.
Trigger: Device (cold-chain telemetry — temperature excursion on a BMC or tanker)
How the workflow runs
- Device
An excursion is reported. A Device Control node watches the cold-chain telemetry stream from the bulk milk coolers and route tankers. When a unit breaches its threshold — or stops reporting — it fires with the unit id, the reading series and the route. The telemetry gateway has no first-party connector; it reaches the workflow via the device/Webhook path and is read back with a Tool (MCP) node.
- Code
Compute the exposure arithmetically. A Code node does the maths on the reading series — minutes above threshold, peak, reporting gaps — deterministically, before any model sees it. Numbers a decision rests on should be computed, not generated; the agent then reasons about figures it did not invent.
- Loop
Map: one pass per affected unit. A route-wide event can implicate several coolers and tankers at once. A Loop node fans a cheap triage agent across each affected unit — isolated context per unit, so a six-unit incident is handled exactly like a one-unit one.
- Agent
Triage agent links exposure to lots. Per unit, the agent takes the computed exposure plus the collection and dispatch timestamps and returns strict JSON: which collection lots sit inside the affected window, which centres and farmers they came from, and the severity band the SOP assigns. It assembles the evidence — it never declares the milk fit.
- Agent
Reduce: incident summary for the route. A reducer agent rolls the per-unit findings into one route incident: the implicated lots, the worst exposure, the farmers and centres involved, and the SOP's prescribed options (re-test / divert to a lower-grade use / hold pending lab / condemn) as OPTIONS, ranked with reasons.
- Approval
Quality manager decides disposition. An Approval node holds the disposition. Condemning, diverting or releasing a load is a food-safety decision with a person's name on it — the workflow presents the reconstructed evidence and the SOP options, and the quality manager chooses. Nothing is auto-cleared and nothing is auto-condemned.
- Output
Notify the route + write the record. On the decision, an Output node pushes the instruction to the route supervisor and tanker driver on WhatsApp (and voice/SMS where data is patchy), and a Tool (MCP) node writes the disposition and its evidence trail back to the quality/MCU system, keyed to the incident id.
Channels & connectors
- Device Control (cold-chain telemetry)
- Webhook
- SMS/Twilio
- Tool (MCP → quality / MCU system)
- Approval / HITL
Outcome
Every temperature excursion becomes a reconstructed, lot-linked incident summary with SOP options on the quality manager's screen within minutes of the breach — instead of a chart nobody reads and a phone call to the centre secretary hours later.
Why it helps
The mechanism is reconstruction plus routing: the temperature series, the collection timestamps and the SOP all exist already, but at 3am nobody assembles them, and the register is written from memory anyway. Doing that assembly automatically turns a silent alarm into a decision-ready case, while the fit-or-not-fit call stays with the person accountable for it.
Build spec
2 agents — 1 triage agent (runs once per affected unit via the Loop node) + 1 reducer (runs once per incident). The Device trigger, the Code node's exposure maths, the Approval and the write-back are workflow nodes, not agents. · Pattern: Device-triggered map-reduce: Device → Code (deterministic maths) → Loop (map per unit) → reducer → quality-manager Approval
System prompt (paste-ready)
(Triage) Assess ONE cold-chain unit's excursion. Inputs: {unit_id, unit_type (BMC|TANKER), computed_exposure {minutes_above_threshold, peak_temp_c, gap_minutes}, threshold_c, collection_events[{lot_id, ts, centre, farmer_ids[]}]}. The exposure figures were computed upstream — use them as given and never recompute or 'correct' them. Return strict JSON {unit_id, affected_lots[], affected_centres[], severity (LOW|MED|HIGH|UNKNOWN), sop_clause_ref, evidence_note}. If the unit stopped reporting, severity=UNKNOWN and say so — a gap is missing evidence, never a favourable reading. You do NOT decide whether the milk is fit and you do NOT output a disposition.Agent roles & model tiers
Excursion triage (per affected unit) — Economy tier — runs once per affected unit on every alert including the noisy false positives, so this choice dominates total spend. Configure a fallback model on a second provider: a 3am provider outage must degrade the alert, not drop it.
Link ONE unit's computed exposure to the collection lots inside the affected window and band it against the SOP. Strict schema-valid JSON. Missing telemetry is UNKNOWN, never 'fine'. No disposition, no fitness judgement.
Incident reducer (once per incident) — Standard tier — cross-unit reasoning and SOP option framing; this is the one output a manager reads at 3am, with a fallback model configured for the same reason
Aggregate the validated per-unit findings into one route incident: implicated lots and farmers, worst exposure, reporting gaps. Then list the SOP's disposition OPTIONS (re-test / divert / hold pending lab / condemn) with the reason each applies, ranked. Present options only — the quality manager decides. Cite lot ids and the computed readings; never estimate a temperature.
MCP connectors
- Cold-chain telemetry gateway (BMC + tanker loggers) — via Device Control / inbound Webhook, then Tool (MCP): fetch the reading series for a unit between two timestamps and read its current state
- MCU / milk-collection system — via Tool (MCP): list collection lots and dispatch times for the route and window, resolve each lot to its centre and farmer ids
- Plant quality system — via Tool (MCP): write the approved disposition and its evidence trail against the incident id
- WhatsApp / SMS — route supervisor and tanker driver instructions
Built-in tools
- knowledge_search (scoped Knowledge collection: cold-chain SOP — thresholds, exposure bands, disposition options)
- http_request (pull the reading series and collection lots; write the approved disposition)
- user-defined tool `excursion_severity` (SpEL, sandboxed formula — no API calls, no code): input.minutesAbove > 120 ? 'HOLD_LOT' : (input.minutesAbove > 30 ? 'ESCALATE' : 'LOG_ONLY') — classifies for routing only, and 'LOG_ONLY' is a routing label, never a food-safety clearance
- user-defined tool `excursion_sms_line` (SpEL): input.unitId + ' ' + input.minutesAbove + 'min above ' + input.thresholdC + 'C, peak ' + input.peakC + 'C — ' + (input.route ?: 'route unknown') — builds the one-line driver SMS in a fixed, scannable shape
Guardrails
- FOOD SAFETY: neither agent may clear milk as fit. They assemble exposure evidence, cite the SOP clause and present options — the fit/divert/condemn decision is the quality manager's at the Approval node, recorded with the evidence that justified it
- The `excursion_severity` tenant expression classifies for routing only. 'LOG_ONLY' means 'do not page the manager', it does NOT mean the milk is safe — no SpEL expression, and no agent, can release a lot
- A telemetry gap is severity UNKNOWN, never an implicit pass — silence is missing evidence and is escalated, not treated as a normal reading
- Exposure figures come from the Code node, not from a model: the agent reasons about minutes and peaks it did not generate, and is instructed never to recompute them
- Severity bands and disposition options come only from the cold-chain SOP in the scoped Knowledge collection — no invented thresholds, no improvised 'it's probably fine'
- Idempotent on the incident id — a re-fired alarm for the same excursion updates the existing case instead of opening a duplicate or re-paging the route
Cost strategy
Cost is N × (economy-tier, hard-capped triage) + ONE standard-tier reduce, where N is affected units — and cold-chain alarms are noisy by nature, so the per-unit pass MUST sit on the economy tier; that single routing choice is the dominant lever. The reducer's one call per incident is where the reasoning budget belongs. Both roles carry a fallback model on a second provider, because an incident at 3am is exactly when you cannot absorb a provider outage. Run the map in Parallel batches for wall-clock speed on a route-wide event without changing per-unit cost.
Output & delivery
The Device node fires on the excursion → a Code node computes minutes-above, peak and gaps deterministically → the Loop maps a cheap triage agent over every affected cooler and tanker → the reducer builds one route incident with SOP options → the quality manager picks the disposition at the Approval node → an Output node instructs the supervisor and driver on WhatsApp/SMS using the tenant's own `excursion_sms_line` format, and a Tool (MCP) node writes the decision and its evidence trail into the quality system. The plant encodes its OWN excursion thresholds in `excursion_severity` — a formula edit in the tool editor, no developer and no redeploy.