Von 94 % auf 100 % bei gescannten deutschen Dokumenten

Dario Klingenberg

Dario Klingenberg

8 Min. Lesezeit

Ein Vision-LLM musste exakte Zahlen von gescannten deutschen Formularen ablesen. Ein Benchmark aus 107 handverifizierten Fragen erreichte 94 % (101/107). Weil das Ganze als elluminate-Experiment mit Reasoning-Level-Bewertung lief, kam jeder Fehlschlag mit der Begründung des Judge-Modells, die auf die Bildauflösung zeigte, nicht auf das Prompting. Drei Experimente später: 107/107. So lief diese Schleife ab.

Wo KI-Projekte scheitern

Die meisten KI-Projekte überstehen die Demo. Sie scheitern auf der Strecke zwischen einem vielversprechenden Prototyp und einem System, dem Sie zutrauen würden, die Rechnung eines Kunden festzulegen: Unternehmen verwerfen 46 % aller KI-Proof-of-Concepts, bevor sie in Produktion gehen (S&P Global, 2025).

Aus unserer Erfahrung liegt das fehlende Stück selten an der Fähigkeit des Modells. Es ist Evidenz: Niemand kann mit Zahlen belegen, ob das System gut genug ist. Also geht es entweder auf gut Glück live, oder gar nicht. elluminate existiert, um genau diese Evidenz zu liefern. Das folgende Beispiel zeigt konkret, wie das in der Praxis aussieht.

Das Problem: Zahlen, die exakt stimmen müssen

Eine gesetzliche Krankenkasse musste exakte Werte aus den Dokumenten extrahieren, die Mitglieder hochladen: Steuerbescheide, Rentenbescheide, Gehaltsabrechnungen sowie eine Mischung aus Online- und handschriftlich ausgefüllten Papierfragebögen. Den Anstoß für dieses Projekt gab die Beitragsberechnung: Bei freiwillig versicherten Mitgliedern richtet sich der monatliche Beitrag nach dem Einkommen, sodass eine von der Seite abgelesene Zahl direkt festlegt, was jemand zahlt. Die Fähigkeit reicht aber über diesen einen Fall hinaus. Die Dokumentenvielfalt wächst mit der Zeit, deshalb ist die Evaluation darauf ausgelegt, über das gesamte Spektrum eingereichter Unterlagen hinweg zu generalisieren, statt auf ein einzelnes Formular oder Feld zugeschnitten zu sein.

Das ist visuelles Frage-Antwort-Verstehen mit einer strikten Exact-Match-Vorgabe: Die Antwort ist eine bestimmte Zahl auf einer bestimmten Seite, und „nah dran” zählt nicht. Ein Modell, das 18.450 als 18.150 liest, setzt für ein reales Mitglied den falschen Beitrag fest. Gescannte Formulare erschweren das zusätzlich: blasser Druck, dichte Tabellen und Handschrift, bei der auch Menschen zweimal hinschauen müssen.

Es gibt einen zweiten Grund, warum die Messlatte bei 100 % liegt und nicht bei 94 %: Die extrahierte Zahl ist nur die Eingangstür. Dahinter läuft die Beitragslogik der Kasse (Einkommensarten, Mindest- und Höchstgrenzen der Beitragsbemessung, Familienversicherung, ein Dickicht an Sonderregeln), und genau dort stecken die eigentlichen Bugs. Solange ein paar Prozent der Werte fehlerhaft ausgelesen werden, bleibt jeder falsche Beitrag mehrdeutig: Eine falsch gelesene Seite und ein Fehler in der Berechnung sehen von außen identisch aus. Eine verifizierte 100-%-Extraktion beseitigt diese Mehrdeutigkeit. Sobald die Eingabedaten verlässlich sind, ist jeder verbleibende Fehler im Endergebnis ein echter Programmierfehler, dem man nachgehen kann, kein Rauschen aus einem unscharfen Scan.

Das getestete System war exakt das Produktiv-Setup des Kunden: ein on-premise betriebenes Open-Weights-Vision-Language-Modell (Qwen-3.5-Familie), gespeist durch dieselbe Bildpipeline, die auch die Dokumente der Mitglieder durchlaufen.

Der Testdatensatz: echte Dokumente, exakte Antworten

Der Benchmark basiert auf 21 echten, anonymisierten Mitgliederdokumenten. Das sind tatsächliche Einreichungen aus den oben genannten Kategorien, keine synthetischen Mock-ups. Synthetisches Papier ist zu sauber: Es würde die Artefakte gar nicht erst zeigen, mit denen das Produktivsystem tatsächlich zurechtkommen muss.

