Un agente de IA es, en el fondo, un bucle: el modelo decide si necesita una herramienta, tu código la ejecuta, le devuelves el resultado y el modelo continúa hasta tener una respuesta. No hace falta ningún framework para construirlo. En esta guía lo hacemos en unas 80 líneas de Python con la API de Anthropic: un agente que lista y lee archivos de una carpeta para responder preguntas sobre ellos.
Cómo se hizo esta guía: el código se escribió con ayuda de IA y se ejecutó con un cliente simulado que reproduce el flujo de herramientas (listar, leer, responder), sin llamar a la API real. El formato de los mensajes se contrastó con la documentación oficial. La primera llamada real la harás tú con tu clave; si algo falla, dínoslo en los comentarios. Más sobre nuestro método en cómo usamos la IA.
Qué es un agente (y qué no)
Un modelo de lenguaje por sí solo solo produce texto. Un agente le añade dos cosas: herramientas (funciones que tu programa puede ejecutar) y un bucle que repite la conversación hasta que el modelo no pide más herramientas. El modelo nunca ejecuta nada: solo pide, y tu código decide si lo hace.
Requisitos
- Python 3.9 o superior.
- Una clave de API de Anthropic guardada en la variable de entorno
ANTHROPIC_API_KEY. - El paquete oficial:
pip install anthropic. - Una carpeta
notas/con algunos archivos de texto para probar.
Paso 1: definir las herramientas
Cada herramienta tiene dos partes: una descripción para el modelo (nombre, explicación y esquema JSON de sus parámetros) y una función real en Python. Aquí damos al agente dos: listar los archivos y leer uno.
from pathlib import Path
import anthropic
MODELO = "claude-sonnet-5-5"
MAX_PASOS = 8 # límite de seguridad: el bucle no puede girar para siempre
SISTEMA = (
"Eres un asistente que trabaja con archivos de una carpeta. "
"Usa las herramientas para leer antes de responder y no inventes contenido."
)
HERRAMIENTAS = [
{
"name": "listar_archivos",
"description": "Lista los archivos de la carpeta de trabajo.",
"input_schema": {"type": "object", "properties": {}},
},
{
"name": "leer_archivo",
"description": "Devuelve el texto de un archivo de la carpeta de trabajo.",
"input_schema": {
"type": "object",
"properties": {"nombre": {"type": "string", "description": "Nombre del archivo"}},
"required": ["nombre"],
},
},
]
Paso 2: las funciones que se ejecutan de verdad
Aquí está la parte que importa para la seguridad. El modelo puede pedir cualquier nombre de archivo, así que la función de lectura comprueba que la ruta final sigue dentro de la carpeta permitida y limita el tamaño devuelto.
CARPETA = Path("notas").resolve()
def listar_archivos():
return "\n".join(sorted(p.name for p in CARPETA.iterdir() if p.is_file()))
def leer_archivo(nombre):
ruta = (CARPETA / nombre).resolve()
if CARPETA not in ruta.parents: # evita salir de la carpeta (../)
return "Error: ruta no permitida"
if not ruta.is_file():
return "Error: el archivo no existe"
return ruta.read_text(encoding="utf-8")[:5000]
FUNCIONES = {"listar_archivos": listar_archivos, "leer_archivo": leer_archivo}
def ejecutar(nombre, entrada):
try:
return str(FUNCIONES[nombre](**entrada))
except Exception as e: # el error vuelve al modelo para que se corrija
return f"Error: {e}"
Fíjate en que los errores no detienen el programa: se devuelven como texto al modelo, que normalmente corrige su siguiente petición (por ejemplo, pidiendo otro nombre de archivo).
Paso 3: el bucle del agente
Este es el corazón. En cada vuelta enviamos la conversación al modelo. Si responde con stop_reason igual a "tool_use", ejecutamos cada herramienta pedida y devolvemos los resultados en un mensaje de usuario con bloques tool_result, cada uno con el tool_use_id de la petición que responde. Si no pide herramientas, esa es la respuesta final.
def agente(pregunta, cliente=None):
cliente = cliente or anthropic.Anthropic() # lee ANTHROPIC_API_KEY del entorno
mensajes = [{"role": "user", "content": pregunta}]
for _ in range(MAX_PASOS):
r = cliente.messages.create(
model=MODELO, max_tokens=1024, system=SISTEMA,
tools=HERRAMIENTAS, messages=mensajes,
)
mensajes.append({"role": "assistant", "content": r.content})
if r.stop_reason != "tool_use": # el modelo ya tiene respuesta final
return "".join(b.text for b in r.content if b.type == "text")
resultados = []
for bloque in r.content:
if bloque.type == "tool_use":
salida = ejecutar(bloque.name, bloque.input)
resultados.append(
{"type": "tool_result", "tool_use_id": bloque.id, "content": salida}
)
mensajes.append({"role": "user", "content": resultados})
return "Me detuve: se alcanzó el límite de pasos."
if __name__ == "__main__":
print(agente(input("Pregunta: ")))
Cómo ejecutarlo
pip install anthropic
export ANTHROPIC_API_KEY="tu-clave" # en Windows (PowerShell): $env:ANTHROPIC_API_KEY="tu-clave"
mkdir notas
echo "Reunión lunes: revisar presupuesto." > notas/reunion.txt
echo "Comprar café y papel." > notas/compras.txt
python agente.py
Prueba con una pregunta como «¿qué tengo que comprar?». Lo esperable es que el agente liste los archivos, lea el que corresponde y responda con su contenido. Si ejecutas la prueba con datos tuyos, ten en cuenta que el texto de esos archivos viaja a la API.
Cómo probar el bucle sin gastar dinero
Como agente() recibe el cliente como parámetro, puedes pasarle un objeto falso que simule las respuestas del modelo. Así compruebas que el bucle encadena listar, leer y responder sin hacer ninguna llamada de pago. Es el método que usamos para verificar el código de esta guía.
from types import SimpleNamespace as NS
import agente as m
class Falso:
def __init__(self):
self.paso = 0
self.messages = self
def create(self, **kw):
self.paso += 1
if self.paso == 1:
return NS(stop_reason="tool_use",
content=[NS(type="tool_use", id="t1", name="listar_archivos", input={})])
if self.paso == 2:
return NS(stop_reason="tool_use",
content=[NS(type="tool_use", id="t2", name="leer_archivo",
input={"nombre": "compras.txt"})])
return NS(stop_reason="end_turn",
content=[NS(type="text", text="Debes comprar café y papel.")])
print(m.agente("¿Qué debo comprar?", cliente=Falso()))
Seguridad: lo que hay que cuidar
- Limita el alcance de cada herramienta. Una herramienta que lee archivos debe quedarse en una carpeta concreta, como hace
leer_archivo. - No des herramientas que ejecuten comandos arbitrarios mientras estés aprendiendo. Si más adelante lo necesitas, pide confirmación al usuario antes de ejecutarlos.
- Mantén el límite de pasos.
MAX_PASOSevita bucles infinitos que consumen saldo. - Desconfía del contenido de los archivos. Un texto leído puede contener instrucciones dirigidas al modelo. Trata lo que leen las herramientas como datos, no como órdenes.
Siguientes mejoras
- Marcar los fallos con
"is_error": Trueen eltool_result, en lugar de devolver solo texto de error. - Registrar cada llamada a una herramienta para poder depurar.
- Añadir una herramienta de escritura que pida confirmación antes de modificar nada.
- Medir cuántos tokens consume cada pregunta y cuánto cuesta.
Preguntas frecuentes
¿Necesito LangChain u otro framework?
No para empezar. Un framework ahorra trabajo cuando tienes muchas herramientas o flujos complejos, pero entender el bucle de 20 líneas te permite depurar cualquier agente después.
¿Cuánto cuesta ejecutarlo?
Depende del modelo y de la longitud de los archivos que lea. Cada vuelta del bucle reenvía la conversación completa, así que cuantas más herramientas use, más tokens consume. Consulta los precios vigentes en la página oficial de Anthropic antes de usarlo con mucho contenido.
¿Sirve con otros modelos?
El patrón (herramientas, bucle, resultados) es el mismo en la mayoría de proveedores, pero los nombres de los campos cambian. El código de esta guía es para la API de Anthropic.
Siguientes pasos
Si aún estás aprendiendo Python, repasa antes las funciones en Python y el control de flujo: el agente usa ambos a fondo.
Fuente: documentación oficial de Anthropic sobre definición de herramientas (platform.claude.com).
