Chuyển từ OpenAI Agents SDK sang Claude — Hướng dẫn migration chi tiết
Nếu bạn đang dùng OpenAI Agents SDK và muốn chuyển sang Claude Agent SDK, bài này mapping từng primitive giữa hai SDK qua ví dụ thực tế: expense approval agent.
Bài viết dựa trên Claude Cookbooks chính thức của Anthropic.
Bạn được gì sau migration?
Claude Agent SDK chạy trên cùng runtime với Claude Code — bạn thừa hưởng:
- Built-in tools:
Read,Edit,Bash,Grep - Layered permission system cho tool gating
- Automatic prompt caching
- Event stream cho progress streaming hoặc mid-run interception
Trade-off: tool definitions explicit hơn (declare schemas thay vì dựa vào type-hint introspection), nhưng ít boilerplate ở mọi chỗ khác.
Mapping tổng quan
| OpenAI Agents SDK | Claude Agent SDK |
|---|---|
Agent(name, instructions, tools) |
ClaudeAgentOptions + system prompt |
@function_tool |
@tool + create_sdk_mcp_server
|
@input_guardrail |
Plain function before loop (hoặc UserPromptSubmit hook) |
@output_guardrail |
Plain function on ResultMessage.result
|
Runner.run(agent, msg) |
ClaudeSDKClient context manager |
| Sessions (client-managed) | Reuse same ClaudeSDKClient
|
conversation_id (server) |
resume=session_id (disk-backed) |
| Built-in tracing dashboard | OTel-native (Grafana/Datadog/Honeycomb) |
handoffs=[...] |
AgentDefinition + Agent tool |
Primitive 1: @function_tool → @tool + MCP Server
OpenAI
from agents import function_tool
@function_tool
def check_policy(expense_type: str, amount: float) -> str:
"""Check if expense complies with company policy"""
if amount > 5000:
return "REQUIRES_APPROVAL: Over $5000 limit"
return "APPROVED: Within policy limits"
Claude
from claude_agent_sdk import tool, create_sdk_mcp_server
@tool(
name="check_policy",
description="Check if expense complies with company policy",
schema={
"type": "object",
"properties": {
"expense_type": {"type": "string"},
"amount": {"type": "number"}
},
"required": ["expense_type", "amount"]
}
)
async def check_policy(args):
if args["amount"] > 5000:
result = "REQUIRES_APPROVAL: Over $5000 limit"
else:
result = "APPROVED: Within policy limits"
return {"content": [{"type": "text", "text": result}]}
# Bundle vào MCP server
expense_server = create_sdk_mcp_server(
"expense", tools=[check_policy]
)
Key difference: Claude yêu cầu explicit JSON Schema thay vì derive từ type hints. Business logic bên trong không thay đổi.
Primitive 2: Agent() → ClaudeAgentOptions
OpenAI
from agents import Agent
agent = Agent(
name="expense_agent",
instructions="You are an expense approval agent...",
tools=[check_policy],
model="gpt-4o"
)
Claude
from claude_agent_sdk import ClaudeAgentOptions
expense_options = ClaudeAgentOptions(
model="claude-sonnet-4-6",
system_prompt="You are an expense approval agent...",
mcp_servers={"expense": expense_server},
allowed_tools=["mcp__expense__check_policy"]
)
Note về permissions: allowed_tools make tool available. Read-only custom tools chạy tự do. Write tools cần approval trừ khi set permission_mode="bypassPermissions".
Primitive 3: Guardrails
Input Guardrail — OpenAI vs Claude
OpenAI: @input_guardrail decorator, registered on Agent.
Claude: Plain function called before loop. Hoặc UserPromptSubmit hook.
# Claude: plain function approach
def validate_input(prompt: str) -> str | None:
if "$" not in prompt and "dollar" not in prompt.lower():
return "Please include dollar amount in your request"
return None # Valid
# Gọi trước khi start client
error = validate_input(user_prompt)
if error:
print(error)
return
# Proceed with agent
async with ClaudeSDKClient(options=expense_options) as client:
await client.query(user_prompt)
...
Output Guardrail
# Claude: check result after loop
async for msg in client.receive_response():
if hasattr(msg, 'result'):
if "REQUIRES_APPROVAL" in msg.result:
# Flag for human review
notify_manager(msg.result)
final_result = msg.result
Primitive 4: Runner.run() → ClaudeSDKClient
OpenAI
result = await Runner.run(agent, "Approve expense: $200 lunch")
print(result.final_output)
Claude
async with ClaudeSDKClient(options=expense_options) as client:
await client.query("Approve expense: $200 lunch")
async for msg in client.receive_response():
if hasattr(msg, 'result'):
print(msg.result)
Claude's event stream cho bạn thấy mọi thứ: tool calls, text blocks, final result — theo thứ tự real-time.
| Event Type | Contains |
|---|---|
| SystemMessage | Session init metadata |
| AssistantMessage | Text blocks hoặc tool-use blocks |
| UserMessage | Tool-result blocks |
| ResultMessage | .result, .usage, .total_cost_usd |
Primitive 5: Sessions
-
In-memory: Reuse same
ClaudeSDKClientinstance -
Disk-backed:
resume=session_idđể persist across restarts
# Session persists across restarts
async with ClaudeSDKClient(
options=expense_options,
resume="session-abc-123" # Disk-backed persistence
) as client:
await client.query("Follow up on yesterday's expense")
...
Observability: OTel-native
OpenAI có built-in tracing dashboard. Claude SDK export qua OpenTelemetry — plugs vào existing Grafana, Datadog, Honeycomb stack.
Migration Checklist
- Port
@function_tool→@tool+ explicit schemas - Replace
Agent()→ClaudeAgentOptions - Move guardrails ra khỏi decorators, thành plain functions
- Replace
Runner.run()→ClaudeSDKClientcontext manager - Port sessions → reuse client hoặc
resume= - Wire OTel export cho observability
- Test từng primitive independently trước khi integrate
Bước tiếp theo: Bắt đầu với Research Agent cho Agent SDK basics, hoặc Chief of Staff Agent cho advanced multi-agent patterns.
Bài viết liên quan
Bai viet co huu ich khong?
Bản quyền thuộc về tác giả. Vui lòng dẫn nguồn khi chia sẻ.





