Creazione di agenti stateful e personalizzati con ADK

1. Introduzione

titolo

Il problema del "pesce rosso"

Immagina di assumere un'agenzia viaggi per pianificare la tua vacanza da sogno a Tokyo. Utilizza l'agente di sessione per vedere "The Goldfish Problem" in azione.

Entri nel suo ufficio e dici:

"Ciao! Voglio pianificare un viaggio di 2 giorni a Tokyo. Mi interessano i siti storici e il sushi."

L'agente risponde con entusiasmo:

"Great! Ho programmato una visita al Palazzo Imperiale e una cena a base di sushi da Sukiyabashi Jiro."

Sorridi e dici:

"Sembra perfetto! Puoi inviarmi l'itinerario?"

L'agente ti guarda con aria assente e ti chiede:

"Ciao! Come posso aiutarti a pianificare un viaggio oggi?"

Questo è il "problema del pesce rosso". Senza memoria, ogni interazione è una tabula rasa. L'intelligenza c'è: l'agente sa come pianificare i viaggi, ma manca la continuità. Per essere veramente utile, un agente AI deve ricordare.

La tua missione di oggi

In questo workshop, risolverai il problema del pesce rosso creando un'agenzia viaggi che ricorda, impara e si adatta. Passerai attraverso 6 livelli di memoria dell'agente, creando un sistema che si comporta meno come un chatbot e più come un assistente personale dedicato.

Livello

Concetto

Il "superpotere"

Livello 1

Session & State

Sostenere una conversazione senza dimenticare

Livello 2

Stato multi-agente

Condivisione di note tra i membri del team

Livello 3

Persistenza

Ricordare le tue preferenze anche dopo il riavvio del sistema

Livello 4

Callback

Aggiornamento della memoria in modo completamente autonomo

Livello 5

Strumenti personalizzati

Lettura e scrittura di profili utente strutturati

Livello 6

Memoria multimodale

"Vedere" e ricordare foto e video

Lo stack di memoria ADK

Prima di scrivere il codice, cerchiamo di capire gli strumenti che stiamo utilizzando. Google Agent Development Kit (ADK) fornisce un modo strutturato per gestire la memoria:

  1. Sessione: il contenitore di una conversazione. Contiene la cronologia di ciò che è stato detto.
  2. Stato: un "blocco note" coppia chiave-valore collegato alla sessione. Gli agenti utilizzano questa memoria per archiviare informazioni specifiche (ad es. destination="Tokyo").
  3. MemoryService: archiviazione a lungo termine. È qui che conserviamo i dati in modo permanente, ad esempio le preferenze utente o i documenti analizzati.

2. Configura

Per alimentare i nostri agenti AI, abbiamo bisogno di due cose: un progetto Google Cloud che fornisca le basi.

Parte 1: attiva l'account di fatturazione

Per eseguire questo codelab, devi disporre di un account di fatturazione con un po' di credito. Per iniziare, utilizza i crediti del banner nella parte superiore di questo codelab. Se hai già un collegamento a un account di fatturazione, puoi saltare questo passaggio.

Parte 2: Ambiente aperto

  1. 👉 Fai clic su questo link per andare direttamente all'editor di Cloud Shell
  2. 👉 Se ti viene richiesto di concedere l'autorizzazione in qualsiasi momento oggi, fai clic su Autorizza per continuare. Fai clic per autorizzare Cloud Shell
  3. 👉 Se il terminale non viene visualizzato nella parte inferiore dello schermo, aprilo:
    • Fai clic su Visualizza.
    • Fai clic su TerminaleApri un nuovo terminale nell'editor di Cloud Shell
  4. 👉💻 Nel terminale, verifica di aver già eseguito l'autenticazione e che il progetto sia impostato sul tuo ID progetto utilizzando il seguente comando:
    gcloud auth list
    
  5. 👉💻 Clona il progetto bootstrap da GitHub:
    git clone https://github.com/cuppibla/memory_agent_starter
    
    
  6. 👉💻 Esegui lo script di configurazione dalla directory del progetto.
    cd ~/memory_agent_starter
    ./init.sh
    
    Lo script gestirà automaticamente il resto della procedura di configurazione.
  7. 👉💻 Imposta l'ID progetto necessario:
    gcloud config set project $(cat ~/project_id.txt) --quiet
    

