はじめに:なぜマルチモーダルエージェントが自動操作の次のパラダイムなのか

従来のRPAは固定座標とDOM構造に依存しており、インターフェースが変更されたり解像度が変わったりすると、スクリプトは即座に機能しなくなります。一方、人間がインターフェースを操作するときは、視覚情報に依存しています。ボタンのスタイル、アイコンの意味、色と位置の関係を見ています。マルチモーダルエージェントはこの能力を機械に与えます。視覚理解を通じて、エージェントは画面を「見て」、操作可能な要素を識別し、アクションシーケンスを生成できます。この記事では、DeepSeekの視覚言語モデルを基盤として、画面理解能力を持つ操作エージェントの構築方法を実演し、エンジニアリングの詳細に踏み込みます。

DeepSeekを選ぶ理由は、APIがOpenAI形式と互換性があり導入障壁が低いだけでなく、deepseek-chatモデルが視覚質問応答タスクで安定した性能を発揮し、base_urlによる環境切り替えも迅速にサポートするからです。私たちは「スクリーンリーダー→意図解析→アクション生成」のクローズドループをゼロから実装し、その中の重要な課題について議論します:ピクセルを構造化セマンティクスに変換する方法、アクションの信頼性を保証する方法、エラー回復を処理する方法。

システムアーキテクチャとデータフロー

私たちのエージェントは4つのレイヤーで構成されています:収集層、知覚層、決定層、実行層です。収集層はスクリーンショットの取得や画像入力の受け取りを担当します。知覚層はDeepSeek視覚モデルを使用して画像をテキスト記述に変換します。例えば「ページ上部に検索ボックスがあり、右側の青いボタンにログインと表示されている」などです。決定層はユーザー指示と知覚結果に基づいて、言語モデルを使用してJSON形式のアクションシーケンスを生成します。実行層はシミュレートされたクリックやキーボード入力を呼び出します。プロセス全体で、知覚と決定の2回モデルを呼び出しますが、プロンプトエンジニアリングにより1回に統合して、遅延とコストを削減できます。

データフローの鍵は中間表現の標準化です。知覚結果をJSON構造に統一し、要素タイプ、テキスト、位置(正規化座標)、信頼度を含めます。決定層はこの構造を受け取り、ユーザーの自然言語指示と組み合わせて、[{"action": "click", "target": {"text": "ログイン"}}] のようなアクションを出力します。この設計により、エージェントは解釈可能性を持ち、ロールバックとリトライも容易になります。

ステップ1:画面知覚—ピクセルから構造化セマンティクスへ

知覚層はエージェントの「目」です。素朴な方法はスクリーンショット全体をモデルに送信することですが、大きな画像はトークン消費を増加させ、精度を低下させます。エンジニアリングでは、通常OpenCVを使用して前処理を行います:冗長な背景の除去、ハイライト領域のクロップ、コントラスト調整、モデル推奨サイズ(例:1024x1024)へのリサイズ。その後、DeepSeekのチャットインターフェースを呼び出し、画像とプロンプトを渡して、JSON形式の要素リストを返すように要求します。

実際には、「画面の内容を説明してください」と直接尋ねると、過度に一般化された回答が得られます。強く制約されたプロンプトが必要です。例えば:「あなたはUI自動化の視覚モジュールです。画面上のすべてのインタラクティブ要素を識別し、JSON配列を出力してください。各要素にはtype(button/input/link)、text(表示されている場合)、bbox(正規化[x,y,w,h])、confidenceを含めてください。余分な説明は出力しないでください。」以下は完全な呼び出し例です:

import base64, requests, json

def encode_image(path):
    with open(path, "rb") as f:
        return base64.b64encode(f.read()).decode()

response = requests.post(
    "https://api.deepseek.com/chat/completions",
    headers={
        "Authorization": f"Bearer your-deepseek-api-key",
        "Content-Type": "application/json"
    },
    json={
        "model": "deepseek-chat",
        "messages": [{
            "role": "user",
            "content": [
                {"type": "text", "text": "あなたはUI自動化の視覚モジュールです。画面上のインタラクティブ要素を識別し、JSON配列を出力してください。各要素にはtype、text、bbox、confidenceを含めてください。"},
                {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{encode_image('screen.png')}"}}
            ]
        }],
        "max_tokens": 800
    }
)
result = response.json()["choices"][0]["message"]["content"]
print(json.loads(result))

実際には、モデルが静的テキストを見逃したり、装飾アイコンをボタンと誤認識したりすることがよくあります。対策としては、プロンプトに「テキストとinputを優先し、純粋な画像装飾は無視する」を追加し、出力スキーマをJSONとして定義した後、pydanticで検証し、失敗した場合は別の劣化プロンプトで一度再試行します。また、ページ要素が多すぎる場合(20個以上)、領域ごとに認識することをお勧めします。そうしないと精度が著しく低下します。

ステップ2:視覚セマンティクスからアクション決定へ

要素リストを取得した後、エージェントはユーザー指示を理解し、操作を生成する必要があります。例えば、ユーザーが「百度でDeepSeekチュートリアルを検索して」と言った場合、エージェントは要素リストから検索ボックスと検索ボタンを見つける必要があります。マッチング戦略はテキスト類似度(fuzzywuzzy)に基づくか、LLMに直接決定させることができます。要素リストと指示をモデルに送信し、アクションシーケンスJSONを出力するよう要求します。

決定プロンプトは次のように設計されています:「ユーザー指示と画面要素が与えられたら、アクションシーケンスを生成してください。アクションタイプはclick/type/scrollで、targetは要素インデックス(0から始まる)です。JSON配列のみを出力してください。」要素インデックスは認識の揺れで変わる可能性があるため、ターゲットとして要素のセマンティックテキストまたは座標を使用し、モデルにconfidenceを出力させることをお勧めします。以下は決定コードのスニペットです:

import requests

def plan_actions(user_cmd, elements):
    prompt = f"ユーザー指示:{user_cmd}\n画面要素:{json.dumps(elements, ensure_ascii=False)}\nJSONアクション配列を出力してください。各アクションにはaction(click/type/scroll)、target(要素テキストまたは座標)、value(typeの場合)を含めてください。"
    resp = requests.post(
        "https://api.deepseek.com/chat/completions",
        headers={"Authorization": "Bearer your-deepseek-api-key"},
        json={
            "model": "deepseek-chat",
            "messages": [{"role": "user", "content": prompt}],
            "temperature": 0.1,
            "response_format": {"type": "json_object"}
        }
    )
    return json.loads(resp.json()["choices"][0]["message"]["content"])["actions"]

ここで重要なポイントが2つあります:第一に、temperatureを0.1に下げて、ランダム性による正当なアクションの拒否を避けます。第二に、JSON出力形式を強制することで(サポートされている場合)、解析失敗を大幅に減らせます。モデルが不正なJSONを返した場合、堅牢なパーサーを実装します:最初の「[」と最後の「]」の間の内容を抽出し、json5ライブラリで解析し、それでも失敗した場合はエラーをモデルにフィードバックして再試行します。

ステップ3:アクション実行と座標マッピング

アクションは認識時のピクセル座標を直接使用できません。スクリーンショットとリアルタイム画面が異なる可能性があるからです。各アクションの前に再スクリーンショットを取得し、ターゲットを特定します。位置特定アルゴリズムは、決定時と同じ要素テキストを最初に探します。動的コンテンツにより欠落している場合は、OCR(例:paddleocr)によるテキスト再抽出にフォールバックします。実行層はpyautoguiまたはwin32apiを使用してマウスとキーボードをシミュレートし、画面スケーリングとマルチモニターの問題に注意します:座標がDPI換算されていることを確認します。

エンジニアリングで遭遇した落とし穴には、クリックが無効(要素が隠れている)、スライダードラッグが不正確、スクロール位置のずれなどがあります。ほとんどの問題の根本原因は、視覚認識が提供するbboxと実際のクリック可能領域の間にずれがあることです(モデルはテキストブロック全体をクリック可能と見なすことが多い)。私たちは補償スキームをまとめました:input要素の場合、中心から右に10ピクセルクリックします。ボタンの場合、テキストの中心をクリックします。さらに、アクション後にインターフェース状態が期待どおりに変化しない場合、エージェントは検証メカニズムを持つべきです—各操作後に前後のスクリーンショットの主要領域を比較し、不一致があればロールバックして代替アクションを試みます。

ステップ4:エラー処理と自己修復

単一の視覚認識精度は100%にはなり得ないため、エージェントにはフォールトトレランスが必要です。私たちは2レベルのエラー処理を設計しました:局所的なフォールトトレランス—アクションが失敗した場合(例:ターゲットが見つからない)、画面を再認識し、要素リストを更新して、もう一度試みます。グローバルな再試行—連続して複数回失敗した場合、決定戦略を変更します。例えば、クリックをキーイベント(Tabでフォーカスを切り替える)に変更します。さらに、DeepSeekの自己説明能力を活用し、エラー情報をモデルにフィードバックして、代替案を提案させます。これはReActパターンに似ています。

効果的なテクニックは「アクション後スナップショット差分」です:各操作後にスクリーンショットを取得し、操作前と知覚ハッシュ(pHash)を計算し、差分がしきい値未満の場合、操作が有効でなかったと見なして再試行をトリガーします。実験では、異なるしきい値が精度に与える影響を比較しました:しきい値0.2では誤報率が8%、0.1では15%に上昇しましたが、再現率は向上しました。最終的に0.15をバランスポイントとして選択し、コードで調整可能なパラメータとして設定しました。

パフォーマンスチューニング:遅延とコストの二重最適化

マルチモーダル呼び出しはテキストのみよりも遅く、高価です。100回の操作をベンチマークしました:完全な知覚-決定プロセスの平均所要時間は2.8秒で、画像エンコーディングとネットワーク転送が60%を占めました。最適化オプションには以下が含まれます:1) DeepSeekのストリーミング出力(stream=true)を使用して最初のトークン待ち時間を短縮。2) 知覚段階で必要なクロップ画像のみを送信してサイズを削減。3) 静的インターフェースにはキャッシュを使用。例えば、ウィンドウタイトルが変わらなければ以前の要素リストを再利用。コスト面では、各操作に約1500入力+300出力トークンが必要で、0.01人民元未満に相当し、許容範囲です。

