Case study: automating product data for 3,000+ products with an LLM
How a Shopware 6 plugin turned product images into multilingual copy, SEO and categories for Illux - and the guardrails that made it safe to run unattended.
Illux sells artwork online. Every product needed a description, SEO metadata, categories, tags and translations into Danish, English, Norwegian and Swedish - and none of it existed in any data source. It was written by hand, product by product, and the cost grew with the catalogue.
I built the automation as sole developer during my internship at WEXO, from problem framing to production. This post is about what made it work in production, not in a demo. The full project write-up is on the Illux Product AI page.
The result first
| Measure | Result |
|---|---|
| Products enriched | 3,000+ |
| Manual enrichment left | none for most of the catalogue |
| Time saved | hundreds of hours |
| Output languages | configurable (4 at launch, all from one AI call) |
| Status | business-critical, still in daily use |
What the automation does
Product images, plus name, manufacturer and attributes, go to a generative-AI model. What comes back is schema-enforced JSON: SEO metadata, a customer-facing description, and category, tag and property assignments from a whitelist, in every configured language from a single call. The language list is a setting: Illux launched with Danish, English, Norwegian and Swedish, and adding German or French is a config change, not a code change.
Three decisions carry most of the weight:
- Schema-enforced output, not prose. The model fills a typed structure, so there’s no brittle text parsing. Properties come from a whitelist of the shop’s existing options; when nothing fits, the model can propose a new one, but it waits in an admin queue until someone approves it.
- A provider-agnostic abstraction. The domain logic talks to an interface, not to one vendor. Swapping the LLM provider doesn’t touch the business code.
- Configuration over code. Output languages, length limits, keyword counts and the scoring rules are all settings, so the shop can tune the system without a developer.
What made it safe to run unattended
Getting a model to write a nice product description is the easy part. Letting it write to a live catalogue every day is the actual project.
A confidence score built from configurable heuristics. The model’s own confidence isn’t enough to trust on its face, so every result is also checked against plain, deterministic rules. The score starts from the model’s per-field confidence, weighted by how much each field matters (meta title, meta description, keywords, description, properties). Each heuristic that fires then subtracts a penalty:
| Heuristic | What it catches |
|---|---|
| Length rules | Titles, meta descriptions and descriptions that are too short or over the configured limits |
| Keyword rules | Too few SEO keywords, or too long in total |
| Generic content | Stock phrases (“perfekt til enhver”, “fantastisk tilføjelse”), lorem ipsum and unfilled [placeholders], matched by configurable patterns |
| Hedging words | Unsure phrasing (“muligvis”, “måske”, “kunne være”: possibly, maybe, could be) from a customer-managed word list |
| Duplicate content | The same text returned in different languages |
| Property sanity | No properties found, too many candidate options (a sign of uncertainty), or a low match rate against the shop’s real properties |
| Completeness | A response that came back partially empty |
Every weight, penalty, threshold and word list is a setting the customer controls, and the total penalty is capped so one rule can’t sink a good result. Each penalty also leaves a plain-language warning, so a reviewer sees why a product was flagged (“meta title too short”, “hedging language in description”), not just a number.
Human-in-the-loop review. Results above the threshold are applied automatically; the rest wait in a transaction-safe review queue. The customer can move the threshold, switch review off (full auto-apply), or send everything to review. A dashboard shows the time saved.
A full audit trail. For every analysis: the exact prompt, model and version, confidence, reviewer, approval history and which batch it came from. When someone asks “why does this product say that?”, there’s an answer.
Resilient batching. Up to 500 products per run, six per API call, processed asynchronously through Symfony Messenger over RabbitMQ so nothing blocks a web request. Retries with exponential backoff, idempotency, rate limiting and caching protect against a flaky or slow AI provider.
An evaluation harness. Models and prompt strategies are compared on average confidence, which also catches quality drift when a model or prompt changes. Without it, “the new model seems better” is a feeling, not a measurement.
The second half: seeing art on your own wall
Wall art is hard to sell online because people can’t picture it at home. The same plugin lets shoppers pick frame, size and material, then see the piece composited into curated room scenes - or into a photo of their own room. Each tile streams in over server-sent events as it’s ready. An admin module generates new photorealistic interiors from structured photographic parameters, and they wait for approval before they go live.
What I’d tell a team considering this
- Start from the data you don’t have. The win here wasn’t “AI”; it was that the metadata didn’t exist anywhere and was expensive to create by hand.
- Make the output typed and constrained. If the model chooses from your own options and has to ask before adding one, it can’t quietly invent new ones.
- Don’t take the model’s word for its own confidence. Cheap, deterministic checks (length, keywords, placeholder text, hedging, duplicates) catch most bad outputs, and they explain themselves.
- Decide the confidence policy with the business, not for it. The threshold is a business decision; the system should make it adjustable.
- Log the prompt and model with every result. You will be asked, and you’ll need to compare when you change models.
- Measure before and after a model change. An evaluation harness is cheap compared to discovering quality drift in production.
If your team has a manual, repetitive data job like this - product data, documents, emails, tickets - that’s the kind of work I take on. The first 30-minute call is free.
// Comments
Only your initials are shown and stored with your comment, never your name or picture. Stored in the EU (Supabase). Delete your comments any time.