Startseite / Artikel

Entwerfen Sie einen RAG-Pipeline, damit KI-Agenten begründen können, anstatt nur abzufragen.

Ein RAG-Pipeline für KI-Agenten sollte Zuverlässigkeit, Quellenvielfalt und Abdeckung offenlegen, damit der Agent Lücken erkennen, erneut nachfragen und erklären kann, wie er zu seinen Ergebnissen gelangt ist.

Verlag: REACTAPP.TOP5 Min. Lesezeit1252 Wörter

Quellen: How to Design a RAG Pipeline So AI Agents Reason, Not Just Query It

Ein Agent ruft einen RAG-Pipeline auf und erhält eine Liste von Dokumenten. Was passiert danach?

In den meisten agentbasierten Konfigurationen wird RAG wie ein Suchfeld behandelt: Man sendet eine Abfrage, nimmt das Ergebnis entgegen und geht weiter. Der Agent kann nicht erkennen, ob die Ergebnisse gut waren, ob sie jeden Aspekt der Frage berücksichtigt haben oder ob ein zweiter Versuch mit einer präziseren Hypothese etwas Wichtiges zutage gefördert hätte. Er arbeitet somit blind.

Das ist wichtig, weil Agenten auf unvollständigen Informationen handeln. Ein Halluzinationserkennungstool kann erfundene Fakten aufdecken, doch blinden Flecken bei der Informationsabrufung sind weitaus schwerer zu erkennen. Der Agent trifft selbstbewusst Entscheidungen auf der Grundlage eines unvollständigen Bildes, und niemand erfährt, was er übersehen hat. Der übliche Entwicklungsprozess – Daten aufnehmen, dann abrufen und anschließend generieren – beinhaltet überhaupt kein Feedbacksignal.

Die Informationsabrufung für den Agenten sichtbar machen

Die Lösung besteht aus einem dünnen Rahmen um den RAG-Pipeline, der strukturierte Signale bereitstellt, über die der Agent nachdenken kann, anstatt sie nur zu verarbeiten.

Anstelle einer einfachen Liste von Dokumenten liefert der Rahmen ein umfangreicheres Objekt. Dieses enthält die Dokumente, eine Zuverlässigkeitsbewertung, einen Indikator für die Quellenvielfalt (wie viele verschiedene Domänen und welcher Zeitraum), Signale zur Abdeckung, die angeben, welche Aspekte der Frage berücksichtigt wurden und welche fehlen, sowie Metadaten zur Extraktion wie das Embedding-Modell, die Größe der Blöcke, der Top-k-Wert und ob ein Reranker verwendet wurde.

{
  "results": [Document, Document, Document],
  "confidence_score": 0.87,
  "source_diversity": {"domains": 3, "timestamps": ["2024-01", "2023-11", "2023-09"]},
  "coverage_signals": {
    "question_aspects_covered": ["technical", "cost"],
    "question_aspects_missing": ["compliance", "deployment"]
  },
  "retrieval_metadata": {
    "embedding_model": "text-embedding-3-large",
    "chunk_size": 512,
    "top_k": 5,
    "reranker_applied": true
  }
}

Dadurch verfügt der Agent über einen Feedbackkanal. Er kann erkennen, dass die Abdeckung unvollständig ist und die Quellen begrenzt sind. Er kann entscheiden, aus einer anderen Perspektive erneut nachzufragen, die Angelegenheit an eine Person weiterzuleiten oder die Lücke in seiner Argumentation festzuhalten.

Drei Ebenen

Ebene 1: Abruf der Instrumente

Zunächst werden die bereits vom System berechneten Werte erfasst. Vektordatenbanken liefern Ähnlichkeitswerte, Reranker geben das Vertrauensniveau an und Blöcke enthalten Zeitstempel sowie Quell-URLs. Diese erste Ebene sammelt diese Werte, ohne die Abruflogik zu verändern: Sie führt die übliche Abfrage durch, extrahiert die Werte, Quellen und Zeitstempel, berechnet aus den Werten einen groben Vertrauenswert durch Durchschnittsbildung und zählt die verschiedenen Quellen.

