跳转至

工具

工具使智能体能够执行操作,例如获取数据、运行代码、调用外部 API,甚至使用计算机。Agent SDK 中有三类工具: - 托管工具:这些工具与 AI 模型一起在大语言模型(LLM)服务器上运行。OpenAI 提供检索、网页搜索和计算机使用作为托管工具。 - 函数调用:这使你可以将任何 Python 函数用作工具。 - 将智能体作为工具:这使你可以将一个智能体用作工具,允许智能体调用其他智能体而无需交接控制权。

托管工具

使用OpenAIResponsesModel时,OpenAI 提供了一些内置工具: - WebSearchTool 使智能体能够搜索网页。 - FileSearchTool 允许从你的 OpenAI 向量存储中检索信息。 - ComputerTool 允许自动化计算机使用任务。

from agents import Agent, FileSearchTool, Runner, WebSearchTool

agent = Agent(
    name="Assistant",
    tools=[
        WebSearchTool(),
        FileSearchTool(
            max_num_results=3,
            vector_store_ids=["VECTOR_STORE_ID"],
        ),
    ],
)

async def main():
    result = await Runner.run(agent, "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?")
    print(result.final_output)

函数工具

你可以将任何 Python 函数用作工具。Agents SDK 会自动设置该工具: - 工具名称将是 Python 函数的名称(或者你可以提供一个名称)。 - 工具描述将取自函数的文档字符串(或者你可以提供一个描述)。 - 函数输入的架构会根据函数的参数自动创建。 - 每个输入的描述取自函数的文档字符串,除非禁用此功能。

我们使用 Python 的 inspect 模块提取函数签名,使用 griffe 解析文档字符串,并使用 pydantic 创建架构。

import json

from typing_extensions import TypedDict, Any

from agents import Agent, FunctionTool, RunContextWrapper, function_tool


class Location(TypedDict):
    lat: float
    long: float

@function_tool  # (1)!
async def fetch_weather(location: Location) -> str:
    # (2)!
    """获取给定位置的天气。

    参数:
        location:要获取天气的位置。
    """
    # 在实际应用中,我们会从天气 API 获取天气
    return "sunny"


@function_tool(name_override="fetch_data")  # (3)!
def read_file(ctx: RunContextWrapper[Any], path: str, directory: str | None = None) -> str:
    """读取文件内容。

    参数:
        path:要读取的文件路径。
        directory:读取文件的目录。
    """
    # 在实际应用中,我们会从文件系统读取文件
    return "<文件内容>"


agent = Agent(
    name="Assistant",
    tools=[fetch_weather, read_file],  # (4)!
)

for tool in agent.tools:
    if isinstance(tool, FunctionTool):
        print(tool.name)
        print(tool.description)
        print(json.dumps(tool.params_json_schema, indent=2))
        print()
1. 你可以将任何 Python 类型用作函数的参数,并且函数可以是同步或异步的。 2. 如果存在文档字符串,则用于获取描述和参数描述。 3. 函数可以选择接受 context(必须是第一个参数)。你还可以设置覆盖值,例如工具名称、描述、使用哪种文档字符串样式等。 4. 你可以将装饰后的函数传递到工具列表中。

展开查看输出
fetch_weather
获取给定位置的天气。
{
"$defs": {
  "Location": {
    "properties": {
      "lat": {
        "title": "Lat",
        "type": "number"
      },
      "long": {
        "title": "Long",
        "type": "number"
      }
    },
    "required": [
      "lat",
      "long"
    ],
    "title": "Location",
    "type": "object"
  }
},
"properties": {
  "location": {
    "$ref": "#/$defs/Location",
    "description": "要获取天气的位置。"
  }
},
"required": [
  "location"
],
"title": "fetch_weather_args",
"type": "object"
}

fetch_data
读取文件内容。
{
"properties": {
  "path": {
    "description": "要读取的文件路径。",
    "title": "Path",
    "type": "string"
  },
  "directory": {
    "anyOf": [
      {
        "type": "string"
      },
      {
        "type": "null"
      }
    ],
    "default": null,
    "description": "读取文件的目录。",
    "title": "Directory"
  }
},
"required": [
  "path"
],
"title": "fetch_data_args",
"type": "object"
}

