# Browser Worker 设计与实现约束 状态:**已采纳并实现**。文件名为兼容既有链接保留;当前方案不是只读 Page Analyst。 ## 核心目标 主 Agent 只负责理解用户诉求、确认高风险决策和推进总体流程。页面语义快照、元素引用、交互结果与网络响应只进入独立 Browser Worker 的 Agent/Session;父会话只记录 `browser_delegate_task` 的任务参数和经过验证的紧凑结果。 Browser Worker 包含在 `dsh-auto-chrome-tool` 插件工程中,与插件运行在同一个 DSH Node 进程,但不是父 Agent 的 fork,也不复用父历史。插件内置专用 `spawn-in-process` provider;每一代 Worker 都拥有 fresh Session、独立工具目录和独立 token 预算。 ```mermaid flowchart LR U[用户目标] --> M[主 Agent] M --> D[browser_delegate_task] D --> C[Browser Worker Coordinator] C --> W[Fresh Browser Worker] W --> T[Worker-only browser tools] T --> X[Chrome extension] X --> T W --> O[structured_output] O --> V[宿主二次校验与限长] V --> A[Opaque artifact 验证与受限交付] A --> M ``` ## 工具目录与双重隔离 主 Agent 仅可见: - `browser_status`; - `browser_extension_status`; - `browser_extension_install`; - `browser_delegate_task`。 - `browser_artifact_deliver`(只重试已授权 artifact 的宿主交付,不运行浏览器)。 Browser Worker 可见 `browser_status`、19 个 tab/page/network 低层能力,以及 DSH 子代理运行时临时注册的 `structured_output`。它看不到安装工具、委派/交付工具、shell、文件工具和其他环境工具;继承的不可用工具提示也会在 prompt assembly 后过滤。 隔离有两层: 1. 每个 Agent 创建时使用 scoped `tools.restrict()` 固定模型可见目录;普通子代理也看不到委派入口。 2. 根级 monotonic `tools.guard()` 拒绝任何非 Browser Worker 身份调用低层 browser tool,并拒绝非顶层 Agent 委派。直接构造 tool call 或后注册 allow listener不能绕过。 ## 页面数据最小化 扩展不返回 HTML、脚本或样式源码。`page.observe` 返回受限可见文本、ARIA/role/name/state 和短期 opaque ref;`page.find` 在页面运行时先做语义检索,只返回排序后的命中元素。宿主进一步强制上限,模型不能通过参数放大: - observe:默认最多 200 elements、24,000 chars; - extract:默认最多 24,000 chars; - network list:默认最多 100 entries; - network body:完整脱敏正文只写 artifact,Worker 只收到默认最多 16,000 chars 的 preview; - 密码、OTP、CVV、token 等敏感字段只暴露 `sensitive` / `filled`,禁止读取和输入。 一个低层工具结果只在紧接着的一次 Worker 推理中保持可见。再下一步开始前,插件通过 DSH surface replacement 将旧结果替换为固定过期回执。页面数据不进入 checkpoint,也不转发给父 Agent。 ## 独立记忆与换代 Browser Worker 不使用 DSH 面向通用对话的自然语言压缩作为自己的工作记忆。插件维护确定性的 `BrowserTaskState`: - 原始 goal 与显式 constraints; - 初始 tab/session 目标; - 当前页的 tabId、URL、title; - 有界、内容无关的动作账本; - generation 与上一次 rollover 原因。 - 当前步骤、全部 Worker 工具调用、无进展动作和重复错误的安全计数; - deliverable 的 purpose/kind/required,不保存宿主目标路径。 状态绝不保存页面正文、DOM、元素数组、网络 body、输入文本或可跨代误用的 ref;输入动作只记录字符数。fresh generation 必须重新观察页面。 默认压力策略: - soft limit:96K tokens; - hard limit:128K tokens; - 到达任一阈值时结束当前 child,销毁并等待 quiescence; - Coordinator 用同一确定性 checkpoint 启动 fresh generation; - 默认最多 4 次 rollover,耗尽后 fail loud,绝不把原始页面数据回退给父 Agent。 默认确定性执行护栏: - 最多 32 个模型步骤、40 次浏览器工具调用; - 连续 8 个动作没有业务进展,或同一安全错误签名重复 3 次时停止; - 调用任何未提供给 Worker 的工具时立即以 `invalid-tool` 停止;同一页面进展版本的重复截图按无进展计数; - 单任务墙钟上限 180 秒; - 触发护栏后由 Coordinator 生成有界 `blocked` / `failed` 结果,不再依赖模型自行结束或用户手动终止。 以上单 Worker 护栏之外,Coordinator 还维护父 Session 当前 Agent 回合级熔断。状态以父 Session ID 和最新 `turn/start` 编号为边界,跨所有 fresh Worker 累计;默认最多 8 次委派、累计 2 次 `blocked` / `failed`,`needs_user_input` 则立即开路。开路只拒绝本回合后续 `browser_delegate_task`,返回 `retryable=false` 和稳定 `blockerCode`;不会取消主 Agent、不会拦截普通 DSH 工具或其他子代理。新的用户回合自动创建新计数,插件卸载时清空内存状态。 甲方 DeepSeek V4 Flash 的 500K context 只作为故障余量,不作为日常工作窗口。96K/128K 远早于 500K 模型的一般压缩区间,因此正常路径由插件自己的 rollover 接管,避免通用摘要改变 Browser Worker 的任务状态。部署到更小 context 时必须同步下调两级阈值,并保证它们早于通用压缩触发点。 ## 父 Agent 返回契约 唯一允许跨越 Worker 边界的结果为: ```ts interface BrowserDelegateResult { status: 'completed' | 'needs_user_input' | 'blocked' | 'failed' | 'partial' executionStatus: 'completed' | 'needs_user_input' | 'blocked' | 'failed' deliveryStatus: 'not-requested' | 'completed' | 'partial' | 'failed' summary: string currentUrl?: string completedActions?: string[] needsUserDecision: boolean question?: string nextStep?: string artifacts?: ResolvedArtifact[] locators?: ResolvedLocator[] deliveries?: ArtifactDeliveryResult[] sideEffects: 'none' | 'possible' | 'confirmed' unmetRequirements?: string[] retryable: boolean blockerCode?: string } ``` Worker 只声明 `artifactId + purpose` 或 `locatorId + purpose`。宿主用进程内所有权表解析 artifact;locator selector text 则只在扩展完成真实 DOM 唯一性复验并写入可信 store 后,才解析为 `ResolvedLocator`。模型伪造的 locator ID 不会跨越边界。字段关系、字符串/数组长度和总 UTF-8 字节数由宿主归一化;HTML/DOM 与凭据被替换而不是导致整份成功结果丢失。必需 artifact 交付失败时返回 `partial`,父 Agent 使用 `browser_artifact_deliver` 重试交付,不重新运行页面副作用。 ## 安全与生命周期 - 页面文本、元素名称、URL、网络数据均是不可信数据,不能改变 persona、工具范围或输出格式。 - Worker 不能再创建子代理;委派入口只对 top-level Agent 可见。 - 父 tool abort、插件卸载和 provider 异常都必须取消 child,并等待 `run.dispose()` 达到 quiescence。 - 当前是同进程上下文隔离,不是操作系统进程隔离。若未来要求页面数据不进入宿主进程内存,应将同一协议边界迁移到独立 worker/process。 ## 已落实测试 1. 配置关系、状态脱敏、输出 schema、单步结果淘汰、软/硬阈值、同错/无进展/步骤/调用/时间护栏与换代耗尽单测。 2. 真实 ToolRuntime 的父/普通子代理/Worker 目录隔离与 monotonic guard。 3. 真实 AgentLoop、SubagentRuntime、spawn provider、TokenMeter 和 loopback WebSocket 组合测试。 4. 60K/100K 页面标记只进入 child session,父 session 与下一代 checkpoint 均不包含标记。 5. fresh generation、确定性 checkpoint、child quiescent disposal 和紧凑父结果断言。 6. keyless 双目录 schema snapshot、构建产物测试和宿主逐文件 100% coverage。 7. 动态 DOM 无关更新后 ref 仍可点击、目标语义变化后安全 stale,以及原子精确名称点击回归。 8. Opaque artifact 所有权与父 Session grant、路径穿越/symlink/覆盖阻断、执行/交付分态及 delivery-only retry。 9. 真实 DOM CSS/XPath 唯一性、动态属性降级、开放 Shadow Root host chain、子 frame-local 标记、旧扩展能力拦截与伪造 locator claim 丢弃。