Your First Agent with Google's Agent Development Kit
Build your first agent with Google's open-source Agent Development Kit — instructions, a tool, and a local run loop you can actually execute.
Download starter repoOn this page▾
- About this series
- What you'll build
- Why this matters (the 5-minute business case)
- Concepts you'll use (2-minute primer)
- Prerequisites
- Setup (5 minutes)
- Exercise 1 — Anatomy of an agent (12 min)
- Exercise 2 — Run it in the ADK web UI (12 min)
- Exercise 3 — Run it programmatically with a Runner (12 min)
- Exercise 4 — Shape behavior through the instruction (10 min)
- Wrap-up
- Looking ahead to Lab 2
- Stretch challenges
- Troubleshooting
- References
About this series
This is the first of a five-part series on Google's Agent Development Kit (ADK) — an open-source, code-first Python framework for building, evaluating, and deploying AI agents. Each lab keeps the previous code and adds one major capability:
- Your first agent (this lab) — a single agent with one tool.
- Tools, state & memory — multiple tools, plus session state and memory so the agent remembers.
- Multi-agent systems — a coordinator delegating to specialists; workflow agents.
- Evaluation, callbacks & safety — evalsets, guardrails, and tracing.
- Deploy & connect — ship it, and connect over A2A and MCP.
By the end you'll have built up a deployed, evaluated, multi-agent system. We use the same Acme Orders assistant throughout so each lab is a small step, not a fresh start.
What you'll build
A complete, working ADK agent: the Acme Orders assistant, which answers customer questions about orders (status, total, delivery date) by calling a tool you write. You'll run it three ways — offline tool tests, the ADK web UI, and programmatically with a Runner — and you'll learn the four pieces every ADK agent is made of: a model, an instruction, tools, and the runtime that drives them.
It's deliberately small. The point of Lab 1 is to make the core agent loop concrete and the project shape familiar, so Labs 2–5 have a foundation to extend.
Why this matters (the 5-minute business case)
Read this section before handing the lab to your team.
A framework like ADK is what turns "calling an LLM" into "operating agents." Anyone can make an API call to a model in an afternoon. The hard part — and where most internal AI projects stall — is everything around the model: managing conversation state, letting the model safely call your systems, orchestrating multi-step work, evaluating quality, and deploying the result. ADK exists to provide those pieces as a coherent, code-first toolkit rather than something every team reinvents. The earlier labs in your broader series covered the open protocols (calling models, discovery, tools, evals); ADK is the framework that assembles them into something you can actually run and ship.
Three reasons this is a leadership-level concern, not just a developer preference:
- It standardizes how your teams build agents. Without a framework, every team invents its own structure for tools, state, and orchestration — which means no shared patterns, no reuse, and painful handoffs. A common kit (the agent is always model + instruction + tools, run by a Runner) makes agents across the org legible to each other and to new hires. That's the same productivity argument that standard web frameworks settled years ago.
- It's code-first, which means agents become real software. ADK agents are plain Python in version control — testable, reviewable, diffable, and deployable through your existing pipelines. That's the opposite of agents trapped in a no-code console or a notebook. The tool logic in this lab is ordinary Python you can unit-test (and we do), which is exactly what makes an agent maintainable.
- It's model- and deployment-flexible. ADK is built to run different models and deploy to different targets (local, Cloud Run, managed runtimes). Building on a framework rather than hardwiring one model/endpoint protects you from lock-in and lets you move as the landscape changes.
The honest caveats, because pragmatic adoption means naming them:
- A framework is a dependency and a learning curve. ADK is young (2.0 shipped with breaking changes from 1.x) and moving fast. You're adopting an evolving API; pin versions and budget for upgrades. For a one-off script, raw model calls may be simpler — reach for ADK when you're building something you'll operate and grow.
- The framework doesn't write the hard parts for you. ADK makes the plumbing easy, but a good agent still needs a clear instruction, well-designed tools, evaluation, and guardrails (Labs 4 covers the last two). The kit lowers the floor; it doesn't raise the ceiling on its own.
- An agent that can call tools can take actions. The moment your agent has tools, "what is it allowed to do, and on whose behalf?" becomes a real question. We keep this lab's tool read-only; later labs add the safety layer. Don't ship action-taking agents without it.
A useful framing for a non-technical stakeholder: "ADK is a standard toolkit for building AI agents as real, version-controlled software — so our teams build them the same way, can test and review them, and can deploy them through our normal process, instead of every project reinventing the basics."
Concepts you'll use (2-minute primer)
Every ADK agent comes down to four things:
- Agent (
LlmAgent, exported asAgent) — the unit you build. It has aname, amodel, aninstruction(its job description), and a list oftools. - Tool — a capability the agent can choose to use. The simplest kind is a function tool: a plain Python function. ADK reads its type hints and docstring to build the schema the model sees, so those aren't documentation — they're the interface.
- Runner — the runtime that drives the loop: it takes a user message, lets the agent reason and call tools, and yields a stream of events (tool calls, tool results, the final answer).
- Session — where conversation state lives. For local dev, an in-memory session service is enough (Lab 2 goes deeper on state and memory).
The loop in one sentence: the Runner sends your message to the agent; the agent may call tools; the Runner streams back events ending in the final response.
Prerequisites
| You need | Notes |
|---|---|
| Python 3.11+ | ADK 2.0 requires it |
pip + network | To install google-adk |
| A Gemini API key | Free at aistudio.google.com/apikey |
| (Optional) Node not needed | ADK ships its own web UI |
Get the starter repo. Files live inadk-lab-01/: theacme_orders/agent package,main.py,test_tools.py. Unzip andcdin.
Setup (5 minutes)
cd adk-lab-01
python3 -m venv .venv && source .venv/bin/activate # Python 3.11+
pip install -r requirements.txt # installs google-adk
cp acme_orders/.env.example acme_orders/.env
# edit acme_orders/.env and set GOOGLE_API_KEY=... (GOOGLE_GENAI_USE_VERTEXAI=FALSE)Confirm the tool logic works without touching ADK or your key:
python test_tools.py # 4/4 tests passedWhy a test before the agent? The tool is plain Python, so its correctness is plain testing — no model required. Keeping tool logic separate from the agent (here, in orders_logic.py) is the habit that keeps agents maintainable: you can prove the tool is right before asking whether the agent uses it well.Exercise 1 — Anatomy of an agent (12 min)
Goal: Read a complete ADK agent and understand its four parts.
Open acme_orders/agent.py. The whole agent is one object:
from google.adk import Agent
from .orders_logic import lookup_order
def get_order_status(order_id: str) -> dict:
"""Look up the status, total, and estimated delivery date of an Acme order.
Args:
order_id: The order identifier, e.g. 'A-1024'.
Returns:
A dict with `status` ('success' or 'error') and order fields.
"""
return lookup_order(order_id)
root_agent = Agent(
name="acme_orders_agent",
model="gemini-2.5-flash",
description="Answers customer questions about Acme orders.",
instruction="You are the Acme Orders assistant. ... Use get_order_status ...",
tools=[get_order_status],
)The four parts:
- `model` — which Gemini model reasons for the agent.
- `instruction` — the agent's standing orders. This is your highest-leverage knob: it tells the agent its role, when to use the tool, and how to behave (here: ask for an ID if missing, never invent order details).
- `tools` — the functions the agent may call. Passing
get_order_statusis all it takes to make it available. - `name` / `description` — identity.
descriptionmatters more in Lab 3, where a coordinator reads it to decide which agent to delegate to.
The docstring is the API. ADK turns get_order_status's signature and docstring into the tool schema the model sees. A vague docstring means the model calls the tool wrong or not at all. Write it like UX copy for the model: what it does, what each argument is, what it returns.Checkpoint: You can read an ADK agent and name its four parts. Now run it.
Exercise 2 — Run it in the ADK web UI (12 min)
Goal: Talk to your agent and watch the event stream — the best way to understand and debug ADK.
From the repo root (the folder containing acme_orders/):
adk web .Open the printed URL, choose acme_orders, and chat. Try:
- "What's the status of order A-1024?" — watch it call
get_order_statusand answer. - "Where's my order?" — it should ask you for an order ID (your instruction told it to).
- "What about A-9999?" — the tool returns an error; the agent should say it couldn't find it, not invent a status.
The web UI shows the event stream for each turn: the model's tool call (with the arguments it chose), the tool's return value, and the final response. This visibility is the point — when an agent misbehaves, you read the events to see whether the model called the wrong tool, passed bad arguments, or misread the result.
Business callout: that event trace is your audit and debugging surface. "Why did the agent say that?" becomes answerable by replaying exactly which tools it called and what they returned — the same governability theme from the MCP and evals labs, now built into the framework's dev loop.
Checkpoint: You've run a real agent and seen how it decides to call a tool. Now drive it from code.
Exercise 3 — Run it programmatically with a Runner (12 min)
Goal: Use ADK from code — the path you'd take to embed an agent in an app, a job, or a test.
Open main.py. The essence:
from google.adk import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
from acme_orders.agent import root_agent
runner = Runner(
agent=root_agent,
app_name="acme_orders",
session_service=InMemorySessionService(),
auto_create_session=True,
)
message = types.Content(role="user", parts=[types.Part(text="Status of A-1024?")])
for event in runner.run(user_id="u", session_id="s", new_message=message):
if event.is_final_response() and event.content:
print(event.content.parts[0].text)Run it:
python main.pyThe key ideas:
- `Runner` is the engine. You give it the agent, a session service (here in-memory), and it drives the loop.
- `runner.run(...)` yields events, not a single string. You filter for
event.is_final_response()to get the answer; the intermediate events (tool calls, results) are there when you want to inspect or stream them. - Messages are `types.Content` with
parts— the same content shape across the Gemini ecosystem.
main.py asks three questions, including A-9999, so you can confirm the agent reports "not found" rather than hallucinating.
Business callout: this is the difference between a chatbot demo and a deployable component. Because the agent runs from code, it slots into your services, your test suite (Lab 4 evaluates it), and your CI/CD — it's software, not a console toy.
Checkpoint: You can run an ADK agent both interactively and from code.
Exercise 4 — Shape behavior through the instruction (10 min)
Goal: Feel how much the instruction controls, by changing it and observing the effect.
The instruction is where most of an agent's behavior is decided. Try these edits in acme_orders/agent.py, re-running adk web . after each:
- Tone. Add "Always respond in a cheerful, upbeat tone with one emoji." Ask a question and watch the persona change — no code change, just instruction.
- Scope control. Add "If the user asks about anything other than Acme orders, politely say that's outside what you can help with." Then ask about the weather and confirm it declines. This is a cheap, important guardrail (Lab 4 adds stronger ones).
- Tool discipline. Temporarily weaken the instruction to just "You are a helpful assistant." and ask about an order. Notice it may answer worse or not call the tool reliably — proof that the instruction, not just the tool list, drives good tool use.
Put the instruction back when you're done.
Business callout: the instruction is product design in prose. It encodes the agent's role, boundaries, and voice — and it's editable by people who aren't deep in the code. That accessibility is a feature, but it's also why instructions deserve review and versioning like any other product spec; a sloppy instruction is a sloppy product.
Checkpoint: You can shape agent behavior through the instruction and you understand it's the highest-leverage control you have.
Wrap-up
You built and ran your first ADK agent. You can now:
- Assemble an agent from its four parts: model, instruction, tools, runtime
- Write a function tool whose docstring/type hints are the model's interface
- Run an agent in the ADK web UI and read its event stream
- Drive an agent from code with a Runner, and shape behavior via the instruction
The one idea to carry forward: an ADK agent is model + instruction + tools, driven by a Runner that streams events. Everything in the rest of this series — memory, multi-agent orchestration, evaluation, deployment — hangs off that small, legible core. Keeping your tool logic as plain, testable Python (as we did here) is the habit that will keep those bigger agents maintainable.
Looking ahead to Lab 2
Right now the agent has no memory — each turn starts fresh beyond the current session's history, and it can't accumulate knowledge. Lab 2 adds more tools and introduces session state and memory, so the assistant can remember a customer's context across turns and interactions. We'll build directly on this repo.
Stretch challenges
- Add a second tool. Write
list_orders_by_status(status: str) -> dictover the same data and add it totools. Watch the model choose between tools based on the question. - Harden the tool. Have
lookup_ordervalidate the ID format (A-####) and return a helpful error for malformed IDs; add a test for it intest_tools.py. - Inspect the events. In
main.py, print every event (not just the final one) to see the tool call and tool result the model produced.
Troubleshooting
| Symptom | Likely cause / fix |
|---|---|
ModuleNotFoundError: google.adk | pip install -r requirements.txt (needs Python 3.11+). The test_tools.py logic runs without it. |
adk: command not found | The CLI ships with google-adk; ensure your venv is activated. |
| Auth / 401 / "API key not valid" | Set GOOGLE_API_KEY in acme_orders/.env and GOOGLE_GENAI_USE_VERTEXAI=FALSE. |
adk web shows no agent | Run it from the folder that contains acme_orders/, and keep acme_orders/__init__.py. |
| Agent invents an order | Strengthen the instruction ("never invent order details") and ensure the tool returns an explicit error status. |
| Session not found (programmatic) | Keep auto_create_session=True, or create the session before runner.run. |
References
Technical details follow the ADK 2.0 Python API:
A note on accuracy for your readers: ADK is evolving quickly (2.0 introduced breaking changes from 1.x). This lab's code targets the ADK 2.0 API (from google.adk import Agent, Runner;model="gemini-2.5-flash"). The tool logic is unit-tested here; the ADK wiring is syntax-verified and follows the 2.0 source — pin yourgoogle-adkversion and re-run before publishing, noting the version you validated.
Written by Wes Goldwater, Director of Engineering at Prosigliere.
Hands-on cloud & pragmatic AI from Goldwater.dev.