Skip to content

Chat und Bots

bl0rb edited this page Oct 3, 2026 · 3 revisions

Diese Seite ordnet ein, wie aus einer Frage im Chat eine belegte Antwort wird und woher die Bots stammen, die dabei antworten. Sie ersetzt keine der massgeblichen Beschreibungen: der Ablauf steht feldgenau in contracts/internal-chat.md, die Bot-Konfiguration in services/runtime/README.md, die Betriebsseite in docs/betrieb.md.

Wer was tut

Vier Dienste sind an einem Chat-Turn beteiligt, in dieser Reihenfolge.

  • Weave Chat (services/chat) ist die Weboberfläche. Sie hat keine Datenbank und genau einen Upstream: das Gateway. Sie ruft weder Runtime noch Retrieval direkt auf.
  • Weave-API (services/api) ist das Gateway: Anmeldung, Rate-Limit, Konversations-Verlauf in PostgreSQL. Chat-Intelligenz gehört ausdrücklich nicht dazu.
  • Weave-Runtime (services/runtime) ist der Bot-Orchestrator: Intent-Routing, Bot-Auswahl, Rechte-Schnittmenge, LLM-Aufruf, Response Guard. Sie ist zustandslos und speichert nichts.
  • Weave-Retrieval (services/retrieval) ist die Suche. Sie wird nur aufgerufen, wenn der Turn sie tatsächlich braucht.

Die Trennung ist der Grund dafür, dass ein Fehlerbild oft weit vom Symptom entfernt liegt: eine Antwort ohne Quellen kann eine Router-Entscheidung sein, eine leere Rechte-Schnittmenge, ein leerer Index oder eine nicht erreichbare Suche. Der trace, den jede Antwort mitliefert, unterscheidet diese Fälle; wie man ihn liest, steht in docs/betrieb.md, Abschnitt 10.

Von der Frage zur belegten Antwort

Zuerst entscheidet der Router, ob überhaupt gesucht wird

Bevor irgendetwas durchsucht wird, klassifiziert der Intent-Router die Nachricht in einen von fünf Intents: conversational, knowledge, document, action, complex. Nur knowledge führt überhaupt zu einer Suche. conversational geht direkt ans Modell und liefert bewusst keine Quellen. document, action und complex beantwortet ein LLM-Bot in dieser Version mit einem festen Hinweistext, ohne Modellaufruf — bei einem n8n-Bot dagegen werden genau diese Intents an den Flow weitergereicht.

Zwei Punkte, die regelmässig missverstanden werden:

  • Der Router läuft in zwei Modi (ROUTER_MODE): rules ist der Standard, ein deterministischer Schlüsselwort-Klassifikator ohne Netzaufruf; llm delegiert die Klassifikation an ein Modell und fällt bei jedem Fehler auf rules zurück, statt den Turn scheitern zu lassen.
  • Eine Router-Entscheidung für knowledge genügt nicht, damit gesucht wird. Zusätzlich muss der Bot selbst Retrieval aktiviert haben. Ein Bot ohne Wissensanbindung löst nie einen Such-Aufruf der Runtime aus, egal wie die Frage lautet. Für einen n8n-Bot gilt das anders herum: Runtime sucht dort grundsätzlich nicht selbst, der Flow sucht mit seinem Delegations-Token.

Wer also glaubt, der Bot habe den Index nicht gefunden, sollte zuerst trace.intent ansehen: conversational bei einer Wissensfrage heisst, dass gar nicht gesucht wurde.

Die Suche: zwei Signale, fusioniert, dann rerankt

Weave-Retrieval ist eine Hybrid-Suche. In Stichworten:

  • Vektorsuche (pgvector) und Volltextsuche laufen parallel über denselben Korpus, jeweils bis top_k Treffer.
  • Reciprocal Rank Fusion kombiniert beide Ranglisten allein über die Rang-Position, nicht über die Rohscores — Cosine-Similarity und Volltext-Rang sind nicht vergleichbar. Ein Chunk, den beide Signale finden, gewinnt fast immer.
  • Reranking ist optional (none als Vorgabe, daneben ein Test-Provider und ein Cohere/Jina-kompatibler API-Provider). Ein Reranker-Ausfall lässt die Suche nie scheitern: sie fällt auf die Fusions-Reihenfolge zurück und markiert das im Trace.
  • Erst nach dem Reranking wird auf final_k gekürzt. Ein Reranker kann eine Kandidatenmenge nur verkleinern, nie vergrössern.

Die vollständige Pipeline samt Parametern, Trace-Feldern und Fehlerbildern steht in services/retrieval/README.md.

Der Response Guard: keine Belege, keine Antwort

Bei aktiviertem Retrieval erzwingt der Guard eine belegte Antwort. Findet die Suche nichts Verwertbares, antwortet der Bot mit einem festen Text statt mit einer vom Modell erfundenen Antwort. Das ist die Vorgabe, nicht eine Option, die erst eingeschaltet werden muss.

Der Trace nennt drei unterschiedliche Gründe, und die Unterscheidung ist der eigentliche Nutzen:

  • no_context — gesucht wurde, gefunden wurde nichts.
  • no_collections — es gab von vornherein keinen Wissensbereich, den dieser Bot und dieser Mensch beide nutzen dürfen. Es wurde gar nicht erst gesucht.
  • filter_excluded_all — die Rechte hätten gereicht, aber die eigene Auswahl in der Seitenleiste hat den Suchraum geleert. Das ist der einzige der drei Fälle, den der Fragende allein beheben kann.

Zwei Dinge, die der Guard ausdrücklich nicht tut: er weicht beim Streaming nicht auf. Ein Guard-Text wird als gewöhnlicher Textstrom ausgeliefert, die Quellenpflicht bestimmt nur, welcher Text gestreamt wird. Und er springt nicht für einen Ausfall ein: ist Weave-Retrieval nicht erreichbar, antwortet Runtime mit einem Fehler, nie mit einer stillen, unbelegten Antwort.

Bei einem n8n-Bot gilt die Quellenpflicht in der Gegenrichtung. Was ein Flow als Quelle zurückmeldet, ist eine Behauptung. Weave prüft jede gemeldete Quelle gegen den signierten Umfang des Delegations-Tokens und verwirft, was nicht passt; die verworfenen Einträge stehen im Trace. Details in contracts/n8n-flow.md.

Der Collection-Filter in der Oberfläche kann nur einschränken

In der Seitenleiste lassen sich Wissensbereiche auswählen. Das ist ein Filter für die nächste Nachricht, keine Rechtevergabe. Die Auswahl schneidet nur von dem ab, was ohnehin lesbar wäre; sie kann nichts hinzufügen. Keine Auswahl bedeutet kein Filter, also alles, was erlaubt ist — nicht "nichts".

Die Autorität liegt serverseitig: Runtime bildet die Schnittmenge aus den Bereichen, die der Bot nutzen darf, und denen, die der anfragende Mensch laut seinem Team lesen darf. Ein Bereichsname aus einem Anfrage-Feld, einem Tool-Argument oder einer Modell-Ausgabe ist immer nur eine Einschränkung innerhalb dieser Schnittmenge. Wie Leserechte an Wissensbereichen entstehen, steht unter Wissensbereiche-und-Freigaben und Rechte-und-Sichtbarkeit.

Zwei Quellen für Bots

Runtime stellt die Bot-Liste vor jeder Anfrage neu zusammen, aus zwei Quellen.

Lokale YAML-Dateien

Bots als YAML-Dateien in Runtimes Bot-Verzeichnis (BOTS_DIR). Im Compose-Betrieb ist das das Volume runtime_bots, das Docker beim ersten Mount aus den Beispiel-Bots des Images befüllt; Dateien dort werden pro Anfrage neu gelesen, ein Neustart ist nicht nötig. Feld-für-Feld-Beschreibung eines Bots — Modell, Prompt, Retrieval, erlaubte Teams, Guard — in services/runtime/README.md, Volume-Details in deploy/README.md.

Zentral in Ingest gepflegte n8n-Bots

Seit v0.1.0 werden n8n-Bots nicht mehr als Datei auf der Platte gepflegt, sondern zentral in Weave-Ingest unter Administration → Bots: Anzeigename, Webhook-Adresse, optionales Flow-Token, Timeout, erlaubte Teams und Wissensbereiche. Runtime holt die aktivierten Einträge pro Anfrage über ein eigenes Service-Token ab. Das Flow-Token liegt verschlüsselt in Ingest und verlässt es nur über diese interne Route.

