OpenAI Agents SDK 完全指南:从“只会动嘴”到“真正干活”的AI

3 阅读17分钟

你有没有遇到过这样的情况——用AI写了一份营销方案,还要自己复制粘贴发邮件;AI跑完了数据分析,还得自己写代码取数;大模型给你列了操作步骤,最后每一步都需要你亲手去点。这就是传统大模型最大的痛点:它像一位聪明的顾问,什么都懂,但就是不会自己动手

OpenAI Agents SDK就是来解决这个“光说不练”的问题的——它让AI从只会回答问题的“解说员”,变成了真正能干活、能调用工具、能处理复杂任务的“执行者”。截至2026年4月30日,这个Python框架在GitHub上已经接近22k颗星,且半个月前刚经历了一次从底层彻底重写。

这篇文章不讲抽象概念,不堆砌专业术语,我们从日常生活中最直观的例子出发,一步步带你搞懂Agent和Tool是怎么回事,然后直接上手写代码。


一、为什么你的AI只能“动嘴”?从两大局限说起

现代大模型本身就像一个天才:你问它“明天天气怎么样”,它能给你写出几百字的天气分析,但它就是查不到明天的实时天气,因为它最后一次训练是在几个月前。你让它“把我桌面上的PDF整理成表格”,它能告诉你具体操作步骤,但它自己不会去双击图标、不会去读文件窗口的内容、也不会帮你拖拽生成表格。

在2026年4月之前,开发者要让AI真正操作外部世界的任务时,通常需要自己搭建一套框架:用LangChain管理流程、用Docker做环境隔离、用Redis做状态缓存,写了一堆基建代码才能真正开始做业务逻辑。

简单来说就是:你有最聪明的“大脑”,但它没有“手脚”。它只会思考,不会行动。

这就像你请了一位顶级顾问,他给你一份完美的策划案之后,你还要自己找团队,甚至自己动手执行。这时候你就想问了:“能不能让它替我执行?”

Agent就是那个能“执行”的答案,而Tool就是它的“五指”。


二、带你彻底搞懂Tool(工具):AI的“五指”

Tool到底是什么

我们用一个最日常的例子来理解。你晚上加班发现桌上出现了零食,可能是四个小伙伴中的一个塞过来的:办公桌对面的大伟、左边工位的莉莉、右边工位的哈娜,或者老板假借他人名义送来的。为了搞清是谁放的,你会列一个名单逐一排查。

Agent调用Tool的道理是一模一样的。Agent接到任务后,会判断它需要调用哪些“名单上的手段”来解决问题——它自己有一个工具列表,里面放着预定义好的Tool,它根据用户的问题,自行决定调用哪一个或哪几个。

在技术层面,Tool就是一个被装饰器标记的普通Python函数。在2026年4月的最新版本中,Agents SDK支持以Python函数、外来托管能力、甚至将另一个Agent本身作为Tool加入工具列表。

Tool帮你解决了什么问题

过去的大模型有三大局限:知识“超时”(学的资料截止在某一天)、无法实际操作(不能调用API、不能读写文件、不能访问内部系统)、以及容易“编造”答不出就拿假数据凑合——这就是俗称的AI“幻觉”。

Tool一次性突破了这三个局限。它让AI可以实时查询最新信息、执行真正的API操作、从企业数据库直接拉数据。当你把Agent + Tool组合使用时,AI就不再是“有建议但不干活”了——而是变成了一位能独立完成完整任务的“数字员工”。

Agent调用Tool的典型流程

假设你构建了一个天气助手Agent,并给它绑定了一个“获取实况气温”的Tool。用户发来一句询问:“厦门现在的温度是多少?”

Agent先收到用户的问题,读完立刻判断:“哦,这个问题需要我调用那个获取实时温度的Tool。”然后它将厦门作为参数传过去,Tool执行完之后拿到真实温度数据,返回给Agent。Agent再用自己的语言重组温度信息,用通顺的句子答复给用户。

这个过程对Agent来说几乎毫不费力,因为Tool已经帮它拿到了原本不可能拿到的实时数据。

三、从源码看懂Tool:手把手创建一个天气查询功能

聊完了原理,我们直接动手。以下这段Python代码定义了两个Tool来说明两个最常用的Tool粒度:一个是单独拉数据的Tool(查天气),一个是串联起来的Tool(多城市+空气质量)。

在开始之前,记得先安装Python包(在终端运行一行命令)并准备环境变量。

pip install openai-agents

在你的项目目录中创建一个.env文件,填入你的模型API配置。这里用国内的阿里通义千问Qwen作为例子:

OPENAI_API_KEY=你的通义千问API密钥
OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
OPENAI_MODEL_NAME=qwen-plus

现在来看一个完整的Tool定义和调用示例:

import asyncio
import json
import os
from openai import AsyncOpenAI
from agents import Agent, OpenAIChatCompletionsModel, Runner, function_tool
from dotenv import load_dotenv
 
# ----- 第一步:加载环境变量中的模型API密钥 -----
load_dotenv()
 
# ----- 第二步:创建一个异步模型客户端(必须AsyncOpenAI)-----
client = AsyncOpenAI(
    base_url=os.getenv("OPENAI_BASE_URL"),
    api_key=os.getenv("OPENAI_API_KEY")
)
 
# ----- 第三步:用装饰器定义第一个Tool(查当前天气)-----
@function_tool
def get_current_weather(city: str) -> str:
    """
    获取指定城市的实时天气信息。
    参数:
        city: 城市名称(如"武汉"、"上海")
    返回:
        JSON字符串,包含天气描述和当前温度。
    """
    # 这里本应调用真正的实时天气API,目前用模拟数据演示
    weather_data = {
        "city": city,
        "condition": "晴天 ☀️",
        "temperature_celsius": 24,
        "wind": "东南风3级"
    }
    return json.dumps(weather_data, ensure_ascii=False)
 
# ----- 第四步:定义第二个Tool(查询空气质量AQI)-----
@function_tool
def get_air_quality(city: str) -> str:
    """
    获取指定城市的空气质量指数(AQI)。
    参数:
        city: 城市名称
    返回:
        JSON字符串,包含AQI值和建议。
    """
    # 模拟空气质量数据
    air_data = {
        "city": city,
        "aqi": 48,
        "level": "优",
        "advice": "非常适合户外活动"
    }
    return json.dumps(air_data, ensure_ascii=False)
 
# ----- 第五步:创建Agent,并绑定上面两个Tool -----
async def main():
    agent = Agent(
        name="全能天气助手",
        instructions=(
            "你是一个专业的城市天气查询助手。当用户问某个城市的天气时,"
            "你必须调用 get_current_weather 来获取实时天气。"
            "当用户问空气质量时,必须调用 get_air_quality。"
            "如果用户同时问了天气和空气质量,你需要依次调用两个函数。"
            "最终用亲切友好的口吻整合结果回答用户。"
        ),
        model=OpenAIChatCompletionsModel(
            model=os.getenv("OPENAI_MODEL_NAME"),
            openai_client=client
        ),
        tools=[get_current_weather, get_air_quality],  # 把两个工具绑定给Agent
    )
 
    # ----- 第六步:运行Agent,可以看到它自动调用了两个Tool -----
    result = await Runner.run(
        agent,
        "重庆市今天的天气怎么样?空气质量如何?适合出门散步吗?"
    )
    print("\n=== Agent最终回复 ===")
    print(result.final_output)
 
if __name__ == "__main__":
    asyncio.run(main())

上述代码的关键点:

  • 每个函数用 @function_tool 装饰器标记后,Agent就能在决策过程中“看到”这些函数。
  • Tool的执行结果可以是字符串、JSON、甚至是完整的Pydantic数据结构体,Agent会自动接管。
  • instructions 字段写得字越多越好:告诉Agent“什么场景下调用哪个Tool”,这比提示它“别猜答案直接取工具数据”效果要好得多。

四、Agent(智能体)的完整概念与三种模型配置

说完了Tool,现在来组装整个AI的“大脑”。主流的Agent开发SDK都会遇到一个实际问题:你用的模型不是OpenAI官方模型怎么办?万一你有多个不同大模型需要串在一起怎么配置?

OpenAI Agents SDK的一个重要设计,就是从设计之初就跟模型供应商解耦:它能兼容100多种不同的LLM(阿里通义、智谱GLM、Claude、甚至开源的LLaMA等)

针对不同使用场景,新版SDK提供了三种模型客户端配置方式:


