Come creare un agente AI in Python: Claude Agent SDK passo passo
Come creare un agente AI che legge e corregge il codice da solo: installiamo Claude Agent SDK, scriviamo il primo agente e aggiungiamo uno strumento e MCP.
Questo articolo in altre lingue:العربيةDeutschEnglishEspañolFrançais日本語Polski简体中文
In questo articolo
- Che cos’è un agente AI dal punto di vista di uno sviluppatore?
- Passo 1: come installare Claude Agent SDK?
- Passo 2: come impostare la chiave API?
- Passo 3: come scrivere il primo agente AI?
- Passo 4: come decidere cosa può fare l’agente da solo?
- Passo 5: come dare all’agente uno strumento personalizzato?
- Passo 6: come collegare l’agente a un server MCP?
- Cosa fare dopo il primo agente?
- A cosa fare attenzione?
- FAQ
Come creare un agente AI senza scrivere tutto il loop da zero? La strada più breve che conosco è Claude Agent SDK, una libreria per Python e TypeScript che ci dà gli stessi strumenti, lo stesso loop dell’agente e la stessa gestione del contesto su cui gira Claude Code. Scriviamo una dozzina di righe di codice e l’agente legge i file, corregge gli errori ed esegue i comandi da solo.
In questa guida costruiamo un primo agente in Python, gli impostiamo i permessi e aggiungiamo uno strumento personalizzato e un server MCP. Tutti i comandi e i frammenti di codice vengono dal quickstart ufficiale di Agent SDK e dalle pagine di documentazione che linko strada facendo.
Che cos’è un agente AI dal punto di vista di uno sviluppatore?
Secondo la documentazione, un agente è un’applicazione che porta a termine un compito pianificando da sola i passi successivi e chiamando strumenti: legge file, esegue comandi o modifica codice. Il quadro generale, senza codice, l’ho descritto in Cos’è un agente AI.
Per uno sviluppatore la cosa più importante è il loop: il modello ragiona, chiama uno strumento, guarda il risultato e decide cosa fare dopo. Agent SDK si occupa del loop, dell’esecuzione degli strumenti, della gestione del contesto e dei nuovi tentativi, e noi ci limitiamo a leggere il flusso dei messaggi.
La documentazione distingue quattro modi di lavorare con Claude:
- Agent SDK, una libreria che esegue l’agente di Claude Code nella nostra applicazione (in un processo che gestiamo noi),
- Claude Code CLI, il terminale per il lavoro quotidiano e interattivo (ne parlo nella guida a Claude Code),
- Client SDK, l’accesso diretto all’API di Claude, dove il loop degli strumenti lo scriviamo noi (oppure lo affidiamo al «tool runner», in beta),
- Managed Agents, un agente ospitato da Anthropic e configurato tramite API.
Questa guida riguarda la prima opzione.
Passo 1: come installare Claude Agent SDK?
Servono Python 3.10+ (oppure Node.js 18.14+ per TypeScript) e un account su Claude Console. Per prima cosa creiamo la cartella del progetto:
mkdir my-agent
cd my-agent
Di default l’agente ha accesso ai file di questa cartella e delle sue sottocartelle. La documentazione consiglia il gestore di pacchetti uv:
uv init
uv add claude-agent-sdk
Con il normale pip (macOS e Linux) è così:
python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk
Curiosità: di solito non serve installare Claude Code a parte, perché entrambi gli SDK includono un binario nativo di Claude Code. L’eccezione sono le piattaforme senza pacchetto precompilato, come Windows ARM64 (in quel caso installiamo Claude Code a mano e l’SDK lo trova nel PATH).
Passo 2: come impostare la chiave API?
Creiamo la chiave in Claude Console e la impostiamo come variabile d’ambiente nel terminale in cui lanceremo l’agente:
export ANTHROPIC_API_KEY=your-api-key
L’SDK non carica automaticamente i file .env, quindi se teniamo la chiave in .env dobbiamo caricarla noi (ad esempio con il pacchetto dotenv). Al posto di una chiave Anthropic si possono usare anche Amazon Bedrock, Claude Platform on AWS, Google Cloud o Microsoft Foundry, impostando la variabile giusta, ad es. CLAUDE_CODE_USE_BEDROCK=1.
Un’avvertenza dalla documentazione: senza approvazione preventiva, Anthropic non consente ai prodotti costruiti con Agent SDK di offrire il login con un account claude.ai né di usare i limiti di un abbonamento. L’agente funziona con una chiave API e paghiamo i token (come tenerli sotto controllo l’ho spiegato parlando dei costi dell’API di Claude).
Passo 3: come scrivere il primo agente AI?
L’esempio del quickstart è un agente che trova e corregge errori nel codice. Prima creiamo un file utils.py con due errori voluti:
def calculate_average(numbers):
total = 0
for num in numbers:
total += num
return total / len(numbers)
def get_user_name(user):
return user["name"].upper()
calculate_average([]) andrà in crash con una divisione per zero e get_user_name(None) con un TypeError. Ora l’agente vero e proprio, il file agent.py:
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage
async def main():
# Agentic loop: streams messages as Claude works
async for message in query(
prompt="Review utils.py for bugs that would cause crashes. Fix any issues you find.",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob"], # Auto-approve these tools
permission_mode="acceptEdits", # Auto-approve file edits
),
):
# Print human-readable output
if isinstance(message, AssistantMessage):
for block in message.content:
if hasattr(block, "text"):
print(block.text) # Claude's reasoning
elif hasattr(block, "name"):
print(f"Tool: {block.name}") # Tool being called
elif isinstance(message, ResultMessage):
print(f"Done: {message.subtype}") # Final result
asyncio.run(main())
Il codice ha tre parti:
querycrea il loop dell’agente e restituisce un iteratore asincrono, quindi leggiamo i messaggi man mano conasync for,promptè il compito. Claude decide da solo quali strumenti usare,optionsè la configurazione: quiallowed_toolsapprova in anticipoRead,EditeGlob, epermission_mode="acceptEdits"accetta automaticamente le modifiche ai file.
Lo lanciamo:
uv run agent.py
(oppure python agent.py con il venv attivo). L’agente stampa il suo ragionamento e ogni strumento che chiama, e alla fine Done: success. Dopo, utils.py gestisce la lista vuota e l’utente mancante. L’agente ha letto il file, trovato i casi limite e corretto il codice da solo, e noi non abbiamo scritto nemmeno una funzione per gestire gli strumenti.
Se compare l’errore Not logged in o Invalid API key, quasi certamente la variabile ANTHROPIC_API_KEY non è impostata in quel terminale.
Passo 4: come decidere cosa può fare l’agente da solo?
Quello che l’agente sa fare dipende dagli strumenti. La documentazione indica tre set tipici:
Read,Glob,Grep: solo analisi, nessuna modifica ai file,Read,Edit,Glob: analisi e modifica del codice,Read,Edit,Bash,Glob,Grep: automazione completa con comandi nel terminale.
Perché l’agente possa, ad esempio, cercare sul web o lanciare i test, basta aggiungere uno strumento e cambiare il prompt:
options = ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob", "Bash"], permission_mode="acceptEdits"
)
Con Bash attivo, il quickstart propone il compito «Write unit tests for utils.py, run them, and fix any failures». Si può anche dare un ruolo all’agente tramite system_prompt, ad esempio quello di uno sviluppatore Python esperto che rispetta PEP 8.
La seconda leva è la modalità dei permessi (permission_mode):
"default"passa ogni chiamata non approvata alla nostra funzionecanUseToole, se non c’è, la rifiuta,"acceptEdits"accetta le modifiche ai file e i comandi semplici comemkdiromvnella cartella di lavoro,"plan"fa sì che l’agente prima esamini il codice e prepari un piano,"dontAsk"non chiede mai: funziona solo ciò che abbiamo approvato in anticipo, il resto viene rifiutato (utile per agenti senza una persona alla tastiera),"auto"fa passare le azioni attraverso un classificatore che le esamina una per una e le blocca o le consente,"bypassPermissions"esegue tutto senza chiedere e, secondo la documentazione, va bene solo in ambienti isolati come la CI o i container.
La regola d’oro della documentazione: "acceptEdits" sul computer di sviluppo, "bypassPermissions" solo dove l’agente non può danneggiare nulla di importante. Perché conta lo spiego nella guida sulla sicurezza degli agenti AI.
Conviene anche impostare subito dei limiti: max_turns limita il numero di turni e max_budget_usd ferma il loop quando il costo stimato viene superato. Senza, l’agente va avanti finché non decide di aver finito, e con compiti aperti come «migliora questo progetto» può volerci un po’ ;)
Passo 5: come dare all’agente uno strumento personalizzato?
Gli strumenti integrati sono solo l’inizio. Uno strumento personalizzato si definisce con il decoratore @tool (nome, descrizione, schema di input e funzione) e poi si impacchetta in un server MCP che gira nello stesso processo. L’esempio della documentazione recupera la temperatura dall’API di Open-Meteo, quindi prima aggiungiamo la libreria httpx:
uv add httpx
La definizione dello strumento:
from typing import Annotated, Any
import httpx
from claude_agent_sdk import tool, create_sdk_mcp_server
# Define a tool: name, description, input schema, handler
@tool(
"get_temperature",
"Get the current temperature at a location",
{
"latitude": Annotated[float, "Latitude coordinate"],
"longitude": Annotated[float, "Longitude coordinate"],
},
)
async def get_temperature(args: dict[str, Any]) -> dict[str, Any]:
async with httpx.AsyncClient() as client:
response = await client.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": args["latitude"],
"longitude": args["longitude"],
"current": "temperature_2m",
"temperature_unit": "fahrenheit",
},
)
data = response.json()
# Return a content array - Claude sees this as the tool result
return {
"content": [
{
"type": "text",
"text": f"Temperature: {data['current']['temperature_2m']}°F",
}
]
}
# Wrap the tool in an in-process MCP server
weather_server = create_sdk_mcp_server(
name="weather",
version="1.0.0",
tools=[get_temperature],
)
Nello stesso file passiamo il server all’agente. Il nome completo dello strumento ha il formato mcp__{server}__{strumento} ed è quello che inseriamo in allowed_tools, così funziona senza chiedere il permesso:
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
options = ClaudeAgentOptions(
mcp_servers={"weather": weather_server},
allowed_tools=["mcp__weather__get_temperature"],
)
async for message in query(
prompt="What's the temperature in San Francisco?",
options=options,
):
# ResultMessage is the final message after all tool calls complete
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
L’agente chiama il nostro strumento e risponde in base al risultato. Al posto del meteo possiamo collegare qualsiasi cosa: il nostro database, un’API interna o uno script.
Passo 6: come collegare l’agente a un server MCP?
I server MCP già pronti (del protocollo in sé ho parlato in Cos’è MCP) si collegano nell’opzione mcp_servers. L’esempio della documentazione collega l’agente via HTTP al server remoto della documentazione di Claude Code:
options = ClaudeAgentOptions(
mcp_servers={
"claude-code-docs": {
"type": "http",
"url": "https://code.claude.com/docs/mcp",
}
},
allowed_tools=["mcp__claude-code-docs__*"],
)
L’asterisco in allowed_tools approva tutti gli strumenti di quel server. Con il prompt «Use the docs MCP server to explain what hooks are in Claude Code» l’agente cerca nella documentazione e restituisce una risposta.
Cosa fare dopo il primo agente?
Agent SDK ha anche il resto delle funzioni di Claude Code:
- hook: codice nostro eseguito prima o dopo la chiamata di uno strumento,
- subagenti per sottocompiti separati,
- sessioni: l’identificativo di
ResultMessage.session_idpermette di riprendere una conversazione con tutto il contesto o di ramificarla, - skill, comandi e memoria caricati dalla cartella
.claude/, come in Claude Code.
Applicazioni di esempio già pronte (tra cui un assistente per la posta e un agente di ricerca) si trovano nel repository claude-agent-sdk-demos.
A cosa fare attenzione?
Primo, ai costi. Il contesto cresce a ogni turno, e un file grande o un comando molto prolisso possono consumare migliaia di token in un colpo. Le parti fisse (prompt di sistema, definizioni degli strumenti) vengono messe in cache automaticamente, ma max_budget_usd conviene impostarlo comunque dal primo giorno (il tetto può essere superato del costo di una risposta, quindi lasciamo un margine).
Secondo, ai permessi. Un agente con Bash e bypassPermissions su un normale portatile è andarsela a cercare, quindi lasciamo l’automazione completa ai container.
Terzo, al branding. Secondo la documentazione, un prodotto costruito con l’SDK può chiamarsi ad esempio «Claude Agent» o «
FAQ
Come creare un agente AI senza scrivere il loop da zero?
Il modo più semplice è usare una libreria che ha già quel loop, come Claude Agent SDK. Installiamo claude-agent-sdk, impostiamo ANTHROPIC_API_KEY e chiamiamo query() con un prompt e un elenco di strumenti consentiti. Il resto, cioè le chiamate agli strumenti e la gestione del contesto, lo fa l’SDK.
Claude Agent SDK è gratuito?
La libreria in sé non costa nulla, ma l’agente usa l’API di Claude, quindi paghiamo i token consumati. Senza l’approvazione di Anthropic non si può collegare a un abbonamento claude.ai.
In cosa Agent SDK è diverso dall’API di Claude?
Con il normale Client SDK (API di Claude) il loop degli strumenti lo scriviamo noi oppure usiamo il tool runner in beta. Agent SDK include gli strumenti integrati di Claude Code (lettura e modifica dei file, comandi, ricerca web), i permessi, le sessioni e gli hook.
Si può scrivere l’agente in TypeScript?
Sì. Il pacchetto è @anthropic-ai/claude-agent-sdk, richiede Node.js 18.14+ e negli esempi della documentazione le opzioni si chiamano come in Python, solo in camelCase (allowedTools, permissionMode).