{"product_id":"chuyển-từ-openai-agents-sdk-sang-claude-hướng-dẫn-migration-chi-tiết","title":"Chuyển từ OpenAI Agents SDK sang Claude — Hướng dẫn migration chi tiết","description":"\n\u003cp\u003eNế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ế: \u003cstrong\u003eexpense approval agent\u003c\/strong\u003e.\u003c\/p\u003e\n\n\u003cp\u003eBài viết dựa trên \u003cstrong\u003eClaude Cookbooks chính thức\u003c\/strong\u003e của Anthropic.\u003c\/p\u003e\n\n\u003ch2\u003eBạn được gì sau migration?\u003c\/h2\u003e\n\n\u003cp\u003eClaude Agent SDK chạy trên cùng runtime với Claude Code — bạn thừa hưởng:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003eBuilt-in tools: \u003ccode\u003eRead\u003c\/code\u003e, \u003ccode\u003eEdit\u003c\/code\u003e, \u003ccode\u003eBash\u003c\/code\u003e, \u003ccode\u003eGrep\u003c\/code\u003e\n\u003c\/li\u003e\n  \u003cli\u003eLayered permission system cho tool gating\u003c\/li\u003e\n  \u003cli\u003eAutomatic prompt caching\u003c\/li\u003e\n  \u003cli\u003eEvent stream cho progress streaming hoặc mid-run interception\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003cp\u003eTrade-off: \u003cstrong\u003etool definitions explicit hơn\u003c\/strong\u003e (declare schemas thay vì dựa vào type-hint introspection), nhưng \u003cstrong\u003eít boilerplate ở mọi chỗ khác\u003c\/strong\u003e.\u003c\/p\u003e\n\n\u003ch2\u003eMapping tổng quan\u003c\/h2\u003e\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n\u003cth\u003eOpenAI Agents SDK\u003c\/th\u003e\n\u003cth\u003eClaude Agent SDK\u003c\/th\u003e\n\u003c\/tr\u003e\n  \u003c\/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eAgent(name, instructions, tools)\u003c\/code\u003e\u003c\/td\u003e\n\u003ctd\u003e\n\u003ccode\u003eClaudeAgentOptions\u003c\/code\u003e + system prompt\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003e@function_tool\u003c\/code\u003e\u003c\/td\u003e\n\u003ctd\u003e\n\u003ccode\u003e@tool\u003c\/code\u003e + \u003ccode\u003ecreate_sdk_mcp_server\u003c\/code\u003e\n\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003e@input_guardrail\u003c\/code\u003e\u003c\/td\u003e\n\u003ctd\u003ePlain function before loop (hoặc UserPromptSubmit hook)\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003e@output_guardrail\u003c\/code\u003e\u003c\/td\u003e\n\u003ctd\u003ePlain function on \u003ccode\u003eResultMessage.result\u003c\/code\u003e\n\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eRunner.run(agent, msg)\u003c\/code\u003e\u003c\/td\u003e\n\u003ctd\u003e\n\u003ccode\u003eClaudeSDKClient\u003c\/code\u003e context manager\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eSessions (client-managed)\u003c\/td\u003e\n\u003ctd\u003eReuse same \u003ccode\u003eClaudeSDKClient\u003c\/code\u003e\n\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003e\n\u003ccode\u003econversation_id\u003c\/code\u003e (server)\u003c\/td\u003e\n\u003ctd\u003e\n\u003ccode\u003eresume=session_id\u003c\/code\u003e (disk-backed)\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eBuilt-in tracing dashboard\u003c\/td\u003e\n\u003ctd\u003eOTel-native (Grafana\/Datadog\/Honeycomb)\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003ehandoffs=[...]\u003c\/code\u003e\u003c\/td\u003e\n\u003ctd\u003e\n\u003ccode\u003eAgentDefinition\u003c\/code\u003e + Agent tool\u003c\/td\u003e\n\u003c\/tr\u003e\n  \u003c\/tbody\u003e\n\u003c\/table\u003e\n\n\u003ch2\u003ePrimitive 1: @function_tool → @tool + MCP Server\u003c\/h2\u003e\n\n\u003ch3\u003eOpenAI\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003efrom agents import function_tool\n\n@function_tool\ndef check_policy(expense_type: str, amount: float) -\u0026gt; str:\n    \"\"\"Check if expense complies with company policy\"\"\"\n    if amount \u0026gt; 5000:\n        return \"REQUIRES_APPROVAL: Over $5000 limit\"\n    return \"APPROVED: Within policy limits\"\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eClaude\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003efrom claude_agent_sdk import tool, create_sdk_mcp_server\n\n@tool(\n    name=\"check_policy\",\n    description=\"Check if expense complies with company policy\",\n    schema={\n        \"type\": \"object\",\n        \"properties\": {\n            \"expense_type\": {\"type\": \"string\"},\n            \"amount\": {\"type\": \"number\"}\n        },\n        \"required\": [\"expense_type\", \"amount\"]\n    }\n)\nasync def check_policy(args):\n    if args[\"amount\"] \u0026gt; 5000:\n        result = \"REQUIRES_APPROVAL: Over $5000 limit\"\n    else:\n        result = \"APPROVED: Within policy limits\"\n    return {\"content\": [{\"type\": \"text\", \"text\": result}]}\n\n# Bundle vào MCP server\nexpense_server = create_sdk_mcp_server(\n    \"expense\", tools=[check_policy]\n)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003e\u003cstrong\u003eKey difference:\u003c\/strong\u003e Claude yêu cầu explicit JSON Schema thay vì derive từ type hints. Business logic bên trong không thay đổi.\u003c\/p\u003e\n\n\u003ch2\u003ePrimitive 2: Agent() → ClaudeAgentOptions\u003c\/h2\u003e\n\n\u003ch3\u003eOpenAI\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003efrom agents import Agent\n\nagent = Agent(\n    name=\"expense_agent\",\n    instructions=\"You are an expense approval agent...\",\n    tools=[check_policy],\n    model=\"gpt-4o\"\n)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eClaude\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003efrom claude_agent_sdk import ClaudeAgentOptions\n\nexpense_options = ClaudeAgentOptions(\n    model=\"claude-sonnet-4-6\",\n    system_prompt=\"You are an expense approval agent...\",\n    mcp_servers={\"expense\": expense_server},\n    allowed_tools=[\"mcp__expense__check_policy\"]\n)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003e\u003cstrong\u003eNote về permissions:\u003c\/strong\u003e \u003ccode\u003eallowed_tools\u003c\/code\u003e make tool available. Read-only custom tools chạy tự do. Write tools cần approval trừ khi set \u003ccode\u003epermission_mode=\"bypassPermissions\"\u003c\/code\u003e.\u003c\/p\u003e\n\n\u003ch2\u003ePrimitive 3: Guardrails\u003c\/h2\u003e\n\n\u003ch3\u003eInput Guardrail — OpenAI vs Claude\u003c\/h3\u003e\n\n\u003cp\u003e\u003cstrong\u003eOpenAI:\u003c\/strong\u003e \u003ccode\u003e@input_guardrail\u003c\/code\u003e decorator, registered on Agent.\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eClaude:\u003c\/strong\u003e Plain function called before loop. Hoặc \u003ccode\u003eUserPromptSubmit\u003c\/code\u003e hook.\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003e# Claude: plain function approach\ndef validate_input(prompt: str) -\u0026gt; str | None:\n    if \"$\" not in prompt and \"dollar\" not in prompt.lower():\n        return \"Please include dollar amount in your request\"\n    return None  # Valid\n\n# Gọi trước khi start client\nerror = validate_input(user_prompt)\nif error:\n    print(error)\n    return\n\n# Proceed with agent\nasync with ClaudeSDKClient(options=expense_options) as client:\n    await client.query(user_prompt)\n    ...\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eOutput Guardrail\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Claude: check result after loop\nasync for msg in client.receive_response():\n    if hasattr(msg, 'result'):\n        if \"REQUIRES_APPROVAL\" in msg.result:\n            # Flag for human review\n            notify_manager(msg.result)\n        final_result = msg.result\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003ePrimitive 4: Runner.run() → ClaudeSDKClient\u003c\/h2\u003e\n\n\u003ch3\u003eOpenAI\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eresult = await Runner.run(agent, \"Approve expense: $200 lunch\")\nprint(result.final_output)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eClaude\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003easync with ClaudeSDKClient(options=expense_options) as client:\n    await client.query(\"Approve expense: $200 lunch\")\n    async for msg in client.receive_response():\n        if hasattr(msg, 'result'):\n            print(msg.result)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eClaude's event stream cho bạn thấy \u003cstrong\u003emọi thứ\u003c\/strong\u003e: tool calls, text blocks, final result — theo thứ tự real-time.\u003c\/p\u003e\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n\u003cth\u003eEvent Type\u003c\/th\u003e\n\u003cth\u003eContains\u003c\/th\u003e\n\u003c\/tr\u003e\n  \u003c\/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n\u003ctd\u003eSystemMessage\u003c\/td\u003e\n\u003ctd\u003eSession init metadata\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eAssistantMessage\u003c\/td\u003e\n\u003ctd\u003eText blocks hoặc tool-use blocks\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eUserMessage\u003c\/td\u003e\n\u003ctd\u003eTool-result blocks\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eResultMessage\u003c\/td\u003e\n\u003ctd\u003e.result, .usage, .total_cost_usd\u003c\/td\u003e\n\u003c\/tr\u003e\n  \u003c\/tbody\u003e\n\u003c\/table\u003e\n\n\u003ch2\u003ePrimitive 5: Sessions\u003c\/h2\u003e\n\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eIn-memory:\u003c\/strong\u003e Reuse same \u003ccode\u003eClaudeSDKClient\u003c\/code\u003e instance\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eDisk-backed:\u003c\/strong\u003e \u003ccode\u003eresume=session_id\u003c\/code\u003e để persist across restarts\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003cpre\u003e\u003ccode\u003e# Session persists across restarts\nasync with ClaudeSDKClient(\n    options=expense_options,\n    resume=\"session-abc-123\"  # Disk-backed persistence\n) as client:\n    await client.query(\"Follow up on yesterday's expense\")\n    ...\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eObservability: OTel-native\u003c\/h2\u003e\n\n\u003cp\u003eOpenAI có built-in tracing dashboard. Claude SDK export qua \u003cstrong\u003eOpenTelemetry\u003c\/strong\u003e — plugs vào existing Grafana, Datadog, Honeycomb stack.\u003c\/p\u003e\n\n\u003ch2\u003eMigration Checklist\u003c\/h2\u003e\n\n\u003col\u003e\n  \u003cli\u003ePort \u003ccode\u003e@function_tool\u003c\/code\u003e → \u003ccode\u003e@tool\u003c\/code\u003e + explicit schemas\u003c\/li\u003e\n  \u003cli\u003eReplace \u003ccode\u003eAgent()\u003c\/code\u003e → \u003ccode\u003eClaudeAgentOptions\u003c\/code\u003e\n\u003c\/li\u003e\n  \u003cli\u003eMove guardrails ra khỏi decorators, thành plain functions\u003c\/li\u003e\n  \u003cli\u003eReplace \u003ccode\u003eRunner.run()\u003c\/code\u003e → \u003ccode\u003eClaudeSDKClient\u003c\/code\u003e context manager\u003c\/li\u003e\n  \u003cli\u003ePort sessions → reuse client hoặc \u003ccode\u003eresume=\u003c\/code\u003e\n\u003c\/li\u003e\n  \u003cli\u003eWire OTel export cho observability\u003c\/li\u003e\n  \u003cli\u003eTest từng primitive independently trước khi integrate\u003c\/li\u003e\n\u003c\/ol\u003e\n\n\u003cp\u003eBước tiếp theo: Bắt đầu với \u003ca href=\"\/en\/collections\/nang-cao\"\u003eResearch Agent\u003c\/a\u003e cho Agent SDK basics, hoặc \u003ca href=\"\/en\/collections\/nang-cao\"\u003eChief of Staff Agent\u003c\/a\u003e cho advanced multi-agent patterns.\u003c\/p\u003e\n\n\u003chr\u003e\n\u003ch3\u003eBài viết liên quan\u003c\/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"\/en\/products\/claude-skills-t%E1%BA%A1o-excel-powerpoint-pdf-t%E1%BB%B1-d%E1%BB%99ng\"\u003eClaude Skills — Tạo Excel, PowerPoint, PDF tự động\u003c\/a\u003e\u003c\/li\u003e\n\u003cli\u003e\u003ca href=\"\/en\/products\/rag-v%E1%BB%9Bi-pinecone-claude-vector-database-cho-ai\"\u003eRAG với Pinecone + Claude — Vector database cho AI\u003c\/a\u003e\u003c\/li\u003e\n\u003cli\u003e\u003ca href=\"\/en\/products\/claude-skills-cho-tai-chinh-dashboard-portfolio-phan-tich\"\u003eClaude Skills cho Tài chính — Dashboard, portfolio, phân tích\u003c\/a\u003e\u003c\/li\u003e\n\u003cli\u003e\u003ca href=\"\/en\/products\/claude-cho-engineering-code-review-t%E1%BB%B1-d%E1%BB%99ng\"\u003eClaude cho Engineering: Code Review tự động\u003c\/a\u003e\u003c\/li\u003e\n\u003cli\u003e\u003ca href=\"\/en\/products\/claude-phan-tich-d%E1%BB%AF-li%E1%BB%87u-h%C6%B0%E1%BB%9Bng-d%E1%BA%ABn-k%E1%BA%BFt-n%E1%BB%91i-cong-c%E1%BB%A5\"\u003eClaude Phân tích Dữ liệu: Hướng dẫn Kết nối Công cụ\u003c\/a\u003e\u003c\/li\u003e\n\u003c\/ul\u003e","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47721724838100,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/chuy_n-t_-openai-agents-sdk-sang-claude-h_ng-d_n-migration-chi-ti_t.jpg?v=1774521036","url":"https:\/\/claude.vn\/en\/products\/chuy%e1%bb%83n-t%e1%bb%ab-openai-agents-sdk-sang-claude-h%c6%b0%e1%bb%9bng-d%e1%ba%abn-migration-chi-ti%e1%ba%bft","provider":"CLAUDE.VN","version":"1.0","type":"link"}