Claude Media
Computer Useの座標ズレを直すスケーリング計算式とmacOS Retinaの罠

Computer Useの座標ズレを直すスケーリング計算式とmacOS Retinaの罠

Computer Useのクリックが外れる最大の原因は座標のスケール不一致です。画面サイズとスクリーンショットサイズの比率計算式、モデル世代ごとの上限差、macOS Retinaの2倍問題を実装コード付きで解説します。

Computer Useのクリックがずれる原因は座標スケール不一致

Computer Useツールで最初につまずくのは、たいてい「クリックした位置が微妙にずれる」現象です。原因の大半は座標のスケール不一致にあります。

Claudeが返すcoordinateは、直前に返したスクリーンショット画像のピクセル空間で表現されます。実際のディスプレイがそのスクリーンショットより大きければ、Claudeの座標をそのまま実画面へ適用するとずれます。

新しいComputer Useツールセット(computer_toolset_20260801)は、表示解像度をリクエストに含みません。旧バージョンにあったdisplay_width_pxのようなパラメータは廃止されています。つまり、スケーリングの計算とその後の座標変換は、すべてアプリケーション側のコードで担う設計です。

ツールセット自体は画像を縮小してくれません。上限を超えた画像をtool_resultに含めると、APIはバリデーションエラーでリクエストごと拒否します。エラーの原因が「単に画像が大きすぎるだけ」なのか「スケーリング計算そのものが間違っている」のかは切り分けにくく、実装初期にまとめて混乱しやすいポイントです。

zoomアクションを使った場合も座標系は変わりません。Claudeはzoomで切り出した拡大画像を見た後も、常にフルスクリーンショットのピクセル空間で座標を返します。拡大画像上の相対位置に変換されるわけではないので、zoomの実装有無にかかわらず、座標のスケーリング処理は1本化しておけます。

スケーリング計算式 — 長辺とビジュアルトークンの両方に収める

スクリーンショットは、モデルの画像サイズ上限に収まるよう、返す前に縮小します。条件は2つあります。28の倍数に切り上げた各辺が長辺の上限以下であることと、ビジュアルトークン数⌈幅 / 28⌉ × ⌈高さ / 28⌉が上限以下であることです。Opus 4.7以降のモデルでは、長辺2,576px、ビジュアルトークン4,784が上限になります。

長辺の比率とトークン数の平方根比率の小さい方を倍率にする近似式は、よく見かける書き方です。ただ、この近似は切り捨てた結果が上限を超えることがあります。2880×1800の画面で、その例を比べます。

くらべる

2880×1800の縮め方(Opus 4.7以降の上限)

4,840トークン

倍率の近似式

倍率は約0.8506で、結果は2449×1531です。ビジュアルトークンが4,784を超え、境界で弾かれる余地が残ります。

4,698トークン

上限に収まる最大の寸法を探す

結果は2420×1512です。2つの条件を両方満たす最大のサイズを選びます。

下のコードは、条件を満たす最大の寸法を二分探索で求めます。考え方は、vision-coordinatesページにある参照実装と同じです。1920×1080は上限内なのでそのまま、3840×2160は2576×1449(4,784トークン)になり、公式ドキュメントの換算表と同じ値です。

import math
 
def tokens(w, h):
    return math.ceil(w / 28) * math.ceil(h / 28)
 
def fit_size(w, h, max_edge=2576, max_tokens=4784):
    def fits(a, b):
        return (math.ceil(a / 28) * 28 <= max_edge
                and math.ceil(b / 28) * 28 <= max_edge
                and tokens(a, b) <= max_tokens)
    if fits(w, h):
        return w, h
    if h > w:
        rh, rw = fit_size(h, w, max_edge, max_tokens)
        return rw, rh
    lo, hi = 1, w  # lo は収まる、hi は収まらない
    while lo + 1 < hi:
        mid = (lo + hi) // 2
        if fits(mid, max(round(mid * h / w), 1)):
            lo = mid
        else:
            hi = mid
    return lo, max(round(lo * h / w), 1)

縮小はスクリーンショット取得時に一度だけ行い、その寸法を保持します。Claudeから返った座標は、縮小後の画像から実画面への比率で戻します。

sent_w, sent_h = fit_size(capture_w, capture_h)
 
def execute_click(x, y):
    perform_click(x * screen_w / sent_w, y * screen_h / sent_h)

比率を毎回同じ関数から導けるようにしておくことが、往復の一貫性を保つ条件です。値を保持し忘れて再計算がずれると、その回だけクリックが外れる再現しにくい不具合になります。

Opus 4.7以降のモデルは上限そのものが変わる

2576(長辺の上限ピクセル数)と4784(ビジュアルトークンの上限)は、Computer Useツールセットに対応するOpus 4.7以降のモデル向けの値です。それ以前のモデルは、より厳しい上限を持っています。

