datarekha

ADK tools — multi-tool agents

Defining tools in Google ADK, using multiple tools in one agent, and the built-in tools (Google Search grounding, code execution) you get for free.

7 min read Intermediate Agentic AI Lesson 46 of 71

What you'll learn

  • Turning a Python function into an ADK tool
  • Using multiple tools in one agent and how the model picks
  • Built-in tools — Google Search grounding, code execution
  • Parallel tool execution and the latency it buys you

Before you start

The pattern for tools in ADK matches what you’d guess after the tool-calling lesson: a Python function with type hints and a docstring is a valid tool. Hand it to the agent’s tools=[...] list and ADK does the schema generation, the call loop, and the parallel execution. This lesson covers the multi-tool agent — the most common production shape — plus the built-in tools Google provides.

A function is a tool

The simplest case: write the function, pass it to the agent.

# requires: pip install google-adk for the agent wiring.
# The three plain functions below are ordinary Python and run anywhere —
# the LlmAgent setup is the real ADK shape you'd paste into a project.

from google.adk.agents import LlmAgent

# 1. Plain Python functions with type hints + docstrings.
def lookup_weather(city: str) -> dict:
    """Get the current temperature for a city in Celsius.

    Args:
        city: City name, e.g. 'Tokyo'.

    Returns:
        A dict with 'city' and 'temperature_c'.
    """
    fake = {"Tokyo": 18, "Lagos": 32, "Reykjavik": 4}
    return {"city": city, "temperature_c": fake.get(city, 20)}

def get_news(topic: str, max_items: int = 3) -> list[dict]:
    """Fetch recent headlines about a topic.

    Args:
        topic: A short search query.
        max_items: How many headlines to return (1-10).
    """
    return [{"title": f"Mock headline {i} about {topic}"} for i in range(max_items)]

def calculate_tip(bill: float, percent: float = 18.0) -> dict:
    """Compute a tip and total for a restaurant bill.

    Args:
        bill: Subtotal in dollars.
        percent: Tip percentage (default 18).
    """
    tip = round(bill * percent / 100, 2)
    return {"tip": tip, "total": round(bill + tip, 2)}

# 2. One agent, three tools. The model picks which to call.
agent = LlmAgent(
    name="assistant",
    model="gemini-2.5-flash",
    instruction=(
        "You are a helpful assistant. Use tools when they match the user's "
        "question. For weather, always call lookup_weather. For news, "
        "always call get_news. For tip math, always call calculate_tip."
    ),
    tools=[lookup_weather, get_news, calculate_tip],
)

# Sanity-check the tools themselves (this DOES run):
print(lookup_weather("Tokyo"))
print(calculate_tip(42.50, 20))
print(get_news("AI", max_items=2))
{'city': 'Tokyo', 'temperature_c': 18}
{'tip': 8.5, 'total': 51.0}
[{'title': 'Mock headline 0 about AI'}, {'title': 'Mock headline 1 about AI'}]

What ADK does behind the scenes:

  • Schema generation. It introspects each function’s signature and docstring, builds a FunctionDeclaration, and hands it to Gemini.
  • The tool-call loop. When the model emits a tool call, ADK resolves the function by name, runs it, and feeds the result back in the next turn — same loop as every other framework.
  • Argument coercion. If the model returns "3" for an int parameter, ADK coerces. If it can’t, it surfaces an error to the model so it can retry with valid arguments.

How the model chooses

The model picks tools based on three signals — and it does not always pick correctly; better names and docstrings are the most direct levers you have:

  1. Tool name. lookup_weather is more legible than lkup_wthr_v2. Name tools like you’d name public APIs.
  2. Docstring. This is the description Gemini sees. Be specific about when to call vs. when not to. “Use this for current weather; do not use for forecasts.”
  3. The agent’s instruction. A nudge in the system prompt — “for weather, always call lookup_weather” — closes the gap when the model is tempted to answer from training data.
assistantgemini-2.5-flashroutes by name + docstringlookup_weather→ weather questionsget_news→ recent headlinescalculate_tip→ tip math
One agent, three tools. The model reads each tool’s name and docstring and routes the question to the matching one.

Parallel tool calls

Gemini supports parallel function calls — the model can request multiple tools in one turn. ADK executes them concurrently. If the user asks “weather in Tokyo and Lagos, and three news headlines about AI”, the model can emit three tool calls at once.

