Módulo 1: Introducción a IA Multimodal

5. Landscape de Modelos

Descripción

El ecosistema multimodal tiene varios proveedores principales: OpenAI (GPT-4 Vision, Whisper, DALL-E, TTS), Anthropic (Claude 3), Google (Gemini). Cada uno tiene fortalezas, limitaciones y costos distintos. En esta cápsula aprenderás a compararlos con criterios concretos y elegir el adecuado para cada caso de uso. No existe "el mejor modelo" — existe "el mejor para tu caso".

Por qué importa: Elegir mal el modelo puede significar 10x más costo, peor calidad, o funcionalidades faltantes. Conocer el landscape te permite tomar decisiones informadas, diseñar fallbacks efectivos, y optimizar tu presupuesto en producción.

Conexión con el módulo: Las cápsulas 02-04 te mostraron capacidades de visión, audio y combinaciones. Aquí comparas los proveedores para saber cuándo usar cada uno. Esta información es fundamental para el Clasificador multimodal (cápsula 08), que debe recomendar el modelo óptimo.


Los 3 Grandes Proveedores

OpenAI

Ecosistema completo: GPT-4o (vision), Whisper (STT), DALL-E 3 (imagen), TTS (voz).

from openai import OpenAI
client = OpenAI()  # Una sola librería para todo

Fortalezas:

  • ✅ Ecosistema integrado (una API key para todo)
  • ✅ GPT-4o excelente en vision y razonamiento
  • ✅ Whisper líder en transcripción
  • ✅ Documentación extensa, comunidad grande
  • gpt-4o-mini ofrece buen balance calidad/precio

Debilidades:

  • ⚠️ Costos pueden acumularse rápidamente
  • ⚠️ Rate limits estrictos en tiers gratuitos/bajos
  • ⚠️ No ofrece speaker diarization en Whisper API

Anthropic

Especializado en razonamiento: Claude 3 (Sonnet, Opus, Haiku) con visión.

from anthropic import Anthropic
client = Anthropic()  # Solo chat/vision

Fortalezas:

  • ✅ Razonamiento profundo (especialmente Opus)
  • ✅ Excelente en documentos complejos y análisis extenso
  • ✅ Context window de 200K tokens
  • ✅ Buenas instrucciones de seguimiento (instruction following)
  • ✅ Menos "alucinaciones" en tareas de extracción

Debilidades:

  • ⚠️ No tiene Whisper/TTS/DALL-E (solo vision + texto)
  • ⚠️ Límite de imagen: 5MB (base64)
  • ⚠️ Más caro que GPT-4o para vision

Google (Gemini)

Contexto masivo: Gemini 1.5 Pro (1M tokens), Flash (rápido y barato).

import google.generativeai as genai
genai.configure(api_key="...")

Fortalezas:

  • ✅ Context window de 1M tokens (el más grande)
  • ✅ Gemini Flash: muy rápido y muy barato
  • ✅ Nativo multimodal (texto, imagen, audio, video en un solo modelo)
  • ✅ Tier gratuito generoso
  • ✅ Procesa video nativamente

Debilidades:

  • ⚠️ Calidad de razonamiento un paso detrás de GPT-4o y Claude 3 Opus
  • ⚠️ API menos madura que OpenAI
  • ⚠️ Menor comunidad y ejemplos

Tablas Comparativas

Vision

CriterioGPT-4o (OpenAI)GPT-4o-miniClaude 3.5 SonnetClaude 3 OpusGemini 1.5 ProGemini 1.5 Flash
OCRExcelenteBuenoExcelenteExcelenteMuy buenoBueno
RazonamientoMuy altoAltoMuy altoMáximoAltoMedio
Imágenes/reqHasta 10Hasta 10MúltiplesMúltiplesMúltiplesMúltiples
Max tamaño20MB20MB5MB (b64)5MB (b64)20MB20MB
Context128K128K200K200K1M1M
Input cost$2.50/1M$0.15/1M$3/1M$15/1M$1.25/1M$0.075/1M
Output cost$10/1M$0.60/1M$15/1M$75/1M$5/1M$0.30/1M
LatenciaMediaBajaMediaAltaMediaMuy baja

Audio (Speech-to-Text)

CriterioWhisper (OpenAI)Google STTAssemblyAI
Idiomas50+100+100+
Costo$0.006/minVariable (~$0.006-0.024)~$0.015/min
Max archivo25 MBStreaming sin límite5 hrs
Tiempo realNo
Speaker IDNo nativo
PrecisiónExcelenteExcelenteExcelente
Modelo localSí (open-source)NoNo
API simplicityMuy simpleComplejaMedia

Text-to-Speech

