Files
google__adk-docs/docs/agents/llm-agents.md
Shahin Saadati 5682576053 Add Kotlin safety settings and includeContents to the agent docs (#2158)
* Add Kotlin safety settings and includeContents to the agent docs

Three related parity gaps, all on the same two types.

docs/safety/index.md had no Kotlin tab at all in the built-in Gemini
safety section. docs/agents/llm-agents.md had a Kotlin tab for
generateContentConfig that set only temperature and maxOutputTokens
while its Python sibling also set safety settings, and no Kotlin tab at
all for includeContents. None of these are new API: safety settings
landed in adk-kotlin 0.5.0 and includeContents has been on LlmAgent
since 0.1.0.

The under-showing tab is the interesting case. A missing-tab scan cannot
see it and neither can a symbol diff, because the tab exists and every
symbol it names is present - only comparing a tab's contents against its
siblings reveals it.

The safety page tab is inline because its siblings are, and because the
snippet elides the required name and model parameters the same way they
do, so there is nothing there that could compile on its own.

* Restore the Go tab's indentation on the safety page

Adding the Kotlin tab accidentally reindented a line inside the Go
sample, which mixes tabs and spaces. The Go tab is a sibling and should
not appear in this diff at all; the page's only non-additive change is
now the badge line.
2026-08-31 12:36:36 -07:00

34 KiB

Simple agents with LlmAgent

Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0Kotlin v0.1.0

The LlmAgent class, often aliased simply as Agent, is a core component in ADK, acting as the core part of your agent application. It leverages the power of a Large Language Model (LLM) or generative AI model for reasoning, understanding natural language, making decisions, generating responses, and interacting with tools. Since this type of agent uses an AI model to interpret instructions and context, the AI model dynamically decides how to proceed, which tools to use (if any), and what output to provide. As such, the behavior of this type of agent is non-deterministic and must be built and evaluated with this behavior in mind.

Building an effective LlmAgent involves defining its identity, clearly guiding its behavior through instructions, and equipping it with the necessary tools and capabilities.

Define agent identity and purpose

First, you need to establish what the agent is and what it's for.

  • name (Required): Every agent needs a unique string identifier. This name is crucial for internal operations, especially in multi-agent systems where agents need to refer to or delegate tasks to each other. Choose a descriptive name that reflects the agent's function (e.g., customer_support_router, billing_inquiry_agent). Avoid reserved names like user.

  • description (Optional, Recommended for Multi-Agent): Provide a concise summary of the agent's capabilities. This description is primarily used by other LLM agents to determine if they should route a task to this agent. Make it specific enough to differentiate it from peers (e.g., "Handles inquiries about current billing statements," not just "Billing agent").

  • model (Required): Specify the underlying LLM that will power this agent's reasoning. This is a string identifier like "gemini-flash-latest". The choice of model impacts the agent's capabilities, cost, and performance. See the Models page for available options and considerations.

=== "Python"

```python
# Example: Defining the basic identity
capital_agent = LlmAgent(
    model="gemini-flash-latest",
    name="capital_agent",
    description="Answers user questions about the capital city of a given country."
    # instruction and tools will be added next
)
```

=== "TypeScript"

```typescript
// Example: Defining the basic identity
const capitalAgent = new LlmAgent({
    model: 'gemini-flash-latest',
    name: 'capital_agent',
    description: 'Answers user questions about the capital city of a given country.',
    // instruction and tools will be added next
});
```

=== "Go"

```go
--8<-- "examples/go/snippets/agents/llm-agents/snippets/main.go:identity"
```

=== "Java"

```java
// Example: Defining the basic identity
LlmAgent capitalAgent =
    LlmAgent.builder()
        .model("gemini-flash-latest")
        .name("capital_agent")
        .description("Answers user questions about the capital city of a given country.")
        // instruction and tools will be added next
        .build();
```

=== "Kotlin"

```kotlin
--8<-- "examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt:identity"
```

Guide the agent with instructions

The instruction parameter is arguably the most critical for shaping an LlmAgent's behavior. It's a string (or a function returning a string) that tells the agent:

  • Its core task or goal.
  • Its personality or persona (e.g., "You are a helpful assistant," "You are a witty pirate").
  • Constraints on its behavior (e.g., "Only answer questions about X," "Never reveal Y").
  • How and when to use its tools. You should explain the purpose of each tool and the circumstances under which it should be called, supplementing any descriptions within the tool itself.
  • The desired format for its output (e.g., "Respond in JSON," "Provide a bulleted list").

Tips for effective instructions:

  • Be Clear and Specific: Avoid ambiguity. Clearly state the desired actions and outcomes.
  • Use Markdown: Improve readability for complex instructions using headings, lists, etc.
  • Provide Examples (Few-Shot): For complex tasks or specific output formats, include examples directly in the instruction.
  • Guide Tool Use: Don't just list tools; explain when and why the agent should use them.

Use dynamic state variables:

  • The instruction is a string template, you can use the {var} syntax to insert dynamic values into the instruction.
  • {var} is used to insert the value of the state variable named var.
  • {artifact.var} is used to insert the text content of the artifact named var.
  • If the state variable or artifact does not exist, the agent will raise an error. If you want to ignore the error, you can append a ? to the variable name as in {var?}.

=== "Python"

```python
# Example: Adding instructions
capital_agent = LlmAgent(
    model="gemini-flash-latest",
    name="capital_agent",
    description="Answers user questions about the capital city of a given country.",
    instruction="""You are an agent that provides the capital city of a country.
When a user asks for the capital of a country:
1. Identify the country name from the user's query.
2. Use the `get_capital_city` tool to find the capital.
3. Respond clearly to the user, stating the capital city.
Example Query: "What's the capital of {country}?"
Example Response: "The capital of France is Paris."
""",
    # tools will be added next
)
```

=== "TypeScript"

```typescript
// Example: Adding instructions
const capitalAgent = new LlmAgent({
    model: 'gemini-flash-latest',
    name: 'capital_agent',
    description: 'Answers user questions about the capital city of a given country.',
    instruction: `You are an agent that provides the capital city of a country.
        When a user asks for the capital of a country:
        1. Identify the country name from the user's query.
        2. Use the \`getCapitalCity\` tool to find the capital.
        3. Respond clearly to the user, stating the capital city.
        Example Query: "What's the capital of {country}?"
        Example Response: "The capital of France is Paris."
        `,
    // tools will be added next
});
```

=== "Go"

```go
--8<-- "examples/go/snippets/agents/llm-agents/snippets/main.go:instruction"
```

=== "Java"

```java
// Example: Adding instructions
LlmAgent capitalAgent =
    LlmAgent.builder()
        .model("gemini-flash-latest")
        .name("capital_agent")
        .description("Answers user questions about the capital city of a given country.")
        .instruction(
            """
            You are an agent that provides the capital city of a country.
            When a user asks for the capital of a country:
            1. Identify the country name from the user's query.
            2. Use the `get_capital_city` tool to find the capital.
            3. Respond clearly to the user, stating the capital city.
            Example Query: "What's the capital of {country}?"
            Example Response: "The capital of France is Paris."
            """)
        // tools will be added next
        .build();
