La llamada más barata a un LLM es la que no se hace. La segunda más barata es la que se hace pagando una fracción del contexto, y la tercera es la que se envía a un modelo más pequeño. Esas tres frases resumen casi todo lo que hoy se llama «caché» en aplicaciones con modelos de lenguaje y agentes, pero detrás hay técnicas con garantías muy distintas: unas nunca devuelven una respuesta equivocada y otras lo hacen a propósito, a cambio de ahorrar más.
Este tema es una clase completa sobre ellas. No vende ninguna. Pretende que, al terminar, puedas implementar cada técnica, estimar cuánto ahorra con tus propios números, saber cuándo falla y decidir cuál usar en un sistema por capas. Está pensado para ingenieras e ingenieros de IA: hay fórmulas, cifras reales con su fuente y código, pero cada idea llega primero con una imagen.
La idea en una imagen: el mostrador de atención
Analogía (no es una descripción literal de un LLM): imagina un mostrador de atención al público con un empleado muy competente pero lento y caro, que es el LLM. Delante hay una carpeta con respuestas ya escritas. Un buen mostrador usa la carpeta en este orden: primero busca si esta misma pregunta ya está resuelta (caché exacto), luego si hay una muy parecida (caché semántico), y antes de entregar la hoja comprueba que realmente responde lo que el cliente preguntó (verificación). Si nada sirve, pasa el caso al empleado, y el empleado, a su vez, no vuelve a leer todo el expediente desde cero cada vez que le haces una pregunta: tiene el expediente subrayado (caché de prefijo).
La analogía se rompe en dos sitios importantes. Primero, en una carpeta de papel «parecida» es obvio cuando no sirve; con vectores, dos frases pueden estar muy cerca sin significar lo mismo (veremos ejemplos con coseno de 0.99). Segundo, el cliente de un mostrador real está presente y puede protestar; en un sistema automatizado el hit erróneo suele pasar sin que nadie lo vea.
De ahí sale la pregunta que ordena todo el tema: ¿cuándo es seguro reutilizar una respuesta? Hay tres casos limpios:
- Cuando la entrada es idéntica y la respuesta no depende de nada más (caché exacto). El riesgo es solo de obsolescencia.
- Cuando lo reutilizado no es la respuesta, sino el trabajo previo (caché de prefijo del proveedor, KV cache). La salida se genera igual; solo se paga menos por leer el contexto.
- Cuando alguien comprueba que la respuesta guardada sirve a esta pregunta (verificación). Aquí empieza el riesgo estadístico, y por eso se mide.
Chequeo: ¿por qué el caché de prefijo del proveedor es «más seguro» que un caché semántico?
Porque no sustituye la respuesta: el modelo la genera de nuevo con el mismo contexto ya procesado. según la documentación de Anthropic (tal como la resume una fuente secundaria), la respuesta es la misma que sin caché; lo único que cambia es lo que pagas y, a veces, la latencia. El caché semántico, en cambio, devuelve una respuesta escrita para otra pregunta, y esa es la fuente de los hits erróneos.
Dónde se gasta el dinero y el tiempo: anatomía del costo
Antes de elegir una técnica hay que saber qué estás comprando. Una llamada a un LLM se factura por tokens de entrada, tokens de salida (que incluyen el razonamiento en Anthropic y en Gemini) y, si usas caché de prefijo, por escribir y leer ese caché. El costo de una llamada es:
En agentes esa asimetría se acentúa, porque en cada paso se reenvía todo el historial. Manus, un producto de agentes en producción, reporta que su proporción media entre tokens de entrada y de salida es de unos 100:1 (blog de ingeniería de Manus; es un solo producto y no un promedio de la industria). Con precios de Sonnet 5.5 eso significa que, por cada token de salida, se pagan 100 de entrada:
| Modelo (USD por millón de tokens) | Entrada | Salida | Escritura caché 5 min | Lectura caché |
|---|---|---|---|---|
| Claude Sonnet 5.5 | $2 | $10 | $2.50 | $0.10 |
| Claude Opus 5.5 | $4 | $20 | $5 | $0.20 |
| Claude Fable 5.1 | $10 | $50 | $12.50 | $0.25 |
| OpenAI gpt-6.1-sol | $2 | $10 | $2.50 (30 min) | $0.10 |
| OpenAI gpt-6-astra | $10 | $50 | $12.50 (30 min) | $1.00 |
Fuentes, consultadas el 26-oct-2026: documentación de precios y de prompt caching de Anthropic y de OpenAI (enlaces al final). Dos advertencias: el factor de lectura no es uniforme (en Anthropic va de 0.025× a 0.1× según el modelo; la frase «90 % de descuento» que se repite en muchos blogs ya no es siempre cierta), y OpenAI indica que el precio promocional de uno de sus modelos rige «al menos hasta el 21 de noviembre de 2026». Gemini 4 Argon, además, duplicará sus precios el 1 de diciembre de 2026 (de $2 a $4 de entrada). Cualquier cálculo de este tema debe leerse con esa fecha.
Sobre latencia: el tiempo hasta el primer token (TTFT) crece con el contexto de entrada, y después la salida se genera token a token. Un paper que midió prompt caching en agentes de investigación reporta que reduce el costo entre 41 % y 80 % y el TTFT entre 13 % y 31 % (Don't Break the Cache, arXiv 2601.06007, enero de 2026; más de 500 sesiones, tres proveedores, prompts de sistema de 10 000 tokens). Nótese la asimetría: el costo cae mucho más que la latencia. No encontré un desglose fiable de TTFT frente a decodificación por tamaño de prompt en producción.
Chequeo: tu agente gasta 95 % del costo en entrada. ¿Qué técnica atacas primero, un caché de respuestas o un caché de prefijo?
El de prefijo. El costo está en reenviar el mismo contexto en cada paso, y ese contexto casi nunca se repite como pregunta completa (el historial cambia en cada turno), así que un caché de respuestas acertaría poco; pero el comienzo del prompt sí es idéntico entre turnos, que es justo lo que el caché de prefijo reutiliza.
Las capas de ahorro, de la más segura a la más agresiva
Ordenadas por riesgo creciente de entregar algo incorrecto, estas son las técnicas que cubre el tema. La figura muestra cómo se encadenan en la ruta de una petición; la tabla las compara.
Antes de gastar tokens, la petición pasa por capas de la más segura a la más agresiva.
| Técnica | Ahorro reportado (fuente y condiciones) | Latencia | Riesgo de respuesta incorrecta | Complejidad | Cuándo conviene |
|---|---|---|---|---|---|
| Caché exacto de respuesta | Sin cifra publicada fiable; ahorra el 100 % de cada hit. No encontré dato fiable de hit rate típico. | De segundos a milisegundos | Ninguno por construcción; sí obsolescencia | Baja | Consultas repetidas literalmente (FAQ cerradas, evaluaciones, jobs reintentados) |
| Caché semántico | AWS: hasta 86 % de costo y 88 % de latencia con 91 % de accuracy de hits (63 796 consultas con paráfrasis, umbral 0.75; blog de proveedor). vCache: 41 % de hits con 0.5 % de error en SemCacheLMArena (paper ICLR 2026). | Hit en ~0.1 s (AWS) | Medio-alto: 8.8 % de hits erróneos en el experimento de AWS | Media | Tráfico con mucha paráfrasis, respuestas tolerantes a error |
| Semántico con verificación | Krites (Apple): hasta 3.9× más respuestas estáticas curadas, en simulación sobre trazas (arXiv 2602.13165). Sin otros datos fiables. | Verificador asíncrono o síncrono: añade su costo | Menor que el semántico solo; no medido de forma independiente | Media-alta | Cuando un hit erróneo es caro |
| Caché de prefijo del proveedor | 41–80 % de costo y 13–31 % de TTFT (arXiv 2601.06007); 7 % → 84 % de hit y −59 % de costo en producción (ProjectDiscovery, blog de ingeniería). | Mejora TTFT; no garantizada | Ninguno: la salida es la misma | Baja | Casi siempre que el prefijo supere el mínimo (512–4096 tokens según modelo) |
| Memoización de herramientas | TVCache: hits de hasta 70 % y 6.9× menos tiempo (RL, no producción); ToolCaching: +11 % hits, −34 % latencia frente a políticas estándar (arXiv 2602.10986, 2601.15335). | Evita la ejecución de la herramienta | Obsolescencia; peligro con efectos | Media | Herramientas puras o de lectura con TTL |
| Caché de planes | APC: −50.31 % de costo, −27.28 % de latencia (NeurIPS 2025, arXiv 2506.14852 v2). Una evaluación independiente de otra tarea midió 0–12 % de acierto de clave (arXiv 2602.18922). | Menos pasos de planificación | Medio: depende del dominio | Alta | Tareas con estructura repetida |
| Enrutamiento y cascadas | RouteLLM: 95 % del rendimiento de GPT-4 con 26 % de llamadas a GPT-4 en MT Bench (blog LMSYS); benchmarks independientes de 2026 dicen que varios routers no superan una línea base simple (arXiv 2601.07206). | Modelo pequeño suele ser más rápido | Medio: el router se equivoca | Media-alta | Tráfico heterogéneo con muchas consultas fáciles |
| Compresión de contexto | LLMLingua: hasta 20× (paper original); mejora de latencia de hasta 18 % en una medición independiente de 2026 (arXiv 2604.02985). | Depende del régimen | Medio: puede borrar datos exactos | Media | Contextos largos en lenguaje natural |
| Batch / Flex | −50 % de precio en Anthropic, OpenAI y Gemini (documentación de cada proveedor). | Peor: minutos a 24 h | Ninguno | Baja | Trabajo no interactivo |
Cómo leer la tabla. Los ahorros no son comparables entre sí: cada fila usa otro dataset, otro modelo y otra definición de «ahorro». Sirve para ordenar ideas y para saber dónde mirar, no para sumar porcentajes. Y las capas se multiplican solo si sus aciertos son independientes, lo cual casi nunca ocurre: un hit exacto evita también la ventaja del caché de prefijo en esa petición.
Chequeo: ¿por qué no puedes sumar «41–80 %» del prefijo con «hasta 86 %» del semántico?
Porque se aplican a conjuntos distintos de tokens y peticiones, y porque el caché semántico, cuando acierta, evita la llamada entera: ese ahorro ya contiene el del prefijo para esa petición. Además, los dos números vienen de experimentos distintos con condiciones distintas.
Caché exacto: la capa que no se equivoca
El caché exacto guarda la respuesta bajo una clave que es un hash de todo lo que puede cambiarla: modelo, parámetros de generación, herramientas y mensajes. La documentación de LiteLLM lo dice sin adornos: los cachés exactos «key on a hash of the whole request, so any change to the conversation is a miss». Esa es su fortaleza (no puede devolver nada que no se haya pedido) y su límite (cualquier cambio mínimo es un miss).
import hashlib, json
def cache_key(model: str, params: dict, messages: list, tools: list | None,
namespace: str) -> str:
"""Clave determinista: todo lo que cambie la respuesta debe entrar aquí."""
payload = {
"ns": namespace, # tenant + versión de prompt/KB (ver sección de invalidación)
"model": model,
"params": params, # temperature, max_tokens, seed, response_format...
"tools": tools or [],
"messages": messages,
}
# sort_keys + separators: el mismo contenido siempre serializa igual
blob = json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
return hashlib.sha256(blob.encode("utf-8")).hexdigest()
def cached_call(store, call_llm, **req):
key = cache_key(**req)
if (hit := store.get(key)) is not None:
return hit
out = call_llm(**req)
store.set(key, out, ex=3600) # TTL: la obsolescencia es el único riesgo de esta capa
return out
Tres detalles que sí muerden en producción:
- Un caché en memoria vive dentro de un proceso. LiteLLM avisa de que con cuatro workers hay cuatro cachés separados y el hit rate cae aproximadamente en esa proporción. Usa un almacén compartido (Redis, Valkey, S3).
- Con temperatura mayor que 0, el caché fija una muestra. El segundo llamador recibe la misma respuesta que el primero, no una nueva. Si necesitas variedad, Helicone permite guardar varias respuestas por clave (
Cache-Bucket-Max-Size); si no, no caches esas llamadas. - Temperatura 0 no implica determinismo. Thinking Machines mostró en septiembre de 2025 que las APIs de LLM siguen sin ser deterministas con temperatura 0, y atribuye la causa a la falta de invarianza al tamaño del lote en los kernels. Para un caché exacto esto no es un error (devuelve la salida ya generada), pero invalida cualquier prueba que compare un hit con «lo que habría respondido el modelo» como si fuera una única verdad.
Chequeo: tu servicio tiene un system prompt con la fecha y hora actual. ¿Qué le pasa al caché exacto?
Su hit rate se acerca a cero, porque la hora entra en la clave y nunca se repite. Soluciones: quitar la hora del prompt si no la necesita el modelo, congelarla a la fecha (sin hora), o excluir esa parte de la clave si no cambia la respuesta (con cuidado: eso es una decisión de equivalencia que debes poder justificar).
El caché semántico a fondo: embeddings, umbral y falsos positivos
El caché semántico relaja la igualdad: en vez de exigir el mismo texto, convierte cada consulta en un vector (un embedding), busca la consulta guardada más cercana y, si se parece lo suficiente, devuelve su respuesta. La medida habitual de cercanía es el coseno:
La consulta se convierte en vector y el índice devuelve la entrada cacheada más parecida con su similitud coseno.
Por qué la similitud no es validez
Zhu et al. (arXiv 2402.01173, Berkeley) lo formulan con precisión: los embeddings de prompts «mostly focus on whether two prompts are semantically similar, which is not necessarily equivalent to whether the same response can answer them». En su dataset difícil, construido sobre Natural Questions, un embedding E5 sin afinar obtenía un AUC de 0.51 (equivale a lanzar una moneda) para predecir si se podía reutilizar la respuesta; tras afinar con destilación subió a 0.81. Otra versión del mismo trabajo, presentada en un taller, da 0.85 → 0.92 con otro dataset: no mezclar ambas cifras.
Un ejemplo ilustrativo pero de calidad baja: un repositorio independiente de un solo autor (semantic-cache-blindspots) reporta que «Migrate database from MySQL to PostgreSQL» y la misma frase al revés tienen coseno 0.9970 con BGE-Small, y que «Amoxicillin 50mg» frente a «500mg» da 0.9680. Su benchmark es pequeño y adversarial, hecho por el autor, así que no sirve para estimar la tasa de falsos positivos en tráfico real; sirve para ver el mecanismo: las diferencias que importan (dirección, cantidad, negación) pesan poco en el vector.
Qué dicen los números medidos
El paper más riguroso que encontré es vCache (Berkeley y TU Múnich, arXiv 2502.03771, aceptado en ICLR 2026), que además aporta una garantía formal. Define error como
| Política | Error (sobre todos los prompts) | Hit rate | Hits erróneos / hits (cálculo mío) |
|---|---|---|---|
| Umbral estático 0.99 | 2.5 % | 37 % | 2.5/37 ≈ 6.8 % |
| Umbral estático 0.98 | 4.1 % | 53 % | ≈ 7.7 % |
| Umbral estático 0.97 | 5.2 % | 67 % | ≈ 7.8 % |
| vCache, δ = 2 % | 0.5 % | 41 % | 0.5/41 ≈ 1.2 % |
| vCache, δ = 5 % | 2.0 % | 54 % | ≈ 3.7 % |
Tres lecciones de esta tabla. (1) Un umbral de 0.99 no es seguro: sigue produciendo 2.5 % de error. (2) Los autores muestran que ningún umbral estático inferior a 1.0 mantiene el error acotado cuando crece la diversidad de prompts: en SemCacheSearchQueries, 0.99 produce 1.7 % de error tras 150 000 muestras. (3) Compara siempre qué se divide por qué: «1 % de error» sobre todos los prompts puede ser «2.5 % de los hits» si el hit rate es 40 %. Muchos papers y blogs mezclan ambas definiciones.
El otro experimento con costos completos es el de AWS (blog de proveedor, noviembre de 2025): 63 796 consultas reales de un chatbot y sus paráfrasis del dataset público SemBenchmarkLmArena, ElastiCache para Valkey, embeddings Titan V2, Claude 3 Haiku, caché inicialmente vacío. Resultados (tabla 1 del blog):
| Umbral | Hit ratio | Accuracy de los hits | Ahorro de costo | Reducción de latencia |
|---|---|---|---|---|
| 0.99 | 23.5 % | 92.1 % | 15.8 % | 17.1 % |
| 0.95 | 56.0 % | 92.6 % | 51.9 % | 57.7 % |
| 0.90 | 74.5 % | 92.3 % | 72.5 % | 72.2 % |
| 0.80 | 87.6 % | 91.8 % | 84.6 % | 86.1 % |
| 0.75 | 90.3 % | 91.2 % | 86.3 % | 88.3 % |
| 0.50 | 94.3 % | 87.5 % | 88.0 % | 89.3 % |
Lecturas mías sobre esa tabla, que el blog no hace: el ahorro es siempre menor que el hit ratio (en 0.75, 90.3 % de hits da 86.3 % de ahorro) porque el caché tiene costo propio; la accuracy no llega a 100 % ni siquiera con 0.99 (92.1 %), y el blog no dice, en lo que leí, qué accuracy tendría el modelo sin caché; y a 0.75 hay unos
Cómo elegir el umbral con datos (y no con intuición)
Los umbrales no se pueden copiar entre sistemas: Azure API Management usa una distancia (score-threshold menor = más estricto, y recomienda empezar cerca de 0.05), mientras que Portkey (0.95 por defecto) y LiteLLM usan similitud (mayor = más estricto). Tampoco entre modelos de embeddings: en la clasificación de vCache, los umbrales óptimos por embedding iban de 0.71 a 1.0. El procedimiento:
- Toma una muestra de tu tráfico real (unos cientos a miles de pares de consultas que tu sistema considere candidatas).
- Etiqueta cada par: ¿la respuesta de una sirve a la otra? Hazlo a mano en una parte y con un juez LLM revisado en el resto.
- Calcula, para cada umbral, el hit rate y la tasa de error.
- Elige el umbral más bajo cuya tasa de error (con margen estadístico) esté dentro de lo que tu negocio tolera. Redis publicó en 2026 un resultado relacionado: los modelos con mejor PR-AUC «are often the worst in operation» con umbral fijo; elegir un embedding es un problema de umbral y utilidad, no solo de ranking (arXiv 2606.19719; solo leí el abstract).
import numpy as np
def wilson_upper(errors: int, n: int, z: float = 1.96) -> float:
"""Cota superior (95 %) de la tasa de error: no te fíes de la tasa observada con n pequeño."""
if n == 0:
return 1.0
p = errors / n
d = 1 + z * z / n
centro = p + z * z / (2 * n)
margen = z * np.sqrt(p * (1 - p) / n + z * z / (4 * n * n))
return (centro + margen) / d
def elegir_umbral(sims, correcto, delta=0.02, candidatos=np.linspace(0.70, 0.995, 60)):
"""
sims: similitud del mejor vecino para cada consulta de validación (array)
correcto: True si servir la respuesta guardada habría sido correcto (array bool)
delta: error máximo tolerado como fracción de los HITS servidos
Devuelve el umbral más bajo (más hits) que cumple la cota, y su cobertura.
"""
sims, correcto = np.asarray(sims), np.asarray(correcto)
for t in candidatos:
servidas = sims >= t
n = int(servidas.sum())
if n < 100: # muy pocos hits: la cota no es informativa
continue
errores = int((~correcto[servidas]).sum())
if wilson_upper(errores, n) <= delta:
return float(t), n / len(sims)
return None, 0.0 # ningún umbral es seguro: no uses caché semántico aquí
Dos cautelas sobre este código. Mide el error sobre los hits que servirías (la definición que coincide con la pregunta «¿cuántas de las respuestas que entrego son malas?»); si quieres comparar con vCache, conviértelo a error sobre todos los prompts multiplicando por el hit rate. Y valida con tráfico futuro, no con el mismo que usaste para elegir: el supuesto de datos independientes e idénticamente distribuidos que hace funcionar a vCache no se cumple cuando cambia el tráfico, y menos si hay un atacante.
Variantes recientes, con cautela
- vCache: aprende un umbral por entrada cacheada y decide al azar si servir o explorar (llamar al LLM), con garantía de que el error esperado no pasa de
\delta si se cumplen sus supuestos. Hasta 12.5× más hits y 26× menos error que GPTCache según sus autores. Costo: cada exploración es una llamada completa. Código abierto envcache-project/vCache. - GPTCache (Zilliz, 2023): el sistema fundacional de código abierto; umbral estático global. No encontré las cifras del paper original; vCache cita «hasta 100× menos latencia».
- MeanCache (arXiv 2403.02694): caché en el dispositivo del usuario con modelo de similitud entrenado de forma federada; reporta +17 % de F-score, +20 % de precisión y −83 % de almacenamiento frente a GPTCache (solo abstract). Valioso por privacidad; no pude leer las condiciones del experimento.
- SCALM (arXiv 2406.00025): +63 % de hit ratio y +77 % de ahorro de tokens frente a GPTCache (solo abstract); no mide hits erróneos según el resumen.
- Umbrales por categoría (arXiv 2510.26835): propone umbral, TTL y cuota distintos por tipo de consulta, y reporta 40–60 % de hit rate en categorías muy repetitivas frente a 5–15 % en las volátiles (solo snippet).
- GenCache (Microsoft, NeurIPS 2025): para prompts estructuralmente similares, 83 % de hit rate con pocos hits erróneos en datasets sin repetición y ~20 % más hits en flujos de agentes (abstract).
Chequeo: tu experimento da «1 % de error» y «40 % de hit rate». ¿Qué fracción de los hits que sirves es incorrecta?
Depende de la definición. Si el 1 % se calcula sobre todos los prompts (como en vCache), la fracción de hits erróneos es
Verificación: cómo hacer seguro el caché semántico
Si la similitud solo ordena candidatos, hace falta una segunda opinión antes de entregar la respuesta. La idea es la misma que en recuperación de información (un recuperador rápido seguido de un reranker lento y preciso), aplicada a una pregunta distinta: no «¿es relevante?» sino «¿esta respuesta guardada contesta esta pregunta nueva?». Hay cuatro familias, de menos a más cara:
- Reglas deterministas sobre lo que el vector pierde. Extrae de ambas consultas números, unidades, entidades nombradas, negaciones y dirección («de A a B»); si no coinciden, no es hit. Es barato y ataca justo los casos del ejemplo de la dosis. No encontré evaluación publicada fiable; es ingeniería, y se prueba con tus datos.
- Cross-encoder o reranker. Un modelo que lee las dos consultas juntas y puntúa su equivalencia. GPTCache incluye un evaluador de este tipo (
SbertCrossencoderEvaluation). No encontré un paper fiable que mida cross-encoders como verificadores de caché con cifras; las que circulan vienen de un repo independiente (reduce falsos positivos de 38.0 % a 14.0 % en su propio benchmark de 300 consultas, con el mismo 44 % de verdaderos positivos): tómalo como ilustración, no como evidencia. - Modelo de decisión pequeño. Los modelos del tema 1 devuelven una probabilidad calibrada para una pregunta cerrada («¿la respuesta guardada contesta esta consulta?») en milisegundos. Encaja de forma natural, pero no encontré mediciones publicadas en esta tarea; mídelo con tu conjunto etiquetado y revisa la calibración antes de fijar un umbral de confianza.
- LLM-juez. El más flexible y el más caro. vCache lo usa para etiquetar equivalencia de respuestas largas; Krites (Apple, arXiv 2602.13165) lo usa de forma asíncrona: sirve con la política de umbral estático en la ruta crítica y, si el vecino estático queda justo por debajo del umbral, un juez verifica en segundo plano y «promueve» la entrada aprobada al caché dinámico. Reporta hasta 3.9× más respuestas estáticas curadas sin cambiar la latencia crítica, pero solo en simulaciones sobre trazas, y no leí su tasa de errores del juez. El riesgo es evidente: una aprobación errónea se amplifica porque queda cacheada.
def servir_o_generar(consulta, vecino, sim, alto=0.97, bajo=0.85, verificador=None, llm=None):
"""Tres bandas: servir, descartar o verificar. alto y bajo salen de elegir_umbral() con tus datos."""
if sim >= alto:
return vecino.respuesta, "hit"
if sim >= bajo and verificador is not None:
# p = probabilidad de que la respuesta guardada sirva a esta consulta
p = verificador(consulta, vecino.consulta, vecino.respuesta)
if p >= 0.95:
return vecino.respuesta, "hit-verificado"
return llm(consulta), "miss"
Un matiz de seguridad que conviene tener presente: verificar la consulta no basta contra un atacante. Los ataques de colisión construyen una consulta que parece cercana a la de la víctima y arrastra un sufijo malicioso; por eso hay defensas que verifican en la respuesta (LaCache) o contra el texto crudo de la clave (Deletion Gain). Lo veremos en la sección de seguridad.
Chequeo: ¿qué ventaja y qué riesgo tiene verificar de forma asíncrona, como Krites?
Ventaja: la latencia de la ruta crítica no cambia, porque el juez trabaja en segundo plano. Riesgo: lo que el juez aprueba pasa a servirse en el futuro; un error del juez deja de ser un error aislado y se repite en cada hit posterior.
Caché en agentes: herramientas, planes y trayectorias
En un agente, lo caro no es solo la respuesta final: son decenas de pasos intermedios, cada uno con una llamada al modelo y, a menudo, una llamada a una herramienta. Eso abre dos lugares nuevos donde reutilizar trabajo: el resultado de las herramientas y la estructura de lo que el agente decide hacer.
Herramientas: memoizar lo puro, vigilar lo demás
Memoizar es guardar el resultado de una función bajo la clave de sus argumentos. Funciona si la función es pura: mismos argumentos, mismo resultado, sin modificar nada. La figura resume la decisión.
El agente pide get_price("ABC") o send_email(...). Antes de ejecutar hay que clasificarla.
- Pura (calcular, convertir, parsear): memoiza con clave = (herramienta, versión, argumentos normalizados). Sin TTL salvo que cambie el código.
- De lectura volátil (precio, estado de un pedido, búsqueda web): memoiza con TTL. La especificación de MCP lo dice sin ambigüedad: el TTL es «a freshness hint, not a guarantee» y el servidor puede cambiar los datos antes de que expire.
- Con efectos (enviar, pagar, escribir): no se memoiza la ejecución. Aquí el problema no es el caché sino la idempotencia (ejecutar dos veces no debe duplicar el efecto), que se resuelve con claves de idempotencia y verificación de postcondiciones.
import functools, time
def memoizar_herramienta(ttl_s: float | None, clave_extra=lambda: ""):
"""Solo para herramientas puras o de lectura. clave_extra() debe incluir tenant/permisos si importan."""
def deco(fn):
cache: dict = {}
@functools.wraps(fn)
def envuelta(**kwargs):
clave = (fn.__name__, clave_extra(), tuple(sorted(kwargs.items())))
hit = cache.get(clave)
if hit and (ttl_s is None or time.monotonic() - hit[0] < ttl_s):
return hit[1]
val = fn(**kwargs)
cache[clave] = (time.monotonic(), val)
return val
return envuelta
return deco
@memoizar_herramienta(ttl_s=300)
def precio(sku: str) -> float: ... # lectura: TTL de 5 min
def enviar_correo(to: str, body: str): ... # efectos: sin decorador
Lo que dice la literatura, con sus límites:
- MCP (borrador de especificación, SEP-2549): define
ttlMsycacheScope(publicoprivate) paratools/list,prompts/list,resources/listyresources/read, entre otros. Según la lista que leí,tools/callno figura entre las operaciones cacheables (inferencia mía; no vi una prohibición explícita). La adopción es escasa: el SDK de Python devuelvettlMs: 0por defecto, es decir, «inmediatamente obsoleto». - LangGraph:
CachePolicy(key_func, ttl)por nodo; la clave por defecto es un hash de la entrada completa del nodo, así que cualquier cambio de estado es un miss. Existe un aviso de seguridad (ejecución remota de código por deserialización en la capa de caché) del que solo vi el título; revisa tu versión. - ToolCaching / VAAC (arXiv 2601.15335): una política de admisión y expulsión guiada por «cacheabilidad» y valor de cachear; hasta 11 % más hits y 34 % menos latencia frente a políticas estándar (no frente a no cachear), con cargas sintéticas y públicas.
- TVCache (arXiv 2602.10986): para herramientas con estado, solo hay hit si todo el historial de llamadas coincide, lo que garantiza el mismo estado del entorno; hasta 70 % de hits y 6.9× menos tiempo mediano de herramienta, pero en post-entrenamiento por refuerzo, no en producción.
- Caché semántico sobre herramientas con parámetros (arXiv 2605.20630): un pipeline industrial midió 30.6× de aceleración en los hits de su caché temporal, y a la vez documentó que el caché semántico puro falla con consultas ricas en parámetros (activo, sensor, ventana de tiempo).
- Ejecución especulativa (PASTE, arXiv 2603.18897): predice llamadas futuras y las ejecuta mientras el modelo sigue generando; −43.5 % de tiempo de tarea. Es latencia, no memoización, y solo es segura con herramientas de solo lectura.
- Paralelizar no es cachear: LLMCompiler (arXiv 2312.04511) reporta hasta 3.7× de latencia y 6.7× de costo frente a ReAct, pero es por planificar un grafo de llamadas en paralelo.
No encontré un estándar de invalidación para resultados de herramientas más allá de TTL y de las notificaciones de cambio de MCP para listas y recursos. En la práctica, la invalidación la diseñas tú: versión en la clave, eventos de cambio que borran claves, o TTL corto.
Planes, trayectorias y habilidades
La segunda idea es no cachear resultados sino estructura: si el agente ya resolvió «reservar una sala», la secuencia de pasos se parece a la siguiente vez aunque la sala sea otra. Tres líneas de trabajo:
- Agentic Plan Caching (APC; Stanford, NeurIPS 2025, arXiv 2506.14852): extrae plantillas de plan de ejecuciones completadas y, ante una tarea nueva, busca una por palabras clave y deja que un modelo ligero la adapte. Reporta −50.31 % de costo y −27.28 % de latencia en promedio manteniendo el rendimiento (v2); la v1 decía −46.62 %: la cifra cambió entre versiones. No pude abrir sus tablas de resultados.
- Agent Workflow Memory (AWM, arXiv 2409.07429): induce rutinas reutilizables desde trayectorias; mejora el éxito relativo en 24.6 % (Mind2Web) y 51.1 % (WebArena) frente a la línea base de los autores, con modelos de 2024.
- Voyager (arXiv 2305.16291): biblioteca de habilidades como código ejecutable, indexada por la descripción; 3.3× más objetos únicos en Minecraft con GPT-4. Mide capacidad, no costo en dólares.
Contraevidencia que debes conocer. Un estudio de 2026 (arXiv 2606.15017, aceptado en EMNLP 2026) comparó AWM, ASI y ReasoningBank contra una línea base «vanilla» con el mismo presupuesto de tokens en cuatro dominios de WebArena y tres modelos recientes, y concluyó que la línea base iguala o supera a los tres métodos en éxito agregado, a menudo con menos tokens. Es un régimen en línea y con modelos distintos a los del paper de AWM, así que no lo refuta, pero impone una disciplina: cualquier caché de planes o memoria se compara contra un baseline con presupuesto igualado. Por otro lado, una evaluación independiente de un solo autor (arXiv 2602.18922) midió 0–12 % de exactitud de clave de caché para APC en tareas de agente personal, un régimen para el que APC no se diseñó: sugiere que el rendimiento depende del dominio, no que APC sea inútil.
Chequeo: ¿por qué un agente que hace «enviar correo» no debe usar un caché semántico sobre sus decisiones?
Porque una decisión cacheada que contiene una llamada a una herramienta con efectos puede reemitir una acción ya ejecutada, o ejecutar una obsoleta con los parámetros de otra petición parecida. Se puede reutilizar el razonamiento previo (el prefijo), pero la decisión de ejecutar se toma cada vez.
Enrutamiento y cascadas: el «caché de capacidad»
Analogía: un hospital no manda a todos los pacientes al especialista; un triaje decide quién necesita al cirujano y quién se resuelve con una enfermera. Un router hace lo mismo con modelos. Se parece a un caché en que evita pagar la capacidad máxima cuando basta una menor. Se rompe porque el triaje a veces falla y no hay forma de «deshacer» el modelo barato: o escalas, o aceptas el error.
Hay dos diseños:
- Router: decide antes de llamar a nadie, a partir de la consulta (RouteLLM, Hybrid LLM, routers comerciales).
- Cascada: llama primero al modelo barato y escala si una señal de confianza lo pide (FrugalGPT, AutoMix). El modelo de decisión del tema 1 es un candidato natural como señal.
| Trabajo | Cifra reportada | Condiciones y cautelas |
|---|---|---|
| FrugalGPT (arXiv 2305.05176, 2023) | Iguala a GPT-4 con hasta 98 % menos costo | Modelos y precios de 2023; «hasta». No extrapolar |
| RouteLLM (LMSYS, 2024) | 95 % del rendimiento de GPT-4 con 26 % de llamadas a GPT-4 en MT Bench (≈ 48 % más barato que un router aleatorio; 75 % con datos aumentados); titular «>85 %» frente a usar solo GPT-4 | Solo dos modelos (GPT-4 Turbo y Mixtral); sin aumentación, en MMLU rindió «near-random». Medición de los propios autores |
| AutoMix (arXiv 2310.12963) | Más de 50 % de costo con rendimiento comparable | QA sobre contexto dado; la métrica principal cambió entre versiones (89 % → 86 % → 57 % «beneficio incremental por costo») |
| Hybrid LLM (Microsoft, ICLR 2024) | Hasta 40 % menos llamadas al modelo grande sin caída de calidad | Según la métrica de los autores |
| IPR (AWS, arXiv 2509.06274) | 43.9 % de ahorro en una plataforma desplegada, con latencia del router < 150 ms | Paper de un proveedor; el benchmark no se liberó. La página de producto de Bedrock dice «hasta 30 %» |
| Not Diamond | 39–61 % en benchmarks; «20 %+» en producción temprana | Datos del proveedor; «aproxima» la calidad, no la iguala |
| LLMRouterBench (arXiv 2601.07206) | «Varios métodos recientes, incluidos routers comerciales, no superan de forma fiable una línea base simple» | Independiente; 400 000+ instancias, 21 datasets, 33 modelos (no vi qué línea base) |
Hay tres matices que no salen en los titulares. (1) Cambiar de modelo rompe el caché de prefijo: Anthropic cuenta en su blog sobre Claude Code que los cachés son propios de cada modelo, y que con 100 000 tokens de contexto en Opus, cambiar a Haiku para una pregunta fácil «would actually be more expensive» porque habría que reconstruir el caché para Haiku. Azure documenta lo mismo para su router, y OpenRouter y Not Diamond diseñan routers «pegajosos» o «conscientes del caché». No encontré una medición independiente de cuánto cuesta esto. (2) Costo por token no es costo por tarea: un artículo de PayPal (arXiv 2608.08528) propone medir el costo por tarea completada, incluyendo reintentos y escalaciones. (3) En cascadas con razonamiento, reenviar una cadena fallida al modelo grande puede empeorar su respuesta (hasta −34.8 puntos según un preprint de un solo grupo, arXiv 2608.13571): mejor empezar de cero al escalar.
Chequeo: tu router manda el 70 % del tráfico al modelo barato. ¿Qué tres cosas mides antes de celebrar el ahorro?
La calidad en tus tareas (frente a siempre usar el modelo fuerte, no frente a un router aleatorio); el costo por tarea completa, incluyendo escalaciones y reintentos; y el hit rate de prefijo antes y después, porque alternar modelos puede destruirlo.
Prompt caching del proveedor y KV cache: abaratar en vez de evitar
El caché de prefijo no evita la llamada: el modelo genera la respuesta igual. Lo que evita es reprocesar el comienzo del prompt. Por dentro, un transformador guarda para cada token procesado unas matrices llamadas K y V (el KV cache); si dos prompts comparten el mismo inicio, esas matrices se pueden reutilizar. OpenAI lo dice así: «The prompt cache stores key-value (KV) tensors, not the tokens themselves». Por eso solo sirve un prefijo exacto: el K y el V de cada token dependen de todos los anteriores.
Todo se procesa y el proveedor guarda el prefijo. Cobra la escritura (en Anthropic, 1.25× el precio de entrada a 5 min).
Reglas de ordenación (idénticas en todos los proveedores)
- Lo estable primero, lo volátil al final. En Anthropic el prefijo se construye en el orden
tools→system→messages; Anthropic recomienda poner al inicio las definiciones de herramientas, las instrucciones, el contexto y los ejemplos. OpenAI y Gemini dan la misma recomendación. - Nada volátil en el prefijo. Marcas de tiempo, identificadores de petición, JSON con claves en orden no determinista (Anthropic menciona Swift y Go). Manus advierte que incluso una marca de tiempo «precise to the second» al comienzo del system prompt «kills your cache hit rate».
- Contexto «append-only». No edites mensajes antiguos; añade al final.
- No cambies herramientas ni modelo a mitad de sesión. Cambiar
toolsinvalida todo en Anthropic; en OpenAI cambian el prefijomodel,tools,parallel_tool_calls,text.format,reasoning.effortycontext_management. Manus recomienda enmascarar herramientas en vez de quitarlas. - Mide el hit rate y alerta. El post de Anthropic sobre Claude Code dice que construyen todo el sistema alrededor del caché y que declaran incidentes (SEV) si el hit rate cae.
Precios y reglas por proveedor (consulta: 26-oct-2026)
| Proveedor | Escritura | Lectura | TTL | Mínimo | Control |
|---|---|---|---|---|---|
| Anthropic | 1.25× (5 min) o 2× (1 h) del precio de entrada | 0.1× en general; 0.05× en Opus 5.5 y Sonnet 5.5; 0.025× en Fable 5.1 | 5 min, refrescado gratis en cada uso; opción de 1 h | 512 a 4096 tokens según el modelo | Hasta 4 breakpoints cache_control; automático o explícito; lookback de 20 bloques |
| OpenAI (GPT-5.6 y posteriores) | 1.25× (30 min) o 2.5× (12 h) | 0.1× en la mayoría; 0.05× en gpt-6.1-sol | 30 min o 12 h mínimos | 1024 tokens | Implícito o breakpoints explícitos; prompt_cache_key |
| Google Gemini | Implícito: sin cargo de escritura visible. Explícito: no encontré dato fiable | Gemini 4 Argon: 0.05× ($0.10 sobre $2.00) | Explícito: 1 h por defecto | 4096 en Gemini 4 y 3.x; 2048 en 2.5 | Gemini 4 solo implícito; solo cachea el request previo si el nuevo añade ≥ 2050 tokens |
| AWS Bedrock | No encontré dato fiable de precios de caché; 1.25× para modelos OpenAI GPT-5.6+ | No encontré dato fiable | 5 min / 1 h (Claude), 30 min (OpenAI) | 512–4096 por checkpoint | Hasta 4 checkpoints; no soportado en inferencia batch |
| DeepSeek | Sin cargo listado | flash: $0.003 frente a $0.15 de miss (off-peak): 0.02× | «best-effort»; se limpia en horas o días | No encontré dato fiable | Automático en disco; prompt_cache_hit_tokens |
Contradicciones en las propias docs: Anthropic dice que el mínimo de Opus 4.7 es 2048 tokens «en toda plataforma» y la tabla de Bedrock dice 4096; Anthropic permite combinar caché con batch (aciertos «best-effort») y Bedrock no soporta caché en su API batch; los tokens cacheados cuentan para el límite por minuto en OpenAI directo, pero no en Anthropic ni en Bedrock. Verifica empíricamente con las métricas de uso: cache_creation_input_tokens y cache_read_input_tokens (Anthropic), cached_tokens y cache_write_tokens (OpenAI), cacheReadInputTokens y cacheWriteInputTokens (Bedrock).
¿Cuándo se paga solo? El punto de equilibrio
Para un prefijo de
Ejemplo completo (cálculo mío con los precios de la tabla): prefijo de 50 000 tokens, 500 tokens nuevos por petición, 20 peticiones dentro del TTL, sin contar la salida, Claude Sonnet 5.5 con caché de 5 minutos.
- Sin caché:
20\cdot 50\,500 = 1\,010\,000 tokens a $2/M = $2.02. - Con caché: escritura
50\,000\cdot 2.50/10^6 = \$0.125 ; 19 lecturas950\,000\cdot 0.10/10^6=\$0.095 ; tokens nuevos10\,000\cdot 2/10^6=\$0.020 . Total $0.240: un 88.1 % menos. - Con Sonnet 4.6 (entrada $3, escritura $3.75, lectura $0.30) el mismo caso baja de $3.03 a $0.5025: −83.4 %.
- Combinado con la API Batch de OpenAI (−50 %), el mismo escenario en gpt-6.1-sol pasa de $2.02 a $0.12 (−94 %) si todos los aciertos ocurren; en Anthropic los aciertos dentro de un batch son «best-effort».
El KV cache en tu propio servidor
Si sirves modelos tú, el mismo principio se implementa en el motor de inferencia. Las cifras son de los abstracts de cada paper y no son comparables entre sí: cada una usa otro hardware, otro modelo y otra línea base.
| Sistema | Idea | Cifra reportada (abstract) |
|---|---|---|
| vLLM / PagedAttention (SOSP 2023, arXiv 2309.06180) | KV en bloques paginados con compartición entre peticiones; el prefix caching automático mapea bloques a su hash | 2–4× de throughput frente a FasterTransformer y Orca con la misma latencia |
| SGLang / RadixAttention (arXiv 2312.07104) | Árbol radix de prefijos para reutilizar KV en programas con varias llamadas | Hasta 6.4× de throughput |
| LMCache (arXiv 2510.09665) | Descarga el KV a CPU, disco o red y lo comparte entre instancias | Hasta 15× de throughput combinado con vLLM; truncar contexto puede reducir a la mitad el hit ratio del prefijo |
| CacheBlend (arXiv 2405.16444) | Reutiliza KV de fragmentos que no son prefijo (RAG) recalculando un subconjunto de tokens | TTFT 2.2–3.3× menor y throughput 2.8–5× mayor sin perder calidad de generación |
| Mooncake (arXiv 2407.00079) | Arquitectura centrada en el KV con prefill y decode separados, caché distribuido (Kimi) | +525 % de throughput en escenarios simulados; 75 % más peticiones en cargas reales |
| CachedAttention (ATC 2024, arXiv 2403.19708) | KV de conversaciones multi-turno en una jerarquía memoria/almacenamiento | TTFT hasta −87 %; costo extremo a extremo hasta −70 % |
| Prompt Cache (MLSys 2024, arXiv 2311.04934) | Módulos de prompt reutilizables aunque no estén al inicio | 8× (GPU) a 60× (CPU) en TTFT |
| Preble (arXiv 2407.00023) | Planificación distribuida que combina reutilización de KV y balanceo | 1.5–14.5× en latencia media |
Hay trabajos de 2025–2026 sobre reutilización de KV fuera del prefijo (por ejemplo Irminsul, ReCache, TokenPilot) que solo vi citados en un resumen secundario, sin verificar en origen; no los uso como evidencia. Una advertencia sobre este tipo de reutilización aproximada: un trabajo citado por esa misma fuente indica que la reutilización fuera del prefijo con recomputación selectiva puede hacer que un prompt idéntico dé respuestas distintas según las peticiones anteriores. El prefijo exacto no tiene ese problema.
Batch y Flex: otra forma de abaratar
Si la tarea no es interactiva, Anthropic, OpenAI y Gemini ofrecen un 50 % de descuento en sus APIs de lotes (resultados en hasta 24 horas; en Anthropic casi siempre antes de 1 hora). OpenAI y Gemini tienen además niveles Flex (mismo precio que Batch, pero con mejor latencia y posibilidad de errores 429/503 sin cargo; en Gemini el objetivo es 1–15 minutos). En OpenAI y Gemini el descuento de batch y flex también escala las tarifas de caché; en Anthropic los multiplicadores se acumulan, pero conviene enviar primero una petición que escriba el prefijo y usar el caché de 1 hora. Por el lado contrario, los niveles prioritarios cobran más: Gemini +75–100 %, OpenAI Fast ≈ 2× en gpt-6.1-sol.
Chequeo: tu agente hace una pausa de 8 minutos esperando una aprobación humana. ¿Qué pasa con el caché de 5 minutos y qué puedes hacer?
Se evicta: la siguiente petición reescribe el prefijo a 1.25× en vez de leerlo a 0.1×. Opciones: usar el TTL de 1 hora (escritura 2×, rentable desde la segunda reutilización), o enviar una petición de «mantenimiento» antes de que expire (hay un paper de 2026, arXiv 2607.19214, que propone un keepalive en el cliente; solo leí su abstract, así que calcula su punto de equilibrio con tus precios).
Memoria y compresión de contexto
La otra mitad de la ecuación de costo es cuánto contexto envías. Reducirlo ahorra tokens, pero cambia el prefijo, de modo que puede romper el caché que acabas de ganar. Las técnicas, con la evidencia que encontré:
- Compresión de prompts (LLMLingua, LLMLingua-2, LongLLMLingua). Un modelo pequeño descarta tokens poco informativos. LLMLingua reporta hasta 20× de compresión con poca pérdida (GSM8K, BBH, ShareGPT); LongLLMLingua, +21.4 % de rendimiento en NaturalQuestions con ~4× menos tokens (en parte porque mitiga el efecto «perdido en el medio») y −94 % de costo en LooGLE; LLMLingua-2, 1.6–2.9× de latencia extremo a extremo con compresión 2–5×. Pero una medición independiente de 2026 (arXiv 2604.02985) halló aceleraciones de hasta 18 % «cuando longitud de prompt, ratio de compresión y hardware encajan». No encontré evaluación en salidas de herramientas estructuradas (JSON, código, trazas), donde borrar tokens puede borrar un identificador.
- Recortar observaciones antiguas. JetBrains Research (arXiv 2508.21433) reporta que enmascarar observaciones viejas «halves cost» frente a un agente sin recorte, igualando el éxito del resumen con LLM, en SWE-agent sobre SWE-bench Verified. Otro estudio de 2026 (arXiv 2605.18854) midió solo 8.6 % de ahorro neto con enmascaramiento, y que los condensadores basados en LLM aumentan el costo de tokens entre 24 % y 94 %. Un tercero (arXiv 2606.00408) muestra que enmascarar ayuda hasta que quita evidencia que el modelo necesitaba, y entonces colapsa. Y uno más (arXiv 2609.32961, ~35 000 ejecuciones) encontró que usar un tercio de los tokens puede tardar 20–80 % más porque el agente necesita más turnos. Conclusión: menos tokens no implica menos tiempo ni menos costo; mide por tarea.
- Edición de contexto del proveedor. Anthropic ofrece una estrategia para borrar resultados de herramientas antiguos y un «memory tool»; su evaluación interna reporta +29 % con edición de contexto, +39 % combinada con memoria, y −84 % de tokens en una prueba de búsqueda de 100 turnos. Es dato del proveedor, y ese 84 % se compara con una línea base que se queda sin contexto, por lo que no es un ahorro general (varios blogs lo repiten sin esa condición).
- Memoria externa (MemGPT/Letta, Mem0). Mem0 reporta 91 % menos de latencia p95 y más de 90 % de ahorro de tokens frente a pasar todo el historial (benchmark LoCoMo, paper de la empresa que vende el producto). Hay disputa con Zep sobre ese resultado, una auditoría independiente que cuestiona la propia base LoCoMo (6.4 % de la clave de respuestas sería incorrecta, según un blog), y un estudio multiagente en el que contexto completo y RAG simple igualan a Mem0 en exactitud (77–81 %). No encontré datos fiables de ahorro de tokens para MemGPT/Letta.
Regla práctica: aplica la compresión en puntos de corte estables (por ejemplo, al compactar una vez cada cierto número de turnos), no en cada turno, y haz que la compactación reutilice el mismo prefijo para que el caché sobreviva. Claude Code lo hace con «cache-safe forking»: la llamada de compactación lleva el mismo system, tools e historial, y solo añade el prompt de compactación.
Chequeo: comprimes el historial en cada turno con LLMLingua y tu factura sube. ¿Por qué puede pasar?
Porque cambiar el historial en cada turno modifica el prefijo y pierdes las lecturas baratas del caché: pagas escritura (1.25×) en vez de lectura (0.1×) sobre todo el contexto. El ahorro de tokens no compensa, y además el compresor añade su propio costo y latencia.
Invalidación y personalización
Un caché sin plan de invalidación es una fuente de respuestas obsoletas. Hay cuatro mecanismos, y los gateways que revisé implementan sobre todo los dos primeros:
- TTL. Cloudflare AI Gateway permite entre 60 segundos y un mes; Helicone usa 7 días por defecto; Anthropic y OpenAI fijan el TTL del prefijo (5 min o 1 h; 30 min o 12 h). Un blog de AWS lo resume: TTL largo sube el hit rate pero aumenta el riesgo de datos desactualizados. Para una base de conocimiento que se actualiza a diario sugiere un TTL de unas 23 horas con jitter aleatorio, para que las entradas no expiren todas a la vez.
- Versión en la clave. Es la única forma limpia de invalidar masivamente: incluye en el espacio de nombres la versión del prompt, del modelo y de la base de conocimiento. Cuando cambias cualquiera, las claves viejas dejan de coincidir y mueren por TTL. Cloudflare (
cf-aig-cache-key), Portkey (x-portkey-cache-namespace), Helicone (Helicone-Cache-Seed) y LiteLLM (namespace) ofrecen el gancho; ninguna de sus docs dicta una convención de versionado, así que esa parte es diseño tuyo. - Eventos. Cuando un dato cambia, borras las claves que dependían de él (LiteLLM devuelve
x-litellm-cache-keypara alimentar/cache/delete). Para cachés semánticos no encontré invalidación por eventos documentada en los gateways revisados; solo TTL y borrado manual. - Sirve-y-revalida (stale-while-revalidate). Servir una entrada vencida y regenerarla en segundo plano es un patrón conocido de HTTP; no encontré ningún gateway de LLM que lo documente, así que sería diseño propio y sin evidencia empírica en este tema.
Para el caché de prefijo, ambos grandes proveedores ofrecen hoy páginas de «diagnóstico de caché» (Anthropic: cache diagnostics, que indica dónde divergió el prefijo; OpenAI: prompt cache diagnostics). Verifiqué que existen en sus documentaciones, pero no leí su contenido ni la fecha de disponibilidad general. No hay API para borrar un prefijo cacheado: se invalida cambiándolo o esperando al TTL.
Qué debe entrar en la clave cuando hay usuarios distintos
Una respuesta cacheada es correcta solo para quien habría recibido esa respuesta. La clave debe incluir todo lo que cambia la respuesta (esta lista es ingeniería mía a partir de lo que cada gateway ya hace por separado):
- Tenant y, si importan, usuario o grupo de permisos. Azure API Management lo expresa con
vary-by(su ejemplo usa el identificador de suscripción). - Modelo, versión y parámetros de generación (Cloudflare y Portkey ya lo hacen).
- Versión del system prompt, de las herramientas y de la base de conocimiento. AWS recomienda, en un RAG, incrustar «the current user message and that retrieved context», no el diálogo crudo.
- Idioma, zona horaria y cualquier señal de localización que cambie la respuesta.
Hay trampas concretas en los gateways. Portkey, en su caché semántico, ignora el system prompt: si tu personalización vive ahí, dos usuarios con permisos distintos pueden compartir hits salvo que uses su espacio de nombres o metadatos. Azure tiene ignore-system-messages con el mismo riesgo si lo activas. Cloudflare incluye el encabezado de autenticación del proveedor en la clave, de modo que en la práctica particiona por clave de API: si compartes la clave entre usuarios finales, comparten caché. Y las semánticas del umbral son opuestas entre Azure y Portkey/LiteLLM (ver la sección de umbrales).
Qué cachear antes de personalizar. En el caché de prefijo, el orden es estático → semi-estático → por usuario → conversación. Claude Code lo hace así: system prompt y herramientas globales, luego el archivo de proyecto (compartido por proyecto), luego estado de sesión y mensajes; y cuando algo cambia a mitad de sesión, lo comunica con un recordatorio en el siguiente mensaje en vez de editar el system prompt. Para respuestas completas, AWS describe una alternativa: cachear una respuesta genérica y pasarla, junto con el contexto del usuario, a un modelo barato que la personalice. «Ofrece menores ahorros» según AWS; no encontré una cifra medida de ese ahorro.
Chequeo: tu caché semántico devuelve a un empleado de ventas una respuesta escrita para un administrador. ¿Qué faltaba en la clave?
El rol o grupo de permisos (y, si el gateway ignora el system prompt, un espacio de nombres explícito por rol). Una respuesta puede ser perfecta para la pregunta y aun así estar prohibida para quien la lee.
Seguridad del caché
Un caché compartido es un recurso compartido, y eso trae tres problemas distintos. Ninguno de los papers siguientes reporta un incidente de producción; son demostraciones de ataque, algunas muy recientes y sin replicación independiente.
- Envenenamiento por colisión de claves (integridad). CacheAttack (HKUST y Fudan, ICML 2026, arXiv 2601.23088) argumenta que una clave semántica es un «hash difuso» y que la localidad que maximiza los hits «fundamentally conflicts» con el efecto avalancha que necesita la resistencia a colisiones. Un atacante busca un sufijo que haga que su consulta colisione con la de la víctima; la víctima recibe la respuesta del atacante. Reporta 86 % de secuestro de respuestas y 90.6 % en un escenario de agente con herramientas, con transferencia entre modelos de embeddings (condiciones exactas no leídas).
- Fuga entre tenants por temporización (confidencialidad). Gu et al. (Stanford, ICML 2025, arXiv 2502.07776) auditaron 17 proveedores de API en septiembre y octubre de 2024: detectaron caché de prompts en 8 y compartición global entre usuarios en 7, incluido OpenAI. El método es un contraste de hipótesis sobre el tiempo de respuesta (un hit es más rápido). Early Bird (arXiv 2409.20002) ataca específicamente el KV cache y el caché semántico midiendo el tiempo hasta el primer token. Un paper de 2026 resume que tres ataques de este tipo alcanzan hasta 100 % de éxito contra vLLM y SGLang sin protección (arXiv 2608.09225; las defensas que propone son de sus autores).
- Fugas por infraestructura intermedia. Dos trabajos de 2026 estudian si los gateways y relays que reenvían a un proveedor con credenciales compartidas hacen que clientes distintos compartan identidad de caché (arXiv 2605.30613 y 2608.17485); solo vi sus resúmenes.
Lo que hacen los proveedores hoy (documentación consultada el 26-oct-2026): OpenAI indica que sus cachés no se comparten entre organizaciones y recomienda prompt_cache_key para evitar el «cache-hit probing» entre usuarios; Anthropic aísla por workspace en su API directa y por organización en Bedrock y Google Cloud, y dice que las claves se generan con un hash criptográfico. No puedo afirmar que estos cambios respondan al paper de Gu et al. (no leí su sección de divulgación). Para DeepSeek, xAI, Gemini y Bedrock, la documentación que leí no habla de nivel de compartición.
Defensas conocidas, todas parciales:
- Particionar por tenant (o por usuario). Elimina la fuga y el envenenamiento entre tenants, y baja el hit rate. Azure
vary-by; en el prefijo,prompt_cache_keypor cliente. - Verificar la respuesta, no solo la consulta. LaCache (arXiv 2608.01718) comprueba también el acierto de los primeros k tokens decodificados de forma especulativa: reduce el acierto del ataque «a casi cero» conservando más de 90 % de la utilidad legítima (abstract; preprint de ago-2026).
- Verificar contra el texto crudo de la clave. Deletion Gain (arXiv 2609.35908, sep-2026) bloquea entre 82.0 % y 98.2 % de las entradas envenenadas con 5 % de falsos positivos, según sus autores.
- No cachear lo que dispara acciones y usar TTL cortos para reducir el radio del daño (recomendación mía; no vi evidencia cuantitativa).
- Caché por usuario en el dispositivo (MeanCache), a costa de perder el efecto de red entre usuarios.
- Desactivar el caché donde la confidencialidad pesa más que el ahorro.
Una nota sobre las cifras de ataque que circulan en prensa secundaria («95.4 % tras cinco sondeos», «80.6 % y 88.3 % en configuraciones de AWS y Azure»): no pude verificarlas en los papers originales, y no las uso como evidencia.
Chequeo: un atacante nota que ciertas respuestas llegan en 80 ms y otras en 3 s. ¿Qué puede inferir y cómo lo frenas?
Que las rápidas son hits: puede probar candidatos de texto hasta ver cuáles ya estaban cacheados, y así deducir lo que otros usuarios preguntaron. Se frena con aislamiento por usuario o tenant en la clave, no compartiendo caché global, y, si el producto lo permite, igualando artificialmente la latencia de hits y misses.
Medir: ahorro neto real
Un caché que acierta mucho y se equivoca poco puede aun así perder dinero. Para decidir hay que restar todo:
El hit rate mínimo para no perder (con
Qué instrumentar
- Hit rate de tokens (prefijo): tokens leídos de caché entre tokens de entrada totales. En Anthropic
input_tokenscuenta solo lo posterior al último breakpoint, así que el denominador son los tres:cache_read_input_tokens + cache_creation_input_tokens + input_tokens. En OpenAI,cached_tokens / input_tokens. Mira la mediana por sesión además del agregado. - Hit rate de peticiones (respuesta) y tasa de hit erróneo (
w ), muestreando hits para etiquetarlos con juez o revisión humana. - Razón de cada miss evitable (diagnósticos de los proveedores) y costo por tarea completada (no por token).
def hit_rate_tokens_anthropic(usage) -> float:
"""usage = respuesta.usage de la API de Anthropic."""
leidos = usage.cache_read_input_tokens
total = leidos + usage.cache_creation_input_tokens + usage.input_tokens
return leidos / total if total else 0.0
def ahorro_neto(h, C, c_emb, c_vec, infra_por_peticion, w, L):
return h * C - (c_emb + c_vec + infra_por_peticion) - h * w * L
Sobre el costo de un hit erróneo: no encontré datos fiables y cuantificados de su costo monetario en producción. Solo hay ejemplos cualitativos (una dosis de 50 mg servida por una de 500 mg en un repo independiente; un agente financiero que ejecuta una orden maliciosa en CacheAttack). La severidad
Chequeo: tu caché tiene 60 % de hit rate y 1 % de hits erróneos. ¿Qué dato falta para saber si conviene?
Diseñar un sistema en capas: arquitectura de referencia y árbol de decisión
Juntando todo, la arquitectura que se deduce de la evidencia ordena las capas por seguridad y por rendimiento esperado, y deja las menos seguras para el final y con verificación:
- Normalización: canonicaliza la petición (orden de claves, espacios, plantilla) y construye la clave con tenant, versiones y parámetros.
- Caché exacto con TTL y espacio de nombres versionado.
- Memoización de herramientas dentro del bucle del agente, solo para herramientas puras o de lectura, con TTL.
- Caché semántico con tres bandas (servir, verificar, descartar), umbrales elegidos con tus datos, aislado por tenant y desactivado para peticiones que disparan acciones.
- Router o cascada para elegir modelo, manteniéndolo estable dentro de una sesión.
- Llamada al LLM con prefijo cacheable: estable primero, volátil al final, con breakpoints y métricas.
- Escritura de vuelta al caché solo si la respuesta es cacheable (sin datos personales ni efectos).
def responder(req, tenant, ctx):
"""Orquestación en capas. Cada capa devuelve (respuesta, etiqueta) o None para pasar a la siguiente."""
ns = f"{tenant.id}:{ctx.prompt_version}:{ctx.kb_version}"
# 1) exacto
k = cache_key(req.model, req.params, req.messages, req.tools, ns)
if (r := exacto.get(k)) is not None:
return r, "exacto"
# 2) semántico, solo si la petición es una consulta (sin efectos) y el tenant lo permite
if req.es_consulta and tenant.semantico_ok:
vec = embebedor(req.texto_clave) # texto clave: consulta + contexto relevante, no el diálogo crudo
vecino, sim = indice.buscar(vec, particion=ns)
if vecino and sim >= UMBRAL_ALTO:
return vecino.respuesta, "semantico"
if vecino and sim >= UMBRAL_BAJO and verificador(req.texto_clave, vecino) >= 0.95:
return vecino.respuesta, "semantico-verificado"
# 3) modelo (router) + prompt con prefijo estable y breakpoints
modelo = router.elegir(req, sesion=ctx.sesion) # estable dentro de la sesión
resp = llm(modelo, prompt_ordenado(req, ctx)) # estable → volátil
registrar_metricas(resp.usage, modelo, ns)
# 4) escribir de vuelta solo si es seguro
if req.es_consulta and not resp.contiene_datos_personales and not resp.dispara_acciones:
exacto.set(k, resp.texto, ex=ctx.ttl)
if tenant.semantico_ok:
indice.insertar(vec, resp.texto, particion=ns, ttl=ctx.ttl)
return resp.texto, "llm"
Árbol de decisión: qué capa usar
| Pregunta | Si la respuesta es sí |
|---|---|
| ¿Tu prompt supera el mínimo del proveedor y su comienzo se repite? | Activa el caché de prefijo y ordénalo (siempre, es la capa de menor riesgo) |
| ¿Hay peticiones literalmente idénticas con respuesta estable? | Caché exacto con TTL y clave versionada |
| ¿Un fallo equivale a servir una respuesta incorrecta a alguien? | Si el costo del error es alto: nada de semántico; si es bajo: semántico con verificación y umbral medido |
| ¿Tus herramientas son de lectura o puras? | Memoiza con TTL; las que tienen efectos, nunca |
| ¿Las tareas del agente repiten estructura? | Prueba caché de planes, pero compáralo contra un baseline con presupuesto igualado |
| ¿Hay mucho tráfico fácil mezclado con difícil? | Router o cascada, midiendo costo por tarea y efecto sobre el prefijo |
| ¿El contexto crece sin parar? | Recorta o compacta en puntos de corte estables, conservando el prefijo |
| ¿No es interactivo? | Batch/Flex (−50 %), combinable con caché de prefijo |
Chequeo: ¿por qué el caché de prefijo va «siempre» y el semántico «según el costo del error»?
Porque el primero no cambia la salida y solo falla de forma segura (un miss cuesta el precio normal, más la prima de escritura), mientras que el segundo cambia la salida cuando se equivoca. La política de capas es exactamente ordenar por el riesgo de entregar algo incorrecto.
Cuándo no cachear
| Situación | Por qué | Alternativa |
|---|---|---|
| Respuestas que deben variar (creatividad, muestreo) | Un caché de respuesta devuelve siempre la misma muestra | Caché de prefijo (no altera la salida) o varias entradas por clave |
| Datos personales o sensibles en la respuesta | Se almacenan hasta el TTL en el gateway; riesgo de fuga y de cumplimiento (análisis mío; no vi doc de gateway que lo cubra) | No cachear la respuesta; Anthropic remite a una página de retención cero para el prefijo |
| Datos en tiempo real | El resultado depende de datos externos; APC y otros trabajos lo señalan como límite de los cachés de respuesta | Cachear estructura (planes) o herramientas con TTL corto |
| Acciones con efectos | Una respuesta que contiene una llamada a herramienta puede reemitir o retrasar una acción | Cachear solo el razonamiento previo; decidir la acción cada vez; idempotencia |
| Tráfico poco repetitivo | Cloudflare y Kong lo dicen en su documentación: el caché exacto vale para «limited prompt options»; el semántico no compensa con poco hardware o si el exacto basta | Medir antes la fracción repetida |
| Tráfico muy espaciado para el prefijo | Si las peticiones llegan más separadas que el TTL, solo pagas escrituras | TTL de 1 h, keepalive o desactivar |
| Agentes con muchos turnos y herramientas, con caché semántico | Turnos consecutivos son muy parecidos pero distintos; LiteLLM avisa de que el semántico «goes badly wrong on agentic traffic» | Desactivar el semántico en el tráfico de agentes; usar prefijo y herramientas |
| Cargas dominadas por la salida | El caché de prefijo solo descuenta entrada | Limitar salida y razonamiento primero |
Repaso: ¿lo entendiste de verdad?
Intenta responder cada pregunta antes de abrirla. Si dudas en alguna, vuelve a la sección correspondiente.
1. Explica por qué «coseno 0.99» no garantiza que la respuesta cacheada sirva.
El coseno mide cercanía de tema en el espacio del embedding, no igualdad de respuesta. Diferencias como una dirección, una cantidad o una negación pesan poco en el vector. En vCache, un umbral estático de 0.99 aún producía 2.5 % de error sobre todos los prompts.
2. Un prefijo de 10 000 tokens se reutiliza 3 veces dentro del TTL, con escritura 1.25× y lectura 0.1×. ¿Cuánto cuesta frente a no cachear, en unidades de «un prefijo»?
Con caché:
3. ¿Por qué meter la hora actual al principio del system prompt destruye el caché de prefijo?
El prefijo debe coincidir de principio a fin; una hora distinta en cada petición cambia el primer bloque, y todo lo que sigue deja de coincidir. Se paga la escritura cada vez y nunca se lee.
4. ¿Qué herramientas puedes memoizar y con qué clave?
Las puras y las de lectura, con clave (herramienta, versión, argumentos normalizados, tenant si importa) y TTL según cuánto tolera la lectura estar desactualizada. Las que tienen efectos, nunca; para ellas se usa idempotencia.
5. ¿Qué evidencia contradice el optimismo sobre cachés de planes y memorias de habilidades?
Un estudio de 2026 (arXiv 2606.15017) donde una línea base con presupuesto de tokens igualado igualaba o superaba a AWM, ASI y ReasoningBank en WebArena con tres modelos recientes, y una evaluación independiente que midió 0–12 % de acierto de clave para APC en agentes personales. La lección: compara siempre contra un baseline con el mismo presupuesto.
6. Tu caché semántico es compartido entre clientes. ¿Qué dos ataques debes considerar y qué los mitiga?
Envenenamiento por colisión de claves (integridad) y fuga por temporización (confidencialidad). Se mitigan con particiones por tenant en la clave, verificación de la respuesta o del texto de la clave, y no cacheando lo que dispara acciones.
Glosario mínimo
- Caché exacto
- Guarda la respuesta bajo un hash de todo lo que puede cambiarla.
- Caché semántico
- Reutiliza la respuesta de la consulta guardada más cercana en el espacio de embeddings.
- Embedding
- Vector que representa un texto; textos de significado cercano quedan cerca.
- Coseno
- Medida de cercanía entre dos vectores, de −1 a 1.
- Umbral / bandas
- Similitud mínima para servir un hit; con bandas, una zona intermedia se verifica.
- Hit erróneo (falso positivo)
- Se sirve una respuesta guardada que no responde a la consulta nueva.
- Verificador
- Modelo o regla que decide si una respuesta guardada sirve para la consulta nueva.
- Caché de prefijo / prompt caching
- El proveedor reutiliza el procesamiento del comienzo idéntico del prompt y cobra menos por leerlo.
- KV cache
- Claves y valores de atención ya calculados de los tokens procesados.
- TTL
- Tiempo de vida de una entrada de caché.
- Memoización
- Guardar el resultado de una función bajo la clave de sus argumentos.
- Idempotencia
- Ejecutar una operación dos veces tiene el mismo efecto que una.
- Router / cascada
- Elegir el modelo antes de llamar (router) o escalar tras una señal de baja confianza (cascada).
- Ahorro neto
- Ahorro por hits menos costo de operar el caché menos costo de los hits erróneos.
Fuentes y límites de lo que aquí se afirma
Todo lo que sigue se consultó el 26 de octubre de 2026 (los precios y reglas de proveedores, ese mismo día) y el campo cambia semana a semana. Distingo tres calidades: documentación oficial (comportamiento y precios, fiable el día de la consulta), papers (de los que muchas veces solo leí el abstract: las cifras son las de los autores, con sus condiciones solo cuando las vi) y blogs de proveedores o de terceros (cifras de marketing o de calidad desigual, marcadas en el texto).
Documentación oficial y blogs de ingeniería
- Anthropic, Prompt caching y Pricing (platform.claude.com/docs); Batch processing; Context editing y Context management (anthropic.com/news/context-management, 29-sep-2025); T. Shihipar, Lessons from building Claude Code: prompt caching is everything (claude.com/blog, 30-abr-2026).
- OpenAI, Prompt caching, Pricing, Batch y Flex processing (developers.openai.com/api/docs).
- Google, Context caching y Pricing de la Gemini API (ai.google.dev, página con «Last updated 2026-10-23»); Flex y Priority inference.
- AWS, Prompt caching de Amazon Bedrock (docs.aws.amazon.com); Lower cost and latency for AI using Amazon ElastiCache as a semantic cache with Amazon Bedrock (aws.amazon.com/blogs/database, 26-nov-2025); Bedrock Intelligent Prompt Routing.
- DeepSeek, Context caching y Pricing; xAI, Prompt caching; Microsoft, Azure API Management: llm-semantic-cache-lookup y Azure OpenAI prompt caching; Foundry model router.
- LiteLLM, Caching; Cloudflare, AI Gateway caching (actualizada 30-sep-2026); Helicone, Caching; Portkey, Simple and semantic cache; Kong, AI Semantic Cache; Redis, LangCache y sus blogs (de proveedor: sus cifras de ahorro son marketing).
- Model Context Protocol, borrador de especificación de caching y SEP-2549.
- Manus, Context Engineering for AI Agents: Lessons from Building Manus; ProjectDiscovery, How we cut LLM cost with prompt caching (10-abr-2026); LangChain, Deep Agents prompt caching (26-jun-2026); LMSYS, RouteLLM (1-jul-2024); Not Diamond y OpenRouter (blogs de proveedor, ago-2026); Thinking Machines, Defeating Nondeterminism in LLM Inference (10-sep-2025).
Papers (arXiv, salvo indicación)
- Caché semántico: vCache, 2502.03771 (ICLR 2026); Zhu et al., 2402.01173; MeanCache, 2403.02694; SCALM, 2406.00025; GPT Semantic Cache, 2411.05276; Redis, 2504.02268 y 2606.19719; categorías, 2510.26835; GenCache, 2511.17565; Krites, 2602.13165.
- Seguridad: CacheAttack, 2601.23088; LaCache, 2608.01718; Deletion Gain, 2609.35908; Gu et al., 2502.07776; Early Bird, 2409.20002; InputSnatch, 2411.18191; 2608.09225; 2605.30613; 2608.17485.
- Prefijo y KV: Don't Break the Cache, 2601.06007; PagedAttention, 2309.06180; SGLang, 2312.07104; LMCache, 2510.09665; CacheBlend, 2405.16444; Mooncake, 2407.00079; CachedAttention, 2403.19708; Prompt Cache, 2311.04934; Preble, 2407.00023; keepalive, 2607.19214.
- Agentes: LLMCompiler, 2312.04511; ToolCaching, 2601.15335; TVCache, 2602.10986; PASTE, 2603.18897; 2605.20630; APC, 2506.14852; AWM, 2409.07429; Voyager, 2305.16291; 2606.15017; 2602.18922.
- Routing: FrugalGPT, 2305.05176; RouteLLM, 2406.18665; AutoMix, 2310.12963; Hybrid LLM, 2404.14618; IPR, 2509.06274; LLMRouterBench, 2601.07206; 2608.08528; 2608.13571.
- Contexto y memoria: LLMLingua, 2310.05736; LongLLMLingua, 2310.06839; LLMLingua-2, 2403.12968; 2604.02985; 2508.21433; 2605.18854; 2606.00408; 2609.32961; Mem0, 2504.19413.
Lo que no pude verificar o no encontré
- De la mayoría de los papers leí solo el abstract o un fragmento; no vi tablas completas ni sus condiciones experimentales. En particular, las cifras de MeanCache, SCALM, Krites, LaCache, Early Bird y CacheAttack son las de sus resúmenes.
- No encontré dato fiable de: un paper que mida cross-encoders como verificadores de caché semántico; el costo monetario real de un hit erróneo en producción; precios de caché en Bedrock y Azure; precio de escritura y mínimo del caché explícito de Gemini; precios y mínimo de tokens de xAI; ahorro medido del caché de embeddings (LiteLLM lo soporta, sin cifras); stale-while-revalidate documentado para LLM; posts de ingeniería de Notion, Pinterest, Dropbox o Cursor sobre este tema; el desglose de TTFT frente a decodificación en producción; el costo cuantificado de perder el prefijo al cambiar de modelo.
- Cifras que no pude confirmar en su origen y no uso como evidencia: «1 000× más tokens en agentes que en chat», los papers de KV fuera del prefijo, las cifras de ataque de 95.4 %, 80.6 % y 88.3 %, y la fecha de disponibilidad general de los diagnósticos de caché de Anthropic.
- Dos blogs afirman que Anthropic cambió el TTL por defecto de 1 h a 5 min en marzo de 2026. No encontré un registro de cambios oficial; hoy la documentación dice 5 minutos por defecto, y es lo que uso.
- Contradicciones que dejé visibles: los precios de Sonnet de Manus ($0.30 lectura, $3 entrada) frente a los actuales; el mínimo de Opus 4.7 entre Anthropic y Bedrock; las semánticas opuestas del umbral entre Azure y Portkey/LiteLLM; los rangos 41–80 %, 45–80 % y 49–80 % del ahorro por caché de prefijo según la versión del paper o del blog; y las cifras de Requesty, que varían de 14 % a 95 % para Claude en Vertex según la página (telemetría de un gateway, con artefactos admitidos por ellos).
- Los precios caducan: Gemini 4 Argon duplica su tarifa el 1-dic-2026, Gemini 3.8 Flash el 1-ene-2027, y un modelo de OpenAI tiene precio promocional hasta, al menos, el 21-nov-2026. Los nombres de modelos son los que aparecen en las páginas consultadas hoy; no los normalicé, y la guía de caché de OpenAI y su página de precios no usan exactamente los mismos nombres.
Si vas a usar cualquiera de estas técnicas en algo que importe, mide con tu tráfico: el hit rate, la tasa de hit erróneo y el costo por tarea dependen más de tus datos que de cualquier cifra de este tema.