The Archive - Memory at Agent Valley

1. Introduzione

Vesper, l'archivista, alla scrivania al piano terra

Nella prima settimana è stato chiesto chi dice cosa fare. La seconda settimana abbiamo chiesto chi decide cosa viene eseguito dopo. La terza settimana abbiamo chiesto cosa lascia una corsa. Questa settimana la conversazione termina e la domanda è cosa sopravvive: si scopre che è il tipo di cosa a decidere dove va.

L'archivio è il luogo in cui si verifica la valle quando qualcosa è andato storto. Una torre che scrive tutto e Vesper, l'archivista, alla scrivania al piano terra. La lanterna si spegne di notte. Vai a chiedere.

È una buona compagnia. Un mese dopo, è felice di conoscerti.

Un record non è un ricordo.

Questa frase è l'intero lab. La scala è composta da quattro pioli, ognuno dei quali corrisponde a un piano della torre:

rung

il pavimento

cosa ricorda

cosa lo mantiene

1

la scrivania e il primo piano

questa visita

session.state · SqliteSessionService

2

secondo piano, il tuo cassetto

questo visitatore

il prefisso user:

3

terzo piano della torre

cosa è stato detto

Memory Bank di Vertex AI

4

floor four, the season

tutta la valle

BigQuery

Ogni gradino più in alto è dovuto alla perdita di qualcosa in quello sottostante.

L'Archivio, aperto: cinque piani e un piccolo gruppo che si occupa di ognuno di loro

Conosci già i cinque modi di dimenticare dell'archivista, perché ognuno di questi ti è capitato durante una chiamata di assistenza:

cosa dice

cosa è andato storto

~~1~~

~~Puoi ripetermi il riferimento?~~

~~forgot within one conversation~~ — week three fixed this

~~2~~

~~Ti ho trasferito. Ricomincia dall'inizio.~~

~~la cronologia è stata eliminata con il processo~~: la settimana 3 ha risolto anche questo problema

3

Ti do il benvenuto Come posso aiutarti oggi?

a una persona con una richiesta aperta del mese scorso

4

Potresti descrivere il danno?

L'ultima volta ha guardato la lanterna

5

Non abbiamo mai visto niente di simile prima.

Trentuno persone hanno fatto questa domanda in questa stagione

La riga 3 si riferisce a questa settimana. Il fallimento si verifica in tre modi diversi: una nuova visita (capitolo 2), una frase senza riga sul foglietto (capitolo 3) e un archivio che esiste solo all'interno della procedura (capitolo 4). Ogni modo è un capitolo. La quarta riga è a tua discrezione: look() in archive/agent.py è il punto in cui legge un segno su una foto. La riga 5 è il capitolo 5 ed è facoltativa.

Non hai bisogno delle settimane da 1 a 3. Un agente è un modello con un'istruzione e alcuni strumenti. Tutto il resto, questa settimana, si spiega da sé.

Scriverai tre righe e ognuna di queste è un concetto di memoria:

the line

Descrizione

tool_context.state[CASE] = case

è scritto dal codice, mai dalla conversazione

CASE = "user:case"

un prefisso è un ciclo di vita

found = await ctx.search_memory(query)

the recall policy — filed is not remembered

C'è già un'altra riga da leggere: la norma di scrittura, await ctx.add_events_to_memory(...). Nel capitolo 3 puoi leggere cosa fa prima di scrivere la riga che la utilizza.

Gli altri due gradini non sono modifiche: costruisci una torre (un Agent Engine che ospita Memory Bank) e lo stesso codice si connette automaticamente, e carichi un warehouse (BigQuery), ci sovrapponi un grafico e fornisci allo stesso agente due query controllate: una per significato e una per connessione.

Ogni capitolo esegue lo stesso ciclo, che è quello che utilizzerai sui tuoi agenti: leggi il codice → scrivi la riga → guardala nella torre. Nessun elemento viene eseguito in modo errato, nessun elemento viene riavviato e nessun elemento viene interrotto con Ctrl+C: ogni modifica viene rilevata nel messaggio successivo e la connessione della torre è una riga in un file che l'archivio rilegge a ogni richiesta. La torre è un disegno che si ridisegna: legge gli stessi file che modifichi e ha tre pulsanti: 🌙 termina la giornata, 🗓 inizia la visita successiva, 🔥 ti dimentica. Tutto il resto viene fatto nel codice e nel terminale.

Due luoghi, e non cambiano mai lavoro: la scheda 1 è il terminale, la scheda 2 è l'archivio. Il banco di lavoro (adk web) viene visualizzato una sola volta, nel capitolo 1, per dare un'occhiata allo stato della sessione non elaborato, dopodiché non ti servirà più. Ogni 👉 di seguito indica quale. 👉💻 è la scheda 1, 👉🖥 è la scheda 2 e 👉🔬 è il banco di lavoro. 👉📖 è apri un file e leggi, non c'è nulla da modificare, mentre 👉✏️ è il tipo di passaggio che modifica. Ogni volta che un passaggio ti chiede di dire qualcosa a Vesper, il messaggio esatto si trova nel blocco sottostante: copialo così com'è. In una sezione la cui intestazione termina con — read only non devi digitare, incollare o fare clic su nulla: il codice è già nel repository e in esecuzione, stampato per la lettura.

La frase sopra la porta di Vesper è quella in alto. La frase di questa settimana è la chiusura della quarta settimana dell'intera stagione:

Fatti e preferenze appartengono a negozi diversi.

