Skip to content
datarekha

Google ADK — intro

Google ADK is a Gemini-first framework for building, inspecting, and deploying agents. Learn the LlmAgent, tools, sessions, runners, event streams, adk web, and the production boundaries that matter.

Written and reviewed by Last reviewed

12 min read Intermediate Agentic AI Lesson 42 of 78

What you'll learn

  • How ADK's LlmAgent, Runner, SessionService, and event stream fit together
  • How one request moves through an ADK agent loop, including sessions and tool calls
  • How to run a small Gemini agent locally with Python and inspect it with adk web
  • When ADK is a better fit than LangGraph, MAF, or a plain model SDK
  • What must change before a local ADK agent is safe to deploy

Before you start

At 3 a.m., a customer asks your data assistant why yesterday’s revenue report is late. The assistant needs to understand the question, perhaps call a warehouse tool, keep the conversation attached to the right customer, and stream a useful answer back to the support console.

A model API can generate the answer. It does not, by itself, manage the session, run the tool, record intermediate steps, or give your team a useful local debugger. You can build those pieces yourself. Most teams eventually do, and then discover they have accidentally built a small framework.

Google Agent Development Kit, usually called ADK, is Google’s framework for that surrounding machinery. The google-adk Python package is Gemini-first, has Google Cloud deployment paths, and includes a local web interface for watching an agent work.

It is not a new kind of model. It is the application layer around a model.

The building blocks

ADK’s central object is an LlmAgent: configuration that combines a language model, instructions, tools, and often sub-agents. It defines what the agent may do, but does not run a conversation or own user state.

A tool is a callable operation the model may request, such as get_revenue_report(region="west"). The Python function does the work; the model chooses whether to request it but does not execute Python automatically.

A session holds one conversation’s user and application identity, events, and state. A session service stores and retrieves sessions. InMemorySessionService is useful during development, but its contents vanish when the process stops.

A Runner orchestrates an invocation. It creates the context, loads the session, invokes the agent, persists events, and yields them to your application. The agent execution flow makes model calls and handles registered tools. Together these pieces form the ADK runtime.

The crucial difference from a one-string model call is that runner.run_async produces an event stream: model responses, tool requests, tool results, and eventually a final response.

UserRunnerAgentModelSessionEvents
The Runner creates the invocation context and invokes the agent; sessions and events preserve what happened.

What happens on one request

For a request such as “What is a window function in SQL?” the path is:

  1. Your application identifies the user and session.
  2. The Runner loads the session and creates invocation context.
  3. ADK combines the agent instruction, conversation context, and new message.
  4. The agent sends that context to the model. If the model requests a tool, ADK emits a tool-call event and invokes the matching function.
  5. The tool result becomes an event and returns to the model.
  6. The model either requests another tool or produces an answer; the Runner persists and yields events as they occur.

The mental model is a state machine, not a chat completion:

user message
    -> model response
    -> tool call, if needed
    -> tool result
    -> model response
    -> final response

With no tools, the example usually needs one model turn. A warehouse lookup needs at least a model turn to choose the lookup, tool execution, and another model turn to explain the result. The final answer may arrive 1.8 seconds later.

Events let a UI show “checking the warehouse” before the answer arrives and let you diagnose whether the model selected the wrong tool, the tool returned stale data, or the final model turn misunderstood a correct result. Do not treat every event as customer-facing text: intermediate events may contain function calls, partial responses, secrets, or implementation details.

A small ADK agent

This complete local shape has no tool yet; it only explains a concept.

import asyncio

from google.adk.agents import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types


root_agent = LlmAgent(
    name="data-explainer",
    model="gemini-2.5-flash",
    instruction=(
        "You explain data engineering concepts in two sentences. "
        "Use one concrete example. Never use marketing language."
    ),
)

session_service = InMemorySessionService()

runner = Runner(
    agent=root_agent,
    app_name="lesson-app",
    session_service=session_service,
)


async def main():
    session = await session_service.create_session(
        app_name="lesson-app",
        user_id="u_1",
    )

    message = types.Content(
        role="user",
        parts=[types.Part(text="What is a window function in SQL?")],
    )

    async for event in runner.run_async(
        user_id="u_1",
        session_id=session.id,
        new_message=message,
    ):
        if event.is_final_response() and event.content:
            for part in event.content.parts:
                if part.text:
                    print(part.text)
                    return


if __name__ == "__main__":
    asyncio.run(main())

The wording is model-generated, so the program prints the first text part of the final response rather than a fixed sentence. The is_final_response and content checks prevent intermediate or non-text events from being displayed.

types.Content and types.Part make the message shape explicit: a user role containing one text part. Pin google-adk and check its current documentation when upgrading; fast-moving SDK signatures are easy to cargo-cult.

The agent definition is reusable; the session is not. If Alice uses user_id="u_1" and session s_alice, her follow-up can load earlier events. Ben’s user_id="u_2" and s_ben cannot. Creating a new session for every HTTP request discards history. Treat application, user, and session identifiers as owned data: a guessable session ID must not grant access to another customer.

The local web UI

From a directory containing your agent package and its agent.py module, run:

my_agents/
  __init__.py
  research_agent/
    __init__.py
    agent.py

The module normally exports root_agent. From the parent directory:

adk web my_agents

