Digital Humanities · TEI · NER · Normdaten

TEI-NER Pipeline

Automatisches Entity-Linking für historische TEI-Editionen

Eine KI-gestützte Pipeline zur automatischen Verknüpfung von Personen, Orten und Organisationen in TEI-XML-Registern mit Normdaten aus der Gemeinsamen Normdatei (GND) und Wikidata. Entwickelt für die Anforderungen frühneuzeitlicher und antiker Editionsprojekte.

Mehr erfahren
Das Projekt

Warum diese Pipeline?

Historische Editionen enthalten hunderte bis tausende Personen, Orte und Organisationen in ihren Registern. Die manuelle Zuordnung von Normdaten-Identifikatoren (GND, Wikidata) ist zeitaufwändig, fehleranfällig und erfordert Expertise in Normdaten-Recherche.

Die TEI-NER Pipeline automatisiert diesen Prozess: Sie durchsucht Wikidata und die GND nach passenden Kandidaten, bewertet sie anhand von Register-Metadaten (Notizen, Lebensdaten, Alternativnamen) und verifiziert die Zuordnungen mittels eines Large Language Models. Das Ergebnis: Normierte, maschinenlesbare TEI-Register mit vollständiger Provenienz-Dokumentation.

12.000+
Entities verarbeitet
98%
Verlinkungsrate (Places)
3
Register-Typen
$0
Kosten (lokal)
Kernfunktionen

Was die Pipeline kann

🔍

Multi-Source-Suche

Parallele Suche in Wikidata und GND/LOBID. Sprach-Kaskade (Latein immer aktiv), SPARQL-Fallback. Externe Datenbanken: 6,5M Personen (persons.db) + 524K Orte (Places.db).

Preload-Cache

SPARQL-Batch-Preloading lädt Normdaten vor dem Pipeline-Lauf in lokalen SQLite-Cache. Phase 1 von ~40 Minuten auf ~30 Sekunden. Offline-Betrieb nach Preload möglich.

🧠

KI-Verifikation

Single-Entity LLM-Verifikation mit ID-basierter Antwort. Qwen3.6-35B-A3B via vLLM/Swift (lokal, empfohlen), Gemma 4, Gemini 2.5 Flash (API) oder Mistral Small 3.2. Goldstandard-validiert: 100% Entity-Korrektheit (quellen-tolerant) über 4 Modelle.

🌐

Multilingual

Latein, Altgriechisch, Frühneuhochdeutsch, Französisch. Historische Schreibweisen erkennen (Phase 1.7 + Phase 2.7). Alt-Namen in Originalsprache durchsuchen.

👥

Personengruppen

Völker, Stämme, Dynastien im Personenregister erkennen. Drucker-Erben und Offizinen als Körperschaften behandeln.

👑

Herrscher-Erkennung

"Karl der Große", "Friedrich der Weise", "Karl V. (Kaiser)" — Beinamen und Epitheta korrekt in GND-Namenskonvention übersetzen.

🛡

Intelligentes Scoring

Multi-Signal-Scoring: Note-Keywords, Domänen-Boost, Geo-Kontext aus Corpus-Profil, Typ-Mismatch-Erkennung. SPARQL-Fallback für Gottheiten. Anachronismus-Erkennung im Code.

📈

HTML-Report

Interaktiver Report mit Suchfeld, Filtern, Confidence-Badges. GND/Wikidata-Links. Konflikte hervorgehoben. Export als CSV.

🔧

Provenienz

Jedes Pipeline-Element mit @resp und @cert. Editoriale IDs werden nie überschrieben. --crossref-editorial ermöglicht Kreuzreferenz auch für editorische IDs.

🎓

Trainings-Export

Ergebnisse als Fine-Tuning-Dataset exportieren. Kandidaten-Splitting, Stratifizierung. Gezieltes Training für Schwachstellen (Alt-Namen, Regionen, Kollektive). ChatML, Alpaca, Conversation.

💻

Lokal-First

Standard: Qwen3.6-35B-A3B via vLLM/Swift oder LM Studio-kompatibles Backend. Backend-Benchmarks zeigen 3,4s p50 im Single-Szenario und 75,9 Output-Token/s im Batch.

📊

Phase 2.7: Spelling-Recovery

VERIFIED_NONE-Entities mit Kandidaten werden erneut per Schreibweisen-Analyse geprüft. Rettet Einträge, bei denen alle Kandidaten abgelehnt wurden.

Architektur

Systemaufbau

Die Pipeline läuft als Docker-Container und kommuniziert mit externen APIs (Wikidata, GND/LOBID), externen Datenbanken (persons.db, Places.db) und einem LLM-Backend (lokal via LM Studio oder Cloud).

Input
TEI-XML Register
Pipeline
tei-ner
Normdaten
Wikidata + GND
Verifikation
LLM
Output
Enriched XML
Python 3.12 + lxml
Docker + Gunicorn
Caddy (Reverse Proxy)
vLLM/Swift / LM Studio / Claude / Gemini
SQLite (Cache + ext. DBs)
n8n (Orchestrierung)
Benchmarks · Mai 2026

