1. Introduction

1. Le défi
Dans les scénarios de réponse aux catastrophes, la coordination des survivants ayant des compétences, des ressources et des besoins différents dans plusieurs lieux nécessite des capacités intelligentes de gestion et de recherche de données. Cet atelier vous apprend à créer un systÚme d'IA de production qui combine :
- đïžÂ Base de donnĂ©es graphiques (Spanner) : stockez les relations complexes entre les survivants, les compĂ©tences et les ressources.
- đ Recherche optimisĂ©e par l'IA : recherche hybride sĂ©mantique et par mot clĂ© Ă l'aide d'embeddings
- đžÂ Traitement multimodal : extraire des donnĂ©es structurĂ©es Ă partir d'images, de texte et de vidĂ©os
- đ€Â Orchestration multi-agent : coordonnez des agents spĂ©cialisĂ©s pour les workflows complexes.
- đ§  MĂ©moire Ă long terme : personnalisation avec Vertex AI Memory Bank

2. Objectifs de l'atelier
Une base de données de graphes du réseau de survivants avec :
- đșïžÂ Visualisation interactive en 3D des relations entre les survivants
- đ Recherche intelligente (par mots clĂ©s, sĂ©mantique et hybride)
- đžÂ Pipeline d'importation multimodal (extraire des entitĂ©s Ă partir d'images/vidĂ©os)
- đ€Â SystĂšme multi-agents pour l'orchestration de tĂąches complexes
- đ§  IntĂ©gration de Memory Bank pour des interactions personnalisĂ©es
3. Technologie principale
Composant | Technologie | Objectif |
Database (Base de donnĂ©es) | Cloud Spanner Graph | Stocker les nĆuds (survivants, compĂ©tences) et les arĂȘtes (relations) |
AI Search | Gemini + Embeddings | Compréhension sémantique et recherche par similarité |
Framework de l'agent | ADK (Agent Development Kit) | Orchestrer des workflows d'IA |
Mémoire | Vertex AI Memory Bank | Stockage à long terme des préférences utilisateur |
Frontend | React + Three.js | Visualisation interactive de graphiques 3D |
2. PrĂ©paration de l'environnement (Ă ignorer si vous ĂȘtes dans l'atelier)
PremiÚre partie : Activer le compte de facturation
- Revendiquez votre compte de facturation avec un crédit de 5 $, dont vous aurez besoin pour votre déploiement. Assurez-vous d'utiliser votre compte Gmail.
DeuxiÚme partie : Environnement ouvert
- đ Cliquez sur ce lien pour accĂ©der directement Ă l'Ă©diteur Cloud Shell.
- đ Si vous ĂȘtes invitĂ© Ă autoriser l'accĂšs Ă un moment donnĂ© aujourd'hui, cliquez sur Autoriser pour continuer.

- đ Si le terminal ne s'affiche pas en bas de l'Ă©cran, ouvrez-le :
- Cliquez sur Afficher.
- Cliquez sur Terminal
.
- đđ»Â Dans le terminal, vĂ©rifiez que vous ĂȘtes dĂ©jĂ authentifiĂ© et que le projet est dĂ©fini sur votre ID de projet Ă l'aide de la commande suivante :
gcloud auth list - đđ»Â Clonez le projet bootstrap depuis GitHub :
git clone https://github.com/google-americas/way-back-home.git
3. Configuration de l'environnement
1. Initialiser
Dans le terminal de l'éditeur Cloud Shell, si le terminal n'apparaßt pas en bas de l'écran, ouvrez-le :
- Cliquez sur Afficher.
- Cliquez sur Terminal

đđ»Â Dans le terminal, rendez le script d'initialisation exĂ©cutable et exĂ©cutez-le :
cd ~/way-back-home/level_2
./init.sh
2. Configurer le projet
đ đ» DĂ©finissez votre ID de projet :
gcloud config set project $(cat ~/project_id.txt) --quiet
đđ»Â Activez les API requises (cela prend environ deux Ă trois minutes) :
gcloud services enable compute.googleapis.com \
aiplatform.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
spanner.googleapis.com \
storage.googleapis.com
3. Exécuter le script de configuration
đđ»Â ExĂ©cutez le script d'installation :
cd ~/way-back-home/level_2
./setup.sh
Cela créera .env pour vous. Dans votre Cloud Shell, ouvrez le projet way_back_home. Sous le dossier level_2, vous pouvez voir que le fichier .env a été créé pour vous. Si vous ne le trouvez pas, cliquez sur View > Toggle Hidden File pour l'afficher. 
4. Charger des exemples de données
đđ»Â AccĂ©dez au backend et installez les dĂ©pendances :
cd ~/way-back-home/level_2/backend
uv sync
đđ»Â Chargez les donnĂ©es initiales des survivants :
uv run python ~/way-back-home/level_2/backend/setup_data.py
Cela crée :
- Instance Spanner (
survivor-network) - Base de données (
graph-db) - Toutes les tables de nĆuds et d'arĂȘtes
- Graphiques de propriĂ©tĂ©s pour les requĂȘtes RĂ©sultat attendu :
============================================================
SUCCESS! Database setup complete.
============================================================
Instance: survivor-network
Database: graph-db
Graph: SurvivorGraph
Access your database at:
https://console.cloud.google.com/spanner/instances/survivor-network/databases/graph-db?project=waybackhome
Si vous cliquez sur le lien aprÚs Access your database at dans le résultat, vous pouvez ouvrir la console Google Cloud Spanner.

Vous verrez Spanner dans la console Google Cloud.

4. Visualiser des données graphiques dans Spanner Studio
Ce guide vous aide à visualiser les données du graphique Survivor Network et à interagir avec elles directement dans la console Google Cloud à l'aide de Spanner Studio. C'est un excellent moyen de vérifier vos données et de comprendre la structure du graphique avant de créer votre agent d'IA.
1. Accéder à Spanner Studio
- à la derniÚre étape, veillez à cliquer sur le lien et à ouvrir Spanner Studio.

