Prüfverfahren der Ähnlichkeitssuche

Wie ein Embedding gegen das Produktionsmodell geprüft wird, und wann es ersetzen darf

Veröffentlichungsdatum

8. Oktober 2026

TippDas Verfahren in einem Satz

Jedes Embedding bekommt dieselben Anfragen, denselben gemeinsamen Pool beurteilter Treffer und dieselbe Entscheidungsregel; ein Urteil ist erst endgültig, wenn Sie das Gold bestätigt haben, und jeder Lauf endet mit einer Sonde am lebenden Modell und mit dem Nachweis, dass die Produktion unverändert ist.

Ablauf

flowchart TD
    fp1["Fingerabdruck der Produktion nehmen"]
    daten["data: Stichprobe und Vokabular, fester Seed"]
    einbetten["embed: jeder Arm, Dokument- und Anfragerolle"]
    ranglisten["rank: Top 50 je Anfrage und Arm"]
    gold["judge: Treffer pooling, Noten VORSCHLAGEN"]
    tor{"Gold bestätigt?"}
    wertung["score: Metriken je Anfrage"]
    paarweise["paired: gepaarter Bootstrap"]
    regel["verdict: Entscheidungsregel"]
    speicher["store_compare: nur für einen Kandidaten"]
    sonde["verify_live und Fingerabdruck prüfen"]
    fp1 --> daten
    daten --> einbetten
    einbetten --> ranglisten
    ranglisten --> gold
    gold --> tor
    tor -->|"ja"| wertung
    tor -->|"nein, nur mit --provisional"| wertung
    wertung --> paarweise
    paarweise --> regel
    regel --> speicher
    speicher --> sonde

Der Lauf steht in scripts/embedding_pilot/procedure.py. Die Schritte sind die Skripte des Piloten; die prüfbare Logik (Metriken, Goldstatus, Kandidatenfilter, Entscheidungsregel, Fingerabdruck) liegt in famulus_proof.aehnlichkeit und hat Tests.

Die Schritte

Schritt Skript Ergebnis Bedingung
Fingerabdruck procedure.py fingerprint take Hash von status.json und chroma/provenance.json, Zeit und Größe von chroma.sqlite3 Der Lauf liest die Produktion nur
data build_data.py Stichprobe von Sätzen (alle Sätze der Zielwörter und Füllsätze je Sprache) und das Vokabular der Lemmata und Wendungen fester Seed 20261008; die Stanza-Datenbank wird nur gelesen; Satzschlüssel ist das Tripel (doc_id, pidx, sent_idx)
embed embed_arms.py Vektoren je Arm, zwischengespeichert Sätze in der Dokumentrolle, Vokabular in der Anfragerolle; ARMS in common.py nicht ändern, während ein Lauf es liest (im Pilot musste ein Lauf deshalb wiederholt werden)
rank retrieve.py die 50 besten Kandidaten je Anfrage, Arm und gekürzter Dimension der Kandidatenfilter ist famulus_proof.aehnlichkeit.kandidaten, die Anfrage ist nicht ihr eigener Nachbar
judge judge.py gemeinsamer Pool (je Arm die besten 15, gekürzte Arme die besten 10, dazu die Ankersätze) mit Noten 0 bis 2 gemini-3.5-flash-lite; Status vorgeschlagen
Tor Ihr Eingriff gold_overrides.json ohne Überschreibungen läuft der Lauf nur mit --provisional weiter, und jedes Urteil trägt „vorläufig“
score score.py nDCG@10, MRR, P@5, P@10, gepoolter Recall@15, Falschsprachrate, Falsch-Nachbarn bei den nicht gekürzten Armen muss unlabelled<=15 0 sein, sonst fehlen Noten im Pool
paired paired.py Differenz je Anfrage mit 95 %-Intervall, Siege und Niederlagen Bootstrap über die Anfragen, fester Seed
verdict procedure.py verdict verdict.json Entscheidungsregel (unten)
store_compare store_compare.py <arm> --real Latenz und Recall von numpy, Chroma, DuckDB und vss nur für einen Kandidaten; der echte Maßstab arbeitet an einer Kopie des Chroma-Speichers
Sonde verify_live.py je Aufgabe eine Anfrage live gegen jedes Modell der Rückgabecode jeder Sonde wird berichtet; was nicht geprüft ist, heißt „unverified“
Fingerabdruck procedure.py fingerprint check „unverändert“ oder die Liste der geänderten Dateien Rückgabecode 0

Die Aufgaben und ihr Gold

Vier Aufgaben, je mit eigener Kandidatenmenge (scripts/embedding_pilot/queries.py):

Aufgabe Anfrage Kandidaten Anker gegen Verzerrung
Wendung lateinische Wendung (lex naturae) Wendungen, Latein —
Verb Verb (versprechen) Verben derselben Sprache expect: vorgeschlagene Synonyme, damit der Pool nicht nur aus dem besteht, was ein Arm fand
Satz Wendung oder Satz Sätze der Stichprobe anchor: alle Sätze mit den Lemmata der Anfrage werden immer beurteilt
Sprachwechsel Lemma einer Sprache Lemmata der anderen expect: vorgeschlagene Übersetzungen

Die Listen expect und anchor sind selbst vorgeschlagen. Sie zählen als Gold erst nach Ihrer Bestätigung.

Das Gold bestätigen

Der Lauf schreibt review_sheet.md, je Anfrage die beurteilten Kandidaten mit Note. Korrekturen stehen in gold_overrides.json:

{"F2_verb": {"versprechen": {"de:zusagen": 2, "de:vertrauen": 0}}}

Eine Anfrage mit Überschreibungen bekommt den Status bestaetigt/korrigiert, die Notiz im Ergebnis lautet dann owner overrides: <n>. Ihre Korrektur geht der Maschine vor; die Notiz im Bericht nennt stets, worauf die Zahlen beruhen.

Die Entscheidungsregel

Je Aufgabe wird die gepaarte Differenz von nDCG@10 (Kandidat minus Referenz, Referenz ist bge-m3) mit ihrem 95 %-Intervall gebildet:

Intervall Urteil der Aufgabe
ganz über null besser
ganz unter null schlechter
schließt null ein unentschieden
Gesamtlage Gesamturteil
mindestens eine Aufgabe schlechter ersetzt die Referenz nicht
keine schlechter, mindestens eine besser Kandidat für den Wechsel
alle unentschieden kein Grund zum Wechsel

Die Regel ist streng im Sinn der Vorsicht: Ein Verlust in einer Aufgabe sperrt den Wechsel, auch wenn andere Aufgaben gewinnen. Sie kennt die Kosten eines Wechsels nicht (neu einbetten, neuer EXPECTED_EMBEDDING_MODEL); sie sagt nur, ob die Qualität einen Blick darauf lohnt. Solange das Gold nur vorgeschlagen ist, steht „vorläufig“ im Urteil.

Ausführen

Die Skripte brauchen numpy, duckdb und requests und Ollama; sie laufen in der Umgebung von ai-lemma-index:

PY=~/ai-mac-studio/pdai/index/ai-lemma-index/.venv/bin/python
cd ~/ai-mac-studio/development/famulus-proof/scripts/embedding_pilot
$PY procedure.py fingerprint take
$PY procedure.py run --arms bge-m3 egemma2-bf16-pfx --stop-after judge
# review_sheet.md prüfen, gold_overrides.json schreiben
$PY procedure.py run --arms bge-m3 egemma2-bf16-pfx
$PY procedure.py verdict --candidate egemma2-bf16-pfx --reference bge-m3
$PY procedure.py fingerprint check
$PY procedure.py status

Der Einbettungsschritt dauert gemessen etwa acht bis zehn Minuten je Arm (13.626 Sätze mit rund 50 Sätzen je Sekunde, dazu 19.181 Vokabulareinträge). Ein Arm, der schon eingebettet ist, wird aus dem Zwischenspeicher gelesen.

