Building a Basic, Stateless ADK Agent

In this chapter, we’re laying the foundational brick for our robust AI agent system. We’ll build a simple, stateless AI agent using Google’s Agent Development Kit (ADK). This initial setup will demonstrate the core interaction loop: receiving user input, processing it with an ADK agent, and generating a response using a large language model (LLM).

This milestone is critical because it establishes the basic communication patterns and environment for our agent, allowing us to confirm the ADK setup and LLM integration are functional. While this agent won’t remember past conversations yet, it provides a functional starting point that we can incrementally enhance with statefulness and persistence in subsequent chapters. By the end of this chapter, you’ll have a running ADK agent that can respond to simple prompts in your local development environment.

Project Overview

Our overarching goal is to develop a long-running, stateful AI agent capable of pausing, resuming, and never losing conversational context. This requires careful design around state persistence and robust recovery mechanisms. This chapter focuses on the absolute first step: understanding the core agent interaction without any memory.

Think of this as building the “reflex” of the agent before giving it a “memory.” A stateless agent is simpler to debug and verify, making it an ideal starting point to ensure our environment and basic ADK integration are correct.

Tech Stack

For this chapter, our primary technologies include:

  • Python: The programming language for our agent logic. We’ll use the latest stable Python 3.x. As of 2026-05-23, this is Python 3.12.3.
  • Google ADK: Google’s Agent Development Kit, which provides the framework for building and running AI agents. The exact latest stable version number for the public ADK library was not explicitly found as a tagged release on 2026-05-23; pip install google-adk typically fetches the most recent stable release available on PyPI.
  • Google Cloud: Primarily for accessing Large Language Models (LLMs) via API keys. While we won’t deploy to Google Cloud in this chapter, having a project ready is essential for API access.

Architecting the Stateless Core

A “stateless” agent processes each user request independently, without retaining any memory of previous interactions within its own runtime. Each conversation turn is a fresh start. This design choice offers simplicity, inherent scalability, and ease of deployment.

Core Interaction Flow

The interaction flow for our basic stateless agent is straightforward:

flowchart TD User_Input[User Input] --> ADK_Agent[ADK Agent] ADK_Agent --> LLM_Integration[LLM Integration] LLM_Integration --> LLM[Large Language Model] LLM --> LLM_Integration LLM_Integration --> ADK_Agent ADK_Agent --> Agent_Response[Agent Response] Agent_Response --> User_Output[User Output]
  1. User Input: The user sends a message to the agent (e.g., via a command-line interface).
  2. ADK Agent: The ADK framework receives this message and dispatches it to our custom agent logic.
  3. LLM Integration: Our agent’s logic uses ADK’s built-in capabilities to interface with an LLM.
  4. Large Language Model (LLM): The LLM processes the prompt and generates a response based only on the current input.
  5. Agent Response: The LLM’s output is returned to our ADK agent.
  6. User Output: The agent sends the final response back to the user.

Project Structure

For this basic setup, we’ll maintain a minimal project structure:

agent_app/
├── main.py
└── requirements.txt
  • main.py: This file will contain our agent’s definition and the logic for handling incoming messages.
  • requirements.txt: This file will list our project’s Python dependencies, primarily the Google ADK.

Milestones for This Chapter

To achieve our stateless agent, we will follow these steps:

  1. Set up the Python Environment: Create a project directory and a virtual environment.
  2. Install Google ADK: Install the necessary libraries using pip.
  3. Configure API Access: Ensure your Google API key is correctly set for LLM interaction.
  4. Implement the Agent Logic: Write the main.py file with our SimpleStatelessAgent.
  5. Run and Verify: Execute the agent locally and confirm its stateless behavior.

Step-by-Step Implementation

Let’s get our hands dirty and build this basic agent.

Prerequisites

