Chiede la chiave OpenAI che non dovrebbe servire
The OPENAI_API_KEY environment variable is not set
Cosa succede davvero. CrewAI non preferisce OpenAI per capriccio: ogni punto del framework che ha bisogno di un modello e non ne trova uno dichiarato ripiega sul default, che è OpenAI. I punti ciechi sono quattro. Un agente senza parametro llm. Il manager del processo gerarchico, che richiede un manager_llm suo. Gli embedding della memoria, che con memory=True usano text-embedding-3-large di OpenAI se non indichi altro. Gli embedding di knowledge e degli strumenti RAG, che usano text-embedding-3-small anche quando tutti gli agenti girano su un altro fornitore. Configurare il modello sull'agente e dimenticare gli altri tre punti è l'errore più segnalato nelle issue del progetto.
Come si risolve. Dichiarando il modello in ogni punto in cui serve: llm su ogni agente, manager_llm sul crew se il processo è gerarchico, embedder sul crew se usi memoria, knowledge o strumenti RAG.
from crewai import Agent, Crew, Process, Task, LLM
locale = LLM(model="ollama/llama3.1", base_url="http://localhost:11434")
ricercatore = Agent(
role="Ricercatore",
goal="Raccogliere fonti su {tema}",
backstory="Cita solo quello che ha letto.",
llm=locale, # senza questo parte il default OpenAI
)
ricerca = Task(
description="Trova 5 fonti su {tema} e riassumile.",
expected_output="5 voci: fonte, data, sintesi in 2 frasi.",
agent=ricercatore,
)
crew = Crew(
agents=[ricercatore],
tasks=[ricerca],
process=Process.hierarchical,
manager_llm=locale, # obbligatorio con hierarchical
memory=True,
embedder={
"provider": "ollama",
"config": {
"model_name": "mxbai-embed-large",
"url": "http://localhost:11434/api/embeddings",
},
},
)errore
Anche gli embedding sono chiamate a un modello
Memoria e knowledge non usano il modello di chat: fanno chiamate di embedding separate, con un default OpenAI tutto loro. È per questo che un crew interamente su Ollama può ancora chiedere la chiave OpenAI. La guida a CrewAI con Ollama copre la configurazione completa in locale.
L'agente gira a vuoto
Maximum iterations reached. Requesting final answer.
Cosa succede davvero. Ogni task è un ciclo: il modello ragiona, chiama uno strumento, legge il risultato, decide se ha finito. max_iter è il tetto di giri per agente (il valore predefinito documentato è 20): raggiunto il tetto, CrewAI chiede al modello la migliore risposta possibile con quello che ha raccolto. Il messaggio quindi non è la malattia, è la febbre: l'agente non aveva un criterio per capire quando fermarsi. Nella maggior parte dei casi la causa è un expected_output vago, tipo "un report completo"; altre volte sono due agenti con allow_delegation=True che si rimbalzano il lavoro.
Come si risolve.
- Scrivi
expected_outputcome un criterio di accettazione: formato, numero di elementi, campi obbligatori. È la correzione che funziona più spesso. - Abbassa
max_itersui compiti semplici: 20 giri per un riassunto sono un sintomo, non una risorsa. - Lascia
allow_delegational suo defaultFalsedove la delega non serve: due agenti che possono delegarsi a vicenda sono un ciclo pronto a chiudersi su se stesso.
Come si previene. Esegui con verbose=True durante lo sviluppo e leggi cosa fa l'agente a ogni giro: i loop si vedono subito. Nota onesta: nelle issue del progetto sono segnalati casi in cui il ciclo non si ferma nemmeno al tetto; se ti capita con un tetto basso e un output preciso, è un bug da segnalare, non un tuo errore.
Errore 429: il fornitore ti sta frenando
RateLimitError: 429, limite di richieste superato
Cosa succede davvero. Il 429 non nasce dentro CrewAI: è il fornitore del modello che rifiuta le chiamate oltre la soglia del tuo piano. CrewAI c'entra però nel volume: un crew fa molte più chiamate di quante il codice lasci immaginare, e la soglia arriva prima di quanto pensi.
attenzione
Un crew fa più chiamate di quante ne vedi
Tre agenti in sequenza non fanno tre chiamate. Ogni agente può iterare fino a max_iter volte, la memoria aggiunge chiamate di embedding a ogni passaggio, e il processo gerarchico aggiunge le valutazioni del manager. Dieci chiamate attese diventano cinquanta senza che nulla sia rotto.
Come si risolve. Con max_rpm, che impone un ritmo massimo di richieste al minuto: si imposta sul singolo agente o sul crew intero, e il valore del crew prevale su quello degli agenti. Sugli errori transitori CrewAI ritenta da solo fino a max_retry_limit volte per agente (default 2). Se il 429 persiste anche a ritmi bassi, il problema è il piano del fornitore, non la configurazione.
from crewai import Agent, Crew, Task
analista = Agent(
role="Analista",
goal="Sintetizzare i dati di {fonte}",
backstory="Si ferma quando le evidenze bastano.",
llm="gpt-5.4-mini",
max_iter=8, # tetto ai giri per singolo task
max_rpm=10, # richieste al minuto dell'agente
)
sintesi = Task(
description="Sintetizza i dati di {fonte} in 5 punti.",
expected_output="5 punti, ognuno con dato e implicazione.",
agent=analista,
)
crew = Crew(
agents=[analista],
tasks=[sintesi],
max_rpm=20, # tetto globale: prevale sugli agenti
)Il modello sbaglia la chiamata allo strumento
Invalid Format: Missing 'Action:' after 'Thought'
Cosa succede davvero. Quando il modello non gestisce bene le chiamate a funzione native, CrewAI ripiega su un protocollo testuale: il modello deve scrivere Thought, Action e Action Input in un formato preciso che il framework interpreta. I modelli OpenAI lo rispettano quasi sempre; i modelli piccoli in locale e alcuni fornitori alternativi lo rompono, e ne escono errori come questo, oppure Received None or empty response from LLM call, oppure strumenti invocati con argomenti inventati.
Come si risolve.
- Assegna i modelli addestrati sul function calling agli agenti che usano strumenti, e riserva i modelli piccoli agli agenti che scrivono solo testo.
- Semplifica gli schemi degli strumenti: pochi argomenti, nomi chiari, descrizioni esplicite. Ogni argomento in più è un punto in cui il modello può sbagliare.
- Non forzare la traduzione di tutto: un prompt che impone di rispondere solo in italiano può portare il modello a tradurre anche le parole chiave
Thought,ActioneAction Input, che invece devono restare intatte. - Con Ollama, verifica che il modello scelto dichiari il supporto ai tool: non tutti lo hanno, e alcuni che lo dichiarano falliscono nelle segnalazioni degli utenti.
Il costo dell'esecuzione è fuori controllo
Un'esecuzione costa dieci volte quello che ti aspettavi
Cosa succede davvero. Il costo di un crew è il prodotto di tre fattori: numero di agenti, giri per agente, dimensione del modello. Ognuno sembra piccolo da solo, moltiplicati esplodono. Prima di ottimizzare, misura: l'oggetto restituito da kickoff() espone il conteggio dei token dell'intera esecuzione, e total_tokens in usage_metrics è la somma di prompt e completion, cioè quello che paghi.
esito = crew.kickoff(inputs={"fonte": "bandi regionali 2026"})
print(esito.token_usage) # token dell'intera esecuzione
print(crew.usage_metrics) # stesso conteggio, letto dal crewCome si risolve. Le leve reali, in ordine di impatto:
- Meno agenti. Due o tre ruoli netti battono una squadra di sei: ogni agente in più aggiunge passaggi di contesto e chiamate.
- Modello piccolo per i ruoli semplici. Il parametro
llmè per agente: il modello grande solo dove la qualità del ragionamento paga, quello economico per estrarre e riformattare. - Sequenziale invece di gerarchico. Il manager del processo gerarchico è un agente in più che chiama il modello a ogni assegnazione e verifica. Usalo solo se il coordinamento serve davvero.
- Tetti espliciti:
max_iterbasso, memoria spenta dove non aggiunge nulla.
Come si previene. Guarda token_usage a ogni modifica del crew, come faresti con un test: i regressi di costo si vedono lì prima che in fattura.
Superata la finestra di contesto
Context length exceeded
Cosa succede davvero. L'input accumulato (prompt, output dei task precedenti, risultati degli strumenti) ha superato la finestra di contesto del modello. Il comportamento lo governa il parametro respect_context_window dell'agente, che esiste e vale True di default: CrewAI riassume automaticamente il contenuto (il messaggio è Summarizing content to fit the model context window) e l'esecuzione continua. Con False l'esecuzione si ferma subito con l'invito a usare testi più piccoli o strumenti RAG.
Come si risolve. Il default va bene per la maggior parte dei casi, ma il riassunto automatico costa una chiamata in più e può perdere dettagli. Se i dettagli sono il punto (contratti, dati numerici), imposta False e spezza tu l'input, oppure passa i documenti lunghi come knowledge: gli agenti recuperano i passaggi rilevanti invece di caricare tutto nel prompt.
L'installazione fallisce
pip install crewai si ferma su requires-python o sulle dipendenze
Cosa succede davvero. Tre cause coprono quasi tutti i casi. Primo: Python 3.14, che il pacchetto esclude dichiarando >=3.10,<3.14, quindi il resolver rifiuta o ripiega su versioni antiche. Secondo: conflitti fra crewai e crewai-tools non allineati, o dipendenze che non compilano sulla tua versione di Python. Terzo: installazione nell'ambiente globale, dove i pacchetti già presenti vincolano il resolver.
Come si risolve. Interprete fra 3.10 e 3.13, ambiente dedicato al progetto, e il percorso raccomandato dalla documentazione, che usa uv e isola da solo le dipendenze. Se aggiorni, aggiorna crewai e crewai-tools insieme, mai uno solo dei due. Il tutorial completo parte esattamente da qui.
python3 --version # serve >= 3.10 e < 3.14
uv tool install crewai # il metodo raccomandato dalle docs
crewai create crew mio_progetto
cd mio_progetto && crewai runLa documentazione non corrisponde al codice
TypeError: got an unexpected keyword argument
Cosa succede davvero. È un problema noto e ricorrente del progetto: il ritmo dei rilasci è alto e la documentazione a volte descrive strumenti non ancora pubblicati, parametri rinominati o esempi che falliscono su un progetto appena creato. Le pagine descrivono l'ultima versione; il tuo ambiente ne ha un'altra. Quando un parametro copiato dalle docs produce questo errore, non sei tu.
Come si risolve. Fidati del tuo ambiente, non della pagina: la firma vera di una funzione la dice Python stesso, con inspect.
import inspect
from crewai import Agent, Crew
print(inspect.signature(Crew.kickoff)) # la firma vera, nel tuo ambiente
print(sorted(Agent.model_fields)) # i parametri reali di Agentnota
Le fonti da incrociare quando qualcosa non torna
Controlla la tua versione con crewai version, poi incrocia il changelog ufficiale e le note di rilascio su GitHub. Se il disallineamento resta, cerca nel forum ufficiale: spesso qualcuno ha già trovato il parametro nuovo.
Il risultato cambia a ogni esecuzione
Lo stesso crew, due output diversi
Cosa succede davvero. Non è un bug: è la natura degli agenti autonomi. In un crew la strategia la decide il modello, quindi due esecuzioni identiche possono seguire strade diverse, chiamare strumenti in ordine diverso e chiudersi con testi diversi. La variabilità del campionamento del modello si somma a quella delle decisioni.
Come si risolve. Si riduce, non si elimina: un expected_output rigido restringe la forma del risultato, un output strutturato la blocca, una temperatura bassa riduce la varianza del modello. Ma se il processo deve essere ripetibile e verificabile passo per passo, lo strumento giusto non è il crew: i Flows esistono esattamente per questo, con passi espliciti, stato ispezionabile ed eventi che collegano un passaggio al successivo.