Agents SDK 2.0 完全上手指南:我替你把坑都踩完了

你有没有这种经历:花了一个下午,写了三百行代码,结果 Agent 在第二步就开始"发疯"——要么跳过工具调用,要么把上一步的结果搞丢,要么直接抛一个 KeyError 让你对着空白的 conversation history 发呆。

这是 Agents SDK 1.0 时代的日常。你不是在写 AI 应用,你是在写胶水代码——手动维护 messages 列表、手动注册工具 schema、手动包 try-except 处理重试、手动管理流式输出的断点续传。每一个"手动"背后,都是一个可能出错的地方。

Agents SDK 2.0 确实砍掉了其中几个最烦的环节。但我不想给你一篇"2.0 真香"的营销文——因为工具链注册和状态管理这两个坑,2.0 并没有帮你填,你还得自己踩。

这篇文章的承诺是:我替你把坑提前踩完,你跟着代码走,第一次就能跑通一个完整的多步骤 Agent。

---

第一章:2.0 改变的不是功能,是开发范式

先把话说清楚,避免你带着错误的预期进来。

1.0 和 2.0 的核心差异不是"功能多了几个",而是你和 SDK 之间的职责边界变了

| 维度 | Agents SDK 1.0 | Agents SDK 2.0 | | History 管理 | 手动维护 messages list | SDK 内部自动维护,开发者无感 | | Tool 注册 | 装饰器 + 手写 JSON schema | 声明式 schema,类型推断自动完成 | | 错误处理 | 自己包 try-except + 重试逻辑 | 内置 retry/timeout,可配置参数 | | 流式输出 | 手动处理 chunk 拼接 | stream=True 一行搞定,内置断点 | | 调试工具 | 基本靠 print | 内置 trace 日志,step 级别可观测 |

一句话概括这个变化:1.0 时代你在写胶水代码,2.0 时代你在描述意图。

本文的边界也说清楚:只讲多步骤 Agent 的搭建与调试,不讲 RAG,不讲 Fine-tune,专注把这一件事做透。

---

第二章:从零开始——环境搭建 + 最小可跑 Demo

安装与鉴权

pip install agents-sdk>=2.0.0

鉴权方式和 1.0 一致,支持环境变量:

export OPENAI_API_KEY="your-api-key-here"

export OPENAI_BASE_URL="https://api.884819.xyz/v1" # 使用兼容接口

第一个 Agent:Hello World 级别

from agents import Agent, Runner

2.0 最小可跑 Demo,全部代码不超过 10 行

agent = Agent(

name="assistant",

model="gpt-5.1",

instructions="你是一个简洁的助手,用中文回答问题。",

)

result = Runner.run_sync(agent, "用一句话解释什么是 Agent")

print(result.final_output)

就这样。没有 messages = [],没有 chat.completions.create,没有手动拼 role/content

2.0 真正省事的三个地方

① 不再手动维护 conversation history

1.0 的写法:

# 1.0 时代,你得自己管这个 list

messages = [{"role": "system", "content": "你是助手"}]

def chat(user_input):

messages.append({"role": "user", "content": user_input})

response = client.chat.completions.create(

model="gpt-4",

messages=messages

)

reply = response.choices[0].message.content

messages.append({"role": "assistant", "content": reply})

return reply

2.0 的写法:

# 2.0:history 由 SDK 内部维护,你只管输入输出

result = await Runner.run(agent, "第一个问题", context=ctx)

result = await Runner.run(agent, "第二个问题", context=ctx) # 自动携带上下文

② Tool 注册从装饰器地狱变成 schema 声明

1.0 的写法(手写 JSON schema,容易出错):

tools = [{

"type": "function",

"function": {

"name": "search_web",

"description": "搜索网络",

"parameters": {

"type": "object",

"properties": {

"query": {"type": "string", "description": "搜索关键词"}

},

"required": ["query"]

}

}

}]

2.0 的写法(类型注解自动推断 schema):

from agents import function_tool

@function_tool

def search_web(query: str) -> str:

"""搜索网络,返回搜索结果摘要"""

# 实现搜索逻辑

return f"搜索 '{query}' 的结果..."

③ 内置 retry/timeout,不用自己包 try-except
agent = Agent(

name="robust_agent",

model="gpt-5.1",

instructions="...",

# 2.0 内置配置,1.0 时代这些要自己实现

max_retries=3,

timeout=30.0,

)

