Zurück zum Blog

Welches Embedding-Modell? Messen statt raten beim Blog-Chatbot

Welches Embedding-Modell?

Messen statt raten: wie der Blog-Chatbot ein neues Suchmodell bekam

Datum 24. Oktober 2026
Kategorie Lokale KI
Lesezeit 15 Minuten
Die Kurzfassung:
  • Der Chatbot auf dieser Seite fand zu einer einfachen Frage den passenden Beitrag erst auf Platz 10. Die naheliegende Reparatur, die Aufgabenpräfixe aus der Modellkarte, hat gemessen nicht geholfen.
  • Statt weiter an einzelnen Fragen zu probieren, habe ich einen Testsatz mit 44 Fragen und ein kleines Messwerkzeug gebaut, das die Suche des Chatbots exakt nachbildet, ohne die Produktion anzufassen.
  • Vier Modelle traten an. Gewonnen hat nomic-embed-text-v2-moe: alle 38 Inhaltsfragen in den Top 5 (vorher 33), Platz 1 bei 32 statt 21 Fragen, und es ist das schnellste der Kandidaten.
  • Die eigentliche Falle beim Wechsel war die Score-Skala. Die alte Schwelle hat nie etwas gefiltert, mit dem neuen Modell hätte sie die Hälfte der Treffer weggeworfen.

Der Auslöser: Platz 10

Nach jedem neuen Blogbeitrag stelle ich dem Chatbot ein paar Fragen dazu. Beim letzten Beitrag über eine Woche mit Sicherheitsupdates lautete eine davon: „Wie sind Passwörter ins SIEM gelangt?“ Die Antwort steht ausführlich im Beitrag. Der Chatbot fand den passenden Abschnitt trotzdem erst auf Platz 10, und weil er nur die besten fünf Beiträge an das Sprachmodell weitergibt, kam die Antwort nie an. Mit Stichworten, die wörtlich im Text stehen, landete derselbe Abschnitt auf Platz 1.

Das ist das typische Verhalten einer Suche, die Wörter vergleicht statt Bedeutung. Genau dafür setzt man aber ein Embedding-Modell ein. Irgendetwas lief also nicht wie gedacht.

Was ein Embedding-Modell hier tut

Der Chatbot arbeitet nach dem üblichen RAG-Muster. Ein nächtlicher Workflow in n8n zerlegt alle Beiträge in Abschnitte von höchstens 1000 Zeichen, lässt jeden Abschnitt von einem Embedding-Modell in einen Vektor mit 768 Zahlen übersetzen und speichert ihn in der Vektordatenbank Qdrant. Kommt eine Frage, wird sie mit demselben Modell übersetzt, und Qdrant liefert die Abschnitte, deren Vektoren am ähnlichsten sind (Kosinus-Ähnlichkeit). Diese Abschnitte bekommt das Sprachmodell als Kontext.

Das Embedding-Modell läuft lokal über Ollama, angesprochen über LiteLLM, und zwar bewusst nur auf der CPU: Die Grafikkarten gehören dem großen Sprachmodell. Bisher war das nomic-embed-text in Version 1, ein verbreitetes, kleines Modell, das vor allem auf englischen Texten trainiert wurde. Der Blog ist deutsch.

Die Suche selbst hatte ich kurz vorher umgebaut: Sie liefert je Beitrag nur noch den besten Abschnitt, bei Inhaltsfragen die besten fünf Beiträge, bei Listenfragen („Welche Beiträge gibt es zu …?“) acht, dann einschließlich der Themenübersichten. Diese Logik musste jede Messung später genau nachbilden.

Erste Idee: Aufgabenpräfixe

Die Modellkarte von nomic-embed-text verlangt Aufgabenpräfixe: Dokumente sollen mit search_document: eingebettet werden, Fragen mit search_query: . Weder der Import noch der Chatbot setzten sie. Das sah nach der Ursache aus, und ein schneller Versuch mit 40 Kandidaten-Abschnitten bestätigte es scheinbar: Die SIEM-Frage sprang von Platz 10 auf Platz 3.

