diff --git a/T-272.-.md b/T-272.-.md new file mode 100644 index 0000000..18c8c3e --- /dev/null +++ b/T-272.-.md @@ -0,0 +1,200 @@ + +> 同步来源:[`docs/tasks/T-272.md`](/chengma/mroubao/src/commit/afc651f75a3abc2676bb13aa8a80f6aa6a25a72e/docs/tasks/T-272.md) · commit `afc651f75a3a` + +--- +id: T-272 +title: 手机端 VLM 兜底选择并复核颜色尺码 +phase: 2 +deps: + - T-207 + - T-246 +status: DOING +created: 2026-08-01 +context_ref: 8739c39 +work_branch: null +write_paths: + - docs/tasks/T-272.md + - docs/current-state.md + - docs/02-requirements.md + - docs/03-tech-stack.md + - docs/04-architecture.md + - docs/05-coding-rules.md + - android-buyer/app/src/main/** + - android-buyer/app/src/test/** +--- + +## 问题 / 背景 + +真机任务 `da6be4b5-ed92-4813-b6d9-ae88d4995be6` 在第一轮候选浏览进入商品规格弹层后, +有时没有选中任务颜色,有时没有选中任务尺码。人眼能看到对应选项,但现有流程只依赖 +无障碍节点规则定位和一次点击后复读,无法处理拼多多灰度页面的祖先可点节点、祖先选中 +状态、规格组新文案或纯视觉选中边框。 + +当前代码有两个确定性缺口: + +1. `PinduoduoSpecificationParser.isOption()` 只接受选项节点自身 `clickable`、`selected` + 或 disabled,没有使用 T-269 已采集的 `selfOrAncestorClickable`。文字子节点不可点而 + 祖先容器可点时,选项在解析阶段就被漏掉,后续点击代码没有机会执行。 +2. `PinduoduoUiElement.selected` 只反映当前节点自身状态;选中状态落在祖先容器时会把成功 + 点击误判为未选中。候选自动化只复读一次,不能形成节点与视觉证据的交叉确认。 + +参考 Obsidian: + +- `ila/项目文档/cmroubao/cmroubao-Android无障碍节点树与VLM混合判断方案.md` +- `ila/项目文档/cmroubao/cmroubao-无障碍节点JSON与VLM兜底方案.md` + +本任务实现**规则优先、手机端 VLM 有界兜底、每次只执行一个规格动作、最终同时复核颜色 +和尺码**。Admin 不代理模型、不保存或下发 provider/API Key;第三轮保底候选不做 SKU +硬匹配,因此不启用本兜底。 + +## 关联需求与交互 + +- 功能:F-005、F-007、F-009 +- 用户故事:US-004、US-006、US-009 +- 交互:IX-006、IX-008、IX-010;不新增采购人员操作 +- 架构:`Android App -> 本机配置的 OpenAI 兼容 VLM Provider`,后端零推理 +- API/数据库:不新增 Go API 或 migration;通过既有执行事件回传非秘密聚合诊断 + +## 行为契约 + +### 1. 执行模式和配置来源 + +- 自动领取任务时,本机 VLM 配置完整、安全且 provider 支持结构化多模态调用,execution + 采用 `AI_ASSISTED`;否则采用 `MANUAL_FIRST`,不得因缺少模型阻塞确定性采购。 +- execution 模式开始后固定。只有 `AI_ASSISTED` 可以调用规格 VLM;`MANUAL_FIRST`、 + provider 失效或配置中途变化时保持确定性规则并跳过无法确认的候选,不静默冒充 AI。 +- provider、Base URL、model 和 API Key 只来自 Roubao 的本地设置;任务 payload 和 + Admin 均不能覆盖。Key 继续使用既有 Keystore-backed 加密存储且不得进入日志/outbox。 + +### 2. 原始 SKU 仍是硬约束 + +- `SkuHardConstraintExtractor` 从任务原始 SKU 冻结唯一 `COLOR` 和 `SIZE` 目标;模型不得 + 改写目标、数量、预算或商品身份。 +- VLM 只允许把冻结目标映射到当前截图/节点中实际存在的一个 observed option,例如 + `BLACK -> 雅黑`;找不到唯一证据返回 `NOT_FOUND/AMBIGUOUS`,不能就近任选。 +- 明确 disabled 或完整规格组中目标不存在属于 `MISMATCH`,不得调用模型覆盖。 + +### 3. 先修确定性节点规则 + +- 规格 option 判定接受 `selfOrAncestorClickable`,但组名、交易文案排除、长度和数量上限 + 保持不变。 +- 采集 `selfOrAncestorSelected`(包含有界祖先的 selected/checked),规格 option 的选中态 + 使用该只读聚合值;遍历层数沿用 `MAX_CLICK_ANCESTORS`,不得无界向上搜索。 +- 规则给出唯一、enabled 的目标时仍走现有快速路径,不调用 VLM。 + +### 4. 脱敏节点 JSON 与同帧截图 + +仅在规则返回 `Absent`(可见信息不完整)、`Ambiguous`、唯一选项点击失败,或点击后节点 +无法确认时构造规格兜底快照: + +- 只允许拼多多前台且页面稳定为 `SPECIFICATION_PANEL`;任一登录、验证码、风控、订单 + 确认或支付边界立即停止,不调用模型覆盖。 +- 从当前 `AccessibilityNodeInfo` 树生成最多 200 个可见/语义/交互节点的扁平 JSON;字段 + 只含临时 `node_id`、parent、depth/path、裁剪文字、class/resource、可见/启用/点击/ + 选中/滚动状态和屏幕内 bounds。 +- 删除系统栏、零面积节点、无结构作用的重复容器和敏感文本;单文字最多 160 字符、深度 + 最多 12,不上传原始对象 `toString()`。 +- 节点 JSON 和 PNG 截图前后各复核一次页面身份,生成包含截图 hash 的 `snapshot_id`; + 页面变化则丢弃,不调用或不执行旧结果。 + +### 5. 定位输出和单步点击 + +定位请求按颜色、尺码分别调用,模型只允许返回严格 JSON: + +```json +{ + "schema_version": 1, + "decision": "TARGET_FOUND", + "group": "COLOR", + "expected": "BLACK", + "observed_option": "雅黑", + "target_node_id": "n42", + "evidence_node_ids": ["n18", "n42"], + "confidence": 0.96, + "reason_code": "UNIQUE_SEMANTIC_MATCH" +} +``` + +- `decision` 仅允许 `TARGET_FOUND/ALREADY_SELECTED/NOT_FOUND/AMBIGUOUS/UNSAFE`; + confidence 初始阈值为 `0.92`。 +- `TARGET_FOUND` 必须唯一引用当前 snapshot 中可见、enabled、具有选项语义且自身或祖先 + 可点击的节点;不得返回坐标、多步计划、确认、购买、下单或支付动作。 +- 网络返回后重新取树,按节点 path + 局部指纹重新定位;目标变化、多个命中或安全条件 + 变化时丢弃。通过后只执行一次 `ACTION_CLICK`,并按现有节奏等待 1 至 3 秒。 + +### 6. 点击后与最终组合复核 + +- 颜色和尺码严格串行:颜色动作后重新观察,再处理尺码;任何动作不得由模型连续规划。 +- 规则点击成功但节点未确认时先记为待复核,不盲目重复;VLM 定位点击同理。 +- 两个步骤结束后必须在同一最终快照上确认颜色和尺码**同时**选中。节点规则已同时确认时 + 不调用模型;否则调用一次只读 VLM,输出仅允许 + `BOTH_SELECTED/NOT_BOTH_SELECTED/AMBIGUOUS/UNSAFE`,阈值 `0.95`。 +- VLM 证明以独立 `selection_proof` 保存,包含 group、冻结目标、页面 observed option、 + method、confidence、snapshot/screenshot hash 和 prompt/schema 版本;不得伪造节点 + `selected=true`。 +- 只有确定性双确认,或有效视觉证明同时覆盖颜色和尺码,候选才满足 SKU 硬约束;否则 + 关闭弹层、跳过候选。不得因模型失败终止整轮或重复同一动作。 + +### 7. 调用预算、缓存和审计 + +- 同一 candidate + group + snapshot + use case 最多调用一次;每候选最多 2 次定位加 1 次 + 最终复核,共 3 次;同一响应不做 HTTP 自动重试或重定向。 +- 单次调用沿用结构化客户端的有界 timeout;超时、网络错误、schema 错误、低置信度和 + prompt injection 文案均按不可用处理。 +- `CandidateSearchDiagnostics` 追加 VLM 调用、接受、拒绝和双规格复核计数,经既有事件 + outbox 回传 Admin;普通日志只写枚举和计数,不写节点 JSON、截图 Base64、endpoint、 + Key、任务标题/SKU或供应商原始正文。 +- 审计证明保存在候选规格证据与本地进度快照中,重启恢复不能把 VLM 证明降级为伪造的 + 确定性选中。 + +## 方案 + +1. 扩展无障碍元素的有界祖先点击/选中聚合,并为规格节点建立临时 path/fingerprint。 +2. 增加受限规格快照、canonical hash、严格 JSON 输入输出、prompt 和 provider-neutral + `PinduoduoSpecificationVlmGateway`。 +3. Android adapter 复用 Roubao 现有 `VLMClient` 与本地 provider 设置,校验/缩放规格 + PNG 后只调用 `predictStructuredOnce()`。 +4. 把 candidate 规格选择重构为规则快速路径 + 可空 VLM fallback;最终证明显式进入 + `PinduoduoSpecificationEvidence` 和硬约束策略。 +5. 追加调用预算和诊断计数,并更新进度存储的向后兼容 JSON。 +6. 本任务只在候选发现链路启用;订单 dry-run 继续使用原确定性选择,待真机样本达到验收 + 后另行复用,避免直接扩大不可逆动作前的模型权限。 + +## 验收要点 + +- [x] 祖先可点、祖先 selected/checked 的颜色/尺码节点能由规则路径正确选择,不调用VLM。 +- [x] 规则唯一成功时模型调用为0;`Absent/Ambiguous/click failed/post-click unknown` 才触发。 +- [x] 节点 JSON 受限、脱敏、可复现,和截图绑定同一 snapshot hash。 +- [x] 模型只能选择 snapshot 中唯一 node ID;页面变化、旧节点、坐标和多步输出不能点击。 +- [x] 颜色、尺码串行单击,最终同快照未同时确认时不保存为 EXACT_MATCH 候选。 +- [x] disabled、完整分组缺失、安全阻塞和 `MANUAL_FIRST` 不能被 VLM 改判。 +- [x] 每候选不超过3次调用,相同 snapshot/use case 不重复,网络失败不形成点击循环。 +- [x] API Key、endpoint、完整节点/响应、标题/SKU和截图 Base64不进入日志或outbox。 +- [x] Debug/Release 单测、lint 和两种 APK 构建通过。 +- [ ] 真机复跑 `da6be4b5-ed92-4813-b6d9-ae88d4995be6` 的同类任务,至少固定颜色缺失、 + 尺码缺失各一个样本,Admin 诊断与最终规格截图一致,支付动作次数为0。 + +## 边界 + +- 不新增 Admin VLM 代理、provider 配置、API Key 字段、Go API 或数据库 migration。 +- 不允许纯视觉坐标 grounding;无可重新定位节点时跳过候选。 +- 不改变第三轮 fallback、候选数量、结果滚动、三轮预算、goods_id 或候选去重。 +- 不把 VLM 规格兜底用于订单确认、提交或支付;不放宽 T-217/T-218 单次提交围栏。 +- 不关闭 T-265/T-268/T-269/T-270/T-271 的独立真机验收项。 + +## 执行记录 + +- 2026-08-01:根据真机任务颜色/尺码偶发未勾选现象和两份 Obsidian 方案创建任务;确认 + 模型使用 Roubao 本地配置,Admin 不代理推理。状态 `TODO`,尚未修改生产代码。 +- 2026-08-01:领取任务,状态改为 `DOING`,开始 Android 实现。 +- 2026-08-01:完成 Android 实现。规格解析器现在使用四层内祖先 clickable 与 + selected/checked;自动领取按本机安全配置固定 `AI_ASSISTED` 或 `MANUAL_FIRST`。 + 候选发现增加最多 200 节点、深度 12、文字 160 的脱敏快照和裁剪 PNG,截图前后 + 复核结构 hash;定位响应只接受严格 node ID,网络返回后重新按 path + fingerprint + 定位并单击。颜色、尺码串行处理,最终视觉证明独立保存且绑定实际规格截图 hash。 +- 2026-08-01:`CandidateSearchDiagnostics` 追加 `specVlmCalls`/`specVlmAccepted`/ + `specVlmRejected`/`specVlmVerified` 四个非秘密聚合键;进度 JSON 和本地候选 manifest + 向后兼容保存 proof。Admin、Go API、数据库和第三轮 fallback 未改动。 +- 2026-08-01:Debug/Release 各 442 项 JVM 单测、`lintDebug`、`assembleDebug`、 + `assembleRelease` 全部通过。真机颜色缺失/尺码缺失样本和 Admin 诊断、截图一致性仍待 + 人工验收,因此 status 保持 `DOING`。