Category: AI

  • Arquitectura de subagentes IA: por qué falla el mega-prompt

    Arquitectura de subagentes IA: por qué falla el mega-prompt

    El año pasado construí un agente que pretendía ser el ingeniero de software definitivo. Fue mi primer intento serio de arquitectura de subagentes IA — y la forma en la que fallé me enseñó por qué un solo agente nunca debería hacerlo todo.

    Su System Prompt ocupaba casi 3.000 palabras. Le instruí para ser arquitecto de software, experto en seguridad, programador senior de TypeScript, tester meticuloso y redactor técnico. Además, le configuré 28 herramientas distintas: leer archivos, escribir código, ejecutar comandos bash, consultar 3 bases de datos y hacer peticiones HTTP.

    Al principio parecía impresionante. En la primera tarea sencilla respondió bien.

    Pero a la cuarta tarea compleja, el sistema colapsó por completo:

    • Confundía las reglas de testing con las de documentación.
    • Para cambiar una sola línea de CSS llamaba a herramientas de base de datos.
    • Consumía 100.000 tokens en cada paso solo leyendo la lista gigante de herramientas disponibles.

    Ese día entendí una verdad fundamental del desarrollo con IA: en lugar de construir un único agente que intente hacerlo todo, necesitas un equipo de subagentes con tareas concretas y contextos aislados.


    Por qué los Mega-Prompts fallan en la práctica

    No es una limitación de que el modelo "sea tonto"; es el resultado de cómo funcionan los Transformers:

    1. Degradación por ruido de herramientas (Tool Noise)

    Cuantas más herramientas (tools) le expones a un modelo en un único turno, mayor es la probabilidad de que elija la herramienta equivocada o invente parámetros incompatibles.

    Con pocas herramientas bien definidas, la precisión de selección se mantiene alta. Cuando la lista crece a decenas de herramientas mezclando responsabilidades distintas (leer archivos, escribir código, consultar bases de datos, hacer peticiones HTTP), esa precisión cae de forma abrupta — es el mismo problema que un desarrollador tendría memorizando 30 comandos de CLI casi idénticos.

    2. Dispersión de atención (Attention Drift)

    Si tu prompt contiene 50 reglas diferentes ("no uses any", "usa el prefijo on en eventos", "escribe tests en Vitest", "documenta en JSDoc"), el mecanismo de atención del LLM diluye la importancia de cada una.

    Cuando el contexto se llena de logs y código, las reglas del medio del prompt simplemente dejan de tener peso estadístico.

    3. Contaminación de contexto

    Si un agente pasa 20 minutos investigando archivos y leyendo logs de error, esos 80.000 tokens de "ruido exploratorio" se quedan atascados en la memoria para siempre.

    Cuando luego le pides que escriba la solución final, su respuesta estará condicionada por todo ese texto basura previo.


    La solución: Arquitectura de Subagentes en 3 capas

    La solución no es hacer prompts más largos ni añadir más mayúsculas al texto. Es aplicar el principio de Responsabilidad Única que llevamos décadas usando en ingeniería de software.

    En Dominicode organizamos el trabajo en tres tipos de subagentes especializados:

                      ┌───────────────────────┐
                      │    AGENTE ORQUESTADOR │
                      │  (Planifica y delega) │
                      └──────────┬────────────┘
                                 │
                ┌────────────────┼────────────────┐
                ▼                ▼                ▼
         ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
         │ RESEARCHER  │  │ IMPLEMENTER │  │  REVIEWER   │
         │ (Read-only) │  │(Write + TDD)│  │ (Auditoría) │
         └─────────────┘  └─────────────┘  └─────────────┘
    

    1. El Investigador (Researcher — Solo Lectura)

    • Herramientas permitidas: Búsqueda en archivos, lectura de código, búsqueda web.
    • Herramientas prohibidas: Edición de archivos, ejecución de comandos destructivos.
    • Misión: Explora el codebase, localiza las funciones relevantes y devuelve un resumen limpio de 50 líneas con los hallazgos. Su memoria sucia de 60.000 tokens se descarta al terminar; solo el resumen pasa al siguiente agente.

    2. El Implementador (Implementer — Escritura + TDD)

    • Herramientas permitidas: Edición precisa de archivos, ejecución de tests.
    • Misión: Recibe el resumen del Researcher y la especificación técnica. Su único objetivo es crear el test, escribir el código mínimo para pasarlo y verificar que compila. No pierde tiempo buscando archivos porque el Researcher ya le dio las rutas exactas.

    3. El Revisor (Reviewer — Auditor de Calidad)

    • Herramientas permitidas: Lectura de diffs de git, linter.
    • Misión: Revisa los cambios antes de hacer commit. Evalúa si se respetan los estándares de tipado, si hay regresiones de rendimiento y si se cumplió la especificación original.

    Cómo se comunican los subagentes sin saturar tokens: El patrón Artifact

    El error habitual al montar sistemas multiagente es hacer que el Agente A le hable al Agente B en un chat conversacional interminable ("Hola Agente B, ¿cómo estás? He encontrado esto…"). Eso gasta tokens en cortesías inútiles.

    El patrón más eficiente es la comunicación mediante artefactos en disco:

    1. El Researcher escribe sus hallazgos en un archivo local: scratch/research_findings.md.
    2. El Orchestrator lee ese archivo y lanza al Implementer pasándole únicamente la ruta del archivo.
    3. El Implementer ejecuta los cambios y escribe el resumen de modificaciones en scratch/changes_summary.md.

    Cada subagente arranca con una ventana de contexto limpia, consumiendo solo los tokens necesarios para su tarea concreta.

    Este flujo de trabajo desacoplado es el que explicamos a fondo en el curso Construye con IA: de la idea al producto con Claude Code, donde mostramos cómo estructurar entornos reales multiagente que no se degradan con el tiempo.


    Ejemplo práctico: Definiendo un subagente en TypeScript

    Si estás creando tus propios agentes con código propio, no necesitas frameworks gigantes. Puedes instanciar agentes especializados restringiendo las tools y el system prompt:

    import { generateText, tool, stepCountIs } from "ai";
    import { anthropic } from "@ai-sdk/anthropic";
    import { z } from "zod";
    
    // Agente especializado solo en investigación.
    // Modelo de ejemplo: sustituye por la versión vigente de Claude en tu build.
    export async function spawnResearcherAgent(query: string, codebaseDir: string) {
      const result = await generateText({
        model: anthropic("claude-sonnet-5"),
        system: `Eres un agente de investigación técnica de solo lectura.
        Tu objetivo es explorar el código en "${codebaseDir}", localizar las funciones clave
        y responder con un informe conciso. NUNCA propongas escribir código ni modificar archivos.`,
        prompt: `Investiga: ${query}`,
        tools: {
          searchFiles: tool({
            description: "Busca patrones en el repositorio",
            inputSchema: z.object({ pattern: z.string() }),
            execute: async ({ pattern }) => {
              // Lógica de búsqueda grep/ripgrep
              return { matches: ["src/auth/service.ts:45", "src/auth/jwt.ts:12"] };
            },
          }),
          readFile: tool({
            description: "Lee un archivo específico",
            inputSchema: z.object({ path: z.string() }),
            execute: async ({ path }) => Bun.file(path).text(),
          }),
        },
        stopWhen: stepCountIs(8),
      });
    
      return result.text; // Salida limpia lista para pasar al Implementador
    }
    

    Al limitar el rol a lectura y 2 herramientas, la tasa de error baja notablemente y el coste por ejecución se reduce al mínimo.


    Qué hacer hoy con tu proyecto

    Si tienes un archivo de prompt de 5 páginas o un agente que intenta resolver todo el ciclo de vida de tu software:

    1. Separa la lectura de la escritura: Crea un agente explorador con herramientas de solo lectura y un agente constructor que solo toque archivos cuando ya sabe exactamente qué cambiar.
    2. Usa especificaciones previas: Antes de lanzar a los subagentes, asegúrate de tener una base firme con Spec-Driven Development (SDD) para que ningún agente tenga que improvisar requisitos sobre la marcha.
    3. Pasa datos, no conversaciones: Haz que tus subagentes se comuniquen a través de archivos estructurados en lugar de historiales de chat kilométricos.

    En Dominicode Labs compartimos arquitecturas reales de subagentes que usamos a diario para automatizar la creación de cursos, la refactorización de código y el mantenimiento de proyectos en producción.

    Deja de pedirle milagros a un mega-prompt. Diseña un sistema de subagentes donde cada uno haga una sola cosa, pero la haga con precisión quirúrgica.

  • Context Drift: por qué tu agente se vuelve tonto en la iteración 15

    Context Drift: por qué tu agente se vuelve tonto en la iteración 15

    El agente empieza como un ingeniero senior.

    Analiza el problema con precisión quirúrgica. Propone una arquitectura impecable. Modifica los primeros módulos con código limpio y bien tipado.

    Pero llega la iteración 14.

    De repente olvida la regla que le pusiste en el primer mensaje. Inventa funciones auxiliares que ya existen. Borra código que él mismo escribió hace cinco minutos. Y para resolver un fallo de compilación, entra en pánico y sugiere reescribir la mitad del proyecto.

    No se ha vuelto tonto de golpe. Está sufriendo Context Drift: el mismo modelo, con las mismas instrucciones, deja de prestarles atención porque el historial las ha sepultado bajo miles de tokens de basura.

    Y aquí va lo importante: esto no se arregla con un modelo mejor ni con una ventana más grande. Se arregla en el bucle.


    Por qué el fallo aparece en la iteración 15 y no en la 3

    Que un modelo reparte su atención de forma desigual —mucha al principio del prompt, mucha al final, poca en el medio— ya lo conoces. Y si no, lo tienes explicado con detalle en Context Engineering: cómo estructurar la memoria de tus agentes de IA. Este post no va de eso.

    Va de lo que pasa cuando ese efecto se combina con un bucle que se ejecuta solo.

    En una conversación normal tú controlas lo que entra. En un agentic loop no: cada iteración inyecta la salida de una herramienta sin que nadie la lea. Y esas salidas son enormes. Un npm test, un git diff, un grep sobre un monorepo o un schema de base de datos rondan fácil los 4.000 tokens cada uno.

    Supón un agente con 2.000 tokens de system prompt y 3.000 de especificación. Eso es lo que de verdad tiene que respetar: 5.000 tokens fijos de instrucciones.

    Iteración Salidas de herramientas acumuladas Total en ventana Peso de tus instrucciones
    3 12.000 tokens 17.000 29 %
    8 32.000 tokens 37.000 14 %
    15 60.000 tokens 65.000 8 %

    Tus reglas no han desaparecido: siguen ahí, íntegras, en el token 1. Lo que ha cambiado es que ahora compiten contra sesenta mil tokens de ruido reciente que el modelo también considera relevante.

    [System prompt + spec: 5.000 tokens] ──► Alta atención
    
    [Log 1: salida de grep]        ──┐
    [Log 2: test runner completo]    ├── Zona de dilución
    [Log 3: schema SQL entero]      ──┘
    
    [Instrucción final] ──────────────────► Alta atención
    

    El 92 % de lo que el modelo está leyendo en la iteración 15 es material que ya no sirve para nada. Ahí es donde empieza a alucinar.


    Las 3 técnicas para eliminar el Context Drift

    1. Poda de salidas de herramientas

    Nunca devuelvas al contexto la salida completa de un comando.

    Si un test runner ejecutó 120 tests y falló uno, el agente necesita saber tres cosas:

    • El estado general (FAILED).
    • El nombre del test que falló.
    • Las 10 líneas relevantes del stack trace.

    Los otros 3.000 tokens de tests en verde son ruido puro: encarecen la factura y compiten por la atención del modelo. Poda en el tool handler, antes de que ese texto llegue al historial. No le pidas al modelo que lo ignore, porque no puede.

    Si quieres ver cuánto te está costando esto de verdad, en medir el consumo de tokens de un agente desgloso en qué se te van realmente.

    2. El patrón scratchpad: memoria en disco, no en el chat

    El peor sitio para guardar el estado de un proyecto es el historial del chat. Es volátil, crece sin control y no puedes consultarlo sin arrastrarlo entero.

    El patrón robusto es obligar al agente a mantener un archivo en disco —scratch/state.md— como única fuente de la verdad. En cada paso clave lo actualiza:

    • Tareas completadas.
    • Tareas pendientes.
    • Decisiones técnicas tomadas y descartadas.

    Cuando el contexto conversacional se ensucia, tiras la conversación entera y arrancas otra pidiéndole que lea ese archivo. El agente recupera todo su foco por unos cientos de tokens en vez de sesenta mil.

    Esta es la versión mínima y sin dependencias del asunto. Si necesitas memoria entre sesiones, perfiles de usuario o recuperación semántica, el terreno está mapeado en implementación de memoria en agentes de IA. Pero empieza por el archivo de texto: resuelve más de lo que parece.

    3. Compactación rodante del historial

    En lugar de acumular treinta mensajes, sustituye el tramo intermedio por un resumen sintético y conserva intactos el system prompt y los últimos turnos.

    Suena trivial, y tiene dos trampas que revientan la petición en producción: cortar un mensaje de resultado de herramienta separándolo de la llamada que lo produjo, y romper la alternancia de roles que exigen algunas APIs.

    export type Role = "system" | "user" | "assistant" | "tool";
    
    export interface AgentMessage {
      role: Role;
      content: string;
    }
    
    /**
     * Sustituye el tramo intermedio del historial por un resumen.
     * Preserva el system prompt y los ultimos `keepLastTurns` mensajes.
     */
    export function compactContext(
      messages: AgentMessage[],
      keepLastTurns = 6,
    ): AgentMessage[] {
      if (keepLastTurns < 1) {
        throw new RangeError("keepLastTurns debe ser >= 1");
      }
    
      const hasSystem = messages[0]?.role === "system";
      const head = hasSystem ? messages.slice(0, 1) : [];
      const body = hasSystem ? messages.slice(1) : messages;
    
      if (body.length <= keepLastTurns) return messages;
    
      // Un mensaje `tool` sin la llamada que lo genero es un 400 en la API.
      // Retrocedemos el corte hasta que deje de apuntar a un resultado huerfano.
      let cut = body.length - keepLastTurns;
      while (cut > 0 && body[cut].role === "tool") cut--;
    
      const middle = body.slice(0, cut);
      if (middle.length === 0) return messages;
    
      const summary: AgentMessage = {
        role: "user",
        content:
          `[RESUMEN DE PASOS ANTERIORES] Se ejecutaron ${middle.length} operaciones ` +
          `de inspeccion y validacion. El estado real esta en scratch/state.md; ` +
          `los mensajes siguientes son la fase activa.`,
      };
    
      return [...head, summary, ...body.slice(cut)];
    }
    

    Dos notas para llevarlo a producción:

    • Si tu proveedor exige alternancia estricta de roles, comprueba que body[cut] no sea otro mensaje de usuario. Si lo es, fusiona el resumen con él en vez de insertarlo aparte.
    • El resumen genérico es un punto de partida, no el final. En cuanto puedas, genera ese texto con una llamada barata a un modelo pequeño que resuma los pasos reales: qué se intentó, qué falló y por qué. Un resumen que dice "se ejecutaron 12 operaciones" evita la saturación, pero no conserva el aprendizaje.

    El coste oculto de compactar (y por qué no debes hacerlo cada turno)

    Aquí es donde mucha gente se pega el tiro en el pie.

    La caché de prompts de los proveedores funciona por prefijo: se reutiliza el principio del prompt mientras siga siendo idéntico. Cuando compactas, reescribes justo esa parte del historial, así que la petición siguiente se paga entera a precio completo, sin descuento de caché.

    Si compactas cada turno, pierdes más de lo que ahorras: tendrás menos tokens, pero pagados todos a tarifa plena y con la caché reconstruyéndose sin parar.

    La regla práctica que uso:

    1. Poda siempre, en cada llamada a herramienta. Eso no toca el prefijo cacheado, porque afecta a lo que todavía no ha entrado.
    2. Compacta por lotes, cuando cruzas un umbral (por ejemplo, el 60 % de la ventana) o cuando termina una fase completa de trabajo.
    3. Nunca compactes a mitad de una subtarea. Espera al cambio de fase: es cuando el resumen sale bien y cuando el corte de caché duele menos.

    Por qué Spec-Driven Development resuelve el 80 % del problema

    El Context Drift no es solo un problema de memoria; es un problema de ambigüedad inicial.

    Si no le das al agente una especificación cerrada en un spec.md, tiene que deducir qué hacer sobre la marcha. Y deducir cuesta tokens: exploración, preguntas, archivos abiertos por si acaso, callejones sin salida. Todo eso acaba en el historial y satura la ventana con material que ni siquiera hacía falta.

    Con Spec-Driven Development el alcance está delimitado desde el primer segundo: el agente sabe qué archivos puede tocar y qué queda fuera. Menos exploración es, literalmente, menos drift.

    Es el método que enseño en profundidad en el curso Construye con IA: de la idea al producto con Claude Code y que tienes explicado paso a paso en el libro de Spec-Driven Development.

    Y si además validas con esquemas de Zod todo lo que entra y sale de tus herramientas, cortas el otro vector de degradación: datos deformados que el agente arrastra durante veinte iteraciones sin que nadie los detecte. Cómo blindar esos contratos lo tienes en el curso de Zod para TypeScript.


    Lo que puedes aplicar hoy

    1. Poda los logs. Limita la salida de cada herramienta y resume los errores antes de inyectarlos.
    2. Externaliza la memoria. Guarda el progreso en un archivo en disco en lugar de confiar en el historial.
    3. Compacta por fases, no por turnos. Y mira el impacto en la caché antes de darlo por bueno.

    En Dominicode Labs diseñamos arquitecturas de agentes que ejecutan tareas de horas sin desviarse del objetivo.

    Tener una ventana de contexto gigante no es una excusa para ser descuidado con lo que metes dentro. La diferencia entre un prototipo frágil y un sistema agéntico de producción no está en el modelo: está en qué le dejas leer en la iteración 15.


    Preguntas frecuentes

    ¿Cada cuántas iteraciones conviene compactar el historial de un agente?

    No lo ates a un número de iteraciones, átalo a un umbral de ocupación de la ventana y a los cambios de fase. Compactar al cruzar el 60 % de la ventana, o al terminar una subtarea completa, funciona mejor que hacerlo cada N turnos: el resumen sale más limpio y no partes el trabajo por la mitad.

    ¿Compactar el contexto invalida la caché de prompts y encarece la factura?

    Sí. La caché funciona por prefijo idéntico, así que al reescribir el tramo intermedio del historial la siguiente petición se paga completa. Por eso conviene compactar por lotes en lugar de cada turno: podar la salida de las herramientas antes de que entren al historial reduce tokens sin tocar el prefijo ya cacheado.

    ¿Qué diferencia hay entre compactar el historial y reiniciar la conversación?

    Compactar conserva el hilo conversacional y sustituye lo antiguo por un resumen; reiniciar tira todo y arranca de cero leyendo el archivo de estado. Compactar es más fácil de aplicar en mitad de una tarea. Reiniciar limpia mejor, pero solo es viable si el estado real vive en disco y no en el chat.

    ¿Cómo distingo un context drift de un fallo del modelo?

    Por la reproducibilidad. Coge la petición que falló, arranca una conversación nueva con el system prompt, el estado actual y esa única instrucción, y vuelve a lanzarla. Si con el contexto limpio sale bien, no era el modelo: era el historial. Si falla igual, el problema está en tus instrucciones o en la propia tarea.

    ¿Sirve de algo esto si uso un modelo con ventana de un millón de tokens?

    Sirve más, no menos. La ventana grande solo amplía cuánta basura cabe antes de que la petición reviente, pero la atención se sigue diluyendo y el coste por llamada crece con todo lo que arrastras. Una ventana grande es margen de maniobra, no un sustituto de la gestión del contexto.


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

  • Test harness para agentes de IA: el banco de pruebas que te falta en CI

    Test harness para agentes de IA: el banco de pruebas que te falta en CI

    Nadie prueba un motor de avión montándolo en un aparato con pasajeros. Lo amarran a un banco de pruebas, le conectan sensores, le inducen fallos y miden qué aguanta. Si revienta, revienta en tierra.

    Con software tenemos el equivalente desde hace décadas y se llama test harness: el andamiaje que rodea al código bajo prueba, le inyecta entradas controladas y comprueba las salidas.

    Con agentes de IA, en cambio, la mayoría probamos en caliente. Lanzamos el agente contra una API real, miramos si el resultado "parece bien" y lo damos por bueno.

    El problema no es la pereza. Es que un agente rompe los tres supuestos sobre los que se construyó todo tu testing:

    • No es determinista: la misma entrada da salidas distintas.
    • Tiene efectos secundarios reales: escribe archivos, llama a APIs, toca bases de datos.
    • No tiene garantía de terminar: puede quedarse en bucle gastando dinero.

    Ya expliqué por qué un LLM por sí solo no es un producto y qué capas necesita alrededor para funcionar en producción. Este post va de la otra mitad del problema, la que casi nadie monta: el arnés que se ejecuta en CI, antes del deploy. Con código.


    Por qué un test unitario normal no sirve aquí

    Un test clásico es un contrato de tres líneas: preparas la entrada, ejecutas, comparas con el valor esperado.

    Con un agente, ese toEqual no existe. La respuesta correcta no es una cadena concreta, es cualquiera de un conjunto amplio de cadenas aceptables. Y si aun así escribes la aserción exacta, tendrás un test que pasa hoy y falla el martes sin que nadie haya tocado nada.

    De ahí sale la reacción habitual, que es la equivocada: dejar de testear el agente y testear solo las funciones puras que lo rodean. Los parsers, los formateadores, los validadores. Cosas que ya sabías hacer.

    Mientras tanto, lo que de verdad puede costarte dinero —el bucle, las llamadas a herramientas, el gasto— viaja a producción sin una sola comprobación.

    El arnés cambia la pregunta. En lugar de "¿ha respondido lo correcto?", que es un problema de evals, pregunta cosas que sí tienen respuesta binaria:

    • ¿Ha llamado a alguna herramienta que no tenía permitida?
    • ¿Se ha pasado del presupuesto de tokens que le di?
    • ¿Ha terminado dentro del tiempo límite?
    • ¿Ha intentado escribir fuera de su directorio temporal?
    • ¿Ha llamado 14 veces a la misma herramienta con los mismos argumentos?

    Eso son tests de verdad: deterministas, rápidos y rojos cuando algo se rompe.

       caso de prueba              TEST HARNESS                  veredicto
      ┌──────────────┐    ┌──────────────────────────────┐    ┌────────────┐
      │ entrada fija │───►│  tools falsas (sin red)      │───►│ PASS/FAIL  │
      │ estado fijo  │    │  presupuesto de tokens       │    │ trace.json │
      └──────────────┘    │  timeout + AbortSignal       │    └────────────┘
                          │  directorio efimero          │
                          └──────────────────────────────┘
    

    Las 3 piezas que hacen testeable a un agente

    1. Herramientas falsas, no red

    La regla es simple: en modo test, el agente no toca nada real. Ni base de datos, ni API de pagos, ni sistema de archivos fuera de un directorio temporal que destruyes al terminar.

    Y no basta con mockear la implementación. Hay que no exponer las herramientas no autorizadas: si el agente ve deleteUser en su lista de tools, tarde o temprano la llamará, y el error que quieres detectar en CI es precisamente ese. Un arnés que expone la herramienta y luego lanza una excepción llega tarde para razonar sobre el diseño, aunque salve los datos.

    Si necesitas ejecutar código generado de verdad —no simularlo—, ahí el aislamiento sube un nivel y toca contenedor: lo conté en Docker sandboxing para ejecutar código de IA de forma segura.

    2. Presupuesto de tokens y timeout que cortan de verdad

    Esta es la pieza que casi todo el mundo escribe mal.

    He visto docenas de arneses con un campo maxTokens en la configuración que no se comprueba en ningún sitio. Y timeouts implementados con Promise.race que devuelven el control al test pero dejan la ejecución corriendo por detrás, gastando tokens contra la API mientras el test ya ha dado verde.

    Un límite que no corta no es un límite: es un comentario.

    3. Traza reproducible

    El arnés graba cada paso: qué herramienta, con qué argumentos, cuánto tardó, cuánto costó. Un array de objetos serializado a JSON.

    Sirve para dos cosas. Para que un fallo en CI sea depurable sin volver a lanzar el agente. Y para escribir aserciones sobre el proceso, no sobre el texto final, que es donde está la señal útil: si el agente llegó al resultado correcto llamando siete veces a la misma consulta, eso es un bug aunque la salida sea perfecta.


    El arnés en TypeScript

    Vamos al código. Un arnés mínimo con presupuesto real, cancelación real y traza, sin dependencias más allá de Zod para validar los argumentos que el modelo envía a cada herramienta.

    Primero, los tipos y el registro de herramientas:

    import { z } from "zod";
    
    export interface HarnessConfig {
      maxTokens: number;
      timeoutMs: number;
      allowedTools: string[];
    }
    
    export interface TraceEntry {
      tool: string;
      args: unknown;
      durationMs: number;
      tokens: number;
    }
    
    /** Herramienta ya validada: el schema queda encapsulado dentro de `run`. */
    export interface HarnessTool {
      cost: number;
      run: (rawArgs: unknown) => Promise<unknown>;
    }
    
    export class BudgetExceededError extends Error {}
    
    /**
     * En el punto de definicion conservas el tipado completo del schema.
     * En el registro todas las tools comparten la misma firma, que es lo
     * que permite recorrerlas en bucle sin castings.
     */
    export function defineTool<S extends z.ZodType>(
      schema: S,
      cost: number,
      run: (args: z.infer<S>) => Promise<unknown>,
    ): HarnessTool {
      return { cost, run: (rawArgs) => run(schema.parse(rawArgs)) };
    }
    
    // Fixtures: nada de esto sale a la red.
    export const testTools: Record<string, HarnessTool> = {
      queryDatabase: defineTool(
        z.object({ table: z.string(), limit: z.number().max(100) }),
        320,
        async ({ table }) => ({ rows: [{ id: 1, table, name: "Fixture User" }] }),
      ),
      sendEmail: defineTool(
        z.object({ to: z.string().email(), body: z.string() }),
        90,
        async () => ({ delivered: true }),
      ),
    };
    

    Ahora el arnés. Fíjate en tres detalles: solo se construyen las herramientas permitidas, el presupuesto se comprueba antes de ejecutar cada llamada, y el temporizador se limpia siempre.

    export type HarnessStatus = "SUCCESS" | "TIMEOUT" | "BUDGET_EXCEEDED" | "FAILED";
    
    export interface HarnessResult {
      status: HarnessStatus;
      tokensUsed: number;
      durationMs: number;
      output: string | null;
      trace: TraceEntry[];
    }
    
    type ToolBox = Record<string, (args: unknown) => Promise<unknown>>;
    
    export async function runWithHarness(
      task: (tools: ToolBox, signal: AbortSignal) => Promise<string>,
      config: HarnessConfig,
    ): Promise<HarnessResult> {
      const startedAt = performance.now();
      const trace: TraceEntry[] = [];
      let tokensUsed = 0;
    
      // 1. Solo existen las tools autorizadas. El resto no se expone.
      const tools: ToolBox = {};
      for (const name of config.allowedTools) {
        const tool = testTools[name];
        // Un nombre desconocido es un error de configuracion del test: que reviente ya.
        if (!tool) throw new Error(`Tool desconocida en allowedTools: ${name}`);
    
        tools[name] = async (rawArgs: unknown) => {
          // 2. El presupuesto se comprueba ANTES de gastar.
          if (tokensUsed + tool.cost > config.maxTokens) {
            throw new BudgetExceededError(
              `Presupuesto agotado: ${tokensUsed} + ${tool.cost} > ${config.maxTokens}`,
            );
          }
          const t0 = performance.now();
          const result = await tool.run(rawArgs); // Zod valida dentro: si no cuadra, revienta
          tokensUsed += tool.cost;
          trace.push({
            tool: name,
            args: rawArgs,
            durationMs: Math.round(performance.now() - t0),
            tokens: tool.cost,
          });
          return result;
        };
      }
    
      // 3. Cancelacion real: la tarea recibe el signal y debe propagarlo al SDK.
      const controller = new AbortController();
      const timer = setTimeout(() => controller.abort(), config.timeoutMs);
    
      const finish = (status: HarnessStatus, output: string | null): HarnessResult => ({
        status,
        tokensUsed,
        durationMs: Math.round(performance.now() - startedAt),
        output,
        trace,
      });
    
      try {
        const output = await task(tools, controller.signal);
        return finish("SUCCESS", output);
      } catch (error) {
        if (controller.signal.aborted) return finish("TIMEOUT", null);
        if (error instanceof BudgetExceededError) return finish("BUDGET_EXCEEDED", null);
        return finish("FAILED", error instanceof Error ? error.message : String(error));
      } finally {
        clearTimeout(timer); // sin esto, el timer mantiene vivo el proceso al terminar
      }
    }
    

    Un aviso honesto sobre el punto 3: el AbortSignal solo cancela de verdad si tu tarea lo propaga al SDK del modelo y a cada fetch. Si lo ignoras, el arnés dará TIMEOUT y devolverá el control al test, pero la llamada seguirá viva por detrás y te la cobrarán igual. El signal no es decorativo: es el único mecanismo que corta el gasto.

    Y ahora sí, un test

    Con esto, probar el bucle del agente vuelve a ser testing normal:

    import { describe, expect, it } from "vitest";
    import { runWithHarness } from "./harness";
    
    describe("agente de facturación", () => {
      it("corta la ejecución al agotar el presupuesto", async () => {
        const result = await runWithHarness(
          async (tools) => {
            // Un agente en bucle: consulta la misma tabla sin parar.
            for (let i = 0; i < 20; i++) {
              await tools.queryDatabase({ table: "invoices", limit: 10 });
            }
            return "listo";
          },
          { maxTokens: 1_000, timeoutMs: 5_000, allowedTools: ["queryDatabase"] },
        );
    
        expect(result.status).toBe("BUDGET_EXCEEDED");
        expect(result.tokensUsed).toBeLessThanOrEqual(1_000);
        expect(result.trace).toHaveLength(3); // 3 × 320 = 960; la cuarta no cabe
      });
    
      it("no expone las herramientas fuera del allowlist", async () => {
        const result = await runWithHarness(
          async (tools) => {
            if ("sendEmail" in tools) return "PELIGRO: tool disponible";
            return "ok";
          },
          { maxTokens: 5_000, timeoutMs: 5_000, allowedTools: ["queryDatabase"] },
        );
    
        expect(result.output).toBe("ok");
      });
    });
    

    Deterministas, sin red, en milisegundos. Se pueden ejecutar en cada push sin pensar en la factura.

    Ese expect(result.trace).toHaveLength(3) es el tipo de aserción que solo puedes escribir si grabas la traza: comprueba el comportamiento del bucle, no el texto de salida.

    Si quieres afinar el diseño de tests y el aislamiento de dependencias externas —que es exactamente el músculo que necesitas aquí—, lo trabajo a fondo en el curso de Testing en Angular con Jest y Testing Library. Y el uso de Zod para validar los argumentos que envía el modelo, con transformaciones y errores tipados, lo tienes en el curso de Zod para TypeScript.


    Qué encaja arriba y qué encaja abajo

    Tres piezas que se confunden todo el rato y conviene separar:

    Pieza Cuándo corre Qué responde
    Test harness En CI, en cada push ¿Se sale de los límites, del allowlist o del tiempo?
    Evals Por lotes, con casos reales ¿La calidad de las respuestas sube o baja?
    Agentic harness En producción, en cada ejecución ¿Cómo lo mantengo controlado con usuarios reales?

    El arnés de pruebas es el más barato de los tres y el que casi nadie tiene. Cuestión de horas montarlo, y atrapa la clase de fallo que más caro sale.

    Sobre el reparto de trabajo entre los tests que escribes tú y los que genera el agente, ya hay un post entero: adopta TDD para implementar pruebas efectivas con agentes de IA. Y sobre por qué la spec y la arquitectura no bastan sin esta capa debajo, también. Este post es la parte que faltaba: el código.

    Si trabajas con Spec-Driven Development, el encaje es directo. Los límites que escribes en la sección de NFRs del spec.md —presupuesto, latencia, herramientas permitidas— dejan de ser un párrafo y pasan a ser los argumentos de HarnessConfig. La especificación se vuelve ejecutable, que es de lo que va el libro de Spec-Driven Development.


    Lo que puedes montar esta semana

    1. Una lista blanca de herramientas por entorno. Que en test solo existan las que necesita el caso.
    2. Un presupuesto que corte. Comprobado antes de cada llamada, no después. Si tu maxTokens no aparece en ningún if, no existe.
    3. Una traza en JSON por ejecución. Y al menos un test que asierte sobre ella, no sobre el texto de salida.

    En Dominicode Labs montamos este tipo de arneses sobre agentes que corren horas sin supervisión.

    Deja de probar tus motores en pleno vuelo. Amárralos al banco, súbeles la presión hasta que rompan y arréglalos en tierra, que es donde sale barato.


    Preguntas frecuentes

    ¿Cómo se testea algo que no es determinista?

    No asertando sobre el texto de salida, sino sobre el comportamiento observable: qué herramientas llamó, con qué argumentos, cuántas veces, cuánto gastó y si terminó a tiempo. Todo eso sí es determinista y da un rojo claro cuando se rompe. La calidad de la respuesta es otra disciplina y se mide por lotes, no en cada push.

    ¿El test harness sustituye a los mocks de toda la vida?

    No, los usa. La diferencia es el alcance: un mock reemplaza una dependencia concreta, mientras que el arnés controla el entorno completo de la ejecución —qué herramientas existen, cuánto puede gastar, cuánto puede tardar y qué queda grabado—. Un mock por sí solo no impide que el agente entre en bucle.

    ¿Hay que llamar al modelo real en estos tests?

    No en los que corren en cada push: se ejecuta el bucle del agente con respuestas fijas, y eso vale para verificar límites, allowlist y control de flujo. Las ejecuciones con modelo real cuestan dinero y tardan, así que van en un job aparte, programado y sobre un conjunto reducido de casos.

    ¿Qué hago si el timeout salta pero el agente sigue gastando dinero?

    Es que estás cortando en el sitio equivocado. Promise.race devuelve el control al test pero no cancela nada: hay que crear un AbortController, pasar su signal a la tarea y propagarlo al SDK del modelo y a cada fetch. Si el SDK que usas no acepta señal de cancelación, el único corte real es aislar la ejecución en un proceso o contenedor aparte y matarlo.

    ¿Merece la pena montarlo si mi agente solo lee datos?

    Sí, por el gasto y por los bucles. Un agente de solo lectura no borra nada, pero puede repetir la misma consulta cuarenta veces y facturarte la broma entera. El presupuesto y la traza detectan ese patrón en CI, que es donde cuesta cero arreglarlo.


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

  • Patrón ReAct en Agentes de IA: Guía del Bucle Thought-Action

    Patrón ReAct en Agentes de IA: Guía del Bucle Thought-Action

    Si le pides a un modelo de lenguaje que resuelva un problema complejo solo con texto, alucina. Si le das herramientas pero no lo dejas razonar, ejecuta acciones a ciegas y rompe tu base de datos.

    En 2022, un paper de investigadores de Princeton y Google (Yao et al.) demostró una verdad incómoda:

    Un LLM que solo "piensa" (Chain-of-Thought) vive desconectado del mundo real y acumula errores lógicos. Un LLM que solo "actúa" (Action-only) carece de plan y ejecuta llamadas a APIs sin evaluar si la respuesta previa tuvo sentido.

    La solución fue combinarlos en un ciclo continuo: ReAct (Reasoning + Acting).

    Cualquier agente moderno que uses hoy —desde Claude Code hasta un asistente con LangGraph o el SDK de OpenAI— desciende directamente de este patrón.

    Aquí te explico la mecánica interna del patrón ReAct en agentes de IA, cómo implementarlo en TypeScript puro sin librerías hinchadas y los tres fallos críticos que debes evitar en producción.


    Anatomía del patrón ReAct en agentes de IA: Thought → Action → Observation

    El patrón ReAct descompone la resolución de cualquier tarea en tres fases iterativas:

                      ┌───────────────────────────────┐
                      ▼                               │
              ┌───────────────┐                       │
              │   1. THOUGHT  │  Razona el estado     │
              └───────┬───────┘                       │
                      │                               │
                      ▼                               │
              ┌───────────────┐                       │
              │   2. ACTION   │  Ejecuta herramienta  │
              └───────┬───────┘                       │
                      │                               │
                      ▼                               │
              ┌───────────────┐                       │
              │ 3.OBSERVATION │  Recibe resultado     │
              └───────┬───────┘                       │
                      │                               │
                      └───────── ¿Finalizado? ────────┘
                                 │           │
                                [No]        [Sí]
                                             │
                                             ▼
                                       [Respuesta Final]
    
    1. Thought (Pensamiento): El modelo analiza el historial de la conversación, identifica qué sub-objetivo tiene pendiente y decide qué información le falta.
    2. Action (Acción): El modelo emite una llamada estructurada a una herramienta externa (una consulta SQL, un comando de terminal, una llamada HTTP o una búsqueda en vector store).
    3. Observation (Observación): El entorno ejecuta la herramienta y devuelve el resultado crudo al contexto del modelo.
    4. Evaluación: El modelo lee la observación, actualiza su pensamiento y decide si ya puede responder al usuario o si necesita otra iteración.

    Implementación mínima en TypeScript (sin frameworks)

    Para entender ReAct no necesitas instalar un framework de 40 dependencias. El núcleo del patrón es un bucle while que evalúa las llamadas a funciones devueltas por el LLM:

    import Anthropic from "@anthropic-ai/sdk";
    
    const client = new Anthropic();
    
    // 1. Definición de herramientas
    const tools: Anthropic.Tool[] = [{
      name: "search_database",
      description: "Busca registros de clientes por email",
      input_schema: {
        type: "object",
        properties: { email: { type: "string" } },
        required: ["email"]
      }
    }];
    
    // 2. Ejecutor de herramientas del entorno
    async function executeTool(name: string, input: any): Promise<string> {
      if (name === "search_database") {
        // Simulación de consulta real
        return JSON.stringify({ id: "usr_402", status: "active", plan: "pro" });
      }
      throw new Error(`Herramienta ${name} no encontrada`);
    }
    
    // 3. El bucle ReAct
    export async function runReActAgent(userGoal: string) {
      const messages: Anthropic.MessageParam[] = [
        { role: "user", content: userGoal }
      ];
    
      const MAX_ITERATIONS = 5;
      let iterations = 0;
    
      while (iterations < MAX_ITERATIONS) {
        iterations++;
    
        // Fase THOUGHT + ACTION (generación del modelo)
        const response = await client.messages.create({
          model: "claude-sonnet-5",
          max_tokens: 1024,
          tools,
          messages
        });
    
        // Si el modelo decide responder directamente, salimos
        if (response.stop_reason === "end_turn") {
          const finalReply = response.content.find(c => c.type === "text");
          return finalReply?.type === "text" ? finalReply.text : "";
        }
    
        // Si el modelo solicita ejecutar una herramienta (ACTION)
        if (response.stop_reason === "tool_use") {
          messages.push({ role: "assistant", content: response.content });
    
          const toolUse = response.content.find(c => c.type === "tool_use");
          if (toolUse && toolUse.type === "tool_use") {
            // Fase OBSERVATION (ejecución real en tu infraestructura)
            const observation = await executeTool(toolUse.name, toolUse.input);
    
            // Se inyecta la observación al historial para la siguiente vuelta
            messages.push({
              role: "user",
              content: [{
                type: "tool_result",
                tool_use_id: toolUse.id,
                content: observation
              }]
            });
          }
        }
      }
    
      throw new Error("El agente alcanzó el límite máximo de iteraciones sin resolver.");
    }
    

    Fíjate en lo que ocurre aquí: el LLM no tiene acceso a tu base de datos. El LLM solo emite la intención de consultar. Tu backend ejecuta la acción, captura la salida y se la devuelve como una Observation.


    ReAct con Tool Calling nativo vs. ReAct por texto plano

    En los primeros papers de 2022, el modelo generaba texto plano con prefijos:

    Thought: Necesito consultar el saldo del usuario con ID 102.
    Action: get_balance[102]
    Observation: 450.00 EUR
    Thought: El usuario tiene 450 euros. Ya puedo responder.
    Final Answer: Tu saldo disponible es de 450,00 €.
    

    Ese enfoque por texto requería expresiones regulares frágiles para parsear qué herramienta quería invocar el modelo.

    Hoy, los proveedores (Anthropic, OpenAI) integran Tool Calling nativo a nivel de tokens. El modelo garantiza JSON válido para los argumentos y separa el bloque de razonamiento del bloque de ejecución mediante bloques tipados (tool_use y tool_result). El principio conceptual sigue siendo 100% ReAct.


    Los 3 problemas del patrón ReAct en producción

    Cuando pasas de una demo a un sistema con usuarios reales, te enfrentas a tres trampas:

    1. Bucles infinitos (Infinite Loop Trap)

    Si una herramienta devuelve un error que el modelo no sabe interpretar (por ejemplo, 500 Internal Server Error), un agente ingenuo intentará llamar a la misma herramienta con los mismos parámetros una y otra vez.

    Solución: Límite estricto de iteraciones (MAX_ITERATIONS = 5-8) y formatear las excepciones dentro de la Observation explicando qué falló para que el modelo cambie de estrategia.

    2. Explosión del context window

    En cada ciclo, la Observation completa se añade al historial. Si una herramienta devuelve un JSON de 4.000 líneas con logs o registros de base de datos, el consumo de tokens y el coste se disparan exponencialmente en la siguiente iteración.

    Solución: Truncar, filtrar y resumir las salidas de las herramientas antes de entregárselas al modelo. Si quieres profundizar en cómo estructurar esa memoria sin que se dispare el contexto, lo cubrimos en context engineering: cómo estructurar la memoria de tus agentes de IA.

    3. Falta de determinismo en pipelines críticos

    ReAct es ideal para exploración y tareas no lineales. Pero si tu flujo de negocio tiene pasos rígidos (Paso 1: Validar DNI → Paso 2: Cobrar en Stripe → Paso 3: Enviar email), dejar que un agente ReAct decida el orden en tiempo real es una irresponsabilidad.

    Para procesos estructurados, la combinación óptima es un grafo determinista (como LangGraph o flujos guiados por especificaciones) donde ReAct se use solo dentro de nodos específicos que requieran adaptabilidad.

    Esta metodología de ingeniería de agentes con límites estrictos es el núcleo del curso Construye con IA: De la Idea al Producto con Claude y Specs, donde mostramos cómo gobernar agentes sin perder el control de la ejecución.


    ReAct vs. Plan-and-Solve: cuándo elegir cuál

    Criterio Patrón ReAct Patrón Plan-and-Solve
    Estrategia Decide el siguiente paso sobre la marcha tras cada observación Genera un plan completo de $N$ pasos antes de actuar
    Adaptabilidad Alta: Si un paso falla, recalcula inmediatamente Baja: Si el entorno cambia, el plan inicial queda obsoleto
    Latencia Mayor (un round-trip al LLM por cada herramienta) Menor (menos llamadas al modelo si el plan es estático)
    Caso de uso ideal Búsqueda interactiva, debugging, navegación web, soporte Generación de informes largos, migraciones batch

    Para patrones avanzados de observabilidad y control de memoria en arquitecturas de agentes, en Dominicode Labs construimos y testeamos implementaciones en producción semana a semana.


    Preguntas frecuentes

    ¿Qué significa ReAct en inteligencia artificial?

    ReAct es el acrónimo de Reasoning and Acting. Es un patrón de diseño para agentes de IA que combina el razonamiento paso a paso (Thought) con la ejecución de herramientas externas (Action) y la lectura de resultados (Observation).

    ¿ReAct es una librería o un concepto arquitectónico?

    Es un concepto arquitectónico. LangGraph tiene un factory prebuilt, create_react_agent (aunque ya está marcado como deprecado a favor de create_agent), y otros frameworks como CrewAI ofrecen sus propias implementaciones de referencia. Pero el patrón en sí no depende de ninguna librería: se implementa en cualquier lenguaje con llamadas nativas a la API del LLM.

    ¿Cuál es la diferencia entre Chain-of-Thought (CoT) y ReAct?

    Chain-of-Thought solo produce razonamiento interno sin interactuar con el entorno exterior, lo que suele derivar en alucinaciones cuando faltan datos. ReAct conecta ese razonamiento con herramientas externas reales (APIs, bases de datos, código).

    ¿Cómo evitar que un agente ReAct gaste demasiados tokens?

    Estableciendo un límite máximo de pasos por sesión, filtrando el contenido de las observaciones antes de inyectarlas al contexto y utilizando modelos más pequeños y rápidos para las llamadas intermedias. Antes de optimizar a ciegas, mide en qué parte del bucle se te va el presupuesto.


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

  • Certificación LangChain: Guía del Examen y Preparación

    Certificación LangChain: Guía del Examen y Preparación

    La semana pasada un desarrollador me preguntó si valía la pena pagar por la certificación oficial de LangChain para conseguir trabajo como AI Engineer.

    Mi respuesta fue directa: el badge digital en LinkedIn te abre la puerta de la entrevista con recursos humanos. Pero en la prueba técnica te van a pedir que depures un grafo cíclico en LangGraph o que arregles un desbordamiento de tokens en producción.

    Si intentas preparar el examen memorizando tutoriales de hace un año, vas a suspender.

    El ecosistema de LangChain ha cambiado de raíz: las cadenas monolíticas (LLMChain, ConversationalRetrievalChain) están formalmente obsoletas. La certificación oficial se llama LangChain Certified Agent Engineer (LCAE) y evalúa el ciclo de vida completo de un agente: construirlo con LCEL y LangGraph, probarlo y monitorizarlo con LangSmith, y desplegarlo en producción.

    Aquí tienes el desglose exacto de lo que entra, los errores que más suspensos provocan y la ruta de estudio para aprobar sin rodeos.


    Qué evalúa LangChain Certified Agent Engineer

    La certificación —oficial de LangChain Academy, no un curso con diploma de cortesía— valida que eres capaz de construir, probar, desplegar y monitorizar agentes de producción. Se organiza en cuatro dominios de peso idéntico, el ciclo de vida completo del agente (lo que LangChain llama ADLC, Agent Development Life Cycle):

            [ LANGCHAIN CERTIFIED AGENT ENGINEER ]
                            │
       ┌──────────┬─────────┼─────────┬──────────┐
       ▼          ▼         ▼         ▼
    Building   Testing   Deploying  Monitoring
     Agents     Agents     Agents     Agents
      25%        25%        25%        25%
    (LCEL,    (LangSmith  (LangGraph  (LangSmith
    LangGraph,  evals,     Platform,   tracing,
    RAG, Tool   datasets)  runtime,    coste,
    Calling)               versionado) latencia)
    

    Formato del examen:

    • Tipo de preguntas: 40 preguntas de opción múltiple, entregadas online con supervisión (proctored).
    • Duración: 120 minutos (~135 minutos contando el tiempo de la plataforma).
    • Puntuación para aprobar: 28 de 40 (70%).
    • Precio: $99 USD.
    • Validez: 24 meses.
    • Idioma: Inglés.
    • Requisito recomendado (no obligatorio): completar los cinco cursos gratuitos fundamentales de LangChain Academy antes de presentarte.

    Los 4 dominios del examen (y las técnicas que necesitas dominar)

    Para aprobar LangChain Certified Agent Engineer, tu preparación debe cubrir los cuatro dominios oficiales, con el mismo peso cada uno:

    Dominio 1: Building Agents (25%)

    Es donde más código nuevo vas a tocar, aunque en el examen pese lo mismo que los otros tres. Reúne LCEL, LangGraph, RAG y Tool Calling.

    LCEL (LangChain Expression Language) y Runnables. Columna vertebral de todo el código moderno de LangChain. Domina la composición con el operador pipe (|) y la interfaz Runnable:

    from langchain_core.runnables import RunnablePassthrough, RunnableParallel
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    
    # Composición declarativa con LCEL
    chain = (
        {"context": retriever, "question": RunnablePassthrough()}
        | prompt
        | model
        | StrOutputParser()
    )
    

    Conceptos clave: métodos estándar (.invoke(), .stream(), .batch(), .ainvoke(), .astream_events()), primitivas auxiliares (RunnableLambda, RunnableParallel, .bind(), .with_fallbacks(), .with_retry()) y streaming asíncrono granular con astream_events(version="v2").

    LangGraph: grafos de estado y agentes cíclicos. LangChain dejó atrás el concepto de "agentes lineales". Hoy los flujos complejos se modelan como grafos dirigidos con estado — si el concepto de grafo te resulta nuevo, cubrimos la base en qué es el graph engineering:

    from langgraph.graph import StateGraph, END
    from typing import TypedDict, Annotated
    import operator
    
    class AgentState(TypedDict):
        messages: Annotated[list, operator.add]
        next_step: str
    
    # Construcción del grafo
    workflow = StateGraph(AgentState)
    workflow.add_node("agent", call_model)
    workflow.add_node("tools", execute_tools)
    
    workflow.add_conditional_edges(
        "agent",
        should_continue,
        {"continue": "tools", "end": END}
    )
    

    Conceptos clave: esquemas de estado con TypedDict y reducers (operator.add), nodos (add_node), aristas fijas (add_edge) y condicionales (add_conditional_edges), persistencia con MemorySaver / SqliteSaver (Checkpointers), y control Human-in-the-loop con interrupt().

    RAG avanzado (Retrieval-Augmented Generation). No basta con saber qué es un VectorStore. Entran estrategias de Chunking (Recursive Character Text Splitter vs Semantic Chunking), búsqueda híbrida (BM25 léxico + Dense Embeddings vectorial vía Reciprocal Rank Fusion) y compresión contextual/Reranking (Cohere Rerank u otros).

    Tool Calling y validación de schemas. Integración de herramientas tipadas con @tool de langchain_core.tools, validación de argumentos con Pydantic (Python) o Zod (TypeScript), y manejo de excepciones con handle_tool_error.

    Dominio 2: Testing Agents (25%)

    Aquí LangSmith deja de ser una herramienta de debugging manual y se convierte en infraestructura de evaluación: creación de datasets de prueba, evaluadores automáticos (LLM-as-a-judge) y comparación de versiones de un agente contra un mismo dataset antes de promoverlo a producción.

    Dominio 3: Deploying Agents (25%)

    El dominio que más se salta la gente porque no hay un notebook bonito que enseñe esto. Cubre llevar el grafo de LangGraph a un runtime de producción (LangGraph Platform u otro hosting propio), gestionar configuración por entorno (variables, secretos, versión del grafo desplegado) y sustituir el checkpointer de desarrollo (SqliteSaver) por uno apto para producción con concurrencia real (por ejemplo, respaldado en Postgres).

    Dominio 4: Monitoring Agents (25%)

    LangSmith otra vez, pero mirando hacia atrás en vez de hacia adelante: trazas de ejecución en producción, latencia por nodo, consumo y coste de tokens, y detección de tendencias de calidad a partir de esas trazas. Activas el tracing con la variable de entorno LANGCHAIN_TRACING_V2=true. Si no tienes claro en qué parte del pipeline se te va el presupuesto, empieza por medir el consumo de tokens de un agente.


    Los 4 errores que hacen suspender a los desarrolladores

    1. Estudiar código de LangChain v0.0.x / v0.1.x: Si en tus prácticas ves from langchain.chains import LLMChain o initialize_agent, estás usando sintaxis deprecada. El examen exige langchain_core y langgraph.
    2. Ignorar el flujo de Streaming: Confundir .stream() con .astream_events() o no saber cómo extraer tokens en tiempo real dentro de un grafo.
    3. No practicar con Checkpointers: Suspender preguntas sobre cómo recuperar el estado anterior de un hilo (thread_id) en LangGraph.
    4. Estudiar solo "Building" e ignorar Deploying y Monitoring: Entre los dos suman la mitad del examen, y son los dominios donde menos contenido gratuito hay. Si tu preparación se queda en notebooks locales con SQLite, vas justo de la mitad de las preguntas.

    Esta disciplina de no depender de tutoriales viejos y estructurar el software antes de escribir código es lo que trabajamos a fondo en el curso Construye con IA: De la Idea al Producto con Claude y Specs, donde aprendes a gobernar agentes con especificaciones formales.


    Plan de estudio de 4 semanas

    Si le dedicas entre 6 y 8 horas semanales, este es el calendario recomendado:

    Semana Dominio Foco de estudio Práctica obligatoria
    Semana 1 Building Agents (I) LCEL y Runnables Construir 3 pipelines usando RunnableParallel, .bind() y .with_fallbacks()
    Semana 2 Building Agents (II) LangGraph, RAG y Tool Calling Montar un agente con ciclo de reflexión, Hybrid Search con Reranking y tool calling
    Semana 3 Testing + Deploying Evals con LangSmith y despliegue en LangGraph Platform Crear un dataset de evaluación, correr LLM-as-a-judge y desplegar el agente con un checkpointer de producción
    Semana 4 Monitoring + Simulacros Trazas, coste y latencia en LangSmith Configurar tracing en producción y hacer tests tipo examen (40 preguntas, 120 minutos)

    ¿Vale la pena obtener la certificación?

    Si eres freelance, consultor técnico o trabajas en una agencia que vende proyectos de IA a clientes corporativos, . En licitaciones y contratos B2B, las certificaciones oficiales son un filtro de confianza rápido.

    Si eres un desarrollador de producto en una startup, la certificación es secundaria: lo que te dará el puesto es tu capacidad para demostrar proyectos en producción con arquitecturas limpias, costes controlados y cero alucinaciones críticas.

    Para ver casos de estudio reales de agentes y arquitecturas con grafos en producción, en Dominicode Labs documentamos experimentos y benchmarks semanales con el ecosistema de IA.


    Preguntas frecuentes

    ¿En qué idioma se presenta el examen de LangChain Certified Agent Engineer?

    El examen se entrega íntegramente en inglés. Los fragmentos de código de las preguntas son mayoritariamente Python, que es donde LangChain y LangGraph tienen más ejemplos y documentación oficial.

    ¿Es necesario saber LangGraph para aprobar la certificación?

    Sí. LangGraph es hoy el componente central del examen para todo lo relativo a agentes, bucles de decisión, memoria conversacional y workflows multi-agente.

    ¿Cuánto tiempo dura la validez del certificado?

    24 meses (2 años) desde la fecha en que apruebas. Pasado ese plazo hay que recertificar, algo razonable dada la velocidad a la que cambian LangChain y LangGraph.

    ¿Cuánto cuesta la certificación LangChain Certified Agent Engineer?

    $99 USD por intento. El examen tiene 40 preguntas de opción múltiple, dura 120 minutos y necesitas acertar 28 de 40 (70%) para aprobar.

    ¿Dónde puedo practicar antes de presentarme?

    La mejor preparación es la documentación oficial de LangChain (v0.3+), los tutoriales de LangGraph Academy y la creación de trazas reales en LangSmith.


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

  • OpenSpec y Claude Code: integración paso a paso del flujo OPSX

    OpenSpec y Claude Code: integración paso a paso del flujo OPSX

    El lunes le pedí a Claude Code que añadiera paginación a un listado. Lo hizo bien.

    El miércoles abrí una sesión nueva en el mismo proyecto y le pedí un filtro. Se inventó otra forma de paginar, distinta a la del lunes, y reescribió la que ya funcionaba.

    No fue culpa del modelo. El contexto de Claude Code vive en la sesión: cierras la terminal y se evapora.

    OpenSpec con Claude Code resuelve eso: lo acordado vive en archivos versionados dentro del repo y el agente los lee antes de tocar código.

    Tutorial de integración OpenSpec Claude Code, paso a paso: instalación, inicialización y el flujo OPSX de principio a fin.


    Aviso rápido: OpenSpec no es OpenAPI

    Comparten cuatro letras y nada más.

    OpenSpec es un framework open source de spec-driven development para asistentes de código, de Fission-AI. No describe endpoints REST. Si has llegado buscando Swagger, este no es tu post.

    Lo que hace es meter una capa de especificación entre tú y el agente: propuesta, diseño, tareas y spec del cambio. Todo en Markdown, todo dentro del repo, todo bajo control de versiones.


    Paso 1: instalar (y el error de scope que arrastran los tutoriales viejos)

    npm install -g @fission-ai/openspec@latest
    

    Fíjate bien en el scope, porque esto:

    # ❌ NO es OpenSpec
    npm install -g openspec
    

    instala otro paquete distinto, sin relación con el framework. Es el fallo más repetido en tutoriales de hace unos meses, y luego pasas media hora preguntándote por qué openspec init no hace lo que dice la documentación.

    Instala siempre el paquete con scope @fission-ai/.


    Paso 2: inicializar OpenSpec en Claude Code

    Desde la raíz del repo:

    cd tu-proyecto
    openspec init
    

    El init te pregunta qué herramienta usas. Selecciona Claude Code.

    Y aquí el detalle que casi nadie explica bien: para Claude Code te crea las dos cosas.

    .claude/skills/openspec-*/SKILL.md    ← una skill por cada acción del flujo
    .claude/commands/opsx/<id>.md         ← los slash commands
    openspec/config.yaml                  ← la configuración del proyecto
    

    Las skills las carga Claude Code solo, sin que tú hagas nada. Los comandos son la puerta de entrada manual cuando quieres disparar una fase concreta. No eliges entre unas y otros: conviven.

    El config.yaml guarda además tus preferencias entre ejecuciones de init y update. Si mañana actualizas OpenSpec, no te vuelve a preguntar todo.


    Paso 3: llena el config.yaml antes de pedir nada

    Este paso parece opcional. No lo es.

    openspec/config.yaml no es un README que el agente abre si le apetece. Su contenido se inyecta en cada petición de planificación. Va dentro del prompt, siempre.

    Dedica cinco minutos a describir de verdad tres cosas: el stack real con sus versiones, las convenciones que sigues (naming, estructura de carpetas, patrón de tests) y lo que está prohibido en el proyecto — esa librería que ya migraste, ese patrón que odias.

    La diferencia se nota en la primera propuesta. Con el config vacío recibes una propuesta genérica de manual. Con el config bien puesto recibes una que usa tus carpetas, tus nombres y tu forma de testear.

    Un apunte honesto: no inventes claves en el YAML. Completa las que el propio init deja generadas y, si necesitas un campo que no existe, mira la doc oficial.

    Es la misma lógica que trabajamos en el curso Construye con IA: el resultado de un agente depende mucho menos del prompt del momento que del contexto estable que le dejaste montado antes.


    Paso 4: el flujo OPSX de principio a fin

    En Claude Code los comandos van con dos puntos: /opsx:<id>. Este detalle importa y ahora verás por qué.

    /opsx:explore — pensar sin comprometerte

    /opsx:explore
    

    Fase de planificación pura. Exploras el problema, discutes enfoques, descartas caminos. No genera artefactos ni te ata a nada.

    Es el comando que más se omite en los tutoriales y el que más rentabilidad da. Cuando saltas directo a propose, el agente propone algo — y lo propone bien argumentado, con lo cual te lo crees. En explore es donde descubres que el problema real era otro, antes de tener cuatro archivos que revisar.

    /opsx:propose — generar la propuesta

    /opsx:propose añadir filtros por categoría al listado de productos
    

    Aquí se materializa el trabajo:

    openspec/changes/<nombre-del-cambio>/
    ├── proposal.md    ← qué se va a hacer y por qué
    ├── design.md      ← cómo, a nivel técnico
    ├── tasks.md       ← el desglose ejecutable
    └── specs/         ← la delta spec del cambio
    

    Y ahora tu parte: leerlo. Este es el punto exacto donde el flujo funciona o no funciona. Corriges asunciones, ajustas el diseño, partes tareas demasiado grandes. Cuesta minutos ahora y ahorra horas después.

    /opsx:apply — implementar contra la spec

    /opsx:apply
    

    El agente implementa tarea por tarea, referenciando la spec acordada. La diferencia con pedirle código a pelo es que ya no hay margen de interpretación.

    /opsx:update y /opsx:sync — mantener la spec viva

    Antes de archivar, el perfil por defecto trae dos comandos más que casi nadie menciona: /opsx:update revisa los artefactos de un cambio si algo se movió a mitad de camino, y /opsx:sync fusiona la delta spec del cambio dentro de las specs generales del proyecto, para que la spec principal quede al día sin tocarla a mano.

    /opsx:archive — cerrar el cambio

    /opsx:archive
    

    Mueve el cambio a openspec/changes/archive/ y actualiza la fuente de verdad del proyecto. A partir de ahí eso ya no es un cambio pendiente: es cómo funciona tu sistema.

    El perfil extendido (y dónde vive /opsx:verify)

    El perfil por defecto (core) trae los seis comandos que acabas de ver: explore, propose, apply, update, sync y archive. Si necesitas control más granular, cambias de perfil:

    openspec config profile
    openspec update
    

    Eso desbloquea /opsx:new, /opsx:continue, /opsx:ff, /opsx:bulk-archive, /opsx:onboard y, el que más se echa en falta, /opsx:verify: contrasta la implementación contra la spec acordada. No es un test runner, es la comprobación de que no se coló nada que nadie pidió y que no falta nada que sí se pidió.

    Empieza sin el perfil extendido. Actívalo cuando el ciclo base (explore → propose → apply → archive) te sepa corto.


    Delta specs: por qué esto sirve en un proyecto que ya existe

    Aquí está la decisión de diseño que hace a OpenSpec usable en el mundo real.

    La spec de un cambio no describe tu sistema entero. Describe solo lo que se mueve:

    ## ADDED Requirements
    
    ### Requirement: Filtrado por categoría
    El listado DEBE permitir filtrar productos por categoría.
    
    #### Scenario: Usuario selecciona una categoría
    - WHEN el usuario selecciona la categoría "Audio"
    - THEN el listado muestra solo productos de esa categoría
    
    ## MODIFIED Requirements
    
    ## REMOVED Requirements
    

    Escenarios en Markdown plano con sintaxis WHEN/THEN. Sin Gherkin, sin herramientas extra, sin plugins.

    Piensa en la alternativa: especificar entera una aplicación con tres años de historia para poder añadir un filtro. No lo hace nadie, y por eso la mayoría de intentos de SDD en brownfield mueren en la segunda semana. Con deltas, la unidad de trabajo es el cambio, no el sistema.

    Si quieres el marco completo detrás de esto — cómo se escribe una spec que un agente pueda ejecutar sin rellenar huecos por su cuenta — lo desarrollo en el libro de Spec-Driven Development. Y para el reverso de la moneda, ya escribí sobre por qué una spec falla con un agente de IA.


    La sintaxis cambia según la herramienta

    Dato práctico que ahorra confusión cuando copias comandos de un tutorial grabado con otro editor:

    Herramienta Sintaxis
    Claude Code /opsx:propose
    Cursor /opsx-propose
    GitHub Copilot /opsx-propose
    Amazon Q @opsx-propose
    Codex $openspec-propose

    Mismo flujo, distinto prefijo. Si el comando no autocompleta en tu editor, casi siempre es esto.


    Si vienes de un tutorial de hace unos meses

    El flujo pre-OPSX está muerto. Pasó de fases cerradas a acciones, y la traducción es esta:

    Antes Ahora
    /openspec:proposal /opsx:propose
    openspec/project.md openspec/config.yaml
    changes/active/ openspec/changes/

    Si tienes un proyecto con la estructura antigua, no lo migres a mano. Vuelve a ejecutar openspec init y deja que la herramienta reconstruya lo suyo.


    Qué hacer hoy con esto

    Abre un proyecto que ya tengas — uno real, con código feo dentro — y no empieces por una feature grande.

    Instala, ejecuta openspec init, dedica cinco minutos de verdad al config.yaml y lanza un /opsx:explore sobre el próximo cambio pequeño que tenías pendiente. Sigue hasta /opsx:archive. Media hora, un ciclo completo.

    Lo que vas a notar no es velocidad. Es que la siguiente sesión de Claude Code arranca sabiendo lo que se decidió en la anterior. Eso es lo que compras aquí.

    Dos avisos para terminar. Esto no sustituye a revisar el código: sustituye a discutir el mismo diseño tres veces. Y no todo cambio merece el ciclo completo — un fix de dos líneas no necesita una propuesta, y sobre eso escribí en cuándo NO usar spec-driven development.

    Si quieres ver este flujo aplicado a proyectos completos, con los casos donde se rompe y cómo se arregla, lo trabajamos dentro de Dominicode Labs.


    Preguntas frecuentes

    ¿OpenSpec es lo mismo que OpenAPI?

    No. OpenSpec es un framework open source de spec-driven development para asistentes de código, creado por Fission-AI. OpenAPI es una especificación para describir APIs REST. Comparten cuatro letras y nada más.

    ¿Por qué mi instalación de OpenSpec no funciona?

    Lo más probable es que hayas instalado el paquete equivocado. El comando correcto es npm install -g @fission-ai/openspec@latest, con el scope @fission-ai. El paquete llamado openspec a secas es otro proyecto distinto, y es el error que arrastran muchos tutoriales antiguos.

    ¿OpenSpec sirve en un proyecto que ya existe o solo en proyectos nuevos?

    Sirve en proyectos existentes, y esa es su mejor característica. La spec de cada cambio es una delta: solo describe lo que se añade, se modifica o se elimina, con las secciones ADDED, MODIFIED y REMOVED Requirements. No necesitas especificar tu sistema entero para empezar.

    ¿Se puede usar OpenSpec con Cursor o Copilot en vez de Claude Code?

    Sí. El flujo es el mismo y lo que cambia es el prefijo de los comandos. Claude Code usa /opsx:propose con dos puntos, Cursor y Copilot usan /opsx-propose con guion, Amazon Q usa @opsx-propose y Codex usa $openspec-propose. Lo seleccionas al ejecutar openspec init.

    ¿Puedo saltarme el comando explore e ir directo a propose?

    Puedes, pero es donde más gente pierde tiempo. El comando explore es la fase de planificación sin compromiso y sirve para descartar enfoques antes de generar propuesta, diseño, tareas y spec. Si vas directo a propose, acabas revisando cuatro artefactos de una solución que quizá resuelve el problema equivocado.

    ¿Qué hago si seguí un tutorial con el flujo antiguo de OpenSpec?

    Ese flujo ya no es válido. El comando /openspec:proposal pasó a /opsx:propose, el archivo openspec/project.md pasó a openspec/config.yaml y la carpeta changes/active/ pasó a openspec/changes/. Lo más limpio es volver a ejecutar openspec init en el proyecto en lugar de renombrar archivos a mano.


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

  • Cómo Funciona el Watermarking de Claude a Nivel de API

    Cómo Funciona el Watermarking de Claude a Nivel de API

    La primera vez que alguien escucha que Claude mete una marca de agua en el texto que genera, se imagina un truco barato de esteganografía.

    Un espacio de ancho cero entre dos palabras. Un carácter Unicode invisible (\u200B). Un patrón binario escondido en los saltos de línea.

    Si fuera eso, un script de Python de dos líneas con un .replace('\u200b', '') o un regex básico destruiría la marca en tres milisegundos.

    Anthropic no ha implementado un truco de caracteres. Desde agosto de 2026, todos los modelos nuevos de Claude integran un watermarking a nivel de API de naturaleza puramente estadística. No hay caracteres ocultos, viaja en el texto plano al copiar y pegar, y no existe ningún parámetro o flag en la API para desactivarla.

    Si tu producto llama a la API de Claude, tus usuarios ya están recibiendo texto marcado, lo sepas o no. Aquí te explico el algoritmo que hay detrás, cómo se diferencia de los metadatos en archivos y qué implica de verdad si construyes sobre esa API — no como usuario ocasional de Claude Code, sino como quien tiene que responder por ello ante sus propios clientes.


    Cómo genera texto un LLM (el paso previo imprescindible)

    Para entender cómo se inserta una señal en un texto sin alterar una sola letra, primero hay que recordar qué hace el modelo en cada ciclo de inferencia.

    Un LLM no "elige una frase completa". Trabaja token a token. Cuando Claude va a generar la siguiente palabra, calcula una distribución de probabilidad sobre su vocabulario completo (los llamados logits, normalizados con una función softmax):

    software      → 28%
    aplicaciones  → 21%
    productos     → 17%
    sistemas      → 14%
    herramientas  → 11%
    otros...      →  9%
    

    En un muestreo normal con cierta temperatura, el modelo elige uno de los tokens más probables. El texto resultante es fluido, coherente y suena natural.

    Aquí es exactamente donde entra el algoritmo de watermarking.


    El algoritmo de Kirchenbauer: listas verdes y sesgo de logits

    Anthropic no ha publicado el mecanismo exacto que usa Claude. Lo único que confirma oficialmente es el principio: la marca usa una clave y las palabras previas para decidir qué palabra elige el modelo entre varias opciones semánticamente equivalentes, sin tocar la calidad del texto. Cita como referencias el paper de Kirchenbauer et al. y SynthID-Text de Google DeepMind, así que lo más razonable —y lo que asume la mayoría del análisis externo— es que Claude use una variante de esa familia de técnicas. Lo que sigue es cómo funciona ese enfoque en general, no una confirmación línea por línea de la implementación interna de Anthropic.

    La técnica estándar en la industria para marcar texto en LLMs no toca el texto después de generarlo. Modifica la probabilidad antes de muestrear.

    El proceso sigue, a grandes rasgos, estos pasos:

    Token anterior (t-1) 
           │
           ▼
    [ Hash + Clave Secreta de Anthropic ]
           │
           ▼
    Partición pseudo-aleatoria del vocabulario
      ├── Lista Verde (Green List) ~ 50%
      └── Lista Roja (Red List)    ~ 50%
           │
           ▼
    Se suma un delta (+δ) a los logits de la Lista Verde
           │
           ▼
    Muestreo del siguiente token (favorece sutilmente el verde)
    
    1. Generación de semilla: Al momento de predecir el siguiente token, el sistema calcula un hash criptográfico del token anterior (o de una ventana de los últimos tokens) combinado con una clave secreta que solo posee Anthropic.
    2. Partición del vocabulario: Ese hash divide pseudo-aleatoriamente todo el diccionario de tokens en dos grupos: una Lista Verde y una Lista Roja, típicamente al 50% cada una.
    3. Sesgo de logits (Logit Biasing): A los logits de los tokens que caen en la Lista Verde se les suma un valor constante positivo.
    4. Muestreo: El modelo muestrea el siguiente token aplicando la distribución con los logits alterados.

    ¿Qué ve un humano vs. qué ve un detector?

    • Un humano lee el texto y no nota nada extraño. Como el sesgo es moderado, el modelo sigue seleccionando palabras de alta probabilidad semántica. El significado, el estilo y la calidad no cambian.
    • Un detector con la clave (hoy, solo Anthropic; han anunciado que van a liberar una API de detección pero todavía no está disponible públicamente) tomaría el texto generado, reconstruiría para cada palabra si pertenecía a la Lista Verde o a la Lista Roja, y contaría cuántos tokens verdes aparecen.

    En un texto escrito por un humano o por un modelo sin marca, la probabilidad de que un token caiga en la lista verde es de alrededor del 50% (puro azar).

    En un texto marcado con este tipo de técnica, esa proporción sube de forma sistemática por encima del azar. Anthropic no ha publicado la cifra exacta para Claude; en la literatura académica sobre KGW/SynthID-Text el desplazamiento suele ser notable con relativamente pocos tokens.

    Con un párrafo de varios cientos de palabras, la probabilidad de que esa acumulación de tokens verdes ocurra por puro azar cae drásticamente. Esa es la base estadística del método, aunque los números concretos que aplica Anthropic a Claude no son públicos.


    Por qué en código fuente la marca es mucho más débil

    Aquí hay un detalle técnico crítico que casi nadie en redes sociales ha mencionado: el watermarking estadístico necesita entropía.

    La entropía mide la cantidad de opciones válidas que tiene el modelo para continuar una frase.

    • En prosa libre (alta entropía): Para decir "construimos sistemas robustos", el modelo puede elegir entre sistemas, aplicaciones, plataformas, soluciones o arquitecturas. Tiene margen de sobra para elegir un token de la Lista Verde sin romper la frase.
    • En código de programación (baja entropía): Si estás escribiendo TypeScript y tienes for (const item of, el siguiente token sintácticamente válido es casi con total seguridad el identificador del array o una estructura iterable. Si fuerzas un token de la lista verde que no encaja sintácticamente, el código no compila.
    // En sintaxis estricta, el espacio de tokens válidos es minúsculo:
    export interface UserSession {
      id: string;
      createdAt: Date;
    }
    

    Si el modelo reduce la temperatura o la sintaxis impone un único token válido, el sesgo de la lista verde no puede aplicarse sin destruir la corrección del programa.

    Por eso, en archivos de código (.ts, .py, .rs) la marca estadística es inherentemente más débil o casi indetectable en fragmentos cortos, mientras que en documentación, emails o artículos es donde más fuerte se fija. Y precisamente porque no puedes apoyarte en el watermark para saber si un fragmento de código viene de un agente, la validación real sigue siendo la de siempre: TDD potenciado por IA, validar el código antes de mergear.

    Esta es la misma disciplina de control y contexto que enseñamos en el curso Construye con IA: De la Idea al Producto con Claude y Specs: cuando entiendes cómo procesan los modelos la probabilidad de los tokens, dejas de tratar a los agentes como cajas mágicas y empiezas a diseñar sistemas predecibles.


    Texto vs. Archivos: la diferencia entre Watermark y C2PA

    Existe una confusión generalizada entre la marca en el texto y la marca en archivos multimedia. Anthropic usa dos tecnologías completamente distintas según el tipo de output:

    Característica Marca de agua en Texto Metadatos en Archivos (.png, .svg)
    Mecanismo Sesgo estadístico en logits (Kirchenbauer / SynthID) Estándar C2PA (Content Authenticity Initiative)
    Dónde vive En la frecuencia y secuencia de las palabras En la cabecera / bloque de metadatos del archivo
    Copiar y pegar Sobrevive (es el texto mismo) No aplica (es un archivo binario)
    Edición / Conversión Se degrada progresivamente si reescribes Se pierde si re-guardas o conviertes el archivo
    Verificación hoy Privada (solo Anthropic tiene la clave) Pública y verificable hoy con c2patool

    Cómo inspeccionar C2PA en tus archivos hoy mismo

    Si generas diagramas SVG o imágenes con Claude y las sirves desde tu backend, puedes auditar los metadatos C2PA en tu propia máquina con la herramienta oficial de la Content Authenticity Initiative:

    # Instalación del cli oficial de C2PA
    brew install c2patool
    # O descargar el binario desde github.com/contentauth/c2pa-rs/releases
    
    # Inspeccionar el manifiesto completo en JSON
    c2patool imagen.png
    
    # Ver solo el resumen de procedencia
    c2patool imagen.png --info
    

    Si el archivo proviene directamente de Claude, el manifiesto C2PA mostrará la firma criptográfica de emisión. Si lo abres en Photoshop, lo recortas y lo guardas como WebP, el contenedor C2PA desaparece a menos que tu software lo re-firme.


    Detector de IA ≠ Detector de Watermark

    Esta distinción te va a ahorrar dolores de cabeza con clientes y product managers cuando te pidan "añadir detección de IA":

    1. Un detector de IA tradicional (heurístico): Analiza perplejidad y ráfagas (burstiness). Intenta adivinar si el estilo parece robótico. Es impreciso, produce falsos positivos atroces con no-nativos en inglés y no sirve como prueba legal.
    2. Un detector de marca de agua: No evalúa estilo ni perplejidad. Aplica una clave criptográfica sobre la secuencia de tokens y comprueba una hipótesis matemática de probabilidad binomial.

    Hoy por hoy, no existe un detector público de watermark para Claude. Si ves una web que afirma "Pega tu texto y te digo si tiene la marca de Claude", es un detector heurístico genérico, no un verificador del watermark de Anthropic.

    Además, publicar un detector abierto crea un problema de seguridad: actúa como un oráculo de optimización. Cualquier usuario podría pasar su texto por un script que altere palabras una a una hasta que el verificador dé negativo, destruyendo la marca con el mínimo esfuerzo.


    Qué significa el watermarking de Claude si construyes sobre la API

    Si integras Claude en tu SaaS o en pipelines de desarrollo interno —repasa lo básico en Claude API: Crash Course para developers con TypeScript si aún no la usas a diario— hay cuatro realidades que debes asumir:

    1. No hay opt-out: No existe un header x-anthropic-disable-watermark: true. La directiva de la Unión Europea (EU AI Act, artículo 50) exige que los proveedores marquen las salidas de IA generativa.
    2. La marca demuestra procesamiento, no autoría única: Si un humano escribe un borrador y le pide a Claude que corrija la puntuación, el texto resultante puede quedar marcado. Anthropic lo aclara en su documentación: la marca indica que el texto pasó por el modelo, no que el humano no intervino.
    3. No prometas a tus clientes outputs "indetectables": Cualquier modelo de negocio basado en vender "artículos indetectables para SEO" o "ensayos indetectables" está técnicamente muerto a medio plazo frente a esquemas de marca estadística.
    4. La paráfrasis profunda degrada la señal: Anthropic mismo lo advierte: una edición ligera probablemente no elimina la marca, pero reescribir el texto a fondo —traducirlo, reordenarlo intensamente o editarlo a mano de forma sustancial— sí la destruye, porque rompe la alineación entre los tokens y la clave con la que se generaron.

    Para patrones avanzados de integración con LLMs y arquitecturas robustas en producción, en Dominicode Labs analizamos continuamente los cambios de la API de Anthropic y cómo adaptar nuestros proyectos.


    Preguntas frecuentes

    ¿Puedo desactivar la marca de agua en la API de Claude?

    No. Anthropic ha desplegado el sistema de watermarking a nivel de inferencia sin ningún parámetro de exclusión en la API ni en las cuentas Enterprise, alineándose con las normativas internacionales como el EU AI Act.

    ¿La marca de agua ralentiza la generación o encarece el coste de tokens?

    Anthropic no ha reportado ningún impacto. Por diseño, este tipo de watermarking solo sesga qué token se elige dentro de la misma distribución de probabilidad que el modelo ya calculaba: no añade tokens extra ni pasadas adicionales de inferencia, así que el coste computacional adicional es marginal.

    ¿Un detector de watermark puede acusarme falsamente de usar IA?

    Con textos largos, la probabilidad matemática de un falso positivo en un test de hipótesis tipo Kirchenbauer es extremadamente baja — es la base estadística del método, aunque Anthropic no ha publicado la tasa exacta para Claude. Aun así, es un mecanismo mucho más fiable que los detectores heurísticos habituales, que fallan con frecuencia.

    ¿Qué pasa si traduzco el texto generado por Claude a otro idioma?

    Si traduces el texto mediante otra herramienta o manualmente, la alineación de los tokens con la clave pseudo-aleatoria original se destruye y la marca de agua estadística deja de ser detectable.


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

  • TDD con IA: valida el código autogenerado antes de mergear

    TDD con IA: valida el código autogenerado antes de mergear

    Revisé una Pull Request generada por un asistente de IA hace un par de semanas.

    El autor de la PR estaba fascinado: "Mira qué limpio quedó el algoritmo de descuentos por volumen. La IA lo escribió en 15 segundos".

    El código tenía nombres impecables, comentarios en JSDoc y tipado de TypeScript sin un solo error. Le faltaba lo único que sostiene el TDD con IA: tests.

    Escribí una prueba unitaria pasando una compra con descuento de cliente VIP combinado con un cupón del 100%. El sistema devolvió un saldo negativo donde la tienda terminaba debiéndole dinero al comprador.

    El modelo de IA no tenía mala intención: simplemente no sabía qué reglas de negocio proteger porque nadie se las había formulado como una prueba ejecutable.

    En la era de los asistentes de código, Test-Driven Development (TDD) no está muerto; es más indispensable que nunca. Aquí tienes el flujo exacto para combinar TDD con IA.


    El nuevo ciclo Red-Green-Refactor con Agentes de Código

    El ciclo clásico de TDD se transforma radicalmente cuando tienes un agente a tu lado:

    ┌─────────────────────────────────────────────────────────────┐
    │                 FLUJO TDD POTENCIADO POR IA                 │
    │                                                             │
    │  1. HUMANO (Diseño)  ──>  Escribe el Test Unitario (ROJO)   │
    │                                   │                         │
    │                                   ▼                         │
    │  2. AGENTE (Código)  ──>  Genera la Implementación (VERDE)  │
    │                                   │                         │
    │                                   ▼                         │
    │  3. DÚO (Calidad)    ──>  Refactoriza con Seguridad         │
    └─────────────────────────────────────────────────────────────┘
    

    En lugar de delegar el diseño a ciegas, el desarrollador asume el rol de arquitecto: define el contrato y los casos de borde en una prueba. El agente asume el trabajo pesado de implementar la sintaxis.


    Ejemplo Práctico: Implementando una lógica de negocio paso a paso

    Paso 1: Escribe el test en fallo (Rojo con Vitest)

    Antes de crear el archivo de lógica, defines el comportamiento esperado:

    // src/pricing/discount-calculator.spec.ts
    import { describe, it, expect } from 'vitest';
    import { calculateTotalWithDiscounts } from './discount-calculator';
    
    describe('calculateTotalWithDiscounts', () => {
      it('aplica descuento por volumen del 10% en compras mayores a $100', () => {
        const total = calculateTotalWithDiscounts({ subtotal: 150, isVip: false, couponPercent: 0 });
        expect(total).toBe(135);
      });
    
      it('nunca devuelve un total negativo incluso con cupones acumulados', () => {
        const total = calculateTotalWithDiscounts({ subtotal: 50, isVip: true, couponPercent: 120 });
        expect(total).toBe(0); // Regla de negocio crítica
      });
    });
    

    Al ejecutar bun test, el test falla inmediatamente porque la función ni siquiera existe.

    Paso 2: Pasa el test al agente como contrato ejecutable

    Invocas a tu agente de IA en la terminal con una instrucción cerrada:

    claude "Lee discount-calculator.spec.ts. Crea el archivo discount-calculator.ts con la implementación mínima necesaria para que los tests pasen en verde. Prohibido modificar el archivo de tests."
    

    Paso 3: El agente genera el código para poner el test en verde

    El agente analiza la firma de tipos esperada y las aserciones, generando la lógica requerida:

    // src/pricing/discount-calculator.ts
    export interface PricingOptions {
      subtotal: number;
      isVip: boolean;
      couponPercent: number;
    }
    
    export function calculateTotalWithDiscounts(options: PricingOptions): number {
      const { subtotal, isVip, couponPercent } = options;
      
      let discount = 0;
      if (subtotal > 100) discount += subtotal * 0.10;
      if (isVip) discount += subtotal * 0.05;
      if (couponPercent > 0) discount += subtotal * (couponPercent / 100);
    
      const finalTotal = subtotal - discount;
      return Math.max(0, finalTotal); // Respeta el caso de borde
    }
    

    El agente ejecuta el test runner de forma autónoma y confirma que la suite está en verde.


    Por qué este flujo recorta los bugs que llegan a producción

    1. Elimina la alucinación de requisitos: El modelo no tiene margen para inventar parámetros porque el test ya definió la interfaz y los valores esperados.
    2. Aislamiento de contexto: No necesitas explicar la arquitectura completa de tu empresa; solo entregas el archivo de prueba.
    3. Refactorización sin miedo: Si mañana quieres optimizar el rendimiento del algoritmo, puedes pedirle a la IA que lo refactorice sabiendo que cualquier regresión encenderá una alarma roja de inmediato.

    Que quede claro: esto no lleva los bugs a cero. Ningún flujo lo hace. Lo que hace es mover el error de "se descubre en producción tres semanas después" a "se descubre en el segundo en que el agente ejecuta la suite". Los fallos que se te escapan siguen siendo los casos que no se te ocurrió escribir.

    Para que el ciclo funcione, la suite tiene que correr en milisegundos, no en minutos: aquí tienes cómo montar pruebas unitarias ultrarrápidas con Vitest. Si el agente tarda 90 segundos en saber si acertó, el bucle rojo-verde deja de ser un bucle.

    En el curso de Testing en Angular con Jest y Testing Library enseñamos a estructurar suites de pruebas profesionales para frontend y backend preparadas para integrarse con flujos automatizados de CI/CD.

    Este enfoque de validación es también uno de los pilares centrales de nuestro libro de Spec-Driven Development (SDD).

    Para descargar pipelines de automatización con Vitest y plantillas de pruebas para agentes, visita Dominicode Labs.


    Qué hacer hoy con esto

    Para la próxima función o endpoint que vayas a programar:

    1. No escribas la implementación.
    2. Escribe primero dos tests unitarios en Vitest: uno para el caso feliz y otro para el caso borde más peligroso.
    3. Pásaselo a tu asistente de IA y pídele que escriba la función que los cumpla.

    Comprobarás dos cosas: que el primer diff llega mucho más cerca de lo que querías, y que las rondas de corrección se reducen a una o dos.

    Si además quieres que el agente derive los tests de una especificación en vez de escribirlos tú a mano, ese es el siguiente escalón: TDD y Spec-First aplicados al desarrollo con IA.


    Preguntas frecuentes

    ¿Por qué TDD es especialmente útil al programar con IA?

    Porque un test unitario actúa como una especificación matemática ejecutable. Los modelos de lenguaje responden con muchísima mayor precisión cuando tienen un criterio binario de éxito (el test pasa o falla) que cuando reciben instrucciones en lenguaje natural ambiguo.

    ¿Se debe permitir que la IA modifique los tests unitarios?

    No. Los tests unitarios deben ser diseñados y aprobados por el desarrollador. Si permites que la IA modifique los tests para que "pasen en verde", corres el riesgo de que relaje las aserciones y oculte errores de negocio.

    ¿Qué framework de tests es más rápido para iterar con agentes de IA?

    Vitest es actualmente la opción más recomendada en el ecosistema TypeScript por su velocidad de arranque instantánea, compatibilidad nativa con ESM y excelente integración en terminales CLI.

    ¿Puede la IA escribir también los tests en lugar del desarrollador?

    Puede escribir el andamiaje y los casos evidentes, pero no debe decidir qué se protege. Si el modelo escribe los tests y la implementación, ambos comparten el mismo malentendido y la suite en verde no demuestra nada. El desarrollador define los casos de borde; la IA rellena el resto.

    ¿Cuántos tests hacen falta antes de pasarle la tarea al agente?

    Dos suelen bastar para arrancar: el caso feliz y el caso de borde más caro si falla. Con eso el agente ya tiene una interfaz cerrada y un criterio binario de éxito. Ampliar la cobertura tiene más sentido después, cuando ya sabes por dónde se rompe la implementación real.


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

  • 5 errores fatales al refactorizar código legacy con IA

    5 errores fatales al refactorizar código legacy con IA

    Hace unos meses me contrataron para modernizar un módulo de facturación escrito en 2019.

    Eran cerca de 3.000 líneas de TypeScript sin tipar, callbacks anidados y lógica de negocio repartida entre controladores y servicios. Pensé: "Le paso esto a un modelo de lenguaje moderno y en 10 minutos lo tengo convertido a funciones puras y tipadas".

    Refactorizar código legacy con IA parecía trivial. Le pedí al modelo que reescribiera el archivo y el resultado parecía una obra de arte: código limpio, nombres elegantes y cero warnings en el editor.

    Desplegamos en staging. A las dos horas saltó la primera alerta: los clientes con direcciones fiscales internacionales no podían facturar. El modelo había considerado que una comprobación con == null de un campo antiguo era "código redundante" y la había borrado, rompiendo seis años de retrocompatibilidad silenciosa.

    Si vas a meter agentes de IA en proyectos legacy, aquí tienes los 5 errores fatales que no puedes permitirte.


    Error 1: Alucinación de versiones y APIs incompatibles

    Los modelos de IA fueron entrenados con millones de repositorios que mezclan código de 2020 con código de 2026.

    Cuando le pides a una IA que modifique un proyecto antiguo de Node.js o Angular:

    • Asume que puedes usar métodos modernos de JavaScript (Array.prototype.toSorted(), Object.groupBy()) en entornos que corren en runtimes sin soporte.
    • Intenta importar métodos de librerías modernas (como rxjs/operators reubicados o versiones incompatibles de Axios).
    // ❌ Código sugerido por IA para un proyecto en Node 18
    const groupedOrders = Object.groupBy(orders, (item) => item.status);
    // En runtime: TypeError: Object.groupBy is not a function
    

    Cómo evitarlo: Especifica siempre el target exacto en tus prompts y configuraciones: "Target Node 18 LTS, ECMAScript 2022. Prohibido usar APIs de ECMAScript 2024+".

    El mecanismo de fondo lo desgrané en por qué la IA se inventa cosas: el modelo no miente, completa el patrón más probable. Y en un repo de 2019, el patrón más probable es el de 2026.


    Error 2: Pérdida silenciosa de contratos y el peligro del any encubierto

    El código legacy suele tener tipos implícitos o estructuras heterogéneas. Cuando la IA intenta "limpiar" esos tipos, con frecuencia toma atajos peligrosos:

    // Antes: código legacy feo, pero con un caso borde que lleva años en producción
    function parseUser(data: Record<string, unknown>) {
      return data.legacy_id ?? data.id;
    }
    
    // ❌ Refactor 'limpio' de la IA que destruye ese caso borde
    interface User { id: string; }
    function parseUser(data: User): User {
      return { id: data.id }; // Se perdió el soporte de legacy_id
    }
    

    La IA optimiza para la legibilidad del código presente, no para la historia oculta de los bugs pasados.


    Error 3: Refactorizar sin Tests de Caracterización previos

    El error más destructivo es pedirle a la IA que reescriba código antes de tener una red de seguridad.

    Si el código no tiene tests, no puedes refactorizar con IA. Punto.

    El protocolo correcto exige crear primero Characterization Tests (Tests de Caja Negra):

    // test/billing.characterization.spec.ts
    import { calculateInvoice } from '../src/legacy/billing';
    
    describe('Billing Legacy Characterization Tests', () => {
      it('preserva el comportamiento exacto para clientes extranjeros', () => {
        const input = { amount: 100, country: 'DE', taxExempt: true };
        const result = calculateInvoice(input);
        expect(result).toMatchSnapshot(); // Congela el comportamiento real antes de tocar nada
      });
    });
    

    Si la suite tarda minutos en correr, nadie la ejecutará antes de cada refactor. Aquí tienes cómo dejar los tests unitarios en milisegundos con Vitest: con IA de por medio, la velocidad del test runner deja de ser comodidad y pasa a ser el límite de tu ciclo de trabajo.

    En el curso de Testing en Angular con Jest y Testing Library dedicamos un módulo completo a blindar código histórico mediante tests de regresión antes de aplicar cualquier modernización.


    Error 4: Saturación y degradación de la ventana de contexto

    En repositorios con cientos de archivos interconectados, pasarle al agente archivos gigantes (1.000+ líneas) provoca pérdida de atención (lost in the middle).

    El agente empieza a ignorar imports cruciales o inventa interfaces auxiliares en lugar de reutilizar las del proyecto.

    Regla de oro: No pidas "refactoriza el módulo de pagos". Pide "extrae el cálculo de impuestos de este archivo a una función pura aislada y valida que el test adjunto siga en verde".


    Error 5: Aceptar Diffs extensos sin revisión granular

    Aceptar un diff de 400 líneas generado por IA sin revisarlo línea a línea es una negligencia profesional.

    ┌─────────────────────────────────────────────────────────────┐
    │                 PROTOCOLO DE REFACTOR CON IA                │
    │                                                             │
    │  1. Test de Caracterización (Fija el comportamiento)       │
    │  2. Spec Técnica (Define lo que se puede y no se puede tocar)│
    │  3. Refactorización atómica (Menos de 80 líneas por paso)  │
    │  4. Verificación de Test Runner en verde                   │
    └─────────────────────────────────────────────────────────────┘
    

    Este es exactamente el enfoque que explicamos en el libro de Spec-Driven Development (SDD): tratar las modificaciones de código como contratos medibles con límites inquebrantables.


    Qué hacer hoy con esto

    Si tienes que tocar un módulo legacy esta semana:

    1. No abras la IA todavía.
    2. Escribe tres tests que cubran los casos de uso principales y los casos de borde más raros que conozcas.
    3. Ejecuta los tests y asegúrate de que pasan.
    4. Solo entonces, entrega el código y los tests a tu agente de IA con la instrucción explícita de no romper la suite.

    Para acceder a checklists de refactorización segura y scripts de validación automática para proyectos empresariales, únete a Dominicode Labs.


    Preguntas frecuentes

    ¿Por qué la IA rompe código legacy que antes funcionaba?

    Porque los modelos de lenguaje intentan simplificar lo que parece "código redundante" sin entender los parches históricos o edge cases que ese código resolvía en producción.

    ¿Qué es un Characterization Test y por qué es indispensable?

    Es una prueba automatizada que captura el comportamiento actual del sistema (con sus virtudes y sus defectos) para garantizar que una refactorización no altere inadvertidamente el resultado final.

    ¿Cómo evitar que la IA use versiones incompatibles de librerías?

    Configurando un archivo de contexto claro (como CLAUDE.md o reglas de proyecto) donde se especifique la versión exacta de Node.js, TypeScript y el target ECMAScript soportado.

    ¿Cuánto código conviene pasarle a la IA en cada refactorización?

    Menos de lo que crees. Por debajo de 80 líneas por paso el diff se revisa entero en un vistazo y cualquier regresión se localiza de inmediato. Con diffs de 300 o 400 líneas nadie revisa de verdad: se aprueba por cansancio.

    ¿Se puede refactorizar código legacy con IA sin tests de ningún tipo?

    No de forma responsable. Si no hay tests, el primer trabajo del agente no es refactorizar sino generar tests de caracterización que congelen el comportamiento actual. Solo cuando esa red está en verde tiene sentido tocar la implementación.


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

  • Roadmap del developer con IA: de junior a fullstack agentic

    Roadmap del developer con IA: de junior a fullstack agentic

    En 2011 mi trabajo diario como programador consistía en memorizar la sintaxis de jQuery, lidiar con los bugs de Internet Explorer 8 y escribir bucles for a mano.

    Si en aquel momento alguien me hubiera dicho que 15 años después un modelo de lenguaje escribiría un algoritmo completo en dos segundos, habría pensado que la profesión de programador iba a desaparecer de la faz de la tierra.

    La realidad ha sido muy distinta, y por eso el roadmap del developer con IA no se parece en nada al de hace cinco años: programar no ha muerto, pero el acto de mecanografiar código se ha convertido en un commodity.

    El desarrollador que solo sabe traducir un ticket de Jira a líneas de JavaScript está en una posición vulnerable. En cambio, el perfil que está multiplicando su valor en el mercado es el Fullstack Agentic Developer: el profesional que diseña la arquitectura, establece los contratos y dirige un ejército de agentes de IA para construir productos en tiempo récord.

    Aquí tienes la hoja de ruta clara para hacer esa transición.


    La Evolución del Perfil: De Codificador a Director de Agentes

    ┌─────────────────────────────────────────────────────────────┐
    │               EVOLUCIÓN DEL ROL DE DEVELOPER                │
    │                                                             │
    │  AYER (Programador Tradicional)                            │
    │  [Escribir sintaxis] ──> [Recordar APIs] ──> [Debug manual] │
    │                                                             │
    │  HOY & MAÑANA (Agentic Developer)                           │
    │  [Diseñar Specs] ──> [Dirigir Agentes/MCP] ──> [TDD & CI]   │
    └─────────────────────────────────────────────────────────────┘
    

    La ventaja competitiva ya no es recordar de memoria los parámetros de un método de array. Tu valor reside en tu criterio técnico para decidir qué construir, con qué arquitectura y bajo qué límites de seguridad.


    Las 4 Habilidades Indispensables para los Próximos 3 Años

    1. Spec-Driven Development (SDD) y Diseño de Contratos

    Tu capacidad para redactar especificaciones técnicas precisas (spec.md) determinará la calidad del software que generen tus agentes. Si no sabes definir límites de dominio, flujos de datos y escenarios WHEN/THEN, los modelos alucinarán y perderás horas corrigiendo código basura.

    Es la habilidad con más retorno de las cuatro. Si empiezas hoy, empieza por aquí: Spec-Driven Development con agentes de IA.

    2. Fundamentos Sólidos de Arquitectura y Tipado Estricto

    Para evaluar si el código generado por un agente es seguro para producción necesitas dominar:

    • Clean Architecture y separación de capas (Domain, Application, Infrastructure).
    • TypeScript estricto, tipos discriminados y esquemas de validación en runtime con Zod.
    • Patrones de concurrencia y diseño de bases de datos relacionales.

    3. Testing Automatizado y Validación Determinista (TDD)

    La IA es probabilística; el software de producción debe ser determinista. La única forma de desplegar a producción sin miedo es contar con suites de pruebas automatizadas con Vitest o Jest que actúen como un guardián implacable ante cualquier regresión.

    4. Orquestación de Agentes, Herramientas y Protocolos MCP

    Aprender a conectar agentes CLI (como Claude Code) con bases de datos, APIs de terceros y herramientas de terminal mediante el Model Context Protocol (MCP) y la creación de custom skills (SKILL.md).

    El salto de nivel aquí está en dejar de escribir prompts y empezar a construir herramientas: cómo crear skills y subagentes personalizados para que tu flujo diario se ejecute solo.


    La Hoja de Ruta (Roadmap) Paso a Paso

    FASE 1: Fundamentos Modernos
    ├── TypeScript 5+ Estricto (Satisfies, Discriminated Unions, Generics)
    ├── Frameworks Reactivos (Angular Signals / Next.js 16 App Router)
    └── Validación de Esquemas con Zod
    
    FASE 2: Red de Seguridad y Metodología
    ├── TDD con Vitest / Jest (Escribir tests antes de generar código)
    └── Flujo SDD (Spec ➔ Plan ➔ Tasks versionados en Git)
    
    FASE 3: Operaciones Agénticas
    ├── Dominio de agentes CLI (Claude Code, Cursor, terminal tools)
    ├── Creación de Custom Skills y Subagentes especializados
    └── Conexión de servidores MCP para acceso a bases de datos y APIs
    
    FASE 4: Producción y Negocio
    ├── Despliegues Serverless, Edge Functions y bases de datos relacionales
    └── Entrega continua (CI/CD) con validación automática de agentes
    

    Para recorrer este camino con proyectos prácticos de extremo a extremo, en el curso Construye con IA: De la Idea al Producto con Claude Code te guiamos paso a paso en la transición hacia el desarrollo agentico.

    Si buscas una comunidad activa donde compartimos arquitecturas reales, plantillas de agentes y debates técnicos semanales, únete a Dominicode Labs.

    También puedes seguir todos nuestros tutoriales y directos gratuitos en el Canal de YouTube de Dominicode.


    Qué hacer hoy con esto

    Elige un proyecto personal o una tarea pequeña de tu trabajo.

    No intentes escribir cada línea de código a mano por nostalgia, ni tampoco le pidas a la IA que haga todo sin supervisión.

    Asume el rol de arquitecto: redacta la especificación, diseña los tests de validación y delega la implementación sintáctica a tu agente. Ese es el nuevo estándar de la ingeniería de software.


    Preguntas frecuentes

    ¿Los agentes de IA van a sustituir a los desarrolladores juniors?

    No van a sustituir a los desarrolladores, pero sí sustituirán el modelo tradicional de trabajo junior basado exclusivamente en escribir sintaxis básica. Los juniors que adopten metodologías estructuradas (SDD, TDD) y aprendan a orquestar agentes avanzarán mucho más rápido hacia niveles senior.

    ¿Por qué aprender TypeScript y testing si la IA puede escribir el código?

    Porque la IA comete errores sutiles y alucinaciones. Sin conocimientos profundos de TypeScript y testing automatizado, no tendrás el criterio técnico necesario para auditar el código generado ni detectar fallos antes de que lleguen a producción.

    ¿Qué es un Servidor MCP (Model Context Protocol)?

    Es un estándar abierto desarrollado por Anthropic que permite a los asistentes de IA conectarse de forma segura con herramientas locales, bases de datos (Postgres, SQLite), repositorios Git y APIs de servicios externos.

    ¿Cuánto se tarda en recorrer este roadmap?

    Depende del punto de partida, pero las cuatro fases no son secuenciales en el tiempo: puedes empezar a escribir specs y tests la semana que viene mientras sigues consolidando fundamentos. Lo que no funciona es saltar a la Fase 3 sin las dos primeras, porque sin criterio técnico no puedes auditar lo que el agente produce.

    ¿Hace falta ser senior para trabajar con agentes de código?

    No, pero sí hace falta saber leer código mejor de lo que lo escribes. Un junior que domina testing y sabe redactar una especificación clara saca más partido a un agente que un senior que le pide código a ciegas y acepta el diff sin revisarlo.


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