Parte 3: configura l'autorizzazione

  1. 👉💻 Abilita le API richieste utilizzando il seguente comando. L'operazione potrebbe richiedere alcuni minuti.
    gcloud services enable \
        cloudresourcemanager.googleapis.com \
        servicenetworking.googleapis.com \
        run.googleapis.com \
        aiplatform.googleapis.com \
        compute.googleapis.com
    
  2. 👉💻 Concedi le autorizzazioni necessarie eseguendo i seguenti comandi nel terminale:
    . ~/memory_agent_starter/set_env.sh
    

Tieni presente che viene creato un file .env. che mostra le informazioni sul progetto.

3. Le fondamenta: sessione e stato

stateful

Il concetto: il contesto è fondamentale

La forma più elementare di memoria è la memoria della sessione. In questo modo, un agente può sapere che "lo" nella frase "Voglio comprarlo" si riferisce alla scarpa di cui parlavi 10 secondi fa.

In ADK, gestiamo questo aspetto con l'oggetto Session.

  • Approccio stateless: creazione di una nuova sessione per ogni messaggio.
  • Approccio stateful: crea una sessione e riutilizzala per l'intera conversazione.

Passaggio 1: esamina l'agente

👉💻 Nel terminale di Cloud Shell, apri il file nell'editor di Cloud Shell eseguendo:

cloudshell edit ~/memory_agent_starter/01_session_agent/agent.py

Apri ~/memory_agent_starter/01_session_agent/agent.py.

👉 Individua il commento # TODO: Create a root agent all'interno della funzione agent.py.

Sostituisci l'intera riga con il seguente codice:

root_agent = LlmAgent(
    name="multi_day_trip_agent",
    model="gemini-2.5-flash",
    description="Agent that progressively plans a multi-day trip, remembering previous days and adapting to user feedback.",
    instruction="""
    You are the "Adaptive Trip Planner" 🗺️ - an AI assistant that builds multi-day travel itineraries step-by-step.

    Your Defining Feature:
    You have short-term memory. You MUST refer back to our conversation to understand the trip's context, what has already been planned, and the user's preferences. If the user asks for a change, you must adapt the plan while keeping the unchanged parts consistent.

    Your Mission:
    1.  **Initiate**: Start by asking for the destination, trip duration, and interests.
    2.  **Plan Progressively**: Plan ONLY ONE DAY at a time. After presenting a plan, ask for confirmation.
    3.  **Handle Feedback**: If a user dislikes a suggestion (e.g., "I don't like museums"), acknowledge their feedback, and provide a *new, alternative* suggestion for that time slot that still fits the overall theme.
    4.  **Maintain Context**: For each new day, ensure the activities are unique and build logically on the previous days. Do not suggest the same things repeatedly.
    5.  **Final Output**: Return each day's itinerary in MARKDOWN format.
    """,
    tools=[google_search]
)

L'istruzione indica all'LLM di ricordare, ma il codice deve fornire la funzionalità per ricordare.

Passaggio 2: i due scenari

Apri ~/memory_agent_starter/01_session_agent/main.py.

👉 Nel terminale di Cloud Shell, apri il file nell'editor di Cloud Shell eseguendo:

cloudshell edit ~/memory_agent_starter/01_session_agent/main.py

Apri ~/memory_agent_starter/01_session_agent/main.py, individua il commento # TODO: Create a runner with in memorysession service all'interno della funzione main.py.

Sostituisci l'intera riga con il seguente codice:

    runner = Runner(
        agent=agent,
        session_service=session_service,
        app_name=agent.name
    )

👉 Individua il commento # TODO: create a different session to test all'interno della funzione main.py.

Sostituisci l'intera riga con il seguente codice:

    tokyo_session_2 = await session_service.create_session(
        app_name=multi_day_agent.name,
        user_id=user_id
    )

Provalo

Abbiamo due funzioni che mostrano la differenza tra la memoria "Goldfish" ed "Elephant".

Scenario 1: stateful (sessione condivisa)

async def run_trip_same_session_scenario(session_service, user_id):
    # 1. Create ONE session
    trip_session = await session_service.create_session(...)

    # 2. Turn 1
    await run_agent_query(..., trip_session, ...)

    # 3. Turn 2 - REUSING the same session!
    # The agent can "see" Turn 1 because it's in the session history.
    await run_agent_query(..., trip_session, ...)

Scenario 2: stateless (nuova sessione ogni volta)