Eine einzelne Frage beweist aber wenig. Ich wollte vor einer Änderung an der Produktion wissen, was die Präfixe über viele Fragen hinweg bewirken, und vor allem, ob sie anderswo etwas verschlechtern.

Ein Testsatz statt Einzelfragen

Der Testsatz besteht aus 38 Inhaltsfragen und 6 Listenfragen. Jede Inhaltsfrage nennt den oder die Beiträge, in denen die Antwort steht; einer davon unter den Treffern genügt. Die Listenfragen zielen auf die Übersichtsdokumente, etwa „Themenübersicht: Lokale KI“. Wichtig war die Formulierung: Die Fragen sind bewusst umschreibend, so wie ein Leser fragt, der den Text nicht vor sich hat. Wer Fragen aus dem Text abschreibt, misst die Wortsuche und bekommt geschönte Zahlen.

Einige Beispiele:

  • „Wie wurde der Arbeitsspeicher des Servers fast halbiert?“
  • „Was passiert, wenn der nächtliche Import die Wissensdatenbank leert?“
  • „Warum verschwinden bei nginx die Sicherheitsheader, wenn man Cache-Control setzt?“
  • „Wie legt man fest, welche Geräte im VPN miteinander sprechen dürfen?“

Das Messwerkzeug

Das Werkzeug ist ein Python-Skript ohne Abhängigkeiten außer der Standardbibliothek. Es arbeitet in zwei Schritten:

  1. Dump: Es liest alle Abschnitte der Live-Collection samt gespeicherter Vektoren aus Qdrant und legt sie als Datei ab. An Qdrant und n8n ändert es nichts.
  2. Lauf: Für jede Variante (Modell plus Präfix) bettet es alle Abschnitte neu ein, speichert die Vektoren zwischen, bettet die Testfragen ein und rechnet die Suche lokal nach: gleiche Filter, je Beitrag der beste Abschnitt, Top 5 bzw. Top 8.

Gezählt wird, wie oft der richtige Beitrag auf Platz 1, in den Top 3 und in den Top 5 landet, dazu der Mean Reciprocal Rank (MRR, der Mittelwert von 1/Platz) und der Score, mit dem die richtigen Treffer gefunden werden. Mit --detail zeigt es je Frage den Platz und, falls nicht Platz 1, was davor lag.

Lehre: Ein Messwerkzeug, das die Produktion nachbildet, braucht eine Kontrolle, dass die Nachbildung stimmt. Das Skript bettet deshalb die Abschnitte zusätzlich genau so ein wie der Import und vergleicht das Ergebnis mit den in Qdrant gespeicherten Vektoren. Kommt dabei eine Kosinus-Ähnlichkeit von 1,0000 heraus, misst das Werkzeug wirklich das, was die Leser bekommen. Liegt sie darunter, stimmt irgendwo die Vorverarbeitung nicht, und alle weiteren Zahlen wären wertlos.

Die Basismessung

Der Ist-Zustand mit nomic v1 ohne Präfixe: 21 von 38 Inhaltsfragen auf Platz 1, 33 in den Top 5, MRR 0,694. Alle 6 Listenfragen fanden ihre Übersicht. Mit Präfixen wurde es nicht besser, sondern gemischt: Platz 1 stieg auf 23, die Top 5 sanken aber auf 31. Für den Chatbot zählen die Top 5, denn nur diese Abschnitte bekommt das Sprachmodell zu sehen. Die Präfixe hätten also zwei Fragen mehr ins Leere laufen lassen.

Die Detailansicht zeigte außerdem, dass vier Fragen bei allen v1-Varianten danebenlagen, am deutlichsten die nach dem halbierten Arbeitsspeicher: Der richtige Beitrag kam erst auf Platz 29 bis 33. Das lag nicht an Präfixen. Das Modell verstand die deutschen Umschreibungen schlicht nicht gut genug.

