Rush Hour: Gemini Enterprise Agent Platform workshop
Module 2

SCALE

Move from "runs on my laptop" to a managed, stateful, secure cloud service — by prompting.

Agent Runtime Sessions Memory Bank Code Execution
Objective

Deploy your working agent to Agent Runtime, add Sessions for conversation continuity, Memory Bank for long-term personalization, and a Code Execution sandbox for safe computation. All by prompting in Antigravity.

Heads up — you deploy ONCE, at the end

This module is structured code first, test locally, deploy once. You make all the changes (runtime entrypoint, sessions, memory, code execution) and prove them with a local smoke test — then deploy a single time in Step 5. A cloud deploy takes ~5–10 min and only tells you about runtime bugs after the wait, so we catch those bugs locally in seconds first.

Platform Artifacts — What You Build in M2

Every artifact is a real, managed enterprise service. After M2, your agent has moved from a local script to a production-grade cloud deployment.

M1 · Local Agent adk web · your laptop deploy Agent Runtime Managed deployment HTTPS endpoint Auto-scaling 0 → N Terraform + Cloud Build Sessions Short-term state Conversation continuity Memory Bank Long-term memory Cross-session recall Code Execution Sandboxed compute Isolated, ephemeral End User HTTPS query Personalized Stateful

1 Agent Runtime Instance

What: A managed, serverless deployment of your ADK agent with an HTTPS endpoint.

Why: Handles scaling (zero to thousands), health monitoring, and versioning. You don't manage containers, VMs, or load balancers.

Where to see it: Agent Platform console → Runtime → your agent instance.

2 Sessions

What: Short-term state that persists across turns within a single conversation.

Why: Keeps the conversation coherent. "What about the next one?" makes sense because the agent remembers you were asking about St Pancras to Paris.

How: ADK's session service stores turn history and agent state per session ID.

3 Memory Bank

What: Long-term, cross-session memory keyed by user identity.

Why: Personalization. The agent remembers your home station, frequent routes, and preferences across separate sessions — even days apart.

How: Key-value storage scoped to user ID. The agent writes memories during conversation; retrieves them at session start.

4 Code Execution Sandbox

What: An isolated compute environment for running model-generated or tool code.

Why: Security. Reroute computations, data transformations, and any generated Python run in ephemeral containers with resource limits and network isolation.

Guarantee: Even if the model is tricked into generating malicious code, the sandbox prevents it from touching your environment or data.

Scale in 6 Steps

Stuck? Use the reference solution to still deploy

If AGY can't produce a working agent (quota, errors, hallucination), you can still finish the deploy: grab the completed project from BwG-track2/reference/, copy its app/ + scripts/ over your project, then run Steps 4–5 (smoke test + deploy). The runtime wrapper there is the same tested one Step 1 fetches.

The Leap

In M1, your agent ran locally. Now you'll deploy it to a managed cloud service that scales from zero to thousands of concurrent users — without infrastructure management.

1
4 min

Prepare the Runtime Entrypoint — no deploy yet

Deploying an ADK agent needs a small runtime wrapper (app/agent_runtime_app.py) that adapts your agent to Agent Runtime. Hand-writing it is fiddly and error-prone, so you simply drop in the workshop's tested, production-ready wrapper — you don't write or edit it.

Paste into AGY Module 2 Step 1: prepare the Agent Runtime entrypoint (do NOT deploy yet). 1. Make the project deploy-ready: copy the data/ directory into app/ so it ships with the bundle, and make data-path resolution relative to the file location (pathlib.Path(__file__).parent) so it works locally and in the container. 2. Generate the project's Agent Runtime entrypoint: agents-cli scaffold enhance . --deployment-target agent_runtime 3. The generated app/agent_runtime_app.py is a naive stub that crashes on the deployed runtime. Replace it with the workshop's TESTED wrapper — you do NOT need to write or understand its internals, just drop it in: curl -fL -o app/agent_runtime_app.py https://kazunori279.github.io/gcp-eoa/BwG-track2/agent_runtime_app.py 4. VERIFY the replacement took (re-running scaffold enhance would overwrite it with the broken stub). The tested wrapper contains the marker "TESTED REFERENCE"; confirm it's there, and re-fetch if not: grep -q "TESTED REFERENCE" app/agent_runtime_app.py || curl -fL -o app/agent_runtime_app.py https://kazunori279.github.io/gcp-eoa/BwG-track2/agent_runtime_app.py Do not proceed until "TESTED REFERENCE" appears in app/agent_runtime_app.py. Do NOT deploy in this step. Step 4 smoke-tests this entrypoint locally; Step 5 deploys once.
Why the tested wrapper (not the scaffold stub)