方式一:全局一次性配置(适合只用一个模型的场景)

如果你手头只有一个大模型(例如阿里通义千问Qwen),并且所有Agent都用它,用全局配置最快。

import asyncio
import os
from openai import AsyncOpenAI
from agents import (
    Agent,
    Runner,
    set_default_openai_client,
    set_default_openai_api,
    set_tracing_disabled,
)
from dotenv import load_dotenv
 
load_dotenv()
 
# ---- 创建唯一的客户端 ----
client = AsyncOpenAI(
    base_url=os.getenv("OPENAI_BASE_URL"),
    api_key=os.getenv("OPENAI_API_KEY")
)
 
# ---- 设置全局长效配置 ----
set_default_openai_client(client)               # 所有Agent共用此client
set_default_openai_api("chat_completions")      # 必须指定这行,否则报错400
set_tracing_disabled(disabled=True)             # 可选,关闭追踪以防止401报错
 
async def main():
    agent = Agent(
        name="写作助理",
        instructions="你是一个专业的诗歌写手,所有回复都用古典七言绝句。",
        model="qwen-plus"  # 注意这里只写字符串,不用再导入模型对象
    )
    result = await Runner.run(agent, "写一首关于大雨的古风诗")
    print(result.final_output)
 
if __name__ == "__main__":
    asyncio.run(main())

这种方式的优点就是干净简单,适合一个团队内统一了模型选型、快速跑通MVP的场景。

方式二:运行时切换模型(适合多模型混合)

许多场景需要在运行时动态改变模型:例如白天用通义千问跑知识库问答,晚上用智谱GLM跑针对科研论文的分析。使用自定义Provider的方式,不需要重启进程就可以切换模型。

from __future__ import annotations
import asyncio
import os
from openai import AsyncOpenAI
from agents import (
    Agent,
    Model,
    ModelProvider,
    OpenAIChatCompletionsModel,
    RunConfig,
    Runner,
)
from dotenv import load_dotenv
 
load_dotenv()
 
client = AsyncOpenAI(
    base_url=os.getenv("OPENAI_BASE_URL"),
    api_key=os.getenv("OPENAI_API_KEY")
)
 
# ---- 自定义Provider:实现get_model接口 ----
class CustomModelProvider(ModelProvider):
    def get_model(self, model_name: str) -> Model:
        return OpenAIChatCompletionsModel(
            model=model_name,
            openai_client=client
        )
 
CUSTOM_PROVIDER = CustomModelProvider()
 
async def main():
    agent = Agent(
        name="多模型翻译员",
        instructions="你是专业的中英互译助手,回复要精炼准确。",
        model="qwen-plus"     # 目前用字符串先占位
    )
 
    result = await Runner.run(
        agent,
        input="请将'人工智能正在改变世界'翻译成英语。",
        run_config=RunConfig(model_provider=CUSTOM_PROVIDER),
    )
 
    print("翻译结果:", result.final_output)
 
if __name__ == "__main__":
    asyncio.run(main())

方式三:Agent绑定单一模型(最细粒度,适合多Agent分工)

如果你的系统里有一张复杂的分工表——接待Agent用通义千问,风控Agent用DeepSeek,科研Agent用GLM——那就用这种直接绑定的方法。

import asyncio
import os
from openai import AsyncOpenAI
from agents import Agent, OpenAIChatCompletionsModel, Runner
from dotenv import load_dotenv
 
load_dotenv()
 
client = AsyncOpenAI(
    base_url=os.getenv("OPENAI_BASE_URL"),
    api_key=os.getenv("OPENAI_API_KEY")
)
 
async def main():
    agent = Agent(
        name="安全敏感助手",
        instructions="你只处理有严格合规要求的政务类问题。",
        model=OpenAIChatCompletionsModel(          # 直接传模型对象,不是字符串
            model=os.getenv("OPENAI_MODEL_NAME"),
            openai_client=client
        ),
    )
 
    result = await Runner.run(agent, "请帮我解读最近的个人所得税政策变化。")
    print(result.final_output)
 
if __name__ == "__main__":
    asyncio.run(main())

五、Agent的三种运行模式(同步、异步、流式)

