Tag: Context Engineering

  • 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.

  • Medir el consumo de tokens de un agente: en qué se te van

    Medir el consumo de tokens de un agente: en qué se te van

    El mes pasado revisé el agente de un cliente. Se comía unos 340 dólares al mes de API y el equipo quería bajarlo.

    Lo primero que hicieron fue lo que hace todo el mundo: recortar el system prompt. Lo dejaron en la mitad, de 1.800 tokens a 900.

    El ahorro real fue del 1,2% por llamada.

    No porque recortar el prompt sea mala idea. Porque nadie se había parado a medir el consumo de tokens antes de tocar nada. El system prompt era el 2,3% de cada petición. El 67% se lo comían los resultados de las herramientas, que nadie había mirado.

    Llevo meses viendo la misma escena. Hay una biblioteca entera de trucos para gastar menos —caching, recortes, modelos más baratos— y casi nadie tiene el paso previo: saber en qué se le van los tokens. Optimizan a ciegas y aciertan por casualidad.

    Este post es ese paso previo.

    Un agente no gasta tokens: reenvía tokens

    La confusión empieza aquí. La gente piensa en "el prompt" como si fuera una cosa que mandas una vez.

    En un agente, cada input que envías se reparte en cuatro bloques:

    1. System prompt. Tus instrucciones. Fijo, se manda igual en cada llamada.
    2. Definiciones de herramientas. Nombres, descripciones y JSON Schema de cada tool, más el system prompt interno que la API añade para habilitar tool use — en Claude Opus 5 son 286 tokens con tool_choice: auto, y 406 con any o tool. También fijo.
    3. Historial de la conversación. Todos los turnos previos: lo que dijo el usuario, lo que respondió el modelo, cada bloque tool_use que emitió.
    4. Resultados de herramientas. El contenido de cada tool_result: el JSON que devolvió tu API, el fichero que leyó, los 40 kB de HTML que trajo el scraper.

    Los dos primeros son constantes. Los dos últimos crecen.

    Y crecen de la peor manera posible, porque la Messages API no tiene estado. En el turno 12 no mandas el turno 12: mandas los turnos 1 a 12 otra vez, enteros, incluida la respuesta de 8.000 tokens que devolvió aquella herramienta en el turno 3 y que ya nadie va a volver a leer.

    Ahí está el efecto que casi nadie ve. Si cada turno añade d tokens al contexto, el total de una sesión de n turnos no crece con n, crece con n²/2. En cristiano: duplicar los turnos de tu agente no duplica el coste, lo multiplica por casi cuatro.

    Un ejemplo con números redondos. Base fija (system + tools) de 6.000 tokens, y cada turno añade otros 6.000 entre respuesta del modelo y resultado de herramienta. La columna de la derecha es el contenido único: el contexto que llega a ver el último turno, a 6.000 tokens por turno.

    Sesión Input acumulado Contenido único
    6 turnos 126.000 tokens 36.000 tokens
    12 turnos 468.000 tokens 72.000 tokens
    24 turnos 1.800.000 tokens 144.000 tokens

    En la sesión de 12 turnos pagas 468.000 tokens de input para procesar 72.000 tokens de material distinto. Seis veces y media. Con Claude Opus 5 a 5 $/millón de input, esa sesión te cuesta 2,34 dólares solo en entrada.

    Si tienes esto claro, ya sabes por qué a aquel equipo recortar el system prompt le ahorró un 1,2%.

    El desglose de un turno real

    Esto es lo que salió al medir el turno 12 de aquel agente de research:

    Componente Tokens % del input
    System prompt 1.800 2,3%
    Definiciones de herramientas (6 tools) 4.200 5,4%
    Historial de la conversación 19.400 24,9%
    Resultados de herramientas 52.600 67,4%
    Total input 78.000 100%

    Mira la última columna y haz las cuentas tú mismo.

    Partir el system prompt por la mitad ahorra 900 tokens: el 1,2% de la llamada. Meter un limit en la query de la base de datos y recortar un 40% los resultados de herramientas ahorra 21.040 tokens: el 27%.

    Mismo esfuerzo de ingeniería. Más de veinte veces más impacto.

    Este desglose no es universal, y ese es justo el punto. Un chatbot de soporte sin herramientas tiene el reparto invertido. Un agente de código con MCP servers cargados puede tener 30.000 tokens solo en definiciones de tools. Por eso el número que importa es el tuyo, no el mío.

    Cómo medir el consumo de tokens de un agente

    Medir el consumo de tokens de un agente es atribuir cada token de input a uno de los cuatro bloques que lo componen —system prompt, definiciones de herramientas, historial y resultados de herramientas— para saber qué porcentaje del gasto genera cada uno. La API de Anthropic da dos instrumentos para hacerlo: uno mira hacia atrás y otro mira hacia delante.

    1. El campo usage de cada respuesta

    Cada respuesta de la Messages API trae un objeto usage. Registra los cuatro campos, siempre, desde el primer día:

    import Anthropic from '@anthropic-ai/sdk';
    import type { MessageCreateParamsNonStreaming } from '@anthropic-ai/sdk/resources/messages';
    
    const client = new Anthropic();
    
    interface RegistroUso {
      ts: string;
      sesion: string;
      turno: number;
      input: number;
      output: number;
      cacheWrite: number;
      cacheRead: number;
    }
    
    const registro: RegistroUso[] = [];
    
    export async function llamada(
      params: MessageCreateParamsNonStreaming,
      meta: { sesion: string; turno: number },
    ) {
      const res = await client.messages.create(params);
      const u = res.usage;
    
      registro.push({
        ts: new Date().toISOString(),
        sesion: meta.sesion,
        turno: meta.turno,
        input: u.input_tokens,
        output: u.output_tokens,
        cacheWrite: u.cache_creation_input_tokens ?? 0,
        cacheRead: u.cache_read_input_tokens ?? 0,
      });
    
      return res;
    }
    

    Aquí hay una trampa que se traga a mucha gente. input_tokens no son todos los tokens que enviaste. Son solo los que van después del último punto de corte de caché. La documentación de Anthropic lo define así:

    total_input = cache_read_input_tokens + cache_creation_input_tokens + input_tokens
    

    Si activas prompt caching y sigues graficando input_tokens a secas, verás una caída espectacular que no significa nada. No has reducido el contexto: lo has movido de columna.

    El coste real se calcula con las tres columnas y sus precios respectivos. Para Claude Opus 5:

    const PRECIO_OPUS_5 = {
      input: 5 / 1_000_000,
      output: 25 / 1_000_000,
      cacheWrite5m: 6.25 / 1_000_000,
      cacheRead: 0.5 / 1_000_000,
    } as const;
    
    export const costeUSD = (r: RegistroUso) =>
      r.input * PRECIO_OPUS_5.input +
      r.output * PRECIO_OPUS_5.output +
      r.cacheWrite * PRECIO_OPUS_5.cacheWrite5m +
      r.cacheRead * PRECIO_OPUS_5.cacheRead;
    

    Precios verificados en la documentación de pricing de Anthropic en agosto de 2026. Si usas otro modelo, cambia la tabla — no los copies de un post de hace seis meses.

    2. El endpoint de conteo para atribuir por componente

    usage te da el total. No te dice cuánto pesa cada bloque. Para eso está /v1/messages/count_tokens, que en el SDK de TypeScript es client.messages.countTokens().

    Acepta los mismos bloques de entrada que messages.createmodel, system, tools, messages— y devuelve { input_tokens: number }. No acepta max_tokens. Es gratis y tiene su propio límite de peticiones, separado del de generación: 2.000 RPM en el tier Start.

    La técnica es medir por diferencias:

    import type { MessageParam, Tool } from '@anthropic-ai/sdk/resources/messages';
    
    // el mismo client del primer bloque
    const MODEL = 'claude-opus-5';
    const PING: MessageParam[] = [{ role: 'user', content: '.' }];
    
    const contar = async (p: {
      system?: string;
      tools?: Tool[];
      messages: MessageParam[];
    }) => (await client.messages.countTokens({ model: MODEL, ...p })).input_tokens;
    
    /** Sustituye el contenido de cada tool_result por un carácter. */
    function vaciarToolResults(messages: MessageParam[]): MessageParam[] {
      return messages.map((m) => {
        if (typeof m.content === 'string') return m;
        return {
          ...m,
          content: m.content.map((b) =>
            b.type === 'tool_result' ? { ...b, content: '.' } : b,
          ),
        };
      });
    }
    
    export async function desglosar(input: {
      system: string;
      tools: Tool[];
      messages: MessageParam[];
    }) {
      const [piso, conSystem, conTools, sinResultados, total] = await Promise.all([
        contar({ messages: PING }),
        contar({ system: input.system, messages: PING }),
        contar({ tools: input.tools, messages: PING }),
        contar({ ...input, messages: vaciarToolResults(input.messages) }),
        contar(input),
      ]);
    
      const system = conSystem - piso;
      const tools = conTools - piso;
      const toolResults = total - sinResultados;
    
      return {
        system,
        tools,
        toolResults,
        historial: total - system - tools - toolResults - piso,
        piso, // andamiaje fijo de la API: sin esta fila, las otras cuatro no suman el total
        total,
      };
    }
    

    Cinco llamadas en paralelo y tienes tu tabla. Lánzalo contra una conversación real serializada de producción, no contra un caso de prueba de tres turnos.

    Tres avisos. El conteo es una estimación y puede desviarse ligeramente del cobro real. El . con el que sustituyes cada tool_result cuenta como token, así que toolResults sale un pelín corto y esa diferencia se te va a historial. Y el endpoint responde con el tokenizador del model que le pases: los modelos desde Opus 4.7 usan uno nuevo que produce en torno a un 30% más de tokens para el mismo texto, así que un conteo hecho contra un modelo viejo no sirve para presupuestar uno nuevo. El idioma añade su propia variación sobre esa base: en español el mismo texto tokeniza un 26% más caro.

    Los cuatro pasos, en orden

    1. Serializa una conversación real de producción, de 10 turnos o más, a un objeto { system, tools, messages }.
    2. Registra el usage de cada llamada con la sesión y el turno como etiquetas.
    3. Pasa esa conversación por desglosar() y obtén los cuatro componentes.
    4. Ordena las filas por porcentaje y ataca solo la primera.

    Qué hacer con cada hallazgo

    Ya tienes el número. Ahora el diagnóstico. Cada fila de la tabla apunta a una intervención distinta:

    Si el bloque fijo (system + tools) es grande pero cache_read_input_tokens sale en cero, no tienes un problema de tamaño, tienes uno de caché. Estás pagando 5 $/millón por reprocesar en cada turno lo mismo que podrías estar leyendo a 0,50 $. Es la palanca con mejor ratio impacto/esfuerzo de esta lista y la expliqué entera en prompt caching en la API de Claude.

    Con los números de antes: de esos 468.000 tokens de input, con la caché refrescándose en cada turno solo 72.000 se escriben (a 6,25 $/millón) y los otros 396.000 se leen (a 0,50 $/millón). La sesión pasa de 2,34 dólares a 0,65. Un 72% menos, sin tocar una sola instrucción.

    Si el system prompt sí es el problema de verdad —pasa, sobre todo dentro de herramientas que inyectan contexto por su cuenta—, entonces sí toca recortar. En Claude Code buena parte de ese peso no lo has escrito tú, y se quita con un flag: lo cuento en el flag que recorta el system prompt dinámico.

    Si el contenido está en español, el desglose ya viene con un recargo de fábrica. El mismo texto tokeniza alrededor de un 26% más caro que en inglés, y eso afecta a tus prompts, a tus tool results y a la salida del modelo. Antes de recortar nada, léete el recargo del tokenizador en español: a veces la optimización correcta es escribir el system prompt en inglés y responder en español.

    Si lo que se disparó es el número de turnos, tu problema no está en ninguna fila de la tabla: está en cuántas veces la construyes. Ojo especialmente después de cambiar de modelo, porque cuánto delega es una propiedad del modelo y se mueve entre versiones — el mecanismo completo está en por qué sube el coste de los subagentes al cambiar de modelo.

    Y si los resultados de herramientas son el 60-70%, como en el caso que abre este post, tienes tres salidas por orden de esfuerzo: paginar y filtrar en tu propia tool antes de devolver nada, resumir los resultados antiguos, o dejar que la API vaya limpiando los tool results caducados.

    Lo último se hace con la estrategia clear_tool_uses_20250919 de context editing, que todavía está en beta. Elegir entre las tres es exactamente el trabajo que describo en gestión estratégica del contexto.

    Un apunte sobre la primera opción, que es la que más gente se salta. Si tu herramienta devuelve el objeto completo de la base de datos porque "por si acaso el modelo lo necesita", estás pagando por cada campo en cada turno restante de la sesión. Definir el contrato de salida de cada tool con un esquema estricto —y devolver solo eso— es una decisión de coste, no de estilo. Un esquema de salida con Zod en la frontera de cada tool tira los campos que no declaraste antes de que lleguen al contexto.

    Empieza por la tabla

    No apliques ni un truco de ahorro esta semana.

    Coge una conversación real de producción, pásala por desglosar() y monta la tabla de cuatro filas. Eso es todo el trabajo de medir el consumo de tokens de un agente: media hora. Y lo más probable es que el mayor porcentaje esté donde no lo esperabas, porque el componente que más pesa suele ser el que nadie mira: el que se genera solo.

    Después optimiza. En ese orden.

    Instrumentar el gasto antes de tocarlo es parte del mismo hábito que enseño en Construye con IA: decidir con datos en lugar de con intuición, también cuando lo que decides es dónde recortar. Y si quieres ver desgloses de agentes reales, con sus facturas y sus tablas delante, eso lo hacemos en Dominicode Labs.

    Preguntas frecuentes sobre el consumo de tokens

    ¿Por qué mi factura sube si no he cambiado el prompt?

    Porque en un agente el coste no depende solo del prompt, depende de cuántos turnos dura cada sesión. El historial se reenvía entero en cada llamada, así que el gasto crece de forma cuadrática con el número de turnos: pasar de 12 a 24 turnos multiplica el input acumulado por casi cuatro, no por dos. Si tu agente empezó a necesitar más iteraciones para cerrar la misma tarea, la factura sube sin que hayas tocado una línea.

    ¿El endpoint de conteo de tokens cuesta dinero?

    No. /v1/messages/count_tokens es gratuito. Sí tiene límite de peticiones por minuto según tu tier —2.000 RPM en Start, 4.000 en Build y 8.000 en Scale—, pero es un límite independiente del de generación de mensajes: usar uno no consume el cupo del otro. No hay excusa de coste para no medir.

    ¿Sirve un tokenizador local como tiktoken para medir esto?

    Para una estimación rápida y offline, vale. Para presupuestar, no. Anthropic no publica su tokenizador y el conteo depende del modelo concreto: los modelos desde Claude Opus 4.7 usan un tokenizador nuevo que genera alrededor de un 30% más de tokens para el mismo texto que los anteriores. Un tokenizador de otra familia de modelos te dará un número que no se parece al que te van a cobrar.

    ¿Por qué input_tokens baja tanto al activar el prompt caching?

    Depende de qué estés midiendo. input_tokens cuenta solo lo que va después del último punto de corte de caché, así que activar caching hace que ese número se desplome sin que hayas reducido el contexto ni un token. Para saber lo que realmente enviaste tienes que sumar cache_read_input_tokens y cache_creation_input_tokens. Grafica siempre las tres series juntas, nunca input_tokens solo.

    ¿Cada cuánto hay que volver a medir el consumo de tokens?

    Cada vez que cambies de modelo, añadas o quites herramientas, o toques lo que devuelve una tool. Los tres modifican el reparto. Lo eficiente es no repetirla a mano: deja el registro de usage corriendo en producción con la sesión y el turno como etiquetas, y el desglose por componente pásalo cuando el coste medio por sesión se salga de su rango normal.

    ¿Y si la conclusión es que necesito menos turnos, no menos tokens?

    Es la conclusión más común y la más incómoda, porque no se arregla con un flag. Un agente que tarda 20 turnos en algo que debería resolver en 6 casi siempre tiene un problema de definición de la tarea, no de contexto. Ahí la palanca no es técnica: es escribir mejor qué tiene que hacer antes de dejarlo correr, que es de lo que va el libro de Spec-Driven Development.


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

  • Qué es el graph engineering: el mapa que tu agente no tiene

    Qué es el graph engineering: el mapa que tu agente no tiene

    Le pedí a un agente que renombrara una función. getUserDatafetchUserProfile. Dos minutos de trabajo.

    Hizo grep, encontró siete referencias, las cambió, corrió los tests. Verde. Commit.

    Reventó al día siguiente. La función también se invocaba desde un mapa de handlers, handlers[action], con el nombre viajando como string dentro de un JSON de configuración. Grep encontró siete referencias. Había doce.

    El agente no falló por falta de contexto ni por usar un modelo flojo. Falló porque grep solo compara cadenas y nadie le dio un mapa de relaciones. De eso va el graph engineering.

    Qué es el graph engineering (y el lío que hay con el nombre)

    Graph engineering es la práctica de representar tu código y tu documentación como un grafo explícito de relaciones: los nodos son símbolos —archivos, funciones, clases, conceptos— y las aristas son las relaciones reales entre ellos: importa, llama, hereda, contiene, referencia.

    En vez de que el agente busque texto y adivine, navega aristas.

    Antes de seguir, un aviso honesto: el término no tiene una definición canónica única en 2026. Se usa para dos cosas distintas.

    La primera es el grafo de orquestación. Nodos como unidades de ejecución, aristas como flujo de control: LangGraph, org graphs, work graphs. Ahí el "graph engineering" es diseñar cómo se conectan varios agentes. Es la conversación que arrancó Peter Steinberger en julio de 2026 con una pregunta de seis palabras"Are we still talking loops or did we shift to graphs yet?" — y que es la continuación natural de lo que conté en loop engineering.

    La segunda es el grafo de recuperación. Nodos como símbolos de tu código, aristas como dependencias reales. Aquí no se decide qué agente actúa después: se decide qué sabe el agente antes de tocar nada.

    Este post va de la segunda. Y no compiten: una es control de flujo, la otra es recuperación. Misma palabra, dos capas del stack.

    Si necesitas el atajo: cuando hables de LangGraph o de coordinar varios agentes, es la primera. Cuando hables de qué código ve tu agente antes de editar, es la segunda.

    Las tres preguntas que ni grep ni los embeddings responden

    Hay tres preguntas sobre tu código que ni la búsqueda por texto ni la búsqueda semántica pueden responder:

    1. Si cambio esto, ¿qué se rompe? El radio de impacto a uno, dos o tres saltos. Grep te da el primer nivel. El transitivo no lo ve nadie.
    2. ¿Quién llama a quién? El call graph completo, con su dirección. Grep te dice que dos archivos mencionan AuthService. No te dice cuál lo consume y cuál lo define.
    3. ¿Qué depende de qué — y qué no depende de nada? Los nodos con grado cero son código muerto, y salen solos. Buscar código muerto con grep es un ejercicio de paciencia.

    Las tres son preguntas sobre topología, no sobre contenido. Por eso hacen falta aristas — y por eso las dos herramientas que usas hoy se quedan cortas.

    Tu agente tiene dos formas de encontrar código, y las dos tienen el mismo agujero.

    Grep busca coincidencia exacta de texto. Es preciso, rápido y determinista. No sabe nada de significado ni de estructura. Si la referencia está construida en runtime, no existe para grep.

    Los embeddings buscan parecido semántico. Encuentran la función de autenticación aunque se llame verificarCredenciales. Pero "se parece" no es "está conectado con". Un chunk sobre logging y otro sobre logging viven cerca en el espacio vectorial aunque uno nunca llame al otro. Es la limitación estructural de RAG que ya toqué en RAG vs fine-tuning.

    Las dos herramientas responden "¿dónde aparece esto?". Ninguna responde "¿con qué está conectado esto?".

    Resumido, con la tercera vía al lado:

    Grep Embeddings Grafo de código
    Pregunta que responde ¿Dónde aparece esta cadena? ¿Dónde hay algo parecido a esto? ¿Con qué está conectado esto?
    Qué necesitas saber antes El nombre exacto Una descripción aproximada Que el símbolo exista
    Ve el segundo salto No No Sí — affected --depth 2
    Ve llamadas indirectas No No Sí, marcadas como INFERRED
    Nivel de certeza Binario: aparece o no aparece Puntuación de similitud EXTRACTED o INFERRED
    Coste de mantenerlo Cero Reindexar + coste de embeddings Re-extracción AST, sin LLM
    Dónde se rompe La referencia se construye en runtime Dos cosas se parecen pero no se llaman Código muy dinámico: DI por string, metaprogramación

    Anatomía del grafo: nodos, aristas y confianza

    Un grafo de código tiene tres piezas: nodos (los símbolos: archivos, funciones, clases), aristas (las relaciones entre ellos) y un nivel de confianza por arista.

    Lo concreto. Construí un grafo con graphify —CLI open source, parseo AST local con tree-sitter, sin vector store— sobre un proyecto pequeño que tengo por ahí. Pequeño a propósito: quería poder verificar a mano cada arista antes de creerme nada. Salieron 115 nodos y 240 aristas.

    Los nodos llevan poco: id, etiqueta, archivo de origen y línea. Lo interesante está en las aristas.

    {
      "source": "src_chunker",
      "target": "src_chunker_needs_chunking",
      "relation": "contains",
      "confidence": "EXTRACTED",
      "source_file": "src/chunker.py",
      "source_location": "L10"
    }
    

    Los ocho tipos de relación que aparecieron en ese grafo:

    Relación Qué conecta ¿La ve grep?
    imports / imports_from Archivo → módulo o símbolo importado Sí, si el nombre aparece literal
    contains Archivo → función o clase que declara Parcialmente
    calls Función → función que invoca Solo el primer nivel
    references Símbolo usado sin invocarlo Sí, si el nombre aparece literal
    inherits Clase → clase base
    method Clase → método que le pertenece
    indirect_call Llamada resuelta en runtime No

    Fíjate en la última fila. indirect_call es exactamente la llamada que grep no ve.

    Y ahora el campo que más me interesa de todo esto, el que casi nadie menciona: confidence. Cada arista viene marcada como EXTRACTED o INFERRED. En mi grafo: 197 extraídas, 43 inferidas.

    EXTRACTED significa que la relación está literalmente en el AST. El parser la leyó, no la dedujo. INFERRED significa que la resolvió el motor uniendo puntos — una llamada cuyo destino tuvo que deducirse.

    Eso cambia cómo usas el resultado. Una arista EXTRACTED la das por buena. Una INFERRED es una hipótesis con nombre y apellidos que puedes ir a verificar al archivo y la línea que te da. Ni los embeddings ni grep te dan esa distinción: grep afirma sin matices, y el score de un embedding te dice cuánto se parece algo, nunca de dónde sale la relación. Aquí lo que se etiqueta es la procedencia.

    Un explain sobre un nodo devuelve esto:

    Node: needs_chunking()
      Source:    src/chunker.py L10
      Degree:    6
    
    Connections (6):
      <-- main() [calls] [INFERRED]
      <-- transcribe() [calls] [INFERRED]
      <-- chunker.py [contains] [EXTRACTED]
      --> Path [references] [EXTRACTED]
      <-- test_needs_chunking_false_for_small_file() [calls] [INFERRED]
      <-- test_needs_chunking_true_for_large_file() [calls] [INFERRED]
    

    Seis líneas. Ahí está el vecindario directo de esa función, con la dirección de cada arista y el nivel de confianza de cada una. Para llegar a lo mismo con grep necesitas varias pasadas y saber de antemano qué buscar.

    Pero el vecindario directo no es el radio de impacto. Para eso hay un comando aparte, que es el que responde literalmente a la pregunta 1: un recorrido inverso por las aristas que tú elijas, a la profundidad que tú digas.

    graphify affected "needs_chunking" --depth 2 --relation calls
    
    Affected nodes for needs_chunking()
    Relations: calls
    Depth: 2
    - test_needs_chunking_false_for_small_file() [calls] tests/test_chunker.py:L24
    - test_needs_chunking_true_for_large_file() [calls] tests/test_chunker.py:L30
    - main() [calls] transcribe.py:L17
    - transcribe() [calls] watch.py:L36
    - test_output_flag_saves_to_specified_path() [calls] tests/test_integration.py:L34
    - test_file_not_found_exits_with_code_1() [calls] tests/test_integration.py:L48
    - test_unsupported_format_exits_with_code_1() [calls] tests/test_integration.py:L55
    - test_api_key_not_in_output() [calls] tests/test_integration.py:L65
    - .on_created() [calls] watch.py:L72
    

    Mira la diferencia. De las seis conexiones del explain, solo cuatro eran llamadas entrantes. El affected a dos saltos da nueve, y las cinco nuevas son las interesantes: los cuatro tests de integración y el handler .on_created() del watcher no tocan needs_chunking directamente, llegan a través de main() y transcribe().

    Ese es el segundo nivel. El que revienta en producción al día siguiente y el que ninguna búsqueda por texto te va a dar, porque no hay ninguna cadena que buscar: la relación existe en la topología, no en el código fuente de esos archivos.

    Aquí está la tesis, y quiero decirla sin vender humo: el grafo no te garantiza encontrar la referencia indirecta. Te da una categoría donde esa relación puede existir y quedar marcada. Grep ni siquiera tiene esa categoría. Esa es toda la diferencia, y es suficiente.

    Cómo usar un grafo de código con un agente de coding, en 3 pasos

    Tres piezas.

    Uno: construyes el grafo y lo dejas en el repo. graphify-out/graph.json más un reporte en markdown. Es un artefacto de tu proyecto, como el lockfile.

    uv tool install graphifyy   # doble "y" mientras reclaman el nombre en PyPI;
                                # el comando y el skill siguen siendo graphify
    graphify install            # registra el skill en tu agente
    graphify update .           # re-extrae solo lo que cambió, sin LLM
    

    Dos: le das al agente una regla de precedencia. Sin esto no sirve de nada, porque el modelo tira de grep por costumbre. En el CLAUDE.md del proyecto:

    - Para preguntas sobre el código, ejecuta primero `graphify query "<pregunta>"`.
      Usa `graphify path "<A>" "<B>"` para relaciones, `graphify explain "<X>"`
      para un concepto concreto y `graphify affected "<X>"` antes de modificar o
      borrar algo. Devuelven un subgrafo acotado, mucho más pequeño que el reporte
      completo o la salida cruda de grep.
    - Después de modificar código, ejecuta `graphify update .`.
    

    Esa regla es la diferencia entre tener un grafo y usarlo. Es la misma idea de fondo que trabajo en el curso de Construye con IA: el agente no es más listo por tener más herramientas, sino por tener reglas claras de cuándo usar cuál.

    Tres: el grafo entra en la ventana como subgrafo, no como volcado. Un explain devuelve seis líneas donde un grep te vuelca cada aparición del término y tú decides después: recuperas menos tokens y mejores, que es el objetivo del context engineering.

    Ojo con una cosa: graphify query no devuelve una respuesta en prosa. Devuelve un recorrido BFS con los nodos encontrados. Es una herramienta de recuperación dentro del harness, no un chatbot. Quien interpreta el subgrafo sigue siendo el modelo.

    Y un apunte de higiene: el proyecto publica cifras de benchmark en su README. Son autoreportadas. Trátalas como lo que son y mide en tu repo.

    Cuándo NO merece la pena montar un grafo de código

    No todo proyecto necesita esto. Cuatro casos donde el grafo estorba más de lo que ayuda.

    Proyectos pequeños. Si el código entra entero en la ventana, el agente ya tiene el grafo en la cabeza y mejor resuelto. Montar recuperación para veinte archivos es sobreingeniería.

    Código muy dinámico. Metaprogramación intensa, inyección de dependencias por string, event buses, decoradores que reescriben comportamiento en runtime. El AST no puede ver lo que solo existe cuando el proceso arranca. El grafo saldrá con más aristas INFERRED que EXTRACTED, o directamente con huecos. Sigue siendo mejor que grep, pero baja mucho el techo.

    Y sí: el bug con el que abrí este post vive justo en esta frontera. Un nombre viajando dentro de un JSON no está en ningún AST. Lo que cambia es que el grafo marca ese hueco como INFERRED o lo deja sin arista, y eso es una señal que puedes leer. Grep te devuelve siete referencias con la misma cara de seguridad que si fueran las doce.

    Si no puedes mantenerlo actualizado. Un grafo obsoleto es peor que no tener grafo, porque el agente confía en él. Necesitas graphify update en un hook de pre-commit, en CI o con graphify watch. Si esto no está automatizado, no lo montes: en dos semanas tienes un mapa de un territorio que ya no existe.

    Si lo que buscas es "qué debería hacer este sistema". El grafo describe el código que existe, no la intención. Para eso el artefacto es la spec — que es, por cierto, otra forma de estructura explícita, y la razón por la que escribí el libro de Spec-Driven Development. El grafo cuenta el presente. La spec define el futuro.

    Y un apunte de madurez: graphify va por la 0.9.x. No es 1.0 todavía, y se nota. Herramienta útil, no infraestructura estable.

    Cómo empezar con graph engineering hoy

    Coge tu repo más feo. El que da miedo tocar.

    Construye el grafo, ejecuta un explain sobre la función que más te intimida y mira su grado. Si el número te sorprende, acabas de descubrir por qué ese refactor lleva meses aplazado.

    Si quieres ver cómo encaja esto con el resto del stack —agentes, MCP, memoria, specs— lo trabajamos a fondo en Dominicode Labs, con proyectos reales y no con ejemplos de juguete.

    Preguntas frecuentes

    ¿Graph engineering es lo mismo que GraphRAG?

    No exactamente. GraphRAG es la implementación de Microsoft que usa un LLM para extraer entidades y relaciones de texto no estructurado, detectar comunidades y resumirlas. Está pensado para corpus documentales.

    Graph engineering es el concepto general de estructurar conocimiento como grafo. Aplicado a código, el grafo se extrae del AST de forma determinista, sin LLM y sin coste por token. GraphRAG es una implementación posible, no la única ni la más barata para código.

    ¿Qué diferencia hay entre graph engineering y loop engineering?

    El loop engineering diseña el bucle de ejecución del agente: qué hace, cómo verifica el resultado y cuándo vuelve a intentarlo. El graph engineering, en la acepción de este post, diseña lo que el agente sabe antes de entrar en ese bucle: un mapa de relaciones de tu código en vez de una búsqueda de texto.

    No compiten. Un agente con un buen bucle y sin mapa repite el mismo error más rápido. Si tus fallos vienen de contexto estructural incompleto, el grafo rinde antes que otra iteración del loop.

    ¿Funciona con TypeScript o solo con Python?

    Los ejemplos de este post salen de un proyecto en Python, pero la extracción es por AST con tree-sitter y las gramáticas que trae cubren los lenguajes habituales: Python, TypeScript, JavaScript, Go, Rust, Java, C, C++, Ruby, C#, Kotlin, Scala y PHP.

    Con TypeScript hay un matiz: cuanto más tira el proyecto de inyección por token, decoradores y factories, más aristas caen en INFERRED. El grafo sigue siendo mejor que grep, pero léelo sabiendo qué parte es hipótesis.

    ¿Necesito una base de datos de grafos como Neo4j?

    Para un repo, no. El grafo de un proyecto normal cabe en un JSON en disco y se recorre con un BFS en memoria. Herramientas como graphify funcionan así, sin servidor y sin dependencias externas.

    Neo4j tiene sentido cuando el grafo es un producto en sí mismo, se consulta desde varios servicios o supera lo que quieres cargar en memoria. Para dar contexto estructural a un agente en tu máquina, es infraestructura que no necesitas.

    ¿El grafo sustituye a los embeddings y a la búsqueda semántica?

    No, y montarlo como sustituto es un error. Responden preguntas distintas.

    Los embeddings responden "¿dónde hay algo parecido a esto?" y toleran que no sepas los nombres exactos. El grafo responde "¿con qué está conectado esto?" y exige que el símbolo exista. Lo razonable es tener las dos vías y una regla de precedencia: para preguntas de estructura, grafo; para exploración difusa, semántica; para strings literales, grep.

    ¿Cada cuánto hay que reconstruir el grafo?

    En cada cambio de código relevante, y automatizado. La re-extracción incremental de código no necesita LLM, así que el coste es tiempo de CPU, no dinero.

    Lo práctico es un hook de pre-commit, un paso en CI o un proceso en watch mientras trabajas. Reconstruirlo a mano cuando te acuerdas es la vía rápida a un grafo obsoleto, y un grafo obsoleto le miente al agente con toda la confianza del mundo.

    ¿Sirve en monorepos grandes?

    Es donde más rinde, precisamente porque el código ya no cabe en la ventana de contexto y grep devuelve ruido. La pega es operativa: la visualización HTML se vuelve pesada por encima de unos miles de nodos, y para eso está la opción de saltarla y quedarte solo con el JSON consultable, que es lo que consume el agente.

    Y si tu organización tiene varios repos en vez de uno solo, puedes fusionar sus grafos en uno para cruzar dependencias entre paquetes.


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

  • Tokens en español: por qué cuestan un 26 % más que en inglés

    Tokens en español: por qué cuestan un 26 % más que en inglés

    Hace unas semanas revisé el consumo de API de un agente de soporte. Sonnet, 25.000 conversaciones al mes, prompts cortos, nada exótico. El equipo había estimado el coste a mano antes de lanzar y la factura llegó cerca de un 26 % por encima.

    Estuvimos media hora buscando la llamada duplicada. No había llamada duplicada.

    El problema eran los tokens en español. No porque un token en español cueste más —el precio por token es idéntico—, sino porque necesitas más tokens para decir exactamente lo mismo.

    Y la parte incómoda es por qué necesitas más. La respuesta cómoda es "el español es más largo". Esa respuesta no llega a explicar ni la mitad de lo que pasa.

    Lo medí.

    La respuesta corta: el español consume un 26,0 % más de tokens que el inglés para transmitir el mismo mensaje, medido sobre cinco pares de textos paralelos con o200k_base (GPT-4o / GPT-5). Unos 10 puntos vienen de que el español usa más palabras; el resto, de que el tokenizador parte cada palabra española en más trozos. El precio por token es idéntico en los dos idiomas: lo que cambia es cuántos necesitas.

    Medí los tokens en español de cinco textos reales

    Cogí cinco textos del tipo que de verdad mandas a un modelo en producción y escribí la versión española y la inglesa de cada uno, con el mismo significado y el mismo registro. Nada de traducciones infladas: pares paralelos.

    Luego los pasé en local por o200k_base, el tokenizador de la familia GPT-4o / GPT-5.

    Tabla 1 — Sobrecoste de tokens del español frente al inglés en cinco pares de textos paralelos, medidos con o200k_base. Medición propia de Dominicode, julio de 2026.

    Tipo de texto Tokens EN Tokens ES Sobrecoste ES vs EN
    System prompt de agente 74 86 +16,2 %
    Documentación técnica 62 85 +37,1 %
    Mensaje de un usuario (soporte) 65 73 +12,3 %
    Fragmento de base de conocimiento (RAG) 67 93 +38,8 %
    Prompt de tarea de desarrollo 55 70 +27,3 %
    Total 323 407 +26,0 %

    El español consume un 26,0 % más de tokens que el inglés para decir exactamente lo mismo. No es una anécdota: es un multiplicador que se aplica a cada llamada de tu aplicación, todos los días.

    Y fíjate en la dispersión, porque ahí está la parte accionable: el mensaje de un usuario se hincha un 12,3 %; un fragmento de base de conocimiento, un 38,8 %. Tres veces más de castigo según el tipo de texto.

    No es que hables más. Es que te parten peor.

    Descompongo ese +26 % en sus dos factores:

    • Palabras: 290 en inglés → 319 en español = +10,0 %
    • Tokens por palabra: 1,114 en inglés → 1,276 en español = +14,5 %. O sin decimales, por si se lee mejor: 111 tokens por cada 100 palabras inglesas frente a 128 por cada 100 españolas.

    Y los dos factores se multiplican, no se suman: 1,10 × 1,145 = 1,26. Ahí está el +26 % completo.

    Traducido: de los 26 puntos de sobrecoste, solo unos 10 vienen de que el español use más palabras. El resto viene de que cada palabra española se rompe en más trozos.

    Esto no es una queja sobre el idioma, es ingeniería. El vocabulario de un tokenizador BPE se construye por estadística sobre un corpus mayoritariamente inglés, así que las secuencias frecuentes en inglés se quedan con las plazas buenas: una palabra inglesa común entra entera en un token y su equivalente española entra a trozos. Súmale que el español conjuga y deriva mucho más, y cada variante es una cadena distinta que el tokenizador no tiene memorizada.

    Eso explica también la dispersión de la tabla. Los dos que menos se inflan son el mensaje de usuario (+12,3 %) y el system prompt (+16,2 %): frases cortas, vocabulario común y terminología técnica que el tokenizador reconoce igual en los dos idiomas. Los que más se inflan son la documentación (+37,1 %) y los fragmentos de RAG (+38,8 %), que llevan prosa española de verdad, con subordinadas y nominalizaciones largas de las de "-ción" y "-miento". La regla práctica: cuanta más prosa explicativa, más sobrecoste.

    El sobrecoste del español baja de +44 % a +26 %

    Pasé los mismos cinco pares por cl100k_base, el tokenizador antiguo de GPT-3.5 y GPT-4: 323 tokens en inglés y 465 en español. Un +44,0 %.

    De +44 % a +26 % en una generación de tokenizador. El vocabulario de o200k_base es más grande y menos anglocéntrico. Pero son dos medidas, no una ley: bajó una vez, no des por hecho que baje siempre. Lo que sí puedes dar por hecho es que si en 2023 tomaste una decisión de arquitectura basada en lo que costaba el español, ese número está caduco.

    Metodología y límites de la medición

    Medición hecha por Bezael Pérez (Dominicode) en julio de 2026: cinco pares de textos paralelos español/inglés —system prompt de agente, documentación técnica, mensaje de soporte, fragmento de RAG y prompt de tarea de desarrollo—, tokenizados en local con o200k_base y cl100k_base. Totales: 323 tokens en inglés frente a 407 en español con o200k_base, y 465 con cl100k_base.

    Cada proveedor usa su propio tokenizador. Anthropic no publica el suyo: la única fuente fiable para contar tokens de Claude es su endpoint count_tokens — y Anthropic la llama estimación, no medida exacta.

    Así que los porcentajes de arriba son de la familia OpenAI (o200k_base), no de Claude. La dirección del efecto es la misma en todos los modelos comerciales, pero el número exacto varía según proveedor y tipo de texto. Anthropic lo admite en su propia página de precios: un token son "aproximadamente 4 caracteres o 0,75 palabras en inglés", y el recuento exacto "varía según el idioma".

    Con Claude hay además un detalle que cambia los números absolutos, avisado en esa misma página: los modelos de la generación 4.7 en adelante —Opus 5 y Sonnet 5 incluidos— usan un tokenizador nuevo que produce alrededor de un 30 % más de tokens que los anteriores para el mismo texto. Se nota en sus estimaciones: el millón de tokens de Opus 5 son ~555.000 palabras en inglés, y en Opus 4.6 eran ~750.000.

    Así que no copies la cifra de este post. Mídela. Es gratis y te cuento cómo abajo.

    Dónde se paga el sobrecoste de tokens en español: factura, contexto y latencia

    1. La factura: cuánto cuesta el sobrecoste al mes

    Tabla 2 — Precios oficiales de la API de Anthropic por millón de tokens, en USD (consultados el 30 de julio de 2026).

    Modelo Entrada ($/M tokens) Salida ($/M tokens)
    Claude Opus 5 $5 $25
    Claude Sonnet 5 $3 $15
    Claude Haiku 4.5 $1 $5

    Ojo a la fecha con Sonnet 5: los $3 / $15 son la tarifa estándar que entra el 1 de septiembre de 2026; hasta el 31 de agosto rige el lanzamiento de $2 / $10, un tercio más barato. Calculo con la estándar porque es la que pagarás cuando esto lleve unos meses en producción.

    Vuelvo al agente del principio: Sonnet 5 a tarifa estándar, 25.000 conversaciones al mes, 1.500 tokens de entrada y 300 de salida por conversación. El modelo responde en el idioma en el que le escriben, así que cuando el usuario escribe en español la salida también se hincha un 26 %.

    • En inglés: entrada 37,5 M × $3 = $112,50 · salida 7,5 M × $15 = $112,50 → $225/mes
    • En español (+26 % en entrada y en salida): entrada 47,25 M × $3 = $141,75 · salida 9,45 M × $15 = $141,75 → $283,50/mes

    Diferencia: $58,50 al mes. $702 al año. Por escribir en el idioma de tus usuarios. El +26 % va aquí como aproximación, por lo que ya expliqué: el tokenizador de Claude no es público.

    A esta escala es asumible. Multiplícalo por diez y ya es una decisión de producto.

    2. La ventana de contexto

    Opus 5 y Sonnet 5 tienen 1 millón de tokens de ventana de contexto. Y ojo con la cuenta, porque aquí el 26 % se da la vuelta: si cada texto te cuesta un 26 % más de tokens, en ese millón te cabe un 21 % menos de contenido (1 ÷ 1,26 = 0,79). Con fragmentos de base de conocimiento, que se hinchan un 38,8 %, la pérdida sube al 28 %.

    En un RAG eso no es una curiosidad académica: es recall —y si todavía estás decidiendo entre RAG y fine-tuning, el idioma entra en la ecuación de coste. Con el mismo presupuesto de contexto inyectas menos fragmentos por consulta.

    Menos evidencia recuperada para la misma pregunta es exactamente la situación en la que un modelo empieza a rellenar huecos, que es el mecanismo que expliqué en por qué la IA se inventa cosas.

    Y antes de que la solución sea "pues meto más": llenar la ventana tampoco es gratis en calidad. Va de eso la regla del 60 % en gestión de contexto.

    3. La latencia

    Un modelo emite los tokens de salida de uno en uno, a un ritmo más o menos fijo. Si tu respuesta en español necesita un 26 % más de tokens, tarda un 26 % más en terminar.

    Ojo con dónde lo notas, porque es fácil confundirse: si haces streaming, el primer token llega igual de rápido —eso lo manda el prefill de la entrada, no la longitud de la salida—, y lo que se alarga es la respuesta completa. Sin streaming, el usuario se come el 26 % entero mirando el cursor parpadear. Y si detrás hay un agente que encadena cinco llamadas, ese 26 % se acumula en cada paso.

    Qué hacer con esto (sin escribir peor español)

    Mide, no estimes. El endpoint count_tokens de Anthropic es gratis. Solo lo limitan las peticiones por minuto de tu tier: 2.000 en Start, 4.000 en Build, 8.000 en Scale.

    import Anthropic from "@anthropic-ai/sdk"
    
    const client = new Anthropic()
    
    const systemEs = "Eres un agente de soporte…"   // tu system prompt real
    const systemEn = "You are a support agent…"     // el mismo, en inglés
    
    async function contar(system: string) {
      const res = await client.messages.countTokens({
        model: "claude-opus-5",
        system,
        messages: [{ role: "user", content: "ping" }],
      })
      return res.input_tokens
    }
    
    console.log(await contar(systemEs), await contar(systemEn))
    

    El "ping" está ahí porque messages es obligatorio; al ser idéntico en las dos llamadas, la diferencia sale limpia. Quince minutos y dejas de discutir con estimaciones.

    El system prompt en inglés, el contenido del usuario en español. El system prompt se repite en cada llamada y tu usuario nunca lo lee. Traducirlo al inglés te quita tokens de encima en todas.

    Pero pon el número antes de comprar la idea. Mi system prompt de prueba baja de 86 a 74 tokens: por 25.000 conversaciones al mes son $0,90 con Sonnet 5. Con un system prompt realista de 2.000 tokens, unos $21 al mes. Con caché, una décima parte de eso. Es una optimización real, pero es de un dígito o dos de dólares — no la vendas como el arreglo.

    Y no es gratis. Si lo traduces, fija el idioma de salida de forma explícita —"Always respond in Spanish, regardless of the language of these instructions"— en vez de dejar que el modelo lo infiera del mensaje. Si dentro tienes few-shots, cuidado: los ejemplos arrastran el idioma de salida tanto como la instrucción. Y deja en español cualquier texto que el modelo tenga que devolver literal —mensajes fijos, disclaimers, nombres de producto—, o te lo traducirá a su manera. Después de tocar el system prompt, vuelve a pasar tus evals.

    Prompt caching. Esta es la palanca de verdad. Si el system prompt y el contexto fijo se repiten entre llamadas, cachéalos: una lectura de caché cuesta 0,1× el precio de entrada, un 90 % menos. Ataca justo la parte repetida, la que paga el 26 % una y otra vez sin cambiar una coma, y por eso rinde un orden de magnitud más que traducir nada. Lo desarrollé entero en prompt caching con la API de Claude. Orden de prioridades: primero cachea, después piensa en el idioma.

    Vigila lo que inyectas en cada consulta. Los fragmentos de base de conocimiento son lo que más se hincha (+38,8 %) y van en cada petición; la documentación técnica le sigue (+37,1 %). Si trabajas con specs, esto te toca de lleno: una spec es documentación técnica que entra en el contexto en cada iteración. Una razón más para escribirlas cortas y estructuradas, como insisto en el libro de Spec-Driven Development.

    Lo que NO debes hacer: escribir peor español para ahorrar tokens. Nada de abreviar, quitar tildes o telegrafiar los prompts como un SMS de 2004. El ahorro es de céntimos, y transliterar o mutilar el texto es justo lo contrario de lo que recomiendan los propios proveedores, que piden enviarlo en su escritura nativa. Si necesitas gastar menos: cachea, elige un modelo más pequeño para la tarea, reduce el número de llamadas o mueve la carga a un modelo local, donde el sobrecoste del español deja de facturarse por token y pasa a ser tiempo de GPU.

    Cómo medir tus tokens en español hoy mismo

    Abre tu system prompt de producción. El real, el que ya está desplegado.

    1. Copia tu system prompt tal cual está desplegado.
    2. Pásalo por count_tokens y anota input_tokens.
    3. Traduce ese mismo prompt al inglés sin recortar contenido.
    4. Pásalo otra vez y anota el segundo número.
    5. Resta, divide por el valor en inglés y multiplica por tus llamadas mensuales y por el precio de entrada de tu modelo.

    En quince minutos tienes tu número, no el mío.

    La mayoría descubre que su problema no era el idioma: era que no estaban cacheando nada. Ese diagnóstico solo aparece cuando mides.

    Si quieres el flujo completo para llevar una idea a producto con Claude Code —y salir con instrumentación, no con intuiciones—, es lo que trabajo en Construye con IA: de la idea al producto con Claude Code. Y si prefieres verlo sobre proyectos reales, con gente peleándose con las mismas facturas, eso pasa cada semana en Dominicode Labs.

    Preguntas frecuentes sobre los tokens en español

    ¿Cuánto más cuesta escribir prompts en español que en inglés?

    En mi medición con o200k_base sobre cinco pares de textos paralelos, un 26,0 % más de tokens para decir lo mismo: 323 en inglés frente a 407 en español.

    El rango va de +12,3 % (mensaje de un usuario) a +38,8 % (fragmento de RAG): el tipo de texto importa tanto como el idioma.

    ¿Es porque el español es más largo?

    Solo en parte, y los dos factores se multiplican, no se suman: un +10,0 % de palabras (290 → 319) por un +14,5 % de tokens por palabra (1,114 → 1,276) sale 1,10 × 1,145 = 1,26. El factor grande es el segundo.

    La causa es el tokenizador, no la verborrea: su vocabulario se entrenó sobre un corpus mayoritariamente inglés, y lo que no está bien representado ahí se fragmenta.

    ¿Cuántos tokens es una palabra en español?

    1,276 tokens por palabra de media en mi medición con o200k_base, frente a 1,114 en inglés. O sin decimales: unos 128 tokens por cada 100 palabras españolas y 111 por cada 100 inglesas.

    Es una media sobre texto real de producción: sube en prosa explicativa y baja en textos cargados de terminología inglesa, que el tokenizador ya conoce.

    ¿Qué tipo de texto se encarece más al escribirlo en español?

    Los fragmentos de base de conocimiento para RAG (+38,8 %) y la documentación técnica (+37,1 %). El que menos, los mensajes que escriben los propios usuarios (+12,3 %).

    La diferencia está en la densidad de jerga inglesa: cuanto más técnico es el texto, más tokens comparte el español con el inglés y menos se infla.

    ¿Cómo cuento los tokens en español que consume Claude?

    Con el endpoint count_tokens de la API de Anthropic, disponible en el SDK oficial como client.messages.countTokens(). Es gratis y solo lo limitan las peticiones por minuto de tu tier: 2.000 en Start, 4.000 en Build, 8.000 en Scale.

    Es además la única fuente oficial, porque Anthropic no publica su tokenizador. Ni siquiera ella es exacta: Anthropic advierte de que el conteo es una estimación y puede desviarse ligeramente del consumo real. Cualquier cifra calculada con o200k_base o cl100k_base es una aproximación de otro proveedor.

    ¿Debo escribir mis prompts en inglés para ahorrar dinero?

    El system prompt puedes traducirlo: se repite en cada llamada, nadie lo lee y los modelos responden en español perfectamente aunque las instrucciones estén en inglés. Pero mide el ahorro antes de moverlo, porque suele ser de un dígito o dos de dólares al mes, y desaparece casi entero si ya estás cacheando.

    El contenido del usuario, no lo toques. Y no traduzcas tu base de conocimiento al inglés solo por coste sin medir antes qué le pasa a la calidad de las respuestas. Antes de eso activa prompt caching: ahorra un 90 % en la parte repetida y no cambia nada de tu producto.

    ¿Este sobrecoste va a desaparecer?

    Se está reduciendo: los mismos cinco pares dan +44,0 % con cl100k_base (GPT-3.5 / GPT-4) y +26,0 % con o200k_base (GPT-4o / GPT-5), porque los vocabularios nuevos son más grandes y menos anglocéntricos.

    Pero no lo tomes como una tendencia garantizada. Son dos medidas, no una ley, y hay contraejemplos: los modelos Claude de la generación 4.7 en adelante usan un tokenizador nuevo que produce alrededor de un 30 % más de tokens que los anteriores para el mismo texto. Vuelve a medir cada vez que cambies de modelo.

    ¿Afecta el idioma a la ventana de contexto?

    Sí, y es el coste que menos se vigila. Cuidado con la cuenta, porque el porcentaje se invierte: en 1 millón de tokens —el que traen Opus 5 y Sonnet 5— cabe un 21 % menos de contenido si está en español, porque un +26 % de tokens por texto equivale a 1 ÷ 1,26 de contenido por ventana. Con fragmentos de RAG (+38,8 %) la pérdida es del 28 %.

    En un RAG eso significa menos fragmentos recuperados por consulta con el mismo presupuesto de contexto.


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

  • Qué es la inteligencia artificial, de verdad: no piensa, predice

    Qué es la inteligencia artificial, de verdad: no piensa, predice

    Hay una pregunta que casi nadie hace en voz alta delante de sus compañeros.

    "Oye, pero qué es la inteligencia artificial en realidad. ¿La cosa piensa o no piensa?"

    No la definición del folleto. El mecanismo. Qué hay al otro lado del cable.

    Es incómoda porque lleva otra dentro: si llevas año y medio escribiendo prompts a diario, ¿cómo es que todavía no lo sabes?

    Y no es una duda académica. Es la razón exacta por la que te sorprendes cuando el modelo inventa un método que no existe, cuando falla sumando cuatro cifras o cuando "olvida" lo que le dijiste cuarenta mensajes atrás.

    La tesis cabe en cuatro palabras: la IA no piensa, predice.

    En una frase: la inteligencia artificial es el campo de la informática que construye sistemas capaces de resolver tareas que asociamos a la inteligencia humana. En su forma dominante hoy —los modelos de lenguaje— el mecanismo es una función con miles de millones de parámetros que recibe una secuencia de tokens y estima la probabilidad del siguiente, repitiendo ese paso hasta terminar la respuesta. No comprende ni razona en el sentido humano: estima continuaciones probables. Lo que sigue es por qué esa diferencia cambia cómo diseñas tu software.


    La definición de inteligencia artificial que no sirve para nada

    La encontrarás en cualquier buscador con alguna variante de esto: la capacidad de las máquinas para realizar tareas que requieren inteligencia humana.

    No es falsa. Es inútil. Define una cosa comparándola con otra que tampoco sabemos definir, y no te permite tomar ni una decisión de ingeniería.

    El pecado original está en el acta de nacimiento del campo. El término lo acuñó John McCarthy en la propuesta del 31 de agosto de 1955 para el Dartmouth Summer Research Project, firmada también por Marvin Minsky, Nathaniel Rochester y Claude Shannon. La conjetura de partida era que cualquier rasgo de la inteligencia puede describirse, en principio, con tanta precisión que se pueda construir una máquina que lo simule.

    Fíjate en el verbo. Simular. Ni ser, ni comprender.

    Lo que en 1955 era una hipótesis de trabajo, en 2026 es un departamento de marketing. En algún punto alguien borró "simular" y se quedó con "inteligencia".


    Qué es la inteligencia artificial de verdad: predicción estadística

    Quítale el nombre bonito. Debajo de ChatGPT, Claude o Copilot hay una función: enorme, con miles de millones de parámetros ajustados durante el entrenamiento, pero una función.

    Recibe una secuencia de tokens y devuelve una distribución de probabilidad sobre el siguiente token. Eso es todo lo que hace en una pasada.

    // Una pasada del modelo, simplificada al hueso
    type Token = number; // id dentro del vocabulario
    type LLM = (contexto: Token[]) => Map<Token, number>; // probabilidad de ser el siguiente
    

    No devuelve "la respuesta". Devuelve, para cada token de su vocabulario, la probabilidad de que sea el siguiente. Un algoritmo de muestreo elige uno, lo añade a la secuencia y la función se ejecuta otra vez. Y otra. Hasta que sale un token de parada.

    Lo que lees en tu editor es ese bucle repetido cientos de veces. En ningún momento el sistema decide qué quiere decir y luego lo redacta. Redactar es lo único que hace.

    Y un token no es una palabra: es un fragmento frecuente de caracteres. La regla gruesa de los conceptos básicos de la API de OpenAI son unos 4 caracteres o 0,75 palabras por token en inglés, y varía según modelo e idioma.

    El detalle parece trivial y no lo es: el número 4.096 no entra en el modelo como el número 4.096, entra partido en trozos. Y aunque entrara limpio, tampoco hay un algoritmo de suma debajo: solo continuación probable.

    La arquitectura que lo hizo posible es el Transformer, de Attention Is All You Need (2017); las piezas de debajo las desglosé en algoritmos de machine learning que todo developer debería entender.

    Piénsalo así: es el autocompletado de tu móvil con un doctorado. La diferencia con el T9 es de escala y arquitectura, no de propósito: los dos estiman qué viene después.


    Diferencia entre inteligencia artificial, machine learning y LLM

    La diferencia es de anidamiento: la inteligencia artificial es el campo entero, el machine learning es un método dentro de ese campo y un LLM es un tipo concreto de modelo de machine learning. Cuando alguien dice "la IA" sin decir de cuál de estos cuatro niveles habla, casi siempre está vendiéndote algo.

    Nivel Qué es Ejemplos
    Inteligencia artificial El campo entero. Incluye técnicas sin aprendizaje: reglas, búsqueda, planificación Un motor de ajedrez clásico, un sistema experto
    Machine learning Un método dentro de la IA: en vez de programar reglas, se ajustan parámetros con datos. Cuando esos modelos son redes neuronales profundas se llama deep learning Spam, churn, recomendadores
    LLM Un tipo de modelo de deep learning: red neuronal transformer entrenada, en su fase base, para predecir el siguiente token GPT, Claude, Gemini, Llama
    Sistema agéntico No es un modelo: es el software que envuelve al LLM con herramientas, permisos y un bucle Claude Code, Cursor

    Los tres primeros niveles están anidados: cada uno contiene al siguiente. El cuarto no es un nivel del mismo tipo — es producto construido encima.

    Así que no toda la IA es machine learning, no todo el machine learning es un LLM, y el chat que abres cada mañana no es un modelo: es un producto con un modelo dentro. Buena parte de lo que te sorprende ocurre en el producto.

    Esa última fila es la que más confusión genera hoy, y le dediqué un post entero: IA generativa vs IA agéntica.

    Si esta tabla te ha ordenado algo, hay una versión mucho más grande: el mapa de la IA, con 120 conceptos colocados por zonas y las conexiones dibujadas, en una hoja para imprimir. Es gratis, y funciona especialmente bien para pasársela a la gente de producto o de negocio que te pregunta estas cosas en las reuniones.


    Por qué esta distinción te cambia decisiones reales

    Aquí es donde la teoría empieza a pagar facturas.

    Entender que el sistema estima en lugar de saber explica de golpe los cinco fallos que más tiempo te hacen perder, y cambia la decisión que tomas en cada uno.

    Lo que ves Causa real Qué hacer
    Inventa un método o una cita "No lo sé" es una continuación menos probable que una respuesta bien redactada Verificador delante: schema, tipos, consulta a la fuente
    "Olvida" lo que dijiste 40 mensajes atrás Cada petición es stateless; alguien recortó el historial que se reenvía Curar el contexto antes de cambiar de modelo
    Falla sumando cuatro cifras Los números entran partidos en tokens: predice, no calcula Darle una herramienta: intérprete, calculadora, query
    No conoce algo de esta semana Sin herramienta de búsqueda solo tiene sus parámetros y tu contexto Declarar la tool de búsqueda o inyectar el dato con RAG
    Dos ejecuciones idénticas dan salidas distintas Hay muestreo; con seed las salidas son mostly deterministic, no deterministas Testear propiedades, no igualdad literal

    Las cinco filas son el mismo hecho visto desde cinco ángulos.

    No hay ningún paso de comprobación

    Si el sistema devuelve continuaciones probables, "no lo sé" es solo otra continuación posible, y suele ser menos probable que una respuesta con pinta de correcta. En ningún momento se pregunta si lo que escribe es verdad: no es que se salte la comprobación, es que la comprobación no existe.

    En septiembre de 2025, investigadores de OpenAI y Georgia Tech publicaron Why Language Models Hallucinate, que sostiene que el problema es corregible: el entrenamiento y las evaluaciones premian adivinar por encima de reconocer incertidumbre. Mientras ese incentivo siga ahí, ningún dato factual del modelo debería entrar en tu sistema sin un verificador delante.

    Desarrollé el mecanismo completo —por qué ocurre, cómo se detecta y qué se puede hacer— en por qué la IA se inventa cosas.

    Qué es la ventana de contexto de un LLM (y por qué no es memoria)

    La ventana de contexto es el número máximo de tokens que caben en una sola petición, sumando lo que envías y lo que el modelo genera. No es memoria: es el tamaño del formulario que rellenas en cada llamada. Con la regla de 0,75 palabras por token, una ventana de 200.000 tokens equivale a unas 150.000 palabras por petición, y se vacía entera en la siguiente.

    El modelo no recuerda nada. La documentación de OpenAI lo dice con esas palabras: cada petición de generación es independiente y stateless, y para varios turnos tienes que reenviar los mensajes anteriores en cada llamada.

    Lo que percibes como "la conversación" es un array que alguien vuelve a mandar entero cada vez —tu código, o el servidor del proveedor si delegas el estado— y tiene que caber en la ventana de contexto junto con la respuesta que se genera.

    Cuando el chat "olvida" lo de hace cuarenta mensajes es porque alguien decidió recortar o resumir ese historial. Ese alguien es el producto, no el modelo.

    De ahí que curar lo que entra en la ventana rinda más que cambiar de modelo, y que escribir una especificación antes de delegar funcione tan bien: le das el contexto exacto en lugar de esperar que lo adivine. Es la metodología que documenté en el libro de Spec-Driven Development.

    Las herramientas pesan más que el modelo

    Si el modelo predice texto, todo lo que requiera verdad, cálculo o estado tiene que venir de fuera.

    Eso es RAG: recuperar documentos relevantes y meterlos en el contexto antes de generar, idea formalizada por Lewis et al. en 2020 en Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks, que combina memoria paramétrica y no paramétrica. Y es también una calculadora o una query a tu base de datos.

    Ojo con la búsqueda web, el malentendido más común: es una herramienta, no una capacidad del modelo. En la API de OpenAI se declara en el array de tools y el modelo decide si la usa. Un LLM a pelo no consulta internet.

    Con eso claro, "¿qué modelo es mejor?" pierde interés frente a "¿qué contexto y qué herramientas le estoy dando?". Ese desplazamiento es, en el fondo, cómo ha cambiado el trabajo del developer con IA.

    No es una función pura, y tu CI necesita saberlo

    Como hay muestreo, la misma entrada no produce la misma salida. La temperatura no es un ajuste de creatividad: es cuánto aplanas o afilas la distribución antes de muestrear. Por encima de 1 la aplanas y entran tokens improbables; por debajo de 1 la afilas; en 0 te quedas siempre con el más probable.

    Y aunque la fijes a cero y pases un seed, no tienes determinismo garantizado. El cookbook de OpenAI sobre el parámetro seed habla de salidas mostly deterministic y avisa de que el determinismo no está garantizado. De hecho expone un system_fingerprint precisamente porque la configuración del backend cambia por debajo sin que tú lo pidas.

    Traducción para tu pipeline: no compares la salida literal de un LLM en un test. Testea propiedades —que el JSON valide contra el schema, que el código compile, que estén los campos obligatorios—. Un expect(salida).toBe("...") contra un modelo es un test rojo esperando su turno.


    Lo que la inteligencia artificial no es

    No razona como un humano. Los modelos de razonamiento se entrenan con refuerzo para generar más tokens intermedios antes de responder, y eso mejora mucho los problemas de varios pasos. Pero es el mismo mecanismo con más pasadas: más recorrido, no otra naturaleza.

    No consulta internet por defecto. Sin una herramienta de búsqueda conectada solo tiene lo que quedó en sus parámetros y lo que tú le pegas en el contexto.

    No aprende de tus conversaciones. Durante la inferencia los pesos están congelados: nada de lo que escribes modifica el modelo. Que tus datos se usen para entrenar versiones futuras es política de producto, no mecánica; en la API de OpenAI, por ejemplo, no se usan salvo opt-in explícito, y conviene revisar la política de tu plan porque cambia entre proveedores.

    No es determinista. Trátalo como un servicio externo con variabilidad, no como una función de tu código.

    No es inteligencia artificial general. Todo lo que hay hoy en producción es IA estrecha: sistemas entrenados para una distribución concreta de tareas. La AGI, un sistema con competencia general equivalente a la humana, sigue siendo un objetivo declarado de los laboratorios y no un producto que puedas llamar por API. Cuando un titular afirma que "la IA ya piensa", casi siempre está midiendo un benchmark estrecho.


    Qué hacer con esto mañana

    1. Cambia la pregunta cuando falle. De "¿el modelo sabe esto?" a "¿está esto en el contexto que le he enviado?". Imprime el payload real antes de culpar al modelo: casi siempre el problema estaba ahí.

    2. Pon un verificador entre el modelo y tu sistema. Schema, tipos, un test, una consulta a la fuente. Si la salida entra directa a producción sin filtro, no tienes una feature de IA: tienes una lotería bien presentada.

    3. Elimina los tests de igualdad literal sobre salidas de LLM. Sustitúyelos por comprobaciones de propiedades, y hazlo esta semana.

    4. Escribe lo que quieres antes de pedirlo. Un párrafo de especificación con el resultado esperado y sus límites vale más que veinte iteraciones de prompt: lo que no esté escrito lo rellenará con lo más probable.


    La frase que te llevas

    La próxima vez que alguien te pregunte si la IA piensa, tienes una respuesta mejor que sí o no.

    No piensa. Estima qué sigue. Y lo hace tan bien que a veces lo confundimos con pensar.

    Cambiar la metáfora del cerebro por la de una función de predicción no te vuelve más escéptico ni te quita potencia. Te vuelve mejor ingeniero: dejas de sorprenderte por lo que hace el sistema y empiezas a diseñar alrededor de sus límites reales.

    Si el siguiente paso es pasar de escribir prompts a construir sistemas que trabajen solos, empieza por la guía para dar el salto a los agentes de IA. Y si quieres el camino completo, de la idea al producto con especificaciones, herramientas y verificación, es lo que montamos paso a paso en el curso Construye con IA. Si prefieres hacerlo acompañado, te espero en Dominicode Labs.


    Preguntas frecuentes

    ¿Qué es la inteligencia artificial, en una sola frase?

    Es el campo que construye sistemas capaces de resolver tareas que asociamos a la inteligencia humana. En su forma dominante hoy —los modelos de lenguaje— el mecanismo es una función con miles de millones de parámetros que, dada una secuencia de tokens, estima la probabilidad del siguiente y repite ese paso hasta completar la respuesta. No hay comprensión ni intención: hay estimación estadística sobre secuencias.

    ¿La inteligencia artificial piensa o razona de verdad?

    No en el sentido en que lo hace una persona. Un modelo no forma una intención y luego la expresa: genera la continuación más probable token a token. Los llamados modelos de razonamiento producen más tokens intermedios antes de la respuesta final, lo que mejora bastante los problemas de varios pasos, pero siguen ejecutando el mismo mecanismo predictivo con más pasadas. Cambia la cantidad de proceso, no su naturaleza.

    ¿Cuál es la diferencia entre inteligencia artificial, machine learning y un LLM?

    Son capas anidadas. La inteligencia artificial es el campo completo e incluye técnicas sin aprendizaje, como los sistemas de reglas o la búsqueda. El machine learning es un método dentro de ese campo: en lugar de programar las reglas, se ajustan parámetros a partir de datos. Un LLM es un tipo concreto de modelo de machine learning, una red neuronal transformer entrenada para predecir el siguiente token. Y el chat que usas no es ninguna de las tres: es un producto que envuelve un LLM.

    ¿La IA aprende de mis conversaciones?

    El modelo no se modifica mientras hablas con él: durante la inferencia sus pesos están congelados y nada de lo que escribes cambia sus parámetros. Cuestión distinta es si el proveedor guarda tus datos y los usa para entrenar versiones futuras, y eso depende del plan y de la empresa. En la API de OpenAI, por ejemplo, los datos no se usan para entrenar salvo opt-in explícito.

    ¿Por qué un LLM falla en operaciones matemáticas sencillas?

    Porque no calcula: predice texto. Los números se dividen en tokens antes de entrar al modelo, así que una cifra larga no llega como cantidad sino como una serie de fragmentos. Lo que estima es cómo suele continuar un cálculo escrito, no cuál es su resultado. Por eso la solución correcta es darle una herramienta —un intérprete, una calculadora, una query— en lugar de confiar en su aritmética.


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

  • RAG vs fine-tuning vs contexto: cuándo usar cada uno

    RAG vs fine-tuning vs contexto: cuándo usar cada uno

    Un cliente me mostró su arquitectura hace unos meses. Había pasado seis semanas haciendo fine-tuning de un modelo para que respondiera preguntas sobre la documentación interna de su empresa.

    Seis semanas. Un dataset de 4.000 pares de pregunta-respuesta construidos a mano. Costes de entrenamiento en GPU. Y al final, el sistema seguía inventándose respuestas cuando la pregunta tocaba un documento que no estaba en el training data.

    Le pregunté por qué no había usado RAG. Me dijo que pensó que fine-tuning era "la solución profesional". Que RAG era para hacer demos rápidas.

    Esa diferencia entre RAG y fine-tuning se malentiende constantemente, y el malentendido sale caro. Pero hay algo peor: casi nadie considera la tercera opción, que es la más barata de las tres y resuelve más casos de los que parece.


    ¿Cuál es la diferencia entre RAG, fine-tuning y contexto?

    La diferencia entre RAG y fine-tuning es qué problema resuelve cada uno: RAG le da al modelo información que no tiene, fine-tuning le cambia la forma de comportarse. Y hay una tercera vía que va antes de las dos: pegar el material en el contexto de la petición. Esta es la tabla que conviene tener delante en una reunión de presupuesto.

    Qué es Cuándo Coste Dónde se rompe
    Contexto Se lo pegas tú en la petición Poco material, uso puntual Bajo, pero lo pagas en cada llamada No cabe, o se pierde lo del medio
    RAG Lo busca en tus documentos al preguntar Mucho material, y que cambia Medio, sobre todo el pipeline Si busca mal, responde mal
    Fine-tuning Ajustas el modelo con ejemplos Formato y tono muy propios, o clasificar y extraer en tu dominio Alto, y se repite con cada modelo nuevo No sirve para meter datos

    La regla, en una línea: contexto para lo puntual, RAG para lo que sabes, fine-tuning para cómo lo dices.

    Si alguien propone fine-tuning para que el modelo conozca vuestros datos, hay una conversación pendiente antes de firmar nada.


    El error conceptual que lo complica todo

    La mayoría de developers que se acercan a este problema lo enmarcan mal desde el principio.

    Piensan en términos de "qué técnica es más potente". Y ahí ya van por el camino equivocado.

    La pregunta correcta no es cuál es más potente. Es: ¿qué problema tienes exactamente?

    Si tu modelo no sabe cosas que necesita saber — información privada, documentos internos, datos recientes — tienes un problema de conocimiento. Contexto o RAG lo resuelven.

    Si tu modelo sabe las cosas pero no las comunica como necesitas — tono diferente, formato específico, comportamiento distinto al por defecto — tienes un problema de comportamiento. Fine-tuning lo resuelve.

    Son problemas distintos. Las soluciones no son intercambiables. Y aquí está la frase que ahorra más dinero de todo este terreno:

    El fine-tuning enseña comportamiento, no información.

    Si le haces fine-tuning con el catálogo de productos, no acabas con un modelo que se sepa el catálogo. Acabas con uno que habla como tu catálogo e inventa referencias con el estilo exacto de las tuyas. Bastante peor que no hacer nada, porque las invenciones son más creíbles.


    Contexto: la opción que deberías agotar primero

    Es la que se salta todo el mundo, y en muchos proyectos es la única que hacía falta.

    Contexto es, literalmente, pegar la información en la petición. El manual, el fragmento de código, las tres facturas de ejemplo. Sin infraestructura, sin pipeline, sin base de datos vectorial. Es lo que la literatura llama in-context learning y lo que en la práctica se acaba llamando prompt stuffing.

    Sus dos límites reales:

    No cabe todo. La ventana de contexto es el número máximo de tokens que entran en una sola petición, sumando lo que envías y lo que el modelo genera. Los modelos actuales manejan ventanas grandes, pero grande no es infinito y el coste sube con lo que metes.

    Se paga en cada llamada. No es una inversión que amortices: es un peaje por petición. Mil consultas al día con el mismo manual de 20.000 tokens delante son veinte millones de tokens al día de material repetido.

    Ese segundo problema tiene solución y casi nadie la aplica, así que le dedico una sección propia más abajo.

    Y una confusión que conviene cortar aquí: la ventana de contexto no es memoria. Una ventana enorme te deja meter mucho de una vez. Memoria es que algo sobreviva a cerrar la sesión. Son cosas distintas y una no da la otra. Cuando un proveedor te venda un asistente "con memoria", la pregunta útil no es si la tiene, sino qué guarda, dónde y durante cuánto tiempo.


    Qué es RAG (Retrieval-Augmented Generation) y cuándo usarlo

    RAG no modifica el modelo. El modelo base sigue siendo exactamente el mismo.

    Lo que hace es intervenir en el momento en que llega una pregunta. Antes de pasársela al modelo, busca en una base de datos vectorial los fragmentos de tus documentos más relevantes para esa consulta, y los inyecta en el prompt. El modelo entonces responde con acceso real a esa información.

    Usuario pregunta: "¿Cuál es la política de devoluciones?"
                             ↓
                 Sistema RAG busca en vectorDB
                             ↓
          Encuentra: chunk del doc "politica-devoluciones-2026.pdf"
                             ↓
        Prompt al modelo: "Contexto: [chunk]. Pregunta: ¿Cuál es...?"
                             ↓
                Modelo responde con información real
    

    La ventaja clave: tus documentos pueden cambiar mañana. Actualizas la base vectorial. El modelo ya tiene acceso a la nueva información. Sin reentrenar nada.

    Visto así, RAG es contexto automatizado: en vez de pegar tú el fragmento correcto, un buscador lo elige por ti en cada petición. Por eso la pregunta de diseño en RAG no es qué modelo usas, es si tu buscador encuentra lo que hace falta.

    Si quieres verlo montado con código, tengo el paso a paso en implementación de RAG en Angular, y las estrategias de búsqueda híbrida en RAG avanzado. Y si estás eligiendo el modelo para el componente generativo, este análisis sobre el mejor modelo LLM local en 2026 te ayuda a no sobreingenierizar la infraestructura.

    Esto es lo que lo hace ideal para documentación interna, bases de conocimiento, FAQs, soporte técnico — cualquier caso donde la información cambia y necesitas que el modelo cite fuentes reales en lugar de fabricar respuestas.

    El límite de RAG está en que no cambia cómo se comporta el modelo. Si necesitas que responda en un tono muy específico, siga un formato exacto, o haga razonamientos que el modelo base no hace bien de forma natural, RAG no te ayuda. Solo le das más información. No lo entrenas.


    Qué es fine-tuning de LLMs y cuándo tiene sentido aplicarlo

    Fine-tuning sí modifica el modelo. Tomas un modelo base preentrenado y lo sigues entrenando con tu propio dataset, ajustando sus pesos para que aprenda los patrones que te interesan.

    El resultado es un modelo diferente. Uno que ha interiorizado un estilo, un formato, un tipo de razonamiento específico. No necesitas darle instrucciones en el prompt porque ya las tiene grabadas en sus pesos.

    # Sin fine-tuning: necesitas el prompt completo
    prompt = """Eres un asistente técnico especializado en Kubernetes.
    Responde siempre con: 1) causa del problema, 2) solución paso a paso,
    3) cómo prevenirlo. Usa terminología técnica precisa. No añadas
    disclaimers. El tono es directo, de senior a senior.
    
    Problema: Mi pod no arranca después de actualizar la imagen..."""
    
    # Con fine-tuning: el modelo ya sabe cómo comportarse
    prompt = "Problema: Mi pod no arranca después de actualizar la imagen..."
    

    El modelo fine-tuneado responde directamente en el formato correcto porque ese comportamiento está en sus pesos. No porque se lo estés recordando en cada llamada.

    Lo que fine-tuning no resuelve: inyectar conocimiento factual nuevo. Si entrenas el modelo en el estilo de tu empresa pero no en los documentos de tu empresa, seguirá sin saber qué contienen esos documentos. Habrá aprendido a comunicarse como tú quieres, pero no a responder con información real que no tenía.

    Y no es entrenar desde cero. Eso cuesta una fortuna y lo hacen muy pocas organizaciones en el planeta. Esto es un retoque sobre un modelo existente.


    La noticia que cambia el cálculo: OpenAI está cerrando su fine-tuning

    Si tomaste esta decisión antes de mayo de 2026, vuelve a tomarla. El tablero ha cambiado.

    OpenAI está retirando su plataforma de fine-tuning self-serve. Lo dice en su propia página de deprecations, con fechas:

    Fecha Qué pasa
    7 de mayo de 2026 Las organizaciones que nunca habían hecho fine-tuning ya no pueden empezar
    2 de julio de 2026 Y tampoco las que llevan 60 días sin ejecutar inferencia sobre un modelo fine-tuneado
    6 de enero de 2027 Los clientes activos dejan de poder crear jobs de fine-tuning

    La inferencia sobre modelos ya fine-tuneados sigue funcionando hasta que se deprecie el modelo base. Y en la guía de fine-tuning la propia OpenAI dice que la plataforma "no es accesible para usuarios nuevos", con una lista de modelos soportados que se ha quedado en la familia gpt-4.1 más gpt-4o para visión y o4-mini para refuerzo.

    Lo interesante es a dónde te manda su propia documentación: al ciclo de evals más prompt engineering. Contexto relevante, instrucciones claras, ejemplos few-shot y, textualmente, "start with gpt-5.6 for new work". Es decir, la primera columna de la tabla de arriba, ejecutada sobre el modelo más nuevo que tengas a mano.

    Y un matiz antes de que alguien lo lea como "el fine-tuning ya no sirve": la misma guía sigue listando como caso válido entrenar un modelo más pequeño, más barato y más rápido para una tarea concreta donde uno grande no sale a cuenta. Eso es exactamente el Caso 4 de más abajo.

    Qué significa esto en la práctica:

    • Si estabas planteándote fine-tuning en OpenAI y no lo has hecho nunca, esa puerta ya está cerrada. No es una decisión pendiente.
    • El fine-tuning sigue existiendo fuera de OpenAI — modelos abiertos con LoRA o QLoRA, y otros proveedores.
    • Pero cuando el proveedor más grande te dice que uses un modelo mejor con mejores prompts en lugar de ajustar uno peor, merece la pena escuchar el argumento antes de montar un pipeline de entrenamiento.

    No es que el fine-tuning haya dejado de servir. Es que el rango de casos donde gana se ha estrechado, y los modelos base han absorbido buena parte de lo que antes justificaba entrenarlos.


    RAG vs fine-tuning: la matriz de decisión con cuatro casos reales

    Hay cuatro combinaciones que aparecen una y otra vez en proyectos reales.

    Caso 1: Chatbot sobre documentación interna

    Necesitas que el modelo responda preguntas sobre tus PDFs, wikis, Notion, Confluence. La información cambia regularmente. El tono puede ser el del modelo base.

    Solución: RAG. Indexas los documentos en una vectorDB (pgvector, Pinecone, Weaviate), configuras el pipeline de retrieval, y el modelo responde con fuentes reales.

    Pero antes de montarlo: si son cuatro documentos que caben en el contexto y cambian una vez al trimestre, empieza por contexto y ahórrate el pipeline. RAG paga cuando el material no cabe o cambia a diario.

    Caso 2: Generador de código en el estilo de tu empresa

    Quieres que el modelo genere código que siga tus convenciones internas, use tus abstracciones propias, evite los patrones que prohíbes.

    Solución clásica: fine-tuning. Pero prueba primero con un documento de convenciones en el contexto y tres o cuatro ejemplos antes/después. Los modelos actuales siguen instrucciones de formato mucho mejor que los de hace dos años.

    Veredicto hoy: contexto primero. Fine-tuning solo si el prompt con convenciones y ejemplos falla de forma medible, no porque el resultado te parezca mejorable.

    Caso 3: Asistente de soporte que responde sobre tus productos Y en tu tono

    Quieres las dos cosas: información factual que cambia, y un comportamiento de comunicación muy específico.

    Solución: RAG para la información, y el comportamiento en el prompt de sistema. Si tras iterar el prompt el formato sigue siendo inconsistente y tienes miles de ejemplos buenos, entonces fine-tuning para la parte de comportamiento. En ese orden, no al revés.

    Caso 4: Clasificador de texto o extractor de entidades

    Necesitas clasificar tickets, extraer entidades de contratos, tareas de NLP muy específicas.

    Aquí es donde fine-tuning sigue defendiéndose mejor: para clasificación y extracción, un modelo pequeño ajustado a tu dominio suele salir más barato en inferencia que uno grande con prompts largos, y más consistente. Es el caso con mejor retorno de los cuatro.


    Los costes reales

    Contexto:

    • Desarrollo: prácticamente nulo. Es construir un prompt.
    • Inferencia: pagas los tokens de entrada en cada petición, así que el coste escala con el volumen, no con el tamaño del proyecto.
    • Mantenimiento: cambiar el texto.
    • Problema principal: el material repetido se paga una y otra vez — salvo que uses caché.

    RAG:

    • Configurar el pipeline de chunking, embedding y retrieval: días de desarrollo, no semanas.
    • Inferencia: coste del modelo base más las llamadas a la vectorDB, que son baratas.
    • Mantenimiento: actualizar la base vectorial cuando cambian los documentos, y es automatizable.
    • Problema principal: la calidad del retrieval. Si buscas mal, el modelo responde mal aunque los documentos sean perfectos.

    Fine-tuning:

    • Construir el dataset de entrenamiento: semanas. Es el cuello de botella real, no la GPU.
    • Entrenamiento: la estructura del coste es por tokens procesados o por horas de GPU según proveedor. Los precios concretos cambian cada pocos meses, así que consúltalos en el proveedor el día que decidas: cualquier cifra que leas en un post de hace medio año está mal.
    • Inferencia: más cara que el mismo modelo sin ajustar. En un proveedor gestionado el modelo fine-tuneado tiene su propia tarifa por token, más alta que la del base, sin que tú hostees nada; con modelos abiertos lo pagas en infraestructura. Por los dos caminos, más que el punto de partida.
    • Problema principal: te ancla a una versión. Cada modelo nuevo obliga a repetir el proceso entero mientras el resto del mundo avanza gratis.

    Ese último punto es el que más se subestima. El coste del fine-tuning no es el entrenamiento: es quedarte fuera de la siguiente generación de modelos.


    La caché de contexto: la palanca que casi nadie activa

    Si tienes un sistema que atiende mil consultas al día y en cada una envía las mismas instrucciones, el mismo manual y los mismos ejemplos, estás pagando por procesar ese material mil veces.

    La caché de contexto guarda el trabajo ya hecho sobre la parte que se repite y lo reutiliza. La parte repetida sale bastante más barata a partir de la segunda vez.

    La condición es exigente y es donde falla todo el mundo: lo repetido tiene que ir siempre al principio y sin variar ni un carácter.

    ✅ CACHEABLE
       [instrucciones fijas][manual fijo][ejemplos fijos][consulta variable]
    
    ❌ NO CACHEABLE
       [fecha y hora][instrucciones fijas][manual fijo][consulta variable]
        ↑ un timestamp al principio invalida el bloque entero
    

    Meter la fecha, un identificador de sesión o el nombre del usuario al comienzo del bloque fijo lo invalida todo. Y nadie te avisa: simplemente sigues pagando el precio completo.

    Hay además un suelo del que casi no se habla: por debajo de unos cientos o unos miles de tokens —el umbral cambia por proveedor y por modelo— la caché no se activa. Tampoco salta un error. Se procesa a precio completo y a otra cosa.

    Las condiciones exactas —qué se cachea, cuánto dura, cuánto descuenta— son distintas en cada proveedor y cambian cada pocos meses. Contrasta antes de contar con el ahorro.

    Si tu factura de IA empieza a crecer, esta es la primera pregunta que hay que hacer en la reunión, antes de discutir de modelos: ¿estamos aprovechando la caché de contexto?


    Qué pasa cuando las combinas

    La combinación que se ve en sistemas de producción serios sigue un patrón concreto. Y es parte de una arquitectura más amplia — si quieres entender cómo el LLM encaja con el resto del sistema, el post sobre qué es un agent harness lo explica con detalle.

    • Contexto para las instrucciones y el comportamiento, con la parte fija cacheada
    • RAG para la información factual que cambia
    • Fine-tuning solo si el comportamiento sigue siendo inconsistente después de agotar las dos anteriores

    Un ejemplo real: un asistente jurídico. El prompt de sistema fija el formato del análisis y la terminología, y va cacheado porque no cambia. RAG conectado a la base de legislación actualizada y a los expedientes del despacho. Fine-tuning, en este caso, ni aparece: el modelo base ya redacta en registro jurídico si se le pide bien.

    Esa es la arquitectura que más veo en productos de IA que funcionan. No es glamorosa. En el curso Construye con IA: de la idea al producto con Claude Code trabajo estas decisiones desde la fase de especificación — antes de escribir una línea de código — para que no llegues a la semana seis arrepintiéndote de la técnica que elegiste.


    El árbol de decisión que uso en consultoría

    Cuando alguien me pregunta qué usar, estas son las preguntas en orden:

    1. ¿Cabe el material en el contexto y cambia poco?

    • Sí → contexto, y cachea la parte fija. Ya está, no montes nada más.
    • No cabe, o cambia a diario → siguiente pregunta.

    2. ¿El problema es que el modelo no tiene la información, o que no se comporta como quieres?

    • No tiene la información → RAG
    • No se comporta bien → siguiente pregunta

    3. ¿Has iterado el prompt de sistema en serio, con ejemplos few-shot?

    • No → hazlo antes. Es gratis y resuelve más de lo que la gente espera.
    • Sí, y sigue inconsistente → siguiente pregunta

    4. ¿Tienes miles de ejemplos de calidad y un problema bien definido que no va a cambiar?

    • No → sigue con prompting. Fine-tuning sin dataset bueno es dinero quemado.
    • Sí → fine-tuning es defendible, siempre que el proveedor te deje.

    La mayoría de los casos que veo en producción se resuelven en los dos primeros escalones. Fine-tuning es potente, pero exige un problema muy bien definido, datos de calidad y tiempo para construirlos.


    Tabla comparativa detallada: RAG vs fine-tuning

    RAG Fine-tuning
    Problema que resuelve El modelo no tiene la información El modelo no se comporta como quieres
    Modifica el modelo No
    Cuándo usar Datos dinámicos, documentos, bases de conocimiento Estilo, formato, clasificación de dominio
    Coste de inicio Medio (pipeline) Alto (dataset + entrenamiento)
    Mantenimiento Fácil (actualizar vectorDB) Costoso (reentrenar cuando cambia el problema)
    Tiempo hasta producción Días Semanas
    Te ancla a un modelo No
    Combinar con el otro
    Disponible en OpenAI (julio 2026) Cerrado a usuarios nuevos; los activos, hasta el 6 de enero de 2027

    Guarda esta tabla. Te va a ahorrar más de una conversación.

    Y si te ha servido ordenar estas tres piezas, hay un mapa con las otras ciento diecinueve: conceptos de IA colocados por zonas, con las conexiones dibujadas, en una hoja para imprimir. Está aquí, gratis — funciona especialmente bien para pasársela a la gente de producto que aprueba estos presupuestos.


    Preguntas frecuentes

    ¿Cuál es la diferencia entre RAG y fine-tuning?

    RAG no toca el modelo: busca fragmentos relevantes en tus documentos y los inyecta en el prompt antes de generar, así que resuelve problemas de conocimiento y se actualiza cambiando un documento. Fine-tuning sí modifica el modelo, ajustando sus pesos con tus ejemplos, y resuelve problemas de comportamiento — tono, formato, criterio de clasificación. La confusión más cara del sector es usar fine-tuning para meter información: no acabas con un modelo que sepa tus datos, sino con uno que inventa datos con tu estilo.

    ¿Cuándo usar fine-tuning de verdad?

    Cuando se cumplen las cuatro condiciones a la vez: el problema es de comportamiento y no de información, ya has iterado el prompt de sistema con ejemplos few-shot y sigue inconsistente, tienes miles de ejemplos de calidad, y la tarea está lo bastante definida como para que no cambie en unos meses. El caso con mejor retorno es la clasificación o extracción de dominio, donde un modelo pequeño ajustado sale más barato en inferencia que uno grande con prompts largos.

    ¿Es verdad que OpenAI está cerrando el fine-tuning?

    Sí, la plataforma self-serve. Según su página de deprecations, desde el 7 de mayo de 2026 las organizaciones que no habían hecho fine-tuning antes ya no pueden crear jobs, y el 6 de enero de 2027 dejarán de poder hacerlo también los clientes activos. La inferencia sobre modelos ya ajustados sigue hasta que se deprecie el modelo base. OpenAI redirige en su lugar a su ciclo de evals y prompt engineering —contexto relevante, instrucciones claras, ejemplos few-shot— y a empezar por su modelo más reciente. El fine-tuning sigue disponible fuera de OpenAI, con modelos abiertos y otros proveedores.

    ¿Qué es la caché de contexto y cuánto ahorra?

    Es guardar el trabajo de procesamiento ya hecho sobre la parte del prompt que se repite en todas las peticiones —instrucciones, manuales, ejemplos— para no volver a pagarla entera cada vez. El requisito es que ese bloque vaya al principio y sea idéntico carácter por carácter: meter un timestamp o un ID de sesión delante lo invalida y sigues pagando el precio completo sin que nadie te avise. Cuánto descuenta y cuánto dura la caché varía por proveedor y cambia cada pocos meses, así que conviene comprobarlo en la documentación antes de meter el ahorro en un presupuesto.

    ¿Qué sale más barato, RAG o fine-tuning?

    RAG, casi siempre, y por dos motivos distintos: el arranque son días de pipeline frente a semanas de dataset, y no te ancla a una versión del modelo, así que no repites el gasto con cada generación nueva. Fine-tuning solo gana en coste cuando la tarea es de clasificación o extracción y puedes servir un modelo pequeño en lugar de uno grande con prompts largos. Antes de comparar las dos, mira si tu prompt fijo es cacheable: en sistemas con mucho volumen esa palanca sola suele mover más la factura que la elección de técnica.

    ¿Cuánto cuesta hacer fine-tuning?

    Tiene tres partidas y solo una no caduca. El dataset son semanas de trabajo humano y es el cuello de botella real. El entrenamiento se factura por tokens procesados o por horas de GPU según el proveedor. La inferencia va a una tarifa más alta que la del modelo base. Cualquier cifra concreta que leas en un post de hace medio año está mal, así que consulta el pricing del proveedor el día que decidas — pero cuenta con que la partida del dataset no la abarata nadie.

    ¿Cambia algo con los modelos de razonamiento?

    Bastante, y en la dirección de necesitar menos fine-tuning. Siguen instrucciones complejas mucho mejor que las generaciones anteriores, así que se comen buena parte de los casos de comportamiento que antes justificaban entrenar un modelo. Lo que no cambia es el acceso a la información: un modelo de razonamiento no conoce tus documentos privados ni lo que pasó esta semana. Para eso RAG sigue siendo la única vía.

    ¿Puedo usar RAG con cualquier LLM?

    Sí. RAG es agnóstico al modelo: funciona con cualquiera que acepte un prompt de texto, sea de OpenAI, Anthropic, Google o un modelo abierto que corras tú. Lo único que necesita es una ventana de contexto suficiente para recibir los fragmentos recuperados junto con la pregunta, y los modelos actuales van sobrados para eso en la mayoría de casos.

    ¿La ventana de contexto es lo mismo que memoria?

    No, y se confunden constantemente. La ventana de contexto es cuánto cabe en una sola petición. Memoria es que algo sobreviva a cerrar la sesión, y eso no está dentro del modelo: es un almacén externo que alguien montó, más una decisión sobre qué merece la pena guardar y recuperar. Una ventana enorme no te da memoria, y por eso un asistente puede seguirte el hilo durante media hora y no conocerte de nada al día siguiente.

    ¿RAG siempre alucina menos que el modelo base?

    Reduce las alucinaciones sobre hechos concretos de tus documentos, porque el modelo tiene el texto real delante. Pero no elimina las que vienen de razonamientos o inferencias mal hechas: si el modelo falla razonando, RAG no le ayuda, porque eso es un problema de capacidad y no de conocimiento. Recuperar bien y responder bien son dos cosas distintas.

    ¿Qué vectorDB conviene para empezar?

    pgvector si ya usas PostgreSQL, porque no añade infraestructura nueva, o un servicio gestionado como Pinecone si prefieres no operar nada. Weaviate y Chroma son buenas opciones open-source para auto-hosting. Evita sobreingenierizar esto al principio: pgvector resuelve la mayoría de los casos sin complejidad extra, y su documentación oficial cubre la instalación en unos minutos.


    Si quieres ver estos patrones aplicados en proyectos reales con código y arquitectura completa, en Dominicode Labs trabajamos este tipo de decisiones técnicas con la comunidad. Proyectos reales, problemas reales, decisiones que puedes aplicar esta semana.


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

  • Cómo gestionar la memoria en agentes entre sesiones de forma eficiente

    Cómo gestionar la memoria en agentes entre sesiones de forma eficiente

    Context Engineering — el arte de gestionar el contexto del agente entre sesiones

    Tiempo estimado de lectura: 5 min

    • Context Engineering es disciplina operativa para mantener la memoria de agentes entre sesiones y evitar que “arranquen desde cero”.
    • Tres archivos clave (CLAUDE.md, AGENTS.md, memory files) convierten reglas, roles y memoria en infraestructura reutilizable.
    • Arquitectura de sub-agentes (orquestador, especialistas, QA) minimiza ruido y mejora precisión, latencia y coste.
    • Prácticas concretas: cierre de sesión obligatorio, outputs estructurados (JSON/Zod), RAG con caché semántico y auditoría por ejecución.

    Context Engineering es la práctica de diseñar y mantener la memoria operativa de agentes entre sesiones. Si aplicas GSD a equipos humanos, Context Engineering aplica GSD a agentes: dividir tareas, documentar decisiones y asegurar que la próxima sesión no sea una pizarra en blanco. Sin esta disciplina, los agentes alucinan, revierten decisiones y se vuelven caras e ineficaces.

    Resumen rápido (lectores con prisa)

    Context Engineering diseña la memoria y reglas permanentes de agentes para mantener continuidad entre sesiones. Úsalo cuando un sistema de agentes necesita precisión, trazabilidad y bajo coste operativo. Importa porque evita alucinaciones, pérdida de progreso y decisiones reversibles. Funciona con tres capas: reglas estáticas, memoria dinámica y sub-agentes especializados.

    El problema

    El problema es elegante y directo: los LLMs razonan bien, pero olvidan. El historial de chat como única memoria funciona en demos; colapsa en repositorios reales. Más tokens en el prompt solo añaden ruido. La solución práctica es convertir el repositorio en la memoria del agente mediante tres capas: reglas estáticas, memoria dinámica y sub-agentes especializados.

    Tres archivos que cambian el juego: CLAUDE.md, AGENTS.md y memory files

    CLAUDE.md (o .cursorrules) — la “constitución” del proyecto

    Propósito: reglas inmutables que el agente debe respetar.

    Contenido práctico: stack exacto (Node 20, React 18), políticas de seguridad, restricciones arquitectónicas y decisiones no negociables.

    Ejemplo (resumen):

    • Stack: Node 20, TypeScript 5.x, Next.js 14.
    • Convenciones: ESLint + Prettier; tests en Vitest.
    • Restricciones: “Nunca acceder a Supabase desde cliente; usar Server Actions”.

    CLAUDE.md es lo primero que un agente debe leer al comenzar.

    AGENTS.md — el organigrama legible por máquinas

    Propósito: definir quién hace qué en un sistema multi-agente.

    Contenido práctico: routing de tareas, roles (FrontendWorker, DBOptimizer, QAReviewer), permisos para commits.

    Resultado: orquestador capaz de delegar la tarea correcta al sub-agente correcto sin mandar TODO el repo como contexto.

    Memory files — la memoria viva (.context/ o .ai/)

    active_task.md: la tarea actual, objetivo y criterios de éxito.

    changelog_ai.md: decisiones y porqués (no sólo el qué).

    lessons_learned.md: problemas recurrentes y soluciones definitivas.

    Rutina obligatoria: al cerrar sesión el agente actualiza estos archivos. Al abrir sesión, los lee y retoma.

    Sub-agentes: aislar contexto, reducir ruido, mejorar precisión

    Arquitectura propuesta:

    Arquitectura propuesta

    • Agente Orquestador (Router): lee AGENTS.md, empaqueta contexto mínimo y llama al especialista.
    • Agente Especialista (Worker): recibe solo lo necesario (e.g., esquema DB + active_task.md) para reducir ruido.
    • Agente Revisor (QA): valida output contra CLAUDE.md y tests antes de aplicar cambios.

    Ventaja: un sub-agente con 10k tokens relevantes produce mejores resultados que un mega-agente con 100k tokens llenos de ruido. También reduce costos y latencia.

    Prácticas operativas (lo que realmente marca la diferencia)

    • Obliga a un “cierre de sesión”: el prompt final del día debe ser “Actualiza active_task.md, changelog_ai.md y sugiere el siguiente paso”.
    • Usa esquemas para outputs: fuerza JSON/Zod para evitar outputs malformados.
    • RAG + caché semántico: externaliza historial pesado en una vector DB y recupera solo lo necesario por tarea. (Ver LangChain)
    • Streaming y UX: implementa streaming token a token para evitar bloqueos de UI. (Vercel AI SDK: Vercel AI SDK)
    • Auditoría y permisos: registra cada ejecución de sub-agente y limita quién puede hacer merges automáticos.

    Estructura mínima recomendada del repo

    /project-root
      /src
      /.ai
        CLAUDE.md
        AGENTS.md
        active_task.md
        changelog_ai.md
        lessons_learned.md
      /.ai/logs
      /docs

    Herramientas y lecturas útiles

    Criterio final para equipos técnicos

    Context Engineering no es documentación bonita. Es infraestructura operativa que convierte agentes en miembros persistentes del equipo. Si inviertes en reglas estáticas (CLAUDE.md), memoria dinámica (memory files) y una red de sub-agentes bien definida (AGENTS.md + orquestador), ganarás precisión, velocidad y trazabilidad. Sin esto, los agentes seguirán siendo costosos “showrooms” en lugar de fuerza productiva real.

    Aplica GSD a tus agentes: trocea, documenta, cierra sesiones. Si lo haces bien, el agente no volverá a arrancar desde cero.

    Dominicode Labs

    Si te interesa profundizar en automatización, agentes y workflows aplicados a equipos técnicos, puedes continuar explorando recursos y experimentos en Dominicode Labs. Es una continuación lógica para poner en práctica patrones de Context Engineering mencionados aquí.

    FAQ

    ¿Qué es Context Engineering?

    Context Engineering es la práctica de diseñar y mantener la memoria operativa y reglas de agentes entre sesiones para mantener continuidad, reducir alucinaciones y preservar decisiones previas.

    ¿Cuándo debo usar CLAUDE.md?

    Usa CLAUDE.md al empezar cualquier sesión de agente: contiene reglas inmutables, stack, convenciones y restricciones que el agente debe respetar. Es el primer archivo que el agente debe leer.

    ¿Para qué sirve AGENTS.md?

    AGENTS.md define roles, routing de tareas y permisos en sistemas multi-agente, permitiendo al orquestador delegar correctamente sin enviar el repo entero como contexto.

    ¿Qué contienen los memory files y quién los actualiza?

    Contain archivos como active_task.md, changelog_ai.md y lessons_learned.md. El agente en sesión debe actualizarlos al cerrar sesión; el próximo agente los lee al abrir sesión.

    ¿Por qué usar sub-agentes en lugar de un mega-agente?

    Porque un sub-agente centrado con contexto relevante (p. ej. 10k tokens) produce resultados más precisos y eficientes que un mega-agente con 100k tokens de ruido. Reduce costos, latencia y errores.

    ¿Qué es RAG y por qué es relevante aquí?

    RAG (Retrieval-Augmented Generation) externaliza historial pesado en una vector DB y recupera solo lo necesario por tarea. Es relevante para evitar enviar todo el historial en cada prompt y mantener precisión.

    ¿Cómo auditar ejecuciones de sub-agentes?

    Registra cada ejecución en logs estructurados (.ai/logs), incluye metadatos (input mínimo, decision hashes) y limita permisos para merges automáticos. La auditoría debe permitir reproducir la decisión y revisar cumplimiento con CLAUDE.md.

  • Cómo Context Engineering Mejora el Uso de IA en Proyectos Técnicos

    Cómo Context Engineering Mejora el Uso de IA en Proyectos Técnicos

    Context Engineering: el skill que separa a quien usa IA de quien la domina — Diferenciar prompt engineering de context engineering, con ejemplos prácticos en proyectos reales

    Context Engineering: el skill que separa a quien usa IA de quien la domina — Diferenciar prompt engineering de context engineering, con ejemplos prácticos en proyectos reales aparece en la primera línea porque esto no es semántica fina: si quieres resultados reproducibles con LLMs, primero dominas el contexto que les das.

    Resumen rápido (lectores con prisa)

    Qué es: Context engineering diseña pipelines que recuperan, filtran, reordenan y entregan exactamente la información que un LLM necesita. Cuándo usarlo: cuando buscas respuestas reproducibles y verificables de modelos. Por qué importa: reduce ruido y evita que el modelo «adivine». Cómo funciona (resumen): chunking semántico, RAG híbrido, re-ranking y trazabilidad del contexto.

    Los modelos no fallan por malos prompts. Fallan porque les lanzas una montaña de información sin estructura y esperan sentido. El paper “Lost in the Middle” documenta por qué contextos enormes con baja señal degradan la precisión: https://arxiv.org/abs/2307.03172.

    Context Engineering: qué es y por qué importa

    Prompt engineering modela la instrucción: rol, formato, few-shot. Es importante, pero es la punta del iceberg.

    Context engineering diseña pipelines que recuperan, filtran, reordenan y entregan exactamente la información que el modelo necesita. Es infraestructura. Es código. Es la diferencia entre un agente que improvisa y uno que actúa con datos verificables.

    Herramientas y lecturas útiles:

    Principios técnicos fundamentales

    • Chunking semántico: corta por límites lógicos (funciones, clases, secciones), no por número de caracteres. Un fragmento coherente = menos ambigüedad.
    • Recuperación híbrida (RAG avanzado): combina búsqueda vectorial con BM25 y filtrado por metadatos. Cada técnica cubre puntos ciegos de la otra.
    • Re-ranking con Cross-Encoders: recupera amplio, reordena preciso. El orden que lee el LLM importa.
    • Grafos de dependencia: extrae import graph para entregar solo archivos que dependen directamente del cambio que quieres hacer.
    • Instrumentación del contexto: registra qué se inyectó, tokens consumidos y rank scores para auditar decisiones.

    Ejemplo 1 — Refactorización en un monorepo TypeScript

    Problema frecuente: cambias la firma de un endpoint y esperas que el asistente actualice todo el frontend y backend.

    Enfoque ingenuo (solo prompt)

    Copias controladores y componentes al chat. Resultado: el modelo inventa imports, omite tipos compartidos y el build falla.

    Enfoque Context Engineering (profesional)

    1) Ejecuta análisis estático con ast-grep para localizar los nodos AST que llaman al endpoint.

    2) Genera un paquete de contexto pequeño: OpenAPI actualizado, la interfaz TypeScript compartida y los snippets AST afectados.

    3) Re-ranquea los fragmentos por relevancia y adjunta tests unitarios mínimos.

    Resultado: PR atómico, compilable y con pruebas que pasan. El LLM actúa sobre lo esencial, no sobre ruido.

    Ejemplo 2 — Agente L2 en n8n que realmente resuelve incidencias

    Problema: un bot en Slack contesta “reinicia” porque carece del estado real del sistema.

    Enfoque ingenuo

    Enviar error text y prompt extenso. Respuestas genéricas.

    Enfoque Context Engineering

    Antes de llamar al LLM, el workflow hace:

    • Query a Datadog/Grafana para obtener los últimos N logs (filtrados por servicio y correlación)
    • Query SQL read-only para validar estado de cuenta/recursos del usuario
    • Búsqueda semántica en documentación interna y re-ranking para extraer la resolución exacta

    El LLM recibe un JSON estructurado con logs, estado y docs. No adivina; redacta una intervención operativa reproducible. En n8n esto se modela como nodos previos que transforman y sanitizan el contexto.

    Guía práctica: checklist para construir pipelines de contexto

    • Define señal mínima: ¿qué datos hacen que la respuesta deje de ser una suposición?
    • Implementa chunking por semántica, no por longitud.
    • Usa RAG híbrido: vector search + BM25 + metadatos.
    • Añade re-ranking con Cross-Encoders para ordenar resultados.
    • Instrumenta: guarda el contexto inyectado (hashes), tokens consumidos y scores.
    • Limita permisos y sanitiza PII antes de inyectar datos sensibles.
    • Versiona specs y pipelines; trátalos como código crítico.

    Riesgos y consideraciones operativas

    • Costo de tokens y latencia: curar contexto reduce tokens inútiles, pero re-ranking y cross-encoders añaden coste computacional.
    • Seguridad y privacidad: nunca inyectes credenciales ni expongas PII sin enmascaramiento. Diseña roles y auditable human-in-the-loop para acciones críticas.
    • Overfitting de contexto: si tu re-ranker prioriza siempre el mismo fragmento, podrías ignorar cambios recientes. Mantén ventanas temporales y freshness rules.

    Conclusión técnica

    Context Engineering no es un nicety; es la capa que convierte IA probabilística en un componente reproducible de tu stack. Los equipos que ganan no son los que escriben mejores prompts; son los que construyen pipelines que entregan al modelo exactamente la señal que necesita, en el formato correcto y con trazabilidad. Eso es lo que separa a quien usa IA de quien la domina.

    Para equipos que trabajan con automatización, agentes, n8n o workflows, explorar prácticas y experimentos adicionales puede acelerar la adopción segura y reproducible. Más recursos y pruebas de concepto están disponibles en Dominicode Labs.

    Tabla de contenidos

    FAQ

    ¿Qué diferencia a prompt engineering de context engineering?

    Prompt engineering diseña la instrucción y el formato de la interacción. Context engineering construye la infraestructura y pipelines que entregan al modelo la información relevante, limpia y ordenada para que esa instrucción produzca resultados reproducibles.

    ¿Cuándo debo priorizar construir pipelines de contexto?

    Cuando las respuestas del modelo necesitan ser verificables, reproducibles o accionables en producción—por ejemplo, cambios de código a gran escala, acciones operativas automatizadas o workflows de soporte.

    ¿Qué es chunking semántico y por qué es importante?

    Es dividir el contenido por límites lógicos (funciones, clases, secciones) en lugar de por caracteres. Reduce ambigüedad y mejora la relevancia de la información entregada al modelo.

    ¿Cómo se integra RAG híbrido en un flujo de trabajo existente?

    Combina búsqueda vectorial para semántica con BM25 para coincidencias léxicas y aplica filtros por metadatos. Recupera amplio, luego re-ranquea con Cross-Encoders para entregar la mejor señal al LLM.

    ¿Qué métricas debo instrumentar para auditar el contexto?

    Guarda hashes del contexto inyectado, tokens consumidos por llamada, scores de recuperación y re-ranking, y un registro de versiones de specs y pipelines.

    ¿Cómo mitigo riesgos de privacidad al inyectar contexto?

    Sanitiza y enmascara PII, limita permisos para queries y usa pipelines read-only para datos sensibles. Diseña revisiones humanas para acciones críticas.

    Tiempo estimado de lectura: 5 min