Files
slogan/styleagent/agent/output.go
T
adminandClaude Opus 4.7 ef22f7672a feat: slogan-agent MVP 服务端完整实现
- 用户域:注册/登录(JWT)/修改密码/个人资料
- 照片/衣橱/身形/化身:上传存储 + 3D 化身模板匹配
- 穿搭生成:天气(高德+和风+缓存) → 规则预筛 → LLM 规划(1次调用)
  → 规则评分(5维100分制) → 全低分触发 LLM 兜底创作 → 异步任务状态机
- 效果图:选主方案后异步生成 3 视角(mock/wanx 供应商 + 内容 hash 缓存 + 每日限额)
- 商业化:合作门店列表(seed 4 家)
- 冒烟:全链路端到端验证通过(mock LLM/天气),23 个 API 端点

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-31 12:15:11 +08:00

72 lines
2.0 KiB
Go
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.
package agent
import (
"encoding/json"
"fmt"
"strings"
)
// PlanOutput 大模型输出的穿搭方案集合
type PlanOutput struct {
Plans []PlanCandidate `json:"plans"`
}
// PlanCandidate 一套穿搭方案
type PlanCandidate struct {
Title string `json:"title"`
Hairstyle string `json:"hairstyle"` // 发型名称(匹配资产库)
HairColor string `json:"hair_color"` // 如 #A0522D
Items []PlanItemOut `json:"items"`
}
// PlanItemOut 方案内一件单品
type PlanItemOut struct {
Slot string `json:"slot"` // 上衣/下装/鞋/配饰
ItemId int64 `json:"item_id,omitempty"` // 衣橱条目(wardrobe 来源)
Name string `json:"name"` // 单品名
Desc string `json:"desc"` // 搭配说明
NewItem bool `json:"new_item"` // 是否为推荐新服装
}
// ParsePlanOutput 解析并校验 LLM 输出(去除 markdown 代码围栏后 json.Unmarshal
func ParsePlanOutput(raw string) (*PlanOutput, error) {
text := strings.TrimSpace(raw)
// 容忍 ```json ... ``` 代码围栏
if strings.HasPrefix(text, "```") {
text = strings.TrimPrefix(text, "```")
if idx := strings.Index(text, "\n"); idx >= 0 {
text = text[idx+1:]
}
text = strings.TrimSuffix(strings.TrimSpace(text), "```")
}
var out PlanOutput
if err := json.Unmarshal([]byte(text), &out); err != nil {
return nil, fmt.Errorf("方案 JSON 解析失败: %w", err)
}
if len(out.Plans) == 0 {
return nil, fmt.Errorf("方案输出为空(plans 缺失)")
}
for i, p := range out.Plans {
if strings.TrimSpace(p.Title) == "" {
return nil, fmt.Errorf("方案 %d 缺少 title", i+1)
}
if len(p.Items) == 0 {
return nil, fmt.Errorf("方案 %d 缺少 items", i+1)
}
for _, it := range p.Items {
if !isValidSlot(it.Slot) {
return nil, fmt.Errorf("方案 %d 含非法 slot: %s", i+1, it.Slot)
}
}
}
return &out, nil
}
func isValidSlot(slot string) bool {
switch slot {
case "上衣", "下装", "鞋", "配饰":
return true
}
return false
}