Aus diesen Dokumenten entstanden 107 Fragen, jede eine gezielte numerische Extraktion (das zu versteuernde Einkommen im Steuerbescheid, der monatliche Betrag im Rentenbescheid, eine einzelne Zahl aus der Gehaltsabrechnung). Fragen und Referenzantworten wurden von einem separaten, leistungsstarken Modell (Claude) beim Lesen jedes Dokuments entworfen und anschließend von Hand gegen die Quelle geprüft. So ist der Antwortschlüssel belastbar und nicht zirkulär. Aus jeder Frage wird ein Testfall: eine Zeile mit Dokument, Frage und Referenzantwort, gegen die bewertet wird. In elluminate ist eine Menge solcher Fälle eine Collection.

Schritt eins: einmal festlegen, was „gut” bedeutet

Eine Evaluation ist immer nur so vertrauenswürdig wie ihre Definition von „richtig”. Der robusteste Weg, das festzulegen, ist eine Reihe einfacher Ja/Nein-Fragen, die ein Fachexperte selbst schreiben und verantworten kann, ganz ohne Code. In elluminate ist diese Menge ein Kriterien-Set. Wir formulierten bewusst ein formattolerantes Kriterium: Die Werte fließen in eine strukturierte Pipeline, also darf ein fehlendes „€”, ein anderes Tausendertrennzeichen oder zusätzlicher Text nicht als Fehler zählen. Nur die Zahl zählt.

# criteria are immutable once created — the name is versioned, so every
# past experiment keeps the exact definition of "good" it ran against
criterion_set, _ = client.get_or_create_criterion_set(
    name="Document Parsing Correctness v2",
    defaults={"criteria": [
        "Enthält die Antwort den Zahlenwert {{reference_answer}}? Nur die "
        "Zahl muss übereinstimmen, Formatierung ist irrelevant: ein "
        "fehlendes €-Zeichen, ein anderes Tausendertrennzeichen, ein "
        "fehlendes ',00' (Nullcent) am Ende oder zusätzlicher erklärender "
        "Text zählt ausdrücklich NICHT als Fehler."
    ]},
)

Weil das Kriterium fix bleibt, spiegelt eine spätere Änderung im Score eine Änderung im System wider, nicht im Test.

Schritt zwei: die Produktions-Ausgaben bewerten

Mit einer Definition von „gut” ist der Rest mechanisch. Wir haben jedes Dokument durch den Produktionspfad geschickt (Bildpipeline, Vision-Modell, Prompt) und die Antworten zur Bewertung gegen das Kriterium nach elluminate hochgeladen. add_responses registriert die Ausgaben, rate_responses bewertet jede einzelne und liefert im DETAILED-Modus zusätzlich die Begründung des Judge-Modells:

responses = experiment.add_responses(
    responses=model_answers,        # generated externally, exactly as in prod
    template_variables=test_cases,
    metadata=metadata,
)
ratings = experiment.rate_responses(responses, rating_mode=RatingMode.DETAILED)

for resp, rating_set in zip(responses, ratings):
    passed = all(r.rating == RatingValue.YES for r in rating_set)
    # DETAILED mode -> each rating also carries the judge's reasoning

Die echte Pipeline Ende-zu-Ende laufen zu lassen, ist entscheidend: Der Score spiegelt das wider, was ein Mitglied tatsächlich bekäme, inklusive des Downscalings, das, wie wir noch sehen werden, über mehrere Antworten entscheidet. Und weil die Generationen gespeichert sind, ist erneutes Bewerten kostenlos: Kriterium verfeinern oder Läufe vergleichen dauert Sekunden, ganz ohne neue Inferenz.

94 %, und elluminate verrät, warum

Der erste Lauf, der die Produktion mit 896-px-Bildern spiegelte, erreichte 101/107 (94 %). Für ein ungetuntes Open-Weights-Modell respektabel, aber hier nicht genug: Jeder der sechs Fehlschläge bedeutet ein Mitglied, dem das falsche Einkommen berechnet wird.

Bei einer bloßen 94-%-Zahl feiern Teams entweder vorschnell den Sieg, oder sie fangen an zu raten. Weil der Lauf mit RatingMode.DETAILED erfolgte, kam jede fehlgeschlagene Antwort mit der Begründung des Judge-Modells, und die sechs Fehlschläge ließen sich schnell einordnen:

  • 4× Handschrift: handschriftliche Daten und Beträge falsch gelesen (eine „1” gelesen als „4”).
  • 1× Kleingedrucktes: eine kleine gedruckte Ziffer als 3 statt 5 gelesen.
  • 1× leere Antwort: das Modell lieferte gar nichts zurück.

Zwei Ausschnitte desselben handschriftlichen Datums, einer unscharf und schwer lesbar, einer scharf und gut lesbar. Dasselbe handschriftliche Datum in zwei Auflösungen. Auf die produktionsüblichen 896 px herunterskaliert, verschwimmt die geschwungene deutsche 1 zu einer 4 (Modell las 03.04.26). Der Zoom-Durchgang rendert nur diese Seite neu, mit 2048 px, und liest sie korrekt (03.01.26). Synthetisches Beispiel, kein echtes Mitgliederdokument.

