Konstruktionszeichnungen durchsuchbar machen: KI-Suche im Zeichnungsarchiv nach dem Vorbild Kuken

Andreas Nigg
Andreas Nigg · Mitgründer, Pondhouse Data
Veröffentlicht 17 Min. Lesezeit
Konstruktionszeichnungen durchsuchbar machen: KI-Suche im Zeichnungsarchiv nach dem Vorbild Kuken
Inhalt

Kuken Industries, ein japanischer Hersteller von Kühltürmen und Lüftungstechnik, hat sein Zeichnungsarchiv aus über 70 Jahren mit KI durchsuchbar gemacht. Techniker geben eine Zeichnungs- oder Teilenummer ein oder laden ein Bild oder PDF einer Zeichnung oder Stückliste hoch. Das System liest die Nummern, schlägt passende Zeichnungsseiten mit Vorschau vor, und die Fachkraft entscheidet anhand des Originals. Im Probebetrieb dauerte eine Suche laut Kuken im Schnitt 4,5 Minuten statt Dutzender Minuten bis zu einem halben Tag.

Wie das Modell, die Architektur und die Kosten aussehen, hat Kuken nicht veröffentlicht. Wir haben das Prinzip deshalb selbst nachgebaut: ein Vision-Modell liest die Schriftfelder, Postgres sucht über normalisierte Nummern, Volltext und Bildähnlichkeit, und ein zweiter Modellaufruf vergleicht die Kandidaten. Getestet haben wir mit 134 Seiten, darunter 60 echte Scans von 1909. Dieser Beitrag erklärt den Fall Kuken, die Bausteine einer solchen Suche, unseren Prototyp mit Code und Messwerten und die Stellen, an denen es schiefgeht.

Was Kuken gebaut hat

Kuken Industries (空研工業) sitzt in Fukuoka, wurde 1956 gegründet und hat laut Pressemitteilung 572 Mitarbeiter. Das Unternehmen baut Klima- und Lüftungsanlagen für große Gebäude, Einkaufszentren und Fabriken und hat einen der größten Anteile am japanischen Markt für Kühltürme.

Das Problem ist typisch für Maschinen- und Anlagenbauer mit langer Geschichte. Braucht ein Kühlturm ein Ersatzteil, passen heute gefertigte Teile nicht immer zu älteren Anlagen. Ohne die Originalzeichnung muss das Teil im Zweifel neu konstruiert werden. Eine Suche nach Modell oder Teilename liefert oft mehrere Zeichnungen, weil Kuken seine Anlagen über die Jahrzehnte an einzelne Kunden angepasst hat. Die Ausführung hängt also vom Kunden und vom Baujahr ab. Als das Archiv noch auf Papier lag, konnte eine Suche einen halben Tag dauern. Auch nach der Digitalisierung mussten die Techniker wissen, unter welchem Namen oder welcher Nummer sie suchen und wo die Datei liegt. Neue Mitarbeiter fragten dafür erfahrene Kollegen.

Gebaut hat Kuken das System gemeinsam mit Recursive, einem KI-Dienstleister aus Tokio und offiziellen OpenAI-Partner, auf Basis der OpenAI API. Laut OpenAI haben die beiden Firmen mehrere Modelle an verschiedenen Zeichnungen getestet, um zu sehen, wie genau und zu welchen Kosten sie Details auslesen.

Der Ablauf in der Web-App sieht laut OpenAI und Pressemitteilung so aus:

  1. Die Fachkraft gibt eine Zeichnungs- oder Teilenummer ein oder lädt ein Bild oder PDF einer Zeichnung oder Stückliste hoch. Ein Screenshot der Stückliste erspart das Abtippen jeder Nummer.
  2. Die KI liest die Teilenummern aus und strukturiert sie.
  3. Die Suche liefert exakte Treffer und zusätzlich Kandidaten, die abweichende Änderungsstände, Unternummern (枝番) und ähnliche Schreibweisen berücksichtigen.
  4. Angezeigt werden die passenden Zeichnungsseiten mit Vorschau, auch Zeichnungen ähnlicher Konstruktionen.
  5. Die Fachkraft prüft Spezifikation des Kühlturms und frühere Ersatzteillieferungen und entscheidet.

Die Pressemitteilung beschreibt das Prinzip in einem Satz: Die KI unterstützt Suche und Vergleich, die Technikerin oder der Techniker prüft das Original und trifft die Entscheidung.

Eine zweite Funktion ist noch im Prototyp: eine Zeichnungsprüfung. Laut Pressemitteilung vergleicht sie den internen Fertigungsauftrag mit Außenmaßzeichnung und Lieferspezifikation, zeigt Abweichungen samt Fundstelle an und prüft auch zeichnungsspezifische Punkte wie fehlende Pflichtvermerke oder Positionsnummern. Handschriftliche Notizen und Layoutfehler deckt sie ausdrücklich nicht vollständig ab, das letzte Wort hat wieder der Mensch.

