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 提供了几个内置工具:
WebSearchTool让 agent 能够搜索网络。FileSearchTool允许从你的 OpenAI Vector Stores 中检索信息。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, "考虑到我的喜好和今天旧金山的天气,我应该去哪家咖啡店?")
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()
- 你可以使用任何 Python 类型作为函数参数,函数可以是同步或异步的。
- 如果存在文档字符串,它们会被用来获取描述和参数描述
- 函数可以选择接受
context(必须是第一个参数)。你还可以设置覆盖项,比如工具名称、描述、使用哪种文档字符串样式等。 - 你可以将装饰过的函数传递给工具列表。
展开查看输出
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(自动参数和文档字符串解析)
如前所述,我们自动解析函数签名以提取工具的模式,并解析文档字符串以提取工具和各个参数的描述。关于这一点的一些注意事项:
- 签名解析是通过
inspect模块完成的。我们使用类型注解来理解参数的类型,并动态构建一个 Pydantic 模型来表示整体模式。它支持大多数类型,包括 Python 原始类型、Pydantic 模型、TypedDicts 等。 - 我们使用
griffe解析文档字符串。支持的文档字符串格式有google、sphinx和numpy。我们尝试自动检测文档字符串格式,但这是尽力而为,你可以在调用function_tool时明确设置它。你也可以通过设置use_docstring_info为False来禁用文档字符串解析。
模式提取的代码位于 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 函数内部处理错误。