Aktueller Messstand

Die letzten lokalen Messungen trennen drei Fragen: Welches LLM-Backend liefert genügend Durchsatz für Verifikation, wie schlagen sich klassische NER-Modelle auf Neuss-Goldstandards, und ob hmBERT für Standoff-NER gegen ein lokales Qwen3.6-LLM bestehen kann.

LLM-Backend

vLLM/Swift ersetzt dflash

6,7×

Mehr Batch-Durchsatz im gemessenen Qwen3.6-35B-A3B-Szenario: 75,9 statt 11,4 Output-Token/s; p50-Latenz sinkt von 108,3s auf 22,9s.

NER-Goldstandard

hmBERT vor spaCy

74,1%

Bestes Neuss-Ergebnis auf 121 Gold-Spans: hmBERT HIPE2020 erreicht 36,7% exact typed F1 und 74,1% relaxed overlap F1; spaCy liegt bei 21,1% / 59,3%.

Band 39d

Encoder statt lokalem LLM

75×

hmBERT verarbeitet die 40-Gold-Span-Probe in 12,8s und erreicht 52,3% relaxed F1; Qwen3.6 lokal braucht 958,8s und erreicht 34,7%.

Messung System Requests / Gold Fehler p50 / Sekunden Output TPS F1
Backend Single vLLM/Swift Qwen3.6 8 Requests 0 3,41s 63,3
Backend Batch vLLM/Swift Qwen3.6 24 Requests 0 22,94s 75,9
Backend Batch dflash Qwen3.6 24 Requests 0 108,28s 11,4
Neuss NER hmBERT HIPE2020 121 Gold 4,86s 36,7% exact · 74,1% relaxed
Neuss NER spaCy de_core_news_lg 121 Gold 3,22s 21,1% exact · 59,3% relaxed
Band 39d hmBERT Standoff-NER 40 Gold 12,82s 25,2% exact · 52,3% relaxed
Band 39d Qwen3.6 Local LLM 40 Gold 958,76s 21,3% exact · 34,7% relaxed
Quellen: benchmarks/results/20260427_*, shared-data/output/*_20260504*/summary.csv. Exact = typed exact span F1; relaxed = overlap F1.
Entwicklung

Evolution der Pipeline

Von regelbasierter NER über Cloud-LLM-Verifikation zum lokalen Agentic Entity Linking.

v8: Hybrid Agentic Entity Linking (aktuell)

97%
ID-Abdeckung (Places)
$0
Kosten (lokal)
~11 Min
pro 100 Entities (Qwen3)

Dreistufige Architektur

Stufe 1: Router
Confidence-Scoring entscheidet autonom. Score ≥0.85 mit klarem Abstand → Auto-Link ohne LLM.
<1ms · ~50% der Entities
Stufe 2: Agent (Whitelisting)
LLM bekommt Kandidaten als Vorschläge und sucht aktiv in der DB. Wählt den besten Match statt Kandidaten abzulehnen.
~3–5s · ~45% der Entities
Stufe 3: NONE
Weder Scoring noch Agent finden einen Match. Korrekte Ablehnung — Entity hat keine Normdaten.
~5% der Entities

Kernkonzept: Whitelisting statt Blacklisting

✗ Blacklisting (alt)
„Hier sind Kandidaten. Lehne die falschen ab.“
→ LLM wird skeptisch, lehnt im Zweifel alles ab.
Ergebnis: 8% MATCH bei Gemma 4
✓ Whitelisting (neu)
„Finde die richtige ID. Hier sind Vorschläge.“
→ LLM sucht aktiv, bestätigt konstruktiv.
Ergebnis: 92–98% MATCH (Goldstandard-validiert)

Technische Pipeline

Phase 0  Corpus-Profiler ($0, lokal)
    36 Sprachen, 26 Epochen, Regionen, Korrespondenz-Netzwerk
Phase 1  Normdaten-Suche ($0)
    persons.db (11,8M) + Places.db (524K) → 95% API-Calls gespart
    NOCASE-Index: 5ms statt 2.000ms pro Lookup
Phase 1.5 Kontext-Harvesting ($0)
Phase 1.7 Historische Schreibweisen (LLM)
Phase 2  Confidence-Router + Agentic Whitelisting
    Router: Score ≥0.85 → Auto-Link (50%)
    Agent: LLM + DB-Tool → SEARCH → RESULT (45%)
    Minimaler System-Prompt (~50 Tokens)
Phase 2.7 Spelling-Recovery
Phase 4  XML + Kreuzreferenz + HTML-Report

Schlüsselinnovationen

