Herramientas de código de Python

Puedes proporcionar código de Python intercalado como herramienta para tu agente, lo que te brinda flexibilidad para extender sus capacidades. Este código puede aceptar entradas proporcionadas por el agente y devolver un resultado que el agente podrá usar en la conversación. Puedes implementar cualquier lógica personalizada, conectarte a bases de datos o APIs propietarias, y garantizar resultados determinísticos para las tareas que requieren precisión.

Nombre y descripción

Cuando crees la herramienta, el nombre de la herramienta y el nombre de la función principal que se llamará deben ser el mismo nombre en formato snake_case.

Las cadenas de documentación son una parte fundamental de la definición de una herramienta de Python. La cadena de documentación de la función se usa como descripción de la herramienta, que se proporciona a los agentes que la usan. Debes considerar las cadenas de documentación como una extensión de las instrucciones. Una cadena de documentación clara, descriptiva y bien estructurada influye directamente en qué tan bien el modelo comprende qué hace tu herramienta, cuándo usarla y qué argumentos proporcionar. Esta es la clave para lograr una selección de herramientas confiable y precisa.

Entorno

En el código de tu herramienta de Python, tienes acceso a ciertas clases y funciones que te ayudan a escribir el código. Para obtener más información, consulta la referencia del entorno de ejecución de Python.

Por ejemplo, el objeto context de tipo ToolContext es una variable disponible de forma global que proporciona una instantánea del estado actual de la conversación. No es necesario que lo importes ni lo definas como un parámetro; puedes acceder a él directamente. Contiene información valiosa para ejecutar lógica compleja.

A continuación, se muestra un desglose de las claves disponibles en el objeto de contexto:

  • function_call_id: Es un ID único para la llamada a la herramienta específica que se está ejecutando.
  • user_content: Es un diccionario que contiene el mensaje más reciente del usuario, incluido el texto y el rol. Este es uno de los atributos de uso frecuente.
  • state: Es un diccionario que representa el estado de la sesión. Puedes usarlo para almacenar y recuperar variables que deben persistir en varios turnos de una conversación (perfil del usuario, contenido del carrito de compras, etcétera).
  • events: Es una lista de todos los eventos del historial de conversación hasta este punto, lo que te permite crear herramientas con una mayor conciencia contextual.
  • session_id: Es el identificador único de toda la sesión de conversación.
  • invocation_id: Es un identificador único del turno actual de la conversación.
  • agent_name: Es el nombre del agente que ejecuta la herramienta.

Acciones específicas del canal y la modalidad

Cuando implementas herramientas o devoluciones de llamadas de Python, a menudo es mejor tener el mismo comportamiento para todas las modalidades y canales, pero hay casos en los que se desea un comportamiento específico del canal o de la modalidad. Puedes verificar el canal o la modalidad de una conversación con la variable global _session.

Enmascaramiento determinístico de herramientas con before_model_callback

Cuando un agente admite texto y voz, su conjunto de herramientas suele contener una combinación de los siguientes elementos:

  • Herramientas visuales o de chat (widgets interactivos de la IU, como custom_actions, card_builder y rich_carousel)
  • Herramientas de telefonía (send_sms_link, transfer_call, dtmf_input)

Aunque las instrucciones de la instrucción para una modalidad determinada (como la voz) no hagan referencia a las herramientas de chat, tener esas herramientas presentes en el esquema del modelo consume tokens de la instrucción y podría provocar invocaciones no deseadas de herramientas.

En el siguiente ejemplo, se usa before_model_callback junto con llm_request.config.hide_tool() (o excluded_tools). La devolución de llamada verifica _session.get("modality") y enmascara de forma dinámica las herramientas que no son aplicables a la modalidad activa antes de que el modelo genere su respuesta:

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)

Herramientas de Python que llaman a otras herramientas

Cuando definas herramientas de código de Python, puedes llamar de forma explícita a otras herramientas definidas en la aplicación del agente. Por ejemplo, si tienes una herramienta de OpenAPI llamada crm_service_get_cart_information, puedes llamarla con el siguiente código:

# 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({})

Muestras de código

En las siguientes secciones, se proporcionan ejemplos.

Obtén la última entrada del usuario

En este ejemplo, se muestra una capacidad fundamental: acceder al mensaje más reciente del usuario. La herramienta inspecciona el objeto context.user_content y extrae el texto del último turno del usuario. Este patrón es esencial para cualquier herramienta que necesite realizar una acción en función de lo que acaba de decir el usuario.

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

Cómo obtener y actualizar variables

Los modelos de lenguaje no pueden modificar directamente el estado de la sesión. Esto es intencional, ya que garantiza que los cambios de estado se controlen de una manera predecible. El estado solo se puede modificar a través de dos mecanismos: herramientas de Python o devoluciones de llamada.

En este ejemplo, se muestra cómo una herramienta de Python puede administrar el estado. Primero, la herramienta lee el customer_profile actual de context.state. Luego, ejecuta su lógica empresarial (agregar puntos) y vuelve a escribir el perfil actualizado en context.state. Luego, este nuevo estado estará disponible para el agente y otras herramientas durante el resto de la sesión.

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"]

Solicitudes de red externas

Las herramientas de Python pueden realizar solicitudes de red externas, lo que resulta útil para recuperar datos en tiempo real o integrarse con servicios de terceros que no tienen una especificación de OpenAPI. Esto proporciona una alternativa flexible al uso de herramientas basadas en OpenAPI.

Limitaciones clave de las herramientas de código de Python:

  • Sin acceso a redes privadas (PNA): Las herramientas de código de Python no pueden acceder directamente a direcciones IP privadas ni resolver dominios DNS privados, incluso si se configura el Directorio de servicios. Si necesitas acceder a recursos dentro de una red privada, debes usar un tipo de herramienta que admita el acceso a la red privada (como las herramientas de OpenAPI o MCP).
  • Acceso a Internet pública: Las herramientas de código de Python solo pueden acceder a los extremos que están disponibles públicamente en Internet.

En el ejemplo, se usa la biblioteca de solicitudes estándar (disponible en el entorno) para llamar a una API pública y recuperar un dato aleatorio.

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