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
Prüfverfahren der Ähnlichkeitssuche
Wie ein Embedding gegen das Produktionsmodell geprüft wird, und wann es ersetzen darf
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
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 statusDer 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
- In
common.pyeinen Eintrag inARMSanlegen: Ollama-Modell, Anfragepräfix, Dokumentpräfix. procedure.py run --arms bge-m3 <neuer-arm> --stop-after judgelaufen lassen. Bereits beurteilte Treffer kommen ausjudge_cache.json; nur neue Treffer gehen an das Modell.- Gold bestätigen, dann den Lauf ohne
--stop-afterwiederholen. - Das Paar
(<neuer-arm>, bge-m3)inpaired.pyeintragen undverdictaufrufen. scripts/docs_aehnlichkeit.pyausfü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 Anfragebestaetigt/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.pyDie 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_idxbeginnt auf jeder Seite neu; ohnepidxzählt man zu wenig Sätze und erfindet Treffer über Seiten hinweg. - Drei Tags, ein Modell.
270m,440mund740mvon 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
expectundanchor. - 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.