SISTEMA DI ACCESSO COMPUTER

Pattern di Utilizzo degli Strumenti: Interfacce Agente-Strumento Affidabili


Il tuo agente ha chiamato uno strumento e ha ricevuto un blob JSON di 40 righe — risposta API grezza, oggetti annidati, codici di errore sepolti dentro un campo status. Il modello lo ha letto, ha scelto un valore che sembrava plausibile e ha continuato. Il valore era sbagliato. Tre passi dopo, l’agente ha scritto con fiducia un rapporto basato su dati errati.

Lo strumento ha funzionato. L’interfaccia ha fallito.

L’utilizzo degli strumenti è il meccanismo che trasforma un modello linguistico in un agente. Ogni capacità del tuo agente — ricerca in database, scrittura di file, chiamate API, interrogazione di servizi — arriva attraverso un’interfaccia strumento. Se l’interfaccia è progettata male, il modello prende decisioni peggiori anche quando il servizio sottostante funziona correttamente. Questa guida copre cinque pattern per costruire interfacce strumento precise, affidabili e pronte per la produzione.

Prerequisiti: Familiarità con Python e l’API Claude. Per il contesto su MCP come livello di trasporto degli strumenti, vedi Costruire il tuo Primo Server MCP.


Perché il Design dell’Interfaccia è Importante

Quando un agente sceglie e usa uno strumento, prende due decisioni:

  1. Quale strumento chiamare — guidato dal name e dalla description dello strumento
  2. Quali argomenti passare — guidato dall’input_schema dello strumento

Le descrizioni ambigue portano a una selezione errata degli strumenti. Gli schemi troppo permissivi permettono al modello di passare input malformati. I risultati non strutturati lo costringono a indovinare cosa è successo. La maggior parte dei bug degli agenti non vive nel ragionamento — vive al confine dello strumento.


Pattern 1: Progettazione Schema-First

Scrivi il JSON schema prima di scrivere l’implementazione. Uno schema rigido vincola il comportamento del modello nella fase di input — prima che qualsiasi cosa venga eseguita.

import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "search_products",
"description": (
"Cerca nel catalogo prodotti per parola chiave. "
"Restituisce un elenco di prodotti corrispondenti con ID, nomi e prezzi. "
"Usalo quando l'utente vuole trovare o sfogliare prodotti."
),
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Parole chiave da cercare"
},
"category": {
"type": "string",
"enum": ["electronics", "clothing", "food", "home", "all"],
"description": "Categoria di prodotto da filtrare. Usa 'all' se non specificato."
},
"max_results": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"description": "Numero di risultati da restituire. Default: 5"
}
},
"required": ["query", "category"]
}
}
]
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "Trovami elettronica sotto i 100€"}]
)

Regole dello schema che riducono gli errori:

  • Usa enum per qualsiasi campo con un insieme fisso di valori validi
  • Imposta minimum/maximum sui campi numerici per evitare input fuori intervallo
  • Segna i campi come required solo quando lo strumento non può davvero funzionare senza di essi
  • Scrivi le descrizioni dalla prospettiva del modello: “Usalo quando…” dice al modello quando chiamare lo strumento

Quando usare: Per ogni definizione di strumento.


Pattern 2: Risultati degli Strumenti Strutturati

Restituisci risultati tipizzati e leggibili dalle macchine. Non restituire mai risposte API grezze.

import json
from dataclasses import dataclass, asdict
from typing import Any, Optional
@dataclass
class ToolResult:
success: bool
data: Optional[Any] = None
error: Optional[str] = None
def to_content(self) -> str:
return json.dumps(asdict(self))
def search_products(
query: str,
category: str,
max_results: int = 5,
) -> ToolResult:
try:
raw_results = _query_database(query, category, limit=max_results)
products = [
{"id": r["product_id"], "name": r["title"], "price": r["price_usd"]}
for r in raw_results
]
return ToolResult(success=True, data={"products": products, "count": len(products)})
except ConnectionError as e:
return ToolResult(success=False, error=f"Database non disponibile: {e}")
except Exception as e:
return ToolResult(success=False, error=f"Ricerca fallita: {type(e).__name__}: {e}")
def _query_database(query, category, limit):
return []

La busta consistente {success, data, error} significa che il modello sa sempre dove cercare. Per la gestione elegante dei guasti, vedi Pattern di Recupero Errori degli Agenti.


Pattern 3: Chiamate di Strumenti in Parallelo

Claude può richiedere più strumenti in un’unica risposta. Elaborali in parallelo invece che sequenzialmente.

from concurrent.futures import ThreadPoolExecutor, as_completed
TOOL_REGISTRY = {
"search_products": search_products,
}
def dispatch_tool(name: str, inputs: dict) -> ToolResult:
handler = TOOL_REGISTRY.get(name)
if not handler:
return ToolResult(success=False, error=f"Strumento sconosciuto: {name}")
return handler(**inputs)
def process_tool_calls(response: anthropic.types.Message) -> list[dict]:
tool_uses = [
block for block in response.content
if block.type == "tool_use"
]
if not tool_uses:
return []
def execute(tool_use):
result = dispatch_tool(tool_use.name, tool_use.input)
return {
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": result.to_content(),
}
with ThreadPoolExecutor(max_workers=len(tool_uses)) as executor:
futures = {executor.submit(execute, tu): tu for tu in tool_uses}
results = []
for future in as_completed(futures):
results.append(future.result())
return results
def run_agent(user_message: str) -> str:
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
tools=tools,
messages=messages,
)
if response.stop_reason == "end_turn":
for block in response.content:
if hasattr(block, "text"):
return block.text
return ""
if response.stop_reason == "tool_use":
tool_results = process_tool_calls(response)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
break
return ""

