接入模型前,先测试 Computer Use API 的动作控制器
用 19 项离线测试检查动作校验、截图调用 ID 和表单完成条件。提供 Python 源码与尚未完成真实联调的浏览器适配器,不需要 API Key 即可运行测试。
Computer Use 集成需要分别回答两个问题:模型能否返回支持的动作,应用能否执行动作并正确判断任务完成?本文先处理后者。下载包提供 Python 控制器与 19 项离线测试,不需要 API Key、浏览器会话或模型费用。
下载控制器源码。已在 Python 3.9.6 下通过测试,并从重新解压的副本复跑。附带的 run_live.py 没有实际执行或通过供应商联调;本文没有真实 API 截图、用量或端点兼容性结论。
先运行离线测试
Python 3.9 或更高版本的标准库即可。在 api-kit 目录执行:
python3 -B -m unittest discover -s . -p 'test_*.py' -v
其中 15 项测试控制器,四项检查接收记录。模型响应和运行时均为模拟,不启动浏览器、不请求网络。模拟截图占位字节并非有效图片,不能发给真实 API。
| 文件 | 作用 |
|---|---|
controller.py | 校验动作、返回观察、根据结果停止 |
test_controller.py | 合成响应与模拟运行时测试 |
test_receiver.py | 内存中的接收记录变更测试 |
run_live.py | 供后续联调的 Playwright/HTTPS Responses 适配器,未完成实测 |
README.md | 离线命令和真实联调前提 |
模型、执行器与验收分开
模型根据观察选择动作;执行器控制浏览器;验收逻辑独立检查应用结果。本例使用 落地页 QA 练习中的表单,输入每次唯一的 @example.test 地址。
开始前读取 /submissions,运行中继续比较:旧记录前缀必须完整不变,只能多出一条与本次地址匹配的记录。旧记录、重复提交或其他地址都不算成功,绿色提示也不够。这个规则只适用于练习表单,生产任务应采用自己的结果凭据,例如已保存的草稿 ID。
固定一种工具协议
参考代码依据 OpenAI Computer Use 文档中的结构化动作路径:先提交截图,接收 computer_call 的有序动作,执行支持的操作,再用匹配的 call_id 返回 computer_call_output 截图;后续请求携带 previous_response_id。
这些 ID 把观察与对应动作关联起来。只有截图而没有正确调用 ID,不能代替工具输出。官方文档是协议依据,不代表附带适配器已兼容某个具体模型。
真实联调前必须一起核对模型、端点和工具 schema。文本请求能成功,不代表该端点支持 Computer Use。示例不默认选择供应商或模型,也不声称 Ofox 端点兼容。
执行前检查动作
支持范围刻意收窄为左键点击、最多 200 字符的输入、指定单键、受限滚动与截图。坐标必须在当前截图范围内,布尔值、非有限数字和错误结构被拒绝。每次响应最多 12 个动作,默认最多四轮模型调用,未知动作停止执行。
每次响应只接受一个 computer call。重复调用 ID、未完成的响应和待处理安全检查会停止流程,不会自动批准。这不是支持全部工具动作或完整审批系统的通用实现。
控制器在每个动作前后检查完成条件,避免表单异步写入成功后还继续点击。参考浏览器适配器在点击或按键后短暂轮询接收记录。这些控制逻辑的本地测试不等于浏览器集成通过。
跟着一个动作看完整处理过程
控制器位于模型响应与浏览器执行器之间,负责拒绝不支持的动作、关联调用和观察,并在应用结果确认完成后停止。本文面向要实现这一层的开发者,不把示例作为可直接投入生产的 Agent。
下面是符合本地控制器结构的合成教学响应。坐标属于假想的100×100视口,不是QA页面按钮的位置,不能直接用于真实任务。
{
"id": "response_demo_1",
"status": "completed",
"output": [{
"type": "computer_call",
"call_id": "call_demo_1",
"actions": [{"type": "click", "button": "left", "x": 10, "y": 20}]
}]
}
外层 status 说明响应生成完成,不代表点击执行成功或表单保存。控制器先校验整批动作,再按序交给运行时,检查接收端,并在需要继续时返回观察。后续截图的 call_id 应为 call_demo_1,而 previous_response_id 是 response_demo_1;两者用途不同,不能混用。
不接模型,直接运行一个最小示例
把以下代码保存为 controller.py 同目录的 walkthrough.py,执行 python3 -B walkthrough.py。它使用下载包里的真实控制器,但运行时是刻意简化的替身:执行一个动作就报告完成,便于观察第二个排队点击如何被跳过。
from controller import run
class DemoRuntime:
width, height = 100, 100
def __init__(self):
self.actions = []
self.done = False
def screenshot(self):
return b"offline-placeholder-not-a-real-png"
def assert_allowed(self):
pass # Fake only: a real runtime must enforce its allowed surface.
def complete(self):
return self.done
def perform(self, action):
self.actions.append(action)
self.done = True # Simulated outcome, not receiver verification.
runtime = DemoRuntime()
def transport(payload):
return {
"id": "response_demo_1",
"status": "completed",
"output": [{
"type": "computer_call",
"call_id": "call_demo_1",
"actions": [
{"type": "click", "x": 10, "y": 20},
{"type": "click", "x": 30, "y": 40}
]
}]
}
result = run(transport, runtime, "Synthetic controller walkthrough")
assert len(runtime.actions) == 1
print(result["status"], result["turns"], len(runtime.actions))
本次实际离线运行输出 verified 1 1,依次表示返回成功状态、一轮模拟响应、一个已执行动作。这里控制器信任 DemoRuntime.complete();示例故意让它很容易满足,所以不能证明表单真的保存。真实集成必须换成独立的结果检查。占位截图字节也不是有效PNG,不能传给真实API。
把模拟完成改成接收端凭据
下载包的浏览器适配器要求旧记录完整不变,且只新增一条与当前唯一地址相符的记录。下面是判定示例:
| 前后变化 | 判定 | 原因 |
|---|---|---|
| 旧记录→相同旧记录+本轮地址 | 接受 | 恰好一个匹配新增 |
| 旧记录→没有变化 | 继续检查,或停止并标记为未确认 | 尚无保存凭据 |
| 旧记录→相同旧记录+两个副本 | 拒绝 | 重复提交 |
| 旧记录被改动后新增本轮地址 | 拒绝 | 基线变化 |
| 旧记录→相同旧记录+其他地址 | 拒绝 | 输入不匹配 |
四项接收端单测覆盖正确新增、错误地址、额外记录和旧历史改变。“无变化”是适配器继续检查的规则,不是第五项单测。真实应用还需要处理时间、并发和读取失败,不能把孤立练习的规则直接当成并发生产方案。QA教程说明人工如何获取同样的前后证据。
19 项测试说明了什么
覆盖错误动作结构、越界坐标、不支持的输入、调用与截图关联、轮数上限、重复调用、提前停止和精确记录变更。回归用例检查完成后不再执行同一批剩余动作,重复或错误地址不能通过验收。
对完成且有有效 ID 的响应,审计保留用量,包括没有动作的结束响应;动作失败时记录已尝试执行的动作。模拟用量不是实际账单。代码也不保证所有启动失败都会生成完整故障文件。
这些测试只验证所列本地逻辑,不证明模型准确率、视觉理解质量、浏览器可靠性或现实任务成功率。
按第一个失败环节排查停止原因
| 错误信息 | 检查位置 | 下一步 |
|---|---|---|
Coordinates outside current viewport | 动作校验 | 对照当前真实截图尺寸和坐标 |
Unsupported action type | 支持范围 | 查看动作类型,不擅自执行未实现动作 |
Missing or repeated call id | 调用关联 | 检查响应ID、调用ID与重试历史 |
Safety check requires human review | 待处理审批 | 停止;示例未实现审批界面 |
Model stopped before receiver verification | 完成判定 | 检查接收记录,不能接受模型口头完成 |
Turn limit reached | 轮数上限 | 重试前确认是否已发生提交 |
整批动作在执行前校验,因此后面的未知动作可以让整批在第一次操作前就停止。但一旦开始执行,后续运行时失败时,前面的动作可能已经产生影响。审计会区分“尝试执行”和“返回成功”,不能撤销已发生的副作用;重启前先查接收结果。
浏览器连接问题见权限排查。没有表单的任务应另定义结果:例如竞品表教程要求生成可解析且保留来源URL的证据文件,而非检查邮箱记录。
真实运行还需要什么
离线测试只确认已覆盖的控制器逻辑。接入真实执行器前,先选定供应商协议,并定义能独立核验的完成结果。
run_live.py 使用全新的 Playwright Chromium 上下文、限制普通页面请求访问范围的来源白名单,以及显式配置的 HTTPS Responses 端点,不连接个人浏览器配置。白名单仅允许练习页面所在来源,页面来源异常或出现新标签时停止;这不是通用浏览器安全沙箱,适配器本身仍待联调。
依据 Playwright 文档在独立环境安装并记录实际版本。本次尚未验证并锁定真实联调所需的依赖版本。通过环境变量提供 COMPUTER_RESPONSES_URL、COMPUTER_MODEL、COMPUTER_API_KEY,不把密钥写入源码或压缩包。
执行须有浏览器与费用授权,不能绕过既有拒绝。每次用新地址和新输出目录,README 给出了带确认参数的调用形式。四次调用与输出 token 上限不是金额预算,还需核查供应商费率和账号端限额。超时仍可能计费,重试前先核对用量和接收状态。
用真实证据验收联调
保留模型 ID、端点主机、运行时版本、任务、脱敏响应、调用 ID、截图与接收结果,分享前检查敏感信息。另用干净环境和新合成地址重复一次,两次结果与实测用量分开记录。不能把不支持工具的失败悄悄换成纯文本输出后称为成功。
目前可以确认的是 19 项离线测试通过;真实供应商与浏览器连接仍需各自的验证证据。
常见问题
- 可以直接把这个例子当成已验证的 API 集成吗?
- 不能。19 项控制器与接收记录离线测试已通过,但真实联调适配器尚未执行,供应商协议、浏览器操作和真实费用都未验证。
- 没有API Key能运行控制器测试吗?
- 可以。标准库测试使用合成响应和模拟运行时,不请求供应商,也不启动浏览器。
- verified状态具体代表什么?
- 表示传入的运行时报告完成。本文演示中的完成是模拟结果;真实集成必须通过独立应用凭据核验。
- 限制轮数就能限制费用吗?
- 不能。轮数限制约束循环次数,不是金额预算;真实调用还需核对费率、用量及账号侧费用限制。