async def run_trip_different_session_scenario(session_service, user_id):
    # Turn 1
    tokyo_session = await session_service.create_session(...)
    await run_agent_query(..., tokyo_session, ...)

    # Turn 2 - Creating a FREASH session
    # The agent has NO IDEA what happened in Turn 1.
    tokyo_session_2 = await session_service.create_session(...)
    await run_agent_query(..., tokyo_session_2, ...)

Passaggio 3: esegui l'agente

Vediamo la differenza in azione. Esegui lo script:

👉💻 Nella riga di comando, esegui il comando seguente:

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/01_session_agent/main.py

Osserva lo scenario 1: l'agente ricorda le tue preferenze dal primo messaggio e adatta il piano nel secondo messaggio.

Osserva lo scenario 2: nel secondo turno ("ti ricordi cosa mi è piaciuto del cibo?"), l'agente fallisce completamente perché si tratta di una nuova sessione. In pratica, dice: "Non so di cosa stai parlando".

Concetto chiave

Regola n. 1 della memoria: riutilizza sempre session.id per mantenere il contesto della conversazione. L'oggetto Session è il buffer di memoria a breve termine dell'agente.

4. Il team: stato multi-agente

cibo

Il concetto: "Il gioco del telefono"

Quando più agenti lavorano insieme, sono come colleghi che si passano una cartella di file. Se un agente scrive una nota nella cartella, l'agente successivo dovrebbe essere in grado di leggerla.

In ADK, questa "cartella" è lo stato.

  • State è un dizionario ({"key": "value"}) che si trova all'interno della sessione.
  • Qualsiasi agente nella sessione può leggere o scrivere nella sessione.

Passaggio 1: esamina il workflow

👉💻 Nel terminale di Cloud Shell, apri il file nell'editor di Cloud Shell eseguendo:

cloudshell edit ~/memory_agent_starter/02_multi_agent/agent.py

👉Nel file ~/memory_agent_starter/02_multi_agent/agent.py, individua il commento # TODO: foodie agent.

Sostituisci l'intera riga con il seguente codice:

foodie_agent = LlmAgent(
    name="foodie_agent",
    model="gemini-2.5-flash",
    tools=[google_search],
    instruction="""You are an expert food critic. Your goal is to find the best restaurant based on a user's request.

    When you recommend a place, you must output *only* the name of the establishment and nothing else.
    For example, if the best sushi is at 'Jin Sho', you should output only: Jin Sho
    """,
    output_key="destination"  # ADK will save the agent's final response to state['destination']
)

👉 Individua il commento # TODO: transportation agent all'interno della funzione agent.py.

Sostituisci l'intera riga con il seguente codice:

transportation_agent = LlmAgent(
    name="transportation_agent",
    model="gemini-2.5-flash",
    tools=[google_search],
    instruction="""You are a navigation assistant. Given a destination, provide clear directions.
    The user wants to go to: {destination}.

    Analyze the user's full original query to find their starting point.
    Then, provide clear directions from that starting point to {destination}.
    """,
)

👉 Individua il commento # TODO: root_agent all'interno della funzione agent.py.

Sostituisci l'intera riga con il seguente codice:

root_agent = SequentialAgent(
    name="find_and_navigate_agent",
    sub_agents=[foodie_agent, transportation_agent],
    description="A workflow that first finds a location and then provides directions to it."
)

Ora abbiamo due agenti che lavorano in sequenza:

  1. Agente Foodie: trova un ristorante.
  2. Agente di trasporto: fornisce le indicazioni stradali per raggiungere il ristorante.

Il passaggio di testimone magico: nota come foodie_agent passa il testimone a transportation_agent.

foodie_agent = LlmAgent(
    # ...
    # CRITICAL: This tells ADK to save the agent's output to state['destination']
    output_key="destination"
)

transportation_agent = LlmAgent(
    # ...
    # CRITICAL: This injects state['destination'] into the prompt
    instruction="""
    The user wants to go to: {destination}.
    Provide clear directions...
    """,
)
  1. output_key="destination": la risposta dell'agente Foodie viene salvata in modo efficiente.
  2. {destination}: l'agente di trasporto legge automaticamente la risposta.

(Nessuna azione richiesta) Passaggio 2: l'orchestratore

Apri 02_multi_agent/main.py.

Utilizziamo un SequentialAgent per eseguirli in ordine.

