
Adk Engineer
- 38 installs
- 2.6k repo stars
- Updated August 5, 2026
- jeremylongshore/claude-code-plugins-plus-skills
Engineers production-ready Google ADK agents with clean structure, tool validation, regression tests, guardrails, and deployment automation.
About
Designs and implements maintainable ADK agent code with module boundaries, structured tool interfaces, retries, logging, and a deployment checklist. A developer uses it to build a shippable single- or multi-agent ADK system with tests and operational guardrails.
- Incremental tool-by-tool implementation with regression tests
- Retries, timeouts, logging, and deployment health checks
Adk Engineer by the numbers
- 38 all-time installs (skills.sh)
- Ranked #8,404 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill adk-engineerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 38 |
|---|---|
| repo stars | ★ 2.6k |
| Last updated | August 5, 2026 |
| Repository | jeremylongshore/claude-code-plugins-plus-skills ↗ |
What it does
Engineers production-ready Google ADK agents with clean structure, tool validation, regression tests, guardrails, and deployment automation.
Files
ADK Engineer
Engineer production-ready Agent Development Kit (ADK) agents and multi-agent systems: clean structure, testability, safe tool usage, and deployment automation.
Overview
Use this skill to design and implement ADK agent code that is maintainable and shippable: clear module boundaries, structured tool interfaces, regression tests, and a deployment checklist (local or Agent Engine).
Prerequisites
- A target runtime (Python/Java/Go) consistent with the project’s pinned versions
- ADK installed (and any required model/provider SDKs configured)
- A test runner available in the repo (unit tests at minimum)
- If deploying: access to a Google Cloud project and permissions for the chosen deployment target
Instructions
1. Clarify requirements: agent goals, tool surface, latency/cost constraints, and deployment target. 2. Propose architecture: single agent vs multi-agent, orchestration pattern, state strategy (Memory Bank / external store). 3. Scaffold structure: agent entrypoint(s), tool modules, config, and tests. 4. Implement incrementally:
- add one tool at a time with input validation and structured outputs
- add regression tests for each tool and critical prompt flows
5. Add operational guardrails: retries/backoff, timeouts, logging, and safe error messages. 6. Validate locally (tests + smoke prompts) and provide a deployment plan (when requested).
Output
- A concrete architecture plan and file layout
- Agent and tool implementations (or patches) with tests
- A validation checklist (commands to run, expected outputs, and failure triage)
- Optional: deployment instructions and post-deploy health checks
Error Handling
- Build/test failures: isolate the failing module, minimize the repro, fix, and add a regression test.
- Tool/runtime errors: enforce structured error responses and safe retries where appropriate.
- Deployment failures: provide the exact failing command, logs to inspect, and least-privilege IAM fixes.
Examples
Example: Productionizing an existing ADK agent
- Request: “Refactor this agent into a clean module structure and add tests before we deploy.”
- Result: reorganized
src/layout, tool boundaries, a test suite, and a deployment checklist.
Example: Multi-agent workflow
- Request: “Build a validator + deployer + monitor agent team with a sequential orchestrator.”
- Result: orchestrator skeleton, per-agent responsibilities, and smoke tests for each step.
Resources
- Full detailed playbook (kept for reference):
${CLAUDE_SKILL_DIR}/references/SKILL.full.md - Repo standards (source of truth):
000-docs/6767-a-SPEC-DR-STND-claude-code-plugins-standard.md000-docs/6767-b-SPEC-DR-STND-claude-skills-standard.md- ADK / Agent Engine docs: https://cloud.google.com/vertex-ai/docs/agent-engine
PRD: ADK Engineer
Version: 2.1.0 Author: Jeremy Longshore Status: Active Marketplace: tonsofskills.com by Intent Solutions Portfolio: jeremylongshore.com
---
Problem Statement
ADK agent code that works in a prototype rarely survives production. Missing test coverage, inconsistent module boundaries, hardcoded tool configurations, and ad-hoc deployment scripts create agents that break on first contact with real traffic. Refactoring after the fact costs 3-5x more than building correctly upfront.
The ADK Engineer skill produces production-grade agent code with clean architecture, test coverage, and deployment automation from the start.
Target Users
| User | Context | Primary Need |
|---|---|---|
| ADK developers | Building new agents | Clean structure with tests from day one |
| Teams with existing agents | Refactoring for production | Module boundaries, test coverage, deployment plan |
| Platform engineers | Standardizing agent development | Repeatable patterns across teams |
| Solo developers | Shipping agent products | End-to-end from code to deployed, tested agent |
Success Criteria
1. Code structure: Generated code follows single-responsibility modules with clear interfaces 2. Test coverage: Every tool function has unit tests; every agent has smoke prompt tests 3. Incremental builds: Each tool added independently with its own validation before wiring to agent 4. Deployment readiness: Generated code deploys without manual intervention to the target platform 5. Operational safety: Retries, timeouts, structured logging, and safe error messages built in
Functional Requirements
1. Clarify agent requirements (goals, tools, constraints, deployment target) 2. Propose architecture (single vs multi-agent, orchestration, state management) 3. Scaffold project structure with proper module boundaries 4. Implement tools incrementally — one at a time, each with tests 5. Add operational guardrails (retries, backoff, timeouts, logging) 6. Validate locally (unit tests + smoke prompts) 7. Generate deployment plan with health checks
Non-Functional Requirements
- Supports Python, Java, and Go runtimes
- Works with any ADK-compatible model provider
- Generates CI-compatible test commands
- Code follows language-specific conventions (PEP 8 for Python, etc.)
Dependencies
- Target language runtime installed
- ADK SDK installed and configured
- Test runner available (pytest, JUnit, go test)
- GCP project access for deployment (optional)
Out of Scope
- Model fine-tuning or training
- UI/frontend development
- Infrastructure provisioning (use jeremy-adk-terraform for that)
- Production monitoring setup (use jeremy-vertex-validator)
ARD: ADK Engineer
Part of Tons of Skills by Intent Solutions | jeremylongshore.com
System Context
The ADK Engineer skill operates within a developer's local repository and optionally interacts with Google Cloud for deployment. It sits between the developer's requirements and production-ready ADK agent code, bridging the gap between intent and shippable implementation. The skill understands ADK's agent hierarchy (Agent, SequentialAgent, ParallelAgent, LoopAgent), tool system (FunctionTool wrappers), and deployment surface (local execution vs Vertex AI Agent Engine).
Developer Request
↓
[ADK Engineer Skill]
├── Reads: existing project files, configs, tests, requirements.txt
├── Writes: agent code, tool modules, tests, deploy scripts, config
├── Calls: local test runners, ADK CLI, gcloud (optional)
└── References: ADK docs, Agent Engine API, Gemini model catalog
↓
Production ADK Agent (local or Agent Engine)
├── Agent entrypoint with system instruction
├── FunctionTool modules with structured returns
├── Test suite (unit + integration)
└── Deployment artifacts (requirements.txt, deploy script)Data Flow
1. Input: User request specifying agent goals, tool surface, orchestration pattern, latency/cost constraints, and deployment target (local vs Agent Engine) 2. Processing: Analyze existing project structure via Glob/Grep, scaffold or patch agent entrypoints and tool modules, generate regression tests, validate with local test runner, and produce deployment configuration when requested 3. Output: Agent source files (Python/Java/Go), tool implementations with input validation, test suites, a validation checklist, and optional deployment commands for Vertex AI Agent Engine
Key Design Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Incremental tool addition | One tool at a time with tests | Prevents regressions and keeps review surface small |
| Structured error responses | All tools return {status, error/data} dicts | Enables consistent error handling across tools and agents |
| Protocol-based DI for testing | Python Protocol classes for agent mocking | Allows unit testing without live LLM calls or network access |
| Sequential orchestration default | SequentialAgent for multi-agent flows | Simplest pattern with predictable debugging; upgrade to Parallel/Loop only when needed |
| Model selection guidance | Gemini 2.5 Flash default, Pro for reasoning | Flash for throughput and cost; Pro only when task complexity demands it |
| Config dataclass pattern | Python @dataclass for AgentConfig | Type-safe configuration; IDE autocomplete; easy to pass and test |
| System instruction as constant | Separate SYSTEM_INSTRUCTION string | Visible, reviewable, and testable prompt; not buried inside Agent constructor |
| Subprocess timeout enforcement | Explicit timeout= on all subprocess calls | Prevents hung tool executions from blocking the agent indefinitely |
Tool Usage Pattern
| Tool | Purpose |
|---|---|
| Read | Inspect existing agent code, configs, and test files to understand current state |
| Write | Create new agent entrypoints, tool modules, config files, and test suites |
| Edit | Patch existing files — add tools, fix bugs, refactor structure |
| Grep | Search for patterns across the codebase (imports, API usage, error handling) |
| Glob | Discover project layout — find all Python files, test files, config files |
| Bash(cmd:*) | Run test suites, linters, ADK CLI commands, and optional gcloud deployments |
Error Handling Strategy
| Error Class | Detection | Recovery |
|---|---|---|
| Build/import failures | Non-zero exit from python -m py_compile or import errors in test output | Isolate the failing module, fix imports or syntax, re-run build |
| Test failures | pytest exit code != 0 or specific test case FAILED lines | Read failure output, fix the root cause, add a regression test for the fix |
| Tool runtime errors | Structured {status: "error"} responses from tool functions | Log the error context, apply retry with backoff for transient failures, surface clear messages for permanent failures |
| Deployment failures | gcloud or Agent Engine SDK returning non-zero or error JSON | Parse the error message, check IAM permissions, verify API enablement, and suggest least-privilege fixes |
| Model quota/timeout | 429 or DEADLINE_EXCEEDED from Gemini API | Implement exponential backoff with jitter; suggest model downgrade (Pro to Flash) or quota increase |
Extension Points
- Custom tool functions: users add new
FunctionToolwrappers following the structured return pattern established intools.py - Orchestration patterns: swap SequentialAgent for ParallelAgent or LoopAgent by changing the pipeline definition in
orchestrator.py - Model override: change the model string in AgentConfig to target different Gemini variants (Flash, Pro, Ultra) or third-party providers
- Deployment targets: extend from local/Agent Engine to Cloud Run or GKE by adding deployment config generators in
deploy/ - Test fixtures: add pytest fixtures in
conftest.pyfor common agent/tool mocking patterns - Session management: integrate Memory Bank for stateful multi-turn conversations by adding session ID tracking
- Observability: add OpenTelemetry tracing spans around tool calls for production debugging
- Cost tracking: instrument token usage per agent call to enable cost attribution and budget alerts
ADK Engineer — Common Errors
Build and Import Errors
| Error | Cause | Fix |
|---|---|---|
ModuleNotFoundError: No module named 'google.adk' | ADK package not installed or wrong virtual environment active | pip install google-adk in the project venv; verify with python -c "import google.adk" |
ImportError: cannot import name 'Agent' from 'google.adk.agents' | Outdated ADK version missing the Agent class | pip install --upgrade google-adk>=0.3.0 |
SyntaxError in agent or tool files | Malformed Python (missing colon, indent error, unclosed bracket) | Run python -m py_compile src/agent.py to locate the exact line |
AttributeError: 'Agent' object has no attribute 'tools' | Using wrong Agent constructor signature for the ADK version | Check ADK changelog; tools= parameter requires FunctionTool wrappers in recent versions |
TypeError: __init__() got an unexpected keyword argument 'instruction' | ADK version uses instructions (plural) not instruction | Change to instructions= or pin to the version matching your code |
Test Failures
| Error | Cause | Fix |
|---|---|---|
pytest: command not found | pytest not installed in the active environment | pip install pytest pytest-cov |
FAILED: mock_agent.run.assert_called_once_with(...) | Agent call signature changed after refactor | Update the mock assertion to match new parameter order or added kwargs |
fixture 'tmp_path' not found | pytest version below 3.9 | pip install --upgrade pytest>=7.0 |
| Coverage below threshold | New tool code added without corresponding tests | Write tests for each new tool function; target 80%+ line coverage |
TimeoutError in tool tests | Subprocess call to linter/test runner exceeds timeout | Increase timeout= parameter or mock the subprocess call in unit tests |
Runtime and Tool Errors
| Error | Cause | Fix |
|---|---|---|
subprocess.TimeoutExpired in tool execution | External command (linter, test runner) hangs | Add explicit timeout= to all subprocess.run() calls; default to 30s |
FileNotFoundError from tool function | Target file path does not exist or contains typo | Validate file paths before passing to tools; return structured error with the attempted path |
json.JSONDecodeError parsing tool output | External tool produced non-JSON output (error message, empty string) | Check result.stdout is non-empty before json.loads(); fall back to raw text on parse failure |
| Tool returns unbounded data | No cap on findings/results causing token bloat | Slice results (e.g., findings[:10]) and add a truncated: true flag |
PermissionError accessing files | File permissions restrict read/write | Check file permissions with ls -la; use chmod or run with appropriate user |
Deployment Errors
| Error | Cause | Fix |
|---|---|---|
google.api_core.exceptions.PermissionDenied: 403 | Service account missing roles/aiplatform.user | gcloud projects add-iam-policy-binding PROJECT --member=serviceAccount:SA --role=roles/aiplatform.user |
Agent Engine creation timeout | Agent package too large or region capacity issue | Reduce package size (exclude test files); try us-central1 for best availability |
INVALID_ARGUMENT: model not supported | Specified Gemini model not available in target region | Use gemini-2.5-flash or gemini-2.5-pro; check regional availability in Vertex AI docs |
Requirements file parse error during Agent Engine deploy | Invalid requirements.txt format (missing versions, local paths) | Pin all dependencies with == versions; remove local path references |
VPC Service Controls violation | Deployment blocked by organization perimeter | Add the deploying service account to the VPC-SC access level; or deploy from within the perimeter |
Configuration Errors
| Error | Cause | Fix |
|---|---|---|
AgentConfig fields ignored | Config object created but not passed to create_agent() | Verify config is passed as argument: create_agent(config=my_config) |
| Wrong region for deployment | region in config doesn't match gcloud default | Explicitly set region in AgentConfig; verify with gcloud config get compute/region |
| Model string not recognized | Using full model path instead of short name | Use gemini-2.5-flash not projects/X/locations/Y/publishers/google/models/gemini-2.5-flash |
| Environment variable not set | Required API key or project ID missing | Export before running: export GOOGLE_CLOUD_PROJECT=my-project; use .env file with python-dotenv |
google.auth.exceptions.DefaultCredentialsError | Application Default Credentials not configured | Run gcloud auth application-default login or set GOOGLE_APPLICATION_CREDENTIALS |
| Conflicting dependency versions | Multiple ADK-related packages with incompatible version pins | Create a clean venv: python -m venv .venv && pip install -r requirements.txt |
Orchestration Errors
| Error | Cause | Fix |
|---|---|---|
SequentialAgent skips sub-agents | Sub-agent returns empty response interpreted as completion | Ensure each sub-agent produces non-empty output; add explicit handoff messages |
ParallelAgent race condition | Multiple agents writing to the same state key | Use unique state keys per parallel branch; merge results in a post-processing step |
LoopAgent infinite loop | Exit condition never satisfied by agent output | Set max_iterations on LoopAgent; add explicit exit instruction in agent prompt |
| Sub-agent not receiving context | Parent agent context not propagated to child | Pass session_id through the orchestrator; verify state sharing configuration |
| Agent ordering wrong in pipeline | Sub-agents list order doesn't match intended sequence | Review sub_agents=[...] list; agents execute in list order for SequentialAgent |
--- [Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)
Examples — ADK Software Engineer
Example 1: Production-Ready ADK Agent with Custom Tools
Build a code review agent with structured tool interfaces and tests.
Project Structure
code-review-agent/
├── src/
│ ├── agent.py # Agent definition
│ ├── tools.py # Custom tool functions
│ └── config.py # Configuration
├── tests/
│ ├── test_agent.py # Unit tests
│ └── test_tools.py # Tool tests
├── pyproject.toml
└── requirements.txtAgent Implementation
# src/config.py
from dataclasses import dataclass
@dataclass
class AgentConfig:
model: str = "gemini-2.5-flash"
project_id: str = ""
region: str = "us-central1"
max_retries: int = 3
timeout_seconds: int = 30# src/tools.py
from google.adk.tools import FunctionTool
from typing import Dict, List
import subprocess
import json
# ADK tools: define plain functions, wrap with FunctionTool.
# The function docstring becomes the tool description for the LLM.
def run_linter(file_path: str, language: str = "python") -> Dict:
"""Run a linter on a file and return findings as structured results."""
linter_map = {
"python": ["ruff", "check", "--output-format=json"],
"typescript": ["eslint", "--format=json"],
}
cmd = linter_map.get(language, linter_map["python"])
cmd.append(file_path)
try:
result = subprocess.run(
cmd, capture_output=True, text=True, timeout=30
)
findings = json.loads(result.stdout) if result.stdout else []
return {
"status": "success",
"file": file_path,
"finding_count": len(findings),
"findings": findings[:10], # Cap at 10 to avoid token bloat
}
except subprocess.TimeoutExpired:
return {"status": "error", "error": "Linter timed out after 30s"}
except FileNotFoundError:
return {"status": "error", "error": f"Linter '{cmd[0]}' not installed"}
def read_file_section(file_path: str, start_line: int, end_line: int) -> Dict:
"""Read lines from a file. Returns content with line numbers."""
try:
with open(file_path, "r") as f:
lines = f.readlines()
start = max(0, start_line - 1)
end = min(len(lines), end_line)
section = lines[start:end]
return {
"status": "success",
"file": file_path,
"start_line": start + 1,
"end_line": end,
"content": "".join(
f"{i+start+1:4d} | {line}" for i, line in enumerate(section)
),
}
except FileNotFoundError:
return {"status": "error", "error": f"File not found: {file_path}"}
def check_test_coverage(module_path: str) -> Dict:
"""Run pytest with coverage and return summary."""
try:
result = subprocess.run(
["python", "-m", "pytest", "--cov=" + module_path,
"--cov-report=json", "-q", "--tb=no"],
capture_output=True, text=True, timeout=60,
)
if result.returncode == 0:
with open("coverage.json") as f:
cov = json.load(f)
return {
"status": "success",
"total_coverage": cov["totals"]["percent_covered"],
"files": {
k: v["summary"]["percent_covered"]
for k, v in cov["files"].items()
},
}
return {"status": "error", "error": result.stderr[:500]}
except Exception as e:
return {"status": "error", "error": str(e)}# src/agent.py
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from src.tools import run_linter, read_file_section, check_test_coverage
from src.config import AgentConfig
SYSTEM_INSTRUCTION = """You are a senior code reviewer for Python projects.
WORKFLOW:
1. Run the linter on each changed file to find static analysis issues
2. Read file sections with the most complex logic for manual review
3. Check test coverage to identify untested code paths
4. Provide a structured review with severity levels
REVIEW FORMAT:
- CRITICAL: Security vulnerabilities, data loss risks, crashes
- WARNING: Performance issues, code smells, missing validation
- SUGGESTION: Style improvements, better patterns, documentation gaps
Always explain WHY something is an issue and provide a concrete fix.
"""
def create_review_agent(config: AgentConfig = None) -> Agent:
"""Create a configured code review agent."""
config = config or AgentConfig()
agent = Agent(
model=config.model,
name="code-review-agent",
description="Reviews code for quality, security, and test coverage",
instruction=SYSTEM_INSTRUCTION,
tools=[
FunctionTool(func=run_linter),
FunctionTool(func=read_file_section),
FunctionTool(func=check_test_coverage),
],
)
return agent
# Usage
if __name__ == "__main__":
agent = create_review_agent()
response = agent.run(
"Review the file src/tools.py for code quality and security issues"
)
print(response.text)Tests
# tests/test_tools.py
import pytest
import tempfile
import os
from src.tools import run_linter, read_file_section, check_test_coverage
class TestReadFileSection:
def test_reads_valid_range(self, tmp_path):
f = tmp_path / "sample.py"
f.write_text("line1\nline2\nline3\nline4\nline5\n")
result = read_file_section(str(f), start_line=2, end_line=4)
assert result["status"] == "success"
assert result["start_line"] == 2
assert result["end_line"] == 4
assert "line2" in result["content"]
assert "line4" in result["content"]
assert "line5" not in result["content"]
def test_handles_missing_file(self):
result = read_file_section("/nonexistent/file.py", 1, 10)
assert result["status"] == "error"
assert "not found" in result["error"].lower()
def test_clamps_out_of_range(self, tmp_path):
f = tmp_path / "short.py"
f.write_text("only\ntwo\n")
result = read_file_section(str(f), start_line=1, end_line=100)
assert result["status"] == "success"
assert result["end_line"] == 2
class TestRunLinter:
def test_returns_error_for_missing_linter(self, tmp_path):
f = tmp_path / "test.py"
f.write_text("x = 1\n")
# ruff may not be installed in test env
result = run_linter(str(f), language="python")
assert result["status"] in ("success", "error")
def test_caps_findings_at_ten(self):
# Verify the cap logic
findings = list(range(20))
capped = findings[:10]
assert len(capped) == 10# tests/test_agent.py
import pytest
from unittest.mock import patch, MagicMock
from src.agent import create_review_agent
from src.config import AgentConfig
def test_agent_creation():
"""Agent initializes with correct tools and config."""
agent = create_review_agent(AgentConfig(model="gemini-2.5-flash"))
assert agent.name == "code-review-agent"
assert len(agent.tools) == 3
def test_agent_custom_config():
"""Agent respects custom configuration."""
config = AgentConfig(
model="gemini-2.5-pro",
project_id="my-project",
region="europe-west4",
)
agent = create_review_agent(config)
assert agent.model == "gemini-2.5-pro"Expected Output
Running the agent:
$ python -m src.agent
## Code Review: src/tools.py
### CRITICAL
- **subprocess injection risk** (line 18): `cmd.append(file_path)` passes user input directly to subprocess. Sanitize `file_path` to reject shell metacharacters or use `shlex.quote()`.
### WARNING
- **Unbounded file read** (read_file_section): No file size check before reading. Add a max file size guard (e.g., 1 MB) to prevent memory issues.
- **Coverage JSON left on disk** (check_test_coverage): `coverage.json` is written but never cleaned up. Use `tempfile.NamedTemporaryFile` instead.
### SUGGESTION
- Add type hints to return values for better IDE support.
- Consider async versions of subprocess calls for concurrent file reviews.
Test coverage: 78% (src/tools.py: 72%, src/agent.py: 85%)---
Example 2: Multi-Agent Sequential Workflow
Build a validator-deployer-monitor agent team.
# orchestrator.py
from google.adk.agents import Agent, SequentialAgent
# Agent 1: Configuration Validator
validator = Agent(
model="gemini-2.5-flash",
name="config-validator",
instruction="""Validate deployment configurations.
Check: required fields present, valid regions, resource limits within quotas,
IAM roles follow least-privilege, no hardcoded secrets.""",
tools=[], # Pure reasoning, no tools needed
)
# Agent 2: Deployer
deployer = Agent(
model="gemini-2.5-flash",
name="deployer",
instruction="""Execute deployments based on validated configurations.
Run gcloud commands, verify resources are created, report deployment status.""",
)
# Agent 3: Health Monitor
monitor = Agent(
model="gemini-2.5-flash",
name="health-monitor",
instruction="""After deployment, verify health.
Check: endpoint responds 200, latency < 500ms, no error logs in last 5 min.""",
)
# Wire into sequential orchestrator
pipeline = SequentialAgent(
name="deploy-pipeline",
sub_agents=[validator, deployer, monitor],
description="Validate config -> Deploy -> Monitor health",
)
# Run the pipeline
result = pipeline.run("""
Deploy a Cloud Run service with:
- Image: gcr.io/my-project/api-server:v2.1.0
- Region: us-central1
- Memory: 512Mi
- Min instances: 1
- Max instances: 10
- Service account: api-server-sa@my-project.iam.gserviceaccount.com
""")
print(result.text)Expected Output
## Pipeline Result
### Step 1: Validation (PASS)
- All required fields present
- Region us-central1 is valid
- Memory 512Mi within quota
- Service account follows naming convention
- No hardcoded secrets detected
### Step 2: Deployment (SUCCESS)
- Deployed api-server to us-central1
- URL: https://api-server-abc123-uc.a.run.app
- Revision: api-server-00002-abc
### Step 3: Health Check (HEALTHY)
- GET /health returned 200 in 142ms
- No errors in Cloud Logging (last 5 min)
- CPU utilization: 12%, Memory: 180Mi/512Mi---
Example 3: Adding Tests to an Existing Agent
Refactor and add regression tests to untested agent code.
# Before: untested agent code
# src/chat_agent.py (original)
from google.adk.agents import Agent
agent = Agent(model="gemini-2.5-flash", name="chat")
def chat(msg):
return agent.run(msg)# After: refactored with testability
# src/chat_agent.py (refactored)
from google.adk.agents import Agent
from typing import Optional, Protocol
class LLMProvider(Protocol):
"""Protocol for dependency injection in tests."""
def run(self, message: str, session_id: Optional[str] = None) -> object: ...
def create_chat_agent(model: str = "gemini-2.5-flash") -> Agent:
return Agent(
model=model,
name="chat-agent",
instruction="You are a helpful assistant. Be concise.",
)
def chat(message: str, agent: Optional[LLMProvider] = None,
session_id: Optional[str] = None) -> str:
"""Send a message and return the response text."""
if agent is None:
agent = create_chat_agent()
if not message or not message.strip():
raise ValueError("Message cannot be empty")
response = agent.run(message, session_id=session_id)
return response.text# tests/test_chat_agent.py
import pytest
from unittest.mock import MagicMock
from src.chat_agent import chat, create_chat_agent
class TestChat:
def test_returns_response_text(self):
mock_agent = MagicMock()
mock_agent.run.return_value.text = "Hello! How can I help?"
result = chat("Hi there", agent=mock_agent)
assert result == "Hello! How can I help?"
mock_agent.run.assert_called_once_with("Hi there", session_id=None)
def test_passes_session_id(self):
mock_agent = MagicMock()
mock_agent.run.return_value.text = "Welcome back"
chat("Hi", agent=mock_agent, session_id="sess-123")
mock_agent.run.assert_called_once_with("Hi", session_id="sess-123")
def test_rejects_empty_message(self):
with pytest.raises(ValueError, match="cannot be empty"):
chat("", agent=MagicMock())
def test_rejects_whitespace_message(self):
with pytest.raises(ValueError, match="cannot be empty"):
chat(" ", agent=MagicMock())
def test_create_agent_defaults(self):
agent = create_chat_agent()
assert agent.name == "chat-agent"
assert agent.model == "gemini-2.5-flash"Running Tests
$ pytest tests/ -v --tb=short
tests/test_chat_agent.py::TestChat::test_returns_response_text PASSED
tests/test_chat_agent.py::TestChat::test_passes_session_id PASSED
tests/test_chat_agent.py::TestChat::test_rejects_empty_message PASSED
tests/test_chat_agent.py::TestChat::test_rejects_whitespace_message PASSED
tests/test_chat_agent.py::TestChat::test_create_agent_defaults PASSED
5 passed in 0.12s--- [Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)
ADK Engineer — Implementation Guide
How the Skill Works
1. Requirement analysis: Parse the user request to identify agent goals, required tools, orchestration pattern (single/sequential/parallel/loop), and deployment target (local or Agent Engine) 2. Project discovery: Glob and Read existing files to understand the current codebase — runtime language, existing agents/tools, test framework, and dependency versions 3. Architecture proposal: Generate a module layout with clear boundaries: agent entrypoints, tool modules, shared config, and test directories 4. Incremental implementation: Write or patch one tool at a time, each with input validation, structured return format ({status, data/error}), and a corresponding test 5. Validation and delivery: Run the test suite via Bash, verify all pass, and produce a summary with deployment instructions when applicable
Project/File Structure
adk-project/
├── src/
│ ├── __init__.py
│ ├── agent.py # Agent definition(s) + system instruction
│ ├── tools.py # FunctionTool wrappers
│ ├── config.py # AgentConfig dataclass
│ └── orchestrator.py # Multi-agent pipeline (if applicable)
├── tests/
│ ├── __init__.py
│ ├── test_agent.py # Agent creation and config tests
│ ├── test_tools.py # Tool function unit tests
│ ├── conftest.py # Shared fixtures and mocks
│ └── test_orchestrator.py # Pipeline integration tests
├── pyproject.toml
├── requirements.txt
└── deploy/
├── deploy.sh # Agent Engine deployment script
└── validate.sh # Post-deploy health checkCore Patterns
Structured Tool Returns
Every tool function returns a consistent dict for predictable agent behavior:
def my_tool(input_param: str) -> dict:
"""Tool description for the LLM."""
try:
result = do_work(input_param)
return {"status": "success", "data": result}
except SpecificError as e:
return {"status": "error", "error": str(e)}Dependency Injection for Testing
Use Python Protocol classes to decouple agent logic from live LLM calls:
from typing import Protocol, Optional
class LLMProvider(Protocol):
def run(self, message: str, session_id: Optional[str] = None) -> object: ...
def process(message: str, agent: Optional[LLMProvider] = None) -> str:
if agent is None:
agent = create_default_agent()
response = agent.run(message)
return response.textMulti-Agent Orchestration
from google.adk.agents import Agent, SequentialAgent
pipeline = SequentialAgent(
name="deploy-pipeline",
sub_agents=[validator_agent, deployer_agent, monitor_agent],
)
result = pipeline.run("Deploy the service with config X")Configuration Reference
| Setting | Required | Default | Purpose |
|---|---|---|---|
GOOGLE_CLOUD_PROJECT | Yes | — | Target GCP project for deployment |
GOOGLE_CLOUD_REGION | No | us-central1 | Region for Agent Engine deployment |
model (in AgentConfig) | No | gemini-2.5-flash | LLM model for agent reasoning |
max_retries (in AgentConfig) | No | 3 | Retry count for transient tool failures |
timeout_seconds (in AgentConfig) | No | 30 | Per-tool execution timeout |
GOOGLE_APPLICATION_CREDENTIALS | No | ADC | Service account key path (prefer WIF/ADC instead) |
Testing Strategy
Run the full test suite before any commit or deployment:
# Unit tests with coverage
pytest tests/ -v --cov=src --cov-report=term-missing --tb=short
# Smoke test: verify agent creates without error
python -c "from src.agent import create_review_agent; a = create_review_agent(); print(f'{a.name}: {len(a.tools)} tools')"
# Integration test (requires ADK and model access)
python -m src.agentPass criteria:
- All unit tests pass (exit code 0)
- Coverage >= 80% on
src/modules - No import errors in smoke test
- Integration test produces structured output (for manual review)
Deployment Pipeline
# 1. Local validation
pytest tests/ -v && echo "Tests pass"
# 2. Package for Agent Engine
pip freeze > requirements.txt
# Ensure only production deps (exclude pytest, dev tools)
# 3. Deploy to Agent Engine
python deploy/deploy.sh # Wraps vertexai.Client().agent_engines.create()
# 4. Post-deploy validation
curl -s https://AGENT_ENDPOINT/.well-known/agent-card | jq .name
# Expect: agent name in response
# 5. Health check
python deploy/validate.sh # Sends test prompt, checks response format--- [Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)
#!/bin/bash
# setup-project.sh - Setup ADK agent development project
set -euo pipefail
PROJECT_NAME="${1:-adk-agent-project}"
LANGUAGE="${2:-python}"
echo "Setting up ADK Agent Development Project"
echo "Name: $PROJECT_NAME"
echo "Language: $LANGUAGE"
echo ""
mkdir -p "$PROJECT_NAME"
cd "$PROJECT_NAME"
if [[ "$LANGUAGE" == "python" ]]; then
# Python project structure
mkdir -p src/{agents,tools,orchestrators,config,utils}
mkdir -p tests/{unit,integration,e2e}
mkdir -p deployment/{terraform,kubernetes}
mkdir -p .github/workflows
# Create __init__.py files
touch src/__init__.py
touch src/agents/__init__.py
touch src/tools/__init__.py
touch src/orchestrators/__init__.py
touch src/config/__init__.py
touch src/utils/__init__.py
# Create requirements.txt
cat > requirements.txt <<'EOF'
google-adk>=0.1.0
google-cloud-aiplatform>=1.40.0
pytest>=7.0.0
pytest-cov>=4.0.0
black>=23.0.0
mypy>=1.0.0
EOF
# Create pyproject.toml
cat > pyproject.toml <<EOF
[project]
name = "$PROJECT_NAME"
version = "1.0.0"
description = "ADK Agent Application"
requires-python = ">=3.11"
dependencies = [
"google-adk>=0.1.0",
"google-cloud-aiplatform>=1.40.0"
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-cov>=4.0.0",
"black>=23.0.0",
"mypy>=1.0.0"
]
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
EOF
# Create README.md
cat > README.md <<EOF
# $PROJECT_NAME
ADK Agent Application
## Setup
\`\`\`bash
pip install -r requirements.txt
\`\`\`
## Run
\`\`\`bash
python -m src.agents.main_agent
\`\`\`
## Test
\`\`\`bash
pytest tests/
\`\`\`
EOF
echo "✓ Python project created"
elif [[ "$LANGUAGE" == "go" ]]; then
# Go project structure
mkdir -p cmd/{agent,cli}
mkdir -p pkg/{agents,tools,config}
mkdir -p internal/{handlers,middleware}
mkdir -p deployments
# Create go.mod
cat > go.mod <<EOF
module github.com/yourusername/$PROJECT_NAME
go 1.21
require (
google.golang.org/adk v0.1.0
)
EOF
echo "✓ Go project created"
fi
echo ""
echo "Project created: $PROJECT_NAME/"
echo "Next steps:"
echo " cd $PROJECT_NAME"
echo " pip install -r requirements.txt # Python"
echo " go mod tidy # Go"