Vier Modelle im Vergleich

Damit war die Frage eine andere: nicht „Präfix ja oder nein“, sondern „welches Modell“. Ich habe drei mehrsprachige Kandidaten ausgewählt, die über Ollama verfügbar sind und auf der CPU in vertretbarer Zeit laufen. Jeder bekam einen eigenen Alias in LiteLLM, eine Ollama-Variante mit num_gpu 0, damit er keinen Grafikspeicher belegt, und dann denselben Testsatz.

Modell Platz 1 Top 5 MRR ms je Abschnitt Dimensionen
nomic-embed-text v1 (bisher)2133/380,694140768
nomic-embed-text v1 mit Präfixen2331/380,725140768
bge-m33437/380,9302621024
qwen3-embedding:0.6b2837/380,8476131024
nomic-embed-text-v2-moe3338/380,919181768

bge-m3 hat knapp den besten Platz-1-Wert und den besten MRR. Ich habe mich trotzdem für nomic-embed-text-v2-moe entschieden, aus drei Gründen:

  • Es ist das einzige Modell, bei dem keine Frage aus den Top 5 fällt. Für den Chatbot ist das die Kennzahl, die zählt: Platz 2 statt Platz 1 merkt kein Leser, Platz 6 schon.
  • Es ist mit 181 ms je Abschnitt der schnellste Kandidat. Der nächtliche Import bettet jedes Mal alles neu ein, die Laufzeit wächst mit jedem Beitrag.
  • Es hat wie das alte Modell 768 Dimensionen. Die Collections in Qdrant konnten bleiben, wie sie sind; bei 1024 Dimensionen hätte ich sie neu anlegen müssen.

Die vier Dauer-Fehlgriffe von v1 landen mit v2 alle auf Platz 1 bis 4, die Frage nach dem Arbeitsspeicher eingeschlossen. Ein Hinweis zur Einordnung: 38 Fragen sind ein kleiner Testsatz. Der Unterschied zwischen 33 und 38 Treffern ist deutlich, der zwischen 37 und 38 kann Zufall sein. Für die Entscheidung gab deshalb nicht die eine Frage den Ausschlag, sondern die Summe aus Qualität, Tempo und Aufwand.

Zwei Grenzen von nomic v2:
  • Das Modell verarbeitet höchstens 512 Token je Text. Unsere Abschnitte mit maximal 1000 Zeichen liegen deutlich darunter. Wer größere Abschnitte einbettet, verliert sonst still das Ende.
  • Ollama nutzt standardmäßig die Grafikkarte. Für den reinen CPU-Betrieb braucht es eine eigene Modelfile-Variante mit PARAMETER num_gpu 0, sonst verdrängt es beim ersten Aufruf Teile des Sprachmodells aus dem Grafikspeicher.

Die Falle: Schwellen hängen am Modell

Beim Vergleich fiel eine Zahl auf, die in keiner Rangliste steht: Die richtigen Treffer von nomic v2 hatten einen Score von minimal 0,414 und im Median 0,569. Bei v1 waren es 0,615 und 0,753. Die Rangfolge ist besser, die absoluten Werte liegen aber viel tiefer. Das ist bei Embedding-Modellen normal, jedes Modell hat seine eigene Skala.

Der Chatbot filtert jedoch nach absoluten Werten. Qdrant lieferte nur Treffer ab 0,55, und der Code, der den Kontext für das Sprachmodell zusammenbaut, verwarf zusätzlich alles bis 0,50. Mit dem neuen Modell wäre etwa die Hälfte der richtigen Treffer an dieser Schwelle hängengeblieben. Der Modellwechsel hätte die Suche dann schlechter gemacht, obwohl das Modell besser ist.

Um neue Schwellen festzulegen, habe ich Fragen gestellt, auf die der Blog keine Antwort hat, etwa nach einem Rezept für Apfelstrudel, und die Scores der besten Treffer mit denen der echten Treffer verglichen. Bei v2 lagen fremde Fragen bei höchstens 0,343, echte Treffer bei mindestens 0,415. Dazwischen liegt eine Lücke, und in diese Lücke kam die neue Schwelle: 0,36 für Inhaltsfragen, 0,30 für Listenfragen, die gern etwas lockerer formuliert sind.

Die alte Schwelle hat nie gewirkt. Dieselbe Messung mit v1 zeigte etwas, das ich nicht gesucht hatte: Fremde Fragen erreichten dort Scores bis 0,667. Die Schwelle von 0,55 lag also mitten im Bereich der fremden Fragen und filterte praktisch nie etwas. Jede Frage bekam Quellen, auch die nach dem Apfelstrudel, und das Sprachmodell musste aus unpassendem Kontext irgendetwas machen. Die Schwelle stand seit Monaten in der Konfiguration und sah nach einer Absicherung aus.

Präfix nur für die Frage

Bei nomic v2 sind die Aufgabenpräfixe ebenfalls vorgesehen. Die Messung zeigte: Das Präfix an der Frage bringt viel, das am Dokument kaum etwas. Mit beiden Präfixen landeten 33 Fragen auf Platz 1, nur mit dem Frage-Präfix 32, die Top 5 blieben in beiden Fällen bei 38 von 38.

Das war wichtig, weil der Import das Dokument-Präfix nicht sauber setzen kann. Er nutzt in n8n die LangChain-Kette aus Document Loader, Text Splitter und Embeddings-Node. Diese Kette bettet die Abschnitte direkt ein, ein Präfix je Abschnitt ist darin nicht vorgesehen. Man hätte es in den Text schreiben müssen, dann stünde es aber auch im gespeicherten Abschnitt und im Kontext des Sprachmodells. Für einen Treffer mehr auf Platz 1 war mir dieser Umbau zu viel. In Produktion bekommt deshalb nur die Frage search_query: vorangestellt, die Dokumente bleiben ohne Präfix.

Die Umsetzung

Ein Modellwechsel betrifft zwei Workflows, die zusammenpassen müssen: den Import, der die Abschnitte einbettet, und den Chatbot, der die Frage einbettet. Vektoren aus verschiedenen Modellen sind nicht vergleichbar. Steht der Chatbot schon auf v2, während in der Datenbank noch v1-Vektoren liegen, liefert die Suche Zufallsergebnisse, ohne Fehlermeldung.

Die Wissensbasis läuft deshalb schon länger nach dem Blue/Green-Prinzip: Es gibt zwei Collections und einen Alias, der auf die aktive zeigt. Der Import baut immer die inaktive neu und schwenkt erst danach den Alias. Damit sah der Ablauf so aus:

  1. Beide Workflows exportieren und als Rückfall ablegen.
  2. Im Import das Modell tauschen, im Chatbot Modell, Frage-Präfix und die beiden Schwellen. Beide Workflows importieren und veröffentlichen.
  3. Den Import sofort von Hand starten. Er brauchte 206 Sekunden und schwenkte danach den Alias auf die neue Collection. Bis dahin beantwortet der Chatbot Fragen mit v2 gegen v1-Vektoren, das Fenster ist also so kurz wie möglich zu halten.
  4. Die alte Collection bleibt bis zum nächsten nächtlichen Lauf unverändert. Bis dahin ist der Rückweg ein Alias-Schwenk plus die alten Workflows.
  5. Den Testsatz gegen die neue Live-Collection laufen lassen, mit Kontrolle der Nachbildung.
  6. Auf der Website echte Fragen stellen, auch fremde.
