Vai al contenuto

I mattoni di CrewAI

12Gli strumenti: dare le mani agli agenti

Perché un agente senza strumenti inventa, come sceglie lo strumento giusto, gli strumenti pronti di crewai_tools, come crearne uno tuo con @tool o BaseTool e che cos'è MCP.

Tempo di lettura: 25 minuti

Nel capitolo 8 il primo crew ha scritto con grande sicurezza una guida del weekend ad Arezzo, ma dentro c'erano luoghi sbagliati o inventati, come una gita a Norcia «a circa 45 minuti». Non era solo colpa del modello piccolo: gli agenti non avevano nessun modo di controllare. Pensa a un cuoco bravissimo chiuso in una stanza senza dispensa e senza telefono: se gli chiedi che cosa c'è oggi al mercato, tira a indovinare. Gli Strumento (tool): Una funzione che l'agente può chiamare per agire fuori dal modello: cercare sul web, leggere un file, interrogare un database, inviare una email. glossario sono la dispensa e il telefono dell'agente. In questo capitolo vedi come funzionano, usi quelli pronti e ne costruisci uno tuo.

Perché un agente ha bisogno di strumenti#

Un LLM: Large Language Model, grande modello linguistico: un programma addestrato su enormi quantità di testo che, data una frase, prevede le parole che seguono. È il motore di ChatGPT, Claude, Gemini. glossario sa soltanto quello che ha letto durante l'addestramento e quello che trova nel Prompt: Il testo che dai in ingresso a un modello: domanda, istruzioni, esempi. La qualità della risposta dipende molto dalla chiarezza del prompt. glossario. Non naviga su internet, non apre i tuoi file, non usa una calcolatrice: produce il testo più plausibile. Quando l'informazione manca, il testo plausibile può essere falso, ed ecco l'Allucinazione: Quando un modello scrive con sicurezza una cosa falsa o inventata. Succede perché il modello genera testo plausibile, non verifica fatti. glossario.

