InPage Agent 浏览器操作链路 (InPage Agent Browser Use Pipeline)
浏览器操作链路 (Browser Use Pipeline) 指的是在纯前端无沙箱或 iframe 隔离环境下,由大语言模型(Large Language Model, LLM)智能体(Agent)感知目标网页文档对象模型(Document Object Model, DOM)结构,并通过跨域通信协议执行交互动作(如点击、键入、滚动、提取)的端到端执行回路。
在 inpage-agent 项目中,系统采用了 DOM 骨干树 (DOM Backbone Tree) 感知路线与基于 postMessage 的双向通信架构,使 Agent 能够直接操作自身在虚拟文件系统(Virtual File System, VFS)中生成并在工作区 iframe 渲染的 HTML 页面。
1. 核心架构与协议契约
[Agent Runtime (runtime.ts)]
│
▼ tools: AGENT_TOOLS
[Tools Dispatch (tools.ts)] ── (容错层兼容直接调用 BROWSER_ACTIONS)
│
▼
[browserUseTool.ts] ── 解析校验 action 与参数
│
▼
[iframeHost.ts] ── 定位 #ws-html-iframe,管理 id 与 15s 超时队列
│ postMessage({ type: '__browser_use_action__', id, action, params })
▼
[Iframe 页面] ── srcdoc 自动注入 browserAgent.ts
├─ get_backbone: 7 步裁剪生成 DOM 骨干树(省 Token 感知)
├─ click / type: 模拟原生事件与受控组件 Value Setter 穿透
├─ scroll / execute_js / get_readable / wait_for_dom_stable
└─ window.parent.postMessage({ type: '__browser_use_result__', id, ok, text, screenshot })1.1 动作协议 (Action Schema)
系统定义了 12 项标准化动作:
get_backbone: 提取 DOM 骨干树摘要,返回带有层次缩进、选择器(sel)、尺寸边界(rect)和短文本的精简树结构,是操作前的关键感知步骤。click: 触发完整鼠标事件序列(mouseover→mousedown→mouseup→click),支持 CSS 选择器或坐标兜底(elementFromPoint)。type: 写入输入框文本,通过HTMLInputElement.prototype的原生 Setter 穿透 React / Vue 受控组件机制,逐字符派发输入事件。scroll/scroll_and_collect: 页面滚动与无限滚动去重收集。execute_js: 页面上下文内执行原生 JS 代码,支持await与return。wait_for_dom_stable: 基于MutationObserver探测页面 DOM 变更梯度,待稳定后回传变更摘要。
2. 踩坑记录与排障要点 (Pitfalls & Troubleshooting)
2.1 坑点 1:系统提示词遗漏导致 Agent 无法主动感知工具能力
- 现象 (Symptom): 用户提出「点那个按钮」或「往输入框输入 xxx」时,Agent 未能使用
browser_use,而是尝试编造回复或使用run_js执行宿主浏览器脚本。 - 根因 (Root Cause): 前端
runtime.ts的buildSystemPrompt()硬编码了read_file、write_file、run_js等工具列表,却未包含browser_use的说明。LLM 接收到的系统提示词引导与 Schema 存在语义偏差。 - 修复措施 (Resolution): 在
buildSystemPrompt()中明确加入browser_use工具描述及其 12 种动作的建议使用流程(如推荐操作前先调用get_backbone)。
2.2 坑点 2:模型幻觉将动作名称作为顶层工具调用
- 现象 (Symptom): 当用户指令为「先 get_backbone 看下页面结构」时,部分模型可能直接输出
<tool_call name="get_backbone">或{"name": "click"},导致工具分发器报Error: 未知工具 "get_backbone"。 - 根因 (Root Cause):
normalizeToolName仅校验了['read_file', 'write_file', 'edit_file', 'list_files', 'run_js', 'browser_use']白名单,未考虑模型将 action 误作为 tool name 的边界情况。 修复措施 (Resolution): 在
tools.ts中引入协议定义的BROWSER_ACTIONS列表,当检测到工具名称属于 12 个动作之一时,自动将调用重定向为browser_use并将动作名注入参数结构:if ((BROWSER_ACTIONS as readonly string[]).includes(cleanName)) { const merged = { action: cleanName, ...args }; return executeBrowserUseTool(JSON.stringify(merged)); }
2.3 坑点 3:HTML 预览视图模式与 Iframe 存在性
- 现象 (Symptom): Agent 提示
未打开 HTML 预览:请先在预览抽屉打开一个 HTML 文件。 - 根因 (Root Cause): 工作区支持「只读预览(View)」与「代码编辑(Edit)」模式。若文件当前处于 CodeMirror 编辑器状态,DOM 中不存在
#ws-html-iframe容器。 - 认知与使用规范: 用户需确保中间主工作台打开该
.html文件且处于「预览」模式,沙箱 Iframe 才会加载并注入监听脚本。
3. 端到端 Playwright 自动化验证规范 (E2E Verification)
在 app/tests/verify-user-path.mjs 中固化了全链路自动化验收流程:
- 生成 HTML: 对话框输入「写一个带按钮和输入框的 test.html」,Agent 调用
write_file写入虚拟文件系统并自动/手动在工作区打开。 - 预览抽屉渲染: 校验
#ws-html-iframe成功挂载,srcdoc中包含沙箱虚拟存储与 Browser Agent 注入脚本。 - 结构感知 (get_backbone): 发送指令「先 get_backbone 看下页面结构」,断言 Agent 返回 DOM 骨干树并成功识别按钮与输入框选择器。
- 键盘键入 (type): 发送指令「往输入框输入 Playwright测试内容」,通过 Playwright 的
frameLocator校验 iframe 内部 input 元素的真实value === "Playwright测试内容"。 - 鼠标点击 (click): 发送指令「点那个按钮」,通过 Playwright 校验 iframe 内部按钮文字切实发生变化(如变为「已成功点击」)。
4. 多文件工作区标签页体系 (Workspace Multi-Tab System)
为了让用户与智能体在多文件、多网页任务中保持清晰的工作上下文,系统在主工作台顶部设计并实现了多标签页容器(Multi-Tab Container):
- 状态模型:
openTabs: WorkspaceTab[],区分本地 VFS 文件(isUrl: false)与外部网络页面(isUrl: true)。 生命周期管理:
openFile(file, mode): 文件已存在时激活并切页;新文件则 push 进openTabs并自动高亮。openUrl(url, mode): 提取 URL 主机与路径简写作为 Tab 标题,注册网络页面 Tab。switchToTab(tabId): 支持脏检查拦截(Unsaved Changes Guard),未保存时弹窗确认保存或丢弃后再平滑切换。closeTab(tabId): 关闭目标标签页,自动激活相邻最近的 Tab;当全部标签页关闭时,重置工作台并平滑淡入主仪表盘(Dashboard)。
5. Agent 远程自动接管体系 (open_in_workspace)
5.1 痛点与解决思路
传统网页 Agent 往往依赖用户在界面中手动寻找文件、点击打开抽屉后才能执行交互,体验割裂。
通过在 Agent Runtime 中引入 open_in_workspace 工具与前端 workspaceBridge,实现智能体的主动唤起与自动化接管:
- 统一命令桥接 (Workspace Bridge):
workspaceBridge.ts提供无依赖解耦通道,允许 Agent 工具层安全调度 UI 视图。 - 本地文件自动挂载: Agent 写入
*.html后,直接调用open_in_workspace({ path: "test.html" }),自动在 Tab 栏创建标签,将视图切换为view模式并立即加载沙箱 iframe,注入 Browser Agent 监听器。 - 外部 URL 代理接管: Agent 调用
open_in_workspace({ url: "https://example.com" })时,系统自动通过 Go 轻量网关/v1/proxy?url=...代理抓取并重写<base href="...">标签,将BROWSER_AGENT_SCRIPT注入页面。绕过浏览器的X-Frame-Options: DENY与跨域postMessage限制,使智能体能够在同一工作台中无缝对真实 Web 页面进行感知与自动化操作。
5.2 避坑要点:Go 反向代理与本地回环网络陷阱
- 现象: 本地环境配置系统代理(如 7890 端口)时,Go Gateway 代理抓取
localhost或127.0.0.1页面会报错502 / Connection Refused。 - 根因: Go 语言
http.DefaultTransport在读取到环境变量HTTP_PROXY时,会将所有 HTTP 请求全部转发给代理服务,包括回环地址。 - 修复措施: 在网关代理处理器中对目标 URL 识别,若属于
127.0.0.1、localhost或私有子网,显式指定不走系统代理的专用&http.Transport{Proxy: nil}。
6. 三栏可拖拽分界与容器查询自适应排版 (Three-Column Resizable Layout & Container Query Typography)
为了使用户在桌面端更自如地分配「文件管理」、「工作台编辑/预览」与「Agent 对话」的视野,系统构建了带有硬性约束的三栏边界拖拽体系:
HTML5 PointerCapture 拖拽手柄:
- 在左栏与工作台、工作台与右栏间分别嵌入 4px 宽的分界手柄(
#resizer-left、#resizer-right),并将其鼠标交互热区通过伪元素扩大至 12px,极大提升光标抓取手感; - 拖拽激活时为
body增加resizing-active,将内部所有iframe的pointer-events临时设为none,避免光标滑入沙箱导致全局拖拽事件丢失。
- 在左栏与工作台、工作台与右栏间分别嵌入 4px 宽的分界手柄(
最小/最大范围约束与中央工作台保护:
- 左栏(文件空间): 最小 180px,最大 420px,默认 260px;
- 右栏(Agent 对话): 最小 280px,最大 580px,默认 420px;
- 中央工作台: 设定 360px 安全保底宽度,左右拖拽均以
window.innerWidth - activeOther - 360为硬性上界,确保代码编辑器与预览区永不被压垮。 - 双击重置与记忆: 支持双击拖拽手柄一键重置默认宽度,并利用
localStorage实现刷新保持。
CSS 容器查询 (
@container) DOM 排版适配:- 传统媒体查询基于整个屏幕视口,无法针对单个侧栏被拖窄时的内部组件提供精准响应。
系统为左栏、中央舞台、右栏声明独立的
container-type: inline-size:- 左栏
<= 220px: 自动隐藏文件列表的体积标签(.file-size),紧凑化条目内边距,确保文件名具有完整的可视截断空间; - 右栏
<= 340px: 自动微调对话气泡字号、时间戳和底部输入框内边距,避免密集卡片挤压; - 工作台
<= 520px: 自动将顶部保存与工具按钮收敛为纯图标模式,缩减标签页最大宽度,防止多 Tab 溢出换行。
- 左栏
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/AI apps