---
title: "Konkrete Tipps: Von einfachem RAG zu besserem RAG"
description: "Das Setup aus jedem Tutorial lässt kaum Kontrolle über die Query zu: eine Anfrage pro Aufruf und vier Chunks zurück. Sechs Änderungen im n8n Canvas fixen das, von Multi-Query bis zum Relevance Threshold."
date: 2025-08-24
language: de
canonical: https://gduv.club/de/articles/better-rag
source: gduv.club
---
Fast jedes n8n RAG-Tutorial endet beim gleichen Canvas: ein AI Agent und ein daran hängender Vector Store Node, der direkt als Tool genutzt wird. Ich habe dieses Setup oft gebaut. Es funktioniert, und eine Zeit lang habe ich es nicht genauer hinterfragt.

Dann fing ich an, schwierigere Fragen zu stellen, und drei Dinge gingen schief, die alle dieselbe Ursache hatten: Der Agent steuert die Suche, und Sie haben keinerlei Kontrolle darüber.

Dies ist die schriftliche Version eines Videos, mit demselben Biologie-Kurs und denselben Zahlen.

[Level Up Your n8n RAG Agents: Smart Multi-Query & Reasoning (with Supabase + GPT-5)](https://www.youtube.com/watch?v=rKTM_SWLHLI)

## Der Canvas, mit dem alle starten

Meine Wissensbasis hierfür ist ein Biologie-Kurs, vektorisiert in Supabase. Fragt man "was ist eine Zelle", antwortet er gut. Das ist die Demo, die jeder zeigt, und sie ist nicht gelogen: einfache Fragen funktionieren.

**AI Agent**

**Wenn Chat-Nachricht empfangen wird** → **AI Agent** (Chat Model: OpenAI Chat Model; Memory: Simple Memory; Tool: Supabase Vector Store (Der Agent schreibt die Query, vier Chunks kommen zurück))

_Drei Sub-Knoten, wobei die Suche einer davon ist. Alles, was in dieser dritten Box passiert, wird zur Laufzeit vom Agenten entschieden._

Das ist alles, was Sie in diesem Setup steuern: eine Beschreibung des Tools, die dem Agenten sagt, wann er es einsetzen soll. Das ist die gesamte Konfigurationsoberfläche.

**Was Sie festlegen** (Einmalig zur Build-Zeit)

- Eine Tool-Beschreibung
- Anzahl der zurückzugebenen Chunks

**Was der Agent entscheidet** (Bei jedem Aufruf)

- Der exakte Query-Text, der an den Vector Store gesendet wird
- Dass es eine Query gibt, niemals zwei
- Nichts darüber, wie nah ein Chunk beieinander liegen muss

_Die linke Spalte zeigt die gesamte Konfigurationsfläche. Alles auf der rechten Seite wird automatisch für Sie entschieden._

### Sie sehen die Query niemals

Der Agent schreibt die Query selbst und sendet sie ab. Sie können ihn über die Tool-Beschreibung in eine Richtung lenken und hoffen. Ob eine Frage umformuliert werden musste oder ob die Formulierung des Nutzers überhaupt nicht mit der Wortwahl in Ihren Dokumenten übereinstimmt, finden Sie erst hinterher im Execution Log heraus.

### Eine Query pro Aufruf

Eine echte Frage besteht oft nicht aus nur einer Frage. Fragen Sie etwas, das erst eine Definition eines Begriffs erfordert und diesen Begriff dann mit einem anderen in Verbindung bringt, muss eine einzige Ähnlichkeitssuche beides gleichzeitig abdecken. Das Ergebnis sind Chunks, die für beides mittelmäßig sind, statt für eines der beiden gut.

Der Agent könnte das Tool zweimal aufrufen, aber das wären zwei volle Durchläufe des Modells, und meistens macht er sich die Mühe nicht.

### Es kommen vier Chunks zurück, egal was Sie gefragt haben

Das war der Punkt, an dem ich das Setup geändert habe. Der Vector Store gibt die vier ähnlichsten Chunks zurück. Die ähnlichsten, nicht die tatsächlich ähnlichen.

Fragen Sie meinen Biologie-Kurs, wann wir heute Abend essen, und Sie erhalten trotzdem vier Chunks. Sie sind weit entfernt, aber sie sind die vier am wenigsten entfernten, also werden sie ausgegeben. Der Agent erhält sie ohne Hinweis darauf, dass sie wertlos sind, und da Modelle kooperativ sind, behandelt er sie als relevanten Inhalt.

**Vier Chunks, die für eine nicht zusammenhängende Frage zurückgegeben wurden, mit Similarity-Scores zwischen 0,11 und 0,19, die alle so an den Agenten übergeben wurden, als wären sie relevant.**

  <div class="dg-json">
    <p class="dg-label">Query: "wann essen wir heute Abend"</p>
    <div class="dg-box dg-box--bad">
      <span class="dg-box__title">4 Chunks zurückgegeben</span>
      <span class="dg-box__note">Zellmembrantransport <span class="dg-json__score">0.19</span></span>
      <span class="dg-box__note">Mitose, Zeitliche Phasen <span class="dg-json__score">0.16</span></span>
      <span class="dg-box__note">Enzymkinetik <span class="dg-json__score">0.13</span></span>
      <span class="dg-box__note">Photosynthese <span class="dg-json__score">0.11</span></span>
    </div>
    <p class="dg-note">Die Scores existieren innerhalb des Vector Stores. Das Tool gibt sie nicht an Sie weiter.</p>
  </div>

## Ersetzen Sie das Tool durch einen Sub-Workflow

Die Lösung besteht darin, dem Agenten nicht den Vector Store direkt zu geben, sondern stattdessen einen Sub-Workflow. In n8n ist das das **Call n8n Workflow** Tool: Der Agent ruft es auf, ein Workflow läuft auf seinem eigenen Canvas, und was der letzte Node ausgibt, erhält der Agent.

Auf der Seite des Agenten ändert sich kaum etwas. Gleicher Trigger, gleicher Agent, nur dass der Vector Store gegen zwei Tools getauscht wurde.

**AI Agent**

**Wenn Chat-Nachricht erhalten** → **AI Agent** (gpt-5-mini, Chat Model: OpenAI Chat Model (gpt-5-mini); Memory: Simple Memory (Die letzten 8 Turns); Tool: Wissensdatenbank abfragen (Der Sub-Workflow. Nimmt ein Array von 1 bis 5 Queries entgegen); Tool: Think (Direkt nach dem Retrieval genutzt, max. 50 Wörter))

_Der Agent läuft hier auf gpt-5-mini, nicht auf einem Frontier-Modell. Sobald das Retrieval die Sortierung übernimmt, besteht die Aufgabe des Agenten nur noch darin, eine Antwort aus den übergebenen Daten zu schreiben, und das ist nicht der schwierige Teil._

Im Sub-Workflow befindet sich die gesamte neue Konfigurationsfläche. Jede Box unten ist ein Schritt, den Sie selbst geschrieben haben und den Sie später im Execution Log einsehen können.

**Sub-Workflow, Tool für Agent**

**RAG Sub-Workflow** (Trigger. Input: queries, ein Array) → **Split Out** (Ein Element pro Query) → **Loop Over Items** (Alles danach läuft einmal pro Query) → **Supabase Vector Store** (Als Node, nicht als Tool. Sie mappen die Query, und jeder Chunk kommt mit seinem Score zurück, Embedding: Embeddings OpenAI) → **RAG-Ausgabe bereinigen** (Behält den Chunk-Text, den Kapitelnamen und den auf zwei Dezimalstellen gerundeten Score) → **Score über 0,4 behalten** (Filter. Gibt immer Daten aus, sodass eine Query ohne Treffer trotzdem fortgeführt wird) → **Irgendwelche Chunks?** (IF, ob etwas übrig geblieben ist)

- _true, etwas überstanden_ → **Chunks aggregieren**

- _false, nichts überstanden_ → **Keine Chunk-Treffer melden** ("Keine Chunks haben den Relevanz-Schwellenwert erreicht, die Wissensdatenbank konnte keine Informationen liefern")

**Loop-Ausgabe vorbereiten** (Die Query, gepaart mit dem Ergebnis)

Loops back: Prepare loop output geht zurück zu Loop Over Items. Wenn jede Query durchgelaufen ist, geht der Loop-Done-Output an einen Aggregate-Node und der Agent erhält ein einzelnes Feld: Knowledge base retrieval.

_Der Filter und das danebenliegende IF sind das Kernstück. Eines entscheidet, was gut genug ist, das andere stellt sicher, dass eine Query, die nichts gefunden hat, dies auch mitteilt, statt Stille zurückzugeben._

### Ein Array von Queries in einem einzigen Tool-Aufruf

Der Trigger des Sub-Workflows akzeptiert ein Feld namens `queries`. Die Tool-Beschreibung teilt dem Agenten mit, dass es sich um ein Array von eins bis fünf handelt, und der System-Prompt sagt dasselbe in die andere Richtung: Je komplexer die Frage des Nutzers, desto mehr soll sie in Sub-Queries aufgeteilt werden, bis zu fünf.

Das ist der Multi-Query-Teil, und er kostet nur einen Tool-Aufruf statt drei.

| Step | What happens | Cost |
| :--- | :--- | ---: |
| Der Nutzer stellt eine komplexe Frage | Zwei Begriffe und deren Interaktion |  |
| Der Agent schreibt drei Abfragen | Definition des ersten Begriffs, des zweiten Begriffs und deren Beziehung | 1 Tool-Aufruf |
| Der Unter-Workflow führt alle drei aus | Eine Schleife und ein Score für jedes Ergebnis |  |
| Ein aggregiertes Element wird zurückgegeben | Gruppiert nach Abfrage |  |

_Der Agent hat die Aufteilung hier selbst entschieden. Ich habe ihm nur gesagt, dass das Feld bis zu fünf Einträge aufnimmt._

**Parameter als JSON definieren, sonst erfolgt die Übergabe als String**

Wenn das Modell einen Tool-Parameter über `$fromAI` befüllt, ist der Standardtyp String. Ein Array mit einem Element wird dann als Text zurückgegeben, der sich nicht parsen lässt. Setzen Sie den Typ auf `json` und schreiben Sie es explizit in die Beschreibung: Nutzen Sie ein Array, selbst wenn es nur eine Frage gibt. Meine Beschreibung endet mit einem Beispiel-Array, da das Modell dieses kopiert.

### Der Score ist der entscheidende Punkt

Innerhalb des Unter-Workflows ist Supabase ein regulärer Node. Ich mappe die Abfrage selbst, und die Antwort enthält einen Similarity Score. Der Filter verwirft alles, was bei 0,4 oder darunter liegt.

Zwei Details, die mich Zeit gekostet haben: Der Filter muss so eingestellt sein, dass er immer Daten ausgibt. Andernfalls wird bei einer Abfrage, bei der nichts passt, gar kein Element erzeugt und der nachfolgende Branch wird nie ausgeführt. Zudem prüft das darauf folgende IF, ob überhaupt ein Chunk übrig geblieben ist. Das ist der Fall, den man explizit behandeln möchte, statt einfach zu schweigen: Der Agent erhält einen Satz, dass die Wissensdatenbank für diese Abfrage nichts Nützliches enthalten hat.

**0,4 ist mein Wert, kein universeller Standard**

Dieser Wert hat sich für diese Wissensdatenbank mit diesen Chunk-Größen und diesem Embedding-Modell bewährt. Ändert man einen dieser drei Faktoren, verschiebt sich auch der Wert. Führen Sie einige echte Fragen sowie einige bewusst unpassende Abfragen aus, prüfen Sie die zurückgegebenen Scores und legen Sie die Grenze dazwischen fest.

### Dem Agenten die Quelle eines Chunks mitteilen

Der Bereinigungsschritt ist wichtiger, als es scheint. Supabase liefert Metadaten zurück, die ich nicht benötige, wie etwa den Content-Type der Quelldatei. Das verbraucht bei jedem Chunk jeder Abfrage unnötige Tokens.

Ich behalte den Chunk-Text plus zwei Felder: das Kapitel, aus dem er stammt, und den Relevance Score, gerundet auf zwei Dezimalstellen. Beides geht an den Agenten. Der Kapitelname ermöglicht es ihm, die Quelle zu nennen. Durch die Weitergabe des Scores sieht der Agent, ob eine seiner fünf Abfragen knapp bei 0,42 lag, während eine andere 0,81 erreichte, und kann diese entsprechend gewichten.

### Rückgabewerte benennen

Die Aggregation am Ende erzeugt keinen anonymen Datenblock. Sie erzeugt ein Feld namens `Knowledge base retrieval`, in dem jeder Eintrag `Query to the knowledge base` mit `Chunks returned` verknüpft ist.

Diese Verknüpfung ist essenziell. Der Agent hat drei Abfragen gesendet, erhält drei beschriftete Gruppen zurück und kann so zuordnen, welche Chunks welchen Teil seiner eigenen Frage beantworten. Übergibt man ihm stattdessen ein flaches Array aus zwölf Chunks, muss er die Zuordnung selbst herleiten, was er zeitweise fehlerhaft tut.

Das Benennen der Felder erfordert einen Set-Node. Es ist die günstigste Maßnahme auf dieser Liste und diejenige, die ich am längsten ignoriert habe.

## Ein Denk-Schritt vor der Antwort

Die andere Änderung betrifft den Agenten selbst. Ich habe ihm ein Think-Tool gegeben. Der System-Prompt weist ihn an, dieses Tool unmittelbar nach der Abfrage zu nutzen: Die Frage mit den Ergebnissen analysieren und prüfen, ob die Informationen tatsächlich ausreichen, um zu antworten.

Die Tool-Beschreibung begrenzt die Antwort auf 50 Wörter. Ohne diese Einschränkung schreibt er ganze Absätze, und ein laut gedachtes Protokoll über vier Chunks ist keine ganze Seite an Tokens wert.

Diese Notizen können im Execution Log eingesehen werden, was mir überraschenderweise sehr gefällt. Wenn eine Antwort falsch ist, verraten die Notizen meist, ob die Abfrage schlecht war oder die Logik.

Die letzten Zeilen des System-Prompts sind die wichtigsten für mich. Antworten Sie nur auf Basis der Kursinhalte; liegt eine Frage außerhalb davon, verweisen Sie darauf, statt zu antworten. Und wenn die benötigten Informationen fehlen, geben Sie dies zu, anstatt auf allgemeines Wissen zurückzugreifen.

Beides funktioniert nur, weil die darunterliegende Abfrage ehrlich kommuniziert, wenn sie leer zurückkehrt. Eine Anweisung, Unwissenheit zuzugeben, ist wertlos, wenn jede Abfrage vier Chunks liefert, die wie Beweise aussehen.

## Was uns das wirklich lehrt

Jeder Fix hier ist im Grunde derselbe. Etwas wurde für mich entschieden, also habe ich es in einen Schritt verschoben, den ich selbst kontrolliere.

Das ist wertvoller als der Workflow selbst. Wenn ich jetzt mit Claude Code arbeite, stelle ich genau die Fragen, zu denen mich dieses Setup gezwungen hat: Was wurde genau gesucht, wie viel kam zurück, wie viel davon war lesenswert und was befindet sich im Kontextfenster, obwohl es niemand braucht. Ein generischer Agent verbirgt all das standardmäßig und liest bereitwillig zwanzig Dateien, um etwas zu beantworten, wofür zwei gereicht hätten.

Einmal die Retrieval-Logik von Hand zu bauen, ist der Weg, um ein Auge für diese Details zu entwickeln.

## Quellen

- [n8n: Call n8n Workflow tool](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolworkflow/)
- [n8n: Supabase vector store node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.vectorstoresupabase/)
- [n8n: `$fromAI` in tool parameters](https://docs.n8n.io/advanced-ai/examples/using-the-fromai-function/)
- [Supabase: pgvector and similarity search](https://supabase.com/docs/guides/ai/vector-columns)