Files
zt-closet-backend/.codebuddy/plans/AI推荐搭配完整性保障_2b3f5eda.md
T

163 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: AI推荐搭配完整性保障
overview: 修复 AI 穿搭推荐可能只输出「外套 + 下装」等不完整搭配的问题:prompt 写死完整性规则,服务端新增完整性校验(缺失品类/冲突品类),不满足时带纠偏提示重试一次,仍不满足则由服务端从衣橱确定性补齐并标注「系统补齐」。
todos:
- id: tokenhub-completeness
content: 用 [skill:lsp-code-analysis] 核对引用点后,实现完整性判定/同品类去重/确定性自动补齐与纠偏提示,并强化提示词
status: completed
- id: tokenhub-tests
content: 新增 pkg/tokenhub 单测:规则判定、冲突剔除、补齐确定性与提示词拼接
status: completed
dependencies:
- tokenhub-completeness
- id: service-orchestration
content: 改造 airecommend.generate:带纠偏提示重试一次(含时间预算保护)并在仍不满足时服务端补齐,补充日志
status: completed
dependencies:
- tokenhub-completeness
- id: regression-verify
content: 跑通 go build、go vet 与全量 go test 回归,确认额度/失败语义与接口契约未变
status: completed
dependencies:
- tokenhub-tests
- service-orchestration
- id: sync-ai-docs
content: 同步接口文档 5.6 与 README:搭配完整性规则、重试与自动补齐说明
status: completed
dependencies:
- service-orchestration
---
## 产品概述
修复 AI 穿搭推荐结果不完整的问题:当前会生成「只有外套 + 裤子」这类缺少上装/内搭的搭配,用户拿到的是穿不出去的不完整方案。需要在提示词与服务端双重保障下,确保推荐结果是一套完整可穿的搭配。
## 核心功能
- **完整性硬规则(按衣橱实际存在的品类判定)**:
- 必须包含「上装」或「连衣裙」二者之一(衣橱存在其中任一时)
- 选择「外套」时不得孤立:必须同时搭配「上装」或「连衣裙」
- 未选择「连衣裙」且衣橱存在「下装」时,必须包含「下装」
- 衣橱存在「鞋」时必须包含「鞋」;「配饰」为可选点缀,不强制
- 选择「连衣裙」时不再搭配「下装」(二者互斥)
- **模型输出不满足时的处理**:先带「缺上装」等纠偏提示重试一次模型调用;仍不满足则由服务端从衣橱自动补齐缺失品类(补齐单品标注「系统补齐」)、并剔除冲突项,不判定任务失败、不浪费用户 AI 额度
- **同品类唯一**:同一品类最多 1 件(超出保留首件),与既有提示词规则一致
- **补齐确定性**:同输入必得同输出(同风格优先 → 主色一致优先 → 最近上传优先),便于线上排查与单测
- **体验保持**:接口出入参、额度计费、失败退额度语义、前端轮询方式均不变;用户最终看到的是完整搭配而非卡单或失败
## 技术栈
- Go + Gin + GORM(沿用现有分层:`pkg/tokenhub` 放提示词与纯校验/补齐函数,`internal/service` 负责编排、日志与落库),不新增依赖、不改数据库结构、不改 DTO 契约
- 测试:Go 标准测试(`pkg/tokenhub` 目前无测试文件,新增 `outfit_recommend_test.go`),纯函数可直接断言,无需 sqlmock/网络
## 实现方案
### 核心思路
在 `pkg/tokenhub` 补齐「完整性规则 + 纠偏提示 + 确定性自动补齐」三件纯函数能力,并把完整性要求写进提示词;在 `internal/service/airecommend.go` 的 `generate` 中编排为「调用 → 校验(含同品类去重)→ 不满足则带纠偏提示重试一次 → 仍不满足则服务端补齐」,形成提示词与服务端双重保障。
```mermaid
flowchart LR
A[调模型生成搭配] --> B[ValidateClothesIDs 过滤幻觉/纠品类]
B --> C[NormalizeCategories 同品类去重]
C --> D{完整性检查<br/>CompletenessIssues}
D -->|完整| G[落库待保存穿搭]
D -->|缺品类/冲突| E[带纠偏提示重试一次模型]
E --> F{再检查}
F -->|完整| G
F -->|仍不完整| H[ApplyAutoComplete<br/>从衣橱补齐缺失品类 + 剔除冲突]
H --> G
```
### 关键决策与权衡
1. **规则放在 `pkg/tokenhub` 而非 service**:完整性判定只依赖「推荐结果 + 衣橱列表」,是纯函数,放 tokenhub 可与提示词同源、便于单测;service 只做编排。
2. **重试只 1 次且带时间预算检查**:`aiRecommendRunTimeout` 维持 3 分钟不变(避免扩大前端等待 SLA、不必改文档中的轮询上限);调用前用 `ctx.Deadline()` 判断剩余预算,剩余不足(< 60s)时跳过模型重试、直接进入服务端补齐路径,避免因重试导致任务超时失败(客户端单次调用上限 120s)。
3. **自动补齐而非失败**(已与用户确认):补齐是纯内存操作、确定性排序,保证「同输入同输出」,并把补齐品类写进 `reason`(如「系统按规则补齐:原推荐缺少上装」),便于用户理解与线上排查。
4. **补齐优先级**:同风格(与已选单品风格众数一致)→ 主色与已选主色众数一致 → 视觉冲击力接近 → ID 最大(最近上传),全部为确定性排序键,不做随机。
5. **不扩权**:不改 `category_main` 分类逻辑、不改前端展示、不新增数据库列、不改额度计费;`RecommendLLMResponse`/`AIRecommendCreateResponse` 结构保持兼容。
### 性能与可靠性
- 最坏情况由 1 次模型调用变为 2 次(仅当首次结果不完整/无有效单品时触发),有预算检查兜底,任务总耗时不超过既有 3 分钟上限;补齐为 O(衣橱件数) 的单次遍历,可忽略。
- 失败路径完全复用既有 `finishFailed`(退额度 + 写失败流水),新增逻辑不改变错误码与额度语义。
- 提示词注入防护:纠偏提示仅由固定枚举品类名拼装(不含任何用户输入),现有 `sanitizePromptField` 逻辑不变。
## 架构设计
沿用现有分层与调用链,改动集中在两处:
```mermaid
flowchart TD
subgraph service
A[AIRecommendService.generate] --> B[tokenhub.Client.GenerateOutfitRecommendation]
A --> C[ValidateClothesIDs / NormalizeCategories]
A --> D[CompletenessIssues 判定]
A --> E[ApplyAutoComplete 补齐]
A --> F[落库 Outfit + OutfitItem + 流水]
end
subgraph pkg/tokenhub
B --> P[buildRecommendPrompt 含完整性规则与纠偏段]
D --> Q[完整性规则纯函数]
E --> Q
E --> R[确定性补齐排序]
end
```
## 目录结构
```
zt-closet-backend/
├── pkg/tokenhub/
│ ├── outfit_recommend.go # [MODIFY] 提示词补充完整性硬规则与「上次输出问题」纠偏段;GenerateOutfitRecommendation 增加 retryHint 参数;
│ │ # 新增 NormalizeCategories(同品类去重)、CompletenessIssues(缺失/冲突判定)、
│ │ # ApplyAutoComplete(确定性补齐+剔冲突)、BuildCorrectionHint 与严格选品提示常量
│ └── outfit_recommend_test.go # [NEW] 规则判定/冲突/去重/补齐确定性/提示词拼接单测
├── internal/service/
│ └── airecommend.go # [MODIFY] generate 编排:校验后不满足则带提示重试一次(含剩余时间预算检查),
│ # 仍不满足则服务端补齐;新增结构化日志;落库/额度/错误码语义不变
├── 接口文档.md # [MODIFY] 5.6 AI 推荐模块业务逻辑补充「搭配完整性保障(重试+服务端补齐)」,第 4 条输入说明与轮询说明保持一致
└── README.md # [MODIFY] 「### 6. AI 推荐」处理流程补充完整性规则与补齐说明
```
## 关键代码结构
```
// pkg/tokenhub/outfit_recommend.go
// GenerateOutfitRecommendation 新增 retryHint 参数(为空表示首次调用;非空时作为「上次输出问题」注入 prompt)。
func (c *Client) GenerateOutfitRecommendation(ctx context.Context, zodiac string, fortune *FortuneItem,
clothes []mysqlmodel.Clothes, scene, retryHint string) (*RecommendLLMResponse, error)
// NormalizeCategories 同一品类最多保留 1 件(保留首次出现),返回被移除的品类。
func (r *RecommendLLMResponse) NormalizeCategories() (removed []string)
// CompletenessIssues 按衣橱实际存在的品类判定,返回缺失品类与冲突品类;两者皆空表示搭配完整。
// 规则:内层(上装或连衣裙)必选;外套不可孤立;未选连衣裙时下装必需;衣橱有鞋则鞋必选;配饰可选。
func (r *RecommendLLMResponse) CompletenessIssues(clothes []mysqlmodel.Clothes) (missing []string, conflicts []string)
// ApplyAutoComplete 确定性补齐缺失品类并剔除冲突项(如连衣裙+下装),返回新增与移除的品类。
func (r *RecommendLLMResponse) ApplyAutoComplete(clothes []mysqlmodel.Clothes) (added []string, removed []string)
// BuildCorrectionHint 生成纠偏提示文本(仅由固定品类枚举拼装,不含用户输入);无问题时返回空串。
func BuildCorrectionHint(missing, conflicts []string) string
// CorrectionHintStrictSelection 首次输出无任何有效单品时的兜底纠偏提示(严格从衣橱选品)。
const CorrectionHintStrictSelection = "..."
```
## 实现要点
- 判定口径:衣橱品类取自 `clothes[].CategoryMain` 且仅统计 6 类(`outfitCategories`);衣橱不含「上装/连衣裙」时不强制内层;衣橱不含「下装/鞋」时不强制对应品类(避免"强制补齐不存在品类"导致空结果)。
- 补齐实现:候选 = 该品类中未被选中的衣物;排序键依次为「风格==已选风格众数」「主色==已选主色众数」「视觉冲击力接近已选众数」「ID 降序」;补齐项 `Type/ClothesUUID` 取衣橱真实值,`Reason` 标注系统补齐;补齐后再跑一次 `CompletenessIssues` 校验(防御性),并限制最多补齐 3 件(上装/连衣裙、下装、鞋)。
- 重试编排:仅当「无有效单品」或「不完整」时重试一次;重试结果需再次 `ValidateClothesIDs`,且仅在 `len(Items) > 0` 时替换首次结果(防止重试反而变空);重试失败(网络/超时)仅告警,继续走原有逻辑与补齐。
- 日志:沿用 `slog.*Context`,重试打 Info(含 `user_id/task_id/missing/conflicts/remaining_budget`),补齐打 Warn(含 `added_categories/removed_categories`),不打印衣橱明细与用户隐私字段。
- 兼容性:`GenerateOutfitRecommendation` 全项目仅 1 处调用(`internal/service/airecommend.go`),签名调整安全;`ValidateClothesIDs` 行为不变,新增方法不破坏既有契约;`ResultContent` 流水落库的是补齐后的最终结果,天然可审计。
- 边界(不做):不改提示词中的运势/场景逻辑与 JSON Schema 结构;不改衣物分类;不改前端轮询与展示;不新增数据库列;重试不额外扣 AI 额度。
## Agent Extensions
### Skill
- **lsp-code-analysis**
- Purpose: 在调整 `GenerateOutfitRecommendation` 签名与新增 `RecommendLLMResponse` 方法前,用语义分析确认全部引用点(调用方、字段读取、既有测试),并核对 `NormalizeCategories/CompletenessIssues/ApplyAutoComplete/BuildCorrectionHint` 等新命名在 `pkg/tokenhub` 内无冲突
- Expected outcome: 输出完整引用清单与受影响文件列表,确保签名调整无遗漏、无编译期破坏,新增导出符号命名唯一