Gemini 2.5 Computer Use登場:Googleが実現した「自律操作AI」が人間の代わりにPCを動かす時代へ

Gemini 2.5 Computer Use Google 実現 自律操作AI 人間 代わり PC 動かす 時代
押さえておきたいポイント
  • Gemini 2.5 Computer Useは、クリックや入力などのPC操作をAIが自律的に行えるモデルです。
  • 従来モデルよりも高精度かつ低遅延で、ClaudeやGPT-4系モデルを上回る性能を示しています。
  • 高リスクな操作には安全確認機能が搭載されており、安心して利用できます。

2025年10月、Googleから新たなAIモデルが登場!

今回リリースされた「Gemini 2.5 Computer Use」は「Gemini 2.5 Pro」を基盤に構築され、ユーザーインターフェース(UI)を直接操作できるAIエージェントです。

本記事では、Gemini 2.5 Computer Useの概要、使い方、実際の使用感について解説します。

最後までお読みいただければ、Gemini 2.5 Computer Useを理解し、活用できるようになります。ぜひ最後までお読みください!

\生成AIを活用して業務プロセスを自動化/

目次

Gemini 2.5 Computer Useの概要

「Gemini 2.5 Computer Use」は、テキスト入力だけでなく、クリック・スクロール・入力などの操作をAIが自律的に実行できるように設計されたモデルです。

これにより、Webフォームの自動入力、ECサイトでのデータ収集、業務アプリの操作といった作業を人間に代わって実行できます。

参考:https://blog.google/technology/google-deepmind/gemini-computer-use-model/

このモデルは「Gemini 2.5 Pro」の視覚理解・推論能力を拡張したもので、画面のスクリーンショットを解析し、自然言語による指示に基づいて操作を理解します。

Gemini 2.5 Computer Useの仕組み

Gemini 2.5 Computer Useは、Gemini APIに新たに追加されたツール「computer_use」です。

このツールは以下の流れで動作します。

  1. ユーザーリクエスト、スクリーンショット、直近の操作履歴を入力として受け取る。
  2. モデルが画面上の要素を解析し、次に行うべきアクションを判断。
  3. クリック・入力・スクロールといったUI操作を、function_callの形で出力。
  4. 実行後の画面を再度スクリーンショットとしてモデルに送り返し、処理を繰り返す。

このループ構造により、AIはまるで人間のようにブラウザを操作します。

特定の操作(購入やログインなど)では、ユーザー確認を要求する仕組みも備わっています。

Gemini 2.5 Computer Useの性能

Gemini 2.5 Computer Useは、複数のWebおよびモバイル制御ベンチマークでトップクラスの性能を示しています。

特にBrowserbase社の「Online-Mind2Web」評価では、競合モデルを上回る高精度かつ低遅延を記録しました。

参考:https://blog.google/technology/google-deepmind/gemini-computer-use-model/

Browserbaseは、OpenAIやAnthropicなどが使うエージェント実行環境の標準評価基盤であり、Gemini 2.5 Computer UseはこれらのベンチマークでClaudeやGPT-4系のブラウザ制御ツールを上回る結果を出しています。

参考:https://blog.google/technology/google-deepmind/gemini-computer-use-model/

上図は動作の精度と処理の遅延時間を表したグラフです。Gemini 2.5 Computer Useは最も右下に位置しており、競合モデルを圧倒

Googleはこの結果を「human-level interaction latency(人間レベルの操作応答速度)に近づいた」と表現しており、実際のWeb操作でも「自然で待ち時間の少ない」ユーザー体験を提供できる段階に到達しています。※1

なお、AIが脆弱性を自動修正するCodeMenderについて詳しく知りたい方は、下記の記事を合わせてご確認ください。

Gemini 2.5 Computer Useの安全性

「Gemini 2.5 Computer Use」は、PCやWebのUIを実際に操作する性質上、意図的な誤用・モデルの予期せぬ挙動・Web環境でのプロンプトインジェクションや詐欺といったリスクを前提に設計されています。

Googleは、こうしたリスクに対処するために安全機能をモデル自体に学習させたうえで、開発者が使える追加のコントロールも提供しています。

まず、モデル内蔵の安全機能に加えて、開発者向けには2つの機能が示されています。