CriterioOpenAI TTSElevenLabsGoogle Cloud TTS
Voces6 predefinidas100+ + clonación200+ WaveNet
NaturalidadAltaMuy altaAlta
ClonaciónNoNo
Costo$15/1M charsDesde $5/moVariable
LatenciaBajaMediaBaja
IdiomasMultiidioma autoMultiidioma40+

Generación de Imágenes

CriterioDALL-E 3 (OpenAI)Stable Diffusion (Replicate)Midjourney
APISí (simple)Sí (via Replicate/APIs)No (solo web/Discord)
CalidadMuy altaAlta (configurable)Muy alta
Costo$0.04-0.08/img~$0.002-0.01/imgSuscripción
ControlPromptPrompt + params + ControlNetPrompt
Velocidad~10-20s~5-15sVariable
EdiciónVariacionesInpainting/outpaintingVariaciones

Árbol de Decisión: ¿Qué Modelo Usar?

Para Vision

¿Qué necesitas?

├── OCR de documentos simples
│   └── gpt-4o-mini (barato, bueno)
│
├── Análisis profundo de documentos complejos
│   └── Claude 3.5 Sonnet o GPT-4o (mejor razonamiento)
│
├── Volumen alto, costo bajo
│   └── Gemini 1.5 Flash (muy barato, rápido)
│
├── Context window enorme (docs largos)
│   └── Gemini 1.5 Pro (1M tokens)
│
├── Máxima calidad de razonamiento
│   └── Claude 3 Opus (caro pero superior)
│
└── Ecosistema integrado (vision + audio + imagen)
    └── OpenAI (GPT-4o + Whisper + DALL-E + TTS)

Para Audio

¿Qué necesitas?

├── Transcripción simple de archivos
│   └── Whisper (simple, barato, excelente)
│
├── Transcripción en tiempo real (streaming)
│   └── Google STT o AssemblyAI
│
├── Speaker diarization (quién dijo qué)
│   └── AssemblyAI
│
├── TTS estándar (buena calidad)
│   └── OpenAI TTS
│
└── TTS premium o clonación de voz
    └── ElevenLabs

Estrategias de Fallback

Fallback por disponibilidad

Si el proveedor principal falla (rate limit, timeout, error), usar alternativa.

from openai import OpenAI
from anthropic import Anthropic
import base64
import logging

logger = logging.getLogger(__name__)

openai_client = OpenAI()
anthropic_client = Anthropic()

def analyze_image_with_fallback(image_path: str, prompt: str) -> dict:
    """Intenta OpenAI → Anthropic → Error."""
    with open(image_path, "rb") as f:
        image_data = base64.b64encode(f.read()).decode()

    # Intento 1: OpenAI
    try:
        response = openai_client.chat.completions.create(
            model="gpt-4o",
            messages=[{
                "role": "user",
                "content": [
                    {"type": "text", "text": prompt},
                    {"type": "image_url", "image_url": {
                        "url": f"data:image/jpeg;base64,{image_data}"
                    }}
                ]
            }],
            max_tokens=500
        )
        return {
            "provider": "openai",
            "model": "gpt-4o",
            "result": response.choices[0].message.content
        }
    except Exception as e:
        logger.warning(f"OpenAI falló: {e}")

    # Intento 2: Anthropic
    try:
        response = anthropic_client.messages.create(
            model="claude-3-5-sonnet-latest",
            max_tokens=500,
            messages=[{
                "role": "user",
                "content": [
                    {
                        "type": "image",
                        "source": {
                            "type": "base64",
                            "media_type": "image/jpeg",
                            "data": image_data
                        }
                    },
                    {"type": "text", "text": prompt}
                ]
            }]
        )
        return {
            "provider": "anthropic",
            "model": "claude-3-5-sonnet",
            "result": response.content[0].text
        }
    except Exception as e:
        logger.warning(f"Anthropic falló: {e}")

    raise RuntimeError("Todos los proveedores de vision fallaron")

Tabla de prioridad de fallback

Caso de usoPrimaryFallback 1Fallback 2
Vision generalgpt-4o-miniGemini Flashgpt-4o
OCR de documentosgpt-4oClaude 3.5 SonnetGemini Pro
TranscripciónWhisperGoogle STTAssemblyAI
TTS estándarOpenAI TTSGoogle TTSElevenLabs
Generación de imagenDALL-E 3Stable Diffusion

Fallback por costo

Usar modelo barato por defecto, escalar a premium si la calidad no es suficiente.

def smart_cost_analysis(image_path: str, prompt: str) -> dict:
    """Empieza con mini, escala a pro si necesario."""
    # Paso 1: Intento con modelo económico
    result = call_vision("gpt-4o-mini", image_path, prompt)

    # Paso 2: Verificar si la respuesta es confiable
    if needs_escalation(result):
        result = call_vision("gpt-4o", image_path, prompt)
        return {"tier": "premium", "result": result}

    return {"tier": "economic", "result": result}


