Macro 的 browser-verifier 子代理:用无头浏览器验证 Web 改动的完整指南
  • 后端
  • 前端
  • 协同办公
  • 即时通讯
  • 企业应用
  • CRM
  • AI Agent
  • 人工智能

【免费下载链接】macro

Macro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — @-linked together with shared AI memory.

项目地址:https://gitcode.com/GitHub_Trending/macro3/macro
点击查看免费下载

在 Macro 这样一个包含邮件、聊天、文档、任务、Agent、通话与 CRM 的统一工作空间中,任何用户可见的改动都不能只靠类型检查和单元测试来判定“修好了”。仓库通过.agents/agents/browser-verifier.md定义了一个专用子代理,要求它在真实浏览器里复现一次改动、按docs/agent-guide的规范操作、留存截图,并只回传结论。本文以该子代理定义文档为主体,结合docs/agent-guide系列指南、仓库前端的浏览器验证约定与 Playwright 配置,说明它为什么这样设计、如何被调度、执行时有哪些硬性边界,以及如何读懂它返回的结论。

子代理的定位:只验证,不改代码

.agents/agents/browser-verifier.md的第一句话就把职责钉死了:

You verify one described web change in a real browser. You do not edit source files.

这里包含三层约束,值得逐条拆开看:

  1. 一次只验证一个改动(one described web change)。验证任务由父代理在 prompt 中描述,子代理不负责猜测需求、不负责扩大范围。
  2. 必须在真实浏览器中验证(in a real browser)。它不能通过阅读代码、跑单测或看截图猜测交互是否成立,而要真正驱动页面。
  3. 不修改源码(You do not edit source files)。这与它的工具集是互相印证的,见下一节的 front matter 解析。

这个定位在仓库的整体代理体系中是有意为之的:AGENTS.md 明确列出.agents/agents/下的两个子代理auditor与browser-verifier,并说明.claude/agents只是指向该目录的软链接——真实文件只存在于.agents/,各工具目录仅做链接,避免多份副本漂移。实际检查也确认.claude/agents是指向../.agents/agents的符号链接。

front matter 逐字段解析

文档开头的元数据块(browser-verifier.md)不是装饰,每一行都直接决定子代理的行为边界:

字段值实际含义与影响
namebrowser-verifier子代理标识符,父代理按此名调度;也是其余工具目录软链接后可见的名字
description验证用户可见的 Web 改动,遵循docs/agent-guide,返回结论与截图路径;建议在改动 UI、路由、编辑器或 AI 界面之后使用,以便 DOM 快照不进入主上下文这既是“什么时候该叫它”的判定条件,也是它存在的核心理由:上下文预算隔离
toolsBash, Read, Grep, Glob只有读取类与命令执行能力,没有 Edit / Write。因此“不得修改源码”不是口头纪律,而是能力上的硬约束
modelsonnet指定该子代理使用的模型档位,与主代理解耦

description里有两个关键词最容易被忽略,却决定它的使用价值:

  • “Use after changing UI, routes, editors, or AI surfaces”:触发面覆盖界面组件、路由、富文本/文档编辑器,以及 AI 相关界面(例如 AI 对话、AI 编辑)。这与仓库前端指南的要求一致——apps/web/AGENTS.md 明确写着“任何用户可见的改动都应该在浏览器里跑一遍再宣布修好;因为类型检查、biome、vitest 全部通过,代码里依然可能存在交互 bug”。
  • “so DOM snapshots stay out of the main context”:Macro 的 DOM 快照体量很大,browser-technique.md 记载分裂视图会重复整篇文档文本、Activity 热力图会额外带来约 400 个节点。把这类噪声放进主代理上下文,会挤掉真正用于推理的空间。子代理把原始快照留在自己的执行环境里,只回传结论与截图路径。

tools与model的组合还有一层工程含义:Bash让它可以自行启动 Playwright Chromium、把快照写入临时文件再用grep过滤(这正是 browser-technique.md 推荐的“filePath:落盘 + Bash grep”做法),而缺少写文件工具则防止它顺手“修一下”。

五步执行流程详解

browser-verifier.md 给出了编号 1 到 4 的四段正文(第 5 段是截图落盘要求),合起来构成一次完整的验证会话:

第一步:先读指南,再动手

Readdocs/agent-guide/README.md, then the guide pages for the surfaces named in the prompt, anddocs/agent-guide/browser-technique.md.

这里要求按三层递进阅读,顺序不能颠倒:

  1. 总索引docs/agent-guide/README.md:它用一张表列出二十多个按“表面(surface)”划分的指南页,包括登录、导航、文档、数据库、表单、AI 对话、频道、任务、邮件、日历、搜索、文件、通话、CRM、PR 评审、活动、通知、外部标签、设置、引导、画布、Soup 实时更新、UI 约定、浏览器技术与可观测性。验证者必须先从这张表里定位到本次改动涉及的那一页。
  2. 具体表面页面:例如改动编辑器就查 documents.md,改动频道就查 channels.md,改动通知就查 notifications.md。这些页面描述了该表面的真实交互路径与边界情况,是“按指南的方式操作”的依据。
  3. 浏览器技术总纲browser-technique.md:跨表面通用的操作与诊断经验。

总索引还给出了本地栈的约定(README.md):前端是http://localhost:<fe>/app,后端代理是https://localhost:<be>(使用仓库内签入的自签名证书,需要信任infra/local/certs/ca.pem),Mailpit 是http://localhost:<mp>,端口由--instance决定,例如lgtm实例使用 27910 / 27909 / 27908。远程浏览器验证时要信任同一份 CA 并访问启动器打印的https://<hostname>:<proxy-port>/app/;在远程主机名上使用纯 HTTP 无法保留安全登录 Cookie。这些约定直接影响验证者能否进入登录态、能否看到真实的邮件与通知。

第二步:使用自己的无头 Playwright Chromium

Use your own headless Playwright Chromium. Never attach to the user's browser or a shared Chrome on port 9222.

这是整份文档中最“硬”的一条禁令。原因可以从仓库配置里读出来:opencode.json 把chrome-devtoolsMCP 配成--browser-url http://127.0.0.1:9222,也就是说9222 端口是主会话共享的浏览器实例。如果子代理挂到这个实例上,会出现几类典型故障:

  • 它与主代理抢同一个标签页,导航互相打断;
  • 主代理正在人工观察(VNC 场景)时,画面被自动化操作污染;
  • 主代理的登录态、页面上下文被覆盖或污染,验证结论不可信。

因此子代理必须自己拉起无头 Chromium,获得独立的浏览器进程、独立的 profile 与独立页面上下文。仓库中大量“无后端浏览器回归”用例也是同一模式:每个测试目录自带playwright.config.ts并通过webServer自行起一个 Vite 服务,例如 Resize 用例的配置 使用固定testDir、testMatch: '*.browser.e2e.ts'、workers: 1、screenshot: 'only-on-failure',并在指定端口拉起bunx vite。这种“自起、自测、自收尾”的结构,正是浏览器验证子代理应当遵循的模板。

第三步:不启动服务,URL 由 prompt 提供

Use the app URL from the prompt. If none is given, ask for one in your result instead of starting servers; don't start or restart the local stack.

这条规则把“环境准备”和“环境验证”彻底分离。理由很实际:

  • 本地栈的启动涉及数据库、后端代理、证书与端口分配(见 README.md 与just run_local的约定),这不属于验证职责;
  • 重启栈会清空登录态与数据状态,破坏其他并行工作的前提;
  • 若 prompt 没给 URL,正确做法是在返回结果中索要 URL,而不是自行猜一个端口去试。

需要区分的例外是:子代理自行拉起的Vite 预览服务(用于渲染待验证页面或运行无后端回归)属于它自己的测试夹具,不属于“本地栈”。文档中的禁令针对的是just run_local这类共享本地栈。

第四步:按指南操作,并覆盖 prompt 列出的边界情况

Exercise the change the way the guide describes, including the edge cases the prompt lists.