1つ目は「per-step safety service」。これはモデル外の推論時サービスが、各アクションを実行前に評価する仕組みです。

2つ目は「system instructions」で、特定の高リスク操作をあらかじめ拒否させる、あるいはユーザー確認を必須にできます。高リスク操作の例としては、システムの整合性を損なう行為、セキュリティ侵害、CAPTCHAの回避、医療機器の操作など。

なお、AIエージェントがブラウザでデバッグするChrome DevTools MCPサーバーについて詳しく知りたい方は、下記の記事を合わせてご確認ください。

Gemini 2.5 Computer Useの料金体系

Gemini 2.5 Computer UseはAPIを使って利用するため、Gemini 2.5 Proのレートと同様です。

  • ≤200K tokens:入力 $1.25 / 出力 $10.00
  • >200K tokens:入力 $2.50 / 出力 $15.00

Gemini 2.5 Computer Useの使い方

Gemini 2.5 Computer Useを使う方法は次の3つです。

  • Google AI Studio
  • Vertex AI
  • Browserbaseデモ

Browserbaseデモはアクセスするだけで利用できるため、少し試してみたい方にもおすすめです。

参考:https://gemini.browserbase.com/

2.5倍速くらいにしていますが、こちらの指示に従ってNVIDIAの株価を調べてくれました。

Google AI StudioからAPIキーを取得すれば、コードを書いて実行することもできます。

サンプルコードはこちら
from typing import List, Tuple
import time
from playwright.sync_api import sync_playwright
from google import genai
from google.genai import types
from google.genai.types import Content, Part

# ===============================
# 設定
# ===============================

API_KEY = ""  # ← ここに自分のGemini APIキーをハードコード
MODEL_NAME = "gemini-2.5-computer-use-preview-10-2025"

SCREEN_WIDTH = 1440
SCREEN_HEIGHT = 900
TURN_LIMIT = 8

# ===============================
# 座標補正関数
# ===============================

def denormalize_x(x: int, screen_width: int) -> int:
    return int(x / 1000 * screen_width)

def denormalize_y(y: int, screen_height: int) -> int:
    return int(y / 1000 * screen_height)

# ===============================
# モデル出力(関数呼び出し)を実行
# ===============================

def execute_function_calls(candidate, page, screen_width, screen_height):
    results = []
    for part in candidate.content.parts:
        if not part.function_call:
            continue

        fc = part.function_call
        fname = fc.name
        args = fc.args or {}

        if "safety_decision" in args:
            print("⚠ Safety check:", args["safety_decision"].get("explanation", ""))
            user = input("Proceed? [y/N]: ").strip().lower()
            if user != "y":
                results.append((fname, {"error": "user_denied"}))
                continue

        try:
            if fname == "open_web_browser":
                pass
            elif fname == "navigate":
                page.goto(args["url"])
            elif fname == "click_at":
                x = denormalize_x(args["x"], screen_width)
                y = denormalize_y(args["y"], screen_height)
                page.mouse.click(x, y)
            elif fname == "type_text_at":
                x = denormalize_x(args["x"], screen_width)
                y = denormalize_y(args["y"], screen_height)
                page.mouse.click(x, y)
                page.keyboard.press("Meta+A")
                page.keyboard.press("Backspace")
                page.keyboard.type(args["text"])
                if args.get("press_enter", True):
                    page.keyboard.press("Enter")
            elif fname == "scroll_document":
                direction = args.get("direction", "down")
                delta = 1200 if direction == "down" else -1200
                page.mouse.wheel(0, delta)
            else:
                print(f"Unhandled function: {fname}")

            page.wait_for_load_state(timeout=5000)
            time.sleep(0.8)
            results.append((fname, {"status": "ok"}))

        except Exception as e:
            results.append((fname, {"error": str(e)}))
            print(f"Error executing {fname}: {e}")
    return results

# ===============================
# 実行後のスクショ+URLを返す
# ===============================

def get_function_responses(page, results):
    screenshot_bytes = page.screenshot(type="png")
    current_url = page.url
    frs = []
    for name, result in results:
        payload = {"url": current_url}
        payload.update(result)
        frs.append(
            types.FunctionResponse(
                name=name,
                response=payload,
                parts=[types.FunctionResponsePart(
                    inline_data=types.FunctionResponseBlob(
                        mime_type="image/png", data=screenshot_bytes
                    )
                )]
            )
        )
    return frs

