尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

如何把 OpenAI Agents SDK 应用迁移到 Claude Agent SDK?

如何把 OpenAI Agents SDK 应用迁移到 Claude Agent SDK? 如何把 OpenAI Agents SDK 应用迁移到 Claude Agent SDK【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks如果你已经用 OpenAI Agents SDK 写好了一个 agent 应用自定义工具、输入/输出 guardrail、多轮会话想把它搬到 Claude Agent SDK 上这篇文章给出的操作路径来自 claude-cookbooks 仓库的 04_migrating_from_openai_agents_sdk.ipynb。这个 notebook 用一个完整的报销审批 agent 做例子把function_tool、input_guardrail、Runner.run等每个 OpenAI 侧原语逐一映射到 Claude 侧等价物并且两个 SDK 都实际跑通最后用三组固定输入做行为对比。迁移完成后业务逻辑不变得到的是 Claude 侧的显式工具 schema、分层权限、自动 prompt caching 和 OpenTelemetry 导出。准备条件Python 3.11 或 3.12熟悉async/await熟悉你要迁出的 OpenAI Agents SDK 原语.env中同时配置ANTHROPIC_API_KEY和OPENAI_API_KEY——notebook 中两个 SDK 都是实际运行的缺一个断言就会失败在 notebook 中安装依赖openai-agents固定为0.9.3%pip install openai-agents0.9.3 claude-agent-sdk python-dotenv注意 notebook 里有一条版本约束openai-agents是 pre-1.0API 变动频繁。如果你升级这个版本需要重新核对Agent/Runner/function_tool的签名不能直接沿用本文的 OpenAI 侧代码。如果你是在仓库目录里跑整个教程系列可以先看 claude_agent_sdk/README.md 的项目初始化步骤uv sync、注册 Jupyter kernel 等。本 notebook 的示例代码里有from utils.agent_visualizer import ...这种导入要求运行时工作目录是claude_agent_sdk/README 也给出了替代方式在该目录下执行uv pip install -e .后任意目录都能导入。notebook 中使用的模型变量为OAI_MODEL gpt-4.1和CLAUDE_MODEL claude-sonnet-4-6下文代码沿用这两个变量。迁移映射表notebook 开头给出了一张总表先对照一遍再逐段动手OpenAI Agents SDKClaude Agent SDKAgent(name, instructions, tools)ClaudeAgentOptions system promptfunction_tooltoolcreate_sdk_mcp_serverinput_guardrail循环前的普通函数或UserPromptSubmithookoutput_guardrail对ResultMessage.result调用的普通函数Runner.run(agent, msg)ClaudeSDKClient上下文管理器Sessions客户端持有历史复用同一个ClaudeSDKClientconversation_id服务端持有resumesession_id落盘恢复内置 tracing dashboardOTel 原生导出接到现有 Grafana/Datadog/Honeycombhandoffs[...]AgentDefinition Agent 工具见文末第一步迁移工具定义OpenAI 侧的function_tool从类型提示和 docstring 推导 schema函数直接进tools[...]。Claude 侧的tool要求显式传入名字、描述和 schema处理函数是async的接收一个args字典返回{content: [{type: text, text: ...}]}。工具要打包成进程内的 MCP server尽管叫 MCP没有子进程和网络传输这个 server 才是传给 agent 的单位。函数内部的业务逻辑不变只换包装import json from claude_agent_sdk import create_sdk_mcp_server, tool tool( check_policy, Look up the expense policy for a category. Returns the approval limit. Valid categories: meals, travel, software, other., {category: str, amount: float}, ) async def check_policy_claude(args): limits {meals: 75.0, travel: 500.0, software: 200.0, other: 50.0} category, amount args[category], args[amount] result { category: category, limit: limits.get(category.lower(), 50.0), requires_receipt: amount 25.0, } return {content: [{type: text, text: json.dumps(result)}]} policy_server create_sdk_mcp_server(nameexpense, tools[check_policy_claude])第二步Agent(...)换成ClaudeAgentOptionsClaudeAgentOptions承载模型、工具和 system promptguardrail 逻辑不再注册在框架对象上而是留在你自己的应用代码里。字段对应关系Agent(...)字段ClaudeAgentOptions等价物instructions...system_prompt...tools[fn]mcp_servers{...}allowed_tools[...]model...model...name...不需要——options 是配置值不是命名实体子 agent 名字写在AgentDefinition上input_guardrails[...]在你的 run 函数里调用见下一步allowed_tools里的工具名遵循mcp__{server_name}__{tool_name}模式。内置工具Read、Edit、Bash、Grep等按名字直接加入例如allowed_tools[Read, Grep, mcp__expense__check_policy]文件系统工具的用法可参考 00_The_one_liner_research_agent.ipynb。from claude_agent_sdk import ClaudeAgentOptions expense_system_prompt ( You approve or flag expense submissions. Always call check_policy first to get the limit for the expense category. Approve if the amount is under the limit; otherwise flag for manager review. ) expense_options ClaudeAgentOptions( modelCLAUDE_MODEL, system_promptexpense_system_prompt, mcp_servers{expense: policy_server}, allowed_tools[mcp__expense__check_policy], )一个容易踩的权限点allowed_tools只是 allow-rule它让工具对 agent 可用agent 能否不经用户批准直接调用取决于permission_mode。像check_policy这样的只读自定义工具默认自由运行写文件或跑 shell 命令的工具在permission_mode不是acceptEdits或bypassPermissions时会弹出确认。你的工具如果只做读不用改这里。第三步guardrail 改为循环前后的普通函数OpenAI 侧input_guardrail验证用户消息、output_guardrail验证最终回答tripwire_triggeredTrue时抛出InputGuardrailTripwireTriggered或输出侧对应异常。Claude 侧没有对应装饰器输入检查在启动 client 之前调用输出检查在循环结束后对ResultMessage.result调用都是普通函数返回(allowed, message)二元组def has_dollar_amount_check(user_input: str) - tuple[bool, str | None]: Input check — returns (allowed, rejection_message). if re.search(r\$\d, user_input): return True, None return False, I need a dollar amount to process this. Please include one (e.g., $47). def has_decision_check(result: str) - tuple[bool, str | None]: Output check — returns (allowed, override_message). if re.search(r\b(approv|flag|review), result, re.IGNORECASE): return True, None return False, I couldnt reach a clear approve/flag decision. Please resubmit.结构上最接近input_guardrail的是 SDK 的UserPromptSubmithook——它在 prompt 到达 Claude 之前触发并可以拦截。但 notebook 演示用普通函数原因明确hook 拦截时ResultMessage.result返回空字符串拦截理由不会传回给调用方而普通函数让你自己控制拒绝文案。output_guardrail没有干净的 hook 等价物Stop在响应完成后触发但不会改写输出。所以默认按上面的普通函数迁移只有当你确实想把检查注册到 options 上时再换成 hook 形式示例见 notebook 原文。如果要拦截的是工具调用而不是用户输入那是另一个用例见 03_The_site_reliability_agent.ipynb 的PreToolUsehook 模式。第四步Runner.run()换成ClaudeSDKClientOpenAI 侧一行result await Runner.run(agent, msg)拿到result.final_output。Claude 侧ClaudeSDKClient是异步上下文管理器.query(msg)发送然后迭代.receive_response()逐条拿事件——每次工具调用、每个文本块、最终结果都按序可见。事件类型事件内容SystemMessage会话初始化元数据AssistantMessage文本块或 tool-use 块UserMessage工具结果块ResultMessage永远是最后一条.result、.usage、.total_cost_usdreceive_response()保证ResultMessage是最后一条事件所以最终答案取messages[-1].resultfrom claude_agent_sdk import ClaudeSDKClient async def run_claude(msg: str) - None: # 输入 guardrail —— agent 运行前短路 allowed, rejection has_dollar_amount_check(msg) if not allowed: print(rejection) return messages [] async with ClaudeSDKClient(optionsexpense_options) as client: await client.query(msg) async for event in client.receive_response(): messages.append(event) # receive_response() 保证 ResultMessage 是最后一条 final messages[-1].result ok, override has_decision_check(final or ) if not ok: print(override) returnSDK 也提供无状态的query()函数用于一次性调用和内置工具见 00_The_one_liner_research_agent.ipynb。但当你用create_sdk_mcp_server带自定义工具时用ClaudeSDKClient——它的持久 transport 负责进程内 MCP 握手。第五步会话迁移OpenAI 有两种会话模式Claude 侧各有一个对应物OAI 模式持久性Claude 等价物result.to_input_list() 重发客户端内存持有历史复用同一个ClaudeSDKClient在同一打开的上下文里再调.query()conversation_id服务端持有进程重启后仍在resumesession_id——会话记录写在本地磁盘重启后仍可恢复内存式多轮进程死了历史就没了async def run_claude_multiturn(): async with ClaudeSDKClient(optionsexpense_options) as client: await client.query(Lunch with Acme, $47) async for _ in client.receive_response(): pass # 消费流 # 同一个 client —— 记得第一轮 await client.query(What about $90?) turn2 [m async for m in client.receive_response()] return turn2[-1].result磁盘式恢复每次运行从ResultMessage.session_id拿到session_id存起来下次运行通过ClaudeAgentOptions(resumesession_id, ...)恢复# 第 1 轮捕获 session_idResultMessage 永远是最后一条事件 async with ClaudeSDKClient(optionsexpense_options) as client: await client.query(Lunch with Acme, $47) turn1 [m async for m in client.receive_response()] session_id turn1[-1].session_id # 第 2 轮新 client、新进程 —— 从磁盘恢复 resume_opts replace(expense_options, resumesession_id) async with ClaudeSDKClient(optionsresume_opts) as client: await client.query(What about $90?) turn2 [m async for m in client.receive_response()] print(f[resumed {session_id[:8]}...] {turn2[-1].result})注意边界会话记录存在本地文件系统跨进程重启有效但不跨机器。如果会话大到超出上下文窗口两种模式下都可以看 misc/session_memory_compaction.ipynb 的压缩做法。验证迁移结果notebook 的验证方式是把三组输入分别跑一遍两个 SDK 并对比。预期行为是批准$47午餐、标记$650机票、拒绝不含金额的那条输入from agents.exceptions import InputGuardrailTripwireTriggered test_inputs [ (Lunch with Acme, $47, approve), (Flight to NYC, $650, flag), (Need approval for the thing, guardrail), ] for msg, expected in test_inputs: try: oai_out await run_oai(msg) except InputGuardrailTripwireTriggered: oai_out [guardrail: InputGuardrailTripwireTriggered] claude_out await run_claude_quiet(msg) print(fOAI: {str(oai_out)[:200]}) print(fClaude: {str(claude_out)[:200]})文档示例输出摘自 notebook 的实际运行具体措辞会随模型回答变化判断标准是行为类别INPUT: Lunch with Acme, $47 (expect: approve) OAI: The expense for lunch ($47) is under the meals category limit of $75. This expense is approved. Claude: ✅ Expense Approved ... | **Status** | **Approved** | INPUT: Flight to NYC, $650 (expect: flag) OAI: The approval limit for travel expenses is $500. ... will be flagged for manager review. Claude: Expense Flagged for Manager Review ... INPUT: Need approval for the thing (expect: guardrail) OAI: [guardrail: InputGuardrailTripwireTriggered] Claude: I need a dollar amount to process this. Please include one (e.g., $47).三条行为类别一致就说明这一层迁移等价了。可选接入 OpenTelemetryOpenAI 侧有内置 tracing dashboardClaude 侧的对应物是把 OTel 导出接到你现有的监控栈上不是迁移必须项——telemetry 开与关agent 行为完全一样。notebook 给出的环境变量OTEL_EXPORTER_OTLP_ENDPOINT里的your-collector需要替换成你自己 collector 的地址export CLAUDE_CODE_ENABLE_TELEMETRY1 export OTEL_METRICS_EXPORTERotlp export OTEL_LOGS_EXPORTERotlp export OTEL_EXPORTER_OTLP_PROTOCOLgrpc export OTEL_EXPORTER_OTLP_ENDPOINThttp://your-collector:4317每次工具调用和 API 请求都会带上prompt.id标签的事件可以把一次 prompt 引起的全部活动串起来指到你现有的 Grafana / Datadog / Honeycomb / Langfuse。可选自动 prompt cachingClaude 侧多出来的部分system prompt 和工具 schema 在第一次调用后自动缓存无需任何配置相同前缀的后续调用只付该前缀输入 token 成本的大约 10%。每次运行都可以通过ResultMessage上的.usage核对比如cache_creation_input_tokens、cache_read_input_tokens、input_tokens。文档示例输出warm cache 场景具体数值不要当作固定预期Run 1: cache_creation38728 cache_read66272 input 4 Run 2: cache_creation 659 cache_read95025 input 4notebook 提醒因为前面的 cell 已经用expense_options跑过到这里 cache 是热的所以 Run 1 就有明显的cache_read冷启动 notebook 的话 Run 1 以cache_creation为主。程序化的成本追踪可以看 observability/usage_cost_api.ipynb。可选handoffs的迁移如果你的 OpenAI 应用用了handoffs[...]心智模型要变OpenAI 侧handoffs[specialist]暴露一个transfer_to_specialist工具specialist 接管会话后第一个 agent 不再运行Claude 侧用AgentDefinition以编程方式定义子 agent经ClaudeAgentOptions(agents{...})传入编排者通过 Agent 工具委派、拿回结果并保持控制权。对纯路由类应用分诊 → 专家差别不大如果agent B 接管后 A 再也不运行是你的关键设计notebook 建议用一层薄薄的 Python dispatcher而不是让 LLM 决定路由from claude_agent_sdk import AgentDefinition approver AgentDefinition( descriptionApproves expenses under policy limits. Use for any submission where the amount is at or under the limit returned by check_policy., promptYou approve expense submissions that are within policy. Confirm the amount and category, and remind the submitter if a receipt is required., tools[mcp__expense__check_policy], ) escalator AgentDefinition( descriptionEscalates over-limit expenses to a manager. Use when the submitted amount exceeds the policy limit for its category., promptYou escalate over-limit expenses. Draft a one-line note to the manager: include the amount, the category, and how far over the limit it is., tools[mcp__expense__check_policy], ) triage_options ClaudeAgentOptions( modelCLAUDE_MODEL, system_promptRoute each expense to the appropriate subagent. Delegate to approver if under limit, escalator if over., mcp_servers{expense: policy_server}, allowed_tools[Agent, mcp__expense__check_policy], agents{approver: approver, escalator: escalator}, )Claude Code CLI 工作流里还能在.claude/agents/*.md文件里定义子 agent文件系统路径面向交互使用SDK 应用随代码分发时用上面的agents参数更合适没有文件系统依赖。完整的多 agent 端到端运行见 01_The_chief_of_staff_agent.ipynb。迁移后需要记住的约束openai-agents锁定在0.9.3pre-1.0 的 API 会变升级前必须重新核对 OpenAI 侧签名UserPromptSubmithook 拦截时ResultMessage.result是空字符串拦截理由不传回调用方——需要自定义拒绝文案时用循环前的普通函数resumesession_id的会话记录在本地磁盘进程重启可恢复不跨机器allowed_tools决定工具是否可用permission_mode决定能否免批准调用两者不要混为一谈内置工具Read/Edit/Bash/Grep等是 Claude 侧运行时自带的按名字加入allowed_tools即可不需要在 OpenAI 侧找对应物。按 notebook 的结论大多数迁移是每个工具多写一点样板显式 schema其他地方少写样板工具逻辑、业务规则原样保留框架层换成ClaudeAgentOptionsClaudeSDKClient行为用上面三组输入验证即可。【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表