# 1. Create a single session for the sequential agent
session = await session_service.create_session(...)

# 2. Run the query
# The SequentialAgent manages the state flow:
# Query -> Foodie -> state['destination'] -> Transportation -> Final Answer
await run_agent_query(root_agent, query, ...)

L'utente invia un prompt:

"Find best sushi in Palo Alto and then tell me how to get there."

Gli agenti collaborano per rispondere.

Passaggio 3: gestisci il team

👉💻 Nel terminale Cloud Shell, esegui il workflow multi-agente:

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/02_multi_agent/main.py

Che cosa succede?

  1. Agente Foodie: trova "Jin Sho" (o simili).
  2. ADK: salva "Jin Sho" in state['destination'].
  3. Agente di trasporto: riceve "Jin Sho" nella sua istruzione.
  4. Risultato: "Per raggiungere Jin Sho dalla stazione Caltrain, cammina lungo University Ave…"

Concetto chiave

Regola n. 2 della memoria: utilizza State per passare informazioni strutturate tra gli agenti. Utilizza output_key per scrivere e {placeholders} per leggere.

5. Riavvio - Persistenza

stateful

Il concetto: "Il problema del riavvio"

Finora, la nostra memoria è stata InMemory. Se interrompi lo script e lo riavvii, l'agente dimentica tutto. È come un computer che cancella il disco rigido ogni volta che lo spegni.

Per risolvere questo problema, abbiamo bisogno della persistenza. Sostituiamo InMemorySessionService con DatabaseSessionService.

Passaggio 1: cambio di database

👉💻 Nel terminale di Cloud Shell, apri il file nell'editor di Cloud Shell eseguendo:

cloudshell edit ~/memory_agent_starter/03_persistent_agent/main.py

👉 Nel file ~/memory_agent_starter/03_persistent_agent/main.py, individua il commento # TODO: Configuration for Persistent Sessions.

Sostituisci l'intera riga con il seguente codice:

SESSIONS_DIR = Path(os.path.expanduser("~")) / ".adk_codelab" / "sessions"
os.makedirs(SESSIONS_DIR, exist_ok=True)
SESSION_DB_FILE = SESSIONS_DIR / "trip_planner.db"
SESSION_URL = f"sqlite:///{SESSION_DB_FILE}"

Ora ogni sessione ed evento viene salvato in un file SQLite.

Passaggio 2: recupero tra sessioni

La persistenza consente non solo di riprendere una conversazione, ma anche di imparare da quelle passate.

Nello stesso file ~/memory_agent_starter/03_persistent_agent/main.py, guarda Scenario di test 3: Cross-Session Retrieval.

👉 Individua il commento # TODO: retrieve the previous session manually

Sostituisci l'intera riga con il seguente codice:

    old_session = await session_service.get_session(
        app_name=root_agent.name, user_id="user_01", session_id=session_id
    )

👉 Individua il commento # TODO: Extract content from the OLD session all'interno della funzione main.py.

Sostituisci l'intera riga con il seguente codice:

                    previous_context += f"- {role}: {text}\n"

👉 Individua il commento # TODO: Manually inject the context to the query all'interno della funzione main.py.

Sostituisci l'intera riga con il seguente codice:

    query_3 = f"""
    {previous_context}

    I'm planning a new trip to Osaka this time. 
    Based on my previous preferences (above), what should I eat?
    """

In questo modo, simuli un utente che torna dopo mesi. Solo con un database puoi recuperare la cronologia precedente.

Passaggio 3: sopravvivere al riavvio

👉💻 Nel terminale, esegui lo script:

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/03_persistent_agent/main.py

Crea un file ~/memory_agent_starter/trip_planner.db. Prova questo: esegui lo script due volte.

  • Alla seconda esecuzione, cerca "Resumed existing session".
  • L'agente ricorderà il contesto della prima esecuzione perché lo carica dal file del database.

Concetto chiave

Regola n. 3 di Memory: utilizza DatabaseSessionService per la produzione. In questo modo, le conversazioni degli utenti vengono conservate anche dopo il riavvio del server e viene abilitata l'analisi della cronologia a lungo termine.

6. The Spy - Callbacks

stateful

A volte è necessario aggiornare la memoria automaticamente in base a ciò che l'agente fa, non solo a ciò che dice. Vuoi una "spia" che osservi l'agente e prenda appunti.

