· 8 min read

Own the model, not the API: cleaning SAP master data with a local SLM

SAP master data can't go to a cloud LLM in a regulated shop. So I fine-tune a small model you own: air-gapped, audited, trained only on synthetic data.

open-source gdpr data-quality

Every SAP migration I have worked on hits the pain points. The master data is not clean, and the same supplier data exists three times with three spellings. Countries are written as “Germany”, “Deutschland”, and “DE”. Dates and amounts arrive in German and US formats. Missing values are encoded five different ways. Cleaning it by hand is slow, and writing a rule for every possible mistake is a job that never finishes.

The obvious modern move is to throw a large language model at it. For a lot of the companies I work with, that move is a non-starter before it begins. You cannot paste supplier and customer master data into a cloud API. Under GDPR it is often not defensible, and for many German firms it is not even a legal question, it is a matter of posture. The data does not leave the building. Full stop.

So I built the other option: a small model you own, fine-tuned to clean your data, that runs on your own hardware with the network cable pulled out. It is fully open source. This post is about why that shape works, and how it grew from a laptop demo into something running in a real air-gapped environment.

The idea: teach the rules to a model, keep the rules as a floor

The tool is Enterprise SLM Data Cleaner. The core loop is simple to state.

Your data standard is written down once, as a readable YAML file: allowed country codes, legal forms, date formats, how a missing value is spelled. A data steward can open and edit it without touching code. From that standard the system invents thousands of practice records, dirty input paired with the correct cleaned output, and fine-tunes a small language model on them. No real data is used in training, which is exactly why the training step is GDPR-uncritical. Your data only appears at runtime, and it stays in-house.

Dirty record goes in, clean one comes out:

// in
{ "name1": "  Muster  Handels ", "legalForm": "mbH", "city": "Muenchen ",
  "country": "Germany", "iban": "de89 3704 0044 0532 0130 00",
  "currency": "€", "status": "aktiv", "validFrom": "01.03.2024", "amount": "1.234,56" }
// out
{ "name1": "Muster Handels", "legalForm": "GmbH", "city": "Muenchen",
  "country": "DE", "iban": "DE89370400440532013000",
  "currency": "EUR", "status": "active", "validFrom": "2024-03-01", "amount": 1234.56 }

The main point is that rules are great at the cases you anticipated. The model shines on the long tail: the misspelling, the odd abbreviation, the format nobody wrote a rule for. So the two work together! Every record the model cleans is re-checked against the rulebook, and if the answer violates a rule, a deterministic engine takes over as a safety net. The model raises the ceiling, the rules guarantee a floor it cannot fall below. Anything the system is unsure about, a rule violation or low model confidence, goes to a manual review queue instead of slipping through.

How it grew

This did not start as an enterprise tool. It started as Local SLM Data Cleaner, a demo you can run on a Mac in an afternoon. Fine-tune a 0.6B model with Apple’s MLX, watch it clean records, done. The whole point was to make the idea legible: you can own the model, and the only thing it costs to run is electricity.

Then people asked to run it on the hardware they actually had, which was rarely a Mac. So I wrote a CUDA port that trains on an NVIDIA GPU with as little as 4 GB of VRAM, swapping MLX for Hugging Face Transformers, PEFT, and TRL. The data generator, eval harness, and runtime stayed the same.

These demos proved the idea, but they were not something you could hand an auditor. Turning “it works on my laptop” into “it runs in production at a regulated company” is most of the actual work, and it is what the enterprise version adds.

What “own the model” has to mean to hold up

A model you own is only worth something if you can prove the claims that come with it. Four things make the difference.

It is trained on invented data, and only invented data. Not one real record is used to fine-tune the model. That is not a convenience, it is the reason the training pipeline is clean under GDPR. Your data enters at runtime and never leaves.

Delivery is sealed, not promised. For production, everything ships as one container run with its network stack removed. The container refuses to start if the model weights do not match the fingerprint pinned in version control, so nobody can swap the model unnoticed. Records go in and out through a mounted folder. Air-gapped is a property of the configuration, not a line in a slide deck.

Every decision is on the record. Each cleaning is appended to an audit log: input, output, every single change, the confidence, and the exact hashes of the model weights and the convention file. The log is append-only. Entries are never edited or deleted, and review decisions are added as new entries. This is the artifact your Datenschutzbeauftragter asks for, and the one an SAP migration sign-off actually needs.

The base model is swappable, and that is a feature. The demo default is Qwen3-0.6B, which is Apache-2.0 but Chinese in origin. Objectively and technically, in this architecture, that does not matter: the (open) weights are a static file of numbers running in a container with no network, so there is no channel for data to go anywhere regardless of who trained them. Commercially and politically it matters a great deal, because “the AI is Chinese” can end a procurement conversation on the spot. The whole stack is model-agnostic! The convention spec, the synthetic data, the eval suite, the audit trail, and the container do not care what sits inside. Swapping to a European base model like Mistral’s Ministral-8B, Fraunhofer’s Teuken-7B, or EuroLLM means retraining, which is cheap because the data is already generated, and re-running the same quality gate.

The quality gate

None of the above is worth anything if a change quietly breaks the cleaning. So the repo carries a pinned suite of test cases, including adversarial ones. Is “Bavaria” wrongly “corrected” into a country? Is “mbH” recognised as GmbH? Is a US date misread as a German one? Continuous integration blocks any change that alters this documented behaviour, and releases are gated by measured accuracy rather than pass/fail. When you swap the base model, it faces the same test set and the same bar.

What the real deployment taught me

There is a production deployment. Because it is air-gapped and the client’s data is confidential, there is genuinely nothing I can screenshot or quote, and that constraint is the whole point of the design. What I can say is that the parts I was most nervous about, the audit trail and the review queue, turned out to be the parts that mattered most. The model’s accuracy got attention in the demo. In production, what people leaned on was being able to answer “why did this record change” with a log entry that named the exact model and convention version. Owning the model is what made that answer possible.

The repo is at github.com/TMFNK/Enterprise-SLM-Data-Cleaner, under AGPL-3.0, with the Mac and CUDA demos linked from it. If you want to see the idea end to end before committing, start with the demo and run it on a laptop.

What I’d do differently next time

Three things.

One: write the review queue rules before the first run. I treated the manual review queue as a catch-all for whatever the model was unsure about, and let the confidence threshold sort it out. That put too much in the queue early. Deciding up front which fields must always be reviewed, regardless of confidence, would have made the first weeks calmer.

Two: pick the base model with procurement in the room, not after. I built on the smallest, fastest model and treated the swap to a European model as a later step. For the clients who care about sovereignty, and the ones who care are usually the ones who need this tool, that question comes up on day one. I would now have the model conversation before the technical one.

Three: seed the convention file from real disagreement, not from the standard. The written data standard and the data people actually enter are two different things. The convention file is more useful when it starts from the messes the people already argue about, than from the perfect document nobody follows. I would spend the first session collecting the real edge cases, not the official rulebook.

← All notes
EB

Eddie Beloiu

Freelance Data Platform Engineer · Munich