Agents SDK 2.0 完全上手指南:我替你把坑都踩完了
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 history1.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让你能看到每一步在做什么 - 工具定义更直观,新人上手成本低了一半
- 工具调用顺序的强约束仍然依赖 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工程