初学者最常困惑的是:我的大模型响应很慢怎么办?我的场景需要服务器实时逐字输出吗?Agents SDK提供了三种灵活的运行模式来解决不同场景下的诉求:

  • 同步调用:适合一次性跑通Shell脚本,或者半夜跑批量数据。
  • 异步调用:适合用FastAPI/Django做大模型的REST后端服务。
  • 流式调用:适合做网页对话时“打字机效果”,即UI上一字一字往外蹦回答。

逻辑上只需要记住一点:如果你不需要UI实时效果,就用最简单的 async/await 异步模式。只有当你在做聊天室、SSE、实时语音对话场景时,才采用第三种流式。

流式调用最像我们平时跟ChatGPT打字的情形:

async def stream_example():
    # 启动流式运行
    streaming_result = Runner.run_streamed(agent, "写一篇关于森林的短日记")
 
    # 接收模型逐个字吐出的增量
    async for event in streaming_result.stream_events():
        if event.type == "raw_response_event":
            delta = event.data.delta
            if delta:
                print(delta, end="", flush=True)   # 实时打印,不换行
 
    print("\n--- 完整全文在此 ---")
    print(streaming_result.final_output)

六、实战争取——让Agent不仅回答问题,还能输出结构化JSON

你和后端工程师对接时,最难受的一个点往往是:大模型输出一段散装文本,后端要费很大力气做正则清洗、提取关键字段。Agents SDK直接内置了Pydantic输出格式,让大模型输出严格符合JSON Schema的结构化Object。

我们拿物流行业的实际场景来说明:假设你的Agent需要调用物流API查询某张运单的流转记录,之后把这批记录同步到数据库,Agent只负责组装符合 ShipmentTrace 格式的结果,剩下的入库由后端用强类型接收。

下面这段代码里,Agent输出的结果不是自然语言,而是一个可直接存数据库的Python对象:

import asyncio
import json
import os
from openai import AsyncOpenAI
from agents import Agent, OpenAIChatCompletionsModel, Runner, function_tool
from pydantic import BaseModel
from dotenv import load_dotenv
 
load_dotenv()
 
client = AsyncOpenAI(
    base_url=os.getenv("OPENAI_BASE_URL"),
    api_key=os.getenv("OPENAI_API_KEY")
)
 
# --- 定义输出的结构体规范 ---
class ShipmentTrace(BaseModel):
    order_id: str          # 运单号
    current_location: str  # 当前所在地
    status: str            # 状态
    estimated_days: int    # 预计剩余天数
    message: str           # 一句话概要
 
# --- 定义一个叫车联网查单的Tool ---
@function_tool
def search_shipment(order_id: str) -> str:
    """根据运单号从数据库中查询物流追踪信息。"""
    # 模拟真实物流API查询结果
    data = {
        "order_id": order_id,
        "location": "上海市浦东中转站",
        "status": "正在转运中",
        "remaining_days": 2
    }
    return json.dumps(data, ensure_ascii=False)
 
async def main():
    agent = Agent(
        name="物流情报员",
        instructions=(
            "当用户询问快递流向时,你必须调用search_shipment获取后端实时数据。"
            "然后输出符合ShipmentTrace结构的最终结果。"
        ),
        model=OpenAIChatCompletionsModel(
            model=os.getenv("OPENAI_MODEL_NAME"),
            openai_client=client
        ),
        tools=[search_shipment],
        output_type=ShipmentTrace,      # 关键:强制Agent输出这个结构
    )
 
    result = await Runner.run(agent, "帮我查一下快递单号SF1234567890的物流状态")
    # result.final_output 的类型是 ShipmentTrace,不是字符串
    print("=== 结构化结果 ===")
    print(result.final_output)
 
    # 转换为字典直接使用
    print("\n=== 转为字典入库 ===")
    dict_data = result.final_output.model_dump()
    print(dict_data)
 
if __name__ == "__main__":
    asyncio.run(main())

七、Agent的真正“新生代能力”:沙盒执行体

文章开头提到,2026年4月中旬Agents SDK经历了一次从底层开始的重写。这次更新引入了两个核心能力:沙盒执行环境和长期Harness控制流。

什么是沙盒(Sandbox)?