2. Comprendre la structure du graphique ("vue d'ensemble")
Considérez l'ensemble de données Survivor Network comme un puzzle logique ou un état de jeu :
Entité | RÎle dans le systÚme | Analogie |
Survivants | Les agents/joueurs | Joueurs |
Biomes | OĂč se trouvent-ils ? | Zones de carte |
Compétences | Ce qu'ils peuvent faire | Fonctionnalités |
Besoins | Ce qui leur manque (crises) | QuĂȘtes/Missions |
Ressources | ĂlĂ©ments trouvĂ©s dans le monde | Loot |
Objectif : l'agent d'IA doit associer des compétences (solutions) à des besoins (problÚmes), en tenant compte des biomes (contraintes de localisation).
đ ArĂȘtes (relations) :
SurvivorInBiome : suivi de la positionSurvivorHasSkill : Inventaire des capacitésSurvivorHasNeed : liste des problÚmes actifs.SurvivorFoundResource : inventaire des articlesSurvivorCanHelp : relation inférée (l'IA la calcule).
3. Interroger le graphique
ExĂ©cutons quelques requĂȘtes pour dĂ©couvrir l'histoire que racontent les donnĂ©es.
Spanner Graph utilise le langage GQL (Graph Query Language). Pour exĂ©cuter une requĂȘte, utilisez GRAPH SurvivorNetwork suivi de votre modĂšle de correspondance.
đ RequĂȘte 1 : Le roster mondial (qui est oĂč ?) C'est votre base : comprendre la localisation est essentiel pour les opĂ©rations de sauvetage.
GRAPH SurvivorNetwork
MATCH result = (s:Survivors)-[:SurvivorInBiome]->(b:Biomes)
RETURN TO_JSON(result) AS json_result
Le rĂ©sultat devrait ĂȘtre le suivant : 
đ RequĂȘte 2 : La matrice de compĂ©tences (capacitĂ©s) Maintenant que vous savez oĂč se trouve chaque personne, dĂ©couvrez ce qu'elle peut faire.
GRAPH SurvivorNetwork
MATCH result = (s:Survivors)-[h:SurvivorHasSkill]->(k:Skills)
RETURN TO_JSON(result) AS json_result
Le rĂ©sultat devrait ĂȘtre le suivant : 
đ RequĂȘte 3 : Qui est en crise ? (Le tableau des missions) : dĂ©couvrez les survivants qui ont besoin d'aide et ce dont ils ont besoin.
GRAPH SurvivorNetwork
MATCH result = (s:Survivors)-[h:SurvivorHasNeed]->(n:Needs)
RETURN TO_JSON(result) AS json_result
Le rĂ©sultat devrait ĂȘtre le suivant : 
đ Avancé : mise en relation â Qui peut aider qui ?
C'est lĂ que le graphique devient puissant. Cette requĂȘte permet de trouver les survivants qui possĂšdent des compĂ©tences permettant de rĂ©pondre aux besoins des autres survivants.
GRAPH SurvivorNetwork
MATCH result = (helper:Survivors)-[:SurvivorHasSkill]->(skill:Skills)-[:SkillTreatsNeed]->(need:Needs)<-[:SurvivorHasNeed]-(helpee:Survivors)
RETURN TO_JSON(result) AS json_result
Le rĂ©sultat devrait ĂȘtre le suivant : 
aside positive Que fait cette requĂȘte ?
Au lieu de simplement afficher "Les premiers secours traitent les brĂ»lures" (ce qui est Ă©vident d'aprĂšs le schĂ©ma), cette requĂȘte trouve :
- Dr. Elena Frost (qui a une formation mĂ©dicale) â peut soigner â Capitaine Tanaka (qui a des brĂ»lures)
- David Chen (qui a suivi une formation de secouriste) â peut soigner â Lt. Park (qui a une entorse Ă la cheville)
Pourquoi cette fonctionnalité est-elle efficace ?
Ce que votre agent d'IA fera :
Lorsqu'un utilisateur pose la question Qui peut soigner les brûlures ?, l'agent :
- ExĂ©cuter une requĂȘte de graphe similaire
- Réponse : "Dr. Frost a une formation médicale et peut aider le capitaine Tanaka"
- L'utilisateur n'a pas besoin de connaßtre les tables ni les relations intermédiaires.
5. Embeddings optimisés par l'IA dans Spanner
1. Pourquoi utiliser des embeddings ? (Aucune action, lecture seule)
Dans le scénario de survie, le temps est essentiel. Lorsqu'un survivant signale une urgence, comme I need someone who can treat burns ou Looking for a medic, il ne peut pas perdre de temps à deviner les noms exacts des compétences dans la base de données.
ScĂ©nario rĂ©el : Survivor : Captain Tanaka has burnsâwe need medical help NOW!
Recherche traditionnelle par mot clĂ© pour "mĂ©decin" â 0 rĂ©sultat â
Recherche sĂ©mantique avec des embeddings : trouve "Formation mĂ©dicale" et "Premiers secours" â
C'est exactement ce dont les agents ont besoin : une recherche intelligente et humaine qui comprend l'intention, et pas seulement les mots clés.
2. Créer un modÚle d'embedding

Nous allons maintenant créer un modÚle qui convertit le texte en embeddings à l'aide de text-embedding-004 de Google.
đ Dans Spanner Studio, exĂ©cutez cette requĂȘte SQL (remplacez $YOUR_PROJECT_ID par l'ID de votre projet) :
âŒïžÂ Dans l'Ă©diteur Cloud Shell, ouvrez File > Open Folder > way-back-home/level_2 pour afficher l'intĂ©gralitĂ© du projet.

đ ExĂ©cutez cette requĂȘte dans Spanner Studio en copiant et en collant la requĂȘte ci-dessous, puis en cliquant sur le bouton "ExĂ©cuter" :
CREATE MODEL TextEmbeddings
INPUT(content STRING(MAX))
OUTPUT(embeddings STRUCT<values ARRAY<FLOAT32>>)
REMOTE OPTIONS (
endpoint = '//aiplatform.googleapis.com/projects/$YOUR_PROJECT_ID/locations/us-central1/publishers/google/models/text-embedding-004'
);
Fonctionnement :
- Crée un modÚle virtuel dans Spanner (aucune pondération de modÚle n'est stockée en local).
- Pointe vers
text-embedding-004de Google sur Vertex AI - Définit le contrat : l'entrée est un texte, la sortie est un tableau flottant de 768 dimensions.
Pourquoi "OPTIONS Ă DISTANCE"Â ?
- Spanner n'exĂ©cute pas le modĂšle lui-mĂȘme
- Il appelle Vertex AI via l'API lorsque vous utilisez
ML.PREDICT. - Zero-ETL : pas besoin d'exporter les données vers Python, de les traiter et de les réimporter
Cliquez sur le bouton Run. Une fois l'opération réussie, vous pouvez voir le résultat ci-dessous :

3. Ajouter une colonne d'embedding
đ Ajoutez une colonne pour stocker les embeddings :
ALTER TABLE Skills ADD COLUMN skill_embedding ARRAY<FLOAT32>;
Cliquez sur le bouton Run. Une fois l'opération réussie, vous pouvez voir le résultat ci-dessous :

4. Générer des embeddings
đ Utilisez l'IA pour crĂ©er des embeddings vectoriels pour chaque compĂ©tence :
UPDATE Skills
SET skill_embedding = (
SELECT embeddings.values
FROM ML.PREDICT(
MODEL TextEmbeddings,
(SELECT name AS content)
)
)
WHERE skill_embedding IS NULL;
Cliquez sur le bouton Run. Une fois l'opération réussie, vous pouvez voir le résultat ci-dessous :

Ce qui se passe : chaque nom d'application (par exemple, "premiers secours") est convertie en un vecteur de 768 dimensions représentant sa signification sémantique.
5. Valider les embeddings
đ VĂ©rifiez que les embeddings ont Ă©tĂ© créés :
SELECT
skill_id,
name,
ARRAY_LENGTH(skill_embedding) AS embedding_dimensions
FROM Skills
LIMIT 5;
Résultat attendu :

6. Tester la recherche sémantique
Nous allons maintenant tester le cas d'utilisation exact de notre scénario : trouver des compétences médicales à l'aide du terme "médecin".
đ Trouver des compĂ©tences semblables Ă "mĂ©decin" :
WITH query_embedding AS (
SELECT embeddings.values AS val
FROM ML.PREDICT(MODEL TextEmbeddings, (SELECT "medic" AS content))
)
SELECT
s.name AS skill_name,
s.category,
COSINE_DISTANCE(s.skill_embedding, (SELECT val FROM query_embedding)) AS distance
FROM Skills AS s
WHERE s.skill_embedding IS NOT NULL
ORDER BY distance ASC
LIMIT 10;
- Convertit le terme de recherche "médecin " de l'utilisateur en embedding
- Stocke les données dans la table temporaire
query_embedding
Résultats attendus (plus la distance est faible, plus les éléments sont semblables) :

7. Créer un modÚle Gemini pour l'analyse

đ CrĂ©ez une rĂ©fĂ©rence de modĂšle d'IA gĂ©nĂ©rative (remplacez $YOUR_PROJECT_ID par l'ID rĂ©el de votre projet) :
CREATE MODEL GeminiPro
INPUT(prompt STRING(MAX))
OUTPUT(content STRING(MAX))
REMOTE OPTIONS (
endpoint = '//aiplatform.googleapis.com/projects/$YOUR_PROJECT_ID/locations/us-central1/publishers/google/models/gemini-2.5-pro',
default_batch_size = 1
);
Différence par rapport au modÚle d'embeddings :
- Embeddings : texte â vecteur (pour la recherche de similaritĂ©s)
- Gemini : texte â texte gĂ©nĂ©rĂ© (pour le raisonnement/l'analyse)

8. Utiliser Gemini pour l'analyse de la compatibilité
đ Analysez les paires de survivants pour vĂ©rifier leur compatibilitĂ© avec la mission :
WITH PairData AS (
SELECT
s1.name AS Name_A,
s2.name AS Name_B,
CONCAT(
"Assess compatibility of these two survivors for a resource-gathering mission. ",
"Survivor 1: ", s1.name, ". ",
"Survivor 2: ", s2.name, ". ",
"Give a score from 1-10 and a 1-sentence reason."
) AS prompt
FROM Survivors s1
JOIN Survivors s2 ON s1.survivor_id < s2.survivor_id
LIMIT 1
)
SELECT
Name_A,
Name_B,
content AS ai_assessment
FROM ML.PREDICT(
MODEL GeminiPro,
(SELECT Name_A, Name_B, prompt FROM PairData)
);
Résultat attendu :
Name_A | Name_B | ai_assessment
----------------|-------------------|----------------
"David Chen" | "Dr. Elena Frost" | "**Score: 9/10** Their compatibility is extremely high as David's practical, hands-on scavenging skills are perfectly complemented by Dr. Frost's specialized knowledge to identify critical medical supplies and avoid biological hazards."
6. Créer votre agent Graph RAG avec la recherche hybride
1. Présentation de l'architecture du systÚme
Cette section explique comment crĂ©er un systĂšme de recherche multimĂ©thode qui permet Ă votre agent de gĂ©rer diffĂ©rents types de requĂȘtes de maniĂšre flexible. Le systĂšme comporte trois couches : couche Agent, couche Outil et couche Service.

Pourquoi trois couches ?
- Séparation des préoccupations : l'agent se concentre sur l'intention, les outils sur l'interface et le service sur l'implémentation.
- Flexibilité : l'agent peut forcer des méthodes spécifiques ou laisser l'IA effectuer le routage automatique.
- Optimisation : possibilité d'ignorer l'analyse d'IA coûteuse lorsque la méthode est connue
Dans cette section, vous allez principalement implémenter la recherche sémantique (RAG), qui consiste à trouver des résultats en fonction de leur signification et pas seulement de leurs mots clés. Nous expliquerons plus tard comment la recherche hybride fusionne plusieurs méthodes.
2. Implémentation du service RAG
đđ»Â Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell en exĂ©cutant la commande suivante :
cloudshell edit ~/way-back-home/level_2/backend/services/hybrid_search_service.py
Recherchez le commentaire # TODO: REPLACE_SQL.
Remplacez toute cette ligne par le code suivant :
# This is your working query from the successful run!
sql = """
WITH query_embedding AS (
SELECT embeddings.values AS val
FROM ML.PREDICT(
MODEL TextEmbeddings,
(SELECT @query AS content)
)
)
SELECT
s.survivor_id,
s.name AS survivor_name,
s.biome,
sk.skill_id,
sk.name AS skill_name,
sk.category,
COSINE_DISTANCE(
sk.skill_embedding,
(SELECT val FROM query_embedding)
) AS distance
FROM Survivors s
JOIN SurvivorHasSkill shs ON s.survivor_id = shs.survivor_id
JOIN Skills sk ON shs.skill_id = sk.skill_id
WHERE sk.skill_embedding IS NOT NULL
ORDER BY distance ASC
LIMIT @limit
"""
3. Définition de l'outil de recherche sémantique
đđ»Â Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell en exĂ©cutant la commande suivante :
cloudshell edit ~/way-back-home/level_2/backend/agent/tools/hybrid_search_tools.py
Dans hybrid_search_tools.py, localisez le commentaire # TODO: REPLACE_SEMANTIC_SEARCH_TOOL.
đ Remplacez toute cette ligne par le code suivant :
async def semantic_search(query: str, limit: int = 10) -> str:
"""
Force semantic (RAG) search using embeddings.
Use this when you specifically want to find things by MEANING,
not just matching keywords. Great for:
- Finding conceptually similar items
- Handling vague or abstract queries
- When exact terms are unknown
Example: "healing abilities" will find "first aid", "surgery",
"herbalism" even though no keywords match exactly.
Args:
query: What you're looking for (describe the concept)
limit: Maximum results
Returns:
Semantically similar results ranked by relevance
"""
try:
service = _get_service()
result = service.smart_search(
query,
force_method=SearchMethod.RAG,
limit=limit
)
return _format_results(
result["results"],
result["analysis"],
show_analysis=True
)
except Exception as e:
return f"Error in semantic search: {str(e)}"
Quand l'agent utilise :
- RequĂȘtes demandant une similaritĂ© ("trouve des Ă©lĂ©ments semblables Ă X")
- RequĂȘtes conceptuelles ("capacitĂ©s de guĂ©rison")
- Lorsque la compréhension du sens est essentielle
4. Guide de décision de l'agent (instructions)
Dans la définition de l'agent, copiez-collez la partie liée à la recherche sémantique dans l'instruction.
đđ»Â Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell en exĂ©cutant la commande suivante :
cloudshell edit ~/way-back-home/level_2/backend/agent/agent.py
L'agent utilise cette instruction pour sélectionner l'outil approprié :
đ Dans le fichier agent.py, recherchez le commentaire # TODO: REPLACE_SEARCH_LOGIC, Replace this whole line (Remplacez toute cette ligne) et remplacez-le par le code suivant :
- `semantic_search`: Force RAG/embedding search
Use for: "Find similar to X", conceptual queries, unknown terminology
Example: "Find skills related to healing"
đ Localisez le commentaire # TODO: ADD_SEARCH_TOOLReplace this whole line (Remplacez toute cette ligne) et remplacez-le par le code suivant :
semantic_search, # Force RAG
5. Comprendre le fonctionnement de la recherche hybride (lecture seule, aucune action requise)
Dans les Ă©tapes 2 Ă Â 4, vous avez implĂ©mentĂ© la recherche sĂ©mantique (RAG), la mĂ©thode de recherche principale qui trouve des rĂ©sultats par signification. Mais vous avez peut-ĂȘtre remarquĂ© que le systĂšme s'appelle "Recherche hybride". Voici comment tout cela s'imbrique :
Fonctionnement de la fusion hybride :
Dans le fichier way-back-home/level_2/backend/services/hybrid_search_service.py, lorsque hybrid_search() est appelé, le service exécute LES DEUX recherches et fusionne les résultats :
# Location: backend/services/hybrid_search_service.py
rank_kw = keyword_ranks.get(surv_id, float('inf'))
rank_rag = rag_ranks.get(surv_id, float('inf'))
rrf_score = 0.0
if rank_kw != float('inf'):
rrf_score += 1.0 / (K + rank_kw)
if rank_rag != float('inf'):
rrf_score += 1.0 / (K + rank_rag)
combined_score = rrf_score
Pour cet atelier de programmation, vous avez implémenté le composant de recherche sémantique (RAG), qui constitue la base. Les méthodes par mots clés et hybrides sont déjà implémentées dans le service. Votre agent peut utiliser les trois.
Félicitations ! Vous avez terminé de créer votre agent Graph RAG avec la recherche hybride.
7. Tester votre agent avec ADKÂ Web
Le moyen le plus simple de tester votre agent est d'utiliser la commande adk web, qui lance votre agent avec une interface de chat intégrée.
1. Exécuter l'agent
đđ»Â AccĂ©dez au rĂ©pertoire backend (oĂč votre agent est dĂ©fini) et lancez l'interface Web :
cd ~/way-back-home/level_2/backend
uv run adk web
Cette commande démarre l'agent défini dans .
agent/agent.py
et ouvre une interface Web pour les tests.
đ Ouvrez l'URL :
La commande génÚre une URL locale (généralement http://127.0.0.1:8000 ou similaire). Ouvrez-le dans votre navigateur.

Une fois que vous avez cliqué sur l'URL, l'interface utilisateur Web de l'ADK s'affiche. Assurez-vous de sélectionner l'agent en haut à gauche.

2. Tester les fonctionnalités de recherche
L'agent est conçu pour acheminer intelligemment vos requĂȘtes. Essayez les entrĂ©es suivantes dans la fenĂȘtre de chat pour voir diffĂ©rentes mĂ©thodes de recherche en action.
đ§ŹÂ A. Graph RAG (recherche sĂ©mantique)
Trouve des Ă©lĂ©ments en fonction de leur signification et de leur concept, mĂȘme si les mots clĂ©s ne correspondent pas.
RequĂȘtes de test : (choisissez l'une des options ci-dessous)
Who can help with injuries?
What abilities are related to survival?
ĂlĂ©ments Ă prendre en compte :
- Le raisonnement doit mentionner la recherche sémantique ou RAG.
- Vous devriez obtenir des résultats conceptuellement liés (par exemple, "Chirurgie" lorsque vous demandez "Premiers secours").
- Les rĂ©sultats seront accompagnĂ©s de l'icĂŽne đ§Ź.
đ B. Recherche hybride
Combine les filtres de mots clĂ©s avec la comprĂ©hension sĂ©mantique pour les requĂȘtes complexes.
RequĂȘtes de test : (choisissez l'une des options ci-dessous)
Find someone who can ply a plane in the volcanic area
Who has healing abilities in the FOSSILIZED?
Who has healing abilities in the mountains?
ĂlĂ©ments Ă prendre en compte :
- La justification doit mentionner la recherche hybride.
- Les résultats doivent correspondre aux DEUX critÚres (concept + lieu/catégorie).
- Les rĂ©sultats trouvĂ©s par les deux mĂ©thodes sont accompagnĂ©s de l'icĂŽne đ et sont classĂ©s en premier.
đđ»Â Une fois les tests terminĂ©s, mettez fin au processus en appuyant sur Ctrl+C dans votre ligne de commande.
8. Exécuter l'application complÚte
Présentation de l'architecture Full Stack

Ajouter SessionService et Runner
đđ»Â Dans le terminal, ouvrez le fichier chat.py dans l'Ă©diteur Cloud Shell en exĂ©cutant la commande suivante (assurez-vous d'avoir appuyĂ© sur Ctrl+C pour mettre fin au processus prĂ©cĂ©dent avant de continuer) :
cloudshell edit ~/way-back-home/level_2/backend/api/routes/chat.py
đ Dans le fichier chat.py, recherchez le commentaire # TODO: REPLACE_INMEMORY_SERVICES, Replace this whole line (Remplacez toute cette ligne) et remplacez-le par le code suivant :
session_service = InMemorySessionService()
memory_service = InMemoryMemoryService()
đ Dans le fichier chat.py, recherchez le commentaire # TODO: REPLACE_RUNNER, Replace this whole line (Remplacez toute cette ligne) et remplacez-le par le code suivant :
runner = Runner(
agent=root_agent,
session_service=session_service,
memory_service=memory_service,
app_name="survivor-network"
)
1. Commencer l'inscription
Si le terminal précédent est toujours en cours d'exécution, mettez-y fin en appuyant sur Ctrl+C.
đđ»Â DĂ©marrer l'application :
cd ~/way-back-home/level_2/
./start_app.sh
Lorsque le backend démarre correctement, Local: http://localhost:5173/" s'affiche, comme suit : 
đ Cliquez sur Local : http://localhost:5173/ dans le terminal.

2. Tester la recherche sémantique
RequĂȘte :
Find skills similar to healing

Ce qui se passe :
- L'agent reconnaßt la demande de similarité
- GénÚre un embedding pour "guérison"
- Utilise la distance de cosinus pour trouver des compétences sémantiquement similaires
- RĂ©sultats : premiers secours (mĂȘme si les noms ne correspondent pas Ă "soins")
3. Tester la recherche hybride
RequĂȘte :
Find medical skills in the mountains
Ce qui se passe :
- Composant de mot clé : filtrez sur
category='medical' - Composant sémantique : intégrer "médical" et classer par similarité
- Fusionner : combine les rĂ©sultats en privilĂ©giant ceux trouvĂ©s par les deux mĂ©thodes đ
RequĂȘte(facultatif)Â :
Who is good at survival and in the forest?
Ce qui se passe :
- Mots clés trouvés :
biome='forest' - Résultats sémantiques : compétences similaires à "survie"
- La méthode hybride combine les deux pour obtenir les meilleurs résultats.
đđ»Â Lorsque vous avez terminĂ© les tests, appuyez sur Ctrl+C dans le terminal pour y mettre fin.
9. Pipeline multimodal : couche d'outillage
Pourquoi avons-nous besoin d'un pipeline multimodal ?
Le réseau de survie n'est pas qu'un texte. Les survivants sur le terrain envoient des données non structurées directement par chat :
- đžÂ Images : photos de ressources, de dangers ou d'Ă©quipements
- đ„ VidĂ©os : rapports d'Ă©tat ou diffusions SOS
- đ Texte : notes ou journaux de terrain
Quels fichiers traitons-nous ?
Contrairement Ă l'Ă©tape prĂ©cĂ©dente, oĂč nous avons recherchĂ© des donnĂ©es existantes, nous allons ici traiter les fichiers importĂ©s par l'utilisateur. L'interface chat.py gĂšre les piĂšces jointes de maniĂšre dynamique :
Source | Contenu | Objectif |
Association d'utilisateurs | Image/Vidéo/Texte | Informations à ajouter au graphique |
Contexte du chat | "Voici une photo des fournitures" | Intention et informations supplémentaires |
Approche prévue : pipeline d'agents séquentiels
Nous utilisons un agent séquentiel (multimedia_agent.py) qui enchaßne des agents spécialisés :

Ce paramÚtre est défini dans backend/agent/multimedia_agent.py en tant que SequentialAgent.
La couche d'outillage fournit les capacités que les agents peuvent invoquer. Les outils gÚrent le "comment" : importer des fichiers, extraire des entités et enregistrer dans la base de données.
1. Ouvrir le fichier d'outils
đđ»Â Ouvrez un nouveau terminal. Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell :
cloudshell edit ~/way-back-home/level_2/backend/agent/tools/extraction_tools.py
2. Implémenter l'outil upload_media
Cet outil importe un fichier local dans Google Cloud Storage.
đ Dans extraction_tools.py, recherchez le commentaire pass # TODO: REPLACE_UPLOAD_MEDIA_FUNCTION.
Remplacez toute cette ligne par le code suivant :
"""
Upload media file to GCS and detect its type.
Args:
file_path: Path to the local file
survivor_id: Optional survivor ID to associate with upload
Returns:
Dict with gcs_uri, media_type, and status
"""
try:
if not file_path:
return {"status": "error", "error": "No file path provided"}
# Strip quotes if present
file_path = file_path.strip().strip("'").strip('"')
if not os.path.exists(file_path):
return {"status": "error", "error": f"File not found: {file_path}"}
gcs_uri, media_type, signed_url = gcs_service.upload_file(file_path, survivor_id)
return {
"status": "success",
"gcs_uri": gcs_uri,
"signed_url": signed_url,
"media_type": media_type.value,
"file_name": os.path.basename(file_path),
"survivor_id": survivor_id
}
except Exception as e:
logger.error(f"Upload failed: {e}")
return {"status": "error", "error": str(e)}
3. Implémenter l'outil extract_from_media
Cet outil est un routeur : il vérifie le media_type et l'envoie à l'extracteur approprié (texte, image ou vidéo).
đ Dans extraction_tools.py, localisez le commentaire pass # TODO: REPLACE_EXTRACT_FROM_MEDIA.
Remplacez toute cette ligne par le code suivant :
"""
Extract entities and relationships from uploaded media.
Args:
gcs_uri: GCS URI of the uploaded file
media_type: Type of media (text/image/video)
signed_url: Optional signed URL for public/temporary access
Returns:
Dict with extraction results
"""
try:
if not gcs_uri:
return {"status": "error", "error": "No GCS URI provided"}
# Select appropriate extractor
if media_type == MediaType.TEXT.value or media_type == "text":
result = await text_extractor.extract(gcs_uri)
elif media_type == MediaType.IMAGE.value or media_type == "image":
result = await image_extractor.extract(gcs_uri)
elif media_type == MediaType.VIDEO.value or media_type == "video":
result = await video_extractor.extract(gcs_uri)
else:
return {"status": "error", "error": f"Unsupported media type: {media_type}"}
# Inject signed URL into broadcast info if present
if signed_url:
if not result.broadcast_info:
result.broadcast_info = {}
result.broadcast_info['thumbnail_url'] = signed_url
return {
"status": "success",
"extraction_result": result.to_dict(), # Return valid JSON dict instead of object
"summary": result.summary,
"entities_count": len(result.entities),
"relationships_count": len(result.relationships),
"entities": [e.to_dict() for e in result.entities],
"relationships": [r.to_dict() for r in result.relationships]
}
except Exception as e:
logger.error(f"Extraction failed: {e}")
return {"status": "error", "error": str(e)}
Informations clés sur l'implémentation :
- EntrĂ©e multimodale : nous transmettons Ă
generate_contentĂ la fois la requĂȘte textuelle (_get_extraction_prompt()) et l'objet image. - Sortie structurĂ©e :
response_mime_type="application/json"garantit que le LLM renvoie un JSON valide, ce qui est essentiel pour le pipeline. - Association d'entitĂ©s visuelles : la requĂȘte inclut des entitĂ©s connues afin que Gemini puisse reconnaĂźtre des personnages spĂ©cifiques.
4. Implémenter l'outil save_to_spanner
Cet outil conserve les entités et les relations extraites dans la base de données Spanner Graph.
đ Dans extraction_tools.py, localisez le commentaire pass # TODO: REPLACE_SPANNER_AGENT.
Remplacez toute cette ligne par le code suivant :
"""
Save extracted entities and relationships to Spanner Graph DB.
Args:
extraction_result: ExtractionResult object (or dict from previous step if passed as dict)
survivor_id: Optional survivor ID to associate with the broadcast
Returns:
Dict with save statistics
"""
try:
# Handle if extraction_result is passed as the wrapper dict from extract_from_media
result_obj = extraction_result
if isinstance(extraction_result, dict) and 'extraction_result' in extraction_result:
result_obj = extraction_result['extraction_result']
# If result_obj is a dict (from to_dict()), reconstruct it
if isinstance(result_obj, dict):
from extractors.base_extractor import ExtractionResult
result_obj = ExtractionResult.from_dict(result_obj)
if not result_obj:
return {"status": "error", "error": "No extraction result provided"}
stats = spanner_service.save_extraction_result(result_obj, survivor_id)
return {
"status": "success",
"entities_created": stats['entities_created'],
"entities_existing": stats['entities_found_existing'],
"relationships_created": stats['relationships_created'],
"broadcast_id": stats['broadcast_id'],
"errors": stats['errors'] if stats['errors'] else None
}
except Exception as e:
logger.error(f"Spanner save failed: {e}")
return {"status": "error", "error": str(e)}
En fournissant aux agents des outils de haut niveau, nous assurons l'intégrité des données tout en tirant parti de leurs capacités de raisonnement.
5. Mettre Ă jour le service GCS
GCSService gÚre l'importation du fichier vers Google Cloud Storage.
đđ»Â Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell :
cloudshell edit ~/way-back-home/level_2/backend/services/gcs_service.py
đ Dans le fichier gcs_service.py, recherchez le commentaire # TODO: REPLACE_SAVE_TO_GCS Ă l'intĂ©rieur de la fonction upload_file.
Remplacez toute cette ligne par le code suivant :
blob = self.bucket.blob(blob_name)
blob.upload_from_filename(file_path)
En abstrayant cela dans un service, l'agent n'a pas besoin de connaßtre les buckets GCS, les noms de blobs ni la génération d'URL signées. Il vous demande simplement d'importer le fichier.
6. (Lecture seule) Pourquoi le workflow agentique > les approches traditionnelles ?
L'avantage de l'agentivité :
Fonctionnalité | Pipeline par lot | En fonction des événements | Workflow agentif |
ComplexitĂ© | Faible (1 script) | ĂlevĂ©e (5 services ou plus) | Faible (1 fichier Python : |
Gestion de l'Ă©tat | Variables globales | ĂlevĂ©e (dĂ©couplĂ©e) | UnifiĂ© (Ă©tat de l'agent) |
Gestion des erreurs | Plantages | Journaux silencieux | Interactive ("Je n'ai pas pu lire ce fichier") |
Commentaires des utilisateurs | Impressions sur console | Recherche nécessaire | Immédiat (partie du chat) |
Adaptabilité | Logique fixe | Fonctions rigides | Intelligente (le LLM décide de l'étape suivante) |
Conscience du contexte | Aucun | Aucun | ComplĂšte (connaĂźt l'intention de l'utilisateur) |
Pourquoi est-ce important ? En utilisant multimedia_agent.py (un SequentialAgent avec quatre sous-agents : Upload â Extract â Save â Summary), nous remplaçons une infrastructure complexe ET des scripts fragiles par une logique d'application intelligente et conversationnelle.
10. Pipeline multimodal : couche de l'agent
La couche d'agent définit l'intelligence, c'est-à -dire les agents qui utilisent des outils pour accomplir des tùches. Chaque agent a un rÎle spécifique et transmet le contexte à l'agent suivant. Vous trouverez ci-dessous un schéma d'architecture pour un systÚme multi-agents.

1. Ouvrir le fichier de l'agent
đđ»Â Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell :
cloudshell edit ~/way-back-home/level_2/backend/agent/multimedia_agent.py
2. Définir l'agent d'importation
Cet agent extrait un chemin d'accĂšs Ă un fichier du message de l'utilisateur et l'importe dans GCS.
đ Dans le fichier multimedia_agent.py, recherchez le commentaire # TODO: REPLACE_UPLOAD_AGENT.
Remplacez toute cette ligne par le code suivant :
upload_agent = LlmAgent(
name="UploadAgent",
model="gemini-2.5-flash",
instruction="""Extract the file path from the user's message and upload it.
Use `upload_media(file_path, survivor_id)` to upload the file.
The survivor_id is optional - include it if the user mentions a specific survivor (e.g., "survivor Sarah" -> "Sarah").
If the user provides a path like "/path/to/file", use that.
Return the upload result with gcs_uri and media_type.""",
tools=[upload_media],
output_key="upload_result"
)
3. Définir l'agent d'extraction
Cet agent "voit" le contenu multimédia importé et extrait des données structurées à l'aide de Gemini Vision.
đ Dans le fichier multimedia_agent.py, recherchez le commentaire # TODO: REPLACE_EXTRACT_AGENT.
Remplacez toute cette ligne par le code suivant :
extraction_agent = LlmAgent(
name="ExtractionAgent",
model="gemini-2.5-flash",
instruction="""Extract information from the uploaded media.
Previous step result: {upload_result}
Use `extract_from_media(gcs_uri, media_type, signed_url)` with the values from the upload result.
The gcs_uri is in upload_result['gcs_uri'], media_type in upload_result['media_type'], and signed_url in upload_result['signed_url'].
Return the extraction results including entities and relationships found.""",
tools=[extract_from_media],
output_key="extraction_result"
)
Notez que instruction fait référence à {upload_result}. C'est ainsi que l'état est transmis entre les agents dans ADK.
4. Définir l'agent Spanner
Cet agent enregistre les entités et les relations extraites dans la base de données de graphes.
đ Dans le fichier multimedia_agent.py, recherchez le commentaire # TODO: REPLACE_SPANNER_AGENT.
Remplacez toute cette ligne par le code suivant :
spanner_agent = LlmAgent(
name="SpannerAgent",
model="gemini-2.5-flash",
instruction="""Save the extracted information to the database.
Upload result: {upload_result}
Extraction result: {extraction_result}
Use `save_to_spanner(extraction_result, survivor_id)` to save to Spanner.
Pass the WHOLE `extraction_result` object/dict from the previous step.
Include survivor_id if it was provided in the upload step.
Return the save statistics.""",
tools=[save_to_spanner],
output_key="spanner_result"
)
Cet agent reçoit le contexte des deux étapes précédentes (upload_result et extraction_result).
5. Définir l'agent Summary
Cet agent synthétise les résultats de toutes les étapes précédentes dans une réponse conviviale.
đ Dans le fichier multimedia_agent.py, recherchez le commentaire summary_instruction="" # TODO: REPLACE_SUMMARY_AGENT_PROMPT.
Remplacez toute cette ligne par le code suivant :
USE_MEMORY_BANK = os.getenv("USE_MEMORY_BANK", "false").lower() == "true"
save_msg = "6. Mention that the data is also being synced to the memory bank." if USE_MEMORY_BANK else ""
summary_instruction = f"""Provide a user-friendly summary of the media processing.
Upload: {{upload_result}}
Extraction: {{extraction_result}}
Database: {{spanner_result}}
Summarize:
1. What file was processed (name and type)
2. Key information extracted (survivors, skills, needs, resources found) - list names and counts
3. Relationships identified
4. What was saved to the database (broadcast ID, number of entities)
5. Any issues encountered
{save_msg}
Be concise but informative."""
Cet agent n'a pas besoin d'outils. Il se contente de lire le contexte partagé et de générer un résumé clair pour l'utilisateur.
𧠠Résumé de l'architecture
intégrée | Fichier | Responsabilité |
Outils |
| Comment faire ? : importer, extraire, enregistrer |
Agent |
| Quoi : orchestrer le pipeline |
11. Pipeline de données multimodales : orchestration
Le cĆur de notre nouveau systĂšme est le MultimediaExtractionPipeline dĂ©fini dans backend/agent/multimedia_agent.py. Il utilise le modĂšle Sequential Agent de l'ADK (Agent Development Kit).
1. Pourquoi utiliser les campagnes séquentielles ?
Le traitement d'un import est une chaßne de dépendances linéaires :
- Vous ne pouvez pas extraire de données tant que vous n'avez pas le fichier (importation).
- Vous ne pouvez pas enregistrer de données tant que vous ne les avez pas extraites.
- Vous ne pouvez pas résumer tant que vous n'avez pas les résultats (Enregistrer).
Un SequentialAgent est idéal pour cela. Il transmet la sortie d'un agent en tant que contexte/entrée à l'agent suivant.
2. Définition de l'agent
Examinons comment le pipeline est assemblĂ© en bas de multimedia_agent.py : đđ»Â Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell en exĂ©cutant la commande suivante :
cloudshell edit ~/way-back-home/level_2/backend/agent/multimedia_agent.py
Il reçoit des entrées des deux étapes précédentes. Recherchez le commentaire # TODO: REPLACE_ORCHESTRATION. Remplacez toute cette ligne par le code suivant :
sub_agents=[upload_agent, extraction_agent, spanner_agent, summary_agent]
3. Contacter l'agent racine
đđ»Â Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell en exĂ©cutant la commande suivante :
cloudshell edit ~/way-back-home/level_2/backend/agent/agent.py
Recherchez le commentaire # TODO: REPLACE_ADD_SUBAGENT. Remplacez toute cette ligne par le code suivant :
sub_agents=[multimedia_agent],
Cet objet unique regroupe quatre "experts" en une seule entité appelable.
4. Flux de données entre les agents
Chaque agent stocke sa sortie dans un contexte partagé auquel les agents suivants peuvent accéder :

5. Ouvrez l'application (ignorez cette étape si l'application est toujours en cours d'exécution).
đđ»Â DĂ©marrer l'application :
cd ~/way-back-home/level_2/
./start_app.sh
đ Cliquez sur Local : http://localhost:5173/ dans le terminal.
6. Tester l'importation d'images
đ Dans l'interface de chat, choisissez l'une des photos ci-dessous et importez-la dans l'interface utilisateur :
Dans l'interface de chat, expliquez à l'agent votre contexte spécifique :
Here is the survivor note
Joignez ensuite l'image ici.


đđ»Â Dans le terminal, une fois les tests terminĂ©s, appuyez sur "Ctrl+C" pour mettre fin au processus.
6. Vérifier l'importation multimodale dans un bucket GCS
- Ouvrez Google Cloud Console Storage.
- Sélectionnez "bucket" dans Cloud Storage.

- Sélectionnez votre bucket, puis cliquez sur
media.

- Affichez l'image que vous avez importée ici.

7. Vérifier l'importation multimodale dans Spanner (facultatif)
Vous trouverez ci-dessous un exemple de résultat dans l'UI pour test_photo1.
- Ouvrez Google Cloud Console Spanner.
- Sélectionnez votre instance :
Survivor Network - Sélectionnez votre base de données :
graph-db - Dans la barre latérale de gauche, cliquez sur Spanner Studio.
đ Dans Spanner Studio, interrogez les nouvelles donnĂ©es :
SELECT
s.name AS Survivor,
s.role AS Role,
b.name AS Biome,
r.name AS FoundResource,
s.created_at
FROM Survivors s
LEFT JOIN SurvivorInBiome sib ON s.survivor_id = sib.survivor_id
LEFT JOIN Biomes b ON sib.biome_id = b.biome_id
LEFT JOIN SurvivorFoundResource sfr ON s.survivor_id = sfr.survivor_id
LEFT JOIN Resources r ON sfr.resource_id = r.resource_id
ORDER BY s.created_at DESC;
Nous pouvons le vérifier en consultant le résultat ci-dessous :

12. Banque de mémoire avec Agent Engine
1. Fonctionnement de la mémoire
Le systÚme utilise une approche à double mémoire pour gérer à la fois le contexte immédiat et l'apprentissage à long terme.

2. Que sont les thÚmes de souvenirs ?
Les thÚmes de mémoire définissent les catégories d'informations que l'agent doit mémoriser au cours des conversations. Considérez-les comme des classeurs pour différents types de préférences utilisateur.
Nos deux thÚmes :
search_preferences : la façon dont l'utilisateur aime effectuer des recherches- PréfÚrent-ils la recherche par mots clés ou la recherche sémantique ?
- Quelles compétences/quels biomes recherchent-ils souvent ?
- Exemple de mémoire : "L'utilisateur préfÚre la recherche sémantique pour les compétences médicales"
urgent_needs_context : les crises qu'ils suivent- Quelles ressources surveillent-ils ?
- Quels survivants sont concernés ?
- Exemple de mémoire : "L'utilisateur suit la pénurie de médicaments dans le camp nord"
3. Configurer des thÚmes de mémoire
Les thÚmes de mémoire personnalisés définissent ce que l'agent doit retenir. Ils sont configurés lors du déploiement d'Agent Engine.
đđ»Â Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell en exĂ©cutant la commande suivante :
cloudshell edit ~/way-back-home/level_2/backend/deploy_agent.py
~/way-back-home/level_2/backend/deploy_agent.py s'ouvre dans votre éditeur.
Nous définissons des objets de structure MemoryTopic pour indiquer au LLM les informations à extraire et à enregistrer.
đ Dans le fichier deploy_agent.py, remplacez # TODO: SET_UP_TOPIC par ce qui suit :
# backend/deploy_agent.py
custom_topics = [
# Topic 1: Survivor Search Preferences
MemoryTopic(
custom_memory_topic=CustomMemoryTopic(
label="search_preferences",
description="""Extract the user's preferences for how they search for survivors. Include:
- Preferred search methods (keyword, semantic, direct lookup)
- Common filters used (biome, role, status)
- Specific skills they value or frequently look for
- Geographic areas of interest (e.g., "forest biome", "mountain outpost")
Example: "User prefers semantic search for finding similar skills."
Example: "User frequently checks for survivors in the Swamp Biome."
""",
)
),
# Topic 2: Urgent Needs Context
MemoryTopic(
custom_memory_topic=CustomMemoryTopic(
label="urgent_needs_context",
description="""Track the user's focus on urgent needs and resource shortages. Include:
- Specific resources they are monitoring (food, medicine, ammo)
- Critical situations they are tracking
- Survivors they are particularly concerned about
Example: "User is monitoring the medicine shortage in the Northern Camp."
Example: "User is looking for a doctor for the injured survivors."
""",
)
)
]
4. Intégration de l'agent
Le code de l'agent doit connaßtre la Memory Bank pour enregistrer et récupérer des informations.
đđ»Â Dans le terminal, ouvrez le fichier dans l'Ă©diteur Cloud Shell en exĂ©cutant la commande suivante :
cloudshell edit ~/way-back-home/level_2/backend/agent/agent.py
~/way-back-home/level_2/backend/agent/agent.py s'ouvre dans votre éditeur.
Création d'agents
Lorsque nous créons l'agent, nous transmettons after_agent_callback pour nous assurer que les sessions sont enregistrées en mémoire aprÚs les interactions. La fonction add_session_to_memory s'exécute de maniÚre asynchrone pour éviter de ralentir la réponse du chat.
đ Dans le fichier agent.py, recherchez le commentaire # TODO: REPLACE_ADD_SESSION_MEMORY, Replace this whole line (Remplacez toute cette ligne) et remplacez-le par le code suivant :
async def add_session_to_memory(
callback_context: CallbackContext
) -> Optional[types.Content]:
"""Automatically save completed sessions to memory bank in the background"""
if hasattr(callback_context, "_invocation_context"):
invocation_context = callback_context._invocation_context
if invocation_context.memory_service:
# Use create_task to run this in the background without blocking the response
asyncio.create_task(
invocation_context.memory_service.add_session_to_memory(
invocation_context.session
)
)
logger.info("Scheduled session save to memory bank in background")
Enregistrement en arriĂšre-plan
đ Dans le fichier agent.py, recherchez le commentaire # TODO: REPLACE_ADD_MEMORY_BANK_TOOL, Replace this whole line (Remplacez toute cette ligne) et remplacez-le par le code suivant :
if USE_MEMORY_BANK:
agent_tools.append(PreloadMemoryTool())
đ Dans le fichier agent.py, recherchez le commentaire # TODO: REPLACE_ADD_CALLBACK, Replace this whole line (Remplacez toute cette ligne) et remplacez-le par le code suivant :
after_agent_callback=add_session_to_memory if USE_MEMORY_BANK else None
Configurer le service de session Vertex AI
đđ»Â Dans le terminal, ouvrez le fichier chat.py dans l'Ă©diteur Cloud Shell en exĂ©cutant la commande suivante :
cloudshell edit ~/way-back-home/level_2/backend/api/routes/chat.py
đ Dans le fichier chat.py, recherchez le commentaire # TODO: REPLACE_VERTEXAI_SERVICES, Replace this whole line (Remplacez toute cette ligne) et remplacez-le par le code suivant :
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
)
4. Configuration et déploiement
Avant de tester les fonctionnalités de mémoire, vous devez déployer l'agent avec les nouveaux thÚmes de mémoire et vous assurer que votre environnement est correctement configuré.
Nous avons fourni un script pratique pour gérer ce processus.
Exécuter le script de déploiement
đđ»Â Dans le terminal, exĂ©cutez le script de dĂ©ploiement :
cd ~/way-back-home/level_2
./deploy_and_update_env.sh
Ce script effectue les actions suivantes :
- Exécute
backend/deploy_agent.pypour enregistrer les thÚmes de l'agent et de la mémoire auprÚs de Vertex AI. - Capture le nouvel ID du moteur d'agent.
- Mise Ă jour automatique de votre fichier
.envavecAGENT_ENGINE_ID. - Assurez-vous que
USE_MEMORY_BANK=TRUEest défini dans votre fichier.env.
[!IMPORTANT] Si vous apportez des modifications à custom_topics dans deploy_agent.py, vous devez réexécuter ce script pour mettre à jour Agent Engine.
13. Valider la banque de données avec des données multimodales
Vous pouvez vérifier que la banque de mémoire fonctionne en enseignant une préférence à l'agent et en vérifiant si elle persiste au fil des sessions.
1. Ouvrez l'application (ignorez cette étape si elle est déjà en cours d'exécution).
Ouvrez à nouveau l'application en suivant les instructions ci-dessous : si le terminal précédent est toujours en cours d'exécution, mettez-y fin en appuyant sur Ctrls+C.
đđ»Â DĂ©marrer l'application :
cd ~/way-back-home/level_2/
./start_app.sh
đ Cliquez sur Local : http://localhost:5173/ dans le terminal.
2. Tester la banque de mémoire avec du texte
Dans l'interface de chat, expliquez à l'agent votre contexte spécifique :
"I'm planning a medical rescue mission in the mountains. I need survivors with first aid and climbing skills."
đ Patientez environ 30 secondes pour que la mĂ©moire soit traitĂ©e en arriĂšre-plan.
2. Démarrer une nouvelle session
Actualisez la page pour effacer l'historique des conversations en cours (mémoire à court terme).
Posez une question qui s'appuie sur le contexte que vous avez fourni précédemment :
"What kind of missions am I interested in?"
Réponse attendue :
"D'aprÚs vos conversations précédentes, vous vous intéressez à  :
- Missions de sauvetage médical
- Opérations en montagne/en haute altitude
- Compétences requises : premiers secours, escalade
Voulez-vous que je trouve des survivants correspondant à ces critÚres ?"
3. Tester avec l'importation d'images
Importez une image et posez la question suivante :
remember this
Vous pouvez choisir l'une des photos ici ou la vĂŽtre, puis l'importer dans l'UIÂ :
4. Vérifier dans Vertex AI Agent Engine
Accédez à Agent Engine dans la console Google Cloud.
- Assurez-vous de sélectionner le projet dans le sélecteur de projets en haut à gauche :

- Vérifiez le moteur d'agent que vous venez de déployer à partir de la commande précédente
use_memory_bank.sh :
Cliquez sur le moteur d'agent que vous venez de créer. - Cliquez sur l'onglet
Memoriesde cet agent déployé pour afficher toute la mémoire.
đđ»Â Lorsque vous avez terminĂ© de tester, cliquez sur "Ctrl+C" dans votre terminal pour mettre fin au processus.
đ FĂ©licitations ! Vous venez d'associer la Memory Bank Ă votre agent.
14. Déployer dans Cloud Run
1. Exécuter le script de déploiement
đđ»Â ExĂ©cutez le script de dĂ©ploiement :
cd ~/way-back-home/level_2
./deploy_cloud_run.sh
Une fois le déploiement réussi, vous obtiendrez l'URL de déploiement :
.
đđ»Â Avant de rĂ©cupĂ©rer l'URL, accordez l'autorisation en exĂ©cutant la commande suivante :
source .env && gcloud run services add-iam-policy-binding survivor-frontend --region $REGION --member=allUsers --role=roles/run.invoker && gcloud run services add-iam-policy-binding survivor-backend --region $REGION --member=allUsers --role=roles/run.invoker
Accédez à l'URL déployée pour voir votre application en direct.
2. Comprendre le pipeline de compilation
Le fichier cloudbuild.yaml définit les étapes séquentielles suivantes :
- Compilation du backend : compile l'image Docker à partir de
backend/Dockerfile. - Déploiement du backend : déploie le conteneur de backend sur Cloud Run.
- Capture URLÂ : obtient la nouvelle URL du backend.
- Compilation du frontend :
- Installe les dépendances.
- Crée l'application React en injectant
VITE_API_URL=.
- Image de l'interface : crée l'image Docker à partir de
frontend/Dockerfile(en empaquetant les éléments statiques). - Frontend Deploy : déploie le conteneur de l'interface.
3. Vérifier le déploiement
Une fois la compilation terminée (consultez le lien vers les journaux fourni par le script), vous pouvez vérifier les points suivants :
- Accédez à la console Cloud Run.
- Recherchez le service
survivor-frontend. - Cliquez sur l'URL pour ouvrir l'application.
- ExĂ©cutez une requĂȘte de recherche pour vous assurer que le frontend peut communiquer avec le backend.
4. (!UNIQUEMENT POUR LES PARTICIPANTS Ă L'ATELIER) Mettre Ă jour votre position
đ đ»Â ExĂ©cutez le script de finalisation :
cd ~/way-back-home/level_2
./set_level_2.sh
Ouvrez waybackhome.dev. Vous verrez que votre position a été mise à jour. Bravo, vous avez terminé le niveau 2 !

(FACULTATIF) 5. Déploiement manuel
Si vous préférez exécuter les commandes manuellement ou mieux comprendre le processus, voici comment utiliser cloudbuild.yaml directement.
Ăcriture cloudbuild.yaml
Un fichier cloudbuild.yaml indique à Google Cloud Build les étapes à exécuter.
- steps : il s'agit d'une liste d'actions séquentielles. Chaque étape s'exécute dans un conteneur (par exemple,
docker,gcloud,node,bash). - substitutions : variables pouvant ĂȘtre transmises au moment de la compilation (par exemple,
$_REGION). - workspace : répertoire partagé dans lequel les étapes peuvent partager des fichiers (comme nous partageons
backend_url.txt).
Exécuter le déploiement
Pour effectuer le déploiement manuellement sans le script, utilisez la commande gcloud builds submit. Vous DEVEZ transmettre les variables de substitution requises.
# Load your env vars first or replace these values manually
export PROJECT_ID=your-project-id
export REGION=us-central1
gcloud builds submit --config cloudbuild.yaml \
--project "$PROJECT_ID" \
--substitutions _REGION="us-central1",_GOOGLE_API_KEY="",_AGENT_ENGINE_ID="your-agent-id",_USE_MEMORY_BANK="TRUE",_GOOGLE_GENAI_USE_VERTEXAI="TRUE"
15. Conclusion
1. Ce que vous avez créé
â
 Base de donnĂ©es graphiques : Spanner avec des nĆuds (survivants, compĂ©tences) et des arĂȘtes (relations)
â
 Recherche IA : recherche par mots clés, sémantique et hybride avec des embeddings
â
 Pipeline multimodal : extraction d'entités à partir d'images/vidéos avec Gemini
â
 SystÚme multi-agents : workflow coordonné avec ADK
â
 Banque de mémoire : personnalisation à long terme avec Vertex AI
â
 Déploiement en production : Cloud Run + Agent Engine
2. Résumé de l'architecture

3. Points clés
- Graph RAG : combine la structure de la base de données graphiques avec des embeddings sémantiques pour une recherche intelligente
- ModÚles multi-agents : pipelines séquentiels pour les workflows complexes en plusieurs étapes
- IA multimodale : extraire des données structurées à partir de contenus multimédias non structurés (images/vidéos)
- Agents avec état : la banque de mémoire permet la personnalisation entre les sessions
4. Contenu de l'atelier
- Level0Â : Identifiez-vous
- Level1 : Emplacement précis
- Niveau 2 : celui-ci : Créer un agent d'IA multimodal avec Graph RAG, ADK et Memory Bank
- Level3 : Créer un agent de streaming bidirectionnel ADK
- Level4Â : SystĂšme multi-agents bidirectionnel en direct
- Level5 : Architecture événementielle avec Google ADK, A2A et Kafka