‫Next '26 Developer Keynote: Enhancing Agents with Memory

1. מבוא

ב-codelab הזה, תשדרגו את סוכני ה-ADK שלכם על ידי הוספת ידע מתמשך ומיוחד. תלמדו איך לנהל את מצב השיחה באמצעות סשנים בפלטפורמת הסוכנים, איך להפעיל למידה לטווח ארוך באמצעות Memory Bank ואיך לשלב נתונים מורכבים של כללי עיר באמצעות Spark ו-AlloyDB ל-RAG (יצירה משולבת-אחזור).

הפעולות שתבצעו:

  • מגדירים סשנים בפלטפורמת הסוכן כדי לשמור את נתוני השיחה.
  • הטמעת מאגר זיכרון כדי לאפשר לסוכנים ללמוד מאינטראקציות קודמות.
  • משתמשים ב-Spark Lightning Engine כדי להטמיע ולעבד את התיעוד של כללי העיר.
  • יצירת מערכת RAG באמצעות AlloyDB וחיפוש וקטורי.
  • פורסים את הסוכן המשופר ב-Agent Platform.

הדרישות

  • דפדפן אינטרנט כמו Chrome
  • פרויקט בענן של Google עם חיוב מופעל
  • היכרות בסיסית עם Python ו-SQL

משך הזמן המשוער: 60 דקות

העלות של המשאבים שנוצרו ב-codelab הזה צריכה להיות פחות מ-5$.

‫2. לפני שמתחילים

יצירת פרויקט ב-Google Cloud

  1. במסוף Google Cloud, בדף לבחירת הפרויקט, בוחרים פרויקט ב-Google Cloud או יוצרים פרויקט.
  2. הקפידו לוודא שהחיוב מופעל בפרויקט שלכם ב-Cloud. כך בודקים אם החיוב מופעל בפרויקט

הפעלת Cloud Shell

Cloud Shell היא סביבת שורת פקודה שפועלת ב-Google Cloud וכוללת מראש את הכלים הנדרשים.

  1. לוחצים על Activate Cloud Shell בחלק העליון של מסוף Google Cloud.
  2. אחרי שמתחברים ל-Cloud Shell, מאמתים את האימות:
    gcloud auth list
    
  3. מוודאים שהפרויקט מוגדר:
    gcloud config get project
    
  4. אם הפרויקט לא מוגדר כמו שציפיתם, מגדירים אותו:
    export PROJECT_ID=<YOUR_PROJECT_ID>
    gcloud config set project $PROJECT_ID
    

אימות האימות:

gcloud auth list

מאשרים את הפרויקט:

gcloud config get project

מגדירים אותו לפי הצורך:

export PROJECT_ID=<YOUR_PROJECT_ID>
gcloud config set project $PROJECT_ID

הפעלת ממשקי ה-API

מריצים את הפקודה הזו כדי להפעיל את כל ממשקי ה-API הנדרשים לניהול סשנים, לעיבוד Spark ול-AlloyDB:

gcloud services enable \
  aiplatform.googleapis.com \
  run.googleapis.com \
  alloydb.googleapis.com \
  dataproc.googleapis.com \
  documentai.googleapis.com \
  storage.googleapis.com \
  secretmanager.googleapis.com

3. הגדרת הסביבה

ב-Codelab הזה תשתמשו בסביבה שהוגדרה מראש במאגר המידע של נאום הפתיחה.

  1. משכפלים את המאגר ועוברים לתיקיית הפרויקט:
git clone https://github.com/GoogleCloudPlatform/next-26-keynotes
cd next-26-keynotes/devkey/enhancing-agents-with-memory
  1. מגדירים סביבה וירטואלית של Python ומתקינים את חבילות ה-ADK הנדרשות:
uv venv
source .venv/bin/activate
uv sync

הגדרת משתני סביבה

