Rush Hour: Gemini Enterprise Agent Platform workshop
Module 1

BUILD

Vibe-build a multi-tool transit-crisis agent by prompting,
then run it locally in the ADK playground.

ADK Agent Garden gemini-3.5-flash
Objective

Stand up a working, multi-tool transit agent by prompting, and run it locally in the ADK playground. You describe what you want; the coding agent writes the code.

Build in 4 Steps

Stuck? There's a reference solution

If AGY gets blocked (quota exhausted, repeated errors, hallucination) and you can't get working code, grab the completed project from BwG-track2/reference/ (agent + 3 tools + the M2 runtime wrapper) and continue — so you can still reach the deploy. See the reference's README for how to drop it in.

1
6 min

Scaffold the Project

We need a project structure before we can write any agent code. The agents-cli scaffold command creates a standard layout with pyproject.toml, agent definition, tools module, and environment config — the same shape that later deploys cleanly to Agent Runtime in M2.

Paste into AGY Module 1 Step 1: scaffold the ADK project (scaffold only). 1. Make sure agents-cli is runnable: add $HOME/.local/bin to PATH (or use its full path) and confirm the active GCP project with "gcloud config get-value project". 2. Scaffold a new ADK agent project for a transit assistant that will answer commuter questions during a service disruption. Name the project exactly "transit-assistant". Use Python, gemini-3.5-flash as the model, and uv for dependency management. 3. Move my existing data/ folder (downloaded in M0) into the project root so the tools can read it later. Scaffold only — do not write the tools yet; I'll describe them in the next step.

Expected Result

AGY scaffolds the project, sets the model, and verifies with a smoke test:

  • pyproject.toml — dependencies including google-adk, managed by uv
  • app/agent.py — agent definition with model="gemini-3.5-flash"
  • app/tools.py — starter tools module (we add the real tools next — here or inline in app/agent.py)
  • GCP project ID resolved via gcloud config fallback
  • data/ at the project root — your M0 GTFS bundle and disruptions.json moved in, so the tools can read it
  • Smoke test passes: agents-cli run "What is the weather in SF?"

What just happened: The agents-cli scaffold skill generated a consistent, deployable project shape from a single sentence. This is a real engineering artifact, not a demo — the exact same structure deploys to cloud in M2.

The 3 Tools — In Detail

Your agent needs three deterministic tools. Each one answers a specific question. Together, they implement the reconciliation pattern.

TOOL 1 Schedule Lookup GTFS → "what should happen" stops.txt + stop_times.txt + trips.txt TOOL 2 Disruption Check disruptions.json → "what is happening" affected trips, delays, cancellations RECONCILE JOIN on trip_id / stop_id The core pattern TOOL 3 Reroute Compute BFS/shortest path exclude disrupted, find alternatives Verified Answer "Take the 14:05 via Brussels, arr. 17:32"

1 get_scheduled_departures

Purpose: Look up "what SHOULD be running" from the static GTFS data.

get_scheduled_departures(station_name: str, time_window_start: str, time_window_end: str = None) → list[dict]
ParameterTypeDescription
station_namestrName or ID of the station (e.g., "St Pancras")
time_window_startstrStart time (HH:MM or ISO)
time_window_endstrEnd time (optional, default +2h)

Returns: List of departures with trip_id, route_name, destination, departure_time, stop_sequence.

Data source: stops.txtstop_times.txttrips.txtroutes.txt (joined on stop_id, trip_id, route_id).

2 check_disruptions

Purpose: Read "what IS happening" from the disruption feed.

check_disruptions(station_name: str = None, trip_id: str = None) → list[dict]
ParameterTypeDescription
station_namestrFilter by affected station (optional)
trip_idstrCheck a specific service (optional)

Returns: List of disruptions with trip_id, affected_stops, delay_minutes, status (cancelled/delayed), cause, time_window.

Data source: data/disruptions.json

3 compute_reroute

Purpose: Find an alternative path avoiding disrupted services.

compute_reroute(origin: str, destination: str, departure_after: str) → dict
ParameterTypeDescription
originstrStarting station
destinationstrTarget station
departure_afterstrEarliest acceptable departure

Returns: Recommended route as a list of legs (each with departure_time, arrival_time, station_from, station_to, trip_id, route_name), plus total_journey_time and num_changes.

Logic: Filter out disrupted services, search remaining connections for a valid path (BFS / shortest-path over the 17-station graph).

2
9 min

Give the Agent Its Tools

A plain LLM knows timetables but not what's happening right now. Our agent needs three deterministic tools to reconcile the published schedule against the live disruption feed. The magic is the join of schedule × disruption on trip_id / stop_id — this is the pattern that makes the agent more than a chatbot.

Paste into AGY Module 1 Step 2: add the three tools. Add three tools to the agent: 1. get_scheduled_departures — queries the static GTFS data in data/gtfs/ to list departures from a given station within a time window. Join stops.txt, stop_times.txt, trips.txt, and routes.txt. 2. check_disruptions — reads data/disruptions.json and returns affected services, delays, and cancellations. Supports filtering by station or trip_id. 3. compute_reroute — given origin, destination, and departure_after, builds a station graph from GTFS connections, removes disrupted services, and finds an alternative path (BFS/shortest-path). Define the three tools in a dedicated app/tools.py (not inline in app/agent.py) and import them into the agent — later modules refer to tools.py for the data-path resolution. Write clear docstrings and type hints so the model can call them correctly. The critical pattern is reconciliation: joining schedule data with disruption data on trip_id and stop_id. Keep tool return values compact — return only what the model needs (trip_id, route, destination, departure/arrival time); do not dump full per-stop lists, so large queries don't overflow the model's context window. Scope: only add the tools and wire them into the agent — do not write the system instruction yet (that's the next step), and do not modify the eval datasets or run evals (that comes later in M4).

Expected Result

AGY writes the three tools into app/tools.py, imports them into app/agent.py, runs linting:

  • get_scheduled_departures — joins stops.txt, calendar_dates.txt, trips.txt, routes.txt, stop_times.txt; normalizes departures with times and route names
  • check_disruptions — parses disruptions.json; filters by station_query (fuzzy) or trip_id (exact)
  • compute_reroute — Dijkstra priority-queue search; filters cancelled trips, adjusts for delays, enforces 5-min transfer buffer
  • Helper: resolve_station_id — maps user input like "St Pancras" to st_pancras_international

Linting passes and a smoke test (agents-cli run "…") confirms the tools are wired up correctly.

What just happened: The agent now has deterministic capabilities. The reconciliation pattern (Tool 1 + Tool 2 joined on trip_id) is the core of what makes this agent useful. See the tool specifications above for details.

3
3 min

Write the System Instruction

The system instruction defines the agent's behavior as an engineering asset — explicit, reviewable, version-controlled. This is what governance (M3) and optimization (M4) will target in later modules.

Paste into AGY Module 1 Step 3: write the system instruction. Write the system instruction for the agent with these five rules: 1. Stay calm and factual. 2. Always reconcile schedule against disruptions before answering. 3. Never invent a train, platform, time, or any detail a tool did not return — if the information isn't available (for example platform assignments), say you don't have it. 4. Proactively compute reroutes when services are disrupted. 5. End every response with one clear recommended action.

Expected Result

AGY updates the instruction field in agent.py with five core rules:

  1. Calm and factual demeanor — reassuring tone, no speculation
  2. Mandatory reconciliation — always call get_scheduled_departures AND check_disruptions, join on trip_id/stop_id before answering
  3. No hallucinated details — only reference trips, times, and facts the tools return; if data (e.g. platform assignments) isn't available, say so instead of guessing
  4. Proactive rerouting — automatically call compute_reroute when any service is disrupted
  5. Actionable conclusion — end every response with exactly one clear recommended action

Linting and tests still pass after the update.

What just happened: The system instruction is not a vibe — it's a specification. In M3 you'll see how governance enforces it; in M4 you'll measure how well the agent follows it.

4
6 min

Run It Locally — The Playground

Time to see the agent in action. Launch the agents-cli playground — this invokes the adk web dev UI under the hood — grab the link, and try several crisis questions yourself. Watch the reasoning trace to see why the agent does what it does — and have the coding agent keep an eye on the running server, catching and fixing any errors as you test.

Paste into AGY Module 1 Step 4: run it locally in the playground. 1. Run the agent locally with adk web by calling the agents-cli playground command, and share the link for me to test. 2. Share multiple queries I can test to make sure the whole system works as intended. 3. Keep checking the server that's running in the background, and catch and fix any errors.
No browser? Test the same scenarios headlessly

If you'd rather (or can't open the Dev UI), have the agent run each scenario against the running playground from the terminal — same agent, no browser:

terminalagents-cli run --url http://127.0.0.1:8080 --mode adk "Will my 13:31 St Pancras to Paris train run today?"

(Point --url at whatever local URL the playground prints.) The trace shows the tool calls and final answer, so you can verify reconciliation/reroute without the Dev UI.

Expected Result

AGY starts the playground in the background and hands you a link to test:

  • Runs agents-cli playground (the adk web Dev UI) and shares the local URL — typically http://localhost:8000
  • Keeps the server running in the background and tails its logs, catching and fixing any runtime errors (wrong data paths, import errors, tool exceptions) as they surface
  • Suggests test queries so you can exercise the whole system — work through the three scenarios below

Scenario 1 — Reconciliation & Proactive Reroute (cancelled service)

The headline flow: reconcile the static schedule against the live disruption feed, then proactively compute an alternative.

promptI'm booked on the 16:31 St Pancras to Paris service today. Is it still running, and if not, how do I still get to Paris?