Per orchestrare più agenti che chiamano ciascuno degli strumenti, vedi Pattern Multi-Agente.


Pattern 4: Wrapper Sicuro per le Chiamate agli Strumenti

Non lasciare mai che le eccezioni degli strumenti raggiungano il ciclo dell’agente senza essere gestite.

import signal
def timeout_handler(signum, frame):
raise TimeoutError("Esecuzione dello strumento scaduta")
def safe_tool_call(
name: str,
inputs: dict,
timeout_seconds: int = 30,
) -> ToolResult:
"""
Esegue uno strumento con timeout, catturando tutte le eccezioni.
Restituisce sempre un ToolResult — non solleva mai eccezioni.
"""
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout_seconds)
try:
return dispatch_tool(name, inputs)
except TimeoutError:
return ToolResult(
success=False,
error=f"Lo strumento '{name}' ha superato il timeout di {timeout_seconds}s"
)
except Exception as e:
return ToolResult(
success=False,
error=f"Lo strumento '{name}' ha sollevato {type(e).__name__}: {e}"
)
finally:
signal.alarm(0)

Quando uno strumento restituisce success: false, il modello può decidere se riprovare, provare un’alternativa o segnalare il fallimento. Per strategie di retry più ampie, vedi Pattern di Recupero Errori.


Pattern 5: Validazione e Troncatura dei Risultati

Valida i risultati degli strumenti prima di restituirli al modello.

MAX_TOOL_RESULT_CHARS = 8000
def validate_result(result: ToolResult, expected_keys: list[str]) -> ToolResult:
if not result.success or not isinstance(result.data, dict):
return result
missing = [k for k in expected_keys if k not in result.data]
if missing:
return ToolResult(
success=False,
error=f"Risposta dello strumento manca dei campi attesi: {missing}"
)
return result
def truncate_result(result: ToolResult) -> ToolResult:
content = result.to_content()
if len(content) <= MAX_TOOL_RESULT_CHARS:
return result
truncated_data = {
"truncated": True,
"chars_omitted": len(content) - MAX_TOOL_RESULT_CHARS,
"content": content[:MAX_TOOL_RESULT_CHARS],
}
return ToolResult(
success=result.success,
data=truncated_data,
error="Risultato troncato — troppo grande per la finestra di contesto",
)
def safe_tool_call_validated(
name: str,
inputs: dict,
expected_keys: list[str] | None = None,
) -> ToolResult:
result = safe_tool_call(name, inputs)
if expected_keys:
result = validate_result(result, expected_keys)
result = truncate_result(result)
return result

Per osservare e fare debug di questi fallimenti in produzione, vedi Debug e Osservabilità.


Errori Comuni

Errore 1: Restituire Risposte API Grezze

Il modello riceve un oggetto annidato con 30 campi, la maggior parte irrilevanti. Sceglie quello sbagliato.

Soluzione: Forma la risposta prima di restituirla. Restituisci solo ciò di cui il modello ha bisogno per la sua prossima decisione.

Errore 2: Strumenti con Effetti Collaterali senza Conferma

Uno strumento che invia un’email, elimina un record o addebita un pagamento non dovrebbe eseguirsi silenziosamente.

Soluzione: Per azioni irreversibili, usa un pattern a due strumenti: plan_email restituisce un’anteprima, send_email invia effettivamente.

Errore 3: Responsabilità degli Strumenti Sovrapposte

Due strumenti che fanno cose simili costringono il modello a indovinare quale usare.

Soluzione: Ogni strumento deve avere uno scopo distinto e non sovrapposto.

Errore 4: Nessun Timeout sugli Strumenti Esterni

Una lenta chiamata API di terze parti blocca indefinitamente l’intero ciclo dell’agente.

Soluzione: Imposta sempre un timeout (Pattern 4).


Lista di Controllo per la Produzione

  • Ogni strumento ha una descrizione dalla prospettiva del modello (“Usalo quando…”)
  • Campi enum per tutti gli input a valore fisso
  • Ogni strumento restituisce {success, data, error} — mai risposte grezze
  • Chiamate agli strumenti avvolte in safe_tool_call
  • Esecuzione parallela per risposte multi-strumento
  • Timeout impostato su ogni strumento esterno
  • Troncatura dei risultati per payload di dimensione variabile
  • Validazione per strumenti che chiamano API esterne

Prossimi Passi

  1. Inizia con lo schema — scrivi il tuo input_schema prima del corpo della funzione
  2. Aggiungi il wrapper ToolResult a ogni strumento esistente
  3. Integra safe_tool_call per rafforzare il ciclo dell’agente
  4. Configura la validazione dei risultati per strumenti che chiamano API esterne

Guide correlate: