Startseite / Artikel

Agentic RAG: Dem LLM die Entscheidung überlegen, wann und wo Daten abgerufen werden sollen.

Warum statische RAG bei mehrstufigen und zeitkritischen Fragen versagt und wie ein LangGraph-Agent zwischen Qdrant und Web-Suchen navigiert, inklusive Schutzmaßnahmen und Einschränkungen.

Verlag: REACTAPP.TOP9 Min. Lesezeit2167 Wörter

Quellen: Your RAG Pipeline Is Lying to You. The Problem Is Not the Embeddings — Agentic RAG

„RAG ist im Grunde ein bereits gelöstes Problem.“ Dieser Satz wird bei Teambesprechungen geäußert, und die ehrliche Antwort lautet: Es hängt davon ab, was man unter Informationsabruf versteht. Jeder, der bereits seit längerer Zeit RAG-Pipelines in der Praxis eingesetzt hat – sei es für Zusammenfassungen von Krankenakten, Workflows vom Natursprache- ins SQL-Format oder mehragentige Forschungssysteme – stößt in der Regel auf denselben Grenzbereich.

Der Grenzbereich liegen nicht in den Embeddings. Er liegt in der Annahme, dass der Informationsabruf nur einmal, an einer festgelegten Stelle, anhand einer einzigen Quelle erfolgt.

Static RAG (Einspeisen der Dokumente, Abrufen der Top-k-Ergebnisse, Übergeben an den LLM) bewältigt einfache, eigenständige Fragen mit stabilem internen Wissen gut. Sobald eine Frage mehrstufig wird, zeitkritisch ist oder sowohl interne Dokumente als auch aktuelle Daten erfordert, beginnt der statische Ablauf, zuverlässige Fehlinformationen zu erzeugen. Ein solcher Ablauf kann auf der Grundlage veralteter interner Richtlinien eine klinische Vorgehensweise empfehlen, obwohl eine schnelle halbstündige Suche im Internet die Änderung bereits aufgezeigt hätte.

Bessere Embeddings werden das nicht beheben. Hilfreich ist es, dem LLM zu ermöglichen, selbst zu entscheiden, wann und wo Daten abgerufen werden sollen.

Das Problem mit „einmal abrufen, immer antworten“

Stellen Sie sich einen Bibliothekar vor, der erst nachdem Sie Ihren Satz beendet haben, nur zu einem einzigen Regal gehen kann – immer derselben Stelle –, um eine Antwort zu geben.

Fragen Sie „Welche Nebenwirkungen hat Metformin?“ – und das System liefert sofort eine Antwort. Fragen Sie nun etwas Schwierigeres: Welche Wechselwirkungen mit anderen Medikamenten hat die FDA kürzlich für Metformin in Kombination mit neueren GLP-1-Medikamenten hervorgehoben, und deckt das aktuelle Patientenprotokoll diese ab? Dafür sind interne Dokumente sowie aktuelle regulatorische Daten erforderlich, zusammen mit einer Analyse der Lücken dazwischen. Statisches RAG zieht einmalig Daten aus einer Quelle ab, während das LLM die erhaltenen Informationen nur oberflächlich verarbeitet.

Das ist keine Hypothese. In einer internen Bewertung eines Gesundheitsversorgungsprozesses [1] erreichte statisches RAG bei stabilen Fragen eine Antwortrelevanz von 89 %, fiel aber auf 61 %, wenn eine Frage die Abgleichung interner klinischer Dokumente mit externen Behandlungsleitlinien erforderte. Grundlegende Bewertungen übersehen dies, weil die Antworten plausibel klingen – sie sind einfach veraltet.

Die Lösung lässt sich leicht beschreiben, ist aber schwierig umzusetzen: Die Informationsbeschaffung wird zu einer Entscheidung statt zu einem Schritt.

Was agentic RAG bedeutet