הנציג דורש הגדרה ספציפית כדי להתחבר לפלטפורמת הנציגים ול-AlloyDB.

  1. מעתיקים את קובץ הסביבה לדוגמה:
cp .env.example .env
  1. פותחים את .env ומעדכנים את השדות הבאים:
    • GOOGLE_CLOUD_PROJECT: מזהה הפרויקט.
    • GOOGLE_CLOUD_LOCATION: us-central1.
    • ALLOYDB_CLUSTER_ID: rules-db.
GOOGLE_CLOUD_PROJECT=<YOUR_PROJECT_ID>
GOOGLE_CLOUD_LOCATION=global
GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_REGION=us-central1
ALLOYDB_CLUSTER_ID=rules-db
  1. מריצים את סקריפט העזר הבא כדי ליצור מופע של Agent Engine לשימוש בסשנים של שיחות ובזיכרון לטווח ארוך. הפעולה הזו תאכלס באופן אוטומטי את AGENT_ENGINE_ID בקובץ .env:
uv run utils/setup_agent_engine.py

אם הפעולה בוצעה ללא שגיאות, התגובה אמורה להיראות כך:

Creating Agent Engine instance...
Successfully created Agent Engine. ID: 1234567890
Updated .env with AGENT_ENGINE_ID=1234567890

4. יצירת סוכן באמצעות ניהול סשנים

בשלב הזה, תפעילו סוכן לתכנון מרתון שיכול לשמור על היסטוריית השיחות בכמה תורות. הדבר הזה מתבצע באמצעות המחלקה App ADK וה-Agent Platform Sessions.

הפעלת סוכן ושירות סשן

פתיחת planner_agent/agent.py. תוכלו לראות איך אנחנו מוסיפים מחלקה של ADK כדי לשלב סשנים של פלטפורמת הסוכן. כך אנחנו יכולים להפוך את הסוכנים שלנו לבעלי מצב לאורך זמן, ולשנות את ההקשר לפי הצורך.

from google.adk.agents import LlmAgent
from google.adk.sessions import VertexAiSessionService
from vertexai.agent_engines import AdkApp

PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT")
REGION = os.environ.get("GOOGLE_CLOUD_REGION", "us-central1")

# Initialize Vertex AI for regional services
if PROJECT_ID:
    vertexai.init(project=PROJECT_ID, location=REGION)

# Define the agent logic
root_agent = LlmAgent(
    name="planner_agent",
    model="gemini-3-flash-preview",
    instruction="You are a helpful marathon planning assistant...",
    tools=[] # We will add tools in the next steps
)

def session_service_builder():
    """Builder for Agent Platform Sessions."""
    return VertexAiSessionService(project=PROJECT_ID, location=REGION)

# Wrap the agent in an AdkApp to manage stateful context
app = AdkApp(
    agent=root_agent,
    session_service_builder=session_service_builder
)

5. הפעלת למידה לטווח ארוך באמצעות Memory Bank

בזמן שניהול הסשן עוקב אחרי שיחות ספציפיות, אתם יכולים לעשות את אותו הדבר לגבי הזיכרון לטווח ארוך. בשלב הזה, מצמידים את הסוכן לMemory Bank של פלטפורמת הסוכנים, שירות זיכרון מנוהל לחלוטין שמוכן לשימוש בארגונים.

הפעלת שירות Memory Bank

התכונה 'מאגר זיכרון' מאפשרת לסוכן לזכור את ההקשר בין סשנים שונים. מעדכנים את planner_agent/agent.py כדי לכלול את שירות הזיכרון:

from google.adk.memory import VertexAiMemoryBankService

def memory_service_builder():
    """Builder for Agent Platform Memory Bank."""
    return VertexAiMemoryBankService(
        project=PROJECT_ID,
        location=REGION,
        agent_engine_id=AGENT_ENGINE_ID
    )

הטמעה של הוספת נתונים אוטומטית לזיכרון

