Vai al contenuto

I mattoni di CrewAI

14Il progetto ufficiale: crewai create e crewai run

Che cosa crea crewai create crew: il progetto JSON di oggi e il progetto classico con YAML e crew.py. Impari a leggere i file generati, a installarli con crewai install, ad avviarli con crewai run e a scegliere tra script e progetto.

Tempo di lettura: 25 minuti

Finora hai scritto ogni crew in un unico file: agenti, task e avvio uno sotto l'altro. Per imparare è perfetto, ma quando il lavoro cresce un file solo diventa come una cucina in cui ingredienti, ricette e pentole stanno tutti sullo stesso tavolo. Il progetto ufficiale di CrewAI è la cucina con i cassetti etichettati: gli agenti in un file, i task in un altro, il codice in un altro ancora, sempre negli stessi posti. Chi apre un progetto CrewAI, che sia un collega o un assistente di programmazione, sa subito dove guardare. Il comando che lo prepara è crewai create crew.

Due tipi di progetto#

La CLI: Command Line Interface: un programma che si usa dal terminale con comandi. CrewAI ne ha una: crewai create, crewai runglossario di CrewAI, il programma crewai che hai installato nel capitolo 6, oggi crea due tipi di progetto diversi. Quando leggi una guida in rete, controlla sempre di quale parla.

