ADK Development Guide
Development lifecycle guardrails for AI agents using Google ADK and Gemini models.
4-Phase Development Lifecycle
Every ADK agent feature follows this workflow. Skipping phases increases risk of eval failures, infinite loops, and deployment issues.
Phase 1: Understand Spec
Before writing any code:
- Read
DESIGN_SPEC.mdcompletely — this is the source of truth - Identify: agent purpose, required tools, safety constraints, success criteria, edge cases
- Flag ambiguities, contradictions, or missing information before proceeding
- Check if similar agent patterns exist in your project already (avoid duplication)
- Clarify: is this a new agent or an enhancement to an existing one?
Stop if:
- The spec is vague or self-contradictory
- Required dependencies are unavailable
- The scope exceeds what a single agent should handle
Phase 2: Build
Follow these patterns:
- Use
ToolContextfor session state — never global variables - Import tools with precision (see ADK Import Precision Rules section below)
- Add
# Fallback:comment to every tool function - Use
InMemoryRunnerfor development and tests - Development commands:
adk web— launch ADK playground for interactive testinguv run pytest tests/ -v— run unit testsuv run ruff check src/ && uv run ruff format src/— lint and formatuv run mypy src/— type check
Stop if:
- Type errors appear when running
mypy - Linting fails (
ruff checkreturns non-zero) - Unit tests fail
Phase 3: Evaluate
Iterate 5–10 rounds per feature:
- Create
evalset.jsonwith: 1 happy path, 1 error path, 1+ edge cases - Create
eval_config.jsonwith appropriate eval criteria - Run:
adk eval ./app evalset.json --config_file_path=eval_config.json --print_detailed_results - Review detailed results — identify failure patterns
- Refine agent instructions, tool signatures, or tool implementations
- Re-run until success rate stabilizes
- Common pitfalls: app name must match directory name, state type mismatches, tool return types
Stop if:
- Success rate plateaus below target threshold (defined in eval_config.json)
- Same test case fails repeatedly for unrelated reasons
Phase 4: Deploy
Use adk-deploy-guide skill:
- Choose between Cloud Run (event-driven, full control) or Agent Engine (managed)
- Verify health endpoint responds after deployment
- Run
uv run pytest tests/evals/locally before opening PR - Eval gates block merge if accuracy thresholds are not met
DESIGN_SPEC.md as Primary Source of Truth
Every ADK agent project MUST have a DESIGN_SPEC.md in the agent service root before any implementation code is written.
Spec Contents (Required Sections)
# Agent Name — Design Spec
## Purpose
One-sentence mission statement.
## Example Use Cases
- User does X, agent responds with Y
- User does A, agent handles error B gracefully
## Required Tools
- tool_name: description and responsibility
- tool_name: description and responsibility
## Safety Constraints
- No [X] without [Y]
- Always [Z] before [A]
- Blocked: [B] under any condition
## Success Criteria
- Agent achieves 90% accuracy on test set
- Tool calls execute in <500ms
- Error recovery succeeds in <1 min
## Edge Cases & Fallbacks
- When tool A fails: [fallback behavior]
- When state is invalid: [reset or error response]
Implementation ↔ Spec Alignment
- Implementation must align with spec — drift = stop and reconcile
- If implementation requires spec change: update spec first, then code
- If spec is impossible to implement: surface this during Phase 1 (Understand), not Phase 2 (Build)
ADK Import Precision Rules
ADK tools must be imported from their specific submodule, not from the package level.
Correct Import Patterns
# ✅ CORRECT — import from specific submodule
from google.adk.tools.google_search import google_search
from google.adk.tools.load_web_page import load_web_page
from google.adk.tools.built_in_code_execution import built_in_code_execution
# ❌ WRONG — DO NOT import from package level
from google.adk.tools import google_search # ✗ Will fail at runtime
from google.adk.tools import load_web_page # ✗ Will fail at runtime
Commonly Used Built-In Tools (Correct Import Paths)
| Tool | Correct Import |
|---|---|
| Google Search | from google.adk.tools.google_search import google_search |
| Load Web Page | from google.adk.tools.load_web_page import load_web_page |
| Built-In Code Execution | from google.adk.tools.built_in_code_execution import built_in_code_execution |
Rule: When in doubt about import syntax, check the google-adk skill or the official ADK documentation — do not guess.
GOOGLE_CLOUD_LOCATION Debugging
Model 404 errors are almost always a location issue, not a model name issue.
Debug Sequence
Check GOOGLE_CLOUD_LOCATION env var — must be set to a region that supports the model
- Verify region supports the model in Google's documentation before assuming
Check GOOGLE_CLOUD_PROJECT env var — must be set to a valid GCP project ID
Only then check model name — but rarely the issue if steps 1–2 are correct
Example Error & Recovery
Error: 404 Model 'projects/my-project/locations/europe-west1/models/gemini-3.1-flash' not found
↓ Fix:
GOOGLE_CLOUD_LOCATION=us-central1 python app.py
Remember: Location is the #1 cause of 404 errors in ADK development. Check it first.
Code Preservation Principle
Once an agent is working, protect it from accidental breakage.
Never Do This Without Explicit Approval
- Change the model in existing agent code
- Rewrite working agent code (refactor incrementally instead)
- Remove tools from an agent
- Rename
output_keyvalues (downstream agents reference them by name) - Modify tool signatures without updating all callers
Safe Refactoring
- Extract duplicated logic into helper functions
- Rename variables for clarity
- Update comments and docstrings
- Add new tools without removing old ones
- Adjust agent instructions (careful — may change behavior)
Breaking Infinite Loops
If the same error occurs 3+ times in a row: STOP immediately. Do not retry the same approach.
Common ADK Infinite Loops
- Tool returns error → model retries same tool → same error (repeat forever)
- State validation fails → agent resets state → validation fails again
- Rate limit hit → agent retries immediately → rate limit hit again
How to Break the Loop
- Add
on_tool_error_callbackto interrupt the retry cycle - Adjust agent instruction to handle the specific error gracefully
- Add state guards to detect invalid state before calling tools
- Implement exponential backoff for rate-limited tools
Escalation Protocol
After 3 identical failures:
STOPPED after 3 identical failures:
- Attempt 1: [what you tried, what failed]
- Attempt 2: [what you tried, what failed]
- Attempt 3: [what you tried, what failed]
Options:
A) Different approach: [describe]
B) Escalate to human — need more context
Project-Specific Constraints
Replace this section with your project's ADK constraints. Document: ADK version pin, model name(s), tool placement rules (R1/R2), eval thresholds, session backend choices, and any compliance requirements.
Example table:
| Constraint | Value | Notes |
|---|---|---|
| ADK version | google-adk==X.Y.Z exactly |
Pin to specific version |
| Model | gemini-3.1-flash |
Default — override per agent if needed |
| Runner (dev) | InMemoryRunner for tests |
Never use real API in unit tests |
| Runner (prod) | Runner for production |
Wire VertexAiSessionService |
| Every tool must have | # Fallback: comment |
Defines degradation mode |
| Eval gates | Accuracy thresholds block merge | Define per-agent thresholds |
Stale Skills Detection
Run periodically to detect outdated skill files:
# Check if skills are outdated (>90 days)
npx skills check -g
# Update all skills to latest version
npx skills update -g
# Update specific skill
npx skills update adk-dev-guide -g
If a skill's last-reviewed date is >90 days old, flag it for review before using.
Testing Without Burning API Credits
When you need to validate system behavior, measure performance, or run CI without real LLM calls:
Deterministic Shadow Agent
Create a variant of your ADK agent that overrides only the LLM decision callback with deterministic heuristics. Same tool contracts, same output schema, same session state — zero model calls.
# Standard LLM-powered agent
class RunnerAgent(BaseAgent):
async def _run_async_impl(self, ctx: InvocationContext):
result = await self.llm.generate_content_async(build_prompt(ctx.session.state))
# ... handle result
# Shadow: same class hierarchy, no LLM
class AutopilotRunnerAgent(RunnerAgent):
async def _run_async_impl(self, ctx: InvocationContext):
state = ctx.session.state
decision = "slow" if state["energy"] < 0.3 else "steady"
ctx.session.state["decision"] = decision
yield Event(author=self.name, content=types.Content(parts=[types.Part(text=decision)]))
Switch via env var — no code-path changes at the call site:
def get_runner_agent() -> BaseAgent:
if os.getenv("RUNNER_MODE") == "autopilot":
return AutopilotRunnerAgent()
return RunnerAgent()
Use the autopilot variant in all InMemoryRunner unit tests. Zero API cost, deterministic assertions.
NDJSON Replay for Integration Tests and Demos
Record every event your ADK agent emits to a .ndjson file during a real run. Replay against the frontend or test harness without running agents:
# Record in agent's broadcast/after_agent layer
with open("recordings/sim-run.ndjson", "a") as f:
f.write(json.dumps({"ts": elapsed, "event": event.model_dump()}) + "\n")
Commit the recording — it becomes a regression baseline. CI replays it; no agents need to be running.
When to use either pattern: CI integration tests, load testing (run 1000s of autopilot agents cheaply), keynote demos, UI development iterations.
Full implementation detail for both patterns:
agentic-agent-variant-ladder.mdinagentic-ai-dev/reference/
Reference Files
adk-dev-lifecycle.md— Full detail on each phase, commands, common pitfallsgoogle-adk/SKILL.md— Agent patterns, tool API reference, ToolContext usageadk-eval-guideskill — Evaluation strategy and eval dataset structureadk-deploy-guideskill — Deployment to Cloud Run / Agent Engine, health checks, rollbackagentic-ai-dev/reference/agentic-agent-variant-ladder.md— Agent variant ladder, deterministic shadow agents, NDJSON replay
Quick Reference: When to Load This Skill
Load adk-dev-guide BEFORE google-adk when:
- Starting a new ADK agent feature
- Debugging an infinite loop or eval failure
- Onboarding a new developer to ADK patterns
- Reviewing an ADK agent's lifecycle
Load google-adk only when:
- Writing agent code (patterns, APIs, ToolContext)
- Implementing a specific tool
- Tweaking agent instructions