import json
import uuid
from openai import OpenAI
from openai.types.responses import (
FunctionToolParam,
ResponseInputParam,
)
def run_computer_use(endpoint, prompt, model="gpt-6-astra"):
client = OpenAI()
session_id = str(uuid.uuid4())
tools: list[FunctionToolParam] = [
{
"type": "function",
"name": "exec_py",
"description": (
"Run Python in a persistent desktop. Variables persist across calls. "
"PyAutoGUI operations are synchronous. Available: pyautogui, time, "
"log(value), and display(PIL_image). Inspect the screen with "
"display(pyautogui.screenshot()) before acting. Use screenshot "
"coordinates and check the screen after a short group of actions. "
"Keep screenshots in memory and PyAutoGUI's fail-safe enabled."
),
"parameters": {
"type": "object",
"properties": {"code": {"type": "string"}},
"required": ["code"],
"additionalProperties": False,
},
"strict": True,
}
]
next_input: ResponseInputParam = [{"role": "user", "content": prompt}]
previous_response_id = None
for turn in range(20):
response = client.responses.create(
model=model,
tools=tools,
input=next_input,
previous_response_id=previous_response_id,
)
if response.status != "completed":
raise RuntimeError(f"Response stopped with status: {response.status}")
calls = [item for item in response.output if item.type == "function_call"]
if not calls and any(
item.type == "message" and item.phase != "commentary"
for item in response.output
):
print(response.output_text)
return
if turn == 19:
raise RuntimeError(
"The task reached the 20-response limit. Inspect the last result."
)
next_input = []
for call in calls:
if call.name != "exec_py":
raise ValueError(f"Unexpected tool: {call.name}")
code = json.loads(call.arguments)["code"]
output = execute_in_sandbox(code, session_id, endpoint)
next_input.append(
{
"type": "function_call_output",
"call_id": call.call_id,
"output": output,
}
)
previous_response_id = response.id「コンピューターの使用」を利用すると、モデルがブラウザやデスクトップの画面を操作できます。フォームへの入力、ユーザーの操作フローのテスト、アプリケーション内のタスクの実行などに利用できます。
モデルが確認する必要のある情報と、誰が環境を運用するかに応じて方法を選びます。
| タスク | 使用するツールやガイド |
|---|---|
| 自分のブラウザやデスクトップでスクリーンショットを取得し、マウスやキーボードで操作する | ComputerToolset と Responses のコンピューターツールを併用します。 |
| 自分のブラウザでページのテキストを読み取る、またはコントロールやタブを操作する | BrowserToolset を使います。ドライバーが指定の操作を関数ツールとして公開します。 |
| OpenAI がホストするブラウザを使う | 別ページの Agents API の「コンピューターの使用」ガイドに従ってください。 |
画面を操作せずに公開情報を取得するには、ウェブ検索を使います。
SDK の使用
SDK は、Python または Node で Responses のツール呼び出しのループを実行します。アプリケーション側では、環境の状態を読み取ったり、許可された操作を実行したりするコードをドライバーとして用意します。セッションと権限の管理、終了時のクリーンアップもアプリケーションが担当します。
ホスティング環境への接続
ブラウザやデスクトップを自分で運用する代わりに、パートナーが提供する環境を利用できます。ドライバーが提供される場合と、パートナーが公開する操作をアプリケーション側で SDK に対応付ける場合があります。セットアップ手順と対応するパッケージのバージョンは、各パートナーのガイドを確認してください。
| 環境 | 統合方法 |
|---|---|
| Browserbase | Browserbase がホストするセッションで Stagehand ブラウザドライバーを使います。Stagehand 統合ガイドに従うか、Python と TypeScript の例を参照してください。 |
| Browser Use | Browser Use ライブラリのドライバーを使うか、Browser Use Cloud との統合を利用して既存の Playwright コードを接続します。どちらのガイドも Python を使用します。 |
| Daytona | ComputerToolset を Daytona のデスクトップサンドボックスで使います。Daytona のコンピューター操作用ヘルパーガイド(Python)に従ってください。Agents SDK の例は、Daytona でコンピューターを操作する実装例を参照してください。 |
| E2B Desktop | デスクトップサンドボックスでコンピューターを操作したり、コードを実行したりできます。E2B の「コンピューターの使用」ガイドに従うか、Python と TypeScript の例を参照してください。 |
セッションにタイムアウトを設定し、実行が失敗した場合もセッションを閉じるか解放してください。クリーンアップに失敗した場合はプロバイダーのダッシュボードを確認します。プロバイダーのネットワークルールを使って、セッションからアクセスできるサイトとリソースを制限してください。
初めてのタスクの実行
Python 3.11 以降または Node.js 22.6 以降を使用します。API キーを作成し、環境変数としてエクスポートしてください。どちらの例も一時的なローカルブラウザを使い、Responses API を呼び出すため、料金が発生する場合があります。
新しいフォルダーにパッケージと Chromium をインストールします。
# Python
python3 -m venv .venv
.venv/bin/python -m pip install openai==3.31.0 playwright==1.61.0
.venv/bin/python -m playwright install chromium
# Node
npm install openai@7.33.0 playwright@1.61.1
npx playwright install chromium
Windows では py -3 を python3 の代わりに、.venv\Scripts\python.exe を .venv/bin/python の代わりに使います。
モデルは架空の荷物受け取りページのスクリーンショットを見て、クリックして受け取り情報を表示します。ドライバーが許可するのは、スクリーンショットの取得と、受け取り情報を表示するコントロールの左クリックだけです。ページからのリクエストはブロックし、実行終了時にブラウザを閉じます。スクリプトは Responses へのリクエストを最大 6 回に制限し、成功を報告する前にページを確認します。プロジェクトには、コンピューターツールをサポートするモデルへのアクセス権が必要です。
使用する言語に合わせて、サンプルを pickup.py または pickup.mjs として保存します。
import asyncio
import base64
from openai import AsyncComputerToolset, AsyncOpenAI
from playwright.async_api import async_playwright
class PickupDriver:
def __init__(self, page):
self.page = page
async def screenshot(self):
image = await self.page.screenshot(type="png")
return {
"type": "computer_screenshot",
"image_url": "data:image/png;base64," + base64.b64encode(image).decode(),
}
async def allowed(self, action):
if action.type == "screenshot":
return True
if action.type != "click" or action.button != "left" or action.keys:
return False
box = await self.page.locator("summary").bounding_box()
return bool(
box
and box["x"] <= action.x < box["x"] + box["width"]
and box["y"] <= action.y < box["y"] + box["height"]
)
async def click(self, action):
# The page may have changed after approval, so bind the click again.
if not await self.allowed(action):
raise RuntimeError("Pickup details moved or the click is not allowed.")
await self.page.mouse.click(action.x, action.y)
async def refuse(self, action):
raise RuntimeError(f"This exercise does not allow {action.type}.")
double_click = drag = keypress = move = scroll = type = wait = refuse
async def main():
async with (
async_playwright() as playwright,
AsyncOpenAI(timeout=30, max_retries=0) as client,
):
browser = await playwright.chromium.launch(env={}, chromium_sandbox=True)
try:
page = await browser.new_page(viewport={"width": 1024, "height": 768})
await page.route("**/*", lambda route: route.abort())
await page.set_content(
"<title>Parcel pickup</title><main>"
"<h1>Parcel pickup</h1><p>Order #1042 is ready.</p>"
"<details><summary>Show pickup details</summary>"
"<p>Locker 18. Pickup code: 6249.</p></details></main>"
)
driver = PickupDriver(page)
tools = AsyncComputerToolset(driver)
try:
response = await tools.run(
client,
{
"model": "gpt-6.1-sol",
"store": False,
"input": "Take a screenshot. Open 'Show pickup details' and report the locker and pickup code. Use only screenshots and left clicks.",
},
before_action=driver.allowed,
max_steps=6,
)
if await page.locator("details").get_attribute("open") is None:
raise RuntimeError("Pickup details are still closed.")
print(
"Verified pickup details:",
await page.locator("details p").inner_text(),
)
print("Answer:", response.output_text)
finally:
tools.close()
finally:
await browser.close()
if __name__ == "__main__":
asyncio.run(main()).venv/bin/python pickup.py または node pickup.mjs を実行します。成功すると、スクリプトは Verified pickup details: Locker 18. Pickup code: 6249. に続いてモデルの回答を出力します。API の安全性チェックが保留中の場合、このサンプルは停止します。アプリケーションがそのチェックに対応できる場合に限り、人による承認を求めるコールバックを追加してください。
モデルは来訪者用バッジのフォームに名前を入力し、Create badge をクリックします。ドライバーが公開する操作は、ページの読み取り、文字入力、クリックだけです。確認用コールバックは、操作対象をこのフォームのコントロールに限定し、名前には Mira だけを許可します。スクリプトは成功を報告する前に、ページに表示された結果を検証します。
使用する言語に合わせて、サンプルを badge.py または badge.mjs として保存します。
import asyncio
from openai import AsyncOpenAI
from openai.lib.browser import AsyncBrowserToolset
from openai.lib.responses_tool_runner import AsyncResponsesToolRunner
from playwright.async_api import async_playwright
class BadgeForm:
def __init__(self, page):
self.page = page
async def state(self):
return {
"tabs": [
{
"id": "badge",
"title": await self.page.title(),
"url": self.page.url,
"active": True,
}
]
}
async def read_page(self, args):
return {
"version": 1,
"text": await self.page.locator("body").inner_text(),
"elements": [
{"ref": "name", "role": "textbox", "label": "Name"},
{"ref": "save", "role": "button", "label": "Create badge"},
],
"omitted": [],
}
async def type(self, args):
await self.page.locator("#name").fill(args["text"])
async def click(self, args):
await self.page.locator("#save").click()
async def confirm(self, context):
args = context["arguments"]
if self.page.url != "about:blank" or args["tab_id"] not in (None, "badge"):
return False
return (
context["member"] == "type"
and args["target"] == "name"
and args["text"] == "Mira"
) or (context["member"] == "click" and args["target"] == "save")
async def main():
async with (
async_playwright() as playwright,
AsyncOpenAI(timeout=30, max_retries=0) as client,
):
browser = await playwright.chromium.launch(env={}, chromium_sandbox=True)
try:
page = await browser.new_page()
await page.route("**/*", lambda route: route.abort())
await page.set_content("""
<title>Visitor badge</title>
<label>Name <input id="name"></label>
<button id="save" onclick="document.querySelector('output').innerText =
'Badge ready: ' + document.querySelector('#name').value">Create badge</button>
<output></output>
""")
driver = BadgeForm(page)
tools = AsyncBrowserToolset(
driver, members=["read_page", "type", "click"], confirm=driver.confirm
)
response = await AsyncResponsesToolRunner(browser=tools).run(
client,
{
"model": "gpt-6-astra",
"store": False,
"input": "Create a visitor badge for Mira.",
},
max_steps=6,
)
result = await page.locator("output").inner_text()
if result != "Badge ready: Mira":
raise RuntimeError(f"The badge was not created: {result!r}")
print(result)
print("Answer:", response.output_text)
finally:
await browser.close()
if __name__ == "__main__":
asyncio.run(main()).venv/bin/python badge.py または node badge.mjs を実行します。成功すると、スクリプトは Badge ready: Mira に続いてモデルの回答を出力します。
API がツールを拒否する場合や Chromium が起動しない場合は、実行時のトラブルシューティングを参照してください。クリック位置がずれる場合は、スクリーンショットと座標を確認してください。
モデルへのアクセス、料金、データの確認
- モデル: ブラウザ操作用の関数ツールを使う場合は、モデルが Responses と Function Calling に対応していることを確認します。ドライバーが画像を返す場合は、画像入力への対応も確認してください。スクリーンショットに基づいてコンピューターを操作する場合は、モデルの Tools 欄で「コンピューターの使用」への対応を確認します。モデルカタログを参照し、プロジェクトから対象のモデルにアクセスできることを確認してください。
- 料金: Responses へのリクエストごとに、入力トークンと出力トークンの料金が発生する場合があります。スクリーンショットは画像入力としてカウントされます。API 料金を確認し、必要に応じて画像入力の料金を見積もるか、リクエストの使用量を確認してください。ホスティング環境の利用には別途料金がかかる場合があります。
- データ: SDK は、ドライバーが返したページのテキストやスクリーンショットを OpenAI に送信します。サンプルでは
storeをfalseに設定していますが、この設定ですべてのデータ保持が無効になるわけではありません。データ管理と画像入力に関する例外を確認してください。環境のプロバイダーには独自のデータポリシーがあります。
変更の確認と承認
次に、Python または Node の予約デモを試してください。架空の予約フォームに入力し、送信前に承認を求め、保存された項目と予約確認内容をチェックします。ブラウザの動作を見ながら、手動操作に切り替えることも、モデルに操作を戻すことも、実行を停止することもできます。どちらのバージョンでも、API を呼び出さずに実際のブラウザでローカルのリハーサルを行えます。
デスクトップで SDK による操作を確認するには、デスクトップ操作を画面で確認できるデモを試してください。
ドライバーの調整
サンプルページを、アプリケーションが管理する環境に置き換えます。ブラウザドライバーでは、members でタスクに必要な操作だけを公開します。コンピュータードライバーでは、必須メソッドを実装し、サポートしていない操作を拒否します。実行が終了するまでセッションを維持し、終了後にアプリケーションが作成したリソースを閉じてください。
ページの内容はモデルの挙動に影響することがあります。新しいブラウザプロファイルか、隔離されたデスクトップを使ってください。ネットワークアクセスを制限し、ページのテキストやスクリーンショットに認証情報が含まれないようにします。変更を加える前に承認を求め、成功を報告する前に保存結果を確認してください。メソッドの仕様、承認、URL ポリシー、復旧方法については、ドライバーリファレンスを参照してください。
選択肢が決まっている場合の Decisions への切り替え
モデルが操作を選び、その引数を生成する必要がある場合は Responses を使います。Decisions を使うのは、許可する操作とそれぞれの引数の値がすべてアプリケーション側であらかじめ決まっている場合だけです。たとえば、アプリケーションが route_billing、route_technical、review を選択肢として提示する場合です。Decisions が選択肢を 1 つ選び、アプリケーションがその操作を承認して実行し、結果を確認します。ページが変わったら、それまでの選択肢は破棄してください。Decisions ガイドを参照してください。
予約デモで試すには、Decisions にアクセスできるキーを使い、OPENAI_DECISIONS_MODEL を設定してサーバーを再起動します。新しい実行を開始し、Decisions を選択してください。
API の直接利用
ツール呼び出しのループを自分で管理する場合は、Responses API を直接使います。環境を用意し、モデルが要求した操作を実行するのはアプリケーション側です。アプリケーションとの接続方法を選んでください。
- コード実行: モデルが PyAutoGUI や Playwright などのライブラリを使って画面を操作するコードを書きます。1 回の呼び出しに複数の操作やループ、条件分岐を組み込めます。
- コンピューターツール: モデルが構造化されたマウス操作とキーボード操作を返し、アプリケーションがそれらをブラウザやデスクトップへの入力に変換します。
GPT-6 Astra ではコード実行を推奨します。代替手段として computer ツールも引き続き利用できます。
すでに Function Calling またはリモート MCP ツールを通じて UI 操作を公開している場合は、そのインターフェースを引き続き使えます。それぞれの統合方法でツールの実行や結果の返却がどう異なるかは、独自の UI ツールの使用を参照してください。
コード実行の利用
コード実行を組み込むには、スクリプトを受け取る関数ツールをモデルに提供します。アプリケーションは隔離されたブラウザまたはデスクトップ環境でスクリプトを実行し、スクリーンショットを含む出力を返します。モデルが前回の作業を引き継げるように、呼び出し間で環境を維持してください。
サンプルアプリの実行
CUA サンプルアプリには、JavaScript と Playwright を使う実装、Python と PyAutoGUI を使う実装、ローカルで実行できるタスク、共通のコンソールが含まれています。
- 隔離された環境で、選んだ実装のセットアップ手順に従います。
- 組み込みのシナリオを選んで実行を開始します。
- 実行された操作、スクリーンショット、最終状態を確認し、タスクが成功したか判断します。
インストール方法、デスクトップの権限、対応環境については、アプリの README を参照してください。実際のサイトやアカウントで使うように変更する前に、安全な実行を確認してください。
独自ランタイムの接続
以下の例では、自分で用意したランタイムを呼び出す API ループを示します。Python、Ruby、Go のクライアントは、PyAutoGUI を使うデスクトップランタイムに Python コードを送信します。JavaScript の例では Playwright でブラウザを操作します。各クライアントは通常の関数ツールを公開し、元の call_id とともにテキストまたは画像を返します。
execute_in_sandbox または executeInSandbox ヘルパーは、実行環境にコードを送り、そこで観測した結果を返します。ヘルパーはブラウザまたはデスクトップのセッションを維持し、実行制限とアプリケーションの権限ルールを適用する必要があります。これらは独自の環境と統合するための例で、サンプルアプリの実行手順とは別です。
import json
import uuid
from openai import OpenAI
from openai.types.responses import (
FunctionToolParam,
ResponseInputParam,
)
def run_computer_use(endpoint, prompt, model="gpt-6-astra"):
client = OpenAI()
session_id = str(uuid.uuid4())
tools: list[FunctionToolParam] = [
{
"type": "function",
"name": "exec_py",
"description": (
"Run Python in a persistent desktop. Variables persist across calls. "
"PyAutoGUI operations are synchronous. Available: pyautogui, time, "
"log(value), and display(PIL_image). Inspect the screen with "
"display(pyautogui.screenshot()) before acting. Use screenshot "
"coordinates and check the screen after a short group of actions. "
"Keep screenshots in memory and PyAutoGUI's fail-safe enabled."
),
"parameters": {
"type": "object",
"properties": {"code": {"type": "string"}},
"required": ["code"],
"additionalProperties": False,
},
"strict": True,
}
]
next_input: ResponseInputParam = [{"role": "user", "content": prompt}]
previous_response_id = None
for turn in range(20):
response = client.responses.create(
model=model,
tools=tools,
input=next_input,
previous_response_id=previous_response_id,
)
if response.status != "completed":
raise RuntimeError(f"Response stopped with status: {response.status}")
calls = [item for item in response.output if item.type == "function_call"]
if not calls and any(
item.type == "message" and item.phase != "commentary"
for item in response.output
):
print(response.output_text)
return
if turn == 19:
raise RuntimeError(
"The task reached the 20-response limit. Inspect the last result."
)
next_input = []
for call in calls:
if call.name != "exec_py":
raise ValueError(f"Unexpected tool: {call.name}")
code = json.loads(call.arguments)["code"]
output = execute_in_sandbox(code, session_id, endpoint)
next_input.append(
{
"type": "function_call_output",
"call_id": call.call_id,
"output": output,
}
)
previous_response_id = response.idimport { randomUUID } from "node:crypto";
import OpenAI from "openai";
async function runComputerUse(endpoint, prompt, model = "gpt-6-astra") {
const client = new OpenAI();
const sessionId = randomUUID();
const tools = [
{
type: "function",
name: "exec_js",
description: `Run JavaScript in a persistent browser. Available: Playwright's
browser, context, and page objects; console.log(value); and display(base64Image).
Save reusable variables on globalThis. Inspect a screenshot before acting and
check the screen after a short group of actions. Keep screenshots in memory.
Use top-level await for async operations. Return images with display() and concise
text with console.log(). The context viewport is 1440x900.`,
parameters: {
type: "object",
properties: { code: { type: "string" } },
required: ["code"],
additionalProperties: false,
},
strict: true,
},
];
let nextInput = [{ role: "user", content: prompt }];
let previousResponseId;
for (let turn = 0; turn < 20; turn++) {
const response = await client.responses.create({
model,
tools,
input: nextInput,
previous_response_id: previousResponseId,
reasoning: { effort: "low" },
});
if (response.status !== "completed") {
throw new Error(`Response stopped with status: ${response.status}`);
}
const calls = response.output.filter(
(item) => item.type === "function_call"
);
if (
calls.length === 0 &&
response.output.some(
(item) => item.type === "message" && item.phase !== "commentary"
)
) {
console.log(response.output_text);
return;
}
if (turn === 19) {
throw new Error(
"The task reached the 20-response limit. Inspect the last result."
);
}
nextInput = [];
for (const call of calls) {
if (call.name !== "exec_js")
throw new Error(`Unexpected tool: ${call.name}`);
const { code } = JSON.parse(call.arguments);
const output = await executeInSandbox(code, sessionId, endpoint);
nextInput.push({
type: "function_call_output",
call_id: call.call_id,
output,
});
}
previousResponseId = response.id;
}
}require "json"
require "openai"
require "securerandom"
def run_computer_use(endpoint, prompt)
client = OpenAI::Client.new
session_id = SecureRandom.uuid
tools = [
{
type: :function,
name: "exec_py",
description: "Run Python in a persistent desktop. Variables persist across calls. PyAutoGUI operations are synchronous. Available: pyautogui, time, log(value), and display(PIL_image). Inspect the screen with display(pyautogui.screenshot()) before acting. Use screenshot coordinates and check the screen after a short group of actions. Keep screenshots in memory and PyAutoGUI's fail-safe enabled.",
parameters: {
type: :object,
properties: { code: { type: :string } },
required: ["code"],
additionalProperties: false
},
strict: true
}
]
next_input = []
next_input << {
role: :user,
content: prompt
}
history = {}
20.times do |turn|
response = client.responses.create(
model: "gpt-6-astra", tools: tools, input: next_input, previous_response_id: history[:id]
)
raise "Response stopped with status: #{response.status}" unless response.status == OpenAI::Responses::ResponseStatus::COMPLETED
calls = response.output.grep(OpenAI::Responses::ResponseFunctionToolCall)
if calls.empty? && response.output.any? { |item| item.is_a?(OpenAI::Responses::ResponseOutputMessage) && item.phase != :commentary }
puts(response.output_text)
return response
end
raise "The task reached the 20-response limit" if turn == 19
next_input.clear
calls.each do |call|
raise "Unexpected tool: #{call.name}" unless call.name == "exec_py"
code = JSON.parse(call.arguments).fetch("code")
raise "Expected Python source text" unless code.is_a?(String)
output = execute_in_sandbox(code, session_id, endpoint)
next_input << {
type: :function_call_output,
call_id: call.call_id,
output: output
}
end
history[:id] = response.id
end
endfunc runComputerUse(ctx context.Context, client openai.Client, endpoint, prompt string, terminal *bufio.Scanner, output io.Writer) error {
session := make([]byte, 16)
if _, err := rand.Read(session); err != nil {
return err
}
sessionID := hex.EncodeToString(session)
tool := responses.ToolParamOfFunction("exec_py", map[string]any{
"type": "object", "properties": map[string]any{"code": map[string]any{"type": "string"}},
"required": []string{"code"}, "additionalProperties": false,
}, true)
tool.OfFunction.Description = openai.String("Run Python in a persistent desktop. Variables persist across calls. " +
"PyAutoGUI operations are synchronous. Available: pyautogui, time, " +
"log(value), and display(PIL_image). Inspect the screen with " +
"display(pyautogui.screenshot()) before acting. Use screenshot " +
"coordinates and check the screen after a short group of actions. " +
"Keep screenshots in memory and PyAutoGUI's fail-safe enabled.")
params := responses.ResponseNewParams{
Model: "gpt-6-astra", Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(prompt)},
}
for turn := 0; turn < 20; turn++ {
response, err := client.Responses.New(ctx, params)
if err != nil {
return err
}
if response.Status != "completed" {
return fmt.Errorf("response stopped with status: %s", response.Status)
}
var calls []responses.ResponseFunctionToolCall
finalMessage := false
for _, item := range response.Output {
if item.Type == "function_call" {
calls = append(calls, item.AsFunctionCall())
}
if item.Type == "message" && item.AsMessage().Phase != "commentary" {
finalMessage = true
}
}
if len(calls) == 0 && finalMessage {
fmt.Fprintln(output, response.OutputText())
return nil
}
if turn == 19 {
return fmt.Errorf("task reached the 20-response limit; inspect the last result")
}
nextInput := responses.ResponseInputParam{}
for _, call := range calls {
if call.Name != "exec_py" {
return fmt.Errorf("unexpected tool: %s", call.Name)
}
var arguments struct {
Code *string `json:"code"`
}
if err := json.Unmarshal([]byte(call.Arguments), &arguments); err != nil {
return err
}
if arguments.Code == nil {
return fmt.Errorf("exec_py requires code")
}
observations, err := executeInSandbox(ctx, endpoint, sessionID, *arguments.Code, terminal, output)
if err != nil {
return err
}
nextInput = append(nextInput, responses.ResponseInputItemUnionParam{
OfFunctionCallOutput: &responses.ResponseInputItemFunctionCallOutputParam{
CallID: openai.String(call.CallID),
Output: responses.ResponseInputItemFunctionCallOutputOutputUnionParam{OfResponseFunctionCallOutputItemArray: observations},
},
})
}
params.Input = responses.ResponseNewParamsInputUnion{OfInputItemList: nextInput}
params.PreviousResponseID = openai.String(response.ID)
}
return nil
}クライアントアダプターの実装全体と、テキストおよび画像の出力に必要な形式については、実行サービスへの接続を参照してください。これらの例にあるサービスインターフェースはアプリケーション側で用意するもので、OpenAI がホストするエンドポイントではありません。
状態の維持と観測結果の返却
呼び出し間でブラウザまたはデスクトップのセッションを維持します。Python または JavaScript の名前空間を維持すれば、変数も引き継げます。モデルが利用できるオブジェクトやヘルパーをツール定義に記載してください。
UI の状態が不明なときは、現在のスクリーンショットをモデルに渡します。いくつかの操作を実行したら、モデルが結果を確認できるよう、再度スクリーンショットを返します。画像はメモリに保持し、detail: "original" を使って解像度を維持してください。スクリーンショットを縮小する場合は、操作を実行する前に、モデルが指定した座標を実行環境の座標系に変換してください。スクリーンショットの取得と解像度を参照してください。
API の会話と実行環境は別々に状態を保持します。会話にはツール呼び出しとその出力を残し、アプリケーションでは対応する環境を維持してください。前のレスポンスから会話を続けても、ブラウザセッション、ログイン状態、ランタイムの変数は復元されません。
コンピューターツールの使用
生成されたコードの代わりに、構造化された操作を受け取る実装では、こちらの方法を使います。
試すには、同じサンプルアプリのセットアップ手順に従い、Native モードを選択して、組み込みのシナリオを実行します。コンピューターツールをサポートするモデルを使ってください。
API とのやり取りは、タスクの送信、返された操作の実行、スクリーンショットの返却の 3 ステップです。以下のコード例では、Show filters というコントロールと検索欄があるページを使います。ツールを組み込む際は、実際の画面に合わせてタスクを調整してください。
環境のセットアップと操作ハンドラーの実装については、統合の実装例を参照してください。
タスクの送信
computer を tools 配列で有効にし、得たい結果を記述します。
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6.1-sol",
tools=[{"type": "computer"}],
input="Check whether the Filters panel is open. If it is not open, click Show filters. Then type penguin in the search box. Use the computer tool for UI interaction.",
)
print(response.output)要求された操作の実行
computer_call には、操作を実行順に並べた actions 配列が含まれます。たとえば、次の呼び出しでは検索欄を選択し、penguin と入力します。
{
"output": [
{
"type": "computer_call",
"call_id": "call_002",
"actions": [
{ "type": "click", "button": "left", "x": 405, "y": 157 },
{ "type": "type", "text": "penguin" }
],
"status": "completed"
}
]
}操作ハンドラーは、これらのリクエストをブラウザまたは OS への入力に変換します。許可された操作を順番に実行し、更新後の画面をキャプチャしてください。モデルが要求できる操作は click、double_click、drag、move、scroll、keypress、type、wait、screenshot です。
最初の呼び出しに screenshot 操作だけが含まれる場合があります。その場合は UI を変更せずに現在の画面をキャプチャして返してください。呼び出しの status: "completed" は、モデルがその呼び出しの生成を終えたことを示します。操作そのものはアプリケーションが実行する必要があります。
キーの対応付け、ドラッグの軌跡、修飾キーの扱いは、操作ハンドラーの実装例を参照してください。
スクリーンショットの返却
computer_call_output を返す際は、その call_id を処理した呼び出しの ID と一致させます。previous_response_id を使ってモデルとの会話を続けます。
from openai import OpenAI
client = OpenAI()
def send_computer_screenshot(response, call_id, screenshot_base64):
return client.responses.create(
model="gpt-6.1-sol",
tools=[{"type": "computer"}],
previous_response_id=response.id,
input=[
{
"type": "computer_call_output",
"call_id": call_id,
"output": {
"type": "computer_screenshot",
"image_url": f"data:image/png;base64,{screenshot_base64}",
"detail": "original",
},
}
],
)このループにも、同じスクリーンショットと状態管理の指針が適用されます。previous_response_id でモデルとの会話を続ける間は、実行環境も維持してください。
モデルが computer_call 項目を返さなくなるまでループを続けます。残りの出力に回答、支援の依頼、または別のツール呼び出しが含まれていないか調べ、アプリケーション上で結果を検証します。この例では、Filters パネルが開き、検索欄に penguin と入力されていれば成功です。
必要な操作ヘルパーとスクリーンショット取得用ヘルパーを含むループの基本構成は、コンピューター操作のループの繰り返しを参照してください。
安全な実行
「コンピューターの使用」は、実際のアカウントやデータに影響を与える可能性があります。以下の制御をモデルへの指示に含めるとともに、アプリケーションと実行環境にも適用してください。
- 環境を制限します。 隔離されたブラウザまたは VM を使い、アクセスを許可するサイトと実行を許可する操作をリストで指定します。アクセス範囲はタスクに必要なものに限定してください。
- 画面の内容を信頼できない入力として扱います。 ページ、文書、ツールの結果に含まれるテキストには、権限を付与したり、ユーザーの指示を上書きしたりする効力はありません。
- 影響の大きい操作はユーザーに確認します。 購入、データ送信、破壊的な変更など、取り消しが難しい操作は、実行するかどうかをユーザーが決められるようにしてください。フォームへの機密情報の入力もデータ送信に該当します。
- 実行に上限を設け、結果を検証します。 ステップ数、時間、または費用に上限を設け、実行をキャンセルできるようにしてください。モデルの最終回答だけに頼らず、実際の結果を確認します。
具体的な承認要件、人への引き継ぎ、プロンプトの例については、確認と同意に関する指針を参照してください。
次のステップ
- 環境のセットアップ、操作ハンドラー、スクリーンショットの取得、実行サービスのアダプターについては、統合の実装例を参照してください。
- 以前の実装を更新する場合は、computer-use-preview からの移行の手順に従ってください。
- ブラウザやデスクトップでのワークフロー全体を確認するには、CUA サンプルアプリを参照してください。