The generated stub is a minimal starting point. To make it run smoothly in the cloud, the tested wrapper adds three deploy-time essentials the deployed runtime needs — region-pinned Session/Memory services, a Memory Bank fallback, and a fresh per-request client (no Event loop is closed) — which Step 4's smoke test verifies locally before you deploy. It also normalizes the session_id Gemini Enterprise sends, which is what makes the agent publishable in M5 — we'll revisit that detail when you publish.

Expected Result

  • data/ ships inside app/ with file-relative path resolution
  • app/agent_runtime_app.py is the tested reference (region-pinned session/memory, memory fallback to InMemoryMemoryService, a per-request client, and session_id normalization for the M5 portal)
  • Nothing deployed yet — that is Step 5
2
4 min

Remember the Commuter — Sessions + Memory Bank

Right now, each query is stateless. We need two kinds of memory: Sessions (short-term, within a conversation) so "What about the next one?" makes sense, and Memory Bank (long-term, cross-session) so the agent remembers your home station next week.

Paste into AGY Module 2 Step 2: add session state + Memory Bank (code only, NO deploy). First, use the google-developer-knowledge MCP to read up on ADK Sessions and Memory Bank (including how memory generation is triggered), so you use current APIs and defaults. 1. Add session state for the current conversation AND long-term memory so the agent remembers my home station across visits. Use ADK's session service for short-term state and Memory Bank for cross-session personalization. 2. Make the code changes and verify short-term session state LOCALLY using InMemorySessionService / InMemoryMemoryService (set the home station, then recall it in the same session). 3. Self-check before you say done (session state alone is NOT enough — Memory Bank is what makes recall work in a NEW session, and it's easy to skip). Do NOT report completion until grep -nE "PreloadMemoryTool|generate_memories|after_agent_callback" app/agent.py shows BOTH: (a) a way to write long-term memory — a generate_memories/add_session_to_memory call wired as the agent's after_agent_callback; and (b) a way to read it back — PreloadMemoryTool in the agent's tools (or an equivalent preload). If either is missing, add it and re-check. (Session-state set_home_station gives same-session recall only; without the Memory Bank write+read the deployed agent will store zero memories and Step 6's cross-session recall will silently fall back to session context.) Do NOT deploy — we make all the code changes first and deploy ONCE at the end (Step 5), after the local smoke test in Step 4.

Expected Result

AGY adds three things to agent.py and runs the test suite locally (no deploy yet — the single deploy happens in Step 5):

  • Session state — a set_home_station tool writes tool_context.state, with an init_state before-agent callback; the home station is read back from injected session state (so recall needs no tool call)
  • Memory Bank — registers PreloadMemoryTool() and a generate_memories_callback (after-agent) that calls callback_context.add_session_to_memory()
  • Graceful fallback — memory callback wrapped in try/except so local pytest runs don't fail when memory services are unavailable

AGY verifies short-term session state locally with in-memory services (set the home station, then recall it in the same session). You'll exercise the full set of scenarios — session state, cross-session memory, and personalized reroute — against the deployed agent in Step 6, after the single deploy.

What just happened: Personalization and continuity are now managed services, not app code you maintain.

3
4 min

Safe Math — Code Execution Sandbox

Give the agent a code execution sandbox so it can write and run Python on the fly for ad-hoc math the fixed tools don't cover — safely, without running model-generated code on the host.

Paste into AGY Module 2 Step 3: add code execution (code only, NO deploy). First, use the google-developer-knowledge MCP to read up on ADK code execution (code executors / BuiltInCodeExecutor), so you use the current API. 1. Add a code execution sandbox so the agent can write and run Python on the fly for ad-hoc math the tools don't already return — like the average delay across delayed trains, the cancellation ratio, or the peak bottleneck hour. Use BuiltInCodeExecutor from google.adk.code_executors (the model's built-in sandbox, no external infra). Run the math over the data the existing tools return. 2. Update the system instruction to mention the sandbox. 3. Self-check before you say done (this edit is easy to skip — do NOT report completion until BOTH checks pass): (a) grep -n "BuiltInCodeExecutor" app/agent.py must show both the from google.adk.code_executors import BuiltInCodeExecutor import AND code_executor=BuiltInCodeExecutor() set on the agent — if either is missing, add it and re-check; (b) run it LOCALLY with agents-cli run using a question that needs this math, and paste the trace line proving the built-in code execution actually ran (executable code + its result in the trace), not a hardcoded tool answer. Do NOT deploy. All code changes are now in (entrypoint, sessions, memory, code exec); next we smoke-test the runtime path locally (Step 4), then deploy ONCE (Step 5).
Why this matters for platform owners

