A GNN that learned our Bill of Materials
How we encoded stairlift components as a graph and used message-passing to predict process time and delivery date in under ten minutes.
A Bill of Materials is not a list. It is a tree with shared subtrees: the same motor sits inside two different rail configurations, the same battery powers three different controller variants. Treating a BOM as a flat list of part numbers throws away the structure that matters most for cost and time prediction.
At Extrema, the sales team was producing customer quotes by hand using a spreadsheet inherited from the previous decade. A quote took two days on average and was wrong about delivery date often enough that customers had stopped trusting it. The classical fix is a rules engine — but a stairlift configurator has hundreds of components and thousands of compatibility constraints. Maintaining a rules engine for that is a full-time job that nobody at the company wanted.
The shape of the problem screamed graph. Each component becomes a typed node — rail, motor, controller, battery, joint, seat. Each edge is one of three types: assembly (this motor goes inside that controller), dependency (this rail length forces that joint count), or swap-compatibility (this motor variant can be substituted for that one). Once you have the graph, predicting cost and time is a regression on the whole graph, conditioned on the leaf-node features the customer specified.
We used GraphSAGE for the encoder — three message-passing layers, mean aggregation, residual connections. The encoder produced a 64-dim graph embedding that fed two small MLP heads: one for total assembly time in person-hours, one for total cost in euros. Training data was three years of completed quotes, around 4,200 graphs, with the ground-truth times and costs recorded by the production floor.
The first model trained in an evening and was 80% accurate on time prediction within ±15%, which sounds good until you realise that ±15% of three weeks is half a week — useless for a delivery promise. The accuracy ceiling came not from the architecture. The accuracy ceiling came from the data. We pulled a random sample of 50 historical BOMs and discovered something embarrassing: the same physical motor appeared under eleven different part numbers, accumulated over a decade of spelling variations, supplier consolidations, and operator typos. The graph thought these were eleven distinct components. The model could not generalise across them.
We spent two months on data cleaning. A canonical-component dictionary, fuzzy-matched against historical part numbers, hand-validated by the warehouse manager. The accuracy on time prediction jumped to ±4% in a single retrain. The architecture had not moved an inch. The lesson is the one nobody writes blog posts about: in industrial settings, your accuracy ceiling is almost always your data quality. A clean tabular CSV with the right features beats a cathedral GNN on dirty data, every single time. The right play is to spend the first month cleaning, not architecting.
The customer-facing system now produces a complete BOM and a delivery date in under ten minutes, with confidence intervals. Sales gets to commit. The customer gets a real promise. The model wins zero points for theoretical elegance and a lot of points for being right.