工具
工具使智能体能够执行操作,例如获取数据、运行代码、调用外部 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()
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,
)
自动参数和文档字符串解析
如前文所述,我们会自动解析函数签名,以提取工具的架构,并解析文档字符串,以提取工具及各个参数的描述。以下是相关说明:
- 签名解析通过
inspect
模块完成。我们使用类型注解来理解参数的类型,并动态构建一个 Pydantic 模型来表示整体架构。它支持大多数类型,包括 Python 基础组件、Pydantic 模型、TypedDicts 等。 - 我们使用
griffe
来解析文档字符串。支持的文档字符串格式有google
、sphinx
和numpy
。我们会尝试自动检测文档字符串格式,但这只是尽力而为,你可以在调用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
函数内处理错误。