“按指南描述的方式操作”意味着不能只点一下主路径就收工。Macro 的指南页本身就是可执行的检查清单,例如:

  • 登录:login.md 覆盖无密码登录全流程、Mailpit 取码、已知崩溃与恢复;
  • 导航:navigation.md 覆盖路由、侧边栏、命令菜单、键盘模型与分屏;
  • AI 对话:ai-chat.md 覆盖独立会话与文档内会话;
  • 实时更新:soup-live-updates.md 覆盖扁平 Soup 列表的实时刷新;
  • UI 约定:ui-conventions.md 覆盖列表加载、顶栏、对话框动作、用户卡片与信息面板等共享行为;
  • 通知与提示:notifications.md、toasts.md。

“包括 prompt 列出的边界情况”则是把父代理的业务判断注入进来:例如“空列表”“权限不足”“断线重连”“移动端窄屏”等,由父代理在 prompt 中列出,子代理逐条走一遍。

第五步(隐含):截图落盘到指定目录

Save screenshots under the scratch directory given in the prompt, or under$TMPDIR.

截图路径有优先级:prompt 指定的临时目录优先,否则用$TMPDIR。这条规则同时解决了两个问题:一是截图不进入仓库(仓库是只读的,也不应被验证产物污染),二是父代理可以按路径直接把图交给用户或后续流程。

从 browser-technique.md 继承的操作经验

browser-technique.md记录的是在 Macro 上使用 chrome-devtools MCP 的经验教训。子代理被要求必读它是合理的:这些经验与具体工具无关,迁移到 Playwright 驱动时同样成立。下面挑出对验证质量影响最大的几条。

快照优先,且不要相信过期引用

browser-technique.md 建议每次导航或面板切换后重新取快照,只在最新快照的 uid 上操作;由于应用是 SolidJS,区域重渲染时 uid 前缀会跳变(7_x→9_x),过期 uid 会直接报错——正确反应是重新取快照,而不是猜一个引用。

快照要落盘,靠 grep 读

同一节指出 Macro 的快照体量巨大(分屏重复整篇文档、热力图增加约 400 节点),因此应带filePath:保存到文件,再用 Bashgrep提取,而不是把整份快照灌进上下文;读取时可过滤掉匹配Notifications (alt的噪声行。这正是子代理的工具集里同时存在Bash与Grep的原因。

contenteditable 不能用 fill

browser-technique.md 的关键结论:

  • fill只对真正的<input>/<textarea>/<select>有效;Macro 的所有编辑器与消息输入框都是 contenteditable,必须先点击聚焦,再type_text,必要时用submitKey: "Enter";
  • 令牌型下拉框(邀请频道成员、添加参与者)需要点击下拉、输入值、观察N option(s) available的实时区域文本,再按 Enter 提交成 chip,最后才提交表单;
  • 单选式标签条(Messages/Attachments/Participants、Signal/Noise/All)点击 radio 输入可能报“element did not become interactive”,应改点相邻的 StaticText 标签;
  • 菜单按钮点一次可能只是聚焦,若下一次快照里没有出现预期菜单,就再点一次;
  • 对话框提交常用Ctrl+Enter(新建任务、后台对话发送)。

这些细节直接决定验证结论的真伪:编辑器内容没输进去,后面的“编辑成功”判断全部失效。

等待要选对信号

browser-technique.md 指出:navigate_page可能在 10 秒超时,但 SPA 其实已经加载,此时应改用等待页面出现预期文本,而不是重试导航;wait_for匹配的是页面任意文本(包括 placeholder 和重复的文档正文),因此要选不可能预先存在的字符串;AI 生成完成没有可靠文本信号,应在evaluate_script中轮询“停止生成”按钮是否消失。文档给出的轮询片段如下:

async () => { for (let i = 0; i < 60; i++) { if (![...document.querySelectorAll('button')] .find(b => /Stop generating|^Stop$/.test(b.textContent.trim()))) return true; await new Promise(r => setTimeout(r, 1000)); } return false; }

失败诊断的两把刀

browser-technique.md 给出的诊断顺序非常明确:遇到错误弹窗或白屏,先list_console_messages(error/warn),再list_network_requests(xhr/fetch),然后对可疑请求做get_network_request看头与体——把控制台错误与失败请求配对,往往一步就能定位故障。常见的几类现象也被逐一记录:

  • 应用根节点为空时,要连script请求一起看:即使/app/返回 200,某个 Vite 模块 502 也会让入口模块无法渲染;生成的 Vite 代理把上游连接限制在 16 个,避免模块加载期间打爆宿主监听器,更新后只需重启受影响的代理;
  • 满屏net::ERR_NETWORK_CHANGED表示环境网络抖动:先在 shell 里用 curl 验证栈,再重新导航;
  • 全屏“Something went terribly wrong”对话框:按Home可恢复导航级崩溃,而 LoroDoc 崩溃需要带ignoreCache: true重新加载(详见 documents.md);
  • 浏览器重启会让页面 id 失效,并在新 profile 下丢失登录态:需要list_pages重新选中并预期重新登录;
  • 每个后端请求都带traceparent头并返回x-request-id,可用于与后端链路关联(详见 observability.md)。

环境隔离与安全边界

把前几节串起来,可以看到一套清晰的隔离设计:

维度主代理 / 人工browser-verifier
浏览器实例chrome-devtoolsMCP 连接127.0.0.1:9222的共享 Chrome(见 opencode.json)自行启动的无头 Playwright Chromium,独立进程与 profile
本地栈由人工或父代理启动(just run_local等)不启动、不重启,只用 prompt 给的 URL
文件系统可写(改代码)只读(无 Edit/Write 工具),仅可落盘截图
上下文业务推理主战场DOM 快照与日志留在子代理内,只回传结论

这套设计还带来一个副作用:验证是幂等的。子代理不会改变仓库、不会重启共享栈、不会污染共享浏览器,因此可以反复调用,或在同一批改动上多次运行而不会互相踩踏。

需要注意一个前置条件:本地栈用的是自签名证书,README.md 明确要求信任infra/local/certs/ca.pem。如果子代理的浏览器实例没有信任该 CA,访问https://localhost:<be>会直接失败,症状看起来像“页面打不开”而不是“改动坏了”——这类环境问题应在返回结果中与真实回归区分开。

返回契约:四种信息,缺一不可

browser-verifier.md 规定了固定返回结构:

  1. Verdict(结论):只能是works、broken、couldn't verify三态之一。
  2. 做过什么,逐步说明,每一步都附上展示该步结果的截图路径。
  3. 看到的控制台错误或失败请求,简短引用原文。
  4. 指南要求检查、但你无法检查的内容。

这个结构有几个精妙之处:

  • 三态结论避免二值化误判。couldn't verify的存在,让“环境没起来”“拿不到 URL”“CA 未信任”这类情形不会被硬塞进broken,从而不会让父代理去修一个并不存在的 bug。
  • 强制逐步截图,把结论与可视证据绑定。截图落在 prompt 指定目录或$TMPDIR,父代理可以直接引用。
  • 强制引用原始错误,而不是转述。控制台原文与失败请求是后续排查的锚点,尤其是配合traceparent/x-request-id时。
  • 强制声明未覆盖项,让“已验证”的边界清晰可审计。

一个符合该契约的返回示例(结构示意,非仓库文件):

Verdict: broken 1. Opened https://localhost:27909/app/ and signed in with the email code from Mailpit. Screenshot: $TMPDIR/verify/01-login.png 2. Opened the target document and typed into the title field (contenteditable, click + type). Screenshot: $TMPDIR/verify/02-title.png 3. Saved, reloaded with ignoreCache, observed the title revert to the old value. Screenshot: $TMPDIR/verify/03-reload.png Console errors: "Failed to fetch" on POST /graphql Failed requests: POST /graphql -> 500, x-request-id: 0f3c... Could not check: mobile drawer layout — prompt listed it, but no phone viewport was provided.

对照上文,这个返回满足全部四项要求:三态结论、逐步截图、原始错误、未覆盖声明。

与其他质量门禁的分工

仓库在交付前有两类子代理质量门禁,职责互不重叠:

  • browser-verifier:回答“这个用户可见改动在真实浏览器里表现如何”。偏交互与视觉行为。
  • auditor:按.agents/skills/audit/SKILL.md在当前分支上跑仓库检查(默认基线origin/main),只返回通过/失败/跳过的表格与失败摘录。偏静态检查、测试与 lint。

AGENTS.md 的“Before handing off”要求交付前运行audit技能(通过auditor子代理),并把 DOM 快照留在主上下文之外;而 apps/web/AGENTS.md 则要求任何用户可见改动都必须在浏览器里跑过。两者合起来才是完整的交付标准:静态检查通过 ≠ 交互正确;浏览器看起来对 ≠ 所有检查通过。

与仓库自带 Playwright 回归的关系

如果这次改动对应的 surface 已经有“无后端浏览器回归”,验证者应当优先跑它,再叠加手工验证。仓库中这类配置分布在各功能目录内,例如:

  • 分屏拖拽与输入框重排:Resize 用例;
  • 讨论链接滚动:messages 用例;
  • 手机端多层 Sheet 背板点击:mobile 用例;
  • 输入框浮动菜单朝向:LexicalMarkdown directive 用例;
  • 日历事件详情定位:calendar-view 用例;
  • 频道草稿持久化:channel Input 用例;
  • 画布区块:block-canvas 用例。

这些用例的共同特征是“无后端”:playwright.config.ts自带webServer启动 Vite,用固定端口与reuseExistingServer: true避免重复启动,workers: 1保证串行,失败才留截图与 trace。对验证者而言,这类套件是确定性最高的证据来源;而需要真实登录态、真实数据与 AI 返回的路径,才依赖docs/agent-guide的手工式操作。

常见失败模式速查

把文档与指南中的约束集中成一张排查表,便于在拿到broken或couldn't verify时快速定位:

现象优先怀疑依据
couldn't verify,提示缺少 URLprompt 未提供 app URL,子代理按规则不自行起服务browser-verifier.md
页面打不开 / 证书错误未信任infra/local/certs/ca.pem,或端口与--instance不匹配README.md
白屏但/app/返回 200某个 Vite 模块 502,需连script请求一起看browser-technique.md
输入后内容为空对 contenteditable 误用fill,应点击后type_textbrowser-technique.md
操作报“元素不可交互”点了 radio 输入而非相邻标签browser-technique.md
AI 编辑“看起来没完成”没有可靠文本信号,需轮询“停止生成”按钮消失browser-technique.md
满屏ERR_NETWORK_CHANGED环境网络抖动,先用 curl 验证栈再重新导航browser-technique.md
全屏“Something went terribly wrong”先按Home;LoroDoc 崩溃需ignoreCache: true重载browser-technique.md
操作后要求重新登录浏览器重启导致页面 id 与会话失效browser-technique.md

落地清单:发起一次高质量验证请求

要让这个子代理发挥最大价值,父代理在发起时应当把四类信息写全,正好对应它执行所需的输入:

  1. 改动描述:改的是哪个 surface(编辑器、路由、AI 界面、通知……),期望看到什么行为。
  2. App URL:本地栈或远程主机的完整/app/地址;缺失就会得到couldn't verify。
  3. 边界情况清单:需要覆盖的空态、错误态、移动端窄屏、权限差异等。
  4. 截图落盘目录:不给则落到$TMPDIR。

再加上两条使用建议:

  • 按表面拆分成多次验证,而不是一次塞进多个不相关表面——文档定义的就是“one described web change”,拆小能让每份返回都有清晰的截图链路。
  • 在有既有 Playwright 回归的 surface 上,先跑回归再手工验证,用确定性用例兜底,把人工/Agent 操作留给无法自动化的 AI 与实时交互路径。

至此,browser-verifier的设计意图可以一句话概括:用一个只读、独立浏览器、必须读指南的子代理,把“改动在真实浏览器里到底行不行”变成一份带截图证据、可审计、且不会污染主上下文的结论。它不替代auditor的静态检查,也不替代仓库中的无后端回归,而是补齐“用户真的能点通”这最后一环。

  • 后端
  • 前端
  • 协同办公
  • 即时通讯
  • 企业应用
  • CRM
  • AI Agent
  • 人工智能

【免费下载链接】macro

Macro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — @-linked together with shared AI memory.

项目地址:https://gitcode.com/GitHub_Trending/macro3/macro
点击查看免费下载

相关推荐

上一篇:终极指南:如何用ComfyUI Video Combine节点制作专业级AI动画视频
下一篇:如何快速掌握AMD Ryzen专业调试工具:面向初学者的完整使用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考