Files
cmautobuy/docs/task/255-admin运行时规格规则ai解析.md
T
2026-08-17 17:21:43 +08:00

7.1 KiB

255 Admin:实现采购运行时真机规格规则与 AI 解析

  • 类型:需求
  • 父级大工单:#96
  • 所属 MVP / 版本:#253 采购运行时 AI 规格纠偏闭环
  • 状态:已完成,待用户验收
  • 日期:2026-08-17
  • Gitea 工单:#255

背景与目标

Client 在真实采购时能够读取 PDD App 当前颜色下的真实可购买尺码,但数据库里的规格可能 来自蝦皮回填,和真机文字不同。原有 MatchSybSpecWithAI 只读取数据库候选,不能保证 返回真机可点击的规格。

本任务实现 #254 已冻结的一次性规格解析命令:Admin 核对任务和真机候选快照,优先用 确定性规则寻找唯一等价候选,规则无法唯一判断时再调用受控 AI,并把第一次结论幂等、 可审计地返回 Client。解析不得修改任务、PDD 主数据或真实采购安全门禁。

最终方案

  • 新增 POST /api/v1/client/tasks/{task_id}/spec-resolution。Handler 限制请求体为 64 KiB, 读取原始字节用于请求哈希,并把 Service 错误转换为 Client 契约定义的稳定错误码。
  • Service 逐项校验 schema、字段长度、1~100 条连续候选、候选原文和 color/size,再按 Client 契约的 UTF-8 长度前缀算法重算候选快照哈希和确定性幂等键。
  • 在写候选观察前核对任务存在、类型为采购、版本、PDD 商品、领取时的完整动态规格, 并确认当前 Client 曾领取该任务。任务已取消、重派或结束不作为拒绝条件。
  • 规则忽略大小写、空白和安全的展示分隔符,处理常见繁简差异,支持 kg / 公斤 / 千克 / 斤 单值和正向区间等价。只有一个等价候选时返回 matched;多个 等价或多个重量区间同时重叠时直接 uncertain,不调用模型。加减号和范围符号保留, 避免把 M+ / M- 或不同重量范围折叠成同一规格。
  • 抽出 SYB 批量 AI 和运行时 AI 共用的候选白名单核心,统一执行运行快照、严格响应结构、 候选编号、冲突/缺失维度和置信度阈值门禁。运行时模型只得到任务目标和请求内短候选, 不能生成规格、真实 PDD 选项键或点击坐标。
  • 使用“短事务写 pending 候选观察 → 事务外规则/AI → 短事务完成一次决策并保存幂等响应”。 同一进程内相同幂等身份串行;数据库唯一键和只允许 pending 完成一次的条件更新负责 最终收敛。中断留下的 pending 由相同请求接管,不另建记录。
  • pdd_products.skus_json.spec_source=shopee_backfill 只追加到非敏感决策原因作为诊断, 不主动触发 AI,也不阻断本来可以由规则唯一确定的候选。沿用 v26 表,不新增字段或迁移。
  • 最终决策只写 purchase_spec_resolutions 和 idempotency_keys,另刷新 Client 活动时间; 不更新任务状态、版本、payload、订单总价上限、结果数据或 PDD skus_json。
  • 不记录完整模型请求/响应、API Key、原始控件树、截图、订单、收货信息、Cookie 或 Token。 模型错误中的当前密钥原文会在进入审计原因前替换为 [REDACTED]。

和建单方案相比,最终实现进一步收紧了两个边界:多个重量候选同时重叠时不交给 AI 猜; +/-/范围 符号不作为普通展示符号删除。由于 #254 的 v26 审计模型没有独立诊断来源列, shopee_backfill 使用决策原因留痕,没有为诊断信息增加数据库迁移。

