API · Tool use · Python

Tool use en la API de Claude con Python: tu primer agente que usa herramientas

Publicado el 22 de octubre de 2026 · Comprobado en la documentación oficial de Anthropic el 21 de octubre de 2026 · 12 min de lectura · Por el equipo de Master en Claude · Blog MasterEnClaude

Una cosa es pedirle a Claude que te explique cómo va un pedido y otra muy distinta que lo consulte de verdad en tu sistema y te responda con el dato real. Lo segundo se llama tool use (también lo verás como function calling o «llamadas a función»), y es la base de cualquier agente que hace cosas: buscar en una base de datos, calcular un presupuesto, crear una tarea o llamar a otra API.

En esta guía construyes, en Python y en unas 100 líneas, un pequeño agente de atención al cliente con dos herramientas. Verás cómo describirlas, cómo funciona el bucle tool_use → tool_result, cómo manejar errores y cuánto cuesta.

Respuesta corta. Con tool use le pasas a Claude, en el parámetro tools, una lista de funciones con su nombre, su descripción y un JSON Schema de sus parámetros. Si Claude decide usar una, responde con stop_reason: "tool_use" y un bloque tool_use con los argumentos. Tu código ejecuta la función y le devuelve el resultado en un bloque tool_result. Repites hasta que Claude responde con texto. Eso es un agente. Si no quieres escribir el bucle, el SDK de Python trae un tool runner (en beta) que lo hace por ti.

Qué es tool use y por qué importa

Claude no ejecuta tu código ni entra en tu base de datos. Lo que hace es decidir qué función conviene llamar y con qué argumentos, según la petición del usuario y la descripción que tú le das de cada herramienta. La ejecución siempre la hace tu programa.

La documentación oficial distingue dos tipos:

Dónde se ejecuta Ejemplos
Herramientas de cliente En tu aplicación Las que defines tú (buscar_pedido), y otras con esquema publicado por Anthropic como bash o text_editor
Herramientas de servidor En la infraestructura de Anthropic web_search, web_fetch, code_execution, tool_search

En este artículo nos centramos en las primeras, que son las que convierten a Claude en el «cerebro» de tu propia aplicación. Si lo que quieres es conectar Claude con herramientas ya hechas (GitHub, Drive, una base de datos), mira también qué es MCP: es el estándar abierto para empaquetar herramientas y reutilizarlas.

Antes de empezar

Necesitas tres cosas:

  1. Una API key de la Claude Console. Si aún no la tienes, aquí explicamos cómo obtener tu API key y hacer tu primera llamada.
  2. Python 3.10 o posterior (el requisito actual del SDK oficial).
  3. El SDK de Anthropic y la clave en una variable de entorno:
pip install anthropic
export ANTHROPIC_API_KEY="tu-api-key"

En Windows (PowerShell) la variable se define con $env:ANTHROPIC_API_KEY="tu-api-key".

En los ejemplos usamos Claude Sonnet 5.5 (claude-sonnet-5-5), que según Anthropic es «la mejor combinación de velocidad e inteligencia» y cuesta menos que Opus. Todos los modelos actuales admiten tool use, así que puedes cambiarlo por claude-opus-5-5 o claude-haiku-5-5 sin tocar nada más.

Cómo funciona el ciclo, en 4 pasos

  1. Tú envías la pregunta del usuario y la lista de herramientas (tools).
  2. Claude responde con stop_reason: "tool_use" y uno o varios bloques tool_use. Cada bloque trae un id, el name de la herramienta y un input con los argumentos.
  3. Tu código ejecuta cada función y devuelve los resultados en un mensaje con role: "user" que contiene bloques tool_result, cada uno con el tool_use_id correspondiente.
  4. Claude lee los resultados y, o bien pide otra herramienta (vuelves al paso 3), o bien responde con texto. Cuando stop_reason ya no es "tool_use", has terminado.

Una respuesta del paso 2 tiene este aspecto:

{
  "stop_reason": "tool_use",
  "content": [
    {"type": "text", "text": "Voy a buscar tu pedido."},
    {
      "type": "tool_use",
      "id": "toolu_01A09q90qw90lq917835lq9",
      "name": "buscar_pedido",
      "input": {"id_pedido": "A-1002"}
    }
  ]
}

Y tu respuesta del paso 3, esta:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
      "content": "{\"estado\": \"preparando\", \"pais\": \"CO\", \"peso_kg\": 3.5}"
    }
  ]
}

Paso 1: describe bien tus herramientas (lo más importante)

Cada herramienta tiene tres campos obligatorios:

  • name: el nombre. Debe cumplir el patrón ^[a-zA-Z0-9_-]{1,128}$, así que nada de espacios ni tildes.
  • description: qué hace, cuándo usarla y qué devuelve.
  • input_schema: un JSON Schema con los parámetros.

Anthropic lo dice sin rodeos: una descripción extremadamente detallada es «con diferencia el factor más importante» para que Claude use bien las herramientas. Recomienda al menos 3 o 4 frases por herramienta: qué hace, cuándo debe usarse (y cuándo no), qué significa cada parámetro y qué limitaciones tiene.

Compara:

  • ❌ "Busca un pedido."
  • ✅ "Busca un pedido de la tienda por su identificador y devuelve su estado (preparando, enviado o entregado), el país de destino (código ISO de 2 letras) y el peso en kg. Úsala siempre que el usuario pregunte por un pedido concreto. No devuelve datos personales del cliente."

Con la segunda, Claude sabe cuándo llamarla, qué esperar y qué no puede pedirle.

Truco: si un parámetro solo admite unos valores concretos, usa enum en el esquema (como hacemos con pais en el ejemplo). Y si necesitas que los argumentos cumplan el esquema al 100 %, añade "strict": true a la herramienta: Anthropic garantiza entonces que el input siempre encaja con tu JSON Schema (strict tool use).

Paso 2: el agente completo en Python

Este es el código entero. Simula una tienda con dos herramientas: buscar_pedido y calcular_envio. Los datos son inventados; en tu caso vendrían de tu base de datos o de tu API.

Guárdalo como agente.py:

import json
import anthropic

client = anthropic.Anthropic()  # lee ANTHROPIC_API_KEY del entorno
MODELO = "claude-sonnet-5-5"

# --- 1. Datos de ejemplo (en tu caso, tu base de datos o tu API) ---
PEDIDOS = {
    "A-1001": {"estado": "enviado", "pais": "MX", "peso_kg": 1.2},
    "A-1002": {"estado": "preparando", "pais": "CO", "peso_kg": 3.5},
}
TARIFAS_EUR_POR_KG = {"ES": 4.0, "MX": 9.0, "CO": 9.5, "AR": 11.0}

# --- 2. Las funciones de Python que Claude podrá pedir ---
def buscar_pedido(id_pedido: str) -> dict:
    if id_pedido not in PEDIDOS:
        raise ValueError(f"No existe el pedido {id_pedido}. Pide al usuario que revise el número.")
    return PEDIDOS[id_pedido]

def calcular_envio(pais: str, peso_kg: float) -> dict:
    if pais not in TARIFAS_EUR_POR_KG:
        raise ValueError(f"No enviamos a {pais}. Países disponibles: {', '.join(TARIFAS_EUR_POR_KG)}.")
    return {"pais": pais, "coste_eur": round(TARIFAS_EUR_POR_KG[pais] * peso_kg, 2)}

FUNCIONES = {"buscar_pedido": buscar_pedido, "calcular_envio": calcular_envio}