Einen neuen Kandidaten aufnehmen

  1. In common.py einen Eintrag in ARMS anlegen: Ollama-Modell, Anfragepräfix, Dokumentpräfix.
  2. procedure.py run --arms bge-m3 <neuer-arm> --stop-after judge laufen lassen. Bereits beurteilte Treffer kommen aus judge_cache.json; nur neue Treffer gehen an das Modell.
  3. Gold bestätigen, dann den Lauf ohne --stop-after wiederholen.
  4. Das Paar (<neuer-arm>, bge-m3) in paired.py eintragen und verdict aufrufen.
  5. scripts/docs_aehnlichkeit.py ausführen, damit die Tabellen dieser Dokumentation das Ergebnis zeigen.

Die Tests

tests/test_aehnlichkeit.py prüft ohne Modell, ohne Ollama und ohne eingehängte Collection:

  • die Metriken an einer Rangliste, die von Hand ausgerechnet ist (nDCG, MRR, P@k, Recall, Falschsprachrate),
  • dass ein unbeurteilter Kandidat als Note 0 zählt und gemeldet wird,
  • den Goldstatus: ohne Überschreibung bleibt alles vorgeschlagen, mit Überschreibung wird nur die betroffene Anfrage bestaetigt/korrigiert,
  • den Kandidatenfilter nach Sprache, Art, Wortart, Mindestzahl und Selbstausschluss,
  • den Bootstrap: derselbe Seed gibt dasselbe Intervall, ein konstanter Unterschied gibt ein Intervall der Breite null,
  • die Entscheidungsregel in allen drei Ausgängen und das „vorläufig“ bei vorgeschlagenem Gold,
  • den Fingerabdruck der Produktion bei einer Änderung, einer neuen und einer verschwundenen Datei,
  • dass jede Liste erwarteter Synonyme zu einer Anfrage gehört und jede Sprachwechselanfrage Übersetzungen nennt,
  • dass die erzeugten Tabellen dieser Dokumentation die Darstellung ihrer Momentaufnahme sind.
cd ~/ai-mac-studio/development/famulus-proof
uv run --extra dev python -m pytest tests/test_aehnlichkeit.py

Die Tests prüfen die Rechnung des Verfahrens, nicht die Güte eines Embeddings. Ob ein Embedding taugt, sagt nur ein Lauf mit bestätigtem Gold.

Was das Verfahren vermeiden soll

Jede dieser Fallen ist im Pilot aufgetreten.

  • Satzschlüssel ohne Seite. sent_idx beginnt auf jeder Seite neu; ohne pidx zählt man zu wenig Sätze und erfindet Treffer über Seiten hinweg.
  • Drei Tags, ein Modell. 270m, 440m und 740m von EmbeddingGemma 2 liefern denselben Textvektor. Sie als drei Arme zu zählen täuscht drei unabhängige Ergebnisse vor.
  • Das Standard-Tag. Es ist quantisiert und schlechter als 270m-bf16-text.
  • Ohne Präfix gemessen. EmbeddingGemma 2 ohne seine Suchpräfixe liegt deutlich unter bge-m3. Ein Vergleich ohne Präfixe misst das Fehlen des Präfixes.
  • Verzerrtes Gold. Wenn nur beurteilt wird, was ein Arm fand, belohnt das Gold diesen Arm. Deshalb expect und anchor.
  • Gekürzte Vektoren im kleinen Pool. Kürzungen werden mit weniger Treffern gepoolt; ihre Werte sind Untergrenzen und kein fairer Vergleich.
  • Zahlen aus dem Vorlauf. Das Gold-Modell urteilt zwischen zwei Läufen nicht gleich. Berichtet werden nur Zahlen, die aus dem letzten Lauf stammen, und die Dokumentation wird aus dessen Momentaufnahme erzeugt.
  • Ein Schritt meldet „angewendet“. Das ist kein Beleg. Deshalb die Sonde am lebenden Modell und der Fingerabdruck, nicht der Satz „Lauf beendet“.
  • Mittelwert ohne Streuung. Ein Abstand von 0,01 bei 15 Anfragen ist kein Unterschied. Es zählt das Intervall.