2. 🧰 Configurazione

  • Cosa: indica un progetto o incolla una chiave, installa e verifica che la toolchain sia verde.
  • Perché: qui non viene avviato nulla. Ogni server viene avviato subito prima del passaggio che lo richiede, quindi quando si apre una schermata sai a quale comando appartiene.
  • Come: uno script che crea il progetto, uno che installa e ti indirizza a Vertex AI e uno preflight che legge il codice e ti indica a che punto sei.

Tutto in questo lab viene eseguito in Cloud Shell: Python, uv e gcloud sono già installati e Gemini è raggiungibile tramite Vertex AI utilizzando le tue credenziali. Non è presente alcuna chiave API in questo lab e non è necessario installare nulla sul laptop.

C'è una cosa che questo lab non può fornirti: un progetto cloud Google con la fatturazione abilitata. Il passaggio 1 ti consente di creare un account che può pagare il progetto, mentre il passaggio 4 trasforma l'account in un progetto o riprende il progetto Agent Valley che hai già creato nella prima, nella seconda o nella terza settimana. Eseguili in questo ordine: ogni passaggio successivo al 4 legge il progetto su cui si basa il passaggio 4.

1. Ottieni un account con fatturazione (~1 minuto)

🌐 Esegui questa operazione prima di qualsiasi altra. Accedi con un Account Google con la fatturazione abilitata. Qualsiasi account di questo tipo va bene. Se il tuo non lo è, inizia una prova senza costi, che ti offre un account di fatturazione, ovvero tutto ciò che serve per il passaggio 4.

2. Apri Cloud Shell (~1 minuto)

🌐 Fai clic su Attiva Cloud Shell nella parte superiore della console Google Cloud. Si tratta dell'icona a forma di terminale nell'intestazione.

Attivare Cloud Shell dall'intestazione della console Google Cloud

