Voltar para o blog
post.md

Como desenhar uma arquitetura multi-LLM com Claude, Gemini e OpenAI

Entenda como criar uma arquitetura multi-LLM com AI Gateway, adapters, roteamento, fallback, controle de custos e observabilidade sem exagerar na abstração.

IA Full StackMulti-LLMClaudeGeminiOpenAIArquitetura

Usar vários modelos de IA não deveria significar espalhar chamadas para APIs diferentes por todo o sistema. Uma camada central com adapters, roteamento, fallback e observabilidade ajuda a organizar essa complexidade.

Contexto

Conforme ferramentas de IA evoluem, fica comum testar diferentes provedores para tarefas diferentes. Um modelo pode ser melhor para análise longa. Outro pode ser mais econômico para classificações simples. Outro pode ter melhor suporte multimodal.

O problema aparece quando essa diversidade vira acoplamento: um controller chama OpenAI, um worker chama Claude, outro serviço chama Gemini e cada ponto trata erro, formato, custo e logs de um jeito diferente.

Arquitetura multi-LLM com backend conectado a um AI Gateway e adapters para OpenAI, Claude, Gemini e modelo local.
O gateway centraliza decisões, mas os adapters preservam as diferenças de cada provedor.

O papel de um AI Gateway

O nome importa menos do que a responsabilidade. Um AI Gateway é uma camada da aplicação que centraliza como chamar modelos, validar respostas, medir custo, aplicar fallback e registrar comportamento.

  • Padronizar entradas e saídas.
  • Selecionar modelo por tarefa, custo, latência ou contexto.
  • Executar retries e fallback com limites.
  • Aplicar cache ou limites de uso quando fizer sentido.
  • Registrar métricas de custo, erro, duração e qualidade.
  • Validar respostas antes de devolver para o domínio.
ai-gateway.types.tsts
1type AIProviderId = 'openai' | 'anthropic' | 'google' | 'local';23type AIRequest = {4  feature: 'summary' | 'classification' | 'code_review';5  input: unknown;6  constraints: {7    maxLatencyMs?: number;8    maxCostCents?: number;9    requiresJson: boolean;10    requiresVision?: boolean;11  };12  trace: {13    userId?: string;14    projectId?: string;15    requestId: string;16  };17};1819type AIResponse<T = unknown> = {20  provider: AIProviderId;21  model: string;22  output: T;23  usage?: {24    inputTokens: number;25    outputTokens: number;26    estimatedCostCents: number;27  };28  latencyMs: number;29};3031interface AIProvider {32  generate<T>(request: AIRequest): Promise<AIResponse<T>>;33}

Adapters reduzem acoplamento

A ideia dos adapters é simples: cada provedor pode ter SDK, formato de resposta, parâmetros e erros próprios, mas o restante da aplicação conversa com uma interface comum.

Isso não significa fingir que todos os modelos são iguais. Alguns recursos são específicos de cada provedor. O objetivo é isolar diferenças operacionais sem esconder decisões importantes.

model-registry.tsts
1const modelRegistry = {2  cheapClassifier: {3    provider: 'openai',4    model: 'small-classifier',5    maxCostCents: 1,6  },7  longContextSummary: {8    provider: 'anthropic',9    model: 'large-context-model',10    maxInputTokens: 180_000,11  },12  multimodalAnalysis: {13    provider: 'google',14    model: 'vision-capable-model',15    requiresVision: true,16  },17} as const;

Roteamento e fallback precisam de critério

O roteamento pode considerar tipo da tarefa, custo, latência, tamanho de contexto, necessidade multimodal, qualidade esperada, disponibilidade e restrições do projeto.

  • Classificação simples pode usar um modelo mais econômico.
  • Documento grande pode exigir contexto maior.
  • Análise de imagem precisa de modelo multimodal.
  • Tarefa crítica pode usar modelo principal e validação adicional.
  • Falha temporária pode acionar retry; indisponibilidade pode acionar fallback.

Fallback parece simples, mas pode duplicar custo rapidamente se for usado sem limite. É preciso definir quais erros justificam nova tentativa, quando trocar de modelo e quando devolver um erro controlado.

routing-policy.jsonjson
1{2  "summary": {3    "primary": "longContextSummary",4    "fallback": ["cheapClassifier"],5    "retry": {6      "maxAttempts": 2,7      "retryOn": ["timeout", "rate_limit", "provider_unavailable"]8    }9  },10  "classification": {11    "primary": "cheapClassifier",12    "fallback": [],13    "retry": {14      "maxAttempts": 1,15      "retryOn": ["provider_unavailable"]16    }17  },18  "critical_review": {19    "primary": "longContextSummary",20    "validator": "cheapClassifier",21    "requiresHumanReview": true22  }23}

Observabilidade não é detalhe

Se a aplicação usa vários modelos, eu gostaria de responder perguntas simples: qual modelo está sendo mais usado, qual funcionalidade custa mais, qual provedor falha mais, quando o fallback foi acionado e quantas respostas vieram inválidas.

Para isso, cada chamada deveria registrar provedor, modelo, tokens quando disponíveis, custo estimado, funcionalidade, origem, duração, status, erro e avaliação do resultado.

Quando não vale a pena

Nem todo projeto precisa de arquitetura multi-LLM. Se existe uma única funcionalidade simples, um único modelo e baixo risco, criar uma camada abstrata demais pode ser exagero.

Conclusão

Arquitetura multi-LLM não é sobre usar vários provedores por moda. É sobre criar um jeito mais controlado de decidir quando, como e por que cada modelo entra em uma funcionalidade.

Abstração boa resolve um problema real. Abstração antecipada só troca bagunça simples por bagunça sofisticada.