Todos los recursos

Marca un prefijo estable como cacheable y bajas latencia y costo en prompts grandes que se repiten. El detalle está en el modelo de match por prefijo, que decide si consigues un hit o terminas pagando precio completo sin enterarte.

Agrega caché de prompts a una llamada de la API de Claude

En resumen

  • La caché de prompts es un match por prefijo. Cualquier cambio de un byte en el prefijo invalida todo lo que viene después. El orden de render es tools, luego system, luego messages.
  • Pon lo estable primero (system prompt congelado, lista de tools determinista). Lo volátil (timestamps, IDs, la pregunta del usuario) va después del último breakpoint de cache_control.
  • Hay un prefijo mínimo cacheable: 4096 tokens en Opus 4.8, 2048 en Sonnet 4.6. Si el prefijo es más corto, no cachea y no te avisa: cero error.
  • Un read de caché cuesta como 0.1x del input base; un write de 5 minutos cuesta como 1.25x. Con dos reusos ya recuperas el costo en el TTL por defecto.
  • Verifica con usage.cache_read_input_tokens. Si te da cero entre requests idénticos, tienes un invalidador silencioso metido en el prefijo.

La caché de prompts es la mejora de rendimiento más barata de la API de Claude, y también la que más gente cree tener activada sin que sea cierto. El modelo mental cabe en una frase: la caché es un match por prefijo, y cualquier cambio de un byte en el prefijo invalida todo lo que viene después. Si ordenas bien, casi todo el ahorro te sale gratis. Si ordenas mal, pones un marcador cache_control, no ves ningún error y terminas pagando precio completo en cada request sin enterarte. Esta guía recorre la mecánica real, un ejemplo en TypeScript, los números que te dicen si de verdad vale la pena, y los invalidadores silenciosos que te tumban el hit rate.

00 · Requisitos previos

Necesitas tres cosas listas. Es un checklist de cinco minutos, no un proyecto.

  • Una llamada a Claude que ya haces de forma repetida: mismo contexto grande, cola distinta. La caché solo rinde si hay repetición. Un prompt de una sola vez no tiene nada que reusar.
  • El paquete @anthropic-ai/sdk instalado y una ANTHROPIC_API_KEY en tu entorno. Los ejemplos asumen Opus 4.8, pero todo aplica igual a Sonnet 4.6 y Haiku 4.5.
  • Una forma de leer response.usage después de la llamada. Ese objeto es tu única fuente de verdad para saber si la caché está funcionando. Sin él, estás adivinando.

Nota

Si tu prompt cambia desde el primer token en cada request (un documento distinto, un system prompt distinto cada vez), mejor detente aquí. No hay prefijo compartido que cachear, y poner cache_control solo te hace pagar el write a cambio de cero reads.

01 · Cómo funciona el match por prefijo

La clave de caché sale de los bytes exactos del prompt renderizado hasta cada breakpoint de cache_control. La API arma tu request en un orden fijo: primero tools, luego system, luego messages. Todo lo que esté antes de un breakpoint tiene que ser idéntico byte por byte entre llamadas para que ese breakpoint pegue.

En ese orden está toda la jugada. Un cambio en una sección anterior invalida la caché de todo lo que viene después, porque los bytes del prefijo ya no coinciden. En concreto:

  • Cambias una definición de tool (agregas, quitas o reordenas) e invalidas tools, system y messages: la caché completa.
  • Cambias el system prompt y conservas la caché de tools, pero pierdes system y messages.
  • Agregas un nuevo turno de usuario y conservas tools y system. Ese es el caso normal, el que no te duele.

La regla práctica que sale de esto: lo estable va primero, lo volátil va al final. Tu bloque de instrucciones congelado, tus documentos recuperados, tu lista de tools van adelante, antes del breakpoint. La pregunta real del usuario, la fecha de hoy, un ID de request van después.

Importante