ADK starts a local server, commonly at localhost:8000, with a playground for chatting and inspecting runs. Depending on the event and model, you can see tool calls, tool results, response parts, and usage details. That makes it quick to distinguish a bad tool result from a model or instruction problem, and to test whether a second turn retains session context.

Project and production boundaries

ADK’s CLI uses a discoverable project shape:

adk create my_agent
adk run my_agent
adk eval my_agent ./tests/eval_set.json
pytest ./tests
adk deploy --help

adk eval consumes evaluation-set files; Python tests still run with pytest. Keep one agent package, an agent.py exporting root_agent, and credentials and project settings outside the agent definition. Never commit a hard-coded API key.

A typical production request is:

HTTP or RPC handler
  -> authenticate user
  -> select or create session
  -> call Runner
  -> translate selected events into the response
  -> persist trace and application outcome

The handler owns authentication and transport. The session service owns conversation state. The runner owns invocation and event flow. The agent owns behaviour and tool declarations. This separation lets you replace InMemorySessionService without rewriting the agent.

Durable sessions survive process restarts, but they do not make tools safe to retry. If refund_customer() succeeds and the network drops before the result reaches your process, a retry can issue two refunds unless the tool uses an idempotency key. Likewise, multiple workers need a shared session store and a policy for concurrent turns; serialize turns per session or design explicitly for concurrency.

For tracing, record request and session identifiers subject to your privacy policy, model calls, tool names, durations, errors, and outcomes. Avoid secrets and sensitive payloads by default. ADK supplies structured events, but your service decides what to retain and who may see it.

ADK versus alternatives

ChooseWhen it fitsCost or limitation
ADKGemini or Google Cloud is central; you want an event model, local trace UI, and Google deployment pathsGemini-native defaults make provider-neutral designs less natural
LangGraphThe workflow is an explicit graph with checkpoints, branching, or human approvalMore graph and state machinery is yours to operate
MAFYour organisation is Microsoft- and .NET-orientedLess natural for a Python and Google Cloud shop
Plain model SDKOne or two model calls with no durable conversation or tool loopYou must build state, retries, events, and tracing as the system grows

ADK is a sensible default for a Gemini-based support assistant on Google Cloud, not automatically for a deterministic workflow with six branches and two human gates. ADK can use other providers through integrations such as LiteLLM, but tool schemas, streaming, multimodal inputs, safety controls, and usage metadata may differ. Test the exact model and feature combination you will run.

When ADK wins—and when it does not

Reach for ADK when Gemini is preferred, Google Cloud already hosts your identity and data, and you want an opinionated runtime with a local event viewer.

Do not use an agent merely because a requirement says “agent.” For “classify this ticket and return JSON,” a direct model call with schema validation is usually cheaper and easier to test. A fixed sequence may be clearer as ordinary application code or an explicit workflow. An agent earns its keep when the model must choose among actions or decide what information to seek next.

There is an operational cost: every extra model turn adds latency and tokens. A tool loop can turn a 700 millisecond answer into a 4 second answer before the database does anything slow. Streaming makes the wait less opaque; it does not remove it. Set budgets for turns, tool duration, context size, and total request time.

In August 2026, the safe description is “Gemini-first and increasingly integrable,” not “Gemini-only.” Pin the SDK, run compatibility tests, and check the official ADK documentation before relying on a method signature.

In one breath

  • ADK is Google’s Gemini-first google-adk framework, with Google Cloud as a natural deployment environment.
  • LlmAgent defines behaviour; sessions hold conversation state; the Runner orchestrates invocations and yields events.
  • adk web is a local debugger, not a production serving layer.
  • Use ADK for Gemini and Google Cloud alignment, a plain SDK for simple calls, LangGraph for explicit graph control, or MAF for a Microsoft-centred stack.

Quick check

Quick check

0/3
Q1What's the role of the Runner in ADK?
Q2When should you use adk web?
Q3Transfer: your agent answers the first message correctly, but forgets it immediately on every follow-up. The model and credentials are healthy. What should you inspect first?

Next

The next lesson covers ADK tools and multi-tool agents: how to expose Python functions, how the model selects among several tools, and which built-in Google capabilities can do more than a hand-written function.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions
What is an AI agent, and how does it differ from a single LLM call?

An AI agent is an application that lets an LLM choose and execute validated tools in a bounded loop, carrying observations and state forward until it reaches a goal or needs approval. A single LLM call produces one response or tool-call proposal and stops; it does not itself provide the loop, live-system access, memory, or side effects.

How do function/tool calling and LLM agents work at a high level?

Tool calling extends the LLM's output space to include structured function invocations. The model emits a JSON object naming a tool and its arguments; the runtime executes the tool and feeds the result back as a new message. An agent is a loop that repeats this cycle — observe, think, act — until the task is complete or a stopping condition is met.

What is tool use or function calling in LLMs, and how do you design good tools for an agent?

Tool use lets an LLM emit a structured request for an external function, which the application validates, authorizes, executes, and returns to the model. Reliable tools have clear descriptions, narrow scope, strict typed inputs, least-privilege access, idempotency, and useful structured errors.

In LlamaIndex, what are nodes and query engines, and how is RAG exposed as a tool to an agent?

Nodes are the retrievable pieces of source content, carrying text, metadata, and relationships. A query engine retrieves relevant nodes and synthesizes an answer; wrapping it in QueryEngineTool lets an agent choose that RAG pipeline as a tool.

Related lessons

Explore further