Cómo crear un agente de IA con Claude desde cero (tutorial 2026)
Este tutorial muestra cómo construir un agente real con Claude Agent SDK — el framework que Anthropic lanzó (renombrado en septiembre 2025 desde "Claude Code SDK") para que cualquiera construya agentes con la misma arquitectura que Claude Code internamente.
No es un "hello world". Vamos a construir un agente útil: un investigador que dado un nombre de empresa busca su web, redes y noticias, y devuelve un brief comercial. Todo el código en Python, ejecutable hoy. Forma parte de la guía de agentes de IA.
Requisitos previos
Python 3.10+ (recomendado 3.11 o 3.12).
API key de Anthropic desde platform.claude.com — coste estimado del tutorial: 0,30-0,80 €.
Node.js 18+ instalado (el SDK lo requiere internamente).
30 minutos.
Paso 1: instalación
pip install claude-agent-sdkSi prefieres TypeScript:
npm install @anthropic-ai/claude-agent-sdkConfigura la key:
export ANTHROPIC_API_KEY="sk-ant-..."Verifica:
from claude_agent_sdk import query
import asyncio
async def main():
async for msg in query(prompt="Di hola en una palabra"):
print(msg)
asyncio.run(main())Si imprime mensajes con "Hola" sin errores, estás listo.
Paso 2: anatomía del agente que vamos a construir
El agente recibe nombre_empresa y debe:
Buscar la web de la empresa.
Visitar la web y extraer descripción y propuesta de valor.
Buscar perfiles en LinkedIn / X.
Buscar noticias recientes.
Redactar un brief en Markdown con: descripción, sector, tamaño aproximado, propuesta de valor, pulse de redes, noticias relevantes.
El agente decide solo el orden y cuándo dar por buena la información. No le decimos en qué orden hacer las cosas — eso es lo que lo hace agente.
Paso 3: definir los tools
from claude_agent_sdk import tool, create_sdk_mcp_server
import requests
from bs4 import BeautifulSoup
@tool("web_search", "Busca en Google y devuelve los 5 primeros resultados", {"query": str})
async def web_search(args):
# En producción usarías SerpAPI, Brave Search API o similar.
# Ejemplo simplificado:
response = requests.get(
"https://api.search.brave.com/res/v1/web/search",
headers={"X-Subscription-Token": BRAVE_API_KEY},
params={"q": args["query"], "count": 5}
)
results = response.json().get("web", {}).get("results", [])
return {
"content": [{
"type": "text",
"text": "\n".join([f"- {r['title']}: {r['url']}" for r in results])
}]
}
@tool("fetch_url", "Descarga una URL y devuelve texto plano de la página", {"url": str})
async def fetch_url(args):
r = requests.get(args["url"], timeout=10, headers={"User-Agent": "Mozilla/5.0"})
soup = BeautifulSoup(r.text, "html.parser")
for tag in soup(["script", "style", "nav", "footer"]):
tag.decompose()
text = " ".join(soup.get_text().split())[:5000]
return {"content": [{"type": "text", "text": text}]}
@tool("save_brief", "Guarda el brief final en Markdown", {"company": str, "content": str})
async def save_brief(args):
filename = f"briefs/{args['company'].replace(' ', '_').lower()}.md"
with open(filename, "w") as f:
f.write(args["content"])
return {"content": [{"type": "text", "text": f"Guardado en {filename}"}]}
server = create_sdk_mcp_server(
name="company-research",
version="1.0.0",
tools=[web_search, fetch_url, save_brief]
)Tres tools, tres responsabilidades claras: buscar, leer, guardar. Esto es selección del context engineering: cada tool devuelve solo lo necesario.
Paso 4: configurar el agente
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
options = ClaudeAgentOptions(
mcp_servers={"research": server},
allowed_tools=[
"mcp__research__web_search",
"mcp__research__fetch_url",
"mcp__research__save_brief"
],
system_prompt="""Eres un agente de investigación comercial.
Dada una empresa, devuelves un brief estructurado en Markdown.
Usa web_search para encontrar URLs, fetch_url para leerlas,
y save_brief al final cuando tengas información suficiente.
Sé eficiente: máximo 8 búsquedas y 6 fetches por empresa.""",
model="claude-opus-4-7",
permission_mode="acceptEdits"
)Detalles importantes:
allowed_tools: lista explícita. El agente NO puede llamar a tools no listadas, evita sorpresas.permission_mode="acceptEdits": el agente ejecuta sin pedir permiso por cada acción. En producción real con tools peligrosas, usadefaulty aprueba manualmente.system_prompt: guía pero no microgestiona. Le decimos qué hacer y cuáles son los límites, no en qué orden.
Paso 5: el loop agéntico
async def research_company(name: str):
async with ClaudeSDKClient(options=options) as client:
await client.query(f"Investiga la empresa: {name}")
async for message in client.receive_response():
# Cada `message` es un turno del loop:
# - tool_use: el LLM decidió llamar a una tool
# - tool_result: resultado que vuelve al LLM
# - text: pensamiento o respuesta intermedia
print(message)
return await client.get_final_response()
if __name__ == "__main__":
import asyncio
brief = asyncio.run(research_company("Anthropic"))
print("\n\n=== BRIEF FINAL ===\n")
print(brief)Cuando ejecutes esto verás algo como:
[turn 1] tool_use: web_search(query="Anthropic empresa AI")
[turn 1] tool_result: - Anthropic - https://anthropic.com ...
[turn 2] tool_use: fetch_url(url="https://anthropic.com")
[turn 2] tool_result: We're an AI safety company building reliable...
[turn 3] tool_use: web_search(query="Anthropic LinkedIn")
...
[turn 8] tool_use: save_brief(company="Anthropic", content="# Anthropic\n...")
[turn 8] tool_result: Guardado en briefs/anthropic.mdEl agente decidió: 8 turnos, 4 búsquedas, 3 fetches, 1 guardado. Tú no le diste el orden.
Paso 6: añadir límites duros
Un agente sin límites puede entrar en bucle o consumir cientos de euros. Añade siempre topes:
options = ClaudeAgentOptions(
# ... resto igual
max_turns=15, # nunca más de 15 turnos
max_thinking_tokens=10_000, # tope de razonamiento por turno
)Para producción serio considera además:
Timeout por ejecución (ej: 5 min hard kill).
Coste máximo por tarea (lleva contador de tokens y aborta).
Rate limiting propio para no exceder límites de Anthropic.
Paso 7: añadir hooks de seguridad
Los hooks de Claude Agent SDK son la forma de validar antes/después de cada tool:
from claude_agent_sdk import HookMatcher
async def block_dangerous_urls(input_data, tool_use_id, context):
if input_data["tool_name"] == "mcp__research__fetch_url":
url = input_data["tool_input"]["url"]
if "internal.empresa.com" in url or "localhost" in url:
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "URL no permitida"
}
}
return {}
options = ClaudeAgentOptions(
# ...
hooks={
"PreToolUse": [
HookMatcher(matcher="mcp__research__fetch_url", hooks=[block_dangerous_urls])
]
}
)Ahora el agente no puede pedir URLs internas aunque "se le ocurra".
Paso 8: optimización de coste
Sin optimizar, este agente cuesta ~0,30-0,80 € por empresa. Con tres mejoras llega a ~0,06 €:
1. Usa Sonnet 4.6 en vez de Opus 4.7
Para investigación documental, Sonnet 4.6 da resultados equivalentes a 1/5 del coste:
options = ClaudeAgentOptions(model="claude-sonnet-4-6", ...)2. Activa prompt caching
Tu system prompt no cambia entre empresas. Cachéalo (90% off en cache hits):
options = ClaudeAgentOptions(
system_prompt={"type": "preset", "preset": "claude_code"},
setting_sources=["project"], # usa CLAUDE.md como caché
model="claude-sonnet-4-6"
)3. Comprime los resultados de fetch_url
Hoy devolvemos 5.000 caracteres de la página. Para muchas webs, 1.500 bastan:
text = " ".join(soup.get_text().split())[:1500] # en vez de 5000Más patrones en enrutamiento inteligente para ahorrar API.
Paso 9: deploy básico
Tres opciones por orden de complejidad:
Cron simple
# crontab -e
0 8 * * 1 /usr/bin/python /opt/agent/research.py >> /var/log/agent.log 2>&1Ejecuta el agente todos los lunes a las 8:00 con la lista de empresas del fin de semana.
Cola con Celery / RQ
Para volumen mayor, encola tareas:
from celery import Celery
app = Celery("agent", broker="redis://localhost")
@app.task
def research_task(company_name):
return asyncio.run(research_company(company_name))Servicio web
Expón el agente como API:
from fastapi import FastAPI
app = FastAPI()
@app.post("/research")
async def research(body: dict):
brief = await research_company(body["company"])
return {"brief": brief}Llama desde tu CRM o desde un workflow de n8n / Make.
Paso 10: monitoring mínimo
Logs estructurados desde el día 1:
import logging
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)
async for message in client.receive_response():
if message.type == "tool_use":
logging.info(f"TOOL company={name} tool={message.tool_name} input={message.input}")
elif message.type == "tool_result":
logging.info(f"RESULT company={name} tool_id={message.tool_use_id} chars={len(str(message.content))}")Métricas que querrás trackear:
Turnos por ejecución (mediana y p95).
Tokens consumidos.
Tasa de éxito (¿generó brief? ¿completo?).
Tools más / menos usadas.
Errores típicos al empezar
"El agente no llama a las tools"
Causa habitual: el system_prompt describe la tarea pero no menciona que use las tools. Solución: lista las tools en el prompt: "Usa web_search para…".
"El agente llama 50 veces a la misma tool"
Causa: no hay max_turns y el modelo se queda probando variaciones. Pon siempre límite duro.
"El brief sale muy genérico"
Causa: las tools devuelven demasiado o demasiado poco. Si fetch_url devuelve 50 caracteres, el modelo no tiene material; si devuelve 50.000, se diluye. Empieza por 1.500-3.000 chars y ajusta.
"Funciona local pero no en producción"
Causa habitual: variables de entorno, paths o permisos. Empaqueta en Docker desde el principio.
Cuándo NO usar agentes
Tutorial honesto: no todo necesita un agente. Si tu caso es:
Una clasificación de texto → usa un prompt simple.
Una traducción → un prompt.
Una respuesta a FAQ → un chatbot, no un agente.
Un workflow rígido (paso 1 → paso 2 → paso 3 sin decisiones) → un script con llamadas a LLM.
Agente cuando: hay decisiones intermedias, número variable de pasos, y/o uso de tools externas.
Conclusión
Construir un agente con Claude Agent SDK es 50% definir buenos tools, 30% cuidar el contexto que reciben, y 20% configurar límites y monitoring. El SDK abstrae el loop agéntico — pero las decisiones de qué herramientas exponer y qué información circulan siguen siendo tuyas. Eso es context engineering.
El código de este tutorial sirve de plantilla para muchos casos: research comercial, análisis competitivo, monitoring de menciones, classification + enrichment de datos.
Para profundizar:
Agentes de IA en 2026: guía completa — pillar del cluster.
Diferencia entre agente y chatbot — cuándo elegir cuál.
Hooks en Claude Code — patrones de seguridad y validación.
Subagentes y delegación — patrón multi-agente.
Fuentes verificadas
Claude Agent SDK Overview — docs oficiales Anthropic.
Claude Agent SDK Python — repo oficial.
Building agents with the Claude Agent SDK — guía de Anthropic.
Datos verificados el 27 de abril de 2026.



