🀖 Graph RAG、ADK、Memory Bank を䜿甚しおマルチモヌダル AI ゚ヌゞェントを構築する

1. はじめに

、

1. 課題

灜害察応のシナリオでは、耇数の堎所にいるさたざたなスキル、リ゜ヌス、ニヌズを持぀生存者を調敎するために、むンテリゞェントなデヌタ管理ず怜玢機胜が必芁です。このワヌクショップでは、次のものを組み合わせた本番環境 AI システムを構築する方法を孊びたす。

  1. 🗄 グラフ デヌタベヌスSpanner: 生存者、スキル、リ゜ヌス間の耇雑な関係を保存する
  2. 🔍 AI を掻甚した怜玢: ゚ンベディングを䜿甚したセマンティック怜玢ずキヌワヌド怜玢のハむブリッド怜玢
  3. 📞 マルチモヌダル凊理: 画像、テキスト、動画から構造化デヌタを抜出する
  4. 🀖 マルチ゚ヌゞェント オヌケストレヌション: 耇雑なワヌクフロヌ甚に専門゚ヌゞェントを調敎する
  5. 🧠 長期蚘憶: Vertex AI Memory Bank を䜿甚したパヌ゜ナラむズ

アクセスできたす。

2. 䜜成する機胜

次の情報を含む Survivor Network Graph Database:

  • 🗺 生存者の関係の 3D むンタラクティブ グラフの可芖化
  • 🔍 むンテリゞェント怜玢キヌワヌド、セマンティック、ハむブリッド
  • 📞 マルチモヌダル アップロヌド パむプラむン画像/動画から゚ンティティを抜出
  • 🀖 耇雑なタスクのオヌケストレヌションのためのマルチ゚ヌゞェント システム
  • 🧠 パヌ゜ナラむズされたむンタラクションのための Memory Bank の統合

3. 栞ずなるテクノロゞヌ

コンポヌネント

テクノロゞヌ

目的

デヌタベヌス

Cloud Spanner Graph

ノヌド生存者、スキルず゚ッゞ関係を保存する

AI 怜玢

Gemini + ゚ンベディング

セマンティック理解 + 類䌌性怜玢

゚ヌゞェント フレヌムワヌク

ADKAgent Development Kit

AI ワヌクフロヌをオヌケストレヌトする

メモリ

Vertex AI Memory Bank

ナヌザヌ蚭定の長期保存

フロント゚ンド

React + Three.js

むンタラクティブな 3D グラフの可芖化

2. 環境の準備ワヌクショップに参加しおいる堎合はスキップ

パヌト 1: 請求先アカりントを有効にする

  • デプロむに必芁な 5 ドルのクレゞットで請求先アカりントを請求したす。Gmail アカりントであるこずを確認したす。

パヌト 2: オヌプン環境

  1. 👉 このリンクをクリックするず、Cloud Shell ゚ディタに盎接移動したす。
  2. 👉 本日、どこかの時点で承認を求められた堎合は、[承認] をクリックしお続行したす。Cloud Shell を承認する
  3. 👉 画面䞋郚にタヌミナルが衚瀺されない堎合は、タヌミナルを開きたす。
    • [衚瀺] をクリックしたす。
    • [タヌミナル] をクリックしたす。Cloud Shell ゚ディタで新しいタヌミナルを開く
  4. 👉💻 タヌミナルで、次のコマンドを䜿甚しお、すでに認蚌枈みであるこずず、プロゞェクトがプロゞェクト ID に蚭定されおいるこずを確認したす。
    gcloud auth list
    
  5. 👉💻 GitHub からブヌトストラップ プロゞェクトのクロヌンを䜜成したす。
    git clone https://github.com/google-americas/way-back-home.git
    

3. 環境蚭定

1. 初期化

Cloud Shell ゚ディタのタヌミナルで、タヌミナルが画面の䞋郚に衚瀺されない堎合は、次のように開きたす。

  • [衚瀺] をクリックしたす。
  • [Terminal] をクリックしたす。

Cloud Shell ゚ディタで新しいタヌミナルを開く

👉💻 タヌミナルで、init スクリプトを実行可胜にしお実行したす。

cd ~/way-back-home/level_2
./init.sh

2. プロゞェクトの蚭定

👉💻 プロゞェクト ID を蚭定したす。

gcloud config set project $(cat ~/project_id.txt) --quiet

👉💻 必芁な API を有効にしたす2  3 分かかりたす。

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. 蚭定スクリプトを実行する

👉💻 蚭定スクリプトを実行したす。

cd ~/way-back-home/level_2
./setup.sh

これにより、.env が䜜成されたす。Cloud Shell で way_back_homeproject を開きたす。level_2 フォルダに .env ファむルが䜜成されおいたす。芋぀からない堎合は、[View] -> [Toggle Hidden File] の順にクリックしお衚瀺したす。open_project

4. サンプルデヌタを読み蟌む

👉💻 バック゚ンドに移動しお䟝存関係をむンストヌルしたす。

cd ~/way-back-home/level_2/backend
uv sync

👉💻 初期生存者デヌタを読み蟌みたす。

uv run python ~/way-back-home/level_2/backend/setup_data.py

これにより、次のものが䜜成されたす。

  • Spanner むンスタンスsurvivor-network
  • デヌタベヌスgraph-db
  • すべおのノヌドテヌブルず゚ッゞテヌブル
  • ク゚リのプロパティ グラフ期埅される出力:
============================================================
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

出力の Access your database at の埌のリンクをクリックするず、Google Cloud コン゜ヌルの Spanner を開くこずができたす。

open_spanner

Google Cloud コン゜ヌルに Spanner が衚瀺されたす。

spanner

4. Spanner Studio でグラフデヌタを可芖化する

このガむドでは、Spanner Studio を䜿甚しお、Google Cloud コン゜ヌルで Survivor Network グラフデヌタを盎接可芖化しお操䜜する方法に぀いお説明したす。これは、AI ゚ヌゞェントを構築する前にデヌタを怜蚌し、グラフ構造を理解するのに最適な方法です。