כדי לוודא שהסוכן לומד מכל תור, אנחנו מוסיפים after_agent_callback. הפונקציה הזו מופעלת אחרי שהסוכן מסיים להשיב, ומאפשרת לו 'לעכל' את הסשן ולשמור זיכרונות רלוונטיים במאגר.

  1. מגדירים את פונקציית הקריאה החוזרת:
async def auto_save_memories(callback_context):
    """Callback to ingest the session into the memory bank after the turn."""
    # In AdkApp, the memory service is available via the invocation context
    if hasattr(callback_context._invocation_context, 'memory_service') and callback_context._invocation_context.memory_service:
        await callback_context._invocation_context.memory_service.add_session_to_memory(
            callback_context._invocation_context.session
        )
  1. מצרפים את הקריאה החוזרת ל-LlmAgent:
root_agent = LlmAgent(
    # ... other params
    after_agent_callback=[auto_save_memories],
)

6. הגדרת AlloyDB ל-RAG

כדי שנוכל להטמיע נתונים של כללים לגבי ערים, אנחנו צריכים מסד נתונים עם ביצועים גבוהים לאחסון הנתונים. בשלב הזה תיצרו אשכול AlloyDB ותפעילו את סכימת מסד הנתונים לחיפוש וקטורי.

1. יצירת אשכול AlloyDB ומכונה ראשית

מריצים את הפקודות האלה ב-Cloud Shell כדי ליצור את האשכול ואת המופע הראשי שלו:

# Create the cluster
gcloud alloydb clusters create rules-db \
  --password=postgres \
  --region=us-central1

# Create the primary instance with IAM authentication enabled
gcloud alloydb instances create rules-db-primary \
  --instance-type=PRIMARY \
  --cpu-count=2 \
  --region=us-central1 \
  --cluster=rules-db \
  --database-flags=alloydb.iam_authentication=on

2. מתן התפקידים הנדרשים ב-IAM

כדי להשתמש בשרת MCP מנוהל של AlloyDB, הזהות שלכם צריכה הרשאות ספציפיות. מריצים את הפקודות הבאות כדי להעניק את התפקידים הנדרשים:

export USER_EMAIL=$(gcloud config get-value account)

# Role to use MCP tools
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="user:$USER_EMAIL" \
  --role="roles/mcp.toolUser"

# Role to execute SQL in AlloyDB
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="user:$USER_EMAIL" \
  --role="roles/alloydb.admin"

# Role for IAM database authentication
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="user:$USER_EMAIL" \
  --role="roles/alloydb.databaseUser"

# Create the IAM-based database user
gcloud alloydb users create "$USER_EMAIL" \
  --cluster=rules-db \
  --region=us-central1 \
  --type=IAM_BASED

3. יצירת מסד נתונים וטבלאות באמצעות AlloyDB Studio

מכיוון שמסדי נתונים וטבלאות של AlloyDB מנוהלים באמצעות SQL, נשתמש ב-AlloyDB Studio במסוף Google Cloud כדי להשלים את הסכימה.

  1. עוברים אל AlloyDB > Clusters ולוחצים על rules-db.
  2. בתפריט הניווט הימני, לוחצים על AlloyDB Studio.
  3. מתחברים באמצעות המשתמש postgres והסיסמה שהגדרתם (postgres).
  4. מריצים את ה-SQL הבא כדי ליצור את מסד הנתונים:
    CREATE DATABASE city_rules;
    
  5. מעבירים את חיבור מסד הנתונים ל-city_rules ב-AlloyDB Studio ומריצים את ה-SQL הבא כדי להתקין תוספים וליצור את הטבלה rules:
    -- Install extensions for vector search and ML
    CREATE EXTENSION IF NOT EXISTS vector;
    CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
    
    -- Create the rules table
    CREATE TABLE IF NOT EXISTS rules (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        text TEXT NOT NULL,
        city TEXT NOT NULL,
        embedding vector(3072) DEFAULT NULL
    );
    
    -- Grant your IAM user access to the table (replace with your email)
    GRANT ALL PRIVILEGES ON TABLE rules TO "YOUR_EMAIL_ADDRESS";
    

