Cómo usar Programmatic Tool Calling de Claude: guía paso a paso con código real
Construye un script Python que consulta 20 registros de gastos en paralelo usando Programmatic Tool Calling de Claude. Menos round-trips, menos tokens, más velocidad para flujos B2B reales.
Cognitiva, agencia chilena de IA, aplica Programmatic Tool Calling de Claude en flujos de cumplimiento para pymes con el objetivo de reducir los round-trips de inferencia y el consumo de tokens. Esta feature —disponible desde 2026 en la API de Anthropic— cambia la forma en que Claude invoca herramientas: en lugar de solicitar cada dato por separado (un round-trip por consulta), Claude escribe código Python que ejecuta todas las llamadas dentro de un contenedor sandboxed, filtra los resultados y entrega solo lo relevante al contexto del modelo. En este tutorial construirás un script completo en Python 3.12 con Anthropic SDK que consulta los registros de gastos de 20 empleados en paralelo, identifica quiénes superaron su límite trimestral y entrega un resumen ejecutivo. Todo en un único intercambio con el modelo, sin múltiples vueltas de inferencia. Al terminar —en aproximadamente 45 minutos— tendrás el patrón listo para adaptarlo a cualquier caso de uso B2B: auditorías, informes de cumplimiento, validaciones masivas de datos o búsquedas agénticas sobre fuentes internas.
Qué es Programmatic Tool Calling y cuándo usarlo
Programmatic Tool Calling permite que Claude invoque tus herramientas desde dentro de un bloque de código Python, en lugar de pedirte que ejecutes cada tool_use de forma individual. La diferencia clave: los resultados intermedios no se cargan al contexto del modelo hasta que el script termina. Solo el output final llega a Claude.
Tool use tradicional vs. Programmatic Tool Calling
| Criterio | Tool use tradicional | Programmatic Tool Calling |
|---|---|---|
| Round-trips al modelo | 20 (uno por empleado) | 1 |
| Datos al contexto del modelo | Cientos de KB de registros crudos | Unos pocos KB (solo resultado filtrado) |
| Lógica condicional | En el cliente | En el script Python de Claude |
La feature es útil en tres escenarios: operaciones de fan-out (consultar N registros o endpoints), resultados grandes que conviene filtrar antes de que lleguen al modelo, y flujos agénticos de búsqueda iterativa. No conviene usarla en flujos estrictamente secuenciales donde cada llamada depende del razonamiento del modelo sobre el resultado anterior.
Restricciones importantes
- No compatible con structured outputs (strict: true).
- No compatible con tool_choice forzado sobre herramientas de calling programático.
- No disponible en Amazon Bedrock ni Vertex AI (solo Claude API, Claude Platform on AWS y Microsoft Foundry).
- No elegible para Zero Data Retention (ZDR): los datos se retienen según la política estándar del contenedor.
- Las herramientas provistas por un MCP connector no pueden invocarse programáticamente.
Prerrequisitos antes de empezar
Para seguir este tutorial necesitas: Python 3.12+, una API key de Anthropic con acceso a claude-opus-4-8 o claude-sonnet-4-6, y Anthropic SDK versión 0.110.0 o superior (la versión que introduce code_execution_20260120).
Instalación del entorno
python -m venv .venv
source .venv/bin/activate
pip install anthropic>=0.110.0Exporta tu API key antes de ejecutar cualquier script:
export ANTHROPIC_API_KEY="<tu-api-key>"Modelos compatibles con code_execution_20260120
- claude-opus-4-8
- claude-opus-4-7
- claude-opus-4-6
- claude-sonnet-4-6
- claude-opus-4-5-20251101
- claude-sonnet-4-5-20250929
Implementación: consulta masiva de gastos con asyncio
El caso de uso es directo: tienes 20 empleados y quieres saber cuáles superaron su presupuesto trimestral. Con tool use tradicional necesitarías 20 round-trips al modelo. Con Programmatic Tool Calling, Claude escribe un script que llama a tu herramienta get_expenses 20 veces dentro del contenedor, filtra los resultados y te devuelve solo los empleados que excedieron el límite.
Paso 1: definir la herramienta con allowed_callers
El campo allowed_callers es la clave de esta feature. Al asignarlo a ["code_execution_20260120"] le indicas a Claude que debe invocar la herramienta desde código Python, no como tool_use directo.
import anthropic
import json
client = anthropic.Anthropic()
# Definicion de herramientas
tools = [
# Herramienta de ejecucion de codigo (obligatoria)
{"type": "code_execution_20260120", "name": "code_execution"},
# Tu herramienta de negocio
{
"name": "get_expenses",
"description": (
"Consulta los gastos de un empleado en un trimestre dado. "
"Retorna JSON con campos: employee_id (str), name (str), "
"total_spent (float), budget_limit (float), quarter (str)."
),
"input_schema": {
"type": "object",
"properties": {
"employee_id": {
"type": "string",
"description": "ID del empleado, formato EMP-XXXX"
},
"quarter": {
"type": "string",
"description": "Trimestre en formato Q1, Q2, Q3 o Q4"
}
},
"required": ["employee_id", "quarter"]
},
# Clave: solo invocable desde el contenedor de code execution
"allowed_callers": ["code_execution_20260120"]
}
]Paso 2: simular tu backend de gastos
En producción, get_expenses llamaría a tu base de datos o ERP. Para este tutorial usamos una función que genera datos de ejemplo:
import random
EMPLOYEES = ["EMP-" + str(1000 + i) for i in range(20)]
def simulate_get_expenses(employee_id: str, quarter: str) -> dict:
"""Simula la respuesta de tu API de RRHH o ERP."""
random.seed(hash(employee_id + quarter))
budget = 500_000 # CLP
spent = random.randint(300_000, 650_000)
return {
"employee_id": employee_id,
"name": "Empleado " + employee_id.split("-")[1],
"total_spent": spent,
"budget_limit": budget,
"quarter": quarter
}Paso 3: el loop de inferencia que maneja el protocolo
Programmatic Tool Calling usa el mismo protocolo de tool_use, pero el campo caller indica que proviene del contenedor. Tu cliente debe procesar estos bloques y devolver los resultados antes de que el contenedor expire (4,5 minutos de inactividad).
def run_compliance_check(quarter: str = "Q1") -> str:
"""
Lanza la consulta de cumplimiento para todos los empleados.
Retorna el analisis en texto plano.
"""
employee_ids = ", ".join(EMPLOYEES)
user_message = (
"Revisa el cumplimiento de gastos del trimestre " + quarter + " "
"para estos 20 empleados: " + employee_ids + ". "
"Usa get_expenses para cada uno. "
"Entrega un listado de quienes superaron su presupuesto "
"y el monto total excedido en CLP."
)
messages = [{"role": "user", "content": user_message}]
container_id = None
# Loop de inferencia
while True:
kwargs = {
"model": "claude-opus-4-8",
"max_tokens": 4096,
"tools": tools,
"messages": messages,
}
# Reusar el contenedor si ya existe
if container_id:
kwargs["container"] = container_id
response = client.messages.create(**kwargs)
# Guardar el ID del contenedor para reuso
if hasattr(response, "container") and response.container:
container_id = response.container.id
# Agregar la respuesta del asistente al historial
messages.append({"role": "assistant", "content": response.content})
# Si Claude termino, retornar el texto
if response.stop_reason == "end_turn":
for block in response.content:
if hasattr(block, "text"):
return block.text
return "Sin respuesta de texto."
# Procesar tool calls (directas o programaticas)
tool_results = []
has_tool_use = False
for block in response.content:
if block.type == "tool_use":
has_tool_use = True
if block.name == "get_expenses":
result = simulate_get_expenses(
employee_id=block.input["employee_id"],
quarter=block.input["quarter"]
)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result)
})
if not has_tool_use:
# No hay mas tool calls: terminar
break
# IMPORTANTE: cuando hay tool calls programaticas, el mensaje
# del usuario solo puede contener tool_result blocks.
messages.append({"role": "user", "content": tool_results})
return "Proceso completado."
if __name__ == "__main__":
resultado = run_compliance_check("Q1")
print(resultado)Paso 4: ejecutar el script
python compliance_check.pyClaude escribe internamente un script async que llama a get_expenses para cada EMP-XXXX, filtra los que superaron budget_limit y entrega un resumen. Tu cliente solo ve el resultado final, no los 20 bloques intermedios de datos.
Verificar que el calling es realmente programático
El campo caller en cada bloque tool_use de la respuesta confirma si la invocación fue directa o programática. Agrega este log al loop para verificarlo:
for block in response.content:
if block.type == "tool_use":
caller_type = getattr(block, "caller", {}).get("type", "direct")
print("[" + block.name + "] caller: " + caller_type)
# Salida esperada:
# [get_expenses] caller: code_execution_20260120Checklist de validación antes de producción
- Verifica que container_id se reutiliza entre turns del mismo request.
- Monitorea expires_at en la respuesta: el contenedor expira a los 4,5 minutos de inactividad.
- Confirma que los mensajes de respuesta a tool calls programáticas contienen SOLO bloques tool_result (sin texto adicional).
- Valida que tus herramientas retornan JSON serializable (strings, números, listas, dicts).
- Activa logs de caller_type en staging para confirmar que allowed_callers está funcionando.
- Mide input_tokens en el objeto Usage de la respuesta antes y después de habilitar allowed_callers en una muestra representativa.
Limitaciones que debes conocer
- allowed_callers no es un bloqueo de seguridad a nivel de API: Claude está guiado a respetarlo, pero tu cliente debe estar preparado para manejar un tool_use directo de cualquier herramienta.
- No uses esta feature con strict: true en el schema de la herramienta.
- No disponible en Bedrock ni Vertex AI.
- Cada tool call desde el contenedor cuenta por separado en los rate limits.
Adaptar el patrón a otros casos de uso B2B
El patrón que acabas de construir es reutilizable. Lo que cambia es la herramienta de negocio y el prompt; el loop de inferencia es idéntico. Aquí van tres variantes habituales en pymes chilenas:
Auditoría de contratos vencidos
Define get_contract(contract_id, date) con allowed_callers: ["code_execution_20260120"]. Claude itera sobre tu lista de contratos, identifica los que vencen en los próximos 30 días y entrega un resumen priorizado.
Validación masiva de proveedores
Define check_supplier_status(rut) que consulta tu ERP. Claude ejecuta las N consultas en el contenedor, agrupa por estado (activo, suspendido, deuda) y te entrega solo las alertas.
Informe de KPIs por sucursal
Define get_branch_kpis(branch_id, month). Claude genera el script que consulta todas las sucursales, calcula desviaciones respecto al objetivo y formatea el informe. Un solo turn de inferencia para un informe que antes requería N consultas manuales.
En todos los casos el patrón es el mismo: una herramienta bien descrita (el campo description es crítico para que Claude sepa cómo deserializar la respuesta), allowed_callers apuntando al contenedor, y un loop que procesa los tool_use hasta que stop_reason sea end_turn.
Siguientes pasos y recursos
Con el patrón dominado, puedes extenderlo en dos direcciones: reusar contenedores entre requests relacionados (pasa container_id en sucesivos llamados para mantener estado) y combinar Programmatic Tool Calling con Tool Search Tool para reducir el overhead de definiciones cuando tienes muchas herramientas registradas.
Si tu caso de uso requiere orquestar múltiples agentes con esta feature, revisa el artículo sobre sistemas multiagente en este blog. Para integración con n8n o Make como capa de orquestación, consulta el artículo sobre el stack Vercel AI SDK + Claude + n8n.
Para evaluar si la feature conviene en tu carga de trabajo, mide input_tokens con y sin allowed_callers en una muestra representativa antes de habilitarla en producción. Los ahorros son significativos en operaciones de fan-out, pero el overhead del contenedor puede superar los ahorros en flujos con pocas llamadas secuenciales.
En Cognitiva acompañamos la implementación de estos patrones en producción. Si quieres llevar este tutorial a tu stack real, escríbenos.
Cómo usar Programmatic Tool Calling de Claude con Python
Construye un script Python que consulta 20 registros de gastos en paralelo usando Programmatic Tool Calling de Claude, reduciendo round-trips y tokens de entrada.
Preparar el entorno
Definir las herramientas
Implementar el loop de inferencia
Verificar y monitorear
Preguntas frecuentes
Da el siguiente paso
Hablemos
Cuéntanos sobre tu negocio. Respondemos en 24 horas.
Otros canales
Escríbenos por WhatsApp o agenda una llamada de 30 minutos.