feat(browser): vision automation loop with screenshots, coordinate actions and bounded challenge solving
- browser.screenshot returns the page as an image content block (≤1280px wide, JPEG quality configurable) for vision-capable upstreams; non-vision upstreams get a redacted text snapshot. Login pages / password fields are marked sensitive and withheld unless a browser.sensitive_screenshot grant exists and allowSensitive is passed. - browser.click_at / type_at / scroll / wait act in screenshot pixels; the server converts via viewport/DPR, requires automation control and an allowlisted host, and writes a thumbnail evidence file (DATA_DIR/tmp/evidence) referenced from agent_run_event. - browser.solve_challenge runs a bounded child agent_run (vision caps only, maxSteps ≤ 12, budget), judges success from page facts, stops immediately on 2FA / SMS / device confirmation, and otherwise moves the session to waiting with evidence and a takeover link. - PiAgentRuntime passes modelImage outputs as pi-ai image content when model.input has image; the ledger never stores image data; coordinate actions use per-invocation idempotency keys. - Tests: image codec, runtime image content, E14 e2e (login solved, 2FA stop, step limit, sensitive withheld). Real camofox 1.17.0 check on example.com recorded in the runbook. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
0765264a47
commit
c437c6bff6
34 files changed
+2194
-35
No files matched your search
+55
-1
@@ -89,7 +89,7 @@ docker build -t camofox-browser:1.17.0 --build-arg APT_MIRROR=mirrors.ustc.edu.c
|
||||
| 站点停用 / 浏览器服务停用 | 下一次访问时会话关闭(`site_disabled` / `provider_disabled`) |
|
||||
|
||||
HTTP 输入类端点(navigate / click / type / scroll / select / 开关标签页 / 带 target 的 download)只接受持有自己人工租约的调用者;
|
||||
快照、截图、会话详情随时可读。截图只给原站视图,`modelSafe=false` 的不进模型。
|
||||
快照、截图、会话详情随时可读。Agent 看截图走 `browser.screenshot`(§3.1),敏感截图默认不进模型。
|
||||
|
||||
## 3. Agent 能力
|
||||
|
||||
@@ -103,6 +103,54 @@ HTTP 输入类端点(navigate / click / type / scroll / select / 开关标签页
|
||||
execute 类需要持续授权(`automation_grant`)或审批。元素引用只来自最近一次快照:导航、点击、按键提交、控制权交接、
|
||||
服务重启之后,旧的 `eN` 报 409 `元素引用已失效…先重新读取页面`。
|
||||
|
||||
### 3.1 视觉自动化(VIS-1)
|
||||
|
||||
截图作为图片交给视觉模型 → 模型给出截图上的坐标 → 服务端换算到页面并操作 → 再截图,循环。用于登录表单、勾选框 / 滑块 /
|
||||
点选类挑战等快照引用够不着的页面;对应 REQ v0.7 §4.4、§10。代码:`apps/server/src/modules/browser/{service,capabilities}.ts`
|
||||
(截图、坐标、证据)、`apps/server/src/modules/agent/challenge.ts`(有界子流程)、`packages/adapters/src/browser/image.ts`(缩放编码)。
|
||||
|
||||
| 能力 | 类别 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `browser.screenshot` | read | 截图缩到 ≤1280 像素宽(缺省 JPEG 质量 70),以 image content 交给模型;同时登记"模型坐标系"并写证据缩略图 |
|
||||
| `browser.click_at` | execute | `{sessionId, x, y, button?}`:坐标 = 最近一张截图的像素;只支持左键;返回点击后的新截图 |
|
||||
| `browser.type_at` | execute | `{sessionId, x?, y?, text, pressEnter?}`:先点再输(不给坐标 = 输入到当前焦点);账号密码仍用 `browser.interact` 的 `credential` |
|
||||
| `browser.scroll` | execute | `{sessionId, dx, dy}` 像素,返回新截图 |
|
||||
| `browser.wait` | read | `{sessionId, ms ≤ 5000}`,返回等待后的新截图 |
|
||||
| `browser.solve_challenge` | execute | `{sessionId, goal, success, maxSteps ≤ 12, budget}`:有界视觉子流程,见下 |
|
||||
|
||||
- **上游必须支持图片输入**:按上游模型的 `input` 是否含 `image` 判定(内置 provider 的目录自带;自定义 openai-compatible 端点目前按纯文本处理)。
|
||||
不支持时截图不发送,能力改给已脱敏的文字快照并提示;`solve_challenge` 直接判定需人工。fake 上游按支持图片处理。
|
||||
- **坐标系**:服务端对每次截图读页面视口与 DPR(`evaluate`),`页面 CSS 像素 = 截图像素 × 视口宽 / 截图宽`;
|
||||
输出的 `coordinates` 记录截图尺寸、原始尺寸、视口、DPR(取不到时 `assumed = true`,按原图、DPR 1)。动作之后旧坐标系作废,
|
||||
必须用新截图的坐标。点击沿用 1.17.0 的做法:`elementFromPoint` 打标记再按选择器点,**画布(canvas)内的位置、跨域 iframe
|
||||
里的控件(如 Cloudflare Turnstile)点不到具体位置**,这类挑战实际只能转人工。
|
||||
- **动作前提**:会话在自动化控制下(人工接管 / 等待处理中一律拒绝),当前页面主机在站点域名白名单内。
|
||||
- **敏感截图**:登录页地址、页面上有密码框、地址带凭据 → `sensitive = true`,默认不交给模型(不做像素遮蔽),改给已脱敏的文字快照;
|
||||
要交给模型需要 `automation_grant.action = browser.sensitive_screenshot`(或 `*`)的持续授权,且调用时传 `allowSensitive: true`。
|
||||
- **二次验证 / 短信 / 本人设备确认**:截图时读页面文字、快照时读快照文本,命中即把会话转等待处理(原因 `two_factor`),
|
||||
之后的自动化动作全部被拒;系统提示也要求模型立即停止。视觉子流程**不能**接手 `two_factor` 等待。
|
||||
- **证据**:每次截图(含动作后的截图)写 480 像素宽的 JPEG 缩略图到 `DATA_DIR/tmp/evidence/<id>.jpg`(0600,7 天后清理);
|
||||
能力输出只给引用 `evidence/<id>.jpg`,随工具结果摘要进 `agent_run_event`。图片数据不进 JSON 文本、不进操作账本。
|
||||
- **授权**:`click_at / type_at / scroll / solve_challenge` 由 `browser.interact` 的持续授权覆盖(也可单独授权);
|
||||
父会话白名单里有 `browser.solve_challenge` 即覆盖子流程用到的截图与坐标能力。坐标动作按调用区分幂等键,崩溃恢复不重放。
|
||||
- 交给模型的截图参数可在 `browser_provider.config` 调:`screenshotMaxWidth`(≤1280)、`screenshotFormat`(`jpeg` / `png`)、
|
||||
`screenshotQuality`(20–95)。
|
||||
|
||||
`browser.solve_challenge` 流程:
|
||||
|
||||
1. 会话须在自动化控制下,或因 `captcha` / `challenge` / `challenge_unsolved` 在等待处理(收回自动化控制,原待处理事项保留);
|
||||
其它等待原因(二次验证、租约过期、Agent 请求人工)与人工接管中一律拒绝。
|
||||
2. 在调用进程里起一个子 `agent_run`(父会话 = 调用它的 Agent 运行,`spawn_reason = browser.solve_challenge`;
|
||||
上游与父会话相同;白名单只有截图 / 坐标点击 / 输入 / 滚动 / 等待 / `browser.snapshot`),一步 = 一次工具调用,
|
||||
`maxSteps` 即工具调用上限;token / 费用 / 时长按 `budget`(时长缺省 180 秒),用量计入父会话。
|
||||
3. 子流程结束后服务端按 `success`(`urlContains` / `urlChanged` / `textPresent` / `textAbsent`,全部满足)读页面核实,
|
||||
不信模型自述。结果:`solved`(关闭原待处理事项)/ `needs_human`(二次验证、模型最后一行写 `NEEDS_HUMAN: 原因`、上游不支持图片)/
|
||||
`exhausted`(步数或预算用尽)/ `failed`。
|
||||
4. 没成功:会话转等待处理(`two_factor` 或 `challenge_unsolved`),待处理事项的 `evidence.challenge` 附最近 12 张证据截图引用与子运行 ID,
|
||||
返回 `takeover.path = /console/sites/<siteId>?tab=origin`(原站视图接管入口)。不自动重试;人可以再次调用或接管处理。
|
||||
|
||||
不承诺通过 Cloudflare 等挑战(REQ §4.4):上游浏览器指纹与点击方式都可能被识别,失败按上面第 4 步转人工。
|
||||
|
||||
## 4. 下载产物
|
||||
|
||||
- 暂存:`DATA_DIR/tmp/browser-downloads/<artifactId>.bin`(目录 0700、文件 0600),超过 24 小时的在下一次捕获时清理。
|
||||
@@ -189,5 +237,11 @@ compose 网络里的 mock NexusPHP 站 `mocknexus:8080`;服务层在宿主进程
|
||||
`userId`(`umt-site-<uuid>-acct-<uuid>`,约 90 字符)上游原样使用(持久化目录按 SHA-256 取名),无字符限制问题;
|
||||
`DELETE /tabs/{id}` 的 `userId` query / body 都认;`DELETE /sessions/{userId}` 关闭该 profile 全部标签页并落盘。
|
||||
|
||||
视觉自动化(VIS-1,2026-09-29,`scripts/browser-verify/vision-real.ts`,只读使用现有 `umt-camofox-1`):打开 `https://example.com/`,
|
||||
截图 `image/png` 1920×992(本次指纹的视口 1920×992、DPR 1;此前两次观测到 1600×819、1433×1145,视口随指纹变化),
|
||||
缩成 1280×661 JPEG 交给模型;在缩放图上取"Learn more"链接中心 (640, 343),按服务端换算得页面 (960, 515),
|
||||
与 `getBoundingClientRect` 的中心 (960.0, 514.8) 一致;点击后跳到 `https://www.iana.org/help/example-domains`,通过。
|
||||
另:1.17.0 的 `POST /click` 在 OpenAPI 里写了 `coordinates: {x, y}`,实测仍报 `ref or selector required`,坐标点击继续走 `elementFromPoint`。
|
||||
|
||||
没有核验的:四个真实站点(观众 / 馒头 / HHClub / CHD)的登录保持、Cloudflare / 验证码挑战、真实站点页面上的提取对比;
|
||||
并发 1 / 2 / 4 会话的性能与资源;原站视图界面本身在浏览器里的点击体验(1.5 秒截图刷新);VNC 插件(镜像已装,未启用、未接入)。
|
||||
Reference in new issue
Block a user