🌐 Fai clic su Apri editor (l'icona della cartella aperta con una matita). Viene visualizzato l'editor di Cloud Shell con Esplora file a sinistra, dove lavori ogni volta che un passaggio è contrassegnato con 👉✏️.

Apri l'editor di Cloud Shell

🌐 Se ti viene chiesto di autorizzare in qualsiasi momento della giornata, fai clic su Autorizza per continuare.

🌐 Apri un terminale all'interno dell'editor, ovvero la scheda 1, e ogni passaggio 👉💻 viene eseguito lì, se non diversamente specificato. Scheda 2 è l'archivio e si apre nel capitolo 1, subito prima del passaggio che lo richiede. Se il terminale non è già in basso nella schermata, aprilo con Visualizza → Terminale:

Apri un nuovo terminale nell'editor di Cloud Shell

🌐 Controlla l'account con cui hai eseguito l'accesso. Deve essere quello del passaggio 1:

gcloud auth list

3. Ottieni il lab (~1 min)

💻 Clona il repository: si tratta del repository dell'archivio, non di quello della prima, della seconda o della terza settimana:

cd ~
git clone https://github.com/cuppibla/agent-valley-archive
cd agent-valley-archive

4. Crea il tuo progetto o riutilizza quello di una settimana precedente (~2 minuti)

💻 Uno script trova il tuo account di fatturazione, crea un progetto e indirizza Cloud Shell a quel progetto. Se hai completato la settimana 1, 2 o 3, il sistema trova prima il progetto Agent Valley e ti offre di riutilizzarlo. Accetta e non dovrai preoccuparti di un secondo progetto fatturabile:

cd ~/agent-valley-archive
./setup_project.sh

👀 Prevedi un ✓ per l'account di fatturazione scelto, un ✓ per il progetto creato o riutilizzato e una chiusura come:

cd ~/agent-valley-archive
Project ready: agent-valley-4271
  Next:  ./setup_codelab.sh

agent-valley-4271 è l'ID progetto, che avrà cifre diverse. All'interno succedono quattro cose:

fatturazione

prende l'account [YYYY-MM-DD] GDP Credit: più recente, se ne hai uno, e qualsiasi altro account di fatturazione aperto altrimenti

project

cerca un progetto Agent Valley di tua proprietà (della prima, della seconda o della terza settimana) e offre di riutilizzarlo; in caso contrario, crea agent-valley-XXXX e lo collega all'account di fatturazione

seleziona

scrive l'ID in ~/project_id.txt ed esegue gcloud config set project, quindi non ti verrà più chiesto di digitarlo

waits

esegue il polling fino a quando il progetto non è effettivamente autorizzato a pubblicare, per un massimo di due minuti

🌐 Lo stesso progetto è ora nella console: il suo ID si trova nella scheda Informazioni sul progetto della dashboard:

L'ID progetto nella dashboard della console

5. Installa e punta a Vertex AI (~3 min)

💻 Installa uv, esporta le tre variabili che inviano ADK a Vertex AI ed esegui la configurazione:

cd ~/agent-valley-archive
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.local/bin/env
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_CLOUD_LOCATION=global
./setup_codelab.sh

$(gcloud config get project) ha qualcosa da restituire perché è stato impostato nel passaggio 4. Questo è il motivo per cui il passaggio 4 viene prima.

setup_codelab.sh è idempotente, ovvero può essere eseguito di nuovo in sicurezza un numero qualsiasi di volte. Svolge cinque operazioni, in ordine:

api

legge il passaggio 4 del progetto selezionato e abilita aiplatform.googleapis.com (Vertex AI e Agent Engine che ospita Memory Bank) e bigquery.googleapis.com (il magazzino del capitolo 5)

uv env

uv venv crea .venv o riutilizza quello già presente, poi uv sync installa esattamente ciò che uv.lock blocca: google-adk, google-genai e tutto ciò che si trova sotto

site

viene eseguito npm ci in site/. L'archivio stesso è un'app Next.js, quindi bash valley.sh nel capitolo 1 non ha più nulla da scaricare. Viene ignorato quando i pacchetti sono già presenti.

.env

scrive le variabili lette dal lab: GOOGLE_GENAI_USE_VERTEXAI, GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_LOCATION, MEMORY_BANK_LOCATION e BQ_DATASET e nessuna chiave API da nessuna parte

controllo del modello

effettua una chiamata reale a Gemini, quindi un progetto errato non riesce in fase di configurazione, non in un secondo momento all'interno di un capitolo

6. Dimostra che è verde (~1 min)

💻 Esegui il controllo preliminare: ogni segno di spunta è un test reale, non una stima:

cd ~/agent-valley-archive
uv run python scripts/preflight.py

👀 Il preflight pone al modello una domanda per dimostrare che le tue credenziali funzionano, controlla che i pacchetti dell'archivio siano a posto, quindi legge il tuo codice e stampa la barra di avanzamento di questa settimana:

cd ~/agent-valley-archive
  desk      ▢ the slip      · write_down writes nothing         chapter 1 · EDIT ONE
  floor 1   ✓ the file      · SqliteSessionService              chapter 1, read only
  floor 2   ▢ the drawer    · CASE = "case"                     chapter 2 · EDIT TWO
  floor 3   ✓ what was said · closing time files the day        chapter 3, read only
  floor 3   ▢ the asking    · recall never looks                chapter 3 · EDIT THREE
  floor 3   ▢ the tower     · InMemoryMemoryService             chapter 4 · built, not edited
  floor 3   ✓ the rules     · USER_PREFERENCES, LANTERN_CONTEXT chapter 4, read only

Tre sono già selezionati. Sono quelli che leggi soltanto. Tre sono righe che scrivi, una è una torre che costruisci. Esegui di nuovo il controllo preliminare in qualsiasi momento del lab e ti dirà a che punto sei: legge i tuoi file, non un elenco di controllo.

3. Sessione, Stato, SqliteSessionService

  • Cosa: scrivi una riga, avvia il banco di lavoro una volta e guarda il riempimento della striscia, poi fai lo stesso nella torre e leggi perché sopravvive alla notte.
  • Perché: la prima cosa da sapere sulla memoria è che nulla viene ricordato da solo. Qualcuno lo scrive.
  • Come: una riga in agent.py, poi non modificare nient'altro.

MODIFICA UNO

👉📖 Apri archive/agent.py e trova write_down. Leggi fino a 👉:

    case = dict(tool_context.state.get(CASE, {}))
    if case.get(field):
        return {"already_written": {field: case[field]}}
    case[field] = value
    # 👉 EDIT ONE — chapter 1. She heard you. Nothing was written. Delete the
    #    "# " in front of the next line, then save — the slip is session state,
    #    and state is written by code, never by the conversation:
    # tool_context.state[CASE] = case
    return {"written": {field: value}}

La funzione crea la ricevuta in un dizionario locale e la restituisce. Un dizionario locale viene eliminato con la chiamata. Esegui questo comando così com'è e Vesper ti sente perfettamente: vengono attivate quattro chiamate write_down, ognuna risponde "written" e lo stato della sessione rimane vuoto, perché non è mai stato raggiunto. Ti ha sentito. Non è stato scritto nulla.

👉✏️ Ancora in archive/agent.py, all'interno di write_down: elimina # davanti a una riga, ovvero # e lo spazio successivo. Prima:

    case[field] = value
    # 👉 EDIT ONE — chapter 1. She heard you. Nothing was written. Delete the
    #    "# " in front of the next line, then save — the slip is session state,
    #    and state is written by code, never by the conversation:
    # tool_context.state[CASE] = case
    return {"written": {field: value}}

Dopo:

    case[field] = value
    # 👉 EDIT ONE — chapter 1. She heard you. Nothing was written. Delete the
    #    "# " in front of the next line, then save — the slip is session state,
    #    and state is written by code, never by the conversation:
    tool_context.state[CASE] = case
    return {"written": {field: value}}

risparmia,

La scheda viene compilata

👉💻 Avvia il workbench. Questa è l'unica volta nel lab in cui lo apri: è l'unica finestra che mostra lo stato della sessione come JSON non elaborato:

cd ~/agent-valley-archive
uv run adk web . --allow_origins="*"

👉🔬 Apri il workbench con Anteprima web → Anteprima sulla porta 8000 (il pulsante Anteprima web è l'icona dell'occhio sopra una finestra in alto a destra di Cloud Shell). Scegli Archivio e, prima di digitare qualsiasi cosa, fai clic su Stato nel pannello a sinistra.

👀 Vuoto. Nessuno ha ancora detto nulla.

👉🔬 Ora di' tutto questo in un unico messaggio:

Hi! My lantern keeps going out at night. My case number is k7f2, and there's a little q7 stamped on the base.

👀 Quattro chip write_down si attivano: fai clic su uno e leggi il JSON: "written": {"no": "k7f2"}. Ora fai di nuovo clic su Stato.

Quattro cose, quattro campi, un messaggio

case   {"no": "k7f2", "item": "lantern", "mark": "q7",
        "symptom": "keeps going out at night"}

👀 Vuoto, poi pieno, all'interno di un messaggio. È ciò che ricorda e non è apparso da solo. Hai scritto la riga che lo scrive. Questo è l'intero meccanismo: una semplice funzione che scrive in tool_context.state e un'istruzione che le dice di utilizzarla ogni volta che il visitatore dice qualcosa che corrisponde a una riga del biglietto.

👉🔬 Chiedile:

What's my case number?

👀 k7f2. Lo legge dal foglietto. Senza EDIT ONE, te l'avrebbe chiesto: la prima riga dell'elenco di assistenza, all'interno di una conversazione.

I due segni di protezione sono lo scontrino in formato cartaceo: quattro brevi linee, ciascuna scritta una volta. Sono più importanti di quanto sembri. Senza, il modello scopre di poter riversare l'intera conversazione in symptom e il capitolo 3, il cui intero punto è la scheda non ha una riga per questo, smette silenziosamente di essere vero. La forma di un negozio viene applicata dal codice che la scrive oppure non viene applicata.

Ora vai alla torre

👉💻 Hai terminato di utilizzare il workbench. Lascialo in esecuzione o chiudi la scheda. Il resto del lab si trova nell'archivio. Apri una seconda scheda del terminale (scheda 2: l'archivio):