Der Score zeigte uns, wo wir standen. Die Begründung zeigte uns, was als Nächstes zu tun war.

Fünf der sechs Fehler liegen in kleinen, schwer erkennbaren Bereichen der Seite, was zwei mögliche Erklärungen offenließ. Entweder waren die Pixel von vornherein nicht da (ein Problem der Bildpipeline), oder das Modell hat die Striche klar gesehen und nur die falsche Lesekonvention angewendet, was sich per Prompt beheben ließe. Die zweite Erklärung ist nicht abwegig: In Deutschland schreibt man die Ziffer 1 mit einem langen Aufstrich, und auf einem groben Scan wird diese Form leicht als 4 gelesen, genau der Fehler aus unserer Liste. Wir haben beide Hypothesen getestet.

Iterieren, ein Experiment nach dem anderen

Versuch 1: Prompt Engineering (hat nicht funktioniert)

Wir haben sieben System-Prompt-Strategien am schwierigsten Einzelfall getestet: deutsche Ziffernkonventionen explizit ausbuchstabiert, erst transkribieren, Striche zählen, Selbstkritik. Die beste Strategie traf 1 von 5 Mal. Die Konvention war also nicht das Problem. Das Modell konnte die Striche schlicht nicht gut genug erkennen.

Versuch 2: Auflösung (der eigentliche Hebel)

Wenn die Pixel nicht da sind, bringt keine Anweisung sie zurück. Die Produktion skalierte jede Seite auf 896 px an der längsten Kante herunter, und bei dieser Größe verschluckt der Vision-Encoder kleine Bereiche beim Pooling. Also haben wir die Auflösung systematisch variiert: gleiche Fragen, gleiches Kriterium, nur die Bildgröße änderte sich zwischen den Läufen.

BildauflösungErfolgsrate
896 px (Produktion)94 % · 101/107
1536 px98 % · 105/107
Zwei-Pass-Zoom + Retries100 % · 107/107

Das letzte handschriftliche Datum wurde erst bei 2048 px lesbar.

Die Anhebung auf 1536 px brachte den Score auf 105/107 (98 %). Ein hartnäckiges handschriftliches Datum brauchte volle 2048 px, bevor es korrekt gelesen wurde, und das in 5 von 5 Läufen, was eher eine Auflösungsgrenze bestätigt als Zufall.

Versuch 3: ein zweistufiger „Zoom”-Durchgang

Jede Seite mit 2048 px zu rendern ist teuer, und die meisten Seiten brauchen das nicht, also ist der zweite Durchgang an eine Bedingung geknüpft. Durchgang eins läuft günstig und niedrig aufgelöst über alle Seiten; zu jeder Antwort meldet das Modell selbst, wo der Wert steht, und als Konfidenz-Score, wie sicher es sich ist, die Zahl sauber gelesen zu haben. Bei hoher Konfidenz (klar gedruckte Zahlen) behalten wir die Antwort und stoppen dort. Bei niedriger Konfidenz, genau das, was Handschrift und Kleingedrucktes erzeugen, rendern wir nur diese eine Seite mit 2048 px neu und lesen sie erneut.

Der selbst gemeldete Score trennt für sich genommen nicht zwischen richtig und falsch, fällt aber zuverlässig unter die Schwelle, sobald der Druck nicht mehr glasklar ist. Das macht ihn zu einem verlässlichen Auslöser für „genauer hinschauen” und behält den teuren Durchgang nur den Seiten vor, die ihn brauchen. Der erneute Lesevorgang schickt die ganze Seite, keinen engen Ausschnitt: Ein leicht verschobener Ausschnitt kann in einer benachbarten Tabellenzelle landen und das Modell die falsche Zahl lesen lassen. Ein Zoom auf die volle Seite fügt nur Pixel hinzu, er kann die Antwort nicht verstecken.

Die letzte Lücke zu 100 % schließen

Übrig blieb die eine leere Antwort. Das Produktionsmodell ist ein „denkendes” Modell mit variablem Reasoning-Budget; gelegentlich verbrauchte es das gesamte Budget beim Nachdenken und lieferte nichts zurück. Die Lösung ist so unspektakulär, wie sie klingt: Retry mit höherem Token-Budget, dazu ein Retry auf Transportebene für instabile Verbindungen. Experiment erneut ausgeführt: 107/107.

