BUILD
Vibe-build a multi-tool transit-crisis agent by prompting,
then run it locally in the ADK playground.
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
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.
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.
Expected Result
AGY scaffolds the project, sets the model, and verifies with a smoke test:
pyproject.toml— dependencies includinggoogle-adk, managed byuvapp/agent.py— agent definition withmodel="gemini-3.5-flash"app/tools.py— starter tools module (we add the real tools next — here or inline inapp/agent.py)- GCP project ID resolved via
gcloud configfallback data/at the project root — your M0 GTFS bundle anddisruptions.jsonmoved 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.
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]
| Parameter | Type | Description |
|---|---|---|
station_name | str | Name or ID of the station (e.g., "St Pancras") |
time_window_start | str | Start time (HH:MM or ISO) |
time_window_end | str | End time (optional, default +2h) |
Returns: List of departures with trip_id, route_name,
destination, departure_time, stop_sequence.
Data source: stops.txt → stop_times.txt →
trips.txt → routes.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]
| Parameter | Type | Description |
|---|---|---|
station_name | str | Filter by affected station (optional) |
trip_id | str | Check 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
| Parameter | Type | Description |
|---|---|---|
origin | str | Starting station |
destination | str | Target station |
departure_after | str | Earliest 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).
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.
Expected Result
AGY writes the three tools into app/tools.py, imports them into app/agent.py, runs linting:
get_scheduled_departures— joinsstops.txt,calendar_dates.txt,trips.txt,routes.txt,stop_times.txt; normalizes departures with times and route namescheck_disruptions— parsesdisruptions.json; filters bystation_query(fuzzy) ortrip_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" tost_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.
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.
Expected Result
AGY updates the instruction field in agent.py with five core rules:
- Calm and factual demeanor — reassuring tone, no speculation
- Mandatory reconciliation — always call
get_scheduled_departuresANDcheck_disruptions, join ontrip_id/stop_idbefore answering - 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
- Proactive rerouting — automatically call
compute_reroutewhen any service is disrupted - 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.
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.
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:
(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(theadk webDev UI) and shares the local URL — typicallyhttp://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.
What to watch for:
- 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. - Calls
check_disruptionsand joins ontrip_id— the 16:31 service is cancelled by the St Pancras throat signal failure. - 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). - 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".
What to watch for:
- Calls
get_scheduled_departuresaround 18:00 to find the Brussels service. - Calls
check_disruptionsand reconciles the service as delayed (+30 min), with a new estimated departure — not cancelled. - 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.
What to watch for:
- Calls
get_scheduled_departuresand finds the 13:31 departure. - Calls
check_disruptions— the trip is not in the affected list (it departs before the 15:00–19:30 disruption window). - 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:
| Order | Tool called | Expected 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
"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
| Symptom | Likely cause | Fix |
|---|---|---|
| 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:
| Step | The 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:
| Step | What ran |
|---|---|
| 1.1 | agents-cli scaffold — created project structure, agent def, tools module, env config |
| 1.2 | Code generation with ADK FunctionTool pattern — docstrings become the model's tool descriptions |
| 1.3 | System instruction injected into the agent's instruction field |
| 1.4 | adk 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