Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 107 additions & 180 deletions docs/tools/built-in-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,10 @@ These are a set of tools aimed to provide integration with BigQuery, namely:
* **`get_dataset_info`**: Fetches metadata about a BigQuery dataset.
* **`list_table_ids`**: Fetches table ids present in a BigQuery dataset.
* **`get_table_info`**: Fetches metadata about a BigQuery table.
* **`execute_sql`**: Runs a SQL query in BigQuery and fetch the result.
* **`execute_sql`**: Runs a SQL query in BigQuery and fetch the result. This tool includes a `dry_run` parameter that allows you to validate a query and get information about it without actually running it.
* **`forecast`**: Runs a BigQuery AI time series forecast using the `AI.FORECAST` function.
* **`ask_data_insights`**: Answers questions about data in BigQuery tables using natural language.
* **`analyze_contribution`**: Analyzes the contribution of specified dimensions to a metric in a BigQuery table.

They are packaged in the toolset `BigQueryToolset`.

Expand Down Expand Up @@ -210,6 +211,104 @@ They are packaged in the toolset `SpannerToolset`.
```


### Customizing Spanner Tools

You can create custom Spanner tools to tailor their functionality to your specific needs. This is particularly useful for creating tools that execute predefined queries with template or parameterized SQL.

#### Template SQL

You can create a custom tool that uses a template SQL query. This is useful when you want to create a tool that executes a specific query with some parts of the query being dynamic.

The following example shows how to create a tool that counts the rows in a table, where the table name is provided as an argument.

```python
from google.adk.tools.spanner import utils as spanner_tool_utils
from google.adk.tools.tool_context import ToolContext
from google.auth.credentials import Credentials
from google.adk.tools.spanner.settings import SpannerToolSettings

def count_rows_in_table(
table_name: str,
credentials: Credentials,
settings: SpannerToolSettings,
tool_context: ToolContext,
):
"""Counts the total number of rows for a specified table.

Args:
table_name: The name of the table for which to count rows.

Returns:
The total number of rows in the table.
"""

sql_template = f"SELECT COUNT(*) FROM {table_name}"

return spanner_tool_utils.execute_sql(
project_id="<PROJECT_ID>",
instance_id="<INSTANCE_ID>",
database_id="<DATABASE_ID>",
query=sql_template,
credentials=credentials,
settings=settings,
tool_context=tool_context,
)
```

!!! warning "Security"
Using f-strings to create SQL queries can make your application vulnerable to SQL injection attacks. It is recommended to use parameterized queries whenever possible.

#### Parameterized SQL

To prevent SQL injection attacks, you can use parameterized queries. The `execute_sql` function supports parameterized queries through the `params` and `params_types` arguments.

The following example shows how to create a tool that searches for hotels in a specific location using a parameterized query.

```python
from google.adk.tools.spanner import utils as spanner_tool_utils
from google.adk.tools.tool_context import ToolContext
from google.auth.credentials import Credentials
from google.adk.tools.spanner.settings import SpannerToolSettings
from google.cloud.spanner_v1 import param_types as spanner_param_types

def search_hotels(
location_name: str,
credentials: Credentials,
settings: SpannerToolSettings,
tool_context: ToolContext,
):
"""Search hotels for a specific location.

This function takes a geographical location name and returns a list of hotels
in that area, including key details for each.

Args:
location_name (str): The geographical location (e.g., city or town) for the
hotel search.

Returns:
The hotels name, rating and description.
"""

sql_template = """
SELECT name, rating, description FROM hotels
WHERE location_name = @location_name
"""
return spanner_tool_utils.execute_sql(
project_id="<PROJECT_ID>",
instance_id="<INSTANCE_ID>",
database_id="<DATABASE_ID>",
query=sql_template,
credentials=credentials,
settings=settings,
tool_context=tool_context,
params={"location_name": location_name},
params_types={"location_name": spanner_param_types.STRING},
)
```

In this example, `@location_name` is a parameter in the SQL query. The value for this parameter is passed in the `params` dictionary, and its type is specified in the `params_types` dictionary. This ensures that the value is properly escaped, preventing SQL injection attacks.

### Bigtable

These are a set of tools aimed to provide integration with Bigtable, namely:
Expand All @@ -228,199 +327,27 @@ They are packaged in the toolset `BigtableToolset`.
--8<-- "examples/python/snippets/tools/built-in-tools/bigtable.py"
```

## Use Built-in tools with other tools
## Using Multiple Built-in Tools

The following code sample demonstrates how to use multiple built-in tools or how
to use built-in tools with other tools by using multiple agents:
You can use multiple built-in tools, such as `VertexAiSearchTool` and `google_search`, together in the same agent. This allows you to create powerful agents that can perform a variety of tasks, such as searching for information and then using that information in a code execution environment.

The following code sample demonstrates how to use `google_search` and the `BuiltInCodeExecutor` in the same agent:

=== "Python"

```py
from google.adk.tools.agent_tool import AgentTool
```python
from google.adk.agents import Agent
from google.adk.tools import google_search
from google.adk.code_executors import BuiltInCodeExecutor


search_agent = Agent(
model='gemini-2.0-flash',
name='SearchAgent',
instruction="""
You're a specialist in Google Search
""",
tools=[google_search],
)
coding_agent = Agent(
model='gemini-2.0-flash',
name='CodeAgent',
instruction="""
You're a specialist in Code Execution
""",
code_executor=BuiltInCodeExecutor(),
)
root_agent = Agent(
name="RootAgent",
model="gemini-2.0-flash",
description="Root Agent",
tools=[AgentTool(agent=search_agent), AgentTool(agent=coding_agent)],
)
```

=== "Java"

```java
import com.google.adk.agents.BaseAgent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.AgentTool;
import com.google.adk.tools.BuiltInCodeExecutionTool;
import com.google.adk.tools.GoogleSearchTool;
import com.google.common.collect.ImmutableList;

public class NestedAgentApp {

private static final String MODEL_ID = "gemini-2.0-flash";

public static void main(String[] args) {

// Define the SearchAgent
LlmAgent searchAgent =
LlmAgent.builder()
.model(MODEL_ID)
.name("SearchAgent")
.instruction("You're a specialist in Google Search")
.tools(new GoogleSearchTool()) // Instantiate GoogleSearchTool
.build();


// Define the CodingAgent
LlmAgent codingAgent =
LlmAgent.builder()
.model(MODEL_ID)
.name("CodeAgent")
.instruction("You're a specialist in Code Execution")
.tools(new BuiltInCodeExecutionTool()) // Instantiate BuiltInCodeExecutionTool
.build();

// Define the RootAgent, which uses AgentTool.create() to wrap SearchAgent and CodingAgent
BaseAgent rootAgent =
LlmAgent.builder()
.name("RootAgent")
.model(MODEL_ID)
.description("Root Agent")
.tools(
AgentTool.create(searchAgent), // Use create method
AgentTool.create(codingAgent) // Use create method
)
.build();

// Note: This sample only demonstrates the agent definitions.
// To run these agents, you'd need to integrate them with a Runner and SessionService,
// similar to the previous examples.
System.out.println("Agents defined successfully:");
System.out.println(" Root Agent: " + rootAgent.name());
System.out.println(" Search Agent (nested): " + searchAgent.name());
System.out.println(" Code Agent (nested): " + codingAgent.name());
}
}
```


### Limitations

!!! warning

Currently, for each root agent or single agent, only one built-in tool is
supported. No other tools of any type can be used in the same agent.

For example, the following approach that uses ***a built-in tool along with
other tools*** within a single agent is **not** currently supported:

=== "Python"

```py
root_agent = Agent(
name="RootAgent",
model="gemini-2.0-flash",
description="Root Agent",
tools=[custom_function],
code_executor=BuiltInCodeExecutor() # <-- not supported when used with tools
)
```

=== "Java"

```java
LlmAgent searchAgent =
LlmAgent.builder()
.model(MODEL_ID)
.name("SearchAgent")
.instruction("You're a specialist in Google Search")
.tools(new GoogleSearchTool(), new YourCustomTool()) // <-- not supported
.build();
```

!!! warning

Built-in tools cannot be used within a sub-agent.

For example, the following approach that uses built-in tools within sub-agents
is **not** currently supported:

=== "Python"

```py
search_agent = Agent(
model='gemini-2.0-flash',
name='SearchAgent',
name='SearchAndCodeAgent',
instruction="""
You're a specialist in Google Search
You are a specialist in Google Search and Code Execution.
""",
tools=[google_search],
)
coding_agent = Agent(
model='gemini-2.0-flash',
name='CodeAgent',
instruction="""
You're a specialist in Code Execution
""",
code_executor=BuiltInCodeExecutor(),
)
root_agent = Agent(
name="RootAgent",
model="gemini-2.0-flash",
description="Root Agent",
sub_agents=[
search_agent,
coding_agent
],
)
```

=== "Java"

```java
LlmAgent searchAgent =
LlmAgent.builder()
.model("gemini-2.0-flash")
.name("SearchAgent")
.instruction("You're a specialist in Google Search")
.tools(new GoogleSearchTool())
.build();

LlmAgent codingAgent =
LlmAgent.builder()
.model("gemini-2.0-flash")
.name("CodeAgent")
.instruction("You're a specialist in Code Execution")
.tools(new BuiltInCodeExecutionTool())
.build();


LlmAgent rootAgent =
LlmAgent.builder()
.name("RootAgent")
.model("gemini-2.0-flash")
.description("Root Agent")
.subAgents(searchAgent, codingAgent) // Not supported, as the sub agents use built in tools.
.build();
```