# ===============================
# メイン処理
# ===============================

def main():
    print("🚀 Starting Gemini 2.5 Computer Use demo (NVDA stock)...")

    # Playwright起動(headless=False で操作を可視化)
    pw = sync_playwright().start()
    browser = pw.chromium.launch(headless=False)
    context = browser.new_context(viewport={"width": SCREEN_WIDTH, "height": SCREEN_HEIGHT})
    page = context.new_page()
    page.goto("https://www.google.com")

    # Gemini APIクライアント
    client = genai.Client(api_key=API_KEY)

    # Computer Useツール設定
    config = types.GenerateContentConfig(
        tools=[types.Tool(
            computer_use=types.ComputerUse(
                environment=types.Environment.ENVIRONMENT_BROWSER
            )
        )]
    )

    # 目標を指定
    goal = (
        "Open Google Finance and read the current stock price and price change for NVIDIA (ticker NVDA). "
        "You can go directly to https://www.google.com/finance/quote/NVDA:NASDAQ. "
        "When you find the data, stop and reply with: 'NVIDIA stock: $<price> (<change>)'. "
        "Do not perform purchases or sign in."
    )

    # 初回入力
    contents: List[Content] = [
        Content(role="user", parts=[
            Part(text=goal),
            Part.from_bytes(data=page.screenshot(type="png"), mime_type="image/png"),
        ])
    ]

    # ループ実行
    for turn in range(TURN_LIMIT):
        print(f"\n--- Turn {turn + 1} ---")
        response = client.models.generate_content(
            model=MODEL_NAME,
            contents=contents,
            config=config,
        )
        candidate = response.candidates[0]
        contents.append(candidate.content)

        has_actions = any(p.function_call for p in candidate.content.parts)
        if not has_actions:
            final_text = " ".join([p.text for p in candidate.content.parts if getattr(p, "text", None)])
            print("\n✅ Result:", final_text.strip())
            break

        results = execute_function_calls(candidate, page, SCREEN_WIDTH, SCREEN_HEIGHT)
        frs = get_function_responses(page, results)
        contents.append(Content(role="user", parts=[Part(function_response=fr) for fr in frs]))

    # 終了
    browser.close()
    pw.stop()
    print("✅ Completed.")

if __name__ == "__main__":
    main()

これでかなり自由にブラウザを操作することが可能です。これまで人力でやっていた作業を、Gemini 2.5 Computer Useが自動で実行してくれます。

対応しているアクション一覧

Gemini 2.5 Computer Useが対応しているアクションは下記の通りです。

コマンド名内容
open_web_browserブラウザを開く
click_at(x, y)指定座標をクリック
type_text_at(x, y, text)文字を入力
scroll_document(direction)ページを上下左右にスクロール
drag_and_drop要素をドラッグ&ドロップ
go_back / go_forwardページ遷移(戻る・進む)
key_combinationキーボード操作(例:Ctrl+C)
Gemini 2.5 Computer Useの対応アクション一覧

Gemini 2.5 Computer UseとClaude Computer useを比較検証

Gemini 2.5 Computer Useがリリースされる前に、ClaudeからComputer useがリリースされています。

今回は、ClaudeのComputer useとGemini 2.5 Computer Useを比較してみます。

比較内容は前述したNVIDIAの株価を取得するものです。

サンプルコードはこちら
import json
import random
import time
import subprocess
from typing import List, Dict, Any
import anthropic

API_KEY = ""  # ← ClaudeのAPIキーをここに記入
MODEL = "claude-3-7-sonnet-20250219"
BETA_FLAG = "computer-use-2025-01-24"

client = anthropic.Anthropic(api_key=API_KEY)