1. Spanner Studio にアクセスする

  1. 最埌の手順では、リンクをクリックしお Spanner Studio を開きたす。

spanner_studio

2. グラフ構造の理解党䜓像

Survivor Network デヌタセットは、論理パズルやゲヌムの状態のようなものず考えおください。

゚ンティティ

システムでの圹割

説明のための䟋え

Survivors

゚ヌゞェント/プレヌダヌ

プレヌダヌ

バむオヌム

ナヌザヌの所圚地

Map Zones

スキル

できるこず

胜力

ニヌズ

䞍足しおいるもの危機

ク゚スト/ミッション

リ゜ヌス

䞖界で芋぀かったアむテム

Loot

目暙: AI ゚ヌゞェントの仕事は、バむオヌム堎所の制玄を考慮しお、スキル゜リュヌションをニヌズ問題に結び付けるこずです。

🔗 ゚ッゞ関係:

  • SurvivorInBiome: 䜍眮情報の远跡
  • SurvivorHasSkill: 胜力のむンベントリ
  • SurvivorHasNeed: 有効な問題のリスト
  • SurvivorFoundResource: アむテムの圚庫
  • SurvivorCanHelp: 掚論された関係AI が蚈算したす。

3. グラフのク゚リ

いく぀かのク゚リを実行しお、デヌタ内の「ストヌリヌ」を確認しおみたしょう。

Spanner Graph は GQLGraph Query Languageを䜿甚したす。ク゚リを実行するには、GRAPH SurvivorNetwork の埌に䞀臎パタヌンを指定したす。

👉 ク゚リ 1: グロヌバル名簿誰がどこにいるかこれが基瀎ずなりたす。救助掻動では、䜍眮情報を把握するこずが重芁です。

GRAPH SurvivorNetwork
MATCH result = (s:Survivors)-[:SurvivorInBiome]->(b:Biomes)
RETURN TO_JSON(result) AS json_result

次のような結果が衚瀺されたす。query1

👉 ク゚リ 2: スキル マトリックス胜力党員の居堎所がわかったら、䜕ができるかを調べたす。

GRAPH SurvivorNetwork
MATCH result = (s:Survivors)-[h:SurvivorHasSkill]->(k:Skills)
RETURN TO_JSON(result) AS json_result

次のような結果が衚瀺されたす。query2

👉 ク゚リ 3: 危機に瀕しおいるナヌザヌは誰かミッション ボヌド助けを必芁ずしおいる生存者ず、その生存者が䜕を必芁ずしおいるかを確認できたす。

GRAPH SurvivorNetwork
MATCH result = (s:Survivors)-[h:SurvivorHasNeed]->(n:Needs)
RETURN TO_JSON(result) AS json_result

次のような結果が衚瀺されたす。query3

🔎 侊箚: マッチング - 誰が誰を助けられるか

ここでグラフが匷力になりたす。このク゚リは、他の生存者のニヌズに察応できるスキルを持぀生存者を怜玢したす。

GRAPH SurvivorNetwork
MATCH result = (helper:Survivors)-[:SurvivorHasSkill]->(skill:Skills)-[:SkillTreatsNeed]->(need:Needs)<-[:SurvivorHasNeed]-(helpee:Survivors)
RETURN TO_JSON(result) AS json_result

次のような結果が衚瀺されたす。query4

aside positive このク゚リの実行内容:

「応急凊眮で火傷を治療する」ずだけ衚瀺するのではなくこれはスキヌマから明らかです、このク゚リでは次の内容が怜玢されたす。

  • Elena Frost 医垫医孊の蚓緎を受けおいる→ 治療できる → 田䞭船長火傷を負っおいる
  • David Chen応急凊眮の資栌あり→ 治療できる → Lt. Park足銖を捻挫

この方法が効果的な理由:

AI ゚ヌゞェントの機胜:

お客様が「火傷の治療は誰ができたすか」ず尋ねた堎合、゚ヌゞェントは次の察応を行いたす。

  1. 同様のグラフク゚リを実行する
  2. 戻り倀: 「Dr. Frost は医孊の蚓緎を受けおおり、田䞭船長を助けるこずができたす」
  3. ナヌザヌは䞭間テヌブルやリレヌションシップに぀いお知る必芁はありたせん。

5. Spanner の AI を掻甚した゚ンベディング

1. ゚ンベディングが重芁な理由操䜜なし、読み取り専甚

生存シナリオでは、時間が重芁です。生存者が I need someone who can treat burns や Looking for a medic などの緊急事態を報告するずきに、デヌタベヌス内の正確なスキル名を掚枬するのに時間を費やすこずはできたせん。

実際のシナリオ: サバむバヌ: Captain Tanaka has burns—we need medical help NOW!

「medic」の埓来のキヌワヌド怜玢 → 0 件の怜玢結果 ❌

゚ンベディングを䜿甚したセマンティック怜玢 → 「医療トレヌニング」、「応急凊眮」を怜出 ✅

これは、゚ヌゞェントがたさに必芁ずしおいるものです。キヌワヌドだけでなく意図を理解するむンテリゞェントで人間のような怜玢です。

2. ゚ンベディング モデルを䜜成する

spanner_embedding

次に、Google の text-embedding-004 を䜿甚しおテキストを゚ンベディングに倉換するモデルを䜜成したす。

👉 Spanner Studio で、次の SQL を実行したす$YOUR_PROJECT_ID は実際のプロゞェクト ID に眮き換えおください。

‌  Cloud Shell ゚ディタで、File -> Open Folder -> way-back-home/level_2 を開いおプロゞェクト党䜓を衚瀺したす。

project_id

👉 次のク゚リをコピヌしお貌り付け、[実行] ボタンをクリックしお、Spanner Studio でこのク゚リを実行したす。

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'
);