What to watch for:

  1. Calls get_scheduled_departures(station_name='St Pancras', start_time='16:00', end_time='17:00', date_str='2026-07-15') to resolve the 16:31 Paris departure.
  2. Calls check_disruptions and joins on trip_id — the 16:31 service is cancelled by the St Pancras throat signal failure.
  3. Proactively calls compute_reroute(origin='St Pancras', destination='Paris Gare du Nord', departure_after='16:31', date_str='2026-07-15') for the earliest alternative (e.g., transferring at Brussels Midi).
  4. Closes with exactly one prominent recommended action.

Scenario 2 — Status of a Delayed Service

Checks that the agent reports a precise delay rather than a blanket "cancelled".

promptWhat's the status of the 18:04 Eurostar from St Pancras to Brussels?

What to watch for:

  1. Calls get_scheduled_departures around 18:00 to find the Brussels service.
  2. Calls check_disruptions and reconciles the service as delayed (+30 min), with a new estimated departure — not cancelled.
  3. Reports the exact delay and still ends with one recommended action.

Scenario 3 — A Service That Is Not Affected (no false positives)

Confirms the agent doesn't invent a disruption for a train outside the affected window.

promptWill my 13:31 St Pancras to Paris train run today?

What to watch for:

  1. Calls get_scheduled_departures and finds the 13:31 departure.
  2. Calls check_disruptions — the trip is not in the affected list (it departs before the 15:00–19:30 disruption window).
  3. Confirms the service is operating normally — no hallucinated disruption — and still ends with one recommended action.

What just happened: The reasoning trace proves the agent is reconciling data, not guessing. This traceability is the foundation for governance (M3) and optimization (M4).

Test & Trace

What to Look For in the Trace

Open the reasoning trace after the agent responds. You should see:

OrderTool calledExpected behavior
1 get_scheduled_departures Queries St Pancras departures around 16:31 — finds the Paris service
2 check_disruptions Checks the 16:31 trip — finds it's cancelled due to the signal failure
3 compute_reroute Finds an alternative via Brussels Midi, with a later departure

What a Good Answer Looks Like

The agent should:

  • Confirm whether the requested service is affected (with specific status: cancelled/delayed)
  • Explain why (signal failure at St Pancras)
  • Offer a concrete reroute with specific times and stations
  • End with one clear recommended action
Example good answer

"Your 16:31 St Pancras–Paris Eurostar is cancelled due to the signal failure. The earliest alternative routes via Brussels Midi — change at Brussels for an onward service to Paris Gare du Nord (1 change). Recommended: rebook onto the next Brussels Midi departure and change there for Paris."

Common Issues

SymptomLikely causeFix
Agent doesn't call tools Missing/bad docstrings or type hints Check tool function signatures and docstrings
Agent invents train times System instruction not enforced Strengthen: "NEVER invent — only reference trips in the data"
Tool returns empty results GTFS data path wrong or not loaded Verify data/gtfs/ files exist and paths in tools are correct
Reroute tool fails Graph not built from GTFS connections Ensure the tool builds a station graph from stop_times.txt

Mentor Checkpoint — Done When:

  • ADK project is scaffolded with correct structure
  • All 3 tools are implemented with docstrings and type hints
  • System instruction is written and attached to the agent
  • Agent runs in the ADK local playground
  • Agent answers the crisis question by calling all 3 tools
  • Agent recommends a concrete reroute (not a vague answer)

Module Recap — BUILD

Beat 1 What You Typed

What you type into Antigravity. In this module, you'll issue 4 prompts:

StepThe Prompt
1.1 Scaffold"Scaffold a new ADK agent project for a transit assistant…"
1.2 Tools"Add three tools: schedule lookup, disruption check, reroute compute…"
1.3 Instruction"Write the system instruction: stay calm, reconcile, never invent…"
1.4 Run & Test"Run it locally with adk web, share the link, and give me queries to test…"

Why it matters: Proves the "do it with a prompt" promise. You described 4 capabilities in natural language and got a working, testable agent.

Beat 2 What Ran Under the Hood

Which skill / ADK feature the coding agent ran for you:

StepWhat ran
1.1agents-cli scaffold — created project structure, agent def, tools module, env config
1.2Code generation with ADK FunctionTool pattern — docstrings become the model's tool descriptions
1.3System instruction injected into the agent's instruction field
1.4adk web / agents-cli playground — local Dev UI for interactive testing and the reasoning trace

Why it matters: Demystifies the automation. You know exactly what happened.

Beat 3 What You Can See in the Console

Leave the IDE and look at the real artifact:

  • Launch the ADK local web playground (adk web)
  • Ask the crisis question and watch the agent call tools
  • Open the reasoning trace — see tool calls, arguments, return values, and the order the agent chose

Why it matters: The agent is not a black box. You can see exactly why it gave the answer it did.

Beat 4 Why It Matters

The enterprise value of what you just built:

  • Consistent project shape — same scaffold deploys cleanly to Agent Runtime (M2)
  • Behavior as code — the system instruction is explicit, reviewable, version-controlled
  • Traceability — the reasoning trace is the foundation for governance (M3) and optimization (M4)
  • Deterministic tools — the agent computes, not guesses; each answer is verifiable against the data