In ADK, questa spia è il callback. adk_callback

  • after_tool_callback: una funzione che viene eseguita ogni volta che l'agente lavora.
  • ToolContext: un modo per scrivere nello stato dall'interno della funzione.

Passaggio 1: logica

👉💻 Nel terminale di Cloud Shell, apri il file nell'editor di Cloud Shell eseguendo:

cloudshell edit ~/memory_agent_starter/04_stateful_agent/agent.py

👉 Nel file ~/memory_agent_starter/04_stateful_agent/agent.py, individua il commento # TODO: Implement call back logic

Sostituisci l'intera riga con il seguente codice:

def save_activity_type_callback(
    tool,
    args: Dict[str, Any],
    tool_context: ToolContext,
    tool_response: Dict[str, Any],
) -> Optional[Dict[str, Any]]:
    """
    Callback to save the TYPE of activity just planned into the session state.
    """
    # 1. Get the actual agent name.
    if tool.name == "transfer_to_agent":
         agent_name = args.get("agent_name")
    else:
         agent_name = tool.name

    activity_type = "unknown"

    # 2. Determine the type based on which agent was actually used
    if agent_name == "museum_expert":
        activity_type = "CULTURAL"
    elif agent_name == "restaurant_expert":
        activity_type = "FOOD"
    elif agent_name == "outdoor_expert":
        activity_type = "OUTDOOR"

    print(f"\n🔔 [CALLBACK] The planner transferred to '{agent_name}'.")

    # 3. Update the state directly
    tool_context.state["last_activity_type"] = activity_type
    print(f"💾 [STATE UPDATE] 'last_activity_type' is now set to: {activity_type}\n")

    return tool_response

👉 Nello stesso file, individua il commento # TODO: add callback to root agent all'interno della funzione 04_stateful_agent/agent.py.

Sostituisci l'intera riga con il seguente codice:

    after_tool_callback=save_activity_type_callback,

Istruzione dinamica: l'istruzione dell'agente è ora una funzione, non una stringa. Cambia in base allo stato.

def get_planner_instruction(context):
    last_activity = context.state.get("last_activity_type", "None")
    
    return f"""
    The last activity was: {last_activity}
    
    If last_activity is 'CULTURAL' -> `museum_expert` is BANNED.
    """

Passaggio 3: testa lo spia

👉💻 Nel terminale, esegui lo script copiando e incollando il comando seguente:

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/04_stateful_agent/main.py

Quando esegui questo agente, vedrai un loop.

  1. Turno 1: chiedi un museo. La spia imposta last_activity="CULTURAL".
  2. Turno 2: chiedi di un altro museo.
  3. Aggiornamenti delle istruzioni per l'agente: "CULTURAL is BANNED".
  4. L'agente dice: "Non posso visitare un altro museo. Che ne dici di un parco?"

Controlla i log della console per [CALLBACK] e [STATE UPDATE]. Puoi vedere la memoria cambiare in tempo reale mentre l'agente lavora.

Concetto chiave

Regola n. 4 della memoria: utilizza i callback per automatizzare la gestione dello stato. L'agente crea il proprio contesto semplicemente svolgendo il suo lavoro.

7. Schedario - Strumenti personalizzati

Il concetto: "Memoria strutturata"

stateful

Finora, la "memoria" è stata un log di chat o una semplice coppia chiave-valore. Ma cosa succede se devi ricordare un profilo utente complesso? Ad esempio, diet: vegan, budget: high, pets: [cat, dog].

Per questo, trattiamo la memoria come uno strumento. L'agente decide esplicitamente quando aprire lo schedario (lettura) e quando archiviare un report (scrittura). diagramma degli strumenti personalizzati

Passaggio 1: gli strumenti

👉💻 Nel terminale di Cloud Shell, apri il file nell'editor di Cloud Shell eseguendo:

cloudshell edit ~/memory_agent_starter/05_profile_agent/tools.py

👉 In questo file: ~/memory_agent_starter/05_profile_agent/tools.py.

Dobbiamo implementare questi due strumenti specifici:

  1. save_user_preferences: scrive in un database.
  2. recall_user_preferences: legge da un database.

Individua il commento # TODO: implement save_user_preferences tools all'interno della funzione ~/memory_agent_starter/05_profile_agent/tools.py.

Sostituisci l'intera riga con il seguente codice:

def save_user_preferences(tool_context: ToolContext, new_preferences: Dict[str, Any]) -> str:
    user_id = tool_context.session.user_id
    with sqlite3.connect(USER_DB_FILE) as conn:
        for key, value in new_preferences.items():
            conn.execute("INSERT INTO user_preferences (user_id, pref_key, pref_value) VALUES (?, ?, ?) ON CONFLICT(user_id, pref_key) DO UPDATE SET pref_value = excluded.pref_value;",
                         (user_id, key, json.dumps(value)))
    return f"Preferences updated: {list(new_preferences.keys())}"

👉 Individua il commento # TODO: implement recall_user_preferences tools all'interno della funzione 05/tools.py.

Sostituisci l'intera riga con il seguente codice:

def recall_user_preferences(tool_context: ToolContext) -> Dict[str, Any]:
    user_id = tool_context.session.user_id
    preferences = {}
    with sqlite3.connect(USER_DB_FILE) as conn:
        rows = conn.execute("SELECT pref_key, pref_value FROM user_preferences WHERE user_id = ?", (user_id,)).fetchall()
        if not rows: return {"message": "No preferences found."}
        for key, value_str in rows: preferences[key] = json.loads(value_str)
    return preferences

L'istruzione forza un workflow:

instruction="""
1. RECALL FIRST: First action MUST be `recall_user_preferences`.
3. LEARN: If a user states a new preference, use `save_user_preferences`.
"""

Passaggio 2: l'esecuzione

👉💻 Nel terminale di Cloud Shell, apri il file nell'editor di Cloud Shell eseguendo:

cloudshell edit ~/memory_agent_starter/05_profile_agent/main.py

Apri ~/memory_agent_starter/05_profile_agent/main.py.

A differenza dei moduli precedenti in cui l'ADK gestiva lo stato automaticamente, qui è l'agente ad avere il controllo.

  • Sceglie di chiamare recall_user_preferences all'inizio.
  • Sceglie di chiamare save_user_preferences quando dici "Sono vegano".

Passaggio 3: crea il profilo

👉💻 Esegui lo script:

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/05_profile_agent/main.py

Prova questo flusso di conversazione:

  1. "Ciao, organizza una cena." -> L'agente controlla il database, ma non trova nulla. Chiede le preferenze.
  2. "Sono vegano." -> L'agente salva "vegana" nel database.
  3. Riavvia lo script.
  4. "Ciao, organizza una cena." -> L'agente controlla il database, vede "vegano" e suggerisce immediatamente un ristorante vegano.

Concetto chiave

Regola n. 5 della memoria: per dati complessi e strutturati, fornisci all'agente strumenti di lettura/scrittura. Consenti all'LLM di gestire il proprio spazio di archiviazione a lungo termine.

8. Il cervello: memoria multimodale

stateful

Il concetto: "The Human Experience"

Gli esseri umani ricordano più del testo. Ricordiamo l'atmosfera di una foto, il suono di una voce, la sensazione di un video.

Vertex AI Memory Bank consente all'agente di gestire la memoria multimodale. Può importare immagini, video e audio, "comprenderli" e recuperarli in un secondo momento.

Passaggio 1: la configurazione

👉💻 Nel terminale di Cloud Shell, apri il file nell'editor di Cloud Shell eseguendo:

cloudshell edit ~/memory_agent_starter/06_multimodal_agent/main.py

👉 Apri 06_multimodal_agent/main.py. Individua il commento # TODO: Configure Memory Bank Topic.

Sostituisci l'intera riga con il seguente codice:

travel_topics = [
    MemoryTopic(
        managed_memory_topic=ManagedMemoryTopic(
            managed_topic_enum=ManagedTopicEnum.USER_PREFERENCES
        )
    ),
    MemoryTopic(
        managed_memory_topic=ManagedMemoryTopic(
            managed_topic_enum=ManagedTopicEnum.USER_PERSONAL_INFO
        )
    ),
    MemoryTopic(
        custom_memory_topic=CustomMemoryTopic(
            label="travel_experiences",
            description="""Memorable travel experiences including:
                - Places visited and impressions
                - Favorite restaurants, cafes, and food experiences
                - Preferred accommodation types and locations
                - Activities enjoyed (museums, hiking, beaches, etc.)
                - Travel companions and social preferences
                - Photos and videos from trips with location context""",
        )
    ),
    MemoryTopic(
        custom_memory_topic=CustomMemoryTopic(
            label="travel_preferences",
            description="""Travel style and preferences:
                - Budget preferences (luxury, mid-range, budget)
                - Transportation preferences (flying, trains, driving)
                - Trip duration preferences
                - Season and weather preferences
                - Cultural interests and language abilities
                - Dietary restrictions and food preferences""",
        )
    ),
    MemoryTopic(
        custom_memory_topic=CustomMemoryTopic(
            label="travel_logistics",
            description="""Practical travel information:
                - Passport and visa information
                - Frequent flyer numbers and hotel loyalty programs
                - Emergency contacts
                - Medical considerations and insurance
                - Packing preferences and essentials
                - Time zone preferences and jet lag strategies""",
        )
    ),
]

