Inspiration

Supply chains don't break in clean, structured ways. They break in a chat message that says "the Huntsville line is waiting on battery packs", or in an email from a supplier that slipped a shipment "to next Friday." Someone has to read that, figure out which factory orders are blocked, move them, tell the right dealers, and look after the customers who are now waiting longer for their car. In a just-in-time company with no spare parts, all of that has to happen fast, and it has to be right.

What interested us about the Hitachi challenge was the tension at the centre of it: we wanted an AI that could read messy human language, but we didn't want an AI making the decisions. A model that invents an order number or skips a dealer is worse than useless when the actions are graded and real records change. So the question we set out to answer was: how do you get the flexibility of an LLM with the reliability of plain code?

What it does

Our control tower reads a feed of operational messages and, for each one:

  • decides whether it's a real incident, an unconfirmed rumour, or noise,
  • works out everything it touches by walking a knowledge graph of the company,
  • plans and carries out the fixes (rescheduling orders, notifying dealers, assigning loaner cars, holding faulty parts, stopping sales, contacting owners),
  • and explains every decision it made, with the rule behind it and the data that justifies it.

On the 15 official events it got 15 of 15 right: 768 actions, 0 failed calls, for less than one cent of AI cost.

How we built it

The LLM reads; code decides. The only AI step is one call per message to Claude Haiku 5.5. It copies phrases out of the message into a fixed form ("Kentucky plant", "next Tuesday", "next Thursday") and is never allowed to invent ids or compute dates. Everything after that is deterministic Python.

A knowledge graph of the company. We loaded all 17 tables into a networkx graph of about 17,000 nodes and 28,000 links: suppliers, shipments, parts, models, plants, production orders, customer orders, customers, dealers, finished vehicles and part lots. A supplier delay becomes a graph walk:

$$\text{Shipment} \rightarrow \text{Part} \leftarrow \text{Model} \leftarrow \text{Production order} \leftarrow \text{Customer order} \rightarrow \text{Dealer}$$

A production order is affected by a delay only if its model uses the late part, it is built at the receiving plant, and its build day $d$ falls in the blocked window:

$$t_{\text{original}} \le d < t_{\text{new}}$$

A coordinator and three specialists. The Coordinator understands the message and escalates when needed. The Supply Chain Planner owns the factory schedule, the Quality agent traces faulty lots to cars, and Dealer & Customer Care owns notifications and loaners. Each agent can only call its own tools, and they communicate over an event bus that doubles as the audit trail.

Scheduling under capacity. Every plant has 5 slots a day and 3 are already used, so each plant-day has only 2 free. Affected orders queue by original build date (the customer promised first is served first) and take the earliest day with room:

$$\text{free}(p, d) = \text{slots}(p) - \lvert \text{orders}(p, d) \rvert > 0$$

When an order's own plant is full until the end of the planning window, it transfers. If several plants are free on the same day, we pick the one closest to the dealers of that order's customers, using the average great-circle distance:

$$\bar{D}(p) = \frac{1}{n}\sum_{i=1}^{n} 2R \arcsin\sqrt{\sin^2!\frac{\Delta\varphi_i}{2} + \cos\varphi_p \cos\varphi_i \sin^2!\frac{\Delta\lambda_i}{2}}$$

Loaners by one ranking. Gold and Platinum customers are sorted by the key

$$(\text{tier},\ \text{promised date},\ \text{order id})$$

and each dealer's small pool is used in that order. In a message with two incidents, both share one ranking, so a Platinum customer from one incident is never beaten by a Gold customer from the other.

Plan, check, rehearse, execute. Before anything reaches the company systems, the plan is checked against the handbook rules (one notice per dealer, nothing unaffected touched, no slot over capacity), then rehearsed on an in-process copy of the organisers' engine. Only a clean rehearsal gets sent. That's how we reached zero failed calls.

Explainability as a first-class feature. Every decision is a node in the graph, linked to its subject, its evidence, the rule it applied, the alternatives it rejected, and the action it produced. Our Streamlit dashboard lets you click any decision and see that path, check any record ("was this order affected, and why?"), and look at a before-and-after slot board and the loaner ranking.

Challenges we faced

  • Ambiguity in the rules. The handbook doesn't say which order gets a free slot first, or which plant wins a tie. We debated prioritising orders with more customers, but realised that "earliest build date first" already means "customer promised first, served first", and it doesn't stretch the rules. For transfers we chose the plant nearest the customers' dealers, the one decision where we went beyond the handbook.
  • Dates written like people write them. "Next Friday" said on a Monday could mean this week or next. We settled it from the data itself: in one message a shipment due on the 15th "slips to next Friday", which only makes sense if next Friday is the 19th.
  • Knowing when not to act. Telling noise ("where's the brochure PDF?") from a rumour ("something may be off with a seat material") from a real incident took a careful gate. Our rule became simple: if anything can't be matched exactly to company records, escalate rather than guess.
  • Model reliability. We tested a second provider, Gemini, to prove the design wasn't tied to one model. Every message it answered produced identical actions, but 3 of 14 requests failed with "server busy." Because the system never guesses, those were escalated safely instead of acted on wrongly.
  • Practical setup. Our campus network intercepts HTTPS, which broke git, pip and API calls until we pointed each tool at the Windows certificate store.

What we learned

  • Put the LLM where language is, and code where rules are. One small, cheap model call per message was enough; the hard part was everything after it.
  • Exclusions are evidence. Showing why a record was left alone (a Ranger doesn't use the late camera kit) builds as much trust as showing what was changed.
  • Verification needs independence. We checked our log with two separate re-implementations of the handbook. When all three agree on every one of 768 actions, you can be confident.
  • A rule you can explain beats a clever rule you can't. Every choice we made can be described in one sentence and switched in a config file.

Built With

Share this project:

Updates

Submission history