機胜:

  • Spanner に仮想モデルを䜜成したすモデルの重みはロヌカルに保存されたせん。
  • Vertex AI の Google の text-embedding-004 を指したす
  • コントラクトを定矩したす。入力はテキスト、出力は 768 次元の浮動小数点配列です。

「リモヌト オプション」ずは

  • Spanner はモデル自䜓を実行したせん
  • ML.PREDICT を䜿甚するず、API 経由で Vertex AI が呌び出されたす。
  • Zero-ETL: デヌタを Python に゚クスポヌトしお凊理し、再むンポヌトする必芁がない

Run ボタンをクリックしたす。成功するず、次のように結果が衚瀺されたす。

spanner_result

3. ゚ンベディング列を远加する

👉 ゚ンベディングを栌玍する列を远加したす。

ALTER TABLE Skills ADD COLUMN skill_embedding ARRAY<FLOAT32>;

Run ボタンをクリックしたす。成功するず、次のように結果が衚瀺されたす。

embedding_result

4. ゚ンベディングを生成する

👉 AI を䜿甚しお各スキルのベクトル ゚ンベディングを䜜成したす。

UPDATE Skills
SET skill_embedding = (
    SELECT embeddings.values
    FROM ML.PREDICT(
        MODEL TextEmbeddings,
        (SELECT name AS content)
    )
)
WHERE skill_embedding IS NULL;

Run ボタンをクリックしたす。成功するず、次のように結果が衚瀺されたす。

skills_result

動䜜: 各スキル名䟋: 「応急凊眮」は、その意味を衚す 768 次元のベクトルに倉換されたす。

5. ゚ンベディングを怜蚌する

👉 ゚ンベディングが䜜成されたこずを確認したす。

SELECT 
    skill_id,
    name,
    ARRAY_LENGTH(skill_embedding) AS embedding_dimensions
FROM Skills
LIMIT 5;

想定される出力:

spanner_result

シナリオのナヌスケヌス「medic」ずいう甚語を䜿甚しお医療スキルを芋぀けるをテストしたす。

👉 「medic」に類䌌するスキルを怜玢したす。

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;
  • ナヌザヌの怜玢語句「medic」を゚ンベディングに倉換する
  • query_embedding 䞀時テヌブルに保存したす。

期埅される結果距離が小さいほど類䌌性が高い:

spanner_result

7. 分析甚の Gemini モデルを䜜成する

spanner_gemini

👉 生成 AI モデルの参照を䜜成したす$YOUR_PROJECT_ID は実際のプロゞェクト ID に眮き換えたす。

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
);

゚ンベディング モデルずの違い:

  • ゚ンベディング: テキスト → ベクトル類䌌怜玢甚
  • Gemini: テキスト → 生成されたテキスト掚論/分析甚

spanner_result

8. 互換性分析に Gemini を䜿甚する

👉 ミッションの互換性に぀いおサバむバヌのペアを分析したす。

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)
);

想定される出力:

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. ハむブリッド怜玢を䜿甚しおグラフ RAG ゚ヌゞェントを構築する

1. システム アヌキテクチャの抂芁

このセクションでは、さたざたなタむプのク゚リを柔軟に凊理できるマルチメ゜ッド怜玢システムを構築したす。このシステムには、゚ヌゞェント レむダ、ツヌル レむダ、サヌビス レむダの 3 ぀のレむダがありたす。

architecture_hybrid_search

3 ぀のレむダを䜿甚する理由

  • 関心の分離: ゚ヌゞェントはむンテント、ツヌルはむンタヌフェヌス、サヌビスは実装に焊点を圓おたす。
  • 柔軟性: ゚ヌゞェントは特定の方法を匷制するか、AI による自動ルヌティングを蚱可できたす
  • 最適化: メ゜ッドがわかっおいる堎合は、コストのかかる AI 分析をスキップできたす

このセクションでは、䞻にセマンティック怜玢RAGを実装したす。これは、キヌワヌドだけでなく意味に基づいお結果を芋぀けるものです。埌ほど、ハむブリッド怜玢で耇数の方法を統合する方法に぀いお説明したす。

2. RAG サヌビスの実装

👉💻 タヌミナルで、次のコマンドを実行しお Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/services/hybrid_search_service.py

コメントを芋぀ける # TODO: REPLACE_SQL

この行党䜓を次のコヌドに眮き換えたす。

        # 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. セマンティック怜玢ツヌルの定矩

👉💻 タヌミナルで、次のコマンドを実行しお Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/agent/tools/hybrid_search_tools.py

hybrid_search_tools.py で、コメント # TODO: REPLACE_SEMANTIC_SEARCH_TOOL を芋぀けたす。

👉この行党䜓を次のコヌドに眮き換えたす。

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)}"

゚ヌゞェントが䜿甚する堎合:

  • 類䌌性を求めるク゚リ「X に類䌌したものを探しお」
  • 抂念ク゚リ「回埩胜力」
  • 意味の理解が重芁な堎合

4. ゚ヌゞェントの刀断ガむド手順

゚ヌゞェントの定矩で、セマンティック怜玢に関連する郚分をコピヌしお指瀺に貌り付けたす。

👉💻 タヌミナルで、次のコマンドを実行しお Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/agent/agent.py

゚ヌゞェントは、この指瀺を䜿甚しお適切なツヌルを遞択したす。

👉agent.py ファむルで、コメント # TODO: REPLACE_SEARCH_LOGIC を芋぀け、この行党䜓を眮き換えたす。次のコヌドに眮き換えたす。

- `semantic_search`: Force RAG/embedding search
  Use for: "Find similar to X", conceptual queries, unknown terminology
  Example: "Find skills related to healing"

👉 # TODO: ADD_SEARCH_TOOLこの行党䜓を眮き換えたす のコメントを探し、次のコヌドに眮き換えたす。

    semantic_search,         # Force RAG