Non-deterministic or model-generated code can't touch your environment. The sandbox enforces resource limits (CPU, memory, time) and network isolation. A compromised prompt can't exfiltrate data or harm the host.

Lab environment — use the built-in sandbox

The managed Agent Platform / Vertex AI code sandbox (VertexAiCodeExecutor, backed by a Vertex AI code-interpreter Extension) is blocked in lab/training projects: it fails with a 500 INTERNAL error because these projects can't create the external VPC-isolated VM the extension needs. So for the lab we use the built-in model sandbox (BuiltInCodeExecutor), which runs Python inside Gemini itself with zero external infra — just to see code execution work end to end.

Tradeoff: the built-in sandbox has no file I/O, so it can't read data/gtfs/* or data/disruptions.json directly — it computes over the structured data your tools return. In a real (non-lab) project, use the managed Agent Platform sandbox for file-based data science.

Expected Result

AGY configures BuiltInCodeExecutor, updates the system instruction, then verifies locally with agents-cli run — using a test question that needs math no single tool returns.

What to watch for:

  1. Calls check_disruptions to pull the affected trips.
  2. The trace shows the model writing and running Python in the built-in sandbox over that data — confirming code execution actually fired, not a hardcoded tool.
  3. Returns computed values: average delay 26.25 min across the 4 delayed trains (45/30/15/15) and a 50% cancellation rate (4 of 8 affected), then closes with one recommended action.

All the code is now in. Next, Step 4 proves the deployed runtime path works locally — before spending a single deploy.

4
3 min

Catch Deploy-Blockers Locally — Runtime Smoke Test

Before deploying, run your actual agent through the same code path the deployed container uses on your laptop — each run is seconds, so a bug in your tools, session/memory wiring, or the runtime wrapper surfaces now instead of after a 5–10 min deploy.

The smoke test imports and runs the agent you built — your tools, instruction, and the session / Memory Bank / code-exec wiring from Steps 2–3 — through the exact entrypoint Agent Runtime uses, and confirms the tested wrapper is still in place (a re-run of scaffold enhance can silently overwrite it). That deployed code path never runs in the M1 adk web playground, so this is your first chance to catch cloud-only failures locally. Three classic ones it surfaces:

What you'd see on deployWhy it happensHow the smoke test catches it
Agent never starts, or every reply is empty — an import / init error Module-level setup in your app/ code (agent, tools, or the wrapper) runs the moment the container loads it; a bad import or initialization the playground tolerated now throws on startup Imports app/agent_runtime_app and builds the app exactly as the runtime does, so any startup error throws locally in seconds
Invalid Session resource name Vertex AI Session & Memory services must be pinned to a real region; left at the default global location, the session resource path the runtime constructs is malformed and rejected Forces a global location, then asserts the services still resolve to a real region — failing loudly if the wrapper's region-pinning is missing
Event loop is closed An async client created once and reused gets bound to an event loop that's already closed by the next request, so the second query onward crashes Drives a real query through async_stream_query in a single asyncio loop, exercising the per-request client path end to end
Paste into AGY Module 2 Step 4: local runtime smoke test (catch deploy-blockers before deploying). Run the workshop's tested smoke test, which exercises the SAME code path the deployed runtime uses — so we never spend a 10-minute deploy to discover a bug a 10-second local run can catch. It: - imports app/agent_runtime_app - asserts the session/memory services resolve to a real region (not "global") - drives one query end-to-end through async_stream_query in a single asyncio loop with a full-path session_id. mkdir -p scripts && curl -fL -o scripts/runtime_smoke.py https://kazunori279.github.io/gcp-eoa/BwG-track2/runtime_smoke.py uv run python scripts/runtime_smoke.py It should print "Smoke query executed successfully" with NO "Invalid Session resource name" and NO "Event loop is closed". If it fails, fix app/agent_runtime_app.py and re-run (each run is seconds). Do NOT deploy yet — Step 5 is the deploy.
Why this catches all three without a live engine

Vertex AI sessions live under the reasoningEngine, so the real sessions API can't be hit before the first deploy — but you don't need it: the init crash shows up on import, the global location shows up as a config/string check, and Event loop is closed reproduces by driving async_stream_query with an in-memory session service through one asyncio loop.

Expected Result

  • scripts/runtime_smoke.py runs locally and prints a real answer
  • No Invalid Session resource name, no Event loop is closed, no import/init error
  • If it fails, AGY fixes app/agent_runtime_app.py and re-runs in seconds — you reach a green smoke test before any deploy
5
8 min

Deploy Once

The smoke test is green, so the runtime path already works. Now deploy a single time, shipping sessions + Memory Bank + code execution together.

Paste into AGY Module 2 Step 5: deploy ONCE. First, a pre-deploy gate: confirm the tested wrapper is still in place — re-running scaffold enhance can silently overwrite it with the broken stub, which would crash the deployed agent. Check for the marker and re-fetch if missing: grep -q "TESTED REFERENCE" app/agent_runtime_app.py || curl -fL -o app/agent_runtime_app.py https://kazunori279.github.io/gcp-eoa/BwG-track2/agent_runtime_app.py Do NOT deploy unless "TESTED REFERENCE" is present in app/agent_runtime_app.py. The local smoke test passes, so deploy to Agent Runtime a single time, shipping sessions + Memory Bank + code execution together: agents-cli deploy --agent-identity (--agent-identity gives the agent a per-user identity for personalization; more in M5.) After deploy completes (~5-10 min), verify the deployed endpoint by running: agents-cli run --url ENDPOINT_URL --mode adk with a test question about the 13:31 St Pancras to Paris train, and share the Console Playground link. Do NOT attempt to fix or debug gcloud auth issues. If deploy status polling fails, just tell me and I will check the console.
Don't wait — overlap the build

This deploy takes ~5–10 min — kick it off and keep exploring the console (or re-read Platform Artifacts above) while it builds. AGY pings you when the endpoint is live. Use the most recent link AGY prints, not one you saved earlier.

Fallback — if deploy fails with an auth error

This is an intermittent Cloud Shell issue. Try the deploy again from your project directory:

terminalcd "$(dirname "$(find ~ -name deployment_metadata.json -o -name pyproject.toml | head -1)")" && agents-cli deploy --agent-identity --no-confirm-project

If it still fails, open the Agent Platform console to check whether the deployment actually went through. Don't let AGY spin on retries.

Expected Result

AGY runs agents-cli deploy --agent-identity once and polls status (~5–10 min). Because the runtime path was proven locally in Step 4, the first deploy serves correctly.

  • A managed Agent Runtime instance (Reasoning Engine) with an HTTPS endpoint, shipping sessions + memory + code execution
  • AGY shares the endpoint URL and the Agent Engine Console Playground link (written to deployment_metadata.json)
  • The remote query confirms trip 9024 (13:31 departure) is unaffected — the signal failure starts at 15:00 — with no session/runtime error
  • Deployed with agent identity enabled and the normalized session_id — both for the M5 portal