cd ~/agent-valley-archive
bash valley.sh

👉🖥 Apri l'archivio con Anteprima web → Cambia porta, digita 3440 e fai clic su Cambia e visualizza anteprima. 3440 non è presente nell'elenco rapido di Anteprima web (8080 è il valore predefinito), quindi devi impostarlo manualmente questa volta. Premi ▶ Avvia, poi L'archivio. Sei alla porta. Scegli chi è venuto a chiedere, un nome e una specie, ed entra.

La torre, tagliata

👀 Un edificio. Vesper si trova alla scrivania al piano terra, dove si svolge la conversazione. Sopra di lei: il primo, il secondo e il terzo piano, oltre a una soffitta buia. I quattro pioli sono i quattro piani. Il tuo Famiglio è seduto al piano terra, perché è il punto più alto che hai raggiunto. La colonna accanto al disegno indica cosa contiene ogni piano al momento: gli stessi quattro negozi della tabella nell'introduzione, nello stesso ordine. Nessun elemento a destra è un pulsante.

👉🖥 Incolla le stesse quattro cose nella casella sotto la conversazione, esattamente questo messaggio, e premi Invio:

Hi! My lantern keeps going out at night. My case number is k7f2, and there's a little q7 stamped on the base.

Quattro righe sulla scrivania e le stesse quattro accanto

👀 Guarda la scrivania. La distinta viene scritta una riga alla volta sul disegno e per intero sotto la distinta accanto. Stesso strumento, stesso stato: la torre disegna semplicemente ciò che ha scritto write_down e continuerà a farlo per il resto del lab.

Ricorda durante la notte, di sola lettura

Cosa hai scritto e cosa significa

the line

Descrizione

dove si trova

tool_context.state[CASE] = case

session state: scritto dal codice, letto dall'istruzione

la scrivania · session.state

SqliteSessionService(DB): lo hai solo letto

l'archivio delle sessioni: una visita in un file, non in un processo

primo piano · archive.db

Questo gradino viene raggiunto con una visita e sopravvive all'orario di chiusura. Non sopravvive alla visita successiva.

4. Stato dell'utente

  • Cosa: torna un mese dopo e il foglietto non c'è più. Cinque caratteri e ti segue.
  • Perché: una sessione è una visita. La visita successiva è una nuova sessione e non ha mai visto l'ultima.
  • Come: cinque caratteri in state.py.

Un mese dopo. Piacere di conoscerti.

Premilo qui

👉🖥 Premi 🗓 un mese dopo. Si tratta di una nuova sessione per lo stesso visitatore, come Avvia una nuova conversazione nella terza settimana.

Ti diamo il benvenuto nell'archivio. Come posso aiutarti?

👉🖥 Guarda la torre. Al primo piano c'è un altro libro, questa volta. Il cassetto al secondo piano è vuoto. Il foglietto sulla scrivania è vuoto.

Un altro libro al primo piano, il cassetto vuoto, la ricevuta bianca.

👀 Perché: lo scontrino è scritto nello stato della sessione e una sessione è questa visita. Modifica visite, modifica sessioni. Non è stato riportato nulla perché non è stato richiesto. L'ultima visita è ancora nel file, ovvero il primo libro del primo piano, ma lo scontrino è stato scritto con una chiave valida per una sola visita.

EDIT TWO

👉✏️ Apri archive/state.py. Trova la 👉. Prima:

# 👉 EDIT TWO — chapter 2. Five characters. A key with no prefix lives for one
#    visit; `user:` follows the visitor into every visit after this.
CASE = "case"

Dopo:

CASE = "user:case"

risparmia, Nessun riavvio: l'archivio reimporta il codice a ogni messaggio.

👉🖥 Incolla di nuovo le stesse quattro cose, ovvero questo messaggio esatto:

Hi! My lantern keeps going out at night. My case number is k7f2, and there's a little q7 stamped on the base.

👉🖥 Poi premi di nuovo 🗓 un mese dopo, lo stesso pulsante di prima.

Il riquadro a scomparsa, con un nome sopra

👀 Il cassetto al secondo piano ha il tuo nome e i quattro campi si trovano all'interno. Il foglietto sulla scrivania è lo stesso di quattro righe estratto dal cassetto quando sei entrato. Il tuo famiglio è salito di un piano. Stessa chiave, ogni visita, un valore: questo è il prefisso che fa il suo lavoro.

Il prefisso è a vita.

prefisso

quanto vive

a quale piano

(nessuno)

questa visita

la scrivania

user:

ogni visita effettuata da questo visitatore

secondo piano, il tuo cassetto

app:

per tutti, per sempre

parte del quarto piano

temp:

solo in questo turno, non sale mai le scale

—

Un tavolo, quattro cassetti. Questo capitolo ne tocca uno.

Ora ti conosce

👉🖥 Questa visita è nuova e non hai ancora detto nulla. Chiedi a lei:

What did I bring in last time?

👀 Ho annotato che hai portato una lanterna, pratica k7f2. È contrassegnato con q7 e hai detto che si spegne di notte. Ogni parola è stata estratta dal riquadro, non dalla conversazione. Questa visita è vecchia di un messaggio e il messaggio non menziona nulla. Questo è lo stato a livello utente che fa il suo lavoro: una chiave user: è presente prima che il visitatore dica una parola, a ogni visita che effettua.

Il cassetto contiene le quattro righe dello scontrino e nient'altro. Ciò che dici e che non è scritto sul modulo, ovvero ciò che hai provato, perché ti interessa, se vuoi un nuovo dispositivo, non viene registrato e un mese dopo non esiste più. Il riquadro a scomparsa attraversa il mese. Una frase no. Il capitolo 3 parla proprio di questo.

Cosa hai scritto e cosa significa

the line

Descrizione

dove si trova

CASE = "user:case"

un prefisso è una durata: la stessa chiave, con ambito limitato al visitatore anziché alla visita

piano 2 · user: stato

Questo gradino raggiunge un visitatore. Il riquadro ti segue, ma la conversazione no.

5. InMemoryMemoryService

  • Cosa: leggi la riga che registra il giorno, scrivi la riga che chiede, poi un orario di chiusura e una domanda.
  • Perché: la cedola è un modulo. Un modulo ha le linee che hai fornito. Le persone non parlano in modo lineare e registrare ciò che dicono è solo metà del lavoro.
  • Come: leggi file; una riga in recall; poi una 🌙, un 🗓 e una domanda.

Orario di chiusura. Passa di nuovo davanti agli scaffali.

Cosa fa lungo la strada di casa (sola lettura)

Il campo non viene memorizzato

Questa linea mette il giorno in pausa. Non viene tolto nulla. Una mensola piena di carte aiuta tanto quanto una vuota se nessuno le guarda e, quando il codice viene spedito, nessuno le guarda: recall, il nodo che viene eseguito prima di Vesper per ogni domanda, non chiede nulla alla torre. Le informazioni che non riesci a ricordare al momento giusto non sono ricordi, ma un registro. La ricerca è l'altra metà, e spetta a te scriverla.

MODIFICA 3: la richiesta

👉📖 In archive/agent.py, trova recall, la funzione appena sopra file. Si tratta di un semplice nodo di funzione che viene eseguito prima di Vesper per ogni domanda, non del modello che decide quando cercare, il che significa che sono visibili anche i momenti in cui non trova nulla. Leggi fino a 👉:

    if query.strip():
        try:
            # 👉 EDIT THREE — chapter 3. Filed is not remembered: the cards are on
            #    the shelf and nobody is looking at them. Delete the "# " in front
            #    of the next line, then save:
            # found = await ctx.search_memory(query)
            pass
        except ValueError:
            # no memory service wired on this runner — degrade, do not crash
            pass

👉✏️ Ancora in archive/agent.py, all'interno di recall: elimina # davanti a una riga, ovvero # e lo spazio successivo. Prima:

        try:
            # 👉 EDIT THREE — chapter 3. Filed is not remembered: the cards are on
            #    the shelf and nobody is looking at them. Delete the "# " in front
            #    of the next line, then save:
            # found = await ctx.search_memory(query)
            pass
        except ValueError:

Dopo:

        try:
            # 👉 EDIT THREE — chapter 3. Filed is not remembered: the cards are on
            #    the shelf and nobody is looking at them. Delete the "# " in front
            #    of the next line, then save:
            found = await ctx.search_memory(query)
            pass
        except ValueError:

Lascia pass dov'è: ora non fa nulla, perché found è già None sopra.

risparmia, Nessun riavvio.

Due righe, due norme e una l'hai scritta tu. Il criterio di scrittura decide cosa conservare e quando (orario di chiusura, l'intera giornata). Le norme sul richiamo decidono quando cercare e con cosa (ogni domanda, la domanda stessa). ADK ti fornisce i verbi, add_events_to_memory e search_memory, e non interviene nelle norme. Questa è la divisione corretta, motivo per cui la richiesta è stata scritta da te.

È ora di chiudere

👉🖥 Dì tre cose per cui non è prevista una riga nella distinta. Un messaggio o tre:

I read by it at night, so night is exactly when I need it.
I've tried hanging it higher. No change.
I don't want a new one — it was a gift.

Premilo qui

👉🖥 Quindi premi 🌙 per oggi è tutto.

Le parole si alzano

👀 Guarda la scrivania. Le tre cose che hai detto si sollevano dal foglio e fluttuano verso l'alto, fino ad atterrare al terzo piano. Sotto le schede accanto alla torre in cui sono elencate, una scheda per ogni messaggio che hai inviato, contrassegnato con quote, le tue frasi parola per parola e in questo processo sopra. La lampada sulla scrivania rimane accesa finché non atterra.

Un mese dopo, lo sa

👉🖥 Premi 🗓 un mese dopo e chiedi:

What have I already tried?

