Lo que vas a aprender
- Qué es un agente y cómo se diferencia de una llamada simple a la API
- Cómo definir herramientas (tools) que Claude puede invocar
- Cómo implementar el bucle agéntico (agentic loop) que ejecuta herramientas
- Cómo mantener el historial de conversación entre turnos
- Cómo manejar errores y evitar bucles infinitos
Paso 1: Instalar el SDK y configurar la API key
Un agente en Claude es un programa donde el modelo decide qué herramientas usar para completar una tarea, ejecuta esas herramientas mediante tu código, y repite el ciclo hasta terminar. Empezamos instalando el SDK oficial de Anthropic. En España y LATAM funciona igual: solo necesitas tu API key desde console.anthropic.com. Nunca la escribas directamente en el código; usa variables de entorno. Crea el entorno virtual para aislar dependencias y evita conflictos con otros proyectos. Exporta la clave en tu terminal antes de ejecutar cualquier script. Si usas Windows, el comando de exportación cambia a 'set' en CMD o '$env:' en PowerShell.
python -m venv venv
source venv/bin/activate # En Windows: venv\Scripts\activate
pip install anthropic
export ANTHROPIC_API_KEY='sk-ant-tu-clave-aqui'
💡 Consejo: Guarda la key en un archivo .env y usa python-dotenv para cargarla automáticamente en desarrollo.
⚠️ Error común: Nunca subas tu API key a GitHub. Añade .env a tu .gitignore desde el primer commit.
Paso 2: Definir las herramientas del agente
Las herramientas (tools) son funciones que Claude puede pedir ejecutar. Cada una se describe con un esquema JSON: nombre, descripción y parámetros de entrada. La descripción es crítica: Claude decide cuándo usar la herramienta basándose en ella, así que sé específico. Aquí definimos dos herramientas de ejemplo: una calculadora y una que consulta el clima (simulada). El campo input_schema sigue el estándar JSON Schema. Los parámetros marcados en 'required' son obligatorios. Cuanto más clara sea la descripción, mejor decidirá Claude qué herramienta usar y con qué argumentos. Piensa en las descripciones como el prompt que le dice al modelo para qué sirve cada capacidad.
tools = [
{
"name": "calcular",
"description": "Evalua una expresion matematica y devuelve el resultado numerico. Usar para cualquier calculo aritmetico.",
"input_schema": {
"type": "object",
"properties": {
"expresion": {
"type": "string",
"description": "Expresion matematica en Python, ej: '23 * 4 + 10'"
}
},
"required": ["expresion"]
}
},
{
"name": "consultar_clima",
"description": "Devuelve la temperatura actual de una ciudad. Usar cuando el usuario pregunte por el clima.",
"input_schema": {
"type": "object",
"properties": {
"ciudad": {"type": "string", "description": "Nombre de la ciudad, ej: 'Madrid'"}
},
"required": ["ciudad"]
}
}
]
💡 Consejo: Escribe las descripciones como si explicaras la herramienta a un becario: cuándo usarla y qué formato espera.
⚠️ Error común: Si dos herramientas tienen descripciones ambiguas, Claude puede confundirse. Diferéncialas claramente.
Paso 3: Implementar las funciones ejecutoras
Claude solo decide QUÉ herramienta usar y con qué argumentos, pero es tu código quien realmente la ejecuta. Aquí escribimos las funciones Python que corresponden a cada herramienta definida. La función 'ejecutar_herramienta' actúa como despachador: recibe el nombre y los argumentos que Claude solicitó, y llama a la implementación correcta. Para la calculadora usamos una evaluación controlada (en producción usa una librería segura, nunca eval directo con input de usuarios). El clima lo simulamos con un diccionario, pero en la vida real harías una llamada a una API como OpenWeatherMap. Devolvemos siempre un string, porque Claude recibirá ese texto como resultado de la herramienta.
def calcular(expresion):
try:
# En produccion usa una libreria segura como simpleeval
resultado = eval(expresion, {"__builtins__": {}}, {})
return str(resultado)
except Exception as e:
return f"Error al calcular: {e}"
def consultar_clima(ciudad):
datos = {"madrid": "18C", "buenos aires": "24C", "bogota": "14C"}
return datos.get(ciudad.lower(), "Ciudad no encontrada")
def ejecutar_herramienta(nombre, args):
if nombre == "calcular":
return calcular(args["expresion"])
elif nombre == "consultar_clima":
return consultar_clima(args["ciudad"])
return "Herramienta desconocida"
💡 Consejo: Envuelve cada ejecución en try/except: si una herramienta falla, devuelve el error como texto y Claude podrá reintentar o explicar el problema.
⚠️ Error común: Nunca uses eval() con input de usuarios reales sin sandbox. Aquí lo limitamos con __builtins__ vacío, pero usa simpleeval en producción.
Paso 4: Construir el bucle agéntico
Este es el corazón del agente. El bucle funciona así: enviamos el mensaje del usuario a Claude junto con las herramientas disponibles. Claude responde. Si su respuesta tiene stop_reason 'tool_use', significa que quiere ejecutar una o más herramientas. Extraemos esas peticiones, las ejecutamos con nuestro código, y devolvemos los resultados a Claude en un nuevo mensaje con rol 'user' que contiene bloques 'tool_result'. Claude entonces procesa esos resultados y puede pedir más herramientas o dar la respuesta final. Repetimos hasta que stop_reason sea 'end_turn'. El límite max_iteraciones evita bucles infinitos si algo sale mal. Fíjate cómo acumulamos todo en 'mensajes' para mantener el contexto completo de la conversación.
import anthropic
client = anthropic.Anthropic()
MODELO = "claude-opus-4-8"
def agente(pregunta_usuario, max_iteraciones=5):
mensajes = [{"role": "user", "content": pregunta_usuario}]
for i in range(max_iteraciones):
respuesta = client.messages.create(
model=MODELO,
max_tokens=1024,
tools=tools,
messages=mensajes
)
mensajes.append({"role": "assistant", "content": respuesta.content})
if respuesta.stop_reason != "tool_use":
# Respuesta final: extraer texto
for bloque in respuesta.content:
if bloque.type == "text":
return bloque.text
return "(sin respuesta de texto)"
# Ejecutar cada herramienta solicitada
resultados = []
for bloque in respuesta.content:
if bloque.type == "tool_use":
salida = ejecutar_herramienta(bloque.name, bloque.input)
resultados.append({
"type": "tool_result",
"tool_use_id": bloque.id,
"content": salida
})
mensajes.append({"role": "user", "content": resultados})
return "Se alcanzo el limite de iteraciones"
💡 Consejo: Claude puede pedir varias herramientas en un solo turno. Por eso iteramos sobre todos los bloques tool_use y devolvemos todos los tool_result juntos.
⚠️ Error común: Si olvidas incluir el tool_use_id en cada tool_result, la API devuelve un error 400. Cada resultado debe emparejarse con su petición.
Paso 5: Probar el agente con tareas reales
Ahora ejecutamos el agente con preguntas que requieren herramientas. La primera combina cálculo, la segunda mezcla clima de dos ciudades y una operación matemática, forzando a Claude a encadenar varias herramientas en secuencia. Verás en la práctica cómo el agente descompone la tarea: primero consulta un dato, luego otro, después calcula, y finalmente redacta una respuesta en lenguaje natural. Esto es lo que diferencia a un agente de una simple llamada a la API: la capacidad de razonar sobre qué pasos dar y ejecutarlos autónomamente. Ejecuta el script y observa cómo responde. Puedes añadir un print dentro del bucle para ver cada herramienta que invoca.
if __name__ == "__main__":
print(agente("Cuanto es 347 multiplicado por 89?"))
print("---")
print(agente("Que temperatura hace en Madrid y en Bogota? Dame la diferencia en grados."))
💡 Consejo: Añade print(f'Usando: {bloque.name} con {bloque.input}') dentro del bucle para depurar y ver el razonamiento paso a paso.
⚠️ Error común: Si el agente no usa las herramientas, revisa que las descripciones sean claras y que estés pasando el parámetro 'tools' en cada llamada.
Paso 6: Añadir un system prompt y memoria de conversación
Para convertir esto en un agente conversacional útil, añadimos un system prompt que define su personalidad y reglas, y persistimos el historial entre preguntas. El system prompt es donde estableces el comportamiento: idioma, tono, límites. Aquí modificamos la función para aceptar y devolver el historial, permitiendo conversaciones de varios turnos donde el agente recuerda lo anterior. Esto es esencial para asistentes reales. Con esta base ya puedes añadir herramientas más potentes: buscar en tu base de datos, llamar APIs internas, leer archivos, o integrar con tus sistemas. El patrón siempre es el mismo: define la herramienta, implementa la función, y el bucle agéntico se encarga del resto.
SYSTEM = """Eres un asistente util que responde en espanol.
Usa las herramientas disponibles cuando sea necesario.
Se conciso y claro en tus respuestas."""
def agente_conversacional(pregunta, historial=None, max_iteraciones=5):
mensajes = historial or []
mensajes.append({"role": "user", "content": pregunta})
for _ in range(max_iteraciones):
respuesta = client.messages.create(
model=MODELO,
max_tokens=1024,
system=SYSTEM,
tools=tools,
messages=mensajes
)
mensajes.append({"role": "assistant", "content": respuesta.content})
if respuesta.stop_reason != "tool_use":
texto = next((b.text for b in respuesta.content if b.type == "text"), "")
return texto, mensajes
resultados = [{"type": "tool_result", "tool_use_id": b.id,
"content": ejecutar_herramienta(b.name, b.input)}
for b in respuesta.content if b.type == "tool_use"]
mensajes.append({"role": "user", "content": resultados})
return "Limite de iteraciones alcanzado", mensajes
# Uso con memoria:
resp, hist = agente_conversacional("Que hora es en Madrid tipicamente al mediodia?")
print(resp)
resp2, hist = agente_conversacional("Y cuanto es eso mas 3 horas?", hist)
print(resp2)
💡 Consejo: Guarda el 'historial' en Redis o una base de datos indexado por usuario para agentes multiusuario en producción.
⚠️ Error común: El historial crece con cada turno y consume tokens. Implementa un límite o resumen automático cuando supere cierto tamaño para controlar costes.
Resultado
Has construido un agente funcional en Claude que decide autónomamente qué herramientas usar, las ejecuta mediante tu código, encadena varios pasos y mantiene memoria de conversación. Entiendes el bucle agéntico completo (tool_use → ejecución → tool_result → respuesta) y cómo manejar errores y límites de iteración. Con este patrón puedes ampliar el agente con cualquier herramienta real: consultas a bases de datos, APIs externas, lectura de archivos o integraciones con tus sistemas internos.