def retrieve_with_signals(query: str, top_k: int = 5):
    # Standard retrieval
    results = vector_db.query(query, top_k=top_k)

    # Capture raw signals
    similarity_scores = [r.score for r in results]
    sources = [r.metadata.get("source") for r in results]
    timestamps = [r.metadata.get("timestamp") for r in results]

    # Compute confidence as mean similarity
    confidence = sum(similarity_scores) / len(similarity_scores)

    return {
        "results": results,
        "confidence_score": confidence,
        "source_diversity": {
            "domains": len(set(sources)),
            "timestamps": sorted(set(timestamps))
        }
    }

Es ist nicht invasiv, da lediglich Daten gelesen werden, die bereits vorhanden sind.

Schicht 2: Abdeckung ermitteln

Danach folgt eine Schicht, die den abgerufenen Inhalt wieder auf die ursprüngliche Frage zuordnet. Eine einfache semantische Vergleich ist ausreichend, es ist kein vollständiger Neuranking erforderlich.

Teilen Sie die Frage in Aspekte auf. Für „Wie deploye ich ein Modell unter Kostenbeschränkungen?“ könnten die Aspekte Deployment, Kosten und Beschränkungen sein. Prüfen Sie anschließend, welche davon im abgerufenen Text vorkommen. Ein LLM oder eine einfache Schlüsselwortextraktion kann die Liste der Aspekte erzeugen, und die Prüfung kann so einfach sein, dass jeweils nach jedem Aspekt im kombinierten Dokumenttext gesucht wird. Die Funktion gibt an, welche Aspekte abgedeckt wurden, welche fehlen und welcher Abdeckungsgrad vorliegt.

def detect_coverage(query: str, documents: list) -> dict:
    # Extract query aspects (simple approach: use an LLM or keyword extraction)
    aspects = extract_query_aspects(query)  # ["deployment", "cost"]

    # Check document coverage
    doc_text = " ".join([d.content for d in documents])
    covered = [a for a in aspects if a.lower() in doc_text.lower()]
    missing = [a for a in aspects if a not in covered]

    return {
        "question_aspects_covered": covered,
        "question_aspects_missing": missing,
        "coverage_ratio": len(covered) / len(aspects) if aspects else 1.0
    }

Hier erhält der Agent Feedback, auf das er reagieren kann. Fehlende Aspekte sollten eine neue Abfrage auslösen. Um den Ansatz zu verbessern, sollten bei der Eingabe jedes Dokuments die entsprechenden Aspekte markiert werden und diese bei der Abrufung verglichen werden.

Schicht 3: Übergeben an den Agenten

Zuletzt sollte dem Agenten ein Tool zur Verfügung gestellt werden, das die Ausgabe des Systems zurückgibt. Dieses Tool nimmt eine Abfrage sowie optional einen zu berücksichtigenden Aspekt entgegen, führt eine Suche mit Signalen durch, ergänzt die Abdeckung und fügt eine Empfehlung hinzu: Erneut abfragen, wenn der Abdeckungsgrad unter 0,7 fällt, ansonsten fortfahren.

tools = [
    {
        "name": "search_with_reasoning",
        "description": "Search the knowledge base and receive coverage signals",
        "parameters": {
            "query": "str",
            "aspect_focus": "optional str (e.g., 'deployment' or 'cost')"
        }
    }
]

def search_with_reasoning(query: str, aspect_focus: str = None) -> dict:
    retrieval_output = retrieve_with_signals(query, top_k=5)
    coverage = detect_coverage(query, retrieval_output["results"])

    return {
        **retrieval_output,
        "coverage_signals": coverage,
        "recommendation": (
            "re-query" if coverage["coverage_ratio"] < 0.7 else "proceed"
        )
    }