Individua il commento # TODO: Configure Memory Bank Customization

Sostituisci l'intera riga con il seguente codice:

memory_bank_config = {
    "customization_configs": [
        {
            "memory_topics": travel_topics,
        }
    ],
    "similarity_search_config": {
        "embedding_model": f"projects/{PROJECT_ID}/locations/{LOCATION}/publishers/google/models/gemini-embedding-001"
    },
    "generation_config": {
        "model": f"projects/{PROJECT_ID}/locations/{LOCATION}/publishers/google/models/gemini-2.5-flash"
    },
}

Passaggio 2: importazione del mondo

In test_trip_planner, inviamo:

  1. Un messaggio ("Ciao")
  2. Un'immagine (punto di riferimento)
  3. Un video (Mar Mediterraneo)
  4. Un clip audio (nota vocale su Gaeta)

Individua il commento # TODO create session service and memory service all'interno della funzione 6_multimodal_agent/main.py.

Sostituisci l'intera riga con il seguente codice:

session_service = VertexAiSessionService(
    project=PROJECT_ID, location=LOCATION, agent_engine_id=agent_engine_id
)
memory_service = VertexAiMemoryBankService(
    project=PROJECT_ID, location=LOCATION, agent_engine_id=agent_engine_id
)

👉 Nello stesso file 06_multimodal_agent/main.py, individua il commento # TODO: create memory from session

Sostituisci l'intera riga con il seguente codice:

    await memory_service.add_session_to_memory(final_session_state)

Questa è la linea magica. Invia tutti questi rich media a Vertex AI, che li elabora e li indicizza.

Passaggio 3: recupero

👉💻 Nel terminale di Cloud Shell, apri il file nell'editor di Cloud Shell eseguendo:

cloudshell edit ~/memory_agent_starter/06_multimodal_agent/agent.py

L'agente ha un PreloadMemoryTool.

tools=[PreloadMemoryTool(), budget_tool]

Quando inizia una nuova sessione, questo strumento cerca automaticamente nella banca della memoria le esperienze passate pertinenti e le inserisce nel contesto.

Passaggio 4: esegui il cervello

👉💻 Nel terminale Cloud Shell, esegui lo script (nota: è necessario un progetto Google Cloud con Vertex AI abilitato):

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/06_multimodal_agent/main.py

Guarda il passaggio finale della verifica:

"In base all'immagine, al video E all'audio che ho condiviso con te in precedenza…"

L'agente risponderà:

"Dovresti visitare Gaeta! Mi hai mostrato un video del Mar Mediterraneo e un'audio clip in cui dicevi di amare Gaeta."

Ha collegato i punti tra diversi tipi di media del passato.

Concetto chiave

Regola n. 6 della memoria: utilizza Vertex AI Memory Bank per un'esperienza di memoria ottimale. Unifica testo, immagini e video in un unico cervello ricercabile.

9. Conclusione

Hai fatto un viaggio da un pesce rosso smemorato a un elefante multimodale.

Hai creato

La funzionalità

Session Agent

Memoria conversazionale a breve termine

Multi-agente

Memoria condivisa del team

Persistent Agent

Cronologia a lungo termine

Agente stateful

Memoria dinamica con aggiornamento automatico

Profile Agent

Memoria dei dati strutturati

Agente multimodale

Memoria sensoriale simile a quella umana

La fiducia si basa sulla memoria. Implementando questi pattern, crei agenti che rispettano il tempo e la cronologia dell'utente, portando a interazioni più profonde ed efficaci.

Inizia a creare i tuoi agenti personalizzati oggi stesso.