```

=== "Kotlin"

```kotlin
--8<-- "examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt:instruction"
```

!!! note "GlobalInstructionPlugin"

To apply shared rules or a consistent personality to *all* 
agents in your system, use `GlobalInstructionPlugin` instead of 
the deprecated `global_instruction` parameter.

Equip the agent with tools

Tools give your LlmAgent capabilities beyond the LLM's built-in knowledge or reasoning. They allow the agent to interact with the outside world, perform calculations, fetch real-time data, or execute specific actions.

  • tools (Optional): Provide a list of tools the agent can use. Each item in the list can be:
    • A native function or method (wrapped as a FunctionTool). Python ADK automatically wraps the native function into a FunctionTool whereas, you must explicitly wrap your Java methods using FunctionTool.create(...). In Kotlin, you can use the @Tool annotation to automatically generate a FunctionTool at compile-time.
    • An instance of a class inheriting from BaseTool.
    • An instance of another agent (AgentTool, enabling agent-to-agent delegation - see Custom agent workflows).

The LLM uses the function/tool names, descriptions (from docstrings or the description field), and parameter schemas to decide which tool to call based on the conversation and its instructions.

=== "Python"

```python
# Define a tool function
def get_capital_city(country: str) -> str:
  """Retrieves the capital city for a given country."""
  # Replace with actual logic (e.g., API call, database lookup)
  capitals = {"france": "Paris", "japan": "Tokyo", "canada": "Ottawa"}
  return capitals.get(country.lower(), f"Sorry, I don't know the capital of {country}.")

