Il modo in cui esploriamo il mondo sta cambiando rapidamente grazie all’AI. Dopo aver sperimentato con successo ChatGPT per trovare un appartamento in Colombia (“Medellìn, come ChatGPT e l’agente immobiliare mi hanno aiutato a trovare casa”, 2023), mi sono chiesto: come l’AI potrebbe trasformare la nostra esperienza di viaggio? Con questa curiosità, ho deciso di spingermi oltre, creando un multi-agent system (MAS) con Crew.AI per guidare la mia esplorazione dell’Italia. Non si tratta solo di ottenere raccomandazioni turistiche, ma di ripensare completamente il concetto di “essere turista” in questo momento storico in cui l’AI sta appena iniziando a mostrarci le sue potenzialità.
L’inizio dell’Avventura
Come può l’AI aiutarmi a vivere un’esperienza di viaggio in Italia più autentica e su misura? L’idea è di creare una serie di agenti intelligenti che si adattino e si specializzino lungo il mio percorso. Quale framework è il più indicato per questo scopo? Di recente ho scoperto Crew.ai, un framework sviluppato dal computer scientist brasiliano João Moura.
Agenti intelligenti
Il concetto di “intelligenza” e la sua applicazione agli “agenti intelligenti” ha suscitato l’interesse di numerose discipline, dalla filosofia alle scienze cognitive, producendo diverse interpretazioni.
Nel tentativo di definire questo campo, i computer scientists Nicholas Robert Jennings e Michael Wooldridge hanno caratterizzato l’agente come un sistema dotato di tre capacità fondamentali: rispondere ai cambiamenti ambientali (reattività), prendere decisioni autonomamente (autonomia) e interagire con altri agenti (capacità sociali). Questa concezione individualista dell’intelligenza si è evoluta negli anni ’90 verso il paradigma d’intelligenza distribuita (DAI, Distributed Artificial Intelligence), teorizzato da Gerhard Weiss. Secondo questa visione, l’intelligenza emergerebbe in sistemi composti da agenti autonomi che, pur non possedendo singolarmente capacità cognitive, raggiungono comportamenti intelligenti attraverso meccanismi di interazione e cooperazione.
Approccio concettuale
Framework come Crew.ai, ad esempio, si basano proprio su questo approccio concettuale, rendendolo particolarmente adatto al mio progetto. Crew.ai permette di creare agenti che si organizzano come membri di una “crew” dove ciascuno possiede competenze e responsabilità specifiche. A differenza di LangGraph, che si concentra sulla definizione di flussi di lavoro sequenziali attraverso grafi di stati, CrewAI pone l’accento sull’interazione e cooperazione tra agenti.
La metafora della “crew” non solo semplifica la gestione di interazioni complesse, ma rispecchia perfettamente le diverse sfaccettature di un viaggio in Italia. Ho così iniziato ad immaginare e a progettare il mio team di agenti: uno specialista in storia dell’arte per guidarmi attraverso musei e monumenti, un esperto di gastronomia per scoprire le autentiche tradizioni culinarie, un pianificatore per ottimizzare gli itinerari e un esperto di logistica per gestire gli aspetti pratici.
La vera potenzialità di Crew.AI sta nella sua architettura flessibile: posso aggiungere nuovi agenti durante il viaggio e, soprattutto, farli comunicare tra loro, creando un sistema che diventa sempre più raffinato e personalizzato con il tempo.
Agent: Esperto locale
Per comprendere meglio come funziona Crew.ai, iniziamo creando un’agente chiamato “Esperto Locale” il cui task sia fornirci in base alle nostre preferenze di viaggio consigli su ristoranti, esperienze locali, eventi e suggerimenti pratici per una determinata destinazione.
Preferenze di viaggio
Iniziamo scrivendo le nostre preferenze di viaggio utilizzando il decoratore @dataclass, in questo modo il compilatore genererà automaticamente i metodi standard come __init__, __repr__ e __eq__, riducendo la verbosità del codice e migliorandone la leggibilità.
from dataclasses import dataclass
from typing import List, Optional
from datetime import datetime
@dataclass
class TravelPreferences:
"""
Classe per le preferenze di viaggio
"""
destination: str
departure_city: str
start_date: datetime
end_date: datetime
budget: str
travelers: int
interests: List[str]
special_requirements: Optional[List[str]] = None
Agent attributes
Iniziamo definendo nel costruttore della classe la “personalità” del nostro agente dandogli degli attributi come un obiettivo chiaro e un background specifico. Questo aiuterà LLM a mantenere una prospettiva coerente nelle sue raccomandazioni.
def __init__(self, llm):
self.agent = Agent(
role='Esperto Locale',
goal='Fornire suggerimenti autentici e locali',
backstory="""Esperto delle tradizioni e cultura locale, specializzato
in esperienze autentiche e consigli pratici per vivere come un locale.""",
llm=llm
)
Tasks
In Crew.ai, un task rappresenta un’unità di lavoro specifica che viene assegnata a un agente AI. Ogni task ha come attributi una descrizione chiara, un output atteso e un agente responsabile della sua esecuzione.
def create_task(self, preferences: TravelPreferences) -> Task:
description = f"""Fornisci consigli locali per {preferences.destination} e per gli eventi che siano fattibili dal {preferences.start_date} al {preferences.end_date}.
Fornisci i risultati in formato JSON:
{{
"restaurants": [
{{
"name": "nome ristorante",
"cuisine": "tipo cucina",
"price_range": "€/€€/€€€",
"specialty": "piatto consigliato",
"location": "zona/indirizzo",
"booking_required": true/false
}}
],
"local_experiences": [
{{
"name": "nome esperienza",
"description": "descrizione",
"duration": "durata",
"best_time": "momento migliore",
"cost": "costo stimato",
"booking_info": "come prenotare"
}}
],
"events": [
{{
"name": "nome evento",
"date": "data",
"location": "luogo",
"description": "descrizione",
"ticket_info": "info biglietti"
}}
],
"local_tips": ["lista consigli pratici"]
}}"""
expected_output = """JSON strutturato con ristoranti consigliati,
esperienze locali, eventi e consigli pratici."""
return Task(
description=description,
expected_output=expected_output,
agent=self.agent
)
Nel codice, il metodo create_task definisce un task specifico. La descrizione del task indica all’agente di fornire consigli locali in formato JSON, includendo ristoranti, esperienze, eventi e suggerimenti pratici. L’output atteso è un JSON strutturato che rispetti questo formato.
Firenze
Traveling is an innate human need
Brian Chesky
La possibilità di creare agenti specializzati, che si adattano alle nostre preferenze e si evolvono insieme al nostro viaggio, apre nuove prospettive per un’esplorazione più autentica e personalizzata del mondo. Firenze, con la sua ricchezza di storia, arte e tradizioni, è il luogo ideale per sperimentare questo nuovo approccio al viaggio.
CrewAI offre la possibilità di connettersi a diversi provider di LLM, inclusi i modelli di OpenAI e quelli locali tramite Ollama, garantendo così la massima flessibilità. Per l’agente “Esperto locale” ho optato come provider per Groq per la sua velocità di inferenza e la sua integrazione con Crew.ai, invece come LLM ho scelto llama-3.3-70b che si distingue per le sue capacità, paragonabili ai modelli di Google e OpenAI nei benchmark standard (come MMLU).
Airbnb, dove alloggiare?
Abituato a scrappare Airbnb per cercare un’appartamento (“Barcellona, dove alloggiare? Airbnb scraper”, 2021), questa volta ho semplicemente deciso di farlo normalmente. Ho prenotato un Airbnb dal 26 al 28 dicembre 2024 (start_date, end_date) per due persone (travelers), usando queste date per le mie preferenze di viaggio a Firenze.
from local_expert_agent import LocalExpertAgent
from travel_preferences import TravelPreferences
from datetime import datetime
from crewai import LLM
provider = "groq"
llm = LLM(
model=f"{provider}/llama-3.3-70b-versatile",
temperature=0,
max_tokens=1024,
stream=False,
api_key=""
)
le = LocalExpertAgent(llm)
preferences = TravelPreferences(
destination="Firenze, Italia",
departure_city="Medellin, Colombia",
start_date=datetime(2024, 12, 26),
end_date=datetime(2024, 12, 28),
budget="medium",
travelers=2,
interests=["cultura", "gastronomia", "storia"],
special_requirements=["vegetarian"]
)
r = le.run_agent(preferences)
print(r)
Raccomandazioni di viaggio
La combinazione di velocità e affidabilità rende Groq particolarmente adatto per un sistema di assistenza turistica che genera risposte strutturate in formato JSON. Questo formato permette una perfetta integrazione con chatbot e task manager come Todoist, consentendo di trasformare automaticamente le raccomandazioni di viaggio in attività. Per esempio, un itinerario turistico può essere convertito istantaneamente in una serie di task su Todoist, completi di date, orari, luoghi e note.
La struttura JSON facilita anche l’estrazione di informazioni chiave come coordinate GPS, orari di apertura e prezzi, rendendo l’esperienza di pianificazione più efficiente e automatizzata.
{
"restaurants":[
{
"name":"La Giostra",
"cuisine":"Toscana",
"price_range":"€€€",
"specialty":"Bistecca alla fiorentina",
"location":"Borgo Pinti, 12r, 50121 Firenze FI",
"booking_required":true
},
{
"name":"Trattoria Sostanza",
"cuisine":"Toscana",
"price_range":"€€",
"specialty":"Ribollita",
"location":"Via del Porcellana, 25r, 50123 Firenze FI",
"booking_required":true
},
{
"name":"Il Latini",
"cuisine":"Toscana",
"price_range":"€€",
"specialty":"Cantucci con vin santo",
"location":"Via dei Palchetti, 6r, 50123 Firenze FI",
"booking_required":true
}
],
"local_experiences":[
{
"name":"Visita al Duomo",
"description":"Esplora la cattedrale di Santa Maria del Fiore e il battistero di San Giovanni",
"duration":"2-3 ore",
"best_time":"mattina",
"cost":"€15-20",
"booking_info":"Prenotazione consigliata sul sito ufficiale"
},
{
"name":"Passeggiata lungo l'Arno",
"description":"Goditi la vista del fiume Arno e dei ponti storici di Firenze",
"duration":"1-2 ore",
"best_time":"tramonto",
"cost":"gratuito",
"booking_info":"nessuna prenotazione necessaria"
},
{
"name":"Visita agli Uffizi",
"description":"Esplora la galleria degli Uffizi e ammira le opere d'arte del Rinascimento",
"duration":"3-4 ore",
"best_time":"mattina",
"cost":"€20-25",
"booking_info":"Prenotazione consigliata sul sito ufficiale"
}
],
"events":[
{
"name":"Concerto di Natale",
"date":"2024-12-26",
"location":"Teatro della Pergola",
"description":"Concerto di musica classica per celebrare il Natale",
"ticket_info":"Biglietti disponibili sul sito ufficiale del teatro"
},
{
"name":"Mercatino di Natale",
"date":"2024-12-27-28",
"location":"Piazza Santa Croce",
"description":"Mercatino di Natale con prodotti locali e artigianali",
"ticket_info":"ingresso gratuito"
}
],
"local_tips":[
"Acquista un biglietto cumulativo per visitare i musei e le attrazioni turistiche",
"Utilizza i mezzi pubblici o cammina per spostarti in città",
"Assaggia i prodotti locali e i vini toscani",
"Visita i quartieri storici e scopri i negozi e le botteghe artigianali",
"Non dimenticare di provare la cucina toscana e i dolci locali"
]
}
Itinerario personalizzato
Le raccomandazioni generate dall’agente Esperto Locale per Firenze dimostrano una conoscenza della città. Per la ristorazione, l’agente ha identificato alcuni dei luoghi più autentici della tradizione fiorentina: La Giostra, rinomata per la sua bistecca alla fiorentina, la storica Trattoria Sostanza con la sua ribollita, e Il Latini, famoso per cantucci e vin santo.
Le esperienze suggerite sembrano bilanciare le attrazioni culturali imperdibili, come la visita agli Uffizi, con momenti più rilassanti come una passeggiata al tramonto lungo l’Arno.
L’agente non ha tralasciato gli eventi stagionali che caratterizzano la vita della città come il suggestivo Mercato di Natale in Santa Croce. Ed infine i consigli pratici vanno dalla raccomandazione di acquistare un biglietto cumulativo per i musei fino all’invito a esplorare il Mercato Centrale, dimostrando una comprensione delle esigenze di un turista.
Viaggio interattivo
Grazie al formato strutturato JSON delle raccomandazioni, possiamo con uno script Python importare i suggerimenti in Todoist, creando un progetto “Guida Firenze” ben organizzato. Possiamo strutturare il progetto in quattro sezioni distinte: “Ristoranti”, “Esperienze Locali”, “Eventi” e “Consigli Utili”.
Ogni ristorante viene importato come task con un’emoji “🍽️” e una descrizione dettagliata che include cucina, fascia di prezzo, specialità e informazioni sulla prenotazione. Le esperienze locali sono contrassegnate con “🎭” e includono durata, momento ideale e costi. Gli eventi cittadini sono marcati con “📅” e i consigli pratici con “💡”.
Questa organizzazione sistematica ha trasformato le raccomandazioni dell’agente in un piano di viaggio interattivo, permettendomi di gestire facilmente prenotazioni, tempistiche e promemoria attraverso l’app Todoist.
Codice completo
import json
from crewai import Agent, Task, Crew
from travel_preferences import TravelPreferences
class LocalExpertAgent(object):
"""
Esperto in consigli ed esperienze locali
"""
def __init__(self, llm):
self.agent = Agent(
role='Esperto Locale',
goal='Fornire suggerimenti autentici e locali',
backstory="""Esperto delle tradizioni e cultura locale, specializzato
in esperienze autentiche e consigli pratici per vivere come un locale.""",
llm=llm
)
def create_task(self, preferences: TravelPreferences) -> Task:
description = f"""Fornisci consigli locali per {preferences.destination} in formato JSON:
{{
"restaurants": [
{{
"name": "nome ristorante",
"cuisine": "tipo cucina",
"price_range": "€/€€/€€€",
"specialty": "piatto consigliato",
"location": "zona/indirizzo",
"booking_required": true/false
}}
],
"local_experiences": [
{{
"name": "nome esperienza",
"description": "descrizione",
"duration": "durata",
"best_time": "momento migliore",
"cost": "costo stimato",
"booking_info": "come prenotare"
}}
],
"events": [
{{
"name": "nome evento",
"date": "data",
"location": "luogo",
"description": "descrizione",
"ticket_info": "info biglietti"
}}
],
"local_tips": ["lista consigli pratici"]
}}"""
expected_output = """JSON strutturato con ristoranti consigliati,
esperienze locali, eventi e consigli pratici."""
return Task(
description=description,
expected_output=expected_output,
agent=self.agent
)
def run_agent(self, preferences: TravelPreferences):
"""
Esegue il task dell'esperto locale e valida l'output
"""
task = self.create_task(preferences)
# Crea un crew solo per questo esperto
crew = Crew(
agents=[self.agent],
tasks=[task],
verbose=False
)
# Esegue il task e ottiene il risultato
crew_output = crew.kickoff()
result = str(crew_output)
data = json.loads(result)
return data