7. העברת נתונים של כללי עיר באמצעות Spark Lightning Engine

כדי לספק תכנון מדויק באמת, לסוכן דרוש יותר מסתם הנחיה מנוסחת היטב. הוא צריך הארקה בנתונים ובהקשר הארגוני. בשלב הזה, תשתמשו ב-Spark Lightning Engine ב-Dataproc Serverless כדי לעבד קובצי PDF גדולים של כללי העיר ולהטמיע אותם ב-AlloyDB.

למה כדאי להשתמש במנוע Spark Lightning?

כדי להעניק לסוכנים ידע מבוסס על נתונים בקנה מידה נרחב, צריך לעבד כמויות עצומות של נתונים לא מובנים. ‫Spark Lightning Engine הוא מנוע ביצועים גבוהים ל-Spark שמאיץ משמעותית את עומסי העבודה האלה. אנחנו משתמשים בו כאן כדי לבצע חלוקה למקטעים סמנטיים במסמכים באמצעות Document AI מבית Google.

הסבר על צינור הנתונים של Spark

לוגיקת ההטמעה מוגדרת ב-spark-setup/spark_alloydb_processor.py. הפייפליין פועל לפי השלבים הבאים:

  1. List PDFs: מאחזר מזהי URI של מסמכים מדלי של Google Cloud Storage.
  2. חילוץ סמנטי: משתמש בפונקציה בהגדרת המשתמש (UDF) כדי לקרוא ל-Document AI API.
  3. Write to AlloyDB (כתיבה ל-AlloyDB): שומר את חלקי הטקסט שחולצו בטבלת AlloyDB שנקראת rules.
# Extract from spark_alloydb_processor.py
def process_document(gcs_uri: str):
    # ... calls Document AI to parse PDF ...
    return chunks

# Parallel processing with Spark Lightning Engine
process_udf = udf(process_document, chunk_schema)
chunked_df = uri_df.withColumn("chunks", process_udf(col("gcs_uri"))) \
                   .select(explode(col("chunks")).alias("chunk")) \
                   .select("chunk.*")

# Save to AlloyDB for Vector Search
chunked_df.write.format("jdbc") \
    .option("url", jdbc_url) \
    .option("dbtable", "rules") \
    .mode("append") \
    .save()

הפעלת משימת ההטמעה

מפעילים את תהליך ההטמעה באמצעות הסקריפט שצוין:

./spark-setup/run_dataproc.sh

8. RAG עם AlloyDB

עכשיו, כשנתוני הכללים של העיר נמצאים ב-AlloyDB, הסוכן יכול להשתמש בהם כדי לבצע Retrieval-Augmented Generation (יצירה משולבת-אחזור, RAG). כך מוודאים שהתוכנית למרתון תואמת לקודי ערים ספציפיים.

היכולות של AlloyDB ל-RAG

‫AlloyDB מצטיין בחיפוש וקטורי, ומאפשר לנו לאחסן נתונים מובְנים והטמעות וקטוריות באותו מקום. הסוכן יכול להשתמש בפונקציה המובנית embedding ב-AlloyDB כדי למצוא את המידע הכי רלוונטי על הכללים.

כדי לתת לסוכן גישה לנתונים האלה, אנחנו מספקים כלי שמבצע שאילתות ב-AlloyDB באמצעות דמיון וקטורי. אפשר לראות את הלוגיקה הזו בדוגמה hybrid_recall.sql, שמראה איך לחשב את המרחק בין שאילתה לבין הכללים המאוחסנים שלנו:

SELECT
    text,
    (embedding <=> 
     embedding('gemini-embedding-001', 
               'Restrictions for running a race on the Las Vegas strip')::vector) 
    as distance
FROM
    rules