ComandoChe cosa creaDove descrivi agenti e task
crewai create crew nomeil progetto JSON, il predefinito in CrewAI 1.15.21crew.jsonc e agents/*.jsonc
crewai create crew nome --classicil progetto classicoconfig/agents.yaml, config/tasks.yaml e crew.py

Il progetto classico è quello che trovi nella maggior parte dei tutorial e nelle skill ufficiali di CrewAI, ed è quello che guardiamo più da vicino; il progetto JSON è la novità, e conviene saperlo riconoscere. In tutti e due i casi usa nomi con il trattino basso, come guida_viaggio. Python non accetta trattini nei nomi dei moduli: se scrivi guida-viaggio, in CrewAI 1.15.21 la CLI crea da sola la cartella guida_viaggio (lo abbiamo verificato con entrambi i tipi di progetto), ma poi il nome che hai scritto e quello della cartella non coincidono, e ci si confonde.

Il progetto JSON: crewai create crew#

Lanciato senza opzioni, il comando apre una procedura guidata nel terminale: ti chiede ruolo, obiettivo e backstory del primo agente, gli strumenti, la descrizione del task e il risultato atteso, poi il provider e il modello, e infine le chiavi API, che scrive nel file .env insieme a MODEL. Crea anche un archivio Git, cioè attiva nella cartella il sistema che tiene la storia delle modifiche ai file.

Terminale
crewai create crew guida_viaggio

Per fotografare il risultato senza rispondere alle domande abbiamo usato la modalità non interattiva, con la Variabile d'ambiente: Un valore con un nome (per esempio OPENAI_API_KEY) che i programmi leggono dal sistema invece che dal codice. Serve a tenere i segreti fuori dai file di codice. glossario CREWAI_DMN=true: accetta le risposte predefinite e non scrive il file .env.

Terminale: crewai create crew prova_json crea il progetto e suggerisce i passi successivi, cioè cd prova_json e crewai run, e quali file personalizzare
La creazione di un progetto JSON, lanciata con CREWAI_DMN=true. In fondo il comando ricorda quali file modificare. screenshot del 15 settembre 2026, crewai 1.15.21
Elenco dei file del progetto JSON: .gitignore, README.md, agents/researcher.jsonc, crew.jsonc, knowledge/user_preference.txt, pyproject.toml, skills/.gitkeep e la cartella tools
I file del progetto JSON appena creato; la cartella nascosta .git è esclusa dall'elenco. screenshot del 15 settembre 2026

Il cuore del progetto sono due file in JSONC, cioè JSON: Formato di testo per dati strutturati con graffe, virgolette e due punti. È il formato predefinito dei nuovi progetti CrewAI (crew.jsonc). glossario con i commenti: le righe che iniziano con // sono note per te, e il programma le ignora. I file generati sono pieni di commenti che elencano le opzioni disponibili; qui sotto trovi crew.jsonc con i commenti tolti, così si vede la struttura.

crew.jsonc
{
  "name": "prova_json",
  "agents": ["researcher"],
  "tasks": [
    {
      "name": "research_task",
      "description": "Research current AI trends and write a concise summary.",
      "expected_output": "A concise markdown report with key findings.",
      "agent": "researcher"
    }
  ],
  "process": "sequential",
  "verbose": true,
  "memory": true,
  "inputs": {}
}

Riconosci quasi tutto. agents elenca gli agenti per nome; ogni task ha description, expected_output e l'agent che lo esegue; process è il processo del capitolo 11; inputs contiene i valori predefiniti dei segnaposto. "memory": true attiva la memoria, che vedrai nel capitolo 15. Il nome researcher rimanda al file agents/researcher.jsonc, anche questo senza commenti:

agents/researcher.jsonc
{
  "role": "Senior Researcher",
  "goal": "Research the requested topic and identify useful findings.",
  "backstory": "You are an experienced researcher who finds relevant information and presents it clearly.",
  "llm": "openai/gpt-4o",
  "tools": [],
  "settings": {
    "verbose": false,
    "allow_delegation": false
  }
}

Nel file generato, i commenti accanto a "tools" spiegano che gli strumenti pronti si indicano per nome, per esempio "SerperDevTool", e quelli scritti da te con "custom:nome", che carica il file tools/nome.py. Il modello si cambia in "llm", nello stesso formato provider/modello che usi nel file .env.

Attenzione

La documentazione avverte di eseguire progetti JSON solo se vengono da fonti di cui ti fidi: gli strumenti custom: e i riferimenti {"python": ...} eseguono codice Python sul tuo computer appena il crew viene caricato. Un progetto scaricato da internet va letto prima di essere avviato.

YAML, spiegato da zero#

Il progetto classico descrive agenti e task in YAML: Formato di file di testo per scrivere configurazioni in modo leggibile, con rientri e coppie chiave: valore. Nei progetti CrewAI classici descrive agenti e task. glossario, un formato pensato per essere letto dalle persone: niente graffe e poche virgolette, perché la struttura la danno i rientri. Ecco un esempio inventato per mostrare tutte le regole in poche righe:

esempio.yaml
# Le righe che iniziano con # sono commenti
ricercatore:
  role: Ricercatore di viaggi
  goal: >
    Trovare le 3 cose migliori da fare
    a {citta} in un weekend
  strumenti:
    - ricerca web
    - lettura pagine

Le regole che ti servono:

  • Ogni riga è una coppia chiave: valore, con uno spazio dopo i due punti.
  • Il rientro dice che cosa sta dentro cosa: role, goal e strumenti appartengono a ricercatore. Usa sempre gli spazi (di solito due), mai il tasto Tab.
  • Il simbolo > dopo i due punti annuncia un testo su più righe: le righe rientrate sotto vengono unite in un'unica frase, con uno spazio al posto di ogni a capo.
  • Le righe che iniziano con un trattino e uno spazio sono gli elementi di una Lista: Una sequenza ordinata di valori tra parentesi quadre: [ricercatore, scrittore]. glossario.
  • I segnaposto come {citta} funzionano come negli script: li sostituisce kickoff(inputs=...).
Se parti da zero

YAML e JSON possono descrivere gli stessi dati: la prima riga dell'esempio, in JSON, sarebbe {"ricercatore": {"role": "Ricercatore di viaggi"}}. YAML è più comodo da scrivere a mano; JSON è più rigido. Il rovescio della medaglia di YAML è che un rientro sbagliato cambia il significato senza avvisarti: se qualcosa non torna, controlla prima gli spazi.

Il progetto classico: --classic#

Nel laboratorio del corso abbiamo creato un progetto classico così; l'opzione --skip-provider salta le domande su provider e chiavi:

Terminale
crewai create crew guida_viaggio --classic --skip-provider
Terminale: crewai create crew guida_viaggio --classic --skip-provider elenca i file creati, tra cui pyproject.toml, main.py, crew.py, agents.yaml e tasks.yaml
Il comando elenca uno per uno i file che crea. screenshot del 15 settembre 2026, crewai 1.15.21
Albero delle cartelle del progetto guida_viaggio: knowledge, src/guida_viaggio con config, tools, crew.py e main.py, poi tests, .gitignore, AGENTS.md, README.md e pyproject.toml
La struttura del progetto classico. Il codice sta dentro src/guida_viaggio. screenshot del 15 settembre 2026
Come si collegano i file di un progetto classico Il comando crewai run esegue uv run run_crew, che chiama la funzione run di main.py. main.py prepara gli inputs e avvia il kickoff della classe definita in crew.py. crew.py, con il decoratore CrewBase, legge agents.yaml e tasks.yaml: ogni metodo con @agent o @task prende la voce YAML con lo stesso nome, e il metodo con @crew unisce tutto. agents.yaml role · goal · backstory tasks.yaml description · agent main.py run(): prepara gli inputs e avvia la crew crew.py @CrewBase @agent un metodo per agente @task un metodo per task @crew unisce tutto nome del metodo = nome della voce nel YAML crewai run dal terminale uv run run_crew comando scritto in pyproject.toml letto letto kickoff chiama run() di main.py
Come si passano il lavoro i file del progetto classico, dal comando crewai run fino alle voci dei file YAML.

Vediamo i file importanti, così come sono stati generati.

config/agents.yaml e config/tasks.yaml#

Il modello di partenza contiene due agenti e due task, in inglese, su un argomento {topic}:

src/guida_viaggio/config/agents.yaml
researcher:
  role: >
    {topic} Senior Data Researcher
  goal: >
    Uncover cutting-edge developments in {topic}
  backstory: >
    You're a seasoned researcher with a knack for uncovering the latest
    developments in {topic}. Known for your ability to find the most relevant
    information and present it in a clear and concise manner.

reporting_analyst:
  role: >
    {topic} Reporting Analyst
  goal: >
    Create detailed reports based on {topic} data analysis and research findings
  backstory: >
    You're a meticulous analyst with a keen eye for detail. You're known for
    your ability to turn complex data into clear and concise reports, making
    it easy for others to understand and act on the information you provide.
src/guida_viaggio/config/tasks.yaml
research_task:
  description: >
    Conduct a thorough research about {topic}
    Make sure you find any interesting and relevant information given
    the current year is {current_year}.
  expected_output: >
    A list with 10 bullet points of the most relevant information about {topic}
  agent: researcher

reporting_task:
  description: >
    Review the context you got and expand each topic into a full section for a report.
    Make sure the report is detailed and contains any and all relevant information.
  expected_output: >
    A fully fledged report with the main topics, each with a full section of information.
    Formatted as markdown without '```'
  agent: reporting_analyst

Ogni voce di primo livello, come researcher o research_task, è un nome che crew.py userà. In tasks.yaml, la riga agent: researcher collega il task all'agente con quel nome. I segnaposto {topic} e {current_year} li riempie main.py.

crew.py: il collegamento#

src/guida_viaggio/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai.agents.agent_builder.base_agent import BaseAgent


@CrewBase
class GuidaViaggio():
    """GuidaViaggio crew"""

    agents: list[BaseAgent]
    tasks: list[Task]

    @agent
    def researcher(self) -> Agent:
        return Agent(
            config=self.agents_config['researcher'], # type: ignore[index]
            verbose=True
        )

    @agent
    def reporting_analyst(self) -> Agent:
        return Agent(
            config=self.agents_config['reporting_analyst'], # type: ignore[index]
            verbose=True
        )

    @task
    def research_task(self) -> Task:
        return Task(
            config=self.tasks_config['research_task'], # type: ignore[index]
        )

    @task
    def reporting_task(self) -> Task:
        return Task(
            config=self.tasks_config['reporting_task'], # type: ignore[index]
            output_file='report.md'
        )

    @crew
    def crew(self) -> Crew:
        """Creates the GuidaViaggio crew"""
        return Crew(
            agents=self.agents,
            tasks=self.tasks,
            process=Process.sequential,
            verbose=True,
        )

Le parti nuove, una alla volta:

  • Le funzioni scritte dentro 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 si chiamano metodi; self è l'oggetto stesso, e serve per raggiungere le sue parti.
  • @CrewBase è un Decoratore: Una riga che inizia con @ sopra una funzione e le aggiunge un comportamento. In CrewAI: @agent, @task, @start, @listen. glossario applicato alla classe: legge da solo config/agents.yaml e config/tasks.yaml e li rende disponibili come self.agents_config e self.tasks_config.
  • Ogni metodo con @agent crea un agente con config=self.agents_config['researcher']: role, goal e backstory arrivano dal YAML. Qui aggiungi ciò che nel YAML non c'è, come tools=[...] del capitolo 12.
  • I metodi con @task fanno lo stesso con i task; output_file='report.md' salva il risultato finale.
  • Il metodo con @crew costruisce la Crew. self.agents e self.tasks li raccoglie CrewAI da tutti i metodi decorati.
  • Il nome del metodo deve coincidere con il nome della voce nel YAML: def researcher va con researcher:. Secondo la guida ufficiale è la causa tipica dell'errore KeyError.
  • Le scritte # type: ignore[index] sono commenti per i programmi che controllano il codice: puoi ignorarle.

main.py: l'avvio#

Ecco la parte iniziale di main.py:

src/guida_viaggio/main.py
#!/usr/bin/env python
import sys
import warnings

from datetime import datetime

from guida_viaggio.crew import GuidaViaggio

warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")


def run():
    """
    Run the crew.
    """
    inputs = {
        'topic': 'AI LLMs',
        'current_year': str(datetime.now().year)
    }

    try:
        GuidaViaggio().crew().kickoff(inputs=inputs)
    except Exception as e:
        raise Exception(f"An error occurred while running the crew: {e}")

La funzione run prepara il Dizionario: Una raccolta di coppie nome → valore tra parentesi graffe: {"citta": "Arezzo"}. glossario inputs e chiama kickoff, proprio come la parte sotto if __name__ == "__main__": dei tuoi script. GuidaViaggio().crew() crea l'oggetto della classe e chiama il suo metodo crew. try ed except intercettano un eventuale errore per mostrarlo con un messaggio più chiaro. Più sotto il file contiene altre funzioni (train, replay, test, run_with_trigger) per usi avanzati che in questo corso non servono.

Gli altri file#

FileA che cosa serve
pyproject.tomlNome del progetto, dipendenze (crewai[tools]>=1.15.21,<2.0.0) e comandi. La riga run_crew = "guida_viaggio.main:run" dice quale funzione avviare.
tools/custom_tool.pyUn modello di strumento scritto con BaseTool, come nel capitolo 12.
knowledge/user_preference.txtUn file di esempio con informazioni per gli agenti (capitolo 15).
AGENTS.mdIstruzioni lunghe 1.207 righe per gli assistenti di programmazione AI che lavorano sul progetto.
README.mdIstruzioni generali, in inglese.
.gitignoreL'elenco dei file che Git non deve salvare. Contiene .env, così le chiavi non finiscono online per sbaglio.
tests/Una cartella vuota, pronta per i test.

Dal primo crew al progetto classico#

Per trasformare il crew del capitolo 8 in un progetto classico basta modificare tre file. Abbiamo fatto la prova in un progetto creato con crewai create crew guida_arezzo --classic --skip-provider. Prima gli agenti:

src/guida_arezzo/config/agents.yaml
# Ogni voce di primo livello è un agente: il nome deve coincidere con il metodo in crew.py.
ricercatore:
  role: >
    Ricercatore di viaggi
  goal: >
    Trovare le 3 cose migliori da fare a {citta} in un weekend
  backstory: >
    Hai girato l'Italia per vent'anni e conosci i posti che valgono davvero.

scrittore:
  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.

Poi i task, con i nomi degli agenti appena scritti:

src/guida_arezzo/config/tasks.yaml
ricerca:
  description: >
    Elenca le 3 attività migliori per un weekend a {citta},
    con un motivo per ciascuna.
  expected_output: >
    Un elenco puntato di 3 voci, in italiano.
  agent: ricercatore

guida:
  description: >
    Usa l'elenco ricevuto per scrivere una mini-guida del weekend a {citta}.
  expected_output: >
    Un testo di massimo 120 parole in italiano, con un titolo.
  agent: scrittore

In crew.py cambiano solo i nomi dei metodi e delle voci, perché devono corrispondere al YAML:

src/guida_arezzo/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai.agents.agent_builder.base_agent import BaseAgent


@CrewBase
class GuidaArezzo():
    """GuidaArezzo crew"""

    agents: list[BaseAgent]
    tasks: list[Task]

    @agent
    def ricercatore(self) -> Agent:  # stesso nome della voce in agents.yaml
        return Agent(
            config=self.agents_config['ricercatore'], # type: ignore[index]
            verbose=True
        )

    @agent
    def scrittore(self) -> Agent:
        return Agent(
            config=self.agents_config['scrittore'], # type: ignore[index]
            verbose=True
        )

    @task
    def ricerca(self) -> Task:  # stesso nome della voce in tasks.yaml
        return Task(
            config=self.tasks_config['ricerca'], # type: ignore[index]
        )

    @task
    def guida(self) -> Task:
        return Task(
            config=self.tasks_config['guida'], # type: ignore[index]
            output_file='guida.md'
        )

    @crew
    def crew(self) -> Crew:
        """Creates the GuidaArezzo crew"""
        return Crew(
            agents=self.agents,
            tasks=self.tasks,
            process=Process.sequential,
            verbose=True,
        )

Infine, in main.py, dentro la funzione run sostituisci il dizionario degli inputs con inputs = {'citta': 'Arezzo'}. Due cose da notare. La classe si chiama GuidaArezzo perché CrewAI la nomina a partire dal nome del progetto. E nessun agente ha llm=: il modello arriva dalla variabile MODEL del file .env. Nella nostra prova, con MODEL=ollama/qwen2.5:7b nel .env, CrewAI ha creato entrambi gli agenti con qwen2.5:7b, e i due task nell'ordine in cui sono scritti, con {citta} sostituito da Arezzo.

Installare e avviare: crewai install e crewai run#

Entra nella cartella del progetto, quella che contiene pyproject.toml, e lancia due comandi:

Terminale
cd guida_arezzo
crewai install
crewai run
  • crewai install installa le 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 del progetto. Dietro le quinte esegue uv sync: uv: Programma gratuito che installa Python e i pacchetti in modo rapido e ordinato. È lo strumento consigliato dalla documentazione di CrewAI. glossario crea l'Ambiente virtuale: Una copia isolata di Python dedicata a un progetto, con i suoi pacchetti. Evita che progetti diversi si pestino i piedi. uv lo crea da solo. glossario del progetto nella cartella .venv e il file uv.lock, che fissa le versioni esatte dei pacchetti. La prima volta scarica parecchio.
  • crewai run avvia la crew. In un progetto classico esegue uv run run_crew, cioè la funzione run di main.py indicata in pyproject.toml. In un progetto JSON carica direttamente crew.jsonc e, se un segnaposto non ha un valore in inputs, te lo chiede nel terminale.

Ecco le prime righe di crewai install nella nostra prova (output reale, accorciato: abbiamo tolto le due righe con il percorso della cartella e l'elenco dei pacchetti):

Output
$ crewai install
Using CPython 3.12.3 interpreter at: /usr/bin/python3
Creating virtual environment at: .venv
Resolved 158 packages in 6.89s
Prepared 1 package in 7.16s
Installed 145 packages in 1.52s

I comandi vanno lanciati dalla cartella principale del progetto: da un'altra cartella crewai run non trova pyproject.toml e non sa che cosa avviare. Se più avanti aggiungi un pacchetto, la documentazione indica uv add nome-pacchetto.

Il file .env nel progetto#

Il file .env va nella cartella principale del progetto, accanto a pyproject.toml, con le stesse righe che usi negli script:

.env
MODEL=ollama/qwen2.5:7b
CREWAI_DISABLE_TELEMETRY=true

Qui non serve --env-file. CrewAI 1.15.21, quando viene caricato, cerca un file .env partendo dalla propria cartella di installazione e risalendo verso l'alto; in un progetto la sua cartella di installazione è dentro .venv, quindi la ricerca arriva alla cartella principale e trova il tuo .env. Negli script singoli, invece, uv installa CrewAI in una cartella a parte, e per questo lì usi --env-file .env. La procedura guidata del progetto JSON scrive il .env per te; con --skip-provider o con CREWAI_DMN=true non viene creato e lo scrivi tu. Grazie al .gitignore generato, resta fuori da Git.

Script o progetto: quando conviene cosa#

Script singoloProgetto ufficiale
Stai imparando o provando un'idea.Il crew diventa uno strumento che usi spesso o che condividi.
Un file da mandare a qualcuno, che lo lancia con uv run.Più persone lavorano sugli stessi file; chi scrive i testi degli agenti può non toccare il codice.
Pochi agenti e task, nessuno strumento tuo.Strumenti tuoi in tools/, file di conoscenza in knowledge/, test.
Nessuna installazione da mantenere.Versioni fissate in uv.lock; è la forma che si aspetta la piattaforma commerciale CrewAI AMP: Agent Management Platform: la piattaforma commerciale di CrewAI Inc. per pubblicare, monitorare e scalare crew e flow. Il framework resta gratuito anche senza AMP. glossario per pubblicare un crew.

Gli esempi pratici del corso restano script singoli, perché sono più facili da leggere tutti d'un fiato. Per un'applicazione vera la documentazione e le skill ufficiali vanno oltre: consigliano di partire da un Flow: Il "copione" che orchestra passi, crew e agenti con uno stato condiviso, condizioni e diramazioni. La documentazione lo consiglia come struttura per le applicazioni vere. glossario, con crewai create flow, che vedrai nel capitolo 16.

Prova tu: dare gli strumenti al ricercatore del progetto

Nel progetto guida_arezzo, dai al ricercatore la ricerca su Google e la lettura delle pagine del capitolo 12. Che cosa devi cambiare, e in quali file?

Soluzione. Il YAML non cambia: gli strumenti sono oggetti Python, quindi si aggiungono in crew.py. In cima al file aggiungi l'import, e nel metodo ricercatore il parametro tools:

Python
# In cima a crew.py, insieme agli altri import:
from crewai_tools import ScrapeWebsiteTool, SerperDevTool

    # Dentro la classe GuidaArezzo, al posto del metodo ricercatore di prima:
    @agent
    def ricercatore(self) -> Agent:
        return Agent(
            config=self.agents_config['ricercatore'], # type: ignore[index]
            tools=[SerperDevTool(), ScrapeWebsiteTool()],
            verbose=True
        )

Poi aggiungi SERPER_API_KEY=... al file .env del progetto. Il pacchetto crewai_tools è già installato, perché il pyproject.toml generato dipende da crewai[tools]. Conviene anche chiedere le fonti nell'expected_output del task ricerca, in tasks.yaml.

In breve
  • crewai create crew nome crea oggi un progetto JSON (crew.jsonc e agents/*.jsonc) con una procedura guidata; --classic crea il progetto con YAML, crew.py e main.py.
  • YAML usa coppie chiave: valore e rientri fatti di spazi; > introduce un testo su più righe.
  • In crew.py, @CrewBase legge i YAML e ogni metodo @agent o @task deve avere lo stesso nome della sua voce.
  • crewai install esegue uv sync; crewai run avvia la crew dalla cartella principale, dove sta anche il .env.
  • Script per imparare e provare; progetto quando il crew cresce, si condivide o va pubblicato.

Nel prossimo capitolo dai alla squadra la capacità di ricordare e di consultare i tuoi documenti: memoria e knowledge.

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