Der Grund für den Umzug: eine Änderung gilt ab der nächsten Anfrage, ohne dass jemand eine Datei in einem Container bearbeitet oder einen Pod neu startet. Und Team- sowie Bereichsauswahl in der Maske sind, wie überall, reine Einschränkungen — Runtime schneidet weiter mit den Rechten des Fragenden.

Die Merge-Regel

Beide Quellen werden gegen dasselbe Bot-Schema und dieselbe Webhook-Allowlist geprüft. Die Allowlist greift dabei beim Zusammenstellen der Liste, nicht erst beim ersten Aufruf, und für beide Quellen gleich: weder eine Konfigurationsdatei noch ein Administrator soll den Dienst in ein beliebiges Netz rufen lassen können. Ist die Allowlist leer, sind n8n-Bots abgeschaltet.

Kollidieren die Quellen, gewinnt die Zentrale: trägt ein zentraler Eintrag dieselbe Bot-ID wie eine lokale YAML-Datei, überschreibt der zentrale Eintrag die lokale Definition. Eine lokale Datei ist damit ein Ausgangspunkt oder ein Rückfall, nie ein stiller Konkurrent zur gepflegten Version.

Der betriebliche Haken: ganz oder gar nicht

Die zentrale Anbindung ist bewusst nicht ausfalltolerant. Sind die beiden Werte für Adresse und Service-Token gesetzt und Ingest antwortet nicht oder liefert Unbrauchbares, scheitert die gesamte Bot-Liste — auch die lokalen YAML-Bots. Runtime meldet sich degraded, /v1/bots antwortet über das Gateway mit 502, und jeder Chat-Turn hängt damit an der Erreichbarkeit von Ingest.

Das ist gewollt: eine halb alte, halb neue Bot-Definition wäre schlimmer als gar keine. Es heisst aber, dass Ingest für den Chat kritisch wird, sobald die zentrale Konfiguration eingeschaltet ist. Sind beide Werte leer, läuft Runtime eigenständig weiter, nur mit den YAML-Bots. Die vollständige Begründung samt Symptomtabelle steht in docs/betrieb.md, Abschnitt 9.

Welches Sprachmodell antwortet (ab v0.7.1)

Unter Administration → Chat & LLM gibt es neben dem zentralen Endpunkt weitere, benannte LLM-Endpunkte, zum Beispiel ein Modell mit nativen Tool-Aufrufen. Pro Bot gilt:

  • LLM-Endpunkt legt fest, womit der Bot antwortet. Leer heisst: der zentrale Endpunkt.
  • Im Chat zur Auswahl anbieten lässt Nutzer pro Chat zwischen diesem und weiteren Endpunkten wählen; im Chat erscheint dann neben dem Eingabefeld eine Auswahl Sprachmodell. Alle aktiven Endpunkte bietet jeden aktivierten an. Ohne Eintrag gibt es keine Auswahl.
  • Ein Subagent im Agentenmodus kann einen eigenen Endpunkt nutzen. Zur Wahl stehen nur Endpunkte, die Tool-Aufrufe unterstützen.

YAML-Bots tragen dieselben Angaben als model.endpoint und model.endpoints (['*'] = alle aktiven). Runtime prüft jede Auswahl gegen die Liste des Bots; einen Endpunkt, den ein Bot noch verwendet, lässt Ingest nicht löschen. n8n-Bots bleiben bei dem Modell, das ihr Flow vorgibt.

Häufige Missverständnisse

  • "Der Bot findet unser Dokument nicht." Meistens ist das keine Suchqualität, sondern eine Stufe davor: kein knowledge-Intent, kein Retrieval am Bot, keine gemeinsame Bereichs-Schnittmenge, oder das Dokument ist nie freigegeben worden. Siehe Wissensbereiche-und-Freigaben.
  • "Die Auswahl in der Seitenleiste gibt mir Zugriff." Sie schränkt nur ein. Rechte entstehen ausschliesslich über Team und Wissensbereich.
  • "Bots konfiguriert man im Chat." Die Oberfläche listet nur, was das Gateway zurückgibt. Gepflegt werden Bots in Runtimes Bot-Verzeichnis oder zentral in Ingest.
  • "Antworten beginnen mit [fake-llm], das ist ein Bug." Das ist der Vorgabe-Provider für Entwicklung und Tests. Echte Provider einrichten: Betrieb.

Weiterlesen