Before we start, ensure you have:

  • Python 3.12.3 (or latest 3.x): Download the installer from python.org.
  • Google Cloud Account and Project: While we won’t deploy to Google Cloud in this chapter, having a project ready for API key management and future deployment is beneficial. Set up billing and enable the necessary APIs (e.g., Generative Language API for Gemini, if you plan to use it directly, though ADK often abstracts this).
  • Version Control: Initialize a Git repository for your project (git init).

1. Set Up the Python Environment

First, create a new project directory and set up a virtual environment. This isolates your project’s dependencies.

mkdir agent_app
cd agent_app
python3 -m venv .venv
source .venv/bin/activate # On Windows, use `.venv\Scripts\activate`
  • Why this matters: Using a virtual environment prevents conflicts between project dependencies and your system-wide Python packages.

2. Install Google ADK

Now, install the Google ADK. As of 2026-05-23, the exact official documentation URL for Google ADK was not found in the search context. We will proceed with the common installation method via pip, which typically points to the latest stable release available on PyPI. Please refer to the official Google Cloud documentation or the project’s GitHub repository for the most up-to-date installation instructions and version specifics when available.

pip install google-adk
  • Explanation: This command installs the core ADK library. Depending on your specific LLM choice, you might need additional client libraries (e.g., google-generativeai for Gemini). The ADK often handles these dependencies implicitly or through specific configurations.

  • requirements.txt: After installation, it’s good practice to capture your dependencies.

    pip freeze > requirements.txt

    This file will now contain google-adk and its transitive dependencies, allowing others (or your future self) to easily replicate the environment.

3. Configure API Access

To allow your agent to interact with an LLM, you’ll need a Google API key with access to generative AI models.

Action: Set your Google API Key as an environment variable.

export GOOGLE_API_KEY="YOUR_ACTUAL_GOOGLE_API_KEY"
# On Windows (PowerShell): $env:GOOGLE_API_KEY="YOUR_ACTUAL_GOOGLE_API_KEY"
# On Windows (CMD): set GOOGLE_API_KEY="YOUR_ACTUAL_GOOGLE_API_KEY"
  • 🧠 Important: Replace "YOUR_ACTUAL_GOOGLE_API_KEY" with your key. You can obtain one from the Google Cloud Console. Ensure the Generative Language API is enabled for your project.
  • Why environment variables? This is a basic security practice. Hardcoding API keys directly into your code is a significant security risk, especially if the code is committed to version control.

4. Define the Basic Agent in main.py

Now, let’s create our main.py file inside the agent_app/ directory.

agent_app/main.py

import adk
import os
import asyncio # For running async code locally

# ⚡ Quick Note: For local development, ADK often defaults to using
# environment variables for API keys.
# Ensure your Google Cloud API key is set as an environment variable:
# `export GOOGLE_API_KEY="YOUR_API_KEY"` (Linux/macOS)
# `$env:GOOGLE_API_KEY="YOUR_API_KEY"` (Windows PowerShell)

class SimpleStatelessAgent(adk.Agent):
    """
    A basic, stateless ADK agent that responds to messages using an LLM.
    It does not retain any memory of past conversations within its runtime.
    """
    def __init__(self):
        super().__init__()
        # 📌 Key Idea: ADK's default LLM integration simplifies setup.
        # It automatically looks for credentials in the environment (e.g., GOOGLE_API_KEY).
        self.llm = adk.llms.default()
        print("SimpleStatelessAgent initialized, ready to process messages.")

    async def respond_to_message(self, message: adk.Message) -> adk.Message:
        """
        Processes an incoming message and generates a response using the LLM.
        Each call is independent, meaning no memory of previous interactions.
        """
        print(f"Agent received message: '{message.text}'")

        try:
            # 🧠 Important: This is the core interaction with the LLM.
            # For a stateless agent, each call to self.llm.generate is independent.
            # ADK handles the prompt formatting and interaction with the LLM API.
            llm_response = await self.llm.generate(message.text)
            response_text = llm_response.text
            print(f"LLM generated response: '{response_text}'")
            return adk.Message(text=response_text)
        except Exception as e:
            # ⚠️ What can go wrong: LLM API errors, network issues, invalid API key, rate limits.
            print(f"Error generating LLM response: {e}")
            return adk.Message(text="I'm sorry, I encountered an error trying to respond. Please try again later.")