You don’t write any special code; it’s the default. The win is latency: three tools running in parallel return in roughly the time of the slowest, not the sum. For an agent with chatty tools (API calls that each take 200-500ms), parallel execution can cut wall-clock time by 2-3×.

Built-in tools — Google Search and code execution

ADK ships two hosted tools that you’d otherwise have to build:

Google Search grounding. Gemini can call Google Search directly, get fresh results, and ground its answer in them. You enable it by listing google_search in the agent’s tools.

Code execution. Gemini can write and execute Python in a sandbox — useful for math, data analysis, and generating plots. Same pattern: list built_in_code_execution.

from google.adk.tools import google_search, built_in_code_execution

agent = LlmAgent(
    name="researcher",
    model="gemini-2.5-flash",
    instruction="Research questions thoroughly. Compute when math is involved.",
    tools=[
        google_search,
        built_in_code_execution,
        # your own tools alongside:
        lookup_weather,
    ],
)

The trade-off: built-in tools are convenient but you give up control. The search tool will hit Google’s index (not your internal docs); code execution runs in Google’s sandbox (not your VPC). For internal RAG or sandboxed compute, you keep writing custom tools.

Sequential reasoning with one agent

A common shape: the agent calls tool A, looks at the result, decides to call tool B. ADK handles this natively — the runner loop keeps going until the model emits a final response (or you cap iterations).

User”Is it warm enough in Tokyo to go for a walk, and any good news today?”Geminilookup_weather(“Tokyo”)   ||   get_news(“today”)ADK runnerruns both tools in parallel, feeds results back to the modelGemini”Yes, 18°C in Tokyo — comfortable for a walk. Top headline: …”
The model picks tools; ADK runs them and feeds results back until a final answer.

The model reasons about the results; you don’t write the orchestration. This is the “just tool use” pattern from the agent design patterns lesson — most production agents are this and nothing more.

In one breath

  • A plain Python function with type hints + a docstring is a valid ADK tool; pass it in tools=[...] and ADK handles schema generation, the call loop, and argument coercion.
  • One agent can hold several tools, and the model picks by three signals — tool name, docstring, and the agent’s instruction — so writing those well is your highest-leverage lever.
  • Parallel tool calls are the default: when the model requests several tools in one turn, ADK runs them concurrently, so total time ≈ the slowest tool, not the sum.
  • Built-in toolsgoogle_search grounding and built_in_code_execution — save integration code but cost you control (Google’s index, Google’s sandbox); for internal RAG or private compute, keep writing custom tools.
  • Hold to ~5–15 tools per agent; beyond that, route to specialist sub-agents. Most production agents are just this tool-use loop, and nothing more.

Quick check

Quick check

0/3
Q1What's the single biggest factor in whether the model picks the right tool?
Q2Why are parallel tool calls a latency win?
Q3What's the trade-off of using `google_search` built-in?

Next

Single agents with tools handle most jobs. The next lesson covers workflow agents — when you need explicit sequential, parallel, or loop orchestration across multiple agents.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions
How do function/tool calling and LLM agents work at a high level?

Tool calling extends the LLM's output space to include structured function invocations. The model emits a JSON object naming a tool and its arguments; the runtime executes the tool and feeds the result back as a new message. An agent is a loop that repeats this cycle — observe, think, act — until the task is complete or a stopping condition is met.

What is tool use or function calling in LLMs, and how do you design good tools for an agent?

Function calling lets an LLM output a structured request to invoke an external function with arguments, which the runtime executes and feeds back, enabling agents to act in the world. Good tool design uses clear names and descriptions, minimal well-typed parameters, narrow single-purpose scope, least privilege, and informative error messages so the model can choose and call them reliably.

What are the major security risks of deploying autonomous agents?

Key risks include prompt injection, especially indirect injection via tool or retrieval outputs, hijacking the agent, excessive tool permissions enabling damaging actions, data exfiltration, confused-deputy privilege escalation, and unbounded loops driving cost or harm. Mitigations include least-privilege tools, sandboxing, input and output guardrails, human-in-the-loop approval for sensitive actions, and audit logging.

What is an AI agent, and how does it differ from a single LLM call?

An agent is an LLM placed in a loop where it reasons, chooses and calls tools or actions, observes the results, and repeats until a goal is met, rather than producing one response and stopping. The key differences are autonomy, tool use, memory and state, and multi-step control flow driven by the model's own decisions.

Related lessons

Explore further

Skip to content