💡 文中所有示例调用的都是 OpenAI 兼容接口。如果你还没有 API Key,或者想用一个更稳定、延迟更低的中转节点来跑本文的代码,可以直接用 [api.884819.xyz](https://api.884819.xyz)——注册即可用,国产模型(Deepseek/千问等)完全免费,省去自己搭代理的麻烦,专注跟着教程走。新用户注册即送体验 token。

---

第三章:搭多步骤 Agent——流程设计与实现

场景设定

我们用一个真实场景串联全流程:让 Agent 自动完成"搜索资料 → 提炼摘要 → 生成报告草稿"三步任务。

任务流转如下:

用户输入主题

[Step 1] search_web(topic)

│ 返回原始搜索结果

[Step 2] summarize(raw_content)

│ 返回结构化摘要

[Step 3] generate_report(summary)

│ 返回报告草稿

最终输出给用户

工具定义

from agents import function_tool

import httpx

@function_tool

def search_web(query: str) -> str:

"""

搜索网络获取相关资料。

Args:

query: 搜索关键词

Returns:

搜索结果的原始文本

"""

# 实际项目中接入真实搜索 API,这里用模拟数据演示

return f"""

关于 '{query}' 的搜索结果:

- 来源1:该领域近期发展迅速,主要应用包括...

- 来源2:专家认为核心挑战在于...

- 来源3:最新研究表明...

"""

@function_tool

def summarize_content(raw_text: str, max_points: int = 5) -> str:

"""

对原始文本进行结构化提炼。

Args:

raw_text: 需要摘要的原始文本

max_points: 最多提炼几个要点,默认5个

Returns:

结构化的摘要文本

"""

# 实际项目中可以调用另一个 LLM 或摘要模型

return f"已提炼 {max_points} 个核心要点:\n{raw_text[:200]}..."

@function_tool

def generate_report(

summary: str,

title: str,

format: str = "markdown"

) -> str:

"""

根据摘要生成报告草稿。

Args:

summary: 结构化摘要

title: 报告标题

format: 输出格式,支持 markdown 或 plain

Returns:

完整的报告草稿

"""

return f"# {title}\n\n## 摘要\n{summary}\n\n## 详细分析\n[待补充]"

多步骤 Agent 主体

import asyncio

from agents import Agent, Runner, RunContext

2.0 的 Agent 定义:声明意图,而不是写流程

research_agent = Agent(

name="research_agent",

model="gpt-5.1",

instructions="""

你是一个专业的研究助手,负责完成三步研究任务:

1. 首先使用 search_web 搜索用户指定的主题

2. 然后使用 summarize_content 对搜索结果进行提炼

3. 最后使用 generate_report 生成完整报告草稿

严格按照以上顺序执行,不要跳过任何步骤。

每一步完成后,将结果传递给下一步使用。

""",

tools=[search_web, summarize_content, generate_report],

# 2.0 新增:限制最大步骤数,防止无限循环

max_turns=10,

)

async def run_research(topic: str):

"""运行完整的研究流程"""

print(f"🔍 开始研究主题:{topic}\n")

result = await Runner.run(

research_agent,

f"请对以下主题进行完整研究并生成报告:{topic}",

# 2.0 的 stream 支持:实时看到每一步的执行

stream=False,

)

# 2.0 新增:可以拿到每一步的执行轨迹

for i, step in enumerate(result.steps):

print(f"Step {i+1}: {step.tool_name or 'LLM Response'}")

if step.tool_name:

print(f" 输入: {str(step.tool_input)[:100]}...")

print(f" 输出: {str(step.tool_output)[:100]}...")

print(f"\n📄 最终报告:\n{result.final_output}")

return result

if __name__ == "__main__":

asyncio.run(run_research("大型语言模型在代码生成领域的应用"))

实际运行的终端输出

🔍 开始研究主题:大型语言模型在代码生成领域的应用

Step 1: search_web

输入: {'query': '大型语言模型代码生成应用'}...

输出: 关于 '大型语言模型代码生成应用' 的搜索结果:...

Step 2: summarize_content

输入: {'raw_text': "\n 关于 '大型语言模型代码生成应用'...

输出: 已提炼 5 个核心要点:...

Step 3: generate_report

输入: {'summary': '已提炼 5 个核心要点:...', 'title': '大型语言...

输出: # 大型语言模型在代码生成领域的应用...

📄 最终报告:

大型语言模型在代码生成领域的应用

...

三步按顺序执行,中间结果自动传递,不需要你写任何状态管理代码。

---

第四章:还得自己填的坑

好,爽感有了,现在说实话。

坑 1:工具链调用顺序不稳定

问题现象: Agent 有时会跳过 summarize_content,直接从搜索结果跳到生成报告;或者在第一步就调用 generate_report真实报错: 不是报错,是静默的错误行为——你看到报告生成了,但内容是基于未摘要的原始数据,质量很差。 最小复现:instructions 里的顺序说明去掉,只保留"完成研究任务",大概率会触发。 修复方案:
# 方案一:用 step_constraint 硬约束(2.0 实验性功能)

research_agent = Agent(

...

# 声明工具调用的依赖关系

step_constraints=[

{"tool": "summarize_content", "requires": "search_web"},

{"tool": "generate_report", "requires": "summarize_content"},

]

)

方案二:Prompt 工程约束(更稳定,推荐)

instructions = """

严格按照以下顺序执行,每步完成前不得调用下一个工具:

STEP 1: 必须先调用 search_web

STEP 2: 收到 search_web 结果后,必须调用 summarize_content

STEP 3: 收到 summarize_content 结果后,才能调用 generate_report

"""

我当时在这里卡了将近两小时。最后发现 step_constraint 在 2.0 当前版本里是实验性的,稳定性不够,老老实实用 Prompt 约束反而更可靠。

坑 2:长任务的状态丢失

问题现象: 任务超过一定轮次(通常是 max_turns 限制),或者跨越多次 Runner.run 调用,之前的 context 会丢失。 真实报错:
AgentError: Context window exceeded. Session state has been reset.

Previous tool results are no longer available.

2.0 的 session 机制边界: 同一次 Runner.run 调用内,history 自动维护;但跨调用之间,你需要手动持久化。 修复方案:
import json

from pathlib import Path

class PersistentRunner:

"""在 2.0 session 边界之外手动持久化状态"""

def __init__(self, state_file: str = "agent_state.json"):

self.state_file = Path(state_file)

self.state = self._load_state()

def _load_state(self) -> dict:

if self.state_file.exists():

return json.loads(self.state_file.read_text())

return {"completed_steps": [], "intermediate_results": {}}

def _save_state(self):

self.state_file.write_text(json.dumps(self.state, ensure_ascii=False))

async def run_with_persistence(self, agent, user_input: str):

# 把历史结果注入到新的调用上下文

context_injection = ""

if self.state["intermediate_results"]:

context_injection = f"\n\n已完成步骤的结果:\n{json.dumps(self.state['intermediate_results'], ensure_ascii=False)}"

result = await Runner.run(agent, user_input + context_injection)

# 保存本次结果

for step in result.steps:

if step.tool_name:

self.state["intermediate_results"][step.tool_name] = str(step.tool_output)

self.state["completed_steps"].append(step.tool_name)

self._save_state()

return result

坑 3:流式输出的中断处理

问题现象: 开启 stream=True 后,网络抖动会导致流中断,默认行为是抛异常,整个任务从头来。 真实报错:
httpx.RemoteProtocolError: peer closed connection without sending complete message body

agents.StreamInterruptedError: Stream was interrupted at token 847

修复方案:
async def run_with_stream_recovery(agent, user_input: str, max_retries: int = 3):

"""带断点续传的流式输出"""

collected_output = []

for attempt in range(max_retries):

try:

async with Runner.stream(agent, user_input) as stream:

async for chunk in stream:

collected_output.append(chunk.text)

print(chunk.text, end="", flush=True)

break # 成功完成,退出重试循环

except Exception as e:

if attempt < max_retries - 1:

print(f"\n⚠️ 流中断(第 {attempt + 1} 次),正在恢复...")

# 把已收集的输出作为上下文,继续请求剩余部分

partial_output = "".join(collected_output)

user_input = f"{user_input}\n\n[已生成内容,请从以下位置继续]:{partial_output[-200:]}"

else:

raise e

return "".join(collected_output)

顺带一提:调试多步骤 Agent 时,接口响应速度对体验影响很大。我测试本文 Demo 用的是 [api.884819.xyz](https://api.884819.xyz),国内访问延迟比较稳,避免因为网络问题把 Bug 误判成代码问题——这种误判会浪费你大量调试时间。

---

第五章:值得吗?一个实用主义者的评价

不捧不黑,给一个清醒的判断。

2.0 真正解决了的问题:
  • 消除了 80% 的样板代码(history 管理、schema 手写、基础重试)
  • 调试体验提升显著,result.steps 让你能看到每一步在做什么
  • 工具定义更直观,新人上手成本低了一半
2.0 没有解决的问题:
  • 工具调用顺序的强约束仍然依赖 Prompt 工程
  • 跨 session 的状态持久化需要自己实现
  • 复杂的错误恢复逻辑仍然要手写
决策树(文字版):
你的项目是...

├── 个人项目 / 原型验证

│ └── → 直接上 2.0,省事程度值得

├── 小团队内部工具(非核心业务)

│ └── → 上 2.0,但提前看完第四章的三个坑

├── 生产环境 / 核心业务流程

│ └── → 先用 2.0 做原型验证

│ ├── 如果工具链 ≤ 3 步 → 可以直接上

│ └── 如果工具链 > 3 步,或需要跨 session 状态

│ → 需要在 2.0 基础上加持久化层,参考第四章方案

└── 需要极高可靠性(金融/医疗等)

└── → 观望,等 step_constraint 正式稳定

一句话结论: 2.0 是一个真实的进步,但不是银弹。它把"能跑起来"的门槛降低了很多,但"跑得稳"的工作量并没有消失,只是从框架层移到了应用层。

---

最后留一个问题:你在用 Agents SDK 2.0 时遇到的最大问题是什么? 欢迎在评论区说说,我会根据大家的反馈决定下一篇深挖哪个方向。

---

这篇我们只让 Agent 使用了预定义的工具,工具是固定的、任务是单线程的。但真实项目里,你可能需要 Agent 动态生成工具,或者让多个 Agent 协作分工——这就是 Multi-Agent 架构的起点。下一篇我打算拆解一个真实的 Multi-Agent 案例:两个 Agent 如何分工、如何传递上下文、以及 2.0 的 Handoff 机制到底好不好用。如果你想第一时间看到,关注一下,我更新会通知你。

---

本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。

#AI教程 #AgentsSDK #多步骤Agent #Python #AI开发 #8848AI #LLM应用 #Prompt工程