Laufzeit und Timeout prüfen. Das neue Modell braucht je Abschnitt rund 30 % länger als das alte. Der Embeddings-Node hat einen Timeout, der schon einmal zu knapp gesetzt war und den Import zwei Wochen lang still scheitern ließ. Vor dem Wechsel habe ich deshalb nachgerechnet, ob der eingestellte Wert für die größeren Stapel noch reicht. Er reicht mit großem Abstand, aber bei einem langsameren Modell wäre das die erste Stelle, an der es bricht.
n8n 2.x führt die veröffentlichte Version aus. Ein Workflow, der nur importiert, aber nicht veröffentlicht wird, läuft mit der alten Fassung weiter. Ob die Änderung wirklich aktiv ist, zeigt erst ein Vergleich der aktiven Version mit dem gespeicherten Entwurf, oder eine echte Anfrage.

Ergebnis und Grenzen

Kennzahl vorher (v1) nachher (v2, live gemessen)
Inhaltsfragen auf Platz 121 von 3832 von 38
Inhaltsfragen in den Top 533 von 3838 von 38
MRR0,6940,909
Listenfragen gefunden6 von 66 von 6
Quellen bei fremden Fragenimmer welchekeine

Die Kontrolle der Nachbildung ergab wieder 1,0000, die Zahlen gelten also für das, was der Chatbot tatsächlich tut. Auf der Website beantwortet er die Frage nach dem halbierten Arbeitsspeicher jetzt inhaltlich statt mit einem Verweis auf einen unpassenden Beitrag, und zum Apfelstrudel bekommt er keine Quellen mehr, statt einen fremden Beitrag als Kontext zu erhalten.

Ganz gelöst ist der Auslöser allerdings nicht. Die vage Frage „Wie sind Passwörter ins SIEM gelangt?“ trifft weiter zuerst einen älteren Beitrag über die Einrichtung von Wazuh, und der handelt tatsächlich auch von SIEM-Passwörtern. Das ist keine falsche Antwort im engeren Sinn, nur nicht die gemeinte. Etwas genauer gefragt steht der richtige Beitrag klar auf Platz 1. An dieser Stelle hilft kein besseres Modell mehr, die Frage ist schlicht mehrdeutig.

Lehre: Der größte Gewinn kam nicht aus dem Modell, sondern aus dem Testsatz. Ohne ihn hätte ich die Präfixe eingebaut, weil die eine Frage besser wurde, und zwei andere unbemerkt verloren. Und ohne den Blick auf die Scores wäre der Modellwechsel an einer Schwelle gescheitert, von der niemand wusste, dass sie nie gewirkt hat. Der Testsatz bleibt im Repository und läuft vor jeder künftigen Änderung an Modell oder Schwellen.

Checkliste für den eigenen RAG-Chatbot

  • Gibt es einen Testsatz mit umschreibend formulierten Fragen und bekannten Zielen? Ohne ihn ist jede Änderung an der Suche ein Ratespiel.
  • Bildet die Messung die echte Suche nach, mit denselben Filtern, Gruppierungen und Grenzen, und gibt es eine Kontrolle, dass sie das tut?
  • Welche Kennzahl zählt für den eigenen Chatbot? Wer fünf Abschnitte weitergibt, sollte die Top 5 messen, nicht nur Platz 1.
  • Erwartet das Embedding-Modell Aufgabenpräfixe, und wurde gemessen, ob sie helfen?
  • Filtert die Score-Schwelle tatsächlich etwas? Ein paar Fragen, auf die die Wissensbasis keine Antwort hat, zeigen es sofort.
  • Wurden die Schwellen nach einem Modellwechsel neu bestimmt? Sie gehören zum Modell, nicht zur Suche.
  • Werden Import und Abfrage im selben Zug umgestellt, und gibt es einen Rückweg, solange die alten Vektoren noch da sind?
  • Passt das Token-Limit des Modells zur Abschnittsgröße, und reicht der Timeout des Imports für das langsamere Modell?