mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
feat(vom): canvas design and fixtures
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
# Visual observation:验收清单
|
||||
|
||||
新 agent 先读 [架构与 PR 划分](visual-observation.md),再按本清单验收当前 PR;无需聊天记录,也无需另写报告、逐项编号或填写模板。历史结果见 [PR 0 证据](visual-observation-evidence.md),不能代替本次执行。
|
||||
|
||||
## 怎么开始
|
||||
|
||||
- 查看分支、HEAD 和工作区改动,确认当前做哪个 PR。PR 0 基于 main `3c5f838`;后续模块对照其前置基线,最终回归对照 main。注意未跟踪文件也属于改动。
|
||||
- 当前仅有设计、合成 fixture 和诊断脚本;未来模块尚未实现。只验收当前阶段及受影响的已有功能,不要求提前完成后续 PR。
|
||||
- 测试放在模块附近,调用实际生产代码。未接线模块可用人工输入和 CDP stub;实现 PR 写明运行命令即可,不强制新建指定目录、命名或诊断 API。
|
||||
- 浏览器验收前核实 CLI 版本,并重新 build、确认扩展 reload。PATH 中的 bsk 曾经比仓库版本旧,不能只凭版本号认定运行了目标代码。
|
||||
- 不 commit/push。截图、原始页面内容和一次性实验放临时目录;只把长期需要的设计、fixture 和测试留在仓库。
|
||||
|
||||
## 现有验证入口
|
||||
|
||||
从仓库根目录运行,命令成功退出且相关断言通过才算成功:
|
||||
|
||||
```sh
|
||||
pnpm vom:test
|
||||
pnpm --filter @browser-skill/extension test src/tools/__tests__/frame-geometry.test.ts src/tools/__tests__/element-geometry.test.ts src/tools/__tests__/observation.test.ts src/tools/__tests__/snapshot-ref.test.ts src/tools/__tests__/interaction.test.ts src/tools/vom src/session-manager/__tests__/ref-store.test.ts
|
||||
pnpm exec biome check apps/extension/test-fixtures/visual-observation
|
||||
VISUAL_EVIDENCE="$(mktemp -d /tmp/bsk-visual-acceptance.XXXXXX)"
|
||||
node apps/extension/test-fixtures/visual-observation/probe.mjs "$VISUAL_EVIDENCE/browser-contract"
|
||||
node apps/extension/test-fixtures/visual-observation/measure-render.mjs "$VISUAL_EVIDENCE/performance"
|
||||
```
|
||||
|
||||
probe 需要 Node 22+ 和 Chrome,配置见 [fixture 说明](../../apps/extension/test-fixtures/visual-observation/README.md)。它验证浏览器事实,不代表后续生产模块已经通过。measure-render 用于旧 renderer 对照,不要求修复历史复杂度。
|
||||
|
||||
实现 PR 还需运行所属模块的新测试、`pnpm --filter @browser-skill/extension compile`、`pnpm ext:test` 和 `pnpm ext:build`。仅修改文档时检查链接和内容即可,不必重跑浏览器。已有失败应说明,不在范围外顺手修复。
|
||||
|
||||
## 每个 PR 怎么算完成
|
||||
|
||||
### PR 0:设计与复现
|
||||
|
||||
只有文档、合成 fixture 和诊断变动;架构、边界及后续 PR 的完成条件明确。现有测试通过;probe 的 checks 非空且全部通过(本版 39 项);measure-render 产生五组 wide/deep 结果。
|
||||
|
||||
按下方浏览器步骤,在 main 重现“表格已绘制,但 observe 没有视觉入口”。合成页面和原始 iWiki 分别确认。原始页面未加载、无权限或缺入口时说明阻塞,不能算已复现。入库内容不含内部页面地址、标识或截图。
|
||||
|
||||
### PR 1:几何与身份
|
||||
|
||||
数值误差:纯函数输入允许 1e-6 CSS px;真实浏览器每条边允许 0.25 CSS px。坐标类型不能混传;动态 frame 归属需运行时检查。
|
||||
|
||||
| 怎么测 | 什么算成功 |
|
||||
|---|---|
|
||||
| top document bounds=(30,50,120,40),document scroll=(10,20),viewport=800×600 | top border region=(20,30,120,40);不受 backing store 尺寸影响 |
|
||||
| 使用 frame.html:local Canvas=(17,23,120,40),same owner content origin=(50,610);nested origin=(87,707);OOPIF origin=(410,610) | top origins 分别=(67,633)、(104,730)、(427,633);不能重复给 nested 加父偏移;owner border 不能代替 content |
|
||||
| child scrollY=10;same frame owner origin 不变 | Canvas top y=623,local y=13;snapshot document y=23;scroll 只扣一次 |
|
||||
| same iframe 以自身左上角为原点 scale(1.25),原 border origin=(40,600),border=10 | owner content origin=(52.5,612.5),Canvas top=(73.75,641.25),尺寸150×50 |
|
||||
| Canvas content 120×40,border 每边5;overflow owner CSS width60/height30/border5/padding4 | Canvas region 宽130高50,live 验证同 box;owner clientRect=(5,5,68,38),不能使用78×48 border范围作为clip |
|
||||
| DPR=1/1.5/2,CSS Canvas120×40;UI zoom与CSS zoom分别处理 | DPR不直接除掉 CSS rect;raster 预测与实际图片分别记录;不以layout/css宽度比作为万能DPR |
|
||||
| 分别构造缺失 owner、失效 frame、旋转、透视、mask、clip-path、活动pinch、变换的overflow祖先 | 前两项 geometry-unavailable,后六类 geometry-unsupported;无伪造精确 region ref、无按border猜偏移 |
|
||||
| 同一操作并发请求同一frame 20次,再开启第二次操作 | 第一次每个metrics来源、owner quad、frame投影最多构建/请求一次;第二次重新获取;异常promise不泄漏到其他操作 |
|
||||
|
||||
### PR 2:采集与共享事实
|
||||
|
||||
| 怎么测 | 什么算成功 |
|
||||
|---|---|
|
||||
| target A/B 各含 backendNodeId=7,frame/document不同 | 两个lookup结果独立;AX join、overlay排除和父子查找不串target |
|
||||
| 4个frame分属2个target,其中一个target有3份document;均成功采集 | 每target恰好一次DOMSnapshot;每frame恰好一次AX批次;失败重试不混入成功请求数;无按frame重复snapshot |
|
||||
| 采集中模拟frame detach或loader/documentElement更换 | 丢弃受影响document的混合事实并标记原因;其他完整document可保留;不得把新AX接到旧DOM |
|
||||
| 输入10,000深链、10,000兄弟、孤儿及A→B→A环 | 无栈溢出/无限循环;孤儿/环有明确不完整状态;不生成完整祖先证据;context推导每有效节点至多一次 |
|
||||
| opacity0祖先;visibility:hidden祖先+visible子;aria-hidden/inert祖先 | 分别得到视觉透明、子可见、语义/交互状态独立;不折叠为同一个excluded |
|
||||
| 一个frame采集失败,其他frame有语义节点;全部成功但无Canvas | 前者partial且保留其他语义;后者empty而非unavailable;错误原因可追踪到frame及阶段 |
|
||||
| 注入CDP调用记录和hover探针;纯计算中途取消 | 所有静态DOM/AX捕获先于hover输入;取消后下一工作块不继续处理,未执行的输入/截图为0;取消不变成普通空结果 |
|
||||
|
||||
### PR 3:视觉发现
|
||||
|
||||
| 怎么测 | 什么算成功 |
|
||||
|---|---|
|
||||
| index.html?noFrames=1 的plain、named、aria-hidden、inert、pointer-none、visibility-override、hidden-overridden、fallback | 八个均为候选;无label不影响发现,有AX/fallback不抑制候选 |
|
||||
| opacity-parent、display-none、visibility-hidden、zero、full-clip | 五个均无候选,原因分别能追踪到视觉/几何规则;不能把aria-hidden列为原因 |
|
||||
| partial-clip、axis-clip、border-clip、border-canvas | crop尺寸分别60×30、60×40、64×34、130×50;边界来自client box/同种Canvas box |
|
||||
| 同frame中删除语义parent,再保持DOM/几何不变 | 候选仍存在、地址不变;语义context可变为null;frame owner本身无法解析时不是该用例 |
|
||||
| 连续两次输入同一只读Facts;对Facts启用深冻结 | 候选内容一致,无输入mutation,无CDP调用;去重前stack-a/b仍是两个候选 |
|
||||
| 普通候选部分越出viewport;完全越界;overlay-owned Canvas;unsupported clip | 分别交集区域、无候选、排除、明确unsupported状态;不把未知状态伪装成empty |
|
||||
|
||||
### PR 4:选择与预算
|
||||
|
||||
预算按每个输出行的 `ceil(line.length/4)` 求和(UTF-16 length),包括头部和截断提示。H 为完整协议头成本,V 为固定行 `[visual] Canvas regions present; use screenshot` 的成本。
|
||||
|
||||
| 怎么测 | 什么算成功 |
|
||||
|---|---|
|
||||
| 同context:无名1000×800候选A,有名10×10候选B,K=1 | 选A;名称不能压倒区域面积 |
|
||||
| 同document/parent且crop完全相同的A/B;另一个parent的C;crop右边相差0.01的D;相邻tile E | A/B仅输出一个代表;C/D/E独立保留;不输出layers=N作为业务解释 |
|
||||
| 面积相同的候选随机打乱20次,地址/位置保持不变 | 排序相同;tie-break顺序固定为frameId、top y、left x、backendNodeId(同一观察内);去重代表取该顺序最小者;字符串按代码单元字典序、数值按数值升序 |
|
||||
| 50,001个不同key,最后一个为最大Canvas,K=1 | 最后最大者仍被选中;dedup key峰值≤50,000;标记dedup-degraded;未因容量耗尽停扫描 |
|
||||
| 100,000候选、K=1/32/512;另测K=0 | 选中数量≤K、heap峰值≤K、exact-key之外的模糊pair比较为0;K=0不产生ref候选;结果与独立全排序小规模oracle一致 |
|
||||
| 带blocking layer的纯输入,候选context在L1、背景、未知各一个 | L1优先,背景不标为物理不可见,未知不伪装L1;同层再按面积及固定 tie-break 排序;不修改Facts |
|
||||
| 预算H-1 | 新observe规划返回insufficient-budget,工具映射invalid_params并给minimum=H;无新文本/ref提交;snapshot旧路径不改变 |
|
||||
| 有候选,预算H、H+V-1、H+V | 前两个输出合法协议头、无悬空ref、truncated=true;H+V能保留固定视觉提示;全部账本≤预算 |
|
||||
| 没有显式maxTokens,候选足够多 | 视觉附加部分cost≤2048;最终视觉条目≤512;不是对整个旧语义输出强加2048 |
|
||||
| 标签含200个Unicode code point、引号、换行、代理对emoji | 标签先截至160 code point再JSON escape,按最终行核算;无断开的代理对,无换行注入额外条目;写不下的行不产生ref |
|
||||
| finite预算取H+V、64、128、256、2048(小于H时拒绝),混合长短条目 | 每次总账本≤budget;输出决定可重复;已知omitted才给精确数,候选/去重组/上游未知数量不混加 |
|
||||
|
||||
### PR 5:类型化 ref
|
||||
|
||||
| 怎么测 | 什么算成功 |
|
||||
|---|---|
|
||||
| 注册DOM和visual-region两个判别目标 | kind决定操作资格;类型测试不能构造visual+interact矛盾状态;不额外接受调用者capability数组 |
|
||||
| 同名ref置于两个session/tab;输入@e1与e1 | canonical形式可解析;不同session/tab不串目标;未知目标为既有not_found类错误 |
|
||||
| 对visual ref调用click/fill/hover/select/get_html/人工帮助DOM定位入口 | 全部按主设计拒绝;CDP输入、DOM HTML读取、DOM高亮等该工具副作用次数为0;不能偷偷降为anchor操作 |
|
||||
| loader变化、target重attach、documentElement替换;另测history.replaceState不改身份 | 前三者拒绝旧目标;最后不单凭URL变化拒绝;实际anchor仍需连接 |
|
||||
| document.open保留loader/document backend但替换html和Canvas | 旧ref失效;只比较loader的实现必须被此测试击败 |
|
||||
| 第二次observe重新绑定e1;第一次ref字符串再次输入 | 明确验证“最新观察映射”而非声称能识别旧字符串;新映射仅来自成功提交;旧DOM工具回归通过 |
|
||||
|
||||
### PR 6:截图执行
|
||||
|
||||
首次 clipScale 为 `min(1, 2048/(max(w,h)*r), sqrt(4000000/(w*h*r*r)))`;r 来自实际 metrics 适配,最终仍检查真实 PNG 尺寸。
|
||||
|
||||
| 怎么测 | 什么算成功 |
|
||||
|---|---|
|
||||
| visual ref解析成功并截图;DOM ref另测 | visual路径scrollIntoView次数0;DOM路径保持BASE策略;visual实时box种类与观察一致 |
|
||||
| 同anchor四边分别移动0.249、0.25、0.251 CSS px;分别改尺寸/裁剪策略 | 前两种仅数值误差可接受,0.251拒绝;document/裁剪策略变化不受容差豁免;不靠old∩live返回小片段 |
|
||||
| 保持身份/几何,重绘Canvas颜色;另移除Canvas | 重绘允许取得当前图像;移除拒绝;不承诺像素快照或把颜色改变误报为stale |
|
||||
| css4096×2048,rasterEstimate2;css4000×4000,rasterEstimate1 | 首次clipScale分别0.25、0.5(不放大,取edge/area/1最小值);实际成功尺寸每边≤2048、面积≤4,000,000 |
|
||||
| mock第一次图片3000×3000,第二次2000×2000;再测两次均3000×3000 | 第一组重试一次并成功,第二组恰好两次后pixel-budget-exceeded;不能无限重试或返回超限图 |
|
||||
| 空data、非法PNG头、0尺寸、负/非有限几何 | 明确失败,不能猜测width/height;此项不要求将PNG头检查宣称为完整解码校验 |
|
||||
| capture前取消;overlay抑制后CDP失败;采集时出现dialog | 取消前不再capture;成功/失败/取消均恢复overlay;dialog机制保持既有契约;无额外输入 |
|
||||
|
||||
### PR 7:observe 接线
|
||||
|
||||
| 怎么测 | 什么算成功 |
|
||||
|---|---|
|
||||
| 固定SemanticScene,无视觉候选;普通预算,与BASE replay同一输入 | 语义文本、ref相对顺序、role/name一致;不因为新通道重跑semantic pipeline |
|
||||
| 同SemanticScene加两个视觉候选,大预算 | 原DOM ref顺序不变,视觉ref在其后;每个输出ref均可在registry找到同目标,registry无未输出新ref |
|
||||
| DOM文本多到耗尽除预留外的预算 | 固定视觉提示可见(预算允许时);truncated=true;不是DOM stopped后无条件跳过视觉提示 |
|
||||
| 视觉capture unavailable但语义成功 | 语义结果保留,观察状态能区分empty/unavailable/truncated;debug reason准确且无原始DOM泄漏 |
|
||||
| snapshot、recording调用、hover probe on/off与BASE对比 | snapshot不新增visual;recording不隐式启用发现;probe默认off,on时仍在静态采集后;相应既有测试通过 |
|
||||
| blocking layer混合DOM/visual、重复候选、不同预算 | 输出层策略与选择模块的活跃层策略一致;头、摘要、条目、occlusion/omitted行均纳入账本;无悬空ref |
|
||||
|
||||
### PR 8:整体验收
|
||||
|
||||
复跑上述模块测试、下方性能对照和浏览器场景,确认原始 iWiki 的缺口已解决。检查没有站点特判、重复的几何/预算权威实现或遗留的替代路径;没有顺带扩展到 OCR、逻辑画布聚合或旧算法重写。不能只凭合成页面通过宣布原始问题解决。
|
||||
|
||||
## 性能怎么验
|
||||
|
||||
在相关模块实现时检查,不拖到最后统一补。旧路径和新增 Canvas 成本分开看:
|
||||
|
||||
- **旧路径**:同输入对照 main/前置基线,已有 render/context/sibling 工作量、语义构建次数和 DOM 操作请求不增加;保留 #165 的回归。不要求消除旧算法的历史二次项。
|
||||
- **无 Canvas**:允许必要检测与共享几何成本,但不构建视觉专用索引、不发视觉专用 CDP 请求、不重复跑语义链路。
|
||||
- **新增树处理**:deep/wide 节点数取 1k、10k、100k;每节点 context 至多推导一次,每条边每个索引 pass 至多访问一次,pass 数不随规模增加。无递归溢出、每候选完整祖先遍历或全祖先数组复制。
|
||||
- **选择**:候选数取 1k、10k、100k,K 取 1、32、512;堆峰值不超过 K,去重 key 不超过 50,000,无模糊两两比较。比较计数符合具体算法推导的上界;小规模结果与独立全排序对照一致,容量耗尽后仍能选中最后的大图。
|
||||
- **请求与取消**:每 target 一次 snapshot、每 frame 一次 AX 批次;单次操作内几何请求共享,不跨操作复用 live cache。几何最大并发 4,新增计算每块最多 256 项并实际让出事件循环,取消后不再启动下一块。
|
||||
- **接线后耗时**:同机器、浏览器和固定内容,分别测无 Canvas 页与合成表格页。各预热 3 次,再基线/新版本交替各 20 次,比较中位数和 p95;旧阶段与新增成本分开。旧阶段中位数增加超过 max(基线 10%, 2ms) 时复测,两轮均超过且可归因于改动则未通过;噪声无法排除则说明尚未确认。操作数仍须满足前述条件,不能用耗时阈值代替它们。
|
||||
|
||||
计数可以由测试插桩取得,不必为验收增加生产接口。性能结果只需说明旧路径是否回退、新增路径的规模与成本,不需要另填表格。
|
||||
|
||||
## 浏览器怎么验
|
||||
|
||||
### 合成表格(PR 0 复现;PR 7/8 验证修复)
|
||||
|
||||
1. 核实 `bsk --version`、`bsk browsers`、`bsk status`;运行 `pnpm ext:build`,确认用户已 reload `apps/extension/dist/chrome-mv3`。
|
||||
2. 单独启动服务器:`python3 -m http.server 4177 --bind 127.0.0.1 --directory apps/extension/test-fixtures/visual-observation`。
|
||||
3. `bsk session start --width 1440 --height 1100`,使用返回的 session id 打开下列页面。尖括号替换为实际值,每条命令传同一 session。
|
||||
|
||||
```sh
|
||||
bsk navigate http://127.0.0.1:4177/table.html --session <session> --json
|
||||
bsk observe --session <session> --json
|
||||
bsk screenshot --session <session> --out <临时目录>/table.png --json
|
||||
```
|
||||
|
||||
4. 把窗口放前台,实际看到三列六行后再采集 observe。load 完成、AX 工具栏出现或背景占位截图都不能证明表格已经绘制。
|
||||
5. main 应有 Page action、Insert record、Filter 三个 DOM 控件,truncated=false,但无表格视觉入口。这是 PR 0 的缺口复现。
|
||||
6. 新实现应保留三个控件,并增加 visual ref。用本次返回的 ref 执行 `bsk screenshot --ref <ref> --session <session> --out <临时目录>/crop.png`,实际查看图片:包含 Canvas column A/B/C 和六行,裁剪对应 780×360 CSS px 的表格区域,不能错到工具栏或 iframe 边框。PNG 满足每边 2048、总像素 4,000,000 的上限。无需生成单元格语义。
|
||||
|
||||
### 几何与动态场景
|
||||
|
||||
PR 1 验证实际生产几何模块;PR 6 验证截图执行;PR 8 复验完整链路。未接 observe 时可注入人工 facts/ref,但必须调用实际模块,不能以独立 probe 代替实现测试。
|
||||
|
||||
- 必测缩放集合:DPR 1/1.5/2 × top/frame;真实 UI zoom 100%/90% × top/frame;CSS zoom 1.25 的 top Canvas;frame scale 1.25 的 same/nested/OOPIF。按 PR 1 的坐标预期核对。OOPIF 需确认实际 iframe target,跨 origin 本身不够。
|
||||
- UI zoom 要通过浏览器菜单或原生快捷键确认,不能以 CSS zoom 或 pinch emulation 替代。旋转、mask、clip-path、活动 pinch、变换 overflow 按不支持策略降级,不输出猜测的精确 ref。
|
||||
- 使用 `index.html?noFrames=1` 的 `#plain`,每个实验从 reload 后的新 ref 开始:平移 1px、宽度改为 121px → 旧 ref 截图要求重新观察;移除节点或 document.open → 旧目标失效;仅重绘颜色 → 截图成功且是新颜色。不要在重新 observe 后拿重用的 eN 字符串声称验证了旧 ref。
|
||||
- visual ref 的 click/fill/hover/select/get_html/DOM 高亮拒绝测试只在合成页或自动测试运行,确认拒绝前没有派发输入、读 HTML 或高亮副作用。
|
||||
|
||||
### 原始 iWiki(PR 0 与 PR 8)
|
||||
|
||||
入口与就绪说明在本机临时文件 `/private/tmp/bsk-visual-design/private-reproduction.md`。文件丢失或登录失效时向用户取得入口/权限,说明这一项未完成;继续独立的合成验证。
|
||||
|
||||
保持只读。前台实际看到表格后采集 observe 和截图:PR 0 确认缺口;PR 8 确认外围 DOM 控件仍在、新 visual ref 能截到实际表格。不要编辑单元格,不要求原页面永久维持历史的 63 个 ref。私有内容不入库。
|
||||
|
||||
结束后停止自己的 session、服务器及临时 Chrome,恢复修改过的 zoom/emulation,不关闭其他任务资源。
|
||||
|
||||
## 做完怎么回复
|
||||
|
||||
直接告诉用户:验了哪个 PR、跑了什么、是否符合上述成功条件;若有失败或未做的检查,说明具体原因。必要时附关键测试输出或截图路径。无需单独报告文件、hash 清单、状态统计或全量日志归档;没运行、被跳过或受阻的项目不要写成通过。
|
||||
@@ -0,0 +1,235 @@
|
||||
# Visual observation:PR 0 验证结果与架构决策
|
||||
|
||||
日期:2026-09-08。生产基线:main `3c5f838`。分支:`codex/visual-observation-design`。
|
||||
|
||||
本次只新增设计、合成 fixture 和诊断工具。没有生产算法变更,没有 commit/push。
|
||||
总体设计及后续 PR 依赖见 [架构与实施计划](visual-observation.md)。
|
||||
本文是历史证据;新会话请按 [验收清单](visual-observation-acceptance.md) 执行相关检查并说明结果,不继承本文的通过结论。
|
||||
|
||||
## 1. 环境与证据等级
|
||||
|
||||
| 环境 | 实际版本/说明 |
|
||||
|---|---|
|
||||
| PATH 中的 bsk | 0.1.7,未用于有效页面基线 |
|
||||
| 仓库已有 target/debug/bsk | 0.2.0;`update --check` 报告发布最新版也是 0.2.0,没有安装更新 |
|
||||
| daemon / connected extension | 0.2.0,protocol 1.1 |
|
||||
| 页面基线 Chrome | 151.0.0.0,由 bsk browsers 报告 |
|
||||
| 生产扩展构建 | `pnpm ext:build`,用户 reload 当前 main 基线构建后采集 |
|
||||
| 独立协议实验 Chrome | 152.0.7977.76,临时无登录 profile;与用户浏览器不是同一个运行实例 |
|
||||
| 基线测试 | VOM 47 tests / 2 files;相关扩展 217 tests / 14 files;全部通过 |
|
||||
|
||||
页面结果是实际 bsk + reload 扩展的结果。协议实验是独立 Chrome 的真实 CDP 响应。
|
||||
临时 replay 是将合成响应送入 main adapter 的诊断,不代表另一条真实浏览器完整运行。
|
||||
不同证据不混用,版本号本身不证明构建 commit。
|
||||
|
||||
所有内部页面原文、地址、iframe 标识、截图和用户信息仅保存在本机临时目录,不进入仓库。
|
||||
本文仅保存匿名化事实,仓库 fixture 完全独立构造。
|
||||
|
||||
## 2. 原始问题已确认:表格加载完成后仍有观察缺口
|
||||
|
||||
有效就绪条件:前台可见页面已经绘制出网格、列标题和六行表格。不能使用以下替代条件:
|
||||
|
||||
- `document.readyState === complete`。
|
||||
- AX 已有嵌入应用的工具栏。
|
||||
- 页面出现 iframe。
|
||||
- 背景截图仍为站点加载 logo。
|
||||
|
||||
本次初始后台窗口恰好出现“AX 已有工具栏,图像仍为加载占位”的情况。这些样本已排除。
|
||||
在前台绘制完成后重新取得匹配的截图和 observe,结果为:
|
||||
|
||||
| 事实 | 结果 |
|
||||
|---|---|
|
||||
| 实际截图 | 可见列标题、网格与六行表格 |
|
||||
| main observe | 63 个 ref,含外围和嵌入应用工具栏 |
|
||||
| truncated | false |
|
||||
| 表格列内容 | 没有相应文本表示 |
|
||||
| visual ref | 没有 |
|
||||
| 直接打开已观察到的嵌入应用 | 发现一个无 aria-label 的 Canvas,CSS 尺寸 1450×835、backing store 2900×1670,DPR 2 |
|
||||
|
||||
直接打开嵌入应用用于验证其渲染介质;其宽度变化是独立 tab 布局,不能拿这组坐标当父页面中的 crop。
|
||||
父页面的截图/VOM 对照与直接嵌入页的 DOM 证据分别记录。
|
||||
|
||||
结论:主要能力缺口是视觉内容没有观察入口;不属于这次有效样本的文本预算耗尽。
|
||||
新架构不能承诺从 Canvas 自动恢复单元格语义;正确结果是保留工具栏,并提供可截图的区域入口。
|
||||
|
||||
## 3. 合成最小复现
|
||||
|
||||
`table.html` → 跨站点 `grid.html`:一个 iframe、DOM 工具栏、一个无名 Canvas。
|
||||
Canvas 绘制三列、六行,所有文字与原页面无关。
|
||||
|
||||
main 实际输出的关键部分:
|
||||
|
||||
```text
|
||||
@view 1440x923
|
||||
@e1 button "Page action"
|
||||
Iframe "Table application"
|
||||
navigation
|
||||
@e2 button "Insert record"
|
||||
@e3 button "Filter"
|
||||
```
|
||||
|
||||
`ref_count=3`,`truncated=false`。截图已人工确认包含 `Canvas column A/B/C` 和六行。
|
||||
这些像素文字既没有文本表示,也没有 visual ref。该最小复现已覆盖原场景的关键结构,
|
||||
无需站点权限或生产选择器。
|
||||
|
||||
## 4. CSS 与语义/交互分离
|
||||
|
||||
真实浏览器实验确认:
|
||||
|
||||
- aria-hidden 祖先、inert 祖先、pointer-events:none 不会使 Canvas 自动视觉消失。
|
||||
- `visibility:hidden` 祖先下的 `visibility:visible` 子 Canvas 可以恢复可见。
|
||||
- `hidden` 元素被 `display:block` 覆盖时仍可绘制。
|
||||
- opacity:0 祖先会使子树透明;不能仅检查 Canvas 自己的 opacity。
|
||||
- display:none 无 layout bounds;visibility:hidden 可以仍有 bounds。
|
||||
- AX fallback 文本不包含绘制的 `PIXEL TABLE 42`,不能用 fallback 存在作为发现去重条件。
|
||||
|
||||
决定:事实层分别保存 computed visibility、透明继承、语义暴露和交互属性。
|
||||
视觉发现不读取 semantic keep/drop 结果;祖先索引只能传播符合属性语义的状态。
|
||||
|
||||
`checkVisibility()` 的实验作用仅限 CSS suppression,不用它证明 overflow、viewport 或遮挡结果。
|
||||
常规 overflow 由独立几何规则负责;任意遮挡不进入首版精确能力范围。
|
||||
|
||||
## 5. 单位、frame 投影与截图
|
||||
|
||||
### 5.1 DPR 与截图像素是不同契约
|
||||
|
||||
独立 Chrome 上对相同 120×40 CSS clip 的结果:
|
||||
|
||||
| Emulated DPR | DOMSnapshot Canvas w×h | Screenshot w×h | layoutViewport/cssLayoutViewport 宽度比 |
|
||||
|---|---|---|---|
|
||||
| 1 | 120×40 | 120×40 | 1 |
|
||||
| 1.5 | 120×40 | 180×60 | 1 |
|
||||
| 2 | 120×40 | 240×80 | 1 |
|
||||
|
||||
因此该比值不能用作通用截图 DPR。原始 DOMSnapshot 单位与截图 raster scale 必须分别适配,
|
||||
输出 PNG 尺寸是最终预算事实。不能把本组 DPR emulation 结果泛化为所有浏览器缩放组合。
|
||||
|
||||
### 5.2 三种缩放分别记账
|
||||
|
||||
- CSS `zoom:1.25`:120px 宽 Canvas 的布局 bounds 为 150px,运行时几何同步变化。
|
||||
- Emulation.setPageScaleFactor(1.5):记录为 page-scale/pinch 类证据,不充当 UI zoom。
|
||||
- 用户 Chrome 原生 UI 缩放到 90%:界面明确显示 90%,DPR 从 2 变为约 1.8,innerWidth 从 1440 变为 1600,visualViewport.scale 仍为 1;Canvas CSS 宽约 120.0000076,截图仍是 2880px 宽。main VOM 视口为 1600×1025。已恢复 100%。
|
||||
|
||||
现有 bsk 的 Meta+- CDP 输入未改变浏览器缩放,所以该无效果尝试不算缩放验证;
|
||||
有效样本来自原生浏览器 UI 操作。真实 UI zoom 仅验证了上述点,不能宣称完整 zoom 矩阵已通过。
|
||||
|
||||
决定:使用 CSS 视口空间作为发现/截图区域公共契约;精确单位 adapter 的实现还必须在 PR 1
|
||||
跑计划内组合回归,不用一个比例同时解决所有协议字段。
|
||||
|
||||
### 5.3 Frame 空间与 main 已知偏移
|
||||
|
||||
fixture local Canvas 原点为 (17,23)。
|
||||
|
||||
| 场景 | 实测 owner content 原点(target root) | 正确 top Canvas 原点 | main parser replay |
|
||||
|---|---|---|---|
|
||||
| 同 target frame,10px border | (50,610) | (67,633) | (57,623) |
|
||||
| 上述 frame 内 nested,另有 7px border | (87,707) | (104,730) | (87,713) |
|
||||
| OOPIF,10px border | (410,610) | (427,633) | 本表不混入非完整 replay 的 OOPIF 结果 |
|
||||
|
||||
根 target 的 DOMSnapshot 含三份 document(top/same/nested);OOPIF 在单独 target 中只返回自己的 document。
|
||||
probe 通过实际 `TargetInfo.type=iframe` 验证 OOPIF,而非仅根据 origin 推断进程隔离。
|
||||
|
||||
子 frame scrollY=10 后,runtime local y 从 23 变为 13,snapshot document y 仍为 23。
|
||||
owner scale(1.25) 后 content quad 宽度变为 375,child innerWidth 仍为 300。
|
||||
|
||||
决定:frame-local 原始矩形必须先减该 document 的滚动,再经过一次权威投影;同 target 的
|
||||
owner quad 已是 target-root 坐标,不能重复逐层加偏移。禁止 border-box 替代 content origin。
|
||||
|
||||
Box 种类也必须进入契约:带 5px border 的 120px Canvas,其 snapshot/border quad 宽 130,
|
||||
content quad 宽 120。视觉入口采用 border-box,实时验证同种 box;iframe 投影则采用 content quad。
|
||||
带 border/padding 的 overflow owner,snapshot.clientRects 对应 `[clientLeft,clientTop,clientWidth,clientHeight]`,
|
||||
本 fixture 为 `[5,5,68,38]`。裁剪边界应从 client box 派生,不能直接拿 ancestor border bounds。
|
||||
单轴 `overflow-x:clip;overflow-y:visible` 的 computed style 保持分轴,必须分轴传播。
|
||||
|
||||
CSS zoom 1.25 后,上述 owner 的 bounds 变为 `[745,455.625,97,59.5]`,clientRects 仍为
|
||||
`[5,5,68,38]`,offsetRects 为 `[596,365,78,48]`。直接按 border-width 比例推导 client box
|
||||
会混入不同空间与整数取整。首版将有 CSS zoom/DOM transform 的 overflow 祖先组合明确降级,
|
||||
不承诺精确 crop;普通未变换 overflow 及 iframe content-quad 投影保留支持。
|
||||
|
||||
## 6. Document 与 ref 时效
|
||||
|
||||
真实 CDP 实验:
|
||||
|
||||
| 操作 | frameId | loaderId | document backend | anchor |
|
||||
|---|---|---|---|---|
|
||||
| history.replaceState | 不变 | 不变 | 不变 | 仍属于当前树 |
|
||||
| 跨 document 导航 | 不变 | 变化 | 变化 | 旧目标不可继续视为当前内容 |
|
||||
| document.open/write/close | 不变 | 不变 | 本次实验不变 | 旧 Canvas 失去连接,documentElement 被替换 |
|
||||
|
||||
决定:身份取 target attachment epoch + frameId + loaderId + documentElement backend identity;
|
||||
工具执行时仍检查 anchor.isConnected 及其所属 documentElement。不能仅凭 loaderId 判断有效。
|
||||
DOMSnapshot 可提供根元素 backend identity,frame graph 需要保留 loaderId;当前 main graph
|
||||
没有该字段,这是后续数据契约的明确修改点。
|
||||
|
||||
12 次静态几何采样完全相同;0.5px 移动可被检测;真实 90% 缩放引入约 1e-5 CSS px 的
|
||||
数值误差。首版采用最大边误差 0.25 CSS px 作为保守工程阈值,超出要求重新 observe。
|
||||
这不是大规模统计得到的通用稳定性保证;改变阈值需更新其测试及行为文档。
|
||||
|
||||
既有 eN 被下一次观察重用后,单靠旧字符串不能检测它来自上一代。内部 generation 不消除
|
||||
该协议限制。本次不改变协议,只验证当前存储目标的身份/几何。Canvas 可以在身份与几何
|
||||
不变时重绘;截图始终表达执行时图像,不承诺观察时像素快照。
|
||||
|
||||
## 7. 既有路径回归基线:用于防止负优化
|
||||
|
||||
诊断复制当前 renderer 到临时目录,在三个明确循环点加计数;生产文件未改变。
|
||||
复现命令见 [fixture README](../../apps/extension/test-fixtures/visual-observation/README.md)。
|
||||
|
||||
| 输入 | Render stack visits | DOM context candidates | Same-container sibling visits |
|
||||
|---|---:|---:|---:|
|
||||
| 100 headings + 100 View buttons | 201 | 10,000 | 15,050 |
|
||||
| 200 headings + 200 View buttons | 401 | 40,000 | 60,100 |
|
||||
| 400 headings + 400 View buttons | 801 | 160,000 | 240,200 |
|
||||
| 2,000 transparent wrappers + control | 2,001 | 0 | 0 |
|
||||
| 4,000 transparent wrappers + control | 4,001 | 0 | 0 |
|
||||
|
||||
显式栈访问线性并通过深树输入;本组宽树上下文计数呈二次增长。这里记录的是现有路径的
|
||||
特征,供后续同输入比较是否发生回退,不是本次要求修复的问题,也不是 Canvas 新算法的模板。
|
||||
计数不是完整 CPU profile,更不能替代 Canvas 发现、裁剪、选择和截图各自的性能测试。
|
||||
|
||||
处理边界:
|
||||
|
||||
1. 既有语义、DOM 操作和截图路径对照 main 不发生性能回退;不把历史算法优化列为本次前置依赖。
|
||||
2. Canvas 新路径按自己的规模和访问模式论证算法;发现、裁剪、去重、选择和截图分别验收。
|
||||
3. 精确 crop key 去重的选择来自首版的覆盖等价契约和新增成本分析,与 #165 采用什么算法无关。
|
||||
4. PR 7/8 分别报告既有路径回归、无 Canvas 页面的额外成本、有 Canvas 页面的新增成本。
|
||||
新功能有合理的额外成本不等于旧算法负优化;重复执行旧工作则属于需要消除的回退。
|
||||
|
||||
## 8. 锁定首版策略与退化
|
||||
|
||||
| 决策 | 首版契约 | 理由/验收 |
|
||||
|---|---|---|
|
||||
| 去重 | 同 document + 同直接 parent + 四条 crop 边完全相等 | 无模糊聚类;误差导致重复比误合并更可控 |
|
||||
| 索引容量 | 50,000 keys;耗尽后其余候选继续未去重选择 | 内存有界,不因早期小图耗尽而停止后续发现 |
|
||||
| K | min(预算推导容量, 512) | 所有行仍逐条核算可变成本 |
|
||||
| 默认视觉预算 | 未给总 maxTokens 时 2048 estimated tokens | 有显式 maxTokens 时全部纳入同一预算 |
|
||||
| 摘要预留 | 固定提示行按实际估算器成本预留 | 不用不可验证的固定 token 常量表示某段文本 |
|
||||
| 几何时效 | 最大边差 > 0.25 CSS px 即重新观察 | 身份和裁剪策略变化独立拒绝 |
|
||||
| 图像 | max edge 2048、max pixels 4,000,000、最多两次 capture | 成功必须以实际尺寸满足后置条件 |
|
||||
| 外部几何并发 | 4 | 单操作缓存;操作结束释放 |
|
||||
| 新增纯计算取消 | 每 256 项形成工作块并让出事件循环 | 不仅是轮询无法及时更新的 signal |
|
||||
|
||||
这些数值是可审查的首版资源政策,不是声称已证明用户体验最优的测量结果。
|
||||
参数可以在对应模块 PR 中经新的证据调整,但必须一并更新契约及测试。
|
||||
|
||||
进一步收敛:首版精确区域支持轴向矩形、普通 overflow 链、正向轴对齐平移/缩放与 frame。
|
||||
旋转、透视、clip-path、mask、活动 pinch-zoom 进入明确的 geometry-unsupported 降级,
|
||||
不给看似精确的 region ref,提示现有视口截图途径。不会把包围矩形偷换成精确区域。
|
||||
|
||||
## 9. 已执行的检查与下一阶段门槛
|
||||
|
||||
已执行:
|
||||
|
||||
- main VOM tests:47 passed。
|
||||
- main 相关 extension tests:217 passed,含 frame/element geometry、observation、interaction、ref-store、VOM。
|
||||
- `pnpm ext:build` 成功,用户 reload 后采集有效页面基线。
|
||||
- 独立 Chrome browser contract probe:39 个断言通过;可复现命令见 fixture README。
|
||||
- main 合成 capture replay、显式栈/上下文操作计数。
|
||||
- 实际 Chrome UI 90% zoom 样本,恢复 100%。
|
||||
- 原始表格与合成嵌入式表格均人工查看截图,并与未截断 observe 对照。
|
||||
|
||||
PR 0 交付的是已明确边界的设计和基线,不是未来功能已经通过:新的 discovery、typed ref、
|
||||
freshness resolver、pixel planner 和 renderer 接线尚未实现。后续每个 PR 必须验证自己的
|
||||
生产实现,不能把本次浏览器事实验证替代实现验收。
|
||||
|
||||
人工证据不设置默认 CI 对内网站点的访问依赖。合成 probe 可按需执行;页面原文不进仓库。
|
||||
后续 PR 的职责和验收保持在主设计中,本文负责记录证据、决策及没有作出的保证。
|
||||
@@ -0,0 +1,675 @@
|
||||
# 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 和身份实验结论见验证结果文档;支持范围之外的行为不作推断。
|
||||
|
||||
本文中的路径为代码职责定位,最终目录命名可适配工程规范;行为契约、问题边界、复杂度预算和验收要求不能因此省略。
|
||||
Reference in New Issue
Block a user