# Add the tool to the agent
capital_agent = LlmAgent(
    model="gemini-flash-latest",
    name="capital_agent",
    description="Answers user questions about the capital city of a given country.",
    instruction="""You are an agent that provides the capital city of a country... (previous instruction text)""",
    tools=[get_capital_city] # Provide the function directly
)
```

=== "TypeScript"

```typescript
import {z} from 'zod';
import { LlmAgent, FunctionTool } from '@google/adk';

// Define the schema for the tool's input parameters
const getCapitalCityParamsSchema = z.object({
    country: z.string().describe('The country to get capital for.'),
});

// Define the tool function itself
async function getCapitalCity(params: z.infer<typeof getCapitalCityParamsSchema>): Promise<{ capitalCity: string }> {
const capitals: Record<string, string> = {
    'france': 'Paris',
    'japan': 'Tokyo',
    'canada': 'Ottawa',
};
const result = capitals[params.country.toLowerCase()] ??
    `Sorry, I don't know the capital of ${params.country}.`;
return {capitalCity: result}; // Tools must return an object
}

// Create an instance of the FunctionTool
const getCapitalCityTool = new FunctionTool({
    name: 'getCapitalCity',
    description: 'Retrieves the capital city for a given country.',
    parameters: getCapitalCityParamsSchema,
    execute: getCapitalCity,
});

// Add the tool to the agent
const capitalAgent = new LlmAgent({
    model: 'gemini-flash-latest',
    name: 'capitalAgent',
    description: 'Answers user questions about the capital city of a given country.',
    instruction: 'You are an agent that provides the capital city of a country...', // Note: the full instruction is omitted for brevity
    tools: [getCapitalCityTool], // Provide the FunctionTool instance in an array
});
```

=== "Go"

```go
--8<-- "examples/go/snippets/agents/llm-agents/snippets/main.go:tool_example"
```

=== "Java"

```java

// Define a tool function
// Retrieves the capital city of a given country.
public static Map<String, Object> getCapitalCity(
        @Schema(name = "country", description = "The country to get capital for")
        String country) {
  // Replace with actual logic (e.g., API call, database lookup)
  Map<String, String> countryCapitals = new HashMap<>();
  countryCapitals.put("canada", "Ottawa");
  countryCapitals.put("france", "Paris");
  countryCapitals.put("japan", "Tokyo");

  String result =
          countryCapitals.getOrDefault(
                  country.toLowerCase(), "Sorry, I couldn't find the capital for " + country + ".");
  return Map.of("result", result); // Tools must return a Map
}

// Add the tool to the agent
FunctionTool capitalTool = FunctionTool.create(experiment.getClass(), "getCapitalCity");
LlmAgent capitalAgent =
    LlmAgent.builder()
        .model("gemini-flash-latest")
        .name("capital_agent")
        .description("Answers user questions about the capital city of a given country.")
        .instruction("You are an agent that provides the capital city of a country... (previous instruction text)")
        .tools(capitalTool) // Provide the function wrapped as a FunctionTool
        .build();
```

=== "Kotlin"

```kotlin
--8<-- "examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt:tool_definition"

// Add the tool to the agent
--8<-- "examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt:tool_usage"
```

Learn more about Tools in Custom Tools.

Advanced configuration and control

Beyond the core parameters, LlmAgent offers several options for finer control:

Fine-tune AI model operation

You can adjust how the underlying AI model generates responses using generate_content_config.

  • generate_content_config (Optional): Pass an instance of google.genai.types.GenerateContentConfig to control parameters like temperature (randomness), max_output_tokens (response length), top_p, top_k, and safety settings.

=== "Python"

```python
from google.genai import types

