Module 6: Running an Agent Programmatically
Theory​
Beyond the Dev UI: Production Execution​
While the adk web Dev UI is an excellent tool for interactive testing, it's not designed for production use. To integrate your agent into a larger application, a backend service, or a custom user interface, you need to run it programmatically.
Running an agent programmatically means you are responsible for managing the components that the Dev UI handles automatically. This gives you complete control over the agent's lifecycle and how it integrates with your application.
Core Components for Programmatic Execution​
When you run an agent outside the Dev UI, you need to explicitly create and manage three key components:
-
The Runner (
Runner)-
Feature: Oversight of agent execution.
-
Description: A Runner is responsible for the actual execution of an agent. It takes user input, manages the session, invokes the appropriate agent, and streams back the events generated by the agent.
-
google.adk.runners.Runner: This is the primary class for running agents. It is a stateless orchestrator that requires anapp_name, theroot_agentto run, and instances of various services (likeSessionService,ArtifactService,MemoryService) to be passed to its constructor. This is the class you will use for production applications where you need to connect to persistent, production-grade services. -
google.adk.runners.InMemoryRunner: This is a convenient subclass ofRunnerthat comes pre-configured with in-memory implementations forSessionService,ArtifactService, andMemoryService. This is ideal for quick local development, testing, and examples where persistence is not required.
InMemoryRunnervs.RunnerUseInMemoryRunnerfor quick local tests and examples. Switch to the baseRunnerclass when you need to integrate with persistent services likeFirestoreSessionServicefor more robust applications. The lab for this module will use the baseRunnerto teach you how the components fit together.Example with
InMemoryRunner:from google.adk.runners import InMemoryRunner
from google.adk.agents import LlmAgent
from google.genai.types import Content, Part
root_agent = LlmAgent(name="my_agent", model="gemini-2.5-flash", instruction="Be helpful.")
runner = InMemoryRunner(agent=root_agent, app_name="MyApp")
# Conceptual usage for run_async:
# async for event in runner.run_async(
# user_id="user123",
# session_id="sessionXYZ",
# new_message=Content(parts=[Part(text="Hello")])
# ):
# print(event) -
-
The Session Service (
InMemorySessionService)- Feature: Conversation history & shared state.
- Description: Sessions are crucial for maintaining conversational context. They store the history of messages and any stateful information the agent needs to remember between turns. Before creating a
Runner, you must first instantiate a session service. TheInMemorySessionServiceis a simple, non-persistent store that keeps session data in memory, perfect for development and simple applications. For production, you would use a persistent service likeFirestoreSessionService.
-
**Structured Messages (
types.Contentandtypes.Part)`- Feature: Structured, multimodal messages.
- Description: Instead of passing a simple string to the agent, you must package it in a structured
Contentobject from thegoogle.genailibrary. AContentobject has arole('user','model', or'tool') and a list ofparts. EachPartcan contain text, an image, or other data. This structure is what allows for rich, multimodal interactions with the underlying Gemini model.
The Asynchronous Event Loop​
The entire process is asynchronous. When you call runner.run_async(), you don't get a single response back. Instead, you get an asynchronous generator that yields Event objects as they happen.
Your application code will typically use an async for loop to iterate through these events and decide how to handle them. For a simple chatbot, you might just print the text from the final 'model' response event. For a more complex application, you might inspect 'tool' events to show a spinner in the UI while a tool is running.
To better visualize this flow, consider the following conceptual diagram:
$$\text{User Query} \xrightarrow{\text{Runner}} \text{Agent} \xrightarrow{\text{LLM/Tools}} \text{Event Stream} \xrightarrow{\text{App Logic}} \text{Response} $$
This highlights the Runner's central role as the orchestrator. For a more detailed visual explanation, refer to these diagrams:


By managing these components yourself, you gain the power to embed your ADK agent into any Python application, from a simple command-line interface to a complex, scalable web service.
Key Takeaways​
- Programmatic execution gives you full control over the agent's lifecycle for integration into custom applications.
- The
Runneris a stateless engine that requires services, like aSessionService, to be passed into it. - The
InMemoryRunneris a convenient subclass ofRunnerwith pre-configured in-memory services for rapid development. - The
InMemorySessionServiceis used to create and manage sessions in memory for development. - User messages must be packaged into structured
types.Contentandtypes.Partobjects. - The
runner.run_async()method returns an asynchronous stream ofEventobjects that you process in a loop.