fix(server): 规格匹配一次失败即放弃,AI 非确定性导致本该匹上的值永久留空 #295

Open
opened 2026-09-16 14:56:12 +08:00 by ila · 1 comment
Owner

来源:2026-09-16 验收 #293 时实测发现。用户原话:「260818PAXYEV7V的44109310287颜色尺码匹配部分,不是全部」。

问题

规格匹配一个值只调一次 AI,失败即放弃。而 AI 对同一输入会给出不同答案,于是本该匹上的值因为一次抽风就永久留空。

证据

同一个请求(SYB 明细 2619,紫色 / S),连续调用:

第1次  failed          「AI 未找到可靠的 PDD 规格」
第2次  auto_confirmed  「颜色与尺码均可从可选规格中唯一匹配到对应原始标签」

虾皮 1528 全量匹配连续三轮:

第1轮 confirmed=4 pending=0 failed=12 skipped=7
第2轮 confirmed=0 pending=0 failed=12 skipped=11
第3轮 confirmed=0 pending=0 failed=12 skipped=11

第 1 轮还能捡回 4 个,说明前一轮的失败里有相当比例是抽风而非真失败。稳定下来的 12 个是真失败(PDD 没有该颜色,或 白色-冰块猫 / 白色-空白版 有歧义,AI 拒绝硬凑——这是正确行为)。

最终仍有 黑色 与 3XL 未映射,而两者在 PDD 8514 里都是唯一对应(黑色-冰块猫 有 9 个在售组合,3XL 建议126-135斤 唯一),属于第一次调用碰上抽风、之后再没机会重试。

AI 配置:deepseek-v4-flash,连通性诊断 connected: true,非服务不可用。

方案

  1. AI 返回「无匹配」或调用出错时重试一次。[必须] 限制重试次数,避免把「PDD 确实没有该颜色」「候选有歧义」这类真失败也反复问——它们每次都会失败,重试只是浪费调用和时间。
  2. [必须] 区分「AI 传输/服务错误」与「AI 明确回答无匹配」。当前 #294 的日志把两者都记成 failed,排查时分不开;前者值得重试,后者重试一次没结果就该认定。