# --- 3. La descripción de las herramientas (lo que ve Claude) ---
TOOLS = [
    {
        "name": "buscar_pedido",
        "description": (
            "Busca un pedido de la tienda por su identificador y devuelve su estado "
            "(preparando, enviado o entregado), el país de destino (código ISO de 2 letras) "
            "y el peso en kg. Úsala siempre que el usuario pregunte por un pedido concreto. "
            "No devuelve datos personales del cliente."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "id_pedido": {"type": "string", "description": "Identificador del pedido, por ejemplo A-1001"}
            },
            "required": ["id_pedido"],
        },
    },
    {
        "name": "calcular_envio",
        "description": (
            "Calcula el coste de envío en euros para un país y un peso. "
            "Úsala cuando el usuario quiera saber cuánto cuesta enviar algo. "
            "Solo admite los países ES, MX, CO y AR."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "pais": {"type": "string", "enum": ["ES", "MX", "CO", "AR"]},
                "peso_kg": {"type": "number", "description": "Peso del paquete en kilogramos"},
            },
            "required": ["pais", "peso_kg"],
        },
    },
]

# --- 4. El bucle del agente ---
def preguntar(pregunta: str, max_vueltas: int = 10) -> str:
    messages = [{"role": "user", "content": pregunta}]
    for _ in range(max_vueltas):
        response = client.messages.create(
            model=MODELO,
            max_tokens=2048,
            system="Eres el asistente de atención al cliente de una tienda online. Responde en español.",
            tools=TOOLS,
            messages=messages,
        )
        # Guardamos la respuesta completa (incluidos los bloques de thinking) tal cual
        messages.append({"role": "assistant", "content": response.content})

        if response.stop_reason != "tool_use":
            return "".join(b.text for b in response.content if b.type == "text")

        # Ejecutamos TODAS las herramientas pedidas y devolvemos los resultados juntos
        resultados = []
        for bloque in response.content:
            if bloque.type != "tool_use":
                continue
            print(f"→ Claude pide {bloque.name}({json.dumps(bloque.input, ensure_ascii=False)})")
            try:
                salida = FUNCIONES[bloque.name](**bloque.input)
                resultados.append({
                    "type": "tool_result",
                    "tool_use_id": bloque.id,
                    "content": json.dumps(salida, ensure_ascii=False),
                })
            except Exception as e:
                resultados.append({
                    "type": "tool_result",
                    "tool_use_id": bloque.id,
                    "content": str(e),
                    "is_error": True,
                })
        messages.append({"role": "user", "content": resultados})

    return "He llegado al límite de vueltas sin una respuesta final."

if __name__ == "__main__":
    print(preguntar("¿Cómo va mi pedido A-1002 y cuánto costaría enviarlo a Argentina en vez de a Colombia?"))

Ejecútalo con python agente.py. Verás algo así (el texto exacto cambia en cada ejecución):

→ Claude pide buscar_pedido({"id_pedido": "A-1002"})
→ Claude pide calcular_envio({"pais": "CO", "peso_kg": 3.5})
→ Claude pide calcular_envio({"pais": "AR", "peso_kg": 3.5})
Tu pedido A-1002 se está preparando. Enviarlo a Colombia cuesta 33,25 € y a Argentina 38,50 €, es decir, 5,25 € más.

Fíjate en que Claude ha encadenado las herramientas por su cuenta: primero ha buscado el pedido para saber el peso y luego ha calculado los dos envíos. Tú no le has dicho en qué orden hacerlo. Eso es lo que convierte un chatbot en un agente.

Paso 3: las 4 reglas que evitan el 90 % de los errores

Estas reglas salen de la documentación oficial y son las que más fallan al empezar.

1. Devuelve el content del asistente tal cual. En el código añadimos response.content completo al historial. Los modelos actuales piensan antes de responder (adaptive thinking), y cuando hay herramientas por medio hay que devolver los bloques de pensamiento completos y sin modificar. Si solo guardas el texto, la siguiente llamada fallará o perderá contexto.

2. Todos los resultados, en un solo mensaje y primero. Claude puede pedir varias herramientas a la vez (llamadas en paralelo, activadas por defecto). Debes ejecutar todas y devolver todos los tool_result en un único mensaje de usuario. Además, los tool_result tienen que ir al principio del content: si pones texto antes, la API devuelve un error 400.

3. Nada entre la petición y el resultado. El mensaje con los tool_result debe ir justo después del mensaje del asistente que pidió las herramientas. Si ves el error «tool_use ids were found without tool_result blocks immediately after», es esto.

