Alejandro Rioja.
AI Agents Operations

Prompt caching con la Claude API: reduce tus costos de entrada sin cambiar de modelo

Alejandro Rioja
Alejandro Rioja
9 min de lectura
TL;DR

El prompt caching reduce el costo de las entradas grandes y estables —tu system prompt, las definiciones de herramientas, los ejemplos few-shot— a aproximadamente el 10% del precio de entrada normal en solicitudes repetidas. El mecanismo es una coincidencia de prefijo: coloca un marcador cache_control al final de tu contenido estable y mantén todo lo volátil después de él. El error que destruye las tasas de aciertos de caché es dejar que un timestamp o un UUID se cuele en el prefijo.

Newsletter gratuita

Cada miércoles. 28.400+ operadores. Sin relleno.

Table of contents

Open Table of contents

Qué hace realmente el prompt caching

Cada llamada a la API de Claude envía tokens. Sin caché, cada token de tu solicitud —system prompt, definiciones de herramientas, ejemplos few-shot y el mensaje del usuario— se cobra a la tarifa de entrada normal. Con caché, un prefijo de esos tokens se almacena en los servidores de Anthropic después de la primera solicitud. En las solicitudes posteriores que comparten ese prefijo exacto, pagas un precio de lectura de caché en lugar de volver a procesarlos desde cero.

La diferencia de costo es real:

Una vez que superas el punto de equilibrio —lo cual ocurre rápido en cualquier agente que se ejecuta más de unas pocas veces al día— cada acierto de caché adicional es un descuento de ~90% sobre esos tokens.

La regla de coincidencia de prefijo

Esta es la única regla de la que se desprende todo lo demás: la clave de caché es una coincidencia de prefijo de tu prompt renderizado.

Los servidores de Anthropic almacenan el contenido renderizado desde el inicio de tu prompt hasta el marcador cache_control. Para que ocurra un acierto de caché en la siguiente solicitud, cada token desde el inicio del prompt hasta ese marcador debe ser idéntico, byte por byte.

El orden de renderizado para la coincidencia de prefijo es: tools → system → messages. Así que tu arreglo de herramientas se hashea primero, luego el bloque system, y después los mensajes en orden.

Lo que esto significa en la práctica: el contenido estable debe ir primero. Si tu system prompt hace referencia a algo dinámico —una fecha actual, un ID de usuario, un ID de rastreo de solicitud— y aparece antes del marcador cache_control, la caché fallará en cada solicitud porque el prefijo cambia constantemente.

Sobre qué poner un marcador de caché

Los objetivos de mayor impacto son:

1. Tu system prompt

Los system prompts suelen ser el bloque estable más grande. Una persona de agente detallada, una lista de reglas de comportamiento, un conjunto de instrucciones de formato de salida: todo esto es idéntico en cada invocación del mismo agente. Márcalo:

typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const response = await client.messages.create({
  model: "claude-opus-4-8",
  max_tokens: 1024,
  system: [
    {
      type: "text",
      text: `You are a content operations agent for alejandrorioja.com.
Your job is to draft blog posts in Alejandro's voice: direct, practitioner, 
first-person, numbered lists, honest caveats. No hedging. No filler. 
Every section must earn its place.

[... 2000 more tokens of stable instructions ...]`,
      cache_control: { type: "ephemeral" },
    },
  ],
  messages: [
    {
      role: "user",
      content: "Draft a post about prompt caching.",
    },
  ],
});

El cache_control: { type: "ephemeral" } en el bloque system le indica a Claude que cachee todo hasta ese bloque, incluyéndolo. El arreglo messages es volátil —distinto en cada solicitud— y queda fuera del límite de la caché.

2. Definiciones de herramientas

Si tu agente usa herramientas, esas definiciones pueden ser considerables. Un esquema de herramienta bien documentado, con descripción, nombres de parámetros y valores enum, puede llegar a 500–1.000 tokens por herramienta. Con 5 herramientas, eso son hasta 5.000 tokens que pagas por reprocesar en cada llamada:

typescript
const response = await client.messages.create({
  model: "claude-opus-4-8",
  max_tokens: 1024,
  tools: [
    {
      name: "search_airtable",
      description: "Search the Airtable content queue...",
      input_schema: { type: "object", properties: { query: { type: "string" } } },
    },
    // ... more tools ...
    {
      name: "post_to_kit",
      description: "Schedule a broadcast via the Kit API...",
      input_schema: { /* ... */ },
      // Mark the last tool to cache the entire tools array
    } as Anthropic.Tool & { cache_control: { type: "ephemeral" } },
  ],
  system: "...",
  messages: [...],
});

Marca la última herramienta del arreglo. La coincidencia de prefijo cubrirá el arreglo completo de herramientas a partir de ese punto.

3. Ejemplos few-shot en messages

Si pasas ejemplos few-shot estáticos como mensajes iniciales en el arreglo messages, esos también se pueden cachear. Estrúcturalos como los primeros N mensajes y marca el último turno de ejemplo:

typescript
const messages: Anthropic.MessageParam[] = [
  {
    role: "user",
    content: [
      {
        type: "text",
        text: "Here are examples of posts in my voice:\n\n[Example 1...]\n\n[Example 2...]",
        cache_control: { type: "ephemeral" },
      } as Anthropic.TextBlockParam & { cache_control: { type: "ephemeral" } },
    ],
  },
  {
    role: "assistant",
    content: "Understood. I'll follow that voice.",
  },
  // The actual user turn follows — this is volatile, no cache marker
  {
    role: "user",
    content: actualUserRequest,
  },
];

Qué NO cachear (los invalidadores silenciosos)

Estas son las cosas que parecen estables pero no lo son, y que arruinarán tu tasa de aciertos en silencio. La API no te avisará. Simplemente verás cache_creation_input_tokens en cada solicitud y te preguntarás por qué.

Timestamps en el system prompt. El error más común con diferencia:

typescript
// This invalidates the cache on every request
const system = `You are an agent. Current time: ${new Date().toISOString()}`;

Mueve los timestamps al mensaje del usuario, que es donde corresponden:

typescript
// Stable system prompt — cacheable
const system = `You are an agent. Use the current time provided by the user.`;

// Volatile user message — not cached
const userMessage = `Current time: ${new Date().toISOString()}. Run the daily brief.`;

UUIDs aleatorios e IDs de rastreo. El mismo problema. Si inyectas un ID de rastreo en el bloque system para tus logs, cada solicitud recibe un prefijo nuevo.

Serialización JSON no determinista. Si serializas un objeto dentro del system prompt y el orden de las claves no está garantizado, la cadena renderizada puede diferir aunque los datos subyacentes sean los mismos. Serializa con un orden de claves estable o usa una cadena de plantilla.

Selección dinámica de ejemplos few-shot. Si eliges los ejemplos few-shot según la consulta actual y los colocas en el prefijo cacheado, has hecho que el prefijo “estable” dependa de la consulta. O te comprometes con ejemplos fijos para la capa de caché, o mueves los ejemplos dinámicos al turno de mensaje sin cachear.

Verificando tu tasa de aciertos de caché

Cada respuesta incluye metadatos de uso. Revísalos:

typescript
const response = await client.messages.create({ /* ... */ });

console.log({
  inputTokens: response.usage.input_tokens,
  cacheRead: response.usage.cache_read_input_tokens,
  cacheWrite: response.usage.cache_creation_input_tokens,
  outputTokens: response.usage.output_tokens,
});

En la primera solicitud: cache_creation_input_tokens será distinto de cero y cache_read_input_tokens será 0. Esa es la escritura.

En un acierto de caché: cache_read_input_tokens será distinto de cero y cache_creation_input_tokens será 0. Esa es la lectura.

Si ves cache_creation_input_tokens en cada solicitud, tu prefijo está cambiando. Agrega una sentencia de log que imprima los primeros 200 caracteres de tu system prompt renderizado antes de cada llamada: un timestamp flotante saltará a la vista de inmediato.

El TTL de 1 hora: cuándo vale la pena el costo extra de escritura

El TTL por defecto es de 5 minutos. Si tu agente se ejecuta a baja frecuencia —menos de una vez cada 5 minutos— estarás pagando costos de escritura de caché en la mayoría de las solicitudes sin obtener lecturas.

typescript
// Opt into a 1-hour TTL
cache_control: { type: "ephemeral", ttl: "1h" }

La escritura con TTL de 1 hora cuesta ~2× el precio base de entrada en lugar de 1,25×. La cuenta: si estás acertando la caché 3 o más veces por hora, el TTL de 1 hora ahorra dinero. Si tu agente se ejecuta una vez al día (como mi resumen diario), ni siquiera el TTL de 1 hora ayudará: pagas costos de escritura cada vez. En ese caso, el beneficio de la caché es modesto a menos que el system prompt sea enorme.

Mi agente de resumen diario tiene un system prompt de 3.000 tokens pero se ejecuta una vez al día. La caché no ayuda. Mi agente de newsletter se ejecuta docenas de veces por sesión mientras redacta: la caché ahorra de forma sustancial.

Pre-calentamiento: hacer que la primera solicitud sea barata

Si sabes que se avecina un pico de tráfico —un trabajo por lotes, el lanzamiento de una API— puedes pre-calentar la caché con una solicitud ficticia de bajo costo:

typescript
// Pre-warm: write the cache at near-zero output cost
await client.messages.create({
  model: "claude-opus-4-8",
  max_tokens: 1, // minimal output
  system: [{ type: "text", text: stableSystemPrompt, cache_control: { type: "ephemeral" } }],
  messages: [{ role: "user", content: "ping" }],
});

// Now the real requests read from cache

Esto es útil sobre todo para el procesamiento por lotes, donde levantas muchas solicitudes en paralelo y quieres que cada una acierte una caché caliente en lugar de competir por escribirla.

Prompt caching en bucles agénticos

En un bucle agéntico de múltiples turnos, el historial de la conversación crece en cada turno. La caché es lo bastante inteligente para manejar esto: usa una ventana de retrospección de 20 bloques, buscando el prefijo coincidente más largo dentro de los últimos 20 bloques de contenido.

La implicación práctica: mantén tu contenido estable (system prompt, definiciones de herramientas) anclado en la parte superior. El historial de conversación creciente al final del arreglo messages no romperá la coincidencia de prefijo de los bloques estables: están antes del contenido volátil, y la coincidencia de prefijo empieza desde arriba.

En la práctica, mis agentes estructuran los turnos así:

code
System (cached) → Tools (cached) → Few-shot (cached) → Turn 1 → Turn 2 → ... → Current turn

La caché cubre todo hasta el marcador del few-shot. El historial de turnos creciente posterior se reprocesa cada vez, pero eso está bien: esos tokens son específicos de la sesión y pequeños en comparación con el prefijo estable.

Cómo se ve en la factura

Toma un agente de alta frecuencia: 100 llamadas al día, system prompt de 4.000 tokens, precios de Sonnet.

Sin caché:

Con caché (TTL de 5 min, asumiendo 50 llamadas/hora en el pico):

Eso es una reducción de aproximadamente 90% en esos tokens de entrada. A escala —1.000 llamadas al día— la diferencia se compone aún más. Y esto se suma a cualquier ahorro por enrutamiento de modelos de la matemática de Haiku vs Sonnet: la caché funciona en todos los niveles.

La conclusión del operador

El prompt caching es la optimización de costos más fácil de la Claude API: un campo adicional en los bloques de contenido que ya estás escribiendo. La restricción es la disciplina en torno a la estabilidad del prefijo: nada dinámico antes del marcador de caché. Si logras mantener tu system prompt, tus herramientas y cualquier ejemplo estático libres de contenido volátil, pagarás ~10% del costo de entrada normal en cada acierto de caché. Para agentes de alta frecuencia con prompts estables grandes, esto es una palanca más grande que cambiar de nivel de modelo.


Relacionado: Matemática de costos de agentes de IA: cuándo Haiku le gana a Sonnet · Agentes disparados por eventos vs. agentes programados · Las 5 herramientas de IA que realmente uso para manejar mi negocio

Seguir leyendo

Recibe el manual de IA en tu buzón

Cada miércoles. 28.400+ operadores. Sin relleno.

↵ para ver todos los resultados esc esc para cerrar