Uno strumento è una Funzione: Un blocco di codice con un nome che fa un lavoro e restituisce un risultato. Si definisce con def e si chiama con le parentesi: somma(2, 3). glossario Python che CrewAI esegue per conto dell'agente: cerca sul web, legge una pagina, apre un file, fa un calcolo. Il risultato torna al modello come testo, e da quel momento l'agente ragiona su dati veri invece che su ricordi vaghi. La guida ufficiale di CrewAI per progettare i task lo dice chiaramente: se un task deve procurarsi dei dati e l'agente non ha strumenti, l'agente li inventa. (Che cos'è esattamente una funzione lo vedi più avanti, quando ne scrivi una.)

Come l'agente decide di usare uno strumento#

Quando dai a un Agent (in CrewAI): Il membro della squadra. Si definisce con tre testi obbligatori — role, goal, backstory — più modello e strumenti opzionali. glossario una lista di strumenti, CrewAI mostra al modello una specie di catalogo: per ogni strumento, il nome, una descrizione e i parametri che accetta. È il ciclo del capitolo 9: il modello decide se rispondere o chiedere uno strumento con certi valori, CrewAI esegue davvero la funzione e rimette il risultato nella conversazione, fino alla risposta finale o al limite di max_iter: Numero massimo di giri di ragionamento che un agente può fare su un task prima di dare la risposta migliore che ha. In CrewAI 1.15 il valore predefinito è 25. glossario.

Come un agente usa uno strumento L'agente legge il compito e il catalogo degli strumenti, dove vede solo nome, descrizione e parametri. Chiede a CrewAI di usare uno strumento con certi valori; CrewAI esegue la funzione Python, che lavora con il mondo esterno, e restituisce il risultato come testo all'agente. Il giro si ripete finché serve, poi l'agente scrive la risposta finale. Task description Agente role · goal · backstory l'LLM ragiona e decide: rispondo o uso uno strumento? Catalogo strumenti nome descrizione parametri l'agente vede solo questo CrewAI esegue la funzione Python Il mondo fuori Google, pagine web, file, calcoli Risposta finale il risultato del task 1 2 · chiede: usa questo, con questi valori 3 4 · il risultato torna come testo 5 · quando ha abbastanza, risponde i passi 2, 3 e 4 si ripetono finché serve, al massimo max_iter giri
Il ciclo dell'agente con gli strumenti. Guarda la freccia tratteggiata: del catalogo il modello conosce solo nome, descrizione e parametri, mai il codice.

La descrizione conta più del codice#

Se la descrizione è vaga, l'agente non capisce quando usare lo strumento, oppure lo usa nel momento sbagliato. Il modello di strumento che CrewAI mette nei progetti nuovi lo ricorda proprio nella descrizione: «Descrizione chiara di a cosa serve questo strumento: il tuo agente avrà bisogno di questa informazione per usarlo» (traduzione nostra).

Descrizione deboleDescrizione utile
«Fa dei conti.»«Calcola il costo di un weekend: alloggio, pasti, totale e quota a persona. Usalo ogni volta che servono conti sul budget di un viaggio.»
«Legge dati.»«Legge il listino prezzi del negozio. Usalo quando ti serve il prezzo aggiornato di un prodotto; il parametro è il codice del prodotto.»

Una buona descrizione dice che cosa fa lo strumento, quando usarlo e che cosa significano i parametri.

Gli strumenti pronti di crewai_tools#

CrewAI ha un pacchetto di strumenti già scritti, crewai_tools. Non arriva con il solo crewai (lo abbiamo verificato): serve la Dipendenza: Un pacchetto di cui il tuo progetto ha bisogno per funzionare. Le dipendenze si elencano in pyproject.toml o in testa allo script. glossario crewai[tools], che è proprio quella scritta in testa a tutti gli script del corso. Ecco i cinque che userai più spesso.

StrumentoChe cosa fa
SerperDevToolCerca su Google e restituisce titoli, link e brevi estratti dei risultati. È l'unico dei cinque che richiede una chiave, SERPER_API_KEY.
ScrapeWebsiteToolLegge il testo di una pagina web.
FileReadToolLegge il contenuto di un file sul tuo computer.
DirectoryReadToolElenca i file contenuti in una cartella.
FileWriterToolScrive un testo in un file.

Si Import: La riga from ... import ... che rende disponibili in un file Python le parti di una libreria, per esempio Agent e Task di CrewAI. glossario tutti allo stesso modo e si creano con le parentesi, eventualmente con qualche impostazione:

Python
from crewai_tools import DirectoryReadTool, FileReadTool, FileWriterTool, ScrapeWebsiteTool, SerperDevTool

ricerca_web = SerperDevTool(n_results=5)              # quanti risultati restituire (predefinito: 10)
lettore_pagine = ScrapeWebsiteTool()                  # senza indirizzo: l'agente sceglie quale pagina leggere
lettore_file = FileReadTool()                         # legge file dentro la cartella da cui lanci lo script
elenco_file = DirectoryReadTool(directory="appunti")  # elenca i file della cartella appunti
scrittore_file = FileWriterTool()                     # scrive file, sempre dentro la cartella consentita

SerperDevTool: la ricerca su Google#

Serper: Servizio online (serper.dev) che restituisce i risultati di ricerca di Google a un programma. Lo usa SerperDevTool; serve la chiave SERPER_API_KEY. glossario è un servizio online che fa ricerche su Google per conto di un programma. Per usarlo ti registri su serper.dev, copi la tua Chiave API: Una lunga password personale che identifica chi usa un'API e a chi addebitare i costi. Va tenuta segreta: mai pubblicarla o condividerla. glossario e la metti nel File .env: File di testo nella cartella del progetto con le variabili d'ambiente, una per riga: NOME=valore. Non va mai condiviso né caricato online. glossario con il nome SERPER_API_KEY. Il 15 settembre 2026 la pagina principale del sito prometteva «2,500 free queries» e «No credit card required», cioè 2.500 ricerche gratuite senza carta di credito. Le offerte cambiano: controlla sul sito prima di contarci.

.env
MODEL=openai/gpt-4.1-mini
OPENAI_API_KEY=sk-...la-tua-chiave...
SERPER_API_KEY=...la-tua-chiave-serper...
CREWAI_DISABLE_TELEMETRY=true

Ogni ricerca che l'agente fa consuma una ricerca del tuo piano, e un agente indeciso può farne diverse per un solo task. Per questo conviene fissare max_iter, che limita i giri di ragionamento.

ScrapeWebsiteTool: leggere una pagina#

La ricerca restituisce solo titoli ed estratti. Per leggere una pagina intera serve il Web scraping: Leggere automaticamente il contenuto di una pagina web da un programma. In CrewAI: ScrapeWebsiteTool. glossario, cioè far scaricare a un programma il testo di un sito. ScrapeWebsiteTool fa esattamente questo. Se lo crei senza indirizzo, l'agente può leggere le pagine che trova; se scrivi ScrapeWebsiteTool(website_url="https://..."), lo limiti a quella pagina. Alcuni siti bloccano i programmi che leggono le pagine, e le condizioni d'uso di un sito possono vietarlo: rispettale.

Gli strumenti per i file#

FileReadTool, DirectoryReadTool e FileWriterTool lavorano sul tuo computer. In CrewAI 1.15.21 lettura e scrittura sono confinate: FileReadTool e FileWriterTool rifiutano i percorsi che escono dalla cartella da cui hai lanciato lo script, a meno che tu non indichi un'altra cartella con il parametro base_dir. È una protezione utile: un agente confuso non può leggere o sovrascrivere file fuori dal tuo progetto.

Attenzione

Molti tutorial in rete usano CodeInterpreterTool, uno strumento che faceva eseguire codice Python all'agente. È stato rimosso: in CrewAI 1.15.21 la riga from crewai_tools import CodeInterpreterTool fallisce con un ImportError (lo abbiamo verificato), anche se la pagina riassuntiva degli strumenti nella documentazione lo elenca ancora. Sono deprecate anche le opzioni degli agenti allow_code_execution e code_execution_mode; per eseguire codice la documentazione rimanda ad ambienti isolati esterni come E2B o Modal. Nei tutorial vecchi trovi anche from crewai_tools import BaseTool: oggi si scrive from crewai.tools import BaseTool, tool.

Strumenti all'agente o al task#

Puoi dare gli strumenti in due punti.

  • All'agente, con Agent(tools=[...]): l'agente li ha a disposizione in tutti i suoi task.
  • Al task, con Task(tools=[...]): valgono solo per quel task e prendono il posto di quelli dell'agente.

In questo frammento il ricercatore ha due strumenti in generale, ma nel task sugli orari può solo leggere pagine:

Python
ricercatore = Agent(
    role="Ricercatore di viaggi",
    goal="Trovare informazioni verificate su {citta}",
    backstory="Controlli sempre le fonti.",
    tools=[ricerca_web, lettore_pagine],  # validi per tutti i task di questo agente
    llm=MODELLO,
)

controllo_orari = Task(
    description="Leggi la pagina degli orari del museo e riporta gli orari del sabato.",
    expected_output="Gli orari del sabato, in italiano, con il link alla pagina.",
    agent=ricercatore,
    tools=[lettore_pagine],  # solo per questo task: niente ricerche, solo lettura
)

Il primo crew, questa volta con la ricerca#

Ora riprendi il crew del capitolo 8 e dai al ricercatore due strumenti: la ricerca su Google e la lettura delle pagine. Cambiano anche i testi: al ricercatore chiedi di annotare le fonti, allo scrittore di non aggiungere nulla che non sia nell'elenco. Gli strumenti da soli non bastano: devi anche chiedere di usarli e di dire da dove vengono le informazioni.

guida_con_ricerca.py
# /// script
# requires-python = ">=3.10,<3.14"
# dependencies = ["crewai[tools]==1.15.21"]
# ///
"""Il primo crew, migliorato: il ricercatore cerca sul web e legge le pagine prima di scrivere."""
import os

from crewai import Agent, Crew, Process, Task
from crewai_tools import ScrapeWebsiteTool, SerperDevTool

# Il modello si sceglie nel file .env (capitolo 7). Se MODEL manca, si usa quello scritto qui.
MODELLO = os.getenv("MODEL", "openai/gpt-4.1-mini")

# Gli strumenti si creano una volta e poi si danno agli agenti.
ricerca_web = SerperDevTool(n_results=5)  # cerca su Google tramite serper.dev (serve SERPER_API_KEY)
lettore_pagine = ScrapeWebsiteTool()      # legge il testo di una pagina web

ricercatore = Agent(
    role="Ricercatore di viaggi",
    goal="Trovare 3 cose da fare a {citta} in un weekend, controllate su fonti web",
    backstory=(
        "Non ti fidi della memoria: cerchi sul web, leggi le pagine dei siti ufficiali "
        "e annoti sempre l'indirizzo della fonte. Se non trovi conferma, lo dici."
    ),
    tools=[ricerca_web, lettore_pagine],  # solo il ricercatore ha gli strumenti
    llm=MODELLO,
    max_iter=10,  # tetto ai giri di ragionamento, e quindi alle ricerche
    verbose=True,
)

scrittore = Agent(
    role="Autore di guide brevi",
    goal="Trasformare appunti di viaggio in un testo chiaro e invitante",
    backstory="Scrivi guide tascabili: frasi corte, niente giri di parole, niente informazioni inventate.",
    llm=MODELLO,
    verbose=True,
)

ricerca = Task(
    description=(
        "Cerca sul web le attività migliori per un weekend a {citta}. "
        "Leggi almeno una pagina di un sito ufficiale o affidabile, poi scegli 3 attività."
    ),
    expected_output=(
        "Un elenco puntato di 3 voci in italiano. Per ogni voce: nome del luogo, "
        "un motivo per andarci, indirizzo web della fonte."
    ),
    agent=ricercatore,
)

guida = Task(
    description=(
        "Usa solo l'elenco ricevuto per scrivere una mini-guida del weekend a {citta}. "
        "Non aggiungere luoghi che non sono nell'elenco."
    ),
    expected_output="Un testo di massimo 150 parole in italiano, con un titolo e l'elenco delle fonti in fondo.",
    agent=scrittore,
    output_file="guida.md",
)

crew = Crew(
    agents=[ricercatore, scrittore],
    tasks=[ricerca, guida],
    process=Process.sequential,
    verbose=True,
)

if __name__ == "__main__":
    risultato = crew.kickoff(inputs={"citta": "Arezzo"})
    print(risultato.raw)

Rispetto al capitolo 8 cambiano tre cose: solo il ricercatore riceve gli strumenti (lo scrittore lavora sul testo del task precedente); n_results=5 gli dà meno testo da leggere; l'expected_output chiede l'indirizzo della fonte, così puoi controllare tu. Con la chiave Serper nel file .env, lo lanci come sempre:

Terminale
uv run --env-file .env guida_con_ricerca.py

Nel terminale vedrai anche i riquadri degli strumenti (quale strumento, con quali valori, che cosa ha restituito); alla fine la guida finisce in guida.md. Questo script l'abbiamo verificato a secco ma non eseguito, perché richiede una chiave Serper personale: per questo non ne trovi l'output. Il prossimo gira senza chiavi e l'abbiamo eseguito davvero.

Creare uno strumento tuo#

Per tutto ciò che gli strumenti pronti non coprono (il listino del tuo negozio, un calcolo della tua azienda) ne scrivi uno tu. Prima servono due parole di Python: funzione e decoratore.

La funzione, spiegata da zero#

Una Funzione si definisce con la parola def, seguita dal nome, dai Parametro: Un valore che passi a una funzione o a una classe tra parentesi, con un nome: Agent(role="...") — qui role è il parametro. glossario tra parentesi e dai due punti. Il corpo, rientrato di quattro spazi, fa il lavoro; return restituisce il risultato. Ecco una funzione che somma due numeri, e come si usa:

Python
def somma(a: int, b: int) -> int:
    """Somma due numeri interi."""
    return a + b


totale = somma(2, 3)  # si "chiama" la funzione con le parentesi: ora totale vale 5

Tre dettagli contano molto quando la funzione diventa uno strumento:

  • a: int dice che il parametro a è un numero intero. Si chiama annotazione di tipo: int è un intero, float un numero con la virgola, str una Stringa: Un testo nel codice, scritto tra virgolette: "Ricercatore". glossario. CrewAI la usa per spiegare al modello che valori passare.
  • -> int dice che cosa restituisce la funzione.
  • Il testo tra tre virgolette subito sotto def si chiama docstring ed è la descrizione della funzione. In uno strumento diventa la descrizione che il modello legge.

Il decoratore#

Un Decoratore: Una riga che inizia con @ sopra una funzione e le aggiunge un comportamento. In CrewAI: @agent, @task, @start, @listen. glossario è una riga che comincia con @, scritta subito sopra una funzione: prende la funzione e le aggiunge un comportamento, senza che tu debba riscriverla. CrewAI ne offre uno per gli strumenti, @tool. La funzione fa sempre lo stesso lavoro, ma diventa un oggetto che un agente può usare, con un nome e una descrizione.

Uno strumento con @tool: il calcolatore del budget#

Fare conti è un punto debole dei modelli linguistici: producono numeri plausibili, non numeri calcolati. Lo script seguente dà a un agente un calcolatore del budget per il weekend. Non servono chiavi: con Ollama gira gratis sul tuo computer.

budget_weekend.py
# /// script
# requires-python = ">=3.10,<3.14"
# dependencies = ["crewai[tools]==1.15.21"]
# ///
"""Un agente calcola il budget di un weekend con uno strumento fatto a mano (nessuna chiave)."""
import os

from crewai import Agent, Crew, Process, Task
from crewai.tools import tool

# Il modello si sceglie nel file .env (capitolo 7). Se MODEL manca, si usa quello scritto qui.
MODELLO = os.getenv("MODEL", "openai/gpt-4.1-mini")


# Il decoratore @tool trasforma la funzione qui sotto in uno strumento per gli agenti.
# Il testo tra parentesi è il nome che l'agente vede.
@tool("Calcolatore budget weekend")
def calcola_budget(notti: int, prezzo_camera: float, persone: int, prezzo_pasto: float) -> str:
    """Calcola il costo di un weekend: alloggio, pasti, totale e quota a persona.
    Usalo ogni volta che servono conti sul budget di un viaggio.
    notti = numero di notti; prezzo_camera = euro a notte per l'intera camera;
    persone = quante persone viaggiano; prezzo_pasto = euro a persona per un pasto.
    Conta due pasti al giorno per ogni notte."""
    alloggio = notti * prezzo_camera
    pasti = notti * 2 * persone * prezzo_pasto
    totale = alloggio + pasti
    # Lo strumento restituisce sempre un testo: è quello che l'agente leggerà.
    return (
        f"Alloggio: {alloggio:.2f} euro. Pasti: {pasti:.2f} euro. "
        f"Totale: {totale:.2f} euro. A persona: {totale / persone:.2f} euro."
    )


consulente = Agent(
    role="Consulente di budget per viaggi",
    goal="Dire con precisione quanto costa un weekend a {citta}",
    backstory=(
        "Non fai mai i conti a mente: usi sempre il Calcolatore budget weekend "
        "e riporti esattamente i numeri che ti restituisce."
    ),
    tools=[calcola_budget],  # la lista degli strumenti che questo agente può usare
    llm=MODELLO,
    max_iter=5,
    verbose=True,
)

budget = Task(
    description=(
        "{persone} persone passano {notti} notti a {citta}. La camera costa {prezzo_camera} euro "
        "a notte e un pasto costa in media {prezzo_pasto} euro a persona. "
        "Calcola il budget usando lo strumento Calcolatore budget weekend."
    ),
    expected_output=(
        "Quattro righe in italiano: alloggio, pasti, totale e quota a persona, "
        "con i numeri restituiti dallo strumento."
    ),
    agent=consulente,
)

crew = Crew(agents=[consulente], tasks=[budget], process=Process.sequential, verbose=True)

if __name__ == "__main__":
    risultato = crew.kickoff(
        inputs={"citta": "Arezzo", "persone": "2", "notti": "2", "prezzo_camera": "95", "prezzo_pasto": "28"}
    )
    print("\n=== RISULTATO FINALE ===")
    print(risultato.raw)

Che cosa succede:

  • @tool("Calcolatore budget weekend") trasforma calcola_budget in uno strumento con quel nome; la docstring diventa la descrizione e le annotazioni (notti: int e le altre) i parametri che il modello deve riempire.
  • Le righe con f"...{totale:.2f}..." costruiscono la stringa di risposta: Python mette al posto delle graffe il valore della variabile, con due cifre dopo la virgola.
  • Nel backstory e nella description chiedi esplicitamente di usare lo strumento: con i modelli piccoli questa insistenza aiuta.

Uno strumento si può provare da solo, senza agente, con il metodo run: è il modo più veloce per scoprire un errore nel tuo codice. Aggiungi questa riga in fondo allo script:

Python
print(calcola_budget.run(notti=2, prezzo_camera=95, persone=2, prezzo_pasto=28))
Output
Alloggio: 190.00 euro. Pasti: 224.00 euro. Totale: 414.00 euro. A persona: 207.00 euro.

Questo output è reale. I conti tornano: 2 notti a 95 euro fanno 190 euro di alloggio; 2 notti per 2 pasti per 2 persone a 28 euro fanno 224 euro di pasti.

Eseguirlo davvero con Ollama#

Abbiamo lanciato lo script con il modello locale qwen2.5:7b, cioè con MODEL=ollama/qwen2.5:7b nel file .env come nel capitolo 7. Ecco la parte centrale dell'output.

Output di terminale: parte l'agente Consulente di budget per viaggi con il task sul weekend a Arezzo, poi il riquadro Tool Execution Completed dello strumento calcolatore_budget_weekend con alloggio 190, pasti 224 e totale 414 euro
Prima parte: l'agente parte e lo strumento restituisce il suo calcolo. I quadratini nei titoli dei riquadri sono icone che il font dello screenshot non sa disegnare. screenshot del 15 settembre 2026, crewai 1.15.21 con qwen2.5:7b
Output di terminale: il riquadro Tool Execution Started con i valori prezzo_camera 95, persone 2, prezzo_pasto 28, notti 2, poi la risposta finale dell'agente con un totale di 414 euro
Seconda parte: i valori che il modello ha passato allo strumento e la risposta finale. Abbiamo saltato una sola riga molto lunga che ripeteva il risultato dello strumento. screenshot del 15 settembre 2026, crewai 1.15.21 con qwen2.5:7b

Come leggerlo:

  • Il riquadro Tool Execution Started mostra i valori scelti dal modello: {'prezzo_camera': 95, 'persone': 2, 'prezzo_pasto': 28, 'notti': 2}. Li ha ricavati dalla frase del task, e sono corretti.
  • Il riquadro Tool Execution Completed mostra il testo restituito dalla funzione. Compare prima di «Started» solo per l'ordine di stampa: la chiamata è una sola (#1 in entrambi). Il nome calcolatore_budget_weekend è quello che hai scritto, messo da CrewAI in minuscolo e senza spazi.
  • La risposta finale riporta il totale giusto, 414 euro, ma su una riga sola invece delle «quattro righe» chieste nell'expected_output: i modelli piccoli seguono il formato in modo approssimativo. Nel prossimo capitolo vedrai come controllarlo in automatico.
  • Nel titolo del task si legge «a Arezzo»: il Segnaposto {…}: Una parola tra graffe, come {citta}, dentro i testi di agenti e task. Al kickoff(inputs={...}) viene sostituita con il valore vero. glossario {citta} sostituisce la parola e basta, non corregge la preposizione.
Attenzione

Nella nostra prova qwen2.5:7b ha usato lo strumento correttamente al primo tentativo, ma non è garantito che succeda sempre: con i modelli piccoli può capitare che l'agente risponda senza chiamare lo strumento, o che passi valori sbagliati. Se nell'output non vedi nessun riquadro «Tool Execution», i numeri della risposta sono stati scritti dal modello, non calcolati.

Lo stesso strumento con BaseTool#

Il secondo modo di creare uno strumento è scrivere una Classe e oggetto: Una classe è uno stampo (per esempio Agent); un oggetto è una cosa concreta creata con quello stampo (il tuo agente ricercatore). glossario che parte dallo stampo BaseTool di CrewAI e ne riempie i pezzi. Scrivi più righe, ma hai più controllo: descrivi ogni parametro separatamente e puoi aggiungere impostazioni tue. È anche la forma che trovi nei progetti creati con crewai create (capitolo 14).

Python
from typing import Type

from crewai.tools import BaseTool
from pydantic import BaseModel, Field


class ParametriBudget(BaseModel):
    """I parametri che l'agente deve fornire."""
    notti: int = Field(..., description="Numero di notti")
    prezzo_camera: float = Field(..., description="Euro a notte per l'intera camera")
    persone: int = Field(..., description="Quante persone viaggiano")
    prezzo_pasto: float = Field(..., description="Euro a persona per un pasto")


class CalcolatoreBudget(BaseTool):
    name: str = "Calcolatore budget weekend"
    description: str = (
        "Calcola alloggio, pasti, totale e quota a persona di un weekend. "
        "Conta due pasti al giorno per ogni notte."
    )
    args_schema: Type[BaseModel] = ParametriBudget  # collega i parametri allo strumento

    def _run(self, notti: int, prezzo_camera: float, persone: int, prezzo_pasto: float) -> str:
        alloggio = notti * prezzo_camera
        pasti = notti * 2 * persone * prezzo_pasto
        totale = alloggio + pasti
        return f"Totale: {totale:.2f} euro. A persona: {totale / persone:.2f} euro."
  • ParametriBudget elenca i parametri, ognuno con tipo e descrizione. BaseModel e Field vengono da una libreria che si chiama Pydantic: la conoscerai bene nel prossimo capitolo.
  • name e description sono il nome e la descrizione che il modello legge.
  • _run contiene il lavoro vero; i nomi dei suoi parametri devono essere gli stessi di ParametriBudget.

Si usa come uno strumento pronto, con le parentesi perché dalla classe crei un oggetto: tools=[CalcolatoreBudget()]. Quale scegliere? @tool per strumenti semplici fatti da una funzione; BaseTool quando lo strumento ha bisogno di impostazioni (un percorso, un indirizzo) o di descrizioni dettagliate per ogni parametro.

MCP, in parole semplici#

Scrivere uno strumento per ogni servizio (il calendario, la posta, GitHub, un database) sarebbe faticoso. MCP: Model Context Protocol: standard aperto per collegare agenti a servizi esterni (calendari, database, GitHub…) tramite "server MCP" già pronti. glossario, Model Context Protocol, è uno standard aperto che funziona come una presa USB: un servizio mette a disposizione un «server MCP» con i suoi strumenti, e qualunque programma compatibile può collegarsi e usarli.

In CrewAI un agente riceve i server MCP nel parametro mcps, accanto a tools. Ecco l'esempio della documentazione, ridotto a un solo server (l'indirizzo è quello di Exa, un motore di ricerca, e richiede una chiave tua; i testi sono in inglese perché copiati dai docs):

