Provider-agnostische Agenten: Warum Adapter allein nicht reichen

Dr. Florian Drechsler19. Februar 20268 min read
KI-AgentenLLMArchitekturMCP

Ein Agent, der gestern zuverlässig mit einem OpenAI-Frontier-Modell funktionierte, versagt heute bei Claude. Nicht weil der Code kaputt ist, sondern weil OpenAI Tool-Calls als tools-Array erwartet, Anthropic tool_use-Content-Blocks verwendet und Google auf function_declarations setzt. Drei Anbieter, drei inkompatible Schemas für dasselbe Konzept.

Die Kopplung deines Agenten an einen einzigen Anbieter bedeutet, dessen Preisänderungen, Ratenlimits und Modell-Deprecations als unveränderliche Naturgesetze zu akzeptieren. LLM-Agnostizismus ist kein Nice-to-have. Es ist eine Überlebensfrage.

TL;DR: Adapter-Patterns (LiteLLM, LangChain) lösen das syntaktische Problem und machen API-Aufrufe gleich aussehend. MCP löst das Tool-Problem und bringt Tools mit jedem Modell zum Laufen. Aber keines löst das semantische Problem: Modelle verhalten sich unterschiedlich, selbst bei identischen Eingaben. Echte Portabilität erfordert alle drei Schichten.

Eine kurze Anmerkung zur Terminologie: Provider = API-Plattform (OpenAI, Anthropic, Google). Modell = spezifische Modell-ID. Framework = Orchestrierungsschicht, die beides abstrahiert.


Das Adapter-Pattern: Wo alle beginnen

Die Lösung, auf die alle großen Frameworks unabhängig voneinander konvergiert sind, ist das Adapter-Pattern.

Wie die großen Drei es umsetzen

  • LiteLLM implementiert es am direktesten: Ein einziger completion()-Aufruf akzeptiert einen Modell-String und routet transparent zu über 100 Anbietern.
  • LangChains BaseChatModel kehrt die Abhängigkeit um. Alle Provider-Integrationen implementieren denselben Schnittstellenvertrag mit BaseMessage-Objekten und einer einheitlichen bind_tools()-Methode.
  • AutoGen v0.4 formalisiert die Grenze durch ein protokollbasiertes Design, bei dem Agenten durch ihre Rollen und Kommunikationsprotokolle definiert werden, nicht durch das zugrunde liegende Modell.

Klingt nach einem gelösten Problem. Die Realität ist erheblich komplexer.


Wo API-Divergenz in der Praxis zuschlägt

Die eigentliche Engineering-Herausforderung liegt in den Details der bidirektionalen Schemaübersetzung.

Drei Anbieter, drei Schemas

Ein vereinfachtes Beispiel macht den Umfang sichtbar:

# Pseudocode - simplified schema, real APIs differ in details

# OpenAI: tools array with function wrapper
tools = [{"type": "function", "function": {"name": "get_weather", "parameters": {...}}}]

# Anthropic: tool_use content blocks
tools = [{"name": "get_weather", "input_schema": {...}}]

# Google: function_declarations (format varies by API version/SDK)
tools = [{"function_declarations": [{"name": "get_weather", "parameters": {...}}]}]

# LiteLLM: accepts OpenAI-like inputs and normalizes internally
response = litellm.completion(
    model="anthropic/claude-sonnet-4-20250514",
    messages=messages,
    tools=tools  # OpenAI format - LiteLLM handles conversion
)

LiteLLM fungiert als Adapter an der HTTP/SDK-Grenze: Ausgehende Anfragen werden in das jeweilige Provider-Schema übersetzt, eingehende Antworten werden in ein kanonisches ModelResponse-Objekt zurückkonvertiert. Vollständige Parität über alle Anbieter und Features ist nicht garantiert, aber die gängigen Fälle werden abgedeckt.

Response-Normalisierung