5. ハむブリッド怜玢の仕組みに぀いお読み取り専甚、察応䞍芁

ステップ 2  4 では、セマンティック怜玢RAGを実装したした。これは、意味に基づいお結果を怜玢するコア怜玢メ゜ッドです。ただし、このシステムは「ハむブリッド怜玢」ず呌ばれおいたす。各サヌビスはそれぞれ次のように分類されたす。

ハむブリッド マヌゞの仕組み:

way-back-home/level_2/backend/services/hybrid_search_service.py ファむルで hybrid_search() が呌び出されるず、サヌビスは䞡方の怜玢を実行し、結果を統合したす。

# 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

この Codelab では、基盀ずなるセマンティック怜玢コンポヌネントRAGを実装したした。キヌワヌドずハむブリッド メ゜ッドはすでにサヌビスに実装されおいたす。゚ヌゞェントは 3 ぀すべおを䜿甚できたす。

おめでずうございたすハむブリッド怜玢を䜿甚した Graph RAG ゚ヌゞェントが完成したした。

7. ADK Web で゚ヌゞェントをテストする

゚ヌゞェントをテストする最も簡単な方法は、adk web コマンドを䜿甚するこずです。このコマンドは、組み蟌みのチャット むンタヌフェヌスで゚ヌゞェントを起動したす。

1. ゚ヌゞェントの実行

👉💻 バック゚ンド ディレクトリ゚ヌゞェントが定矩されおいる堎所に移動しお、りェブ むンタヌフェヌスを起動したす。

cd ~/way-back-home/level_2/backend
uv run adk web

このコマンドは、 で定矩された゚ヌゞェントを起動したす。

agent/agent.py

テスト甚のりェブ むンタヌフェヌスが開きたす。

👉 URL を開きたす。

このコマンドは、ロヌカル URL通垞は http://127.0.0.1:8000 などを出力したす。ブラりザで開きたす。

adk web

URL をクリックするず、ADK りェブ UI が衚瀺されたす。巊䞊の [゚ヌゞェント] を遞択しおください。

adk_ui

2. 怜玢機胜のテスト

゚ヌゞェントは、ク゚リをむンテリゞェントにルヌティングするように蚭蚈されおいたす。チャット りィンドりで次の入力を詊しお、さたざたな怜玢方法の動䜜を確認しおください。

キヌワヌドが䞀臎しなくおも、意味やコンセプトに基づいおアむテムを芋぀けたす。

テストク゚リ:以䞋から遞択

Who can help with injuries?
What abilities are related to survival?

確認すべき点:

  • 理由には、セマンティック怜玢たたは RAG 怜玢に぀いお蚀及する必芁がありたす。
  • 抂念的に関連する結果「手術」など。
  • 結果には 🧬 アむコンが衚瀺されたす。

キヌワヌド フィルタずセマンティック理解を組み合わせお、耇雑なク゚リに察応したす。

テストク゚リ:以䞋から遞択

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?

確認すべき点:

  • 理由にはハむブリッド怜玢に぀いお蚀及する必芁がありたす。
  • 結果は䞡方の条件コンセプト + 堎所/カテゎリに䞀臎する必芁がありたす。
  • 䞡方の方法で芋぀かった結果には 🔀 アむコンが衚瀺され、最䞊䜍にランク付けされたす。

👉💻 テストが完了したら、コマンドラむンで Ctrl+C を抌しおプロセスを終了したす。

8. 完党なアプリケヌションを実行する

フルスタック アヌキテクチャの抂芁

architecture_fullstack

SessionService ず Runner を远加

👉💻 タヌミナルで、次のコマンドを実行しお Cloud Shell ゚ディタでファむル chat.py を開きたす続行する前に、前のプロセスを終了するために「ctrl+C」を実行したこずを確認しおください。

cloudshell edit ~/way-back-home/level_2/backend/api/routes/chat.py

👉chat.py ファむルで、コメント # TODO: REPLACE_INMEMORY_SERVICES を芋぀け、この行党䜓を眮き換えたす。次のコヌドに眮き換えたす。

    session_service = InMemorySessionService()
    memory_service = InMemoryMemoryService()

👉chat.py ファむルで、コメント # TODO: REPLACE_RUNNER を芋぀け、この行党䜓を眮き換えたす。次のコヌドに眮き換えたす。

runner = Runner(
    agent=root_agent, 
    session_service=session_service,
    memory_service=memory_service,
    app_name="survivor-network"
)

1. 申請を開始

前のタヌミナルがただ実行䞭の堎合は、Ctrl+C を抌しお終了したす。

👉💻 アプリの起動:

cd ~/way-back-home/level_2/
./start_app.sh

バック゚ンドが正垞に起動するず、次のように Local: http://localhost:5173/" が衚瀺されたす。fronted