4. Si algo falla, díselo a Claude con is_error. No lances la excepción y cortes el programa. Devuelve un tool_result con "is_error": true y un mensaje útil. Anthropic recomienda mensajes que expliquen qué ha pasado y qué probar después (por ejemplo, «No existe el pedido A-9999. Pide al usuario que revise el número»), no un simple «error». Así Claude puede recuperarse o pedirle el dato al usuario.

Seguridad: lo que devuelven tus herramientas (una web, un email, un formulario) puede contener instrucciones maliciosas. Anthropic recomienda dejar ese contenido dentro de los tool_result, nunca copiarlo al system ni a un mensaje de usuario normal.

Atajo: el tool runner del SDK de Python

Si no necesitas controlar cada paso (por ejemplo, para pedir aprobación humana antes de ejecutar algo), el SDK de Python incluye un tool runner, de momento en beta. Defines la función con el decorador @beta_tool, con type hints y un docstring, y el SDK genera el JSON Schema, ejecuta las herramientas, gestiona el historial y repite el bucle por ti:

import json
from anthropic import Anthropic, beta_tool

client = Anthropic()

@beta_tool
def calcular_envio(pais: str, peso_kg: float) -> str:
    """Calcula el coste de envío en euros para un país y un peso.

    Args:
        pais: Código ISO de 2 letras del país de destino (ES, MX, CO o AR)
        peso_kg: Peso del paquete en kilogramos
    """
    tarifas = {"ES": 4.0, "MX": 9.0, "CO": 9.5, "AR": 11.0}
    if pais not in tarifas:
        raise ValueError(f"No enviamos a {pais}.")
    return json.dumps({"pais": pais, "coste_eur": round(tarifas[pais] * peso_kg, 2)})

runner = client.beta.messages.tool_runner(
    model="claude-sonnet-5-5",
    max_tokens=2048,
    max_iterations=10,
    tools=[calcular_envio],
    messages=[{"role": "user", "content": "¿Cuánto cuesta enviar 2 kg a México?"}],
)
final = runner.until_done()
print("".join(b.text for b in final.content if b.type == "text"))

Si una herramienta lanza una excepción, el runner la captura y se la devuelve a Claude como tool_result con is_error: true. Usa max_iterations para poner un límite de vueltas y no llevarte sorpresas en la factura.

¿Cuándo usar cada uno? El runner es ideal para prototipos y agentes sencillos. El bucle manual es mejor cuando quieres registrar cada llamada, pedir confirmación antes de acciones delicadas (borrar, pagar, enviar) o decidir qué herramientas permitir según el usuario.

Controlar cuándo usa Claude las herramientas: tool_choice

Por defecto (tool_choice: {"type": "auto"}), Claude decide en cada turno si usa una herramienta o responde directamente. Puedes orientarlo desde el system prompt: según Anthropic, una frase como «Usa las herramientas para investigar antes de responder» aumenta su uso.

Otras opciones útiles:

  • {"type": "none"}: le prohíbe usar herramientas en ese turno.
  • {"type": "auto", "disable_parallel_tool_use": true}: como mucho una herramienta por respuesta.
  • {"type": "any"} o {"type": "tool", "name": "..."}: obligan a usar una herramienta. Ojo: a 21 de octubre de 2026, Claude Opus 5.5, Sonnet 5.5 y Fable 5.1 no admiten forzar herramientas y devuelven un error 400. Con esos modelos, Anthropic recomienda auto combinado con strict tool use, o structured outputs si lo que necesitas es una respuesta con un formato JSON fijo.

¿Cuánto cuesta usar herramientas?

Tool use no tiene una tarifa aparte: pagas los tokens de siempre. Pero las herramientas añaden tokens de entrada:

  • La definición de tus tools (nombres, descripciones y esquemas) se envía en cada llamada.
  • Los bloques tool_use y tool_result forman parte del historial.
  • La API añade un system prompt interno para activar las herramientas: 286 tokens con Claude Sonnet 5.5, Opus 5.5 y Haiku 5.5 (con tool_choice en auto o none).

