RAG vs MCP: So wählst du das richtige Muster für LLM-Anwendungen
Große Sprachmodelle entfalten ihren größten Nutzen, wenn du für eine Aufgabe die passende Strategie wählst. Grob betrachtet gibt es zwei verbreitete Muster, auf die man dabei meist zurückgreift: Retrieval-Augmented Generation (RAG) und das Model Context Protocol (MCP). RAG setzt darauf, Antworten in vorhandenen Inhalten wie Dokumenten, Handbüchern oder Wissensdatenbanken zu verankern. MCP hingegen zielt darauf ab, einem Modell die Fähigkeit zu geben, aktuelle Daten abzurufen oder Handlungen auszuführen – über Tools, APIs und automatisierte Workflows.
Wenn du nachvollziehbare Antworten brauchst, die klar an strukturiertes Wissen gebunden sind, ist RAG in der Regel die passende Wahl. Wenn dein Anwendungsfall dagegen frische Daten oder direkte Systeminteraktionen verlangt, erweitert MCP das Modell um genau diese Fähigkeiten. In der Praxis kombinieren viele reale Lösungen beide Ansätze: RAG liefert Kontext und Begründung, MCP führt eine Aktion aus, und RAG wird anschließend erneut genutzt, um das Ergebnis sauber, verständlich und begründet an den Nutzer zurückzugeben.
Dieser Leitfaden liefert praxisnahe Hinweise und Entscheidungsregeln dafür, wann welcher Ansatz sinnvoll ist – inklusive typischer Stolpersteine. Außerdem wird gezeigt, warum RAG → MCP → RAG-Abläufe häufig das Fundament produktionsreifer Systeme bilden.
Wichtigste Erkenntnisse
- Zwei ergänzende Interaktionsmuster: Die meisten LLM-Anwendungsfälle lassen sich entweder als Wissensabruf (RAG) oder als toolbasierte Aktion (MCP) einordnen.
- RAG ist stark bei Nachschlage- und Grounding-Aufgaben: Nutze RAG bei statischem oder semi-statischem Wissen, wenn du Zitate, Belege und klare Faktentransparenz brauchst.
- MCP ermöglicht Aktionen in Echtzeit: MCP ist ideal, wenn APIs, Datenbanken oder Workflows ins Spiel kommen, die aktuelle Daten und Zustandsänderungen erfordern.
- Beide Ansätze haben Risiken: RAG kann unter veralteten Inhalten, Chunking-Problemen oder zu viel Prompt-Ballast leiden. MCP wird riskant, wenn Tools unklar beschrieben sind, Tool-Aufrufe in Schleifen geraten oder Nebenwirkungen potenziell unsichere Aktionen auslösen.
- Viele Produktionssysteme kombinieren beides: Wissen wird per RAG geholt, Aktionen werden per MCP ausgeführt, und RAG liefert anschließend eine begründete Erklärung des Ergebnisses.
- Hybride Abläufe sind häufig: Viele reale Deployments nutzen eine kombinierte Strecke – zuerst Retrieval mit RAG, dann Action mit MCP, danach zurück zu RAG für Explain-and-Justify.
Voraussetzungen
- Grundverständnis von LLMs: Du solltest wissen, was große Sprachmodelle sind und wie sie Eingaben und Ausgaben verarbeiten.
- Sicher im Umgang mit APIs und Datenbanken: Du solltest verstehen, warum Tool-Calls, strukturierte Quellen und APIs in Systemdesigns eine Rolle spielen.
- Grundkenntnisse zu Retrieval-Konzepten: Ein funktionierendes Verständnis von Suche, Indexing, Embeddings, TF-IDF oder zumindest die Begriffe hilft, den RAG-Teil besser nachzuvollziehen.
- Programmierkenntnisse (Python): Da der Leitfaden Beispielcode enthält (Retriever, Tool-Registries, Dataclasses), ist es hilfreich, Python-Code lesen zu können.
- Systemdenken: Du solltest bereit sein, Trade-offs, Failure-Modes und hybride Flows zu bewerten, wenn du praxisnahe KI-Systeme baust.
RAG und MCP verstehen
Bevor man die beiden Ansätze gegeneinander abwägt, ist es sinnvoll, ihre Bedeutung klar zu definieren.
Retrieval-Augmented Generation (RAG)
RAG ergänzt ein LLM um einen Retrieval-Schritt, zum Beispiel über eine Suchmaschine oder eine Vektor-Datenbank. Inhalte werden dafür indexiert, indem Dokumente in Chunks aufgeteilt, Embeddings erzeugt und anschließend im Index gespeichert werden. Kommt eine Anfrage rein, werden die relevantesten Chunks aus dem Index geholt und dem LLM als zusätzlicher Kontext mitgegeben. Das Modell erstellt dann eine Antwort, die auf diesen abgerufenen Informationen basiert.
Model Context Protocol (MCP)
MCP ist ein formales Contract-Muster, um externe Tools und Datenquellen an ein Modell anzubinden. In einem MCP-basierten Setup werden Tools (Funktionen, APIs, Datenbankabfragen und mehr) zusammen mit ihren Interfaces registriert – typischerweise mit Tool-Name und JSON-Schema für Input und Output. Wenn ein Modell eine Aufgabe erhält, kann es ein Tool auswählen und einen strukturierten Aufruf ausgeben (zum Beispiel als JSON mit Parametern). Ein Host-System überwacht diese Tool-Calls, führt die passende Funktion oder API-Anfrage aus und liefert das Ergebnis zurück an das Modell.
Wann RAG die beste Wahl ist
Als Faustregel solltest du RAG immer dann als Standardlösung betrachten, wenn die benötigte Information bereits in Dokumentationen vorhanden oder relativ stabil ist. RAG ist sinnvoll, wenn:
- die Antwort bereits in einer statischen oder semi-statischen Wissensquelle steckt – etwa in Richtlinien, Produktspezifikationen, Runbooks oder wissenschaftlichen Papern.
- du Nachvollziehbarkeit und belegbasierte Ausgaben brauchst. Mit RAG kann das Modell problemlos Quellen referenzieren oder exakt auf das Dokument und die Stelle verweisen, die die Aussage stützt.
- ultra-niedrige Latenz keine harte Vorgabe ist (im Rahmen). Wenn dein Use Case ein wenig Verzögerung toleriert und nicht für jede Anfrage Live-APIs aufrufen muss, ist RAG eine starke Option.
Wann MCP die beste Wahl ist
MCP wird besonders wertvoll, wenn statische Dokumentation allein nicht ausreicht. Du solltest MCP (also einen toolbasierten Ansatz) wählen, wenn:
- du aktuelle oder dynamische Daten brauchst, die nicht in Dokumenten enthalten sind. Wenn die Informationen hinter einer API oder Datenbank liegen und sich im Zeitverlauf verändern – etwa Lagerbestand, Wetter oder Accountdaten – sollte das Modell ein Tool nutzen, um den neuesten Stand abzurufen.
- du willst, dass das Modell eine Aktion ausführt statt nur eine Antwort zu geben. Beispiele sind das Erstellen eines Support-Tickets, das Versenden einer Onboarding-Mail oder das Auslösen einer Nachbestellung.
- du mehrstufige Workflows benötigst. Mit MCP kann das Modell Tool-Call-Sequenzen planen und ausführen, zum Beispiel indem es das Ergebnis eines API-Calls nutzt, um zu entscheiden, ob ein weiterer Call notwendig ist.
Typische Fehlerbilder, die du einplanen solltest
Sowohl RAG als auch MCP bringen vorhersehbare Failure-Modes mit. Wer sie früh kennt, kann robuste Systeme bauen. Im Folgenden sind häufige Probleme aufgeführt, die man berücksichtigen sollte.
RAG Failure Modes
- Veraltete oder fehlende Inhalte: Wenn der Dokumentenkorpus nicht aktuell ist oder die benötigte Information gar nicht enthält, kann Retrieval diese Antwort nicht „herbeizaubern“.
- Chunking- und Recall-Probleme: RAG-Systeme teilen Dokumente oft in kleinere Teile. Wenn ein relevanter Fakt über mehrere Chunks verteilt ist oder die Query andere Begriffe/Synonyme nutzt als der gespeicherte Text, kann der Retriever die passende Passage verfehlen.
- Überladener Kontext: Werden zu viele Chunks in den Prompt gepackt – besonders über das Kontextlimit hinaus oder mit viel irrelevantem Text – kann die Modellleistung deutlich sinken.
MCP Failure Modes
- Schwache Tool-Definitionen: Wenn Tool-Namen zu ungenau sind, Beschreibungen schlecht formuliert sind oder Schemas unsauber definiert sind, kann das Modell Tools falsch nutzen oder das richtige Tool nicht auswählen.
- Planungsschleifen und Tool-Missbrauch: Ohne Begrenzungen kann ein Modell in Endlosschleifen geraten oder Tools zyklisch aufrufen, wenn es unsicher ist, wie es ein Problem lösen soll – insbesondere wenn Tool-Responses nicht weiterhelfen und keine Guardrails weitere Versuche stoppen.
- Sicherheit und unbeabsichtigte Nebenwirkungen: Wenn ein Modell Aktionen ausführen darf, müssen Side-Effects mitgedacht werden. Wenn ein Tool ohne Autorisierung auf private Daten zugreifen oder privilegierte Aktionen ausführen kann, besteht Missbrauchs- oder Fehlbedienungsrisiko.
Diese Failure-Modes beeinflussen direkt die Schutzmaßnahmen im Design. Bei RAG heißt das: eine hochwertige Knowledge Base aufbauen und pflegen und passende Retrieval-Strategien nutzen. Bei MCP heißt das: klare Schemas, eindeutige Tool-Definitionen und Usage-Policies bereitstellen – plus Guardrails, um riskante Aktionen zu begrenzen.
RAG oder MCP wählen
Hier sind kurze Faustregeln, mit denen du für ein neues Feature oder eine Nutzeranfrage entscheiden kannst, womit du starten solltest:
- Wenn sich die Anfrage durch Lesen vorhandener Texte lösen lässt, nutze RAG. Frage dich: „Ist diese Information irgendwo bereits dokumentiert, und hat das System Zugriff darauf?“ Wenn ja, ist es sehr wahrscheinlich ein Retrieval-Problem.
- Wenn die Anfrage Live-Daten oder eine Aktion erfordert, nutze MCP. Wenn ein Mensch den Wunsch erfüllen würde, indem er in einer Datenbank nachschaut, eine API abfragt oder in einem System einen Button klickt, ist das ein sehr starkes Signal für einen Tool-Ansatz.
- Wenn Wissen und Aktion zusammen nötig sind, wähle einen hybriden Flow. Viele reale Probleme brauchen beides: zuerst wird eine Policy oder Regel per Retrieval geholt, dann wird eine Aktion ausgeführt, und anschließend muss das Ergebnis erklärt werden. Das Muster RAG → MCP → RAG ist dafür ein häufiges Rezept.
RAG vs MCP Demo: Bookstore Operations Assistant
Diese Demo zeigt zwei Wege, um Nutzerfragen zum Betrieb einer Buchhandlung zu beantworten. Beim RAG-Ansatz wird die Antwort aus einem statischen Policy-„Handbook“ geholt. Beim MCP-ähnlichen Tool-Ansatz wird eine Live-Funktion aufgerufen, um Lagerstatus zu prüfen oder eine Aktion auszuführen. In diesem Beispiel wird RAG über einen einfachen TF-IDF-Retriever über statische Policy-Texte umgesetzt. Die „MCP“-Tools werden durch einfache Python-Funktionen simuliert (es wird kein echtes LLM verwendet).
Setup & Imports
„““
Run locally:
python -m pip install –upgrade pip
pip install gradio scikit-learn numpy pandas
python RAG_vs_MCP_Demo_App.py
„““
from __future__ import annotations
import re
import json
from dataclasses import dataclass, asdict
from typing import Any, Callable, Dict, List, Tuple, Optional
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import gradio as gr
Static Handbook Retrieval (RAG)
Der RAG-Teil behandelt das Policy-„Handbook“ als kleine Wissensdatenbank. Der Code speichert vier Policy-Dokumente (Returns, Shipping, Membership, Gift Cards) in DOCS. Anschließend wird ein TfidfVectorizer(stop_words="english") auf diese Texte trainiert, um eine Term-Dokument-Matrix zu erzeugen.
TF-IDF (Term Frequency–Inverse Document Frequency) ist eine numerische Methode, die Begriffe danach gewichtet, wie aussagekräftig sie innerhalb eines Korpus sind. Die Nutzerfrage wird mit demselben TF-IDF-Modell transformiert, anschließend wird die Cosine Similarity zwischen Query-Vektor und Dokumentvektoren berechnet.
# ------------------------------
# 1) Tiny knowledge base (handbook) for RAG
# ------------------------------
DOCS = [
{
"id": "policy_returns",
"title": "Returns & Refunds Policy",
"text": (
"Customers can return most new, unopened items within 30 days of delivery for a full refund. "
"Items must be in their original condition with receipt. Refunds are processed to the original payment method. "
"Defective or damaged items are eligible for free return shipping."
),
},
{
"id": "policy_shipping",
"title": "Shipping Policy",
"text": (
"Standard shipping typically takes 3 to 5 business days. Expedited shipping options are available at checkout. "
"International orders may take 7 to 14 business days depending on destination and customs."
),
},
{
"id": "policy_membership",
"title": "Membership Benefits",
"text": (
"Members earn 2 points per dollar spent, get early access to new releases, and receive a monthly newsletter with curated picks. "
"Points can be redeemed for discounts at checkout."
),
},
{
"id": "policy_giftcards",
"title": "Gift Cards",
"text": (
"Gift cards are available in denominations from $10 to $200 and are redeemable online or in-store. "
"They do not expire and cannot be redeemed for cash except where required by law."
),
},
]
# Fit a very small TF-IDF retriever at startup
VECTORIZER = TfidfVectorizer(stop_words="english")
KB_TEXTS = [d["text"] for d in DOCS]
KB_MATRIX = VECTORIZER.fit_transform(KB_TEXTS)
def rag_retrieve(query: str, k: int = 3) -> List[Dict[str, Any]]:
"""Return top-k documents as {id,title,text,score}."""
if not query.strip():
return []
q_vec = VECTORIZER.transform([query])
sims = cosine_similarity(q_vec, KB_MATRIX)[0]
idxs = np.argsort(-sims)[:k]
results = []
for i in idxs:
results.append({
"id": DOCS[i]["id"],
"title": DOCS[i]["title"],
"text": DOCS[i]["text"],
"score": float(sims[i]),
})
return results
def rag_answer(query: str, k: int = 3) -> Tuple[str, List[Dict[str, Any]]]:
"""Simple, template-y answer based on top-k docs."""
hits = rag_retrieve(query, k=k)
if not hits:
return ("I couldn't find anything relevant in the handbook.", [])
# Compose a short grounded answer using snippets
bullets = []
for h in hits:
# Take the first sentence as a snippet
first_sentence = h["text"].split(".")[0].strip()
if first_sentence:
bullets.append(f"- **{h['title']}**: {first_sentence}.")
answer = (
"**Handbook says:**\n" + "\n".join(bullets) +
"\n\n(Answer generated from retrieved policy snippets; no LLM used.)"
)
return answer, hits
So funktioniert das RAG-Retrieval
Der RAG-Retrieval-Prozess läuft nach diesen Schritten ab:
- Policy-Texte indexieren: Jeder Abschnitt aus dem Handbook wird in einen TF-IDF-Vektor umgewandelt.
- Query abbilden: Die Nutzerfrage wird mit demselben Vectorizer in einen TF-IDF-Vektor transformiert.
- Ähnlichkeit berechnen: Zwischen dem Query-Vektor und allen Policy-Dokument-Vektoren wird die Cosine Similarity ermittelt.
- Top-Dokumente auswählen: Die
kDokumente mit den höchsten Similarity-Scores werden als relevanteste Treffer genommen.
Das ist eine schlanke Ersatzlösung für eine vollständige RAG-Pipeline, die üblicherweise neuronale Embeddings einsetzen würde. Nachdem die passendsten Handbook-Policies gefunden wurden, baut rag_answer() daraus eine kurze, begründete Antwort. Für jedes abgerufene Dokument wird der erste Satz aus dem Policy-Text gezogen und als Bullet Point zusammen mit dem Policy-Titel formatiert.
MCP-Style Tools und hybrides Routing in einer RAG-vs-MCP-Demo
MCP-Style Tools: Live Inventory
In der Demo steht der „MCP“-Teil dafür, wie ein Assistant Funktionen aufrufen kann, die mit Echtzeit-Inventardaten arbeiten.
# ------------------------------
# 2) MCP-style tool registry + client executor
# ------------------------------
@dataclass
class ToolParam:
name: str
type: str # e.g., "string", "number", "integer"
description: str
required: bool = True
@dataclass
class ToolSpec:
name: str
description: str
params: List[ToolParam]
@dataclass
class ToolCall:
tool_name: str
args: Dict[str, Any]
@dataclass
class ToolResult:
tool_name: str
args: Dict[str, Any]
result: Any
ok: bool
error: Optional[str] = None
# In-memory "live" inventory
INVENTORY: Dict[str, Dict[str, Any]] = {
"Dune": {"stock": 7, "price": 19.99},
"Clean Code": {"stock": 2, "price": 25.99},
"The Pragmatic Programmer": {"stock": 5, "price": 31.50},
"Deep Learning": {"stock": 1, "price": 64.00},
}
# Define actual tool functions
def tool_get_inventory(title: str) -> Dict[str, Any]:
rec = INVENTORY.get(title)
if not rec:
return {"title": title, "found": False, "message": f"'{title}' not in inventory."}
return {"title": title, "found": True, **rec}
def tool_set_price(title: str, new_price: float) -> Dict[str, Any]:
rec = INVENTORY.get(title)
if not rec:
return {"title": title, "updated": False, "message": f"'{title}' not in inventory."}
rec["price"] = float(new_price)
return {"title": title, "updated": True, **rec}
def tool_place_order(title: str, quantity: int) -> Dict[str, Any]:
rec = INVENTORY.get(title)
if not rec:
return {"title": title, "ordered": False, "message": f"'{title}' not in inventory."}
if quantity <= 0:
return {"title": title, "ordered": False, "message": "Quantity must be positive."}
rec["stock"] += int(quantity)
return {"title": title, "ordered": True, "added": int(quantity), **rec}
# Registry of specs (like MCP manifests)
TOOL_SPECS: Dict[str, ToolSpec] = {
"get_inventory": ToolSpec(
name="get_inventory",
description="Get stock and price for a given book title.",
params=[
ToolParam("title", "string", "Exact book title"),
],
),
"set_price": ToolSpec(
name="set_price",
description="Update the price for a book title.",
params=[
ToolParam("title", "string", "Exact book title"),
ToolParam("new_price", "number", "New price in dollars"),
],
),
"place_order": ToolSpec(
name="place_order",
description="Increase stock by ordering more copies.",
params=[
ToolParam("title", "string", "Exact book title"),
ToolParam("quantity", "integer", "How many copies to add"),
],
),
}
# Mapping tool names to callables
TOOL_IMPLS: Dict[str, Callable[..., Any]] = {
"get_inventory": tool_get_inventory,
"set_price": tool_set_price,
"place_order": tool_place_order,
}
def validate_and_call(call: ToolCall) -> ToolResult:
spec = TOOL_SPECS.get(call.tool_name)
if not spec:
return ToolResult(tool_name=call.tool_name, args=call.args, result=None, ok=False, error="Unknown tool")
# minimal validation
for p in spec.params:
if p.required and p.name not in call.args:
return ToolResult(tool_name=call.tool_name, args=call.args, result=None, ok=False, error=f"Missing param: {p.name}")
try:
fn = TOOL_IMPLS[call.tool_name]
result = fn(**call.args)
return ToolResult(tool_name=call.tool_name, args=call.args, result=result, ok=True)
except Exception as e:
return ToolResult(tool_name=call.tool_name, args=call.args, result=None, ok=False, error=str(e))
Das Skript hält eine In-Memory-Sammlung von Büchern vor, bei der jedes Buch einen Lagerbestand und einen Preis besitzt. Darauf aufbauend gibt es drei Tool-Funktionen, mit denen sich das Inventar prüfen oder verändern lässt:
- get_inventory(title: str): Sucht ein Buch anhand seines Titels und liefert Bestand und Preis zurück – oder eine Meldung, wenn der Titel nicht vorhanden ist.
- set_price(title: str, new_price: float): Setzt den gespeicherten Preis eines Buches auf den angegebenen Wert.
- place_order(title: str, quantity: int): Erhöht den Bestand um die gewünschte Menge (als Simulation einer Nachbestellung).
Alle diese Tools werden in einer einfachen In-Memory-Tool-Registry registriert. Das ToolSpec jedes Tools enthält den Namen, eine Beschreibung und ein Parameter-Schema (Name, Typ, Beschreibung). Das entspricht dem Prinzip, wie MCP- oder Function-Calling-Systeme Tools über strukturierte Eingabedefinitionen beschreiben. In einer echten LLM-API würden diese Tools ebenso über JSON-Schemas veröffentlicht werden, die Felder wie title, new_price oder quantity definieren.
MCP „standardizes how tools are defined, hosted, and exposed to LLMs” und erleichtert es dem Modell, Tools zu finden und korrekt zu verwenden. In dieser Python-Demo werden dafür schlanke Dataclasses (ToolParam, ToolSpec und verwandte Typen) genutzt, um diese Schemas abzubilden.
Die Funktion validate_and_call() erhält einen vorgeschlagenen ToolCall (Tool-Name plus Argumente), führt eine minimale Validierung durch und ruft anschließend die zugehörige Python-Funktion auf. Zurück kommt ein ToolResult, das entweder die Tool-Ausgabe oder einen Fehler enthält. Das ist vergleichbar mit einem Backend, das in einem MCP-ähnlichen LLM-System einen Function-Call vom Modell annimmt und die passende API-Operation ausführt.
Routing von Anfragen: RAG vs Tools vs Both
Die Demo-App kann in drei Modi starten: Auto, RAG only oder Tools only. Im Auto-Modus entscheiden einfache Heuristiken, wie jede Query geroutet wird.
- Wenn die Query ein Policy-Keyword enthält, wird RAG-Retrieval aktiviert.
- Wenn die Query Begriffe wie „in stock“, „price“ oder „order“ zusammen mit einem zitierten Titel enthält, wird ein Tool-Call ausgelöst.
- Wenn beide Arten von Bedarf auftreten (z. B. „Dune“ kombiniert mit „return policy“), wird die Route auf „both“ gesetzt, sodass Retrieval und Tool-Call ausgeführt werden.
- Ansonsten fällt die Logik standardmäßig auf den einen oder anderen Modus zurück.
Das passt zur Grundidee: RAG ist für stabiles Wissens-Nachschlagen gedacht, während Tool-Calls für Live-Daten oder Aktionen besser geeignet sind.
# ------------------------------
# 3) Simple planner/router: choose RAG vs Tools (MCP-style) vs Both
# ------------------------------
TOOL_KEYWORDS = {
"get_inventory": ["in stock", "stock", "available", "availability", "have", "inventory"],
"set_price": ["change price", "set price", "update price", "price to", "discount", "mark down"],
"place_order": ["order", "restock", "add", "increase stock"],
}
BOOK_TITLE_PATTERN = r"'([^']+)'|\"([^\"]+)\"" # capture 'Title' or "Title"
def extract_titles(text: str) -> List[str]:
titles = []
for m in re.finditer(BOOK_TITLE_PATTERN, text):
titles.append(m.group(1) or m.group(2))
return titles
def decide_tools(query: str) -> Optional[ToolCall]:
q = query.lower()
titles = extract_titles(query)
# get_inventory
if any(kw in q for kw in TOOL_KEYWORDS["get_inventory"]):
if titles:
return ToolCall(tool_name="get_inventory", args={"title": titles[0]})
# set_price (look for a number)
if any(kw in q for kw in TOOL_KEYWORDS["set_price"]):
price_match = re.search(r"(\d+\.?\d*)", q)
if titles and price_match:
return ToolCall(tool_name="set_price", args={"title": titles[0], "new_price": float(price_match.group(1))})
# place_order (look for an integer quantity)
if any(kw in q for kw in TOOL_KEYWORDS["place_order"]):
qty_match = re.search(r"(\d+)", q)
if titles and qty_match:
return ToolCall(tool_name="place_order", args={"title": titles[0], "quantity": int(qty_match.group(1))})
return None
def route_query(query: str, mode: str = "Auto") -> str:
if mode == "RAG only":
return "rag"
if mode == "Tools only":
return "tools"
# Auto: detect whether we need tools, rag, or both
# If a single sentence includes both a policy question + inventory check, we'll call it "both".
needs_tool = decide_tools(query) is not None
needs_rag = any(ch in query.lower() for ch in ["policy", "return", "refund", "shipping", "membership", "gift card", "gift cards", "benefits"])
if needs_tool and needs_rag:
return "both"
if needs_tool:
return "tools"
return "rag"
In diesem Demo-Szenario gilt:
- Eine Frage wie „What is our returns policy?“ enthält „returns“ und triggert dadurch RAG-Retrieval.
- Eine Frage wie „Do we have ‘Dune’ in stock?“ enthält „in stock“ und einen zitierten Titel, wodurch das
get_inventory-Tool ausgewählt wird. - Wenn ein Nutzer beides kombiniert – „Do we have ‘Dune’ in stock, and what is our return policy?“ – wird die Route zu „both“ und die Antwort enthält sowohl Handbook-Inhalte als auch Live-Inventar-Daten.
Queries verarbeiten und die finale Antwort erstellen
Die Funktion handle_query(q, mode, show_trace) führt das oben beschriebene Routing aus und setzt daraus die finale Ausgabe zusammen.
# ------------------------------
# 4) Orchestrator: build a human-friendly answer + trace
# ------------------------------
def handle_query(query: str, mode: str = "Auto", show_trace: bool = True) -> Tuple[str, str, pd.DataFrame]:
route = route_query(query, mode=mode)
tool_trace: List[Dict[str, Any]] = []
rag_hits: List[Dict[str, Any]] = []
parts: List[str] = []
if route in ("rag", "both"):
rag_ans, rag_hits = rag_answer(query)
parts.append(rag_ans)
if route in ("tools", "both"):
call = decide_tools(query)
if call:
res = validate_and_call(call)
tool_trace.append(asdict(call))
tool_trace[-1]["result"] = res.result
tool_trace[-1]["ok"] = res.ok
if res.error:
tool_trace[-1]["error"] = res.error
# Compose a user-friendly tool result string
if res.ok and isinstance(res.result, dict):
if call.tool_name == "get_inventory":
if res.result.get("found"):
parts.append(
f"**Inventory:** '{res.result['title']}' -- stock: {res.result['stock']}, price: ${res.result['price']:.2f}"
)
else:
parts.append(f"**Inventory:** {res.result.get('message','Not found')}" )
elif call.tool_name == "set_price":
if res.result.get("updated"):
parts.append(
f"**Price updated:** '{res.result['title']}' is now ${res.result['price']:.2f}"
)
else:
parts.append(f"**Set price failed:** {res.result.get('message','Error')}" )
elif call.tool_name == "place_order":
if res.result.get("ordered"):
parts.append(
f"**Order placed:** Added {res.result['added']} copies of '{res.result['title']}'. New stock: {res.result['stock']}"
)
else:
parts.append(f"**Order failed:** {res.result.get('message','Error')}" )
else:
parts.append("Tool call failed.")
else:
parts.append("No suitable tool call inferred from your request.")
# Prepare trace artifacts
trace = {
"route": route,
"tool_calls": tool_trace,
"retrieved_docs": rag_hits,
}
# DataFrame for retrieved docs (for a quick visual)
df = pd.DataFrame([
{
"id": h["id"],
"title": h["title"],
"score": round(h["score"], 3),
"snippet": h["text"][:140] + ("..." if len(h["text"])>140 else ""),
}
for h in rag_hits
])
answer_md = "\n\n".join(parts) if parts else "(No answer composed.)"
trace_json = json.dumps(trace, indent=2)
return answer_md, trace_json, df
Der Ablauf ist grob so aufgebaut:
- RAG-Flow: Der RAG-Teil ruft
rag_answer(q)auf. Das liefert eine Markdown-Antwort plus die abgerufenen Dokumente. - Tool-Flow: Wenn Tools benötigt werden, nutzt die Logik
decide_tools(q), um einenToolCallabzuleiten. Danach führtvalidate_and_call()das Tool aus und liefert das Ergebnis zurück. - Antwort-Zusammenbau: Die RAG-Antwort und die Tool-Ausgabe werden mit Newlines zusammengesetzt. Bei „both“ sind beide Teile enthalten; andernfalls wird der nicht benötigte Part einfach weggelassen.
User Interface: Gradio Demo
In Gradio besteht die Oberfläche, die mit gr.Blocks gebaut wird, aus einem Titel und Hinweisen, einem Eingabe-Textboxfeld für die Nutzeranfrage, einem Dropdown zur Auswahl des Routing-Modus (Auto, RAG only, Tools only) sowie einem „Run“-Button. Unterhalb der Eingaben werden die Antwort (Markdown), der Trace (JSON) und eine Tabelle mit den abgerufenen Dokumenten angezeigt.
Da Gradio Webserver und Rendering übernimmt, konzentriert sich das Skript vor allem auf Routing und Antwortlogik. Sobald der Nutzer auf „Run“ klickt, wird handle_query() mit den eingegebenen Werten ausgeführt. Anschließend zeigt die UI die kombinierte Antwort sowie die Trace-Ausgabe. Wenn du das Skript startest, öffnet sich eine lokale Webseite, auf der du Anfragen eingeben und sofort Ergebnisse sehen kannst, die sowohl Retrieval als auch Live-Tool-Ausführung nutzen.
# ------------------------------
# 5) Gradio UI
# ------------------------------
with gr.Blocks(title="RAG vs MCP Demo: Bookstore Ops Assistant") as demo:
gr.Markdown(
"# RAG vs MCP Demo: Bookstore Ops Assistant\n"
"Use this sandbox to feel the difference between RAG (lookup from a handbook) and MCP-style tools (act on live data).\n\n"
"**Tips**: Put book titles in quotes, e.g., 'Dune' or \"Clean Code\"."
)
with gr.Row():
query = gr.Textbox(label="Your request", placeholder="e.g., Do we have 'Dune' in stock? Or: What is our returns policy?", lines=2)
with gr.Row():
mode = gr.Dropdown(["Auto", "RAG only", "Tools only"], value="Auto", label="Routing mode")
show_trace = gr.Checkbox(True, label="Show trace")
submit = gr.Button("Run", variant="primary")
answer = gr.Markdown(label="Answer")
trace = gr.JSON(label="Trace (route, tool calls, retrieved docs)")
table = gr.Dataframe(headers=["id", "title", "score", "snippet"], label="Retrieved docs (RAG)")
def _run(q, m, t):
ans, tr, df = handle_query(q or "", mode=m or "Auto", show_trace=bool(t))
return ans, json.loads(tr), df
submit.click(_run, inputs=[query, mode, show_trace], outputs=[answer, trace, table])
if __name__ == "__main__":
demo.launch()
Beispiel-Queries zum Ausprobieren
Du kannst die folgenden Fragen testen:
- “What is our returns policy?”
- “How long does standard shipping take?”
- “Do we have ‘Dune’ in stock?”
- “Order 3 copies of ‘The Pragmatic Programmer’”
- “Change the price of ‘Clean Code’ to 29.99”
- “Do we have ‘Dune’ in stock, and what is our return policy?”
Diese Beispiele zeigen den Unterschied zwischen RAG (statisches FAQ-ähnliches Wissen) und Tools, die auf Echtzeit-Inventardaten arbeiten. In echten Deployments sind viele moderne KI-Systeme gemischt aufgebaut. Ein Support-Chatbot könnte zum Beispiel Accountdaten über API-Calls abrufen und gleichzeitig Produktdokumentation aus einer Retrieval-gestützten Knowledge Base referenzieren.
FAQ: RAG vs MCP
1. Wann sollte ich RAG statt MCP verwenden?
Nutze RAG, wenn die Antwort bereits in dokumentiertem Wissen vorhanden ist – etwa in Policies, Spezifikationen, Handbüchern oder FAQ-Seiten. Ein Retrieval-Ansatz ist besonders effektiv, wenn du faktenbasierte Antworten brauchst oder wenn es nicht entscheidend ist, immer den allerneuesten Echtzeit-Stand zu haben.
2. Wann ist MCP die bessere Wahl?
Wähle MCP, wenn die Aufgabe von Live-Daten abhängt oder sich die Daten häufig ändern. Es ist auch die stärkere Option, wenn eine Anfrage APIs oder Datenbanken benötigt oder wenn das Modell eine Aktion ausführen muss, zum Beispiel ein Ticket erstellen, eine E-Mail senden oder einen System-Workflow auslösen.
3. Was sind die größten Risiken beider Ansätze?
RAG risks: Veraltete oder fehlende Dokumentation, schwaches Retrieval durch Chunking-Probleme oder Synonym-Unterschiede sowie das Überladen des Modellkontexts mit zu vielen irrelevanten Chunks.
MCP risks: Tools, die schlecht beschrieben sind (unklare Schemas oder verwirrende Tool-Namen), Situationen, in denen das Modell in Schleifen gerät oder Tools falsch nutzt, sowie Sicherheitsrisiken, wenn Tools unbeabsichtigte Aktionen ermöglichen.
4. Kann ich RAG und MCP in einem Workflow kombinieren?
Ja. Viele praktische Use Cases brauchen beide Muster. Ein Assistant könnte zum Beispiel zuerst eine Garantie-Policy via RAG abrufen, dann über MCP eine Bestellung für ein Ersatzgerät auslösen und anschließend bestätigen, was passiert ist – inklusive Verweis auf die zuvor abgerufene Policy. Der „RAG → MCP → RAG“-Workflow ist in Produktionsumgebungen verbreitet.
5. Wie entscheide ich schnell zwischen RAG und MCP für eine neue Anfrage?
Eine einfache Regel lautet:
- If a human would “look it up in a document,” use RAG.
- If a human would “click a button or query a database,” use MCP.
Fazit
Betrachte RAG als den Weg, auf vorhandenes Wissen zuzugreifen, und MCP als den Weg, Handlungen auszuführen und Live-Informationen zu holen. In vielen Fällen ist RAG der Einstieg, wenn Informationen bereits dokumentiert sind und Zitate oder Nachvollziehbarkeit wichtig sind. MCP ist dann das richtige Werkzeug, wenn eine Aufgabe von APIs, Datenbanken oder mehrstufigen Workflows abhängt. Wenn beide Muster gut umgesetzt sind, lassen sie sich zu End-to-End-Flows (RAG → MCP → RAG) kombinieren, die Entscheidungen begründen und gleichzeitig echte Aktionen ausführen können.