改了哪些

  • admin/handler/api/client_api.go
    • 注册一次性规格解析路由,增加请求大小限制和稳定 HTTP 错误映射。
  • admin/handler/api/client_api_test.go
    • 验证超过 64 KiB 的请求在进入数据库前返回 INVALID_BODY。
  • admin/main.go
    • 把既有 AI 密钥存储和端点策略注入 Client API。
  • admin/repository/task.go
    • 任务身份查询补充版本和原始 PDD 动态规格,供运行时核对。
  • admin/service/ai_specmatch_client.go
    • 增加 SYB 与运行时共用的模型候选白名单、阈值和冲突门禁核心。
  • admin/service/ai_specmatch.go
    • 原 SYB AI 编排改用共用核心,保留原有人工映射优先和保存前复查流程。
  • admin/service/purchase_spec_resolution.go
    • 实现请求/哈希校验、任务授权、规则、AI、诊断、并发门禁、短事务和幂等响应。
  • admin/service/purchase_spec_resolution_test.go
    • 覆盖规则边界、任务越权、AI 门禁、密钥脱敏、并发重放、主数据不变和诊断来源。
  • docs/admin/01-requirements.md
    • 记录采购运行时真机规格解析的产品和安全边界。
  • docs/admin/02-architecture.md
    • 记录 Client API、共用 AI 核心和两阶段短事务编排。

验收结果

验收标准 结果
未领取、非采购、版本、商品或任务规格不一致时拒绝且不写解析记录 通过
完全等价、繁简等价和唯一公斤/斤单值及区间由规则匹配,不调用模型 通过
多个等价、多个区间重叠或模型报告信息缺失时不自动选择 通过
AI 只能选择当前候选;伪造编号、低置信度、冲突和错误不能返回 matched 通过
同一幂等请求重试和并发得到一致响应、一个最终记录和一次有效模型调用 通过(隔离 SQLite 与 race 测试)
shopee_backfill 只记录诊断来源,不触发或拒绝解析 通过
不修改任务状态、payload、价格上限、结果数据和 PDD 主规格 通过
日志、数据库和响应不保存 API Key、原始 XML、订单或个人数据 通过
原 SYB AI 共用白名单核心后保持人工优先和原门禁 通过(全量 Go 回归;MySQL 场景见未验证项)
Go 1.23.0 格式、build、test 和 vet 通过
MySQL 8.4 _test 服务编排集成测试 未验证

测试

  • 执行的命令:
    • $env:GOTOOLCHAIN='go1.23.0'; gofmt -l .
    • $env:GOTOOLCHAIN='go1.23.0'; go test ./... -count=1
    • $env:GOTOOLCHAIN='go1.23.0'; go vet ./...
    • $env:GOTOOLCHAIN='go1.23.0'; go build ./...
    • $env:GOTOOLCHAIN='go1.23.0'; go test -race ./service -run 'Test(MatchPurchaseSpec|ParsePurchaseSpec|ResolvePurchaseSpec)' -count=1
  • 结果:Go 1.23.0 全量测试、vet、构建和本任务 race 测试通过。运行时服务编排测试默认在 临时隔离 SQLite 数据库执行;设置 CMAUTOBUY_MYSQL_TEST=1 时会自动改用项目的隔离 MySQL 8 _test 数据库。
  • 没验证到的部分:本机 MySQL 8.4 服务没有当前可用的授权测试账号,因此未执行 CMAUTOBUY_MYSQL_TEST=1 的 #255 服务集成用例;没有用生产库替代测试库。依赖工单 #254 已验证 v26 MySQL 8.4 表、约束、唯一键和 Repository。本工单也未做 Client 真机联调, Client 的采集和续跑属于后续 #256~#258。

遗留问题

  • 配置可创建临时数据库的本地 MySQL 8 _test 账号后,应补跑: $env:CMAUTOBUY_MYSQL_TEST='1'; go test ./service -run 'TestResolvePurchaseSpec' -count=1 -v。

相关提交

  • de36cfd 实现采购运行时规格规则与 AI 解析