Category: Blog

Your blog category

  • Cómo correr modelos de IA en local con Ollama

    Cómo correr modelos de IA en local con Ollama

    Era domingo por la noche. Tenía que probar un flujo de extracción de datos con un LLM y el presupuesto del cliente para APIs era cero. Nada aprobado, nada disponible.

    La solución: correr modelos IA en local. Instalé Ollama en diez minutos, bajé Llama 3 y terminé el prototipo antes de medianoche sin gastar un solo centavo en tokens.

    Eso es lo que te da correr modelos en local: velocidad, independencia y control total sobre lo que entra y lo que sale del modelo. Y en 2026, con los modelos open source en el estado en que están, la calidad ya no es una excusa para no hacerlo.

    Correr modelos IA en local significa ejecutar un Large Language Model directamente en tu hardware — sin enviar datos a APIs externas, sin coste por token y con latencia controlada. Herramientas como Ollama hacen que el proceso sea tan sencillo como instalar cualquier CLI.


    Por qué correr modelos en local (y cuándo importa de verdad)

    Hay cuatro razones concretas. Y van más allá del coste.

    Privacidad. Si trabajas con datos sensibles — contratos, código propietario, información médica — mandar esos datos a una API externa es un riesgo legal y contractual. Con modelos IA en local, los datos nunca salen de tu máquina.

    Latencia. Una llamada a GPT-4o tiene entre 300ms y 2 segundos de ida y vuelta dependiendo del tamaño del prompt. Un modelo local bien configurado en una máquina decente responde en milisegundos para tareas simples. Si construyes herramientas de developer experience o pipelines de CI con IA, esa diferencia importa.

    Iteración sin coste. En fases de prototipado puedes hacer miles de llamadas al día para testear prompts, ajustar pipelines y explorar comportamientos del modelo. Con una API de pago, eso se acumula rápido. En local, es gratis.

    Trabajo offline. Aviones, conexiones lentas, redes corporativas con proxies restrictivos. Un modelo local funciona siempre.


    Las dos herramientas que necesitas conocer

    Ollama — la opción de developer

    Ollama es la herramienta que más uso y la que recomiendo si eres developer. Es una CLI + servidor HTTP que gestiona la descarga, configuración y ejecución de modelos como si fueran contenedores Docker.

    Funciona en macOS, Linux y Windows. Tiene una API REST compatible con el formato de OpenAI, lo que significa que puedes usar clientes existentes con un cambio mínimo de URL. Y tiene una comunidad activa con más de 100 modelos disponibles en su registry.

    Si sabes usar Docker, Ollama te va a resultar natural inmediatamente.

    LM Studio — la opción visual

    LM Studio es la alternativa para quienes prefieren una interfaz gráfica. Permite navegar, descargar y comparar modelos con un chat visual, ideal para exploración inicial o para compartir con equipos no técnicos.

    También tiene un servidor local compatible con OpenAI. La diferencia clave: Ollama es mejor para integrar en código y scripts; LM Studio es mejor para explorar modelos de forma interactiva.

    Para este post me centro en Ollama porque es donde vive el flujo de trabajo real de un developer.


    Tutorial: instalar Ollama y correr tu primer modelo

    1. Instalación

    macOS:

    brew install --cask ollama

    Linux:

    curl -fsSL https://ollama.com/install.sh | sh

    Windows:
    Descarga el instalador desde ollama.com/download y ejecútalo. Ollama se instala como servicio y arranca automáticamente.

    Una vez instalado, verifica que funciona:

    ollama --version

    2. Descargar y correr un modelo

    # Descargar y correr Llama 3.2 (3B — ligero, ideal para empezar)
    ollama run llama3.2
    
    # Descargar sin abrir el chat
    ollama pull llama3.2
    
    # Descargar Mistral 7B
    ollama pull mistral
    
    # Descargar Gemma 3 (Google, muy eficiente)
    ollama pull gemma3
    
    # Descargar Qwen 2.5 (excelente en código)
    ollama pull qwen2.5-coder
    
    # Descargar Phi-4 (Microsoft, pequeño y capaz)
    ollama pull phi4

    Al ejecutar ollama run se abre un REPL interactivo en la terminal. Escribe tu prompt y responde como cualquier chat. Para salir: /bye.

    3. Gestionar modelos

    # Ver modelos descargados
    ollama list
    
    # Eliminar un modelo
    ollama rm mistral
    
    # Ver información de un modelo
    ollama show llama3.2

    4. Usar la API REST de Ollama

    Cuando Ollama está corriendo, expone una API en http://localhost:11434. Puedes usarla directamente con curl:

    # Generar una respuesta (completions)
    curl http://localhost:11434/api/generate \
      -H "Content-Type: application/json" \
      -d '{
        "model": "llama3.2",
        "prompt": "Explica qué es un closure en JavaScript en dos frases",
        "stream": false
      }'
    
    # Formato compatible con OpenAI (chat completions)
    curl http://localhost:11434/v1/chat/completions \
      -H "Content-Type: application/json" \
      -d '{
        "model": "llama3.2",
        "messages": [
          { "role": "user", "content": "Qué es un closure en JavaScript?" }
        ]
      }'

    El endpoint /v1/chat/completions es compatible con el SDK oficial de OpenAI. Solo tienes que cambiar la baseURL.


    Modelos recomendados según caso de uso

    Estos son los modelos IA en local con mejor rendimiento según el hardware disponible y el caso de uso:

    Modelo Tamaño Ideal para RAM mínima
    llama3.2:3b ~2 GB Tareas simples, prototipado rápido 8 GB
    llama3.1:8b ~5 GB Razonamiento general, chat 8 GB
    mistral:7b ~4 GB Instrucciones, resumen, generación de texto 8 GB
    qwen2.5-coder:7b ~4 GB Generación y revisión de código 8 GB
    gemma3:9b ~6 GB Tareas multilingues, contexto largo 16 GB
    phi4:14b ~9 GB Razonamiento complejo, análisis 16 GB
    llama3.3:70b ~40 GB Calidad cercana a GPT-4o 64 GB
    deepseek-r1:14b ~9 GB Razonamiento con chain-of-thought 16 GB

    Para una máquina de desarrollo con 16 GB de RAM, qwen2.5-coder:7b para tareas de código y llama3.1:8b para texto general cubren el 90% de los casos.


    Local vs nube: cuándo usar cada uno

    Criterio Local (Ollama) Nube (OpenAI, Anthropic, Google)
    Coste por llamada Gratis $0.001–$0.015 por 1K tokens
    Privacidad de datos Total Depende del proveedor y contrato
    Calidad en tareas complejas Buena (modelos 7B–14B) Excelente (modelos frontier)
    Latencia (primer token) Baja en hardware potente Varía: 300ms–2s
    Escalabilidad Limitada por tu hardware Prácticamente ilimitada
    Modelos de razonamiento avanzado Limitado o1, Claude Sonnet 4, Gemini 2.5 Pro
    Setup inicial 10 minutos Registro + API key
    Trabajo offline No
    Ideal para Prototipado, privacidad, CI/CD, dev tooling Producción con usuarios reales, tareas complejas

    La regla que uso: local para desarrollar, nube para producir. Prototipa barato, valida rápido, y cuando el producto necesita escalar o resolver tareas de mayor complejidad, mueve las llamadas críticas a un modelo cloud.


    Integración con TypeScript y JavaScript

    Ollama tiene su propio cliente oficial para Node.js. Dado que expone un endpoint compatible con OpenAI, puedes usar el paquete openai directamente — sin reescribir nada en tus proyectos existentes. Después de integrar modelos IA en local en varios proyectos TypeScript, estos son los tres patrones que más uso en producción:

    Con el SDK de OpenAI (compatibilidad directa)

    import OpenAI from "openai";
    
    const client = new OpenAI({
      baseURL: "http://localhost:11434/v1",
      apiKey: "ollama", // cualquier string no vacío
    });
    
    async function ask(prompt: string): Promise<string> {
      const response = await client.chat.completions.create({
        model: "llama3.2",
        messages: [{ role: "user", content: prompt }],
      });
    
      return response.choices[0].message.content ?? "";
    }
    
    const answer = await ask("Genera un schema Zod para un usuario con nombre y email");
    console.log(answer);

    Nota: El await en nivel superior requiere ESM. Añade "type": "module" en tu package.json o usa extensión .mts.

    El prompt del ejemplo genera un schema Zod. Si quieres dominar la validación de datos con TypeScript para estos pipelines, el curso de Zod cubre exactamente ese flujo con ejemplos reales.

    Con el cliente nativo de Ollama

    import { Ollama } from "ollama";
    
    const ollama = new Ollama({ host: "http://localhost:11434" });
    
    // Respuesta completa
    const response = await ollama.chat({
      model: "qwen2.5-coder",
      messages: [
        {
          role: "user",
          content: "Escribe una función TypeScript que valide un email con regex",
        },
      ],
    });
    
    console.log(response.message.content);
    
    // Streaming
    const stream = await ollama.chat({
      model: "qwen2.5-coder",
      messages: [{ role: "user", content: "Explica el patron Repository" }],
      stream: true,
    });
    
    for await (const chunk of stream) {
      process.stdout.write(chunk.message.content);
    }

    Instala el cliente oficial:

    npm install ollama
    # o con bun
    bun add ollama

    Cambiar entre local y nube con una variable de entorno

    Este patrón es el que más uso en proyectos reales — permite alternar entre Ollama y OpenAI sin cambiar código:

    import OpenAI from "openai";
    
    const isLocal = process.env.USE_LOCAL_LLM === "true";
    
    const client = new OpenAI({
      baseURL: isLocal ? "http://localhost:11434/v1" : "https://api.openai.com/v1",
      apiKey: isLocal ? "ollama" : process.env.OPENAI_API_KEY,
    });
    
    const model = isLocal ? "llama3.2" : "gpt-4o-mini";

    Con USE_LOCAL_LLM=true en desarrollo y la variable desactivada en producción, tienes el mejor de los dos mundos sin duplicar lógica. Esta es exactamente la clase de arquitectura que trabajamos en el curso Construye con IA, donde construimos productos completos con IA desde la especificación hasta el despliegue.


    FAQ — Preguntas frecuentes sobre modelos IA en local

    ¿Qué hardware necesito para correr modelos IA en local?

    Para modelos de 7B parámetros necesitas un mínimo de 8 GB de RAM y cualquier CPU moderna (últimos 5 años). Con GPU dedicada (NVIDIA o Apple Silicon) la inferencia es entre 5x y 10x más rápida. Un MacBook Pro M2 con 16 GB corre llama3.1:8b sin problemas. Para modelos de 70B necesitas al menos 64 GB de RAM o una GPU con suficiente VRAM.

    ¿Son los modelos locales comparables a GPT-4o o Claude?

    No para tareas complejas de razonamiento multi-paso o generación de código sofisticado. Pero para clasificación, resumen, extracción de información estructurada, generación de texto simple y asistencia en código básico, modelos como qwen2.5-coder:7b o llama3.1:8b son más que suficientes. La brecha se ha reducido mucho en el último año.

    ¿Puedo usar Ollama en un servidor de CI/CD?

    Sí. Ollama funciona en Linux sin cabecera y puede correr en Docker. El caso de uso más habitual: un runner de GitHub Actions con un runner self-hosted que tiene Ollama instalado para ejecutar checks de calidad de código o validaciones automáticas sin coste por llamada.

    ¿Cuánto espacio en disco ocupan los modelos?

    Depende del modelo y la cuantización. llama3.2:3b ocupa unos 2 GB. llama3.1:8b unos 5 GB. llama3.3:70b alrededor de 40 GB. Ollama descarga versiones cuantizadas (Q4 por defecto) que reducen el tamaño a la mitad aproximadamente manteniendo el 90–95% de la calidad del modelo original.

    ¿Qué diferencia hay entre Ollama y LM Studio?

    Ollama es una herramienta CLI-first, sin interfaz gráfica, pensada para integración en scripts y aplicaciones. LM Studio tiene una UI de escritorio con chat visual, gestor de modelos y comparador integrado. Ambas exponen una API local compatible con OpenAI. Si eres developer y quieres integrar el modelo en código, Ollama. Si quieres explorar modelos visualmente o presentarlos a stakeholders, LM Studio.

    ¿Puedo hacer fine-tuning de modelos con Ollama?

    Ollama no está diseñado para fine-tuning. Su función es servir modelos, no entrenarlos. Para fine-tuning de modelos open source necesitas herramientas como Unsloth, LoRA con Hugging Face o plataformas como Together AI y Replicate. Ollama puede servir después el modelo fine-tuneado si lo exportas en formato GGUF.

    ¿Es seguro usar modelos locales en producción?

    Depende del caso. Para un servicio interno o una herramienta de developer experience, perfectamente. Para un producto con miles de usuarios concurrentes, los modelos locales tienen limitaciones de escalabilidad obvias: un solo servidor solo puede atender las peticiones que su hardware permite procesar en paralelo. En ese escenario, la nube escala horizontalmente de forma que local no puede igualar.

    ¿Cómo elijo entre los modelos disponibles en Ollama?

    Empieza con llama3.2:3b para validar el flujo de tu aplicación (descarga rápida, responde rápido). Sube a llama3.1:8b o qwen2.5-coder:7b cuando necesites mejor calidad. Si tu máquina tiene 32 GB+ de RAM, phi4:14b o gemma3:27b son excelentes opciones intermedias antes de saltar a los modelos de 70B.


    Para ir más lejos

    Si estás construyendo productos con IA — integrando modelos IA en local en pipelines completos, agentes y herramientas que lleguen a usuarios reales — el siguiente paso es estructurar esa arquitectura desde el principio.

    El post sobre CLAUDE.md: el system prompt de tu proyecto explica cómo dar contexto permanente al agente. Clean Architecture en frontend con IA muestra cómo mantener las capas limpias cuando el agente genera código. Y si te preguntas hacia dónde va todo esto profesionalmente, la guía sobre qué es un Agentic Engineer cierra el mapa.

    En Dominicode Labs encontrarás proyectos completos, recursos avanzados y una comunidad de developers que están exactamente en esa fase: construyendo con IA en producción, no en tutoriales de YouTube.


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

  • Product builder: el cambio de mentalidad que la IA hace posible

    Product builder: el cambio de mentalidad que la IA hace posible

    Hace tres años me llegó un mensaje de un developer con siete años de experiencia en React. Me decía:

    “Bezael, sé hacer cualquier cosa que me pidan. Pero no tengo nada propio. Ni una app, ni un proyecto, ni un ingreso fuera de mi salario.”

    Lo que describía no era un problema de habilidades técnicas. Era un problema de identidad.

    Se veía a sí mismo como alguien que ejecuta. Alguien que recibe tickets, los cierra, y espera el siguiente. Un programador en el sentido más literal del término.

    Y eso, en 2026, es el camino más directo a la irrelevancia.

    Lo que ese developer necesitaba — lo que muchos developers necesitan — es pasar de ejecutar a construir: convertirse en un product builder.


    El developer que ejecuta vs. el product builder que construye

    Un product builder es un developer que combina criterio técnico con pensamiento de producto: no solo implementa soluciones, sino que decide qué problemas merecen ser resueltos y para quién.

    Hay una diferencia fundamental entre los dos perfiles, y no tiene nada que ver con el nivel técnico.

    Programador tradicional Product builder
    Pregunta: “¿Cómo lo implemento?” Pregunta primero: “¿Debería implementarlo?”
    Espera que alguien le diga qué construir Tiene una tesis propia sobre qué problema merece ser resuelto
    Mide su valor en líneas de código o tecnologías que domina Mide su valor en si algo que construyó funciona para alguien real

    No estoy diciendo que uno sea mejor persona que el otro. Estoy diciendo que el mercado está cambiando a una velocidad que hace que el primer perfil sea cada vez más reemplazable — y el segundo, más valioso que nunca.


    Por qué ahora es el momento exacto para hacer este cambio

    La barrera técnica para construir un producto ha colapsado.

    Antes, si querías lanzar algo solo, necesitabas dominar frontend, backend, base de datos, autenticación, despliegue, y probablemente seis frameworks distintos. Necesitabas un equipo o años de práctica en cada capa.

    Hoy, con herramientas como Claude Code, un developer con criterio puede tener un MVP funcionando en días. No porque la IA programe por ti — sino porque amplifica lo que ya sabes y elimina la fricción entre la idea y el código que la materializa.

    Eso cambia la ecuación por completo. Ya no es técnica la limitante. Es saber qué construir, para quién, y por qué alguien pagaría por ello.

    Eso es exactamente lo que trabajo en el curso Construye con IA: usar la IA no para generar código al azar, sino para ir de una idea real a un producto real con criterio de producto desde el principio.


    Qué comportamientos concretos tiene un product builder

    No voy a darte una lista de buzzwords. Te voy a decir cómo actúa alguien que ya hizo el cambio.

    Empieza por el problema, no por la tecnología. Antes de elegir un stack, un product builder ya sabe a qué usuario le duele qué cosa. La tecnología es una consecuencia de la solución, no el punto de partida.

    Shipea antes de que esté perfecto. El perfeccionismo técnico es el enemigo número uno de construir productos. Un product builder sabe que una versión imperfecta en manos de usuarios reales vale más que una versión perfecta en un repositorio privado.

    Habla con usuarios. No con amigos que te dicen que tu idea es buena. Con personas que tienen el problema que quieres resolver. Y aprende a distinguir entre lo que dicen que quieren y lo que realmente usarían.

    Entiende el negocio. No necesitas un MBA. Necesitas entender por qué alguien pagaría, cuánto pagaría, y cómo llegas a esa persona. Un product builder piensa en distribución desde el día uno.

    Itera con datos. No con opiniones. Lanza, mide, ajusta. El ciclo es corto y deliberado.


    Las cinco habilidades que nadie te enseñó en ningún bootcamp

    1. Pensamiento de producto

    No es saber usar Figma ni saber escribir user stories. Es desarrollar el hábito de preguntarte: “¿Qué problema real resuelve esto? ¿Para quién específicamente?”

    Cuando ves una app que usas cada día, un product builder la desmonta mentalmente: qué decisiones tomaron, qué sacrificaron, por qué funciona.

    2. Velocidad de validación

    La idea de construir durante meses antes de mostrar algo a alguien es una trampa. El objetivo no es construir — es aprender lo antes posible si lo que estás construyendo tiene sentido.

    Eso significa aprender a hacer prototipos rápidos y demos que generan feedback real. Una landing page que vende antes de que exista el producto ya es validación.

    3. Escritura que convierte

    Un product builder sabe explicar su producto en una frase. Sabe escribir una descripción que hace que alguien quiera probarlo. Sabe comunicar valor, no features.

    Esta habilidad — que parece ajena al mundo técnico — es una de las más diferenciadoras.

    4. Distribución y audiencia

    El código más limpio del mundo no vale nada si nadie lo usa. Un product builder piensa desde el principio en cómo va a llegar a sus usuarios: SEO, comunidad, contenido, partnerships, cold outreach.

    No tienes que hacerlo todo. Pero tienes que tener una respuesta a la pregunta: “¿Cómo van a enterarse de que esto existe?”

    5. Tolerancia a la ambigüedad

    Este es el más difícil para muchos developers, porque venimos de entornos donde los requisitos están (supuestamente) definidos. Construir un producto propio significa tomar decisiones con información incompleta, constantemente.

    Aprender a avanzar sin certeza total es una habilidad que se entrena, no que se tiene o no se tiene.


    El rol de la IA en todo esto

    La IA no te convierte en product builder. Eso lo haces tú con las decisiones que tomas.

    Lo que sí hace la IA es eliminar excusas.

    Antes, “no tengo tiempo para construir algo propio porque el backend me llevaría meses” era una razón real. Hoy no lo es. Hoy puedes hacer el backend en días, el frontend en días, el despliegue en horas.

    Lo que la IA no puede hacer por ti es decidir qué problema merece tu atención. No puede hablar con tus usuarios potenciales. No puede construir la audiencia que va a usar lo que hagas.

    Esa es tu parte. Y es la parte que más importa.

    Si quieres ver cómo trabajo este proceso — de la idea al producto con criterio de ingeniería y de negocio — en Dominicode Labs tenemos proyectos reales donde aplicamos exactamente esto: spec, validación, shipping, iteración.


    Cómo empezar el cambio hoy (sin abandonar tu trabajo)

    No te estoy pidiendo que renuncies ni que lances una startup la semana que viene. Te estoy pidiendo algo mucho más concreto.

    Elige un problema que tengas tú mismo — algo que te frustra como developer, como usuario, como persona — y pasa dos semanas construyendo una solución mínima. No perfecta. Mínima.

    Compártela con cinco personas que tengan el mismo problema. Observa qué pasa.

    Eso es un ciclo completo de product builder. Y lo puedes hacer este mes.

    La metodología que uso para estructurar este proceso — desde la especificación hasta el producto funcionando — está documentada en el Libro SDD. No es solo para proyectos grandes: es para cualquier developer que quiera pasar de “tengo una idea” a “tengo algo que funciona y que alguien usa”.


    El cambio no es técnico. Es de identidad.

    Volviendo al developer que me escribió hace tres años.

    Le dije algo simple: deja de pensar en qué tecnologías sabes y empieza a pensar en qué problema puedes resolver para alguien esta semana.

    No le dije que aprendiera product management. No le dije que hiciera un curso de negocios. Le dije que eligiera un problema pequeño y real, y que construyera algo — no para su portfolio, sino para alguien que lo necesita.

    Hoy tiene un producto SaaS que le genera ingresos recurrentes, lo sigue manteniendo como side project, y lleva ocho meses sin depender de que alguien le diga qué ticket hacer.

    Eso es lo que significa ser un product builder. No es un título. Es una forma de relacionarte con lo que construyes.

    Y la IA ha puesto esa posibilidad al alcance de cualquier developer que decida tomarla.


    Preguntas frecuentes

    ¿Un product builder necesita saber de diseño?
    No necesitas ser diseñador. Sí necesitas entender los principios básicos de UX y tener criterio para cuando algo es demasiado confuso para un usuario. Herramientas como Figma o incluso componentes UI prefabricados resuelven la mayor parte del problema visual. Lo que no puede resolver una herramienta es saber si tu producto tiene sentido.

    ¿Se puede ser product builder trabajando para una empresa?
    Sí, y de hecho es uno de los perfiles más buscados en empresas de producto. La diferencia es que aplicas el pensamiento de producto dentro de un equipo: cuestionas los requisitos, propones soluciones, mides el impacto real de lo que construyes. No eres el que ejecuta tickets — eres el que ayuda a decidir qué tickets merece la pena hacer.

    ¿La IA reemplaza al product builder?
    La IA reemplaza al developer que ejecuta tareas sin criterio. Al product builder lo amplifica, porque puede construir más rápido y experimentar más sin necesitar un equipo grande. La IA toma decisiones técnicas; el product builder toma decisiones de producto. Son funciones distintas.

    ¿Por dónde empiezo si nunca he lanzado nada propio?
    Empieza por un problema que conozcas bien — preferiblemente uno que tú mismo tengas. Construye la versión más pequeña posible que lo resuelva. Compártela con gente real antes de que esté “lista”. El error más común es esperar a tenerlo perfecto antes de mostrárselo a alguien. La retroalimentación temprana es lo que convierte una idea en un producto.

    ¿Cuánto tiempo lleva la transición de programador a product builder?
    No es una transición con fecha de fin — es un cambio de mentalidad que se profundiza con cada proyecto. El primer ciclo completo (idea, construcción mínima, usuarios reales, iteración) ya te cambia cómo ves el trabajo. La mayoría de developers que conozco que hicieron el cambio notan la diferencia después del primer proyecto propio que alguien usa de verdad.


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

  • Agentic loop: el mecanismo detrás de los agentes de IA

    Agentic loop: el mecanismo detrás de los agentes de IA

    La primera vez que vi a Claude Code refactorizar un módulo entero de TypeScript por sí solo, pensé que estaba viendo magia.

    Abrió el archivo. Leyó el código. Identificó el problema. Buscó dependencias en otros archivos. Editó tres ficheros en orden. Ejecutó los tests. Vio que uno fallaba. Corrigió el error. Volvió a ejecutar. Verde. Listo.

    Todo eso sin que yo le dijera qué hacer en cada paso. Le di un objetivo y él resolvió el camino.

    Lo que estaba viendo no era magia. Era el agentic loop en funcionamiento — el mecanismo que convierte un LLM pasivo en un agente que percibe, decide y actúa de forma continua hasta completar una tarea. Si estás construyendo con IA en 2026, entender cómo funciona este bucle es tan importante como entender cómo funciona el event loop de Node.

    Soy Bezael Pérez, developer senior y fundador de Dominicode. Llevo más de 15 años trabajando con software y los últimos dos obsesionado con cómo los agentes de IA cambian la forma de construir productos.


    La diferencia que cambia todo: LLM vs. agente

    Un LLM por sí solo es una función de texto: le das un input, devuelve un output. Extraordinariamente potente, pero estático. No sabe qué pasó antes. No puede hacer nada en el mundo real. No puede corregirse si se equivoca. Recibe. Responde. Se detiene.

    Un agente es diferente en una sola dimensión — pero esa dimensión lo cambia todo: puede actuar sobre su entorno y observar las consecuencias.

    Cuando le preguntas a ChatGPT “¿cómo conecto a PostgreSQL desde TypeScript?”, eso es un LLM. Te da la respuesta. Te toca a ti ejecutarla, ver si funciona, corregirla si falla.

    Cuando Claude Code abre tu proyecto, lee los archivos, escribe el código, ejecuta los tests y corrige los errores — eso es un agente con agentic loop. La diferencia no está en el modelo. Está en el bucle.


    Las fases del agentic loop

    El agentic loop no es un concepto abstracto. Es una arquitectura de ejecución con fases concretas que se repiten hasta que el agente completa su objetivo o se queda sin herramientas para avanzar. Este patrón lo formalizaron Yao et al. en el paper ReAct: Synergizing Reasoning and Acting in Language Models (2022) y es hoy la base de la mayoría de frameworks de agentes.

    Fase 1: Percibir

    El agente recibe información del entorno. Puede ser el mensaje del usuario, el resultado de una herramienta ejecutada en el ciclo anterior, el contenido de un archivo, una respuesta HTTP, el output de un comando de terminal.

    Esta fase parece trivial. No lo es. La calidad de lo que el agente percibe determina la calidad de lo que decide a continuación. Un agente que lee mal el contexto toma decisiones incorrectas con total confianza — y eso en producción es mucho más peligroso que un error que falla de forma evidente.

    Este fragmento muestra qué recibe el agente al inicio de cada ciclo: el mensaje del usuario, los resultados de herramientas anteriores y el system prompt que define sus capacidades:

    // Lo que el agente "percibe" en cada ciclo
    const context = {
      userMessage: "Refactoriza el módulo de autenticación para usar Signals",
      previousToolResults: [
        {
          tool: "read_file",
          output: "// auth.service.ts\nimport { Injectable } from '@angular/core'..."
        }
      ],
      systemPrompt: "Eres un assistant que refactoriza código Angular. Tienes acceso a las herramientas: read_file, write_file, execute_command."
    };

    Fase 2: Razonar

    El LLM procesa el contexto acumulado y decide qué hacer a continuación. Esta es la fase donde el modelo aplica su capacidad de razonamiento: evalúa el estado actual, compara con el objetivo, identifica el próximo paso.

    En modelos modernos como Claude Sonnet o GPT-4o, esta fase incluye razonamiento encadenado — el modelo se habla a sí mismo internamente antes de producir una decisión. En Claude, Anthropic expone este razonamiento explícitamente como “extended thinking” en la respuesta de la API — una feature específica de su plataforma, no un estándar cross-API.

    Lo que el agente produce en esta fase no es una respuesta de texto. Es una decisión estructurada: qué herramienta usar, con qué argumentos, por qué.

    En lugar de texto libre, el razonamiento del agente produce una llamada estructurada a herramienta. Este pseudocódigo representa esa decisión (el campo thinking es razonamiento interno del modelo — no lo recibes como developer en la respuesta de la API):

    // pseudocódigo — thinking es interno al modelo, no un campo de la API
    const agentDecision = {
      thinking: "Necesito leer el archivo auth.service.ts antes de modificarlo",
      toolCall: {
        name: "read_file",
        arguments: {
          path: "src/app/auth/auth.service.ts"
        }
      }
    };

    Fase 3: Actuar

    El agente ejecuta la herramienta que decidió usar. Aquí es donde la IA toca el mundo real: escribe en disco, llama a una API externa, ejecuta SQL, navega una página web, envía un email.

    Esta es también la fase más delicada desde el punto de vista de seguridad y control. Una acción ejecutada no se puede deshacer fácilmente. Por eso los sistemas de agentes bien diseñados implementan sandboxes, confirmaciones humanas para acciones irreversibles y límites explícitos en qué herramientas puede usar el agente.

    La función executeToolCall implementa esta capa de ejecución: recibe la decisión estructurada del agente y ejecuta la acción real sobre el sistema:

    // Ejecución de la herramienta — el agente actúa sobre el entorno
    async function executeToolCall(toolCall: ToolCall): Promise<ToolResult> {
      switch (toolCall.name) {
        case "read_file":
          return { output: await fs.readFile(toolCall.arguments.path, "utf-8") };
        case "write_file":
          await fs.writeFile(toolCall.arguments.path, toolCall.arguments.content);
          return { output: "Archivo escrito correctamente" };
        case "execute_command":
          const result = await exec(toolCall.arguments.command);
          return { output: result.stdout, error: result.stderr };
        default:
          throw new Error(Herramienta desconocida: ${toolCall.name});
      }
    }

    Fase 4: Observar

    El agente recibe el resultado de la acción ejecutada y lo incorpora a su contexto. Si leyó un archivo, ahora tiene el contenido. Si ejecutó un test, ahora sabe si pasó o falló. Si llamó a una API, tiene la respuesta — o el error.

    Esta fase cierra el bucle. El resultado de la observación se convierte en nuevo input para la siguiente iteración de Percibir → Razonar → Actuar. El agente actualiza su modelo interno del estado del mundo y decide si ha completado su objetivo o si necesita otro ciclo.

    El resultado de la herramienta vuelve al contexto como un mensaje más. El modelo evalúa si ha terminado o si necesita otra herramienta:

    // El resultado se incorpora al contexto para el siguiente ciclo
    messages.push({
      role: "tool",
      content: toolResult.output,
      toolCallId: toolCall.id
    });
    
    

    // ¿Ha completado el objetivo? El modelo decide. const nextStep = await llm.complete(messages); // Si devuelve texto sin tool_call → tarea completada // Si devuelve otro tool_call → el bucle continúa

    Repetir (o detenerse)

    El bucle continúa mientras el agente tenga herramientas que ejecutar y no haya alcanzado su objetivo. Se detiene cuando el modelo produce una respuesta de texto sin invocar ninguna herramienta — lo que indica que considera la tarea completada — o cuando alcanza el límite de iteraciones definido en la configuración.

    Ese límite de iteraciones no es un detalle menor. Es una de las decisiones de diseño más importantes en un sistema agéntico. Un agente sin límite puede quedar atrapado en un bucle infinito consumiendo tokens y ejecutando acciones hasta que alguien apague el proceso.


    Cómo lo implementan las herramientas reales

    No tienes que construir el agentic loop desde cero. Las herramientas que ya existen lo implementan por ti, con tres aproximaciones distintas: ejecución local directa (Claude Code), orquestación de grafos (LangGraph) y no-code visual (n8n). Cada una optimiza para un perfil diferente de developer y caso de uso.

    • Claude Code — Loop completo con herramientas del sistema operativo: leer/escribir archivos, ejecutar comandos de terminal, buscar en el codebase. El agente decide autónomamente qué pasos dar y puedes verlo trabajar en tiempo real en la terminal.
    • LangChain / LangGraph — Loop como grafo de nodos configurables. Tú defines las transiciones, condiciones de parada y herramientas. Más control y flexibilidad para flujos con ramificaciones complejas.
    • n8n — AI Agent nodes que envuelven el loop en un flujo visual. Ideal para automatizaciones de negocio con APIs externas, webhooks y transformaciones de datos sin escribir código.
    • AutoGPT / BabyAGI — La primera ola de agentes. Implementaron el loop de forma casi literal: generaban sus propias subtareas, las priorizaban y las ejecutaban. Funcionaban en demos, fallaban en producción por acumulación de errores en cada ciclo y falta de controles.

    Si quieres profundizar en cómo construir el harness que envuelve el loop, este análisis sobre harness engineering con agentes de IA cubre la capa de orquestación en detalle.


    Por qué los agentes fallan — y no es culpa del modelo

    El agentic loop tiene un problema estructural que los developers aprenden a la fuerza: los errores se propagan y se amplifican.

    En un LLM normal, si el modelo alucina en la respuesta, el usuario lo ve y puede corregirlo. En un agente con agentic loop, si el modelo toma una decisión incorrecta en el ciclo 2, esa decisión puede contaminar los ciclos 3, 4 y 5 antes de que nadie se dé cuenta. Para cuando el agente termina, puede haber modificado archivos, llamado a APIs y tomado decisiones basadas en una premisa incorrecta del principio.

    Hay tres patrones de fallo que aparecen una y otra vez en producción:

    Context drift — El contexto acumulado crece ciclo a ciclo. En conversaciones largas, el modelo empieza a perder el hilo de los objetivos originales y se centra en los últimos resultados. El agente puede alcanzar un estado donde “funciona” localmente pero ha perdido el objetivo global.

    Tool loop — El agente entra en un ciclo donde ejecuta la misma herramienta con los mismos argumentos repetidamente porque no sabe cómo interpretar el resultado. Sin un límite de iteraciones y sin detección de patrones repetitivos, consume tokens hasta el límite de la sesión.

    Overconfidence — El modelo decide con alta confianza en casos donde debería pedir confirmación. Elimina un archivo que creía temporal. Envía un email que debía ser un borrador. Ejecuta una migración de base de datos en producción. La confianza del modelo no tiene correlación con la corrección de la acción.

    La solución no es usar un modelo más inteligente. Es diseñar el sistema con los controles correctos: límites de iteración, human-in-the-loop para acciones irreversibles, y observabilidad para saber exactamente qué está haciendo el agente en cada ciclo. Si te interesa la parte de observabilidad, en el post sobre observabilidad en LLMs: traza, mide y depura tus agentes cubrimos cómo instrumentar el loop con trazas y métricas reales.


    Cuándo usar el agentic loop (y cuándo no)

    El agentic loop resuelve una clase específica de problemas. Usarlo para todo es uno de los errores más comunes que veo en equipos que empiezan con IA.

    Úsalo cuando:

    • La tarea requiere múltiples pasos que dependen del resultado de los anteriores
    • El objetivo está claro pero el camino para alcanzarlo no se puede definir de antemano
    • Necesitas interactuar con herramientas externas en función del contexto
    • La tarea implica explorar un espacio de posibilidades (búsqueda, refactorización, análisis)

    Ejemplos concretos: refactorizar un módulo de código, investigar un bug leyendo múltiples archivos, rellenar formularios complejos a partir de documentos, ejecutar una pipeline de procesamiento de datos donde cada paso depende del anterior.

    No lo uses cuando:

    • Puedes resolver el problema con un solo prompt bien diseñado
    • La latencia importa y el usuario está esperando una respuesta inmediata
    • Las acciones son irreversibles y el contexto no garantiza que el agente tomará la decisión correcta
    • El problema tiene una solución determinista que no requiere razonamiento iterativo

    Un agente que escribe el texto de un email de bienvenida es sobre-ingeniería. Un LLM con el prompt correcto lo hace en un ciclo, sin herramientas, en 200ms. Reserva el agentic loop para los problemas que lo necesitan de verdad.

    En el curso Construye con IA abordamos exactamente este criterio de decisión: qué arquitectura elegir para cada problema, cuándo un agente añade valor real y cuándo un prompt bien diseñado es más efectivo, rápido y barato.


    El agentic loop en 2026: dónde está el límite real

    El límite ya no es la capacidad del modelo. Los LLMs actuales razonan lo suficientemente bien como para completar tareas complejas de múltiples pasos.

    El límite es la confianza que puedes depositar en el sistema.

    Confiar en que el agente tomará la decisión correcta en cada ciclo, sin supervisión humana, es una apuesta que depende del dominio, del riesgo de las acciones y de la calidad de las herramientas que le has dado. En tareas de desarrollo donde los cambios son reversibles (código en un repositorio con git), puedes darle mucha autonomía. En tareas que afectan a clientes o datos de producción, el loop necesita checkpoints humanos.

    El patrón que están adoptando los equipos más avanzados es human-in-the-loop selectivo: el agente actúa de forma autónoma en la mayoría de ciclos, pero solicita confirmación explícita antes de ejecutar acciones que superen un umbral de riesgo definido en el sistema.

    No es rendirse al agente ni microgestionar cada paso. Es diseño de sistema con criterio.

    Si quieres ver cómo aplico este patrón en proyectos reales — y explorar los proyectos que la comunidad está construyendo con agentes en producción — pásate por Dominicode Labs. Hay recursos, proyectos y discusiones que no publicaré en el blog.


    Antes de meterte con el bucle conviene tener clara la frontera: qué es un agente de IA y en qué se diferencia de un workflow, con el test para aplicarlo a tu propio código.

    FAQ — Preguntas frecuentes sobre el agentic loop

    ¿Qué diferencia hay entre un chatbot y un agente con agentic loop?

    Un chatbot procesa mensajes y genera respuestas de texto. No ejecuta acciones en el mundo real ni mantiene un estado entre ciclos más allá del historial de conversación. Un agente con agentic loop puede leer archivos, llamar a APIs, ejecutar código y tomar decisiones basadas en los resultados de esas acciones — repitiendo el ciclo hasta completar un objetivo complejo.

    ¿El agentic loop necesita un modelo específico o funciona con cualquier LLM?

    Técnicamente funciona con cualquier LLM que soporte function calling o tool use. En la práctica, la calidad del loop depende mucho de la capacidad del modelo para razonar sobre los resultados de las herramientas y decidir el siguiente paso correcto. Claude Sonnet, GPT-4o y Gemini 2.5 Pro son los modelos que ofrecen resultados más consistentes hoy. Modelos más pequeños fallan con más frecuencia en las fases de razonamiento y en la detección de cuándo el objetivo está completo.

    ¿Cuántas iteraciones puede hacer un agente antes de fallar o perder el hilo?

    Depende del modelo y del tamaño del contexto. Los modelos actuales con ventanas de contexto grandes (200k tokens en Claude) pueden mantener coherencia durante decenas de iteraciones en tareas bien definidas. En la práctica, la degradación empieza a notarse alrededor de las 20-30 iteraciones en tareas complejas con mucho contexto acumulado. Un buen sistema define un maxIterations entre 10 y 50 según el dominio, con lógica de parada anticipada si detecta patrones repetitivos.

    ¿Claude Code usa un agentic loop?

    Sí. Claude Code implementa el agentic loop completo: lee el contexto del proyecto, decide qué herramientas usar (leer archivos, escribir código, ejecutar comandos), observa los resultados y repite hasta completar la tarea. La diferencia con un uso básico de la API de Claude es que Claude Code orquesta este bucle de forma transparente, con acceso al filesystem y al terminal, y con la capacidad de autocorregirse cuando un test falla o un comando devuelve un error.

    ¿Es el agentic loop lo mismo que el “chain of thought”?

    No. Chain of thought es una técnica de prompting donde el modelo razona paso a paso antes de dar una respuesta — todo ocurre dentro de una sola llamada al LLM. El agentic loop es una arquitectura de ejecución que implica múltiples llamadas al modelo, ejecución real de herramientas entre llamadas, y un estado que se actualiza en cada ciclo. Chain of thought puede ser parte de la fase de razonamiento dentro del loop, pero son conceptos de nivel diferente.


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

    Si este post te ha sido útil, hay más contenido técnico sobre IA aplicada al desarrollo en el canal de YouTube de Dominicode.

  • Observabilidad en LLMs: traza, mide y depura tus agentes de IA

    Observabilidad en LLMs: traza, mide y depura tus agentes de IA

    El equipo había estado tres semanas construyendo un agente de soporte técnico. Funcionaba bien en local. Lo lanzaron a producción un lunes por la mañana.

    El viernes tenían una factura de $1.200 en tokens, tres tickets de clientes con respuestas completamente inventadas y ninguna pista de en qué paso del flujo había empezado a fallar todo.

    El agente había estado corriendo. Respondiendo. Consumiendo. Pero nadie podía ver qué estaba pasando dentro.

    Ese es el problema que resuelve la observabilidad en LLMs — el conjunto de técnicas que permite registrar, medir y analizar cada paso de un agente de IA en producción: los prompts enviados, las herramientas invocadas, los tokens consumidos y los errores producidos. Si estás construyendo cualquier cosa con IA en producción, necesitas entenderla antes de que te pase lo mismo.


    El problema real: los LLMs son cajas negras que facturan

    Con una API REST tradicional tienes un contrato claro: envías una petición, recibes una respuesta, mides el tiempo, logueas el error. La traza es determinista.

    Con un LLM, el contrato se rompe. Puedes enviar el mismo prompt dos veces y recibir respuestas distintas. Un agente puede invocar herramientas en un orden diferente al esperado. El modelo puede alucinarse con un dato en el paso 3 de 7 y tú solo ves el resultado final — correcto en formato, incorrecto en contenido.

    Sin observabilidad, estás volando a ciegas. Y en producción, volar a ciegas con un LLM significa costos impredecibles, degradación silenciosa de calidad y bugs que no aparecen en ningún test.


    Los tres pilares de la observabilidad en LLMs

    La observabilidad clásica tiene tres dimensiones. En el mundo de los LLMs, cada una significa algo distinto.

    Trazas (Traces)

    Una traza registra el camino completo de una ejecución: qué prompt se envió, qué herramientas se llamaron, en qué orden, con qué inputs y qué outputs. En un agente con múltiples pasos, una traza te muestra el árbol completo de decisiones — no solo el resultado final.

    Es la diferencia entre saber que tu agente “falló” y saber exactamente en qué llamada a qué herramienta empezó a descarrilarse.

    Métricas

    Latencia por llamada, costo en tokens (input + output), tasa de éxito de herramientas, número de reintentos. Estas métricas agregadas te dicen si tu sistema está degradándose con el tiempo o si hay un prompt específico que consume diez veces más tokens que los demás.

    Logs estructurados

    No los logs de consola que escribes para depurar en local. Logs que capturen el contexto completo de cada ejecución: qué usuario lanzó la petición, qué versión del prompt se usó, qué modelo, qué temperatura. Logs que puedas consultar después cuando alguien reporte un comportamiento extraño hace 48 horas.


    Herramientas de observabilidad LLM en 2026

    Cinco herramientas que cualquier developer que construye con IA debería conocer:

    Herramienta Tipo Self-hosted Stack ideal Plan gratuito
    Langfuse SDK + plataforma ✅ Sí Cualquier API 50k obs/mes
    LangSmith Plataforma ❌ No LangChain Sí (limitado)
    Helicone Proxy ❌ No Multi-proveedor
    Arize Phoenix Análisis offline ✅ Sí Evaluación por lotes Open source
    OpenTelemetry GenAI Estándar ✅ Sí Stacks OTEL existentes Open source

    Langfuse — El estándar open source. Puedes autohospedarlo o usar su cloud. Tiene SDK para TypeScript, Python e integración nativa con LangChain, Vercel AI SDK y llamadas directas a la API de Anthropic u OpenAI. Es la opción que recomiendo si quieres control total sobre tus datos.

    LangSmith — El producto de LangChain. Excelente si ya usas LangChain en tu stack, pero te ata al ecosistema. Para proyectos con llamadas directas a la API, Langfuse gana en flexibilidad.

    Helicone — Proxy que se pone delante de cualquier llamada a la mayoría de proveedores LLM (OpenAI, Anthropic, Azure, LiteLLM y otros). Configuración en dos minutos, observabilidad inmediata. Ideal para proyectos donde no puedes tocar el código de integración o quieres monitoreo rápido sin instrumentación.

    Arize Phoenix — Enfocado en evaluación y análisis offline. Útil cuando quieres analizar lotes de ejecuciones para detectar problemas de calidad sistemáticos, no solo monitoreo en tiempo real.

    Semantic conventions for generative AI systems (OTel GenAI) — El estándar OTEL para IA lleva trazas de LLMs al stack de observabilidad que ya tienes (Grafana, Datadog, Honeycomb). Si tu empresa ya tiene infraestructura OTEL, esta es la forma de integrar los LLMs sin añadir otra herramienta.


    Implementación real con TypeScript y Langfuse

    Suficiente teoría. Esto es cómo lo implementas en un proyecto TypeScript.

    Primero, instala el SDK (versión actual: langfuse@3.x):

    npm install langfuse
    

    Inicializa el cliente una sola vez en tu aplicación — en un módulo singleton si usas NestJS, en un archivo de configuración si es un script:

    import Langfuse from "langfuse";
    
    export const langfuse = new Langfuse({
      publicKey: process.env.LANGFUSE_PUBLIC_KEY!,
      secretKey: process.env.LANGFUSE_SECRET_KEY!,
      baseUrl: "https://cloud.langfuse.com", // o tu instancia autohospedada
    });
    

    Ahora instrumenta una llamada a Claude. El patrón es siempre el mismo: creas una traza, creas una generación dentro de esa traza, ejecutas la llamada y registras el resultado:

    import Anthropic from "@anthropic-ai/sdk";
    import { langfuse } from "./langfuse-client";
    
    const client = new Anthropic();
    
    async function generateSupportResponse(
      userMessage: string,
      userId: string
    ): Promise<string> {
      // Abre la traza — representa toda la operación de negocio
      const trace = langfuse.trace({
        name: "support-response",
        userId,
        metadata: { channel: "web" },
      });
    
      // Crea una generación — representa una sola llamada al LLM
      const generation = trace.generation({
        name: "claude-response",
        model: "claude-sonnet-4-6",
        input: [{ role: "user", content: userMessage }],
      });
    
      try {
        const response = await client.messages.create({
          model: "claude-sonnet-4-6",
          max_tokens: 1024,
          messages: [{ role: "user", content: userMessage }],
        });
    
        const outputText =
          response.content[0].type === "text" ? response.content[0].text : "";
    
        // Registra la respuesta y el uso de tokens
        generation.end({
          output: outputText,
          usage: {
            input: response.usage.input_tokens,
            output: response.usage.output_tokens,
          },
        });
    
        return outputText;
      } catch (error) {
        // Registra errores también — son datos cruciales
        generation.end({
          level: "ERROR",
          statusMessage: error instanceof Error ? error.message : "Unknown error",
        });
        throw error;
      } finally {
        // Vacía el buffer antes de cerrar el proceso
        await langfuse.shutdownAsync();
      }
    }
    

    Para un agente con múltiples pasos — por ejemplo, uno que busca en una base de datos antes de responder — anidas spans dentro de la traza:

    async function agentWithToolCall(query: string, userId: string) {
      const trace = langfuse.trace({
        name: "agent-with-tools",
        userId,
        input: { query },
      });
    
      // Span para la búsqueda en base de datos
      const searchSpan = trace.span({
        name: "database-search",
        input: { query },
      });
    
      const searchResults = await searchDatabase(query);
    
      searchSpan.end({
        output: { resultCount: searchResults.length },
      });
    
      // Generación con los resultados como contexto
      const generation = trace.generation({
        name: "synthesis",
        model: "claude-sonnet-4-6",
        input: buildPromptWithContext(query, searchResults),
      });
    
      const response = await callClaude(query, searchResults);
    
      generation.end({ output: response });
      trace.update({ output: { response } });
      await langfuse.shutdownAsync();
    
      return response;
    }
    

    Con esto, cada ejecución queda registrada en Langfuse con su árbol completo de spans. Puedes ver exactamente cuánto tardó la búsqueda, cuántos tokens consumió la síntesis y qué prompt produjo esa respuesta que el cliente reportó como incorrecta.

    Es exactamente este tipo de arquitectura observable la que construimos en el curso Construye con IA — desde el primer agente hasta el sistema completo en producción, con trazabilidad desde el día uno.


    Los errores que aparecen cuando no tienes observabilidad

    Estos son los tres problemas que he visto repetirse en proyectos sin observabilidad — y que solo se detectan cuando ya han causado daño:

    Alucinaciones no detectadas. El agente responde con confianza. El formato es correcto. El dato está mal. Sin trazas, nunca sabes en qué paso del flujo el modelo inventó algo. Con trazas, identificas el prompt exacto que produce el problema y lo corriges.

    Latencias ocultas. El endpoint responde en 8 segundos. No sabes si el problema está en la llamada al LLM, en la búsqueda vectorial o en el postprocesado. Las métricas por span te dicen exactamente dónde está el cuello de botella.

    Costos fuera de control. Un prompt mal diseñado puede consumir 10x más tokens que uno equivalente. Sin métricas de uso por operación, solo ves la factura de fin de mes. Con observabilidad, detectas el problema en el primer día de producción.


    Por dónde empezar si ya tienes un proyecto en marcha

    No tienes que instrumentar todo de golpe. El orden que funciona:

    1. Añade Langfuse o Helicone a la llamada al LLM más crítica de tu sistema.
    2. Registra siempre: modelo, versión del prompt, userId, tokens usados.
    3. Cuando detectes un comportamiento inesperado, usa las trazas para reproducirlo.
    4. Una vez que el patrón funciona, extiéndelo al resto de llamadas.

    La observabilidad no es una feature opcional que añades al final. Es la infraestructura que te permite iterar sobre tus prompts con datos reales — no con intuición.

    Si quieres construir sistemas con IA desde cero con buenas prácticas de producción integradas desde el principio, en Dominicode Labs tenemos proyectos y recursos donde trabajamos exactamente esto junto a otros developers hispanohablantes.


    FAQ — Preguntas frecuentes sobre LLM Observability

    ¿Qué diferencia hay entre observabilidad en LLMs y logging tradicional?

    El logging tradicional captura eventos discretos: errores, requests, respuestas. La observabilidad en LLMs captura el flujo completo de razonamiento de un agente: qué herramientas invocó, en qué orden, con qué contexto, y cómo cada paso influyó en el resultado final. La diferencia es la misma que entre saber que tu avión aterrizó tarde y saber exactamente en qué tramo de la ruta perdió tiempo.

    ¿Necesito observabilidad si solo uso un LLM para tareas sencillas como resúmenes de texto?

    Si está en producción y lo usa más de un usuario, sí. Incluso para tareas aparentemente sencillas, la observabilidad te da visibilidad sobre costos (¿cuánto estoy gastando por resumen?), calidad (¿hay inputs que producen respuestas malas consistentemente?) y latencia (¿hay prompts que tardan 5x más que otros?). El esfuerzo de instrumentar una sola llamada es de menos de 20 líneas de código.

    ¿Langfuse es mejor que LangSmith?

    Depende de tu stack. Si usas LangChain, LangSmith es la integración más natural. Si haces llamadas directas a la API de Anthropic u OpenAI, o usas el Vercel AI SDK, Langfuse tiene mejor soporte, es open source y puedes autohospedarlo. Para proyectos donde los datos son sensibles y no quieres enviarlos a un tercero, Langfuse self-hosted es la única opción razonable.

    ¿Cómo gestiono la observabilidad en un agente con múltiples LLMs y herramientas?

    Usando el modelo de trazas jerárquicas: una traza por operación de negocio, spans para cada herramienta o paso intermedio, y generaciones para cada llamada al LLM. Langfuse, LangSmith y Arize Phoenix soportan este modelo de forma nativa. La clave es diseñar las trazas pensando en qué preguntas querrás responder cuando algo falle — no en lo que es fácil de capturar.

    ¿Cuánto cuesta implementar observabilidad con Langfuse?

    Langfuse Cloud tiene un plan gratuito generoso (50.000 observaciones al mes en la fecha de publicación de este post). Para proyectos con volumen alto, puedes autohospedarlo en tu propia infraestructura con Docker — el costo es solo el del servidor. LangSmith y Helicone tienen también niveles gratuitos, pero con menos control sobre los datos.


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

  • Novedades de Angular v22: todo lo que cambia en esta versión

    Novedades de Angular v22: todo lo que cambia en esta versión

    Un compañero me preguntó la semana pasada: “¿Merece la pena actualizar ya a Angular v22?”.

    Le respondí lo mismo que le diría a ti: las novedades de Angular v22 no son parches ni renombrados. Son APIs nuevas que reemplazan patrones que llevan años instalados en nuestra cabeza — y varias de ellas pasan a ser estables en esta versión. Si llevas tiempo esperando que Angular se parezca a lo que promete ser, v22 es esa versión.

    Afecta cómo gestionas estado asíncrono, cómo escribes formularios, cómo arranca la detección de cambios y cómo declaras componentes. Todo a la vez. En una sola versión.

    Este post es el mapa. Si quieres ir al detalle de alguna feature concreta, tienes posts específicos linkados donde corresponde.

    ¿Qué es Angular v22? Angular v22 es la versión que consolida las Signal APIs como estándar principal del framework. Introduce la Resource API estable (resource(), rxResource()), el nuevo decorator @Service(), Signal Forms experimental, y avanza en zoneless como camino recomendado para proyectos nuevos. Es la versión con más cambios de fondo desde que Angular adoptó standalone components.


    Qué cambia de raíz en Angular v22

    Antes de ver las APIs una a una, hay una idea central que explica casi todo lo que trae v22:

    Angular se está moviendo hacia un modelo completamente basado en Signals, sin Zones y sin boilerplate innecesario.

    Eso no es nuevo como dirección. Lo que es nuevo en v22 es que varias piezas de ese puzzle pasan a ser estables o por lo menos usables en producción experimental. Ya no es solo teoría.

    Patrón anterior vs equivalente en Angular v22

    Patrón anterior Equivalente en Angular v22
    HttpClient.get().subscribe() httpResource() (experimental)
    Subject + switchMap resource() / rxResource() (estables)
    new FormControl() Signal Forms formField() (experimental)
    @Injectable({ providedIn: 'root' }) @Service() (estable)
    ChangeDetectionStrategy.Default ChangeDetectionStrategy.Eager
    standalone: true explícito Default desde v22 — ya no hace falta
    allowSignalWrites: true en effect Eliminado — ya no necesario

    Resource API en Angular v22: gestión de estado asíncrono sin subscribe

    La novedad más importante de Angular v22 en el día a día son las tres APIs de Resource. resource() y rxResource() pasan a ser estables:

    resource() — async state sin subscribe

    resource() es la forma nativa de Angular para manejar operaciones asíncronas reactivas. Defines un loader con una Promise y el framework gestiona loading, error y datos por ti.

    import { resource, signal } from '@angular/core';
    

    @Component({ ... })

    export class ProductListComponent {

    categoryId = signal(1);

    products = resource({

    request: () => this.categoryId(),

    loader: ({ request }) =>

    fetch(/api/products?category=${request}).then(r => r.json())

    });

    }

    En el template:

    @if (products.status() === 'loading') {
    

    <p>Cargando...</p>

    }

    @if (products.status() === 'resolved') {

    @for (product of products.value(); track product.id) {

    <li>{{ product.name }}</li>

    }

    }

    Los estados posibles son strings literales: 'idle', 'loading', 'reloading', 'resolved', 'error', 'local'. No hay enum. No uses ResourceStatus.Loading — no existe así.

    rxResource() — cuando el backend habla en Observables

    Si tienes servicios que devuelven Observables (la mayoría de los proyectos reales), usa rxResource(). La clave es que el parámetro se llama stream, no loader:

    import { rxResource } from '@angular/core/rxjs-interop';
    

    @Component({ ... })

    export class OrdersComponent {

    userId = signal(42);

    orders = rxResource({

    request: () => this.userId(),

    stream: ({ request }) => this.ordersService.getByUser(request)

    });

    }

    La diferencia con resource() es solo el parámetro: loader para Promises, stream para Observables. El resto del comportamiento es idéntico.

    httpResource() — HTTP reactivo sin HttpClient.get().pipe(...)

    httpResource() es la versión experimental especializada en HTTP — úsala con precaución en producción. El primer argumento siempre es una función, nunca un string directo.

    import { httpResource } from '@angular/core';
    

    @Component({ ... })

    export class UserProfileComponent {

    userId = signal(1);

    user = httpResource<User>(() => /api/users/${this.userId()});

    }

    Requiere provideHttpClient() en tu configuración. Devuelve un HttpResourceRef que expone .value(), .status(), .statusCode() y .headers() como signals.

    Si quieres profundidad en las tres APIs, tengo un post dedicado: Resource API en Angular 22: el fin del subscribe() manual.


    linkedSignal() estable: el signal derivado que puedes escribir

    linkedSignal() resuelve un problema concreto que no tenía solución limpia hasta ahora: quieres un signal que se inicialice (y se resetee) a partir de otro signal, pero que también puedas modificar manualmente.

    Ejemplo clásico: una lista de items y un item seleccionado que vuelve al primero cuando cambia la lista.

    import { signal, linkedSignal } from '@angular/core';
    

    @Component({ ... })

    export class ItemSelectorComponent {

    items = signal(['Angular', 'React', 'Vue']);

    selectedItem = linkedSignal(() => this.items()[0]);

    }

    Cuando items cambia, selectedItem vuelve automáticamente al primer elemento. Pero puedes escribir en selectedItem en cualquier momento:

    this.selectedItem.set('React'); // funciona

    Con un computed() no puedes hacer eso. Con un signal() normal perderías el vínculo con items. linkedSignal() es la pieza que faltaba entre los dos.


    debounced() experimental: búsquedas sin setTimeout manual

    debounced() es una API experimental de Angular v22 para manejar valores con delay configurable. No devuelve un Signal — devuelve un Resource, así que se lee con .value() y .status(), igual que resource().

    Es ideal para barras de búsqueda donde no quieres disparar una petición por cada tecla. Al ser experimental, la firma exacta puede cambiar antes de estabilizarse — consulta siempre la documentación oficial de angular.dev antes de usarla en producción.


    Signal Forms experimental: formularios basados en Signals

    Angular v22 introduce Signal Forms como API experimental. No reemplaza a Reactive Forms todavía — no está pensado para producción sin asumir el riesgo de breaking changes — pero marca la dirección clara hacia la que van los formularios en Angular.

    La premisa es eliminar el FormBuilder, los FormGroup y los valueChanges basados en Observables. Todo como signals.

    Es la feature que más puede cambiar antes de estabilizarse, así que úsala con precaución en proyectos reales y estate pendiente a las release notes.


    effect() ya no necesita allowSignalWrites

    Pequeño pero importante: a partir de v22, escribir en un signal dentro de un effect() está permitido por defecto. La opción allowSignalWrites está deprecada y no debes usarla.

    Antes (v21 y anteriores):

    // v21 — necesitabas esto:
    

    effect(() => {

    this.count.set(this.source() * 2);

    }, { allowSignalWrites: true });

    Ahora (v22):

    // v22 — simplemente funciona:
    

    effect(() => {

    this.count.set(this.source() * 2);

    });

    Si tienes código con allowSignalWrites: true, no se rompe todavía. Pero el compilador te avisará que está deprecated. Es uno de esos cambios que limpias en 10 minutos con un find & replace.


    ChangeDetectionStrategy.Eager y el adiós definitivo a Default

    Angular v22 introduce ChangeDetectionStrategy.Eager como el nuevo nombre para la estrategia de detección de cambios que antes se llamaba Default.

    ChangeDetectionStrategy.Default pasa a ser un alias deprecated de Eager. Si tienes componentes sin estrategia explícita, no se rompen, pero la nomenclatura oficial cambia:

    // Antes (sigue funcionando, pero deprecated el nombre):
    

    @Component({

    changeDetection: ChangeDetectionStrategy.Default

    })

    // Ahora (lo correcto en v22):

    @Component({

    changeDetection: ChangeDetectionStrategy.Eager

    })

    En la práctica, para componentes nuevos lo relevante sigue siendo usar OnPush cuando sea posible y avanzar hacia zoneless. Eager es el fallback explícito cuando necesitas el comportamiento clásico por nombre, no por omisión.


    Zoneless en Angular v22: cómo funciona y cuándo usarlo en producción

    Zone.js ha sido la pieza más criticada del runtime de Angular desde que existe. En v22 el modo zoneless avanza significativamente como alternativa estable para proyectos nuevos.

    Sin Zone.js, Angular solo ejecuta detección de cambios cuando se lo dices explícitamente: a través de signals, events, o marcando el componente como sucio manualmente. El resultado son aplicaciones más predecibles, más fáciles de depurar y más rápidas en la mayoría de los escenarios.

    Para activarlo en una app nueva:

    // main.ts
    

    bootstrapApplication(AppComponent, {

    providers: [

    provideExperimentalZonelessChangeDetection()

    ]

    });

    En el Curso de Angular Moderno cubrimos la arquitectura zoneless con Signals desde cero — actualizado a v22.


    standalone: true ya no es necesario

    A partir de v22, standalone es el default. No necesitas escribir standalone: true en ningún componente nuevo:

    // v21 y anteriores — necesitabas declararlo:
    

    @Component({

    standalone: true,

    selector: 'app-product-card',

    ...

    })

    // v22 — standalone por defecto, sin declaración:

    @Component({

    selector: 'app-product-card',

    ...

    })

    Solo necesitas standalone: false si quieres un componente que NO sea standalone — que es el caso raro ahora.


    Nuevo decorator @Service() en Angular v22: para qué sirve

    Si llevas tiempo usando inject() en lugar de constructor injection, este cambio te va a gustar.

    Angular v22 introduce @Service() como alternativa directa a @Injectable({ providedIn: 'root' }). Sin opciones, sin configuración — declaras la clase como servicio y Angular la provee en root automáticamente.

    // Antes
    

    @Injectable({ providedIn: 'root' })

    export class AuthService {

    private http = inject(HttpClient);

    }

    // Angular v22

    @Service()

    export class AuthService {

    private http = inject(HttpClient);

    }

    Un detalle importante: @Service() solo funciona con inject(). Si intentas usar constructor injection con @Service(), obtendrás un error — el decorator asume el modelo de inyección funcional. Si necesitas constructor injection o configuración avanzada como providedIn: 'platform', sigue usando @Injectable.

    Es coherente con la dirección que lleva el framework desde que inject() llegó — alejarse del constructor como único punto de entrada de dependencias.


    Angular v22: tabla completa de features estables y experimentales

    Feature Estado en v22 Para producción
    resource() Estable
    rxResource() Estable
    linkedSignal() Estable
    @Service() Estable
    standalone: true default Estable
    allowSignalWrites deprecated Estable Quitar el flag
    ChangeDetectionStrategy.Eager Estable
    httpResource() Experimental Con precaución
    debounced() Experimental Con precaución
    Signal Forms Experimental No recomendado aún
    Zoneless Developer preview Proyectos nuevos

    Cómo migrar a Angular v22 desde Angular 20 o 21: guía paso a paso

    No necesitas migrar todo a la vez. Esta es la secuencia que tiene más sentido:

    • Elimina allowSignalWrites: true de tus effects. Es trivial y lo haces en un PR.
    • Adopta linkedSignal() donde tengas signals que dependen de otros y se resetean. Los encontrarás fácilmente.
    • Migra a @Service() en servicios simples que ya usen inject(). La ganancia es inmediata en legibilidad.
    • Empieza a usar resource() en componentes nuevos en lugar de switchMap + HttpClient.get(). No tienes que migrar los existentes de golpe.
    • Experimenta con zoneless en un proyecto nuevo o en un módulo aislado.
    • Deja Signal Forms para más adelante hasta que estabilice.

    Si quieres ir más al fondo en cómo funciona el testing de estos nuevos patrones, tengo el Curso de Testing en Angular actualizado con Jest y Testing Library — resource() y linkedSignal() cambian cómo se escriben los tests de componentes.


    FAQ — Preguntas frecuentes sobre Angular v22

    ¿Es Angular v22 compatible con Angular v19 o v20?

    La migración de v19/v20 a v22 es incremental. Las APIs nuevas son aditivas — no rompen el código existente. standalone: true sigue funcionando aunque ya no sea necesario. ChangeDetectionStrategy.Default sigue siendo un alias válido aunque deprecated. Puedes actualizar con ng update y adoptar las nuevas APIs a tu ritmo sin necesidad de reescribir nada de golpe.

    ¿httpResource() reemplaza a HttpClient en Angular v22?

    No. HttpClient no desaparece en v22 y sigue siendo la opción recomendada para lógica HTTP compleja. httpResource() es una alternativa más ergonómica para casos concretos: cuando tienes un signal como parámetro reactivo de la petición y quieres gestionar loading/error automáticamente. Para interceptores custom, peticiones en paralelo o manejo avanzado de headers, HttpClient con RxJS sigue siendo la herramienta correcta. Además, httpResource() es experimental en v22, así que no es recomendable adoptarlo masivamente en proyectos en producción todavía.

    ¿Qué diferencia hay entre resource() y httpResource()?
    resource() es genérico: acepta cualquier función que devuelva una Promise como loader. httpResource() está especializado en HTTP y usa internamente HttpClient, por lo que respeta interceptores, el provideHttpClient() configurado y expone metadatos de la respuesta como .statusCode() y .headers(). Para llamadas HTTP simples con parámetros reactivos, httpResource() es más cómodo. Para lógica asíncrona que no sea HTTP, resource() es la opción.
    ¿Signal Forms reemplaza a Reactive Forms en v22?

    No. Signal Forms es experimental en v22 y no está pensado para reemplazar Reactive Forms todavía. Reactive Forms sigue siendo la opción estable y recomendada para formularios complejos en producción. Signal Forms marca la dirección futura del framework — formularios completamente basados en signals sin FormBuilder ni valueChanges — pero antes de considerarla lista para producción necesita que la API se estabilice, cosa que no ocurre en v22.

    ¿Puedo usar zoneless ya en producción con Angular v22?

    Depende del proyecto. Para proyectos nuevos que uses signals de forma consistente, zoneless es viable — el equipo de Angular lo recomienda como el camino a seguir. Para proyectos existentes que mezclan Zone.js con código legacy que depende del ciclo de detección de cambios automático, la migración requiere más cuidado. En v22 el modo zoneless sigue marcado como “experimental” en el nombre del provider (provideExperimentalZonelessChangeDetection()), aunque en la práctica es bastante estable para proyectos nuevos bien estructurados.

    ¿linkedSignal() es lo mismo que computed() con posibilidad de escritura?

    Conceptualmente se parecen, pero con una diferencia clave: linkedSignal() tiene dependencia reactiva sobre otro signal para su valor inicial y para resetearse automáticamente cuando ese signal cambia. computed() es de solo lectura — no puedes escribir en él. signal() es escribible pero no tiene vínculo reactivo con otros signals. linkedSignal() combina los dos comportamientos: se actualiza cuando cambia su fuente y también acepta escrituras manuales, lo que lo hace ideal para estados que tienen un “valor por defecto reactivo” pero que el usuario puede sobrescribir.

    ¿Para qué sirve @Service() y cuándo no usarlo?
    @Service() es el nuevo decorator de Angular v22 que simplifica la declaración de servicios singleton en root. Equivale a @Injectable({ providedIn: 'root' }) pero sin configuración. Solo funciona con inject() — si intentas constructor injection con @Service(), obtendrás un error. Úsalo en servicios simples que ya sigan el patrón de inject(). Si necesitas providedIn: 'platform', providedIn: 'any' u otras opciones avanzadas, sigue usando @Injectable con su configuración completa.


    Si estás construyendo con IA en tu día a día como developer, el curso Construye con IA te muestra cómo integrar Claude Code en tu flujo de trabajo real con proyectos Angular y TypeScript.


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

  • Cómo evitar que los agentes elijan mal sus herramientas en proyectos de IA

    Cómo evitar que los agentes elijan mal sus herramientas en proyectos de IA

    El problema real del tool_use: cuándo los agentes eligen mal sus herramientas

    Tiempo estimado de lectura: 4 min

    • Diseña cada tool como un contrato: propósito claro, condición binaria de activación, restricciones negadas y un schema de salida.
    • Reduce la superficie de decisión: retrieval dinámico y máquinas de estado cuando el catálogo supera ~10–15 tools.
    • Valida estrictamente: enums, formatos concretos y validación back-end (ej. Zod) para evitar argumentos corruptos.
    • Mide lo que importa: precisión de selección, retries, tokens gastados y casos de misuse en staging.

    Si tu agente tiene acceso a muchas herramientas y no defines reglas explícitas, no estás ante un fallo del modelo: estás ante un diseño roto. Este artículo explica por qué ocurren elecciones equivocadas y cómo diseñar descripciones de herramientas que reducen errores de selección hasta en ~80% en implementaciones reales. Conectar APIs es trivial; conseguir que un LLM seleccione la herramienta correcta, con argumentos válidos y sin invocar capacidades fuera de scope, es ingeniería.

    Resumen rápido (lectores con prisa)

    Define cada herramienta como un contrato: una frase de propósito, una condición de activación binaria, restricciones explícitas de uso y un schema de salida mínimo. Usa retrieval dinámico para reducir opciones y máquinas de estado para limitar permisos. Valida en back-end (ej. Zod) y mide precisión de selección y retries.

    El problema real del tool_use: cuándo los agentes eligen mal sus herramientas — causas

    Tres causas estructurales explican la mayoría de las fallas en producción:

    1. Solapamiento semántico. Dos o más tools parecen válidas para la misma intención; el modelo elige por probabilidades.
    2. Ausencia de fronteras negativas. Documentas qué hace la tool pero no cuándo está prohibida; el modelo probará usos peligrosos.
    3. Sobrecarga del contexto. Inyectar 30–40 esquemas en el prompt produce “lost in the middle” y pérdida de atención (ver estudio).

    Si no mitigues estas tres fuentes de ambigüedad, el agente duplicará llamadas, generará argumentos corruptos o intentará acciones destructivas.

    Cómo diseñar descripciones que reducen errores de selección (estructura de 4 campos)

    Piensa la descripción de cada herramienta como un contrato para una red neuronal: precisa, restrictiva y ejecutable. Sigue estos cuatro campos en todas las tools:

    1. Propósito (What) — Una sola frase: acción exacta.
    2. Condición de activación (When) — “Úsalo SOLO cuando…” (condición binary o claramente verificable).
    3. Restricción excluyente (When NOT) — “NO lo uses para…” y alternativa sugerida.
    4. Formato de salida (Expected Output) — JSON schema mínimo que el agente puede comprobar antes de llamar.

    Ejemplo práctico

    Descripción:

    “Recupera el estado y el último comentario de un ticket de Jira. Úsalo SOLO con un ticket ID válido (PROJ-123). NO lo uses para búsquedas por texto; usa search_jira_tickets para eso.”

    Schema (JSON / Zod-like)

    {
    “type”: “object”,
    “properties”: {
    “ticketId”: { “type”: “string”, “pattern”: “^[A-Z]+-\\d+$” }
    },
    “required”: [“ticketId”]
    }

    Ese nivel de precisión elimina ambigüedad en cuándo y cómo llamar la tool.

    Schemas como enrutadores: reglas prácticas

    • Evita tipos genéricos. Si esperas fecha, exige format: “date-time”.
    • Describe cada propiedad. El LLM usará esa descripción para construir el valor.
    • Forza enums para valores discretos. Los LLM respetan enums con alta consistencia.
    • Implementa validación estricta (Zod) en el back-end y devuelve errores estructurados (Result pattern) si el LLM envía datos inválidos.

    Arquitectura para catálogos grandes: Dynamic Tool Retrieval y State Machines

    Cuando tienes >10–15 tools, las descripciones no bastan. Aplica dos patrones:

    • Dynamic Tool Retrieval (RAG de herramientas). Embeddiza la intención del usuario y busca en una DB vectorial las 3–4 tools más relevantes; solo esas se inyectan en el prompt. Implementaciones prácticas usan pgvector o sistemas vectoriales gestionados. Reducir la superficie de decisión aumenta la precisión drásticamente.
    • Máquinas de estado / orquestación. Divide responsabilidades entre sub-agentes con permisos limitados. Herramientas de orquestación: n8n, LangGraph o XState. El nodo de “consulta” solo expone tools de lectura; el nodo de “modificación” habilita herramientas de escritura tras condiciones de validación.

    Restricción por estado = seguridad + predictibilidad.

    Métricas y pruebas que importan

    No te fíes de sensaciones. Mide:

    • Precisión de selección (tool chosen vs. tool expected).
    • Retries por tipo de error.
    • Tokens gastados en reintentos inútiles.
    • Casos de “tool misuse” detectados en staging.

    Introduce tests que simulen entradas ambiguas y fallos de herramientas. Si una nueva tool aumenta la entropía del sistema, el pipeline debe bloquear el cambio hasta ajustar descripciones/schemas o introducir retrieval/state gating.

    Conclusión operativa

    El problema real del tool_use no se arregla con prompts más largos ni con nombres más creativos. Se arregla con contratos: descripciones inmutables que indiquen qué hacer y qué no hacer; schemas que validen argumentos; retrieval que reduzca la superficie de decisión; y orquestación que limite permisos por estado. En la práctica, aplicar esta disciplina reduce los errores de selección de herramientas en la mayoría de despliegues (hasta ~80% en nuestras pruebas) y convierte agentes ruidosos en sistemas previsibles y auditablemente seguros.

    Si vas a exponer nuevas herramientas a un agente, no te preguntes si el LLM “entenderá”. Pregunta primero: ¿puede automatizarse la verificación de la condición de activación y la restricción excluyente? Si la respuesta es no, no la expongas todavía. Limita opciones, mejora instrucciones y mejora tus probabilidades de tener un agente que elige bien.

    Para experimentos, plantillas y recursos relacionados con agentes y workflows, revisa Dominicode Labs. Es una extensión natural de las prácticas descritas aquí, con ejemplos aplicables a despliegues reales.

    FAQ

    ¿Por qué el LLM elige la herramienta equivocada?

    Porque hay ambigüedad: solapamiento semántico entre tools, falta de fronteras negativas o sobrecarga del contexto. Si no se reducen estas fuentes, el modelo elige por probabilidades y puede fallar.

    ¿Qué debe contener una descripción de tool?

    Cuatro campos: Propósito (una frase), Condición de activación (¿cuándo usarla? — binaria), Restricción excluyente (¿cuándo NO usarla? con alternativa) y Formato de salida (schema mínimo verificable).

    ¿Qué es Dynamic Tool Retrieval y cuándo usarlo?

    Es el patrón de embeddizar la intención y recuperar las N tools más relevantes desde una DB vectorial (RAG de herramientas). Úsalo cuando tengas más de ~10–15 tools para reducir la superficie de decisión.

    ¿Cómo aplicar validación estricta en producción?

    Define schemas concretos (fechas con format, enums, patterns), valida en back-end (ej. Zod) y devuelve errores estructurados. Bloquea ejecuciones si la validación falla.

    ¿Qué métricas debo medir primero?

    Precisión de selección (tool chosen vs. expected), retries por tipo de error y tokens gastados en reintentos. También monitorea casos de tool misuse en staging.

    ¿Cuándo no exponer una nueva tool al agente?

    Si no puedes automatizar la verificación de la condición de activación y de la restricción excluyente, no la expongas. Mejor ajusta instrucciones, schemas o añade gating por retrieval/state antes de desplegar.

  • Qué es un Agentic Engineer y cómo convertirte en uno en 2026

    Qué es un Agentic Engineer y cómo convertirte en uno en 2026

    El año pasado hablé con un developer que llevaba tres meses usando Claude como copiloto. Me dijo: “Bezael, soy un 30% más rápido. Pero sigo sin entender lo que ocurre debajo.”

    Tres meses. Treinta por ciento más de velocidad. Y la sensación de que le faltaba algo.

    Le faltaba exactamente esto: pasar de usar agentes de IA a diseñarlos. De consumir herramientas a entender qué las hace funcionar en producción, dónde fallan, cómo orquestarlas para que resuelvan problemas complejos sin supervisión constante.

    Eso es agentic engineering — y en 2026 se está convirtiendo en la disciplina más relevante para cualquier developer que construya con IA.

    Qué es exactamente el agentic engineering

    El agentic engineering es la ingeniería de software especializada en diseñar, construir y operar sistemas de agentes de IA que trabajan de forma autónoma para completar objetivos.

    No es usar ChatGPT para escribir código más rápido. No es añadir un botón de “generar con IA” a tu app. Es una disciplina de arquitectura de sistemas con sus propios patrones, sus propias decisiones de diseño y sus propios problemas de producción.

    La diferencia práctica: un developer que usa IA como copiloto recibe sugerencias y decide qué aceptar. Un agentic engineer diseña el sistema donde la IA toma decisiones, ejecuta acciones y se corrige a sí misma — y lo hace de forma predecible, trazable y fiable.

    Esa es la distancia. No es trivial.

    Por qué importa ahora y no en dos años

    Hasta 2024, los agentes eran demos. Impresionantes en vídeo, rotos en producción. El modelo se confundía, las herramientas fallaban, el contexto se perdía a las diez iteraciones.

    En 2025 cambió algo fundamental: los modelos de frontera dieron un salto cualitativo en razonamiento. Claude Sonnet 4 (Anthropic, 2025), GPT-4o y Gemini 2.5 Pro pueden mantener objetivos complejos durante decenas de ciclos de herramienta sin perder el hilo — algo que sus predecesores de 2023 no podían hacer de forma fiable.

    Eso abrió una ventana que no va a durar indefinidamente: los developers que entiendan cómo orquestar estos sistemas tienen una ventaja real ahora, antes de que esto se empaquete en herramientas de no-code para cualquiera.

    La demanda ya está llegando. Las empresas no buscan developers que sepan usar IA como asistente. Buscan developers que sepan construir sistemas donde la IA hace trabajo autónomo de verdad — revisión de código, análisis de datos, procesamiento de documentos, automatización de pipelines enteros.

    El mercado se está moviendo. La pregunta es si tú te mueves con él o lo observas desde fuera.

    La diferencia real con vibe coding (y por qué importa)

    Hay que nombrarlo porque está en todas partes: el vibe coding — dejar que el modelo genere código mientras tú aceptas todo sin entender qué hace.

    No es necesariamente malo para prototipar. Pero confundir vibe coding con agentic engineering es uno de los errores más caros que puedo ver en un developer que quiere construir cosas serias.

    El vibe coding asume que el modelo siempre sabe lo que hace. El agentic engineering parte de la premisa contraria: el modelo es poderoso pero falible, y tu trabajo como ingeniero es diseñar el sistema que lo hace fiable.

    La diferencia concreta:

    Vibe coding Agentic Engineering
    Acepta la sugerencia del modelo Diseña el sistema que valida la salida del modelo
    Trabaja con prompts sueltos Trabaja con pipelines de contexto, memoria y herramientas
    No entiende por qué funciona Entiende el agentic loop y puede depurarlo
    Falla en producción sin saber por qué Instrumenta observabilidad para ver qué hace el agente
    Escala hasta el primer bug complejo Escala porque el sistema tiene controles de calidad

    El vibe coding te da velocidad al principio. El agentic engineering te da sistemas que funcionan en producción durante meses, sin que tengas que apagar el servidor a las 2 de la mañana porque el agente tomó una decisión que no deberías haberle permitido.

    Qué sabe hacer un Agentic Engineer

    Esto no es una lista de tecnologías. Es un mapa de competencias — cada una representa una decisión de diseño real que separa un sistema de agentes que funciona de uno que falla.

    Habilidades técnicas core

    Habilidad Por qué importa en producción
    Diseño de flujos multi-agente Saber cuándo descomponer en subagentes y cuándo no — la descomposición innecesaria multiplica los puntos de fallo
    Gestión de contexto y memoria El contexto mal diseñado es la causa número uno de degradación en agentes de larga ejecución
    Tool design y herramientas seguras Las herramientas mal diseñadas son el vector de ataque más común en sistemas agénticos
    Orquestación y handoffs Cómo un agente pasa trabajo a otro sin perder información crítica en la transferencia
    Observabilidad y trazabilidad Sin trazas, depurar un agente en producción es imposible — solo ves inputs y outputs, no el razonamiento
    Límites y control humano Definir qué acciones requieren confirmación y cuáles pueden ser autónomas — no todo el tiempo, no nunca
    Evaluación de agentes Cómo medir si el agente está haciendo bien su trabajo, más allá de “parece correcto”

    Habilidades de sistema

    Un agentic engineer también tiene que pensar en capas más amplias:

    • Arquitectura de prompts de sistema — no es escribir un prompt, es diseñar las instrucciones que gobiernan el comportamiento del agente en todos los escenarios posibles
    • Gestión de errores en pipelines asíncronos — los fallos en sistemas multi-agente no se propagan como en código síncrono normal
    • Estrategias de retry y fallback — qué hace el sistema cuando el modelo devuelve una respuesta malformada o una herramienta falla en el ciclo 8 de 15
    • Cost management — los tokens tienen precio; un agente que entra en bucle puede consumir más en una hora que todo un mes de uso normal

    La diferencia con el developer tradicional

    Un developer tradicional escribe código que ejecuta instrucciones exactas. Sabes exactamente lo que hará tu función processPayment() si la lees línea a línea.

    Un agentic engineer trabaja con sistemas donde el comportamiento exacto es no determinista. El mismo input puede producir outputs ligeramente diferentes. El agente puede resolver el problema de tres maneras distintas y todas pueden ser válidas — o puede fallar de formas que no estaban en ningún test.

    Esto no hace el trabajo más fácil. Lo hace diferente. Requiere un cambio de mentalidad: de “verificar que el código es correcto” a “diseñar el sistema para que los errores sean detectables, contenidos y recuperables”.

    También requiere entender el negocio a un nivel más profundo. Cuando un agente tiene autonomía para tomar decisiones, las consecuencias de una decisión incorrecta son mayores que cuando un developer escribe código que hace exactamente lo que le dicen.

    El agentic engineer tiene que entender qué acciones son reversibles, cuáles tienen consecuencias económicas y cuáles requieren supervisión humana.

    Cómo convertirte en un Agentic Engineer: roadmap práctico

    No hay un título. No hay una certificación que lo valide todavía. Lo que hay es experiencia construyendo sistemas reales y la capacidad de razonar sobre ellos.

    Este es el roadmap que yo seguiría si empezara hoy:

    Fase 1 — Entiende el mecanismo antes de las abstracciones (2-3 semanas)

    Implementa un agentic loop desde cero con la API de Anthropic o OpenAI. Sin LangChain. Sin frameworks. Solo el bucle percibir-razonar-actuar con tres herramientas básicas: leer archivos, escribir archivos, ejecutar comandos. Si no has leído el post sobre el agentic loop, empieza por ahí — cubre exactamente esta capa.

    La estructura mínima en TypeScript tiene este aspecto:

    while (objective.isNotComplete()) {
      const observation = await perceive(environment);  // leer contexto
      const decision = await llm.reason(observation);   // razonar con el modelo
      const result = await tools.execute(decision);     // ejecutar herramienta
    
      if (decision.type === "final_answer") break;
      environment.update(result);                       // actualizar estado
    }

    No es código de producción — es el esqueleto. Entender qué entra y qué sale en cada paso es lo que te permite depurar cuando el agente toma una decisión inesperada en el ciclo 12.

    El objetivo no es llegar rápido a producción. Es entender qué ocurre en cada iteración para poder diagnosticar problemas después.

    Fase 2 — Diseña tu primer agente con propósito real (3-4 semanas)

    Coge un problema concreto de tu trabajo diario y construye un agente que lo resuelva. No un agente genérico. Uno que haga una cosa específica bien: revisar PRs, procesar facturas, generar reportes a partir de datos, lo que sea que tenga valor en tu contexto.

    La restricción de “un problema específico” es intencional. Los agentes generalistas fallan más que los especializados. Empieza acotado.

    Fase 3 — Introduce observabilidad desde el principio (paralelo a Fase 2)

    Antes de confiar en que tu agente funciona, instrumenta lo que hace. Registra cada herramienta que llama, cada decisión que toma, cuántos tokens consume por ciclo. Sin esta capa no puedes mejorar el sistema — solo puedes rezar para que funcione.

    Fase 4 — Construye tu primer sistema multi-agente (4-6 semanas)

    Aquí está el salto real. Diseña un sistema donde dos o más agentes colaboran: un orquestador que divide el trabajo y subagentes que lo ejecutan. Implementa los handoffs. Entiende dónde se pierde contexto en la transferencia y cómo evitarlo.

    Este es el nivel donde empieza a tener sentido hablar de agentic engineering como disciplina, no como experimento.

    Fase 5 — Opera en producción (continuo)

    Despliega. Observa los fallos reales. Itera. Los problemas que solo aparecen en producción — usuarios que hacen cosas inesperadas, APIs externas que fallan en el momento equivocado, el modelo que decide hacer algo creativo con un input ambiguo — son los que te convierten en engineer de verdad.

    Dónde aprenderlo hoy

    La teoría ya no es el problema. Lo que falta en casi todo el material disponible es el criterio: cuándo usar qué patrón, cómo depurar cuando el sistema falla, qué decisiones de arquitectura importan en producción y cuáles son optimización prematura.

    En el curso Construye con IA cubrimos exactamente este criterio: desde el agentic loop hasta el diseño de sistemas multi-agente, pasando por las decisiones de arquitectura que hacen que un agente funcione en producción y no solo en demos.

    Y si quieres el marco estructural para diseñar antes de construir — la metodología que evita construir el sistema equivocado — el libro de Spec-Driven Development explica cómo especificar sistemas de agentes antes de escribir una sola línea de código.

    El developer que llegó a tiempo

    Vuelvo al developer del principio. El que era un 30% más rápido pero no entendía lo que ocurría debajo.

    Le dije que esa sensación era buena. No porque ser ignorante sea bueno, sino porque reconocer el gap es el primer paso para cerrarlo.

    La mayoría de los developers que usan IA hoy están en ese punto. Más rápidos. Más productivos. Pero construyendo sobre una caja negra que no controlan.

    El agentic engineering es la disciplina que convierte esa caja negra en un sistema que entiendes, que puedes depurar y que puedes confiar en que funciona cuando no estás mirando.

    Eso no es el futuro. Es lo que los mejores developers están haciendo ahora mismo.

    Si quieres ver cómo aplicamos estos principios en proyectos reales — con análisis de arquitecturas, sesiones de revisión de código y una comunidad de developers que ya construyen sistemas de agentes en producción — pásate por Dominicode Labs.

    Y si antes de la disciplina quieres el objeto —qué es un agente de IA, su anatomía y los grados de autonomía—, esa es la pieza base sobre la que se apoya todo lo anterior.

    FAQ — Preguntas frecuentes sobre Agentic Engineering

    ¿Qué es el agentic engineering exactamente?

    El agentic engineering es la disciplina de ingeniería de software especializada en diseñar, construir y operar sistemas de agentes de IA autónomos. A diferencia del desarrollo de software tradicional, trabaja con sistemas donde el comportamiento es no determinista y los agentes pueden tomar decisiones, ejecutar acciones y corregirse a sí mismos en tiempo real. El foco está en hacer esos sistemas predecibles, trazables y fiables en producción, no solo en demos controladas.

    ¿En qué se diferencia un Agentic Engineer de un developer que usa IA?

    Un developer que usa IA la utiliza como asistente: genera código, sugiere soluciones, responde preguntas. El agentic engineer diseña sistemas donde la IA actúa con autonomía: orquesta tareas, gestiona herramientas, mantiene contexto y opera sin supervisión constante. La diferencia no es de herramientas sino de nivel de abstracción y responsabilidad sobre el sistema.

    ¿Se necesita experiencia con LLMs para convertirse en Agentic Engineer?

    No es imprescindible, pero acelera mucho entender cómo funcionan los LLMs internamente: cómo procesan el contexto, por qué el tamaño del contexto importa, cómo el diseño del prompt afecta al comportamiento. Un developer con experiencia en arquitecturas de backend distribuidas tiene una ventaja real — los problemas de fiabilidad, observabilidad y gestión de errores son conceptualmente similares.

    ¿Cuáles son los frameworks más usados en agentic engineering hoy?

    En 2026 los más extendidos son LangGraph (para flujos con estado y ramificaciones complejas), las primitivas nativas de Anthropic con tool use, y las de OpenAI con function calling. Claude Code es una implementación completa de un agentic loop para desarrollo de software. Para orquestación visual y automatizaciones de negocio, n8n tiene nodos de AI Agent que implementan el loop sin escribir código. La recomendación es aprender el mecanismo antes que el framework — los frameworks cambian, el agentic loop no.

    ¿El agentic engineering reemplaza al desarrollo de software tradicional?

    No lo reemplaza, lo extiende. Los sistemas de agentes necesitan infraestructura, APIs, bases de datos, autenticación — todo el stack de desarrollo tradicional. Lo que cambia es la capa de lógica de negocio: en lugar de escribir código imperativo que ejecuta pasos exactos, el agentic engineer diseña el sistema que permite a la IA razonar sobre esos pasos. Ambas capas son necesarias y complementarias.

    ¿Qué diferencia hay entre agentic engineering y prompt engineering?

    El prompt engineering es una técnica dentro del agentic engineering — diseñar las instrucciones que gobiernan el comportamiento del agente. Pero el agentic engineering es mucho más amplio: incluye arquitectura de sistemas, diseño de herramientas, gestión de memoria y contexto, observabilidad, estrategias de fallback y operaciones en producción. Un buen prompt es necesario pero no suficiente para construir un agente que funcione en producción.


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

    Si este post te ha sido útil, hay más contenido técnico sobre IA aplicada al desarrollo en el canal de YouTube de Dominicode.

  • Clean Architecture en frontend con IA: respeta las capas

    Clean Architecture en frontend con IA: respeta las capas

    Le pedí a un agente que generara la feature de listado de productos para un proyecto frontend.

    Veinte segundos después tenía el código listo. Funcionaba. Los datos aparecían en pantalla.

    Y entonces abrí el componente y vi esto:

    // ProductListComponent.tsx — lo que el agente generó sin contexto
    const ProductListComponent = () => {
      const [products, setProducts] = useState([]);
    
      useEffect(() => {
        fetch("https://api.myapp.com/products")
          .then(res => res.json())
          .then(data => setProducts(data));
      }, []);
    
      return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
    };

    Una llamada HTTP directamente en el componente. Sin interface. Sin use case. Sin repository. Sin manejo de errores. La lógica de negocio mezclada con la presentación, exactamente lo que llevaba seis meses evitando en ese proyecto.

    El agente no hizo lo que yo quería. Hizo lo que era más rápido de generar.

    Ese es el problema real cuando usas IA en un proyecto con clean architecture frontend: la IA optimiza para el camino más corto, no para el más correcto. Y sin guía, el camino más corto siempre es el spaghetti.

    (Los ejemplos de este post usan TypeScript 5.4 con Angular 19 / React 18 como referencia, y Claude Code en su versión de 2026. El CLAUDE.md y los principios aplican igualmente a Cursor y GitHub Copilot.)

    Por qué la IA destroza la arquitectura si la dejas sola

    Clean Architecture en frontend no es difícil de entender. Es difícil de sostener.

    Cualquier developer senior entiende la separación de capas. El problema es que cuando el equipo crece, cuando hay presión de tiempo, cuando alguien nuevo entra al proyecto — las capas se erosionan. Un fetch aquí, una lógica de transformación allá directamente en el componente.

    La IA acelera exactamente este problema.

    Los LLMs aprenden de código real que existe en internet. Y el código real que existe en internet está lleno de llamadas HTTP en componentes, lógica de negocio en controllers, transformaciones de datos sin tipado. Los modelos han visto millones de ejemplos de ese código. Lo reproducen con total confianza porque estadísticamente es el patrón más frecuente.

    Si no le dices al agente qué arquitectura sigue tu proyecto, asumirá que no tienes ninguna.

    Las capas que importan en frontend

    Clean Architecture en frontend es un patrón de organización de código que divide la aplicación en tres capas independientes (Domain, Data, Presentation) con una regla de dependencia estricta: las capas externas dependen de las internas, nunca al revés.

    En frontend, estas tres capas se pueden modelar de forma clara — independientemente de si usas Angular, React o Vue:

    Domain — El núcleo. Aquí viven las entities (los modelos de negocio), los use cases (la lógica que define qué puede hacer el sistema) y los ports (las interfaces que definen contratos sin implementación concreta).

    Data — La capa de acceso a datos. Repositories (implementaciones concretas de los ports), DTOs (los objetos que llegan de la API tal como los devuelve el servidor), y adapters/mappers (la transformación de DTO a entity).

    Presentation — Lo que el usuario ve. Componentes, páginas, ViewModels (la forma específica en que la presentación necesita los datos), y el estado de UI.

    La regla de dependencia es simple: las capas externas dependen de las internas. La Presentation conoce el Domain. El Data implementa los contratos del Domain. El Domain no sabe que existe ninguna de las otras dos.

    Presentation → Domain ← Data

    El componente no habla con la API. Habla con un use case. El use case habla con un repository port. El repository concrete habla con la API y transforma los datos antes de devolverlos.

    Eso es lo que el agente rompió cuando puso el fetch directamente en el componente.

    Dónde la IA puede ayudarte más en Clean Architecture

    La IA es extraordinariamente buena en el trabajo más aburrido de Clean Architecture.

    Crear interfaces de repositorios. Generar mappers entre DTOs y entities. Escribir use cases que siguen un patrón uniforme. Crear tests unitarios de use cases que no tienen dependencias externas. Esas son tareas repetitivas, con patrones claros, donde el agente brilla.

    Y son exactamente las tareas que los developers saltamos “para ir más rápido” y que luego generan deuda técnica durante meses.

    Tarea de Clean Architecture IA sin contexto IA con contexto
    Generar entity con validación Genera clase plana sin contratos Sigue el patrón de entity existente
    Crear repository port (interface) Puede saltárselo e ir a la implementación Crea interface primero, luego implementación
    Escribir adapter/mapper DTO → Entity Transforma inline en el componente Crea mapper en capa Data con tipos explícitos
    Implementar use case Mezcla lógica de UI con lógica de negocio Separa correctamente, inyecta el port
    Manejo de errores en Data layer Try/catch en el componente Manejo en el repository, domain errors tipados
    Test de use case Test de integración con API real Unit test con mock del repository port

    La diferencia entre las dos columnas no es el modelo. Es el contexto que le das.

    Cómo darle contexto al agente para que respete la arquitectura

    Hay tres mecanismos que uso y que funcionan en producción.

    1. Estructura de carpetas que documenta la arquitectura

    Si tu estructura de carpetas refleja las capas, el agente las ve antes de generar código. Cuando lee el proyecto antes de actuar, el patrón es obvio:

    src/
    ├── domain/
    │   ├── entities/
    │   │   └── product.entity.ts
    │   ├── use-cases/
    │   │   └── get-products.use-case.ts
    │   └── ports/
    │       └── product.repository.port.ts
    ├── data/
    │   ├── repositories/
    │   │   └── product.repository.ts
    │   ├── dtos/
    │   │   └── product.dto.ts
    │   └── mappers/
    │       └── product.mapper.ts
    └── presentation/
        ├── components/
        │   └── product-list/
        └── view-models/
            └── product-list.vm.ts

    Un agente que lee esta estructura sabe dónde va cada pieza. La carpeta es la arquitectura documentada.

    2. CLAUDE.md con reglas de arquitectura

    Si usas Claude Code, el archivo CLAUDE.md en la raíz del proyecto es leído automáticamente antes de que el agente actúe. Es tu oportunidad de definir las reglas del juego:

    # Arquitectura del proyecto
    
    Este proyecto sigue Clean Architecture con tres capas:
    
    ## Reglas de dependencia (OBLIGATORIAS)
    - Los componentes en presentation/ NUNCA importan directamente de data/
    - Los componentes solo usan use cases de domain/use-cases/
    - Los use cases solo conocen ports de domain/ports/, nunca implementaciones concretas
    - Todo acceso a API externo va en data/repositories/, nunca en componentes ni use cases
    
    ## Antes de generar código nuevo
    1. Si es lógica de negocio → crea use case en domain/use-cases/
    2. Si es acceso a datos → crea o modifica el repository en data/repositories/
    3. Si es transformación de datos → crea mapper en data/mappers/
    4. Si el port no existe → créalo en domain/ports/ antes de la implementación
    
    ## Naming conventions
    - Entities: *.entity.ts
    - Use cases: get-products.use-case.ts (verbo + sustantivo)
    - Ports: product.repository.port.ts
    - DTOs: product.dto.ts
    - Mappers: product.mapper.ts

    Esto no es opcional. Es la diferencia entre un agente que genera spaghetti y uno que genera código que encaja en tu arquitectura.

    3. Prompt con diagrama de capas

    Cuando pides una feature específica, incluye siempre la capa donde debe vivir:

    Necesito implementar la feature "obtener lista de productos" siguiendo la arquitectura del proyecto.
    
    Genera en este orden:
    1. ProductDTO en data/dtos/ (tal como viene de la API)
    2. ProductEntity en domain/entities/ (modelo de negocio limpio)
    3. ProductMapper en data/mappers/ (transforma DTO → Entity)
    4. IProductRepository port en domain/ports/ (interface del contrato)
    5. ProductRepository en data/repositories/ (implementación concreta que usa fetch)
    6. GetProductsUseCase en domain/use-cases/ (orquesta el repositorio, devuelve entities)
    
    El componente ya existe — no lo modifiques. Solo genera las capas de dominio y datos.

    Ejemplo práctico: de DTO a Use Case con el agente

    Así es como queda el código cuando el agente tiene contexto. Le pedí exactamente las piezas del ejemplo anterior con el CLAUDE.md activo:

    // domain/entities/product.entity.ts
    export interface ProductEntity {
      id: string;
      name: string;
      price: number;
      inStock: boolean;
    }
    
    // data/dtos/product.dto.ts
    export interface ProductDTO {
      id: string;
      nombre: string;         // la API devuelve en español
      precio_eur: number;     // con otro nombre
      stock: number;          // número, no boolean
    }
    
    // data/mappers/product.mapper.ts
    import { ProductDTO } from "../dtos/product.dto";
    import { ProductEntity } from "../../domain/entities/product.entity";
    
    export const toProductEntity = (dto: ProductDTO): ProductEntity => ({
      id: dto.id,
      name: dto.nombre,
      price: dto.precio_eur,
      inStock: dto.stock > 0,
    });
    
    // domain/ports/product.repository.port.ts
    import { ProductEntity } from "../entities/product.entity";
    
    export interface IProductRepository {
      getAll(): Promise<ProductEntity[]>;
      getById(id: string): Promise<ProductEntity | null>;
    }
    
    // data/repositories/product.repository.ts
    import { IProductRepository } from "../../domain/ports/product.repository.port";
    import { ProductEntity } from "../../domain/entities/product.entity";
    import { ProductDTO } from "../dtos/product.dto";
    import { toProductEntity } from "../mappers/product.mapper";
    
    export class ProductRepository implements IProductRepository {
      private readonly baseUrl = "https://api.myapp.com";
    
      async getAll(): Promise<ProductEntity[]> {
        const response = await fetch(`${this.baseUrl}/products`);
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const dtos: ProductDTO[] = await response.json();
        return dtos.map(toProductEntity);
      }
    
      async getById(id: string): Promise<ProductEntity | null> {
        const response = await fetch(`${this.baseUrl}/products/${id}`);
        if (response.status === 404) return null;
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const dto: ProductDTO = await response.json();
        return toProductEntity(dto);
      }
    }
    
    // domain/use-cases/get-products.use-case.ts
    import { IProductRepository } from "../ports/product.repository.port";
    import { ProductEntity } from "../entities/product.entity";
    
    export class GetProductsUseCase {
      constructor(private readonly productRepository: IProductRepository) {}
    
      async execute(): Promise<ProductEntity[]> {
        // El use case no tiene try/catch — delega el manejo de errores al repository.
        // Los errores de infraestructura suben como excepciones; la capa de presentación decide cómo mostrarlos.
        return this.productRepository.getAll();
      }
    }

    El componente ahora solo necesita instanciar el use case e invocar execute(). No sabe que existe una API. No sabe el formato de los DTOs. No hace transformaciones. Solo le habla al dominio.

    Eso es Clean Architecture aplicada. Y el agente lo generó todo en un solo turno porque sabía exactamente dónde iba cada pieza.

    Dónde la IA falla aunque tengas contexto

    El CLAUDE.md no es una bala de plata.

    Hay situaciones donde el agente ignora las reglas o las interpreta de forma inesperada. Las más comunes:

    Features cross-capa sin spec previa. Si le pides “añade filtros al listado de productos”, el agente puede añadir el estado del filtro en el use case (lógica de UI en el dominio), en la URL de la API directamente, o en el componente — sin pasar por el use case. La feature es compleja y el agente toma atajos.

    Refactorizaciones de archivos existentes. Al modificar código que ya existe y que no sigue la arquitectura, el agente tiende a preservar el patrón existente en lugar de corregirlo. Si el archivo ya tiene un fetch en el componente y le pides que añada una funcionalidad, lo más probable es que añada otro fetch.

    Código sin tests previos. Sin tests que fallen cuando se rompe la arquitectura, el agente no recibe feedback negativo cuando viola las capas. El código compila, parece correcto, y el problema solo aparece cuando otro developer intenta extender la feature meses después.

    La solución a los tres casos es la misma: spec primero, código después.

    La hoja de ruta correcta: SDD + IA

    Lo que marca la diferencia no es qué agente usas. Es si empiezas con una especificación o si vas directo al código.

    Cuando escribes la spec primero — qué entities existen, qué use cases necesita la feature, qué contratos definen los ports — el agente tiene un mapa. No adivina la arquitectura. La sigue porque está documentada antes de que genere la primera línea.

    Spec-Driven Development (SDD) es exactamente esta metodología: especificar antes de implementar, usar la spec como contrato entre el developer y el agente. He documentado todo el proceso — con plantillas, ejemplos y el flujo completo — en el Libro SDD. Si tu proyecto tiene problemas de arquitectura cuando usas IA, el libro es el punto de partida más directo que tengo para darte.

    Si quieres entender primero cómo funciona el bucle interno del agente — el ciclo percibir-razonar-actuar que subyace a todo esto — el post sobre el agentic loop y la guía sobre qué es un Agentic Engineer completan el contexto antes de aplicarlo a tu arquitectura.

    El flujo práctico es este:

    1. Escribe la spec: entities, use cases, ports, contratos
    2. Configura CLAUDE.md con las reglas de arquitectura
    3. Pide al agente que genere una capa a la vez, en orden
    4. Review: ¿la implementación respeta la spec?
    5. Añade tests que fallen si alguien rompe las capas
    6. Itera

    Cada paso reduce el espacio de decisión del agente. Y reducir el espacio de decisión es reducir el riesgo de que genere spaghetti.

    Si quieres ver este flujo aplicado a proyectos reales — desde la spec hasta el producto funcionando — el curso Construye con IA cubre exactamente esto: cómo trabajar con agentes de IA respetando la arquitectura, con ejemplos en TypeScript y el proceso completo desde idea hasta código en producción.

    FAQ — Preguntas frecuentes

    ¿Clean Architecture en frontend es sobreingeniería para proyectos pequeños?

    Depende del criterio de “pequeño”. Si el proyecto va a crecer, va a tener más de un developer tocando el código o va a ser mantenido más de seis meses, Clean Architecture paga su coste desde el primer mes. El problema no es la arquitectura en sí — es implementarla de forma rígida cuando no añade valor. Para un script de 200 líneas o un prototipo desechable, no la necesitas. Para cualquier producto real, la separación de capas es lo que permite que la IA ayude en lugar de crear deuda.

    ¿Funciona el mismo enfoque con Cursor, GitHub Copilot o cualquier otro agente?

    Sí. El CLAUDE.md es específico de Claude Code, pero el principio es universal: cualquier agente que pueda leer el contexto del proyecto antes de generar código va a producir mejores resultados. En Cursor usas archivos .cursor/rules/*.mdc (la convención actual desde 2025; .cursorrules sigue funcionando por retrocompatibilidad). En GitHub Copilot puedes añadir instrucciones en el repositorio o en el prompt. La estructura de carpetas funciona con todos porque es parte del contexto que el agente lee automáticamente cuando explora el proyecto.

    ¿Cómo testeo que la arquitectura se está respetando?

    La forma más efectiva es con import constraints a nivel de build o linting. En proyectos TypeScript puedes usar eslint-plugin-boundaries para definir reglas de qué capas pueden importar de cuáles. Cuando el agente viola la arquitectura, el linter falla antes de que el código llegue a revisión. Es la red de seguridad que hace que el enfoque sea sostenible en equipos o en proyectos donde usas IA intensivamente.


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

  • Cómo implementar memoria en agentes antes de herramientas para mejorar la efectividad

    Cómo implementar memoria en agentes antes de herramientas para mejorar la efectividad

    Por qué tu agente necesita memoria antes de herramientas

    Tiempo estimado de lectura: 4 min

    • Memoria antes de herramientas: Sin memoria contextual, las herramientas incrementan errores, coste y riesgo de daño a datos y presupuesto.
    • Episódica vs semántica: Episódica = historial reciente; semántica = hechos persistentes indexados por similitud.
    • Run loop recomendado: Buscar semántica → recuperar episódica → ensamblar prompt → validar argumentos → ejecutar → persistir resultados.
    • Métricas y trazabilidad: Mide validaciones fallidas, reintentos, TTFT, coste por request y porcentaje de intervención humana.

    Si tu primer movimiento fue añadir herramientas al agente, lo estás haciendo al revés. Entender por qué tu agente necesita memoria antes de herramientas es la diferencia entre un sistema que actúa y uno que razona. Sin memoria contextual, un agente repite errores, alucina resultados y convierte cada tool en una bomba de relojería para tus datos y tu presupuesto.

    Resumen rápido (lectores con prisa)

    Qué es: La memoria provee contexto (episódico y semántico) que evita repetir errores y reduce alucinaciones.

    Cuándo usarla: Antes de permitir que un agente invoque herramientas en producción o gestione datos persistentes.

    Por qué importa: Mejora coherencia, reduce coste y riesgo, y permite recuperación cuando una tool falla.

    Cómo funciona (resumen): Buscar en memoria semántica → recuperar historial episódico → ensamblar prompt → validar y ejecutar herramientas → persistir resultados.

    Por qué tu agente necesita memoria antes de herramientas

    Las herramientas son los actuadores; la memoria es el mapa. Cuando una llamada a una API falla o una transacción SQL choca, el agente sin memoria solo ve el último input. Reintenta lo mismo, consume tokens y, en el peor de los casos, escribe datos corruptos en producción. Con memoria —episódica y semántica— el agente sabe qué intentó, qué falló y cómo adaptar su estrategia sin volver a romper nada.

    Aumentar la capacidad de acción sin dotar de historia al agente es ampliar su radio de daño.

    Memoria episódica vs semántica: función y uso práctico

    Memoria episódica (corto plazo)

    • Qué es: historial cronológico de la sesión —mensajes, decisiones del modelo y resultados de herramientas.
    • Para qué sirve: coherencia conversacional y rastreo de pasos en flujos multilargo.
    • Implementación típica: Redis o caché en memoria con TTL y extracción de los últimos N eventos.
    • Estrategias: sliding window (mantén los N mensajes más recientes) y resumen periódico (el modelo genera una síntesis que reemplaza bloques antiguos).

    Memoria semántica (largo plazo)

    • Qué es: hechos persistentes sobre usuarios, reglas, configuraciones y decisiones previas, indexados por similitud semántica.
    • Para qué sirve: recuperar contexto relevante que trasciende sesiones (preferencias, infraestructuras, políticas).
    • Implementación típica: bases de datos vectoriales (pgvector sobre PostgreSQL es una opción pragmática: pgvector).
    • Patrón habitual: RAG aplicado a la memoria del agente —consulta embeddings antes de ensamblar el prompt.

    No son intercambiables: la episódica responde “qué pasó ahora”, la semántica responde “qué deberías saber de antes”.

    El run loop correcto (práctico y reproducible)

    Antes de permitir que el agente invoque una tool, sigue este flujo:

    1. Embeddiza el input del usuario y consulta la memoria semántica (top-k).
    2. Recupera los últimos N eventos de la memoria episódica.
    3. Ensambla el prompt: instrucciones base + hechos semánticos verificados + resumen episódico + herramientas disponibles.
    4. Envía al modelo; si decide llamar una tool, valida los argumentos con un esquema antes de ejecutar.
    5. Persiste la decisión y el resultado en la memoria episódica.
    6. Si la llamada falla, serializa el error (estructura ZodError o equivalente) y úsalo para autocorrección o para encolar revisión humana.

    sem = searchSemanticMemory(userVector)
    epi = loadEpisodic(sessionId, N)
    context = buildContext(systemPrompt, sem, epi, toolsMeta)
    decision = model.decide(context)
    if decision.tool → validate → execute → saveEpisodic(result)

    Ese orden evita que las herramientas actúen en el vacío.

    Por qué no basta con ventanas de contexto gigantes

    Modelos con contexto masivo (Gemini, Claude) tienta a inyectar todo el historial en cada petición. En teoría funciona; en producción falla por tres razones:

    • Latencia (TTFT): enviar 100k–1M tokens degrada la experiencia.
    • Coste: procesar historial enorme en cada request sale caro.
    • Precisión: la atención se degrada cuando la información clave está enterrada (lost in the middle). Ver discusión técnica.

    La memoria estructurada filtra, prioriza y entrega solo lo relevante; es más eficiente, auditable y económico.

    Operacionalidad: métricas y señales que importan

    Mide para poder decidir:

    • tasa de validación fallida de herramientas (/% llamadas rechazadas por schema),
    • reintentos por fallo (media y P95),
    • latencia TTFT y costo por request (tokens consumidos),
    • porcentaje de decisiones que derivaron en intervención humana.

    Registra siempre: prompt construido, fragments recuperados, rawResponse del LLM, result de Zod (.error.flatten()), y la tool invocada. Sin trazabilidad no hay postmortem útil.

    Criterio para arquitectos y equipos

    Antes de añadir una nueva tool, pregúntate: si esa tool falla, ¿el agente tiene suficiente historia para entender por qué y recuperarse sin crear daño? Si la respuesta es no, diseña memoria. Empieza con episodic + semántic básico (Redis + pgvector), políticas de resumen y validación estricta de inputs. Solo entonces añade más herramientas.

    La memoria no es una mejora incremental: es la infraestructura que permite que las herramientas sean seguras, eficaces y escalables. Construir al revés es barato hoy y peligroso mañana. En el siguiente artículo veremos patrones de resumen semántico y cómo integrar autocorrección de argumentos usando errores estructurados (Zod) para convertir fallos en aprendizaje automático.

    Para equipos que trabajan con agentes y workflows, una referencia práctica y recursos adicionales están disponibles en Dominicode Labs. Considera esto como continuidad técnica: plantéalo si necesitas plantillas de run loops, patrones de memoria y ejemplos de validación de herramientas.

    FAQ

    ¿Qué diferencia práctica hay entre memoria episódica y memoria semántica?

    La memoria episódica guarda el historial reciente de la sesión (mensajes, decisiones, resultados) y sirve para coherencia conversacional y seguimiento de flujos multilargo. La memoria semántica guarda hechos persistentes indexados por similitud (preferencias, reglas, configuraciones) que se recuperan entre sesiones.

    ¿Por qué validar argumentos antes de ejecutar una herramienta?

    Validar previene ejecuciones incorrectas que consumen tokens, fallan o dañan datos en producción. Es la barrera que evita que inputs malformados o decisiones erróneas se conviertan en efectos adversos.

    ¿Qué métricas debo priorizar al operar agentes en producción?

    Tasa de validación fallida de herramientas, reintentos por fallo (media y P95), latencia TTFT, coste por request (tokens) y porcentaje de decisiones que derivaron en intervención humana. Registra también prompt construido, fragments recuperados y rawResponse del LLM para trazabilidad.

    ¿Es suficiente aumentar el contexto del modelo en cada petición?

    No. En producción esto aumenta latencia y coste, y puede degradar la precisión cuando información clave se pierde en un contexto enorme. La memoria estructurada entrega solo lo relevante de forma priorizada y auditable.

    ¿Qué hacer cuando una call a la tool falla repetidamente?

    Serializa el error (estructura ZodError o equivalente), persiste el fallo en memoria episódica, usa la información para autocorrección y, si es necesario, encola revisión humana. Registra detalles para postmortem.

    ¿Qué herramientas tecnológicas se recomiendan para empezar?

    Empezar con una memoria episódica en Redis y una memoria semántica en una base vectorial práctica como pgvector sobre PostgreSQL. Añade políticas de resumen y validación estricta de inputs antes de expandir herramientas.

  • Cómo redactar una spec efectiva para Claude Code

    Cómo redactar una spec efectiva para Claude Code

    Anatomía de una buena spec para Claude Code

    Tiempo estimado de lectura: 6 min

    • Una spec compacta y accionable evita suposiciones del agente y reduce iteraciones.
    • La estructura mínima: Requirements → Design → Tasks → Implementation.
    • Para bugs: seguir Report → Analyze → Fix → Verify.
    • Coloca SPEC.md junto al código y versiona la spec con el PR.

    Introducción

    Anatomía de una buena spec para Claude Code: si esperas que un agente genere código alineado con tu arquitectura, la spec es el mínimo imprescindible. Sin ella, Claude Code (o cualquier agente) hará suposiciones; con ella, ejecutará decisiones coherentes desde la primera iteración.

    Claude Code opera sobre repositorios y contexto local; el modelo subyacente (Claude) razona según la información que le entregues. Documenta la intención antes de pedir implementación y evitarás iteraciones costosas. Referencias útiles: Anthropic — Claude Code overview y Claude.

    Resumen rápido (lectores con prisa)

    Qué es: Una spec compacta y accionable que define comportamiento observable, diseño, tareas y criterios de aceptación para que Claude Code ejecute sin inventar.

    Cuándo usarla: Antes de pedir a un agente que implemente features o arregle bugs en un repositorio.

    Por qué importa: Minimiza suposiciones del agente, reduce iteraciones y evita parches superficiales.

    Cómo funciona: Estructura mínima: Requirements → Design → Tasks → Implementation; para bugs: Report → Analyze → Fix → Verify.

    Anatomía de una buena spec para Claude Code: estructura y propósito

    Una spec útil no es un tratado largo. Es un artefacto compacto y accionable, pensado para que un agente pueda ejecutar sin inventar. Su estructura mínima:

    1. Requirements → 2. Design → 3. Tasks → 4. Implementation

    Para bugs: Report → Analyze → Fix → Verify.

    Cada bloque reduce incertidumbre y acota el espacio de decisiones del agente.

    1. Requirements — qué debe hacer el sistema (externo)

    Define el comportamiento observable, no la implementación.

    Incluye:

    • Comportamiento nominal: qué hace la API/función.
    • Casos de borde: inputs nulos, límites, formatos erróneos.
    • Restricciones no funcionales: latencia p95 < 200 ms, tamaño máximo de payload 2 MB.
    • Dependencias permitidas/prohibidas.

    Ejemplo (sin spec vs con spec):

    Sin spec: “Crea endpoint para usuarios”.
    Con spec: “POST /users: recibe {email, name}. Valida email según RFC 5321. Inserta en PostgreSQL usando el ORM X. Devuelve 201 con {id, email, name} o 409 si email existe. No usar nuevas dependencias.”

    2. Design — cómo debe integrarse la solución (interno)

    Define firmas, modelos y patrones. Evita que el agente elija un estilo distinto al del repo.

    Incluye:

    • Firma de funciones/handlers (tipado).
    • Modelos DTO/Entity.
    • Patrones obligatorios (repositorio, servicios, inyección).
    • Efectos secundarios permitidos (logs, eventos, mutaciones).

    Plantilla mínima:

    Function: createUser(payload: CreateUserDto): Promise
    Models: CreateUserDto, UserDto, UserEntity (campos, tipos)
    Patterns: usar userRepository.insert, no acceso directo a SQL.

    3. Tasks — pasos atómicos y ordenados

    Desglosa el trabajo en tareas verificables. Un agente ejecuta mejor secuencias claras.

    Ejemplo de Tasks para feature nueva:

    1. Añadir CreateUserDto en src/models.
    2. Implementar userRepository.insert según patrón existente.
    3. Implementar handler POST /users con validación.
    4. Añadir tests unitarios (caso feliz, email duplicado, payload inválido).
    5. Actualizar documentación OpenAPI.

    Cada tarea debe producir un artefacto comprobable.

    4. Implementation — criterios de aceptación y pruebas

    Define qué significa “terminado”. No dependas solo de que compile o pase CI.

    Incluye:

    • Cobertura mínima (ej. 80% sobre módulo).
    • Tests obligatorios (unit + integración básica).
    • Requisitos de performance y seguridad.
    • Revisión arquitectónica (no introducir dependencias nuevas, mantener separaciones).

    Ejemplo: “Merge solo si tests pasan y cobertura del módulo ≥ 85%; latencia p95 < 200ms en test de integración local.”

    Flujo para bugs: Report → Analyze → Fix → Verify

    Para corrección de errores, no saltes al fix. Sigue este flujo:

    • Report: pasos reproducibles, logs, versión del commit.
    • Analyze: causa raíz documentada (por el agente o humano) con ubicación del código.
    • Fix: parche mínimo que restaure el contrato.
    • Verify: tests que confirmen el caso original y aseguren regresión negativa.

    Pedir “arregla X” sin Analyze genera parches superficiales que reaparecen.

    Ejemplos reales (comparativa rápida)

    Caso: validar emails

    Sin spec: agente instala validator.js y devuelve distinto comportamiento al estándar del proyecto.

    Con spec: “validateEmail(input: string): boolean — RFC 5321, rechaza dominios locales, no usar libs externas.” Resultado: implementación consistente y sin nuevas dependencias.

    Caso: feature auth token

    Sin spec: token store ad-hoc en memoria.

    Con spec: define AuthToken interface, TTL, almacenamiento en redis existente y tests. Resultado: integración correcta con infra existente.

    Práctica recomendada y colocación en repo

    • Coloca SPEC.md junto al test file o en la carpeta del feature.
    • Versiona la spec con el mismo PR.
    • Incluye ejemplos de I/O y criterios de aceptación textuales.
    • Si usas herramientas visuales, añade diagramas Mermaid (https://mermaid.js.org/) o contrato OpenAPI (https://spec.openapis.org/).

    Conclusión

    Claude Code puede automatizar implementaciones, pero su fidelidad depende de tu spec. La diferencia entre un parche plausible y una integración sostenible es específica: Requirements → Design → Tasks → Implementation para features; Report → Analyze → Fix → Verify para bugs. Escribe la spec antes de ejecutar al agente. Lo barato es ahorrar minutos ahora; lo caro es rehacer horas después.

    Dominicode Labs

    Si trabajas con automatización, agentes o workflows, considera recursos prácticos y experimentos en Dominicode Labs. Es una continuación lógica para explorar patrones operativos y plantillas de spec aplicables a pipelines de IA y automatización.

    FAQ

    Respuesta — ¿Qué debe contener la sección Requirements de la spec?

    Debe definir el comportamiento observable: casos nominales, bordes, restricciones no funcionales (p. ej. latencia, tamaño de payload) y dependencias permitidas o prohibidas.

    Respuesta — ¿Por qué es importante definir el Design explícitamente?

    Porque evita que el agente elija un estilo distinto al del repositorio. Definir firmas, modelos y patrones garantiza consistencia con la arquitectura existente.

    Respuesta — ¿Cómo se desglosan las Tasks de forma efectiva?

    Divídelas en pasos atómicos y ordenados que produzcan artefactos comprobables (archivos, tests, cambios en la API). Cada tarea debe ser verificable aisladamente.

    Respuesta — ¿Qué criterios deben incluirse en Implementation?

    Criterios de aceptación claros: cobertura mínima de tests, pruebas obligatorias (unit/integración), requisitos de performance y restricciones de seguridad o dependencias.

    Respuesta — ¿Cuál es el flujo recomendado para corregir bugs?

    Report (pasos reproducibles y logs) → Analyze (causa raíz y ubicación) → Fix (parche mínimo) → Verify (tests que confirmen y prevengan regresiones).

    Respuesta — ¿Dónde debo colocar la SPEC.md en el repo?

    Junto al test file o en la carpeta del feature. Versiona la spec en el mismo PR para mantener trazabilidad.