Hay un prefijo mínimo cacheable, y depende del modelo. En Opus 4.8, Opus 4.7, Opus 4.6 y Haiku 4.5 son 4096 tokens; en Sonnet 4.6 son 2048. Por debajo de ese umbral la caché no hace nada en silencio: vas a ver cache_creation_input_tokens: 0 sin ningún error. No te molestes en cachear un prompt de 1K tokens.

02 · Agregar el breakpoint

Agrega un marcador cache_control al último bloque que quieras incluir en el prefijo cacheado. La forma más simple es el auto-cacheo a nivel raíz, que coloca el marcador por ti en el último bloque cacheable:

import Anthropic from "@anthropic-ai/sdk"

const client = new Anthropic()

const res = await client.messages.create({
  model: "claude-opus-4-8",
  max_tokens: 1024,
  cache_control: { type: "ephemeral" }, // cachea el último bloque cacheable
  system: longInstructionsAndPolicy,    // grande y estable: vive en el prefijo
  messages,                             // la cola que cambia
})

Cuando necesitas más precisión, cachear solo la parte compartida de un request mientras la cola varía, pon el marcador en un bloque de contenido específico. El patrón de abajo cachea un preámbulo compartido y deja la pregunta variable sin cachear, para que cada request lea el mismo prefijo en vez de escribir una entrada nueva que nunca va a reusar:

const res = await client.messages.create({
  model: "claude-opus-4-8",
  max_tokens: 1024,
  system: [
    {
      type: "text",
      text: longInstructionsAndPolicy,
      cache_control: { type: "ephemeral" }, // TTL de 5 minutos por defecto
    },
  ],
  messages: [
    {
      role: "user",
      content: [
        { type: "text", text: sharedRetrievedDocs, cache_control: { type: "ephemeral" } },
        { type: "text", text: usersQuestion }, // sin marcador: cambia cada vez
      ],
    },
  ],
})

Tienes un máximo de 4 breakpoints por request, y cache_control puede ir en cualquier bloque de contenido: texto de system, definiciones de tools o bloques de mensaje. Para un set largo de instrucciones más una lista fija de tools, un breakpoint en el último bloque de system cachea tools y system juntos, porque tools se renderiza antes que system.

03 · Confirmar que de verdad pegaste un hit

Este es el paso que todo el mundo se salta, y es el único que te dice la verdad. El objeto usage de cada respuesta te reporta exactamente lo que pasó:

  • cache_creation_input_tokens: tokens escritos a la caché en este request, cobrados al premium de write de 1.25x.
  • cache_read_input_tokens: tokens servidos desde la caché, cobrados como a 0.1x.
  • input_tokens: el resto sin cachear, cobrado a precio completo.
console.log(res.usage.cache_creation_input_tokens) // primera llamada: alto; luego: 0
console.log(res.usage.cache_read_input_tokens)     // primera llamada: 0; luego: alto
console.log(res.usage.input_tokens)                // solo la cola sin cachear

El patrón que esperas entre dos llamadas con el mismo prefijo: la primera muestra un número grande en cache_creation y cero reads; de ahí en adelante todas muestran cero creation y un cache_read grande. Si cache_read_input_tokens se te queda en cero entre requests que crees idénticos, tienes un invalidador silencioso. Ve a la sección 05.

Consejo

Un detalle que se pasa por alto y vale oro: input_tokens es solo el resto sin cachear. Si tu agente corrió una hora y input_tokens marca 4K, eso no quiere decir que procesó apenas 4K tokens. El resto vino de la caché. El tamaño total del prompt es la suma de los tres campos. Fíjate en la suma, no en el número suelto.

04 · Los números, para que sepas si vale la pena

La caché no es gratis, y un marcador en el lugar equivocado te cuesta dinero en vez de ahorrártelo. Las cuentas son lo bastante simples como para calcularlas de cabeza.

Un read de caché cuesta como 0.1x del precio base de input. Un write cuesta como 1.25x con el TTL por defecto de 5 minutos, o 2x con el TTL de 1 hora. Así que con el TTL por defecto, dos requests quedan a mano: el primero paga 1.25x por escribir, el segundo paga 0.1x por leer, total 1.35x, contra 2x de dos requests sin cachear. Del tercer request en adelante, todo es ganancia.

Cuándo rinde el TTL de 1 hora

El TTL de 1 hora mantiene viva la entrada durante los huecos del tráfico a ráfagas, pero el costo de write duplicado (2x) significa que necesitas al menos tres reusos para quedar a mano, en vez de dos. Úsalo cuando tu tráfico llega a ráfagas con huecos de inactividad mayores a cinco minutos: una cola de soporte que se calma de noche, un batch job que corre cada hora. Para tráfico continuo, donde los requests llegan cada pocos segundos, quédate con el por defecto. Cada request real mantiene la caché caliente por su cuenta y el premium del write de 1 hora se desperdicia.

Dónde importa esto en la práctica

El caso clásico es un contexto grande y fijo reusado en muchas llamadas: un set de documentos para RAG que consultas una y otra vez, una política o guía de estilo larga que prefija cada request, un bloque de definiciones de tools en un loop de agente. Piensa en un loop de deliberación que reusa el mismo prompt grande de encuadre a lo largo de decenas de turnos. Justo la forma donde un solo breakpoint sobre el prefijo estable convierte reads repetidos a precio completo en reads de 0.1x.

05 · Invalidadores silenciosos (la parte que de verdad muerde)

Todo lo de arriba es fácil. Aquí es donde se cae la caché de verdad. Un invalidador silencioso es cualquier cosa que cambie los bytes del prefijo entre requests que crees idénticos. Sin error, solo un hit rate en cero. Cuando cache_read_input_tokens se queda en cero, hazle grep a tu código de armado de prompts buscando esto:

  1. Un timestamp en el prefijo. new Date(), Date.now(), un "fecha actual: …" interpolado en el system prompt: el prefijo cambia en cada request. Muévelo después del último breakpoint, a la cola del mensaje.
  2. Un ID de request o UUID al principio del contenido. Mismo problema: cada request se vuelve único byte a byte. Los identificadores por request van al final del todo, si es que van.
  3. Serialización JSON no determinista. Un JSON.stringify sobre un objeto cuyo orden de claves no es estable, o cualquier cosa armada a partir de un Set, produce bytes distintos en cada corrida. Serializa de forma determinista (ordena las claves).
  4. Un valor por usuario en el system prompt. Interpolar un ID o nombre de usuario en el prefijo compartido le da a cada usuario un prefijo distinto y mata el reuso entre usuarios. Mete ese contexto más adelante, en messages.
  5. Un set de tools que varía por request. Las tools se renderizan en la posición 0; armarlas condicionalmente (buildTools(user)) significa que nada cachea entre usuarios. Mantén la lista de tools fija y determinista, ordenada por nombre.

Atención

Nunca pongas una variable por request arriba de un breakpoint. Es la forma más común de "activar la caché" y obtener exactamente nada. Si cambias el string del modelo, eso también invalida la caché, porque las cachés van por modelo, así que la primera llamada con un modelo nuevo siempre escribe de cero.

Dos detalles más que conviene saber en loops largos de agente. Cada breakpoint retrocede como máximo 20 bloques de contenido buscando una entrada de caché previa, así que un turno que agregue más de 20 bloques (común cuando hay muchos pares tool_use/tool_result) puede fallar en silencio. Mete un breakpoint intermedio cada 15 bloques más o menos. Y una entrada de caché solo se vuelve legible después de que la primera respuesta empieza a hacer streaming, así que N requests en paralelo con prefijos idénticos pagan todos precio completo. Lanza uno, espera su primer token, y de ahí dispara el resto.

La caché de prompts premia un armado de prompts aburrido y disciplinado: congela el frente, ordena lo que serializas, empuja todo lo que cambia hacia atrás, y confirma con usage en vez de dar por sentado. Hazlo y bajas latencia y costo en cada prompt grande repetido sin una sola sorpresa en la factura. Sáltate el paso de verificación y vas a desplegar un marcador que no hace nada, que es peor que no tener caché, porque vas a creer que el problema ya está resuelto.

