如何使用 OpenAI Agents SDK 处理编程任务
上个月我在做一个代码审查工具时卡壳了。我有个能分析 Python 文件的脚本,但我需要它能真正干活——跑测试、做 lint 检查、修复问题,还要把复杂的重构工作移交给更强大的模型。如果直接用原生 API 调用来把这些串起来,代码就会变成一团乱麻,全是回调链和状态管理。就在那时候,我发现了 OpenAI Agents SDK。
这个 SDK 给我提供了一种很清爽的方式来定义具有特定角色的 Agent,给它们配备工具,并让它们互相交接工作。以下是我把它用在编程工作流中摸索出来的一些经验。
我要解决的问题
我们团队有个简单的 PR 审查机器人,但它只能留评论。我希望它能做到:
- 读取修改过的文件
- 在隔离环境中跑 lint 和测试
- 给出实际的修复建议(而不只是指出问题)
- 自动应用简单的修复,比如调整 import 顺序
用单次 LLM 调用来做这事很不靠谱。模型经常会瞎编测试结果,或者去动它不该动的代码。我需要的是边界清晰的独立 Agent。
入门
首先,安装 SDK:
pip install openai-agents
你需要配置好 API 密钥:
export OPENAI_API_KEY="sk-..."
这个 SDK 是与提供商无关的(provider-agnostic)——它支持 OpenAI 的 Responses 和 Chat Completions API,还通过集成支持了 100 多种其他 LLM。不过在处理编程任务时,我还是坚持用 GPT-4o,因为它处理代码的能力确实强。
搭建我的第一个编程 Agent
我从最简单的开始——一个能读取文件并提出改进建议的 Agent:
from agents import Agent, Runner
code_reviewer = Agent(
name="code-reviewer",
instructions="""You are a senior Python developer reviewing code.
Focus on:
- Bug risks and logic errors
- Performance issues
- Style violations (PEP 8)
- Missing error handling
Be specific. Reference line numbers. Suggest actual fixes, not just problems.""",
model="gpt-4o",
)
result = Runner.run_sync(code_reviewer, input="Review this code:\n\ndef get_user(id):\n db = connect()\n user = db.query(f'SELECT * FROM users WHERE id = {id}')\n return user")
print(result.final_output)
这招立马奏效了。Agent 抓出了 SQL 注入漏洞,并建议使用参数化查询。但它没法采取实际行动——只能动嘴皮子。
添加工具,让 Agent 能干活
真正的威力在于给 Agent 配备工具。我定义了一些 Agent 可以调用的函数:
from agents import Agent, Runner, function_tool
@function_tool
def read_file(path: str) -> str:
"""Read a file and return its contents."""
with open(path, "r") as f:
return f.read()
@function_tool
def run_linter(path: str) -> str:
"""Run ruff linter on a file and return the output."""
import subprocess
result = subprocess.run(
["ruff", "check", path],
capture_output=True,
text=True
)
return result.stdout or "No linting issues found."
@function_tool
def write_file(path: str, content: str) -> str:
"""Write content to a file. Use with caution."""
with open(path, "w") as f:
f.write(content)
return f"Successfully wrote to {path}"
reviewer_with_tools = Agent(
name="code-reviewer",
instructions="""You are a code review agent.
1. Read the file using read_file
2. Run the linter using run_linter
3. Analyze both the code and lint output
4. For simple fixes (import order, unused vars), apply them with write_file
5. For complex issues, just report them
Always read a file before writing to it. Never write without reading first.""",
tools=[read_file, run_linter, write_file],
model="gpt-4o",
)
result = Runner.run_sync(
reviewer_with_tools,
input="Review and fix /home/me/projects/myapp/utils.py"
)
第一个惊喜:Agent 居然真的按顺序执行了指令。它先读了文件,跑了 lint,然后才进行针对性的修复。我本来以为它会直接跳到写文件的步骤,但明确的分步指令让它稳稳地按套路出牌。
**我早期踩过的坑:**我一开始没在工具函数里加上类型提示和文档字符串。SDK 需要靠这些来告诉模型每个工具是干嘛的、需要什么参数。我没加的时候,Agent 调用工具时就会传错参数——比如传个列表而不是字符串,或者干脆漏掉必填参数。
交接:构建多 Agent 工作流
单个 Agent 处理简单任务还行,但我的审查机器人需要更专业的分工。我想要一个 Agent 专门处理快速修复,另一个负责深度分析。SDK 的交接(handoff)功能让 Agent 之间可以互相委派任务:
from agents import Agent, Runner, handoff
quick_fixer = Agent(
name="quick-fixer",
instructions="""You handle simple, mechanical code fixes:
- Import ordering (isort style)
- Removing unused imports
- Trailing whitespace
- Missing trailing newlines
Read the file, apply fixes, write it back. Don't overthink it.""",
tools=[read_file, write_file],
model="gpt-4o-mini", # 便宜模型处理简单任务
)
deep_analyst = Agent(
name="deep-analyst",
instructions="""You handle complex code review:
- Logic bugs and race conditions
- Security vulnerabilities
- Architecture concerns
- Performance bottlenecks
Read the file, analyze thoroughly, and provide a detailed report.
Do NOT modify files — only report findings.""",
tools=[read_file, run_linter],
model="gpt-4o", # 强模型处理复杂分析
)
orchestrator = Agent(
name="review-orchestrator",
instructions="""You triage code review requests.
- For simple style/formatting issues, hand off to quick-fixer
- For bugs, security issues, or architecture concerns, hand off to deep-analyst
- If both apply, hand off to deep-analyst first, then quick-fixer
Start by reading the file and running the linter to assess what's needed.""",
tools=[read_file, run_linter],
handoffs=[handoff(quick_fixer), handoff(deep_analyst)],
model="gpt-4o",
)
result = Runner.run_sync(
orchestrator,
input="Review /home/me/projects/myapp/auth/login.py"
)
这就是我的“顿悟”时刻。编排 Agent 读取了文件后,判断出里面既有 SQL 注入漏洞(复杂问题),又有 import 顺序问题(简单问题),于是先把任务交给了深度分析 Agent,然后才交给快速修复 Agent。每个 Agent 各司其职,互不干扰。
有个让我绊了一下的点:交接会创建一个新的 Agent 运行,所以状态不会自动带过去。如果深度分析 Agent 发现了什么情况需要让快速修复 Agent 知道,你必须通过交接显式地把上下文传过去。我的解决办法是让编排 Agent 在每次交接前先做个总结。
沙箱 Agent:安全的代码执行
SDK 最近新增了沙箱 Agent——这是一种预配置好在容器中运行的 Agent,专门处理长时间运行的任务。这对编程来说太重要了,因为你肯定不想让 Agent 一不小心在你的真实文件系统上执行了 rm -rf。
from agents import SandboxAgent, Runner
sandboxed_coder = SandboxAgent(
name="sandboxed-coder",
instructions="""You are a coding agent working in a sandbox.
You can run commands, edit files, and execute code safely.
Run tests after making any changes to verify your work.""",
model="gpt-4o",
)
result = Runner.run_sync(
sandboxed_coder,
input="Fix the failing test in /workspace/tests/test_auth.py"
)
我还没怎么深度使用沙箱 Agent——它的配置需要 Docker,而且文档里写得不是很明白。但只要你的工作流涉及 Agent 运行任意代码,用沙箱绝对是正道。
护栏:防止 Agent 搞破坏
我是吃尽苦头才学会用护栏(Guardrails)的。我的快速修复 Agent 曾经“修复”了一个文件,明明只需要改 2 行,它却重写了 300 行。护栏可以让你验证输入和输出:
from agents import Agent, GuardrailFunctionOutput, input_guardrail
@input_guardrail
def check_file_path(ctx, agent, input_data):
"""Ensure the agent only works on files in allowed directories."""
# Extract file path from input
if "/etc/" in str(input_data) or "/root/" in str(input_data):
return GuardrailFunctionOutput(
output_info="Blocked: path outside allowed directories",
tripwire_triggered=True,
)
return GuardrailFunctionOutput(
output_info="Path OK",
tripwire_triggered=False,
)
safe_reviewer = Agent(
name="safe-reviewer",
instructions="Review and fix code in the project directory only.",
tools=[read_file, write_file, run_linter],
input_guardrails=[check_file_path],
model="gpt-4o",
)
一旦触发了护栏,Agent 就会立马停止。这可救了我的命,有一次 Agent 试图“热心”地帮我改 .bashrc 文件。
追踪:调试 Agent 工作流
内置的追踪(Tracing)功能出乎意料地好用。当我的多 Agent 工作流产出诡异的结果时,我能清楚地看到到底发生了什么:
from agents import trace
with trace("code-review-workflow"):
result = Runner.run_sync(
orchestrator,
input="Review /home/me/projects/myapp/api/handlers.py"
)
追踪会显示每一次工具调用、每一次交接和每一个模型响应。我曾经抓到一个 Bug:深度分析 Agent 在完成工作后没有终止,而是交还给了编排 Agent,导致陷入了死循环。多亏了追踪,几秒钟就定位到了问题。
我的实战经验分享
机械任务用便宜模型。 我的快速修复 Agent 跑在 gpt-4o-mini 上,处理了 80% 的工作,成本只有十分之一。只有深度分析 Agent 才需要 gpt-4o。
指令要极其具体。 “修复代码”这种指令会导致一片混乱。“读取文件,运行 ruff,只应用 ruff 建议的修复,然后写回文件”——这样写才靠谱。
一定要先读后写。 我现在会在每个 Agent 的指令里都加上这条。不然的话,Agent 有时会基于它训练数据里的内容去写文件,而不是基于实际的文件内容。
给工具加上类型提示。 SDK 会用类型注解为模型生成工具的 schema。少了类型提示,Agent 就会犯迷糊。
先用简单文件测试。 我一开始拿一个 2000 行的模块测试,出了问题根本分不清是 Agent 的锅还是我工具定义的锅。换用小文件测试后,调试速度快多了。
实话实说的局限性
这个 SDK 很轻量,这既是优点也是缺点。它没有内置的文件 diff 功能——当我的 Agent 写文件时,它是直接全覆盖的。我不得不单独加上 git 集成,这样在接受修改前还能审查一下。
跨交接的上下文管理得靠手动。如果 Agent A 发现了 Agent B 需要知道的信息,你必须在指令里想办法把信息传过去。SDK 不会在工作流的 Agent 之间自动共享对话历史。
文档有些地方还是太简略了。我花了一个小时才搞清楚怎么正确构建交接提示词,因为示例没覆盖到我的用例。GitHub 上的 examples 目录其实比官方文档更有用。
沙箱 Agent 的配置需要 Docker 知识,而且这部分还没什么像样的文档。如果你对容器不太熟,还是先用普通 Agent 加上严格的护栏吧。
尽管有这些不完善的地方,这个 SDK 还是用更可靠、更易修改的代码,替掉了我项目里手写的约 400 行编排代码。这种多 Agent 模式——由编排 Agent 分派给专家 Agent——非常契合真实的编程工作流,这比以前单次提示词的方法好使太多了。