Welche Ergebnisse Kuken berichtet

Alle Zahlen stammen von Kuken selbst und wurden nicht unabhängig geprüft:

KennzahlWertArt der ZahlQuelle
Suchzeit im Probebetrieb (Werk Fukuoka)Ø 4,5 Minuten, vorher Dutzende Minuten bis eine Stunde, manchmal ein halber Tagim Probebetrieb gemessenPressemitteilung, MONOist
Suchaufwand pro Jahretwa 90 % wenigerHochrechnung auf Basis der Ist-Zahlen des Geschäftsjahres 2025OpenAI, MONOist
Zeichnungsprüfungrund 90 % der KI-Hinweise waren echte AbweichungenPrototyptestOpenAI

Die 90 % bei der Zeichnungsprüfung beschreiben die Präzision: Von den markierten Stellen waren neun von zehn echte Fehler. Wie viele Fehler das Werkzeug übersehen hat, steht nirgends. Laut OpenAI nutzen erfahrene Techniker die gewonnene Zeit, um jüngere Kollegen einzuarbeiten, und weil Zeichnungen in der App angesehen werden, wird weniger gedruckt. Für Ende des Jahres plant Kuken laut Pressemitteilung den Regelbetrieb in den Werken Fukuoka und Chiba.

Nicht veröffentlicht sind das verwendete Modell, die Architektur, die Kosten, die Größe des Archivs und die Methode, mit der die Zeiten gemessen wurden. Alles, was dieser Beitrag ab hier über Technik sagt, kommt aus unserem Nachbau und nicht von Kuken.

Die Bausteine einer Zeichnungssuche

Eine Konstruktionszeichnung ist vor allem Grafik. Durchsuchbarer Text steht fast nur im Schriftfeld und in der Stückliste, und genau dort stehen die Nummern, nach denen Techniker suchen. Daraus ergibt sich ein Ablauf in vier Stufen:

Ablauf einer KI-Zeichnungssuche: aufbereiten, lesen, suchen, entscheiden

  1. Aufbereiten: Scans und PDFs werden zu Bildern, Blaupausen (weiße Linien auf dunklem Grund) werden invertiert, schiefe Scans gerade gerückt. Das Schriftfeld rechts unten wird zusätzlich als Ausschnitt gespeichert.
  2. Lesen: Ein Vision-Modell liest Zeichnungsnummer, Änderungsindex, Benennung, Werkstoff, Datum und die Stückliste in ein festes Schema. Eine lokale Texterkennung (OCR) liest das Schriftfeld unabhängig davon, und Abweichungen werden markiert.
  3. Suchen: Zuerst zählt die exakte Nummer in normalisierter Form. Danach werden mehrere Ranglisten zusammengeführt: unscharfe Suche über die Nummern, Volltext über Benennungen und erkannten Text und, wenn ein Bild hochgeladen wurde, Bildähnlichkeit.
  4. Entscheiden: Die Fachkraft sieht Vorschauen und auf Wunsch einen KI-Vergleich je Kandidat, prüft das Original und bestätigt oder verwirft. Diese Rückmeldungen lassen sich später für bessere Rankings nutzen.

Die Reihenfolge hat einen Grund: Nummern sind präzise und billig zu durchsuchen, deshalb tragen sie die Suche. Bildähnlichkeit hilft, wenn keine Nummer vorliegt, etwa bei einem Foto aus der Anlage. Und weil Zeichnungen derselben Baureihe sich oft nur in Maßen unterscheiden, entscheidet am Ende ein Mensch.

Unser Prototyp und die Testdaten

Um zu messen, ob eine Suche funktioniert, braucht man Zeichnungen, deren richtige Antwort man kennt. Wir haben deshalb zwei Datensätze verwendet:

  • 74 künstlich erzeugte Zeichnungen eines fiktiven Herstellers: sechs Bauteilarten (Flansch, Deckel, Welle, Lagerbuchse, Haltewinkel, Ventilgehäuse) mit je fünf Größen und einem bis vier Änderungsständen, mit Schriftfeld, Änderungstabelle und deutschen Werkstoffangaben. Ältere Blätter tragen die Nummer im alten Format KT 2041/03, neuere als KT-2041-03, und 60 der 74 Blätter sind wie alte Scans gealtert (Rauschen, Tintenverlust, Schräglage, JPEG). Dazu kommen fünf Stücklisten mit 40 Zeilen in unordentlicher Schreibweise und 30 schräg fotografierte Ausschnitte.
  • 60 echte Scans aus dem US-Nationalarchiv: Maschinenzeichnungen des Zerstörers USS Preston von etwa 1909, gemeinfrei, mit Ventilen, Pumpen, Turbinenteilen und gedruckten Stücklisten auf dem Blatt.

