You can provide inline Python code as a tool for your agent, which provides flexibility to extend your agent's capabilities. This code can accept inputs provided by the agent, and return a result used by the agent in the conversation. You can implement any custom logic, connect to proprietary APIs or databases, and ensure deterministic outcomes for tasks that require precision.
Name and description
When creating the tool, the name of the tool and the name of the main function to be called should be the same name in snake case.
Docstrings are a critical part of defining a Python tool. The function's docstring is used as the tool description, which is provided to agents that make use of the tool. You should think of docstrings as an extension of prompting. A clear, descriptive, and well-structured docstring directly influences how well the model understands what your tool does, when to use it, and what arguments to provide. This is the key to achieving reliable and accurate tool selection.
Environment
In your Python tool code, you have access to certain classes and functions that help you write the code. For more information, see the Python runtime reference.
For example,
the context object is a globally available variable
that provides a snapshot of the current conversation state.
You don't need to import it or define it as a parameter;
you can access it directly.
It contains valuable information for executing complex logic.
Here is a breakdown of the available keys in the context object:
function_call_id: A unique ID for the specific tool call being executed.user_content: A dictionary containing the most recent message from the user, including the text and role. This is one of the most commonly used attributes.state: A dictionary representing the session state. You can use this to store and retrieve variables that need to persist across multiple turns in a conversation (user profile, shopping cart contents, and so on).events: A list of all events in the conversation history up to this point, allowing you to build tools with more complex contextual awareness.session_id: The unique identifier for the entire conversation session.invocation_id: A unique identifier for the current turn of the conversation.agent_name: The name of the agent that is executing the tool.
Python tools calling other tools
When defining Python code tools,
you can explicitly call other tools defined in the agent application.
For example,
if you have an OpenAPI tool called crm_service_get_cart_information,
you can call that tool with the following code:
# Deterministically call another tool from this tool.
# This syntax for OpenAPI spec tool is:
# tools.<tool_name>_<endpoint_name>({tool_args})
res = tools.crm_service_get_cart_information({})
Code samples
The following sections provide samples.
Get last user input
This sample demonstrates a fundamental capability:
accessing the user's most recent message.
The tool inspects the context.user_content object
and extracts the text from the last user turn.
This pattern is essential for any tool that needs to perform an action
based directly on what the user just said.
from typing import Optional
# Docstrings in tools are important because they are directly
# sent to the model as the description for the tool. You should
# think of docstrings as an extension of prompting. Clear and
# descriptive docstrings will yield higher quality tool
# selection from the model.
def get_last_user_utterance() -> Optional[str]:
"""
Retrieves the most recent message sent by the user from the conversation history.
Returns:
The text of the last user message, or None if no user messages are found.
"""
# The 'context.user_content' contains the last input data
# provided by the user.
# We can filter it to find only the text input from the user.
user_messages = [
part["text"] for part in context.user_content["parts"]
if context.user_content["role"] == "user"
]
if user_messages:
# The most recent message is the first one in the list.
return user_messages[0]
return None
Get and update variables
Language models cannot directly modify session state. This is intentional, as it ensures that state changes are handled in a controlled and predictable way. State can only be modified through two mechanisms: Python Tools or Callbacks.
This sample shows how a Python tool can manage state.
The tool first reads the current customer_profile from context.state.
It then performs its business logic (adding points)
and writes the updated profile back into context.state.
This new state is then be available to the agent
and other tools for the remainder of the session.
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any
# Using Pydantic defines the expected structure of your state variables. This makes your code more reliable and easier to
# debug.
class CustomerProfile(BaseModel):
email: Optional[str] = None
loyalty_points: int = Field(default=0, ge=0) # Must be >= 0
plan: str = "Standard"
# Docstrings in tools are important because they are directly
# sent to the model as the description for the tool. You should
# think of docstrings as an extension of prompting. Clear and
# descriptive docstrings will yield higher quality tool
# selection from the model.
def update_customer_loyalty_points(points_to_add: int) -> Dict[str, Any]:
"""
Adds loyalty points to the customer's profile and returns the updated profile.
Args:
points_to_add: The number of loyalty points to add to the existing total.
Returns:
A dictionary containing the customer's updated profile information.
"""
# 1. Get the current profile data from the session state.
# The .get() method safely returns an empty dict if
# 'customer_profile' doesn't exist.
current_profile_data = context.state.get("customer_profile", {})
# 2. Load the data into a Pydantic model for validation and easy access.
profile = CustomerProfile(**current_profile_data)
# 3. Perform the business logic.
# Print statements can be used for debugging and will show
# up in the tracing details.
profile.loyalty_points += points_to_add
print(f"Updated loyalty points to: {profile.loyalty_points}")
# 4. Save the updated data back into the session state.
# .model_dump() converts the Pydantic model back to a
# dictionary.
context.state["customer_profile"] = profile.model_dump()
return context.state["customer_profile"]
External network requests
Python tools can make external network requests, which is useful for fetching real-time data or integrating with third-party services that don't have an OpenAPI specification. This provides a flexible alternative to using OpenAPI-based tools. The sample uses the standard requests library (available in the environment) to call a public API and retrieve a random fact.
from typing import Optional
# Docstrings in tools are important because they are directly sent to the model as the
# description for the tool. You should think of docstrings as an extension of prompting.
# Clear and descriptive docstrings will yield higher quality tool selection from the model.
def get_random_fact() -> Optional[str]:
"""
Fetches a random fact from a public API.
Returns:
A string containing the fact on success, or None if an error occurs.
"""
# The 'ces_requests' library is inspired by 'requests', a standard and powerful way in Python
# to make HTTP network calls to any external API, just like you would with `curl` or a web browser.
url = "https://uselessfacts.jsph.pl/api/v2/facts/random"
try:
# This example calls a public API that is completely open and requires no authentication
# (like an API key). Many other APIs for services like weather or e-commerce require you
# to send credentials, often as an API key in the request headers or parameters.
res = ces_requests.request(method=HttpMethod.GET, url=url)
# This is a standard practice with 'ces_requests' to check if the call was successful. It will
# raise an error for statuses like 404 or 500.
res.raise_for_status()
fact_data = res.json()
return fact_data.get("text")
except:
return None