Regelkatalog für Personenreconciliation
Ziel: für eine Kandidatenperson (aus einer externen Quelle, z. B. dem Arbeitsblatt in test.docx) anhand aller vorhandenen Informationen entscheiden, ob sie im FactGrid-Auszug bereits vorhanden ist — und wenn ja, als welches Item.
Dieser Katalog ist die fachliche Spezifikation. Die ausführbare Umsetzung steht in app/server.py (reconcile(), Endpoint POST /api/reconcile); die Tabellenfelder liefert build/enrich_index.py.
---
1. Eingabemodell
Eine Kandidatenperson wird über die Spalten des Arbeitsblatts beschrieben. Jede Spalte mappt auf ein oder mehrere FactGrid-Merkmale:
| Arbeitsblatt-Spalte | FactGrid-Merkmal | Feld im Index |
|---|---|---|
| Name | Label / Vor- + Nachname | persons.label_de, family_q, given_q |
| Geburtsdatum und -ort | P77 / P82 | birth_year/birth_date, qref birthplace |
| (Sterbedatum) | P38 | death_year/death_date |
| Beruf | P165 | qref profession (+ P3-Expansion) |
| wohnhaft | P83 / P208 | qref residence |
| Datum der Erwähnung | — | Plausibilitätsfenster gegen Lebenszeit |
| Religion | P172 | qref religion |
| Geschlecht (falls bekannt) | P154 | persons.sex_q |
| Eltern (falls bekannt) | P141 / P142 | qref father / mother |
| Ehepartner (falls bekannt) | P84 | qref spouse |
| Angehörigen-Namen (Vater/Mutter/Ehepartner, Freitext) | — | Token-Vergleich gegen das Label des Angehörigen-Items |
| externe ID (GND/VIAF…) | external-id | person_extid |
| Bemerkung | — (frei) | — |
---
2. Warum ein gestuftes Verfahren? (Datenlage)
Gemessen am Bestand (666.301 Personen, stark mittelalterlich- portugiesisch geprägt). „Dup-Quote" = Anteil der Personen, die sich einen Schlüssel mit mindestens einer anderen Person teilen:
| Schlüssel | Dup-Quote | Folgerung |
|---|---|---|
| Name allein | 16,5 % | nie hinreichend (z. B. „João Afonso" ×290) |
| Name + Geburtsjahr | 0,8 % | starke Identität |
| Name + Geburts- + Sterbejahr | 0,4 % | praktisch eindeutig |
| Familienname-Q + Vornamen-Q | 12,5 % | allein zu schwach |
| Familie + Vornamen + Geburtsjahr | 0,9 % | stark |
Zusätzlich ist die Feldabdeckung ungleich: Geburtsjahr 55 %, Sterbejahr 21 %, Geschlecht ~100 %, ≥1 externe ID ~30 %. Konsequenz: Es gibt selten ein Merkmal, das immer entscheidet. Daher wird in Stufen gearbeitet — erst deterministisch, dann harte Ausschlüsse, dann starke Treffer, zuletzt gewichtetes Scoring. Die erste zutreffende Stufe entscheidet.
---
3. Namensnormalisierung
Vor jedem Namensvergleich:
- Kleinschreibung; Diakritika entfernen (NFKD, wie FTS
remove_diacritics 2— „Gonçalo" → „goncalo"). - In Token-Menge zerlegen (reihenfolgeunabhängig: „Herbart, Wilhelm" == „Wilhelm Herbart").
- Satzzeichen strippen; Initialen (
W.) als Präfix-Match auf volle Token behandeln.
Vergleichsarten: exakt (Token-Mengen gleich) · Teilmenge (alle Kandidaten-Token im Item enthalten oder umgekehrt) · fuzzy (Überlappungs- anteil über Schwelle).
---
4. Der Regelkatalog
Tier 0.0 — Kurator-Veto ⇒ nicht identisch
- R0.0 Ist eines der beiden Items über P514 „Nicht identisch mit" auf das andere verwiesen, sind sie verschieden — ohne weitere Prüfung. Diese Stufe steht vor Tier 0, und nur diese eine Regel tut das. Der Grund: P514 wird von FactGrid-Kuratoren von Hand gesetzt, um zwei leicht verwechselbare Items ausdrücklich auseinanderzuhalten. Das ist eine menschliche Entscheidung über genau die Frage, die die Engine zu schätzen versucht — sie schlägt deshalb auch eine gemeinsame externe ID (R0.2). Genau dort sitzt der Nutzen: In der Stichproben-Verifikation waren die
extid-Fehltreffer durchweg Fälle, in denen die geteilte ID einer Institution, Familie oder einem Katalog gehörte, nicht der Person. Ein P514-Veto geht nicht in die manuelle Prüfung (mehrdeutig), sondern fällt ganz heraus: Ein Mensch hat den Fall bereits entschieden, ihn erneut vorzulegen wäre doppelte Arbeit.
Tier 0 — Deterministische Identität ⇒ identisch
- R0.1 Gleiche FactGrid-Q-ID → dieselbe Person (trivial, aber prüfen, wenn die Quelle bereits eine Q-ID nennt).
- R0.2 Gemeinsame externe ID (GND P76, VIAF u. a. —
person_extid): identisch. Externe IDs sind global eindeutig; ein Treffer sticht jeden Namens-/Datumszweifel. ~30 % der Personen haben ≥1 solche ID.
Tier 1 — Harte Ausschlüsse ⇒ nicht identisch
Greifen trotz Namensgleichheit. Sie verhindern die häufigste Fehlerquelle (gleicher Name, verschiedene Person).
- R1.1 Geschlecht (P154): verschiedenes Geschlecht ⇒ nicht identisch. Billig und nahezu flächendeckend vorhanden.
- R1.2 Eltern-Relation (P141/P142): ist die eine Person als Vater oder Mutter der anderen verzeichnet, sind sie verschieden. (Erste Regel aus
regel.txt: „Vater und Sohn sind nicht identisch.") - R1.3 Generationsabstand: Geburtsjahr-Differenz > 30 Jahre ⇒ nicht identisch; ebenso unvereinbare Lebenszeitfenster. (Zweite Hälfte der Regel aus
regel.txt.) - R1.4 Biologische Plausibilität: Geburt nach Tod; Lebensspanne > 110 Jahre; „Datum der Erwähnung" außerhalb \[Geburt − 0, Tod + Puffer] ⇒ Widerspruch, nicht identisch. Bei unbekanntem Sterbejahr gilt zusätzlich ein Alters-Cap: Erwähnung mehr als
REC_MAX_MENTION_AGEJahre (Default 110) nach der Geburt ⇒ nicht identisch. - R1.5 Widersprüchliche tagesgenaue Geburtsdaten: liegen auf beiden Seiten tagesgenaue Geburtsdaten vor und deren Jahre > 2 Jahre auseinander ⇒ nicht identisch. (Nur grobe Jahresangaben werden toleranter behandelt; abweichende Geburtsorte sind kein harter Ausschluss, sondern ein starkes Gegenmerkmal im Scoring, siehe Tier 3.)
- R1.6 Ehepartner: ist die per Q-ID angegebene Ehepartner-Person selbst der Kandidat ⇒ nicht identisch (analog R1.2).
Tier 2 — Starke positive Übereinstimmung ⇒ identisch
- R2.1 Normalisierter Name gleich und Geburtsjahr gleich (zusätzlich Sterbejahr gleich, falls beidseitig vorhanden). Belegt durch die 0,8 %-/0,4 %-Dup-Quoten oben.
- R2.2 Familienname-Q gleich und Vornamen-Q-Mengen überlappen und Geburtsjahr gleich. Greift auch ohne identische Label-Schreibweise.
Tier 3 — Gewichtetes Scoring (alle übrigen Fälle)
Summe der zutreffenden Gewichte; entscheidet zwischen identisch / mehrdeutig / verschieden. Gewichte und Schwellen sind in app/server.py oben einstellbar (Stil wie BERUF_P3_UP) — und zur Laufzeit durch eigene Regeln überschreibbar: Auf /regeln lassen sich natürlichsprachlich formulierte Regeln (z. B. „Personen werden nicht älter als 90 Jahre") LLM-gestützt in Parameteränderungen übersetzen und aktivieren (POST /api/rules/translate, POST /api/rules; Katalog REC_PARAMS). Das betrifft neben den Gewichten und Schwellen dieses Tiers auch die harten Grenzen aus Tier 1 (REC_MAX_GEN_GAP, REC_MAX_LIFESPAN).
| Merkmal | Bedingung | Gewicht |
|---|---|---|
| Name | exakte Token-Menge | +5 |
| Name | Teilmenge | +3 |
| Name | fuzzy-Überlappung | +2 |
| Geburtsjahr | identisch | +4 |
| Geburtsjahr | ±2 Jahre | +2 |
| Geburtsort | gleich (P82) | +3 |
| Geburtsort | beidseitig bekannt, aber verschieden | −3 |
| Wohnort | gleich (P83/P208) | +2 |
| Beruf | gleich (inkl. P3-Expansion ±2) | +2 |
| Eltern | gleicher Vater/Mutter | +4 |
| Ehepartner | gleiche Q-ID (P84) | +4 |
| Angehörigen-Name | Vater-/Mutter-/Ehepartner-Name der Eingabe passt (exakt/Teilmenge/fuzzy) zum Label des entsprechenden Angehörigen des Items | +3 je Kind |
| Organisation | gleich (P91) | +2 |
| Religion | gleich (P172) | +1 |
| Erwähnung | liegt in Lebenszeit | +1 |
Schwellen (Vorschlag): Score ≥ 8 ⇒ identisch · 4–7 ⇒ mehrdeutig (manuelle Prüfung) · < 4 ⇒ verschieden.
---
5. Ergebnisklassen (Report-Sicht, vgl. build/bericht.html §6)
- eindeutig — genau ein Kandidat mit Verdikt „identisch".
- mehrdeutig — 2–5 Kandidaten im Score-Korridor; Vorlage zur manuellen Entscheidung.
- zu unspezifisch — > 5 schwache Kandidaten (typisch bei Allerweltsnamen ohne Datum); Eingabe muss präzisiert werden.
- kein Treffer — keine Kandidaten ⇒ Person ist (vermutlich) neu.
---
6. Bekannte Grenzen
- Patronyme („João Afonso") ohne Datum sind strukturell nicht auflösbar → landen korrekt in „zu unspezifisch".
- Ausschlüsse sind nur so gut wie die Datenabdeckung: fehlt das Geschlecht oder Geburtsjahr beim Item, kann der jeweilige Ausschluss nicht greifen.
- Externe IDs sind das stärkste Signal, aber nur bei ~30 % vorhanden.
- Freitext-Eingaben (Beruf/Ort/Religion) werden gegen FactGrid zu Q-IDs aufgelöst (gecacht); ist die Auflösung falsch, wirkt das Merkmal nicht.
---
7. All-Pairs-Lauf über den gesamten Bestand (build/find_duplicates.py)
Der Katalog oben prüft eine externe Kandidatenperson. Für die Dubletten-Suche innerhalb des Bestands läuft derselbe Regelkatalog item-gegen-item über alle per Blocking gebildeten Paare (geteilte externe ID · Name+Geburtsjahr · Familienname+Vorname+Jahr · gleicher Name ≤ 12 Mitglieder). „identisch"-Kanten werden per Union-Find zu Clustern verschmolzen. Ausgabe: dup_pairs, dup_clusters, dup_cluster_members, name_collisions, dup_meta.
Acht Verschärfungen gegenüber der Einzelfall-Reconciliation — alle nötig, weil im Bestand selbst (anders als beim sauber gepflegten externen Kandidaten) gleichnamige Verwandte, Namensumkehrungen und konfligierende Normdaten massenhaft vorkommen:
- Namens-Reihenfolge. „exakt" (R2.1, +5) ist reihenfolge-sensitiv (nach einer Nachname-Komma-Drehung „Herbart, Wilhelm" = „Wilhelm Herbart"). Sonst kollabiert die Token-Menge „A son of B" auf „B son of A" — verschiedene (oft Vater/Sohn-)Personen in osmanisch-griechischen Patronymregistern.
- Anker-Pflicht. Ein score-„identisch" (Tier 3) verlangt einen diskriminierenden Anker (Geburtsjahr oder Geburtsort — siehe Punkt 8 zu Eltern). Name + Wohnort + Religion allein sind in einem Dorfregister nahezu konstant → Deckelung auf „mehrdeutig".
- Geburtsjahr-Konflikt (−3): beide bekannt und > 2 J. auseinander (≤ 30, sonst harter Ausschluss R1.3) — symmetrisch zum Geburtsort-Konflikt.
- Sterbejahr-Konflikt (−3): beide bekannt und ≥ 2 J. auseinander.
- R1.4 erweitert — geboren nach Tod: ist eine Person nach dem Tod der anderen geboren, sind sie verschieden. Fängt Necronyme (ein Kind wird nach einem verstorbenen Geschwister benannt; gleiche Eltern + Name, aber disjunkte Lebenszeit).
- Weiches Veto → „mehrdeutig" (kein harter Ausschluss, sondern Review):
- Tagesgenaues Geburtsdatum abweichend (gleiches/nahes Jahr, anderer Tag).
- Getrennte Normdatensätze: beide tragen unter derselben externe-ID-Eigen schaft verschiedene Werte (eigene GND/VIAF/Arolsen-Häftlingsnummern) → getrennt katalogisiert, also verschieden. Beides verhinderte die häufigste Fehlmerge (gleicher Name + Geburtsjahr, z. B. Arolsen-Opfer).
- Externe-ID-Konflikt statt blindem Tier 0. Eine geteilte externe ID gilt nur als „identisch", wenn kein harter Ausschluss und kein weiches Veto widerspricht; sonst
extid_konflikt(Review). Der Dump enthält Haushalts-/ Werk-IDs, die Ehepaare oder Generationen verbinden, und konflierte Normdaten (eine GND auf zwei reale Personen). - Geteilte Eltern sind kein Identitäts-Anker. Gleiche Eltern (+4) zählen zwar zum Score, ankern aber kein score-„identisch" für sich allein — Geschwister und Necronyme teilen dieselben Eltern. Nur mit zusätzlicher Datums-/Geburtsort-Übereinstimmung wird daraus „identisch"; sonst „mehrdeutig". (Per Verifikation der häufigste verbliebene Fehlmerge im score-Tier.)
Cluster-Kohärenz. Union-Find über „identisch"-Kanten kann zwei paarweise unvereinbare Records transitiv verschmelzen (A~B~C, aber A/C widersprüchlich). Solche Cluster werden als incoherent markiert (Mitglied nach Tod eines anderen geboren, oder Geburtsjahr-Spanne > 2 J.) und sind im Report filterbar.
Kurator-Veto (R0.0) im All-Pairs-Lauf. Die P514-Regel greift hier genauso und läuft vor allen anderen Stufen; die Zahl der so verworfenen Paare steht als p514_veto_pairs in dup_meta. Vor Einführung führte die Engine 35 Paare als „identisch", die in FactGrid ausdrücklich als nicht identisch markiert waren (6× extid, 15× name_byear, 14× score).
Gemessene Präzision (adversariale Stichproben-Verifikation gegen Live-FactGrid, build/verify_duplicates.py → build/verification.json). Stand 05.08.2026 nach R0.0, 240 Paare: extid 0,91 und name_byear 0,85 oben, fam_given_byear 0,63, reiner score 0,53 bleibt Prüf-Kandidat. Review-Routen sauber kalibriert (extid_konflikt 0,96, score-„mehrdeutig" 0,83). „identisch" gesamt 0,72. Der Report führt die Tiers nach Vertrauensstufe (hoch/mittel/niedrig) getrennt.
Die Stichprobe misst nur Präzision, keinen Recall (nur Kandidatenpaare enthalten). Und sie braucht Größe: ein Vorlauf mit 80 Paaren maß name_byear auf 0,94 — bei n=18 Rauschen, mit n=54 sind es 0,75. Unter ~30 Paaren je Tier sind die Zahlen nicht belastbar.