Künstlich erzeugte Zeichnung eines Flanschs im Format A3 mit Schriftfeld und Änderungstabelle

Echte Zeichnung von 1909: Standard-Rotgussventil der USS Preston, invertiert (Quelle: National Archives)

Gebaut ist der Prototyp aus wenigen Bausteinen, alle in Python 3.13, getestet am 11. November 2026:

BausteinWerkzeugVersion
Lesen der SchriftfelderOpenAI gpt-6.1-terra über die Responses APIopenai 3.49.0, pydantic 2.14.3
OCR-GegenprobeRapidOCR mit PP-OCRv6 auf onnxruntime3.10.0 / 1.31.1
Datenbank und SuchePostgreSQL mit pg_trgm und pgvector17.11 / 1.6 / 0.8.0
Bildvektorenfacebook/dinov2-base auf der CPUtransformers 5.22.2, torch 2.15.0+cpu
BildaufbereitungPyMuPDF, Pillow, OpenCV2.1 / 13.0.0 / 5.0.0.93
OberflächeFastAPI mit einer Jinja-Seite0.162.4

Ein Hinweis zur Installation: pip install torch holt von PyPI die Variante mit CUDA-Bibliotheken, mehrere Gigabyte groß. Für einen reinen CPU-Server installieren Sie torch aus dem Index https://download.pytorch.org/whl/cpu.

Schriftfelder mit einem Vision-Modell auslesen

Das Modell bekommt pro Seite zwei Bilder: das ganze Blatt in hoher Auflösung (detail: "high") und den Ausschnitt rechts unten in Originalauflösung (detail: "original"). Das ganze Blatt liefert den Zusammenhang, etwa eine Stückliste oben rechts. Der Ausschnitt sorgt dafür, dass kleine Ziffern im Schriftfeld nicht beim Verkleinern verloren gehen.

Die Antwort ist an ein Pydantic-Schema gebunden. Das erzwingt gültiges JSON mit festen Feldern und macht aus "steht nicht drauf" ein null statt einer erfundenen Angabe:

1class PartRow(BaseModel):
2 pos: str | None = Field(description="Position or piece number in the parts list")
3 name: str | None
4 part_no: str | None = Field(description="Part, pattern or drawing number of this row exactly as printed")
5 material: str | None
6
7
8class DrawingMeta(BaseModel):
9 drawing_no: str | None = Field(description="Drawing number from the title block exactly as printed, without the revision")
10 revision: str | None = Field(description="Current revision / index letter or number; null if the sheet has none")
11 title: str | None = Field(description="Part or sheet title as printed")
12 material: str | None
13 date: str | None = Field(description="Drawing date as printed")
14 scale: str | None
15 other_numbers: list[str] = Field(description="Other identifiers on the sheet (order, file, project, ship numbers)")
16 parts_list: list[PartRow] = Field(description="Rows of a parts list / bill of materials printed on the sheet, else empty")
17 legibility: Literal["good", "partly_illegible", "illegible"]

Der Aufruf selbst ist kurz. responses.parse gibt direkt ein DrawingMeta-Objekt zurück, und mit reasoning auf low bleibt die Antwort schnell und günstig:

1def call_model(content, schema, effort="low"):
2 t = time.time()
3 resp = client().responses.parse(
4 model=MODEL,
5 input=[{"role": "user", "content": content}],
6 text_format=schema,
7 reasoning={"effort": effort},
8 )
9 ...
10
11
12def extract_drawing(img, layout_hint=None):
13 prompt = EXTRACT_PROMPT + ("\n\nAbout this archive's title block: " + layout_hint if layout_hint else "")
14 content = [
15 {"type": "input_text", "text": prompt},
16 {"type": "input_image", "image_url": to_data_url(img, 2400), "detail": "high"},
17 {"type": "input_image", "image_url": to_data_url(title_block_crop(img), 1600), "detail": "original"},
18 ]
19 return call_model(content, DrawingMeta)

Der Prompt verlangt, Nummern Zeichen für Zeichen so zu übernehmen, wie sie gedruckt sind, und unleserliche Zeichen als ? zu markieren. Normalisiert wird später im Code, nicht im Modell. Für ein gealtertes Blatt von 1973 kam das zurück:

1{
2 "drawing_no": "KT 2041/01",
3 "revision": "A",
4 "title": "Flansch DN80 PN16",
5 "material": "St 37-2",
6 "date": "1973-01-16",
7 "scale": "1:2",
8 "other_numbers": [],
9 "parts_list": [],
10 "legibility": "good"
11}

