{"product_id":"parallel-tool-calls-gọi-nhiều-tools-dồng-thời-với-claude","title":"Parallel Tool Calls — Gọi nhiều tools đồng thời với Claude","description":"\n\u003cp\u003eKhi bạn có một câu hỏi cần nhiều pieces of information độc lập — thời tiết, giờ hiện tại, tỷ giá — thao tác hiệu quả nhất là gọi tất cả APIs cùng lúc. Claude \u003cem\u003ecó thể\u003c\/em\u003e làm điều này bằng cách trả về nhiều \u003ccode\u003etool_use\u003c\/code\u003e blocks trong một response. Tuy nhiên, trong thực tế với Claude 3.7 Sonnet, điều này không phải lúc nào cũng xảy ra.\u003c\/p\u003e\n\n\u003cp\u003eBài này giải thích vấn đề và trình bày giải pháp thực tế: \u003cstrong\u003ebatch meta-tool\u003c\/strong\u003e.\u003c\/p\u003e\n\n\u003ch2\u003eCách Parallel Tool Calls hoạt động (lý thuyết)\u003c\/h2\u003e\n\n\u003cp\u003eVề mặt API, Claude có thể trả về nhiều tool_use blocks trong một response:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003e# Response ly tuong voi parallel calls:\n# content = [\n#   ToolUseBlock(id=\"tu_001\", name=\"get_weather\", input={\"city\": \"Ha Noi\"}),\n#   ToolUseBlock(id=\"tu_002\", name=\"get_time\", input={\"timezone\": \"Asia\/Ho_Chi_Minh\"}),\n#   ToolUseBlock(id=\"tu_003\", name=\"get_exchange_rate\", input={\"from\": \"USD\", \"to\": \"VND\"})\n# ]\n\n# Neu dieu nay xay ra, ban gui tat ca results cung luc:\ntool_results = [\n    {\"type\": \"tool_result\", \"tool_use_id\": \"tu_001\", \"content\": \"28 do C, co may\"},\n    {\"type\": \"tool_result\", \"tool_use_id\": \"tu_002\", \"content\": \"09:45 AM\"},\n    {\"type\": \"tool_result\", \"tool_use_id\": \"tu_003\", \"content\": \"25,450 VND\/USD\"},\n]\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eKhi nhận được tất cả results cùng một lượt, client có thể execute chúng song song — \u003cstrong\u003egiảm latency từ N*T xuống còn T\u003c\/strong\u003e (với T là thời gian mỗi API call).\u003c\/p\u003e\n\n\u003ch2\u003eVấn đề thực tế: Claude 3.7 Sonnet không luôn parallel\u003c\/h2\u003e\n\n\u003cp\u003eDù bạn truyền \u003ccode\u003ebetas=[\"interleaved-thinking-2025-05-14\"]\u003c\/code\u003e hoặc system prompt yêu cầu parallel calls, Claude 3.7 Sonnet thường chọn gọi tools \u003cstrong\u003etuần tự\u003c\/strong\u003e — từng tool một, chờ kết quả rồi mới gọi tool tiếp theo.\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport anthropic\n\nclient = anthropic.Anthropic()\n\ntools = [\n    {\n        \"name\": \"get_weather\",\n        \"description\": \"Lay thong tin thoi tiet hien tai cua mot thanh pho\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"city\": {\"type\": \"string\", \"description\": \"Ten thanh pho\"}\n            },\n            \"required\": [\"city\"]\n        }\n    },\n    {\n        \"name\": \"get_time\",\n        \"description\": \"Lay gio hien tai theo timezone\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"timezone\": {\"type\": \"string\", \"description\": \"VD: Asia\/Ho_Chi_Minh\"}\n            },\n            \"required\": [\"timezone\"]\n        }\n    }\n]\n\n# Test xem Claude co parallel khong\nresponse = client.messages.create(\n    model=\"claude-3-7-sonnet-20250219\",\n    max_tokens=1024,\n    tools=tools,\n    messages=[{\n        \"role\": \"user\",\n        \"content\": \"Cho toi biet thoi tiet Ha Noi va gio hien tai VN?\"\n    }]\n)\n\ntool_calls = [b for b in response.content if b.type == \"tool_use\"]\nprint(f\"So tool calls trong 1 luot: {len(tool_calls)}\")\n# Ket qua thuong gap: 1 (sequential thay vi 2)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eĐiều này có nghĩa: thay vì 1 vòng lặp, bạn có 2-3 vòng lặp — mỗi vòng là một round-trip API. Với tools có latency cao (database queries, external APIs), đây là vấn đề nghiêm trọng về performance.\u003c\/p\u003e\n\n\u003ch2\u003eGiải pháp: Batch Meta-Tool\u003c\/h2\u003e\n\n\u003cp\u003eÝ tưởng: tạo một \u003cstrong\u003etool đặc biệt\u003c\/strong\u003e nhận vào một danh sách các tool invocations và thực thi tất cả cùng lúc. Claude gọi một tool duy nhất, nhưng tool đó chạy nhiều operations song song.\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003ebatch_tool = {\n    \"name\": \"batch_tool\",\n    \"description\": \"\"\"Cong cu dac biet de goi nhieu tools song song.\n    Su dung khi can nhieu pieces of thong tin doc lap cung luc.\n    Moi invocation trong batch se duoc thuc thi dong thoi.\n    QUAN TRONG: Chi dung batch_tool khi cac calls doc lap nhau\n    (ket qua cua call nay khong anh huong den input cua call khac).\"\"\",\n    \"input_schema\": {\n        \"type\": \"object\",\n        \"properties\": {\n            \"invocations\": {\n                \"type\": \"array\",\n                \"items\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"tool_name\": {\n                            \"type\": \"string\",\n                            \"description\": \"Ten tool can goi\"\n                        },\n                        \"input\": {\n                            \"type\": \"object\",\n                            \"description\": \"Input cho tool do\"\n                        }\n                    },\n                    \"required\": [\"tool_name\", \"input\"]\n                },\n                \"description\": \"Danh sach cac tool calls can thuc hien song song\"\n            }\n        },\n        \"required\": [\"invocations\"]\n    }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eImplement Batch Execution\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport threading\nimport json\n\ndef get_weather(city):\n    \"\"\"Goi weather API (gia lap)\"\"\"\n    # Trong thuc te, day la HTTP request den weather service\n    weather_data = {\n        \"Ha Noi\": \"28 do C, co may, do am 75%\",\n        \"Ho Chi Minh\": \"33 do C, nang, do am 65%\",\n        \"Da Nang\": \"30 do C, it may, do am 70%\"\n    }\n    return weather_data.get(city, f\"Khong co du lieu cho {city}\")\n\ndef get_time(timezone):\n    \"\"\"Lay gio hien tai theo timezone\"\"\"\n    from datetime import datetime\n    import pytz\n    tz = pytz.timezone(timezone)\n    now = datetime.now(tz)\n    return now.strftime(\"%H:%M %d\/%m\/%Y\")\n\ndef execute_single_tool(tool_name, tool_input):\n    \"\"\"Thuc thi mot tool don le\"\"\"\n    if tool_name == \"get_weather\":\n        return get_weather(tool_input[\"city\"])\n    elif tool_name == \"get_time\":\n        return get_time(tool_input[\"timezone\"])\n    return f\"Tool {tool_name} khong ton tai\"\n\ndef execute_batch_tool(invocations):\n    \"\"\"\n    Thuc thi nhieu tools song song bang threading.\n    Tra ve danh sach ket qua theo thu tu invocations.\n    \"\"\"\n    results = [None] * len(invocations)\n    errors = [None] * len(invocations)\n\n    def run_tool(index, tool_name, tool_input):\n        try:\n            results[index] = execute_single_tool(tool_name, tool_input)\n        except Exception as e:\n            errors[index] = str(e)\n            results[index] = f\"Loi: {str(e)}\"\n\n    # Khoi dong tat ca threads cung luc\n    threads = []\n    for i, inv in enumerate(invocations):\n        t = threading.Thread(\n            target=run_tool,\n            args=(i, inv[\"tool_name\"], inv[\"input\"])\n        )\n        threads.append(t)\n        t.start()\n\n    # Cho tat ca threads hoan thanh\n    for t in threads:\n        t.join()\n\n    # Format ket qua\n    batch_result = {}\n    for i, inv in enumerate(invocations):\n        batch_result[inv[\"tool_name\"]] = results[i]\n\n    return json.dumps(batch_result, ensure_ascii=False)\n\ndef execute_tool(tool_name, tool_input):\n    \"\"\"Router chinh - xu ly ca batch va single tools\"\"\"\n    if tool_name == \"batch_tool\":\n        return execute_batch_tool(tool_input[\"invocations\"])\n    return execute_single_tool(tool_name, tool_input)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eAgent Loop với Batch Support\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003eALL_TOOLS = [batch_tool, get_weather_tool_def, get_time_tool_def]\n\ndef run_agent_with_batch(user_message):\n    messages = [{\"role\": \"user\", \"content\": user_message}]\n\n    while True:\n        response = client.messages.create(\n            model=\"claude-3-7-sonnet-20250219\",\n            max_tokens=1024,\n            tools=ALL_TOOLS,\n            messages=messages\n        )\n\n        if response.stop_reason == \"end_turn\":\n            return response.content[0].text\n\n        if response.stop_reason == \"tool_use\":\n            messages.append({\"role\": \"assistant\", \"content\": response.content})\n            tool_results = []\n\n            for block in response.content:\n                if block.type == \"tool_use\":\n                    print(f\"[Tool] {block.name}: {json.dumps(block.input, ensure_ascii=False)[:100]}\")\n                    result = execute_tool(block.name, block.input)\n                    tool_results.append({\n                        \"type\": \"tool_result\",\n                        \"tool_use_id\": block.id,\n                        \"content\": result\n                    })\n\n            messages.append({\"role\": \"user\", \"content\": tool_results})\n        else:\n            break\n\n    return \"\"\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eDemo và So sánh Latency\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport time\n\n# Test khong dung batch\ndef test_sequential():\n    start = time.time()\n    # Gia su moi tool mat 500ms\n    # Sequential: 500ms + 500ms = 1000ms\n    print(\"Sequential: ~1000ms cho 2 tools\")\n\n# Test voi batch tool\ndef test_with_batch():\n    start = time.time()\n\n    response = run_agent_with_batch(\n        \"Cho toi biet thoi tiet Ha Noi va gio hien tai Viet Nam?\"\n    )\n    elapsed = (time.time() - start) * 1000\n\n    print(f\"Response: {response}\")\n    print(f\"Time: {elapsed:.0f}ms\")\n\ntest_with_batch()\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eOutput mong đợi khi Claude dùng batch_tool:\u003c\/h3\u003e\n\n\u003cpre\u003e\u003ccode\u003e[Tool] batch_tool: {\"invocations\": [\n  {\"tool_name\": \"get_weather\", \"input\": {\"city\": \"Ha Noi\"}},\n  {\"tool_name\": \"get_time\", \"input\": {\"timezone\": \"Asia\/Ho_Chi_Minh\"}}\n]}\n\nResponse: Thoi tiet Ha Noi hien tai la 28 do C, co may, do am 75%.\nGio Viet Nam hien tai la 09:45 SA ngay 26\/03\/2026.\nTime: ~520ms (thay vi ~1020ms sequential)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eVới batch tool, \u003cstrong\u003ecả 2 tools chạy song song\u003c\/strong\u003e — tổng thời gian gần bằng thời gian của 1 tool đơn lẻ.\u003c\/p\u003e\n\n\u003ch2\u003eKhi nào Batch Tool thực sự hữu ích?\u003c\/h2\u003e\n\n\u003cp\u003eLợi ích lớn nhất khi:\u003c\/p\u003e\n\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eExternal API calls có latency cao\u003c\/strong\u003e — database queries, third-party services (100ms+)\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eNhiều tools độc lập cần cùng lúc\u003c\/strong\u003e — dashboard reports, morning briefings\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eUser experience quan trọng\u003c\/strong\u003e — chatbot cần phản hồi nhanh\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003cp\u003eBatch tool \u003cstrong\u003ekhông cần thiết\u003c\/strong\u003e khi:\u003c\/p\u003e\n\n\u003cul\u003e\n  \u003cli\u003eTools phụ thuộc nhau (output của tool A là input của tool B)\u003c\/li\u003e\n  \u003cli\u003eTools chạy trong memory — latency đã quá nhỏ\u003c\/li\u003e\n  \u003cli\u003eChỉ có 1-2 tools trong toàn bộ application\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eGiới hạn và Lưu ý\u003c\/h2\u003e\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n\u003cth\u003eVấn đề\u003c\/th\u003e\n\u003cth\u003eChi tiết\u003c\/th\u003e\n\u003c\/tr\u003e\n  \u003c\/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n\u003ctd\u003eClaude không luôn dùng batch\u003c\/td\u003e\n\u003ctd\u003eClaude quyết định khi nào dùng batch_tool — không guarantee. System prompt có thể giúp nhưng không phải lúc nào cũng hiệu quả.\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eThread safety\u003c\/td\u003e\n\u003ctd\u003eĐảm bảo các tools của bạn thread-safe khi chạy parallel. Tránh shared mutable state.\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eError handling\u003c\/td\u003e\n\u003ctd\u003eMột tool fail không nên làm cả batch fail. Bắt exception per-tool và trả về error message.\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eTimeout\u003c\/td\u003e\n\u003ctd\u003eSet timeout cho từng tool trong batch — tránh một slow tool làm chậm cả batch.\u003c\/td\u003e\n\u003c\/tr\u003e\n  \u003c\/tbody\u003e\n\u003c\/table\u003e\n\n\u003ch2\u003eGợi ý System Prompt\u003c\/h2\u003e\n\n\u003cp\u003eĐể Claude ưu tiên dùng batch_tool, thêm instruction vào system prompt:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eSYSTEM_PROMPT = \"\"\"...\n\nKhi can nhieu thong tin doc lap, LUON su dung batch_tool thay vi goi\ntung tool mot. Dieu nay giup giam latency va cai thien trai nghiem nguoi dung.\nCac truong hop nen dung batch: thoi tiet nhieu thanh pho, bao cao nhieu metrics,\nlay thong tin tu nhieu sources cung luc.\n\n...\"\"\"\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eKỹ thuật này không thể đảm bảo 100% Claude sẽ luôn dùng batch, nhưng tăng đáng kể tần suất Claude chọn parallel execution.\u003c\/p\u003e\n\n\u003cp\u003eBài tiếp theo: kết hợp Vision với Tool Use để \u003cstrong\u003etrích xuất dữ liệu có cấu trúc từ hình ảnh\u003c\/strong\u003e — ứng dụng multimodal AI thực tế nhất trong năm 2026.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47721766322388,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/parallel-tool-calls-g_i-nhi_u-tools-d_ng-th_i-v_i-claude.jpg?v=1774521656","url":"https:\/\/claude.vn\/products\/parallel-tool-calls-g%e1%bb%8di-nhi%e1%bb%81u-tools-d%e1%bb%93ng-th%e1%bb%9di-v%e1%bb%9bi-claude","provider":"CLAUDE.VN","version":"1.0","type":"link"}