また、「高速パス」も実装しました:ユーザー指示が一般的なテンプレート(例:「特定のボタンをクリック」)に一致する場合、LLM決定をスキップし、要素テキストマッチングに基づいて直接実行し、速度を0.4秒に向上させました。実際には、約30%の操作が高速パスを利用でき、全体的なエクスペリエンスが大幅に向上しました。以下の表は、異なる構成での比較です:

方式平均遅延成功率コスト/回
純粋な視覚認識2.8秒89%0.01元
高速パス+視覚1.2秒93%0.006元
再試行メカニズム付き3.5秒97%0.015元

デモから本番へ:エンジニアリングの要点

プロトタイプを本番に移行するには、安定性とセキュリティを解決する必要があります。まず、API呼び出しにはレート制限を避けるために並行制御を実装する必要があります。私たちはasyncioとセマフォを使用しています。次に、すべての外部入力(ユーザー指示、画像)は機密情報をフィルタリングし、プロンプトインジェクションを防ぐ必要があります。例えば、ユーザーが指示に「以前の指示を無視」を含める可能性があります。コンテンツセキュリティチェックを追加し、指示の長さとキーワードを制限しました。最後に、モデル出力はホワイトリスト検証を通過し、期待されるアクションタイプのみを許可する必要があります。

もう一つの見落とされがちな点:環境の一貫性です。OSによってスクリーンショットの色空間やマウス制御APIが大きく異なるため、抽象化レイヤーをカプセル化し、統一インターフェースを提供します。Windowsではpyautogui、Linuxではxdotool、macOSではQuartzを使用します。メンテナンスコストは増加しますが、クロスプラットフォーム互換性が得られます。

ケースレビュー:PPTページめくりと注釈の自動化

「音声指示に基づいてスライドに注釈を付ける」を例にします。ユーザーが「3ページ目のタイトルの下に下線を引いて」と言った場合、エージェントのフロー:現在のスライドを知覚し、タイトル領域を識別します。決定層はdraw_lineアクションを生成します。実行層はOpenCVを使用して対応する座標に線を描画し、スクリーンショットで検証します。プロセス中に発生した問題は、スライドページが切り替わると座標が無効になることです—各アクションの前に再知覚を強制し、ページ識別子(タイトルテキストなど)を記録して位置特定に使用しました。

もう一つの教訓:指示が複数のステップを含む場合(例:「最初に次のページに移動し、次にグラフをクリック」)、すべてのアクションを一度に生成すると、2番目のステップが最初のステップ後のインターフェース状態に依存するため、しばしば失敗します。私たちの解決策は反復的な操作です:現在のステップのみを生成し、実行後にもう一度決定層を呼び出し、クローズドループを形成します。呼び出し回数は増えますが、成功率は70%から94%に向上しました。

まとめと今後の展望

この記事では、マルチモーダルエージェントの理論から実践までの完全な道筋を示しました。鍵となるのは:視覚知覚の正確さ、アクション決定の信頼性、そしてフォールトトレランスメカニズムの設計です。DeepSeekモデルはこのタスクで安定した性能を発揮し、APIの柔軟性によりプロンプトと出力形式をカスタマイズできます。次のステップとして、エージェントが頻繁に使用するインターフェースのレイアウトを記憶し、繰り返しの知覚を減らすためのメモリメカニズムを導入する予定です。また、視覚言語モデルを使用してアクション座標を直接予測し、中間表現を排除して遅延をさらに削減することも探求しています。

マルチモーダルエージェントの可能性はこれにとどまりません。自動テスト、アクセシビリティ支援、リモートデスクトップ制御などに応用できます。この記事の詳細が、実際のプロジェクトで一般的な落とし穴を回避し、独自の自動化ビジュアルエージェントを構築するのに役立つことを願っています。