简单说,沙盒就是一个完全隔离的、轻量级的执行场所。Agent在沙盒里可以安全地执行计算机命令、读/写文件、安装依赖包、甚至生成最终产物(如PDF报告或Excel文件)——但它绝对不能碰到你的本地核心系统文件,也不能外泄你的服务器API密钥。新版SDK原生已经接入七家主流沙盒提供商:Cloudflare、Vercel、Modal、E2B等等。

传统AI Agent容易出问题的原因是:你调用API给的模型是一个“黑盒”,它可能会执行你意料之外的指令,比如在代码解释器里无意中运行rm -rf等危险操作。沙盒技术的意义在于,即便Agent犯了错,也只限制在沙盒那间独立的“隔离小屋”里,不会炸毁整栋楼或拖垮你的核心系统。

什么是Harness(模型控制流)?

如果你做一个“长周期Agent”,比如让AI分析一整年的10万条客户物流记录,最终出一份PowerPoint版的年度报告——这个流程可能需要持续运行几个小时。中间一旦临时崩溃或网络抖动,所有进度就全没了。

Harness相当于给Agent配备了“暂停/恢复”功能,可以快照记录Agent当前状态(读到了哪一行日志、做了哪几步任务),等计算恢复后从最近的断点继续执行,不需要重启整个Agent。它同时还包括审批流、状态追踪、多步Handoff记录等各种企业级工具体系。过去这些能力必须靠开发者自己用一连串额外框架来拼凑,现在一个import就能搞定。

八、Agent运行追踪与可视化

当你的智能体系统膨胀到三五个Agent互相交互的时候,调试工作会变得异常复杂——你很难判断是哪个Agent把指令给错,还是Tool给错了参数。SDK内置的追踪(Tracing)会在这里派上用场。

简单来说,Agent一次完整的请求会被打成一个Trace(追踪ID),Trace内部会进一步拆分成多个Span(跨度),可以分别观察到每次LLM生成用时多久、每次Tool调用花了多少秒、是否有Handoff挂起异常等。

在常见的Web框架或FastAPI中,你只需要像这样把Agent调用包裹进一个Trace代码块:

from agents import trace, Runner
 
with trace("完整客服助手业务流程"):
    result1 = await Runner.run(sales_agent, "用户想要最新的电脑报价")
    result2 = await Runner.run(audit_agent, f"核查订单 {result1.final_output} 是否合规")

SDK会自动把两次Agent对话收拢在同一个trace_id下,后端开发者可以通过OpenAI官方Traces面板或第三方集成(W&B、LangSmith、MLflow、Pydantic Logfire)查看到完整链路。这在排查“到底哪个子Agent给我的数据是脏数据”时,效率提升是巨大的。

九、总结:从今天开始拥抱真正“能干活”的Agent

至此,我们已经从Tool、Agent、模型配置、运行方式、结构化输出,一路走到沙盒执行与生产级Harness。

我们把本文最重要的几个点浓缩成一张清单:

  1. Tool是Agent的手和脚:你只需要实现两个辅助函数并用 @function_tool 装饰,Agent就能调用它们完成外部真实任务。
  2. Agent是大脑与调度中心:它根据用户问题决定调哪些Tool,然后把Tool返回的数据转译成回复。
  3. 模型配置非常灵活:Adapter设计使得100多种非OpenAI的模型都能在SDK上运行,团队可以自由选择最合适的模型服务商。
  4. 三种运行模式灵活可变:同步版本跑脚本/批量任务;异步版本与Web后端集成;流式版本专为UI做实时对话效果。
  5. 结构化输出极度友好:用Pydantic定义业务对象,Agent直接返回格式正确的JSON,后端工程师拿到就能入库。
  6. 沙盒与Harness是2026年4月更新的核心:让Agent跑得又安全又持久,企业级应用的生产力得到了质的飞跃。
  7. 追踪调试功能让多Agent系统的调试不再是盲人摸象。

你现在可以立刻做的事情:

如果你从来没用过AI Agent框架,建议先拷贝全局配置方式一的代码跑通第一个Agent。五分钟之后,你会看到大模型终于能“动起来”调取外部数据了。

如果你已经是开发者,想要直接搬上生产环境、跑长时任务处理:直接上手引入沙盒环境+自定义Tool列表。

不管你有多少大模型调用的使用经验,Agents SDK提供了一种思维观念的转变:AI不再是被动的“问答机”,而是一台真正能帮你解决复杂业务的操作系统。