Claude Computer Use を本番環境で使い始めて半年ほど経ちますが、最初に感じた「これは本当に動くのか?」という半信半疑は、今では「どこまで任せられるか」という問いに変わっています。macOS での一般提供が始まり、実務での採用事例も増えてきた今、改めてこの機能の全体像と実践的な使い方を整理したいと思います。
Computer Use の本質 — スクリーンショットと操作の繰り返し
Computer Use が他のAI機能と根本的に異なる点は、Claudeが「見る→判断する→操作する」というループを自律的に回す点にあります。内部的には次の流れで動いています。
スクリーンショットを取得 → Claudeがその画像を見てどこをクリックすべきか判断 → 座標を指定してクリック・入力・スクロールを実行 → 再度スクリーンショットを取得して結果を確認 → 次の操作へ
この設計の妙は、GUI を解析する専用のビジョンモデルを使っていないことです。Claude の汎用的なマルチモーダル理解力を使っているため、見たことのないUIにも柔軟に対応できます。その代わり、ピクセル座標の精度に限界があり、密集したUIや動的コンテンツでは誤操作が発生しやすいという特性もあります。
使う前にこの特性を理解しておくことが、実務での成功率を大きく左右します。
セットアップ — APIキーから動作確認まで
1. 必要なもの
pip install anthropic pillowモデルは claude-opus-4-6 または claude-sonnet-4-6 を使います。Computer Use は現時点で Bedrock や Vertex AI でも利用できますが、最新機能は Anthropic API 直接接続で最も早く使えます。
2. 最小構成のコード
import anthropic
import base64
from PIL import ImageGrab
client = anthropic.Anthropic()
def take_screenshot():
"""スクリーンショットを取得してbase64エンコードする"""
screenshot = ImageGrab.grab()
screenshot = screenshot.resize(
(screenshot.width // 2, screenshot.height // 2)
) # コスト削減のためリサイズ
screenshot.save("/tmp/screen.png")
with open("/tmp/screen.png", "rb") as f:
return base64.standard_b64encode(f.read()).decode("utf-8")
def run_computer_use_task(task: str):
screenshot_b64 = take_screenshot()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
tools=[
{
"type": "computer_20241022",
"name": "computer",
"display_width_px": 1280,
"display_height_px": 800,
}
],
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": screenshot_b64,
},
},
{"type": "text", "text": task}
],
}
],
)
return responseこのコードはClaudeに「現在のスクリーンショットを見て、タスクを実行してください」と指示するものです。実際にはここから操作ループを実装する必要があります。
3. 操作ループの実装
Computer Use の核心は、Claudeが tool_use ブロックで操作を返してきたとき、実際にその操作を実行してから、結果のスクリーンショットをClaudeに返すループです。
import subprocess
import time
def execute_action(action: dict) -> str:
"""Claudeが指示したアクションを実行する"""
action_type = action.get("action")
if action_type == "screenshot":
return take_screenshot()
elif action_type == "left_click":
x, y = action["coordinate"]
subprocess.run(["cliclick", f"c:{x},{y}"]) # macOS
# Linux の場合: subprocess.run(["xdotool", "click", "--clearmodifiers", "1"])
time.sleep(0.5)
return take_screenshot()
elif action_type == "type":
text = action["text"]
subprocess.run(["cliclick", f"t:{text}"])
time.sleep(0.3)
return take_screenshot()
elif action_type == "key":
key = action["key"]
subprocess.run(["cliclick", f"kp:{key}"])
time.sleep(0.3)
return take_screenshot()
elif action_type == "scroll":
x, y = action["coordinate"]
direction = action.get("direction", "down")
amount = action.get("amount", 3)
if direction == "down":
subprocess.run(["cliclick", f"dd:{x},{y}"])
return take_screenshot()
return take_screenshot()
def run_task_with_loop(task: str, max_iterations: int = 20):
"""操作ループを伴う完全なタスク実行"""
messages = [
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": take_screenshot(),
},
},
{"type": "text", "text": task}
],
}
]
for i in range(max_iterations):
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
tools=[{
"type": "computer_20241022",
"name": "computer",
"display_width_px": 1280,
"display_height_px": 800,
}],
messages=messages,
)
# stop_reason が "end_turn" なら完了
if response.stop_reason == "end_turn":
print("タスク完了")
break
# tool_use ブロックを処理
tool_results = []
for block in response.content:
if block.type == "tool_use" and block.name == "computer":
new_screenshot = execute_action(block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": new_screenshot,
},
}
],
})
# メッセージ履歴を更新
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
return response