Python
research_agent = Agent(
    role="Research Analyst",
    goal="Find and analyze information using advanced search tools",
    backstory="Expert researcher with access to multiple data sources",
    mcps=[
        # l'indirizzo di un server MCP remoto: i suoi strumenti si aggiungono a quelli dell'agente
        "https://mcp.exa.ai/mcp?api_key=your_key&profile=your_profile",
    ],
)

Per usare mcps la documentazione indica di aggiungere il pacchetto mcp (uv add mcp). E ripete, in grassetto, una regola da prendere sul serio: usa solo server MCP di cui ti fidi, perché i loro strumenti agiscono davvero sui servizi a cui li colleghi. Negli esempi del corso non useremo MCP; se vuoi approfondire, parti dalla panoramica ufficiale su MCP.

Prova tu: uno strumento che conta le parole

Scrivi con @tool uno strumento «Contatore di parole» che riceve un testo e restituisce quante parole contiene, poi provalo da solo con run. Una soluzione:

Python
from crewai.tools import tool


@tool("Contatore di parole")
def conta_parole(testo: str) -> str:
    """Conta quante parole ci sono in un testo. Usalo per controllare la lunghezza di una bozza."""
    return f"Il testo contiene {len(testo.split())} parole."


print(conta_parole.run(testo="Arezzo è una città toscana"))

L'output è Il testo contiene 5 parole.: split() divide il testo negli spazi e len conta i pezzi. Per usarlo davvero, aggiungilo alla lista tools dello scrittore e chiedi nel task di controllare che la guida non superi 120 parole.

In breve
  • Senza strumenti un agente può solo generare testo plausibile: su fatti e numeri rischia di inventare.
  • Del catalogo degli strumenti il modello vede solo nome, descrizione e parametri: scrivi descrizioni chiare.
  • crewai_tools offre strumenti pronti come SerperDevTool (chiave SERPER_API_KEY), ScrapeWebsiteTool, FileReadTool, DirectoryReadTool e FileWriterTool; CodeInterpreterTool non esiste più.
  • Gli strumenti si danno all'agente, per tutti i suoi task, o al task, solo per quello.
  • Uno strumento tuo si crea con @tool su una funzione o con una classe BaseTool; provalo prima con run.
  • MCP collega gli agenti a server di strumenti già pronti, con il parametro mcps.

Nel prossimo capitolo impari a ottenere dagli agenti risultati con una forma precisa e a controllarli in automatico: risultati strutturati e guardrail.

Corso indipendente, non affiliato a CrewAI Inc. Contenuti verificati su CrewAI 1.15.21 il 15 settembre 2026.