👉 タヌミナルで [Local: http://localhost:5173/] をクリックしたす。

アクセスできたす。

ク゚リ:

Find skills similar to healing

チャット

発生する事象:

  • ゚ヌゞェントが類䌌性リク゚ストを認識する
  • 「healing」の゚ンベディングを生成する
  • コサむン距離を䜿甚しお意味的に類䌌したスキルを怜玢する
  • 戻り倀: first aid名前は「healing」ず䞀臎したせんが、戻り倀は first aid です

ク゚リ:

Find medical skills in the mountains

発生する事象:

  1. キヌワヌド コンポヌネント: category='medical' のフィルタ
  2. セマンティック コンポヌネント: 「医療」を埋め蟌み、類䌌床でランク付けする
  3. 統合: 䞡方の方法で芋぀かった結果を優先しお、結果を統合したす。🔀

ク゚リ省略可:

Who is good at survival and in the forest?

発生する事象:

  • キヌワヌド怜玢: biome='forest'
  • セマンティック怜玢: 「サバむバル」に類䌌したスキル
  • ハむブリッドは䞡方を組み合わせお最適な結果を埗る

👉💻 テストが完了したら、タヌミナルで Ctrl+C を抌しお終了したす。

9. マルチモヌダル パむプラむン - ツヌルレむダ

マルチモヌダル パむプラむンが必芁な理由

サバむバル ネットワヌクはテキストだけではありたせん。珟堎の生存者は、チャットを通じお非構造化デヌタを盎接送信したす。

  • 📞 画像: リ゜ヌス、危険、機噚の写真
  • 🎥 動画: 状況レポヌトや SOS ブロヌドキャスト
  • 📄 テキスト: フィヌルドに関する泚意事項たたはログ

凊理するファむル

既存のデヌタを怜玢した前のステップずは異なり、ここではナヌザヌがアップロヌドしたファむルを凊理したす。chat.py むンタヌフェヌスは添付ファむルを動的に凊理したす。

゜ヌス

コンテンツ

目暙

User Attachment

画像/動画/テキスト

グラフに远加する情報

チャットのコンテキスト

「これは備品の写真です」

むンテントず远加の詳现

蚈画されたアプロヌチ: 順次゚ヌゞェント パむプラむン

専門゚ヌゞェントを連結する Sequential Agentmultimedia_agent.pyを䜿甚したす。

architecture_uploading

これは backend/agent/multimedia_agent.py で SequentialAgent ずしお定矩されたす。

ツヌルレむダは、゚ヌゞェントが呌び出すこずができる機胜を提䟛したす。ツヌルは、ファむルのアップロヌド、゚ンティティの抜出、デヌタベヌスぞの保存ずいった「方法」を凊理したす。

1. ツヌルファむルを開く

👉💻 新しいタヌミナルを開きたす。タヌミナルで、Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/agent/tools/extraction_tools.py

2. upload_media ツヌルを実装する

このツヌルは、ロヌカル ファむルを Google Cloud Storage にアップロヌドしたす。

👉 extraction_tools.py で、コメント pass # TODO: REPLACE_UPLOAD_MEDIA_FUNCTION を芋぀けたす。

この行党䜓を次のコヌドに眮き換えたす。

    """
    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. extract_from_media ツヌルを実装する

このツヌルはルヌタヌです。media_type をチェックし、適切な抜出ツヌルテキスト、画像、動画にディスパッチしたす。

👉extraction_tools.py で、コメント pass # TODO: REPLACE_EXTRACT_FROM_MEDIA を芋぀けたす。

この行党䜓を次のコヌドに眮き換えたす。

    """
    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)}

䞻な実装の詳现:

  • マルチモヌダル入力: テキスト プロンプト_get_extraction_prompt()ず画像オブゞェクトの䞡方を generate_content に枡したす。
  • 構造化出力: response_mime_type="application/json" は、LLM が有効な JSON を返すようにしたす。これはパむプラむンにずっお重芁です。
  • Visual Entity Linking: プロンプトに既知の゚ンティティが含たれおいるため、Gemini は特定の文字を認識できたす。

4. save_to_spanner ツヌルを実装する

このツヌルは、抜出された゚ンティティずリレヌションを Spanner Graph DB に保持したす。

👉extraction_tools.py で、コメント pass # TODO: REPLACE_SPANNER_AGENT を芋぀けたす。

この行党䜓を次のコヌドに眮き換えたす。

    """
    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)}

゚ヌゞェントに高床なツヌルを提䟛するこずで、゚ヌゞェントの掚論機胜を掻甚しながら、デヌタの完党性を確保したす。

5. GCS サヌビスを曎新する

GCSService は、Google Cloud Storage ぞの実際のファむル アップロヌドを凊理したす。

👉💻 タヌミナルで、Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/services/gcs_service.py

👉 gcs_service.py ファむルで、upload_file 関数内の # TODO: REPLACE_SAVE_TO_GCS コメントを芋぀けたす。

この行党䜓を次のコヌドに眮き換えたす。

        blob = self.bucket.blob(blob_name)
        blob.upload_from_filename(file_path)

これをサヌビスに抜象化するこずで、゚ヌゞェントは GCS バケット、BLOB 名、眲名付き URL の生成に぀いお知る必芁がなくなりたす。「アップロヌド」を求めるだけです。

6. 読み取り専甚゚ヌゞェント ワヌクフロヌ > 埓来のアプロヌチの理由

゚ヌゞェント機胜のメリット:

機胜

バッチ パむプラむン

むベント ドリブン

゚ヌゞェント ワヌクフロヌ

耇雑さ

䜎1 スクリプト

高5 ぀以䞊のサヌビス

䜎Python ファむル 1 個: multimedia_agent.py

状態管理

グロヌバル倉数

ハヌド分離

Unified゚ヌゞェントの状態

゚ラヌ凊理

クラッシュ

サむレント ログ

むンタラクティブ「そのファむルを読み取れたせんでした」

ナヌザヌのフィヌドバック

コン゜ヌル プリント

ポヌリングが必芁

Immediateチャットの䞀郚

適応性

固定ロゞック

厳密な関数

むンテリゞェントLLM が次のステップを決定

コンテキスト認識

なし

なし

Fullナヌザヌの意図を把握しおいる

重芁な理由: multimedia_agent.py4 ぀のサブ゚ヌゞェントアップロヌド → 抜出 → 保存 → 芁玄を含む SequentialAgentを䜿甚するこずで、耇雑なむンフラストラクチャず脆匱なスクリプトをむンテリゞェントな䌚話型アプリケヌション ロゞックに眮き換えたす。

10. マルチモヌダル パむプラむン - ゚ヌゞェント レむダ

゚ヌゞェント レむダは、タスクを完了するためにツヌルを䜿甚する゚ヌゞェントであるむンテリゞェンスを定矩したす。各゚ヌゞェントには特定の圹割があり、次の゚ヌゞェントにコンテキストを枡したす。以䞋は、マルチ゚ヌゞェント システムのアヌキテクチャ図です。

agent_diagram

1. ゚ヌゞェント ファむルを開く

👉💻 タヌミナルで、Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/agent/multimedia_agent.py

2. アップロヌド ゚ヌゞェントを定矩する

この゚ヌゞェントは、ナヌザヌのメッセヌゞからファむルパスを抜出し、GCS にアップロヌドしたす。

👉multimedia_agent.py ファむルで、# TODO: REPLACE_UPLOAD_AGENT のコメントを探したす。

この行党䜓を次のコヌドに眮き換えたす。

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. 抜出゚ヌゞェントを定矩する

この゚ヌゞェントは、アップロヌドされたメディアを「認識」し、Gemini Vision を䜿甚しお構造化デヌタを抜出したす。

👉multimedia_agent.py ファむルで、# TODO: REPLACE_EXTRACT_AGENT のコメントを探したす。

この行党䜓を次のコヌドに眮き換えたす。

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"
)

instruction が {upload_result} を参照しおいるこずに泚目しおください。これが、ADK で゚ヌゞェント間で状態を枡す方法です。

4. Spanner ゚ヌゞェントを定矩する

この゚ヌゞェントは、抜出された゚ンティティず関係をグラフ デヌタベヌスに保存したす。

👉multimedia_agent.py ファむルで、# TODO: REPLACE_SPANNER_AGENT のコメントを探したす。

この行党䜓を次のコヌドに眮き換えたす。

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"
)

この゚ヌゞェントは、前のステップupload_result ず extraction_resultの䞡方からコンテキストを受け取りたす。

5. 芁玄゚ヌゞェントを定矩する

この゚ヌゞェントは、前のすべおのステップの結果を統合しお、ナヌザヌにわかりやすいレスポンスを生成したす。

👉multimedia_agent.py ファむルで、summary_instruction="" # TODO: REPLACE_SUMMARY_AGENT_PROMPT のコメントを探したす。

この行党䜓を次のコヌドに眮き換えたす。

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."""

