Tag: LLM

  • La comparativa definitiva en Mac: oMLX vs llama.cpp

    La comparativa definitiva en Mac: oMLX vs llama.cpp

    Cuando decides correr modelos de lenguaje locales en tu Mac, casi toda la documentación y tutoriales de internet te apuntan al mismo lugar: descarga Ollama o compila llama.cpp. Son las herramientas estándar y más populares del ecosistema. Como vimos en nuestra guía de hardware y modelos para LLMs locales, correr modelos en local requiere conocer bien los límites de tu máquina.

    Sin embargo, la arquitectura de Apple Silicon es particular. Y tratar a tu Mac como si fuera una máquina x86 genérica con una tarjeta de video tradicional es desperdiciar la potencia de su memoria unificada.

    En pruebas reales con agentes y tareas de larga duración, oMLX y llama.cpp demuestran comportamientos radicalmente opuestos.

    Hoy te quiero enseñar la comparativa de rendimiento definitiva de oMLX vs llama.cpp para que sepas exactamente cuál elegir según las necesidades específicas de tus agentes de IA.


    Latencia inicial vs. Velocidad sostenida

    El error más común al medir la velocidad de una IA local es fijarse únicamente en la velocidad de generación final (tokens por segundo). En entornos reales, el rendimiento se divide en dos métricas críticas:

    1. TTFT (Time to First Token / Tiempo hasta el primer token): Es el tiempo que tarda la GPU en procesar el prompt de entrada antes de empezar a responder.
    2. Generación Sostenida: La velocidad pura de escritura una vez que el modelo ha empezado a hablar.

    En las comparativas oficiales utilizando hardware idéntico y el modelo Qwen3.5-9B, los resultados arrojan una conclusión nítida:

    • llama.cpp (Ollama / LM Studio): Es el rey indiscutible de la baja latencia (TTFT). Empieza a responder prácticamente al instante. Es la mejor opción para interfaces de chat interactivas donde el usuario espera una reacción visual inmediata.
    • oMLX (Framework MLX): Aunque tarda unos segundos más en pre-procesar el prompt inicial (fase de prefill), su velocidad de generación sostenida es masiva, llegando a multiplicar por hasta 4.14x el rendimiento bajo concurrencia gracias a su arquitectura de continuous batching y caché SSD.

    La batalla por la Memoria: ¿GGUF o MLX?

    El formato de los pesos que descargas determina cómo el servidor exprime tu hardware:

    llama.cpp y el ecosistema GGUF

    El formato GGUF está sumamente optimizado para ejecutarse en memorias ajustadas (como portátiles de 16GB de RAM). Puedes correr modelos cuantizados ligeros consumiendo muy pocos recursos del sistema, lo que te permite mantener otras aplicaciones abiertas sin comprometer el rendimiento general de tu Mac.

    oMLX y los directorios MLX

    oMLX trabaja exclusivamente con formatos nativos de MLX. Este formato espera que el modelo cargue directamente sobre la memoria unificada Metal de Apple Silicon. Para exprimirlo en tareas pesadas de código o multi-agente, el sweet spot recomendado de hardware sube a 64GB de RAM unificada o superior.

    Si cuentas con ese hardware, oMLX utiliza su pila de caché de dos niveles (PagedSSDCacheManager y PagedCacheManager) para mover las partes frías del contexto de tu IDE al SSD de tu Mac, permitiendo restaurar prefijos de forma inmediata sin re-computar y ahorrando gigabytes de memoria real.


    Cuándo elegir cada motor para tus Agentes

    La decisión arquitectónica óptima es sencilla si analizas el caso de uso:

    Elige oMLX si:

    • Estás desarrollando con agentes autónomos multitarea en segundo plano (como Hermes Agent) que envían múltiples peticiones paralelas gracias al continuous batching.
    • Trabajas con contextos de código muy largos donde la reutilización de prefijos de caché de oMLX evita tener que procesar todo el repositorio en cada llamada.
    • Tienes un Mac de desarrollo de gama alta (32GB, 64GB o Studio/Mini dedicados).

    Elige llama.cpp (Ollama) si:

    • Tu flujo de trabajo es puramente interactivo (un chat directo uno a uno donde el TTFT es prioritario).
    • Corres la IA en movilidad en un portátil con recursos de RAM ajustados (16GB).
    • Necesitas portabilidad multiplataforma (escribir scripts que deban correr idénticos tanto en macOS como en Windows o servidores Linux).

    Esta comparativa y análisis de optimización de infraestructura local es la que ponemos en práctica en el curso de Construye con IA para calibrar entornos y que detallamos a nivel de código de bajo nivel en el nuevo curso de Hermes Agent.


    Conclusión: El software correcto para el silicio correcto

    No limites el rendimiento de tu hardware Apple Silicon por usar loaders de propósito general. Entender cuándo delegar tus tareas a llama.cpp para interactividad rápida o a oMLX para tareas pesadas de automatización autónoma te dará una ventaja competitiva masiva en velocidad y coste de computación local.

    Si quieres debatir con otros desarrolladores senior sobre benchmarks de modelos locales y optimización de cachés, te espero en Dominicode Labs.


    Preguntas Frecuentes (FAQ)

    ¿Cuál es el sweet spot de hardware para usar oMLX en Mac?

    Aunque puede funcionar en máquinas con 16GB de memoria unificada utilizando modelos pequeños de 7B u 8B parámetros bien cuantizados, oMLX destaca especialmente en configuraciones de 64GB de RAM o más. En estas capacidades, permite cargar múltiples modelos en paralelo y utilizar cachés de contexto masivas sin swap de disco ralentizado.

    ¿Se pueden convertir modelos de formato Hugging Face a MLX de forma sencilla?

    Sí. La librería oficial de Apple mlx-lm incluye una utilidad de consola muy sencilla. Ejecutando python -m mlx_lm.convert --hf-path autor/modelo -q puedes descargar, convertir y cuantizar cualquier modelo compatible directamente desde Hugging Face a tu máquina en formato nativo para oMLX.

    ¿Cómo ayuda el continuous batching de oMLX a los agentes de IA?

    Si ejecutas sub-agentes paralelos (por ejemplo, un agente de código y un agente de testing al mismo tiempo), llama.cpp clásico procesará sus peticiones de forma secuencial (encolándolas). oMLX las agrupa de forma dinámica en la GPU de Apple, procesando ambas respuestas simultáneamente sin duplicar la latencia.

    ¿Ollama soporta el formato nativo de MLX?

    No. Ollama está basado en llama.cpp y su arquitectura interna gira en torno a la carga y ejecución de archivos en formato GGUF. Para correr modelos en formato MLX utilizando aceleración nativa pura de Apple, debes utilizar servidores específicos del ecosistema como oMLX o el servidor propio de mlx-lm.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • GPT-5.6 vía API: guía práctica para developers

    GPT-5.6 vía API: guía práctica para developers

    Actualizas el string del modelo en tu código. gpt-5.5 pasa a gpt-5.6. Compilas, despliegas, todo responde igual… hasta que llega la factura de OpenAI y el costo por output es seis veces más alto que la semana pasada.

    No rompiste nada. Elegiste mal el modelo.

    GPT-5.6 no es un modelo — es una familia de tres, con nombres que no explican nada hasta que entiendes el sistema detrás. Si vienes de GPT-5.5 o evalúas moverte desde Claude, migrar sin leer la letra pequeña te puede salir caro. Literalmente.

    Esta es la guía que me hubiera gustado tener el día del lanzamiento: qué es cada variante, cómo llamarlas desde la API con TypeScript, qué cambia con Programmatic Tool Calling, y si vale la pena tocar tu stack si ya construyes con Claude Code.

    Qué es realmente GPT-5.6 (y por qué el nombre importa)

    OpenAI cambió el sistema de versionado con este lanzamiento. El número —5.6— identifica la generación. El nombre —Sol, Terra o Luna— identifica el nivel de capacidad, y cada nivel puede avanzar a su propio ritmo sin esperar un salto de versión completo.

    Tres variantes, disponibles desde el 9 de julio de 2026 en ChatGPT, Codex y la API:

    • Sol — el modelo insignia. Máxima capacidad de razonamiento.
    • Terra — el punto medio. Buen rendimiento a mejor precio.
    • Luna — el económico. Para volumen alto y tareas simples.

    Los tres comparten ventana de contexto: 1.05M tokens de entrada, 128K tokens de salida como máximo. La diferencia no está en cuánto texto aguantan — está en cuánto “piensan” antes de responder.

    Cómo llamar a GPT-5.6 desde la API

    Cada variante tiene su propio slug: gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna. Si usas el alias gpt-5.6 a secas, apunta a Sol por defecto.

    import OpenAI from "openai";
    
    const client = new OpenAI();
    
    const response = await client.responses.create({
      model: "gpt-5.6-terra",
      reasoning: { effort: "medium" },
      input: "Resume los cambios de este PR en 3 bullets técnicos.",
    });
    
    console.log(response.output_text);
    

    Nota el endpoint: responses.create, no chat.completions.create. GPT-5.6 usa la Responses API para razonamiento y tool-calling. Chat Completions sigue funcionando, pero no tiene acceso a las funciones nuevas de esta generación.

    El parámetro que de verdad mueve la aguja del costo es reasoning.effort. Acepta none, low, medium, high, xhigh y max, con medium por defecto. Si vienes de GPT-5.5, la recomendación de OpenAI es probar un nivel por debajo del que ya usabas. GPT-5.6 suele mantener la calidad con menos tokens de razonamiento, y eso es directamente menos costo por request.

    Hay un segundo parámetro, reasoning.mode, que acepta "pro" para forzar que el modelo trabaje más antes de devolver una única respuesta final. Resérvalo para tareas donde una respuesta mediocre te cuesta más que los tokens extra. Piensa en debugging de un incidente en producción, no en un endpoint de autocompletado.

    Programmatic Tool Calling: la función que cambia cómo diseñas agentes

    Esto es lo más relevante si ya construyes agentes con function calling. Antes, cada llamada a una tool implicaba un round-trip completo: el modelo pide la tool, tú la ejecutas, le devuelves el resultado, el modelo decide el siguiente paso. En un workflow con cinco o seis tools, eso son cinco o seis idas y vueltas completas al modelo.

    Programmatic Tool Calling elimina la mayoría de esos round-trips.

    Con Programmatic Tool Calling, GPT-5.6 escribe JavaScript que se ejecuta en un sandbox V8 aislado —sin acceso a red— y coordina varias llamadas a tools dentro de un mismo turno:

    // prStatusSchema y coverageSchema son schemas de Zod definidos aparte, omitidos aquí por brevedad
    const response = await client.responses.create({
      model: "gpt-5.6-sol",
      tools: [
        { type: "function", name: "get_pr_status", parameters: prStatusSchema },
        { type: "function", name: "get_test_coverage", parameters: coverageSchema },
      ],
      input: "Revisa el PR #482 y dime si está listo para mergear.",
    });
    

    Internamente, el modelo puede generar algo como esto y ejecutarlo sin volver a consultarte:

    const [status, coverage] = await Promise.all([
      tools.get_pr_status({ pr: 482 }),
      tools.get_test_coverage({ pr: 482 }),
    ]);
    

    Sin round-trips intermedios. Aquí conviene una precisión, porque circula mucho porcentaje sin fuente: la documentación oficial de Programmatic Tool Calling no publica ninguna cifra de ahorro. Dice que el efecto depende de la tarea y de lo que devuelvan tus tools, y recomienda medirlo comparando ambos enfoques sobre casos representativos. Las únicas cifras publicadas de este patrón son de Anthropic, sobre su propia implementación: de 43.588 a 27.297 tokens de media, un 37% menos en tareas de investigación compleja. Si tu agente encadena varias tools de forma predecible —no necesitas el juicio del modelo entre cada paso— esta es la razón concreta para migrar a la Responses API si aún no lo has hecho.

    Ojo: esta función solo existe en la Responses API. Chat Completions no la soporta.

    Valida la salida con Zod, no confíes en el string

    Cuando le pides a GPT-5.6 —o a cualquier LLM— que devuelva JSON, vas a recibir respuestas que casi cumplen tu schema. Ese “casi” es el problema en producción.

    import { z } from "zod";
    
    const AnalisisDiffSchema = z.object({
      resumen: z.string(),
      cambiosBreaking: z.boolean(),
      archivosAfectados: z.array(z.string()),
    });
    
    const response = await client.responses.create({
      model: "gpt-5.6-terra",
      input: `Analiza este diff y responde en JSON: ${diff}`,
    });
    
    const analisis = AnalisisDiffSchema.parse(JSON.parse(response.output_text));
    

    Si el modelo devuelve un campo de más, uno de menos, o un tipo equivocado, parse lanza el error ahí mismo — no seis pasos después, cuando ya rompiste el pipeline de otro sistema. Es exactamente el patrón que trabajo en el curso de Zod para TypeScript: la validación no es opcional cuando la fuente de tus datos es un modelo probabilístico.

    Precios: Sol, Terra, Luna, y dónde entra Claude

    Modelo Input Output Contexto
    GPT-5.6 Sol $5 $30 1.05M / 128K salida
    GPT-5.6 Terra $2.50 $15 1.05M / 128K salida
    GPT-5.6 Luna $1 $6 1.05M / 128K salida
    Claude Sonnet 5 (hasta 31 ago 2026) $2 $10 1M / 128K salida
    Claude Sonnet 5 (desde 1 sep 2026) $3 $15 1M / 128K salida

    Con estos números, Terra es el punto de comparación real contra Claude Sonnet 5 — ambos apuntan al mismo caso de uso: producción, buen razonamiento, sin pagar precio de flagship. Sol solo se justifica cuando el problema es genuinamente difícil: razonamiento largo, agentes con muchos pasos, código complejo donde una respuesta mediocre cuesta más en debugging que en tokens.

    Una advertencia sobre comparar precios “por token” entre proveedores: no son manzanas con manzanas. Claude Sonnet 5 estrenó un tokenizer nuevo que genera hasta 30% más tokens para el mismo texto que su versión anterior, según la documentación oficial de Claude. El precio por millón de tokens no te dice el costo real de tu prompt — para eso necesitas correr tus prompts reales contra ambos modelos y medir.

    Cuándo quedarte en Claude Code y cuándo meter GPT-5.6 en tu stack

    Si ya tienes armado tu stack de IA agéntica alrededor de Claude Code, no hay ninguna urgencia de migrar todo. El ecosistema de agentes, skills y MCP que ya tienes montado no se traslada gratis a otro proveedor — cambiar de modelo no es cambiar un string, es revalidar todo el comportamiento agéntico que dependía de ese modelo específico.

    Donde sí tiene sentido meter GPT-5.6 en tu stack:

    • Tareas de alto volumen y baja complejidad. Usa Luna. Es una fracción del costo de Sol o de Claude Sonnet 5 para clasificación, extracción o resúmenes cortos.
    • Workflows con muchas tool calls predecibles. Programmatic Tool Calling puede recortar tu factura de forma directa, sin tocar la lógica de negocio.
    • Comparar output real en tu caso de uso. Nada reemplaza correr el mismo prompt contra Terra y contra Claude Sonnet 5 con tus datos reales, no con benchmarks genéricos.

    Este tipo de decisión —qué modelo, para qué tarea, con qué presupuesto— es exactamente el criterio que trabajamos en el curso Construye con IA: no se trata de casarte con un proveedor, se trata de construir producto sin quemar presupuesto en la elección equivocada.

    Qué hacer hoy

    Sin escribir una línea de producto nuevo, puedes:

    1. Correr tu endpoint más caro contra gpt-5.6-terra con reasoning.effort: "low" y comparar costo y calidad contra tu modelo actual.
    2. Si tu agente encadena tres o más tools por turno, prueba Programmatic Tool Calling en un flujo de staging y mide la reducción real de tokens.
    3. Añade un schema de Zod a cualquier endpoint que hoy confíe en el JSON crudo de un LLM.

    Ninguno de estos tres pasos te compromete a nada. Son experimentos de una tarde que te dan datos reales en lugar de benchmarks de marketing.

    Si quieres ver estos patrones aplicados en proyectos completos —no solo snippets sueltos— en Dominicode Labs están el código y las decisiones de arquitectura detrás de cada integración.

    Preguntas frecuentes

    ¿Qué significa que GPT-5.6 tenga tres variantes (Sol, Terra, Luna)?

    OpenAI separó el número de versión del nivel de capacidad. El 5.6 es la generación; Sol, Terra y Luna son niveles de capacidad que pueden evolucionar en su propio calendario, sin esperar a un salto de versión completo.

    ¿Qué variante de GPT-5.6 debo usar por defecto en producción?

    Depende del caso de uso. Terra es el punto de equilibrio para la mayoría de aplicaciones de producción. Sol solo se justifica en tareas de razonamiento largo o agentes con muchos pasos. Luna sirve para volumen alto y tareas simples, donde el costo por token importa más que el techo de capacidad.

    ¿GPT-5.6 es más barato o más caro que Claude Sonnet 5?

    Depende de la variante. Terra ($2.50 input / $15 output por millón de tokens) queda cerca del precio estándar de Claude Sonnet 5 ($3 / $15 desde el 1 de septiembre de 2026). Sol es notablemente más caro. Luna es la opción más económica de las dos familias. Compara con tus prompts reales, no solo con la tabla de precios — los tokenizers no son iguales entre proveedores.

    ¿Necesito reescribir mi código si vengo de GPT-5.5?

    En parte. El endpoint de la Responses API se mantiene, pero OpenAI recomienda tratar la migración como un ajuste de reasoning.effort, no solo un cambio de string en el nombre del modelo. Probar un nivel de esfuerzo por debajo del que usabas suele mantener la calidad con menos costo.

    ¿Programmatic Tool Calling funciona con Chat Completions?

    No. Es una función exclusiva de la Responses API. Si tu integración sigue en Chat Completions, no tienes acceso a esta función ni a otras capacidades nuevas de la generación GPT-5.6.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Cómo correr LLMs locales en 2026: Guía de hardware y modelos

    Cómo correr LLMs locales en 2026: Guía de hardware y modelos

    El mes pasado vi la factura de API de OpenAI de un desarrollador independiente que estaba probando un agente de traducción automática de bases de datos. Había consumido $842 USD en un solo fin de semana debido a un bucle infinito de prompts que devoró el contexto de su modelo repetidamente.

    Casi le da algo.

    La experimentación con agentes de IA es el futuro, pero depender ciegamente de APIs en la nube puede ser una ruina financiera para desarrolladores independientes o empresas con políticas estrictas de privacidad.

    Hoy te quiero explicar cómo configurar tu entorno para correr LLMs locales en 2026, analizando qué hardware necesitas realmente y qué modelos de código abierto superan a las opciones comerciales para desarrollo local.


    Por qué el desarrollo local es el estándar en 2026

    Hasta hace poco, correr un modelo en tu propio ordenador era una experiencia frustrante: los modelos pequeños de 7B parámetros eran lentos, "alucinaban" demasiado y carecían de capacidades de razonamiento para escribir código complejo.

    En 2026, la situación ha cambiado radicalmente por tres factores:

    1. Eficiencia en la cuantización: Gracias a formatos avanzados de compresión (como GGUF y EXL2), un modelo de 8B o 14B parámetros mantiene el 98% de su precisión consumiendo la mitad de VRAM.
    2. Capacidad de razonamiento nativa: Modelos como Llama 3.3, Qwen 2.5 Coder y la serie DeepSeek R1 en local ofrecen razonamiento avanzado sin salir de tu máquina.
    3. Privacidad absoluta: Tus datos de código, logs de clientes y bases de datos nunca viajan por internet.

    Correr modelos locales es la mejor forma de testear tus agentes y automatizaciones antes de desplegarlos a producción en la nube.


    El Hardware que necesitas (VRAM es el único rey)

    El error más común al planificar un entorno local de IA es invertir en procesadores rápidos (CPU) o grandes cantidades de memoria RAM convencional. Para la IA, la velocidad del procesamiento y la latencia dependen de la VRAM (Memoria de Vídeo) de tu tarjeta gráfica.

    Aquí tienes la matriz de hardware recomendada según tu presupuesto y objetivos en 2026:

    Nivel Hardware Mínimo Capacidad de Modelos
    Básico (Estudiante) GPU de 8GB VRAM (RTX 4060) o Mac M-Series (16GB RAM) Llama 3.2 3B / Qwen 2.5 Coder 7B (Cuantizados)
    Sweet Spot (Developer) GPU de 16GB VRAM (RTX 4080 / 4070Ti) o Mac M-Series (36GB RAM) Llama 3.1 8B / Qwen 2.5 Coder 14B (Precisión Completa)
    Avanzado (Enterprise) 2x GPU de 24GB VRAM (RTX 3090/4090) o Mac Studio (64GB+ RAM) Llama 3.3 70B / DeepSeek R1 32B (Razonamiento Completo)

    Si eres usuario de Mac, la memoria unificada de Apple Silicon funciona como VRAM. Un Mac Mini o Macbook Pro con 36GB o 64GB de RAM unificada es una de las soluciones más eficientes y silenciosas para correr agentes locales.


    Los mejores modelos locales para Developers en 2026

    Si tu objetivo principal es escribir código, configurar bases de datos o crear agentes DevOps, no uses modelos genéricos. Estos son los reyes del código abierto en 2026:

    • Qwen 2.5 Coder (7B y 14B): Es el rey indiscutible para autocompletado y edición en IDEs como Cursor o VS Code. Supera a muchos modelos propietarios en sintaxis de TypeScript, Python y Rust.
    • Llama 3.1 (8B) / Llama 3.3 (70B): La opción de Meta es la más estable para agentes conversacionales que requieren memoria semántica persistente o integrarse con herramientas externas.
    • DeepSeek R1 (Versiones destiladas de 8B o 14B): Excelente para resolución de bugs complejos y optimización de algoritmos que requieren pasos de pensamiento lógico antes de emitir una respuesta.

    Setup de Arranque Rápido con Ollama

    La forma más sencilla de empezar hoy es utilizar Ollama, una herramienta que gestiona los modelos locales en segundo plano y expone una API compatible con OpenAI para que puedas conectarla a cualquier aplicación.

    1. Descarga Ollama de su sitio oficial.
    2. Ejecuta en tu terminal el modelo deseado:
      ollama run qwen2.5-coder:7b
      
    3. Conecta tus agentes o herramientas de desarrollo apuntando la API Base a: http://localhost:11434/v1.

    Este es exactamente el flujo de base local que enseñamos a configurar y optimizar en nuestro curso de Construye con IA para evitar costes recurrentes de API durante el desarrollo de productos.


    Conclusión: Controla tus costes de desarrollo

    Depender exclusivamente de la nube no solo te hace vulnerable a caídas de red y cambios de precios de API, sino que limita tu velocidad de experimentación. Al aprender a correr LLMs locales, desbloqueas pruebas infinitas y seguras, las cuales son ideales para testear el bucle agéntico o agentic loop sin costes de API.

    Si quieres debatir sobre configuraciones de hardware personalizadas, benchmarks de modelos en local y cómo conectar estos LLMs a tus pipelines de producción, te espero en Dominicode Labs.


    Preguntas Frecuentes (FAQ)

    ¿Se pueden correr LLMs locales en 2026 sin tarjeta gráfica (GPU)?

    Sí, herramientas como Ollama y Llama.cpp admiten ejecución en CPU utilizando la memoria RAM del sistema. Sin embargo, la velocidad de generación (tokens por segundo) será extremadamente lenta en comparación con una GPU, lo que los hace poco prácticos para flujos de desarrollo ágiles.

    ¿Qué es la cuantización de un modelo de IA?

    Es un proceso de compresión matemática que reduce la precisión de los pesos del modelo (por ejemplo, de 16 bits a 4 u 8 bits). Esto reduce drásticamente el uso de VRAM y memoria, permitiendo correr modelos grandes en tarjetas gráficas de gama media con una pérdida de precisión casi imperceptible.

    ¿Ollama es compatible con herramientas como Cursor o VS Code?

    Sí, Ollama expone un servidor local compatible con la especificación de API de OpenAI. Puedes configurar tu editor de código o framework de agentes favorito para que use la URL http://localhost:11434 como proveedor personalizado y consuma tus modelos locales de forma directa.

    ¿Qué modelo local es mejor para desarrollo de software en 2026?

    Para autocompletado y redacción de código rápido, Qwen 2.5 Coder (en sus variantes de 7B o 14B) ofrece el mejor rendimiento en relación al consumo de recursos. Para tareas complejas de depuración o lógica pesada, las variantes cuantizadas de DeepSeek R1 son la opción recomendada.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • NestJS + Vercel AI SDK: backend streaming IA en producción

    NestJS + Vercel AI SDK: backend streaming IA en producción

    ANTHROPIC_API_KEY=sk-ant-xxxxxxxx

    
    En `app.module.ts`, registra `ConfigModule`:
    
    ```typescript
    // src/app.module.ts
    import { Module } from '@nestjs/common';
    import { ConfigModule } from '@nestjs/config';
    import { AiModule } from './ai/ai.module';
    
    @Module({
      imports: [
        ConfigModule.forRoot({ isGlobal: true }),
        AiModule,
      ],
    })
    export class AppModule {}
    

    isGlobal: true significa que ConfigService está disponible en todos los módulos sin importarlo individualmente. Práctico.


    La estructura del AiModule

    Antes de escribir código, la estructura:

    src/
      ai/
        ai.module.ts
        ai.controller.ts
        ai.service.ts
        dto/
          chat.dto.ts
    

    Cuatro archivos. Eso es todo lo que necesita un endpoint de streaming limpio.


    Paso 1: El DTO de validación

    El primer punto de defensa es el DTO. Define el contrato del request:

    // src/ai/dto/chat.dto.ts
    import { IsArray, IsIn, IsString, ValidateNested, ArrayMinSize } from 'class-validator';
    import { Type } from 'class-transformer';
    
    export class ChatMessageDto {
      @IsIn(['user', 'assistant', 'system'])
      role: 'user' | 'assistant' | 'system';
    
      @IsString()
      content: string;
    }
    
    export class ChatRequestDto {
      @IsArray()
      @ArrayMinSize(1)
      @ValidateNested({ each: true })
      @Type(() => ChatMessageDto)
      messages: ChatMessageDto[];
    }
    

    @ValidateNested({ each: true }) valida cada elemento del array individualmente. Si el frontend manda un mensaje con role: 'hacker' o sin content, el request rebota antes de tocar el servicio.

    Para que ValidationPipe funcione globalmente, añádelo en main.ts:

    // src/main.ts
    import { NestFactory } from '@nestjs/core';
    import { ValidationPipe } from '@nestjs/common';
    import { AppModule } from './app.module';
    
    async function bootstrap() {
      const app = NestFactory.create(AppModule);
    
      app.useGlobalPipes(new ValidationPipe({
        transform: true,
        whitelist: true,    // elimina propiedades no declaradas en el DTO
        forbidNonWhitelisted: true,
      }));
    
      // CORS para el frontend Angular en desarrollo
      app.enableCors({
        origin: process.env.FRONTEND_URL ?? 'http://localhost:4200',
        methods: ['POST', 'OPTIONS'],
      });
    
      await app.listen(process.env.PORT ?? 3000);
    }
    
    bootstrap();
    

    whitelist: true es especialmente importante aquí: elimina cualquier campo del body que no esté declarado en el DTO. Si alguien intenta inyectar propiedades extra en el request, NestJS las ignora antes de que lleguen al servicio.


    Paso 2: El AiService

    El servicio encapsula toda la lógica de llamada al modelo. El controlador no sabe qué modelo usamos ni cómo se configura — solo llama al servicio y recibe el stream.

    // src/ai/ai.service.ts
    import { Injectable } from '@nestjs/common';
    import { ConfigService } from '@nestjs/config';
    import { streamText, CoreMessage } from 'ai';
    import { createAnthropic } from '@ai-sdk/anthropic';
    
    @Injectable()
    export class AiService {
      private readonly anthropic;
    
      constructor(private readonly config: ConfigService) {
        this.anthropic = createAnthropic({
          apiKey: this.config.getOrThrow<string>('ANTHROPIC_API_KEY'),
        });
      }
    
      streamChat(messages: CoreMessage[]) {
        return streamText({
          model: this.anthropic('claude-sonnet-4-6'),
          system: `Eres un asistente técnico especializado en desarrollo de software.
    Responde en español de forma concisa y directa.
    Si el usuario pregunta sobre código, incluye ejemplos concretos.`,
          messages,
          maxTokens: 1024,
        });
      }
    }
    

    Dos decisiones importantes aquí:

    createAnthropic({ apiKey }) en el constructor — el cliente de Anthropic se crea una sola vez cuando NestJS instancia el servicio. No se recrea en cada petición. Eso evita overhead innecesario.

    config.getOrThrow<string>('ANTHROPIC_API_KEY') — si la variable de entorno no existe, la app falla en el arranque con un error claro en lugar de fallar silenciosamente en el primer request. Fail fast.

    maxTokens: 1024 es un límite defensivo. Sin él, un usuario puede hacer una pregunta que genere una respuesta de 8.000 tokens, multiplicando el costo por 8. Ajusta según tu caso de uso.


    Paso 3: El AiController con streaming

    El controlador es donde ocurre la magia del streaming. La clave está en cómo NestJS maneja la respuesta HTTP nativa:

    // src/ai/ai.controller.ts
    import {
      Controller,
      Post,
      Body,
      Res,
      HttpCode,
      HttpStatus,
    } from '@nestjs/common';
    import { Response } from 'express';
    import { AiService } from './ai.service';
    import { ChatRequestDto } from './dto/chat.dto';
    import { CoreMessage } from 'ai';
    
    @Controller('api')
    export class AiController {
      constructor(private readonly aiService: AiService) {}
    
      @Post('chat')
      @HttpCode(HttpStatus.OK)
      async chat(
        @Body() body: ChatRequestDto,
        @Res() res: Response,
      ): Promise<void> {
        const messages = body.messages as CoreMessage[];
    
        const result = this.aiService.streamChat(messages);
    
        // toUIMessageStreamResponse() genera una Response Web estándar
        // con el protocolo SSE del AI SDK
        const streamResponse = result.toUIMessageStreamResponse();
    
        // Propagamos los headers del AI SDK a la respuesta de Express
        streamResponse.headers.forEach((value, key) => {
          res.setHeader(key, value);
        });
    
        res.status(streamResponse.status);
    
        // Volcamos el body del ReadableStream a la respuesta de Express
        if (streamResponse.body) {
          const reader = streamResponse.body.getReader();
    
          const pump = async () => {
            while (true) {
              const { done, value } = await reader.read();
              if (done) {
                res.end();
                break;
              }
              res.write(value);
            }
          };
    
          pump().catch((err) => {
            console.error('[AiController] Error en stream:', err);
            if (!res.headersSent) {
              res.status(500).json({ error: 'Error interno del stream' });
            } else {
              res.end();
            }
          });
        } else {
          res.status(500).json({ error: 'No se pudo iniciar el stream' });
        }
      }
    }
    

    ¿Por qué este patrón de pump manual en lugar de pipe()?

    toUIMessageStreamResponse() devuelve una Response Web estándar (la del spec WHATWG), no un stream de Node.js. Express trabaja con streams de Node.js. El pump manual convierte uno en el otro sin dependencias adicionales. Es verboso pero explícito — sabes exactamente qué hace cada línea.

    El bloque catch en el pump gestiona dos escenarios: si el error ocurre antes de enviar headers, devuelve un 500 con JSON. Si ocurre después (cuando el stream ya está activo), llama a res.end() para cerrar la conexión limpiamente. Sin este manejo, el cliente se quedaría esperando indefinidamente.


    Paso 4: El AiModule

    El módulo agrupa las tres piezas:

    // src/ai/ai.module.ts
    import { Module } from '@nestjs/common';
    import { AiController } from './ai.controller';
    import { AiService } from './ai.service';
    
    @Module({
      controllers: [AiController],
      providers: [AiService],
      exports: [AiService], // por si otros módulos necesitan AiService
    })
    export class AiModule {}
    

    Exportar AiService es una decisión de diseño: si en el futuro un módulo de AgentsModule o DocumentModule necesita llamar al modelo, importan AiModule y tienen el servicio disponible sin duplicar configuración.


    Rate limiting: el paso que nadie incluye

    Sin rate limiting, un solo usuario puede vaciar tu cuota de Anthropic en minutos. NestJS tiene @nestjs/throttler para esto:

    npm install @nestjs/throttler
    

    Configúralo en AppModule:

    // src/app.module.ts
    import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
    import { APP_GUARD } from '@nestjs/core';
    
    @Module({
      imports: [
        ConfigModule.forRoot({ isGlobal: true }),
        ThrottlerModule.forRoot([{
          name: 'short',
          ttl: 60_000,   // 1 minuto en ms
          limit: 10,     // máximo 10 requests por minuto por IP
        }]),
        AiModule,
      ],
      providers: [
        {
          provide: APP_GUARD,
          useClass: ThrottlerGuard,
        },
      ],
    })
    export class AppModule {}
    

    10 requests por minuto por IP es un límite conservador para un chat. En producción, ajusta según el plan de Anthropic que tengas y el perfil de uso esperado. Si tus usuarios son developers que mandan snippets de código largos, 10 puede ser demasiado restrictivo. Si es un chat de soporte con usuarios anónimos, puede ser demasiado permisivo.

    ThrottlerGuard como APP_GUARD aplica el límite a todos los endpoints automáticamente. Si quieres excluir algunos endpoints del límite, usa el decorador @SkipThrottle() en el controlador correspondiente.


    Conectar con el frontend Angular

    Este backend está diseñado para ser el complemento del post Angular v22 + Vercel AI SDK: streaming de IA en tu app en 20 minutos.

    El frontend Angular usa fetch nativo con ReadableStream. El cambio que necesitas en el componente Angular es mínimo: actualizar la URL del endpoint del servidor Bun del post anterior (típicamente en el puerto 4000) a http://localhost:3000/api/chat de este servidor NestJS. El contrato del API no cambia — misma ruta, mismo formato de mensajes.

    La diferencia está en el protocolo de stream. El servidor Bun del post anterior usa toTextStreamResponse(), que devuelve texto plano. Este NestJS usa toUIMessageStreamResponse(), que usa el protocolo SSE estructurado del AI SDK. Para consumir este protocolo desde Angular sin la librería useChat de React, el componente Angular necesita parsear los chunks SSE en lugar de concatenarlos directamente.

    Si ya tienes el frontend del post anterior y quieres migrar a este backend sin tocar el componente, cambia en AiService.streamChat() el retorno a toTextStreamResponse():

    // AiService — variante compatible con el componente Angular del post anterior
    streamChat(messages: CoreMessage[]) {
      return streamText({
        model: this.anthropic('claude-sonnet-4-6'),
        system: 'Eres un asistente técnico...',
        messages,
        maxTokens: 1024,
      });
      // En el controlador usar toTextStreamResponse() en vez de toUIMessageStreamResponse()
    }
    

    Y en el controlador, sustituye result.toUIMessageStreamResponse() por result.toTextStreamResponse(). El componente Angular del post anterior funciona sin cambios.

    La versión con toUIMessageStreamResponse() es la recomendada para proyectos nuevos porque soporta tool calls, metadatos de uso de tokens, y datos personalizados dentro del mismo stream — funcionalidades que toTextStreamResponse() no puede transmitir.

    Característica toUIMessageStreamResponse() toTextStreamResponse()
    Protocolo AI SDK SSE estructurado Texto plano
    Tool calls ✅ ❌
    Metadatos de tokens ✅ ❌
    Compatible con useChat ✅ ❌
    Parsing manual en cliente Necesario sin useChat No necesario
    Cuándo usarlo Proyectos nuevos Compatibilidad con cliente simple

    Manejo de errores: más allá del try/catch

    El error handling que ya tenemos en el pump del controlador cubre los fallos en el stream activo. Pero hay errores que ocurren antes del stream — cuando la API de Anthropic devuelve un 429 (rate limit) o un 500:

    // src/ai/ai.controller.ts — versión con manejo de errores completo
    import { APICallError } from 'ai';
    
    @Post('chat')
    @HttpCode(HttpStatus.OK)
    async chat(
      @Body() body: ChatRequestDto,
      @Res() res: Response,
    ): Promise<void> {
      try {
        const messages = body.messages as CoreMessage[];
        const result = this.aiService.streamChat(messages);
        const streamResponse = result.toUIMessageStreamResponse();
    
        streamResponse.headers.forEach((value, key) => {
          res.setHeader(key, value);
        });
        res.status(streamResponse.status);
    
        if (streamResponse.body) {
          const reader = streamResponse.body.getReader();
    
          const pump = async () => {
            while (true) {
              const { done, value } = await reader.read();
              if (done) { res.end(); break; }
              res.write(value);
            }
          };
    
          await pump();
        }
      } catch (error) {
        if (APICallError.isInstance(error)) {
          // Error de la API del LLM (429, 500, etc.)
          console.error('[AiController] Error API LLM:', error.message, error.statusCode);
    
          if (!res.headersSent) {
            const statusCode = error.statusCode === 429 ? 429 : 502;
            res.status(statusCode).json({
              error: error.statusCode === 429
                ? 'Demasiadas peticiones al modelo. Inténtalo en unos segundos.'
                : 'Error al conectar con el modelo de IA.',
            });
          } else {
            res.end();
          }
        } else {
          console.error('[AiController] Error inesperado:', error);
          if (!res.headersSent) {
            res.status(500).json({ error: 'Error interno del servidor.' });
          } else {
            res.end();
          }
        }
      }
    }
    

    APICallError.isInstance(error) es el type guard del AI SDK para distinguir errores de la API del LLM de errores genéricos. Útil para devolver mensajes de error específicos al cliente sin exponer detalles internos.


    Ejecutar el servidor

    # Desarrollo con hot reload
    npm run start:dev
    
    # Producción
    npm run build && npm run start:prod
    

    El servidor levanta en http://localhost:3000. Prueba el endpoint:

    curl -X POST http://localhost:3000/api/chat \
      -H "Content-Type: application/json" \
      -d '{"messages": [{"role": "user", "content": "Qué es NestJS en una frase"}]}' \
      --no-buffer
    

    Verás los chunks SSE llegar en tiempo real en la terminal. Eso confirma que el streaming funciona.


    El AiModule en producción: qué añadir después

    Lo que hemos construido es una base sólida. En un entorno de producción real, los siguientes pasos son:

    1. Autenticación. Añadir un AuthGuard de JWT al endpoint chat para que solo usuarios autenticados consuman tokens. Sin esto, cualquiera con la URL puede vaciar tu cuota.

    2. Logging estructurado. Usar @nestjs/winston o Pino para loguear cada request con userId, messageCount, y tokensUsed. El AI SDK expone usage en el stream — puedes capturarlo en el onFinish callback de streamText.

    3. Persistencia del historial. El backend actual es stateless — el historial viene del cliente en cada request. En producción con usuarios autenticados, guarda el historial en base de datos y envía solo el conversationId desde el frontend. El servidor reconstruye el historial antes de llamar al modelo.

    4. Selección de modelo por request. Si tu app da a los usuarios la opción de elegir entre Claude Sonnet y Claude Haiku (más barato), añade un campo model al DTO y pásalo al servicio. La abstracción del AI SDK hace que el cambio sea trivial.

    Si quieres profundizar en este tipo de decisiones de arquitectura — cómo estructurar un producto completo con IA desde la idea hasta producción — en el curso Construye con IA: de la idea al producto con Claude Code lo vemos con proyectos reales, no con demos de laboratorio.


    FAQ

    ¿Puedo usar este módulo con Fastify en lugar de Express?

    Sí, pero el pump manual del controlador cambia. Fastify usa Reply en lugar de Response de Express, y el método para escribir chunks es reply.raw.write(). El @Res() res: Response del controlador funcionará si configuras passThrough: true en el decorador: @Res({ passThrough: false }). La lógica del pump en sí no cambia — solo los métodos de la respuesta.

    ¿El rate limiting con ThrottlerGuard funciona bien detrás de un proxy o load balancer?

    Por defecto, ThrottlerGuard usa la IP del request. Si tu app está detrás de un proxy (Nginx, Cloudflare, etc.), la IP será siempre la del proxy. Configura ThrottlerModule con throttlers y usa ThrottlerGuard extendido que lea X-Forwarded-For. Alternativamente, delega el rate limiting al proxy — Nginx tiene limit_req_zone para esto.

    ¿Cómo evito que el stream consuma tokens si el cliente desconecta?

    streamText del AI SDK no cancela automáticamente la petición a Anthropic cuando el cliente cierra la conexión HTTP. Para implementar cancelación, pasa un AbortSignal a streamText:

    streamChat(messages: CoreMessage[], signal?: AbortSignal) {
      return streamText({
        model: this.anthropic('claude-sonnet-4-6'),
        messages,
        abortSignal: signal,
      });
    }
    

    En el controlador, escucha el evento close de la respuesta y llama a abortController.abort(). Esto cancela la llamada a la API antes de que el modelo termine de generar.

    ¿Puedo usar @ai-sdk/openai o @ai-sdk/google en lugar de Anthropic?

    Sí. Cambia createAnthropic por createOpenAI o createGoogleGenerativeAI en AiService y actualiza el nombre del modelo. El resto del módulo — controlador, DTO, rate limiting, manejo de errores — no cambia. Esa es exactamente la ventaja de usar el AI SDK como capa de abstracción: cambias de proveedor en un sitio.

    ¿CoreMessage[] es compatible con el formato de mensajes que manda el componente Angular del post anterior?

    CoreMessage del AI SDK acepta objetos con role ('user', 'assistant', 'system') y content (string). El ChatMessage del componente Angular del post anterior tiene exactamente esa forma. El cast body.messages as CoreMessage[] funciona directamente — no necesitas transformar nada.


    Cierre

    Un backend de streaming de IA no es complicado. Lo que sí es complicado es hacerlo bien desde el principio: que valide los inputs, que no queme tokens cuando el cliente desconecta, que no se caiga cuando Anthropic devuelve un 429, que tenga un límite razonable de peticiones por IP.

    NestJS más el Vercel AI SDK resuelven ese conjunto de problemas con una arquitectura que ya conoces si llevas tiempo en el ecosistema TypeScript. No hay magia — hay módulos, servicios, inyección de dependencias, y un stream que fluye limpio de principio a fin.

    El AiModule que has construido hoy es reutilizable. Impórtalo en cualquier NestJS existente, ajusta el system prompt y el modelo, y tienes un endpoint de IA en producción en menos de una hora.

    Si quieres llevarlo más lejos — tool calls, agentes con memoria, pipelines de documentos — en Dominicode Labs tenemos los proyectos completos con los patrones que usamos en producción, incluyendo ejemplos de NestJS con AI SDK con autenticación, persistencia y cancelación de streams.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Prompt Caching en Claude: reduce tu factura de API un 90%

    Prompt Caching en Claude: reduce tu factura de API un 90%

    El mes pasado revisé los gastos de API de un proyecto que lleva seis semanas en producción. Un agente conversacional para análisis de documentos legales. El cliente lo usa unas 40 veces al día.

    La factura: $340 en un mes.

    El system prompt tenía 8.000 tokens. Las definiciones de herramientas, otros 3.000. En cada llamada, esos 11.000 tokens se procesaban desde cero. Cuarenta veces al día. Treinta días al mes.

    Activé prompt caching. La siguiente factura: $38.

    No cambié la lógica del agente. No modifiqué los prompts. Solo añadí tres líneas de configuración.

    Eso es lo que hace el prompt caching de Claude. Y la mayoría de developers que trabajan con la API de Anthropic aún no lo tienen activado.


    Qué es el prompt caching y cómo funciona

    Cuando haces una llamada a la API de Claude, pagas por cada token que el modelo procesa. System prompt, herramientas, historial de conversación, contexto de documentos: todo se cobra como tokens de entrada.

    El problema es que en la mayoría de aplicaciones reales, una parte enorme de esos tokens es idéntica en cada llamada. Tu system prompt no cambia. Las definiciones de tus herramientas no cambian. El contexto de un documento que estás analizando no cambia entre preguntas del usuario.

    El prompt caching te permite marcar esas partes estáticas para que Claude las almacene en caché. La documentación oficial de prompt caching cubre todos los modelos y casos edge. La primera vez que se procesa ese contenido, se escribe en caché. En las llamadas posteriores, en lugar de reprocesar esos tokens, Claude los lee desde el caché.

    El coste de un cache write es 1.25x el precio base — ligeramente más caro que una llamada normal. El coste de un cache read es 0.1x el precio base. Es decir, un 90% más barato.

    En un agent loop con 40 llamadas al día, pagas el 1.25x una vez. Las otras 39 veces pagas el 0.1x. La aritmética es brutal a tu favor.

    El TTL del caché

    El caché tiene un TTL (Time To Live) de 5 minutos por defecto. Mientras haya llamadas dentro de esa ventana, el caché se renueva automáticamente sin coste adicional. Si una conversación tiene mensajes frecuentes, el caché se mantiene activo.

    Existe también un TTL de 1 hora, que cuesta 2x el precio base en la escritura. Útil cuando tienes contextos que se reutilizan con menos frecuencia pero son muy costosos de regenerar.

    El mínimo de tokens para activar el caché

    No todo se puede cachear. El sistema exige un mínimo de tokens para crear una entrada de caché. Para claude-sonnet-4-6 y claude-opus-4-8, el mínimo es 1.024 tokens. Para claude-haiku-4-5, el umbral sube a 4.096 tokens — cuatro veces más alto, relevante si usas Haiku con prompts cortos. Si tu system prompt tiene menos tokens que el mínimo de tu modelo, el caché no se activa.

    En proyectos donde el system prompt es corto, la estrategia correcta es incluir el contexto del dominio directamente en el system prompt hasta superar ese umbral, o cachear las definiciones de herramientas junto con el sistema.


    Cómo habilitarlo: código TypeScript con el SDK oficial

    Aquí está el patrón que uso en producción. Nada de magia — tres cambios concretos en tu código.

    Habilitación básica: system prompt con cache_control

    import Anthropic from "@anthropic-ai/sdk";
    
    const client = new Anthropic();
    
    const response = await client.messages.create({
      model: "claude-sonnet-4-6",
      max_tokens: 1024,
      system: [
        {
          type: "text",
          text: `Eres un asistente especializado en análisis de documentos legales.
          
    Tu rol es:
    - Identificar cláusulas de riesgo en contratos
    - Resumir términos clave de forma clara y precisa
    - Señalar inconsistencias o ambigüedades legales
    - Comparar términos con estándares del sector
    
    [...aquí va el resto del system prompt extenso, con contexto del dominio,
    instrucciones detalladas, ejemplos de formato de respuesta, etc.
    Debe superar los 1.024 tokens para activar el caché...]`,
          cache_control: { type: "ephemeral" }, // <-- esto es todo lo que necesitas
        },
      ],
      messages: [
        {
          role: "user",
          content: "Analiza la cláusula de terminación de este contrato: ...",
        },
      ],
    });
    
    console.log(response.usage);
    

    En la primera llamada, usage mostrará:

    {
      "input_tokens": 45,
      "cache_creation_input_tokens": 1280,
      "cache_read_input_tokens": 0,
      "output_tokens": 312
    }
    

    En la segunda llamada (dentro de los 5 minutos):

    {
      "input_tokens": 45,
      "cache_creation_input_tokens": 0,
      "cache_read_input_tokens": 1280,
      "output_tokens": 289
    }
    

    cache_read_input_tokens tiene el 10% del coste. El system prompt completo se leyó desde caché. Esos 1.280 tokens no se procesaron desde cero.

    Cacheando herramientas y system prompt juntos

    Cuando tienes definiciones de herramientas largas — algo habitual en agentes con MCP o con múltiples funciones — el ahorro se multiplica. Aquí el patrón para cachear ambas cosas:

    import Anthropic from "@anthropic-ai/sdk";
    
    const client = new Anthropic();
    
    // Las definiciones de herramientas son estáticas — candidatas perfectas para caché
    const tools: Anthropic.Tool[] = [
      {
        name: "search_legal_database",
        description: `Busca en la base de datos legal precedentes y jurisprudencia relevante.
        Usa esta herramienta cuando necesites comparar cláusulas con casos anteriores o
        encontrar interpretaciones judiciales de términos específicos. La búsqueda incluye
        bases de datos de España, México, Argentina y Colombia. Devuelve hasta 10 resultados
        ordenados por relevancia con fecha, tribunal y resumen del caso.`,
        input_schema: {
          type: "object" as const,
          properties: {
            query: {
              type: "string",
              description: "Término o frase legal a buscar",
            },
            jurisdiction: {
              type: "string",
              enum: ["ES", "MX", "AR", "CO", "ALL"],
              description: "Jurisdicción a consultar",
            },
            date_range: {
              type: "string",
              description: "Rango de fechas en formato YYYY-YYYY",
            },
          },
          required: ["query"],
        },
      },
      {
        name: "analyze_clause_risk",
        description: `Analiza el nivel de riesgo de una cláusula contractual.
        Evalúa factores como onerosidad excesiva, cláusulas abusivas según legislación
        vigente, asimetría de obligaciones y exposición a penalidades. Devuelve un score
        de riesgo del 1 al 10 con justificación detallada y recomendaciones de negociación.`,
        input_schema: {
          type: "object" as const,
          properties: {
            clause_text: {
              type: "string",
              description: "Texto completo de la cláusula a analizar",
            },
            contract_type: {
              type: "string",
              description: "Tipo de contrato (laboral, mercantil, arrendamiento, etc.)",
            },
          },
          required: ["clause_text"],
        },
      },
      // cache_control al final del array de tools — marca el punto de caché
    ];
    
    // Añadimos cache_control al último tool para cachear todo el bloque
    const toolsWithCache = tools.map((tool, index) =>
      index === tools.length - 1
        ? { ...tool, cache_control: { type: "ephemeral" as const } }
        : tool
    );
    
    const response = await client.messages.create({
      model: "claude-sonnet-4-6",
      max_tokens: 2048,
      system: [
        {
          type: "text",
          text: "Eres un asistente especializado en análisis legal...",
          cache_control: { type: "ephemeral" }, // system prompt cacheado
        },
      ],
      tools: toolsWithCache, // tools cacheadas
      messages: [
        {
          role: "user",
          content: "¿Cuál es el riesgo de esta cláusula de no competencia?",
        },
      ],
    });
    

    Monitorizar el ahorro en tiempo real

    Esta función te dice exactamente cuánto has ahorrado en cada llamada:

    interface CostMonitor {
      inputTokensCost: number;
      cacheWriteCost: number;
      cacheReadCost: number;
      outputTokensCost: number;
      totalCost: number;
      savings: number;
      savingsPercent: number;
    }
    
    // Precios para claude-sonnet-4-6 por millón de tokens (en dólares)
    const PRICING = {
      input: 3.0,
      cacheWrite: 3.75, // 1.25x
      cacheRead: 0.3,   // 0.1x
      output: 15.0,
    };
    
    function calculateCallCost(usage: Anthropic.Usage): CostMonitor {
      const inputCost = (usage.input_tokens / 1_000_000) * PRICING.input;
      const cacheWriteCost =
        ((usage.cache_creation_input_tokens ?? 0) / 1_000_000) * PRICING.cacheWrite;
      const cacheReadCost =
        ((usage.cache_read_input_tokens ?? 0) / 1_000_000) * PRICING.cacheRead;
      const outputCost = (usage.output_tokens / 1_000_000) * PRICING.output;
    
      const totalCost = inputCost + cacheWriteCost + cacheReadCost + outputCost;
    
      // Coste hipotético sin caché (todos los tokens al precio base)
      const totalInputTokens =
        usage.input_tokens +
        (usage.cache_creation_input_tokens ?? 0) +
        (usage.cache_read_input_tokens ?? 0);
      const costWithoutCache =
        (totalInputTokens / 1_000_000) * PRICING.input + outputCost;
    
      const savings = costWithoutCache - totalCost;
      const savingsPercent =
        costWithoutCache > 0 ? (savings / costWithoutCache) * 100 : 0;
    
      return {
        inputTokensCost: inputCost,
        cacheWriteCost,
        cacheReadCost,
        outputTokensCost: outputCost,
        totalCost,
        savings,
        savingsPercent,
      };
    }
    
    // Uso:
    const monitor = calculateCallCost(response.usage);
    console.log(`Ahorro: $${monitor.savings.toFixed(6)} (${monitor.savingsPercent.toFixed(1)}%)`);
    

    Qué debes cachear y qué no

    Los mejores candidatos para el caché

    System prompts largos. Es el caso más obvio. Si tu system prompt tiene instrucciones de rol, reglas de formato, contexto del dominio y ejemplos, estás mirando fácilmente 2.000-8.000 tokens que se repiten en cada llamada. Cachear el system prompt es lo primero que debes activar.

    Definiciones de herramientas (tools). Especialmente en agentes con MCP o con muchas funciones. Las definiciones de tools incluyen nombres, descripciones detalladas y schemas completos. Pueden sumar 3.000-5.000 tokens fácilmente. Son siempre estáticas dentro de una sesión.

    Contexto de documentos. Si tu aplicación analiza un documento largo (un contrato, una especificación técnica, un PDF), ese documento va en el mensaje del usuario pero cambia muy poco. Puedes cachearlo con cache_control en el bloque del contenido del mensaje.

    Historial de conversación en agent loops. En un loop donde el agente tiene muchos turnos, cachear el historial acumulado evita pagar por reprocesar el contexto completo en cada iteración.

    Qué NO debes cachear

    El turno actual del usuario. Es el error más común. El mensaje que el usuario acaba de escribir cambia en cada llamada — si intentas cachearlo, el caché nunca tendrá un hit porque el contenido es siempre distinto.

    Tokens de extended thinking. Si usas extended thinking con Claude, los tokens del proceso de razonamiento interno no se cachean. Esto es relevante si estás midiendo ahorros en pipelines que usan thinking — los números no escalarán de la misma forma.

    Contenido que cambia con frecuencia. Si tienes un bloque de contexto que se actualiza cada pocos minutos (resultados de una búsqueda en tiempo real, estado de una sesión volátil), no tiene sentido marcarlo para caché porque nunca habrá un hit.

    Bloques demasiado pequeños. Si un bloque tiene menos de 1.024 tokens, el sistema no lo cacheará. No añadas cache_control a fragmentos pequeños — solo añade latencia sin beneficio.


    Comparación de coste: sin caching vs con caching

    Escenario real: un agente con 40 llamadas diarias durante 30 días.

    • System prompt: 5.000 tokens
    • Tools: 3.000 tokens
    • Pregunta del usuario: ~100 tokens (variable)
    • Respuesta del modelo: ~400 tokens (variable)
    • Modelo: claude-sonnet-4-6
    Escenario Coste por llamada Total mensual
    Sin caching (8.100 input + 400 output) $0.0303 $36.36
    Con caching — 1ª llamada del día (cache write 8.000 + 100 input + 400 output) $0.037 —
    Con caching — llamadas 2–40 (cache read 8.000 + 100 input + 400 output) $0.0084 —
    Con caching — total diario (1ª + 39 × $0.0084) $0.365/día $10.95

    Ahorro: 70%. Y esto asumiendo que el caché expira cada día. Con conversaciones más densas donde el TTL de 5 minutos se aprovecha bien, el ahorro sube al 85-90%.


    Preguntas frecuentes sobre prompt caching en Claude

    ¿El caché es compartido entre usuarios?
    No. El caché es privado por workspace de Anthropic. Desde febrero de 2026, hay aislamiento completo por workspace. Los datos de un usuario nunca se mezclan con los de otro.

    ¿Qué pasa si cambio el system prompt? ¿Se invalida el caché?
    Sí. El caché funciona por contenido exacto. Si modificas un solo carácter del bloque cacheado, se genera una nueva entrada de caché (cache write) en la siguiente llamada. El caché anterior expira según su TTL sin coste adicional.

    ¿Puedo cachear múltiples bloques en la misma llamada?
    Sí, hasta un máximo de cuatro breakpoints de caché por request. La restricción importante es el orden: los bloques con TTL más largo (1 hora) deben aparecer antes que los de TTL más corto (5 minutos) en la estructura del request.

    ¿El caching funciona con streaming?
    Sí. El prompt caching es compatible con la API de streaming de Claude. Los campos cache_creation_input_tokens y cache_read_input_tokens aparecen en el evento message_start del stream — no en message_delta. Es el primer evento emitido, antes de que lleguen los tokens de respuesta.


    El siguiente nivel: combinar con Claude Code

    Si ya estás explorando agentes más complejos, el prompt caching cambia la ecuación de coste de forma radical. Un agent loop sin caching que hace 10 iteraciones paga los tokens del system prompt y las tools diez veces. Con caching, los paga una vez y lee el resto.

    En Claude Code: Effort, Models, Tools y Context hay una sección completa sobre cómo gestiona Claude Code el contexto en agent loops largos — es el contexto perfecto para entender dónde encaja el caching a nivel de infraestructura.

    Y si quieres construir productos reales sobre la API de Anthropic con esta clase de optimizaciones ya integradas desde el primer sprint, el curso Construye con IA: De la Idea al Producto con Claude cubre el stack completo — desde la arquitectura del agente hasta el control de costes en producción.


    Lo que puedes hacer hoy

    Si tienes una aplicación que usa la API de Claude en producción, abre el código y busca dónde defines el system prompt. Si es una cadena de texto plana, conviértela en un array con cache_control: { type: "ephemeral" }.

    Eso solo. Una línea de cambio. Comprueba la siguiente factura.

    Si además tienes tools largas, aplica el mismo patrón al último elemento del array de herramientas. Tendrás dos puntos de caché activos y el ahorro será inmediato.

    El prompt caching no es una optimización avanzada que requiere rediseñar tu arquitectura. Es una configuración de tres minutos que debería estar activa en cualquier aplicación seria sobre la API de Claude. Si no la tienes, estás pagando de más desde el primer día.


    Bezael Pérez — Fundador de Dominicode. Developer senior con 15+ años construyendo software. Si construyes con IA y quieres profundizar más allá de los tutoriales, en Dominicode Labs estamos trabajando en proyectos reales con la API de Anthropic, arquitecturas de agentes y todo lo que no cabe en un post.

  • Claude Fable 5 vuelve: qué pasó y qué cambia para developers

    Claude Fable 5 vuelve: qué pasó y qué cambia para developers

    El 12 de junio de 2026, Anthropic apagó Claude Fable 5 de golpe.

    Sin aviso previo. Sin fecha de vuelta. Sin explicación técnica completa. El modelo que llevaba apenas tres días disponible desapareció para todos los usuarios del planeta — Europa, Latinoamérica, Asia, todos — porque el gobierno de EE.UU. no podía verificar nacionalidades en tiempo real y decidió cortar el acceso global en lugar de arriesgarse.

    Ese mismo día, developers de medio mundo abrieron Claude.ai y encontraron un modelo degradado. Los que habían empezado a construir pipelines con Fable 5 tuvieron que pivotar sobre la marcha. Y los que llevábamos años viendo cómo la IA maduraba como industria recibimos un recordatorio brutal: cuando un modelo tiene capacidades que un Estado considera amenaza para la seguridad nacional, el interruptor lo tiene el Estado, no Anthropic.

    Hoy, 1 de julio de 2026, Claude Fable 5 vuelve. Y la historia de cómo llegamos hasta aquí dice más sobre el futuro de la IA que cualquier benchmark.


    Lo que pasó: el jailbreak que lo cambió todo

    Investigadores de Amazon descubrieron una técnica que permitía a Fable 5 identificar vulnerabilidades en software y, en al menos un caso documentado, demostrar cómo explotarlas. El gobierno de EE.UU. reaccionó con rapidez: el mismo día 12 de junio, el Departamento de Comercio aplicó controles de exportación de emergencia que afectaron tanto a Fable 5 como a Mythos 5.

    La tensión entre ambas partes fue pública. El gobierno argumentó que el problema podría haberse corregido antes de la suspensión. Anthropic respondió que la técnica era más estrecha y específica de lo que la orden de emergencia implicaba — no una vulnerabilidad sistémica, sino un vector concreto que requerirían semanas de investigación para reproducir.

    El debate sobre la severidad real del jailbreak sigue abierto. El resultado fue inequívoco: controles de exportación de emergencia, suspensión global, y Anthropic sin poder verificar la nacionalidad de sus usuarios en tiempo real.

    No había otra salida. Apagaron todo.


    Fable 5 y Mythos 5: la diferencia que importa

    Aquí hay un matiz que mucha cobertura mediática perdió.

    Mythos 5 es la denominación interna de los modelos con capacidades cibernéticas más avanzadas que Anthropic ha construido jamás — superiores a cualquier otro modelo del mercado en ese dominio. Tras la suspensión, Anthropic decidió que Mythos 5 solo estará disponible para socios del Proyecto Glasswing, un programa de ciberseguridad defensiva con acceso controlado y supervisión directa.

    Fable 5 es diferente. Es el modelo de propósito general que se lanza hoy con los salvaguardas más fuertes que Anthropic ha implementado en ningún modelo de su historia. Anthropic afirma explícitamente que Fable 5 "no proporciona capacidades ofensivas únicas" — es decir, no hace nada que un atacante sofisticado no pudiera hacer con las herramientas que ya existen.

    Fable 5 Mythos 5
    Propósito General (razonamiento, código, escritura) Ciberseguridad avanzada
    Acceso Público (planes de pago) Solo Proyecto Glasswing
    API pública ✅ Sí ❌ No
    Capacidades ofensivas No únicas respecto a herramientas existentes Superiores a cualquier otro modelo del mercado

    La distinción es importante para cualquier developer que esté construyendo con la API. No estás usando Mythos 5. Estás usando Fable 5, que ha pasado por una revisión de seguridad que ningún modelo anterior había tenido.


    Qué cambió en Claude Fable 5: los nuevos salvaguardas

    Anthropic no volvió con el mismo modelo. Volvió con un clasificador de seguridad reentrenado específicamente para detectar y bloquear la técnica descrita en el reporte de Amazon.

    Según el anuncio oficial de Anthropic, el nuevo clasificador bloquea el comportamiento problemático en más del 99% de los casos. Cuando se activa, la solicitud no falla en silencio — se redirige automáticamente a Claude Opus 4.8. El usuario recibe respuesta, pero sin las capacidades que generaron el problema.

    El mecanismo de defensa tiene tres capas:

    1. El entrenamiento base del modelo, que ya rechaza asistencia con solicitudes peligrosas.
    2. Un clasificador específico para el patrón de jailbreak identificado por Amazon.
    3. Un margen de seguridad ampliado — Anthropic subió el umbral de bloqueo de forma deliberada, asumiendo más falsos positivos para reducir el riesgo de usos maliciosos.

    Ese tercer punto es el que más impacta a developers en producción. Más falsos positivos significa que algunas solicitudes legítimas relacionadas con ciberseguridad, análisis de código o auditoría de vulnerabilidades van a llegar a Opus 4.8 en lugar de Fable 5. No es un bug. Es una decisión consciente de arquitectura de seguridad.

    El Departamento de Comercio de EE.UU. verificó los salvaguardas y los calificó de "extraordinariamente fuertes". El 30 de junio levantó los controles de exportación. El 1 de julio, Fable 5 vuelve.


    Disponibilidad desde hoy: lo que necesitas saber

    La reactivación es global desde el 1 de julio en Claude.ai, Claude Platform, Claude Code y Claude Cowork.

    AWS, Google Cloud y Microsoft Foundry se reactivarán "lo antes posible" — sin fecha concreta confirmada.

    Hay un período de transición con límites temporales:

    • Hasta el 7 de julio: planes Pro, Max, Team y empresas seleccionadas tienen acceso a Fable 5 con hasta el 50% de sus límites de uso semanal habituales.
    • Después del 7 de julio: disponible mediante créditos de uso, sin restricción porcentual.

    Si estás en el plan gratuito, no hay cambios respecto a antes de la suspensión. Fable 5 era y sigue siendo acceso de pago.

    Para los que construimos con la API de Anthropic, el modelo vuelve a estar disponible desde hoy. Si tenías pipelines configurados con Fable 5 antes del 12 de junio, probablemente ya están activos de nuevo. Verifica tu dashboard y el comportamiento del clasificador con tus casos de uso específicos — especialmente si tienes prompts relacionados con análisis de código o seguridad.


    El nuevo marco de evaluación de jailbreaks

    Lo más interesante de lo que Anthropic publicó esta semana no son los salvaguardas. Es el marco que proponen como estándar industrial para evaluar la severidad de un jailbreak.

    Cuatro criterios:

    1. Ganancia de capacidad. ¿Cuánto supera lo que ya existe? Un jailbreak que replica lo que hace una herramienta de código abierto pesa menos que uno que desbloquea algo genuinamente nuevo.

    2. Amplitud. ¿Cuántas tareas ofensivas distintas habilita? Un jailbreak muy específico (un tipo de ataque, un vector) no es lo mismo que uno que abre la puerta a toda una clase de capacidades.

    3. Facilidad de armamento. ¿Cuánto esfuerzo humano experto requiere convertir el output en un ataque real? Hay una diferencia enorme entre "el modelo identifica una vulnerabilidad" y "el modelo produce un exploit listo para ejecutar".

    4. Descubribilidad. ¿Cómo de fácil es que un actor malicioso llegue a esta técnica? Un jailbreak que requiere semanas de ingeniería de prompts por parte de investigadores avanzados no tiene el mismo riesgo que uno que circula en un foro público.

    Este marco no es solo teoría. Anthropic lo propone como base para que gobiernos, empresas y laboratorios de IA puedan hablar de jailbreaks con criterios objetivos en lugar de reacciones políticas de emergencia.

    Si trabajas en seguridad o builds productos con IA, este marco te va a ser útil.


    Lo que esto significa para developers que construyen con IA

    Hace tres semanas, el modelo más capaz del mercado desapareció sin fecha de vuelta. Hoy está de vuelta con salvaguardas que ningún modelo anterior había tenido, respaldado por verificación gubernamental y un nuevo marco de evaluación que puede convertirse en estándar.

    ¿Qué cambia para nosotros?

    Primero, la confirmación de algo que debíamos asumir pero que muchos ignoraban: los modelos más capaces van a estar regulados. No es una posibilidad futura. Es el presente. El mismo día que Fable 5 volvió, Mythos 5 quedó restringido a socios controlados del Proyecto Glasswing. La IA de alto impacto va a tener fricción institucional. Cuanto antes lo integremos en nuestra planificación de producto, mejor.

    Segundo, la arquitectura de fallback importa más de lo que pensamos. Si tu producto dependía de Fable 5 el 12 de junio, tuviste un problema durante diecinueve días. Los mejores sistemas tienen fallback a modelos alternativos — no porque anticipen este escenario exacto, sino porque construyen con redundancia desde el principio.

    Tercero, y esto es lo más importante: la madurez del sector se mide en cómo responde a los errores, no en si los comete. Anthropic tardó diecinueve días en volver. En esas tres semanas entrenaron un nuevo clasificador, pasaron una auditoría gubernamental, propusieron un marco de evaluación de jailbreaks que puede convertirse en estándar, y redefinieron el acceso a Mythos 5. Eso no es una crisis mal gestionada. Es una empresa que aprendió en tiempo real bajo presión máxima.

    Nosotros podemos hacer lo mismo en nuestros productos.

    En el curso Construye con IA hablo de esto en profundidad: cómo construir sistemas que no colapsen cuando el modelo subyacente cambia, se actualiza o desaparece temporalmente. La resiliencia arquitectural no es un añadido. Es la condición de base para cualquier producto serio con IA.


    Preguntas frecuentes sobre Claude Fable 5

    ¿Claude Fable 5 está disponible hoy para todos los usuarios?

    Desde el 1 de julio de 2026, Fable 5 está disponible en Claude.ai, Claude Platform, Claude Code y Claude Cowork para usuarios en todos los países. AWS, Google Cloud y Microsoft Foundry se reactivarán próximamente. El acceso a Fable 5 requiere un plan de pago (Pro, Max, Team o Enterprise).

    ¿Qué es el jailbreak que causó la suspensión de Claude Fable 5?

    Investigadores de Amazon descubrieron una técnica de prompting que permitía a Fable 5 identificar vulnerabilidades en software y, en al menos un caso, demostrar cómo explotarlas. El gobierno de EE.UU. aplicó controles de exportación de emergencia el 12 de junio de 2026, lo que llevó a Anthropic a suspender el acceso global porque no podía verificar la nacionalidad de sus usuarios en tiempo real.

    ¿Cuál es la diferencia entre Claude Fable 5 y Mythos 5?

    Fable 5 es el modelo de propósito general disponible desde hoy para el público. Mythos 5 es la denominación de los modelos con capacidades cibernéticas avanzadas — superiores a cualquier otro modelo del mercado — restringido exclusivamente a socios del Proyecto Glasswing para ciberseguridad defensiva. No es accesible a través de la API pública.

    ¿Cómo afectan los nuevos salvaguardas al uso de Fable 5 en desarrollo de software?

    El nuevo clasificador bloquea el patrón de jailbreak en más del 99% de los casos, redirigiendo esas solicitudes a Claude Opus 4.8. Anthropic aumentó deliberadamente el margen de seguridad, lo que genera más falsos positivos en tareas de análisis de código, auditoría de seguridad o detección de vulnerabilidades. Si tu caso de uso incluye estas áreas, testea tu pipeline con Fable 5 para verificar el comportamiento del clasificador.

    ¿Qué límites de uso tiene Claude Fable 5 tras la vuelta?

    Hasta el 7 de julio de 2026, los planes Pro, Max, Team y empresas Enterprise seleccionadas tienen acceso a Fable 5 con hasta el 50% de sus límites de uso semanal habituales. Después del 7 de julio, el modelo estará disponible mediante créditos de uso sin restricción porcentual.

    ¿Puede volver a ocurrir una suspensión similar con otros modelos de Anthropic?

    Sí. Los controles de exportación son un instrumento legal que el gobierno de EE.UU. puede aplicar a cualquier modelo con capacidades que considere una amenaza. La colaboración reforzada entre Anthropic y el gobierno reduce la probabilidad de una suspensión de emergencia, pero no la elimina. Cualquier arquitectura de producto con IA debe contemplar escenarios de indisponibilidad del modelo principal.


    Si quieres estar al día de cómo estos eventos impactan a los developers que construyen con IA, en Dominicode Labs analizamos en tiempo real las decisiones de los grandes laboratorios y sus implicaciones para producción. Y en el canal de YouTube seguiré cubriendo la evolución de Fable 5 en las próximas semanas.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Claude Sonnet 5: el modelo que trabaja solo mientras tú duermes

    Claude Sonnet 5: el modelo que trabaja solo mientras tú duermes

    Me pasó hace unos meses revisando el output de un agente que había dejado corriendo toda la noche.

    Esperaba encontrar la tarea a medias. Un formulario sin completar. Alguna herramienta mal llamada. Lo habitual con los modelos de la generación anterior: empezaban bien, pero a mitad de camino se perdían, pedían confirmación o simplemente paraban.

    En cambio, encontré el trabajo terminado. Del principio al fin. Sin intervención.

    Ese momento cambia algo en tu cabeza como developer. No es que la IA sea "mejor". Es que ya no necesita que estés mirando.

    Claude Sonnet 5 es exactamente esa promesa hecha modelo. Anthropic lo lanzó el 30 de junio de 2026 y lo describe como "el modelo Sonnet más agéntico hasta la fecha". No es marketing vacío — la diferencia en tareas autónomas y multi-paso es medible y, para quien construye con IA, es relevante desde el primer día.


    Por qué Sonnet 5 es distinto a todo lo anterior

    Hasta ahora, la frontera estaba clara: si querías un agente que realmente terminase el trabajo, necesitabas Opus. Sonnet era el punto medio — rápido, accesible, suficientemente bueno para tareas simples. Pero en flujos complejos con múltiples pasos, herramientas y decisiones encadenadas, Sonnet se quedaba corto.

    Claude Sonnet 5 rompe esa frontera.

    Anthropic no ha simplemente subido los parámetros. Han optimizado específicamente para comportamiento agéntico: planificación de tareas, uso coordinado de herramientas (navegadores, terminales, APIs), y lo más relevante — la capacidad de verificar su propio resultado sin que se lo pidas.

    Eso último importa más de lo que parece. Un modelo que ejecuta código y luego comprueba si el output es el esperado, sin que tú se lo digas, está un paso más cerca de un colaborador que de una herramienta.


    Las capacidades agénticas en detalle

    Hay tres áreas donde el cambio es palpable:

    Tareas multi-paso sin interrupciones. Modelos anteriores tendían a pedir confirmación o detenerse cuando encontraban ambigüedad. Sonnet 5 mantiene el hilo. Algunos partners de Anthropic reportan que "terminó el trabajo de principio a fin sin intervención" — algo que antes era territorio exclusivo de Opus 4.

    Uso de herramientas coordinado. Puede combinar búsqueda web, ejecución de código y llamadas a APIs en la misma tarea sin perder el contexto de lo que estaba haciendo. No es nuevo que los modelos puedan usar herramientas — lo nuevo es que lo hacen con coherencia a lo largo de cadenas largas de razonamiento.

    Auto-verificación del resultado. Si ejecuta una query de base de datos o genera un archivo, puede evaluar si el resultado tiene sentido antes de dártelo. Esto reduce drásticamente la necesidad de loops de revisión en tus agentes.

    Si estás construyendo con la API de Claude o con Claude Code, estas tres capacidades cambian el diseño de tus flujos. No necesitas los mismos guardrails de antes. No necesitas los mismos puntos de control manual.

    En el curso de Construye con IA cubrimos exactamente este tipo de arquitectura de agentes — y con Sonnet 5 muchos de esos patrones se simplifican considerablemente.


    Benchmarks: qué dicen los números

    Los benchmarks importan, pero necesitan contexto. Aquí va la comparativa relevante para developers:

    Benchmark Claude Sonnet 5 Claude Sonnet 4.6 Claude Opus 4.8
    BrowseComp Superior Base de comparación Superior
    OSWorld-Verified Superior Base de comparación Superior
    Razonamiento general Muy cercano a Opus Inferior Referencia
    Codificación Notable mejora Base Referencia
    Esfuerzo "extra high" Iguala a Opus 4.8 — Referencia

    Evaluaciones cualitativas basadas en el anuncio oficial de Anthropic (30 jun 2026).

    El dato más interesante: a nivel de esfuerzo máximo, Sonnet 5 iguala a Opus 4.8. Esto es arquitectónicamente significativo. Significa que para la mayoría de las tareas que antes justificaban pagar el precio de Opus, ahora puedes usar Sonnet 5 a un coste mucho menor.

    La excepción es ciberseguridad. Anthropic es explícito: Sonnet 5 no fue entrenado deliberadamente para tareas de seguridad ofensiva, y Opus 4.8 sigue siendo superior en ese dominio específico.


    Precios y disponibilidad

    Plan Precio hasta 31 ago 2026 Precio desde 1 sep 2026
    Input tokens $2 / M tokens $3 / M tokens
    Output tokens $10 / M tokens $15 / M tokens

    Anthropic ha aplicado un precio introductorio hasta finales de agosto. Si estás evaluando el switch en la API, este es el momento óptimo para hacerlo.

    Dónde está disponible:

    • Modelo predeterminado en los planes Free y Pro de Claude.ai
    • Disponible en Max, Team, Enterprise
    • Claude Code
    • API (model ID: claude-sonnet-5)

    Si usas Claude.ai directamente, ya lo tienes — es el modelo por defecto desde el lanzamiento.


    El tokenizador actualizado: impacto práctico

    Este punto se menciona poco y puede sorprenderte en producción.

    Sonnet 5 usa un tokenizador actualizado similar al que se introdujo con Opus 4.7. El resultado es que el mismo texto que antes ocupaba X tokens ahora puede ocupar entre 1.0× y 1.35× más tokens.

    ¿Qué significa esto en la práctica?

    Si tienes prompts largos con contexto extenso (documentos, conversaciones, sistemas de RAG), tu consumo de tokens aumentará. Anthropic compensa esto con el precio introductorio, pero necesitas tener este factor en cuenta al proyectar costes para producción.

    Una regla rápida: si venías de Sonnet 4.6 y tienes prompts de más de 5.000 tokens, haz una prueba controlada antes de cambiar el modelo en producción. Mide el consumo real, no lo estimes desde los benchmarks públicos.

    Para esto el post sobre prompt caching en Claude es directamente aplicable — con el nuevo tokenizador, el caching se vuelve aún más relevante para controlar costes.


    Cómo empezar hoy

    En Claude.ai: Ya está activo. Es el modelo por defecto. No necesitas hacer nada.

    En la API:

    El siguiente ejemplo muestra la integración mínima con el SDK oficial @anthropic-ai/sdk para TypeScript. El único cambio respecto a modelos anteriores es el model ID: claude-sonnet-5.

    import Anthropic from "@anthropic-ai/sdk";
    
    const client = new Anthropic();
    
    const response = await client.messages.create({
      model: "claude-sonnet-5",
      max_tokens: 4096,
      messages: [
        {
          role: "user",
          content: "Analiza este código y sugiere mejoras de rendimiento...",
        },
      ],
    });
    

    El cambio de model ID es inmediato. Si tienes un sistema en producción con claude-sonnet-4-6, cambiar a claude-sonnet-5 no requiere modificar nada más en la integración básica.

    Si usas Claude Code, el modelo ya está disponible y puedes seleccionarlo desde la configuración del cliente.


    Qué significa esto para developers que construyen con IA

    Voy a ser directo porque creo que es lo que necesitas saber.

    El lanzamiento de Sonnet 5 no es un update de rendimiento incremental. Es un reposicionamiento del tier medio.

    Hasta ahora, la decisión era: velocidad y coste (Haiku/Sonnet) vs. capacidad y razonamiento complejo (Opus). Con Sonnet 5, esa brecha se cierra de forma significativa. Puedes construir agentes que realmente terminen el trabajo, a un coste que tiene sentido para producción.

    Para quien está construyendo productos con IA — y no solo experimentando — esto cambia la ecuación de build vs. cost. Puedes subir la ambición de tus agentes sin subir proporcionalmente el presupuesto.

    El riesgo que veo es el de siempre: sobreestimar la autonomía del modelo en los primeros días. Sonnet 5 es notablemente mejor en tareas autónomas, pero sigue siendo un modelo de lenguaje. Sigue necesitando specs claras, herramientas bien definidas y tests que verifiquen los outputs.

    En Dominicode Labs estamos ya trabajando con Sonnet 5 en los proyectos de la comunidad — si quieres ver cómo se integra en flujos reales de producción, es donde está pasando.


    Preguntas frecuentes sobre Claude Sonnet 5

    ¿Qué diferencia hay entre Claude Sonnet 5 y Claude Sonnet 4.6?

    Claude Sonnet 5 está optimizado para comportamiento agéntico: puede planificar y ejecutar tareas multi-paso sin interrupciones, verificar sus propios resultados automáticamente y usar herramientas (navegadores, terminales, APIs) de forma coordinada en cadenas largas de razonamiento. Sonnet 4.6 era capaz, pero tendía a detenerse o pedir confirmación en tareas complejas. La mejora en benchmarks como BrowseComp y OSWorld-Verified refleja exactamente esta diferencia en tareas autónomas.

    ¿Es Claude Sonnet 5 tan bueno como Claude Opus 4.8?

    En la mayoría de tareas cotidianas de codificación, razonamiento y trabajo de conocimiento, Sonnet 5 está muy cerca de Opus 4.8 — y a nivel de esfuerzo máximo puede igualarlo. La excepción es el dominio de ciberseguridad, donde Opus 4.8 sigue siendo superior porque fue entrenado deliberadamente para esas tareas. Para el 90% de los casos de uso de desarrollo con IA, Sonnet 5 es suficientemente capaz y mucho más accesible en precio.

    ¿Cómo afecta el nuevo tokenizador a mis costes en la API?

    El tokenizador actualizado puede incrementar el consumo de tokens en un factor de 1.0× a 1.35× respecto a modelos anteriores. Esto es especialmente relevante con contextos largos: documentos, conversaciones extendidas, sistemas RAG. Anthropic compensa esto con el precio introductorio vigente hasta el 31 de agosto de 2026. La recomendación práctica es medir el consumo real con tus prompts de producción antes de estimar costes a escala.

    ¿Puedo usar Claude Sonnet 5 en Claude Code?

    Sí. Claude Sonnet 5 está disponible en Claude Code desde el lanzamiento. Dado que Claude Code es una herramienta diseñada precisamente para tareas agénticas de desarrollo — escribir, ejecutar, verificar código de forma autónoma — la combinación con Sonnet 5 es especialmente potente. Puedes seleccionar el modelo desde la configuración del cliente.

    ¿Qué precio tiene Claude Sonnet 5 y cuándo cambia?

    El precio introductorio vigente hasta el 31 de agosto de 2026 es $2/M tokens de input y $10/M tokens de output. A partir del 1 de septiembre de 2026 pasa a $3/M input y $15/M output. Si estás evaluando la migración en producción, hacerlo antes de septiembre tiene sentido económico.

    ¿Claude Sonnet 5 ya está disponible en el plan gratuito de Claude.ai?

    Sí. Desde el lanzamiento el 30 de junio de 2026, Claude Sonnet 5 es el modelo predeterminado en todos los planes de Claude.ai, incluyendo el gratuito. No necesitas hacer ningún cambio — si abres Claude.ai hoy, ya estás usando Sonnet 5.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Angular v22 + Vercel AI SDK: streaming de IA con Signals

    Angular v22 + Vercel AI SDK: streaming de IA con Signals

    ANTHROPIC_API_KEY=sk-ant-xxxxxxxx

    
    ---
    
    ## Paso 1: El servidor backend con streamText
    
    Crea un archivo `server/chat.ts` fuera del proyecto Angular (o en un monorepo aparte). Este servidor tiene un solo endpoint: recibe mensajes, llama a Claude, y hace streaming de la respuesta.
    
    ```typescript
    // server/chat.ts
    import { streamText } from 'ai';
    import { anthropic } from '@ai-sdk/anthropic';
    
    const server = Bun.serve({
      port: 3000,
      async fetch(req) {
        // CORS para desarrollo local
        if (req.method === 'OPTIONS') {
          return new Response(null, {
            headers: {
              'Access-Control-Allow-Origin': '*',
              'Access-Control-Allow-Methods': 'POST, OPTIONS',
              'Access-Control-Allow-Headers': 'Content-Type',
            },
          });
        }
    
        if (req.method === 'POST' && new URL(req.url).pathname === '/api/chat') {
          const { messages } = await req.json();
    
          const result = streamText({
            model: anthropic('claude-sonnet-4-6'),
            system: 'Eres un asistente técnico especializado en Angular y desarrollo frontend moderno. Responde en español de forma concisa y directa.',
            messages,
          });
    
          return result.toTextStreamResponse({
            headers: {
              'Access-Control-Allow-Origin': '*',
            },
          });
        }
    
        return new Response('Not found', { status: 404 });
      },
    });
    
    console.log(`Servidor corriendo en http://localhost:${server.port}`);
    

    Arrancar el servidor:

    bun run server/chat.ts
    

    streamText de la AI SDK devuelve un objeto con varios métodos. toTextStreamResponse() genera una Response HTTP estándar con Content-Type: text/plain; charset=utf-8 y Transfer-Encoding: chunked — exactamente lo que necesita el cliente para consumir el stream token a token.


    Paso 2: El modelo de datos

    Antes del componente, define la interfaz de mensaje. Simple:

    // src/app/chat/chat.types.ts
    export interface ChatMessage {
      role: 'user' | 'assistant';
      content: string;
    }
    

    Paso 3: El componente Angular v22 con Signals

    Aquí es donde la magia ocurre. No necesitas HttpClient con responseType: 'text' — eso no soporta streaming incremental. Necesitas fetch nativo con ReadableStream.

    // src/app/chat/chat.component.ts
    import {
      Component,
      signal,
      computed,
      ChangeDetectionStrategy,
    } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    import { ChatMessage } from './chat.types';
    
    @Component({
      selector: 'app-chat',
      standalone: true,
      imports: [FormsModule],
      changeDetection: ChangeDetectionStrategy.OnPush,
      templateUrl: './chat.component.html',
    })
    export class ChatComponent {
      messages = signal<ChatMessage[]>([]);
      userInput = signal('');
      isStreaming = signal(false);
    
      canSend = computed(
        () => this.userInput().trim().length > 0 && !this.isStreaming()
      );
    
      async sendMessage() {
        const content = this.userInput().trim();
        if (!content || this.isStreaming()) return;
    
        // Añadir mensaje del usuario
        this.messages.update((msgs) => [
          ...msgs,
          { role: 'user', content },
        ]);
        this.userInput.set('');
        this.isStreaming.set(true);
    
        // Capturar mensajes ANTES del placeholder — la API rechaza content vacío como último mensaje
        const messagesToSend = this.messages();
    
        // Placeholder para la respuesta del asistente
        this.messages.update((msgs) => [
          ...msgs,
          { role: 'assistant', content: '' },
        ]);
    
        try {
          const response = await fetch('http://localhost:3000/api/chat', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ messages: messagesToSend }),
          });
    
          if (!response.ok) throw new Error(`HTTP ${response.status}`);
          if (!response.body) throw new Error('No stream body');
    
          const reader = response.body.getReader();
          const decoder = new TextDecoder();
    
          while (true) {
            const { done, value } = await reader.read();
            if (done) break;
    
            const chunk = decoder.decode(value, { stream: true });
    
            // Actualiza el último mensaje (el del asistente) acumulando el chunk
            this.messages.update((msgs) => {
              const updated = [...msgs];
              const last = updated[updated.length - 1];
              updated[updated.length - 1] = {
                ...last,
                content: last.content + chunk,
              };
              return updated;
            });
          }
        } catch (error) {
          console.error('Error en streaming:', error);
          this.messages.update((msgs) => {
            const updated = [...msgs];
            updated[updated.length - 1] = {
              role: 'assistant',
              content: 'Error al conectar con el servidor. Comprueba que el backend está corriendo.',
            };
            return updated;
          });
        } finally {
          this.isStreaming.set(false);
        }
      }
    
      handleEnter(event: KeyboardEvent) {
        if (event.key === 'Enter' && !event.shiftKey) {
          event.preventDefault();
          this.sendMessage();
        }
      }
    }
    

    Tres decisiones clave en este componente:

    messages = signal<ChatMessage[]>([]) — todo el historial de la conversación vive en un signal. Cada vez que llega un chunk, actualizamos el último mensaje del array con update(). Angular detecta el cambio y re-renderiza solo ese elemento.

    ChangeDetectionStrategy.OnPush — esencial para este patrón. Sin esto, Angular ejecutaría la detección de cambios en cada tick mientras el stream está activo. Con OnPush + Signals, Angular solo actualiza cuando el signal cambia — que es exactamente cuando llega un chunk nuevo.

    fetch nativo en lugar de HttpClient — HttpClient es poderoso para peticiones normales, pero para streaming necesitas acceso al ReadableStream crudo del Response. fetch te da eso directamente con response.body.getReader().


    Paso 4: El template con el nuevo control flow

    El template aprovecha el control flow de Angular v17+ (@for, @if) y lee los signals directamente — sin async pipe, sin | async, sin subscripciones.

    <!-- src/app/chat/chat.component.html -->
    <div class="chat-container">
      <div class="messages-area">
        @if (messages().length === 0) {
          <p class="empty-state">Escribe un mensaje para empezar.</p>
        }
    
        @for (msg of messages(); track $index) {
          <div class="message" [class]="msg.role">
            <span class="role-label">
              {{ msg.role === 'user' ? 'Tú' : 'Asistente' }}
            </span>
            <p class="message-content">{{ msg.content }}</p>
    
            @if (msg.role === 'assistant' && $last && isStreaming()) {
              <span class="cursor-blink">|</span>
            }
          </div>
        }
      </div>
    
      <div class="input-area">
        <textarea
          [value]="userInput()"
          (input)="userInput.set($any($event.target).value)"
          (keydown)="handleEnter($event)"
          placeholder="Escribe tu mensaje... (Enter para enviar)"
          rows="3"
          [disabled]="isStreaming()"
        ></textarea>
    
        <button
          (click)="sendMessage()"
          [disabled]="!canSend()"
        >
          @if (isStreaming()) {
            Generando...
          } @else {
            Enviar
          }
        </button>
      </div>
    </div>
    

    El cursor parpadeante | aparece solo en el último mensaje del asistente mientras isStreaming() es true. Es un detalle pequeño que hace que la experiencia se sienta viva.


    Paso 5: Estilos mínimos (opcional)

    /* src/app/chat/chat.component.css */
    .chat-container {
      display: flex;
      flex-direction: column;
      height: 100vh;
      max-width: 800px;
      margin: 0 auto;
      padding: 1rem;
      gap: 1rem;
    }
    
    .messages-area {
      flex: 1;
      overflow-y: auto;
      display: flex;
      flex-direction: column;
      gap: 1rem;
      padding: 1rem;
      border: 1px solid #e5e7eb;
      border-radius: 0.5rem;
    }
    
    .message {
      padding: 0.75rem 1rem;
      border-radius: 0.5rem;
      max-width: 80%;
    }
    
    .message.user {
      background: #e90464;
      color: white;
      align-self: flex-end;
    }
    
    .message.assistant {
      background: #f3f4f6;
      color: #111827;
      align-self: flex-start;
    }
    
    .role-label {
      font-size: 0.75rem;
      font-weight: 600;
      opacity: 0.7;
      display: block;
      margin-bottom: 0.25rem;
    }
    
    .cursor-blink {
      animation: blink 1s step-end infinite;
    }
    
    @keyframes blink {
      50% { opacity: 0; }
    }
    
    .input-area {
      display: flex;
      gap: 0.5rem;
    }
    
    textarea {
      flex: 1;
      padding: 0.75rem;
      border: 1px solid #d1d5db;
      border-radius: 0.5rem;
      resize: none;
      font-family: inherit;
    }
    
    button {
      padding: 0.75rem 1.5rem;
      background: #e90464;
      color: white;
      border: none;
      border-radius: 0.5rem;
      cursor: pointer;
      font-weight: 600;
      align-self: flex-end;
    }
    
    button:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }
    

    El resultado

    Arranca los dos procesos:

    # Terminal 1: backend
    bun run server/chat.ts
    
    # Terminal 2: Angular
    ng serve
    

    Abre http://localhost:4200. Escribe cualquier pregunta técnica. Las palabras aparecen token a token mientras Claude las genera. El botón muestra "Generando…" y el cursor parpadea al final del último mensaje.

    Eso es streaming real, en Angular v22, con Signals, en menos de 20 minutos.


    Por qué este patrón funciona bien en producción

    Si quieres entender cómo se conectan estos patrones con el desarrollo de productos completos con IA, el post sobre cómo crear productos con IA para vender muestra el panorama completo.

    Lo que tienes aquí no es un prototipo. Es un patrón que escala:

    El estado es predecible. Todo vive en messages = signal<ChatMessage[]>([]). No hay subscripciones dispersas, no hay Subject de BehaviorSubject, no hay que recordar hacer unsubscribe. El signal se actualiza, Angular re-renderiza lo necesario, punto.

    El backend es stateless. Cada petición envía el historial completo de mensajes. Así funciona la API de Anthropic — no hay sesión en el servidor, lo que facilita el escalado horizontal.

    ChangeDetectionStrategy.OnPush es obligatorio aquí. Con Zone.js y la detección de cambios por defecto, Angular correría su ciclo de detección constantemente mientras el stream está activo. Con OnPush + Signals, solo actualiza cuando el signal cambia.

    Si quieres llevar esto más allá — añadir herramientas (tool calls), mantener sesiones con localStorage, o integrar el chat dentro de una app Angular más grande con routing y autenticación — el patrón es el mismo. Cambias el modelo en el servidor, añades tools a streamText, y el componente no necesita modificarse.

    Si ya tienes experiencia con Angular y quieres dominar Signals, componentes standalone y el control flow moderno que hemos usado aquí, en el Curso Angular Moderno lo cubrimos desde la arquitectura hasta producción — incluyendo patrones de integración con APIs externas como esta.

    Y si quieres ir más allá del chat básico y construir agentes reales con Claude — con herramientas, contexto persistente, y pipelines de desarrollo AI-first — eso es exactamente lo que enseñamos en Construye con IA: de la idea al producto con Claude Code.


    FAQ

    ¿Necesito Angular Universal (SSR) para que esto funcione?

    No. El streaming ocurre entre el cliente Angular (browser) y el servidor Bun que creamos. Angular SSR es irrelevante para este patrón — el componente de chat vive completamente en el cliente. Si tienes SSR activado, asegúrate de que el componente de chat solo se renderiza en el browser con isPlatformBrowser o usando @defer.

    ¿Puedo usar el mismo enfoque con OpenAI o Google Gemini en lugar de Anthropic?

    Sí. Cambia @ai-sdk/anthropic por @ai-sdk/openai o @ai-sdk/google, y sustituye anthropic('claude-sonnet-4-6') por openai('gpt-4o') o google('gemini-2.5-pro'). El resto del código — el componente Angular, el consumo del stream, los Signals — no cambia. Esa es una de las ventajas del Vercel AI SDK: abstrae el proveedor.

    ¿Qué pasa si el usuario envía el siguiente mensaje mientras el anterior aún está en streaming?

    El botón está deshabilitado mientras isStreaming() es true gracias al computed canSend. El usuario no puede enviar otro mensaje hasta que el stream termine. Si quieres cancelar el stream activo al recibir un nuevo mensaje, puedes guardar el reader como propiedad del componente y llamar a reader.cancel() antes de iniciar la nueva petición.

    ¿Cómo manejo el historial para conversaciones largas?

    La API de Anthropic tiene un límite de tokens por request. Para conversaciones largas, lo más simple es limitar el historial que envías al servidor — por ejemplo, los últimos 20 mensajes. En producción, lo correcto es implementar una ventana deslizante o resumir el historial antiguo con un llamada previa al modelo. Por ahora, con this.messages().slice(-20) en el body del fetch tienes un control básico suficiente para empezar.

    ¿Puedo usar HttpClient en lugar de fetch nativo?

    HttpClient con responseType: 'text' recibe el texto completo cuando la conexión cierra — no es streaming incremental. Para streaming real necesitas acceso al ReadableStream crudo de la Response, que solo fetch te proporciona directamente. Podrías implementar un interceptor custom o un HttpBackend alternativo, pero la complejidad no vale la pena. fetch nativo es la solución correcta aquí.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Claude Code Routines: automatiza agentes sin encender tu PC

    Claude Code Routines: automatiza agentes sin encender tu PC

    El viernes por la tarde cerré el portátil con 23 issues sin triagear en el repo, tres PRs esperando revisión de documentación y un changelog que nadie había actualizado en dos semanas.

    El lunes por la mañana, todo estaba hecho.

    No porque contraté a nadie. No porque dejé el ordenador encendido todo el fin de semana. Fue la primera vez que sentí que las Claude Code Routines no eran una feature más de Anthropic — eran la diferencia entre usar IA como herramienta y usarla como infraestructura.


    De herramienta a infraestructura: el salto que cambia todo

    Si ya usas Claude Code de forma interactiva, sabes lo que puede hacer. Le das contexto, le pides algo, revisa tu código, abre PRs. Pero todo depende de que tú estés sentado delante del teclado, iniciando cada conversación.

    Las Routines rompen esa dependencia.

    Una Routine es una configuración guardada de Claude Code: un prompt, uno o más repositorios y un conjunto de conectores (MCP), empaquetados una sola vez y ejecutados de forma automática. Lo que las hace distintas de cualquier script de bash con un cron job es que corren en la infraestructura cloud de Anthropic. Tu máquina puede estar apagada. Claude sigue trabajando.

    Están disponibles desde abril de 2026 en research preview para todos los planes de pago: Pro, Max, Team y Enterprise. Se crean desde claude.ai/code/routines — consulta la documentación oficial de Claude Code para ver los últimos límites y cambios.


    Los tres tipos de trigger

    Una Routine puede tener uno o varios triggers combinados. Esto es lo que hace que el modelo sea flexible de verdad.

    1. Schedule (cron)

    Ejecuta la Routine de forma recurrente: cada hora, diariamente, entre semana o cada semana. Si necesitas un intervalo personalizado — por ejemplo, cada dos horas o el primer día de cada mes — configuras el preset más cercano en la interfaz de claude.ai/code/routines.

    El intervalo mínimo es una hora. Expressions que corren con más frecuencia se rechazan.

    También existe el concepto de one-off run: disparas la Routine una sola vez en un timestamp futuro. Útil para recordatorios diferidos, limpiezas post-deploy o tareas que tienen que correr "cuando aterrice ese PR de upstream". Después de ejecutarse, la Routine se auto-deshabilita. Y un detalle importante: los one-off runs no cuentan contra el límite diario de Routines.

    2. GitHub event

    Dispara una sesión nueva automáticamente cuando ocurre un evento en un repositorio conectado. Los eventos soportados incluyen pull request (opened, closed, labeled, synchronized…) y release (created, published, edited…).

    Puedes añadir filtros para reducir exactamente cuándo se dispara: autor del PR, título, rama base, rama head, labels, si es draft o no, si está mergeado. Cada evento que pasa los filtros abre su propia sesión independiente — no hay reutilización de sesiones entre eventos.

    Para usar GitHub triggers hace falta instalar la Claude GitHub App en el repositorio. No basta con el acceso que configuras en /web-setup para clonar repos.

    3. Webhook (API trigger)

    Cada Routine con este trigger tiene un endpoint HTTP dedicado. Le haces POST con un bearer token y arranca una sesión nueva. El cuerpo de la request acepta un campo text opcional — puedes pasarle el cuerpo de una alerta, un stack trace o cualquier contexto que la Routine necesite para esa ejecución concreta.

    La respuesta devuelve el ID y la URL de la sesión creada, así puedes abrirla en el navegador para ver qué está haciendo Claude en tiempo real.

    curl -X POST https://api.anthropic.com/v1/claude_code/routines/trig_01ABCDEF.../fire \
      -H "Authorization: Bearer sk-ant-oat01-xxxxx" \
      -H "anthropic-beta: experimental-cc-routine-2026-04-01" \
      -H "anthropic-version: 2023-06-01" \
      -H "Content-Type: application/json" \
      -d '{"text": "Error crítico en producción: SEN-4521. Stack trace adjunto."}'
    

    Tres Routines que puedes activar esta semana

    Estos no son ejemplos de documentación. Son los casos de uso que más sentido tienen para un developer indie o un equipo pequeño.

    Triage de issues cada noche

    Un trigger de schedule que corre de lunes a viernes a las 23:00. El prompt le dice a Claude que lea todos los issues abiertos desde la última ejecución, aplique labels según el área de código referenciada, asigne propietario y publique un resumen en Slack. Llegas por la mañana con la cola de trabajo ya priorizada.

    Requiere: conector de GitHub + conector de Slack configurados como MCP connectors en tu cuenta de claude.ai.

    Code review automatizado en cada PR

    Un trigger de GitHub que reacciona a pull_request.opened con filtro is draft: false. El prompt aplica el checklist de revisión de tu equipo: seguridad, performance, style. Deja comentarios inline y un resumen para los revisores humanos. Los humanos se concentran en diseño y arquitectura — lo mecánico lo hace Claude.

    Este es el tipo de automatización que en equipos de 1-3 personas elimina el cuello de botella de revisión completamente.

    Changelog automático post-merge

    Un trigger de GitHub en pull_request.closed filtrado a is merged: true en la rama main. El prompt le pide a Claude que lea el diff del PR mergeado, extraiga el cambio relevante en lenguaje humano y lo añada al CHANGELOG.md en un PR nuevo. Sin nunca más tener que acordarte de documentar lo que acabas de subir a producción.


    Lo que paga el coste

    Las Routines consumen cuota de suscripción de la misma manera que una sesión interactiva. Además, hay un límite diario de runs por cuenta según el plan:

    • Pro: 5 runs diarios
    • Max: 15 runs diarios
    • Team / Enterprise: 25 runs diarios

    Si superas el límite o la cuota de suscripción, las ejecuciones siguientes se rechazan hasta que se resetea la ventana — salvo que tengas usage credits activados, en cuyo caso sigue corriendo en modo metered.

    Los GitHub triggers también tienen un cap por hora durante la research preview. Si un repositorio muy activo dispara demasiados eventos, los excedentes se descartan hasta que se resetea la ventana. Los límites actuales los ves en claude.ai/code/routines.

    El hecho de que sea research preview significa que los límites, la API y el comportamiento pueden cambiar. No construyas pipelines de producción críticos sobre esto todavía — pero sí es el momento perfecto para experimentar y entender cómo integrar esto en tu workflow.


    Routines vs. Managed Agents: no es lo mismo

    Anthropic también ha lanzado Claude Managed Agents con dos features que suenan parecidas pero son una capa distinta: Dreaming y Outcomes.

    La diferencia es importante para no confundirlos.

    Las Routines son un mecanismo de scheduling y ejecución. Definen cuándo y cómo corre una sesión de Claude Code. Son infraestructura de automatización.

    Dreaming es un proceso que revisa las sesiones pasadas de tus agentes y los memory stores, extrae patrones y perfecciona las memorias para que el agente mejore con el tiempo. Es un sistema de aprendizaje retrospectivo, no de ejecución de tareas.

    Outcomes es una feature de evaluación: defines un rubric de éxito y un evaluador separado (con su propio context window, para no contaminarse con el razonamiento del agente) revisa el output y le dice al agente qué corregir si no cumple el criterio. Es un loop de calidad, no de scheduling.

    Dicho de forma directa: las Routines responden a "¿cuándo y con qué trigger corre esto?". Managed Agents responde a "¿cómo mejora y cómo evalúa su propio output el agente?". Pueden usarse juntos, pero son capas con responsabilidades distintas.


    Un detalle que no está en la documentación oficial

    Cuando una Routine corre, lo hace de forma completamente autónoma. Sin permission mode, sin prompts de aprobación durante la ejecución. Claude puede ejecutar comandos de shell, usar skills del repositorio clonado y llamar a todos los conectores que hayas incluido.

    Esto es potente. Y también es la razón por la que el prompt de la Routine es el artefacto más importante del sistema. A diferencia de una sesión interactiva donde puedes corregir el rumbo, aquí el prompt tiene que ser autocontenido y explícito sobre qué hacer y qué aspecto tiene el éxito.

    Por defecto, Claude solo puede hacer push a ramas con prefijo claude/. Para permitirle escribir en ramas existentes o protegidas, tienes que habilitar explícitamente "Allow unrestricted branch pushes" en la configuración de la Routine. Una salvaguarda razonable.

    Las Routines pertenecen a tu cuenta individual de claude.ai. Los commits, los PRs y las acciones en conectores como Slack o Linear aparecen como tú — con tu identidad de GitHub, tu Slack, etc. Eso tiene implicaciones de auditoría que vale la pena tener en cuenta si trabajas en equipo.


    El shift real

    Llevo tiempo diciendo que el developer indie de 2026 puede operar con la capacidad de un equipo pequeño si usa bien las herramientas que tiene. Si llegas nuevo a Claude Code, el post sobre Effort, Models, Tools y Context te da el mapa completo antes de entrar en Routines. Y si quieres entender la capa de arquitectura detrás de los agentes, el post sobre agentic harness completa el cuadro. Las Routines son la prueba más concreta de eso que he visto hasta ahora.

    No es sobre chatear con IA. Es sobre delegar trabajo real a agentes que corren en la nube con tu identidad, contra tus repos, con tus herramientas. Y que lo hacen mientras tú duermes, estás en una reunión o simplemente tienes el portátil cerrado.

    Si llevas tiempo usando Claude Code de forma interactiva, las Routines son el siguiente paso natural. Si quieres un sistema para construir esto de forma ordenada — desde la idea hasta el producto sin caos — en el curso Construye con IA en Udemy cubrimos exactamente ese proceso: cómo estructurar el trabajo con Claude Code para que escale más allá de la sesión interactiva.

    Y si quieres ver cómo otros developers están implementando esto en proyectos reales, en Dominicode Labs estamos documentando los patrones que funcionan — incluyendo los prompts de Routines que uso en mi propio workflow.


    Preguntas frecuentes

    ¿Necesito tener mi servidor propio para usar Claude Code Routines?

    No. Las Routines corren directamente en la infraestructura cloud de Anthropic. No necesitas EC2, Railway, Fly.io ni ningún servidor propio. El único requisito es una suscripción de pago a Claude (Pro, Max, Team o Enterprise) con Claude Code on the web habilitado.

    ¿Cuál es la diferencia entre una Routine y un Desktop Scheduled Task?

    Los Desktop Scheduled Tasks corren en tu máquina local cuando el app de escritorio de Claude Code está abierto. Tienen acceso a tus archivos locales pero requieren que tu ordenador esté encendido. Las Routines corren en la nube de Anthropic independientemente de si tienes el ordenador encendido o el app abierto.

    ¿Puedo combinar varios tipos de trigger en la misma Routine?

    Sí. Una misma Routine puede tener triggers de schedule, de GitHub event y de API al mismo tiempo. Por ejemplo, una Routine de revisión de PRs puede correr de forma programada cada noche, dispararse también cuando se abre un PR nuevo en GitHub, y aceptar ejecuciones manuales vía webhook desde tu pipeline de CI/CD.

    ¿Qué pasa si una Routine falla o Claude no completa la tarea?

    El indicador de estado verde en el historial de runs solo significa que la sesión se inició y terminó sin errores de infraestructura — no que la tarea se completó con éxito. Para saber qué hizo Claude realmente tienes que abrir la sesión y revisar el transcript. Los errores de red, los conectores que faltan o los fallos a nivel de tarea aparecen en el transcript, no en el indicador de estado.

    ¿Las Routines tienen acceso a todos mis conectores MCP?

    Por defecto, cuando creas una Routine, incluye todos tus MCP connectors conectados en claude.ai. La recomendación de Anthropic es quitar los que no necesita la Routine específica para limitar el alcance de lo que Claude puede hacer durante la ejecución. Los MCP servers que hayas añadido localmente en el CLI con claude mcp add no están disponibles en Routines — tienes que añadirlos como connectors en claude.ai/customize/connectors.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Claude Code: Effort, Models, Tools y Context para developers

    Claude Code: Effort, Models, Tools y Context para developers

    La primera vez que abrí Claude Code, lo traté como un chat más inteligente. Le pegaba código, le pedía que lo arreglara, copiaba la respuesta. Funcionaba, pero lo estaba usando como una versión cara de Stack Overflow.

    Tardé tres semanas en entender que Claude Code no es un chatbot. Es un agente que ejecuta herramientas reales en tu sistema, que puede leer tu repositorio entero, que tiene niveles de razonamiento configurables y que toma decisiones en cadena sin que tú intervengas en cada paso.

    Cuando lo entendí así, cambió todo.

    Este post es lo que me hubiera gustado leer antes de empezar. No es un tutorial de instalación — asume que ya lo tienes corriendo. Es una explicación honesta de los cuatro conceptos que determinan si Claude Code trabaja para ti o contra ti: Effort, Models, Tools y Context.


    Effort — el nivel de razonamiento que decides gastar

    Cuando Claude Code procesa una tarea, no siempre piensa igual de profundo. Puedes configurar cuánto razonamiento aplica desde la UI de Claude Code o mediante la opción de esfuerzo en la configuración. Los niveles son cuatro: low, medium, high y max.

    Esto no es marketing. Es la diferencia entre gastar dos segundos y gastar dos minutos en una misma pregunta, con respuestas radicalmente distintas.

    Low — cuando la velocidad importa más que la precisión

    Con low, Claude Code responde rápido y sin profundizar demasiado. Es útil para tareas mecánicas y predecibles: renombrar variables, formatear código, generar boilerplate que ya tienes en mente pero no quieres teclear.

    Si le pides "añade un método toString() a esta clase", no necesita razonar sobre arquitectura. low es suficiente.

    Medium — el nivel por defecto para trabajo diario

    medium es lo que usas el 80% del tiempo. Hay razonamiento real, considera contexto, pero no entra en análisis profundo de consecuencias. Funciona bien para refactoring moderado, explicaciones técnicas, generación de tests unitarios para funciones simples.

    Es el equilibrio entre velocidad y calidad que necesitas en un flujo de trabajo normal.

    High — cuando el error cuesta caro

    Aquí Claude Code empieza a razonar sobre consecuencias. Evalúa múltiples opciones antes de decidir, considera casos borde, analiza impacto en el resto del sistema.

    Úsalo cuando toques código crítico: un servicio de autenticación, la lógica de pagos, una migración de base de datos, un cambio arquitectural en el core de la aplicación. El tiempo extra que tarda se justifica con la reducción de errores no detectados.

    Max — análisis exhaustivo, sin atajos

    max activa el razonamiento más profundo disponible. Claude Code descompone el problema en partes, considera múltiples estrategias, evalúa trade-offs explícitamente.

    Esto no es para trabajo diario. Es para cuando necesitas que te ayude a diseñar la arquitectura de un módulo nuevo, cuando tienes un bug imposible de reproducir que llevas días persiguiendo, o cuando vas a tomar una decisión técnica con consecuencias a largo plazo.

    El coste es tiempo y tokens. La ganancia es profundidad real.

    Regla práctica: empieza con medium. Si la respuesta no llega al nivel que necesitas, sube un nivel. No uses max por defecto — no tiene sentido pagar el coste de razonamiento exhaustivo para añadir un campo en un formulario.


    Models — cuál elegir y por qué importa

    Claude Code tiene acceso a varios modelos bajo el capó. No todos son iguales en velocidad, coste ni capacidad. Elegir mal aquí es tirar dinero o tirar tiempo.

    A junio de 2026, los modelos disponibles en Claude Code son:

    Claude Haiku 4.5 — velocidad máxima, coste mínimo

    Haiku es el modelo pequeño. Responde en segundos, cuesta muy poco por token, y es más que suficiente para tareas de bajo peso cognitivo: completar líneas de código, responder preguntas de documentación, generar snippets concretos que ya tienes pensados.

    En un workflow agentic donde Claude Code ejecuta decenas de llamadas encadenadas (leer archivos, buscar patrones, escribir logs), Haiku hace el trabajo de las subtareas sin disparar el coste.

    Claude Sonnet 4.6 — el modelo de trabajo diario

    Sonnet es el punto dulce. Más capaz que Haiku en razonamiento y contexto largo, más rápido y barato que Opus, suficientemente potente para el 90% de las tareas de un developer.

    Refactoring complejo, generación de tests con lógica no trivial, debugging asistido, implementación de features completas — Sonnet lo maneja bien. Si no sabes cuál usar, empieza aquí.

    Claude Opus 4.8 — para problemas difíciles

    Opus es el modelo grande. Más lento, más caro, y considerablemente más capaz cuando el problema requiere razonamiento profundo, comprensión de contexto muy largo o análisis de consecuencias en sistemas complejos.

    No lo uses para tareas rutinarias. Sí lo uses cuando estés diseñando una arquitectura nueva, cuando el problema tiene múltiples dependencias que hay que razonar en paralelo, o cuando los outputs de Sonnet no son suficientemente precisos para tu caso.

    Claude Fable 5 — el modelo más potente

    Fable es el frontier model de Anthropic. Capacidades extendidas de razonamiento, mejor manejo de contexto muy largo y mayor precisión en tareas de alta complejidad. En Claude Code aparece como opción para las tareas más exigentes.

    Úsalo con criterio: el coste es significativamente mayor. Tiene sentido cuando diseñas sistemas críticos, cuando necesitas que el modelo razone sobre un codebase completo de miles de archivos, o cuando el nivel de precisión que necesitas no lo alcanza Opus.

    La decisión práctica: para trabajo diario usa Sonnet. Para subtareas rápidas y repetitivas dentro de un agente, Haiku. Para decisiones técnicas importantes o problemas difíciles, Opus o Fable. El modelo correcto no es el más potente — es el que resuelve el problema con el menor coste posible.


    Tools — las herramientas built-in que hacen a Claude Code un agente real

    Aquí está la diferencia fundamental entre Claude Code y un chatbot: Claude Code tiene herramientas que ejecuta de verdad en tu sistema. No simula leer archivos — los lee. No describe cómo haría una búsqueda — la hace.

    Estas son las herramientas principales y para qué sirve cada una:

    Herramienta Qué hace
    Read Lee el contenido de un archivo del filesystem. Claude ve exactamente lo que hay en el archivo, con números de línea.
    Edit Modifica un fragmento concreto de un archivo existente. Solo envía el diff, no reescribe todo el archivo.
    Write Crea un archivo nuevo o sobreescribe uno completo. Más costoso que Edit — úsalo solo cuando el cambio afecta a todo el archivo.
    Bash Ejecuta comandos de shell reales en tu sistema. Tests, builds, git, scripts, cualquier cosa que harías en terminal.
    Glob Busca archivos por patrón (**/*.ts, src/**/*.spec.ts). Útil para que Claude Code entienda la estructura del proyecto antes de actuar.
    Grep Busca contenido dentro de archivos por expresión regular. Para localizar dónde se usa una función, qué archivos importan un módulo, qué tests cubren una clase.
    WebSearch Hace búsquedas web reales. Útil cuando necesita documentación actualizada, información sobre versiones recientes o validar datos externos.
    WebFetch Descarga y procesa el contenido de una URL concreta. Para leer documentación oficial, specs de una API, changelog de una librería.
    Agent Lanza un subagente — una instancia paralela de Claude Code que ejecuta una subtarea de forma independiente. Arquitectura agentic en acción.
    TodoRead / TodoWrite Gestiona una lista de tareas interna de la sesión. Claude Code se auto-organiza las tareas que tiene pendientes en una tarea compleja.

    Lo que hace potente a este conjunto no es ninguna herramienta por sí sola — es la combinación. Claude Code lee la estructura del proyecto con Glob, localiza el código relevante con Grep, lo lee con Read, lo modifica con Edit, y ejecuta los tests con Bash. Todo en secuencia, sin que tú intervengas en cada paso.

    Este es el flujo que hace que una instrucción como "refactoriza el módulo de autenticación para que use el nuevo interceptor HTTP" produzca cambios reales en diez archivos distintos, con los tests pasando al final.

    La referencia completa de todas las herramientas y sus parámetros está en la documentación oficial de Claude Code.

    Si quieres ver cómo encajan estas herramientas con el resto del stack IA, en Stack IA agéntica en 2026: qué usar, qué ignorar y cuál elijo analizo exactamente eso.

    Si te interesa construir workflows agenticos más avanzados con Claude Code — desde la idea hasta un producto deployado — el curso Construye con IA: De la Idea al Producto con Claude y Specs cubre exactamente eso: cómo orquestar estas herramientas para que Claude Code trabaje con autonomía real.


    Context — cómo sabe Claude Code dónde está y qué importa

    El contexto es el factor más subestimado de Claude Code. Puedes tener el modelo correcto, el nivel de esfuerzo correcto y todas las herramientas disponibles — si Claude Code no entiende el contexto de tu proyecto, los outputs serán genéricos.

    @files y @folders — lo que le pones delante

    En la interfaz de Claude Code puedes mencionar archivos o carpetas con @. Cuando escribes @src/app/auth/auth.service.ts, Claude Code lee ese archivo y lo incluye directamente en el contexto de la conversación antes de procesar tu instrucción.

    Con @src/app/auth/ incluyes toda la carpeta. Claude Code procesa los archivos relevantes y construye una comprensión del módulo antes de actuar.

    Esto no es solo "adjuntar archivos". Es darle a Claude Code el mapa del territorio antes de pedirle que navegue.

    @url — documentación externa en tiempo real

    @url le permite a Claude Code leer el contenido de una URL y usarlo como contexto. Si necesitas que siga la documentación oficial de Angular v22 antes de modificar tu código de routing, puedes darle la URL del changelog y él la procesa.

    Esto elimina el problema clásico de los LLMs con conocimiento desactualizado. Si la librería sacó una versión nueva hace dos semanas, puedes darle la fuente actualizada directamente.

    CLAUDE.md — la memoria persistente del proyecto

    El archivo CLAUDE.md en la raíz de tu proyecto es la forma de darle a Claude Code instrucciones permanentes que se cargan en cada sesión.

    Aquí defines las convenciones del proyecto: cómo nombrar archivos, qué patrones arquitecturales seguís, qué comandos son los válidos, qué herramientas externas usáis, qué NO debe tocar sin confirmación explícita. Un CLAUDE.md bien escrito hace que Claude Code se comporte como un developer que conoce las reglas del equipo desde el primer día.

    No es opcional. Es la diferencia entre un agente que trabaja contigo y uno que trabaja en paralelo a ti sin coordinación.

    Memoria entre sesiones

    Por defecto, cada sesión de Claude Code empieza sin memoria de conversaciones anteriores. El contexto no persiste automáticamente.

    La forma correcta de manejar esto es el CLAUDE.md: las decisiones técnicas importantes, las convenciones acordadas, las restricciones del proyecto — todo lo que necesita persistir va ahí. No en el historial de conversación.

    Para proyectos más complejos, puedes estructurar archivos adicionales de contexto (specs, planes, documentos de arquitectura) y referenciarlos con @ al inicio de cada sesión. Es un flujo de trabajo, no una feature automática.

    En Dominicode Labs tenemos proyectos reales donde aplicamos exactamente esta estructura — con los archivos de contexto organizados para que Claude Code mantenga coherencia a lo largo de semanas de desarrollo.


    Cuatro hábitos para usar Claude Code como un agente real

    Claude Code no es difícil. Pero usarlo bien requiere entender que no es un chatbot avanzado — es un agente con herramientas reales, niveles de razonamiento configurables, múltiples modelos con características distintas, y un sistema de contexto que tú controlas.

    Elegir el modelo correcto para cada tarea, configurar el esfuerzo según lo que está en juego, dejar que las tools hagan el trabajo sin microgestionar cada paso, y mantener un CLAUDE.md que le dé continuidad al proyecto — esos cuatro hábitos son la diferencia entre usarlo como un buscador caro y usarlo como un colaborador técnico real.

    El siguiente paso es construir algo con él. No un script de prueba — un flujo de trabajo real donde Claude Code gestione decisiones en cadena. Si quieres ver ese proceso desde el principio, el curso Construye con IA: De la Idea al Producto con Claude y Specs parte exactamente de aquí.


    FAQ — Preguntas frecuentes sobre Claude Code

    ¿Claude Code funciona con cualquier lenguaje de programación?

    Sí. Claude Code no está limitado a ningún stack. Funciona igual con TypeScript, Python, Go, Rust, Java o cualquier lenguaje que puedas ejecutar desde terminal. Las herramientas como Bash, Glob y Grep operan sobre el filesystem, no sobre el lenguaje. Lo que sí varía es la calidad del output según el lenguaje — para TypeScript y Python la precisión es especialmente alta porque son los lenguajes más representados en el entrenamiento.

    ¿Cuál es la diferencia real entre Sonnet y Opus para trabajo diario?

    En la práctica, para el 90% de las tareas cotidianas no notarás diferencia en calidad. Sí notarás diferencia en velocidad y coste. Opus tarda más y consume más tokens. La diferencia se hace evidente en problemas complejos con mucho contexto: cuando le das un módulo de 3.000 líneas y le pides que entienda las dependencias implícitas antes de refactorizar, Opus razona más profundo. Para añadir un endpoint nuevo a una API que ya funciona, Sonnet es suficiente.

    ¿Cómo evito que Claude Code modifique archivos que no debe tocar?

    Con el CLAUDE.md. Puedes definir explícitamente qué archivos o carpetas son de solo lectura, qué operaciones requieren confirmación explícita tuya antes de ejecutarse, y qué convenciones debe respetar siempre. Claude Code en modo interactivo ya solicita confirmación antes de ejecutar operaciones destructivas — y con autoApproveEdits: false en tu settings.json puedes reforzar ese control para cualquier edición de archivos.

    ¿Claude Code puede trabajar en proyectos con múltiples repositorios?

    Sí, pero con matices. Claude Code opera desde el directorio donde lo lanzas y puede leer rutas relativas o absolutas fuera de él si tienes los permisos correctos. Para proyectos monorepo o arquitecturas con múltiples repos relacionados, la práctica recomendada es lanzarlo desde la raíz del monorepo y gestionar el contexto con @carpetas específicas para cada subtarea. Si trabajas con Angular en un monorepo, el curso de Angular Moderno cubre la estructura de proyectos que mejor se integra con flujos agenticos.

    ¿Cuánto contexto puede manejar Claude Code en una sesión?

    Depende del modelo. Los modelos actuales de Claude tienen ventanas de contexto de 200.000 tokens, lo que equivale a varios cientos de miles de líneas de código. En la práctica, el límite operativo es antes: a partir de cierto volumen, la calidad del razonamiento empieza a degradarse aunque técnicamente quepa más. La buena práctica es ser selectivo con el contexto que cargas — usar @ para incluir solo los archivos relevantes para la tarea actual, no volcar el repositorio entero en cada sesión.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.