# BrowserSkill 视觉观察:架构设计、边界约束与 PR 实施计划 > 日期:2026-09-08 > 状态:PR 0 设计验证稿;基线、浏览器实验和参数决策见 [验证结果](visual-observation-evidence.md)。生产实现尚未开始。执行步骤和成功条件见 [验收清单](visual-observation-acceptance.md),无需另写报告。 > 实施基线:从实施时确认的 main 新建分支;本次分析使用本地 main `3c5f838`。 > 参考材料:当前 `fix/vom-canvas-observation` 分支、PR #165、此前的 visual-surface 架构蓝图。旧实现和旧测试是证据来源,不是不可改变的规格。 > 本次范围:设计规格及可复现的合成 fixture/诊断工具;不修改生产代码,不继承旧分支提交。 ## 1. 设计结论 本次工程的目标是: **为 VOM 建立独立的视觉观察通道,共享可靠的采集与几何基础,通过明确的区域身份提供截图能力,并使处理成本随输入规模可预测增长。** 工作方式是先确定整体架构、行为契约、模块依赖、复杂度和验收矩阵,再分 PR 实现。分步实现不等于分步思考架构。任何实现阶段需要改变已定契约时,必须先更新设计及相关验收项,不得用局部特殊分支掩盖契约冲突。 本次不是一次目录整理,也不是通用视觉理解平台重写。设计完整覆盖当前能力的生命周期,但不为尚无需求和证据的未来能力预建实现。 ### 1.1 必须成立的五个结果 1. observe 能表达“这里存在需要看图理解的 Canvas 内容”,不依赖该元素是否有可用 AX 语义。 2. 视觉入口的 document、frame、target、节点及坐标归属明确。 3. 视觉 ref 与 DOM 操作 ref 具有明确且不可矛盾的工具契约。 4. 截图解析当前目标并验证时效,返回浏览器最终合成的当前图像及真实尺寸。 5. 采集、索引、发现、选择、输出和截图均有复杂度或资源上限,并从首个实现 PR 开始验证。 ### 1.2 不能用来证明成功的指标 - 代码文件拆得更多。 - 旧分支全部测试通过,但没有重新审核测试中的行为假设。 - 能识别某个特定 嵌入式 Canvas 表格 页面,却依赖站点 class、URL 或布局常量。 - 单个简单页面运行很快,但没有最坏输入分析。 - 文本出现 surface 标记,但其截图对应关系、时效和工具限制没有验证。 ## 2. 问题本质与现状 ### 2.1 main 的基础能力 当前 main 的主要路径为: ```text DOMSnapshot + 各 frame 的 AX → FrameDocument → semantic graph:构建、解析、结构整理 → VomScene → VOM 文本及 DOM ref ``` 已经存在 frame document、语义图、几何模块、ref store、截图路径和显式栈渲染器。应复用这些职责,不在旁边建立第二套长期运行的采集、frame traversal 或语义树。 缺口是:元素的 DOM/AX 描述不等于 Canvas 内部的文字、图形和布局。补 role、名称或普通可点击 ref,并不能建立完整的视觉观察能力。 ### 2.2 必须分别解决的问题 | 问题 | 要回答的具体问题 | 不属于该问题的内容 | |---|---|---| | 视觉发现 | 哪些 Canvas 构成可观察候选? | 它是否可点击、输出多少条 | | 空间定位 | 候选在顶层视口的区域是什么? | 它是否重要、属于哪个业务画布 | | 输出选择 | 有限预算下展示哪些入口? | 改写几何事实、推断业务实体 | | ref 身份 | 入口绑定什么目标、允许哪些工具使用? | 像素缩放、内容识别 | | 时效验证 | 当前目标是否仍符合此次观察契约? | 保证 Canvas 像素未发生重绘 | | 图像获取 | 如何获取当前合成区域并限制成本? | 重建 Canvas 内部对象 | ### 2.3 当前分支值得保留与需要重审的部分 值得保留:语义/视觉双通道、视觉附录、单节点截图锚点、frame 几何共享、像素预算、相关失败场景和 fixture。 需要重审: - 将 `aria-hidden`、`inert` 与视觉隐藏合并排除。语义暴露、交互资格、视觉存在是不同维度。 - 将旧区域与实时区域的交集当作时效验证。区域重合不能证明目标仍对应原观察。 - 将几何重合和共同父节点解释成逻辑画布身份。 - 将名称有无作为重要性的绝对优先条件,可能让大量有标签的小图排在无标签主画布之前。 - 视觉附录依赖 DOM 渲染后的剩余预算,可能使视觉通道完全消失。 - 在每个候选上重复祖先遍历,或假设引入空间索引便自动消除了最坏二次复杂度。 PR #165 优化了既有渲染及上下文查询路径。本次将它作为性能回归保护基线,不作为 Canvas 算法模板,也不据此要求重做原有算法。Canvas 的发现、裁剪、选择和截图具有不同的数据规模与访问模式,应分别设计和验证适合自己的算法。 PR 0 已在用户指定页面确认:表格实际绘制后,main 输出外围工具栏而没有表格列内容及视觉入口,且未截断。直接打开嵌入应用确认存在无标签 Canvas。内部页面资料仅留临时目录;入库使用匿名化结论和独立合成 fixture。 ## 3. 范围收敛 ### 3.1 本次纳入 - Canvas 元素锚定的视觉观察入口。 - 顶层、同 target 子 frame、OOPIF 及嵌套 frame 的身份和坐标归属。 - 已明确支持的视口及 overflow 裁剪。 - 无标签、存在 AX、不可交互但有视觉内容等候选情况。 - 有界输出选择及保守的重复截图入口去重。 - 视觉 ref 的工具契约和 document/几何时效检查。 - 截图区域解析、像素预算、真实输出尺寸验证和有界重试。 - 深层 DOM、密集小图、重叠 Canvas 和多 frame 的性能约束。 ### 3.2 本次不纳入 - OCR、图像语义识别、Canvas 内部对象树。 - 画布内部点选、拖拽或内部对象 ref。 - 地图 tile 拼接、跨节点逻辑画布聚合、多 anchor 区域。 - 面向所有 SVG、视频、CSS 背景等介质的通用发现插件系统。 - 任意页面遮挡、透明度、clip-path 和 mask 的精确像素级重建。 - 改写所有现有语义算法、表单、hover probe 或 recording 协议。 - 新的 ref 字符串协议、跨 observe 持久对象 ID、分页接口。 边界外的能力可以提出独立设计,不能通过放宽当前去重阈值或给单节点 target 塞可选字段实现。 ## 4. 总体架构 ```mermaid flowchart TD Browser[浏览器 / CDP] --> Capture[CaptureCoordinator] Capture --> Batch[RawCaptureBatch] Batch --> Geometry[GeometryContext] Batch --> Normalize[Normalize + DocumentIndex] Geometry --> Normalize Normalize --> Facts[ObservationFacts] Facts --> Semantic[现有 SemanticPipeline] Facts --> Discover[VisualDiscovery] Discover --> Candidates[VisualCandidates] Semantic --> SemanticScene[SemanticScene] Candidates --> Select[VisualSelection] SemanticScene --> Context[输出上下文 / 活跃层策略] Context --> Select SemanticScene --> Renderer[ObservationRenderer + BudgetPlan] Select --> Renderer Renderer --> Output[文本 + Typed Ref Descriptors] Output --> Registry[RefRegistry] Registry --> Resolve[当前目标及区域解析] Browser --> Resolve Resolve --> Pixel[PixelPlanner] Pixel --> Shot[CaptureExecutor] Browser --> Shot ``` ### 4.1 架构原则 1. 共享事实,分开解释。视觉存在不复用语义节点的保留/排除结论。 2. 语义关系是视觉区域的可选上下文,不是视觉身份的根。 3. 几何只有一套解释和投影实现,观察与工具执行使用不同生命周期的上下文。 4. renderer 只处理数据;浏览器调用止于采集或工具执行边界。 5. 预算选择只影响输出,不回写采集事实。 6. ref 的 kind 决定工具适用性,不能由调用者传入另一份可矛盾能力数组。 7. 模块失败必须可归因;不能将“没有候选”与“采集失败”混为一谈。 8. 所有资源限制都具有可解释的退化行为,不只是一句“超过上限停止”。 ## 5. 模块划分与实现位置 以下为目标职责和建议位置,不要求第一个 PR 搬动全部文件。迁移文件与修改算法尽量分开;模块完成切换后删除被替代路径。 ```text apps/extension/src/tools/ observation.ts # 工具入口与结果接线,逐步减去内部实现 observation/ capture-coordinator.ts # CDP 编排及批次生命周期 capture-types.ts # 原始批次、归属、阶段状态 normalize.ts # 原始节点 → 归一化事实 document-index.ts # 按 document 建立共享索引 facts.ts # 下游可读事实契约 geometry/ coordinate-types.ts frame-context.ts # frame/target 投影权威入口 primitives.ts # 复用/迁入现有数学实现 vom/ capture.ts # 保留或拆出的 DOMSnapshot adapter frame-document.ts # 复用现有 frame 归属逻辑 semantic-graph/ # 保留语义职责 visual/ types.ts discovery.ts selection.ts coverage-dedup.ts refs/ target.ts resolve.ts screenshot/ resolve-region.ts pixel-plan.ts capture.ts apps/extension/src/session-manager/ ref-store.ts # session 生命周期与注册存储 packages/vom/src/ types.ts # 纯场景及渲染契约 render.ts # 保留显式栈语义遍历 render-budget.ts # 唯一输出预算核算 visual-appendix.ts # 纯数据视觉条目输出 ``` 不同时保留旧 `geometry.ts` 与新 primitives 的两套数学实现。上述目录可按现有工程规范调整,但职责与依赖不得随意调整。 ### 5.1 CaptureCoordinator 输入:tab/target、已验证的 URL、取消信号、采集选项。 输出:带来源、时间范围、document 身份和阶段状态的 RawCaptureBatch。 - 按 target 分组采集;DOMSnapshot 每 target 一次成功采集,重试单独计数且有界。 - AX 按协议支持的 frame/target 组织,数量有预算。 - 原始采集完成后才能运行会改变页面状态的 hover probe,保留已有行为顺序。 - 明确记录无法采集的 frame,不能把失败 frame 的数据借用其他 target 补齐。 - 不判断 Canvas 重要性,不投放 ref。 批次是来源和生命周期边界,不是浏览器原子事务。`Readonly` 或冻结对象不能证明各 CDP 响应来自同一瞬间。遇到可检测的导航/document 更换时应中止或有界重采集,不能拼接不同 document 的数据。 ### 5.2 GeometryContext 输入:该次操作的 frame graph、原始 metrics、按需取得的 owner quad。 输出:frame-local/target-local 到 top-viewport 的投影、裁剪及当前节点区域解析。 - 坐标类型同时携带空间及所属 frame/target;仅标记 `frame-local` 不足以防止错 frame 使用。 - 原始 DOMSnapshot 单位、CSS 视口单位、截图像素单位分别适配。 - Canvas 视觉入口采用元素 border-box 的可截图区域;实时解析明确请求同一种 box,不能沿用默认 content-quad 路径再将边框差异误判为移动。iframe 子视口投影仍采用 owner content quad,两者职责不同。 - 普通 overflow 裁剪使用 ancestor client/scrollport box,不使用祖先 border bounds。adapter 保留 includeDOMRects 的 client offsets/size 及其单位;存在缩放时先转换到相同坐标空间,禁止直接相加不同单位。证据不足时明确降级,不猜测 border/padding 常量。 - 一个操作内复用 metrics、owner quad 和 projection promise;不跨 observe 或 tool execution 复用 live geometry。 - 合成变换可以预计算;裁剪边界复杂度须真实计入,不能靠 promise cache 就宣称全部计算线性。 - 不允许通过 iframe border box 加减常量替代有证据支持的 content geometry。 ### 5.3 Normalize / ObservationFacts / DocumentIndex 输入:原始批次及几何上下文。 输出:按 document 隔离的只读事实和有界共享索引。 - Raw node 不能含语义不明的 top rect;normalized node 的派生字段由 normalization 创建。 - 节点 key 包含 document/target 所属信息,不能仅用 backendNodeId 跨 target 建表。 - 建立 node lookup、parent/children 等基础索引;派生索引必须有明确消费者和复杂度。 - 用显式栈和 visiting/visited 状态处理任意深度及异常环。 - orphan 保留“祖先信息不完整”状态;可以在已知范围内生成保守候选,但不能伪装成完整裁剪证据。 - 视觉样式、AX 暴露、交互资格分开保存,禁止万能 `excluded` 字段承载全部规则。 - 不给每个节点复制完整祖先数组或完整裁剪链;必要时共享结构或按需解析,计入内存预算。 ### 5.4 SemanticPipeline 消费归一化事实,输出现有语义场景。通过窄 adapter 接入新事实契约;迁移完成后删除不再需要的 adapter。 - 不因为视觉发现需求改变 semantic keep/drop、名称恢复或可交互推断规则。 - 不把视觉候选塞回语义树遍历。 - PR #165 的显式栈、上下文索引及行为回归保留。 - 偶然发现的既有语义性能问题只作范围外记录,不构成本次工作项或前置依赖。本次只要求不使原有路径发生性能回退。 ### 5.5 VisualDiscovery 输入:ObservationFacts。 输出:候选及发现状态,不含最终 ref,不依赖 token 预算。 候选表达一个 Canvas 节点锚定的截图区域。必须具有可解析地址、支持范围内的几何证据和非空候选区域。 - 无 label 不排除;有 AX 内容也不能证明像素内容已被完整描述。 - `aria-hidden`、`inert`、`pointer-events:none` 不单独构成视觉排除原因。 - 零尺寸、无渲染区域、已证实的完全透明/隐藏、支持范围内的完全裁剪可排除。 - `visibility` 等继承与覆盖行为必须按实际 CSS 语义处理,不能把祖先 hidden 一律不可逆传播。 - `hidden` 等属性不应替代最终渲染事实;存在特殊状态时按实验确认的规则处理。 - agent 自身 overlay 排除应按所属 document/target 传播。 - 常规轴向 overflow 累积在一致的坐标空间计算;变换后不能把 top-space 包围矩形当作精确 local-axis clip。 - 不重建任意遮挡。几何区域是截图候选范围,不保证范围内每个像素均来自该 Canvas。 ### 5.6 VisualSelection / CoverageDedup 输入:候选、输出上下文及预算规划所需参数。 输出:选中候选、明确的遗漏/退化状态。 - 默认以区域覆盖量等可解释几何依据排序;label 是命名事实,不作为压倒面积的优先条件。 - 活跃层优先级属于选择策略;被降低优先级不等于物理不可见。 - 排序键预计算;同层按可见面积降序,再按 frameId、top y、left x、backendNodeId 升序决定顺序。字符串按代码单元字典序、数值按数值比较;同一精确去重组取该顺序最小者。不把跨批次 backend id 稳定性作为保证。 - 去重只减少重复截图入口,不产生新的 logical surface。 - 首版仅在同 document、同直接 DOM parent、四条裁剪边数值完全相同的候选之间去重,使用精确 key 的 Map;不使用近似 IoU、尺寸 band 或空间邻居比较。亚像素不同的范围保留独立入口。 - 首版不输出无法证明的 `layers=N` 业务解释。去重成员只是覆盖证据,不是内部对象模型。 - 精确去重索引达到 50,000 个 key 后,剩余候选以未去重形式继续进入有界选择,记录去重退化;不得直接丢掉剩余区域。索引已知 key 仍可命中去重。 - 即使没有去重,top-K 选择和总输出成本也必须有界。 ### 5.7 ObservationRenderer / BudgetPlan 输入:SemanticScene、视觉选择结果和输出预算。 输出:文本及与已提交行严格对应的 typed ref descriptors。 - 唯一模块核算总文本预算;摘要、头部、条目和截断提示全部计入。新 observe 的统一账本为每个实际输出行分别 `ceil(line.length / 4)` 后求和,length 使用 JavaScript UTF-16 单位;不混用整段核算与逐行核算。 - 保留语义显式栈和 DOM ref 相对顺序;视觉条目在语义阶段之后提交。 - 有视觉候选时,规划阶段预留简短视觉提示;其余视觉条目使用明确分配的剩余预算。 - 极小预算不保证语义与视觉同时完整,必须产生确定的截断结果。 - 写入行与提交 ref 在逻辑上原子:未写入的条目不能留下可解析 ref。 - 使用既有 token 估算方法时,预算表示“估算 token 上限”,不能宣称精确模型 tokenizer 上限。 - 新 observe 预算规划若发现显式预算不足以容纳最小协议头,返回 insufficient-budget,由工具层映射为既有 invalid_params,附最低估算成本;不生成半截协议头或新 ref。snapshot 的旧预算语义不在本次顺带改变。这是 observe 极小预算的明确兼容性例外。逐行统一账本也可能改变临界预算的截断位置;足够预算下无 Canvas 的语义文本及顺序保持不变。 - 首版固定视觉提示为 `[visual] Canvas regions present; use screenshot`,成本由同一估算器计算。有候选但条目不足时仍尝试保留提示;连提示也容不下则只返回合法的截断语义输出,truncated=true。未知计数不伪装成精确遗漏数。 - 输出标签最多 160 个 Unicode code point,再经 JSON escaping 与整行成本核算;排序不依赖标签长度。原始采集输入量 B 仍单独计入,不因输出裁剪便宣称采集内存有界。 - `snapshot` 继续使用既定静态语义输出;新视觉入口首先仅接入 `observe`。不要顺带改变 recording/其他调用方默认选项。 ### 5.8 RefRegistry / Resolve 保存 session/tab 隔离的目标描述。DOM 与视觉目标通过判别联合区分;kind 为权威,外部展示能力可从 kind 派生。 ```ts // 概念契约,具体类型名与仓库适配时确定。 type RefTarget = | { kind: "dom"; address: NodeAddress } | { kind: "visual-region"; anchor: NodeAddress; observed: ObservedGeometry; observationId: ObservationId; }; // NodeAddress 必须能表示 tab、CDP target、frame、document 身份及 backend node。 // 坐标值同时保留所属空间及 owner,不使用无归属 Rect 穿越模块边界。 ``` 工具适用性: | 消费入口 | DOM ref | Visual ref | |---|---|---| | screenshot | 维持既有 DOM 截图策略 | 使用视觉区域策略 | | click/fill/hover/select 等 | 维持既有检查 | 拒绝,且拒绝发生在派发输入前 | | get_html | 维持既有行为 | 拒绝;不隐式降级为 anchor 的 HTML | | 人工帮助定位 | 维持既有行为 | 若当前入口必须解释为 DOM 目标则拒绝;未来支持视觉区域需另立契约 | 所有 ref 消费者都必须通过类型化 resolver,不能依赖调用者记得传可选 capability 参数。 现有 `eN` 重用存在明确限制:如果新的 observe 已将 `e1` 重新绑定,单凭客户端传来的旧字符串 `e1` 无法判断它来自上一代。内部 generation 不会自动解决该问题。观察身份采用 target attach epoch + frameId + loaderId + documentElement backend identity,并在执行时验证 anchor 仍连接到匹配的 documentElement;loaderId 单独不足以覆盖 document.open。 本次保留当前“最新观察映射”的外部语义;只承诺能检测当前已存条目的 document/节点/几何失效,不承诺识别所有跨代旧字符串。彻底解决需独立协议设计。 ### 5.9 ScreenshotExecution 分成三部分: 1. `resolve-region`:按目标种类解析当前身份与几何、验证 document 和区域变化。 2. `pixel-plan`:纯数值函数,输出 clip scale 等计划。 3. `capture`:overlay suppression、CDP 截图、真实尺寸读取、有界重试、取消及 dialog 集成。 - Visual ref 不自动滚动,不通过旧、新区域交集掩盖位移。 - 首版对超过既定容差的位移、尺寸或裁剪变化返回“需要重新观察”;容差在 PR 0 由测量误差证据锁定。 - 截图返回执行时浏览器最终合成图,不是观察时像素快照。 - document 身份验证不能替代几何验证;几何相同也不能证明 Canvas 内容未重绘。 - 像素规划采用明确 metrics 适配值;真实 PNG 尺寸是输出预算后置条件的依据。 - 尺寸头校验不宣称完整 PNG 内容验证;缺失/不合法数据与预算超限分开归因。 - 成功返回必须满足像素上限;有界重试仍失败则返回失败,不能猜测尺寸或无界重拍。 ## 6. 算法与资源预算 ### 6.0 两类独立的性能责任 **既有路径:防止回退。** 在相同输入、选项和环境下对照 main,检查原有语义处理、DOM ref、普通交互与截图的工作量、内存和实际耗时。不得因接入 Canvas 而重复运行语义解析、重建已有索引、增加原有节点的上下文查询,或撤销 #165 的优化。本次不要求把既有算法统一改成某个复杂度。 **新增路径:独立设计。** 围绕 Canvas 数量、frame 数量、裁剪复杂度、输出预算及截图像素量分析新成本;不照搬旧 renderer 的算法,也不以“旧算法使用了索引/显式栈”代替新问题的算法论证。下文复杂度与资源要求约束新增或本次实际改动的职责。 性能验收分三组,分别报告,不能互相替代: | 验收组 | 对照方式 | 必须说明的结果 | |---|---|---| | 既有语义/操作路径 | 同一输入在 main 与新分支运行,隔离新增视觉工作 | 既有阶段未引入额外遍历、重复请求或可归因的性能回退 | | 无 Canvas 页面 | 实际完整 observe 对照 | 仅保留必要的候选检测开销;确认无候选后不建视觉索引、不执行视觉几何查询或截图 | | 有 Canvas 页面 | 单独测量视觉各阶段,再测完整 observe | 新成本与 C/F/P/K/像素等规模相符,资源有界;不把合理的新功能成本误记为旧算法退化 | 实际耗时需要多次同环境对照并结合操作数判断,不能仅凭一个容易波动的耗时值下结论。新增字段采集或共享模块调整的额外开销也必须归因并报告,不能藏在“原有成本”中。 ### 6.1 规模定义 | 符号 | 含义 | |---|---| | B | 原始协议数据及字符串总量 | | N | 采集 DOM 节点数 | | A | AX 节点数 | | F / T | frame 数 / CDP target 数 | | C | 视觉候选数 | | K | 最大选中输出条目数 | | Q | 去重实际候选比较次数 | | P | 几何计算量,如顶点处理、裁剪操作总数 | | H | 语义上下文查询实际处理的数据总量 | | L | 最终输出文本长度 | 不能忽略 B、P、H 后笼统宣称整个管线 `O(N)`。原始数据解码至少与输入量相关,复杂裁剪和语义上下文的总成本需要显式计算。 ### 6.2 模块性能契约 | 阶段 | 复杂度/上限目标 | 禁止的实现 | |---|---|---| | 原始解码 | 与 B 线性相关 | 为每个节点重新解码共享列或字符串 | | 文档索引 | `O(N + A + F)`,不含协议字节解码 | 每个候选全文扫描 | | 可合成祖先状态 | `O(N)` 建立,候选常数次查询 | 每 Canvas 完整祖先 walk | | Frame 几何 | 变换/证据按 frame 复用,额外成本计入 P | 每 node 查询 metrics/quad | | 发现 | 索引后 `O(C)`,额外几何成本计入 P | 发现函数内重新查 CDP | | top-K | `O(C log(K+1) + K log(K+1))` | 无需要地重复全量排序 | | 去重 | 精确 key Map,摊销 O(C),索引至多 50,000 个 key;首版 Q=0 | 引入模糊匹配和同 bucket 两两比较 | | 新增视觉渲染与既有渲染接线 | 新增工作按选中条目及 L 核算;既有 H 对照 main 不回退 | 为视觉条目重新执行语义遍历或扫描全部节点 | | 截图 | 调用和重试次数有界,成功图像像素数有界 | 无限重试、猜测输出尺寸 | K 从预算允许的最小条目成本推导,并受异常安全上限约束;实际每行仍核算成本。面积优先 top-K 不应被描述为严格求解“可变长度条目预算最优装包”,本次不实现此类优化问题。 ### 6.3 首版资源参数及锁定规则 以下是 PR 0 锁定的首版策略参数。它们是有明确退化语义的工程预算,不是性能最优值的统计结论;调整须更新对应契约测试: - Visual 截图最大边:2048 px;最大面积:4,000,000 pixels。 - 单次视觉截图最多 2 次 capture 尝试。 - Frame 几何外部请求最大并发:4。 - K 异常安全上限 512;正常数量由预算与条目成本决定。 - 几何变化阈值:top-viewport CSS px 的四条边最大差值超过 0.25 即要求重新 observe;document、anchor、裁剪策略改变不受该容差豁免。 - 没有显式 maxTokens 时,视觉通道估算 token 预算 2048;有显式预算时,优先为固定摘要行按当前估算器计算实际预留量,条目用剩余预算,全部计入同一总预算。 - 精确覆盖去重索引最多 50,000 个 key;耗尽后继续未去重 top-K,标记 dedup-degraded;不得停止扫描剩余候选。 - 新增纯计算阶段每 256 个节点/候选形成可取消工作块;需要实际让出事件循环,而不是只读一个尚无机会更新的 AbortSignal。 每个限额均须有单位、适用阶段、计数口径和耗尽行为。数字集中在所属模块策略配置中,不散落为多个含义相近的常量。若 PR 0 未锁定参数及退化契约,对应实现 PR 不得以随意常量合入。 ### 6.4 性能测试方法 - 节点访问、祖先推导、候选比较、CDP 请求数、最大并发及中间集合大小均做可断言计数。 - 对新增树处理使用深树/宽树测试,证明无调用栈溢出或重复全量扫描;既有 renderer 的对应输入用于 main 前后回归对照,不要求本次消除其历史复杂度。 - 规模翻倍时检查操作数增长是否符合分析,避免只看一次墙钟时间。 - 精确去重索引至少测试:全部重合、亚像素差异、同 crop 不同 parent、全部互不相交、容量耗尽;未来若引入空间索引,必须另增退化测试。 - 大量有名小图与无名主画布并存,验证排序不会因标签策略丢失主要区域。 - 保留 #165 回归;为新增模块增加性能契约,而不是声称旧测试覆盖整个链路。 - 真实耗时基准用于观察额外成本和实际收益,需记录环境;不使用耗时阈值代替算法验证。按验收清单的性能步骤 预热 3 次、交替采样 20 次,分别报告旧阶段和新增成本;旧阶段中位数增加超过 max(基线 10%, 2ms) 时独立复测,两轮均超过且可归因于改动则 FAIL,无法排除噪声则 BLOCKED。 ## 7. 失败、降级与一致性 内部状态至少区分: ```text capture-unavailable document-changed frame-ownership-unresolved geometry-unavailable geometry-unsupported outside-supported-clip discovery-partial selection-truncated dedup-degraded target-detached geometry-changed image-invalid pixel-budget-exceeded ``` 这些是内部分类,不要求逐项扩展外部 RPC code。外部沿用既有错误分类并给出准确 message/必要 detail;不可把所有时效或几何失败叫权限问题。 原则: - 视觉分支失败不应使已获得的语义结果无故丢失。 - 没有候选、没有预算、没有采集成功必须可区分。 - exact omitted count 只在可计算时输出;原始候选数与去重后的区域数不能相加冒充精确数量。 - 捕获批次不保证页面静止;可检测的身份变化必须处理,不可检测的像素重绘不作虚假保证。 - 取消在阶段边界和有界工作块之间检查,不能在大型纯计算循环结束后才响应。 ## 8. 兼容性与影响面约束 1. 从 main 新建实现分支;不将当前分支整体 cherry-pick 作为迁移方式。 2. 没有视觉候选的页面,语义文本、DOM ref 相对顺序和操作行为维持原有契约。 例外为上述 observe 显式预算小于最小协议头的 invalid_params 行为及统一账本导致的临界预算截断位置变化;足够预算不改变旧语义。 3. 有视觉候选的 observe 允许新增视觉文本,并按新预算规划改变截断位置;这是明确行为变化。 4. `snapshot`、recording、hover probe 默认行为不随视觉通道接入而隐式改变。 5. 修正已证明错误的坐标允许改变输出位置,不以错误 golden 为兼容目标。 6. 几何共享改动必须覆盖已有 DOM 点击/截图路径回归;不以 Canvas 测试代替。 7. CLI/协议外形优先保持;确需新增外部字段、错误子码或参数时,必须在对应 PR 明确列出全部消费者。 8. 不进行无关格式化、目录大搬迁、依赖升级或语义启发式修改。 9. 不长期保留两套 capture decoder、metrics adapter、frame resolver、token accounting 或 pixel planner。 10. 不把站点特征、fixture 标签、CSS class 或 URL 放入生产决策。 ## 9. 设计验证清单与关闭方式 | 待验证项 | 证据 | 必须形成的决定 | |---|---|---| | 嵌入式 Canvas 表格 原始失败 | main 的观察输出、对应截图及结构证据 | 失败归属、最小复现和成功标准 | | DOMSnapshot / metrics 单位 | DPR、zoom、scroll、frame 矩阵 | 原始单位适配和转换入口(实验结论见证据文档) | | frame / target 坐标 | 同 target、OOPIF、nested、带边框/变换样例 | content quad 来源、坐标归属及支持范围 | | document 身份 | 导航、同 frame document 替换、target 重建 | 使用哪些现有标识/事件,何时失效 | | CSS 视觉规则 | visibility 覆盖、opacity、hidden、单轴 overflow | 可合成状态和不支持情况 | | 时效容差 | 静止页面测量噪声、可控移动/缩放/裁剪变化 | 容差单位、比较规则和失败行为 | | 预算 | 调用方 max_tokens 使用方式、密集画布样例 | 提示预留、K、限额与截断规则 | | 去重 | 重合、近重合、不同父节点、相邻 tile | 首版等价规则和 Q 上限 | 验证结果与最终决定见 [PR 0 证据及决策](visual-observation-evidence.md)。首版精确几何支持收敛到已验证的轴向矩形、正向轴对齐平移/缩放、普通 frame/overflow 链。旋转、透视、clip-path、mask 和活动 pinch-zoom 不承诺精确区域 ref:返回 geometry-unsupported 状态并提示使用现有视口截图。后续扩展必须独立验证。浏览器 UI zoom 与 CSS zoom 分开验证,不能用 pinch emulation 代替 UI zoom。 进一步限定:CSS zoom/DOM transform 作用于 overflow 裁剪祖先的组合,首版也进入 geometry-unsupported。 实验表明 snapshot.bounds 已缩放,而 clientRects/offsetRects 仍有未缩放和整数取整语义,不能靠一个宽度比准确恢复所有边。 普通 Canvas 的 CSS zoom bounds 与 iframe content quad 缩放仍可使用;这不意味着缩放后的任意 DOM scrollport 都已获得精确支持。 Facts 必须保留 client box、transform/zoom 及 clip-path/mask 等支持性证据,discovery 返回状态而不是另写近似投影。 ## 10. PR 划分与依赖 PR 0 是设计与证据入口;PR 1—6 建立模块;PR 7 接入公开 observe;PR 8 完成跨模块验收和迁移收尾。编号是顺序计划,不是实际 GitHub PR 编号。 ```text PR 0 设计规格与实验 └─ PR 1 几何与身份契约 └─ PR 2 采集批次与共享事实 └─ PR 3 视觉发现 └─ PR 4 选择、去重与预算 PR 1 + PR 2 ──→ PR 5 类型化 ref PR 1 + PR 5 ──→ PR 6 视觉截图执行 PR 2 + PR 3 + PR 4 + PR 5 + PR 6 └─ PR 7 observe 组合与接线 └─ PR 8 整体验收与迁移收尾 ``` 独立模块可以使用人工输入测试,不必等待真实上游接线。实现可依赖前序已合入 main 的 PR,避免多个大型长期分支互相漂移。阶段未接线的模块必须有紧邻的消费计划,不保留无期限死代码或对用户暴露半成品。 ### PR 0:规格收敛、最小复现与基线 完成: - 审核现有分支 fixture,区分正确规格与需要推翻的预期。 - 记录 嵌入式 Canvas 表格 原始失败及 main 基线,建立独立于站点的最小 fixture。 - 完成第 9 节实验,锁定核心数据类型、支持范围、容差和资源参数。 - 固定 #165 的相关回归结果和操作数基线;不夸大旧测试覆盖范围。 - 将行为契约映射到后续每个模块的测试清单。 不做:生产算法替换、全目录迁移、正式视觉通道接入。 验收:核心身份、坐标、裁剪、预算和失效规则不存在未指定行为;未支持情况有明确策略。若仍有未知项,先调整范围而不是让后续 PR 猜测。 ### PR 1:统一几何与节点身份契约 完成: - 引入有 owner 的坐标和地址类型,区分 frame/target/top/CSS/image 空间。 - 统一 metrics 解释、frame projection 和 owner geometry 查询。 - 共享单次操作内的 promise cache;保证采集和执行使用独立上下文。 - 给既有 DOM 几何入口做窄适配;移除已被替代的投影路径。 不做:Canvas 发现、排序、ref 外部行为变化。 验收:scroll、border、同 target、OOPIF、nested、支持范围内的 transform/DPR/zoom 矩阵通过;已有 DOM 点击与截图回归通过;请求计数和几何成本可解释。 ### PR 2:采集批次、归一化事实和共享索引 完成: - 将原始解码、采集编排、normalization 明确分开。 - 按 target 去重采集,记录 document 身份、时间范围和阶段失败。 - 建立 ObservationFacts、document 隔离 lookup/children 索引和正确的祖先状态结构。 - 现有 SemanticPipeline 通过 adapter 消费新事实;保持 hover 在静态采集后发生。 - 删除不再使用的可变中间态和重复解码/归属推断路径。 不做:变更语义保留规则、视觉输出和截图政策。 验收:语义回归及 frame ownership 通过;deep/wide/orphan/cycle 有明确结果;解码、索引、采集数量符合预算;无每节点完整祖先复制。 ### PR 3:纯数据视觉候选发现 完成: - 实现 VisualCandidate 及 discoverVisualCandidates。 - 落实无标签、有 AX、aria-hidden/inert/pointer-events、透明/隐藏、裁剪规则。 - 候选关联 document/anchor/geometry,语义上下文只作为可选信息。 - 返回发现失败或不完整状态,避免与无候选混淆。 不做:ref 分配、截图、token 排序、逻辑画布合并。 验收:每条发现规则都有正反 fixture;输入同一份事实输出确定;索引后的候选发现不重复扫 DOM 或走完整祖先链;不增加 CDP 调用。 ### PR 4:有界选择、保守去重与输出预算计划 完成: - 明确几何优先排序及确定性 tie-breaker,预计算比较键。 - 实现有界 top-K,K 受文本预算和安全限额约束。 - 实现精确覆盖 key 去重及索引容量耗尽后“继续未去重选择”的退化行为;首版 Q=0,不进行模糊两两比较。 - 建立纯 BudgetPlan 和视觉摘要/条目格式,统一预算核算契约。 - 明确候选数、输出数、遗漏数及去重退化信息的口径。 不做:多 anchor target、业务画布推断、生产 observe 接线。 验收:密集小图、无名主画布、全部重合、同 bucket 不等价、极小预算均有确定结果;比较次数、堆大小、输出成本有界。 ### PR 5:类型化 ref 注册与工具解析 完成: - RefStore 保存判别联合,绑定 document 及 observation 信息。 - kind 派生工具适用性,删除可独立构造的矛盾 capability 状态。 - 审核并迁移截图、交互、get_html、人工帮助等全部 ref 消费者。 - 保持 eN 外部语义,文档明确跨代字符串识别限制。 不做:引入新版 ref 协议、自动把视觉 ref 当 DOM anchor 使用。 验收:跨 session/tab 隔离;document 失效;全部工具矩阵通过;拒绝在输入派发前发生;旧 DOM ref 行为保持。 ### PR 6:视觉截图的区域解析、像素规划与执行 完成: - 分离 resolve-region、pixel-plan、capture executor。 - 实现 visual 不滚动、身份验证、几何容差及重新观察错误。 - 实现真实输出尺寸后置条件、有界重试和准确失败归因。 - 复用 overlay suppression、取消和 dialog 机制。 不做:Canvas 内部截图、像素冻结、多节点 union、内部点交互。 验收:移动/缩放/裁剪变化、节点移除、document 替换、DPR/zoom、无效图像、超预算和取消路径通过;成功图像符合像素预算;现有 DOM 截图回归通过。 ### PR 7:观察结果组合与 observe 接线 完成: - 将 Facts 的语义/视觉两路结果交给统一输出规划和 renderer。 - 保留 #165 显式栈和 DOM ref 相对顺序,追加视觉条目。 - 仅对已写入条目提交 ref,失败/截断状态正确传递。 - 视觉提示参与预先预算,避免 DOM 耗尽预算后完全无提示。 - 仅在 observe 默认启用新视觉通道;必要的 CLI/skill 使用说明同步更新。 不做:顺带修改 snapshot、recording 或 hover 默认策略。 验收:从人工 facts 到文本/ref 的集成测试、真实单 Canvas observe → screenshot 闭环通过;无 Canvas 页面回归通过;极小预算无悬空 ref。 ### PR 8:跨模块验收、性能证据与收尾 完成: - 在真实浏览器运行完整 fixture 矩阵和 嵌入式 Canvas 表格 原始场景,保存可复现结果。 - 验证整个管线的请求数量、集合规模、比较次数、深树安全和真实耗时。 - 检查新模块是否存在重复解释、长期 adapter、无消费者代码或语义影响扩散。 - 收尾剩余迁移文档、调试信息说明、支持范围和已知限制。 不做:把此前未测试的性能正确性全部拖到本 PR,也不接纳新的功能范围。 验收:第 11 节全部通过;若发现某模块契约错误,修正在该模块并补对应验收,不在最终接线处加站点特判。 ## 11. 总体验收矩阵 本节是范围索引,具体怎么做、什么算成功见 [验收清单](visual-observation-acceptance.md)。新会话按当前 PR 执行并直接说明结果即可,无需报告模板或逐项登记。修改契约时同步清单,避免两套标准。 | 维度 | 必测场景 | 主要责任 | |---|---|---| | 发现 | 无名、有 AX、aria-hidden、inert、pointer-events:none | PR 3 | | 视觉状态 | 零尺寸、透明祖先、visibility 覆盖、支持范围内 hidden 状态 | PR 0/2/3 | | 裁剪 | viewport、单轴/嵌套 overflow、支持范围内变换 | PR 1/2/3 | | frame | 同 target、OOPIF、nested、border、缺失 owner | PR 1/2 | | 身份 | target 冲突、节点移除、导航、document 替换 | PR 2/5/6 | | 输出 | DOM 顺序、视觉提示、modal 策略、极小预算、遗漏口径 | PR 4/7 | | ref | 工具矩阵、跨 tab/session、无悬空 ref | PR 5/7 | | 截图 | 几何变化、DPR/zoom、实际像素、重试、overlay | PR 6 | | 算法 | 深树、宽树、高密度、全部重合、索引退化 | 各模块 PR | | 回归 | #165、普通 DOM 点击/截图、snapshot、recording、hover | PR 1/2/7/8 | | 产品结果 | main 原始失败与新链路的 嵌入式 Canvas 表格 场景对照 | PR 0/8 | ## 12. 相比旧蓝图的明确调整 | 旧方向 | 本文调整 | 原因 | |---|---|---| | 全链路 transaction 先行 | 建立来源明确的 capture batch;不承诺原子快照 | 生命周期模型不能代替一致性证据 | | candidate → stack → logical surface 必经分层 | candidate + 输出覆盖去重;logical surface 不进入本次 | 限制身份模型及截图复杂度 | | 统一祖先 excluded | 视觉、语义、交互状态分开 | 避免加速错误规则 | | 各阶段一律兼容 adapter | 仅消费者迁移需要时使用,并随切换删除 | 避免临时抽象堆积 | | 性能作为后续优化阶段 | 每个模块设计/实现均有复杂度与计数验收 | 防止功能完成后再补算法 | | renderer 最后尝试附录 | 先规划预算,再分阶段输出 | 避免视觉入口系统性消失 | | observed rect 与 live rect 求交 | 身份和几何分别验证 | 避免成功返回错误截图片段 | | generation 暗示 freshness 已解决 | 明确现有 eN 重用限制 | 防止无法实现的旧字符串识别承诺 | | 当前测试全部视为兼容基准 | 先审核期望,再保留正确回归 | 测试不能替代行为规格 | ## 13. 完成定义与设计变更纪律 工程完成必须同时满足: - 五条核心结果全部成立,真实 嵌入式 Canvas 表格 场景及独立 fixture 均有证据。 - 每个模块只有一个权威实现,依赖方向与本文一致。 - 坐标和节点地址跨边界时身份明确,失败可归因。 - 视觉发现不依赖语义树保留结果,不混淆视觉与交互属性。 - 性能上限及退化可测试;复杂度没有遗漏协议输入量和几何工作量。 - 现有语义、DOM 操作及 #165 成果受到明确回归保护。 - 当前支持范围、近似/拒绝行为和 ref freshness 限制已记录。 - 不存在为了通过原始站点而增加的生产特判。 每个实现 PR 的描述必须写明:解决哪个独立问题、改变哪个契约、哪些行为允许变化、验证哪些正确性与性能要求、删除哪条旧路径。若没有删除旧路径,应说明保留原因和明确的后续消费/移除 PR。 新的案例若属于已定规则范围,应修复负责该规则的模块;若要求改变目标身份、几何支持或对外协议,应先形成设计变更。不得在 observation 接线、renderer 或截图执行末端不断补跨层例外。 ## 14. 参考与证据来源 - 本地 main:`3c5f838`;当前分析分支:`fix/vom-canvas-observation`。 - PR #165 本地合并提交:`5ed8287`;实现提交:`641d934`。涉及 renderer 上下文索引、显式栈等优化。 - 主要代码范围:`apps/extension/src/tools/observation.ts`、`tools/vom/capture.ts`、`tools/vom/frame-capture.ts`、`tools/vom/semantic-graph/`、`tools/frame-geometry.ts`、`session-manager/ref-store.ts`、`packages/vom/src/render.ts`。 - 当前分支参考:`tools/vom/rendered-surfaces/`、`tools/visual-screenshot-plan.ts` 及相关 fixture/test。 - 原 visual-surface 架构蓝图作为历史参考,不是本设计的执行规格。 - 浏览器语义参考:[aria-hidden](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-hidden)、[inert](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/inert)、[visibility](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/visibility)。本次 CDP 单位、frame 和身份实验结论见验证结果文档;支持范围之外的行为不作推断。 本文中的路径为代码职责定位,最终目录命名可适配工程规范;行为契约、问题边界、复杂度预算和验收要求不能因此省略。