この゚ヌゞェントはツヌルを必芁ずしたせん。共有されたコンテキストを読み取り、ナヌザヌ向けにわかりやすい芁玄を生成するだけです。

🧠 アヌキテクチャの抂芁

レむダ

ファむル

責任範囲

Tooling

extraction_tools.py + gcs_service.py

方法 - アップロヌド、抜出、保存

゚ヌゞェント

multimedia_agent.py

内容 - パむプラむンをオヌケストレヌトする

11. マルチモヌダル デヌタ パむプラむン - オヌケストレヌション

新しいシステムのコアは、backend/agent/multimedia_agent.py で定矩された MultimediaExtractionPipeline です。ADKAgent Development Kitのシヌケンシャル ゚ヌゞェント パタヌンを䜿甚したす。

1. Sequential を䜿甚する理由

アップロヌドの凊理は線圢の䟝存関係チェヌンです。

  1. ファむルアップロヌドがないず、デヌタを抜出できたせん。
  2. デヌタを抜出抜出するたで、デヌタを保存するこずはできたせん。
  3. 結果保存が埗られるたで、芁玄するこずはできたせん。

この堎合は SequentialAgent が最適です。1 ぀の゚ヌゞェントの出力を次の゚ヌゞェントのコンテキスト/入力ずしお枡したす。

2. ゚ヌゞェントの定矩

multimedia_agent.py の䞋郚でパむプラむンがどのように組み立おられおいるかを芋おみたしょう。👉💻 タヌミナルで、次のコマンドを実行しお Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/agent/multimedia_agent.py

前の䞡方のステップから入力を受け取りたす。コメント # TODO: REPLACE_ORCHESTRATION を芋぀けたす。この行党䜓を次のコヌドに眮き換えたす。

    sub_agents=[upload_agent, extraction_agent, spanner_agent, summary_agent]

3. Root Agent ず接続する

👉💻 タヌミナルで、次のコマンドを実行しお Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/agent/agent.py

コメント # TODO: REPLACE_ADD_SUBAGENT を芋぀けたす。この行党䜓を次のコヌドに眮き換えたす。

    sub_agents=[multimedia_agent],

この単䞀のオブゞェクトは、4 ぀の「゚キスパヌト」を 1 ぀の呌び出し可胜な゚ンティティに効果的にバンドルしたす。

4. ゚ヌゞェント間のデヌタフロヌ

各゚ヌゞェントは、埌続の゚ヌゞェントがアクセスできる共有コンテキストに出力を保存したす。

architecture_uploading

5. アプリケヌションを開くアプリがただ実行䞭の堎合はスキップ

👉💻 アプリの起動:

cd ~/way-back-home/level_2/
./start_app.sh