非目标

  • 不改任何匹配判据,不放宽候选校验与可售 SKU 校验。
  • 不引入置信度门槛(#200 已取消)。
  • 不自动创建采购。
  • 不更换 AI 服务商,不改模型配置。

验收

  • 首次「无匹配」的值会被重试,唯一对应的值(如 黑色 → 黑色-冰块猫)最终能匹上。
  • PDD 确实没有的颜色和有歧义的 白色 仍然匹不上,且不会被反复重试。
  • 日志能区分传输错误与明确无匹配。
  • 重试次数有上限,单次匹配的总调用量可预期。

验证

go test ./app/goauto/purchase/... ./app/goauto/task/...;本地以虾皮 1528 复验 黑色 与 3XL。

文档影响

匹配行为的重试语义变化,需更新对应 Wiki 业务规则页面。

> 来源:2026-09-16 验收 #293 时实测发现。用户原话:「260818PAXYEV7V的44109310287颜色尺码匹配部分,不是全部」。 ## 问题 规格匹配一个值只调一次 AI,失败即放弃。而 AI 对同一输入会给出不同答案,于是本该匹上的值因为一次抽风就永久留空。 ## 证据 同一个请求(SYB 明细 2619,紫色 / S),连续调用: ``` 第1次 failed 「AI 未找到可靠的 PDD 规格」 第2次 auto_confirmed 「颜色与尺码均可从可选规格中唯一匹配到对应原始标签」 ``` 虾皮 1528 全量匹配连续三轮: ``` 第1轮 confirmed=4 pending=0 failed=12 skipped=7 第2轮 confirmed=0 pending=0 failed=12 skipped=11 第3轮 confirmed=0 pending=0 failed=12 skipped=11 ``` 第 1 轮还能捡回 4 个,说明前一轮的失败里有相当比例是抽风而非真失败。稳定下来的 12 个是真失败(PDD 没有该颜色,或 `白色-冰块猫` / `白色-空白版` 有歧义,AI 拒绝硬凑——这是正确行为)。 最终仍有 `黑色` 与 `3XL` 未映射,而两者在 PDD 8514 里都是唯一对应(`黑色-冰块猫` 有 9 个在售组合,`3XL 建议126-135斤` 唯一),属于第一次调用碰上抽风、之后再没机会重试。 AI 配置:`deepseek-v4-flash`,连通性诊断 `connected: true`,非服务不可用。 ## 方案 1. AI 返回「无匹配」或调用出错时重试一次。`[必须]` 限制重试次数,避免把「PDD 确实没有该颜色」「候选有歧义」这类真失败也反复问——它们每次都会失败,重试只是浪费调用和时间。 2. `[必须]` 区分「AI 传输/服务错误」与「AI 明确回答无匹配」。当前 #294 的日志把两者都记成 `failed`,排查时分不开;前者值得重试,后者重试一次没结果就该认定。 ## 非目标 - 不改任何匹配判据,不放宽候选校验与可售 SKU 校验。 - 不引入置信度门槛(#200 已取消)。 - 不自动创建采购。 - 不更换 AI 服务商,不改模型配置。 ## 验收 - [ ] 首次「无匹配」的值会被重试,唯一对应的值(如 `黑色 → 黑色-冰块猫`)最终能匹上。 - [ ] PDD 确实没有的颜色和有歧义的 `白色` 仍然匹不上,且不会被反复重试。 - [ ] 日志能区分传输错误与明确无匹配。 - [ ] 重试次数有上限,单次匹配的总调用量可预期。 ## 验证 `go test ./app/goauto/purchase/... ./app/goauto/task/...`;本地以虾皮 1528 复验 `黑色` 与 `3XL`。 ## 文档影响 匹配行为的重试语义变化,需更新对应 Wiki 业务规则页面。
Author
Owner

实施

提交 08d77bd,新增 app/goauto/purchase/spec_match_retry.go。

resolveSpecMatch 重试一次(specMatchAttempts = 2),并返回 specMatchNoMatch / specMatchUnavailable 供调用方区分。BatchSpecMatch 与 MatchArchiveSpecs 两条路径都接上;MatchArchiveSpecs 的结果按两类分开计数,日志由 failed=N 改为 no_match=N unavailable=N。

[必须] 重试上限定为 2 次。真失败每次都会失败——实测虾皮 1528 连续三轮全量匹配,失败数稳定在 12(PDD 确实没有那些颜色,白色-冰块猫 / 白色-空白版 有歧义),再重试只是浪费调用。

已验证

新增 3 个用例:首次抽风重试后成功且只调 2 次;真无匹配不超过上限;传输错误不被当成无匹配。task / purchase / shopeeproduct / aimatching 全绿;sybimport 仍为 12 条失败,与 #285 基线一致。

未验证

未在本地真实跑一次图搜后的匹配,观察 黑色 是否因重试而匹上。

遗留

虾皮商品详情页「一键匹配颜色和尺码」走的是 shopeeproduct.suggestMappings,是第三条路径,本次未接入重试。实测该按钮对 黑色 返回「AI 未给出可靠建议」,同样是抽风。是否一并接入需另行确认。

状态

待验收。

## 实施 提交 `08d77bd`,新增 `app/goauto/purchase/spec_match_retry.go`。 `resolveSpecMatch` 重试一次(`specMatchAttempts = 2`),并返回 `specMatchNoMatch` / `specMatchUnavailable` 供调用方区分。`BatchSpecMatch` 与 `MatchArchiveSpecs` 两条路径都接上;`MatchArchiveSpecs` 的结果按两类分开计数,日志由 `failed=N` 改为 `no_match=N unavailable=N`。 `[必须]` 重试上限定为 2 次。真失败每次都会失败——实测虾皮 1528 连续三轮全量匹配,失败数稳定在 12(PDD 确实没有那些颜色,`白色-冰块猫` / `白色-空白版` 有歧义),再重试只是浪费调用。 ## 已验证 新增 3 个用例:首次抽风重试后成功且只调 2 次;真无匹配不超过上限;传输错误不被当成无匹配。`task` / `purchase` / `shopeeproduct` / `aimatching` 全绿;`sybimport` 仍为 12 条失败,与 #285 基线一致。 ## 未验证 未在本地真实跑一次图搜后的匹配,观察 `黑色` 是否因重试而匹上。 ## 遗留 虾皮商品详情页「一键匹配颜色和尺码」走的是 `shopeeproduct.suggestMappings`,是第三条路径,本次未接入重试。实测该按钮对 `黑色` 返回「AI 未给出可靠建议」,同样是抽风。是否一并接入需另行确认。 ## 状态 待验收。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#295