Auf den 74 künstlichen Blättern lag das Modell bei Nummer, Index, Benennung, Werkstoff und Datum in allen Fällen richtig, gealtert oder nicht. Die reine OCR mit einem regulären Ausdruck fand Nummer und Index ebenfalls auf allen 74 Blättern. Das sagt vor allem, dass unsere künstlichen Schriftfelder zu sauber waren: gedruckte Schrift, immer gleiches Layout. Die echten Scans waren deutlich schwieriger.

Pro Seite verbrauchte ein Aufruf im Mittel 4.904 Eingabe- und 305 Ausgabe-Token. Bei den Listenpreisen von gpt-6.1-terra (1 US-Dollar je Million Eingabe-Token, 5 US-Dollar je Million Ausgabe-Token) sind das rund 0,6 Cent pro Seite. Rechnerisch kosten 100.000 Seiten damit etwa 640 US-Dollar, mit der Batch API laut Preisliste die Hälfte. Ein Aufruf dauerte im Median 4,4 Sekunden.

Die OCR-Gegenprobe

Ein Vision-Modell liest nicht fehlerfrei, und seine Fehler sehen plausibel aus. Deshalb liest eine zweite, ganz andere Technik dasselbe Schriftfeld: die lokale OCR RapidOCR. Findet sich die vom Modell gelesene Nummer nicht annähernd im OCR-Text wieder, wird die Seite markiert:

1def cross_check(drawing_no, ocr_lines):
2 """Does the local OCR see the same drawing number as the model? Returns (score, best OCR line)."""
3 from rapidfuzz import fuzz
4 target = norm(drawing_no)
5 if not target or not ocr_lines:
6 return 0.0, None
7 best = max(ocr_lines, key=lambda l: fuzz.partial_ratio(target, norm(l["text"])) if len(norm(l["text"])) >= 4 else 0)
8 return fuzz.partial_ratio(target, norm(best["text"])), best["text"]

Wichtig ist partial_ratio statt eines einfachen Vergleichs. Auf den alten Blättern steht vor der Nummer meist "Dr." oder "Dr. No.", und die OCR liest das als Teil der Nummer mit (DR.NO.62-553-69). Mit einem einfachen Vergleich hatten wir deshalb zunächst 30 von 60 echten Scans als Abweichung markiert, fast alle zu Unrecht. Mit dem Teilstring-Vergleich blieben vier Markierungen übrig. Zwei davon waren echte Lesefehler des Modells, zwei waren Brüche wie "1½", die die OCR als "12" liest.

Wenn das Modell das Schriftfeld missversteht

Auf den Scans von 1909 zeigte sich ein Fehler, der in jedem gewachsenen Archiv vorkommt. Unser Prompt sagte, der Änderungsstand stehe im Feld "Index" oder "Rev", wie auf deutschen Zeichnungen üblich. Die Blätter der New York Shipbuilding Company haben unten rechts aber ein Kästchen "INDEX / GROUP / FILE NUMBER", eine Ablagenummer:

Schriftfeld einer Zeichnung von 1909 mit Zeichnungsnummer N-583L-1½ und dem Ablagefeld INDEX 24 (Quelle: National Archives)

Das Modell folgte unserem Prompt und trug die Ablagenummer als Index ein:

1{
2 "drawing_no": "N-583L-1½ R",
3 "revision": "24",
4 "title": "STANDARD 1½ COMPOSITION VALVE FOR PRESSURES FROM 0-100 LBS",
5 "material": null,
6 "date": null,
7 "scale": "12\"",
8 "other_numbers": [
9 "T. B. D. No. 19",
10 "Q",
11 "0-134"
12 ],
13 ...
14}

Abhilfe schafft ein Satz pro Schriftfeld-Layout, den der Prompt bei Seiten aus diesem Bestand anhängt:

1LAYOUT_HINTS = {
2 "nara": "1909 US Navy sheets (New York Shipbuilding Co.). The drawing number follows 'Dr' or 'Dr. No.' "
3 "in the title box. The small box 'INDEX / GROUP / FILE NUMBER' is a filing reference, not a revision: "
4 "these sheets have no revision field, so return null. Keep trailing letters and fractions as part of the number.",
5}
PromptSeitenIndex fälschlich gefülltOCR bestätigt die NummerStückliste erkannt
allgemein6027 (45 %)57 (95 %)50 (83 %)
mit Layout-Hinweis60056 (93 %)50 (83 %)

Der Hinweis hat den Index-Fehler vollständig beseitigt, aber auch einen neuen Fehler erzeugt: Aus 62-534-4 wurde 62-534-A, vermutlich wegen des Satzes über angehängte Buchstaben. Die OCR-Gegenprobe hat genau diese Seite markiert. Zwei weitere Lesefehler blieben in beiden Varianten: 62-553-7 als 62-333-7 (von der Gegenprobe markiert) und 62-584-6 als G2-584-6. Den zweiten machte die OCR genauso, er fiel also nicht auf, wird aber von der Normalisierung im nächsten Abschnitt aufgefangen.