モデル世代長辺の上限総量の上限
Opus 4.7以降(ツールセット対応モデル全て)長辺の上限2,576px総量の上限ビジュアルトークン4,784(約3.75メガピクセル)
それ以前のモデル長辺の上限1,568px総量の上限ビジュアルトークン1,568

旧モデルの総量は、Computer Useのページでは約1.15メガピクセル、visionのページではビジュアルトークン1,568と書かれています。トークンに直すと約1.23メガピクセルで、1.15とは差があります。

同じ1920×1080でも、モデルの世代で結果が分かれます。

数字

1920×1080のスクリーンショットを返したとき

  • Opus 4.7以降

    縮小なし

    2,691トークン

  • それ以前のモデル

    1456×819

    1,560トークン

  • 4K(3840×2160)

    2576×1449

    Opus 4.7以降。4,784トークン

visionページの換算表の値

画質が妙に粗いときは、どちらの上限で計算しているかをまず疑います。旧モデルを使う場合は、同じfit_sizeにfit_size(w, h, 1568, 1568)と上限を渡せば足ります。公式の旧モデル向け例(1568px / 約1.15メガピクセル)の近似式で1920×1080を縮めると1429×804で、1456×819より約2%粗くなります。

macOS Retinaは2倍でスクリーンショットを撮る

macOSのRetinaディスプレイは、デバイスピクセル比2でスクリーンショットをキャプチャします。論理的な画面座標の2倍の解像度で画像が生成されるため、そのまま渡すと、Claudeの座標系とクリックを送る座標系が2倍ずれます。公式の対処は、送る前に2分の1へ縮小するか、返ってきた座標を実行前に半分にするかのどちらかです。

縮小と座標の戻しを1つの比率にまとめると、2倍の補正を別に持たずに済みます。論理1440×900(撮影は2880×1800)の画面を例に流れを追います。

手順

Retinaの画面でクリック座標を戻す流れ

  1. 1

    撮影する

    論理解像度は1440×900、Retinaで撮った画像は2880×1800です。

  2. 2

    縮小して返す

    fit_size(2880, 1800)の結果は2420×1512です。この寸法をsent_w、sent_hとして保持します。

  3. 3

    Claudeが座標を返す

    縮小後の画像上で、たとえば(1210, 756)が返ります。

  4. 4

    論理座標へ戻す

    1210 × 1440 / 2420 = 720、756 × 900 / 1512 = 450で、クリックは(720, 450)です。

縮小率の逆数だけで戻すと、(1210, 756)は(1440, 900)になります。これは物理ピクセルの座標で、論理座標では画面の右下の隅です。縮小と2倍の補正を二重に掛けるのも、逆方向のずれの原因です。開発機がRetinaで本番環境がRetinaでない構成もあるため、環境ごとに撮影サイズと論理サイズを取得して比率を計算します。

解像度はどう選ぶか

スクリーンショットの解像度は、大きければ良いというものではありません。公式ドキュメントが挙げる用途別の目安は次のとおりです。

用途解像度の目安
一般的なデスクトップ操作解像度の目安1024x768または1280x720
Webアプリケーションの操作解像度の目安1280x800または1366x768
避けるべき範囲解像度の目安1920x1080超(パフォーマンス低下を招きやすい)

スクリーンショットはbase64のPNGかJPEGでエンコードし、大きな画像は圧縮を検討します。タイムスタンプや表示状態のようなメタデータを添えておくと、後からログを追うときに役立ちます。

高い解像度を使う場合は、座標が正しくスケーリングされているかを重点的に確認します。

zoomメンバーツールで小さい対象の精度を上げる

解像度を上げずに精度を確保する手段として、zoomメンバーツールがあります。regionパラメータで[x0, y0, x1, y1]の矩形を指定すると、その範囲だけをフル解像度で切り出し、アスペクト比を保ったまま通常のスクリーンショットと同じ寸法に収めて返します。ファイル名やタブのタイトル、ステータスバーの文字、行番号、ボタンのラベルのように、縮小したフルスクリーンショットでは読み取れない小さな文字を確認したいときに使います。ブラウザ操作でアクセシビリティツリーが使えるページなら、ref指定という座標を経由しない選択肢もあります(参考: Browser Use Toolの座標指定)。

Claudeが期待通りにzoomを呼ばない場合、画面全体について聞くのではなく、対象の領域や要素を名指しして質問すると呼び出されやすくなります。zoomが実装できない環境では、有効なまま放置してエラーを返し続けるのではなく、configsで明示的に無効化しておきます。

縮小のずれを黙って通さない設定

モデルを切り替えたときや、画像の取得元を変えたときに、サーバー側の縮小が静かに入り込むことがあります。通常のimageブロックにはtransformationsフィールドがあり、"oversized_image": "error"を付けると、縮小が必要な画像は400のinvalid_request_errorで弾かれます。エラーには、画像の寸法と収まる最大の寸法が出ます。

{
  "type": "image",
  "source": { "type": "base64", "media_type": "image/png", "data": "..." },
  "transformations": { "oversized_image": "error" }
}