Puntos clave

  • La caché es un match por prefijo: lo estable primero, lo volátil (timestamps, IDs, la pregunta del usuario) después del último breakpoint de cache_control.
  • Respeta el tamaño mínimo cacheable (4096 tokens en Opus 4.8) o la caché no hace nada en silencio.
  • Verifica siempre con usage.cache_read_input_tokens; un cero persistente significa que hay un invalidador silencioso en tu prefijo.
  • Por defecto, el TTL de 5 minutos; con dos reusos ya quedas a mano. Usa el de 1 hora solo para tráfico a ráfagas con huecos de inactividad largos.
  • Mantén las tools y el modelo fijos durante la conversación: cambiar cualquiera de los dos invalida la caché completa.

Preguntas frecuentes

¿Por qué cache_read_input_tokens siempre da cero aunque agregué cache_control?

Casi siempre es un invalidador silencioso en el prefijo: un timestamp, un UUID, un valor por usuario, o serialización JSON no determinista que te cambia los bytes del prefijo entre requests. La otra causa común es un prefijo por debajo del tamaño mínimo cacheable (4096 tokens en Opus 4.8). Saca un diff de los bytes del prompt renderizado entre dos requests y ahí ves la diferencia.

¿Debo usar el TTL de 5 minutos o el de 1 hora?

Por defecto, 5 minutos. El premium de write es más bajo (1.25x contra 2x) y el tráfico continuo mantiene la entrada caliente por su cuenta. Usa el TTL de 1 hora solo cuando tu tráfico es a ráfagas con huecos de inactividad mayores a cinco minutos, y recuerda que necesita tres reusos para quedar a mano en vez de dos.

¿Puedo cachear las definiciones de tools, o solo el system prompt?

Ambos. Tools se renderiza antes que system, así que un solo breakpoint de cache_control en el último bloque de system cachea tools y system juntos. Solo mantén la lista de tools determinista: ordénala por nombre y nunca la armes condicionalmente por usuario, o invalidas la caché completa (tools va en la posición 0).

¿Cambiar de modelo invalida la caché?

Sí. Las cachés van por modelo, así que el primer request con un modelo nuevo siempre escribe de cero. Esto importa en una migración: pasar de Sonnet 4.6 a Opus 4.8 significa que tu primer lote de requests vuelve a pagar el premium de write. La colocación de cache_control se conserva igual; solo hay que reconstruir el contenido cacheado.

¿Vale la pena la caché de prompts para un chatbot con prompts cortos?

Solo si hay un prefijo compartido grande. Un chatbot con un system prompt de 6K tokens reusado entre turnos encaja perfecto: cachea el bloque de system una vez. Un chatbot cuyo prompt entero es el mensaje corto del usuario no tiene nada que cachear y además queda por debajo del tamaño mínimo cacheable. El factor que decide es el tamaño del prefijo repetido, no el tipo de producto.

¿Cómo mantengo la caché funcionando en un loop largo de agente con muchas tool calls?

Vigila dos límites. Cada breakpoint retrocede como máximo 20 bloques de contenido, así que un turno con más de 20 pares tool_use/tool_result puede fallar: mete un breakpoint intermedio más o menos cada 15 bloques. Y para el fan-out, lanza un request, espera su primer token en streaming, y de ahí dispara el resto, ya que una entrada de caché solo se vuelve legible cuando la primera respuesta empieza a hacer streaming.

¿Prefieres que lo hagamos por ti?

Esto mismo lo construimos para negocios como el tuyo. La primera conversación es gratis y sin compromiso.

Escríbenos por WhatsApp

Escríbenos por WhatsApp

Escanéalo con tu teléfono para escribirnos por WhatsApp.

Escanéalo con tu teléfono para escribirnos por WhatsApp.

¿Estás desde el teléfono y no puedes escanear? Escríbenos a info@ilustrari.com

Primera conversación gratis. Te responde el fundador.

Recursos relacionados