Daraus folgen zwei praktische Regeln. Erstens: Jedes Schriftfeld-Layout in Ihrem Archiv bekommt eine kurze Beschreibung, und Änderungen daran testen Sie an einer Stichprobe, bevor Sie das ganze Archiv neu einlesen. Zweitens: Markierte Seiten gehören beim Einlesen in eine Prüfliste für einen Menschen. Denn eine falsch gelesene Nummer macht die Zeichnung unauffindbar. In unserem Test fand die korrekte Suche nach 62-553-7 das betroffene Blatt nicht unter den ersten drei Treffern, weil im Index 62-333-7 stand.

Zeichnungsnummern normalisieren und unscharf suchen

Kuken nennt Änderungsstände, Unternummern und Schreibvarianten ausdrücklich als Grund, warum eine einfache Suche nicht reicht. Dieselbe Nummer taucht in vielen Formen auf: KT-2041-03, KT 2041/03, KT204103, kt-2041-3 oder mit dem Buchstaben O statt der Null. Der Prototyp speichert deshalb neben der gedruckten Nummer zwei Schlüssel:

1CONFUSABLE = str.maketrans("OILZSGB", "0112568") # characters OCR and models swap with digits
2
3
4def norm(s):
5 """Normalise an identifier for matching: upper case, confusable letters to digits, drop separators."""
6 if not s:
7 return ""
8 return re.sub(r"[^A-Z0-9]", "", s.upper().translate(CONFUSABLE))
9
10
11def key(s):
12 """Like norm(), but leading zeros of each number group are dropped: KT-2041-03 and kt-2041-3 -> KT20413."""
13 if not s:
14 return ""
15 groups = re.split(r"[^A-Z0-9]+", s.upper().translate(CONFUSABLE))
16 return "".join(g.lstrip("0") or "0" if g.isdigit() else g for g in groups if g)

Beide Funktionen laufen auf beiden Seiten, beim Einlesen und bei der Suche. Dass dabei auch echte Buchstaben zu Ziffern werden (aus G2 wird 62), ist gewollt: Solange es symmetrisch passiert, findet die Suche die Zeichnung trotzdem. Den Änderungsstand trennt die Suche vorher ab, wenn er erkennbar ist (Rev. B, Ind. C, -B), und sortiert die passende Revision nach oben, sonst die neueste.

Für alles, was nicht exakt passt, nutzt Postgres die Erweiterung pg_trgm: Sie zerlegt Zeichenketten in Dreiergruppen und findet ähnliche Nummern über einen GIN-Index. Das Schema dazu, gekürzt:

1CREATE EXTENSION IF NOT EXISTS vector;
2CREATE EXTENSION IF NOT EXISTS pg_trgm;
3CREATE TABLE IF NOT EXISTS drawings (
4 id text PRIMARY KEY,
5 ...
6 drawing_no text, -- as printed (model extraction)
7 drawing_no_norm text, -- normalised for matching
8 drawing_no_key text, -- normalised, leading zeros dropped
9 revision text,
10 title text,
11 ...
12 embedding vector(1536),
13 fts tsvector GENERATED ALWAYS AS (
14 to_tsvector('simple', coalesce(drawing_no, '') || ' ' || coalesce(title, '') || ' ' || coalesce(ocr_text, ''))
15 ) STORED
16);
17...
18CREATE INDEX IF NOT EXISTS drawings_no_trgm ON drawings USING gin (drawing_no_norm gin_trgm_ops);
19CREATE INDEX IF NOT EXISTS parts_no_trgm ON drawing_parts USING gin (part_no_norm gin_trgm_ops);
20CREATE INDEX IF NOT EXISTS drawings_fts ON drawings USING gin (fts);
21CREATE INDEX IF NOT EXISTS drawings_vec ON drawings USING hnsw (embedding vector_cosine_ops);

Die Tabelle drawing_parts enthält die Zeilen der Stücklisten, die das Modell auf den Blättern gefunden hat (334 Zeilen auf 50 der 60 alten Scans). So findet eine Teilenummer auch die Zeichnung, auf der das Teil nur in der Stückliste steht.

Wie viel die Normalisierung bringt, zeigt ein Test mit allen 30 Bauteilen, jeweils in sieben Schreibweisen gesucht. Gezählt ist, ob das richtige Bauteil auf Platz 1 (Hit@1) oder unter den ersten drei (Hit@3) steht. Bei der Schreibweise mit Revision muss auch der Änderungsstand stimmen.

