« Phase 22 README · Warmup · Deep Dive · Principal Deep Dive · Core Contributor · Staff Notes

Phase 22 — Hitchhiker's Guide to Google ADK

The compressed practitioner tour. Read the WARMUP for the derivations; this is the stuff you say in the meeting.

30-second mental model

Google ADK is a code-first, model-agnostic agent framework, Gemini/Vertex-optimized. An agent is an LlmAgent = model + instruction + tools + output_key. A FunctionTool wraps a Python function and auto-derives its schema from the signature. A Runner drives the loop and yields Events (model response, tool call, tool result, final). The signature idea: workflow agents (Sequential/Parallel/Loop) put deterministic, code-defined orchestration around LLM leaves — as opposed to sub_agents + transfer_to_agent, where the model routes. State is a scoped dict (user:/app:/temp:) in a SessionService; callbacks (before/after × agent/model/tool) are the interception layer, and a before_* that returns a value short-circuits the step (cache / guardrail). Ship it to Vertex AI Agent Engine.

The things to tattoo on your arm

ThingWhat it is
LlmAgent / Agentmodel + instruction + tools + output_key + sub_agents
FunctionToolPython function → schema derived from the signature; no default ⇒ required
Runner.run(...)drives the loop, yields Events; event.is_final_response() pulls the answer
output_keywrites the agent's final text to session.state[output_key] — the composition seam
SequentialAgentrun sub-agents in order, shared state (step 2 sees step 1's output_key)
ParallelAgentrun sub-agents on the same input, independent branches, merge outputs
LoopAgentrepeat until a sub-agent **escalate**s OR max_iterations
sub_agents + transferthe model picks which child to hand control to (non-deterministic)
state scopesuser: (per-user) · app: (whole app) · temp: (this turn) · none (this session)
callback short-circuita before_* returning a value skips the wrapped step
deployVertex AI Agent Engine (managed sessions, scaling, tracing) or Cloud Run

The one distinction interviewers actually probe

Deterministic workflow agents (order fixed in code) vs LLM-driven sub_agent transfer (order chosen by the model at runtime).

Say it as: "ADK's philosophy is deterministic structure around LLM reasoning. If the flow is knowable, I encode it as a SequentialAgent/ParallelAgent/LoopAgent so it's testable and replayable; I only use sub_agents + transfer_to_agent when routing genuinely needs to read the input at runtime. Reliability compounds — I don't spend it on a routing decision I could hard-code."

Framework one-liners

  • Google ADK = LlmAgent + tools + a Runner that yields Events; workflow agents for deterministic orchestration; scoped SessionService state; callbacks for guardrails; Vertex deploy.
  • LangGraph (Phase 18) = you draw the graph (nodes/edges/reducers/checkpoints); max control.
  • OpenAI Agents SDK = Agent + Runner + handoffs; minimal API, OpenAI-centric.
  • All three = elaborations of while not done: proposal = model(context); execute.

War stories

  • The "AI dispatcher" that flaked 1 in 8 times. A top-level LlmAgent with five sub_agents was routing a known three-stage pipeline. The model occasionally transferred to the wrong child. Rewriting it as a SequentialAgent (deterministic) killed the flakiness overnight — the routing never needed the model.
  • The parallel research agent with the heisenbug. Two ParallelAgent branches, and branch B's instruction referenced branch A's output_key. It "worked" in dev (A happened to finish first) and failed in prod. Branches must be independent; a downstream sequential step combines them.
  • The $-per-day that wouldn't drop. Same repeated support questions hitting Gemini every time. A ten-line before_model_callback cache (hash the request, return the stored Content on a hit) cut model calls ~40% — the short-circuit contract doing exactly what it's for.
  • The tool that deleted the wrong thing — once. After that, every mutating tool got a before_tool_callback that checks the args against an allow-list and returns a rejection to block the call. The guardrail is testable: assert the function ran zero times.

Vocabulary

LlmAgent/Agent (reasoning unit) · FunctionTool (function → auto-schema) · Runner (loop that yields Events) · Event (what happened; carries state_delta, escalate) · output_key (final text → state) · workflow agent (Sequential/Parallel/Loop, deterministic) · sub_agents / transfer_to_agent (LLM-driven delegation) · escalate (stop-the-loop signal) · SessionService (holds sessions + scoped state) · state scopes (user:/app:/temp:) · MemoryService (long-term recall) · callbacks (before/after × agent/model/tool) · short-circuit (a before_* return skips the step) · Vertex AI Agent Engine (managed deploy).

Beginner mistakes

  1. Thinking workflow agents use an LLM to pick the order (they're the deterministic option).
  2. Letting ParallelAgent branches depend on each other's output (race — they're concurrent).
  3. Forgetting output_key, then wondering why a SequentialAgent's steps can't see each other.
  4. Treating state as one flat dict — user:tier and tier go to different stores.
  5. Using callbacks only for logging and missing the before_* short-circuit (caches/guardrails).
  6. Reaching for sub_agents + transfer when a SequentialAgent would be reliable and testable.
  7. No max_iterations on a LoopAgent that relies on a model to escalate — it can spin.
  8. Assuming ADK is Gemini-only (it's model-agnostic via LiteLlm; just Gemini/Vertex-optimized).