agent = LlmAgent(
    # ... other params
    generate_content_config=types.GenerateContentConfig(
        temperature=0.2, # More deterministic output
        max_output_tokens=250,
        safety_settings=[
            types.SafetySetting(
                category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
                threshold=types.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
            )
        ]
    )
)
```

=== "TypeScript"

```typescript
import { GenerateContentConfig } from '@google/genai';

const generateContentConfig: GenerateContentConfig = {
    temperature: 0.2, // More deterministic output
    maxOutputTokens: 250,
};

const agent = new LlmAgent({
    // ... other params
    generateContentConfig,
});
```

=== "Go"

```go
import "google.golang.org/genai"

--8<-- "examples/go/snippets/agents/llm-agents/snippets/main.go:gen_config"
```

=== "Java"

```java
import com.google.genai.types.GenerateContentConfig;

LlmAgent agent =
    LlmAgent.builder()
        // ... other params
        .generateContentConfig(GenerateContentConfig.builder()
            .temperature(0.2F) // More deterministic output
            .maxOutputTokens(250)
            .build())
        .build();
```

=== "Kotlin"

```kotlin
--8<-- "examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt:gen_config"
```

Configure a default model

Supported in ADKPython v1.22.0

You can set a system-wide default model for all LlmAgent instances using the set_default_model class method. If you do not specify a model when creating an agent, it falls back to ADK's built-in default model. This setting helps you avoid redundant model specifications and easily change the model for all agents at once.

=== "Python"

```python
from google.adk.agents import LlmAgent

# Set a new default model for all agents
LlmAgent.set_default_model("gemini-flash-latest")

# This agent will now use "gemini-flash-latest" by default
agent_with_default_model = LlmAgent(
    name="default_model_agent",
    instruction="You are a helpful assistant."
)

# You can still override the default for specific agents
specific_agent = LlmAgent(
    name="specific_model_agent",
    model="gemini-pro-latest",
    instruction="You are a creative writer."
)
```

Structure data input and output

For scenarios requiring structured data exchange with an LLM Agent, the ADK provides mechanisms to define expected input and desired output formats using schema definitions.

  • input_schema (Optional): Define a schema representing the expected input structure. If set, the user message content passed to this agent must be a JSON string conforming to this schema. Your instructions should guide the user or preceding agent accordingly.

  • output_schema (Optional): Define a schema representing the desired output structure. If set, the agent's final response must be a JSON string conforming to this schema.

!!! warning "Warning: Using output_schema with tools"

Using `output_schema` with `tools` in the same LLM request is only supported
by specific models, including [Gemini
3.0](https://ai.google.dev/gemini-api/docs/function-calling?example=meeting#structured-output).
For other models, ADK falls back to a [`set_model_response` function
tool](https://github.com/google/adk-python/blob/main/src/google/adk/flows/llm_flows/_output_schema_processor.py)
to collect the structured output, which may not work reliably. In such
cases, consider using sub-agents that handle output formatting separately.
  • output_key (Optional): Provide a string key. If set, the text content of the agent's final response will be automatically saved to the session's state dictionary under this key. This is useful for passing results between agents or steps in a workflow.

    • In Python, this might look like: session.state[output_key] = agent_response_text
    • In Java: session.state().put(outputKey, agentResponseText)
    • In Golang, within a callback handler: ctx.State().Set(output_key, agentResponseText)

    When output_schema is also set, the parsed response is stored instead of the text: a dict in Python, and a Map in Java and Kotlin.

!!! note "Schema validation in Java and Kotlin"

Java and Kotlin check the response against the *structure* of the schema —
`type`, `required`, `nullable`, `anyOf` and `items` (see
[`SchemaUtils`](https://github.com/google/adk-kotlin/blob/v0.8.0/core/src/commonMain/kotlin/com/google/adk/kt/SchemaUtils.kt)).
Constraint fields such as `pattern`, `minLength` and `minimum` are sent to
the model as part of the schema, but ADK does not re-check them, so the
model decides whether to honor them. Python validates against a Pydantic
model, which does enforce the constraints declared on it.

Java and Kotlin accept only a top-level object schema; a top-level array or
primitive fails validation. Python also supports list and primitive output
schemas.

If the response fails validation, ADK logs the error and stores the raw
response string under `output_key` instead of the parsed object (see
[`LlmAgent`](https://github.com/google/adk-kotlin/blob/v0.8.0/core/src/commonMain/kotlin/com/google/adk/kt/agents/LlmAgent.kt)).

=== "Python"

The input and output schema is typically a `Pydantic` BaseModel.

```python
from pydantic import BaseModel, Field

class CapitalOutput(BaseModel):
    capital: str = Field(description="The capital of the country.")

structured_capital_agent = LlmAgent(
    # ... name, model, description
    instruction="""You are a Capital Information Agent. Given a country, respond ONLY with a JSON object containing the capital. Format: {"capital": "capital_name"}""",
    output_schema=CapitalOutput, # Enforce JSON output
    output_key="found_capital"  # Store result in state['found_capital']
    # Cannot use tools=[get_capital_city] effectively here
)
```

=== "TypeScript"

```typescript
import {z} from 'zod';
import { Schema, Type } from '@google/genai';

// Define the schema for the output
const CapitalOutputSchema: Schema = {
    type: Type.OBJECT,
    properties: {
        capital: {
            type: Type.STRING,
            description: 'The capital of the country.',
        },
    },
    required: ['capital'],
};

// Create the LlmAgent instance
const structuredCapitalAgent = new LlmAgent({
    // ... name, model, description
    instruction: `You are a Capital Information Agent. Given a country, respond ONLY with a JSON object containing the capital. Format: {"capital": "capital_name"}`,
    outputSchema: CapitalOutputSchema, // Enforce JSON output
    outputKey: 'found_capital', // Store result in state['found_capital']
    // Cannot use tools effectively here
});
```

=== "Go"

The input and output schema is a `google.genai.types.Schema` object.

```go
--8<-- "examples/go/snippets/agents/llm-agents/snippets/main.go:schema_example"
```

=== "Java"

 The input and output schema is a `google.genai.types.Schema` object.

```java
private static final Schema CAPITAL_OUTPUT =
    Schema.builder()
        .type("OBJECT")
        .description("Schema for capital city information.")
        .properties(
            Map.of(
                "capital",
                Schema.builder()
                    .type("STRING")
                    .description("The capital city of the country.")
                    .build()))
        .build();

LlmAgent structuredCapitalAgent =
    LlmAgent.builder()
        // ... name, model, description
        .instruction(
                "You are a Capital Information Agent. Given a country, respond ONLY with a JSON object containing the capital. Format: {\"capital\": \"capital_name\"}")
        .outputSchema(CAPITAL_OUTPUT) // Enforce JSON output
        .outputKey("found_capital") // Store result in state.get("found_capital")
        // Cannot use tools(getCapitalCity) effectively here
        .build();
```

=== "Kotlin"

The input and output schema is ADK's own `com.google.adk.kt.types.Schema`,
not the same-named type in the GenAI SDK. Starting with ADK Kotlin v0.8.0,
the JSON schema includes constraints for the following fields: `pattern`,
`minLength`, `maxLength`, `minimum`, `maximum`, `minItems`, `maxItems`,
`format`, `nullable`, `default`, `anyOf` and `title`.

```kotlin
--8<-- "examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt:schema_example"
```

The `format` field accepts only the values the model allows for the field's type. For
the accepted values, see the Gemini [`Schema`
reference](https://ai.google.dev/api/caching#Schema).

The `default` field must contain a JSON-native value. ADK's own `Json` serializes one,
but a hand-rolled serializer without a contextual `Any` serializer does not.

Manage agent context

Control whether the agent receives the prior conversation history.

  • include_contents (Optional, Default: 'default'): Determines if the contents (history) are sent to the LLM.
    • 'default': The agent receives the relevant conversation history.
    • 'none': The agent receives no prior contents. It operates based solely on its current instruction and any input provided in the current turn (useful for stateless tasks or enforcing specific contexts).

=== "Python"

```python
stateless_agent = LlmAgent(
    # ... other params
    include_contents='none'
)
```

=== "TypeScript"

```typescript
const statelessAgent = new LlmAgent({
    // ... other params
    includeContents: 'none',
});
```

=== "Go"

```go
import "google.golang.org/adk/v2/agent/llmagent"

--8<-- "examples/go/snippets/agents/llm-agents/snippets/main.go:include_contents"
```

=== "Java"

```java
import com.google.adk.agents.LlmAgent.IncludeContents;