Schreibweise der SucheZeichenkette gleichNormalisiert gleichHit@1 ohne Nullen-SchlüsselHit@1Hit@3
KT-2041-03 (neues Format)67 %100 %100 %100 %100 %
KT 2041/03 (altes Format)53 %100 %100 %100 %100 %
KT2041030 %100 %100 %100 %100 %
kt-2041-30 %0 %20 %100 %100 %
KT-2O41-03 (O statt 0)0 %100 %100 %100 %100 %
KT-2041-03 Rev. B0 %100 %100 %100 %100 %
KT-2401-03 (Zahlendreher)0 %0 %50 %50 %70 %

Ein reiner Zeichenkettenvergleich findet selbst bei korrekter Schreibweise nur zwei Drittel, weil ältere Blätter das alte Format tragen. Der Schlüssel ohne führende Nullen hebt kt-2041-3 von 20 auf 100 %. Zahlendreher bleiben schwierig: Bei KT-2401-03 findet die Trigramm-Suche zwar die gemeinte KT-2041-03 auf den Plätzen 1 und 2 (beide Revisionen), direkt dahinter folgt aber schon ein Deckel mit der Nummer KT 2014/01. Genau deshalb zeigt die Oberfläche bei unscharfen Treffern mehrere Kandidaten und keine einzelne Antwort. Die Suche selbst dauerte bei 134 Seiten im Median eine Millisekunde.

Suche nach kt-2041-3: Die exakten Treffer KT-2041-03 in Revision B und A stehen vorne, danach ähnliche Nummern mit ihren Rängen

Exakte Treffer zuerst, dann Rangfusion

Wie kombiniert man eine exakte Nummernsuche, eine unscharfe Suche, Volltext und Bildähnlichkeit zu einer Liste? Die Werte der einzelnen Suchen sind nicht vergleichbar: eine Trigramm-Ähnlichkeit von 0,6 und eine Kosinus-Distanz von 0,2 haben nichts miteinander zu tun. Reciprocal Rank Fusion (RRF) umgeht das und nutzt nur die Ränge. Jede Liste gibt einem Kandidaten 1 / (60 + Rang) Punkte, und die Punkte werden addiert. So beschreibt es auch das Hybrid-Search-Beispiel von pgvector. Exakte Treffer stellt der Prototyp davor:

1 scores = {}
2 for name, ids in lists.items():
3 for rank, i in enumerate(ids, 1):
4 scores[i] = scores.get(i, 0.0) + 1.0 / (K + rank)
5 fused = sorted(scores, key=lambda i: -scores[i])
6 order = exact + [i for i in fused if i not in exact]
7 out = []
8 for i in order[:limit]:
9 out.append({"id": i, "exact": i in exact, "rrf": round(scores.get(i, 0.0), 4),
10 "ranks": {n: ids.index(i) + 1 for n, ids in lists.items() if i in ids}})

Jeder Treffer trägt seine Ränge aus den einzelnen Listen mit (trgm #2, fts #3, vector #1), und die Oberfläche zeigt sie an. Für die Akzeptanz hilft das: Eine Fachkraft sieht sofort, ob ein Kandidat wegen der Nummer oben steht oder nur, weil er ähnlich aussieht. Wie Hybridsuche in einem verwalteten Dienst funktioniert, zeigt unser Beitrag zu RAG mit Azure AI Search.

Stücklisten hochladen statt abtippen

Kukens Techniker laden Screenshots von Stücklisten hoch, statt jede Nummer einzutippen. Der Prototyp macht das mit einem zweiten Schema: Das Modell entscheidet, ob das Bild eine Zeichnung, eine Stückliste oder etwas anderes ist, und gibt bei einer Stückliste jede Zeile zurück. Jede Zeilennummer läuft dann durch dieselbe Suche.

Hochgeladene Stückliste mit Nummern in verschiedenen Schreibweisen: KT 5032/01, KT201402, kt-5032-1, KT-6120-03 Ind. A

Getestet haben wir fünf Stücklisten mit zusammen 40 Zeilen, zwei davon gealtert:

PrüfungErgebnis
Als Stückliste erkannt5 von 5
Zeilen gefunden40 von 40
Nummer exakt wie gedruckt übernommen37 von 40
Richtige Zeichnung auf Platz 1 (33 Zeilen im Archiv)30 von 33
Richtige Zeichnung unter den ersten drei31 von 33
Falscher exakter Treffer für Teile, die nicht im Archiv sind (7 Zeilen)0 von 7

Bei den drei nicht exakt übernommenen Nummern hat das Modell den Buchstaben O stillschweigend in eine Null "korrigiert", obwohl der Prompt das Gegenteil verlangt. Hier war die Korrektur richtig, aber verlassen sollten Sie sich darauf nicht. Normalisieren Sie deshalb immer auf beiden Seiten. Die drei Fehlgriffe auf Platz 1 waren Zahlendreher. Und für Normteile, die gar nicht im Archiv liegen, meldete die Suche nie einen exakten Treffer, sondern nur ähnliche Kandidaten.

Ergebnis einer hochgeladenen Stückliste: pro Zeile die drei besten Kandidaten, exakte Treffer markiert

Wie man strukturierte Daten aus PDFs und Office-Dateien allgemein mit Sprachmodellen herausholt, beschreiben wir ausführlicher in Dokumentenextraktion mit LLMs.

Suche über Bildähnlichkeit

Manchmal gibt es keine Nummer: ein Foto eines Bauteils im Ausdruck, ein Ausschnitt ohne Schriftfeld. Dafür berechnet der Prototyp für jede Seite einen Bildvektor mit DINOv2, einem frei verfügbaren Bildmodell von Meta (Apache-2.0), und speichert ihn in pgvector. Zwei Vorverarbeitungen sollen helfen: Rahmen und Schriftfeld werden weiß übermalt, damit nicht das immer gleiche Formular den Vektor bestimmt, und das Bild wird auf die Linien zugeschnitten, damit ein Foto und ein ganzes Blatt ähnlich groß abgebildet sind.

1def embed(img, mode="mask+crop"):
2 """DINOv2 embedding: CLS token and mean of patch tokens, each L2-normalised, concatenated (1536 dims)."""
3 import torch
4 if mode in ("mask", "mask+crop"):
5 img = mask_page(img)
6 if mode == "mask+crop":
7 img = content_crop(img)
8 proc, model = dino()
9 inputs = proc(images=pad_square(img), return_tensors="pt", do_center_crop=False, do_resize=False)
10 with torch.no_grad():
11 out = model(**inputs).last_hidden_state[0]
12 cls, mean = out[0], out[1:].mean(0)
13 v = torch.cat([cls / cls.norm(), mean / mean.norm()]) / np.sqrt(2)
14 return v.numpy().astype(np.float32)

Das Bild wird vorher auf ein weißes Quadrat von 448 Pixeln gelegt. do_center_crop=False ist wichtig: Die Standardeinstellung des DINOv2-Prozessors schneidet die Mitte aus und würde bei einem Querformat die Hälfte der Zeichnung wegwerfen. Auf der CPU dauerte ein Vektor 0,83 Sekunden.

Als Anfragen dienten 30 schräg fotografierte Ausschnitte der Ansichten ohne Schriftfeld, gesucht in allen 134 Seiten:

Anfragebild für die Bildsuche: schräg fotografierter Ausschnitt eines Flanschs ohne Schriftfeld

ArchivvektorGleiches Bauteil auf Platz 1Gleiches Bauteil unter den ersten drei
ganzes Blatt20 %43 %
Rahmen und Schriftfeld übermalt30 %43 %
übermalt und auf die Linien zugeschnitten27 %43 %

Der Grund ist einfach: Fünf Flansche unterscheiden sich nur in Durchmesser, Lochzahl und Dicke, und ein allgemeines Bildmodell sieht in allen fünf "einen Flansch". Die Bauteilart traf es nämlich gut: In 26 von 30 Fällen (87 %) war der erste Treffer die richtige Art. Auf den echten Scans, deren Blätter sich stark unterscheiden, fand die Bildsuche bei 15 gedrehten und unscharfen Ausschnitten von 55 % eines Blatts das Ursprungsblatt zwölfmal auf Platz 1 und 14-mal unter den ersten drei.

Bildähnlichkeit ist also ein guter Einstieg ("zeig mir Lagerbuchsen wie diese") und eine Hilfe gegen Doppelungen, aber kein Ersatz für die Nummer. Das passt zu Kuken, deren Suche laut Pressemitteilung auf Teilenummern aufbaut. Wer Größenvarianten per Bild unterscheiden will, braucht mehr als einen allgemeinen Vektor, etwa ausgelesene Hauptmaße als Filter.

Suchergebnis über die Scans von 1909 mit Markierungen für OCR-Abweichung und teilweise unleserliche Blätter

Die KI-Prüfung als zweite Meinung

Der letzte Baustein entspricht Kukens Leitsatz: Die KI vergleicht, der Mensch entscheidet. Auf Knopfdruck schickt der Prototyp die Anfrage und das Kandidatenblatt an das Modell und bekommt ein festes Ergebnis zurück: same_part mit yes, no oder uncertain, eine Liste konkreter Unterschiede und die Belegstelle auf der Zeichnung. Der Prompt erlaubt "yes" nur, wenn Nummernfamilie und, falls angefragt, Änderungsstand passen und nichts Sichtbares widerspricht.

Wir haben das für 14 Stücklistenzeilen und jeweils die drei besten Kandidaten laufen lassen, also 42 Vergleiche:

Kandidat laut TestdatenModell sagt "yes""uncertain""no"
falsches Bauteil (16)0016
richtiges Bauteil (26)1349

Zu einem falschen Bauteil hat das Modell nie "yes" gesagt. Die "no" bei richtigen Bauteilen hatten meist einen nachvollziehbaren Grund. Dreimal war die Nummer in der Stückliste ein Zahlendreher, und das Modell hat zu Recht widersprochen. Sechsmal hat es echte Widersprüche auf unseren Zeichnungen gefunden: Die Benennung lautet "Haltewinkel 120x120", bemaßt sind aber 120 mal 100 Millimeter, oder die Welle wurde in einer Revision verlängert, ohne dass die Benennung mitgeändert wurde. Das war ein Fehler unseres Generators, aber genau so ein Fehler, wie er auf echten Zeichnungen vorkommt. Die vier "uncertain" betrafen Nummern mit dem Buchstaben O, die das Modell als möglichen Übertragungsfehler meldete.

KI-Prüfung eines Kandidaten: gleiche Nummer, aber die Benennung widerspricht den Maßen

Das Verhalten ähnelt dem, was Kuken für die eigene Zeichnungsprüfung berichtet: Die Hinweise sind meist berechtigt. Ob ein Widerspruch im Einzelfall zählt, kann nur die Fachkraft beurteilen. Deshalb hat jeder Kandidat in der Oberfläche die Knöpfe "Confirm" und "Reject", und die Entscheidungen landen in einer eigenen Tabelle.

Was der Prototyp zeigt und was nicht

Der Prototyp zeigt, dass sich das Prinzip mit Standardbausteinen in rund 600 Zeilen Python nachbauen lässt und dass die Nummernsuche mit sauberer Normalisierung sehr zuverlässig ist. Die gesamten Testläufe kosteten 1,55 US-Dollar für 245 Modellaufrufe, davon 196 zum Auslesen von Seiten.

Er zeigt nicht, wie gut das Verfahren auf Ihrem Archiv funktioniert. 134 Seiten sind wenig, bei 100.000 Seiten gibt es viel mehr ähnliche Nummern, und die Hit@1-Werte dürften sinken. Unsere künstlichen Schriftfelder waren zu sauber, um Modell und OCR wirklich zu fordern. Die echten Scans von 1909 hatten keine Musterlösung; dort haben wir die Abweichungen zwischen Modell und OCR von Hand am Bild geprüft. Handschriftliche Änderungen, gefaltete Blätter, mehrseitige Zeichnungssätze und CAD-Formate haben wir nicht getestet. Kukens Zahlen zur Zeitersparnis lassen sich aus unserem Test weder bestätigen noch widerlegen.

Für ein eigenes Projekt ergibt sich aus den Messungen diese Reihenfolge:

  1. Eine Stichprobe von 200 bis 500 Seiten aus allen Epochen und Schriftfeld-Layouts ziehen und dafür die richtigen Nummern von Hand erfassen. Ohne diese Musterlösung können Sie nichts messen.
  2. Für jedes Layout eine Beschreibung schreiben und das Auslesen an der Stichprobe testen, mit OCR-Gegenprobe und einer Prüfliste für markierte Seiten.
  3. Die Normalisierungsregeln aus den Schreibweisen ableiten, die in Ihren Stücklisten, im ERP und in E-Mails wirklich vorkommen.
  4. Erst dann Bildähnlichkeit und KI-Prüfung ergänzen und die Bestätigungen der Fachleute sammeln, um das Ranking zu verbessern.

Wenn Sie ein technisches Archiv auf diese Weise durchsuchbar machen wollen und dabei Unterstützung beim Zuschnitt oder beim Bau eines Prototyps brauchen, sprechen Sie uns an.

Quellen und Bildnachweis

Sie möchten zuverlässige KI-Agenten-Systeme bauen?

Wir haben auf Basis aktueller Forschung einen ausführlichen Leitfaden (auf Englisch) erstellt, wie Sie robuste KI-Agenten-Systeme bauen, die auch im produktiven Betrieb funktionieren. Der Leitfaden behandelt:

  • die 14 systematischen Fehlerquellen in Multi-Agenten-Systemen
  • belegte Best Practices für das Design von Agenten
  • strukturierte Kommunikationsprotokolle und Prüfmechanismen
Andreas Nigg

Über den Autor

Andreas Nigg

Mitgründer, Pondhouse Data

Andreas entwickelt seit mehr als acht Jahren Datenanwendungen. Bei Pondhouse Data konzipiert und baut er KI-Systeme und integriert sie in komplexe, gewachsene Unternehmensumgebungen.