Ein einziger sauberer Lauf kann „behoben” aber nicht von „Glück gehabt” unterscheiden: Dieser Fehler war von Anfang an stochastisch. Genau da zählt günstige Wiederholung: Fragen und Kriterium stehen schon fest, also kostet ein erneuter Lauf nur ein paar Minuten Inferenz und sonst nichts. Das 2048-px-Ergebnis von vorhin wurde genauso abgesichert, über fünf Läufe hinweg. Von einem Start bei 94 % brauchte es drei Experimente, um verifizierte 107/107 zu erreichen. Klar ist: Diese 100 % bedeuten, dass die 107 Fragen im Benchmark gelöst sind, nicht dass jedes Dokument, das ein Mitglied einreichen könnte, fehlerfrei gelesen wird. Deshalb bleibt der Datensatz nicht bei 107 eingefroren. Die Produktion bringt laufend schwierigere Fälle zutage (eine unleserlichere Handschriftprobe, ein Formularlayout, das der Benchmark noch nie gesehen hat), und jeder davon wird als neue Frage zurück in den Benchmark aufgenommen, sodass die Messlatte bei 100 % bleibt, während der Benchmark stetig wächst. So schließt sich die Generalisierungslücke: Jeder im Feld gefundene Fehler wird zu einem dauerhaften Test, gegen den das System nicht mehr stillschweigend zurückfallen kann.

Warum die Schleife funktioniert hat

Keine der Lösungen war besonders raffiniert: mehr Pixel, ein zweiter Blick auf schwierige Seiten, ein Retry. Die eigentliche Arbeit bestand darin, zu wissen, welche Änderung nötig ist, und zu belegen, dass sie wirkt, und genau das macht elluminate zur Routine:

  1. Experten definieren „gut”: ein formattolerantes Ja/Nein-Kriterium, verantwortet von den Menschen, die die Domäne kennen, nicht versteckt im Code.
  2. Evidenz, schnell: Reasoning-Level-Bewertung machte aus sechs Fehlschlägen eine Diagnose; gespeicherte Generationen machten erneutes Bewerten sofort möglich.
  3. Fairer Vergleich: dieselben Fragen und dasselbe Kriterium über jeden Lauf hinweg, sodass jede Änderung (ein Prompt, eine Auflösung, ein zweiter Durchgang) ein isoliertes, vergleichbares Experiment war.

Ein Detail, das man leicht übersieht: Während der Iteration haben wir Lösungskandidaten zunächst nur an den fehlgeschlagenen Fällen geprüft (der schnellste Weg zu sehen, ob eine Änderung überhaupt hilft). Gezählt hat aber kein Score, bevor nicht alle 107 Fragen erneut durchgelaufen waren, denn eine Änderung kann auch bisher bestehende Fälle kippen. Dass eine höhere Auflösung nach hinten losgeht, ist schwer vorstellbar, aber das Zwei-Pass-Setup änderte den Prompt für jede einzelne Seite, weil jede Antwort jetzt mit einer Position und einer Konfidenz kommt, und das hätte durchaus zuvor korrekte Antworten durcheinanderbringen können. Dass das nicht passiert ist, hat sich erst durch den erneuten Lauf über den vollständigen Benchmark nach jeder Änderung gezeigt, ohne zusätzliche Kosten außer der Inferenz selbst.

Der Benchmark überlebt außerdem das Projekt: Die Collection und das Kriterium bleiben in elluminate erhalten, sodass die Neubewertung eines ausgetauschten Modells oder eines neuen Dokumenttyps nur Minuten dauert und jede künftige Änderung von einer bekannten Baseline aus startet.

Jeder Schritt lieferte eine Zahl, die sich verteidigen ließ, und die gespeicherten Experimente dienen zugleich als der Audit-Trail, den eine EU-AI-Act-Prüfung erwartet. Für einen regulierten Workflow, der Einkommensdaten von Mitgliedern verarbeitet, ist dieser Trail eine Grundvoraussetzung, um überhaupt live gehen zu dürfen.


Kunde und Dokumente anonymisiert; Beispielwerte sind illustrativ, keine echten Zahlen. Das getestete System ist ein on-premise betriebenes Open-Weights-Vision-Language-Modell, evaluiert im Ist-Zustand des Deployments.

Das ist ein Weg, aus „wir glauben, es funktioniert” ein „wir wissen, es funktioniert” zu machen. Dieselbe Methodik funktioniert für jedes KI-System, bei dem Sie die Performance präzise vergleichen müssen: Modell-Upgrades, Prompt-Änderungen, Pipeline-Anpassungen.

Interessiert, strukturierte Evaluationen für Ihre eigenen KI-Systeme aufzusetzen?

Demo mit unseren Gründern buchen

Weitere Artikel

Nutzen Sie das volle Potenzial von KI

Erfahren Sie, wie unsere Produkte Ihnen helfen können, KI-Agenten sicher zu evaluieren, deployen und zu überwachen.