Als Tool für Ihren Agenten können Sie Inline-Python-Code bereitstellen. So können Sie die Funktionen Ihres Agenten flexibel erweitern. Dieser Code nimmt Eingaben des Agenten entgegen und gibt ein Ergebnis zurück, das der Agent in der Unterhaltung verwendet. Sie können beliebige benutzerdefinierte Logik implementieren, eine Verbindung zu proprietären APIs oder Datenbanken herstellen und deterministische Ergebnisse für Aufgaben erzielen, die Präzision erfordern.
Name und Beschreibung
Beim Erstellen des Tools sollten der Name des Tools und der Name der aufzurufenden Hauptfunktion identisch sein und im Snake Case-Format vorliegen.
Docstrings sind ein wichtiger Bestandteil der Definition eines Python-Tools. Der Docstring der Funktion wird als Tool-Beschreibung verwendet, die für Agents bereitgestellt wird, die das Tool nutzen. Sie sollten Docstrings als Erweiterung von Prompts betrachten. Ein klarer, beschreibender und gut strukturierter Docstring wirkt sich direkt darauf aus, wie gut das Modell versteht, was Ihr Tool tut, wann es verwendet werden sollte und welche Argumente angegeben werden müssen. Das ist der Schlüssel zu einer zuverlässigen und genauen Tool-Auswahl.
Umgebung
Im Python-Toolcode haben Sie Zugriff auf bestimmte Klassen und Funktionen, die Ihnen beim Schreiben des Codes helfen. Weitere Informationen finden Sie in der Referenz zur Python-Laufzeit.
Das context-Objekt vom Typ ToolContext ist beispielsweise eine global verfügbare Variable, die eine Momentaufnahme des aktuellen Konversationsstatus enthält.
Sie müssen sie nicht importieren oder als Parameter definieren, sondern können direkt darauf zugreifen.
Sie enthält wertvolle Informationen für die Ausführung komplexer Logik.
Hier finden Sie eine Aufschlüsselung der verfügbaren Schlüssel im Kontextobjekt:
function_call_id: Eine eindeutige ID für den ausgeführten Tool-Aufruf.user_content: Ein Dictionary mit der letzten Nachricht des Nutzers, einschließlich Text und Rolle. Dies ist eines der am häufigsten verwendeten Attribute.state: Ein Dictionary, das den Sitzungsstatus darstellt. Damit können Sie Variablen speichern und abrufen, die über mehrere Konversationsrunden hinweg bestehen bleiben müssen (z. B. Nutzerprofil oder Einkaufswageninhalte).events: Eine Liste aller Ereignisse im Unterhaltungsverlauf bis zu diesem Zeitpunkt. So können Sie Tools mit komplexerem Kontextbewusstsein erstellen.session_id: Die eindeutige Kennung für die gesamte Unterhaltungssitzung.invocation_id: Eine eindeutige Kennung für den aktuellen Zug der Unterhaltung.agent_name: Der Name des Agenten, der das Tool ausführt.
Channelspezifische und modalitätsspezifische Aktionen
Bei der Implementierung von Python-Tools oder Callbacks ist es oft am besten, dass alle Modalitäten und Channels dasselbe Verhalten aufweisen. Es gibt jedoch Fälle, in denen ein channelspezifisches oder modalitätsspezifisches Verhalten gewünscht ist. Sie können den Channel oder die Modalität für eine Unterhaltung mit der globalen Variablen _session prüfen.
Deterministische Tool-Maskierung mit before_model_callback
Wenn ein Agent sowohl Text als auch Sprache unterstützt, enthält das Toolset des Agents häufig eine Mischung aus:
- Chat-/visuelle Tools (interaktive UI-Widgets wie
custom_actions,card_builder,rich_carousel) - Telefonietools (
send_sms_link,transfer_call,dtmf_input)
Auch wenn in den Prompt-Anweisungen für eine bestimmte Modalität (z. B. Sprache) nicht auf die Chat-Tools verwiesen wird, verbraucht das Vorhandensein dieser Tools im Schema des Modells Prompt-Tokens und kann zu unbeabsichtigten Tool-Aufrufen führen.
Im folgenden Beispiel wird before_model_callback zusammen mit llm_request.config.hide_tool() (oder excluded_tools) verwendet. Der Callback prüft _session.get("modality") und maskiert Tools, die nicht für die aktive Modalität geeignet sind, dynamisch, bevor das Modell seine Antwort generiert:
def before_model_callback(
callback_context: CallbackContext, llm_request: LlmRequest
) -> None:
_session = callback_context.variables.get("_session", {})
modality = _session.get("modality", "TEXT").upper()
if modality == "VOICE":
# Hide visual UI widgets & chat-only tools from the model on voice calls
for tool_name in ["custom_actions", "plan_builder", "rich_carousel"]:
llm_request.config.hide_tool(tool_name)
else:
# Hide telephony-specific tools on text chat
for tool_name in ["send_sms_link", "hang_up_call"]:
llm_request.config.hide_tool(tool_name)
Python-Tools, die andere Tools aufrufen
Wenn Sie Python-Code-Tools definieren, können Sie explizit andere Tools aufrufen, die in der Agentenanwendung definiert sind.
Wenn Sie beispielsweise ein OpenAPI-Tool namens crm_service_get_cart_information haben, können Sie es mit dem folgenden Code aufrufen:
# 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({})
Codebeispiele
In den folgenden Abschnitten finden Sie Beispiele.
Letzte Nutzereingabe abrufen
In diesem Beispiel wird eine grundlegende Funktion demonstriert: der Zugriff auf die letzte Nachricht des Nutzers.
Das Tool untersucht das context.user_content-Objekt und extrahiert den Text aus der letzten Nutzeräußerung.
Dieses Muster ist für alle Tools unerlässlich, die eine Aktion direkt auf Grundlage dessen ausführen müssen, was der Nutzer gerade gesagt hat.
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
Variablen abrufen und aktualisieren
Sprachmodelle können den Sitzungsstatus nicht direkt ändern. Das ist so gewollt, da so sichergestellt wird, dass Statusänderungen kontrolliert und vorhersehbar erfolgen. Der Status kann nur über zwei Mechanismen geändert werden: Python-Tools oder Callbacks.
In diesem Beispiel wird gezeigt, wie ein Python-Tool den Status verwalten kann.
Das Tool liest zuerst die aktuelle customer_profile aus context.state.
Anschließend wird die Geschäftslogik ausgeführt (Punkte werden hinzugefügt) und das aktualisierte Profil wird zurück in „context.state“ geschrieben.
Dieser neue Status ist dann für den Agenten und andere Tools für den Rest der Sitzung verfügbar.
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"]
Externe Netzwerkanfragen
Python-Tools können externe Netzwerkanfragen stellen. Das ist nützlich, um Echtzeitdaten abzurufen oder Drittanbieterdienste zu integrieren, für die keine OpenAPI-Spezifikation vorhanden ist. Dies ist eine flexible Alternative zur Verwendung von OpenAPI-basierten Tools.
Wichtige Einschränkungen für Python-Code-Tools:
- Kein Zugriff auf private Netzwerke (Private Network Access, PNA): Python-Code-Tools können nicht direkt auf private IP-Adressen zugreifen oder private DNS-Domains auflösen, auch wenn Service Directory konfiguriert ist. Wenn Sie auf Ressourcen in einem privaten Netzwerk zugreifen müssen, müssen Sie stattdessen einen Tooltyp verwenden, der den Zugriff auf private Netzwerke unterstützt, z. B. OpenAPI- oder MCP-Tools.
- Zugriff auf das öffentliche Internet: Python-Code-Tools können nur auf Endpunkte zugreifen, die öffentlich im Internet verfügbar sind.
Im Beispiel wird die Standardbibliothek „requests“ (in der Umgebung verfügbar) verwendet, um eine öffentliche API aufzurufen und einen zufälligen Fakt abzurufen.
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.get(url=url, json=request_body, headers=headers)
# Example POST request
#res = ces_requests.post(url=url, json=request_body, headers=headers)
# 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