Hagamos una estimación orientativa con Claude Sonnet 5.5 ($2 por millón de tokens de entrada y $10 por millón de salida). Una ejecución del agente de arriba hace unas 3 llamadas. Si cada una envía unos 1.500 tokens y genera unos 500 (contando el razonamiento), el total sale por unos $0,024, unos 2 céntimos de euro. Es una estimación: el coste real te lo da el campo usage de cada respuesta.

Las herramientas de servidor (como web_search) sí pueden tener un coste extra por uso. Para calcular tu gasto con más detalle, y ver cómo abaratarlo con la caché de prompts, tienes la guía de precio de la API de Claude.

Errores frecuentes con tool use (y cómo arreglarlos)

Síntoma Causa probable Solución
Error 400 «tool_use ids were found without tool_result blocks immediately after» Falta algún tool_result o hay un mensaje en medio Responde a todos los tool_use en el mensaje siguiente
Error 400 al enviar los resultados Texto antes de los tool_result Pon los tool_result primero y el texto, si lo hay, después
Error 400 con tool_choice: any o tool El modelo (Opus 5.5, Sonnet 5.5, Fable 5.1) no admite forzar herramientas Usa auto y orienta con el system prompt, o strict tool use
Claude no usa la herramienta La descripción es pobre o no dice cuándo usarla Reescríbela con 3 o 4 frases: qué hace, cuándo, qué devuelve
Claude se inventa un argumento (por ejemplo, una ciudad) Faltaba el dato y lo ha supuesto Indica en la descripción que debe preguntar si falta, o en el system prompt
El agente da vueltas sin parar No hay límite de iteraciones Pon un máximo (max_vueltas en el bucle manual, max_iterations en el runner)

Preguntas frecuentes sobre tool use en la API de Claude

¿Qué diferencia hay entre tool use y function calling?

Ninguna. Es el mismo concepto con dos nombres. Anthropic lo llama tool use y explica en su documentación que también se conoce como function calling: Claude decide qué función llamar y con qué argumentos, y tu aplicación la ejecuta.

¿Claude ejecuta mi código?

No, en el caso de las herramientas de cliente. Claude solo devuelve un bloque tool_use con el nombre de la función y los argumentos, y es tu programa el que la ejecuta y le devuelve el resultado. Solo las herramientas de servidor, como web_search o code_execution, se ejecutan en la infraestructura de Anthropic.

¿Cuánto cuesta usar herramientas en la API de Claude?

Se paga por tokens, igual que cualquier otra llamada. Las definiciones de las herramientas y los bloques tool_use y tool_result cuentan como tokens, y la API añade un system prompt interno de 286 tokens con Claude Sonnet 5.5, Opus 5.5 y Haiku 5.5. Algunas herramientas de servidor, como la búsqueda web, tienen un coste adicional por uso.

¿Puede Claude llamar a varias herramientas a la vez?

Sí. Las llamadas en paralelo están activadas por defecto en los modelos actuales. Tienes que ejecutar todas las herramientas pedidas y devolver todos los resultados en un único mensaje de usuario. Si prefieres una herramienta por turno, usa disable_parallel_tool_use: true dentro de tool_choice.

¿Qué relación tiene tool use con MCP?

MCP (Model Context Protocol) es un estándar para empaquetar herramientas en un servidor y reutilizarlas en distintas aplicaciones. Por debajo, Claude sigue usando tool use: MCP solo cambia de dónde salen las herramientas. Lo explicamos en qué es MCP.

Ya tienes tu primer agente. Ahora, llévalo a producción

En la ruta API para desarrolladores (22 lecciones) y la ruta MCP y agentes (16 lecciones) de MasterEnClaude pasas de este ejemplo a agentes reales: streaming, caché, servidores MCP propios y el bucle de agente completo, con un test en cada lección y en español.

La nueva edición, actualizada a Claude 5.5, abre el 17 de noviembre. Mismo precio en todos los países, con el equivalente en tu moneda. Hoy no pagas nada: reserva tu plaza y te avisamos el día de la apertura.

Reservar mi plaza gratis →
Prefiero empezar con la guía gratuita