DB-First: 11,8M Personen + 524K Orte lokal — kein API-Rate-Limiting
KV-Cache-Patch: mlx_vlm PromptCacheState + TurboQuant-Fix
Minimaler Prompt: 50 Tokens statt 5.000 — Gemma 4 Qualität 12x besser
Entity-Search-Tool: LLM sucht selbst in SQLite mit SEARCH-Befehl
Gazetteer-Kaskade: Exakt → Kölner Phonetik → Trigram (opt-in)
Völker-DB: 42+ Ethnien mit Singular/Plural/Latein manuell ergänzt

Goldstandard-Vergleich (100 Einträge, April 2026)

Modell Architektur Strikt Tolerant Fehler Laufzeit
Qwen3-30B-A3B ★ MoE 30B/3B 93% 100% 0 ~11 min
Gemma 4 26B-A4B MoE 26B/4B 94% 99% 1 ~18 min
Gemini 2.5 Flash Cloud API 98% 100% 0 ~8 min
Mistral Small 3.2 Dense 24B 93% 98% 2 ~25 min
hmBERT v9 ⚡ Encoder 110M 91% 94% 6 ~6 min
Tolerant = GOV/GND/Wikidata als gleiche Entität gewertet. Fehler = semantisch falsche Entity. hmBERT = domänenspezifischer Bi-/Cross-Encoder (900 MB, kein LLM-Server nötig).

hmBERT Entity-Linker (Experimentell)

Bi-Encoder + Cross-Encoder
Domänenspezifisch trainiert auf historischen TEI-Korpora (Neuss + Rhodomanologia). Basis: dbmdz/bert-base-historic-multilingual-cased. ~900 MB statt 15–18 GB.
Kein GPU/LLM-Server · Deterministisch · ~6 min / 100 Entities
Zwei Betriebsmodi
--bert-linker: BERT ersetzt LLM komplett (91% strikt, 94% tolerant)
--bert-prefilter: BERT reduziert Kandidaten, LLM verifiziert (Hybrid)
94% der Fälle brauchen kein LLM · 6% erfordern Reasoning
Dokumentation

Unterseiten

Aktuell · v8

Hybrid Agentic Architecture

Aktuelle Pipeline-Architektur: Confidence-Router, Agentic Whitelisting, Entity-Search-Tool. Whitelisting vs. Blacklisting, Ergebnisse, technische Details.

Architektur öffnen
Referenz · v6

Pipeline-Ablaufplan (v6)

Programmablaufplan der v6-Architektur: Corpus-Profiler, Normdaten-Suche, Filter & Scoring, LLM-Verifikation (Blacklisting), Kreuzreferenz.

Ablaufplan öffnen
Referenz · v6

Workflow-Übersicht (v6)

Visuelle Darstellung des v6-Workflows. Datenflüsse, Mechanismen, Live-Beispiel (Echo).

Workflow öffnen
Quickstart

Loslegen

# Lokales LLM (empfohlen: Qwen3-30B-A3B)
./run_pipeline.sh --llm-model mlx-community/Qwen3-30B-A3B-Instruct-2507-4bit

# Lokales LLM (Alternative: Gemma 4)
./run_pipeline.sh
# oder manuell:
docker compose exec tei-ner python3 -m src.enrich_register_verified \
  --register /data/shared/register/output-persons.xml \
  --texts /data/shared/input/meine-texte/ \
  --output /data/shared/output/enriched-persons/ \
  --llm-model gemma-4 \
  --llm-base-url http://host.docker.internal:1234/v1 \
  --no-batch --force-all --verbose

# Gemini 2.5 Flash (API — günstig + schnell)
docker compose exec tei-ner python3 -m src.enrich_register_verified \
  --register /data/shared/register/output-persons.xml \
  --texts /data/shared/input/meine-texte/ \
  --output /data/shared/output/enriched-persons/ \
  --llm-model gemini-2.5-flash --force-all --no-batch --verbose

# Cloud (Claude Sonnet, Sync-Modus)
docker compose exec tei-ner python3 -m src.enrich_register_verified \
  --register /data/shared/register/output-persons.xml \
  --texts /data/shared/input/meine-texte/ \
  --output /data/shared/output/enriched-persons/ \
  --llm-model claude-sonnet-4-6 --force-all --no-batch --verbose

# hmBERT Entity-Linker (kein LLM-Server nötig, 900 MB)
docker compose exec tei-ner python3 -m src.enrich_register_verified \
  --register /data/shared/register/places.xml \
  --texts /data/shared/input/meine-texte/ \
  --output /data/shared/output/enriched/ \
  --bert-linker \
  --bert-biencoder /data/shared/models/nel/biencoder.pt \
  --bert-crossencoder /data/shared/models/nel/crossencoder.pt \
  --no-batch --force-all --verbose

# CSV-Export
python3 -m src.csv_handler export --input output/enriched-persons/ --output output/csv/

# Training-Export (ChatML/Alpaca/Conversation)
python3 -m src.training_exporter --input output/ --split-candidates --stratify