Die Unterschiede gehen tiefer als Anfrageformate:

  • Stop-Gründe: Anthropic gibt stop_reason: "end_turn" zurück. LiteLLM schreibt es in einen normalisierten finish_reason um.
  • Response-Struktur: Geminis tief verschachtelte candidates[0].content.parts-Struktur wird geflacht, bevor der Agentencode sie sieht.
  • Konsistenter Zugriff: Dein Code liest immer choices[0].message.content und choices[0].message.tool_calls, unabhängig vom dahinterliegenden Anbieter.

Tool-Result-Übergabe

Auch die Rückgabe von Tool-Ergebnissen divergiert grundlegend:

AnbieterWie Tool-Ergebnisse übergeben werden
OpenAIDedizierte tool-Rolle in der Nachricht
Anthropicuser-Nachrichten mit tool_result-Content-Blocks
GooglefunctionCall-Parts
Keine native UnterstützungLiteLLM serialisiert Schemas in den System-Prompt, parst Freitext via Regex

Die letzte Zeile ist aufschlussreich. Es funktioniert, aber es zeigt die Grenzen syntaktischer Normalisierung.

Framework-Level-Output-Parsing

Auf Framework-Ebene adressiert LangChain die Response-Heterogenität durch eine geschichtete BaseOutputParser-Hierarchie:

  • .with_structured_output() dispatcht automatisch an OpenAI Function Calling, Anthropic tool_use-Blocks oder Googles response_schema, basierend auf den Fähigkeiten jedes Anbieters.
  • Ein OutputFixingParser implementiert selbstheilende Retry-Schleifen für fehlerhaftes JSON.

Das reflektiert eine harte Wahrheit: Man kann nicht davon ausgehen, dass die Modellausgabe konsistent ist.

Kernerkenntnis: Schemaübersetzung und semantische Äquivalenz sind zwei verschiedene Probleme. Parallele Tool-Call-Behandlung, tool_choice-Erzwingung, Streaming-Finish-Gründe, all das bleibt providerspezifisch, selbst nach syntaktischer Normalisierung.


MCP: Die zweite Standardisierungsachse

Während LiteLLM und LangChain die Modellschicht abstrahieren, entstand parallel ein zweiter Standardisierungsvektor, diesmal nicht für das Modell, sondern für die Tools.

Das N x M-Problem

Vor MCP erforderte die Verbindung von N Agenten-Frameworks mit M Tools N x M individuelle Integrationen. Jedes Framework und jeder Anbieter verwendete inkompatible Tool-Schemaformate.

Anthropics Model Context Protocol (MCP), Ende 2024 veröffentlicht, reduziert dies auf N+M, indem ein einzelnes JSON-RPC-2.0-basiertes Protokoll definiert wird, über das Tool-Server ihre Fähigkeiten exponieren und Agenten-Clients sie konsumieren.

Wie es funktioniert

Agent Host → MCP tools/list → Tool Schemas → Model Call → Tool Call → Tool Result → next step

Man kann es sich wie das Language Server Protocol (LSP) vorstellen: Genau wie LSP die Kommunikation zwischen IDEs und Language-Servern standardisierte, schafft MCP eine universelle Schnittstelle zwischen Agenten und Tools. USB-C für KI: standardisierte Entdeckung, einheitliche Aufrufe, normalisierte Fehlerbehandlung.

Warum es architektonisch relevant ist

MCP operiert eine Schicht unterhalb der LLM-Provider-Abstraktion:

  • Ein MCP-Server weiß nicht und kümmert sich nicht darum, welches LLM seine Tools aufruft. Nur die Host-Anwendung benötigt providerspezifischen Integrationscode.
  • LLM-Anbieter wechseln, ohne Tool-Server umzuschreiben.
  • Neue Tools hinzufügen, ohne Agentencode anzufassen.

MCPs Modellagnostizismus ist eine Protokolleigenschaft, keine Bibliothekskonfiguration. Tool-Schemas leben auf MCP-Servern und werden zur Laufzeit über standardisierte tools/list-Aufrufe dynamisch entdeckt. Das Sampling-Primitiv (sampling/createMessage) geht weiter: MCP-Server können LLM-Completions über den Host anfordern, ohne ein Modell-SDK einzubetten.

Das Zwei-Schichten-Modell

Zusammen mit LiteLLM entsteht eine klare Trennung:

SchichtWas sie normalisiertBeispiel
Framework-SchichtLLM-API (Anfragen, Antworten, Auth)LiteLLM, LangChain-Adapter
Tool-SchichtTool-Entdeckung, Aufruf, ErgebnisseMCP-Server

Beide Quellen von Provider-Lock-in werden gleichzeitig eliminiert.

Hinweis: MCP standardisiert die Schnittstelle, nicht das Verhalten des Modells. Wie zuverlässig ein Modell Tool-Schemas befolgt, wie viele parallele Tool-Calls es planen kann, wie es Tool-Fehler behandelt, das sind Modellqualitätseigenschaften, die kein Protokoll strukturell lösen kann. MCP ist eine notwendige, aber nicht hinreichende Bedingung für Provider-Portabilität.

Die schnelle Integration durch OpenAI, Google DeepMind und Microsoft innerhalb eines Jahres nach der Veröffentlichung bestätigt: MCP adressierte ein reales, weit empfundenes Koordinationsproblem.


Wie Frameworks beide Schichten kombinieren

MCP und Modellabstraktion sind Bausteine. Die Framework-Landschaft zeigt drei architektonische Ansätze, die denselben Kern teilen.

Das gemeinsame Prinzip

Alle großen Frameworks implementieren Dependency Inversion: High-Level-Agentenlogik hängt von Abstraktionen ab, nicht von konkreten LLM-Implementierungen:

  • LangChain verwendet BaseChatModel
  • AutoGen verwendet ChatCompletionClient
  • smolagents verwendet eine einheitliche Model-Schnittstelle

In jedem Fall erfordert der Wechsel des Anbieters nur eine Änderung der Modellinstanziierung. Anthropics Engineering Guide empfiehlt explizit, die Orchestrierungsschicht vom zugrunde liegenden Modell entkoppelt zu halten.

Wo sich die Ansätze unterscheiden

FrameworkArchitekturProvider-AbstraktionWo bricht die Abstraktion?
LangChain / LangGraphGraph-basierte OrchestrierungBaseChatModel + breite Adapter-BibliothekStrukturierter Output, Streaming-Verhalten
AutoGen v0.4Event-getriebenes Actor-ModellChatCompletionClient-Protokolltool_choice-Erzwingung, parallele Aufrufe
smolagentsMinimaler Kern (~1.000 Zeilen)Einheitliche Model-SchnittstelleProviderspezifische Grounding-Features

Der Aufstieg der Multi-Model-Graphen

Die bedeutendste architektonische Evolution ist der Wechsel von Single-Provider-Agentenschleifen zu heterogenen Multi-Model-Graphen.

LangGraph kodiert Agenten-Workflows als zustandsbehaftete gerichtete Graphen, in denen verschiedene Knoten verschiedene LLM-Backends nutzen können: ein schnelles, günstiges Modell für Routing und Klassifikation, ein leistungsfähigeres für die Generierung.

AutoGen geht weiter: Ein Planer-Agent auf einem Frontier-Modell kann Aufgaben an spezialisierte Ausführungs- und Kritiker-Agenten auf anderen Anbietern delegieren, alles innerhalb eines einzigen Multi-Agent-Workflows.

Am anderen Ende des Spektrums steht Amazon Bedrock Agents, wo die Infrastruktur selbst Provider-Neutralität erzwingt, anstatt Entwickler zu erfordern, Adapter-Patterns zu implementieren.

Einen praktischen Einblick, wie diese Multi-Model-Graphen in Produktionspipelines mit spezialisierten Agenten, Quality Gates und Workspace-Isolation eingesetzt werden, bietet So funktionieren agentenbasierte Entwicklungs-Workflows.


Interface-Kompatibilität ist nicht Portabilität

Frameworks lösen das Interface-Problem elegant. Aber Interface-Kompatibilität und echte Portabilität sind zwei verschiedene Dinge.

Derselbe Prompt und derselbe Agenten-Graph erzeugen unterschiedliches emergentes Verhalten, abhängig vom dahinterliegenden Modell.