6
6 min

Test the Deployed Agent — Run the Scenarios

Now that the Step 5 deploy is live, open the Agent Engine Console Playground link AGY shared and work through these scenarios against the deployed agent — the same endpoint your users hit. Run them in order; Scenario 2 depends on Scenario 1.

Scenario 1 — Short-Term Session State (set the home station)

Verifies the agent processes set_home_station and stores it in the active session's state.

prompt 1Hello, please set my home station to Paris Gare du Nord.

What to watch for: the trace shows set_home_station(station_name='Paris Gare du Nord'); the agent confirms it's set.

prompt 2What is my home station again?

What to watch for: the agent answers immediately without calling any tool, reading "Paris Gare du Nord" from the injected session state.

Scenario 2 — Cross-Session Memory (Memory Bank)

Click New Session / Reset Session (top-right of the Console Playground). This starts a new session ID but keeps your user_id.

promptWhat is my home station and are there any active disruptions affecting Eurostar trains connected to it today?

What to watch for:

  1. The agent preloads your user:home_station memory ("Paris Gare du Nord") — proving recall across a brand-new session.
  2. It queries check_disruptions(station_name='Paris Gare du Nord').
  3. It lists the active disruptions on Paris-bound Eurostar services from the St Pancras throat signal failure (e.g., the cancelled 15:31 and 16:31 departures and the delayed 17:31/18:31 services).

Verify it in the console — this is the ground truth. Open the Memory Bank console and confirm a memory was actually written. You should see a row like:

ScopeResource NameFact
app_name: app
user_id: cli-user
…/4586226276930420736 My home station is Paris Gare du Nord.