def run_bash(cmd: str, retries: int = 5, base_delay: float = 0.8) -> Dict[str, Any]:
    last = {"exit_code": -1, "stdout": "", "stderr": "", "http_status": None}
    for attempt in range(1, retries + 1):
        try:
            completed = subprocess.run(
                cmd, shell=True, capture_output=True, text=True, timeout=20
            )
            out = completed.stdout
            http_status = None
            if "HTTP_STATUS:" in out:
                *body_lines, status_line = out.strip().splitlines()
                if status_line.startswith("HTTP_STATUS:"):
                    http_status = int(status_line.split(":")[1])
                    out = "\n".join(body_lines)
            last = {
                "exit_code": completed.returncode,
                "stdout": out[:200000],
                "stderr": completed.stderr[:200000],
                "http_status": http_status,
            }
            if http_status and 200 <= http_status < 300:
                return last
            if http_status in (429, 500, 502, 503, 504):
                delay = base_delay * (2 ** (attempt - 1)) + random.uniform(0, 0.4)
                print(f"⚠ HTTP {http_status}. retry {attempt}/{retries} after {delay:.1f}s")
                time.sleep(delay)
                continue
            return last
        except Exception as e:
            last = {"exit_code": -1, "stdout": "", "stderr": str(e), "http_status": None}
            time.sleep(0.3)
    return last

def main():
    user_goal = (
        "Use ONE bash call to fetch raw JSON from "
        "https://query1.finance.yahoo.com/v7/finance/quote?symbols=NVDA . "
        "I will parse JSON and include a parsed object (brief) in tool_result. "
        "AFTER receiving tool_result, DO NOT call any tool again and DO NOT retry. "
        "If brief contains price data, output exactly one line: "
        "'NVDA price: $<price> (<change>, <changePercent>%)'. "
        "If brief has an 'error' field, output exactly one line: "
        "'Temporarily rate-limited. Please try again later.'"
    )

    tools = [
        {
            "type": "computer_20250124",
            "name": "computer",
            "display_width_px": 1024,
            "display_height_px": 768,
            "display_number": 1,
        },
        {
            "type": "text_editor_20250124",
            "name": "str_replace_editor",  # ← ここが正解!
        },
        {
            "type": "bash_20250124",
            "name": "bash",
        },
    ]

    messages: List[Dict[str, Any]] = [{"role": "user", "content": user_goal}]

    for _ in range(4):
        resp = client.beta.messages.create(
            model=MODEL,
            max_tokens=800,
            messages=messages,
            tools=tools,
            betas=[BETA_FLAG],
        )
        content = resp.content
        messages.append({"role": "assistant", "content": content})
        tool_calls = [b for b in content if getattr(b, "type", None) == "tool_use"]
        if not tool_calls:

            final_texts = [b.text for b in content if getattr(b, "type", None) == "text"]
            print("✅ Result:", " ".join(final_texts).strip())
            break

        tool_results_blocks: List[Dict[str, Any]] = []
        for call in tool_calls:
            name = call.name
            tool_input = call.input or {}

            if name == "bash":
                # ---- 固定curlコマンド(ヘッダ付き・HTTPコード出力)----
                url = "https://query1.finance.yahoo.com/v7/finance/quote?symbols=NVDA"
                ua = (
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) "
                    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0 Safari/537.36"
                )
                cmd = (
                    "curl -s --compressed "
                    f"-H 'User-Agent: {ua}' "
                    "-H 'Accept: application/json' "
                    "-H 'Accept-Language: en-US,en;q=0.9' "
                    "-H 'Connection: keep-alive' "
                    f"'{url}' "
                    r"-w '\nHTTP_STATUS:%{http_code}\n'"
                )
                result = run_bash(cmd)

                # ---- HTTPステータス確認 ----
                status = result.get("http_status")
                body = result.get("stdout", "")

                if not status or status < 200 or status >= 300:
                    brief = {"error": "upstream_error", "http_status": status, "note": "rate-limited or blocked"}
                else:
                    try:
                        data = json.loads(body)
                        res = (data.get("quoteResponse") or {}).get("result") or []
                        if res:
                            q = res[0]
                            brief = {
                                "symbol": q.get("symbol"),
                                "price": q.get("regularMarketPrice"),
                                "change": q.get("regularMarketChange"),
                                "changePercent": q.get("regularMarketChangePercent"),
                                "currency": q.get("currency"),
                                "marketState": q.get("marketState"),
                            }
                        else:
                            brief = {"error": "empty_result", "http_status": status}
                    except Exception as e:
                        brief = {"error": f"parse_failed: {e}", "http_status": status}

                # Claudeへのtool_result(briefのみ)
                tool_results_blocks.append({
                    "type": "tool_result",
                    "tool_use_id": call.id,
                    "content": [{"type": "text", "text": json.dumps(brief)}],
                })

        # ---- Claudeへtool_resultを返して最終回答を得る ----
        messages.append({"role": "user", "content": tool_results_blocks})
        resp_final = client.beta.messages.create(
            model=MODEL,
            max_tokens=800,
            messages=messages,
            tools=tools,
            betas=[BETA_FLAG],
        )

        final_texts = [b.text for b in resp_final.content if getattr(b, "type", None) == "text"]
        final_text = " ".join(final_texts).strip()

        if final_text:
            print("✅ Result:", final_text)
        else:
            # Claudeが応答しなかった場合のフォールバック
            try:
                last_brief = json.loads(tool_results_blocks[-1]["content"][0]["text"])
                if last_brief.get("price") is not None:
                    p = last_brief["price"]
                    ch = last_brief.get("change")
                    chp = last_brief.get("changePercent")
                    print(f"✅ Fallback: NVDA price: ${p} ({ch}, {chp}%)")
                else:
                    print("⚠ Claude returned no text and brief had no price.")
            except Exception as e:
                print(f"⚠ Claude returned no text. Fallback failed: {e}")
        break

