Module 9: Creating Custom Function Tools
Theoryβ
Unlocking Unlimited Capabilitiesβ
While built-in tools like google_search are powerful, the true potential of your agent is realized when you give it custom capabilities tailored to your specific needs. This is done by creating Custom Function Tools.
A custom function tool is, at its core, a regular Python function that you write and then make available to your agent. This allows you to connect your agent to virtually anything you can program: a proprietary database, a third-party API, a complex business logic algorithm, or even another AI model.
How it Works: From Python to Agent Toolβ
When you add one of your Python functions to an agent's tools list, the ADK framework performs a clever transformation behind the scenes. It inspects your function's signature
βits name, parameters, type hints, and docstringβand automatically generates a detailed description, or schema.
This schema is what's provided to the Large Language Model. The LLM doesn't see your Python code. It only sees this schema, which tells it:
- The tool's name (from your function name).
- What the tool does (from your function's docstring).
- What parameters it needs (from your function's arguments).
- What data type each parameter should be (from your type hints).
Based on this information, the LLM can intelligently decide when to use your function and what arguments to pass to it.
The Anatomy of a Well-Defined Tool Functionβ
For the LLM to use your tool correctly and reliably, it's crucial to define your Python function in a structured way.
1. Descriptive Function Nameβ
The name should clearly indicate the action the function performs.
- Good:
get_weather,calculate_loan_payment,lookup_order_status - Bad:
process_data,run_logic,my_function
2. Clear Parameters with Type Hintsβ
Each argument your function takes must have a type hint. This is non-negotiable, as it tells the LLM what kind of data to provide.
# The LLM knows 'city' must be a string and 'is_forecast' must be a boolean.
def get_weather(city: str, is_forecast: bool):
...
3. The All-Important Docstringβ
The docstring is the most critical part. It serves as the tool's description for the LLM. A good docstring should explain:
- Purpose: What does the tool do?
- Usage: When should the agent use this tool? Provide context.
- Parameters: Describe what each parameter represents.
def get_weather(city: str, is_forecast: bool):
"""
Fetches the current weather or a 3-day forecast for a specific city.
Use this tool when a user asks about the weather.
Args:
city: The name of the city (e.g., "San Francisco").
is_forecast: Set to True for a 3-day forecast, False for current conditions.
"""
...
4. Structured Return Valueβ
A tool function in the ADK must return a dictionary. This allows you to provide a structured result that the LLM can easily understand. It is a best practice to include a status key to indicate the outcome.
def get_weather(city: str, is_forecast: bool) -> dict:
"""... docstring ..."""
# ... logic to fetch weather ...
if success:
return {"status": "success", "report": "It is sunny."}
else:
return {"status": "error", "message": "Could not find weather for that city."}
The LLM will receive this dictionary and use its contents to formulate the final response to the user.
Registering Tools with Your Agentβ
You can register your custom functions as tools using either the Python-based or YAML-based approach.
Python (Primary Approach):
In your agent.py, you directly import your functions and wrap them in a FunctionTool.
# In agent.py
from google.adk.agents import LlmAgent
from google.adk.tools import FunctionTool
from .tools.calculator import add, subtract # Import your functions
root_agent = LlmAgent(
# ... other params
tools=[
FunctionTool(fn=add),
FunctionTool(fn=subtract),
]
)
YAML (Alternative Approach):
In root_agent.yaml, you reference the functions using their module path.
# In root_agent.yaml
# ... other params
tools:
- name: tools.calculator.add
- name: tools.calculator.subtract
In the lab for this module, you will put all these principles into practice by building a set of calculator functions and integrating them into a new "Calculator" agent.
Key Takeawaysβ
- Custom Function Tools allow you to connect your agent to any capability you can program in Python.
- The ADK automatically generates a tool schema from your function's signature (name, parameters, type hints) and its docstring.
- A well-defined tool function must have a descriptive name, clear type hints for all parameters, and a detailed docstring explaining its purpose and usage.
- All custom tool functions must return a dictionary.
- Tools are registered in your agent's definition, with the Python-based approach being the primary method.
Limitations: Mixing Tool Typesβ
As you start building more complex agents, it's important to be aware of a current limitation in the ADK regarding tool usage.
One Built-in Tool Per Agentβ
Currently, a single agent generally supports using only one type of tool at a time.
Specifically, you cannot easily mix a Built-in Tool (like google_search) with Custom Function Tools or other capabilities (like a code executor) within the same agent definition.
Unsupported Example:
You cannot simply list both google_search and your own custom_function in the same tools list for a single agent.
# This approach is NOT currently supported
root_agent = Agent(
name="MixedToolAgent",
model="gemini-2.5-flash",
tools=[google_search, custom_function], # Mixing types may cause issues
)
The Workaround: Multi-Agent Systemsβ
So, how do you build an agent that can search the web and use your custom calculator?
The solution is to use a Multi-Agent System. Instead of one agent doing everything, you create two specialized agents:
- A "Search Specialist" agent with the
google_searchtool. - A "Calculator Specialist" agent with your custom function tools.
- A "Coordinator" agent that delegates tasks to the specialists.
You will learn exactly how to build these powerful multi-agent architectures in Module 15. For now, focus on mastering custom tools within a single agent.