def needs_escalation(result: str) -> bool:
    """Detecta si el resultado necesita un modelo mejor."""
    uncertainty_phrases = [
        "no estoy seguro", "no puedo determinar",
        "la imagen no es clara", "no es posible"
    ]
    return any(phrase in result.lower() for phrase in uncertainty_phrases)

Troubleshooting

Problema 1: Rate limit exceeded

Causa: Demasiadas requests al mismo proveedor.

Solución:

import time

def call_with_rate_limit(func, *args, max_retries=3, **kwargs):
    for attempt in range(max_retries):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            if "rate_limit" in str(e).lower():
                wait = 2 ** attempt
                time.sleep(wait)
            else:
                raise
    raise RuntimeError(f"Rate limit después de {max_retries} intentos")

Problema 2: Diferencias de formato entre proveedores

Causa: OpenAI, Anthropic y Google tienen formatos de API distintos.

Solución: Crear wrappers con interfaz unificada (como se muestra en el código de fallback arriba).

Problema 3: API de Anthropic diferente a OpenAI

Causa: Las APIs tienen interfaces distintas para enviar imágenes.

Solución: Nota las diferencias clave:

# OpenAI: imagen como "image_url" con data URI
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,..."}}

# Anthropic: imagen como "image" con source object
{"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": "..."}}

# Google: usa PIL.Image directamente
model.generate_content([prompt, PIL.Image.open(path)])

Problema 4: Costos inesperados

Causa: No monitorear uso.

Solución: Implementar tracking de costos por request:

COSTS = {
    "gpt-4o": {"input": 2.50, "output": 10.0},      # por 1M tokens
    "gpt-4o-mini": {"input": 0.15, "output": 0.60},
    "whisper-1": {"per_minute": 0.006},
    "tts-1": {"per_million_chars": 15.0},
    "dall-e-3": {"per_image": 0.04},
}

def estimate_cost(model: str, input_tokens: int = 0,
                  output_tokens: int = 0) -> float:
    if model in COSTS:
        cost_info = COSTS[model]
        return (
            (input_tokens / 1_000_000) * cost_info.get("input", 0)
            + (output_tokens / 1_000_000) * cost_info.get("output", 0)
        )
    return 0.0

Ejercicios

Ejercicio 1: Tabla de decisión personalizada (Fácil)

Para un proyecto de análisis de facturas en español (10,000 facturas/mes), ¿qué proveedor elegirías y por qué? Considera costo, calidad OCR, y volumen.

Ver solución

Análisis:

  • Volumen: 10,000 facturas/mes = alto volumen
  • Requisito: OCR de facturas (texto + estructura)
  • Idioma: Español

Recomendación por tiers:

  1. Gemini 1.5 Flash: $0.075/1M tokens input → más barato por volumen. OCR bueno.
  2. GPT-4o-mini: $0.15/1M tokens → buen balance. OCR bueno. Fallback si Flash no basta.
  3. GPT-4o: Solo para facturas complejas donde mini falla.

Estrategia: Flash por defecto (90% del volumen), GPT-4o-mini para fallback (9%), GPT-4o para casos difíciles (1%).

Costo estimado: ~$75-150/mes (vs ~$2,500/mes si usaras GPT-4o para todo).

Ejercicio 2: Wrapper multi-proveedor (Medio)

Crea una función vision_analyze que acepte un parámetro provider ("openai", "anthropic", "google") y llame a la API correspondiente con la misma interfaz.

Ver solución
def vision_analyze(
    image_path: str,
    prompt: str,
    provider: str = "openai"
) -> str:
    with open(image_path, "rb") as f:
        image_data = base64.b64encode(f.read()).decode()

    if provider == "openai":
        response = openai_client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{
                "role": "user",
                "content": [
                    {"type": "text", "text": prompt},
                    {"type": "image_url", "image_url": {
                        "url": f"data:image/jpeg;base64,{image_data}"
                    }}
                ]
            }],
            max_tokens=500
        )
        return response.choices[0].message.content

    elif provider == "anthropic":
        response = anthropic_client.messages.create(
            model="claude-3-5-sonnet-latest",
            max_tokens=500,
            messages=[{
                "role": "user",
                "content": [
                    {"type": "image", "source": {
                        "type": "base64",
                        "media_type": "image/jpeg",
                        "data": image_data
                    }},
                    {"type": "text", "text": prompt}
                ]
            }]
        )
        return response.content[0].text

    elif provider == "google":
        import google.generativeai as genai
        from PIL import Image
        model = genai.GenerativeModel("gemini-1.5-flash")
        img = Image.open(image_path)
        response = model.generate_content([prompt, img])
        return response.text

    else:
        raise ValueError(f"Proveedor no soportado: {provider}")