👀 Hai provato ad appenderlo più in alto, ma non ha funzionato. Lo sa. La scheda non ha mai avuto una riga per questo, quindi il cassiere non avrebbe potuto rispondere; recall ha trovato la scheda, l'ha scritta in ctx.state["recalled"] e le sue istruzioni lo prevedono. Una riga è la differenza.

E tutto è nella sua testa

Guarda sotto le carte accanto alla torre: in questo processo. Al momento, la memoria è InMemoryMemoryService, un dizionario all'interno di bash valley.sh, e dura esattamente quanto il processo. Chiudi la torre stasera e riaprila domani: i libri sono al primo piano, il cassetto al secondo e il terzo è buio. La distinzione tra maiuscole e minuscole è stata mantenuta nel file. Ciò che hai detto è rimasto nel processo, che non esiste più.

La persistenza non è memoria.

Il terzo piano presenta due problemi: è in corso e si tratta di una citazione, non di un fatto. Il capitolo successivo risolve entrambi i problemi, senza alcun codice.

Ciò che leggi, ciò che scrivi e di cosa si tratta

the line

Descrizione

dove si trova

await ctx.add_events_to_memory(events, custom_metadata=FILING): lo hai solo letto

la policy di scrittura, un giorno intero, all'orario di chiusura

piano 3 · il servizio di memoria

found = await ctx.search_memory(query) — EDIT THREE

la policy di richiamo: ogni domanda, prima di rispondere

recall() → ctx.state["recalled"]

Questo anello raggiunge ciò che è stato detto e lo conserva in un dizionario all'interno di un processo, con le tue parole. Entrambe le metà sono sbagliate e il capitolo successivo le corregge senza alcun codice.

6. MemoryBank

  • Cosa: uno script crea una Memory Bank; le due righe che hai già iniziano a scriverci fatti e a leggerli.
  • Perché: un servizio di memoria è un servizio. La posizione dell'archivio è una decisione di deployment, non dell'agente. Puoi modificarla senza toccare l'agente.
  • Come: esegui make_tower.py, pronuncia tre frasi, premi 🌙, premi 🗓, fai una domanda. Poi una visualizzazione di sola lettura della scrittura e della lettura.

Costruisci la torre

Il terzo piano del capitolo 3 era un dizionario Python all'interno di bash valley.sh. Memory Bank è la versione gestita di questo piano: si trova su un'istanza di Agent Engine nel tuo progetto, conserva fatti distillati dalla giornata anziché le tue frasi e li trova in base al significato anziché alle parole condivise. Non devi eseguire il deployment di Vesper in un Agent Engine per utilizzarlo. Un'istanza senza deployment ospita comunque una Memory Bank ed è proprio questo che crea lo script: una torre senza abitanti, il cui unico compito è conservare le carte.

👉💻 Nella scheda 1 (circa un minuto):

cd ~/agent-valley-archive
uv run python scripts/make_tower.py

👀 Aspettati:

  building the tower in YOUR_PROJECT / us-central1 — about a minute...
  built, with the two topics in archive/topics.py configured on it.

  resource: projects/.../locations/us-central1/reasoningEngines/6792...
  AGENT_ENGINE written to .env

  Next — nothing to restart. The Archive reads .env on every message:
    look at floor three — under the cards it now says Memory Bank.
    (adk web, if you use it:  --memory_service_uri "agentengine://projects/.../reasoningEngines/6792...")

Una chiamata SDK, agent_engines.create(), e una riga scritta in .env. L'archivio rilegge il file a ogni richiesta, quindi non è necessario riavviare nulla.

Guarda i risultati

👉🖥 Guarda il terzo piano: la torre si ridisegna da sola in un paio di secondi. Sotto Le carte ora c'è scritto Memory Bank e lo scaffale è vuoto: le tre citazioni del capitolo 3 erano in fase di elaborazione e questo non è il processo. Ripeti le tre frasi, un messaggio o tre:

I read by it at night, so night is exactly when I need it.
I've tried hanging it higher. No change.
I don't want a new one — it was a gift.

👉🖥 Premi 🌙 per oggi è tutto. L'estrazione richiede circa otto secondi. Poi guarda al terzo piano.

A sinistra: ciò che ha tenuto a mente. Destra: ciò che la torre conserva.

👀 Le schede non sono le tue frasi. Dicono cose come Uso la lanterna per leggere di notte, ma si spegne di continuo e Ho già provato ad appendere la lanterna più in alto, ciascuna contrassegnata fact con una data. Distillazione. Ciò che è stato inserito è una conversazione. Ciò che è emerso sono fatti.

👉🖥 Premi 🗓 un mese dopo: una nuova visita, quindi niente di quello che hai detto è più nella conversazione. Chiedile questo, una domanda che non condivide una sola parola con le carte:

Should I just get a different lamp?

👀 Poiché si trattava di un regalo, hai detto che non vuoi sostituire la lanterna e hai già provato ad appenderla più in alto. Ogni parola proveniva dalla carta e solo la carta avrebbe potuto dirle: questa visita è vecchia di un messaggio. Ricerca di somiglianze. La torre corrisponde al significato, non alle parole: la domanda diceva lampada e diversa, la carta dice lanterna e sostituire. E ora non fa più parte della procedura: chiudi la torre stasera e le carte saranno lì domani, perché si trovano nell'Agent Engine, non in bash valley.sh.

Scrittura e lettura: sola lettura

Il diritto all'oblio

Premilo qui

👉🖥 Premi 🔥 dimenticami sotto la casella del messaggio.

👀 Tre piani si spengono e uno no:

cosa contiene

dopo 🔥

terzo piano, le carte

ciò che ricordava: i fatti che Memory Bank ha estrapolato dalle tue visite

andato via: memories.delete, una scheda alla volta

secondo piano, il cassetto

user:case, lo scontrino che segue questo visitatore da una visita all'altra

cancellata

la scrivania, la ricevuta

la copia della richiesta di questa visita

cancellata

piano terra, i libri

le visite stesse: una sessione in archive.db per visita, ogni messaggio

intatti: il conteggio non cambia

I libri sono la registrazione: che una visita è avvenuta, in quale giorno e cosa è stato detto. Le schede e il cassetto sono la memoria: ciò che ha tratto da queste visite e che porta con sé nella prossima. Una richiesta di oblio riguarda la memoria, ovvero i fatti derivati da una persona e lo stato che la segue. La decisione di conservare o meno la registrazione di una visita è separata e viene presa per motivi diversi, ad esempio per una traccia di audit o una cronologia dell'assistenza. Questo lab la mantiene appositamente, in modo che tu possa vedere le due cose separarsi: la registrazione e la memoria non sono la stessa cosa e dimenticare una non influisce sull'altra. Questa è la frase sopra la porta di Vesper, vista dall'altro lato.

L'astrazione della memoria dell'ADK ha add_events_to_memory e search_memory. Non ha eliminazioni. L'oblio è un'azione di governance, non un'azione dell'agente, quindi si trova nell'applicazione archive/tower.py, memories.delete sul client non elaborato, dove il modello non può raggiungerla per errore. La possibilità di dimenticare di un agente è qualcosa che devi progettare, esattamente come la possibilità di ricordare.

Cosa hai creato e letto e di cosa si tratta

la cosa

Descrizione

dove si trova

make_tower.py → AGENT_ENGINE in .env: creato, non modificato, nessun riavvio

il servizio di memoria, come servizio: una decisione di deployment, al di fuori dell'agente

Vertex AI Memory Bank su un Agent Engine

tower_service(engine): lo hai solo letto

lo stesso slot Runner(memory_service=...), un'altra casa

archive/service.py

"allowed_topics": TOPICS in FILING: sola lettura

cosa può essere conservato, deciso al momento della scrittura, non nel prompt

archive/topics.py + la configurazione della torre

🔥 → memories.delete — premuto, non scritto

L'oblio è un'azione dell'applicazione; l'API di memoria dell'ADK non prevede l'eliminazione

archive/tower.py

Questo gradino raggiunge ciò che è stato detto. Un giorno entra, un fatto esce, distillato in base a ciò che gli argomenti consentono, e il libro al primo piano non si è mai mosso.

7. GraphRAG

Facoltativo.

  • Che cosa: uno script carica il passato della valle in BigQuery, genera incorporamenti all'interno del warehouse e sovrappone un grafico alle tabelle senza spostare una riga. Poi due domande a Vesper, che ha due query controllate: una per ciò che suona in modo simile e una per ciò che è connesso.
  • Perché: suona simile non significa che sia connesso. Un vettore trova dove cercare, un grafico trova ciò che è allegato e la risposta deve contenere il percorso che ha seguito.
  • Come: bash scripts/season.sh, una volta. Tutto il resto di questo capitolo è di sola lettura.

La torre di Vesper è visibile a te, (app_name, user_id), a causa dei lavori in corso. Le visite di altre persone non fanno parte dei suoi ricordi e cercarle più a fondo non cambierebbe la situazione. Sono della valle e la valle li conserva in un magazzino. Trentuno visitatori in questa stagione sono arrivati perché una lanterna Q7 si è spenta di notte e uno di loro ha trovato la causa. Questo capitolo carica il magazzino e inserisce due query fisse.

Grafico RAG

RAG è recupera, poi genera: trova i record giusti, passali al modello e lascia che risponda. La RAG semplice recupera i dati in base alla somiglianza: incorpora la domanda ed estrai i record più vicini. In questo modo, vengono trovati contenuti che assomigliano alla domanda, ed è qui che si interrompe la torre del capitolo 4. Graph RAG aggiunge un secondo passaggio di recupero che segue gli archi (chiavi che un magazzino ha già) da qualsiasi somiglianza trovata a qualsiasi elemento allegato. I due passaggi rispondono a domande diverse e questo capitolo ti invita a porle entrambe:

a terra

la domanda a cui risponde

come

in questo capitolo

somiglianza

Dove devo cercare?

incorpora la domanda, VECTOR_SEARCH

season_search: quando il visitatore non porta alcun segno

attraversamento

che cosa è connesso?

MATCH un percorso attraverso un grafico delle proprietà

season_known_issue: uno fisso MATCH

generazione

Cosa devo dire al visitatore?

il modello, con il percorso come citazione

la sua risposta

Esegui

La stagione viene spedita come file CSV semplici in season/: visitatori, articoli, richieste, correzioni, stagioni, come li esporta un sistema di assistenza. Uno script li accompagna fino al magazzino ed è l'unica cosa che esegui in questo capitolo.

👉💻 Nella scheda 1 (circa tre minuti; stampa ogni passaggio man mano che procede e si interrompe al primo errore):

cd ~/agent-valley-archive
bash scripts/season.sh

👀 Al termine, il quarto piano della torre si illumina e la legenda accanto indica aperto.

Cosa ha creato: sola lettura

Come lo legge: sola lettura

Chiedile

👉🖥 Il quarto piano è aperto della torre. Chiedi come farebbe un normale visitatore:

Has anyone else had a q7 lantern go out at night?

