Skip to content

Tools(工具)

Tools 让 agents 能够执行各种操作:比如获取数据、运行代码、调用外部 API,甚至使用电脑。Agent SDK 中有三类工具:

  • Hosted tools(托管工具):这些工具在 LLM 服务器上与 AI 模型一起运行。OpenAI 提供了检索、网络搜索和电脑使用作为托管工具。
  • Function calling(函数调用):这让你可以将任何 Python 函数作为工具使用。
  • Agents as tools(作为工具的 agents):这允许你将一个 agent 作为工具使用,使 Agents 能够调用其他 agents 而不必移交控制权。

Hosted tools(托管工具)

当使用 OpenAIResponsesModel 时,OpenAI 提供了几个内置工具:

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, "考虑到我的喜好和今天旧金山的天气,我应该去哪家咖啡店?")
    print(result.final_output)

Function tools(函数工具)

你可以将任何 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)!
    """获取指定位置的天气。

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


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

    Args:
        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"
}

Custom function tools(自定义函数工具)

有时,你可能不想使用 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 "完成"


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} 今年 {parsed.age} 岁")


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

Automatic argument and docstring parsing(自动参数和文档字符串解析)

如前所述,我们自动解析函数签名以提取工具的模式,并解析文档字符串以提取工具和各个参数的描述。关于这一点的一些注意事项:

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

模式提取的代码位于 agents.function_schema

Agents as tools(作为工具的 agents)

在某些工作流程中,你可能希望一个中央 agent 协调一个专业 agents 网络,而不是移交控制权。你可以通过将 agents 建模为工具来实现这一点。

from agents import Agent, Runner
import asyncio

spanish_agent = Agent(
    name="西班牙语 agent",
    instructions="你将用户的消息翻译成西班牙语",
)

french_agent = Agent(
    name="法语 agent",
    instructions="你将用户的消息翻译成法语",
)

orchestrator_agent = Agent(
    name="协调 agent",
    instructions=(
        "你是一个翻译 agent。你使用给你的工具进行翻译。"
        "如果被要求多种翻译,你调用相关工具。"
    ),
    tools=[
        spanish_agent.as_tool(
            tool_name="translate_to_spanish",
            tool_description="将用户的消息翻译成西班牙语",
        ),
        french_agent.as_tool(
            tool_name="translate_to_french",
            tool_description="将用户的消息翻译成法语",
        ),
    ],
)

async def main():
    result = await Runner.run(orchestrator_agent, input="用西班牙语说'你好,你好吗?'")
    print(result.final_output)

Handling errors in function tools(处理函数工具中的错误)

当你通过 @function_tool 创建函数工具时,你可以传递一个 failure_error_function。这是一个函数,在工具调用崩溃的情况下向 LLM 提供错误响应。

  • 默认情况下(即如果你不传递任何内容),它会运行一个 default_tool_error_function,告诉 LLM 发生了错误。
  • 如果你传递自己的错误函数,它会运行该函数,并将响应发送给 LLM。
  • 如果你明确传递 None,那么任何工具调用错误都会被重新引发,供你处理。这可能是 ModelBehaviorError(如果模型生成了无效的 JSON),或者是 UserError(如果你的代码崩溃)等。

如果你手动创建一个 FunctionTool 对象,那么你必须在 on_invoke_tool 函数内部处理错误。