WHERE city = 'Las Vegas'
ORDER BY
    distance ASC
LIMIT 5;

הארקה של הסוכן בכללים מקומיים באמצעות כלי RAG

כדי שהסוכן יוכל להשתמש בכלי, צריך להגדיר אותו ב-planner_agent/tools.py ואז לרשום אותו ב-planner_agent/agent.py. נשתמש בשרת מרוחק מנוהל של AlloyDB MCP מ-Google Cloud כדי להתחבר למסד הנתונים שלנו.

  1. מגדירים את הכלי ב-planner_agent/tools.py באמצעות התבנית Hybrid Recall. נשתמש בפרוטוקול streamable_http כדי להתחבר לשרת ה-MCP המנוהל של AlloyDB:
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def get_local_and_traffic_rules(query: str) -> str:
    """Uses vector search in AlloyDB via managed MCP server."""
    # Vector search query using built-in AlloyDB embedding functions
    sql = f"SELECT text FROM rules WHERE city = 'Las Vegas' ORDER BY embedding <=> google_ml.embedding('gemini-embedding-001', '{query}')::vector ASC LIMIT 5;"
    
    # Establish a streamable HTTP connection to the MCP server
    async with streamablehttp_client(url, headers=get_auth_headers()) as (read_stream, write_stream, _):
        async with ClientSession(read_stream, write_stream) as session:
            await session.initialize()
            result = await session.call_tool(
                "execute_sql",
                arguments={
                    "instance": full_instance_name,
                    "database": "city_rules",
                    "sqlStatement": sql
                }
            )
            return "\n".join([c.text for c in result.content if hasattr(c, 'text')])
  1. רושמים את הכלי ומסיימים את התהליך planner_agent/agent.py:
# ... imports ...

# Assemble the Agent
root_agent = LlmAgent(
    name="planner_agent",
    model="gemini-3-flash-preview",
    instruction="You are a helpful marathon planning assistant...",
    tools=[
        get_local_and_traffic_rules,
    ],
    after_agent_callback=[auto_save_memories],
)

# 2. Wrap the agent in an AdkApp to manage the stateful lifecycle
app = AdkApp(
    agent=root_agent,
    session_service_builder=session_service_builder,
    memory_service_builder=memory_service_builder
)

9. הדרכה ממומחים בנושא מיומנויות של סוכנים

מיומנויות של סוכנים הן מודולים עצמאיים שמספקים לנציגים הוראות ספציפיות, הנחיות ומשאבים שיעזרו להם לבצע משימות בצורה יעילה יותר. במקום להעמיס על הנחיית המערכת הוראות מורכבות לכל כלי, אתם יכולים להגדיר את המומחיות הזו ככישורים שנטענים רק כשצריך.

‫Google מספקת מיומנויות מוכנות מראש למוצרי Google (כמו AlloyDB ו-BigQuery) כדי להבטיח שהסוכנים שלכם יפעלו בהתאם לשיטות המומלצות בתחום לשליפת נתונים ולניהול משאבים. אפשר לעיין בדוגמאות האלה ובדוגמאות נוספות במאגר הכישורים של Google. כאן אפשר למצוא מיומנויות בסיסיות ב-AlloyDB.

1. עיון בקובץ המיומנויות

פותחים את קובץ המיומנות שהוגדר מראש בכתובת planner_agent/skills/get-local-and-traffic-rules/SKILL.md. כך זה נראה:

---
name: get-local-and-traffic-rules
description: Retrieve local rules and traffic information for a specific jurisdiction.
---
# get_local_and_traffic_rules Skill

This skill provides guidelines on how to effectively use the `get_local_and_traffic_rules` tool.

## Overview
The `get_local_and_traffic_rules` tool interfaces with an AlloyDB database to perform vector similarity searches on a corpus of rules and traffic information using a provided natural language query.