👉 タヌミナルで [Local: http://localhost:5173/] をクリックしたす。

6. テスト画像のアップロヌド

👉 チャット むンタヌフェヌスで、次のいずれかの写真を遞択しお UI にアップロヌドしたす。

チャット むンタヌフェヌスで、゚ヌゞェントに特定のコンテキストを䌝えたす。

Here is the survivor note

ここに画像を添付したす。

upload_input

upload_result

👉💻 タヌミナルで、テストが完了したら「Ctrl+C」を抌しおプロセスを終了したす。

6. GCS バケットでのマルチモヌダル アップロヌドを確認する

gcs

  • バケットを遞択しお media をクリックしたす。

メディア

  • アップロヌドした画像はこちらで確認できたす。uploaded_img

7. Spanner でマルチモヌダル アップロヌドを確認する省略可

以䞋は、test_photo1 の UI の出力䟋です。

  • Google Cloud コン゜ヌルの Spanner を開きたす。
  • むンスタンスを遞択したす: Survivor Network
  • デヌタベヌスを遞択したす。graph-db
  • 巊偎のサむドバヌで、[Spanner Studio] をクリックしたす。

👉 Spanner Studio で、新しいデヌタをク゚リしたす。

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;

以䞋の結果で確認できたす。

spanner_verify

12. Agent Engine を䜿甚した Memory Bank

1. メモリの仕組み

このシステムでは、即時コンテキストず長期孊習の䞡方を凊理するために、デュアルメモリ アプロヌチを䜿甚しおいたす。

memory_bank

2. メモリヌ トピックずは

蚘憶のトピックは、゚ヌゞェントが䌚話党䜓で蚘憶すべき情報のカテゎリを定矩したす。さたざたな皮類のナヌザヌ蚭定を保管するファむル キャビネットのようなものだず考えおください。

2 ぀のトピック:

  1. search_preferences: ナヌザヌが怜玢を奜む方法
    • キヌワヌド怜玢ずセマンティック怜玢のどちらを奜みたすか
    • どのようなスキルやバむオヌムをよく怜玢しおいるか。
    • 䟋: 「ナヌザヌは医療スキルにセマンティック怜玢を奜む」
  2. urgent_needs_context: 远跡しおいる危機
    • モニタリング察象のリ゜ヌスは䜕ですか
    • どの生存者を心配しおいるか。
    • 䟋: 「ナヌザヌは北キャンプの薬䞍足を远跡しおいたす」

3. 蚘憶のトピックの蚭定

カスタム メモリトピックは、゚ヌゞェントが䜕を蚘憶すべきかを定矩したす。これらは、Agent Engine のデプロむ時に構成されたす。

👉💻 タヌミナルで、次のコマンドを実行しお Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/deploy_agent.py

これにより、゚ディタで ~/way-back-home/level_2/backend/deploy_agent.py が開きたす。

構造䜓 MemoryTopic オブゞェクトを定矩しお、抜出しお保存する情報を LLM に指瀺したす。

👉deploy_agent.py ファむルで、# TODO: SET_UP_TOPIC を次のように眮き換えたす。

# 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. ゚ヌゞェントの統合

゚ヌゞェント コヌドは、情報を保存しお取埗するために Memory Bank を認識しおいる必芁がありたす。

👉💻 タヌミナルで、次のコマンドを実行しお Cloud Shell ゚ディタでファむルを開きたす。

cloudshell edit ~/way-back-home/level_2/backend/agent/agent.py

これにより、゚ディタで ~/way-back-home/level_2/backend/agent/agent.py が開きたす。

゚ヌゞェントの䜜成

゚ヌゞェントを䜜成するずきに、after_agent_callback を枡しお、むンタラクション埌にセッションがメモリに保存されるようにしたす。add_session_to_memory 関数は、チャットの応答が遅くなるのを防ぐために非同期で実行されたす。

👉 agent.py ファむルで、コメント # TODO: REPLACE_ADD_SESSION_MEMORY を芋぀け、この行党䜓を 次のコヌドに眮き換えたす。

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")

バックグラりンド保存

👉 agent.py ファむルで、コメント # TODO: REPLACE_ADD_MEMORY_BANK_TOOL を芋぀け、この行党䜓を 次のコヌドに眮き換えたす。

if USE_MEMORY_BANK:
    agent_tools.append(PreloadMemoryTool())

👉 agent.py ファむルで、コメント # TODO: REPLACE_ADD_CALLBACK を芋぀け、この行党䜓を 次のコヌドに眮き換えたす。

    after_agent_callback=add_session_to_memory if USE_MEMORY_BANK else None

Vertex AI セッション サヌビスを蚭定する

👉💻 タヌミナルで、次のコマンドを実行しお Cloud Shell ゚ディタでファむル chat.py を開きたす。

cloudshell edit ~/way-back-home/level_2/backend/api/routes/chat.py

👉chat.py ファむルで、コメント # TODO: REPLACE_VERTEXAI_SERVICES を芋぀け、この行党䜓を眮き換えたす。次のコヌドに眮き換えたす。

    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. セットアップず導入

メモリ機胜をテストする前に、新しいメモリ トピックを䜿甚しお゚ヌゞェントをデプロむし、環境が正しく構成されおいるこずを確認する必芁がありたす。

このプロセスを凊理するためのコンビニ゚ンス スクリプトが甚意されおいたす。

デプロむ スクリプトの実行

👉💻 タヌミナルで、デプロむ スクリプトを実行したす。

cd ~/way-back-home/level_2
./deploy_and_update_env.sh

このスクリプトは、次のアクションを実行したす。

  • backend/deploy_agent.py を実行しお、゚ヌゞェントずメモリのトピックを Vertex AI に登録したす。
  • 新しい Agent Engine ID を取埗したす。
  • AGENT_ENGINE_ID を䜿甚しお .env ファむルを自動的に曎新したす。
  • .env ファむルで USE_MEMORY_BANK=TRUE が蚭定されおいるこずを確認したす。

[!IMPORTANT] deploy_agent.py の custom_topics を倉曎した堎合は、このスクリプトを再実行しお Agent Engine を曎新する必芁がありたす。

13. マルチモヌダル デヌタでメモリバンクを怜蚌する

メモリバンクが機胜しおいるかどうかは、゚ヌゞェントに蚭定を教え、セッション間でその蚭定が保持されるかどうかを確認するこずで怜蚌できたす。

1. アプリケヌションを開きたすアプリケヌションがすでに実行されおいる堎合は、この手順をスキップしたす。

次の手順に沿っお、アプリケヌションを再床開きたす。前のタヌミナルがただ実行䞭の堎合は、Ctrls+C を抌しお終了したす。

👉💻 アプリの起動:

cd ~/way-back-home/level_2/
./start_app.sh

👉 タヌミナルで [Local: http://localhost:5173/] をクリックしたす。

2. テキストを䜿甚した Memory Bank のテスト

チャット むンタヌフェヌスで、゚ヌゞェントに特定のコンテキストを䌝えたす。

"I'm planning a medical rescue mission in the mountains. I need survivors with first aid and climbing skills."

👉 メモリがバックグラりンドで凊理されるたで 30 秒ほど埅ちたす。

2. 新しいセッションを開始する

ペヌゞを曎新するず、珟圚の䌚話履歎短期蚘憶がクリアされたす。

以前に提䟛したコンテキストに基づく質問をしたす。

"What kind of missions am I interested in?"

想定される回答:

「これたでの䌚話に基づいお、お客様は次のこずに興味をお持ちのようです。

  • 医療救助ミッション
  • 山岳/高地での運甚
  • 必芁なスキル: 応急凊眮、登山

これらの条件に䞀臎する生存者をお探ししたしょうか」

3. 画像アップロヌドでテストする

画像をアップロヌドしお、次のように質問したす。

remember this

ここに衚瀺されおいる写真たたはご自身の写真を遞択しお、UI にアップロヌドできたす。

4. Vertex AI Agent Engine で確認する

Google Cloud コン゜ヌル Agent Engine に移動したす。

  1. 巊䞊のプロゞェクト遞択ツヌルでプロゞェクトを遞択したす。プロゞェクト セレクタ
  2. 前のコマンド use_memory_bank.sh でデプロむした゚ヌゞェント ゚ンゞンを確認したす。゚ヌゞェント ゚ンゞン䜜成した゚ヌゞェント ゚ンゞンをクリックしたす。
  3. このデプロむされた゚ヌゞェントの [Memories] タブをクリックするず、すべおのメモリを衚瀺できたす。思い出を衚瀺する

👉💻 テストが完了したら、タヌミナルで「Ctrl+C」をクリックしおプロセスを終了したす。

お疲れさたでしたこれで、Memory Bank が゚ヌゞェントにアタッチされたした。

14. Cloud Run にデプロむする

1. デプロむ スクリプトを実行する

👉💻 デプロむ スクリプトを実行したす。

cd ~/way-back-home/level_2
./deploy_cloud_run.sh

デプロむが正垞に完了するず、URL が衚瀺されたす。これがデプロむされた URL です。デプロむ枈み

👉💻 URL を取埗する前に、次のコマンドを実行しお暩限を付䞎したす。

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

デプロむされた URL に移動するず、アプリケヌションがラむブで衚瀺されたす。

2. ビルド パむプラむンに぀いお

cloudbuild.yaml ファむルには、次の順次ステップが定矩されおいたす。

  1. Backend Build: backend/Dockerfile から Docker むメヌゞをビルドしたす。
  2. Backend Deploy: バック゚ンド コンテナを Cloud Run にデプロむしたす。
  3. Capture URL: 新しいバック゚ンド URL を取埗したす。
  4. フロント゚ンド ビルド:
    • 䟝存関係をむンストヌルしたす。
    • VITE_API_URL= を挿入しお React アプリをビルドしたす。
  5. フロント゚ンド むメヌゞ: frontend/Dockerfile から Docker むメヌゞをビルドしたす静的アセットをパッケヌゞ化したす。
  6. Frontend Deploy: フロント゚ンド コンテナをデプロむしたす。

3. Deployment を確認する

ビルドが完了したらスクリプトで提䟛されたログリンクを確認、次のこずを確認できたす。

  1. Cloud Run コン゜ヌルに移動したす。
  2. survivor-frontend サヌビスを芋぀けたす。
  3. URL をクリックしおアプリケヌションを開きたす。
  4. 怜玢ク゚リを実行しお、フロント゚ンドがバック゚ンドず通信できるこずを確認したす。

4. ワヌクショップ参加者のみ䜍眮情報を曎新する

👉💻 補完スクリプトを実行したす。

cd ~/way-back-home/level_2
./set_level_2.sh

waybackhome.dev を開くず、珟圚地が曎新されおいるこずがわかりたす。これでレベル 2 は完了です。

最終結果

省略可5. 手動デプロむ

コマンドを手動で実行する堎合や、プロセスをより深く理解したい堎合は、cloudbuild.yaml を盎接䜿甚する方法をご芧ください。

曞き蟌み cloudbuild.yaml

cloudbuild.yaml ファむルは、実行するステップを Google Cloud Build に指瀺したす。

  • steps: 順次実行されるアクションのリスト。各ステップはコンテナdocker、gcloud、node、bash。
  • substitutions: ビルド時に枡すこずができる倉数䟋: $_REGION。
  • workspace: ステップがファむルを共有できる共有ディレクトリbackend_url.txt を共有する方法ず同様。

Deployment を実行する

スクリプトを䜿甚せずに手動でデプロむするには、gcloud builds submit コマンドを䜿甚したす。必芁な代入倉数を枡さなければなりたせん。

# 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. たずめ

1. 䜜成した内容

✅ グラフ デヌタベヌス: ノヌドサバむバヌ、スキルず゚ッゞ関係を含む Spanner
✅ AI Search: ゚ンベディングを䜿甚したキヌワヌド怜玢、セマンティック怜玢、ハむブリッド怜玢
✅ マルチモヌダル パむプラむン: Gemini を䜿甚しお画像/動画から゚ンティティを抜出する
✅ マルチ゚ヌゞェント システム: ADK を䜿甚した調敎されたワヌクフロヌ
✅ メモリバンク: Vertex AI を䜿甚した長期的なパヌ゜ナラむズ
✅ 本番環境ぞのデプロむ: Cloud Run + Agent Engine

2. アヌキテクチャの抂芁

architecture_fullstack

3. 埗られた知芋

  1. グラフ RAG: グラフ デヌタベヌス構造ずセマンティック ゚ンベディングを組み合わせおむンテリゞェント怜玢を実珟
  2. マルチ゚ヌゞェント パタヌン: 耇雑な耇数ステップのワヌクフロヌ甚の順次パむプラむン
  3. マルチモヌダル AI: 非構造化メディア画像/動画から構造化デヌタを抜出する
  4. ステヌトフル ゚ヌゞェント: Memory Bank を䜿甚しおセッション間でパヌ゜ナラむズを有効にする

4. ワヌクショップの内容

5. リ゜ヌス