自定义函数工具

有时,你可能不想将 Python 函数用作工具。如果你愿意,可以直接创建一个 FunctionTool。你需要提供: - name - description - params_json_schema,即参数的 JSON 模式 - on_invoke_tool,这是一个异步函数,接收上下文和 JSON 字符串格式的参数,并且必须以字符串形式返回工具输出。

from typing import Any

from pydantic import BaseModel

from agents import RunContextWrapper, FunctionTool



def do_some_work(data: str) -> str:
    return "done"


class FunctionArgs(BaseModel):
    username: str
    age: int


async def run_function(ctx: RunContextWrapper[Any], args: str) -> str:
    parsed = FunctionArgs.model_validate_json(args)
    return do_some_work(data=f"{parsed.username} is {parsed.age} years old")


tool = FunctionTool(
    name="处理用户数据",
    description="处理提取的用户数据",
    params_json_schema=FunctionArgs.model_json_schema(),
    on_invoke_tool=run_function,
)

自动参数和文档字符串解析

如前文所述,我们会自动解析函数签名,以提取工具的架构,并解析文档字符串,以提取工具及各个参数的描述。以下是相关说明:

  1. 签名解析通过 inspect 模块完成。我们使用类型注解来理解参数的类型,并动态构建一个 Pydantic 模型来表示整体架构。它支持大多数类型,包括 Python 基础组件、Pydantic 模型、TypedDicts 等。
  2. 我们使用 griffe 来解析文档字符串。支持的文档字符串格式有 googlesphinxnumpy。我们会尝试自动检测文档字符串格式,但这只是尽力而为,你可以在调用 function_tool 时显式设置格式。你也可以通过将 use_docstring_info 设置为 False 来禁用文档字符串解析。

架构提取的代码位于agents.function_schema

作为工具的智能体

在某些工作流程中,你可能希望由一个中央智能体来协调一组专门的智能体,而不是进行控制权交接。你可以通过将智能体建模为工具来实现这一点。

from agents import Agent, Runner
import asyncio

spanish_agent = Agent(
    name="Spanish agent",
    instructions="You translate the user's message to Spanish",
)

french_agent = Agent(
    name="French agent",
    instructions="You translate the user's message to French",
)

orchestrator_agent = Agent(
    name="orchestrator_agent",
    instructions=(
        "You are a translation agent. You use the tools given to you to translate."
        "If asked for multiple translations, you call the relevant tools."
    ),
    tools=[
        spanish_agent.as_tool(
            tool_name="translate_to_spanish",
            tool_description="Translate the user's message to Spanish",
        ),
        french_agent.as_tool(
            tool_name="translate_to_french",
            tool_description="Translate the user's message to French",
        ),
    ],
)

async def main():
    result = await Runner.run(orchestrator_agent, input="Say 'Hello, how are you?' in Spanish.")
    print(result.final_output)

自定义工具智能体

agent.as_tool 函数是一个便捷方法,可轻松将智能体转换为工具。不过,它并不支持所有配置;例如,你无法设置 max_turns。对于高级用例,可在工具实现中直接使用 Runner.run

@function_tool
async def run_my_agent() -> str:
  """一个使用自定义配置运行智能体的工具。

    agent = Agent(name="My agent", instructions="...")

    result = await Runner.run(
        agent,
        input="...",
        max_turns=5,
        run_config=...
    )

    return str(result.final_output)

处理函数工具中的错误

通过 @function_tool 创建函数工具时,可以传入 failure_error_function。这是一个在工具调用失败时向大语言模型(LLM)提供错误响应的函数。 - 默认情况下(即未传入任何内容),它会运行 default_tool_error_function,告知大语言模型发生了错误。 - 如果传入自定义的错误函数,则会运行该函数,并将响应发送给大语言模型。 - 如果显式传入 None,那么任何工具调用错误将被重新抛出,由你来处理。如果模型生成的 JSON 无效,可能会抛出 ModelBehaviorError;如果你的代码崩溃,可能会抛出 UserError 等。

如果手动创建 FunctionTool 对象,则必须在 on_invoke_tool 函数内处理错误。