Durante il mio breve soggiorno a Firenze, mentre mi trovavo agli Uffizi, mi sono reso conto che l’implementazione del sistema multi-agente (MAS) del con Crew.AI aveva alcuni limiti nella gestione dei percorsi espositivi e delle opere d’arte.
Dagli Uffizi ai dati: strutturare l’esperienza museale
If exhibitions are designed with
visitors in mind, the outcome could benefit
both visitors and museums
Stephen Bitgood, Engaging the Visitor
Il turista medio, me compreso, che ha solo tre giorni per scoprire Firenze, considera la Galleria degli Uffizi una tappa obbligatoria, nonostante il tempo limitato a disposizione, spesso non superiore a due ore. Come molti altri visitatori con un’agenda serrata, in queste poche ore si cerca di ammirare i capolavori di Botticelli, Leonardo da Vinci e Michelangelo.
Per il visitatore veloce l’agente intelligente potrebbe fornire indicazioni per la visita, guidando i visitatori attraverso le sale più significative del museo, con particolare attenzione alla celebre sala dedicata a Sandro Botticelli (1445-1510).
Nella sala A11-A12, due capolavori assoluti del Rinascimento italiano catturano l’essenza di un’epoca: “La Nascita di Venere” (1485), con la sua eterea dea che emerge dalle acque, e “La Primavera” (1480), raffinata allegoria che intreccia mitologia classica e filosofia neo-platonica.
Il palazzo stesso racchiude una storia affascinante: progettato dal genio di Giorgio Vasari (1511-1574) e edificato tra il 1560 e il 1580, si distingue per la sua caratteristica forma a “U”. Alla morte del Vasari (1574) i lavori proseguirono sotto la direzione di Alfonso Parigi (1535-1590) e Bernardo Buontalenti (1531-1608) cui spetta il completamento dell’edificio, raccordato alla Loggia dei Lanzi nel 1580.
Percorso espositivo
Questa peculiare struttura architettonica non è solo un elemento distintivo, ma si trasforma in un naturale percorso espositivo che accompagna i visitatori attraverso secoli di storia dell’arte. Come evidenziato dalla mappa ufficiale degli Uffizi realizzata dallo studio “Cantiere Creativo”, che riesce a far convivere visivamente tante informazioni disparate in uno spazio limitato senza per questo perdere in chiarezza ed efficacia (“Come abbiamo creato la mappa delle Gallerie degli Uffizi”).
La vera sfida sarebbe creare un’esperienza museale personalizzata per ogni visitatore degli Uffizi attraverso l’implementazione di un sistema multi-agente intelligente (MAS) in grado di comprendere le preferenze individuali e creare un percorso personalizzato tra le opere più significative. Un tale approccio potrebbe anche aiutare a mitigare la sensazione di “fatica museale” (Museum Fatigue), ottimizzando il tempo di visita e guidando i visitatori verso un’esperienza più mirata e soddisfacente, riducendo così il rischio di sovraccarico sensoriale e cognitivo.
Perché ci sentiamo stanchi?
Durante la mia visita agli Uffizi, mi sono spesso sentito sopraffatto dalla vastità del museo, smarrito tra le innumerevoli sale e la quantità impressionante di opere d’arte esposte. Come sottolinea lo psicologo Stephen Bitgood, esperto in visitor studies, l’esperienza museale richiede uno sforzo mentale prolungato, esponendo il visitatore a una serie di sfide intellettuali che possono risultare tanto stimolanti quanto faticose.
La “fatica museale” è uno stato psico-fisico di disagio che si manifesta frequentemente durante la visita a grandi musei. Questo fenomeno, descritto dalla letteratura come una combinazione di fattori fisici, cognitivi e ambientali, si traduce in una diminuzione dell’attenzione e dell’interesse del visitatore man mano che procede nel percorso espositivo. Probabilmente il primo studioso a occuparsene fu Benjamin Ives Gilman (1852-1933), museologo americano e curatore del Boston Museum of Fine Arts. Nel 1910, Gilman avviò i primi studi empirici sul “museum fatigue” (Scientific Monthly, 1916) analizzando le cause e gli effetti dell’affaticamento durante l’esperienza museale.
Galleria intelligente
Sfruttando la peculiare forma a U e con l’ausilio della mappa, l’agente intelligente potrebbe ottimizzare l’esperienza museale guidando i visitatori in un percorso logico e coerente. Ma quali tipologie di visitatori possiamo identificare?
Percorsi su misura
Dalla ricerca condotta da “Cantiere Creativo”” per la realizzazione della mappa delle Gallerie degli Uffizi è emersa non solo la necessità di uno studio approfondito dell’architettura e delle planimetrie del museo, ma anche l’opportunità di analizzare il profilo dei visitatori. Questa profilazione ha evidenziato come il percorso veloce (1-2 ore) sia preferito dai “time-constrained explorer”, visitatori in viaggio per diverse ragioni con tempo limitato, che desiderano concentrarsi sulle opere principali. Invece il percorso classico (più di 2 ore) è l’ideale per il “cultural explorer”, un visitatore che cerca un equilibrio tra approfondimento e tempo a disposizione, interessato a diverse epoche artistiche e aperto alla scoperta di artisti meno noti oltre ai grandi maestri. Infine, il percorso completo (più di 3 ore) è pensato per l’”art enthusiast”, un appassionato che vuole dedicare tempo significativo all’esplorazione sistematica della collezione, interessato ai dettagli e alle connessioni tra le opere, e desideroso di non tralasciare alcun aspetto della visita, inclusa la collezione di autoritratti d’artista.
[
{
"visitor_type": "time-constrained explorer",
"description": "Focalizzato sui capolavori principali perché ha il tempo limitato e punta all'essenziale. Cerca un'esperienza concisa ma significativa delle opere più importanti",
"characteristics": [
"Disponibilità di tempo: 1-2 ore",
"Preferenza per opere iconiche e universalmente riconosciute",
"Interesse per la contestualizzazione rapida delle opere principali",
"Apprezzamento per percorsi ottimizzati e ben strutturati",
"Focus sulla comprensione generale piuttosto che sui dettagli",
"Predilezione per opere con forte impatto visivo ed emotivo"
]
},
{
"visitor_type": "cultural explorer",
"description": "Interessato a diverse epoche artistiche, al contesto storico-culturale e aperto alla scoperta di artisti meno noti. Cerca un equilibrio tra approfondimento e gestione del tempo",
"characteristics": [
"Interesse per diverse epoche",
"Attenzione al contesto storico",
"Apertura a nuove scoperte",
"Ricerca di equilibrio tempo/approfondimento"
]
},
{
"visitor_type": "art enthusiast",
"description": "Appassionato che dedica tempo significativo per un'esperienza esaustiva. Interessato ai dettagli, alle connessioni tra opere e apprezza una visita sistematica e approfondita dell'intera collezione",
"characteristics": [
"Dedizione significativa di tempo",
"Interesse per i dettagli",
"Approccio sistematico",
"Ricerca di connessioni tra opere"
]
}
]
Questa profilazione dei visitatori permette all’agente intelligente di personalizzare l’esperienza museale in base alle specifiche esigenze temporali e agli interessi culturali di ogni visitatore, garantendo una fruizione ottimale delle opere esposte.
Leggere la mappa
Tramite l’architettura multi-agente (MAS) di Crew.AI, combinata con LLM Gemini, è possibile estrarre dalla mappa degli Uffizi i percorsi di visita, le opere più significative e la loro esatta collocazione all’interno della struttura museale.
import json
import re
from crewai import Agent, Task, Crew, LLM
from PyPDF2 import PdfReader
from typing import List, Dict, Callable
from crewai.tools import BaseTool
from pydantic import Field
from travel_preferences import TravelPreferences
api_key = "<API-KEY>"
llm = LLM(
model="gemini/gemini-1.5-pro",
temperature=0,
stream=False,
api_key=api_key
)
# Inizializzazione dell'analista
exhibition_guide_agent = ExhibitionGuideAgent(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", "musei"],
special_requirements=["vegetarian"],
visitor_type="cultural explorer"
)
# Analisi della guida
result = exhibition_guide_agent.run_agent(preferences,
"pdf/1733741605-brochure_uffizi_ita_web-mappa-uffizi-con-vasariano.pdf")
print(result)
Custom Tool
Iniziamo implementando un custom tool per Crew.AI che abilita la lettura del contenuto testuale da documenti PDF. Per creare un custom tool, è necessario implementare una classe che erediti da CrewAI BaseTool o utilizzare il decoratore tool.
La struttura base richiede la definizione di alcuni elementi: un name che identifica il tool, una description che spieghi all’agente cosa fa il tool, e un metodo run che contenga la logica effettiva.
from crewai.tools import BaseTool
from typing import Callable
from pydantic import Field
class PDFReaderTool(BaseTool):
name: str = "pdf_reader"
description: str = "utile per leggere un file PDF e restituirne il contenuto testuale. Input: il percorso del file pdf. Output: il contenuto testuale del PDF"
read_pdf_function: Callable = Field(description="funzione per leggere il pdf")
def _run(self, pdf_path: str) -> str:
try:
return self.read_pdf_function(pdf_path)
except Exception as e:
return f"Errore durante la lettura del PDF: {e}"
È importante fornire una descrizione chiara e dettagliata del tool poiché questa verrà utilizzata dall’agente AI per capire quando e come utilizzarlo. Per una migliore integrazione, è consigliabile anche aggiungere gestione degli errori e logging appropriati.
Takeaway
L’esperienza diretta presso le Gallerie degli Uffizi ha evidenziato come il sistema multi-agente (MAS) possa evolvere verso una comprensione più approfondita e articolata del museo, sfruttando strumenti già esistenti, come la mappa stessa del museo.
Il sistema potrebbe inoltre suggerire opzioni utili, come l’acquisto anticipato dei biglietti online e la scelta del miglior orario di visita per evitare le lunghe code, che solitamente si concentrano nelle prime ore del mattino o nel tardo pomeriggio.
L’integrazione di queste informazioni contestuali, combinata con la capacità di generare percorsi personalizzati basati sul profilo del visitatore, costituirebbe un elemento fondamentale per arricchire e ottimizzare l’esperienza di visita.
Codice completo
PDFReaderTool
class PDFReaderTool(BaseTool):
name: str = "pdf_reader"
description: str = "utile per leggere un file PDF e restituirne il contenuto testuale. Input: il percorso del file pdf. Output: il contenuto testuale del PDF"
read_pdf_function: Callable = Field(description="funzione per leggere il pdf")
def _run(self, pdf_path: str) -> str:
try:
return self.read_pdf_function(pdf_path)
except Exception as e:
return f"Errore durante la lettura del PDF: {e}"
ExhibithionGuideAgent
class ExhibitionGuideAgent:
"""
Agente specializzato nell'analisi dei percorsi museali
"""
def __init__(self, llm: LLM):
self.llm = llm
self.agent = Agent(
role='Guida percorsi museali',
goal='Analizzare percorsi museali ed identificare opere principali da un PDF.',
backstory="""Esperto in analisi di guide museali e percorsi artistici,
specializzato nell'identificazione di percorsi ottimali e opere significative.
Utilizza strumenti per leggere PDF e analizzarne il contenuto.""",
llm=self.llm,
tools=[self.create_pdf_tool()]
)
def read_pdf(self, pdf_path: str) -> str:
"""
Legge il testo dal PDF.
"""
try:
reader = PdfReader(pdf_path)
pdf_text = ""
for page in reader.pages:
pdf_text += page.extract_text() + "\n"
return pdf_text
except Exception as e:
print(f"Errore nella lettura del PDF: {str(e)}")
return ""
def create_pdf_tool(self) -> PDFReaderTool:
return PDFReaderTool(read_pdf_function=self.read_pdf)
def _get_visitor_type(self, visitor_type) -> dict:
visitors = [
{
"visitor_type": "time-constrained explorer",
"description": "Focalizzato sui capolavori principali perché ha il tempo limitato e punta all'essenziale. Cerca un'esperienza concisa ma significativa delle opere più importanti",
"characteristics": [
"Disponibilità di tempo: 1-2 ore",
"Preferenza per opere iconiche e universalmente riconosciute",
"Interesse per la contestualizzazione rapida delle opere principali",
"Apprezzamento per percorsi ottimizzati e ben strutturati",
"Focus sulla comprensione generale piuttosto che sui dettagli",
"Predilezione per opere con forte impatto visivo ed emotivo"
]
},
{
"visitor_type": "cultural explorer",
"description": "Interessato a diverse epoche artistiche, al contesto storico-culturale e aperto alla scoperta di artisti meno noti. Cerca un equilibrio tra approfondimento e gestione del tempo",
"characteristics": [
"Interesse per diverse epoche",
"Attenzione al contesto storico",
"Apertura a nuove scoperte",
"Ricerca di equilibrio tempo/approfondimento"
]
},
{
"visitor_type": "art enthusiast",
"description": "Appassionato che dedica tempo significativo per un'esperienza esaustiva. Interessato ai dettagli, alle connessioni tra opere e apprezza una visita sistematica e approfondita dell'intera collezione",
"characteristics": [
"Dedizione significativa di tempo",
"Interesse per i dettagli",
"Approccio sistematico",
"Ricerca di connessioni tra opere"
]
}
]
return next((visitor for visitor in visitors if visitor["visitor_type"].lower() == visitor_type.lower()), None)
def create_task(self, preferences, pdf_path: str) -> Task:
"""
Crea un task per l'analisi dei percorsi e opere
"""
visitor = self._get_visitor_type(preferences.visitor_type)
characteristics = " - ".join(visitor["characteristics"])
description = f"""
Utilizzando lo strumento `pdf_reader`, leggi il PDF nel percorso '{pdf_path}' e fornisci un'analisi dettagliata seguendo queste linee guida:
CONTESTO:
- Tipo di visitatore: {visitor['visitor_type'].lower()}
- Profilo visitatore: {visitor['description']}
- Caratteristiche chiave: {characteristics}
ANALISI RICHIESTE:
1. PERCORSI DI VISITA UFFICIALI:
- Estrai i percorsi principali
- Per ogni percorso cataloga:
* Nome esatto
* Durata specificata
* Piano/i coinvolti
* Uscita consigliata
* Eventuali limitazioni/note (es. accesso guardaroba)
2. CATALOGAZIONE SALE E OPERE:
- Per ogni piano (Terra, Primo, Secondo):
* Lista completa delle sale con codici (es. A1, B1, etc.)
* Raggruppamento per periodo storico/collezione
* Collegamenti tra sale (scaloni, corridoi)
3. SERVIZI E FACILITIES:
- Posizione e tipo di:
* Ingressi/Uscite (inclusi accessi accessibili)
* Servizi (guardaroba, bagni, etc.)
* Punti vendita (biglietteria, bookshop, etc.)
4. OPERE PRINCIPALI:
- Identifica tutte le opere principali
* Nome dell'opera/artista
* Ubicazione precisa (sala)
* Periodo storico
* Eventuali note speciali
5. Raccomandazioni Personalizzate
- Seleziona il percorso ufficiale più adatto al profilo del visitatore
- Crea un percorso personalizzato suggerendo le opere che meglio si allineano al profilo del visitatore
- Spiegazione delle opere scelte per il percorso personalizzato
VINCOLI:
- Mantieni la coerenza con il profilo del visitatore fornito
- Assicurati che tutte le informazioni di ubicazione siano precise
- Mantenere i codici originali delle sale (A1, B1, etc.)
- Preservare le relazioni spaziali tra sale e percorsi
- Includere note su accessibilità dove presenti
- Rispettare la nomenclatura ufficiale del museo
- Includi solo informazioni esplicitamente presenti nel PDF
FORMATO OUTPUT:
Restituisci un oggetto JSON con la seguente struttura:
{{
"numero_percorsi": 0,
"percorsi_ufficiali": [
{{
"nome": "",
"durata": "",
"note": ""
}}
],
"opere_principali": [
{{
"opera": "",
"piano": "",
"sala": "",
"periodo": ""
}}
],
"percorso_ufficiale_consigliato":{{
"nome": "",
"durata": "",
"note": ""
}},
"spiegazione_percorso_personalizzato": "",
"percorso_personalizzato": [
{{
"opera": "",
"piano": "",
"sala": "",
"periodo": ""
}}
],
"servizi": {{
"ingressi": "",
"uscite": "",
"facilities": ""
}}
}}
"""
expected_output = """
JSON strutturato con il numero totale dei percorsi, i dettagli dei percorsi più brevi
o veloci e la posizione precisa delle opere principali.
"""
return Task(
description=description,
expected_output=expected_output,
agent=self.agent
)
def _process_result(self, result):
result = re.sub(r'```(?:json)?\n?(.*?)\n?```', r'\1', result, flags=re.DOTALL)
result = re.sub(r'\\n', '', result) # rimuovi i caratteri di fine riga
result = re.sub(r'\s+', ' ', result) # rimpiazza le sequenze di spazi con un singolo spazio
return result
def run_agent(self, preferences: TravelPreferences, pdf_path: str) -> Dict:
"""
Esegue l'analisi completa della guida
"""
task = self.create_task(preferences, pdf_path)
crew = Crew(
agents=[self.agent],
tasks=[task],
verbose=False
)
crew_output = crew.kickoff()
result = str(crew_output)
# Pulisci l'output per evitare errori JSON
result = self._process_result(result)
return json.loads(result)
How to test
api_key = "<API-KEY>"
llm = LLM(
model="gemini/gemini-1.5-pro",
temperature=0,
stream=False,
api_key=api_key
)
# Inizializzazione dell'analista
exhibition_guide_agent = ExhibitionGuideAgent(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", "musei"],
special_requirements=["vegetarian"],
visitor_type="cultural explorer"
)
# Analisi della guida
result = exhibition_guide_agent.run_agent(preferences,
"pdf/1733741605-brochure_uffizi_ita_web-mappa-uffizi-con-vasariano.pdf")