Python 代码工具

您可以提供内嵌 Python 代码作为智能体的工具,从而灵活地扩展智能体的功能。此代码可以接受代理提供的输入,并返回代理在对话中使用的结果。您可以实现任何自定义逻辑,连接到专有 API 或数据库,并确保需要精确度的任务获得确定性结果。

名称及描述

创建工具时,工具的名称和要调用的主函数的名称应采用蛇形命名法,且名称相同。

文档字符串是定义 Python 工具的关键部分。 函数的文档字符串用作工具说明,提供给使用该工具的代理。 您应将文档字符串视为提示的扩展。 清晰、描述性强且结构合理的 docstring 会直接影响模型对以下方面的理解程度:工具的功能、使用时机以及要提供的实参。这是实现可靠且准确的工具选择的关键。

环境

在 Python 工具代码中,您可以访问某些类和函数,这些类和函数可帮助您编写代码。如需了解详情,请参阅 Python 运行时参考文档

例如,context 对象是一个全局可用变量,可提供当前对话状态的快照。您无需导入或将其定义为参数,可以直接访问它。 它包含用于执行复杂逻辑的有价值的信息。

下面简要介绍了上下文对象中的可用键:

  • function_call_id:正在执行的特定工具调用的唯一 ID。
  • user_content:一个字典,包含用户的最新消息,包括文本和角色。 这是最常用的属性之一。
  • state:表示会话状态的字典。您可以使用此功能来存储和检索需要在对话中的多个轮次之间保持不变的变量(用户个人资料、购物车内容等)。
  • events:对话历史记录中截至目前的所有事件的列表,可让您构建具有更复杂上下文感知能力的工具。
  • session_id:整个对话会话的唯一标识符。
  • invocation_id:当前对话轮次的唯一标识符。
  • agent_name:执行工具的代理的名称。

Python 工具调用其他工具

定义 Python 代码工具时,您可以显式调用代理应用中定义的其他工具。例如,如果您有一个名为 crm_service_get_cart_information 的 OpenAPI 工具,则可以使用以下代码调用该工具:

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

代码示例

以下部分提供了示例。

获取上次用户输入

此示例演示了一项基本功能:访问用户的最新消息。该工具会检查 context.user_content 对象,并从最后一轮用户对话中提取文本。对于任何需要直接根据用户刚刚说的话执行操作的工具,此模式都至关重要。

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

获取和更新变量

语言模型无法直接修改会话状态。 这是有意为之,因为这样可确保以可控且可预测的方式处理状态变化。只能通过两种机制修改状态:Python 工具或回调。

此示例展示了 Python 工具如何管理状态。 该工具首先从 context.state 读取当前 customer_profile。 然后,它会执行其业务逻辑(添加积分),并将更新后的个人资料写回 context.state。然后,在会话的剩余时间内,智能体和其他工具便可使用此新状态。

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

外部网络请求

Python 工具可以发出外部网络请求,这对于提取实时数据或与没有 OpenAPI 规范的第三方服务集成非常有用。这提供了一种灵活的替代方案,可用于替代基于 OpenAPI 的工具。该示例使用标准 requests 库(在环境中可用)调用公共 API 并检索随机事实。

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