if __name__ == "__main__":
    main()
結果はこちら
⚠ HTTP 429. retry 1/5 after 1.1s
⚠ HTTP 429. retry 2/5 after 1.7s
⚠ HTTP 429. retry 3/5 after 3.5s
⚠ HTTP 429. retry 4/5 after 6.6s
⚠ HTTP 429. retry 5/5 after 13.2s
✅ Result: Temporarily rate-limited. Please try again later.

時間がかかった割には株価を取得できていません。また、ClaudeのComputer useを使う場合、Dockerを使わないとブラウザを開いて実行ができないようなので、手軽さでみてもGemini 2.5 Computer Useを使うのが良いなと感じました。

なお、OpenAIのワークフロー構築AIエージェントについて詳しく知りたい方は、下記の記事を合わせてご確認ください。

まとめ

本記事ではGemini 2.5 Computer Useの概要から使い方、実際に使ってみた所感をお伝えしました。

過去にはClaude Computer useがリリースされていますが、実際に使ってみた印象としては、Gemini 2.5 Computer Useに軍配が上がると感じました。

ぜひ皆さんも本記事を参考にGemini 2.5 Computer Useを使ってみてください!

WEELが“失敗しないAI導入”を伴走します。

最後に

いかがだったでしょうか?

Gemini 2.5 Computer Useを業務自動化やWeb操作AIに導入したい方は、無料相談で具体的な導入支援を受けてみてください。実運用に最適なセットアップ方法をご案内します。

株式会社WEELは、自社・業務特化の効果が出るAIプロダクト開発が強みです!

開発実績として、

・新規事業室での「リサーチ」「分析」「事業計画検討」を70%自動化するAIエージェント
・社内お問い合わせの1次回答を自動化するRAG型のチャットボット
・過去事例や最新情報を加味して、10秒で記事のたたき台を作成できるAIプロダクト
・お客様からのメール対応の工数を80%削減したAIメール
・サーバーやAI PCを活用したオンプレでの生成AI活用
・生徒の感情や学習状況を踏まえ、勉強をアシストするAIアシスタント

などの開発実績がございます。

生成AIを活用したプロダクト開発の支援内容は、以下のページでも詳しくご覧いただけます。
➡︎株式会社WEELのサービスを詳しく見る。

まずは、「無料相談」にてご相談を承っておりますので、ご興味がある方はぜひご連絡ください。
➡︎生成AIを使った業務効率化、生成AIツールの開発について相談をしてみる。

生成AIを社内で活用していきたい方へ
無料相談

「生成AIを社内で活用したい」「生成AIの事業をやっていきたい」という方に向けて、生成AI社内セミナー・勉強会をさせていただいております。

セミナー内容や料金については、ご相談ください。

また、サービス紹介資料もご用意しておりますので、併せてご確認ください。

投稿者

  • WEEL Media部

    株式会社WEELが運営する生成系AI関連メディア「生成AI Media」は、AIの専門家によるWebメディアです。 AIに特化した編集部がAIの活用方法、導入事例、ニュース、トレンド情報を発信しています。

  • URLをコピーしました!
  • URLをコピーしました!
目次