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:
| Anbieter | Wie Tool-Ergebnisse übergeben werden |
|---|
| OpenAI | Dedizierte tool-Rolle in der Nachricht |
| Anthropic | user-Nachrichten mit tool_result-Content-Blocks |
| Google | functionCall-Parts |
| Keine native Unterstützung | LiteLLM 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:
| Schicht | Was sie normalisiert | Beispiel |
|---|
| Framework-Schicht | LLM-API (Anfragen, Antworten, Auth) | LiteLLM, LangChain-Adapter |
| Tool-Schicht | Tool-Entdeckung, Aufruf, Ergebnisse | MCP-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
| Framework | Architektur | Provider-Abstraktion | Wo bricht die Abstraktion? |
|---|
| LangChain / LangGraph | Graph-basierte Orchestrierung | BaseChatModel + breite Adapter-Bibliothek | Strukturierter Output, Streaming-Verhalten |
| AutoGen v0.4 | Event-getriebenes Actor-Modell | ChatCompletionClient-Protokoll | tool_choice-Erzwingung, parallele Aufrufe |
| smolagents | Minimaler Kern (~1.000 Zeilen) | Einheitliche Model-Schnittstelle | Providerspezifische 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
- Register bekannter Modell-/Aufgaben-Paarungen pflegen für zuverlässige Produktionsdeployments
- Fallback-Ketten konfigurieren, die aktiviert werden, wenn ein Modell einen Laufzeit-Fähigkeitscheck nicht besteht
- Observability-Hooks implementieren, um Verhaltensdrift zu erkennen, bevor sie in nachgelagerte Fehler kaskadiert
- 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?"
An agent that reliably worked with an OpenAI frontier model yesterday fails on Claude today. Not because the code is broken, but because OpenAI expects tool calls as a tools array, Anthropic uses tool_use content blocks, and Google relies on function_declarations. Three providers, three incompatible schemas for the same concept.
Coupling your agent to a single provider means accepting their pricing changes, rate limits, and model deprecations as immutable laws of nature. LLM agnosticism isn't a nice-to-have. It's a survival question.
TL;DR: Adapter patterns (LiteLLM, LangChain) solve the syntactic problem, making API calls look the same. MCP solves the tool problem, making tools work with any model. But neither solves the semantic problem: models behave differently even with identical inputs. True portability requires all three layers.
A quick note on terminology: Provider = API platform (OpenAI, Anthropic, Google). Model = specific model ID. Framework = orchestration layer that abstracts both.
The Adapter Pattern: Where Everyone Starts
The solution that all major frameworks converged on independently is the adapter pattern.
How the Big Three Do It
- LiteLLM implements it most directly: a single
completion() call accepts a model string and transparently routes to over 100 providers.
- LangChain's
BaseChatModel inverts the dependency. All provider integrations implement the same interface contract with BaseMessage objects and a unified bind_tools() method.
- AutoGen v0.4 formalizes the boundary through a protocol-based design where agents are defined by their roles and communication protocols, not the underlying model.
Sounds like a solved problem. The reality is considerably more complex.
Where API Divergence Hits in Practice
The real engineering challenge lives in the details of bidirectional schema translation.
Three Providers, Three Schemas
A simplified example makes the scope visible:
# 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 operates as an adapter at the HTTP/SDK boundary: outgoing requests are translated into the respective provider schema, incoming responses are converted back into a canonical ModelResponse object. Full parity across all providers and features isn't guaranteed, but the common cases are covered.
Response Normalization
The differences go deeper than request formats:
- Stop reasons: Anthropic returns
stop_reason: "end_turn". LiteLLM rewrites it into a normalized finish_reason.
- Response structure: Gemini's deeply nested
candidates[0].content.parts structure gets flattened before agent code sees it.
- Consistent access: Your code always reads
choices[0].message.content and choices[0].message.tool_calls, regardless of the provider behind it.
Tool Result Passing
Passing tool results back also diverges fundamentally:
| Provider | How Tool Results Are Passed |
|---|
| OpenAI | Dedicated tool role message |
| Anthropic | user messages with tool_result content blocks |
| Google | functionCall parts |
| No native support | LiteLLM serializes schemas into system prompt, parses freetext via regex |
That last row is telling. It works, but it shows the limits of syntactic normalization.
Framework-Level Output Parsing
At the framework level, LangChain addresses response heterogeneity through a layered BaseOutputParser hierarchy:
.with_structured_output() automatically dispatches to OpenAI function calling, Anthropic tool_use blocks, or Google's response_schema, based on each provider's capabilities.
- An
OutputFixingParser implements self-healing retry loops for malformed JSON.
This reflects a hard truth: you can't assume model output will be consistent.
Key insight: Schema translation and semantic equivalence are two different problems. Parallel tool-call handling, tool_choice enforcement, streaming finish reasons, all of this remains provider-specific, even after syntactic normalization.
MCP: The Second Standardization Axis
While LiteLLM and LangChain abstract the model layer, a second standardization vector emerged in parallel, this time not for the model, but for the tools.
The N×M Problem
Before MCP, connecting N agent frameworks with M tools required N×M custom integrations. Every framework and every provider used incompatible tool schema formats.
Anthropic's Model Context Protocol (MCP), released in late 2024, reduces this to N+M by defining a single JSON-RPC 2.0-based protocol through which tool servers expose their capabilities and agent clients consume them.
How It Works
Agent Host → MCP tools/list → Tool Schemas → Model Call → Tool Call → Tool Result → next step
Think of it like the Language Server Protocol (LSP): just as LSP standardized communication between IDEs and language servers, MCP creates a universal interface between agents and tools. USB-C for AI: standardized discovery, unified invocations, normalized error handling.
Why It Matters Architecturally
MCP operates one layer below the LLM provider abstraction:
- An MCP server doesn't know and doesn't care which LLM calls its tools. Only the host application needs provider-specific integration code.
- Swap LLM providers without rewriting tool servers.
- Add new tools without touching agent code.
MCP's model agnosticism is a protocol property, not a library configuration. Tool schemas live on MCP servers and are dynamically discovered at runtime via standardized tools/list calls. The sampling primitive (sampling/createMessage) takes this further: MCP servers can request LLM completions through the host without embedding a model SDK.
The Two-Layer Model
Together with LiteLLM, a clean separation emerges:
| Layer | What It Normalizes | Example |
|---|
| Framework layer | LLM API (requests, responses, auth) | LiteLLM, LangChain adapters |
| Tool layer | Tool discovery, invocation, results | MCP servers |
Both sources of provider lock-in are eliminated simultaneously.
Note: MCP standardizes the interface, not the model's behavior. How reliably a model follows tool schemas, how many parallel tool calls it can plan, how it handles tool errors, these are model quality properties that no protocol can structurally solve. MCP is a necessary but not sufficient condition for provider portability.
The rapid integration by OpenAI, Google DeepMind, and Microsoft within a year of release confirms: MCP addressed a real, widely felt coordination problem.
How Frameworks Combine Both Layers
MCP and model abstraction are building blocks. The framework landscape shows three architectural approaches that share the same core.
The Common Principle
All major frameworks implement dependency inversion: high-level agent logic depends on abstractions, not on concrete LLM implementations:
- LangChain uses
BaseChatModel
- AutoGen uses
ChatCompletionClient
- smolagents uses a unified
Model interface
In each case, switching providers only requires changing the model instantiation. Anthropic's Engineering Guide explicitly recommends keeping the orchestration layer decoupled from the underlying model.
Where the Approaches Differ
| Framework | Architecture | Provider Abstraction | Where Does the Abstraction Break? |
|---|
| LangChain / LangGraph | Graph-based orchestration | BaseChatModel + broad adapter library | Structured output, streaming behavior |
| AutoGen v0.4 | Event-driven actor model | ChatCompletionClient protocol | tool_choice enforcement, parallel calls |
| smolagents | Minimal core (~1,000 lines) | Unified Model interface | Provider-specific grounding features |
The Rise of Multi-Model Graphs
The most significant architectural evolution is the shift from single-provider agent loops to heterogeneous multi-model graphs.
LangGraph encodes agent workflows as stateful directed graphs where different nodes can use different LLM backends, a fast, cheap model for routing and classification, a more capable one for generation.
AutoGen goes further: a planner agent on one frontier model can delegate tasks to specialized execution and critic agents on other providers, all within a single multi-agent workflow.
At the other end of the spectrum sits Amazon Bedrock Agents, where the infrastructure itself enforces provider neutrality instead of requiring developers to implement adapter patterns.
For a practical look at how these multi-model graphs are deployed in production pipelines with specialized agents, quality gates, and workspace isolation, see How Agent-Based Development Workflows Work.
Interface Compatibility Is Not Portability
Frameworks solve the interface problem elegantly. But interface compatibility and true portability are two different things.
The same prompt and the same agent graph produce different emergent behaviors depending on the model behind it.
Three Dimensions of Behavioral Variance
1. Instruction-Following Fidelity
Models differ in how precisely they follow agent loop instructions like ReAct prompting or structured output requests.
2. Tool-Call Reliability
Parallel tool-call behavior, tool_choice enforcement, and streaming finish reasons vary per provider, even after syntactic normalization.
3. Prompt Portability
A system prompt optimized for one model behaves differently with another provider. Prompt templates require re-engineering per model family. This isn't a one-time configuration effort. It's an ongoing cost.
The Non-Linear Drop-Off
Studies like AgentBench show that performance gaps between frontier and mid-tier models aren't gradual degradations but sometimes drastic, non-linear drops.
Anthropic's Engineering Guide states it directly: models below a reliability threshold break entire agent graphs instead of degrading gracefully.
What This Means for Your Architecture
A dedicated model evaluation layer alongside the abstraction layer isn't optional overhead. It belongs to the architecture.
Four Practices That Matter
- Maintain registries of known model/task pairings for reliable production deployments
- Configure fallback chains that activate when a model fails a runtime capability check
- Implement observability hooks to detect behavioral drift before it cascades into downstream failures
- Write per-provider behavioral tests covering tool schema adherence, parallel planning, and error recovery
The Fundamental Tradeoff
Every provider-agnostic framework confronts you with the same tension: universal abstraction versus access to model-specific capabilities. Extended thinking, enforced JSON schemas, grounding capabilities, all of this requires provider-specific code paths that break through the abstraction.
Checklist: Provider-Agnostic Agent Architecture
Anyone who takes portability seriously needs more than an adapter pattern:
- Canonical message model + canonical tool-call model: a unified internal representation that doesn't directly depend on any provider
- Provider capability matrix: which provider supports streaming, parallel tool calls, schema enforcement, grounding?
- Contract tests for tool adherence: automated tests that verify per provider whether tool schemas are correctly followed
- Golden traces: reference runs (prompt + tools + expected calls) per provider that serve as regression tests
- Runtime fallback policy: defined escalation when a model fails runtime capability checks
- Observability: tool-call error taxonomy + drift detection to catch behavioral shifts between provider versions
The Bottom Line
Start with the abstraction as default. Drop down to provider-specific code when you need the features, but explicitly document every such escape.
Portability as the norm, provider optimization as a deliberate exception.
The question that should guide your architecture decision isn't "Which provider is best?" but "Which parts of my agent are allowed to depend on the provider, and which aren't?"
Un agente que funcionaba de manera confiable con un modelo frontier de OpenAI ayer falla con Claude hoy. No porque el código esté roto, sino porque OpenAI espera las llamadas a herramientas como un array tools, Anthropic usa content blocks tool_use, y Google utiliza function_declarations. Tres proveedores, tres esquemas incompatibles para el mismo concepto.
Acoplar tu agente a un solo proveedor significa aceptar sus cambios de precios, límites de tasa y deprecaciones de modelos como leyes inmutables de la naturaleza. El agnosticismo de LLM no es un nice-to-have. Es una cuestión de supervivencia.
TL;DR: Los patrones adaptadores (LiteLLM, LangChain) resuelven el problema sintáctico, haciendo que las llamadas a la API se vean iguales. MCP resuelve el problema de las herramientas, haciendo que funcionen con cualquier modelo. Pero ninguno resuelve el problema semántico: los modelos se comportan de manera diferente incluso con entradas idénticas. La verdadera portabilidad requiere las tres capas.
Una nota rápida sobre terminología: Proveedor = plataforma API (OpenAI, Anthropic, Google). Modelo = ID de modelo específico. Framework = capa de orquestación que abstrae ambos.
El patrón adaptador: Donde todos empiezan
La solución en la que todos los frameworks principales convergieron independientemente es el patrón adaptador.
Cómo lo hacen los tres grandes
- LiteLLM lo implementa de la manera más directa: una única llamada
completion() acepta una cadena de modelo y enruta de forma transparente a más de 100 proveedores.
- El
BaseChatModel de LangChain invierte la dependencia. Todas las integraciones de proveedores implementan el mismo contrato de interfaz con objetos BaseMessage y un método unificado bind_tools().
- AutoGen v0.4 formaliza el límite a través de un diseño basado en protocolos donde los agentes se definen por sus roles y protocolos de comunicación, no por el modelo subyacente.
Suena como un problema resuelto. La realidad es considerablemente más compleja.
Dónde impacta la divergencia de API en la práctica
El verdadero desafío de ingeniería está en los detalles de la traducción bidireccional de esquemas.
Tres proveedores, tres esquemas
Un ejemplo simplificado hace visible el alcance:
# 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 opera como adaptador en el límite HTTP/SDK: las solicitudes salientes se traducen al esquema respectivo del proveedor, las respuestas entrantes se convierten de vuelta a un objeto ModelResponse canónico. No se garantiza la paridad completa entre todos los proveedores y funciones, pero los casos comunes están cubiertos.
Normalización de respuestas
Las diferencias van más allá de los formatos de solicitud:
- Razones de parada: Anthropic retorna
stop_reason: "end_turn". LiteLLM lo reescribe en un finish_reason normalizado.
- Estructura de respuesta: La estructura profundamente anidada
candidates[0].content.parts de Gemini se aplana antes de que el código del agente la vea.
- Acceso consistente: Tu código siempre lee
choices[0].message.content y choices[0].message.tool_calls, independientemente del proveedor detrás.
Paso de resultados de herramientas
Pasar los resultados de herramientas también diverge fundamentalmente:
| Proveedor | Cómo se pasan los resultados de herramientas |
|---|
| OpenAI | Mensaje dedicado con rol tool |
| Anthropic | Mensajes user con content blocks tool_result |
| Google | Parts functionCall |
| Sin soporte nativo | LiteLLM serializa esquemas en el system prompt, parsea texto libre via regex |
Esa última fila es reveladora. Funciona, pero muestra los límites de la normalización sintáctica.
Parsing de salida a nivel de framework
A nivel de framework, LangChain aborda la heterogeneidad de respuestas a través de una jerarquía BaseOutputParser por capas:
.with_structured_output() despacha automáticamente a OpenAI function calling, bloques tool_use de Anthropic, o response_schema de Google, según las capacidades de cada proveedor.
- Un
OutputFixingParser implementa bucles de reintento auto-reparadores para JSON malformado.
Esto refleja una verdad difícil: no puedes asumir que la salida del modelo será consistente.
Insight clave: La traducción de esquemas y la equivalencia semántica son dos problemas diferentes. El manejo de tool-calls paralelos, la aplicación de tool_choice, las razones de finalización de streaming, todo esto sigue siendo específico del proveedor, incluso después de la normalización sintáctica.
MCP: El segundo eje de estandarización
Mientras LiteLLM y LangChain abstraen la capa del modelo, un segundo vector de estandarización surgió en paralelo, esta vez no para el modelo, sino para las herramientas.
El problema N x M
Antes de MCP, conectar N frameworks de agentes con M herramientas requería N x M integraciones personalizadas. Cada framework y cada proveedor usaba formatos de esquema de herramientas incompatibles.
El Model Context Protocol (MCP) de Anthropic, lanzado a finales de 2024, reduce esto a N+M al definir un único protocolo basado en JSON-RPC 2.0 a través del cual los servidores de herramientas exponen sus capacidades y los clientes agentes las consumen.
Cómo funciona
Agent Host → MCP tools/list → Tool Schemas → Model Call → Tool Call → Tool Result → next step
Piénsalo como el Language Server Protocol (LSP): así como LSP estandarizó la comunicación entre IDEs y servidores de lenguaje, MCP crea una interfaz universal entre agentes y herramientas. USB-C para IA: descubrimiento estandarizado, invocaciones unificadas, manejo de errores normalizado.
Por qué importa arquitectónicamente
MCP opera una capa debajo de la abstracción del proveedor LLM:
- Un servidor MCP no sabe ni le importa qué LLM llama a sus herramientas. Solo la aplicación host necesita código de integración específico del proveedor.
- Cambiar proveedores de LLM sin reescribir servidores de herramientas.
- Agregar nuevas herramientas sin tocar el código del agente.
El agnosticismo de modelo de MCP es una propiedad del protocolo, no una configuración de biblioteca. Los esquemas de herramientas viven en servidores MCP y se descubren dinámicamente en tiempo de ejecución a través de llamadas estandarizadas tools/list. El primitivo de sampling (sampling/createMessage) va más allá: los servidores MCP pueden solicitar completions de LLM a través del host sin incrustar un SDK de modelo.
El modelo de dos capas
Junto con LiteLLM, surge una separación clara:
| Capa | Qué normaliza | Ejemplo |
|---|
| Capa de framework | API de LLM (solicitudes, respuestas, auth) | LiteLLM, adaptadores de LangChain |
| Capa de herramientas | Descubrimiento, invocación, resultados de herramientas | Servidores MCP |
Ambas fuentes de lock-in del proveedor se eliminan simultáneamente.
Nota: MCP estandariza la interfaz, no el comportamiento del modelo. Cuán confiablemente un modelo sigue los esquemas de herramientas, cuántas llamadas a herramientas paralelas puede planificar, cómo maneja errores de herramientas, estas son propiedades de calidad del modelo que ningún protocolo puede resolver estructuralmente. MCP es una condición necesaria pero no suficiente para la portabilidad del proveedor.
La rápida integración por OpenAI, Google DeepMind y Microsoft dentro de un año del lanzamiento confirma: MCP abordó un problema de coordinación real y ampliamente sentido.
Cómo los frameworks combinan ambas capas
MCP y la abstracción del modelo son bloques de construcción. El panorama de frameworks muestra tres enfoques arquitectónicos que comparten el mismo núcleo.
El principio común
Todos los frameworks principales implementan inversión de dependencia: la lógica de agente de alto nivel depende de abstracciones, no de implementaciones concretas de LLM:
- LangChain usa
BaseChatModel
- AutoGen usa
ChatCompletionClient
- smolagents usa una interfaz
Model unificada
En cada caso, cambiar de proveedor solo requiere cambiar la instanciación del modelo. La Guía de Ingeniería de Anthropic recomienda explícitamente mantener la capa de orquestación desacoplada del modelo subyacente.
Dónde difieren los enfoques
| Framework | Arquitectura | Abstracción de proveedor | ¿Dónde se rompe la abstracción? |
|---|
| LangChain / LangGraph | Orquestación basada en grafos | BaseChatModel + amplia biblioteca de adaptadores | Salida estructurada, comportamiento de streaming |
| AutoGen v0.4 | Modelo de actores dirigido por eventos | Protocolo ChatCompletionClient | Aplicación de tool_choice, llamadas paralelas |
| smolagents | Núcleo mínimo (~1,000 líneas) | Interfaz Model unificada | Features de grounding específicos del proveedor |
El auge de los grafos multi-modelo
La evolución arquitectónica más significativa es el cambio de bucles de agente de un solo proveedor a grafos heterogéneos multi-modelo.
LangGraph codifica flujos de trabajo de agentes como grafos dirigidos con estado donde diferentes nodos pueden usar diferentes backends de LLM: un modelo rápido y económico para enrutamiento y clasificación, uno más capaz para la generación.
AutoGen va más allá: un agente planificador en un modelo frontier puede delegar tareas a agentes de ejecución y críticos especializados en otros proveedores, todo dentro de un único flujo de trabajo multi-agente.
En el otro extremo del espectro está Amazon Bedrock Agents, donde la infraestructura misma impone la neutralidad del proveedor en lugar de requerir que los desarrolladores implementen patrones adaptadores.
Para una visión práctica de cómo estos grafos multi-modelo se despliegan en pipelines de producción con agentes especializados, Quality Gates y aislamiento de workspace, consulta Así funcionan los workflows de desarrollo basados en agentes.
La compatibilidad de interfaz no es portabilidad
Los frameworks resuelven el problema de la interfaz elegantemente. Pero la compatibilidad de interfaz y la verdadera portabilidad son dos cosas diferentes.
El mismo prompt y el mismo grafo de agente producen comportamientos emergentes diferentes dependiendo del modelo detrás.
Tres dimensiones de la varianza de comportamiento
1. Fidelidad en el seguimiento de instrucciones
Los modelos difieren en cuán precisamente siguen las instrucciones del bucle del agente como ReAct prompting o solicitudes de salida estructurada.
2. Confiabilidad de llamadas a herramientas
El comportamiento de llamadas a herramientas paralelas, la aplicación de tool_choice y las razones de finalización de streaming varían por proveedor, incluso después de la normalización sintáctica.
3. Portabilidad de prompts
Un system prompt optimizado para un modelo se comporta de manera diferente con otro proveedor. Las plantillas de prompt requieren reingeniería por familia de modelos. Esto no es un esfuerzo de configuración único. Es un costo continuo.
La caída no lineal
Estudios como AgentBench muestran que las brechas de rendimiento entre modelos frontier y de nivel medio no son degradaciones graduales sino a veces caídas drásticas y no lineales.
La Guía de Ingeniería de Anthropic lo dice directamente: los modelos por debajo de un umbral de confiabilidad rompen grafos de agente enteros en lugar de degradarse graciosamente.
Qué significa esto para tu arquitectura
Una capa dedicada de evaluación de modelos junto con la capa de abstracción no es un overhead opcional. Pertenece a la arquitectura.
Cuatro prácticas que importan
- Mantener registros de pares modelo/tarea conocidos para despliegues de producción confiables
- Configurar cadenas de fallback que se activan cuando un modelo falla una verificación de capacidad en tiempo de ejecución
- Implementar hooks de observabilidad para detectar la deriva de comportamiento antes de que se propague en fallos aguas abajo
- Escribir pruebas de comportamiento por proveedor que cubran el cumplimiento de esquemas de herramientas, la planificación paralela y la recuperación de errores
El compromiso fundamental
Cada framework agnóstico al proveedor te confronta con la misma tensión: abstracción universal versus acceso a capacidades específicas del modelo. Extended thinking, esquemas JSON forzados, capacidades de grounding, todo esto requiere rutas de código específicas del proveedor que rompen la abstracción.
Lista de verificación: Arquitectura de agente agnóstica al proveedor
Quien tome la portabilidad en serio necesita más que un patrón adaptador:
- Modelo de mensaje canónico + modelo de tool-call canónico: una representación interna unificada que no depende directamente de ningún proveedor
- Matriz de capacidades del proveedor: ¿qué proveedor soporta streaming, llamadas a herramientas paralelas, aplicación de esquemas, grounding?
- Pruebas de contrato para cumplimiento de herramientas: pruebas automatizadas que verifican por proveedor si los esquemas de herramientas se siguen correctamente
- Golden traces: ejecuciones de referencia (prompt + herramientas + llamadas esperadas) por proveedor que sirven como pruebas de regresión
- Política de fallback en tiempo de ejecución: escalación definida cuando un modelo falla verificaciones de capacidad en tiempo de ejecución
- Observabilidad: taxonomía de errores de tool-call + detección de deriva para capturar cambios de comportamiento entre versiones del proveedor
La conclusión
Empieza con la abstracción como predeterminado. Baja al código específico del proveedor cuando necesites las funcionalidades, pero documenta explícitamente cada uno de esos escapes.
Portabilidad como norma, optimización del proveedor como excepción deliberada.
La pregunta que debería guiar tu decisión arquitectónica no es "¿Cuál proveedor es el mejor?" sino "¿Qué partes de mi agente pueden depender del proveedor, y cuáles no?"