Ir al contenido principal
Migración de LLM en producción: cambia de provider sin romper tu agente

Migración de LLM en producción: cambia de provider sin romper tu agente

AI Integration
••5 min read•Por Daily Miranda Pardo

Llevas doce meses sobre un provider. Lo conoces bien, tienes tu stack afinado, el agente funciona. Entonces algo cambia: los costes suben, hay un modelo mejor en otro provider, el equipo quiere aprovechar funcionalidades que tu proveedor actual no tiene. Decides migrar.

Copias el cliente, adaptas los parámetros, despliegas. Y el agente que llevaba un año funcionando empieza a comportarse diferente, a seleccionar mal las herramientas o a fallar en casos que antes no daban ningún problema.

El problema no es la calidad del nuevo provider. Es que cada uno habla un dialecto distinto y nadie te advirtió de los detalles que importan en producción.

Por qué las APIs de LLM no son intercambiables

A nivel de documentación parece sencillo: ambos providers reciben mensajes y devuelven respuestas. En producción, las diferencias son mucho más sutiles:

Formato de tool calling:

  • OpenAI: finish_reason: "tool_calls", respuesta en message.tool_calls[]
  • Anthropic: stop_reason: "tool_use", respuesta en content[] como bloques de tipo tool_use

Parámetro de tokens:

  • OpenAI: max_completion_tokens (el antiguo max_tokens está deprecado)
  • Anthropic: max_tokens (requerido, sin valor por defecto)

Formato de streaming:

  • OpenAI: eventos ChatCompletionChunk con delta acumulativo
  • Anthropic: eventos content_block_delta con índice explícito de bloque

Resultados de tool use:

  • OpenAI: mensaje de rol tool con tool_call_id
  • Anthropic: bloque de contenido tool_result dentro de un mensaje de usuario

Cualquiera de estas diferencias, si la pasas por alto, produce fallos que no lanzan excepciones pero que sí rompen la lógica del agente silenciosamente.

El patrón: capa de abstracción + migración gradual

La solución que usamos en los proyectos de integración de agentes IA de DAILYMP tiene dos partes: una interfaz común que abstrae las diferencias, y un router de tráfico que mueve usuarios gradualmente al nuevo provider.

Paso 1: define una interfaz agnóstica de provider

// lib/llm/types.ts
export interface LLMRequest {
  messages: Message[];
  tools?: ToolDefinition[];
  maxTokens?: number;
  temperature?: number;
  systemPrompt?: string;
}

export interface LLMResponse {
  content: string;
  toolCalls?: ToolCall[];
  stopReason: 'end_turn' | 'tool_use' | 'max_tokens';
  usage: { inputTokens: number; outputTokens: number };
}

export interface LLMClient {
  complete(req: LLMRequest): Promise<LLMResponse>;
  stream(req: LLMRequest): AsyncIterable<LLMChunk>;
}

Paso 2: implementa un adapter por provider

Cada adapter traduce tu interfaz común al formato nativo del provider. El código que llama al LLM nunca importa el SDK directamente — solo usa LLMClient.

// lib/llm/anthropic-client.ts
export class AnthropicClient implements LLMClient {
  private client = new Anthropic();

  async complete(req: LLMRequest): Promise<LLMResponse> {
    const res = await this.client.messages.create({
      model: 'claude-sonnet-4-6',
      max_tokens: req.maxTokens ?? 4096,
      system: req.systemPrompt,
      messages: req.messages.map(adaptMessageToAnthropic),
      tools: req.tools?.map(adaptToolToAnthropic),
    });

    return {
      content: extractTextContent(res.content),
      toolCalls: extractToolCalls(res.content),
      stopReason: mapStopReason(res.stop_reason),
      usage: { inputTokens: res.usage.input_tokens, outputTokens: res.usage.output_tokens },
    };
  }
}

El adapter de OpenAI sigue la misma estructura pero habla el dialecto de OpenAI. Tu agente no cambia una sola línea cuando cambias de provider.

Paso 3: router de tráfico con rollout gradual

No cambias el 100% del tráfico de golpe. Empiezas con el 5%, observas los evals y métricas, y vas subiendo:

// lib/llm/router.ts
export function getLLMClient(userId: string): LLMClient {
  const rolloutPercent = getFeatureFlag('llm_provider_b_rollout'); // 5 → 20 → 50 → 100
  const bucket = hashUserId(userId) % 100;

  if (bucket < rolloutPercent) {
    return new AnthropicClient();
  }
  return new OpenAIClient();
}

// En tu handler de agente:
const llm = getLLMClient(session.userId);
const response = await llm.complete(request);

Con hashUserId determinista, el mismo usuario siempre va al mismo provider durante todo el rollout. Esto es importante para mantener coherencia en conversaciones multi-turno y para que los usuarios no experimenten comportamientos inconsistentes.

Los tres gotchas que nadie documenta

1. Los tool names deben ser únicos y sin espacios Anthropic es más estricto aquí que OpenAI. Si tienes tools con nombres como "get customer data" (con espacio), Anthropic las rechaza con un 400. Valídalos antes de empezar.

2. El system prompt de Anthropic va en un campo separado En OpenAI, el system prompt es un mensaje con role: "system" dentro del array de mensajes. En Anthropic es el campo system del objeto principal. Tu adapter tiene que extraerlo correctamente o el agente ignora las instrucciones del sistema.

3. Los mensajes de usuario y asistente deben alternarse Anthropic valida estrictamente que los mensajes alternen entre user y assistant. Si tienes dos mensajes de usuario consecutivos (algo que OpenAI permite), la migración falla inmediatamente. Normaliza el historial en el adapter.

Cuándo ejecutar los evals de validación

En la migración gradual, los evals son la puerta de entrada a cada fase. Antes de pasar del 5% al 20%, corres el eval suite completo sobre los usuarios del 5% y validas que el score no haya caído. Si cae, reviertes el rollout a 0% en segundos.

Para un agente de producción bien instrumentado, la migración completa entre providers suele tomar entre dos y tres semanas a este ritmo. Es un proceso aburrido, lento y casi sin incidentes. Exactamente como debería ser.

Por qué hacerlo bien desde el principio

Si ya tienes un agente en producción sin capa de abstracción, tienes deuda técnica que va a cobrarte en el peor momento. La siguiente vez que un provider suba precios, deje de dar soporte a una versión de API o simplemente salga un modelo mejor en otro sitio, tendrás que reescribir el núcleo del sistema en lugar de cambiar una línea de configuración.

En los proyectos que montamos en DAILYMP este patrón va incluido desde el día uno. No como una decisión de arquitectura exótica — como higiene básica de un sistema que va a vivir en producción durante años.

Si tienes un agente corriendo sobre un provider y estás pensando en una migración, o si quieres revisar si tu arquitectura actual soporta un cambio sin coste en el futuro, hablamos.

Escríbeme por WhatsApp →

Compartir artículo

LinkedInXWhatsApp

¿Procesos repetitivos en tu empresa?

Descarga gratis el Mapa de Automatización IA — los 5 procesos que más tiempo roban y cómo resolverlos.

Sin spam. Solo el PDF. Puedes darte de baja cuando quieras.

Escrito por Daily Miranda Pardo

Ayudo a empresas a automatizar procesos, crear agentes IA y conectar sistemas inteligentes.