mirror of
https://github.com/google/adk-docs.git
synced 2026-09-14 16:16:59 +08:00
c75e61f5f3
The site rendered one language under two names. Tab labels were split 137 `TypeScript` / 53 `Typescript`, with three pages carrying both spellings at once (custom-agents.md 7/7, patterns.md 1/7, function-tools.md 4/1), and the language-support badges were split 67/18 the same way. Because pymdownx.tabbed slugifies tab labels to lowercase, both variants rendered and linked fine, so no link check or build warning ever flagged it -- it was visible only to readers, as two names for one SDK. Every user-visible occurrence is normalized to `TypeScript`, plus the two inconsistencies that turned up while doing it. 80 changed lines, accounted for exactly: 53 tab label === "Typescript" -> === "TypeScript" 18 badge span lst-typescript">Typescript -> TypeScript 3 prose mention cloud-run.md, mcp-tools.md, workflows/patterns.md 2 api-reference/index.md card heading and link text 1 badge div attr title="...Python and Typescript." 1 mkdocs.yml nav Typescript ADK -> TypeScript ADK 1 code fence ```javascript -> ```typescript on a .ts include 1 artifacts/index.md closing summary sentence --- 80 The first six rows are pure casing: 78 lines that differ from their originals by nothing but `Typescript` -> `TypeScript`. The last two are not, and are the reason this is not a `sed`: llm-agents.md:872 fenced `--8<-- ".../capital_agent.ts"` as ```javascript. It was the only javascript-fenced `.ts` include in docs/ (the other 189 TypeScript fences are correct), and it cost that one snippet its TypeScript highlighting. artifacts/index.md:1084 closed the page by naming languages and got the list wrong. It described reaching the artifact methods "using Python's context objects or directly interacting with the `BaseArtifactService` in Java" -- a two-language enumeration at the end of a page that carries Python, TypeScript, Go, Java and Kotlin tabs (11/10/10/10/11), and one that contradicts :556, which correctly names four of them. The enumeration is dropped rather than extended: the sentence now describes the two ways to reach these methods -- through the context object, or through `BaseArtifactService` -- which is what the page actually teaches and does not rot when a sixth language is added. docs/api-reference/index.md is included even though the rest of docs/api-reference/ is generated output that must not be touched. That tree holds 3,140 generated HTML files and exactly one hand-authored page: this one. It is Markdown, it is the only api-reference entry mkdocs.yml lists as `.md` rather than `index.html` (:272, :441), it uses Material `grid cards` and `:fontawesome-*:` shortcodes, and it carries a `CONTRIBUTORS:` note citing issues #1716 and #1717. Its TypeScript card already said "TypeScript" twice in its body text while its heading and link text said "Typescript"; those two are now consistent with the body. No generated file is modified. Not in this change: the broken `SseConnectionParams` sample in mcp-tools.md (docs-ts/p6c-mcp-ts-sample) and the `@google/adk` example version bumps (docs-ts/p6b-example-versions). Only the casing of the prose line above that sample is touched here. Verified: `mkdocs build` exits 0 with an empty warning set on both main and this branch, and the two warning sets are identical. A rendered before/after diff of the whole site shows every `__tabbed_*` id, every tab radio id and every heading anchor unchanged. Zero `=== "Typescript"` and zero `lst-typescript">Typescript` remain anywhere in the repo. Co-authored-by: Amaad Martin <amaadmartin@google.com>
900 lines
32 KiB
Markdown
900 lines
32 KiB
Markdown
# Function tools
|
||
|
||
<div class="language-support-tag">
|
||
<span class="lst-supported">Supported in ADK</span><span class="lst-python">Python v0.1.0</span><span class="lst-typescript">TypeScript v0.2.0</span><span class="lst-go">Go v0.1.0</span><span class="lst-java">Java v0.1.0</span><span class="lst-kotlin">Kotlin v0.1.0</span>
|
||
</div>
|
||
|
||
When pre-built ADK tools don't meet your requirements, you can create custom
|
||
*function tools*. Building function tools allows you to create tailored
|
||
functionality, such as connecting to proprietary databases or implementing
|
||
unique algorithms. For example, a function tool, `myfinancetool`, might be a
|
||
function that calculates a specific financial metric. ADK also supports
|
||
long-running functions, so if that calculation takes a while, the agent can
|
||
continue working on other tasks.
|
||
|
||
ADK offers several ways to create functions tools, each suited to different
|
||
levels of complexity and control:
|
||
|
||
- [Function tools](#function-tool)
|
||
- [Long running function tools](#long-run-tool)
|
||
- [Agent-as-a-Tool](#agent-tool)
|
||
|
||
## Function tools {#function-tool}
|
||
|
||
Transforming a Python function into a tool is a straightforward way to integrate
|
||
custom logic into your agents. When you assign a function to an agent’s `tools`
|
||
list, the framework automatically wraps it as a `FunctionTool`.
|
||
|
||
### How it works
|
||
|
||
The ADK framework automatically inspects your Python function's
|
||
signature—including its name, docstring, parameters, type hints, and default
|
||
values—to generate a schema. This schema is what the LLM uses to understand the
|
||
tool's purpose, when to use it, and what arguments it requires.
|
||
|
||
### Define function signatures
|
||
|
||
A well-defined function signature is crucial for the LLM to use your tool
|
||
correctly.
|
||
|
||
#### Parameters
|
||
|
||
##### Required parameters
|
||
|
||
=== "Python"
|
||
|
||
A parameter is considered **required** if it has a type hint but **no
|
||
default value**. The LLM must provide a value for this argument when it
|
||
calls the tool. The parameter's description is taken from the function's
|
||
docstring.
|
||
|
||
???+ "Example: Required Parameters"
|
||
```python
|
||
def get_weather(city: str, unit: str):
|
||
"""
|
||
Retrieves the weather for a city in the specified unit.
|
||
|
||
Args:
|
||
city (str): The city name.
|
||
unit (str): The temperature unit, either 'Celsius' or 'Fahrenheit'.
|
||
"""
|
||
# ... function logic ...
|
||
return {"status": "success", "report": f"Weather for {city} is sunny."}
|
||
```
|
||
|
||
In this example, both `city` and `unit` are mandatory. If the LLM tries to
|
||
call `get_weather` without one of them, the ADK will return an error to the
|
||
LLM, prompting it to correct the call.
|
||
|
||
=== "Go"
|
||
|
||
In Go, you use struct tags to control the JSON schema. The two primary tags
|
||
are `json` and `jsonschema`.
|
||
|
||
A parameter is considered **required** if its struct field does **not** have
|
||
the `omitempty` or `omitzero` option in its `json` tag.
|
||
|
||
The `jsonschema` tag is used to provide the argument's description. This is
|
||
crucial for the LLM to understand what the argument is for.
|
||
|
||
???+ "Example: Required Parameters"
|
||
```go
|
||
// GetWeatherParams defines the arguments for the getWeather tool.
|
||
type GetWeatherParams struct {
|
||
// This field is REQUIRED (no "omitempty").
|
||
// The jsonschema tag provides the description.
|
||
Location string `json:"location" jsonschema:"The city and state, e.g., San Francisco, CA"`
|
||
|
||
// This field is also REQUIRED.
|
||
Unit string `json:"unit" jsonschema:"The temperature unit, either 'celsius' or 'fahrenheit'"`
|
||
}
|
||
```
|
||
|
||
In this example, both `location` and `unit` are mandatory.
|
||
|
||
=== "Java"
|
||
|
||
In Java, primitive types (e.g., `int`, `double`, `boolean`) are inherently
|
||
**required** because they cannot be null. For object types (like `String` or
|
||
`Integer`), they are typically considered required unless explicitly marked
|
||
as optional.
|
||
|
||
The `@Schema` annotation is used to provide the argument's description and
|
||
can explicitly define parameter properties. This is crucial for the LLM to
|
||
understand what the argument is for.
|
||
|
||
???+ "Example: Required Parameters"
|
||
```java
|
||
// The @Schema annotation on the parameter provides the description.
|
||
public static Map<String, Object> getWeather(
|
||
@Schema(description = "The city and state, e.g., San Francisco, CA", name = "location")
|
||
String location,
|
||
|
||
@Schema(description = "The temperature unit, either 'Celsius' or 'Fahrenheit'", name = "unit")
|
||
String unit) {
|
||
|
||
// ... function logic ...
|
||
return Map.of("status", "success", "report", "Weather for " + location + " is sunny.");
|
||
}
|
||
```
|
||
|
||
In this example, both `location` and `unit` are mandatory.
|
||
|
||
=== "Kotlin"
|
||
|
||
In Kotlin, parameters are considered **required** by default if they are of
|
||
a non-nullable type and have no default value. The LLM must provide a value
|
||
for these arguments.
|
||
|
||
The `@Param` annotation is used to provide the argument's description. This
|
||
is crucial for the LLM to understand what the argument is for.
|
||
|
||
???+ "Example: Required Parameters"
|
||
```kotlin
|
||
--8<-- "examples/kotlin/snippets/tools/function-tools/RequiredParams.kt:required_params"
|
||
```
|
||
|
||
In this example, both `location` and `unit` are mandatory.
|
||
|
||
##### Optional parameters
|
||
|
||
=== "Python"
|
||
|
||
A parameter is considered **optional** if you provide a **default value**.
|
||
This is the standard Python way to define optional arguments. You can also
|
||
mark a parameter as optional using `typing.Optional[SomeType]` or the `|
|
||
None` syntax (Python 3.10+).
|
||
|
||
Use defaults only for values that are truly optional. Do not add defaults
|
||
for information the model should derive from the user request or ask the
|
||
user to provide.
|
||
|
||
???+ "Example: Optional Parameters"
|
||
```python
|
||
def search_flights(destination: str, departure_date: str, flexible_days: int = 0):
|
||
"""
|
||
Searches for flights.
|
||
|
||
Args:
|
||
destination (str): The destination city.
|
||
departure_date (str): The desired departure date.
|
||
flexible_days (int, optional): Number of flexible days for the search. Defaults to 0.
|
||
"""
|
||
# ... function logic ...
|
||
if flexible_days > 0:
|
||
return {"status": "success", "report": f"Found flexible flights to {destination}."}
|
||
return {"status": "success", "report": f"Found flights to {destination} on {departure_date}."}
|
||
```
|
||
|
||
Here, `flexible_days` is optional. The LLM can choose to provide it, but
|
||
it's not required.
|
||
|
||
=== "Go"
|
||
|
||
A parameter is considered **optional** if its struct field has the
|
||
`omitempty` or `omitzero` option in its `json` tag.
|
||
|
||
???+ "Example: Optional Parameters"
|
||
```go
|
||
// GetWeatherParams defines the arguments for the getWeather tool.
|
||
type GetWeatherParams struct {
|
||
// Location is required.
|
||
Location string `json:"location" jsonschema:"The city and state, e.g., San Francisco, CA"`
|
||
|
||
// Unit is optional.
|
||
Unit string `json:"unit,omitempty" jsonschema:"The temperature unit, either 'celsius' or 'fahrenheit'"`
|
||
|
||
// Days is optional.
|
||
Days int `json:"days,omitzero" jsonschema:"The number of forecast days to return (defaults to 1)"`
|
||
}
|
||
```
|
||
Here, `unit` and `days` are optional. The LLM can choose to provide them,
|
||
but they are not required.
|
||
|
||
=== "Java"
|
||
|
||
A parameter can be considered **optional** in Java by using object types
|
||
that allow `null` values (such as `Integer` instead of `int`), or by
|
||
explicitly defining it as optional using `java.util.Optional`.
|
||
|
||
???+ "Example: Optional Parameters"
|
||
```java
|
||
import java.util.Map;
|
||
import java.util.Optional;
|
||
|
||
public static Map<String, Object> searchFlights(
|
||
@Schema(description = "The destination city.", name = "destination")
|
||
String destination,
|
||
|
||
@Schema(description = "The desired departure date.", name = "departureDate")
|
||
String departureDate,
|
||
|
||
@Schema(description = "Number of flexible days for the search. Defaults to 0.", name = "flexibleDays")
|
||
Optional<Integer> flexibleDays) {
|
||
|
||
// ... function logic ...
|
||
int days = flexibleDays.orElse(0);
|
||
if (days > 0) {
|
||
return Map.of("status", "success", "report", "Found flexible flights to " + destination + ".");
|
||
}
|
||
return Map.of("status", "success", "report", "Found flights to " + destination + " on " + departureDate + ".");
|
||
}
|
||
```
|
||
|
||
Here, `flexibleDays` is optional. The LLM can choose to provide it, but it's
|
||
not required.
|
||
|
||
=== "Kotlin"
|
||
|
||
In Kotlin, a parameter is considered **optional** if it is of a **nullable
|
||
type** or if it has a **default value**.
|
||
|
||
???+ "Example: Optional Parameters"
|
||
```kotlin
|
||
--8<-- "examples/kotlin/snippets/tools/function-tools/OptionalParams.kt:optional_params"
|
||
```
|
||
|
||
Here, `flexibleDays` is optional. The LLM can choose to provide it, but it's
|
||
not required.
|
||
|
||
##### Optional parameters with `typing.Optional`
|
||
|
||
You can also mark a parameter as optional using `typing.Optional[SomeType]` or
|
||
the `| None` syntax (Python 3.10+). This signals that the parameter can be
|
||
`None`. When combined with a default value of `None`, it behaves as a standard
|
||
optional parameter.
|
||
|
||
???+ "Example: `typing.Optional`"
|
||
=== "Python"
|
||
|
||
```python
|
||
from typing import Optional
|
||
|
||
def create_user_profile(username: str, bio: Optional[str] = None):
|
||
"""
|
||
Creates a new user profile.
|
||
|
||
Args:
|
||
username (str): The user's unique username.
|
||
bio (str, optional): A short biography for the user. Defaults to None.
|
||
"""
|
||
# ... function logic ...
|
||
if bio:
|
||
return {"status": "success", "message": f"Profile for {username} created with a bio."}
|
||
return {"status": "success", "message": f"Profile for {username} created."}
|
||
```
|
||
|
||
##### Variadic parameters (`*args` and `**kwargs`)
|
||
|
||
While you can include `*args` (variable positional arguments) and `**kwargs`
|
||
(variable keyword arguments) in your function signature for other purposes, they
|
||
are **ignored by the ADK framework** when generating the tool schema for the
|
||
LLM. The LLM will not be aware of them and cannot pass arguments to them. It's
|
||
best to rely on explicitly defined parameters for all data you expect from the
|
||
LLM.
|
||
|
||
#### Context injection
|
||
|
||
Context injection allows your custom functions to access the agent's
|
||
environment, such as session state or available actions. To enable, add a
|
||
parameter typed as `ToolContext` to your function. ADK automatically injects the
|
||
context data before your function runs and ensures this parameter is not visible
|
||
to the LLM.
|
||
|
||
```python
|
||
from google.adk.tools import ToolContext
|
||
|
||
def my_tool(arg1: str, tool_context: ToolContext):
|
||
# Example: Accessing session state
|
||
user_id = tool_context.state.get("user_id")
|
||
# Example: Triggering an action
|
||
# tool_context.actions.transfer_to_agent = "secondary_agent"
|
||
```
|
||
|
||
`ToolContext` provides access to:
|
||
|
||
- **`state`:** A dictionary-like object for session-scoped data.
|
||
- **`actions`:** Controls for agent behavior, for example `transfer_to_agent`.
|
||
- **Methods**: To handle artifacts, such as `load_artifact` or `save_artifact`.
|
||
|
||
##### Customize the parameter name
|
||
|
||
By default, the injected parameter is called `tool_context`, but you can name
|
||
the parameter anything you want. ADK detects it by its `ToolContext` type
|
||
annotation rather than by name. For example, to use the name `ctx`:
|
||
|
||
```python
|
||
from google.adk.tools import ToolContext
|
||
|
||
def my_tool(arg1: str, ctx: ToolContext):
|
||
# 'ctx' receives the ToolContext because of its type annotation
|
||
user_id = ctx.state.get("user_id")
|
||
```
|
||
|
||
#### Return type
|
||
|
||
The preferred return type for a Function Tool is a **dictionary** in Python, a
|
||
**Map** or custom **Record or POJO** in Java, an **object** in TypeScript, or a
|
||
**Map** or **Data Class** in Kotlin. This allows you to structure the response
|
||
with key-value pairs, providing context and clarity to the LLM. If your function
|
||
returns a type other than a dictionary or map, the framework automatically wraps
|
||
it into a dictionary with a single key named **"result"**.
|
||
|
||
Strive to make your return values as descriptive as possible. *For example,*
|
||
instead of returning a numeric error code, return a dictionary with an
|
||
"error_message" key containing a human-readable explanation. **Remember that the
|
||
LLM**, not a piece of code, needs to understand the result. As a best practice,
|
||
include a "status" key in your return dictionary to indicate the overall outcome
|
||
(e.g., "success", "error", "pending"), providing the LLM with a clear signal
|
||
about the operation's state.
|
||
|
||
#### Docstrings
|
||
|
||
The docstring of your function serves as the tool's **description** and is sent
|
||
to the LLM. Therefore, a well-written and comprehensive docstring is crucial for
|
||
the LLM to understand how to use the tool effectively. Clearly explain the
|
||
purpose of the function, the meaning of its parameters, and the expected return
|
||
values. In Java, you can use Javadoc comments or the
|
||
`@Schema(description="...")` annotation on your method to serve as this
|
||
description. In Kotlin, you can use KDoc comments or the
|
||
`@Tool(description="...")` and `@Param(description="...")` annotations to
|
||
provide these descriptions.
|
||
|
||
### Pass data between tools
|
||
|
||
When an agent calls multiple tools in a sequence, you might need to pass data
|
||
from one tool to another. The recommended way to do this is by using the `temp:`
|
||
prefix in the session state.
|
||
|
||
A tool can write data to a `temp:` variable, and a subsequent tool can read it.
|
||
This data is only available for the current invocation and is discarded
|
||
afterwards.
|
||
|
||
!!! note "Shared Invocation Context"
|
||
|
||
All tool calls within a single agent turn share the same
|
||
`InvocationContext`. This means they also share the same temporary (`temp:`)
|
||
state, which is how data can be passed between them.
|
||
|
||
### Example
|
||
|
||
??? "Example"
|
||
|
||
=== "Python"
|
||
|
||
This tool is a python function which obtains the Stock price of a given
|
||
Stock ticker/ symbol.
|
||
|
||
<u>Note</u>: You need to `pip install yfinance` library before using
|
||
this tool.
|
||
|
||
```python
|
||
--8<-- "examples/python/snippets/tools/function-tools/func_tool.py"
|
||
```
|
||
|
||
The return value from this tool will be wrapped into a dictionary.
|
||
|
||
```json
|
||
{"result": "$123"}
|
||
```
|
||
|
||
=== "TypeScript"
|
||
|
||
This tool retrieves the mocked value of a stock price.
|
||
|
||
```typescript
|
||
--8<-- "examples/typescript/snippets/tools/function-tools/function-tools-example.ts"
|
||
```
|
||
|
||
The return value from this tool will be an object.
|
||
|
||
```json
|
||
For input `GOOG`: {"price": 2800.0, "currency": "USD"}
|
||
```
|
||
|
||
=== "Go"
|
||
|
||
This tool retrieves the mocked value of a stock price.
|
||
|
||
```go
|
||
import (
|
||
"google.golang.org/adk/v2/agent"
|
||
"google.golang.org/adk/v2/agent/llmagent"
|
||
"google.golang.org/adk/v2/model/gemini"
|
||
"google.golang.org/adk/v2/runner"
|
||
"google.golang.org/adk/v2/session"
|
||
"google.golang.org/adk/v2/tool"
|
||
"google.golang.org/adk/v2/tool/functiontool"
|
||
"google.golang.org/genai"
|
||
)
|
||
|
||
--8<-- "examples/go/snippets/tools/function-tools/func_tool.go"
|
||
```
|
||
|
||
The return value from this tool will be a `getStockPriceResults` instance.
|
||
|
||
```json
|
||
For input `{"symbol": "GOOG"}`: {"price":300.6,"symbol":"GOOG"}
|
||
```
|
||
|
||
=== "Java"
|
||
|
||
This tool retrieves the mocked value of a stock price.
|
||
|
||
```java
|
||
--8<-- "examples/java/snippets/src/main/java/tools/StockPriceAgent.java:full_code"
|
||
```
|
||
|
||
The return value from this tool will be wrapped into a Map<String, Object>.
|
||
|
||
```json
|
||
For input `GOOG`: {"symbol": "GOOG", "price": "1.0"}
|
||
```
|
||
|
||
=== "Kotlin"
|
||
|
||
This tool retrieves the mocked value of a stock price.
|
||
|
||
```kotlin
|
||
--8<-- "examples/kotlin/snippets/tools/function-tools/FuncTool.kt:full_example"
|
||
```
|
||
|
||
The return value from this tool will be a Map.
|
||
|
||
```json
|
||
For input `GOOG`: {"symbol": "GOOG", "price": 123.45}
|
||
```
|
||
|
||
### Best practices
|
||
|
||
While you have considerable flexibility in defining your function, remember that
|
||
simplicity enhances usability for the LLM. Consider these guidelines:
|
||
|
||
- **Fewer Parameters are Better:** Minimize the number of parameters to reduce
|
||
complexity.
|
||
- **Simple Data Types:** Favor primitive data types like `str` and `int` over
|
||
custom classes whenever possible.
|
||
- **Meaningful Names:** The function's name and parameter names significantly
|
||
influence how the LLM interprets and utilizes the tool. Choose names that
|
||
clearly reflect the function's purpose and the meaning of its inputs. Avoid
|
||
generic names like `do_stuff()` or `beAgent()`.
|
||
- **Build for Parallel Execution:** Improve function calling performance when
|
||
multiple tools are run by building for asynchronous operation. For information
|
||
on enabling parallel execution for tools, see [Increase tool performance with
|
||
parallel execution](/tools-custom/performance/).
|
||
|
||
## Long running function tools {#long-run-tool}
|
||
|
||
This tool is designed to help you start and manage tasks that are handled
|
||
outside the operation of your agent workflow, and require a significant amount
|
||
of processing time, without blocking the agent's execution. This tool is a
|
||
subclass of `FunctionTool`.
|
||
|
||
When using a `LongRunningFunctionTool`, your function can initiate the
|
||
long-running operation and optionally return an **initial result**, such as a
|
||
long-running operation id. Once a long running function tool is invoked the
|
||
agent runner pauses the agent run and lets the agent client to decide whether to
|
||
continue or wait until the long-running operation finishes. The agent client can
|
||
query the progress of the long-running operation and send back an intermediate
|
||
or final response. The agent can then continue with other tasks. An example is
|
||
the human-in-the-loop scenario where the agent needs human approval before
|
||
proceeding with a task.
|
||
|
||
!!! warning "Warning: Execution handling"
|
||
|
||
Long Running Function Tools are designed to help you start and *manage* long
|
||
running tasks as part of your agent workflow, but ***not perform*** the
|
||
actual, long task. For tasks that require significant time to complete, you
|
||
should implement a separate server to do the task.
|
||
|
||
!!! tip "Tip: Parallel execution"
|
||
|
||
Depending on the type of tool you are building, designing for asynchronous
|
||
operation may be a better solution than creating a long running tool. For
|
||
more information, see [Increase tool performance with parallel
|
||
execution](/tools-custom/performance/).
|
||
|
||
### How it works
|
||
|
||
In Python, you wrap a function with `LongRunningFunctionTool`. In Java, you pass
|
||
a Method name to `LongRunningFunctionTool.create()`. In TypeScript, you
|
||
instantiate the `LongRunningFunctionTool` class.
|
||
|
||
1. **Initiation:** When the LLM calls the tool, your function starts the
|
||
long-running operation.
|
||
2. **Initial Updates:** Your function should optionally return an initial result
|
||
(e.g. the long-running operation id). The ADK framework takes the result and
|
||
sends it back to the LLM packaged within a `FunctionResponse`. This allows
|
||
the LLM to inform the user (e.g., status, percentage complete, messages). And
|
||
then the agent run is ended / paused.
|
||
3. **Continue or Wait:** After each agent run is completed. Agent client can
|
||
query the progress of the long-running operation and decide whether to
|
||
continue the agent run with an intermediate response (to update the progress)
|
||
or wait until a final response is retrieved. Agent client should send the
|
||
intermediate or final response back to the agent for the next run.
|
||
4. **Framework Handling:** The ADK framework manages the execution. It sends the
|
||
intermediate or final `FunctionResponse` sent by agent client to the LLM to
|
||
generate a user friendly message.
|
||
|
||
### Create the tool
|
||
|
||
Define your tool function and wrap it using the `LongRunningFunctionTool` class:
|
||
|
||
=== "Python"
|
||
|
||
```python
|
||
--8<-- "examples/python/snippets/tools/function-tools/human_in_the_loop.py:define_long_running_function"
|
||
```
|
||
|
||
=== "TypeScript"
|
||
|
||
```typescript
|
||
--8<-- "examples/typescript/snippets/tools/function-tools/long-running-function-tool-example.ts:define_long_running_function"
|
||
```
|
||
|
||
=== "Go"
|
||
|
||
```go
|
||
import (
|
||
"google.golang.org/adk/v2/agent"
|
||
"google.golang.org/adk/v2/agent/llmagent"
|
||
"google.golang.org/adk/v2/model/gemini"
|
||
"google.golang.org/adk/v2/tool"
|
||
"google.golang.org/adk/v2/tool/functiontool"
|
||
"google.golang.org/genai"
|
||
)
|
||
|
||
--8<-- "examples/go/snippets/tools/function-tools/long-running-tool/long_running_tool.go:create_long_running_tool"
|
||
```
|
||
|
||
=== "Java"
|
||
|
||
```java
|
||
import com.google.adk.agents.LlmAgent;
|
||
import com.google.adk.tools.LongRunningFunctionTool;
|
||
import java.util.HashMap;
|
||
import java.util.Map;
|
||
|
||
public class ExampleLongRunningFunction {
|
||
|
||
// Define your Long Running function.
|
||
// Ask for approval for the reimbursement.
|
||
public static Map<String, Object> askForApproval(String purpose, double amount) {
|
||
// Simulate creating a ticket and sending a notification
|
||
System.out.println(
|
||
"Simulating ticket creation for purpose: " + purpose + ", amount: " + amount);
|
||
|
||
// Send a notification to the approver with the link of the ticket
|
||
Map<String, Object> result = new HashMap<>();
|
||
result.put("status", "pending");
|
||
result.put("approver", "Sean Zhou");
|
||
result.put("purpose", purpose);
|
||
result.put("amount", amount);
|
||
result.put("ticket-id", "approval-ticket-1");
|
||
return result;
|
||
}
|
||
|
||
public static void main(String[] args) throws NoSuchMethodException {
|
||
// Pass the method to LongRunningFunctionTool.create
|
||
LongRunningFunctionTool approveTool =
|
||
LongRunningFunctionTool.create(ExampleLongRunningFunction.class, "askForApproval");
|
||
|
||
// Include the tool in the agent
|
||
LlmAgent approverAgent =
|
||
LlmAgent.builder()
|
||
// ...
|
||
.tools(approveTool)
|
||
.build();
|
||
}
|
||
}
|
||
```
|
||
|
||
=== "Kotlin"
|
||
|
||
In Kotlin, you can create a long-running function tool by setting the
|
||
`isLongRunning` property to `true` in the `@Tool` annotation.
|
||
|
||
```kotlin
|
||
--8<-- "examples/kotlin/snippets/tools/function-tools/LongRunningTool.kt:long_running_tool"
|
||
```
|
||
|
||
### Intermediate / final result updates
|
||
|
||
Agent client received an event with long running function calls and check the
|
||
status of the ticket. Then Agent client can send the intermediate or final
|
||
response back to update the progress. The framework packages this value (even if
|
||
it's None) into the content of the `FunctionResponse` sent back to the LLM.
|
||
|
||
!!! note "Note: Long running function response with Resume feature"
|
||
|
||
If your ADK agent workflow is configured with the [Resume](/runtime/resume/)
|
||
feature, you also must include the Invocation ID (`invocation_id`) parameter
|
||
with the long running function response. The Invocation ID you provide must
|
||
be the same invocation that generated the long running function request,
|
||
otherwise the system starts a new invocation with the response. If your
|
||
agent uses the Resume feature, consider including the Invocation ID as a
|
||
parameter with your long running function request, so it can be included
|
||
with the response. For more details on using the Resume feature, see [Resume
|
||
stopped agents](/runtime/resume/).
|
||
|
||
??? Tip "Applies to only Java ADK"
|
||
|
||
When passing `ToolContext` with Function Tools, ensure that one of the
|
||
following is true:
|
||
|
||
- The Schema is passed with the ToolContext parameter in the function
|
||
signature, like:
|
||
|
||
```
|
||
@com.google.adk.tools.Annotations.Schema(name = "toolContext") ToolContext toolContext
|
||
```
|
||
OR
|
||
|
||
- The following `-parameters` flag is set to the mvn compiler plugin
|
||
|
||
```
|
||
<build>
|
||
<plugins>
|
||
<plugin>
|
||
<groupId>org.apache.maven.plugins</groupId>
|
||
<artifactId>maven-compiler-plugin</artifactId>
|
||
<version>3.14.0</version> <!-- or newer -->
|
||
<configuration>
|
||
<compilerArgs>
|
||
<arg>-parameters</arg>
|
||
</compilerArgs>
|
||
</configuration>
|
||
</plugin>
|
||
</plugins>
|
||
</build>
|
||
```
|
||
|
||
=== "Python"
|
||
|
||
```python
|
||
--8<-- "examples/python/snippets/tools/function-tools/human_in_the_loop.py:call_reimbursement_tool"
|
||
```
|
||
|
||
=== "TypeScript"
|
||
|
||
```typescript
|
||
--8<-- "examples/typescript/snippets/tools/function-tools/long-running-function-tool-example.ts"
|
||
```
|
||
|
||
=== "Go"
|
||
|
||
The following example demonstrates a multi-turn workflow. First, the user
|
||
asks the agent to create a ticket. The agent calls the long-running tool and
|
||
the client captures the `FunctionCall` ID. The client then simulates the
|
||
asynchronous work completing by sending subsequent `FunctionResponse`
|
||
messages back to the agent to provide the ticket ID and final status.
|
||
|
||
```go
|
||
--8<-- "examples/go/snippets/tools/function-tools/long-running-tool/long_running_tool.go:run_long_running_tool"
|
||
```
|
||
|
||
=== "Java"
|
||
|
||
```java
|
||
--8<-- "examples/java/snippets/src/main/java/tools/LongRunningFunctionExample.java:full_code"
|
||
```
|
||
|
||
??? "Python complete example: File Processing Simulation"
|
||
|
||
```python
|
||
--8<-- "examples/python/snippets/tools/function-tools/human_in_the_loop.py"
|
||
```
|
||
|
||
#### Key aspects of this example
|
||
|
||
- **`LongRunningFunctionTool`**: Wraps the supplied method/function; the
|
||
framework handles sending yielded updates and the final return value as
|
||
sequential FunctionResponses.
|
||
- **Agent instruction**: Directs the LLM to use the tool and understand the
|
||
incoming FunctionResponse stream (progress vs. completion) for user updates.
|
||
- **Final return**: The function returns the final result dictionary, which is
|
||
sent in the concluding FunctionResponse to indicate completion.
|
||
|
||
## Agent-as-a-Tool {#agent-tool}
|
||
|
||
This feature allows you to leverage the capabilities of other agents within your
|
||
system by calling them as tools. The Agent-as-a-Tool enables you to invoke
|
||
another agent to perform a specific task, effectively **delegating
|
||
responsibility**. This is conceptually similar to creating a Python function
|
||
that calls another agent and uses the agent's response as the function's return
|
||
value.
|
||
|
||
### Key difference from sub-agents
|
||
|
||
It's important to distinguish an Agent-as-a-Tool from a sub-agent.
|
||
|
||
- **Agent-as-a-Tool:** When Agent A calls Agent B as a tool (using
|
||
Agent-as-a-Tool), Agent B's answer is **passed back** to Agent A, which then
|
||
summarizes the answer and generates a response to the user. Agent A retains
|
||
control and continues to handle future user input.
|
||
- **Sub-agent:** When Agent A calls Agent B as a sub-agent, the responsibility
|
||
of answering the user is completely **transferred to Agent B**. Agent A is
|
||
effectively out of the loop. All subsequent user input will be answered by
|
||
Agent B.
|
||
|
||
### Use `AgentTool`
|
||
|
||
To use an agent as a tool, wrap the agent with the `AgentTool` class.
|
||
|
||
=== "Python"
|
||
|
||
```python
|
||
tools=[AgentTool(agent=agent_b)]
|
||
```
|
||
|
||
=== "TypeScript"
|
||
|
||
```typescript
|
||
tools: [new AgentTool({agent: agentB})]
|
||
```
|
||
|
||
=== "Go"
|
||
|
||
```go
|
||
agenttool.New(agent, &agenttool.Config{...})
|
||
```
|
||
|
||
=== "Java"
|
||
|
||
```java
|
||
AgentTool.create(agent)
|
||
```
|
||
|
||
=== "Kotlin"
|
||
|
||
```kotlin
|
||
AgentTool(agent = agentB)
|
||
```
|
||
|
||
### Customize your agent tool
|
||
|
||
The `AgentTool` class provides the following attributes for customizing its
|
||
behavior.
|
||
|
||
#### Skip summarization
|
||
|
||
**`skip_summarization`** (boolean)
|
||
If set to `True`, this customization instructs the framework to bypass the LLM-based summarization of the tool agent's response. This feature is best used when the tool's output is already well-formatted and requires no further processing.
|
||
|
||
- **Use:** Python/TypeScript (`skip_summarization`); Kotlin/Java
|
||
(`skipSummarization`).
|
||
|
||
??? "Example"
|
||
|
||
=== "Python"
|
||
|
||
```python
|
||
--8<-- "examples/python/snippets/tools/function-tools/summarizer.py"
|
||
```
|
||
|
||
=== "TypeScript"
|
||
|
||
```typescript
|
||
--8<-- "examples/typescript/snippets/tools/function-tools/agent-as-a-tool-example.ts"
|
||
```
|
||
|
||
=== "Go"
|
||
|
||
```go
|
||
import (
|
||
"google.golang.org/adk/v2/agent"
|
||
"google.golang.org/adk/v2/agent/llmagent"
|
||
"google.golang.org/adk/v2/model/gemini"
|
||
"google.golang.org/adk/v2/tool"
|
||
"google.golang.org/adk/v2/tool/agenttool"
|
||
"google.golang.org/genai"
|
||
)
|
||
|
||
--8<-- "examples/go/snippets/tools/function-tools/func_tool.go:agent_tool_example"
|
||
```
|
||
|
||
=== "Java"
|
||
|
||
```java
|
||
--8<-- "examples/java/snippets/src/main/java/tools/AgentToolCustomization.java:full_code"
|
||
```
|
||
|
||
=== "Kotlin"
|
||
|
||
```kotlin
|
||
--8<-- "examples/kotlin/snippets/tools/function-tools/AgentTool.kt:agent_tool"
|
||
```
|
||
|
||
##### How it works
|
||
1. When the `root_agent` receives the long text, its instruction tells it to use
|
||
the 'summarize' tool for long texts.
|
||
2. The framework recognizes 'summarize' as an `AgentTool` that wraps the
|
||
`summary_agent`.
|
||
3. Behind the scenes, the `root_agent` will call the `summary_agent` with the
|
||
long text as input.
|
||
4. The `summary_agent` will process the text according to its instruction and
|
||
generate a summary.
|
||
5. **The response from the `summary_agent` is then passed back to the
|
||
`root_agent`.**
|
||
6. The `root_agent` can then take the summary and formulate its final response
|
||
to the user (e.g., "Here's a summary of the text: ...")
|
||
|
||
#### Propagate grounding metadata
|
||
|
||
**`propagate_grounding_metadata`** (boolean, default: `False`)
|
||
If set to `True`, the tool automatically forwards any grounding metadata, such as Google Search citations, generated by the sub-agent up to the parent agent's session state. This customization ensures that citations are preserved when using specialized search agents as tools.
|
||
|
||
=== "Python"
|
||
|
||
```python
|
||
from google.adk.agents import Agent
|
||
from google.adk.tools import AgentTool
|
||
|
||
search_specialist_agent = Agent(
|
||
# Specify your generative model
|
||
model="gemini-flash-latest",
|
||
name="search_specialist_agent",
|
||
instruction=(
|
||
"You are a search expert. Find and "
|
||
"compile citations on requested topics."
|
||
),
|
||
# Add any search tools here
|
||
)
|
||
|
||
search_agent_tool = AgentTool(
|
||
agent=search_specialist_agent,
|
||
# Keeps citations intact back to the root
|
||
propagate_grounding_metadata=True
|
||
)
|
||
|
||
root_agent = Agent(
|
||
model="gemini-flash-latest",
|
||
name="root_agent",
|
||
description=(
|
||
"A central coordinator that delegates "
|
||
"to specialist agents."
|
||
),
|
||
tools=[search_agent_tool]
|
||
)
|
||
```
|
||
|
||
#### Control plugin inheritance
|
||
|
||
When you wrap an agent with `AgentTool`, you can control whether it
|
||
inherits plugins from the parent runner using the `include_plugins`
|
||
parameter.
|
||
|
||
* **`include_plugins=True` (default):** The child agent inherits all
|
||
plugins from the parent, preserving trace spans and event streaming.
|
||
* **`include_plugins=False`:** The child agent runs in an isolated
|
||
environment without inheriting any plugins from the parent. Use this
|
||
setting to ensure an agent's execution is self-contained and unaffected
|
||
by the parent's plugin environment.
|
||
|
||
=== "Python"
|
||
|
||
```python
|
||
from google.adk.tools import agent_tool
|
||
|
||
# Placeholder definition for MyImageAgent
|
||
class MyImageAgent:
|
||
def __init__(
|
||
self, name="My Agent", description="A simple image agent."
|
||
):
|
||
self.name = name
|
||
# Added description attribute
|
||
self.description = description
|
||
|
||
# Example 1: Isolate MyImageAgent from parent plugins
|
||
my_isolated_tool = agent_tool.AgentTool(
|
||
agent=MyImageAgent(), # Instantiate MyImageAgent
|
||
include_plugins=False
|
||
)
|
||
|
||
# Example 2: Inherit plugins
|
||
my_observable_tool = agent_tool.AgentTool(
|
||
agent=MyImageAgent(), # Instantiate MyImageAgent
|
||
include_plugins=True
|
||
)
|
||
```
|