## Usage Guidelines
1. **Query Specificity**: When calling the tool, provide specific details in the `query` argument. For example, instead of querying "food rules", use "rules regarding food vendors during public events".
2. **Contextual Use**: Use the tool when planning events or activities that require adherence to local municipal or state rules (e.g., street closures, noise ordinances, environmental rules).
3. **Handling Results**: The tool returns a string containing the text of the top 5 most relevant rules. If no error occurs, parse the returned string to inform your planning tasks.
4. **Error Handling**: If an error string is returned (e.g., "Error querying rules: ..."), you must report this failure or attempt an alternative approach if applicable.

## Underlying Mechanism
- The tool uses `google_ml.embedding` to convert the query into a vector representation.
- It calculates distance (`<=>`) against the `embedding` column in the `rules` table on an AlloyDB instance.
- Results are fetched in descending order of similarity, limited to 5 results.

2. איך המיומנות רשומה

ב-planner_agent/agent.py, המיומנות נטענת מהספרייה ונוספת לכלים של הסוכן. כך נראה הקוד:

import pathlib
from google.adk.skills import load_skill_from_dir
from google.adk.tools import skill_toolset

# Load the AlloyDB skill from its directory
alloydb_skill = load_skill_from_dir(pathlib.Path(__file__).parent / "skills" / "get-local-and-traffic-rules")

# Assemble the Agent with the Skill Toolset
root_agent = LlmAgent(
    name="planner_agent",
    model="gemini-3-flash-preview",
    instruction="You are a helpful marathon planning assistant...",
    tools=[
        get_local_and_traffic_rules,
        skill_toolset.SkillToolset(skills=[alloydb_skill])
    ],
    after_agent_callback=[auto_save_memories],
)

10. בדיקת הנציג

  1. מפעילים את הסוכן באופן מקומי:
uv run adk run planner_agent
  1. שואלים שאלה לגבי כללי העיר: [user]: What are the rules for running a race on the Las Vegas strip?

הסוכן יתקשר לכלי get_local_and_traffic_rules, יבצע חיפוש וקטורים ב-AlloyDB ויחזיר תשובה על סמך חלקי הכללים הרשמיים שעובדו על ידי Spark.

11. פריסת הסוכן

פריסה ל-Agent Platform

uv run adk deploy agent_engine \
  --env_file .env \
  planner_agent

12. הסרת המשאבים

כדי להימנע מחיובים שוטפים, מוחקים את המשאבים שנוצרו במהלך ה-codelab הזה.

מחיקת אשכול AlloyDB

# Delete the AlloyDB Cluster
gcloud alloydb clusters delete rules-db --region=us-central1 --force

מחיקת אפליקציית Agent Runtime

אפשר למחוק את המופע של Reasoning Engine דרך המסוף או באמצעות הפקודה gcloud (אם יש לכם את שם המשאב). כדי לפשט את התהליך, אפשר להשתמש במסוף:

  1. עוברים לדף Agent Runtime.
  2. בוחרים באפשרות planner_agent –> לוחצים על סמל האפשרויות הנוספות (3 נקודות) בצד שמאל.
  3. לוחצים על מחיקה.

13. מזל טוב

מעולה! שיפרתם בהצלחה סוכן ADK עם יכולות מתקדמות של זיכרון וביסוס נתונים.

מה למדתם

  • סוכנים עם שמירת מצב: שילוב של סשנים בפלטפורמת הסוכנים כדי לשמור על ההקשר של השיחה.
  • למידה לטווח ארוך: צירוף מאגר זיכרון של פלטפורמת נציגים כדי לאפשר לנציג ללמוד מאינטראקציות עם משתמשים.
  • הטמעת נתונים: שימוש ב-Spark Lightning Engine וב-Document AI לעיבוד מסמכים לא מובנים.
  • RAG: יצירת מערכת חיפוש וקטורי ב-AlloyDB כדי להנחות את הסוכן לפי כללים מהעולם האמיתי.

השלבים הבאים