1920×1080の画像は、旧モデルを指定したリクエストでは1456×819への縮小が必要としてエラーになり、Opus 4.7以降のモデルでは収まります。クリック対象になる画像だけにこの指定を付け、ロゴのように縮んでも困らない画像は既定のdownsizeのままにできます。Computer Useのtool_resultは、上限を超えると前述のとおり検証エラーで拒否されます。

スクリーンショット履歴が増えるとキャッシュも壊れる

長時間のエージェントループは、スクリーンショットを1枚あたりおよそ1,000〜1,800入力トークン消費しながら蓄積していきます。1リクエストに含まれる画像が20枚を超えると、すべての画像がより厳しいサイズ制限の対象になります。対策は、各スクリーンショットの長辺を2000px以下に抑えるか、20枚以下になるよう古いものを間引くかのどちらかです。

間引き方にもコツがあります。毎ターン1枚ずつ削除すると、プロンプトキャッシュの接頭辞が毎回変わってしまい、キャッシュが効かなくなります。直近3枚を残して25ターンごとにまとめて間引く、といった「間引きをバッチで行う」運用にすると、間引きが起きないターンの間はキャッシュの接頭辞がバイト単位で同一に保たれます。

Claude Fable 5.1・Claude Opus 5.5・Claude Sonnet 5.5では、クライアント側の削除を避けます。古いスクリーンショットを消すと、それ以降のすべてのthinkingブロックが無効になるためです。削除ではなく、スクリーンショットを2000px以下にリサイズし、サーバー側のtool result clearingで古いものをコンテキストから落とす方法が示されています。どうしても削除するなら、prefix_mismatch_behavior: "drop_block"を設定したままにします。

実行前に座標をバリデーションする

スケーリングを正しく実装しても、Claudeが画面外の座標を返すことはあり得ます。実際にクリックへ渡す前に、座標がディスプレイの範囲内に収まっているかを確認しておくと、範囲外クリックによる不可解な挙動を未然に防げます。

display_width, display_height = 1024, 768
 
def validate_action(action_type, params):
    if action_type == "left_click" and "coordinate" in params:
        x, y = params["coordinate"]
        if not (0 <= x < display_width and 0 <= y < display_height):
            return False, "Coordinates out of bounds"
    return True, None

バリデーションで弾いた場合は、実行をスキップしてis_error: trueのツール結果を返します。スケーリングの計算式やRetina補正にバグが残っていると、この範囲外エラーが高頻度で出るようになるため、実装初期の動作確認にも使える簡易的な検知手段になります。あわせて、実行したアクションと結果をログに残しておくと、後から「どのターンでスケールがずれ始めたか」を追いやすくなります。

スクリーンショット取得が失敗したときの返し方

画面ロックやディスプレイの一時的な不可用性で、スクリーンショットの取得自体が失敗することもあります。この場合もtool_resultの形式は変えず、is_error: trueと失敗理由を短いテキストで返します。

{
  "type": "tool_result",
  "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
  "toolset_name": "computer",
  "content": "Error: Failed to capture screenshot. Display may be locked or unavailable.",
  "is_error": true
}

画面外座標や範囲外クリックも同じ形式で返します。バッチアクションの途中でこのエラーが発生した場合は、残りのブロックを実行せず、決まった文言でスキップした旨を返す必要があります。座標のスケーリングそのものとは別問題ですが、スケーリングのバグはこの経路でエラーとして表面化することが多いです。

症状から原因を切り分ける早見表

座標のスケーリングに関連する不具合は、症状ごとに疑うべき箇所が異なります。実装を見直す前に、次の対応関係で当たりを付けておくと遠回りを避けられます。

症状まず疑う箇所
クリックが一定方向にずれ続けるまず疑う箇所スクリーンショットと実画面のサイズ比を計算していない
macOSでだけクリックが2倍近くずれるまず疑う箇所Retinaのデバイスピクセル比2を補正していない
Opus 4.7以降で画質が妙に粗いまず疑う箇所旧モデル向けの上限(1568px / 1,568トークン)をそのまま使っている
長時間のループで途中から挙動が不安定になるまず疑う箇所スクリーンショット履歴が20枚を超え、より厳しい制限の対象になっている

いずれも「Claudeが見ている画像の座標系」と「実際にクリックを送る座標系」がどこかでずれているという点は共通しています。スケーリング処理を1箇所にまとめてログを仕込んでおくと、後から原因を追いやすくなります。

まとめ

座標のずれは、Claudeが見た画像と、クリックを送る画面の寸法比を1か所で管理すれば、ほとんど消えます。縮小の寸法は近似式でなく上限内の最大値で求め、Retinaの2倍もその比率に含めます。小さい対象は解像度全体を上げるよりzoomで読ませる方が軽く済みます。実装の骨格はComputer Useツールを自前実装する最小構成に、スケーリングを直してもクリックが外れる場合の原因はクリックが外れる4パターンの診断表にあります。

この記事を共有:XはてブLinkedIn