{"product_id":"vision-tool-use-trich-xuất-dữ-liệu-từ-hinh-ảnh","title":"Vision + Tool Use — Trích xuất dữ liệu từ hình ảnh","description":"\n\u003cp\u003eMột trong những ứng dụng thú vị nhất của Claude API là kết hợp \u003cstrong\u003eVision\u003c\/strong\u003e (phân tích hình ảnh) với \u003cstrong\u003eTool Use\u003c\/strong\u003e (output có cấu trúc). Kết quả: bạn có thể gửi một bức ảnh và nhận về JSON data — không cần OCR riêng, không cần computer vision model chuyên biệt.\u003c\/p\u003e\n\n\u003cp\u003eBài này xây dựng pipeline trích xuất thông tin dinh dưỡng từ ảnh nhãn thực phẩm — bài toán thực tế cho app theo dõi sức khỏe, hệ thống quản lý kho thực phẩm, hoặc ứng dụng đọc nhãn cho người khiếm thị.\u003c\/p\u003e\n\n\u003ch2\u003eTại sao kết hợp Vision + Tool Use?\u003c\/h2\u003e\n\n\u003cp\u003eVision alone (chỉ gửi ảnh và hỏi) cho output dạng text tự nhiên:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003e# Chi dung Vision - output khong nhat quan\nresponse = client.messages.create(\n    model=\"claude-opus-4-5\",\n    max_tokens=512,\n    messages=[{\n        \"role\": \"user\",\n        \"content\": [\n            {\"type\": \"image\", \"source\": {\"type\": \"url\", \"url\": image_url}},\n            {\"type\": \"text\", \"text\": \"Bao nhieu calories trong san pham nay?\"}\n        ]\n    }]\n)\n# Output co the la:\n# \"San pham nay chua khoang 250 calories moi khau phan.\"\n# \"Calories: 250 kcal\"\n# \"250 Cal (dua tren khau phan 30g)\"\n# Moi lan chay khac nhau!\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eVision + Tool Use cho output JSON có cấu trúc, nhất quán 100%:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003e# Vision + Tool Use - output co cau truc bao dam\n# {\n#   \"calories\": 250,\n#   \"serving_size\": \"30g\",\n#   \"fat_total_g\": 12.5,\n#   \"cholesterol_mg\": 0,\n#   \"sodium_mg\": 180,\n#   \"carbohydrates_g\": 31,\n#   \"protein_g\": 3,\n#   ...\n# }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eĐịnh nghĩa Nutrition Tool\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport anthropic\nimport base64\nimport json\nfrom pathlib import Path\n\nclient = anthropic.Anthropic()\n\nnutrition_tool = {\n    \"name\": \"print_nutrition_info\",\n    \"description\": \"Hien thi thong tin dinh duong tu nhan thuc pham da duoc doc tu hinh anh\",\n    \"input_schema\": {\n        \"type\": \"object\",\n        \"properties\": {\n            \"product_name\": {\n                \"type\": \"string\",\n                \"description\": \"Ten san pham (neu doc duoc tu nhan)\"\n            },\n            \"serving_size\": {\n                \"type\": \"string\",\n                \"description\": \"Khau phan an, VD: '30g', '1 cup (240ml)'\"\n            },\n            \"servings_per_container\": {\n                \"type\": \"number\",\n                \"description\": \"So khau phan trong bao bi, null neu khong co\"\n            },\n            \"calories\": {\n                \"type\": \"number\",\n                \"description\": \"Calories moi khau phan (kcal)\"\n            },\n            \"calories_from_fat\": {\n                \"type\": \"number\",\n                \"description\": \"Calories tu chat beo, null neu khong co\"\n            },\n            \"fat_total_g\": {\n                \"type\": \"number\",\n                \"description\": \"Tong chat beo (gram)\"\n            },\n            \"fat_saturated_g\": {\n                \"type\": \"number\",\n                \"description\": \"Chat beo bao hoa (gram), null neu khong co\"\n            },\n            \"fat_trans_g\": {\n                \"type\": \"number\",\n                \"description\": \"Chat beo trans (gram), null neu khong co\"\n            },\n            \"cholesterol_mg\": {\n                \"type\": \"number\",\n                \"description\": \"Cholesterol (miligram)\"\n            },\n            \"sodium_mg\": {\n                \"type\": \"number\",\n                \"description\": \"Natri\/Muoi (miligram)\"\n            },\n            \"carbohydrates_g\": {\n                \"type\": \"number\",\n                \"description\": \"Tong carbohydrate (gram)\"\n            },\n            \"fiber_g\": {\n                \"type\": \"number\",\n                \"description\": \"Chat xo (gram), null neu khong co\"\n            },\n            \"sugar_g\": {\n                \"type\": \"number\",\n                \"description\": \"Duong (gram), null neu khong co\"\n            },\n            \"protein_g\": {\n                \"type\": \"number\",\n                \"description\": \"Protein (gram)\"\n            },\n            \"vitamins_minerals\": {\n                \"type\": \"array\",\n                \"items\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"name\": {\"type\": \"string\"},\n                        \"amount\": {\"type\": \"string\"},\n                        \"daily_value_percent\": {\"type\": \"number\"}\n                    }\n                },\n                \"description\": \"Danh sach vitamins va khoang chat (neu co)\"\n            },\n            \"confidence\": {\n                \"type\": \"string\",\n                \"enum\": [\"high\", \"medium\", \"low\"],\n                \"description\": \"Do tin cay cua viec doc nhan - high: nhan ro net, low: nhan mo\/bi che\"\n            },\n            \"notes\": {\n                \"type\": \"string\",\n                \"description\": \"Ghi chu them (nhan bi che mot phan, don vi khac thuong, v.v.)\"\n            }\n        },\n        \"required\": [\"serving_size\", \"calories\", \"fat_total_g\", \"cholesterol_mg\",\n                     \"sodium_mg\", \"carbohydrates_g\", \"protein_g\", \"confidence\"]\n    }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eGửi Ảnh đến Claude\u003c\/h2\u003e\n\n\u003cp\u003eClaude hỗ trợ hai cách gửi ảnh: URL trực tiếp hoặc base64 encoded.\u003c\/p\u003e\n\n\u003ch3\u003eCách 1: Gửi qua URL\u003c\/h3\u003e\n\n\u003cpre\u003e\u003ccode\u003edef extract_nutrition_from_url(image_url):\n    response = client.messages.create(\n        model=\"claude-opus-4-5\",\n        max_tokens=1024,\n        tools=[nutrition_tool],\n        tool_choice={\"type\": \"tool\", \"name\": \"print_nutrition_info\"},\n        messages=[{\n            \"role\": \"user\",\n            \"content\": [\n                {\n                    \"type\": \"image\",\n                    \"source\": {\n                        \"type\": \"url\",\n                        \"url\": image_url\n                    }\n                },\n                {\n                    \"type\": \"text\",\n                    \"text\": \"Doc va trich xuat thong tin dinh duong day du tu nhan thuc pham trong hinh.\"\n                }\n            ]\n        }]\n    )\n\n    if response.content[0].type == \"tool_use\":\n        return response.content[0].input\n    return None\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eCách 2: Gửi file local qua base64\u003c\/h3\u003e\n\n\u003cpre\u003e\u003ccode\u003edef extract_nutrition_from_file(image_path):\n    # Doc file va chuyen sang base64\n    with open(image_path, \"rb\") as f:\n        image_data = base64.standard_b64encode(f.read()).decode(\"utf-8\")\n\n    # Xac dinh media type tu extension\n    ext = Path(image_path).suffix.lower()\n    media_types = {\n        \".jpg\": \"image\/jpeg\",\n        \".jpeg\": \"image\/jpeg\",\n        \".png\": \"image\/png\",\n        \".gif\": \"image\/gif\",\n        \".webp\": \"image\/webp\"\n    }\n    media_type = media_types.get(ext, \"image\/jpeg\")\n\n    response = client.messages.create(\n        model=\"claude-opus-4-5\",\n        max_tokens=1024,\n        tools=[nutrition_tool],\n        tool_choice={\"type\": \"tool\", \"name\": \"print_nutrition_info\"},\n        messages=[{\n            \"role\": \"user\",\n            \"content\": [\n                {\n                    \"type\": \"image\",\n                    \"source\": {\n                        \"type\": \"base64\",\n                        \"media_type\": media_type,\n                        \"data\": image_data\n                    }\n                },\n                {\n                    \"type\": \"text\",\n                    \"text\": \"Doc va trich xuat thong tin dinh duong day du tu nhan thuc pham trong hinh.\"\n                }\n            ]\n        }]\n    )\n\n    if response.content[0].type == \"tool_use\":\n        return response.content[0].input\n    return None\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eHiển thị Kết quả\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003edef display_nutrition(data):\n    \"\"\"In thong tin dinh duong dep mat\"\"\"\n    print(\"=\" * 50)\n    if data.get(\"product_name\"):\n        print(f\"SAN PHAM: {data['product_name']}\")\n    print(f\"Khau phan: {data['serving_size']}\")\n    if data.get(\"servings_per_container\"):\n        print(f\"So khau phan: {data['servings_per_container']}\")\n    print(\"=\" * 50)\n    print(f\"{'Calories':\u0026lt;30} {data['calories']} kcal\")\n    if data.get(\"calories_from_fat\"):\n        print(f\"  Tu chat beo: {data['calories_from_fat']} kcal\")\n    print(\"-\" * 50)\n    print(f\"{'Tong chat beo':\u0026lt;30} {data['fat_total_g']}g\")\n    if data.get(\"fat_saturated_g\") is not None:\n        print(f\"  Chat beo bao hoa: {data['fat_saturated_g']}g\")\n    if data.get(\"fat_trans_g\") is not None:\n        print(f\"  Chat beo trans: {data['fat_trans_g']}g\")\n    print(f\"{'Cholesterol':\u0026lt;30} {data['cholesterol_mg']}mg\")\n    print(f\"{'Natri':\u0026lt;30} {data['sodium_mg']}mg\")\n    print(f\"{'Tong Carbohydrate':\u0026lt;30} {data['carbohydrates_g']}g\")\n    if data.get(\"fiber_g\") is not None:\n        print(f\"  Chat xo: {data['fiber_g']}g\")\n    if data.get(\"sugar_g\") is not None:\n        print(f\"  Duong: {data['sugar_g']}g\")\n    print(f\"{'Protein':\u0026lt;30} {data['protein_g']}g\")\n\n    if data.get(\"vitamins_minerals\"):\n        print(\"-\" * 50)\n        print(\"Vitamins va Khoang chat:\")\n        for vm in data[\"vitamins_minerals\"]:\n            dv = f\"({vm['daily_value_percent']}% DV)\" if vm.get(\"daily_value_percent\") else \"\"\n            print(f\"  {vm['name']}: {vm['amount']} {dv}\")\n\n    print(\"=\" * 50)\n    print(f\"Do tin cay: {data['confidence'].upper()}\")\n    if data.get(\"notes\"):\n        print(f\"Ghi chu: {data['notes']}\")\n\n# Test voi anh nutrition label tu web\ntest_url = \"https:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/a\/a4\/Nutricion.jpg\/400px-Nutricion.jpg\"\nresult = extract_nutrition_from_url(test_url)\nif result:\n    display_nutrition(result)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eXử lý nhiều ảnh cùng lúc\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport asyncio\nfrom concurrent.futures import ThreadPoolExecutor\n\ndef batch_extract_nutrition(image_urls, max_workers=5):\n    \"\"\"\n    Trich xuat thong tin dinh duong tu nhieu anh song song.\n    Tra ve list ket qua theo thu tu input.\n    \"\"\"\n    results = []\n    errors = []\n\n    with ThreadPoolExecutor(max_workers=max_workers) as executor:\n        futures = {\n            executor.submit(extract_nutrition_from_url, url): i\n            for i, url in enumerate(image_urls)\n        }\n\n        indexed_results = [None] * len(image_urls)\n        for future in futures:\n            idx = futures[future]\n            try:\n                indexed_results[idx] = {\n                    \"url\": image_urls[idx],\n                    \"data\": future.result(),\n                    \"success\": True\n                }\n            except Exception as e:\n                indexed_results[idx] = {\n                    \"url\": image_urls[idx],\n                    \"error\": str(e),\n                    \"success\": False\n                }\n\n    return indexed_results\n\n# Test voi nhieu anh\nurls = [\n    \"https:\/\/example.com\/product1-label.jpg\",\n    \"https:\/\/example.com\/product2-label.jpg\",\n    \"https:\/\/example.com\/product3-label.jpg\",\n]\n\nresults = batch_extract_nutrition(urls)\nfor r in results:\n    if r[\"success\"]:\n        print(f\"OK: {r['url']}\")\n        print(f\"  Calories: {r['data']['calories']}\")\n    else:\n        print(f\"LOI: {r['url']} - {r['error']}\")\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eỨng dụng thực tế\u003c\/h2\u003e\n\n\u003ch3\u003e1. App theo dõi dinh dưỡng\u003c\/h3\u003e\n\u003cp\u003eUser chụp ảnh thức ăn, app trích xuất thông tin và tự động cộng vào nhật ký dinh dưỡng hàng ngày. Với Claude Vision + Tool Use, chỉ cần vài trăm dòng code để có feature này.\u003c\/p\u003e\n\n\u003ch3\u003e2. Hệ thống so sánh sản phẩm\u003c\/h3\u003e\n\u003cp\u003eScan nhiều nhãn sản phẩm, so sánh nutritional profile, gợi ý lựa chọn lành mạnh hơn. Kết hợp với database sản phẩm để build recommendation engine.\u003c\/p\u003e\n\n\u003ch3\u003e3. Accessibility tool\u003c\/h3\u003e\n\u003cp\u003eNgười dùng khiếm thị chụp ảnh nhãn sản phẩm, hệ thống đọc to thông tin dinh dưỡng quan trọng. Confidence field giúp cảnh báo khi ảnh không rõ.\u003c\/p\u003e\n\n\u003ch3\u003e4. Quality control trong sản xuất\u003c\/h3\u003e\n\u003cp\u003eKiểm tra tự động xem nhãn in trên sản phẩm có đúng format, đủ thông tin bắt buộc theo quy định hay không.\u003c\/p\u003e\n\n\u003ch2\u003eGiới hạn và Xử lý Edge Cases\u003c\/h2\u003e\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n\u003cth\u003eTình huống\u003c\/th\u003e\n\u003cth\u003eCách xử lý\u003c\/th\u003e\n\u003c\/tr\u003e\n  \u003c\/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n\u003ctd\u003eẢnh mờ, khuất\u003c\/td\u003e\n\u003ctd\u003eField \u003ccode\u003econfidence: \"low\"\u003c\/code\u003e + notes mô tả vấn đề\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eNhãn tiếng nước ngoài\u003c\/td\u003e\n\u003ctd\u003eClaude đọc được nhiều ngôn ngữ — thêm instruction \"dịch sang tiếng Việt\"\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eĐơn vị khác thường\u003c\/td\u003e\n\u003ctd\u003eField \u003ccode\u003enotes\u003c\/code\u003e để Claude ghi rõ đơn vị gốc\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eKhông phải nhãn dinh dưỡng\u003c\/td\u003e\n\u003ctd\u003eClaude trả về confidence: \"low\" và notes giải thích\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eẢnh quá lớn\u003c\/td\u003e\n\u003ctd\u003eResize xuống 1024x1024 trước khi gửi — Claude không cần resolution cao để đọc text\u003c\/td\u003e\n\u003c\/tr\u003e\n  \u003c\/tbody\u003e\n\u003c\/table\u003e\n\n\u003ch2\u003eTối ưu Chi phí\u003c\/h2\u003e\n\n\u003cp\u003eVision requests tốn nhiều token hơn text-only. Một số tips:\u003c\/p\u003e\n\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eResize ảnh\u003c\/strong\u003e trước khi gửi — 800x800 thường đủ để đọc text rõ\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eCrop vùng nhãn\u003c\/strong\u003e thay vì gửi cả ảnh sản phẩm — giảm 50-70% image tokens\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eDùng claude-haiku\u003c\/strong\u003e cho batch processing lớn — rẻ hơn nhiều, vẫn đọc text tốt\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eCache kết quả\u003c\/strong\u003e theo image hash — tránh phân tích lại ảnh đã xử lý\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport hashlib\n\n# Cache don gian dung in-memory dict\n_nutrition_cache = {}\n\ndef extract_with_cache(image_url):\n    # Tao cache key tu URL\n    cache_key = hashlib.md5(image_url.encode()).hexdigest()\n\n    if cache_key in _nutrition_cache:\n        print(f\"[Cache hit] {cache_key}\")\n        return _nutrition_cache[cache_key]\n\n    result = extract_nutrition_from_url(image_url)\n    _nutrition_cache[cache_key] = result\n    return result\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eTổng kết: Vision + Tool Use Pattern\u003c\/h2\u003e\n\n\u003cp\u003ePattern Vision + Tool Use hoạt động tốt cho bất kỳ bài toán nào cần \u003cstrong\u003etrích xuất structured data từ visual content\u003c\/strong\u003e:\u003c\/p\u003e\n\n\u003cul\u003e\n  \u003cli\u003eĐọc hóa đơn, receipt — extract line items, total, date\u003c\/li\u003e\n  \u003cli\u003ePhân tích business card — extract tên, email, phone, company\u003c\/li\u003e\n  \u003cli\u003eĐọc bảng trong ảnh — convert sang JSON\/CSV\u003c\/li\u003e\n  \u003cli\u003eTrích xuất thông số từ ảnh sản phẩm kỹ thuật\u003c\/li\u003e\n  \u003cli\u003ePhân tích chart\/biểu đồ — extract data points\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003cp\u003eChỉ cần định nghĩa tool schema phù hợp với cấu trúc data bạn cần, gửi ảnh kèm tool, và đọc \u003ccode\u003etool.input\u003c\/code\u003e — Claude làm phần còn lại.\u003c\/p\u003e\n\n\u003cp\u003eĐây là điểm kết thúc của series Tool Use cơ bản. Bước tiếp theo trong hành trình Claude API: tìm hiểu về \u003cstrong\u003ePrompt Caching\u003c\/strong\u003e để giảm chi phí khi làm việc với system prompts dài và \u003cstrong\u003eStreaming\u003c\/strong\u003e để cải thiện UX trong ứng dụng chat thời gian thực.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47721766748372,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/vision-tool-use-trich-xu_t-d_-li_u-t_-hinh-_nh.jpg?v=1774506582","url":"https:\/\/claude.vn\/products\/vision-tool-use-trich-xu%e1%ba%a5t-d%e1%bb%af-li%e1%bb%87u-t%e1%bb%ab-hinh-%e1%ba%a3nh","provider":"CLAUDE.VN","version":"1.0","type":"link"}