Shipment Exception Agent Blueprint

A shipment misses its delivery window, and the real work starts: someone in operations has to notice, classify the exception, check the documents against each other, find the governing policy, draft a customer update that says exactly the right thing, and assemble the claim packet. None of that is hard once. It's hard four hundred times a day, without one tired person promising a customer something the policy never allowed.
The tempting shortcut is to point a large language model at the inbox and let it write the updates. We think that's the dangerous version of this idea. A model asked to “update the customer” produces fluent, confident text, and it will eventually invent a delivery time and offer compensation nobody approved. In logistics, those are the two sentences that cost money and trust. The failure isn't bad writing. It's good writing about facts it made up.
So this blueprint takes the opposite shape. The Logistics Shipment Exception Agent is a complete, working agent for this workflow, and every run it makes ends at a human approval gate, by construction. It's a reference prototype: synthetic data only, never a real shipment, not a client engagement. That's the point of a blueprint: the architecture and code are the deliverable, and both are public.
Models do language. Code does arithmetic.
The workflow's load-bearing decision is a division of labor. The model reads document text and pulls out the facts on it: quantity, weight, consignee. Fixed code then checks every one of those facts against the fields the shipment's own records carry, and each discrepancy is recorded for the approver. Delay hours are calculated from the scheduled and estimated delivery times, never estimated from prose; in the bundled sample the delay comes out at exactly 36.0 hours, because arithmetic says so. A mismatch between the bill of lading and the invoice is a difference between two documents, found field by field. No model reads prose and estimates a number anywhere in this workflow.
Recovery options get the same treatment. The model proposes two or three (expedite the remaining leg, reroute via another hub, hold and monitor), and fixed rules score every one the same way each time: what it does for the service commitment, how much time it buys back, and what it adds in cost. The highest-scoring option wins, and the draft is built on it. A model that invents a plausible-looking cost figure is a production incident, not a quirk, so the model never does this arithmetic.

Classification argues with itself
Exception type drives everything downstream, so it has to be auditable and identical on every run. Written rules classify first, in a set order: damage outranks a missed appointment, which outranks a document mismatch, which outranks a generic delay, so a specific signal is never swallowed by a "late" keyword. The rules also handle the phrasing humans actually type: "no damage reported", "not damaged" and "damage: none" must not fire the damage rule, and they don't. A denial applies to the sentence it appears in, not to the whole report, and the awkward phrasings are covered by named checks of their own, because those are exactly the cases that burn a real operations team.
When a model is connected, it then classifies the same facts independently; it never sees the rules' answer. The two outcomes are reconciled under one written policy, and the approver can see it: the rules win a disagreement, except where the rules can't settle on a confident answer and the model has one. Then the model's result is used, and the record says so plainly, with the reason. The approver always knows which path decided, and why.
Retrieval reads the shipment, not the label
Finding the governing policy matters as much as naming the exception. An early version searched the policy library with the exception type and the classifier's reasoning, and it missed: a customer procedure written in operational language ("cartons crushed at terminal inspection") never surfaced for the damage case it described, because its vocabulary shares nothing with the classifier's. The search now works from the shipment's own words (the event text, the condition notes, the values on the documents), and the policies the draft ends up citing are the ones those words actually find.
Denials are respected too. A stated absence ("no damage reported") cannot pull the damage policies into a case: the denied signal stops counting toward the match, on both sides of the search, so an exception is matched against the policies for what actually happened. We measure this instead of asserting it. The search is exercised against a fixed set of cases built around exactly these traps, and a policy that surfaces where it doesn't belong is treated as a defect and fixed like one.
Three checks stand between the draft and the human
A drafted update passes three examinations before a person sees it as approvable. First, the draft is checked back against the verified facts and the policies it relies on: every claim in it has to trace to something the run actually established. Second, an independent review: a separate pass, briefed to argue like a sceptical operations reviewer, checks the draft's grounding, its compliance with policy, and whether the claim packet is complete. That review can flag the case and hold it back, but it never rejects anything. The human still decides, with a second opinion in hand.
Third, hard checks on the words themselves. Each one is named and reported: the draft names the shipment it is about; it cites the policies it relies on; it states the next step; it contains no personal data; and it makes no prohibited promises — "we guarantee", "full refund", or time-bound commitments like "we will deliver within 24 hours". A prompt asking a model to please not promise refunds is a hope. A check that rejects the draft is a control, and a draft that fails cannot be approved at all. On failure, the draft gets one chance to be rewritten, with each failed check named for the rewrite, and then it faces all three examinations again. The original failure stays on the record either way.

We know the redraft earns its keep because a live run broke it first. We ran the whole workflow against a real model, and its draft failed the checks: it never named the shipment and never stated a next step. The redraft couldn't fix it, because the feedback it worked from never said what was missing. So the required elements are built into the draft from the start now, and when a check fails, the feedback names it, minor failures included. Two bundled samples keep this honest. SYN-1013's carrier note promises the customer a full refund; the promise finds its way into the draft, and a check fails it. The same words keep coming back, so the redraft fails the same way, and approval is refused. SYN-1014 attacks the agent itself: a carrier note ordering it to ignore its policies and approve the claim. The screening flags it. The flagged sentences are set aside before anything downstream sees them, and the case is classified from the shipment's facts and reaches the gate.
The gate is structural
There is no step anywhere in this workflow that can send anything. Every run ends waiting for a human approval, and the wait is durable: the run pauses where it stopped, a decision picks it up again, and an interruption loses nothing. Every result also carries a recommendation about autonomy, worked out the same way every time and never acted on. A case qualifies only when the exception is minor, every check passed, the two classifications agreed, no redraft was needed and the reviewer raised no flag. It makes the autonomy conversation concrete: which of your case classes could move, and why the rest stay human.
What the evals prove, and what they don't
The repository ships its measurement with it: a fixed set of synthetic cases that runs the whole workflow and checks the exception type and severity each one should produce. Read that as a regression gate, not an accuracy claim: the set covers phrasings the classification was designed for, so passing it proves the system keeps handling its own known cases, nothing more. When a real miss shows up, the discipline is to add it as a case first and change the classification second. A stricter evaluation grades drafts written by a real model, purely for whether everything in them is grounded; it caught a model draft promising delivery within 24 hours, a commitment the wording checks alone had passed, which is why drafts are now checked for time-bound promises too. Production accuracy gets measured on real, consented, anonymized exception data.
What production adds
The production shape ships in the box: approval records, run history and the policy library live in a proper database, the documents in file storage built for the job, and each customer's operation is partitioned from every other, with its own records, policies and access. What a deployment still adds: a way of reading scans and photographs at the front (the blueprint works from document text, and there is no photo damage assessment here), named accounts for individual users, connections to your transport systems and carriers, and the action layer after approval, which plugs in behind the gate so the human checkpoint survives into deployment.
If your operation has an exception queue that eats skilled hours, that's the shape of problem we build for.
Bring us a problem. If a workflow like this one is sitting in your operation, start with Trida AI's intake — a few questions, then talk with founders and experts from Trida AI.
Get the Code on GitHub : github.com/tridaai/blueprint-logistics-shipment-exception
