Saltar al contenido
estudIA

Agentes4 min de lectura

Tu primer agente con la API de Claude, paso a paso

Construye en Python un agente sencillo que usa herramientas: cómo se definen, cómo funciona el bucle de agente y cómo añadir límites y supervisión humana.

En resumen

  1. Prepara el entorno. Instala el SDK de Python y guarda tu clave de API en una variable de entorno.
  2. Define las herramientas. Describe cada herramienta con nombre, descripción y parámetros en JSON Schema.
  3. Escribe el bucle. Llama al modelo, ejecuta las herramientas que pida y devuélvele los resultados hasta que termine.
  4. Pon límites. Número máximo de vueltas, aprobación humana para acciones sensibles y registro de lo que hace.
  5. Pruébalo. Lanza tareas sencillas, mira cada llamada y mejora las descripciones de las herramientas.

Un agente es un modelo que usa herramientas en bucle hasta lograr un objetivo. Suena complejo, pero la idea cabe en unas pocas líneas de código. En este tutorial construimos uno en Python con la API de Claude: le damos dos herramientas, le pedimos una tarea y vemos cómo decide qué usar. Al final entenderás qué pasa dentro de Claude Code, Codex y compañía.

Necesitas saber algo de Python y una cuenta en la consola de Claude con una clave de API.

Paso 1: prepara el entorno

pip install anthropic
export ANTHROPIC_API_KEY="tu-clave"   # en Windows: setx ANTHROPIC_API_KEY "tu-clave"

Nunca pegues la clave en el código ni la subas a un repositorio. Pon un límite de gasto en la consola.

Paso 2: define las herramientas

Una herramienta es una función tuya que el modelo puede pedir ejecutar. Se describe con un nombre, una descripción y sus parámetros en JSON Schema. La descripción es lo más importante: es lo único que el modelo ve para decidir cuándo usarla.

import anthropic

client = anthropic.Anthropic()  # lee ANTHROPIC_API_KEY del entorno

TOOLS = [
    {
        "name": "calculadora",
        "description": "Evalúa una expresión aritmética con + - * / y paréntesis. Úsala para cualquier cálculo, en vez de calcular de cabeza.",
        "input_schema": {
            "type": "object",
            "properties": {"expresion": {"type": "string", "description": "Por ejemplo: (1200 * 0.21) + 15"}},
            "required": ["expresion"],
        },
    },
    {
        "name": "guardar_nota",
        "description": "Guarda una nota de texto en el archivo notas.txt. Úsala solo cuando el usuario pida guardar algo.",
        "input_schema": {
            "type": "object",
            "properties": {"texto": {"type": "string"}},
            "required": ["texto"],
        },
    },
]

Paso 3: escribe las funciones de verdad

import ast, operator as op

OPS = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.USub: op.neg}

def calcular(expr: str) -> str:
    # Evaluación segura: solo números y operaciones básicas, nunca eval().
    def ev(n):
        if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)):
            return n.value
        if isinstance(n, ast.BinOp) and type(n.op) in OPS:
            return OPS[type(n.op)](ev(n.left), ev(n.right))
        if isinstance(n, ast.UnaryOp) and type(n.op) in OPS:
            return OPS[type(n.op)](ev(n.operand))
        raise ValueError("expresión no permitida")
    return str(ev(ast.parse(expr, mode="eval").body))

def guardar_nota(texto: str) -> str:
    if input(f"¿Guardar la nota «{texto}»? (s/n) ").lower() != "s":
        return "El usuario ha rechazado guardar la nota."
    with open("notas.txt", "a", encoding="utf-8") as f:
        f.write(texto + "\n")
    return "Nota guardada."

def ejecutar(nombre: str, args: dict) -> str:
    try:
        if nombre == "calculadora":
            return calcular(args["expresion"])
        if nombre == "guardar_nota":
            return guardar_nota(args["texto"])
        return f"Herramienta desconocida: {nombre}"
    except Exception as e:
        return f"Error: {e}"

Fíjate en dos decisiones de seguridad: la calculadora no usa eval() (ejecutaría cualquier código) y guardar una nota pide confirmación humana. Son las barreras de seguridad mínimas de cualquier agente.

Paso 4: el bucle del agente

Este es el corazón: llamar al modelo, ejecutar las herramientas que pida, devolverle los resultados y repetir hasta que responda sin pedir más herramientas.

def agente(tarea: str, max_vueltas: int = 10) -> str:
    mensajes = [{"role": "user", "content": tarea}]
    for _ in range(max_vueltas):
        respuesta = client.messages.create(
            model="claude-sonnet-5-5",
            max_tokens=4000,
            tools=TOOLS,
            messages=mensajes,
        )
        if respuesta.stop_reason != "tool_use":
            return "".join(b.text for b in respuesta.content if b.type == "text")

        mensajes.append({"role": "assistant", "content": respuesta.content})
        resultados = []
        for bloque in respuesta.content:
            if bloque.type == "tool_use":
                print(f"→ {bloque.name}({bloque.input})")
                resultados.append({
                    "type": "tool_result",
                    "tool_use_id": bloque.id,
                    "content": ejecutar(bloque.name, bloque.input),
                })
        mensajes.append({"role": "user", "content": resultados})
    return "He parado: se ha alcanzado el límite de vueltas."

print(agente("Un portátil cuesta 899 € sin IVA. ¿Cuánto cuesta con el 21 % de IVA? Guarda el resultado en una nota."))

Al ejecutarlo verás algo así: el modelo pide la calculadora, recibe el resultado, pide guardar la nota (y tú confirmas) y termina con una respuesta en texto.

Qué está pasando

  1. El modelo recibe la tarea y la lista de herramientas.
  2. Si necesita una herramienta, responde con stop_reason == "tool_use" y un bloque con el nombre y los argumentos.
  3. Tu código la ejecuta y le devuelve un tool_result con el mismo tool_use_id.
  4. El modelo decide si necesita otra herramienta o si ya puede responder.

Eso es todo lo que hay debajo de un agente. Los agentes «de verdad» añaden más herramientas, memoria, subagentes y compactación de contexto, pero el bucle es el mismo.

Paso 5: pon límites

  • Máximo de vueltas (ya lo tienes) para que un error no lo deje en bucle gastando tokens.
  • Aprobación humana para cualquier acción con efectos: enviar, borrar, pagar, publicar.
  • Registro de cada llamada: qué herramienta, con qué argumentos y qué devolvió.
  • Herramientas mínimas: cuantas menos y más concretas, menos puede salir mal.

Lo desarrollamos en seguridad de agentes, y en patrones de diseño de agentes verás cuándo te conviene un agente y cuándo un flujo fijo.

Siguientes pasos

  • Cambia claude-sonnet-5-5 por claude-opus-5-5 para tareas más difíciles y compara.
  • Conecta herramientas reales mediante MCP en lugar de escribirlas a mano.
  • Prueba el tool runner del SDK, que gestiona este bucle por ti a partir de funciones decoradas.

Preguntas frecuentes

¿Cuánto cuesta probarlo?

Pagas por tokens. Un agente sencillo como este gasta muy poco por ejecución con Claude Sonnet 5.5 (2 $ por millón de tokens de entrada y 10 $ de salida). Pon un límite de gasto en la consola antes de empezar.

¿Hay una forma más rápida?

Sí: el SDK de Python tiene un tool runner que gestiona el bucle por ti a partir de funciones decoradas. Aquí lo escribimos a mano para que entiendas qué pasa por dentro.

Términos del glosario

Fuentes

Artículos relacionados