Optimizaciones: strict, defer_loading, cache_control
Objetivo
Al terminar esta lección sabrás 3 optimizaciones beta clave para tools — strict (valida el schema antes de ejecutar), defer_loading (no carga la definición de la tool a menos que sea necesaria), y cache_control (cachea las definiciones de tools para no pagar tokens en cada request). Cuándo usar cada una, qué impacto tienen en latencia/costo/robustez, y cómo combinarlas.
Concepto
El problema que optimizan
Cuando un agente tiene muchas tools (10, 30, 100+), tres problemas aparecen:
- Prompt inflado: cada tool definition ocupa tokens, y se inyecta en cada request. 30 tools = varios miles de tokens fijos por llamada.
- Validación client-side: el modelo puede devolver
inputque no matchea el schema (ej: faltar un required, type incorrecto). Sin validación, tu código crashea. - Cold start en agentes largos: en un loop multi-turn, estás pagando por las mismas tool definitions una y otra vez.
Las 3 optimizaciones de esta lección atacan cada uno:
| Problema | Optimización |
|---|---|
| Schema drift | strict: true |
| Prompt inflado con tools raramente usadas | defer_loading: true |
| Tokens repetidos entre turnos | cache_control |
1. strict — validación estructurada garantizada
{
"name": "create_ticket",
"description": "...",
"strict": true,
"input_schema": { ... }
}Con strict: true:
- El modelo garantiza que el
inputdel tool_use matchea el schema exactamente. - No aparecen campos extra no definidos en
properties. - Todos los
requiredestán presentes. - Los
enumse respetan (no aparecen valores fuera del set).
Sin strict:
{
"name": "create_ticket",
"input": {
"title": "bug",
"priority": "super_urgent", ← no está en el enum
"extra_field": "hola" ← no está en el schema
}
}Tu código tiene que validar, detectar, y re-promptear.
Con strict:
{
"input": {
"title": "bug",
"priority": "urgent" ← valor del enum
}
}Garantizado por el modelo.
2. defer_loading — tools bajo demanda
{
"name": "rare_admin_tool",
"description": "...",
"defer_loading": true,
"input_schema": { ... }
}Con defer_loading: true, la definición completa de la tool NO se inyecta al system prompt por default. El modelo solo ve una versión mínima (name + description corta). Si al modelo le parece relevante, pide "cargar" la definición completa — entonces Anthropic la inyecta y el modelo puede usarla.
Caso de uso: agentes con catálogos grandes de tools (ej: 200 tools cliente, Claude Code con 50+ skills).
Beneficio: no pagás tokens por tools que probablemente no vas a usar en este turno.
Costo: latencia — si la tool sí es necesaria, el modelo necesita un round-trip interno para cargarla.
Regla: usá defer_loading: true para tools con baja probabilidad de uso, y false (default) para tools frecuentes.
3. cache_control — cachear tool definitions
{
"tools": [
{ "name": "tool_a", "description": "...", "input_schema": {...} },
{ "name": "tool_b", "description": "...", "input_schema": {...} },
{
"name": "tool_c",
"description": "...",
"input_schema": {...},
"cache_control": { "type": "ephemeral", "ttl": "5m" }
}
]
}El cache_control sobre la última tool del array marca un "cache breakpoint": todo el prefix (system prompt + tools hasta ese punto) se cachea. En requests siguientes dentro del TTL, Anthropic reusa el cache y te cobra una fracción de los tokens (cache hit).
Cuándo cachea:
- Segunda request con el mismo prefix exacto dentro del TTL.
- Contador de tokens: aparece
usage.cache_creation_input_tokens(primera vez) y luegousage.cache_read_input_tokens(hits siguientes, cobrados al ~10%).
Cuándo NO cachea:
- Cambiaste cualquier tool (order, name, description, schema).
- TTL expiró.
- El prefix es distinto en bytes.
Combinar las 3
Un agente profesional típicamente combina:
{
"tools": [
{
"name": "common_tool",
"strict": true,
"input_schema": {...}
},
{
"name": "rare_admin_tool",
"defer_loading": true,
"input_schema": {...}
},
{
"name": "last_tool",
"strict": true,
"input_schema": {...},
"cache_control": { "type": "ephemeral", "ttl": "1h" }
}
]
}- Tools frecuentes →
strictpara validación gratis. - Tools raras →
defer_loadingpara no pagar tokens siempre. - Último del array →
cache_controlpara cachear todo el prefix.
Ejecución real
Nota: output abreviado — el efecto de estas optimizaciones se observa en
usage(cache_read_input_tokens, cache_creation_input_tokens) y en la validación del input. No hay cambio en la shape del content.
Request con strict + cache_control:
curl -s https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-haiku-4-5",
"max_tokens": 500,
"tools": [
{
"name": "extract_contact",
"description": "Extract contact info from text.",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
"phone": {"type": "string"}
},
"required": ["name", "email"]
},
"cache_control": {"type": "ephemeral", "ttl": "5m"}
}
],
"tool_choice": {"type": "tool", "name": "extract_contact"},
"messages": [
{"role": "user", "content": "Contactate con Mariana Lopez al mariana@example.com"}
]
}'Response (primera vez — cache miss):
{
"content": [{
"type": "tool_use",
"id": "toolu_...",
"name": "extract_contact",
"input": { "name": "Mariana Lopez", "email": "mariana@example.com" }
}],
"stop_reason": "tool_use",
"usage": {
"input_tokens": 50,
"cache_creation_input_tokens": 420,
"cache_read_input_tokens": 0,
"output_tokens": 60
}
}Request 2, idéntica salvo por el mensaje user (dentro del TTL):
{
"usage": {
"input_tokens": 55,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 420,
"output_tokens": 60
}
}cache_read_input_tokens: 420 → te cobran esos a un ~10% del precio normal. Ahorro real.
Medir el ahorro
const resp = await client.messages.create({...});
const cacheCreation = resp.usage.cache_creation_input_tokens ?? 0;
const cacheRead = resp.usage.cache_read_input_tokens ?? 0;
const inputTokens = resp.usage.input_tokens ?? 0;
console.log(`Cache creation: ${cacheCreation}`);
console.log(`Cache read (90% cheaper): ${cacheRead}`);
console.log(`Uncached input: ${inputTokens}`);En un agente con 50 tools y 20 turnos, el cache_control reduce costo típicamente 30-60%.
Anti-patterns
- ❌ Activar
strict: truepor defecto en tools con schemas inestables. Si todavía estás iterando el schema,strictte fuerza a rigidez prematura. Prendelo cuando el schema esté consolidado. - ❌ Usar
defer_loadingpara la tool principal del agente. Si es la que usás en el 80% de turnos, paga el hit del load cada vez.defer_loadinges para la "long tail". - ❌ Poner
cache_controlen cada tool del array. Solo el último breakpoint cuenta para cachear el prefix completo. Multiples breakpoints = multiples cache levels (complica mucho; solo lo vas a querer en casos avanzados). - ❌ Olvidar que cualquier cambio al prefix invalida el cache. Reordenar tools, cambiar una description, agregar una tool al medio → cache invalidado. El orden y contenido deben ser idénticos entre requests.
- ❌ Mezclar
ttl: "1h"sin el beta header. Requiereanthropic-beta: extended-cache-ttl-2025-04-11. Sin eso, el TTL se silencia a 5m. - ❌ Asumir que
strictreemplaza toda validación. Valida schema, pero NO valida lógica (ej:emailconformat: "email"garantiza sintaxis pero no que el email exista). Validación de negocio sigue siendo tuya.
Recap
strict: true— el modelo garantiza que elinputdel tool_use matchea el schema. Reemplaza gran parte de tu validación client-side.defer_loading: true— la definición completa solo se carga si el modelo la necesita. Ideal para agentes con muchas tools raramente usadas.cache_control: {type: "ephemeral", ttl: "5m"|"1h"}— cachea el prefix (tools + system). Segunda request con mismo prefix = tokens a ~10% del precio.- Combinalas:
stricten tools de schema estable,defer_loadingen tools raras,cache_controlen la última tool del array para cachear todo. - Medí con
usage.cache_creation_input_tokensyusage.cache_read_input_tokens.
Fuente oficial: platform.claude.com/docs/en/build-with-claude/tool-use/implementation · platform.claude.com/docs/en/build-with-claude/prompt-cachingEjercicio: