From 587226b3b3e8fdfbd64ef408acb439b93b601250 Mon Sep 17 00:00:00 2001 From: George Weale Date: Tue, 28 Jul 2026 17:09:19 +0000 Subject: [PATCH 1/2] docs(agents): fix broken agent snippets and stale API claims --- docs/agents/config.md | 13 +++-- docs/agents/custom-agents.md | 7 ++- docs/agents/llm-agents.md | 49 ++++++++++++------- docs/agents/managed-agents.md | 28 ++++++++--- docs/agents/workflow-agents/index.md | 4 ++ docs/agents/workflow-agents/loop-agents.md | 9 ++-- .../agents/workflow-agents/parallel-agents.md | 7 ++- .../workflow-agents/sequential-agents.md | 5 +- 8 files changed, 85 insertions(+), 37 deletions(-) diff --git a/docs/agents/config.md b/docs/agents/config.md index 6249931601..151051a6a4 100644 --- a/docs/agents/config.md +++ b/docs/agents/config.md @@ -42,9 +42,10 @@ details on what you must install and configure before you can run agents with the Agent Config files. !!! note - The Agent Config feature currently only supports Gemini models. For more - information about additional; functional restrictions, see - [Known limitations](#known-limitations). + The `model:` key of an Agent Config takes a Gemini model name. To use + another model, construct a `BaseLlm` instance in Python and reference it + from the `model_code:` key. For more information about additional; + functional restrictions, see [Known limitations](#known-limitations). To set up ADK for use with Agent Config: @@ -275,8 +276,10 @@ deployment guides. The Agent Config feature is experimental and includes the following limitations: -- **Model support:** Only Gemini models are currently supported. - Integration with third-party models is in progress. +- **Model support:** The `model:` key only accepts a Gemini model name. To use + another model, such as `LiteLlm`, construct it in Python and reference it + from the `model_code:` key, which takes the fully qualified name of the + `BaseLlm` instance. The `model:` and `model_code:` keys cannot both be set. - **Programming language:** The Agent Config feature currently supports Python and Java code for tools and other functionality requiring programming code. - **ADK Tool support:** The following ADK tools are supported by the Agent diff --git a/docs/agents/custom-agents.md b/docs/agents/custom-agents.md index ce6eb9405e..91169d03cb 100644 --- a/docs/agents/custom-agents.md +++ b/docs/agents/custom-agents.md @@ -69,6 +69,7 @@ The core of any custom agent is the method where you define its unique asynchron * **Signature:** `async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:` * **Asynchronous Generator:** It must be an `async def` function and return an `AsyncGenerator`. This allows it to `yield` events produced by sub-agents or its own logic back to the runner. * **`ctx` (InvocationContext):** Provides access to crucial runtime information, most importantly `ctx.session.state`, which is the primary way to share data between steps orchestrated by your custom agent. + * **Live (audio/video) runs:** The `_run_async_impl` method only covers text-based runs. To support `run_live()`, also override `_run_live_impl`, which has the same signature. Without that override, live runs raise `NotImplementedError`. === "TypeScript" @@ -246,7 +247,7 @@ The core of any custom agent is the method where you define its unique asynchron Typically, a custom agent orchestrates other agents (like `LlmAgent`, `LoopAgent`, etc.). -* **Initialization:** You usually pass instances of these sub-agents into your custom agent's constructor and store them as instance fields/attributes (e.g., `this.story_generator = story_generator_instance` or `self.story_generator = story_generator_instance`). This makes them accessible within the custom agent's core asynchronous execution logic (such as: `_run_async_impl` method). +* **Initialization:** You usually pass instances of these sub-agents into your custom agent's constructor and store them as instance fields/attributes (e.g., `this.story_generator = story_generator_instance` or `self.story_generator = story_generator_instance`). This makes them accessible within the custom agent's core asynchronous execution logic (such as: `_run_async_impl` method). In Python, `BaseAgent` is a Pydantic model configured with `extra='forbid'`, so declare each sub-agent you keep as an annotated class-level field (e.g., `story_generator: LlmAgent`) and pass it to `super().__init__(...)`. Assigning an undeclared attribute in `__init__` raises a `ValueError`. * **Sub Agents List:** When initializing the `BaseAgent` using it's `super()` constructor, you should pass a `sub agents` list. This list tells the ADK framework about the agents that are part of this custom agent's immediate hierarchy. It's important for framework features like lifecycle management, introspection, and potentially future routing capabilities, even if your core execution logic (`_run_async_impl`) calls the agents directly via `self.xxx_agent`. Include the agents that your custom logic directly invokes at the top level. * **State:** As mentioned, `ctx.session.state` is the standard way sub-agents (especially `LlmAgent`s using `output key`) communicate results back to the orchestrator and how the orchestrator passes necessary inputs down. @@ -866,7 +867,9 @@ Allows an [`LlmAgent`](llm-agents.md) to treat another `BaseAgent` instance as a ```python # Conceptual Setup: Agent as a Tool from google.adk.agents import LlmAgent, BaseAgent + from google.adk.events import Event from google.adk.tools import agent_tool + from google.genai import types from pydantic import BaseModel @@ -879,7 +882,7 @@ Allows an [`LlmAgent`](llm-agents.md) to treat another `BaseAgent` instance as a prompt = ctx.session.state.get("image_prompt", "default prompt") # ... generate image bytes ... image_bytes = b"..." - yield Event(author=self.name, content=types.Content(parts=[types.Part.from_bytes(image_bytes, "image/png")])) + yield Event(author=self.name, content=types.Content(parts=[types.Part.from_bytes(data=image_bytes, mime_type="image/png")])) image_agent = ImageGeneratorAgent() diff --git a/docs/agents/llm-agents.md b/docs/agents/llm-agents.md index 799cd09c5c..9525a3b4e5 100644 --- a/docs/agents/llm-agents.md +++ b/docs/agents/llm-agents.md @@ -26,8 +26,8 @@ First, you need to establish what the agent *is* and what it's *for*. `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`. + `customer_support_router`, `billing_inquiry_agent`). In Python, the name must + be a valid Python identifier, and ADK rejects the reserved name `user`. - **`description` (Optional, Recommended for Multi-Agent):** Provide a concise summary of the agent's capabilities. This description is primarily used by @@ -35,11 +35,13 @@ First, you need to establish what the agent *is* and what it's *for*. 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](/agents/models/) page for available options and - considerations. +- **`model` (Optional in Python):** Specify the underlying LLM that will power + this agent's reasoning. This is a string identifier like + `"gemini-flash-latest"`. If you omit `model`, the agent inherits the model of + its nearest ancestor agent, or falls back to ADK's built-in default model + (see [Configure a default model](#configure-a-default-model)). The choice of + model impacts the agent's capabilities, cost, and performance. See the + [Models](/agents/models/) page for available options and considerations. === "Python" @@ -141,7 +143,7 @@ tells the agent: 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 Query: "What's the capital of France?" Example Response: "The capital of France is Paris." """, # tools will be added next @@ -204,7 +206,10 @@ tells the agent: ``` **Note:** For instructions that apply to *all* agents in a system, consider -using `global_instruction` on the root agent. +using `global_instruction` on the root agent. In Python, `global_instruction` +is deprecated and emits a `DeprecationWarning`. Use `GlobalInstructionPlugin` +(from `google.adk.plugins.global_instruction_plugin`) instead, which provides +the same functionality at the App level. ## Equip the agent with tools @@ -220,9 +225,11 @@ calculations, fetch real-time data, or execute specific actions. 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 + - An `AgentTool` wrapping another agent, enabling agent-to-agent delegation - see [Custom agent - workflows](/agents/custom-agents/#delegation)). + workflows](/agents/custom-agents/#delegation). In Python, wrap the agent + as `AgentTool(agent=...)`, or add it to `sub_agents` to delegate without + a tool. The LLM uses the function/tool names, descriptions (from docstrings or the `description` field), and parameter schemas to decide which tool to call based @@ -351,7 +358,10 @@ You can adjust how the underlying AI model generates responses using - **`generate_content_config` (Optional):** Pass an instance of [`google.genai.types.GenerateContentConfig`](https://googleapis.github.io/python-genai/genai.html#genai.types.GenerateContentConfig) to control parameters like `temperature` (randomness), `max_output_tokens` - (response length), `top_p`, `top_k`, and safety settings. + (response length), `top_p`, `top_k`, and safety settings. In Python, ADK + raises a `ValueError` if `generate_content_config` sets `tools`, + `system_instruction`, or `response_schema`. Set those through the agent's + `tools`, `instruction`, and `output_schema` fields instead. === "Python" @@ -459,9 +469,10 @@ 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. + structure when this agent is invoked *as a tool*. In Python, `AgentTool` + validates the calling agent's arguments against the schema and passes the + result as this agent's input. The schema does not apply to user messages sent + directly to the agent. - **`output_schema` (Optional):** Define a schema representing the desired output structure. If set, the agent's final response *must* be a JSON string @@ -502,7 +513,7 @@ schema definitions. 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 + # tools=[...] may be combined with output_schema; see the warning above ) ``` @@ -667,6 +678,7 @@ reasoning and planning before execution. There are two main planners: from google.genai import types my_agent = Agent( + name="my_agent", model="gemini-flash-latest", planner=BuiltInPlanner( thinking_config=types.ThinkingConfig( @@ -688,6 +700,7 @@ reasoning and planning before execution. There are two main planners: from google.adk.planners import PlanReActPlanner my_agent = Agent( + name="my_agent", model="gemini-flash-latest", planner=PlanReActPlanner(), # ... your tools here @@ -813,7 +826,9 @@ agent = LlmAgent( # Session and Runner session_service = InMemorySessionService() -session = session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID) +session = asyncio.run( + 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 diff --git a/docs/agents/managed-agents.md b/docs/agents/managed-agents.md index 0a3d373bef..b807734a2d 100644 --- a/docs/agents/managed-agents.md +++ b/docs/agents/managed-agents.md @@ -34,6 +34,9 @@ back into your ADK flow. Managed agents provide several built-in advantages: provision or secure. - **No client-side function declarations:** Server-side tools are configured on the managed agent, so you don't declare or execute them locally. +- **Composable as an inline tool:** Set `mode='single_turn'` to expose the + managed agent to a parent `LlmAgent` as an inline single-turn tool (the + recommended alternative to wrapping it in `AgentTool`). ## When to use managed agents vs. building your own @@ -42,7 +45,8 @@ mostly a trade-off between out-of-the-box power and fine-grained control. - **Managed agents** give you a powerful agent out of the box, but with limited flexibility. The toolset is predefined and server-side, the agent runs only in - the managed environment, and client-side or MCP tools are not supported. + the managed environment, and client-executed tools are not supported, though + server-side remote MCP servers are. - **ADK agents** (such as [`LlmAgent`](/agents/llm-agents/)) give you fine-grained control over the model, instructions, tools (including custom function tools and MCP tools), and where execution happens. @@ -137,9 +141,10 @@ than in your ADK process. ### Local session vs. remote state `ManagedAgent` keeps almost no state locally. The ADK session persists only two -values on the events it emits: the `previous_interaction_id` and the sandbox +values on the events it emits: the `interaction_id` and the sandbox `environment_id`. On each new turn the agent recovers both by scanning prior -session events, then reuses them so the conversation and its sandbox continue. +session events, then sends them back to the backend so the conversation and its +sandbox continue. Everything else lives server-side. The Managed Agents API owns the sandbox environment and the full interaction history, and that remote interaction, not @@ -154,7 +159,10 @@ state; it never re-sends prior turns. Managed Agents API is currently served only from the `global` location. Regional endpoints raise an error. - **Server-side tools only:** Client-executed tools (Python functions, - callables) and MCP tools are not supported and raise a `NotImplementedError`. + callables) and raw `types.Tool` MCP configs (`types.Tool.mcp_servers`) are not + supported and raise a `NotImplementedError`. Server-side remote MCP servers + are supported: declare them as `google.adk.tools.RemoteMcpServer` specs in + `tools`. - **Streaming only:** The agent uses streaming interactions (`stream=True`). Background-polling execution and strictly non-streaming connections are not yet fully supported. @@ -165,9 +173,15 @@ state; it never re-sends prior turns. ## Next steps - **Samples:** [Managed Agent - Basic](https://github.com/google/adk-python/tree/main/contributing/samples/managed_agent/basic) - and [Managed Agent Code - Execution](https://github.com/google/adk-python/tree/main/contributing/samples/managed_agent/code_execution). + Basic](https://github.com/google/adk-python/tree/main/contributing/samples/managed_agent/basic), + [Managed Agent Code + Execution](https://github.com/google/adk-python/tree/main/contributing/samples/managed_agent/code_execution), + [Managed Agent Remote + MCP](https://github.com/google/adk-python/tree/main/contributing/samples/managed_agent/remote_mcp), + [Managed Agent Single + Turn](https://github.com/google/adk-python/tree/main/contributing/samples/managed_agent/single_turn), + and [Managed Agent System + Instruction](https://github.com/google/adk-python/tree/main/contributing/samples/managed_agent/system_instruction). - **Backend documentation:** [Gemini API Agents](https://ai.google.dev/gemini-api/docs/agents) and [Agent Platform Managed diff --git a/docs/agents/workflow-agents/index.md b/docs/agents/workflow-agents/index.md index 0004855e90..3b06aaaef4 100644 --- a/docs/agents/workflow-agents/index.md +++ b/docs/agents/workflow-agents/index.md @@ -20,6 +20,10 @@ how and when other agents run, defining the control flow of a process. These workflow architectures provide more control, flexibility and capability to evolve your agent workflows over time. + In Python, `SequentialAgent`, `ParallelAgent`, and `LoopAgent` are + deprecated: constructing one emits a `DeprecationWarning`, and the classes + will be removed in a future release. + Template agent workflows in ADK **Figure 1.** Execution patterns of template workflows in ADK diff --git a/docs/agents/workflow-agents/loop-agents.md b/docs/agents/workflow-agents/loop-agents.md index 9a692893c1..bbb67857c2 100644 --- a/docs/agents/workflow-agents/loop-agents.md +++ b/docs/agents/workflow-agents/loop-agents.md @@ -22,6 +22,9 @@ the ***LoopAgent*** object you define. [graph-based workflows](/graphs/) and [dynamic workflows](/graphs/dynamic/). + In Python, `LoopAgent` is deprecated: constructing one emits a + `DeprecationWarning`, and the class will be removed in a future release. + ### Example scenario You want to build an agent that can generate images of food, but sometimes when @@ -41,8 +44,8 @@ When the `LoopAgent`'s `Run Async` method is called, it performs the following a _Crucially_, the `LoopAgent` itself does _not_ inherently decide when to stop looping. You _must_ implement a termination mechanism to prevent infinite loops. Common strategies include: - * **Max Iterations**: Set a maximum number of iterations in the `LoopAgent`. **The loop will terminate after that many iterations**. - * **Escalation from sub-agent**: Design one or more sub-agents to evaluate a condition (e.g., "Is the document quality good enough?", "Has a consensus been reached?"). If the condition is met, the sub-agent can signal termination (e.g., by raising a custom event, setting a flag in a shared context, or returning a specific value). + * **Max Iterations**: Set a maximum number of iterations in the `LoopAgent`. **The loop will terminate after that many iterations**. In Python, `max_iterations` defaults to `None`, which means the loop only stops on escalation. + * **Escalation from sub-agent**: Design one or more sub-agents to evaluate a condition (e.g., "Is the document quality good enough?", "Has a consensus been reached?"). If the condition is met, the sub-agent signals termination by emitting an event whose actions set `escalate` to true. In Python, a custom agent yields an `Event` with `actions=EventActions(escalate=True)`, while a callback or tool sets `ctx.actions.escalate = True`. The built-in `exit_loop` tool sets that flag for you. ![Loop Agent](/assets/loop-agent.png) @@ -57,7 +60,7 @@ Imagine a scenario where you want to iteratively improve a document: LoopAgent(sub_agents=[WriterAgent, CriticAgent], max_iterations=5) ``` -In this setup, the `LoopAgent` would manage the iterative process. The `CriticAgent` could be **designed to return a "STOP" signal when the document reaches a satisfactory quality level**, preventing further iterations. Alternatively, the `max iterations` parameter could be used to limit the process to a fixed number of cycles, or external logic could be implemented to make stop decisions. The **loop would run at most five times**, ensuring the iterative refinement doesn't continue indefinitely. +In this setup, the `LoopAgent` would manage the iterative process. The `CriticAgent` could be **given an escalation tool such as `exit_loop`, which it calls once the document reaches a satisfactory quality level**, preventing further iterations. Alternatively, the `max_iterations` parameter could be used to limit the process to a fixed number of cycles. The **loop would run at most five times**, ensuring the iterative refinement doesn't continue indefinitely. ???+ "Full Code" diff --git a/docs/agents/workflow-agents/parallel-agents.md b/docs/agents/workflow-agents/parallel-agents.md index b80217bed6..3061e370bf 100644 --- a/docs/agents/workflow-agents/parallel-agents.md +++ b/docs/agents/workflow-agents/parallel-agents.md @@ -29,19 +29,22 @@ ultimately managed by the ***ParallelAgent*** object you define. [graph-based workflows](/graphs/) and [dynamic workflows](/graphs/dynamic/). + In Python, `ParallelAgent` is deprecated: constructing one emits a + `DeprecationWarning`, and the class will be removed in a future release. + ### How it works When the `ParallelAgent`'s `run_async()` method is called: 1. **Concurrent Execution:** It initiates the `run_async()` method of *each* sub-agent present in the `sub_agents` list *concurrently*. This means all the agents start running at (approximately) the same time. -2. **Independent Branches:** Each sub-agent operates in its own execution branch. There is ***no* automatic sharing of conversation history or state between these branches** during execution. +2. **Independent Branches:** Each sub-agent operates in its own execution branch, and there is ***no* automatic sharing of conversation history between these branches** during execution. A sub-agent sees only the events on its own branch. Session state is shared: all branches run against the same session, so a value one sub-agent writes (e.g., through `output_key`) is visible to the other branches and to later agents. 3. **Result Collection:** The `ParallelAgent` manages the parallel execution and, typically, provides a way to access the results from each sub-agent after they have completed (e.g., through a list of results or events). The order of results may not be deterministic. ### Independent Execution and State Management It's *crucial* to understand that sub-agents within a `ParallelAgent` run independently. If you *need* communication or data sharing between these agents, you must implement it explicitly. Possible approaches include: -* **Shared `InvocationContext`:** You could pass a shared `InvocationContext` object to each sub-agent. This object could act as a shared data store. However, you'd need to manage concurrent access to this shared context carefully (e.g., using locks) to avoid race conditions. +* **Shared session state:** All branches run against the same session, so give each sub-agent an `output_key` and read those keys from a later agent's instruction. Because the branches run concurrently, do not rely on one branch reading a key another branch is still writing. * **External State Management:** Use an external database, message queue, or other mechanism to manage shared state and facilitate communication between agents. * **Post-Processing:** Collect results from each branch, and then implement logic to coordinate data afterwards. diff --git a/docs/agents/workflow-agents/sequential-agents.md b/docs/agents/workflow-agents/sequential-agents.md index 9d9e6cf649..ea972ed7bb 100644 --- a/docs/agents/workflow-agents/sequential-agents.md +++ b/docs/agents/workflow-agents/sequential-agents.md @@ -22,6 +22,9 @@ object you define. [graph-based workflows](/graphs/) and [dynamic workflows](/graphs/dynamic/). + In Python, `SequentialAgent` is deprecated: constructing one emits a + `DeprecationWarning`, and the class will be removed in a future release. + ### Example scenario You want to build an agent that can summarize any webpage, using two tools: @@ -59,7 +62,7 @@ in the following code snippet: SequentialAgent(sub_agents=[CodeWriterAgent, CodeReviewerAgent, CodeRefactorerAgent]) ``` -This ensures the code is written, *then* reviewed, and *finally* refactored, in a strict, dependable order. **The output from each sub-agent is passed to the next by storing them in state via [Output Key](/agents/llm-agents/##data-handling)**. +This ensures the code is written, *then* reviewed, and *finally* refactored, in a strict, dependable order. **The output from each sub-agent is passed to the next by storing them in state via [Output Key](/agents/llm-agents/#data-handling)**. ???+ "Code" From 0c2ed1e3062a89c3eae86ab4551c1c60d766829f Mon Sep 17 00:00:00 2001 From: George Weale Date: Thu, 30 Jul 2026 00:22:21 +0000 Subject: [PATCH 2/2] docs(agents): fix output_schema guidance, BaseAgent sample, deprecation notes --- docs/agents/config.md | 13 +++- docs/agents/custom-agents.md | 26 ++++++-- docs/agents/llm-agents.md | 64 +++++++++++-------- docs/agents/workflow-agents/index.md | 4 +- docs/agents/workflow-agents/loop-agents.md | 9 +-- .../agents/workflow-agents/parallel-agents.md | 9 +-- .../workflow-agents/sequential-agents.md | 7 +- 7 files changed, 85 insertions(+), 47 deletions(-) diff --git a/docs/agents/config.md b/docs/agents/config.md index 151051a6a4..ee18d45a2f 100644 --- a/docs/agents/config.md +++ b/docs/agents/config.md @@ -149,6 +149,10 @@ For more information about the ADK command line options, see the You can also bypass the CLI and dynamically load and execute a configuration-based agent directly in your code. The utility loads the configuration and instantiates the proper agent class (such as `LlmAgent`) transparently as a `BaseAgent` subclass. +!!! note + In Python, `config_agent_utils.from_config` is deprecated: calling it emits + a `DeprecationWarning`, and it will be removed in a future version. + === "Python" ```python @@ -278,8 +282,13 @@ limitations: - **Model support:** The `model:` key only accepts a Gemini model name. To use another model, such as `LiteLlm`, construct it in Python and reference it - from the `model_code:` key, which takes the fully qualified name of the - `BaseLlm` instance. The `model:` and `model_code:` keys cannot both be set. + from the `model_code:` key, which takes a mapping whose `name:` sub-key is + the fully qualified name of the `BaseLlm` instance: + + model_code: + name: my_library.clients.my_litellm + + The `model:` and `model_code:` keys cannot both be set. - **Programming language:** The Agent Config feature currently supports Python and Java code for tools and other functionality requiring programming code. - **ADK Tool support:** The following ADK tools are supported by the Agent diff --git a/docs/agents/custom-agents.md b/docs/agents/custom-agents.md index 91169d03cb..6a01b960c5 100644 --- a/docs/agents/custom-agents.md +++ b/docs/agents/custom-agents.md @@ -277,12 +277,25 @@ The foundation for structuring multi-agent systems is the parent-child relations ```python # Conceptual Example: Defining Hierarchy + from typing import AsyncGenerator + from google.adk.agents import LlmAgent, BaseAgent + from google.adk.agents.invocation_context import InvocationContext + from google.adk.events import Event + + + # A custom non-LLM agent must implement _run_async_impl; instantiating + # BaseAgent directly raises NotImplementedError when it runs. + class TaskExecutor(BaseAgent): + async def _run_async_impl( + self, ctx: InvocationContext + ) -> AsyncGenerator[Event, None]: + yield Event(author=self.name) # Define individual agents greeter = LlmAgent(name="Greeter", model="gemini-flash-latest") - task_doer = BaseAgent(name="TaskExecutor") # Custom non-LLM agent + task_doer = TaskExecutor(name="TaskExecutor") # Custom non-LLM agent # Create parent agent and assign children via sub_agents @@ -394,6 +407,12 @@ The foundation for structuring multi-agent systems is the parent-child relations ADK includes specialized agents derived from `BaseAgent` that don't perform tasks themselves but orchestrate the execution flow of their `sub_agents`. +!!! note "Deprecated in Python" + + In Python, `SequentialAgent`, `ParallelAgent`, and `LoopAgent` are + deprecated in favor of `Workflow`: constructing one emits a + `DeprecationWarning`, and the classes will be removed in a future release. + * **[`SequentialAgent`](workflow-agents/sequential-agents.md):** Executes its `sub_agents` one after another in the order they are listed. * **Context:** Passes the *same* [`InvocationContext`](../runtime/index.md) sequentially, allowing agents to easily pass results via shared state. @@ -663,7 +682,7 @@ Agents within a system often need to exchange data or trigger actions in one ano The most fundamental way for agents operating within the same invocation (and thus sharing the same [`Session`](/sessions/session/) object via the `InvocationContext`) to communicate passively. -* **Mechanism:** One agent (or its tool/callback) writes a value (`context.state['data_key'] = processed_data`), and a subsequent agent reads it (`data = context.state.get('data_key')`). State changes are tracked via [`CallbackContext`](../callbacks/index.md). +* **Mechanism:** One agent (or its tool/callback) writes a value (`context.state['data_key'] = processed_data`), and a subsequent agent reads it (`data = context.state.get('data_key')`). In Python, [`CallbackContext`](../callbacks/index.md) and `ToolContext` are the same delta-aware context class, and every write to `context.state` is recorded as a `state_delta` on the event the agent emits. * **Convenience:** The `output_key` property on [`LlmAgent`](llm-agents.md) automatically saves the agent's final response text (or structured output) to the specified state key. * **Nature:** Asynchronous, passive communication. Ideal for pipelines orchestrated by `SequentialAgent` or passing data across `LoopAgent` iterations. * **See Also:** [State Management](../sessions/state.md) @@ -870,7 +889,6 @@ Allows an [`LlmAgent`](llm-agents.md) to treat another `BaseAgent` instance as a from google.adk.events import Event from google.adk.tools import agent_tool from google.genai import types - from pydantic import BaseModel # Define a target agent (could be LlmAgent or custom BaseAgent) @@ -1157,7 +1175,7 @@ These are standard `LlmAgent` definitions, responsible for specific tasks. Their === "Python" ```python - GEMINI_2_FLASH = "gemini-flash-latest" # Define model constant + GEMINI_2_FLASH = "gemini-2.0-flash" # Define model constant --8<-- "examples/python/snippets/agents/custom-agent/storyflow_agent.py:llmagents" ``` diff --git a/docs/agents/llm-agents.md b/docs/agents/llm-agents.md index 9525a3b4e5..5ea3920d18 100644 --- a/docs/agents/llm-agents.md +++ b/docs/agents/llm-agents.md @@ -36,9 +36,11 @@ First, you need to establish what the agent *is* and what it's *for*. inquiries about current billing statements," not just "Billing agent"). - **`model` (Optional in Python):** Specify the underlying LLM that will power - this agent's reasoning. This is a string identifier like - `"gemini-flash-latest"`. If you omit `model`, the agent inherits the model of - its nearest ancestor agent, or falls back to ADK's built-in default model + this agent's reasoning. This is either a string identifier like + `"gemini-flash-latest"` or a `BaseLlm` instance. If you omit `model`, the + agent inherits the model of its nearest `LlmAgent` ancestor — a + `SequentialAgent`, `ParallelAgent`, or custom `BaseAgent` parent is skipped + over, not consulted — or falls back to ADK's built-in default model (see [Configure a default model](#configure-a-default-model)). The choice of model impacts the agent's capabilities, cost, and performance. See the [Models](/agents/models/) page for available options and considerations. @@ -125,7 +127,9 @@ tells the agent: - 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. +- `{artifact.var}` is used to insert the artifact named var. The artifact is + loaded as a `google.genai.types.Part` and inserted as `str(part)`, which + renders every field of the part, not just its text content. - 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?}`. @@ -163,7 +167,7 @@ tells the agent: 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 Query: "What's the capital of France?" Example Response: "The capital of France is Paris." `, // tools will be added next @@ -192,7 +196,7 @@ tells the agent: 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 Query: "What's the capital of France?" Example Response: "The capital of France is Paris." """) // tools will be added next @@ -360,8 +364,9 @@ You can adjust how the underlying AI model generates responses using to control parameters like `temperature` (randomness), `max_output_tokens` (response length), `top_p`, `top_k`, and safety settings. In Python, ADK raises a `ValueError` if `generate_content_config` sets `tools`, - `system_instruction`, or `response_schema`. Set those through the agent's - `tools`, `instruction`, and `output_schema` fields instead. + `system_instruction`, `response_schema`, or `http_options.base_url`. Set the + first three through the agent's `tools`, `instruction`, and `output_schema` + fields instead, and set the base URL on the model or its client. === "Python" @@ -478,22 +483,26 @@ schema definitions. 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, workarounds using [function - tools](https://github.com/google/adk-python/blob/main/src/google/adk/flows/llm_flows/_output_schema_processor.py)) - in ADK 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. +!!! note "Using `output_schema` with `tools`" + + In Python, ADK supports `output_schema` and `tools` on the same agent: the + tools stay available during the agent's thought loop, and the schema is + enforced only on the final response. When the model accepts both in a single + request — a `LiteLlm` model, or Gemini 2.0 and later on Vertex AI — ADK + passes them through directly. Otherwise ADK adds an internal + `set_model_response` tool and instructs the model to return its final answer + through that tool. See + [`_output_schema_processor.py`](https://github.com/google/adk-python/blob/main/src/google/adk/flows/llm_flows/_output_schema_processor.py). + +- **`output_key` (Optional):** Provide a string key. If set, 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` + agent_response_text`. If `output_schema` is also set, ADK parses the + response against the schema first and stores the parsed value — a `dict` + for a `BaseModel` schema, a `list[dict]` for `list[BaseModel]` — rather + than the response text. - In Java: `session.state().put(outputKey, agentResponseText)` - In Golang, within a callback handler: `ctx.State().Set(output_key, agentResponseText)` @@ -513,7 +522,7 @@ schema definitions. 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'] - # tools=[...] may be combined with output_schema; see the warning above + # tools=[...] may be combined with output_schema; see the note above ) ``` @@ -541,7 +550,6 @@ schema definitions. 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 }); ``` @@ -578,7 +586,6 @@ schema definitions. "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(); ``` @@ -838,7 +845,7 @@ def call_agent(query): for event in events: print(f"\nDEBUG EVENT: {event}\n") - if event.is_final_response() and event.content: + if event.is_final_response() and event.content and event.content.parts: final_answer = event.content.parts[0].text.strip() print("\n🟢 FINAL ANSWER\n", final_answer, "\n") @@ -919,6 +926,7 @@ the following: 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 shared instructions (`global_instruction`). See [Multi-agent + and shared instructions (`global_instruction`, which is deprecated in Python + in favor of `GlobalInstructionPlugin`). See [Multi-agent workflows](/workflows/) and [collaborative agent teams](/workflows/collaboration/). diff --git a/docs/agents/workflow-agents/index.md b/docs/agents/workflow-agents/index.md index 3b06aaaef4..aaac65a1aa 100644 --- a/docs/agents/workflow-agents/index.md +++ b/docs/agents/workflow-agents/index.md @@ -21,8 +21,8 @@ how and when other agents run, defining the control flow of a process. and capability to evolve your agent workflows over time. In Python, `SequentialAgent`, `ParallelAgent`, and `LoopAgent` are - deprecated: constructing one emits a `DeprecationWarning`, and the classes - will be removed in a future release. + deprecated in favor of `Workflow`: constructing one emits a + `DeprecationWarning`, and the classes will be removed in a future release. Template agent workflows in ADK diff --git a/docs/agents/workflow-agents/loop-agents.md b/docs/agents/workflow-agents/loop-agents.md index bbb67857c2..768279e484 100644 --- a/docs/agents/workflow-agents/loop-agents.md +++ b/docs/agents/workflow-agents/loop-agents.md @@ -22,8 +22,9 @@ the ***LoopAgent*** object you define. [graph-based workflows](/graphs/) and [dynamic workflows](/graphs/dynamic/). - In Python, `LoopAgent` is deprecated: constructing one emits a - `DeprecationWarning`, and the class will be removed in a future release. + In Python, `LoopAgent` is deprecated in favor of `Workflow`: constructing + one emits a `DeprecationWarning`, and the class will be removed in a future + release. ### Example scenario @@ -45,7 +46,7 @@ When the `LoopAgent`'s `Run Async` method is called, it performs the following a _Crucially_, the `LoopAgent` itself does _not_ inherently decide when to stop looping. You _must_ implement a termination mechanism to prevent infinite loops. Common strategies include: * **Max Iterations**: Set a maximum number of iterations in the `LoopAgent`. **The loop will terminate after that many iterations**. In Python, `max_iterations` defaults to `None`, which means the loop only stops on escalation. - * **Escalation from sub-agent**: Design one or more sub-agents to evaluate a condition (e.g., "Is the document quality good enough?", "Has a consensus been reached?"). If the condition is met, the sub-agent signals termination by emitting an event whose actions set `escalate` to true. In Python, a custom agent yields an `Event` with `actions=EventActions(escalate=True)`, while a callback or tool sets `ctx.actions.escalate = True`. The built-in `exit_loop` tool sets that flag for you. + * **Escalation from sub-agent**: Design one or more sub-agents to evaluate a condition (e.g., "Is the document quality good enough?", "Has a consensus been reached?"). If the condition is met, the sub-agent signals termination by emitting an event whose actions set `escalate` to true. In Python, a custom agent yields an `Event` with `actions=EventActions(escalate=True)`, while a callback sets `callback_context.actions.escalate = True` and a tool sets `tool_context.actions.escalate = True`. The built-in `exit_loop` tool sets that flag for you. ![Loop Agent](/assets/loop-agent.png) @@ -57,7 +58,7 @@ Imagine a scenario where you want to iteratively improve a document: * **Critic Agent:** An `LlmAgent` that critiques the draft, identifying areas for improvement. ```py - LoopAgent(sub_agents=[WriterAgent, CriticAgent], max_iterations=5) + LoopAgent(name="RefinementLoop", sub_agents=[WriterAgent, CriticAgent], max_iterations=5) ``` In this setup, the `LoopAgent` would manage the iterative process. The `CriticAgent` could be **given an escalation tool such as `exit_loop`, which it calls once the document reaches a satisfactory quality level**, preventing further iterations. Alternatively, the `max_iterations` parameter could be used to limit the process to a fixed number of cycles. The **loop would run at most five times**, ensuring the iterative refinement doesn't continue indefinitely. diff --git a/docs/agents/workflow-agents/parallel-agents.md b/docs/agents/workflow-agents/parallel-agents.md index 3061e370bf..f829877ff3 100644 --- a/docs/agents/workflow-agents/parallel-agents.md +++ b/docs/agents/workflow-agents/parallel-agents.md @@ -29,8 +29,9 @@ ultimately managed by the ***ParallelAgent*** object you define. [graph-based workflows](/graphs/) and [dynamic workflows](/graphs/dynamic/). - In Python, `ParallelAgent` is deprecated: constructing one emits a - `DeprecationWarning`, and the class will be removed in a future release. + In Python, `ParallelAgent` is deprecated in favor of `Workflow`: + constructing one emits a `DeprecationWarning`, and the class will be removed + in a future release. ### How it works @@ -38,7 +39,7 @@ When the `ParallelAgent`'s `run_async()` method is called: 1. **Concurrent Execution:** It initiates the `run_async()` method of *each* sub-agent present in the `sub_agents` list *concurrently*. This means all the agents start running at (approximately) the same time. 2. **Independent Branches:** Each sub-agent operates in its own execution branch, and there is ***no* automatic sharing of conversation history between these branches** during execution. A sub-agent sees only the events on its own branch. Session state is shared: all branches run against the same session, so a value one sub-agent writes (e.g., through `output_key`) is visible to the other branches and to later agents. -3. **Result Collection:** The `ParallelAgent` manages the parallel execution and, typically, provides a way to access the results from each sub-agent after they have completed (e.g., through a list of results or events). The order of results may not be deterministic. +3. **Result Collection:** The `ParallelAgent` merges the sub-agents' event streams and yields each event as it is produced; it does not gather the results into a list you can read afterwards. Because the branches interleave, the event order is not deterministic. To use a sub-agent's result, read it from the shared session state — for example, the key it was given through `output_key`. ### Independent Execution and State Management @@ -59,7 +60,7 @@ Imagine researching multiple topics simultaneously: 3. **Researcher Agent 3:** An `LlmAgent` that researches "carbon capture methods." ```py - ParallelAgent(sub_agents=[ResearcherAgent1, ResearcherAgent2, ResearcherAgent3]) + ParallelAgent(name="ParallelWebResearchAgent", sub_agents=[ResearcherAgent1, ResearcherAgent2, ResearcherAgent3]) ``` These research tasks are independent. Using a `ParallelAgent` allows them to run concurrently, potentially reducing the total research time significantly compared to running them sequentially. The results from each agent would be collected separately after they finish. diff --git a/docs/agents/workflow-agents/sequential-agents.md b/docs/agents/workflow-agents/sequential-agents.md index ea972ed7bb..ad5cddee7b 100644 --- a/docs/agents/workflow-agents/sequential-agents.md +++ b/docs/agents/workflow-agents/sequential-agents.md @@ -22,8 +22,9 @@ object you define. [graph-based workflows](/graphs/) and [dynamic workflows](/graphs/dynamic/). - In Python, `SequentialAgent` is deprecated: constructing one emits a - `DeprecationWarning`, and the class will be removed in a future release. + In Python, `SequentialAgent` is deprecated in favor of `Workflow`: + constructing one emits a `DeprecationWarning`, and the class will be removed + in a future release. ### Example scenario @@ -59,7 +60,7 @@ Using a `SequentialAgent` makes it simple to define this exection flow, as shown in the following code snippet: ```py -SequentialAgent(sub_agents=[CodeWriterAgent, CodeReviewerAgent, CodeRefactorerAgent]) +SequentialAgent(name="CodePipelineAgent", sub_agents=[CodeWriterAgent, CodeReviewerAgent, CodeRefactorerAgent]) ``` This ensures the code is written, *then* reviewed, and *finally* refactored, in a strict, dependable order. **The output from each sub-agent is passed to the next by storing them in state via [Output Key](/agents/llm-agents/#data-handling)**.