Caché de prompts
Reduce aún más el coste de entrada sobre la mitad de precio: usa la caché de cada fabricante y deja que Half Off Dev lleve cada conversación al canal con su caché.
La mitad de precio es solo el punto de partida. La caché de prompts abarata mucho más la entrada en cargas que reenvían el mismo prefijo: chats largos, asistentes de programación, recuperación sobre un conjunto fijo de documentos. Un modelo que procesó un prefijo hace poco puede reutilizar ese trabajo, y los tokens reutilizados se cobran a la tarifa de lectura de caché. En claude-sonnet-5.5 son $0.05 por millón de tokens, frente a $1.00 de la entrada sin caché.
La caché a través de Half Off Dev se apoya en dos piezas:
- La caché del propio fabricante, usada tal como la documenta cada uno. Campos como
cache_controlyprompt_cache_keyse reenvían sin cambios. - Afinidad de conversación. Una caché solo existe en el canal que la escribió. El gateway reconoce los turnos sucesivos de una misma conversación y los devuelve a ese canal, así que la caché sigue ahí en el turno siguiente.
Claude: marca lo que quieres cachear#
Claude solo guarda en caché lo que marcas. Añade cache_control al último bloque del prefijo estable (definiciones de funciones, prompt de sistema, turnos anteriores). Se permiten hasta cuatro puntos de corte, o un único cache_control de nivel superior que cachea todo hasta el último bloque.
response = client.messages.create(
model="claude-sonnet-5.5",
max_tokens=1024,
system=[{
"type": "text",
"text": PRODUCT_MANUAL,
"cache_control": {"type": "ephemeral"},
}],
messages=chat_history,
)
print(response.usage)usage informa cache_creation_input_tokens (escritos, cobrados a la tarifa de escritura de caché) y cache_read_input_tokens (leídos, cobrados a la tarifa de lectura). Los prefijos más cortos que el mínimo del modelo no se cachean. La guía de caché de Anthropic explica dónde poner los puntos de corte y qué invalida una caché.
GPT: automática, con una clave opcional#
Los modelos GPT cachean solos los prefijos largos, así que no hay nada que marcar. Pon primero el contenido estable y al final lo que cambia. Un prompt_cache_key que se mantenga igual para una conversación o un usuario mejora la tasa de aciertos, y el gateway usa ese mismo valor para mantener la conversación en un canal.
response = client.chat.completions.create(
model="gpt-6.1-sol",
messages=chat_history,
prompt_cache_key="thread_51c7d0",
)
print(response.usage.prompt_tokens_details.cached_tokens)usage.prompt_tokens_details.cached_tokens se cobra a la tarifa de lectura de caché; el resto de prompt_tokens, a la de entrada. Los modelos GPT no tienen cargo aparte por escritura de caché. Detalles en la guía de caché de OpenAI.
Gemini: caché implícita#
Los modelos Gemini cachean automáticamente los prefijos repetidos. Los tokens cacheados aparecen en usageMetadata.cachedContentTokenCount y se cobran a la tarifa de lectura de caché, sin cargo por escritura. La caché de contexto explícita mediante la API cachedContents no está disponible.
Cómo se asocian los turnos a una conversación#
Cada conversación recibe una huella, tomada de la primera de estas fuentes que esté presente:
| Fuente | Formato de API |
|---|---|
Cabecera x-session-id | Todos |
Cabecera x-halfoff-session | Todos |
Cabecera x-claude-code-session-id (la envía Claude Code) | Todos |
metadata.user_id en el cuerpo | Anthropic |
prompt_cache_key en el cuerpo | OpenAI |
user en el cuerpo | OpenAI |
| Prompt de sistema más el primer mensaje del usuario | Todos |
La última fila funciona sin que hagas nada. Una conversación crece añadiendo mensajes, así que su prompt de sistema y el primer mensaje del usuario no cambian, y cada turno produce la misma huella. Solo se comparan los primeros 4,000 caracteres de cada uno.
Una huella pertenece a una clave de API, un modelo y un grupo de canales. Si envías la misma conversación con otra clave o a otro modelo, empieza una fijación nueva.
Cuánto dura una fijación#
La primera llamada correcta de una conversación la fija al canal que la atendió, y los turnos siguientes prueban ese canal primero.
- Duración. Una fijación vive hasta una hora y se renueva mientras la conversación sigue activa. Tras 30 a 60 minutos sin actividad caduca, y el siguiente turno se coloca como si la conversación fuera nueva.
- Caché ya caducada. En formato Anthropic Messages, la fijación también termina cuando la conversación lleva inactiva más tiempo del que dura su caché: 5 minutos, o 1 hora si tus puntos de corte
cache_controlusanttl: "1h". Ya no queda nada que proteger, así que el siguiente turno se coloca de cero en lugar de esperar a su antiguo canal. - Canal ocupado. Si el canal fijado está al máximo, el turno lo espera un momento (alrededor de un segundo para prompts cortos, hasta diez segundos para los largos) en vez de moverse y pagar una nueva escritura de caché. Si sigue lleno, el turno se mueve.
- Canal con fallos. Si el canal fijado no está sano, el turno pasa a uno sano y la fijación se va con él. Ese turno escribe una caché nueva y los siguientes leen de ella.
Da a cada conversación un id explícito#
La huella automática se rompe si el primer mensaje del usuario cambia de un turno a otro, por ejemplo cuando tu app resume o reescribe el historial antiguo. En ese caso, envía un id estable por conversación. Una cabecera funciona en todos los formatos:
curl https://halfoff.dev/v1/messages \
-H "x-api-key: $HALFOFF_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "x-session-id: thread_51c7d0" \
-H "content-type: application/json" \
-d '{"model": "claude-sonnet-5.5", "max_tokens": 1024,
"messages": [{"role": "user", "content": "Sigue donde lo dejamos."}]}'Los campos del cuerpo sirven igual: metadata.user_id en formato Anthropic, prompt_cache_key en formato OpenAI. Usa un id por conversación; compartir un mismo id entre muchas conversaciones sin relación las amontona todas en un solo canal.
La caché en los registros y la facturación#
Para cada llamada, Registros muestra:
| Campo | Significado |
|---|---|
| Ent. | Entrada sin caché más escrituras de caché. |
| En caché | Tokens de entrada leídos de la caché. |
| Escritura de caché (detalles) | Tokens de entrada escritos en la caché. |
| Acierto de caché (detalles) | Tokens cacheados como proporción de toda la entrada. |
| Afinidad (detalles) | explicit: enviaste un id de sesión. sticky: la conversación estaba fijada y volvió a su canal. new: primera llamada, o la fijación había caducado o su canal no estaba disponible. none: no se pudo construir una huella. |
El resumen del panel muestra tu tasa de aciertos de caché en todas las llamadas. Cada tipo de token tiene su propia tarifa a mitad de precio; consulta la fórmula.
¿Tasa de aciertos baja?
Si todos los turnos de una conversación aparecen como new, algo en su prompt de sistema o en el primer mensaje del usuario cambia entre turnos. El culpable habitual es una marca de tiempo en el prompt de sistema, que además anula la caché del fabricante: muévela al último mensaje del usuario o envía un id de sesión explícito. Si los turnos aparecen como sticky pero En caché sigue vacío, comprueba que el prefijo alcance la longitud mínima del modelo y, en Claude, que lleve cache_control.