# This block ensures the agent runs when the script is executed directly.
if __name__ == "__main__":
    print("Starting SimpleStatelessAgent...")
    # adk.run() starts the agent's message processing loop.
    # By default, it will use a simple console interface for interaction.
    adk.run(SimpleStatelessAgent)

Code Explanation:

  • import adk: Imports the Google Agent Development Kit, providing the core framework.
  • import os: Used for reminders about environment variables, though not directly used in the agent’s runtime logic here (ADK handles env var lookup internally).
  • class SimpleStatelessAgent(adk.Agent):: Defines our agent class. Inheriting from adk.Agent is fundamental for ADK to recognize and manage our agent.
  • __init__(self):
    • super().__init__(): Calls the constructor of the base adk.Agent class, which is necessary for ADK’s internal setup and lifecycle management.
    • self.llm = adk.llms.default(): This line is crucial. It initializes an LLM client using ADK’s default configuration. ADK is designed to abstract away the specific LLM provider (like Gemini, PaLM, etc.) and automatically looks for credentials (like GOOGLE_API_KEY or GOOGLE_APPLICATION_CREDENTIALS) in your environment.
  • async def respond_to_message(self, message: adk.Message) -> adk.Message:: This is the core asynchronous method where our agent’s logic resides. ADK automatically calls this method when a new message arrives.
    • message: adk.Message: The input message object contains the user’s text and other metadata.
    • llm_response = await self.llm.generate(message.text): This is where the agent sends the user’s message directly to the LLM and awaits its reply. For a stateless agent, this is the entire “brain” for generating a response.
    • return adk.Message(text=response_text): The agent wraps the LLM’s text response back into an adk.Message object and returns it, which ADK then sends back to the user.
  • if __name__ == "__main__":: This standard Python idiom ensures the code inside only runs when the script is executed directly, not when imported as a module.
  • adk.run(SimpleStatelessAgent): This function from ADK starts the agent’s runtime. It handles setting up the environment, listening for messages (e.g., from a local console interface by default), and routing them to your agent’s respond_to_message method.

Testing & Verification

To verify our basic agent is working, we’ll run it locally and interact with it via the command line.

1. Run the Agent

From your agent_app directory, with your virtual environment activated, execute the main.py script:

python main.py

You should see output indicating the agent is starting, and then a prompt for your input:

Starting SimpleStatelessAgent...
SimpleStatelessAgent initialized, ready to process messages.
ADK Agent is ready. Type your message and press Enter.
>

2. Interact with the Agent

Type a message and press Enter. Observe the agent’s responses.

> Hello there!
Agent received message: 'Hello there!'
LLM generated response: 'Hello! How can I help you today?'
> What is the capital of France?
Agent received message: 'What is the capital of France?'
LLM generated response: 'The capital of France is Paris.'
> What did I just ask you?
Agent received message: 'What did I just ask you?'
LLM generated response: 'You asked me what the capital of France is.'
> Do you remember what I asked before that?
Agent received message: 'Do you remember what I asked before that?'
LLM generated response: 'As an AI, I do not retain memory of previous conversations. How can I assist you now?'

Expected Behavior:

  • The agent should respond to your queries using the LLM, demonstrating successful integration.
  • Crucially, when you ask “Do you remember what I asked before that?”, the agent should indicate it does not retain memory of past turns. This explicitly confirms its stateless nature. Each respond_to_message call is indeed independent.
  • Why this matters: This verification confirms that the agent is indeed stateless, which is the exact behavior we designed for this chapter. Understanding this limitation is key before we introduce state.

Quick Debugging Checks:

  • GOOGLE_API_KEY not set or invalid: If you get authentication errors or “permission denied” from the LLM, double-check that your GOOGLE_API_KEY environment variable is correctly set and contains a valid key. Also, ensure the Generative Language API is enabled for your Google Cloud project.
  • No response or script exits: If the agent starts but doesn’t provide a prompt or exits immediately, check the console for Python traceback errors. There might be a typo in your main.py or an issue with the ADK installation.
  • Slow response: LLM calls involve network latency. If responses are consistently very slow (~500ms+), check your internet connection or the LLM provider’s status page.

Production Considerations

Even for a basic stateless agent, considering production implications early on is vital for building robust systems.

  • Scalability: Stateless agents are inherently scalable. Since each request is independent, you can easily run multiple instances of SimpleStatelessAgent behind a load balancer without worrying about shared state or complex synchronization. This is a significant advantage for high-throughput applications, allowing for simple horizontal scaling.
  • Security: Always manage your GOOGLE_API_KEY securely. In production, never hardcode it or rely solely on environment variables directly on compute instances. Utilize a dedicated secret management service like Google Secret Manager. For Google Cloud deployments, leverage service accounts with fine-grained permissions (e.g., roles/generativelanguage.developer) instead of user API keys.
  • Observability: Beyond simple print() statements, integrate a robust logging framework (e.g., Python’s standard logging module). Capture request details, LLM prompts, LLM responses, and any errors with appropriate log levels (DEBUG, INFO, WARNING, ERROR). This is crucial for debugging, understanding agent behavior, and auditing in a production environment. Consider structured logging for easier parsing by monitoring tools.
  • Maintainability: The current main.py is simple. As agent logic grows, consider separating concerns into different modules (e.g., llm_service.py for LLM interaction, agent_logic.py for custom agent rules). This improves code organization and testability.

Common Issues & Solutions

  1. ADK Installation Fails:

    • Issue: pip install google-adk throws errors, often related to build tools or specific Python versions.
    • Solution: Ensure you are using a supported Python version (typically Python 3.8+). Update pip itself (python -m pip install --upgrade pip). If encountering C++ compiler errors, ensure you have the necessary build tools for your OS (e.g., build-essential on Debian/Ubuntu, Xcode Command Line Tools on macOS, Visual C++ Build Tools on Windows).
  2. LLM Authentication Errors:

    • Issue: Agent starts but fails with errors like google.api_core.exceptions.PermissionDenied or AuthenticationError when trying to generate a response.
    • Solution:
      • Verify your GOOGLE_API_KEY environment variable is set correctly and the key itself is valid.
      • Confirm that the Google Cloud project associated with your API key has the “Generative Language API” enabled.
      • Check for region restrictions or quotas on your API key.
  3. Agent Not Responding / Script Exits Immediately:

    • Issue: The python main.py command runs, but the agent doesn’t provide a prompt or exits without error.
    • Solution:
      • Ensure adk.run(SimpleStatelessAgent) is correctly called within the if __name__ == "__main__": block.
      • Check for any syntax errors in your main.py that might prevent the script from reaching the adk.run() call.
      • Verify your virtual environment is activated and google-adk is installed within it.

Summary & Next Step

You’ve successfully built and tested your first basic, stateless AI agent using Google’s ADK!

  • You now understand the fundamental components of an ADK agent and its core request-response loop with an LLM.
  • You’ve implemented the agent code and verified that it processes each message independently, without retaining memory.
  • You’ve considered initial production concerns like scalability, security, and observability for stateless systems.

This stateless foundation is robust and scalable, but real-world agents often need to remember context, user preferences, or past interactions to provide a truly conversational experience. In the next chapter, we’ll address this by implementing an in-memory state management system, allowing our agent to maintain conversational context within a single user session. This will be our first step towards building a truly long-running, stateful agent.

References

This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.