From 44d42b094369725dc8e89e326a2ce5bda6d3e361 Mon Sep 17 00:00:00 2001 From: maogeigei Date: Thu, 8 Oct 2026 22:22:01 +0800 Subject: [PATCH] =?UTF-8?q?browser-harness=EF=BC=9A=E8=A1=A5=E4=B8=A4?= =?UTF-8?q?=E6=9D=A1=E6=B5=8F=E8=A7=88=E5=99=A8=E5=A4=8D=E7=94=A8=E7=BA=AA?= =?UTF-8?q?=E5=BE=8B=EF=BC=88=E7=94=A8=E6=88=B7=E5=BD=93=E5=9C=BA=E7=82=B9?= =?UTF-8?q?=E7=A0=B4=EF=BC=89=EF=BC=8B=20=E5=85=A5=E5=BA=93=E7=A7=AF?= =?UTF-8?q?=E5=8E=8B=E7=9A=84=E3=80=8C=E8=B0=83=20AI=20=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=8F=90=E9=97=AE=E6=96=B9=E6=B3=95=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 🔴 先探测、能复用就复用:动手第一件事查 9223 上有没有已在跑的实例, 有就直接连(netstat / curl /json/version 两种探法),⛔ 不再起第二个。 专治「为了量一次尺寸就另起一个 headless 实例」这类小活 (用户原话:「为什么要重新开浏览器,先检查之前是否开过浏览能否复用」) - 🔴 一个页面只开一个标签:同一页面全程一个标签,换路由改 hash、换视口走 Emulation, 只有并排对比两个不同页面才开第二个、用完立刻关;收工前 list_tabs() 复核, 不给自己留尾巴、也不关别人的页 (用户原话:「后续调试网页 一个页面开一个标签就行了不要反复开」) - ⛔ 别自建取数通道:要 DOM 数值就用 js(...),别为「量一个高度」另写脚本去起 Chrome 同文件另有 2026-10-07 定案的「用 browser-harness 调 AI 工具(ChatGPT 等)的提问方法」一节, 一直是未提交状态,随本次一并入库(常驻后台任务守端口、独立实例 9223、profile 复用保登录态)。 --- browser-harness/SKILL.md | 87 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/browser-harness/SKILL.md b/browser-harness/SKILL.md index 8f24884..774344c 100644 --- a/browser-harness/SKILL.md +++ b/browser-harness/SKILL.md @@ -18,9 +18,94 @@ Direct browser control via CDP. For task-specific edits, use `agent-workspace/ag - ⛔ **不附着用户日常 Chrome**。下面「Local Chrome」一节说的"默认附着正在运行的 Chrome" 在本机**不适用** —— 那样会弹「允许远程调试?」去动用户自己的浏览器(2026-09-26 实际踩到)。 ⇒ 必须起**独立实例**:`BU_CDP_URL=127.0.0.1:9223`(不要用用户 Chrome 默认的 9222)。 -- ✅ **动手前先 `list_tabs()`** 看清有哪些标签页,别盲点。 +- 🔴 **先探测、能复用就复用**(2026-10-08 用户当场点破:「为什么要重新开浏览器,先检查之前是否开过浏览能否复用」)。 + 动手第一件事是查 `9223` 上**有没有已经在跑的实例**;有 ⇒ 直接连,⛔ 不要再起一个。 + ⛔ 「量个尺寸 / 截个图 / 取个数」这类小活最容易犯这个错 —— 为了量一次就另起一个 headless 实例, + 既违了上面的禁令,又会和常驻实例抢同一个 profile。 + 探法(第一条最轻,够用): + `netstat -ano | grep -E "127\.0\.0\.1:9223 .*LISTENING"` ⇒ 有 pid = 在跑; + `curl --noproxy '*' -s http://127.0.0.1:9223/json/version` ⇒ 回 JSON 说明端口后面确实是 CDP。 + 确认在跑之后按下面第 2 条的 env 写法连上去,先 `list_tabs()` 摸清现有页, + 自己需要的页用 `new_tab()` 开 —— ⛔ 不碰别人的页,也⛔ 不为自己方便关掉现有页。 +- 🔴 **一个页面只开一个标签,⛔ 不要反复开**(2026-10-08 用户令:「后续调试网页 一个页面开一个标签就行了不要反复开」)。 + 这条接在上一颗子弹后面:**先复用浏览器**是做对了,但**别在同一个浏览器里再反复开页**。 + 典型的错法就是调试一个原型:量一次 `new_tab(原型)` 一次,量完还忘了关 —— 一次会话下来 + 共享实例里就堆出一串**同一个页面**的标签(用户 2026-10-08 亲眼看到,才出的这条令)。 + 正确做法,按便宜的排: + - **同一个页面**(同一个 URL / 同一份原型)**从头到尾就那一个标签**。 + - 换路由 ⇒ 改 hash(`js("location.hash='#/x'")`)或 `goto_url()`,⛔ 不要 `new_tab()`。 + - 换视口 ⇒ `cdp("Emulation.setDeviceMetricsOverride", width=…, height=…)` 在**同一个标签**里改, + ⛔ 不要为了量另一个宽度再开一个标签;量完 `cdp("Emulation.clearDeviceMetricsOverride")` 还原。 + - 要**并排对比两个不同页面**(比如参照物 vs 我方)⇒ 这时才开第二个,**用完立刻 `close_tab()`**。 + - 完全不需要页面时(只取一个数、截一张图)⇒ 复用**当前那个**标签,别再开。 + - 🔴 **收工前必须 `list_tabs()` 复核一遍,把自己开的一个不留**。共享实例是别人的工作现场, + ⛔ 别留尾巴;反向也一样 —— ⛔ 别顺手关掉不是自己开的页。 +- ✅ **动手前先 `list_tabs()`** 看清有哪些标签页,别盲点。(这条同时是上面那条的**收工复核**用的) +- ⛔ **别自建取数通道**:需要 DOM 数值就用 `js(...)`、需要截图就用 harness 的截图helper, + ⛔ 不要为了「量一个高度」另写一个脚本去起 Chrome。 - ✅ 静态页 / 公开 API 取数**优先 `curl` 或 WebFetch** —— 用不着浏览器就别起。 +## 用 browser-harness 调 AI 工具(ChatGPT 等)的提问方法 + +> 2026-10-07 用户定案。适用于「打开 ChatGPT / Claude 网页版求建议、核对方案、做结构评审」这类活。 + +### 1. 🔴 浏览器要由**常驻后台任务**开着 + +本机沙箱会在**父进程退出时连带杀掉子进程**。实测对照: + +- 普通(同步)调用里起 Chrome ⇒ 端口 1 秒内就绪,**调用一返回 Chrome 立刻死**(随后 `curl` 报 `WinError 10061`)。 +- 丢进**后台任务、且该任务一直不退出**(起完浏览器就守着端口)⇒ 浏览器**全程常驻**。 + +⇒ 做法:先起一个**常驻任务**把浏览器开着(守端口,掉了就写日志),之后每次操作**只连不起**。 +⛔ 别让每次调用各自起一遍浏览器 —— 父进程一退全没,而且多个实例会抢同一个 profile。 + +### 2. 起独立实例(端口 9223) + +```bash +chrome.exe --remote-debugging-port=9223 \ + --user-data-dir=E:/ProgramData/.workbuddy/tmp/bu-chatgpt-profile \ + --proxy-server="socks5://127.0.0.1:10800" about:blank +``` + +- 端口用 **9223**(用户日常 Chrome 是 9222);`--user-data-dir` 固定复用 ⇒ **登录态保留**。 +- `BU_CDP_URL` **必须带 scheme**:`http://127.0.0.1:9223`。 +- 必须清掉沙箱代理变量(`HTTP_PROXY` / `HTTPS_PROXY` 会劫持本地 CDP ⇒ `502 Bad Gateway`): + `env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy NO_PROXY=127.0.0.1,localhost BU_CDP_URL=http://127.0.0.1:9223 browser-harness` +- 可执行体写全名:`~/.local/bin/browser-harness.exe`。 + +### 3. 提问怎么写 + +- **说清「意图和目标」+ 给「完整上下文」**,末尾一句明确提问 —— 用户口径:不用写太多控制指令。 +- ⛔ **它看不到你的本机路径**。要么**把全文逐字内联**,要么**上传文件**;⛔ 不要用「见 xxx.md」「文件在 E:/…」代替内容。 +- 一轮一个主题。 + +### 4. ✅ 消息发出去的硬证据 = **输入框被清空** + +⚠️ **页尾出现你写的提问 ≠ 已发出** —— 输入框本身就在 `main` 里,它的内容会落在 `innerText` **尾部**,看起来就像已发送。发送动作之后**回读输入框长度归零**才算数。 + +### 5. ChatGPT 当前页面(实测,UI 会换代) + +| 项 | 写法 | +|---|---| +| 输入框 | `div.ProseMirror[role="textbox"]`(`#prompt-textarea` 已不存在) | +| 发送按钮 | `#composer-submit-button` → `button[data-testid="send-button"]` → `button[aria-label="发送"]` | +| 取正文 | `(document.querySelector('main')||document.body).innerText` | +| 上传文件 | `input[type=file]` + `cdp("DOM.setFileInputFiles", files=[路径], nodeId=...)` | +| 切分锚点 | `你说:` / `ChatGPT 说:` / `ChatGPT 可能会出错` | + +三个易误判点: + +1. 一页可能有**多个** `ChatGPT 说:`(历史轮次)⇒ 取正文用 **`rfind` 取最后一个**,`find` 会切到上一轮。 +2. **「ChatGPT 可能会出错」不是完稿信号**(它一直在页面上)⇒ 完稿判据 = **正文长度连续两次采样不变**。 +3. **「思考」是输入框工具栏的按钮文案**,不是「正在生成」状态。 + +### 6. 脚本怎么写 + +- ⛔ 不要在 Python 里拼 heredoc + 转义引号喂给 harness(嵌套转义 ⇒ `SyntaxError`)。 +- ✅ 每一步脚本**写成独立 `.py` 文件**,驱动脚本读文件后经 stdin 喂给 harness。 +- ✅ 全程**日志落盘**(每步 rc + 关键读数),失败能直接定位到哪一步。 +- 多标签页时:先 `cdp("Target.getTargets", flatten=True)` 看清有哪些页,必要时关掉重复页再 `new_tab(目标URL)`;各步落在不同标签页上会导致读数错乱。 + ## When Not to Use A basic fetch of public information needs no browser. If a plain HTTP request can read it — a public page, an API, docs — use `curl` or your fetch tool, and leave the browser alone. Use browser-harness when the task needs interaction (click, type, navigate), the user's logged-in session, JS rendering, or a bot-protected page. If a direct fetch fails or returns a shell page, then escalate to the browser.