Memory Bank writes are asynchronous. The memory is generated by the turn's after_agent_callback after Scenario 1's session ends, so the row (and Scenario 2's cross-session recall) can take a few seconds to appear — if it's not there yet, wait a moment and refresh / re-ask before concluding anything.

If the agent "remembers" but no row ever appears here, the recall came from conversation history, not Memory Bank — so make sure you truly started a new session (new session ID, same user_id) before trusting the result.

Scenario 3 — Personalized Reconciliation & Reroute (memory + the full chain)

Confirms the deployed agent fuses long-term memory with live reconciliation: it infers "home" from Memory Bank, then reconciles and reroutes.

promptI'm heading home from St Pancras this evening — what are my options given the disruption?

What to watch for:

  1. Recalls your user:home_station ("Paris Gare du Nord") from memory — you never restate the destination.
  2. Calls get_scheduled_departures(station_name='St Pancras', start_time='17:00', end_time='20:00', date_str='2026-07-15') and check_disruptions, reconciling on trip_id to flag the cancelled/delayed evening Paris services.
  3. Proactively calls compute_reroute(origin='St Pancras', destination='Paris Gare du Nord', departure_after='17:31', date_str='2026-07-15') for the earliest alternative (e.g., via Brussels Midi).
  4. Closes with exactly one prominent recommended action.

Scenario 4 — Code Execution (ad-hoc math)

Confirms the built-in sandbox computes a stat no single tool returns — the same question AGY verified locally in Step 3, now on the deployed agent.

promptAcross the currently disrupted St Pancras departures, what's the average delay of the delayed trains, and what fraction are cancelled?

What to watch for: the trace shows the model writing and running Python in the built-in sandbox, returning e.g. average delay 26.25 min and a 50% cancellation rate, then one recommended action.

Be patient — this one is slow: code-execution queries run a multi-step write-execute-analyze loop in the sandbox, so on the deployed agent this can take a few minutes, and the first call may even time out while the sandbox warms up — if it does, just send it again. The latency is normal, not a failure.

Expected Result

All four scenarios pass against the deployed agent: session-state recall, cross-session memory (with a row in the Memory Bank console), personalized reroute, and sandbox-computed math — the same endpoint your end users hit.

Fallback — if a scenario returns nothing (or errors)

The Step 4 smoke test should have caught this class of bug already. If a deployed scenario still returns a blank reply after some tool calls, it means an exception inside the deployed agent. Have AGY pull the Agent Runtime (Reasoning Engine) logs from Cloud Logging and find the cause:

Paste into AGY Module 2 Step 6 (debug): find why the deployed agent returned nothing. The deployed agent called some tools then returned an empty response. Read the Agent Runtime logs from Cloud Logging and identify the cause — do NOT change code yet, just diagnose and tell me the root cause and the smallest fix. Read project/region/engine id from deployment_metadata.json, then pull the recent stdout + stderr (stderr has the tracebacks): gcloud logging read 'resource.type="aiplatform.googleapis.com/ReasoningEngine" AND resource.labels.reasoning_engine_id="ENGINE_ID" AND (logId("reasoning_engine_stdout") OR logId("reasoning_engine_stderr"))' --project=PROJECT_ID --freshness=1h --order=desc --limit=100 Quote the traceback/error lines around the failing request and tell me the root cause.

Mentor Checkpoint — Done When:

  • Runtime entrypoint is region-correct, with memory fallback + per-request client
  • Local runtime smoke test passes (no session / event-loop error) before deploying
  • Agent is deployed to Agent Runtime in a single deploy, with a live endpoint
  • Endpoint answers the crisis question correctly
  • Session state maintains conversation context across turns
  • Memory Bank stores and recalls the commuter's home station
  • A new session recalls the stored home station
  • Agent uses code execution to compute an ad-hoc stat (e.g., average delay)

Module Recap — SCALE

Beat 1 What You Typed

What you type into Antigravity. In this module:

StepThe Prompt
2.1 Prep"Prepare the Agent Runtime entrypoint — region-correct sessions/memory, memory fallback, per-request client, normalized session_id. Don't deploy yet."
2.2 Memory"Add session state and long-term memory so the agent remembers my home station — verify locally with in-memory services, no deploy."
2.3 Code Exec"Add a code execution sandbox (BuiltInCodeExecutor) for ad-hoc math, test it locally with agents-cli. No deploy."
2.4 Smoke test"Build a local runtime smoke test that drives async_stream_query in one event loop — iterate until it passes, before any deploy."
2.5 Deploy once"Now deploy once with agent identity — shipping sessions + memory + code exec together."

Why it matters: A handful of prompts take you from a local script to a deployed, stateful, personalized cloud service that can compute on the fly — and by testing the runtime path locally first, you deploy once instead of debugging through repeated cloud deploys.

Beat 2 What Ran Under the Hood

What the coding agent ran for you:

StepWhat ran
2.1agents-cli scaffold enhance — generated a region-correct agent_runtime_app.py (memory fallback, per-request client, normalized session_id)
2.2ADK Session service + Memory Bank integration — state persistence and cross-session recall (verified locally with in-memory services)
2.3BuiltInCodeExecutor — model-written Python runs in Gemini's built-in sandbox for ad-hoc math
2.4scripts/runtime_smoke.py — drove async_stream_query through one event loop locally to catch session/loop bugs in seconds
2.5agents-cli deploy --agent-identity — one deploy: packaged the ADK app, created the Agent Runtime instance, provisioned the HTTPS endpoint

Beat 3 What You Can See in the Console

Leave the IDE and see the real enterprise artifacts:

  • Agent Platform console — find your deployed agent, see its endpoint and health status
  • Live endpoint — query it over HTTPS and get the crisis answer from the cloud
  • Memory write + new session — see a memory stored, then recalled in a fresh session
  • Code execution trace — watch the model write and run Python for a stat like average delay

Beat 4 Why It Matters

The enterprise value of what you just built:

  • Serverless scale — zero to thousands of concurrent users, no infrastructure management
  • Stateful by default — sessions and memory are managed services, not app code
  • Secure compute — model-generated code runs in an isolated sandbox, never on your host
  • Same project shape — M1's code deployed without modification