diff --git a/T-281.-.md b/T-281.-.md new file mode 100644 index 0000000..ad689ca --- /dev/null +++ b/T-281.-.md @@ -0,0 +1,98 @@ + +> 同步来源:[`docs/tasks/T-281.md`](/chengma/mroubao/src/commit/afc651f75a3abc2676bb13aa8a80f6aa6a25a72e/docs/tasks/T-281.md) · commit `afc651f75a3a` + +--- +id: T-281 +title: 修复首轮规格 AI 兜底并记录可诊断证据 +phase: 2 +deps: + - T-276 + - T-277 + - T-278 +status: DONE +created: 2026-08-03 +context_ref: 5a6a2b8 +work_branch: null +write_paths: + - docs/tasks/T-281.md + - docs/current-state.md + - docs/04-architecture.md + - docs/05-coding-rules.md + - android-buyer/app/src/main/** + - android-buyer/app/src/test/** +--- + +## 问题 / 背景 + +任务 `55e61d13-63ef-4673-b8dd-959c1d220339` 的第一轮浏览了 4 个候选,规格模型 +调用 3 次但 `accepted=0`、`verified=0`;其中 2 次被合并记为拒绝,另 1 次在整轮 +200 秒超时时没有留下终态。规则侧读取到 6 条价格文本,但全部被归为 `pricePrefix`, +`priceParsed=0`。第一轮没有形成候选后,第二轮按 T-276 契约关闭规格目标和模型,最终 +回传的两个保底候选自然都是空 `sku_text`、空 `price_text`。 + +当前实现还存在三个可复现缺口:规则点击返回成功但复读没有选中时仍强制结束该规格; +完整规格组判定目标缺失时不经过模型;无价格上限任务仍因价格为空消耗最终模型调用。 +模型网关失败、合同不符、低置信度、节点无效、页面过期和点击失败又都折叠为一个拒绝 +计数,现有“记录”模块无法说明具体失败原因。 + +## 行为契约 + +1. 第一轮继续先走确定性规则。规则点击后复读未选中、目标语义缺失或歧义时,必须在 + 当前规格弹层和同一候选内调用本机规格模型兜底;AI 只能返回当前节点 ID,App 复核 + 可见、启用、可点击及快照仍有效后才能单步点击。 +2. 确定性规则确认目标规格不可用时保持不可购买,不允许 AI 改选另一个相似规格;登录、 + 验证码、风控、订单确认和支付边界保持原安全停止优先级。 +3. 每次模型调用使用独立有界超时,并在取消时保留 `TIMEOUT/CANCELLED` 诊断;整轮超时 + 仍必须停止当前候选,不得延长到离线执行授权之外。 +4. 任务未设置 `max_budget_cents` 时,候选仍尽力读取页面价格,但价格为空或歧义不得单独 + 触发模型调用或阻止 SKU 匹配;有价格上限时保持组合价验证要求。 +5. 价格解析器允许一组固定的拼多多价格标签与唯一人民币金额组合,例如“拼单价¥19.9” + 和“券后价¥19.9”;价格区间、“起”、划线原价、多金额和未知标签继续拒绝。模型仍只能 + 指向原始价格节点,最终金额必须由本地解析器计算。 +6. App 私有 SQLite 按 execution 保存采购运行摘要、规则判定、模型阶段、稳定拒绝码、 + 节点快照摘要/hash、模型合同响应摘要、点击复核及候选接受/拒绝结果,并在“记录”模块 + 可查看。最多保留最近 50 个采购 execution,每个 execution 最多 200 个事件。 +7. 本地诊断不得保存截图/Base64、API Key、Bearer token、Cookie、收货信息、完整任务 + 描述或任意 HTTP header。无效模型原文只保存 SHA-256 和长度;合法响应仅保存 schema + 白名单字段。后台继续只接收有界聚合计数和稳定拒绝码分布,不上传节点 JSON。 + +## 方案 + +1. 为 `PinduoduoCandidateAutomation` 增加 `priceRequired` 和规格诊断观察器;修正规则点击 + 后复核失败及完整组误判缺失的 VLM 分支,同时保持 disabled fail-closed。 +2. 为规格 VLM 引入稳定拒绝原因、单次超时和结构化 trace;解析器逐项返回合同、置信度、 + 节点、证据、价格等拒绝原因,不再只返回无信息的 `Rejected`。 +3. 扩展人民币价格解析器,仅规范化固定标签,继续拒绝范围、后缀和多个金额。 +4. 新增采购运行 SQLite journal,并把 journal 记录映射为既有 `ExecutionRecord`,与普通 + Agent 记录合并展示、分别删除;候选搜索开始、规则/VLM 判定和终态均追加事件。 +5. 增加规则点击未生效后 AI 恢复、完整组语义误判后 AI 恢复、disabled 不点击、空价格 + 不消耗模型、固定价格标签解析、模型拒绝原因和 journal 保留策略测试。 + +## 验收要点 + +- [x] 规则点击返回成功但页面未选中时,AI 定位并点击目标后可通过双规格复核。 +- [x] 规则把完整规格组判断为目标缺失时允许 AI 语义映射;disabled 规格仍不会被点击。 +- [x] 空价格任务不会只因价格缺失调用模型;有预算任务仍要求可验证组合价。 +- [x] 固定标签单金额可解析,区间、“起”、未知前缀和多金额保持拒绝。 +- [x] 每次 AI 调用都有稳定终态或取消记录,记录模块可按采购 execution 查看规则、AI 和 + 候选摘要;敏感数据和截图不入库、不上传后台。 +- [x] Android Debug/Release 单测、lint 和 APK 构建通过。 + +## 边界 + +- 不修改第二轮只回传两个保底候选且不调用规格模型的 T-276 契约。 +- 不降低商品身份、人工确认、订单授权、提交围栏或支付边界。 +- 不把本机模型配置迁移到 Admin,不新增后端 VLM 代理。 +- 不在本任务执行真实下单或支付真机 smoke。 + +## 执行记录 + +- 2026-08-03:根据真机任务第一轮模型零接受、价格前缀零解析和本地证据不足问题创建 + 任务,状态 `TODO`。 +- 2026-08-03:修正规则点击复核、完整组别名误判和无预算价格分支;模型调用增加 20 秒 + 超时、稳定拒绝码及脱敏 trace,后台事件只增加聚合原因分布。 +- 2026-08-03:新增 App 私有 SQLite 采购 journal,并合并到“记录”页面;保留上限为 + 50 次 execution、每次 200 个事件。 +- 2026-08-03:通过 `:app:testDebugUnitTest`、`:app:testReleaseUnitTest`、 + `:app:compileDebugAndroidTestKotlin`、`:app:lintDebug`、`:app:assembleDebug` 和 + `:app:assembleRelease`。Android 仪器测试已编译,未在本任务中执行真实下单 smoke。