RAG Query-Routing in der Praxis: Multi-Vektorspeicher und intelligente Retrieval-Verteilung

Drei Uhr nachts – im Produktivsystem blinken wieder die Alarme.
Auf dem Dashboard schießt die Antwortzeit für „Q3-Umsatz Ostchina“ auf 12 Sekunden. In der Demo lief alles glatt – warum bricht es live ein? Schlimmer: Die Logs zeigen die volle Multi-Hop-Reasoning-Pipeline – nur für eine einfache Faktenabfrage.
„Mit Kanonen auf Spatzen schießen“, sagte ein Kollege trocken.
Klar wurde: Das Problem ist nicht die Retrieval-Genauigkeit, sondern die Retrieval-Strategie. Klassisches RAG verhält sich wie ein „blinder Retriever“: immer derselbe Ablauf – Vektorsuche plus LLM-Generierung. Einfache Fragen werden überfrachtet, komplexe unterversorgt.
Dieser Artikel zeigt, wie Sie Ihrem RAG einen „Verkehrsregler“ geben – einen Query-Router. Er erkennt Merkmale der Anfrage und leitet sie auf die passende Strecke: Schnellpfad für Fakten, Tiefgang für Reasoning, Multi-Source für vollständige Antworten.
Ehrlich: Diese Lösung hat das Projekt gerettet.
1. Warum Query-Routing? – Vom blinden Retrieval zur intelligenten Verteilung
Zuerst die Stolpersteine aus der Praxis.
Letztes Jahr bauten wir ein E-Commerce-Support-RAG. Vier Wissensbereiche: Produkte, Rückgabe, Logistik, Marketing. Tests grün – nach dem Go-live verdoppelten sich Beschwerden.
In den Logs ein Klassiker: „Wie lange dauert die Erstattung?“ – Antworten zu Versandzeiten und Aktionen. Nicht falsch, aber unfokussiert; zu viel Rauschen verwirrt Nutzer.
Wissensinterferenz ist der erste Schmerz: Alles in einem Vektorspeicher → gemischte Treffer. Frage zu Szenario A, „ähnlicher“ Inhalt aus B.
Der zweite Schmerz ist subtiler: Antworteffizienz.
„Wie hoch war der Q3-Umsatz in Ostchina?“ – eine Faktenfrage. Reichen DB oder Keyword-Suche. Stattdessen: Embedding, Cosinus, Top-K, LLM – 1–2 Sekunden und hoher Aufwand.
Der dritte Schmerz: Intent-Fehlklassifikation.
„Video mit kürzester Wiedergabedauer“ – SelfQueryRetriever erkennt „Wiedergabedauer“ oft nicht als Metadatenfilter. „Hat der Streik den Kurs beeinflusst?“ braucht Multi-Hop – ein Vektorretriever reicht nicht.
Sie brauchen einen Router, der die Lage „liest“ und den passenden Pfad wählt – wie in einem Restaurant: Theke für Schnellgerichte, Küche für Aufwand, Auslieferung für Kanäle.
2. Kernarchitektur – Drei-Schichten-Modell
Kernidee: Schichten, klare Rollen.
Nutzeranfrage
↓
┌─────────────────────────────────┐
│ Routing: Szenario-Klassifikation │
│ (LLM / Semantic Router) │
│ → python_docs / js_docs / go_docs│
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Retrieval: Szenario-Vektorspeicher │
│ Chroma(python) / Chroma(js)... │
│ → top-k Chunks │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Merge: RRF-Algorithmus │
│ RRF(d) = Σ 1/(k + rank(d)) │
│ → finale Antwort │
└─────────────────────────────────┘
↓
Antwort generieren
Die Routing-Schicht ist das „Gehirn“: Pfad wählen – LLM-Logik oder Semantic Router (Details folgen).
Die Retrieval-Schicht sind die „Hände“: pro Szenario ein Index (Python-, JS-, Go-Docs). Routing entscheidet wo, Retrieval sucht.
Die Merge-Schicht ist der „Schiedsrichter“: bei Multi-Szenario-Ergebnissen sortiert RRF (Reciprocal Rank Fusion).
LangChain EnsembleRetriever in Kurzform:
from langchain.retrievers import EnsembleRetriever
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
python_store = Chroma(
persist_directory="./chroma_python",
embedding_function=OpenAIEmbeddings()
)
python_retriever = python_store.as_retriever(search_kwargs={"k": 5})
js_store = Chroma(
persist_directory="./chroma_js",
embedding_function=OpenAIEmbeddings()
)
js_retriever = js_store.as_retriever(search_kwargs={"k": 5})
ensemble_retriever = EnsembleRetriever(
retrievers=[python_retriever, js_retriever],
c=60
)
docs = ensemble_retriever.invoke("Wie behandelt man asynchrone Callbacks?")
print(f"Gefunden: {len(docs)} Chunks")
EnsembleRetriever ruft beide Retriever parallel auf und merged per RRF. c=60 ist ein bewährter Kompromiss – zu groß glättet, zu klein betont Spitzenränge.
Im Test stieg die Genauigkeit von 72 % auf 92 % – auf Kosten etwas Latenz durch parallele Abfragen.
3. Drei Routing-Strategien – Logisch, semantisch, Metadaten
3.1 Logisches Routing: LLM als Dispatcher
Direkter Ansatz: LLM analysiert Intent und wählt die Datenquelle.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_deepseek import ChatDeepSeek
system_prompt = """Du bist ein Routing-Experte für Programmierfragen.
Leite nach Sprache zur Datenquelle:
- Python → python_docs
- JavaScript → js_docs
- Go → golang_docs
- unklar → general_docs
Nur den Datenquellennamen zurückgeben."""
prompt = ChatPromptTemplate.from_messages([
("system", system_prompt),
("human", "{query}")
])
llm = ChatDeepSeek(model="deepseek-chat", temperature=0)
router_chain = prompt | llm | StrOutputParser()
query = "Wie implementiert man in Python einen asynchronen Crawler?"
datasource = router_chain.invoke({"query": query})
print(f"Routing: {datasource}") # python_docs
Flexibel für komplexe Intents („Python vs. Go Concurrency“ → mehrere Quellen + Ensemble). Nachteil: jeder Routingschritt kostet ~0,5–1 s LLM-Latenz und API-Gebühren.
3.2 Semantisches Routing: Vektoren statt LLM
Für Tempo: Semantic Router – wie unscharfes if/else mit Beispiel-Utterances pro Route.
from semantic_router import Route, RouteLayer
from semantic_router.encoders import OpenAIEncoder
python_route = Route(
name="python_docs",
utterances=[
"Dateien in Python lesen",
"Python-Dekoratoren",
"Asynchrones Programmieren in Python",
"List Comprehension in Python",
]
)
js_route = Route(
name="js_docs",
utterances=[
"JavaScript async Callbacks",
"DOM in JS manipulieren",
"Node.js Event Loop",
"Promise vs. async/await in JS",
]
)
route_layer = RouteLayer(
encoder=OpenAIEncoder(),
routes=[python_route, js_route]
)
query = "Wie nutzt man Python-Generatoren?"
result = route_layer(query)
print(f"Routing: {result.name}") # python_docs
3–5× schneller als LLM-Routing: Embedding ~100 ms vs. LLM 500 ms+. Grenze: Routen müssen vordefiniert sein; unbekannte Fragetypen → None. Ideal bei stabilen Abfragemustern.
3.3 Metadaten-Routing: SelfQueryRetriever
Bei reichen Metadaten (Kategorie, Sprache, Datum) filtert SelfQueryRetriever präzise:
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain.chains.query_constructor.base import AttributeInfo
from langchain_openai import ChatOpenAI
metadata_field_info = [
AttributeInfo(
name="category",
description="tutorial, api, guide, troubleshooting",
type="string"
),
AttributeInfo(
name="language",
description="python, javascript, golang",
type="string"
),
AttributeInfo(
name="date",
description="Veröffentlichungsdatum",
type="date"
)
]
llm = ChatOpenAI(model="gpt-4", temperature=0)
retriever = SelfQueryRetriever.from_llm(
llm=llm,
vectorstore=vectorstore,
document_contents="Programmier-Dokumentation",
metadata_field_info=metadata_field_info,
verbose=True
)
query = "Aktuelle Python-Tutorials"
docs = retriever.invoke(query)
# z. B. category == "tutorial" AND language == "python", sortiert nach date
Stark bei sauberen Tags – ohne Metadatenqualität wenig Nutzen.
4. Multi-Vektorspeicher – EnsembleRetriever im Detail
Manche Fragen betreffen mehrere Domänen („Python vs. JavaScript Async vergleichen“) – dann parallel suchen und mergen.
RRF-Prinzip
RRF(d) = Σ 1/(k + rank(d))
d: Dokumentrank(d): Rang in einem Retriever (ab 1)k: Glättung, typisch 60
Beispiel: Retriever A Rang 2 → 1/62; B Rang 5 → 1/65; Summe sortiert alle Dokumente.
RRF nutzt Ränge, nicht rohe Scores – wichtig, wenn Vektor-Cosinus und BM25 unterschiedliche Skalen haben.
Dense + Sparse
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_community.vectorstores import Chroma
bm25_retriever = BM25Retriever.from_texts(documents_text_list, k=5)
vector_retriever = Chroma.from_texts(
documents_text_list,
embedding=OpenAIEmbeddings()
).as_retriever(search_kwargs={"k": 5})
ensemble = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.4, 0.6],
c=60
)
docs = ensemble.invoke("LangChain Agent Tool Calling")
Gewichte nach Abfrageart: semantisch höher Vektor (0,6–0,7), Keyword höher BM25 (0,5–0,6), sonst 0,5/0,5.
Qualität mit TruLens
from trulens_eval import Feedback, TruChain
from trulens_eval.feedback.provider.openai import OpenAI
provider = OpenAI()
relevance_feedback = Feedback(
provider.relevance, name="Answer Relevance"
).on_input_output()
context_relevance_feedback = Feedback(
provider.context_relevance, name="Context Relevance"
).on_input().on(context)
tru_recorder = TruChain(
chain=ensemble_retriever_chain,
feedbacks=[relevance_feedback, context_relevance_feedback],
feedback_mode="with_chain"
)
with tru_recorder as recording:
response = ensemble_retriever_chain.invoke({"query": test_query})
Einzelner Vektorretriever: Kontext-Relevanz ~0,72; mit Ensemble ~0,91.
5. Performance und Best Practices
Vier Varianten auf 500 Anfragen (vier Szenarien):
| Strategie | Ø Antwortzeit | Genauigkeit | Einsatz |
|---|---|---|---|
| Kein Routing (ein Store) | 1,2 s | 72 % | Ein Domäne |
| Logisch (LLM) | 1,8 s | 85 % | Multi-Domäne, komplex |
| Semantisch | 0,5 s | 88 % | Feste Fragetypen |
| Ensemble RRF | 1,0 s | 92 % | Hybrid, Multi-Source |
Semantisch 3–4× schneller als logisch (Embedding ~100 ms vs. LLM ~800 ms). Ensemble höchste Genauigkeit, etwas mehr Ressourcen. Ohne Routing paradox oft langsamer: mehr Rauschen belastet die Generierung.
Empfehlungen:
- Einfache Szenarien → Semantic Router – schnell, günstig.
- Multi-Hop / Vergleich → logisches Routing – LLM versteht Cross-Domain besser.
- Unsicherer Intent → Ensemble – 3–4 Retriever als Obergrenze.
- Reiche Metadaten → SelfQuery – präzise Filter.
- Hybrid in Produktion – semantisch zuerst; Confidence < 0,6 → logischer Fallback.
Zusammenfassung
Viele optimieren Embeddings und Prompts und vergessen Query-Routing.
Die Frage „Schnell- oder Tiefgangspfad?“ allein kann Latenz von 1,8 s auf 0,5 s und Genauigkeit von 72 % auf 92 % bewegen.
- Einfach → semantisch
- Komplex → logisch
- Multi-Source → Ensemble + RRF
- Metadaten → SelfQuery
Unser Produktiv-Setup: semantische Erststufe, Fallback logisch, Multi-Source per Ensemble – Lösungsrate im Support-Bot von 68 % auf 89 %.
Beispielcode: GitHub-Repository. Fragen gern in den Kommentaren.
RAG Query-Routing-System implementieren
Intelligente RAG-Routing-Architektur mit semantischem, logischem Routing und Multi-Source-Retrieval
⏱️ Estimated time: 45 min
- 1
Step 1: Routing-Strategie wählen
Je nach Szenario: feste Fragetypen → semantisches Routing (~100 ms); mehrere Domänen → logisches Routing (~800 ms); Mischszenarien → Ensemble RRF (bis 92 % Genauigkeit). - 2
Step 2: Semantisches Routing umsetzen
Mit semantic-router: Route und RouteLayer definieren, OpenAIEncoder nutzen, Beispiel-Utterances pro Datenquelle hinterlegen – ohne LLM-Aufruf pro Anfrage. - 3
Step 3: EnsembleRetriever konfigurieren
Mehrere Retriever (z. B. BM25 + Vektor) mit EnsembleRetriever verbinden, weights und c=60 setzen; Gewichte nach Abfrageart (semantisch vs. Keyword) anpassen. - 4
Step 4: Retrieval-Qualität messen
TruLens für Answer Relevance und Context Relevance; vor/nach Ensemble vergleichen – im Test stieg Kontext-Relevanz von 0,72 auf 0,91.
FAQ
Semantisches oder logisches Routing – was ist besser?
Wie setzt man den RRF-Parameter c im EnsembleRetriever?
Was tun bei Routing-Fehlschlag?
Welche Metadaten braucht SelfQueryRetriever?
Werden parallele Multi-Retriever zu langsam?
Wie stark beeinflusst Routing die RAG-Performance?
6 Min. Lesezeit · Veröffentlicht am: 13. Mai 2026 · Aktualisiert am: 14. Juli 2026
RAG Engineering Guide
Wenn du über die Suche hier gelandet bist, kommst du am schnellsten weiter, indem du zum vorherigen oder nächsten Beitrag dieser Serie springst.
Vorheriger
RAG Query-Routing in der Praxis: Multi-Vektorspeicher und intelligente Retrieval-Verteilung
RAG Query-Routing in der Praxis: Systematischer Vergleich von logischem Routing, semantischem Routing und EnsembleRetriever – mit vollständiger LangChain-Implementierung sowie Kostenoptimierung durch Semantic Caching und Tiered Retrieval.
Teil 4 von 5
Nächster
Dies ist bisher der neueste Beitrag dieser Serie.
Ähnliche Beiträge
RAG + Agent: Architektur der nächsten KI-Anwendungsgeneration

RAG + Agent: Architektur der nächsten KI-Anwendungsgeneration
RAG-Vektordatenbank-Auswahl in der Praxis: Pinecone vs Weaviate vs Milvus im Deep-Dive


Kommentare
Melde dich mit GitHub an, um einen Kommentar zu hinterlassen