In agentic RAG beschafft das LLM die Informationen nicht an einem vorgegebenen Punkt. Es betrachtet die Informationsbeschaffung als Werkzeug, ähnlich wie einen Taschenrechner oder einen Codeausführer, und nutzt es, wenn es die vorliegenden Beweise für unzureichend hält. Daraus ergeben sich drei Verhaltensweisen, die ein statischer Ablauf nicht ermöglichen kann.

  • Bedingte Informationsbeschaffung. Wenn der Kontext bereits die Frage beantwortet, überspringt der Agent die Informationsbeschaffung ganz.
  • Mehrschrittige Informationsbeschaffung. Der Agent holt etwas ab, liest es, erkennt, dass ein Teil fehlt, und geht erneut danach suchen, wobei jeder Schritt von den Ergebnissen des vorherigen beeinflusst wird. So recherchieren Menschen auch.
  • Quellrouten. Der Agent wählt je nach Frage zwischen einem Vektorlagern, einer Websuche oder einer strukturierten Datenbank aus. „Was besagt unsere Richtlinie?“ führt zum Vektorlager, während „Was hat die FDA letzte Woche bekannt gegeben?“ zur Websuche führt.
  • Keine dieser Ideen ist neu. Der Artikel „ReAct“ von Yao und Kollegen (2022) beschreibt das Muster des Abwechselns von Schlussfolgerungs Schritten mit Toolaufrufen, und agierendes RAG ist ReAct in Kombination mit der Informationsabrufung [2].

    Die jüngste Veränderung liegt in den Werkzeugen. LangGraph macht endlich stateful Schleifen mit Verzweigungen praktikabel. Tavily bietet eine für Agenten konzipierte Websuche-API, die Inhalt bereitstellt, der für Large Language Models geeignet ist, und gibt an, mehr als 100 Millionen Anfragen pro Monat mit einer p50-Suchzeit von 180 ms bewältigen zu können [3]. Weitere Details finden Sie im Beitrag „Tavily 101“.

    Drei bewegliche Komponenten

    Stellen Sie sich einen Detektiv statt einen Bibliothekar vor. Eine Bibliothekarin holt auf Anweisung etwas. Ein Detektiv liest, was vorhanden ist, ermittelt, was fehlt, holt es, liest erneut und setzt so fort, bis eine Schlussfolgerung vertretbar ist. Der Kreislauf besteht aus drei Komponenten.

    1. Der Router bestimmt, welche Art der Abrufung eine Anfrage erfordert. Das LLM betrachtet die Frage und entscheidet, ob der vorhandene Kontext ausreicht, ob Dokumente benötigt werden, ob das Internet benötigt wird oder beides.
    2. Die Abrufwerkzeuge sind die Quellen für Beweise. Zwei Werkzeuge sind das absolute Minimum: eines durchsucht einen Vektorlagerraum mit internen Dokumenten, das andere durchsucht das Internet nach aktuellen Informationen. Jedes ist eine gewöhnliche Funktion, die das LLM mit einer Abfragesuche aufruft.
  • Die Überprüfung der Grundlage fragt nach, ob die Beweise ausreichen. Bevor die endgültige Antwort verfasst wird, bewertet der Agent, ob das Erhaltene ausreichend und relevant ist; falls nicht, holt er erneut Daten. Statische Pipelines können diese Schleife nicht darstellen.
  • Eine funktionierende Implementierung

    Hier ist ein hybrider Agent in LangGraph, der zwischen einem Qdrant-Vektorlagern und der Tavily-Websuche wechselt. Er wählt das passende Werkzeug, kann beide nacheinander aufrufen und holt erneut Daten, wenn das erste Ergebnis nicht ausreicht, um die Antwort zu stützen. Der Qdrant-Teil folgt dem LangChain-Integrationstool.

    from typing import Annotated, Literal
    from langgraph.graph import StateGraph, START, END
    from langgraph.prebuilt import ToolNode
    from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
    from langchain_core.tools import tool
    from langchain_openai import ChatOpenAI
    from langchain_qdrant import QdrantVectorStore
    from langchain_openai import OpenAIEmbeddings
    from tavily import TavilyClient
    from qdrant_client import QdrantClient
    import operator
    from typing import TypedDict, Sequence
    
    # --- State definition ---
    class AgentState(TypedDict):
        messages: Annotated[Sequence, operator.add]
        retrieval_count: int  # guard against infinite loops
    
    # --- Tool 1: Vector store retrieval (internal docs) ---
    qdrant_client = QdrantClient(url="http://localhost:6333")
    embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
    vector_store = QdrantVectorStore(
        client=qdrant_client,
        collection_name="internal_docs",
        embedding=embeddings,
    )
    @tool
    def retrieve_from_docs(query: str) -> str:
        """
        Search internal document knowledge base.
        Use for questions about internal policies, protocols, or proprietary knowledge.
        """
        results = vector_store.similarity_search(query, k=4)
        if not results:
            return "No relevant internal documents found for this query."
        return "\n\n---\n\n".join([
            f"[Source: {doc.metadata.get('source', 'internal')}]\n{doc.page_content}"
            for doc in results
        ])
    
    # --- Tool 2: Web retrieval (live data) ---
    tavily = TavilyClient(api_key="YOUR_TAVILY_API_KEY")
    @tool
    def retrieve_from_web(query: str) -> str:
        """
        Search the live web for current information.
        Use for recent news, regulatory updates, or anything that may have changed
        since the internal knowledge base was last updated.
        """
        results = tavily.search(
            query=query,
            max_results=5,
            search_depth="advanced",  # deep retrieval for better accuracy
        )
        if not results.get("results"):
            return "No relevant web results found."
        return "\n\n---\n\n".join([
            f"[Source: {r['url']}]\n{r['content']}"
            for r in results["results"]
        ])
    
    # --- Agent node ---
    tools = [retrieve_from_docs, retrieve_from_web]
    tool_node = ToolNode(tools)
    llm = ChatOpenAI(model="gpt-4o", temperature=0).bind_tools(tools)
    SYSTEM_PROMPT = """You are a research assistant with access to two retrieval tools:
    - retrieve_from_docs: internal document knowledge base (policies, protocols, proprietary data)
    - retrieve_from_web: live web search (recent news, regulatory updates, real-time information)
    For any question:
    1. Decide whether you need to retrieve information or can answer from context.
    2. If retrieval is needed, choose the right source - or call both if the question spans internal and live knowledge.
    3. After retrieving, check if the evidence is sufficient. If not, retrieve again with a refined query.
    4. Always cite your sources in the final answer.
    Do not hallucinate. If you cannot find sufficient evidence, say so explicitly."""
    def call_agent(state: AgentState) -> AgentState:
        messages = [SystemMessage(content=SYSTEM_PROMPT)] + list(state["messages"])
        response = llm.invoke(messages)
        return {
            "messages": [response],
            "retrieval_count": state.get("retrieval_count", 0)
        }
    def update_retrieval_count(state: AgentState) -> AgentState:
        return {
            "messages": [],
            "retrieval_count": state.get("retrieval_count", 0) + 1
        }
    
    # --- Routing logic ---
    MAX_RETRIEVALS = 3  # prevent infinite loops
    def should_continue(state: AgentState) -> Literal["tools", "end"]:
        last_message = state["messages"][-1]
        # If the LLM called a tool and we haven't hit the loop guard, continue
        if hasattr(last_message, "tool_calls") and last_message.tool_calls:
            if state.get("retrieval_count", 0) < MAX_RETRIEVALS:
                return "tools"
        return "end"
    
    # --- Build the graph ---
    graph_builder = StateGraph(AgentState)
    graph_builder.add_node("agent", call_agent)
    graph_builder.add_node("tools", tool_node)
    graph_builder.add_node("count_retrieval", update_retrieval_count)
    graph_builder.add_edge(START, "agent")
    graph_builder.add_conditional_edges(
        "agent",
        should_continue,
        {"tools": "tools", "end": END}
    )
    graph_builder.add_edge("tools", "count_retrieval")
    graph_builder.add_edge("count_retrieval", "agent")
    agent = graph_builder.compile()
    
    # --- Run it ---
    if __name__ == "__main__":
        result = agent.invoke({
            "messages": [
                HumanMessage(content=(
                    "What does our current patient protocol say about GLP-1 usage, "
                    "and are there any FDA safety updates issued in 2025 we should be aware of?"
                ))
            ],
            "retrieval_count": 0,
        })
        print(result["messages"][-1].content)
    

    Einige Details verdienen Aufmerksamkeit.

    Der Schutzmechanismus MAX_RETRIEVALS in der Routing-Logik ist keine bloße Dekoration. Ohne ihn können Agenten bei einer unklaren Frage in insgesamt 15 Abfragen geraten, wodurch Tokens und Geduld verschwendet werden. In der Regel reichen drei Abfragen aus. Falls nicht, ist die Frage zu allgemein oder die Tools liefern unbrauchbare Ergebnisse.

    Das Feld retrieval_count im Zustand sorgt dafür, dass der Schleifenaufruf beendet werden kann. LangGraphs StateGraph überträgt diesen Wert bei jeder Knotenübergang, sodass er während des gesamten Gesprächsverlaufs erhalten bleibt und nicht nur innerhalb eines einzigen Zyklus.

    Auch der Systemprompt hat große Bedeutung. Dem Modell mitzuteilen, wann welche Tools verwendet werden sollen – und nicht nur, dass Tools vorhanden sind – unterscheidet einen Agenten, der vernünftig routet, von einem, der aus Faulheit für alles retrieve_from_web aufruft.

    Wenn agierende RAG-Systeme die Situation verschlimmern

    Es ersetzt den statischen RAG nicht. Es ersetzt ihn nur bei bestimmten Problemformen und führt in vier Fällen zu negativen Auswirkungen.

    • Die Latenz ist die Hauptbeschränkung. Jeder Durchlauf durch die Schleife kostet einen zusätzlichen Modellaufruf sowie einen Toolaufruf. Für einen einfachen Q&A-Assistenten, bei dem die p95-Latenz unter einer Sekunde bleiben muss, ist ein Agent, der zwei oder drei Mal durchlaufen muss, keine gute Lösung. Ein gut abgestimmter statischer Retriever ist schneller und ausreichend.
    • Die Wissensbasis ist stabil und selbstständig. Wenn alle von den Nutzern gestellten Fragen zuverlässig aus dem Vektorlagern beantwortet werden können, fügt man unnötig Komplexität hinzu. Messen Sie zunächst den statischen Ablauf mit RAGAS (Paper). Wenn die Kontextwiederholungsrate über 85 % und die Genauigkeit der Antworten über 90 % liegt, ist der agentebasierte Wrapper überflüssig.
  • Man kann den Werkzeugauswahlmechanismus des Modells nicht vertrauen. Mit sorgfältig formulierten Systemanweisungen erreichen GPT-4o und Claude Sonnet laut denselben internen Messungen zu etwa 88 Prozent der Zeit eine korrekte Auswahl zwischen Dokumenten- und Webabfragen. Die verbleibenden 12 Prozent führen zu Quellenfehlern, die schwieriger zu beheben sind als einfache Halluzinationen, da die Antwort zwar fundiert ist, aber einfach am falschen Ort steht. Bei schwächeren Modellen muss die Werkzeugauswahl vor dem Einsatz überprüft werden.
  • Zeitnahe Daten sind nicht vertrauenswürdig. Webabfragen erweitern die Angriffsfläche. Tavily filtert in seiner API nach Prompt-Injection-Versuchen, doch kein Filter ist perfekt; daher sollte alles, was ein Agent auf Basis von Webinhalten tut, diese Inhalte als potenziell feindlich betrachten.
  • Wählen Sie agierendes RAG, wenn das Wissen aus mehreren Quellen stammt und einige davon sich ändern. Beginnen Sie mit statischem RAG und aktualisieren Sie es, wenn die RAGAS-Werte einen spezifischen Fehler aufzeigen, den dieses Design behebt.

    Wo anzufangen

    Kleinster Schritt, unter fünf Minuten: Fügen Sie mit from langchain_community.tools.tavily_search import TavilySearchResults ein Tavily-Tool zu einem vorhandenen LangChain-Agenten hinzu. LangGraph ist noch nicht notwendig. Machen Sie das Tool zugänglich, schreiben Sie einen Systemprompt, der angibt, wann es aufgerufen werden soll, führen Sie fünf Testabfragen durch und beobachten Sie, welche Ergebnisse ausgewählt werden.

    Nächster Schritt, etwa eine Nachmittagsstunde: Weisen Sie das oben gezeigte LangGraph-Beispiel auf Ihren eigenen Vektor-Speicher hin und behalten Sie beide Tools bei. Lassen Sie den Abrufzähler zunächst aus, um zu sehen, wie oft der Agent wiederholt abruft, fügen Sie ihn anschließend hinzu. Der Unterschied lehrt viel.

    Fortgeschritten: Fügen Sie nach jedem Tool-Ergebnis und vor dem nächsten Schritt des Agents einen Bewertungsschritt ein. Ein kleinerer, günstigerer LLM-Aufruf beantwortet mit ja oder nein, ob der abgerufte Kontext ausreicht, und erst danach erstellt das Hauptmodell seine Antwort. Dadurch wird die Kontextnutzung von „in Tests ausreichend“ auf „in der Produktion zuverlässig“ erhoben.

    Tool-Routing-Fehler, die 12 % mehr auftreten, sind der Hauptgrund für die meisten Debugging-Aufwände in der Produktion. Instrumentieren Sie diese frühzeitig, damit der Bewertungsknoten sie vor den Nutzern erkennt.

    Quellen

    [1] Interne Bewertungen an einem Gesundheits-RAG-Pipeline-System unter Verwendung von RAGAS 0,1.x anhand einer sorgfältig ausgewählten Sammlung von 200 klinischen Abfragen. Keine veröffentlichte Studie.

    [2] Yao et al. (2022), ReAct: Synergizing Reasoning and Acting in Language Models, arXiv:2210.03629.

    [3] Tavily-Blog, Tavily 101, mit den Angaben zu Latenz und Datenmenge wie auf der Produktseite im Mai 2026 angegeben.

    Verwandte Artikel

  • grep Beat Vector Search in Agent Memory Tests, and the Harness Mattered as Much — Eine Studie von 116 LongMemEval-Fragen zeigte, dass grep vor der Vektorabrufung vorn lag, doch die Art und Weise, wie das Framework Ergebnisse liefert, beeinflusste die Genauigkeit genauso stark wie der Abrufmechanismus selbst. Was man daraus lernen kann.
  • Expert RAG: HyDE, Compression, Self-Querying and Agentic Workflows — Vier fortschrittliche Abruftechniken für produktive RAG-Lösungen in Haystack: HyDE, kontextuelle Kompression, Selbstabfragen sowie agentebasierte Workflows – inklusive Angaben dazu, wann welche Technik zum Einsatz kommt.
  • Schicht für Schicht: Entwurf eines echten RAG-Auswertungspipelines für KI-Agenten — Planen Sie einen RAG-Pipeline mit den Kontrakten SourceDocument und DocumentChunk, Chunking, Embeddings sowie ChromaDB, alles hinter einem RetrievalService, der die Stabilität des Agenten gewährleistet.
  • Ein RAG-Projekt ist kein einziger riesiger Python-Datei: Eine skalierbare Ordnerstruktur — Teilen Sie eine Demo-RAG-Anwendung in Bereiche wie Eingabe, Chunking, Embeddings, Vektorlager, Auswertung, Prompts, LLM-Client, API, Tests und Logs auf, damit sie über die Anfangsphase hinaus wachsen kann.
  • Praktische Hinweise: Warum Ihr RAG-Pipeline einen Re-Ranker benötigt (und wie) – Schritt-für-Schritt-Anleitung zu den Praktischen Hinweisen: Warum Ihr RAG-Pipeline einen Re-Ranker benötigt (und wie), inklusive Verträgen, Überprüfungen sowie Code-Blöcken für Teams, die dieses Muster einsetzen.