Risponde e l'ultima riga è il percorso

👀 Risponde: altre 31 persone… la maggior parte ha detto che la fiamma era bassa e si è spenta dopo un'ora… Marlow la lontra ha scoperto che gli stoppini erano immersi troppo poco e l'ultima riga della sua risposta è il percorso. Questo è il suo output: leggilo, non restituirglielo:

mark q7 → 31 visits this season → 10 different things said → the wicks in the q7 batch were dipped too shallow — they drown in the oil and starve in an hour

Il percorso è la citazione. Ogni hop è un bordo del grafico e un bordo è una chiave, quindi chiunque può ripercorrerlo e ottenere lo stesso risultato. Un punteggio di somiglianza non può essere percorso di nuovo da nessuno. Se dice, poco prima del percorso, che le righe sono state restituite tramite tre join perché il progetto non ha una prenotazione del grafico, significa che la versione del progetto rifiuta GRAPH_TABLE: le righe e il percorso sono gli stessi.

👉🖥 Ora lascia fuori il segno: invia questo messaggio:

Has anyone else had a lantern that keeps dying once the sun is down?

👀 Arriva allo stesso trentuno e allo stesso percorso. Ciò che è cambiato è il percorso: season_search ha corso per primo, ha trovato cinque visitatori che sembrano simili, ognuno con q7, ha preso quel segno e solo allora ha camminato. Simile non è connesso; simile è dove cercare.

Che cosa è stato caricato e di cosa si tratta

la cosa

Descrizione

dove vive

bq load con schemi espliciti: esecuzione, non scritto

un magazzino: colonne con nomi e tipi

archive.visitors / items / asks / fixes / seasons

ML.GENERATE_EMBEDDING + VECTOR_SEARCH

il passaggio della somiglianza: dove cercare

archive.ask_embeddings, il modello embedder

CREATE PROPERTY GRAPH: nessuna riga spostata

il passaggio di attraversamento: cosa è connesso; le tabelle diventano nodi e frecce

archive.season_graph

GQL in archive/season/__init__.py: un parametro

l'unica passeggiata che può fare; il percorso è la citazione

season_known_issue(mark)

Poche decine di visitatori non sono un motivo per acquistare un magazzino. Questo capitolo insegna la forma della query, non la scalabilità. Se ingrandisci questo grafico di mille volte, il valore di MATCH non cambia di un carattere.

8. Chiudi

Tre linee, una torre, un magazzino: quattro piani. Nessun riavvio.

il piolo

Cosa hai fatto

cosa ha acquistato

ONE

tool_context.state[CASE] = case: una modifica

un errore che si verifica: lo stato viene scritto dal codice

DUE

CASE = "user:case": una modifica

un riquadro a scomparsa che segue il visitatore

la presentazione

await ctx.add_events_to_memory(..., custom_metadata=FILING) - letto, non scritto

ciò che è stato detto, conservato

TRE

found = await ctx.search_memory(query): una modifica

ciò che è stato conservato, richiesto, archiviato non viene ricordato

la torre

make_tower.py, poi AGENT_ENGINE in .env: creato e collegato da una linea, nessun riavvio

una torre che distilla e sopravvive alla notte

le regole

allowed_topics in FILING: letto, non scritto

solo ciò che deve essere conservato

the season

season.sh: caricato, incorporato in posizione, un grafico sovrapposto, camminato con uno MATCH

le visite di tutti gli altri, per significato e per connessione

Che cosa è stato sostituito

ADK 2

cosa scriveresti invece

quanto ti costa

session.state + uno strumento, conservato da SqliteSessionService

un dict accanto al ciclo di chat, salvato su disco

il modello non può leggerlo e tu scrivi il curriculum e il blocco

il prefisso user:

una tabella degli utenti e un'unione a ogni svolta

decidere a cosa appartiene una chiave, ogni volta

add_events_to_memory + search_memory in un nodo funzione

un cron che scarica le trascrizioni e il modello che decide quando esaminarle

non fa altro che scaricare, non filtra nulla e non vedi mai le volte in cui non ha funzionato.

VertexAiMemoryBankService + allowed_topics

un archivio vettoriale, un prompt di estrazione, un job di consolidamento e "non ricordare curiosità" nel prompt

tre servizi e la regola rimane valida finché il modello non ritiene diversamente

VECTOR_SEARCH + una query GQL fissa MATCH

esportazione, incorporamento altrove, un database a grafo, un job di sincronizzazione e l'SQL di scrittura del modello

i dati vengono estratti dal warehouse per essere compresi e scopri quali unioni ha ipotizzato quando un cliente

Tre domande, fermati al primo sì

  1. Avrà importanza domani? No → non conservare nulla. Esiste solo nella conversazione.
  2. Una funzione può controllarlo? Sì → il riquadro a scomparsa. Il codice lo scrive, lo legge a ogni turno e nessuna frase può modificarlo.
  3. Altrimenti → la torre. Estratto da ciò che è stato detto, cercato quando viene richiesto, rivisto in base a ciò che viene detto successivamente.

La frase

Un record non è un ricordo. Un ricordo è una registrazione più due decisioni: cosa conservare e quando guardare. Inoltre, il tipo di cosa che è decide su quale piano si trova.

Fatti e preferenze appartengono a negozi diversi.

👀 Ogni piano è illuminato. Il tuo familiare si trova in alto. Il timbro dell'archivio si trova nel passaporto e la settimana 5 apre il mercato notturno, dove Nix gestisce il palco ad alta voce, in tempo reale, e tutto ciò che sa la torre è con te.