Drei Dimensionen der Verhaltensvarianz

1. Instruction-Following-Genauigkeit

Modelle unterscheiden sich darin, wie präzise sie Agentenschleifen-Instruktionen wie ReAct-Prompting oder strukturierte Output-Anfragen befolgen.

2. Tool-Call-Zuverlässigkeit

Paralleles Tool-Call-Verhalten, tool_choice-Erzwingung und Streaming-Finish-Gründe variieren pro Anbieter, selbst nach syntaktischer Normalisierung.

3. Prompt-Portabilität

Ein System-Prompt, der für ein Modell optimiert ist, verhält sich mit einem anderen Anbieter anders. Prompt-Templates erfordern Re-Engineering pro Modellfamilie. Das ist kein einmaliger Konfigurationsaufwand. Es sind laufende Kosten.

Der nichtlineare Abfall

Studien wie AgentBench zeigen, dass Leistungsunterschiede zwischen Frontier- und Mid-Tier-Modellen keine graduellen Degradierungen sind, sondern manchmal drastische, nichtlineare Einbrüche.

Anthropics Engineering Guide formuliert es direkt: Modelle unterhalb einer Zuverlässigkeitsschwelle brechen ganze Agenten-Graphen, anstatt graceful zu degradieren.


Was das für deine Architektur bedeutet

Eine dedizierte Modellevaluierungsschicht neben der Abstraktionsschicht ist kein optionaler Overhead. Sie gehört zur Architektur.

Vier Praktiken, die zählen

  1. Register bekannter Modell-/Aufgaben-Paarungen pflegen für zuverlässige Produktionsdeployments
  2. Fallback-Ketten konfigurieren, die aktiviert werden, wenn ein Modell einen Laufzeit-Fähigkeitscheck nicht besteht
  3. Observability-Hooks implementieren, um Verhaltensdrift zu erkennen, bevor sie in nachgelagerte Fehler kaskadiert
  4. Providerspezifische Verhaltenstests schreiben, die Tool-Schema-Einhaltung, parallele Planung und Fehlerwiederherstellung abdecken

Der fundamentale Kompromiss

Jedes provider-agnostische Framework konfrontiert dich mit derselben Spannung: universelle Abstraktion versus Zugang zu modellspezifischen Fähigkeiten. Extended Thinking, erzwungene JSON-Schemas, Grounding-Fähigkeiten, all das erfordert providerspezifische Codepfade, die durch die Abstraktion brechen.

Checkliste: Provider-agnostische Agentenarchitektur

Wer Portabilität ernst nimmt, braucht mehr als ein Adapter-Pattern:

  • Kanonisches Nachrichtenmodell + kanonisches Tool-Call-Modell: eine einheitliche interne Repräsentation, die nicht direkt von einem Anbieter abhängt
  • Provider-Fähigkeitsmatrix: Welcher Anbieter unterstützt Streaming, parallele Tool-Calls, Schema-Erzwingung, Grounding?
  • Vertragstests für Tool-Einhaltung: Automatisierte Tests, die pro Anbieter verifizieren, ob Tool-Schemas korrekt befolgt werden
  • Golden Traces: Referenzläufe (Prompt + Tools + erwartete Aufrufe) pro Anbieter, die als Regressionstests dienen
  • Laufzeit-Fallback-Policy: Definierte Eskalation, wenn ein Modell Laufzeit-Fähigkeitschecks nicht besteht
  • Observability: Tool-Call-Fehlertaxonomie + Drifterkennung, um Verhaltensänderungen zwischen Provider-Versionen zu erkennen

Das Fazit

Beginne mit der Abstraktion als Standard. Steige auf providerspezifischen Code herab, wenn du die Features brauchst, aber dokumentiere jeden solchen Ausbruch explizit.

Portabilität als Norm, Provider-Optimierung als bewusste Ausnahme.

Die Frage, die deine Architekturentscheidung leiten sollte, ist nicht "Welcher Anbieter ist der beste?", sondern "Welche Teile meines Agenten dürfen vom Anbieter abhängen, und welche nicht?"

Diesen Artikel teilen

Verwandte Artikel