Explicación: El wrapper unifica la interfaz de los 3 proveedores. Facilita cambiar de proveedor sin modificar el código que lo llama.

Ejercicio 3: Benchmark de proveedores (Medio)

Crea una función que envíe la misma imagen y prompt a los 3 proveedores y compare resultados (respuesta, latencia).

Ver solución
import time

def benchmark_providers(image_path: str, prompt: str) -> list[dict]:
    providers = ["openai", "anthropic", "google"]
    results = []

    for provider in providers:
        try:
            start = time.time()
            result = vision_analyze(image_path, prompt, provider)
            latency = round(time.time() - start, 2)
            results.append({
                "provider": provider,
                "status": "ok",
                "latency_s": latency,
                "response_length": len(result),
                "response_preview": result[:200]
            })
        except Exception as e:
            results.append({
                "provider": provider,
                "status": "error",
                "error": str(e)
            })

    return results

# Uso
results = benchmark_providers("test_image.jpg", "Describe esta imagen.")
for r in results:
    print(f"{r['provider']}: {r.get('latency_s', 'N/A')}s - {r['status']}")

Explicación: Benchmarking te permite decidir con datos reales qué proveedor usar para tu caso de uso específico.

Ejercicio 4: Calculator de costos (Difícil)

Crea una función que, dado un plan de uso mensual (N imágenes con vision, M minutos de audio, K imágenes generadas), calcule el costo por proveedor.

Ver solución
def estimate_monthly_cost(
    vision_images: int = 0,
    audio_minutes: int = 0,
    generated_images: int = 0,
    tts_characters: int = 0
) -> dict:
    avg_tokens_per_image = 800  # tokens promedio por imagen
    avg_output_tokens = 300

    costs = {}

    # OpenAI
    vision_cost = (
        (vision_images * avg_tokens_per_image / 1_000_000) * 2.50
        + (vision_images * avg_output_tokens / 1_000_000) * 10.0
    )
    audio_cost = audio_minutes * 0.006
    image_cost = generated_images * 0.04
    tts_cost = (tts_characters / 1_000_000) * 15
    costs["openai"] = round(
        vision_cost + audio_cost + image_cost + tts_cost, 2
    )

    # GPT-4o-mini (vision más barata)
    vision_mini = (
        (vision_images * avg_tokens_per_image / 1_000_000) * 0.15
        + (vision_images * avg_output_tokens / 1_000_000) * 0.60
    )
    costs["openai_mini"] = round(
        vision_mini + audio_cost + image_cost + tts_cost, 2
    )

    # Gemini Flash
    vision_flash = (
        (vision_images * avg_tokens_per_image / 1_000_000) * 0.075
        + (vision_images * avg_output_tokens / 1_000_000) * 0.30
    )
    costs["gemini_flash"] = round(vision_flash, 2)

    return costs

# Uso
monthly = estimate_monthly_cost(
    vision_images=10000,
    audio_minutes=500,
    generated_images=100,
    tts_characters=500000
)
print(monthly)
# Output: {"openai": 27.5, "openai_mini": 4.31, "gemini_flash": 0.69}

Explicación: La diferencia de costo entre proveedores y modelos puede ser 10-40x. Esta función te ayuda a tomar decisiones informadas antes de comprometerte con un proveedor.


Resumen

En esta cápsula aprendiste:

  • 3 proveedores principales: OpenAI (ecosistema completo), Anthropic (razonamiento profundo), Google (contexto masivo y costo bajo)
  • Cada proveedor tiene trade-offs claros en costo, calidad, latencia y funcionalidades
  • El árbol de decisión te guía: qué necesitas → qué modelo usar
  • Fallback por disponibilidad (proveedor falla → alternativa) y por costo (barato primero → premium si necesario)
  • Gemini Flash es el más barato para vision por volumen
  • Claude 3 Opus tiene el mejor razonamiento pero es el más caro
  • GPT-4o-mini ofrece el mejor balance calidad/precio para la mayoría de casos
  • Para volumen alto, la diferencia de costo entre modelos puede ser 10-40x
  • Un wrapper multi-proveedor con interfaz unificada facilita cambiar de modelo sin reescribir código

Próxima cápsula: Formatos y APIs — los detalles técnicos de Base64, URLs, tamaños y costos por endpoint.


Recursos Adicionales

  1. OpenAI Pricing — Precios actualizados de todos los modelos OpenAI
  2. Anthropic Pricing — Precios de Claude 3
  3. Google AI Pricing — Precios de Gemini (incluye tier gratuito)
  4. Replicate Models — Catálogo de modelos incluyendo Stable Diffusion
  5. ElevenLabs Pricing — Precios de TTS premium
  6. AssemblyAI Pricing — Precios de transcripción con features avanzadas