Der Agent erhält diese Empfehlung und kann ihr folgen oder sie mithilfe seines Fachwissens überschreiben.

Überprüfen, ob die Signale vertrauenswürdig sind

Das System ist nur dann nützlich, wenn seine Signale genau sind, daher sollten drei Aspekte getestet werden.

  • Genauigkeit der Signale. Vorhersagt der Zuverlässigkeitswert tatsächlich die Qualität der Ergebnisse? Führen Sie etwa zwanzig Abfragen durch, bewerten Sie die Ergebnisse manuell von eins bis fünf und korrelieren Sie die Bewertungen mit dem Wert. Ein Korrelationskoeffizient von etwa 0,75 ist ein angemessenes Ziel.
  • Erkennung des Abdeckungsumfangs. Markieren Sie manuell, welche Aspekte jeder Frage von den Dokumenten beantwortet werden, und vergleichen Sie dies anschließend mit dem Detektor bei etwa zehn unterschiedlichen Abfragen. Streben Sie eine Genauigkeit von über 85 % an.
  • Verhalten des Agents. Protokollieren Sie jede Entscheidung zur erneuten Abfrage. Wenn in den meisten Fällen ein Abdeckungsratio unter 0,7 zu einer neuen Abfrage führt, achtet der Agent auf das Signal.

Kosten und Grenzen

Es entsteht zusätzliche Latenz. Die Abdeckung erfordert einen zweiten semantischen Durchlauf, wodurch bei der Abfrage von fünf Dokumenten zusätzlich 50 bis 100 Millisekunden hinzukommen können. Wenn das zu viel ist, sollten die Signale im Cache gespeichert oder asynchron berechnet werden.

Das Framework geht außerdem davon aus, dass die Abfrageschicht ausreichend funktionsfähig ist, um Daten bereitzustellen. Wenn der Vektor-Speicher keine Scores zurückgibt oder die Blöcke keine Metadaten enthalten, muss zunächst die Instrumentierung erfolgen.

Zudem handelt es sich bei den Signalen um Heuristiken. Ein hoher Vertrauenswert garantiert keine korrekten Schlussfolgerungen. Das Framework liefert dem Agenten bessere, aber nicht perfekte Eingaben, weshalb nach wie vor Überprüfungen auf Halluzinationen notwendig sind.

Warum es sich lohnt

Agenten, die den Ablauf der Informationsbeschaffung nachvollziehen können, sind transparenter und zuverlässiger. Sie können erklären, warum sie erneut suchen, Unsicherheiten markieren und bei geringer Abdeckung an einen Menschen weiterleiten. Das ist der Unterschied zwischen einer Suchmaschine und einem Forschungsassistenten: Die eine liefert Ergebnisse, der andere liefert Ergebnisse und gibt an, worin er unsicher ist.

Erste Schritte

  1. Führen Sie in dieser Woche Schritt 1 durch. Zeigen Sie die Zuverlässigkeit sowie die Vielfalt der Quellen an, protokollieren Sie diese einen Tag lang und beobachten Sie Muster.
  2. Fügen Sie eine Abdeckungserkennung für Ihre häufigsten Abfragtypen hinzu und überprüfen Sie diese manuell an zehn Beispielen.
  3. Integrieren Sie diese Funktion in den Agenten und messen Sie, ob dieser bei geringer Abdeckung erneut abfragt sowie ob seine Argumentation verbessert wird.

Sobald das Harness vorhanden ist, lassen sich weitere Signale leicht integrieren: Erkennung semantischer Abweichungen, Frischheitsprüfungen sowie Hilfsmittel zur Faktenüberprüfung. Jedes davon bietet dem Agenten mehr Anhaltspunkte für seine Entscheidungen. Beginnen Sie klein, erstellen Sie einen Feedbackkanal und lassen Sie den Agenten lernen, darauf zu hören.