Prompt caching en harnesses de agentes: qué mutaciones lo invalidan sin avisar
Un agente que pierde el prompt cache no falla. Sigue respondiendo bien, solo que cada turno vuelve a facturar y a latencia completa el system prompt entero, la lista de tools y el historial acumulado. No hay excepción ni log de error que lo delate: el costo simplemente sube y nadie sabe por qué hasta que alguien compara facturas.
Esta semana, el changelog de Claude Code y su Agent SDK para TypeScript documentó, versión tras versión, cuántas formas distintas hay de romper esa cache sin querer. No es un solo bug: es una categoría entera de bugs, y todos comparten la misma causa raíz.
El prefijo se supone estable, pero casi nada lo trata así
El prompt caching depende de que el prefijo de la conversación (system prompt, definiciones de tools, contexto de hooks) llegue byte por byte igual entre turnos. Cualquier cambio en ese prefijo, aunque sea agregar una tool al final de la lista, invalida todo lo que venía cacheado desde ahí.
La versión 2.1.265 de Claude Code corrigió dos casos: reanudar un subagente lanzado en foreground le cambiaba la lista de tools y el system prompt prefix, rompiendo la reutilización de cache para ese agente; y los agentes teammate y los subagentes reanudados movían el contexto del hook SubagentStart y las skills precargadas fuera del prefijo en turnos posteriores, con el mismo efecto.
La 2.1.268 sumó más casos de la misma familia: un worker en background bifurcado de una conversación agregaba EnterWorktree al bloque de tools a mitad de sesión; tools de MCP y de plugins que llegaban a mitad de sesión se insertaban en la lista de tools en sesiones sin ToolSearch; cambiar de modelo con /model reenviaba cada definición de tool completa; y un conector MCP que se reconectaba en un momento distinto al de antes reescribía el set de tools inline en sesiones reanudadas. La misma versión corrigió que el prompt caching y el extended thinking se rompieran a mitad de sesión en sesiones del SDK que usan excludeDynamicSections, porque el primer mensaje se volvía a renderizar en cada request.
La 2.1.269 agregó dos más: la cache quedaba parcialmente invalidada en el turno siguiente a una respuesta cortada por el límite de tokens de salida y reanudada automáticamente, y reanudar una sesión después de interrumpir a Claude a mitad de un pensamiento podía cambiar cómo se reenviaba el contexto anterior. Esa misma versión agregó --system-prompt-snapshot off, para renderizar el system prompt fresco en cada request cuando lo que se quiere es iterar el texto del prompt, dejando claro que el comportamiento normal es justamente lo contrario: usar el prompt grabado, no uno recalculado.
El Agent SDK de TypeScript 0.3.267 formalizó esa distinción: cambió systemPrompt para que quede grabado por defecto en prompts personalizados y appends, de modo que un cambio de prompt a mitad de sesión recién surte efecto en la siguiente compactación, salvo que se pase snapshot: false para volver al renderizado por request.
La lección
El patrón se repite en cada bug: algo que debería vivir fuera del prefijo cacheado (una tool nueva, un hook, un cambio de modelo, una reconexión de MCP) termina insertándose dentro de él, o el prefijo grabado se vuelve a calcular cuando no debería. Cada uno de esos puntos de mutación es invisible en el código hasta que alguien mide el costo o la latencia y encuentra que la cache dejó de pegar.
Si tu harness mantiene una conversación de larga duración con tools y system prompt fijos, cualquier mecanismo que agregue o quite contenido de ese prefijo en caliente (plugins que se cargan a mitad de sesión, reconexiones de servidores MCP, cambios de modelo, hooks que inyectan contexto) es un candidato directo a romper la cache. Y a diferencia de un bug funcional, este no se nota en los tests: se nota en la cuenta.
Reglas para no perder la cache sin darte cuenta
Trata el system prompt y la lista de tools como un prefijo inmutable durante la vida de una sesión: agregar contenido nuevo debe ser append-only, nunca una edición in place de lo que ya se envió.
Cuando una tool aparece a mitad de sesión (reconexión de MCP, plugin, helper interno), inyéctala como definición diferida en vez de insertarla en el bloque de tools existente.
Si tu harness soporta cambiar de modelo o reanudar sesiones interrumpidas, audita explícitamente si esa operación fuerza un reenvío completo del prefijo. Si lo hace, es un incidente de costo, no un detalle de implementación.
Por defecto, graba el system prompt (snapshot) en vez de recalcularlo en cada request. Solo renderízalo fresco cuando estás iterando activamente sobre el texto del prompt, y hazlo con un flag explícito, no como comportamiento implícito.
Fuentes: anthropics/claude-code v2.1.265, anthropics/claude-code v2.1.268, anthropics/claude-code v2.1.269, anthropics/claude-agent-sdk-typescript v0.3.267
Seguir leyendo
Esto se demuestra en video, y se sigue a diario en la comunidad
Las demos completas van al canal. El seguimiento release por release, adentro.