LlmAgent statelessAgent =
    LlmAgent.builder()
        // ... other params
        .includeContents(IncludeContents.NONE)
        .build();
```

=== "Kotlin"

```kotlin
--8<-- "examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt:include_contents"
```

!!! note "Go v2.0.0: agent execution modes"

ADK Go v2.0.0 introduces an explicit `Mode` field on `llmagent.Config` that
controls how the agent runs when used inside a graph-based or dynamic
workflow. Three modes are available:

- **`ModeChat`** (default for an agent used as a sub-agent): The agent
  participates in a multi-turn conversation with the user and is reachable
  from peer agents via `transfer_to_agent`.
- **`ModeSingleTurn`** (default for an agent used as a node in a workflow):
  The agent completes its task in a single turn without chatting with the
  user.
- **`ModeTask`**: A task agent that chats with the user to accomplish a task
  — in contrast to `ModeSingleTurn`, it can interact with the user across
  turns to complete the work.

When you wrap an `llmagent` with `workflow.NewAgentNode`, the workflow
engine automatically sets the mode to `ModeSingleTurn` if no mode is
specified — equivalent to Python's `mode="single_turn"` on an agent used as
a workflow node. For more information on composing agents in graph-based
workflows, see [Graph-based agent workflows](/graphs/).

Configure a planner

Supported in ADKPython v0.1.0

planner (Optional): Assign a BasePlanner instance to enable multi-step reasoning and planning before execution. There are two main planners:

  • BuiltInPlanner: Leverages the model's built-in planning capabilities (e.g., Gemini's thinking feature). See Gemini Thinking for details and examples.

    Here, the thinking_budget parameter guides the model on the number of thinking tokens to use when generating a response. The include_thoughts parameter controls whether the model should include its raw thoughts and internal reasoning process in the response.

    from google.adk import Agent
    from google.adk.planners import BuiltInPlanner
    from google.genai import types
    
    my_agent = Agent(
        model="gemini-flash-latest",
        planner=BuiltInPlanner(
            thinking_config=types.ThinkingConfig(
                include_thoughts=True,
                thinking_budget=1024,
            )
        ),
        # ... your tools here
    )
    
  • PlanReActPlanner: This planner instructs the model to follow a specific structure in its output: first create a plan, then execute actions (like calling tools), and provide reasoning for its steps. It's particularly useful for models that don't have a built-in "thinking" feature.

    from google.adk import Agent
    from google.adk.planners import PlanReActPlanner
    
    my_agent = Agent(
        model="gemini-flash-latest",
        planner=PlanReActPlanner(),
        # ... your tools here
    )
    

    The agent's response will follow a structured format:

    [user]: ai news
    [google_search_agent]: /*PLANNING*/
    1. Perform a Google search for "latest AI news" to get current updates and headlines related to artificial intelligence.
    2. Synthesize the information from the search results to provide a summary of recent AI news.
    
    /*ACTION*/
    /*REASONING*/
    The search results provide a comprehensive overview of recent AI news, covering various aspects like company developments, research breakthroughs, and applications. I have enough information to answer the user's request.
    
    /*FINAL_ANSWER*/
    Here's a summary of recent AI news:
    ....
    

Example for using built-in-planner:

from dotenv import load_dotenv


import asyncio
import os

from google.genai import types
from google.adk.agents.llm_agent import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService # Optional
from google.adk.planners import BasePlanner, BuiltInPlanner, PlanReActPlanner
from google.adk.models import LlmRequest

from google.genai.types import ThinkingConfig
from google.genai.types import GenerateContentConfig

import datetime
from zoneinfo import ZoneInfo

APP_NAME = "weather_app"
USER_ID = "1234"
SESSION_ID = "session1234"

def get_weather(city: str) -> dict:
    """Retrieves the current weather report for a specified city.

    Args:
        city (str): The name of the city for which to retrieve the weather report.

    Returns:
        dict: status and result or error msg.
    """
    if city.lower() == "new york":
        return {
            "status": "success",
            "report": (
                "The weather in New York is sunny with a temperature of 25 degrees"
                " Celsius (77 degrees Fahrenheit)."
            ),
        }
    else:
        return {
            "status": "error",
            "error_message": f"Weather information for '{city}' is not available.",
        }


def get_current_time(city: str) -> dict:
    """Returns the current time in a specified city.

    Args:
        city (str): The name of the city for which to retrieve the current time.

    Returns:
        dict: status and result or error msg.
    """

    if city.lower() == "new york":
        tz_identifier = "America/New_York"
    else:
        return {
            "status": "error",
            "error_message": (
                f"Sorry, I don't have timezone information for {city}."
            ),
        }

    tz = ZoneInfo(tz_identifier)
    now = datetime.datetime.now(tz)
    report = (
        f'The current time in {city} is {now.strftime("%Y-%m-%d %H:%M:%S %Z%z")}'
    )
    return {"status": "success", "report": report}

# Step 1: Create a ThinkingConfig
thinking_config = ThinkingConfig(
    include_thoughts=True,   # Ask the model to include its thoughts in the response
    thinking_budget=256      # Limit the 'thinking' to 256 tokens (adjust as needed)
)
print("ThinkingConfig:", thinking_config)

# Step 2: Instantiate BuiltInPlanner
planner = BuiltInPlanner(
    thinking_config=thinking_config
)
print("BuiltInPlanner created.")

# Step 3: Wrap the planner in an LlmAgent
agent = LlmAgent(
    model="gemini-flash-latest",  # Set your model name
    name="weather_and_time_agent",
    instruction="You are an agent that returns time and weather",
    planner=planner,
    tools=[get_weather, get_current_time]
)

# Session and Runner
session_service = InMemorySessionService()
session = session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner = Runner(agent=agent, app_name=APP_NAME, session_service=session_service)

# Agent Interaction
def call_agent(query):
    content = types.Content(role='user', parts=[types.Part(text=query)])
    events = runner.run(user_id=USER_ID, session_id=SESSION_ID, new_message=content)

    for event in events:
        print(f"\nDEBUG EVENT: {event}\n")
        if event.is_final_response() and event.content:
            final_answer = event.content.parts[0].text.strip()
            print("\n🟢 FINAL ANSWER\n", final_answer, "\n")

call_agent("If it's raining in New York right now, what is the current temperature?")

Code execution

Supported in ADKPython v0.1.0Java v0.1.0
  • code_executor (Optional): Provide a BaseCodeExecutor instance to allow the agent to execute code blocks found in the LLM's response. For more information, see Code Execution with Gemini API.

=== "Python"

```python
--8<-- "examples/python/snippets/tools/built-in-tools/code_execution.py"
```

=== "Java"

```java
--8<-- "examples/java/snippets/src/main/java/tools/CodeExecutionAgentApp.java:full_code"
```

Code example

This following example demonstrates the core concepts discussed in this page. More complex agents might incorporate schemas, context control, and planning.

??? "Code" Here's the complete basic capital_agent:

=== "Python"

    ```python
    --8<-- "examples/python/snippets/agents/llm-agent/capital_agent.py"
    ```

=== "TypeScript"

    ```typescript
    --8<-- "examples/typescript/snippets/agents/llm-agent/capital_agent.ts"
    ```

=== "Go"

    ```go
    --8<-- "examples/go/snippets/agents/llm-agents/main.go:full_code"
    ```

=== "Java"

    ```java
    --8<-- "examples/java/snippets/src/main/java/agents/LlmAgentExample.java:full_code"
    ```

=== "Kotlin"

    ```kotlin
    --8<-- "examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt:full_example"
    ```

Additional features

ADK provides additional features for agents not covered in this guide, including the following:

  • Callbacks: Add more controls by intercepting agent execution points, including before and after model calls, and before and after tool calls with Callbacks.
  • Graph-based workflows: Compose LLM agents as steps in deterministic, graph-based pipelines using Graph-based agent workflows. In Go v2.0.0, use workflow.NewAgentNode to wrap any LLM agent as a workflow node.
  • Multi-agent systems: Advanced strategies for agent interaction, including agent transfer (disallow_transfer_to_parent, disallow_transfer_to_peers), and consistent identity and rules for every agent in your app (GlobalInstructionPlugin). See Multi-agent workflows and collaborative agent teams.