表名、配额种子和字段名以产品方案与架构为准;文档仓库补 .gitignore,避免 git add -A 把 API Key 推进远端。 Co-authored-by: Cursor <cursoragent@cursor.com>
86 KiB
「美搭」技术架构文档
版本:v1.4 | 更新日期:2026-08-14 | 状态:待技术评审
本文的定位:把产品需求翻译成可直接拆解为开发任务的技术方案。读完这份文档,应该能回答三个问题——系统由哪些部分组成、数据长什么样、每条关键链路怎么跑通。
前置阅读:《技术选型与调研报告》(所有选型结论与理由,本文只引用编号
TD-xx,不重复论证) 配套阅读:《需求规格说明》(需求编号FR-xx/NFR-xx)、《合规与安全方案》、《开发任务拆解》
1. 架构目标与约束
1.1 这个系统真正难的地方
在动手设计之前先明确难点,否则很容易把它当成一个「CRUD + 调几个 AI 接口」的普通应用,那会在三个月后付出代价:
- 它是一个「长耗时任务为主」的系统,不是请求-响应系统。报告生成、换装出图都在数十秒量级。这意味着任务中心、状态机、幂等、进度反馈、失败退款、断线恢复不是附加功能,而是主干链路。
- AI 输出必须被约束,而不是被信任。报告结论与搭配建议要可解释、可审计、不能违反禁忌规则。因此架构上必须有「检索 → 规则约束 → 生成 → 生成后校验」的闭环,而不是把用户问题直接丢给大模型。
- 它处理的是敏感个人信息(人脸),且产出的全部是 AI 生成内容。合规不是上线前补的文档,而是架构约束:原图不落盘、生成物统一打标识、输入输出双向审核。
- 成本随活跃度线性增长。GPU 出图与大模型调用都是按量付费。如果没有从第一天就把成本埋进链路并可归因到功能和用户,用户量上来后无法优化,只能凭感觉砍功能。
- 知识库是持续演进的资产,它的更新频率远高于代码。所以规则、色板、单品必须是数据而不是代码,且有配套的运营入口与回归测试。
1.2 质量属性优先级
按项目当前阶段(V1 单端、效果优先、团队 4~6 人)排序:
| 优先级 | 质量属性 | 具体要求 |
|---|---|---|
| 1 | 正确性与可解释性 | 建议不违反硬性禁忌;每条结论可追溯到规则或知识条目 |
| 2 | 合规性 | 人脸处理、AI 标识、内容审核、备案登记均满足现行要求 |
| 3 | 可演进性 | 域边界清晰,V2 拆服务/换模型/多端时不需要重写 |
| 4 | 可观测性 | 业务漏斗、AI 成本、任务健康三张看板齐备 |
| 5 | 性能 | 见 NFR 指标;报告与出图允许秒级到分钟级,但等待体验必须做好 |
| 6 | 极致成本优化 | V1 只要求「可归因、可预警」,不要求「最省」 |
1.3 架构约束(不可协商)
| 约束 | 来源 |
|---|---|
| 客户端为鸿蒙原生 ArkTS,服务端保持端无关 | TD-01 |
| 服务端全 Python(FastAPI) | 项目决策 |
| 基础设施以腾讯云为主,服务器部署在中国大陆 | 项目决策 + TD-15 手机号获取要求 |
| 人脸原图不进入持久化存储 | TD-20 |
| 所有对外生成物必须经过统一合规出口 | TD-21 |
| V1 不接入任何支付能力,配额为唯一成本控制手段 | TD-28 |
| V2 起虚拟商品支付必须走华为 IAP | TD-16 |
| V1 不上微服务、不上 K8s、不上专用向量库与图数据库 | TD-08/TD-09,团队规模 |
2. 系统上下文
┌──────────────────────┐
│ 鸿蒙客户端 (ArkTS) │
└──────────┬───────────┘
│ HTTPS / SSE
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ 美搭服务端 (Python / 腾讯云) │
│ │
│ API 层 ──▶ 业务域模块 ──▶ AI 能力层 ──▶ 数据层 │
│ │ │
│ └──▶ 任务队列 ──▶ Worker │
└───┬──────────┬──────────┬──────────┬──────────┬──────────┬───────────────┘
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐┌──────────────┐
│华为服务 ││大模型 ││腾讯云AI ││自有换装 ││腾讯云 ││腾讯云 ││腾讯云基础设施 │
│账号/推送││DeepSeek││人脸识别 ││发型服务 ││天御审核 ││短信SMS ││COS/CDN/PG/ │
│Push ││智谱GLM ││ ││(GPU) ││ ││ ││Redis/CLS │
└────────┘└────────┘└────────┘└────────┘└────────┘└────────┘└──────────────┘
注:V1 不接入天气 API(TD-33),Agent 的天气类问题走「反问用户 + 城市月份推断气候区」降级
外部依赖及其失效影响(每一个都要有降级方案,见 §13.4):
| 外部系统 | 用途 | 失效影响 | 降级策略 |
|---|---|---|---|
| 华为 Account Kit | 登录(通道一) | 该通道不可用 | 手机号验证码登录仍可用(两条通道不得互相依赖);游客模式可继续体验 |
| 腾讯云短信 SMS | 登录(通道二,TD-32) |
该通道不可用 | 华为账号登录仍可用;登录页给出明确提示而非笼统报错 |
| 华为 Push Kit | 任务完成通知 | 通知不到 | 应用内任务中心轮询兜底 |
| DeepSeek / GLM | 文案生成、对话 | 报告与对话不可用 | 双供应商自动切换;均失败时报告排队重试,对话返回兜底话术 |
| 腾讯云人脸识别 | 面部分析 | 报告无法生成面部维度 | 报告降级为「体型+色彩」子集,标注待补全 |
| 自有换装服务 | 出图 | 换装不可用 | 任务转排队,不扣配额,超时退还 |
| 腾讯云天御 | 内容审核 | 不可降级 | 审核失败一律按「拦截」处理(fail-close),宁可少发不可漏审 |
| 华为 IAP | 支付(V2 起) | 无法购买 | 提示稍后再试,不影响已有权益。V1 不依赖此项 |
🔑 登录必须有两条互不依赖的通道,这是 V1 唯一的单点故障防护。华为账号是唯一通道时,华为侧授权异常会让用户直接进不来产品;而一键登录已推至 V2(
TD-29),所以手机号验证码是 V1 的必需项,不是可选项。
3. 总体架构
3.1 分层视图
┌───────────────────────────────────────────────────────────────────────┐
│ L1 客户端层 │
│ 鸿蒙 ArkTS:页面 / 状态管理 / 端侧缓存 / 图片压缩 / SSE流式渲染 │
└───────────────────────────────────────────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────────┐
│ L2 接入层 │
│ CLB + Nginx:TLS / 限流 / 防刷 / 请求日志 / 静态与图片走 CDN │
└───────────────────────────────────────────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────────┐
│ L3 API 层(FastAPI,面向端的编排层) │
│ 认证鉴权 · 参数校验 · 幂等 · 配额校验与预扣 · 统一响应 · OpenAPI │
└───────────────────────────────────────────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────────┐
│ L4 业务域层(模块化单体:包内隔离,禁止跨域直接访问对方数据表) │
│ ┌────────┬────────┬────────┬────────┬────────┬────────┬────────┐ │
│ │identity│profile │ report │ agent │ tryon │ kb │billing │ │
│ │账号会话│画像记忆│美学报告│对话Agent│换装发型│ 知识库 │配额计量│ │
│ ├────────┼────────┼────────┼────────┼────────┴────────┴────────┤ │
│ │ task │wardrobe│compliance│ notify │ │ │
│ │任务中心│ 衣橱V2 │合规出口 │ 通知 │ │ │
│ └────────┴────────┴─────────┴────────┴────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────────┐
│ L5 AI 能力层(可独立部署与扩容,对上以内部 API 暴露) │
│ ┌──────────────┬──────────────┬──────────────┬──────────────┐ │
│ │ llm-gateway │ vision-svc │retrieval-svc │ rules-engine │ │
│ │多模型路由 │分割/关键点/ │混合检索+重排 │决策表求值+ │ │
│ │Prompt版本 │色值采样/体型 │ │可解释路径 │ │
│ ├──────────────┼──────────────┼──────────────┴──────────────┤ │
│ │ matting-svc │ tryon-svc │ │ │
│ │批量抠图 │换装/发型(GPU)│ │ │
│ └──────────────┴──────────────┴─────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────────┐
│ L6 数据层 │
│ PostgreSQL + pgvector(业务/知识库/向量/事件)· Redis(缓存/队列/限流) │
│ COS 四桶(临时/用户内容/知识素材/报告产物)· CLS(日志与审计) │
└───────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────┐
横向贯穿: │ Celery Workers(gpu/cpu/io/beat)│
│ 可观测性(OTel/Prom/Grafana) │
│ 配置与密钥(SSM/KMS) │
└──────────────────────────────┘
3.2 为什么是「模块化单体 + 独立 AI 服务」
这是本架构最关键的一个取舍,理由:
业务域做单体:identity/profile/report/agent/billing 之间事务关联紧密(一次报告生成要扣配额、写画像、写记忆、记成本)。拆成微服务后这些操作就需要分布式事务或最终一致性补偿,对 4~6 人团队是净损失。单进程内一个数据库事务就解决了。
AI 能力做独立服务:它们的资源画像与业务代码完全不同——GPU 依赖、模型文件体积、推理并发模型、扩缩容节奏都不一样。混在一个进程里会导致「换装排队把 API 拖慢」。独立部署后可以单独限流、单独扩容、单独重启。
但单体不等于混乱。硬性规则:
- 每个域是一个 Python 包,只能通过对方暴露的 service 接口调用,禁止直接查对方的表。
- 领域逻辑放在
packages/domain,不依赖 FastAPI、不依赖数据库会话,可纯单元测试。 - 跨域的写操作通过域内事件(进程内事件总线 + 持久化 outbox)解耦,例如「报告完成」事件被 profile(写画像)、memory(写记忆)、notify(推送)、billing(结算成本)分别消费。
- 数据库虽是单库,但表按域前缀命名,为将来拆库预留边界。
演进触发条件:当某个域的写入 QPS 或数据量成为瓶颈,或需要独立发布节奏时,按 §19 的路径拆出。不要提前拆。
4. 模块职责与依赖
4.1 业务域模块清单
| 模块 | 职责 | 关键对外能力 | 不负责 |
|---|---|---|---|
identity |
账号、登录、会话、设备、游客迁移、同意授权记录 | login() get_current_user() merge_guest() record_consent() |
权益判定(归 billing) |
profile |
用户画像(诊断结论)+ 分层记忆系统 | get_profile() get_memory_snapshot() write_memory() resolve_conflict() |
报告生成流程 |
report |
报告触发判定、生成编排、版本管理、分享 | can_trigger() create_report() get_report() create_share() |
具体的视觉分析与文案生成(调 AI 层) |
agent |
会话管理、意图识别、上下文组装、生成编排 | chat_stream() extract_preference() |
知识检索实现(调 retrieval) |
tryon |
换装/发型的业务编排、素材校验、结果管理 | create_tryon_task() get_result() |
GPU 推理(调 tryon-svc) |
kb |
知识实体/关系/规则/素材的读写与发布 | query_entities() get_rules() publish() |
检索算法(调 retrieval) |
billing |
V1:配额账本、流水、周期重置、任务奖励发放。V2 增:会员、订单、IAP 校验 | check_quota() reserve() commit() refund() grant() |
定价策略配置(配置化);V1 无支付相关职责 |
task |
统一任务中心:创建、状态机、进度、查询、取消 | submit() get_status() list_user_tasks() |
具体任务实现(Worker) |
compliance |
统一合规出口:审核、水印、元数据标识、内容编号登记、审计 | moderate() publish_asset() register_content() |
业务判断 |
notify |
推送、应用内消息、Token 管理 | push() notify_task_done() |
— |
wardrobe |
我的衣橱(V2) | — | — |
域与包不是一一对应。 上表按产品职责切(
profile含记忆);§16.1 的packages/按发布与依赖切。记忆有独立表、独立生命周期,且会被 agent / report 同时写入,因此代码上是独立的packages/memory,产品职责仍归 profile 域。跨域只许 importmemory的service,不许直接读pf_memory。
4.2 依赖规则
identity ← 所有模块(读当前用户)
billing ← report / tryon / agent(消耗资源前预扣)
task ← report / tryon(长耗时任务统一走它)
compliance ← report / tryon / agent / kb(所有产物出口)
profile ← report(写入)/ agent(读取+写入)
kb ← agent / report(读取)
禁止的依赖:compliance 不得反向依赖任何业务域(它必须能被任何模块安全调用);identity 不得依赖其他业务域(避免登录链路被业务拖累)。
4.3 AI 能力层服务
| 服务 | 形态 | 内容 | 资源 |
|---|---|---|---|
llm-gateway |
进程内包(V1)→ 独立服务(V2) | 多供应商路由、Prompt 版本、结构化输出约束、流式、缓存前缀优化、成本计量、限流降级 | IO 密集 |
vision-svc |
独立服务 | 人像分割(云)、人体关键点(ONNX 自建)、色值采样、体型测算、人脸属性与关键点(云) | CPU(含 ONNX) |
retrieval-svc |
进程内包 | 混合检索:硬过滤 → 规则命中 → 向量召回 → BM25 → 重排 → 解释路径 | CPU + PG |
rules-engine |
进程内包 | 决策表求值、硬约束校验、命中链输出 | CPU |
matting-svc |
离线批处理 | BiRefNet 抠图 + 白底标准化 + 规格校验 | GPU(按需) |
tryon-svc |
独立服务(现有服务改造) | 换装三工作流、发型更换/发色 | GPU(常驻或按需) |
llm-gateway 为何 V1 先做进程内包:它没有独立的资源诉求(纯 IO),拆成服务只增加一跳网络。但它的接口必须按远程调用来设计(无共享内存假设、明确超时、可序列化的入参出参),这样 V2 拆出去时零成本。
4.4 现有换装服务的改造要求
既然服务自有可控(TD-00 前提),必须做以下改造,否则它会成为整个系统的不稳定源:
| 改造项 | 现状问题 | 要求 |
|---|---|---|
| 鉴权 | 可能是开放接口 | 改为内部服务,仅允许内网/网关调用,带服务间令牌 |
| 调用模式 | 同步长连接 | 改为提交任务 + 回调通知(或提供任务查询接口),避免长连接占用 |
| 幂等 | 无 | 接受 idempotency_key,重复提交返回同一结果 |
| 并发控制 | 无 | 明确最大并发数并对外暴露,让上游队列按容量限流 |
| 成本埋点 | 无 | 每次推理返回 gpu_seconds 与模型版本,供成本归因 |
| 结果存储 | 直传阿里云 OSS | 改为写入腾讯云 COS(统一存储与生命周期治理);历史数据做迁移或双读 |
| 输入校验 | 未知 | 前置校验图片规格、人脸存在性、单品白底图规格,尽早失败 |
| 版本管理 | 未知 | 模型/工作流版本号随结果返回,便于回溯效果变化 |
5. 技术栈总览
| 层次 | 选型 | 决策依据 |
|---|---|---|
| 客户端 | HarmonyOS ArkTS + ArkUI | TD-01 |
| 端侧能力 | Account Kit / Push Kit(V1);IAP Kit / Core Speech Kit(V2) | TD-15~TD-17、TD-14、TD-28 |
| 接入 | 腾讯云 CLB + Nginx + CDN | TD-03 |
| 服务端 | Python 3.12 + FastAPI + Uvicorn/Gunicorn | TD-02 |
| ORM/迁移 | SQLAlchemy 2.0 (async) + Alembic | TD-02 |
| 校验 | Pydantic v2(同时用于 LLM 结构化输出约束) | TD-02 |
| 任务 | Celery + Redis(gpu/cpu/io/beat 四队列) | TD-11 |
| 数据库 | PostgreSQL 16 + pgvector | TD-08 |
| 缓存 | Redis 7 | — |
| 存储 | 腾讯云 COS(4 桶)+ CDN | TD-03 |
| 大模型 | DeepSeek(主)+ 智谱 GLM(备/视觉) | TD-04 |
| Embedding | BGE-M3(自建 ONNX) | TD-22 |
| 视觉 | 腾讯云人脸识别 + RTMPose(ONNX) + BiRefNet | TD-05~TD-07 |
| 审核 | 腾讯云天御 IMS/TMS | TD-13 |
| 渲染 | Playwright + Chromium(服务端长图/PDF) | TD-12 |
| 可观测 | OpenTelemetry + Prometheus + Grafana + CLS | TD-24 |
| 部署 | Docker + Docker Compose(V1)→ TKE(V2) | TD-03 |
| CI/CD | GitHub Actions 或腾讯云 CODING;ruff + mypy + pytest 卡关 | TD-02 |
6. 数据模型
6.1 设计原则
- 单库多域,表名以域前缀区分(
id_/pf_/rp_/kb_/ag_/tr_/bl_/tk_/cp_)。 - 主键统一用雪花 ID(bigint),不用自增(避免枚举攻击与将来分库冲突),不用 UUID 作主键(索引局部性差)。对外暴露的 ID 做混淆编码。
- 不存人脸原图,不存可反推生物特征的数据。诊断结果只存分类标签与色值等派生量(
TD-20)。 - 所有涉及配额/金额的表都有幂等键与流水,任何余额变更必须有对应流水记录,余额可由流水重算校验。V1 虽不涉及金钱,此原则同等适用 —— 用户对"次数被白扣"的敏感度不比扣钱低。
- 软删除 + 硬删除双轨:常规软删除;账号注销走硬删除任务(合规要求,见《合规与安全方案》)。
- JSONB 用于「结构会演进但不需要索引全部字段」的场景(诊断结论、规则条件、检索轨迹);需要过滤的字段一律提升为列。
6.2 核心表清单
identity 域
| 表 | 关键字段 | 说明 |
|---|---|---|
id_user |
user_id PK, status('active'/'frozen'/'deleting'/'deleted'), nickname, avatar_key, register_source, created_at, last_active_at, deleted_at |
内部主键,与外部身份解耦。手机号不存本表,见下行 |
id_user_identity |
user_id, provider('huawei'/'phone'), union_id, open_id, phone_enc(AES 加密), phone_hash(检索用), UNIQUE(provider,union_id) |
一个用户可有多个外部身份。手机号作为一种 provider 存在本表,不在 id_user 冗余 —— 它是登录凭证而非用户属性,且便于统一加密与脱敏(NFR-SEC-03)。检索用 phone_hash,展示用解密后掩码 |
id_guest |
guest_id, device_fp, merged_to_user_id, created_at |
游客数据,登录后合并 |
id_device |
device_id, user_id, push_token, os_version, last_seen_at |
推送 Token 管理 |
id_consent |
consent_id, user_id, consent_type, granted bool, doc_version, granted_at, revoked_at, client_ip, ua |
合规留证:人脸的单独同意必须独立成行。枚举见下表 |
id_session |
session_id, user_id, refresh_token_hash, expire_at, revoked |
刷新令牌服务端可撤销 |
profile 域(画像与记忆)
| 表 | 关键字段 | 说明 |
|---|---|---|
pf_profile |
profile_id, user_id, label, is_default |
预留多档案(帮家人诊断的场景),V1 只创建默认档案 |
pf_diagnosis |
diagnosis_id, profile_id, source_report_id, dimensions JSONB, version, overall_confidence, created_at |
32 个诊断维度的结构化结论(字典见美学报告 §4,为唯一事实源;编号 33/34 已随 TD-31 移除且不再复用);按版本追加,不覆盖 |
pf_memory |
memory_id, user_id, layer(L0~L5), mem_key, mem_value JSONB, confidence numeric, status, source, evidence_ref, valid_from, last_reinforced_at, decay_after, created_at |
记忆项主表;索引 (user_id,layer,status)。status 与 source 的枚举取值以《用户系统与记忆系统-需求文档》§3.3 为唯一事实源,见下表 |
pf_memory_event |
event_id, memory_id, action('create'/'reinforce'/'decay'/'conflict'/'user_edit'/'delete'), detail JSONB, created_at |
记忆变更审计,支撑「我的记忆」页面的可解释性 |
pf_memory_snapshot |
user_id, snapshot JSONB, token_count, content_hash, built_at, expire_at |
注入 LLM 的画像摘要缓存,避免每轮重算 |
枚举取值(本轮对齐,此前架构与用户系统文档给了两套不兼容的枚举)
⚠️ 原架构表写
status=archived/rejected、source=explicit/implicit/report/behavior,与用户系统 §3.3 的枚举无法对应。以用户系统文档为准,因为它的取值承载了衰减与冲突消解的语义(decayed/overridden必须可区分,否则 §3.6 的冲突消解无法实现)。
| 字段 | 取值 | 语义要点 |
|---|---|---|
pf_memory.status |
active / pending_confirm / decayed / overridden / deleted |
decayed(置信度衰减至 < 0.3,可恢复)与 overridden(被新证据覆盖)必须区分,前者可由用户恢复,后者不可 |
pf_memory.source |
explicit_user_input / user_setting / report_vision / self_reported / behavior_inferred / llm_extracted |
衰减规则只作用于 behavior_inferred 与 llm_extracted(用户系统 §3.5),因此不能合并为笼统的 implicit |
id_consent.consent_type |
privacy(隐私政策)/ face(人脸,须独立成行)/ personalization(个性化推荐)/ push(推送通知)/ minor(未成年人确认) |
⚠️ personalization 不可省略:用户系统 §3.10 要求记忆写入必须建立在该项授权之上,缺了它记忆系统就没有合规依据 |
report 域
| 表 | 关键字段 | 说明 |
|---|---|---|
rp_report |
report_id, user_id, profile_id, tier('basic'/'full'), status, version, result JSONB, model_info JSONB, content_code, rendered_asset_key, cost_cents, created_at, completed_at |
content_code 为 AI 标识内容编号(TD-21) |
rp_input |
input_id, report_id, questionnaire JSONB, photo_meta JSONB, derived_features JSONB |
只存派生特征与图片元信息,不存原图 |
rp_share |
share_id, report_id, share_token, expire_at, view_count, revoked |
分享链接可失效、可撤回 |
kb 域(知识库)
| 表 | 关键字段 | 说明 |
|---|---|---|
kb_entity |
entity_id, entity_type(12 类), name, attrs JSONB, status('draft'/'reviewing'/'published'/'archived'), version, source, license, updated_by |
12 类实体统一表 + attrs 差异化 |
kb_relation |
from_id, rel_type, to_id, weight, confidence, evidence |
邻接表,递归 CTE 做多跳(TD-09) |
kb_rule |
rule_id, version, status, priority, conditions JSONB, effects JSONB, is_hard, evidence, author, reviewer, published_at |
决策表(TD-10) |
kb_chunk |
chunk_id, entity_id, chunk_type, text, embedding vector(1024), embedding_model, embedding_version, tsv tsvector |
向量 + 全文双索引,支撑混合检索 |
kb_asset |
asset_id, entity_id, asset_type, cos_key, spec_check JSONB, tryon_ready bool, review_status, license |
tryon_ready 是换装闭环的关键标志位 |
kb_color |
color_id, season_code, role('base'/'accent'/'avoid'), lab_l,lab_a,lab_b, hex, name_cn |
自建色板(TD-18) |
kb_gap |
gap_id, query_text, filter_ctx JSONB, hit_count, first_seen_at |
知识缺口埋点:无检索结果的查询,驱动内容生产 |
agent 域
| 表 | 关键字段 | 说明 |
|---|---|---|
ag_conversation |
conv_id, user_id, title, rolling_summary, status, msg_count, created_at |
长会话滚动摘要存这里(L4 记忆) |
ag_message |
msg_id, conv_id, role, content, intent, retrieval_trace JSONB, rule_hits JSONB, model, tokens_in/out/cached, cost_cents, moderation JSONB, created_at |
retrieval_trace + rule_hits 让每个回答可复盘 |
ag_feedback |
feedback_id, msg_id, rating, reason, created_at |
驱动隐式偏好推断与质量评估 |
tryon 域
| 表 | 关键字段 | 说明 |
|---|---|---|
tr_task |
task_id, user_id, workflow_type, input_refs JSONB, result_asset_key, gpu_seconds, model_version, content_code, cost_cents |
与统一任务中心 tk_task 一对一关联 |
tr_favorite |
fav_id, user_id, asset_key, tags, created_at |
用户收藏的效果图 |
task 域(统一任务中心)
| 表 | 关键字段 | 说明 |
|---|---|---|
tk_task |
task_id, user_id, task_type('report'/'tryon'/'hairstyle'/'matting'/'render'), status, progress, stage_text, idempotency_key UNIQUE, payload JSONB, error_code, retry_count, quota_reserved, submitted_at, started_at, finished_at, eta_seconds |
全系统唯一的长耗时任务视图,前端任务中心直接读它。⚠️ 字段名由 points_reserved 改为 quota_reserved —— V1 是配额不是点数,用 points 命名会在实现期误导 |
billing 域(V1 为配额与计量,无支付)
V1 不接支付(
TD-28)。本域在 V1 的职责是配额限制与计量。关键设计:表结构一次做到位,V2 上支付时不需要迁移。
| 表 | 关键字段 | V1 | 说明 |
|---|---|---|---|
bl_quota |
user_id, quota_key, period_key, used, limit_value, bonus_granted, updated_at;UNIQUE(user_id,quota_key,period_key) |
✅ | V1 扣减判定的唯一依据。周期配额计量(日/月/终身),按 period_key 自然重置,不要写定时清零任务。limit_value 是本周期创建时从规则拷下的 base_limit 快照(规则中途被改,本周期仍按快照判定);bonus_granted 物化本周期已获任务奖励,避免每次判定都聚合流水 |
bl_quota_rule |
quota_key, period_type, base_limit, task_bonus_max, hard_cap |
✅ | 配额规则配置化(FR-BILL-04),改配置不发版。数值与 quota_key 映射见下表,一律引用产品方案 §3.2,禁止在种子里另写一套 |
bl_ledger |
tx_id, user_id, delta, tx_type, status, reason, ref_type, ref_id, balance_after, idempotency_key UNIQUE, created_at |
✅ | 流水是真相,但不参与 V1 的判定(判定看 bl_quota)。tx_type V1 用 system_grant/consume/refund 三种,V2 增加 purchase/gift —— 这是 V1→V2 免迁移的关键 |
bl_wallet |
user_id PK, balance, version(乐观锁) |
✅ | ⚠️ V1 仅用于「跨周期不清零」的额度(如首次终身报告);周期类额度一律走 bl_quota。V2 语义扩展为点数余额,同一张表,不改结构 |
bl_quota_rule.quota_key 与产品方案 §3.2 的对应(种子必须与此表一致):
quota_key |
对应 §3.2 行 | period_type |
base / task_bonus_max / hard_cap |
|---|---|---|---|
agent_chat |
AI 对话 | day | 10 / 10 / 40 |
tryon |
换装试穿 | day | 3 / 3 / 8 |
hairstyle |
发型 / 发色试戴 | day | 3 / 2 / 6 |
hd_export |
高清导出 | day | 1 / 0 / 3 |
report_basic |
初级美学报告(首次) | lifetime | 1 / 0 / 1 |
report_regen |
报告重新生成 | month | 1 / 1 / 2 |
「每日 ≤ 2」在月额度上限为 2 时自然满足,不必再拆一条日配额。记忆条数 200 是存储上限,不是消耗型配额,不进本表。 游客额度按设备维度计量,依赖 id_guest;游客合并链路未建之前不写入 guest_* key,避免种子里出现没人读的第二套口径。
🔑 四张表的职责边界见 §7.4 开头。这是最容易实现成"两套并行扣减逻辑"的地方,务必先读那一段。 |
bl_membership|user_id,level,valid_from,valid_to,source_order_id,auto_renew| ❌ V2 | 会员 | |bl_order|order_id,user_id,product_id,product_type,price_cents,platform('huawei_iap'),purchase_tokenUNIQUE,status,verified_at,raw_receiptJSONB | ❌ V2 |purchase_token唯一约束防重复发货(TD-16) |
原
bl_point_tx改名为bl_ledger:V1 阶段它记录的是配额流水而非点数流水,用point命名会在 V1 造成误解。改用中性名称,同时新增tx_type字段承载两个阶段的语义差异。
compliance 域
| 表 | 关键字段 | 说明 |
|---|---|---|
cp_content_registry |
content_code PK(27 位), content_type, ref_type, ref_id, provider_code, model_info JSONB, generated_at |
AI 生成内容编号登记(TD-21) |
cp_moderation_log |
log_id, target_type, target_id, provider, suggestion, labels JSONB, action, created_at |
审核留证 |
cp_audit_log |
audit_id, actor_type, actor_id, action, target, before JSONB, after JSONB, ip, created_at |
后台操作审计,知识库改动必须留痕 |
cp_data_request |
request_id, user_id, type('export'/'delete'), status, completed_at |
个人信息导出/删除请求(法定权利) |
观测与成本
| 表 | 关键字段 | 说明 |
|---|---|---|
ob_ai_call |
call_id, user_id, scene, provider, model, prompt_id, prompt_version, tokens_in/out/cached, tokens_reasoning, latency_ms, cost_cents, rate_tier, success, error_code, ref_type, ref_id, created_at |
成本归因的唯一数据源,按月分区。prompt_id/prompt_version 使每次调用可复现(§8.1 要求 Prompt 版本化,缺这两列就无法回答「上周的报告是哪版 Prompt 生成的」);tokens_reasoning 单独记思考模式 token(按输出价计费,混进 tokens_out 会掩盖开关思考模式的成本差异);rate_tier 记录该次调用适用的峰谷档位,否则事后无法复核 cost_cents。保留口径:明细至少保留到 V2 定价完成(T-P6-16);满 12 个月的分区可迁冷存储,不可按常规日志周期删除 —— 删了就失去定价依据 |
ob_event |
event_id, user_id, event_name, props JSONB, client_ts, server_ts |
埋点事件,按月分区(TD-23) |
6.3 诊断维度的存储形态
32 个诊断维度(字典见《美学报告-需求文档》§4,为唯一事实源)用 pf_diagnosis.dimensions JSONB 存储,但每个维度都是结构化三元组,不是自由文本:
{
"color_season": {
"value": "浅暖柔和型",
"confidence": 0.82,
"evidence": ["skin_lab:[68.2,12.4,18.7]", "hair_lab:[24.1,6.2,9.8]", "contrast:low"],
"source": "vision+rule",
"rule_hits": ["R-COLOR-0021", "R-COLOR-0034"]
},
"body_shape": {
"value": "X型",
"confidence": 0.76,
"evidence": ["shoulder_hip_ratio:1.02", "waist_hip_ratio:0.71"],
"source": "vision+rule",
"rule_hits": ["R-BODY-0007"]
}
}
为什么必须带 confidence 和 evidence:低置信度维度要在报告中降级表述(「你的体型偏向 X 型」而不是断言),并且要能在用户质疑时说明依据。这也是记忆系统冲突消解的输入——用户显式反馈会覆盖低置信度的机器推断。
7. 关键链路设计
7.1 美学报告生成(最复杂的链路)
客户端 API task/Worker AI 能力层
│ │ │ │
│ 1.POST /reports │ │ │
│ (idempotency_key) │ │ │
├───────────────────────▶│ │ │
│ │ 2.触发资格校验 │ │
│ │ (已有报告?配额?) │ │
│ │ 3.配额预扣(reserve) │ │
│ │ 4.创建 tk_task │ │
│◀── task_id ────────────┤ │ │
│ │ 5.入队(cpu队列) │ │
│ ├───────────────────▶│ │
│ 6.订阅进度(轮询/Push) │ │ 7.拉取临时区原图 │
│ │ ├───────────────────▶│
│ │ │ 8.内容审核(图片) │
│ │ │ 9.人像分割 │
│ │ │ 10.人体关键点(ONNX) │
│ │ │ 11.人脸关键点+属性 │
│ │ │ 12.色值采样(Lab) │
│ │ │ 13.体型/季型/脸型测算│
│ │ │◀── 结构化诊断 ──────┤
│ │ │ 14.**立即删除原图** │
│ │ │ 15.规则引擎求值 │
│ │ │ (建议/禁忌/依据) │
│ │ │ 16.知识库检索 │
│ │ │ (配色/单品/发型) │
│ │ │ 17.LLM 生成文案 │
│ │ │ (结构化输出约束) │
│ │ │ 18.生成后校验 │
│ │ │ (禁忌违反=0?色值在板内?)│
│ │ │ 19.文本审核 │
│ │ │ 20.渲染长图(Playwright)│
│ │ │ 21.合规出口: │
│ │ │ 水印+元数据+编号登记│
│ │ │ 22.写画像/记忆/成本 │
│ │ │ 23.配额 commit │
│ │ │ 24.Push 通知 │
│◀──── 报告就绪 ─────────┴────────────────────┤ │
关键工程要求:
| 要求 | 做法 |
|---|---|
| 原图生命周期 | 上传直传 COS 临时桶(预签名 URL,不经过应用服务器);Worker 拉取后处理,第 14 步显式删除;桶级 2 小时生命周期作为兜底 |
| 失败即退款 | 任何步骤失败,tk_task 转 failed 并触发 refund();用户看到明确原因与「重试」入口 |
| 分步可恢复 | 第 7~13 步的视觉分析结果落库为中间态,LLM 或渲染失败时不需要重跑视觉分析(也就不需要用户重新上传) |
| 生成后校验不可跳过 | 校验失败最多重试 2 次(提高约束强度),仍失败则降级为「规则直出模板文案」,绝不放行违规内容 |
| 等待体验 | 阶段文案随进度更新;预估时间基于历史 p50;超过阈值转为「后台生成 + 完成推送」,允许用户离开页面 |
| 幂等 | 相同 idempotency_key 重复提交返回同一 task_id,不重复扣减配额 |
7.2 Agent 对话(含检索与画像注入)
用户提问
│
▼
① 输入审核(TMS + 备案关键词库)───拦截──▶ 返回安全提示,记录日志
│ 通过
▼
② 意图识别(轻量模型 / 规则前置)
├─ 场景咨询 / 单品搭配 / 风格建议 / 闲聊 / 越界(医疗、身材评判等)
▼
③ 上下文组装(token 预算严格分配)
├─ 系统提示词(固定前缀,放最前以命中缓存)
├─ 画像摘要(L0/L1/L2,来自 pf_memory_snapshot)
├─ 会话滚动摘要(L4)
└─ 检索结果(见 ④)
▼
④ 混合检索(retrieval-svc)
├─ a. 硬过滤:SQL WHERE(季型/体型禁忌/场合/季节)
├─ b. 规则命中:rules-engine 求值,取建议与禁忌
├─ c. 向量召回:pgvector 相似度 Top-K
├─ d. 关键词召回:tsvector BM25
├─ e. 融合重排:RRF + 规则权重 + 新鲜度
└─ f. 输出:知识片段 + 命中规则 ID + 图谱路径(供解释链)
▼
⑤ LLM 生成(llm-gateway,流式 SSE)
└─ 约束:只能使用检索到的知识;硬禁忌不得出现;输出结构化搭配卡片
▼
⑥ 生成后校验:禁忌违反检查 + 色值在色板内检查
▼
⑦ 输出审核(TMS)───拦截──▶ 重新生成或返回兜底
▼
⑧ 落库:message + retrieval_trace + rule_hits + 成本
▼
⑨ 异步:偏好抽取 → 写入 L2 记忆(显式)/ 生成待确认候选(隐式)
Token 预算分配(必须硬性限制,否则成本会失控)
🔒 本表是 token 预算的唯一定义位置。
FR-MEM-04、FR-AGT-03、产品方案 §2.4.2、用户系统 §3.8、穿搭 Agent §3.5.2/§10.2、T-P3-17一律引用本表,不得复述数值。⚠️ 此前存在两套互相矛盾的口径,本轮已查明原因并修正。原架构表写「会话摘要 ≤ 400、总输入 ≤ 3700」,用户系统 §3.8 写「会话上下文 ≤ 800、记忆注入合计 ≤ 1400」。错在架构表:用户系统的 800 是「最近 6 轮原文 + 更早轮次的滚动摘要」,而架构表只算了滚动摘要、漏掉了近轮原文。因此以用户系统 §3.8 的分项为准,并修正总输入合计。
| 层级 | 部分 | 预算 | 说明 |
|---|---|---|---|
| 总输入 | 系统提示词 | ≤ 800 | 固定不变,放最前以最大化缓存命中 |
| 总输入 | 记忆注入(小计) | ≤ 1400 | 展开见下四行;这是 FR-MEM-04 的验收口径 |
| └ 记忆注入 | 画像摘要 L0/L1 | ≤ 250 | 模板化压缩,非 LLM 生成 |
| └ 记忆注入 | 偏好记忆 L2 | ≤ 150 | confidence ≥ 0.5 按置信度取 Top 15,硬禁忌优先保留 |
| └ 记忆注入 | 事件记忆 L3(向量召回) | ≤ 200 | V2 能力,V1 预算预留但不使用 |
| └ 记忆注入 | 会话上下文 L4 | ≤ 800 | = 最近 6 轮原文 ≤ 400 + 更早轮次滚动摘要 ≤ 400 |
| 总输入 | 检索知识 | ≤ 1500 | 重排后取前 N 条,超出则截断而非全塞 |
| 总输入 | 用户当前输入 | ≤ 500 | 超长提示精简 |
| 总输入合计 | ≤ 4200 | FR-AGT-03 的验收口径;原 3700 因漏算近轮原文而偏低 |
|
| 输出 | ≤ 800 | 超长回答不利于阅读,也贵 |
V1 实际用量:L3 不启用,故记忆注入 ≤ 1200、总输入 ≤ 4000,留有余量。
裁剪优先级(预算超限时按此顺序丢弃,不得报错):事件记忆 L3 → 滚动摘要尾部 → 检索知识低分项 → 偏好记忆低置信项 → 近轮原文最早的几轮。硬禁忌与安全约束永不裁剪 —— 裁掉它们会导致推荐用户明确拒绝的品类,属 P0 缺陷(产品方案 §8.3.3 要求禁忌违反率为 0)。
缓存前缀纪律:系统提示词与稳定知识片段必须放在 prompt 头部且不含时间戳、随机 ID、用户昵称等变量,否则缓存永远不命中,输入成本翻数倍(TD-04)。
7.3 换装任务
① 前置校验:单品是否 tryon_ready?用户照片是否合规且质量达标?
② 配额:check_quota → reserve
③ 创建 tk_task(type=tryon) → 入 gpu 队列
④ Worker:调 tryon-svc(带 idempotency_key),拿到 gpu_seconds
⑤ 结果图审核(IMS)
⑥ 合规出口:水印「AI 生成」+ 写入元数据与内容编号 → 存 COS 用户内容桶
⑦ commit 配额、记录成本、Push 通知
⑧ 失败:区分可重试(网络/排队超时)与不可重试(输入非法/审核拒绝),后者立即退还配额
队列即容量:gpu 队列的并发上限直接由 GPU 容量决定,并把「当前排队位次 + 预估等待」返回给用户。不要让请求无限积压——超过阈值直接拒绝新任务并提示稍后再试,这比让用户等 10 分钟体验更好。
7.4 配额消耗(V1)与 IAP 支付(V2)
V1:四张表的职责边界(必须先看这段,否则会实现成两套并行的扣减逻辑)
billing 域有四张表,此前文档没写清它们的关系,导致 bl_quota.used 与 bl_wallet.balance 看起来像两套重复机制。实际分工是:
| 表 | 在 V1 的角色 | 是否参与 V1 的扣减判定 |
|---|---|---|
bl_quota_rule |
规则:每个 quota_key 的周期类型、基础额度、任务奖励上限、防刷硬上限 |
提供判定用的阈值,配置化,改配置不发版 |
bl_quota |
周期计数器:(user_id, quota_key, period_key) 上的 used 与本周期已获得的 bonus_granted |
✅ V1 的判定就在这张表上 |
bl_ledger |
流水(真相):每一次发放、消耗、退还都留痕,供对账与 V2 定价分析 | ❌ 不参与判定,但每次判定通过后必须写 |
bl_wallet |
余额缓存:V1 语义为「跨周期不清零的可累积额度」 | ⚠️ V1 仅"首次终身"类额度用它(如首次报告),周期类额度不用 |
一句话概括:V1 的判定看 bl_quota(周期计数器),V2 的判定才看 bl_wallet(点数余额)。bl_ledger 在两个版本里都只是流水。 之所以 V1 就把 bl_wallet/bl_ledger 建起来,是为了 V2 上支付时不迁移数据(§6.2),不是因为 V1 需要它们做判定。
判定公式中 task_bonus 的来源已明确:不是从 bl_ledger 实时聚合(那样每次判定都要扫流水,且并发下不可靠),而是在 bl_quota 上物化为 bonus_granted 字段,发放任务奖励时同时递增它。判定公式因此是纯单行读:
有效上限 = MIN(
bl_quota_rule.base_limit + bl_quota.bonus_granted, -- 基础额度 + 本周期已获奖励
bl_quota_rule.hard_cap -- 防刷硬上限,任何情况不可超越
)
放行条件 = bl_quota.used < 有效上限
V1:配额消耗三段式
① 熔断检查:全局日成本水位是否已达 100%?(NFR-COST-02)
↓ 已熔断 → 直接拒绝,且【不得预扣】,返回"今日服务繁忙"
② 校验:SELECT ... FOR UPDATE 读 bl_quota 单行 → 按上方公式判定
↓ (服务端硬校验,客户端提示仅为体验,FR-BILL-06)
③ 预扣 reserve → bl_quota.used += N(同一事务内)
bl_ledger 写 delta=-N, tx_type=consume, status=reserved
↓
④ 任务执行(Celery,长耗时)
↓
成功:commit → 流水 status=committed(bl_quota.used 不变,已在③计入)
失败:refund → bl_quota.used -= N
bl_ledger 写反向流水 delta=+N, tx_type=refund(带 idempotency_key)
发放:任务奖励 → bl_quota.bonus_granted += N(受 task_bonus_max 约束)
bl_ledger 写 delta=+N, tx_type=system_grant
↓ (同一任务同一周期幂等,FR-BILL-03)
三处最容易写错的地方:
- 熔断必须在预扣之前(步骤①在③之前)。顺序颠倒会导致平台自身限流却扣掉用户额度 —— 这是
NFR-COST-02里「配额净扣减 = 0」那条验收标准要防的情况。 bl_quota.used的增减必须与bl_ledger写入在同一个数据库事务里,否则对账永远不平。- 周期重置靠
period_key自然切换,不要写定时清零任务。period_key形如2026-08-13(日)/2026-08(月)/lifetime(终身),新周期首次访问时插入新行即可。定时清零在跨时区、补偿执行、任务漏跑时都会出错。
为什么预扣而不是直接扣:长耗时任务可能失败,直接扣会产生「扣了额度没拿到结果」的客诉。先冻结再确认,失败自动释放。V1 虽然不涉及金钱,这个问题依然存在 —— 用户对"次数被白扣"的敏感度不比扣钱低,而且 V1 没有客服退款兜底手段,只能靠机制保证。
对账:所有变更都写流水,余额可由流水重算校验(每日定时,不一致则告警)。V1 就要做,否则 V2 上支付时对账逻辑得从零开发且无历史数据验证。
V2:IAP 支付(V1 不实现)
购买:客户端 IAP 下单 → 支付成功拿 purchase_token
↓
POST /orders/verify (purchase_token)
↓
服务端调华为 IAP 校验接口(**必须服务端校验**,不可信客户端结果)
↓
UNIQUE(purchase_token) 保证只发货一次
↓
发放点数/会员 → 写 bl_ledger(tx_type=purchase,带 idempotency_key)
V1→V2 的衔接成本被刻意压到最低:消耗侧逻辑(reserve/commit/refund)完全复用,只需新增「购买」这一条发放路径。这是 §6.2 中坚持让 V1 与 V2 共用 bl_wallet / bl_ledger 的收益。
7.5 统一合规出口
所有对外生成物必须经过 compliance.publish_asset(),没有例外。
async def publish_asset(
raw: bytes | str,
content_type: Literal["image", "text", "pdf"],
ref: ContentRef,
model_info: ModelInfo,
) -> PublishedAsset:
# 1. 内容审核(fail-close:审核服务不可用则拒绝发布)
# 2. 生成 27 位内容编号并登记 cp_content_registry
# 3. 显式标识:图片加可见「AI 生成」水印;文本加提示语
# 4. 隐式标识:写入文件元数据(属性信息 + 服务提供者编码 + 内容编号)
# 5. 上传目标 COS 桶,返回带签名的访问地址
必须专门测试的点:图片经过 CDN、缩略图处理、客户端保存与再次分享后,元数据是否仍然存在。很多图片处理链路会剥离 EXIF/XMP,一旦剥离就等于没做隐式标识(TD-21)。
8. AI 能力层设计
8.1 LLM 网关
class LLMGateway:
async def complete(
self,
scene: Scene, # 场景决定模型档位、超时、预算
messages: list[Message],
schema: type[BaseModel] | None, # 需要结构化输出时传 Pydantic 模型
stream: bool = False,
user_id: int | None = None,
) -> Completion | AsyncIterator[Chunk]:
...
内置能力:
| 能力 | 实现要点 |
|---|---|
| 场景化路由 | scene → (provider, model, temperature, max_tokens, timeout) 全部配置化,改模型不改代码 |
| 故障转移 | 主供应商超时/5xx/限流 → 自动切备用;连续失败触发熔断,冷却后半开探测 |
| 结构化输出 | 传入 Pydantic Schema,网关负责 JSON Schema 约束 + 解析失败重试 + 校验 |
| Prompt 版本 | Prompt 存文件(随代码版本化)+ 场景绑定版本号;A/B 时按用户哈希分流 |
| 成本计量 | 每次调用写 ob_ai_call,含缓存命中 token 数 |
| 预算保护 | 场景级日预算 + 用户级频控;超限降级到轻量模型或拒绝 |
| 审核联动 | 输入输出审核在网关内统一完成,业务方无需关心 |
| 可复现 | 记录 prompt 版本 + 模型版本 + 参数,便于回溯「为什么当时这么答」 |
8.2 视觉服务
POST /internal/vision/analyze
├─ segment_portrait → 腾讯云 SegmentPortraitPic → 前景掩膜
├─ body_keypoints → RTMPose ONNX → 关键点(肩/髋/踝定高)
├─ face_landmarks → 腾讯云 AnalyzeFace(90点) / AnalyzeDenseLandmarks(888点,仅全面报告)
├─ face_attributes → 腾讯云 DetectFaceAttributes(Skin/Hair/Shape/Eye/...)
├─ color_sampling → 自研:按关键点定位采样区,转 Lab,稳健统计(去高光/阴影)
└─ body_measure → 自研:掩膜宽度剖面法(TD-06)
返回:结构化特征 + 每项置信度 + 质量问题标记(模糊/遮挡/多人/姿态异常)
质量门禁前置:图片质量不达标(多人、遮挡、侧脸过大、光线过暗)应在分析前就返回明确的重拍指引,而不是硬算出一个低置信度结果。用户宁愿重拍一次,也不愿收到一份错的报告。
8.3 混合检索的实现
核心是一条 SQL 完成硬过滤 + 向量召回(这正是选 pgvector 的收益):
WITH filtered AS (
SELECT c.chunk_id, c.entity_id, c.text, c.embedding, e.attrs
FROM kb_chunk c
JOIN kb_entity e ON e.entity_id = c.entity_id
WHERE e.status = 'published'
AND e.entity_type = ANY(:entity_types)
AND (:occasion IS NULL OR e.attrs->'occasions' ? :occasion)
AND NOT (e.attrs->'avoid_body_shapes' ? :body_shape) -- 硬禁忌直接排除
),
vec AS (
SELECT chunk_id, 1 - (embedding <=> :query_vec) AS vec_score
FROM filtered ORDER BY embedding <=> :query_vec LIMIT 50
),
kw AS (
SELECT chunk_id, ts_rank(tsv, plainto_tsquery('simple', :query_text)) AS kw_score
FROM kb_chunk WHERE tsv @@ plainto_tsquery('simple', :query_text) LIMIT 50
)
SELECT ... -- RRF 融合,再叠加规则权重与新鲜度
重排权重(可配置,需用黄金集调参):最终分 = 0.45×向量 + 0.2×关键词 + 0.25×规则权重 + 0.1×新鲜度。规则命中的条目要有明显加权——专业规则的可信度高于语义相似度,这是自建知识库的意义所在。
8.4 记忆系统的读写
读(注入):
get_memory_snapshot(user_id, budget_tokens)
→ 优先级排序:L0 身份 > L1 画像 > L2 显式偏好 > L5 衣橱 > L3 事件
→ 过滤:status='active' AND confidence >= 阈值
→ 裁剪到预算内 → 缓存(hash 变更才重建)
写(置信度初值的唯一事实源是《用户系统与记忆系统-需求文档》§3.5,本处原写 0.95 与之冲突,已更正为 0.90):
用户确认 / 设置页配置 → confidence=1.00
对话中明确陈述 → confidence=0.90,立即生效,回执告知用户「已记住」
报告产出(机器诊断) → confidence=诊断置信度(高置信维度 0.85 / 争议维度 0.65),写 L1
自评问卷 → confidence=0.60
行为推断(隐式) → confidence=0.50,累积证据达阈值生成 status='pending_confirm'
→ 在合适时机征询确认,确认后才转 active(且 confidence 置 1.0)
衰减与冲突:定时任务按 decay_after 降低未被强化的记忆置信度;同 key 冲突时显式 > 报告 > 隐式,时间新 > 时间旧,并写 pf_memory_event 留痕。用户在「我的记忆」页面的编辑拥有最高优先级且不可被自动覆盖。
9. API 设计规范
9.1 基本约定
| 项 | 约定 |
|---|---|
| 基础路径 | 规范写法为 /api/v1,大版本进路径。/v1/* 作为等价别名由中间件重写到 /api/v1/*,两种写法返回完全一致的响应;文档、客户端与新代码一律写 /api/v1(别名只为兼容早期任务描述中的 /v1,不要在新代码里使用) |
| 风格 | 面向资源的 REST;流式对话用 SSE(text/event-stream) |
| 认证 | Authorization: Bearer <access_token>(JWT,15 分钟)+ 刷新令牌(服务端可撤销) |
| 幂等 | 写操作支持 Idempotency-Key 头,服务端 24 小时内保证同键同结果 |
| 分页 | 游标分页(cursor + limit),不用 offset |
| 时间 | 全部 ISO 8601 UTC,客户端负责本地化 |
| 文档 | FastAPI 自动生成 OpenAPI,契约先行:先定 schema 再写实现 |
9.2 统一响应
{ "code": 0, "message": "ok", "data": { }, "request_id": "..." }
{ "code": 40301, "message": "今日额度已用完", "data": { "required": 1, "remaining": 0, "reset_at": "..." },
"request_id": "...", "hint": "明天恢复,或完成任务多换 3 次" }
错误码分段:4xxxx 客户端错误、5xxxx 服务端错误、6xxxx 外部依赖错误(便于监控区分「我们的问题」和「厂商的问题」)。每个错误码都要有用户可读的 message 和可选的 hint(告诉用户怎么办)——AI 产品的失败场景多,错误文案是体验的一部分。
9.3 主要接口清单
| 域 | 方法与路径 | 说明 |
|---|---|---|
| 认证 | POST /auth/huawei POST /auth/refresh POST /auth/guest POST /auth/merge |
登录、刷新、游客、游客合并 |
| 用户 | GET/PATCH /me POST /me/consents DELETE /me(注销) |
含同意记录与注销 |
| 上传 | POST /uploads/presign |
返回 COS 临时桶预签名 URL,图片不经应用服务器 |
| 报告 | POST /reports GET /reports/{id} GET /reports POST /reports/{id}/share POST /reports/{id}/regenerate |
|
| 画像 | GET /profile GET /profile/memories PATCH /profile/memories/{id} DELETE /profile/memories/{id} |
「我的记忆」可见可控 |
| Agent | POST /agent/conversations POST /agent/conversations/{id}/messages(SSE) POST /agent/messages/{id}/feedback |
|
| 换装 | POST /tryon/tasks GET /tryon/tasks/{id} POST /tryon/results/{id}/favorite |
|
| 发型 | GET /hairstyles POST /hairstyles/tasks |
|
| 任务 | GET /tasks GET /tasks/{id} POST /tasks/{id}/cancel |
任务中心 |
| 知识 | GET /kb/items GET /kb/items/{id} |
只读对外 |
| 配额 | GET /quota GET /quota/ledger POST /quota/tasks/{task_key}/claim |
V1;hint 文案不得含价格字样(FR-BILL-07) |
| 支付 | GET /products POST /orders/verify |
V2 起 |
| 埋点 | POST /events/batch |
客户端批量上报 |
10. 缓存策略
| 数据 | 位置 | TTL | 失效方式 |
|---|---|---|---|
| 记忆快照 | Redis | 1 小时 | 记忆写入时按 user_id 主动失效 |
| 知识检索结果 | Redis | 10 分钟 | 键含 query hash + 过滤条件 + 知识库版本号 |
| 规则集(已发布) | 进程内 + Redis | 5 分钟 | 发布时广播失效 |
| 色板 | 进程内 | 长期 | 版本号变更时重载 |
| 报告长图 | COS + CDN | 长期 | 报告版本变更生成新对象(不覆盖,便于回溯) |
| LLM 结果 | 不缓存业务层 | — | 依赖供应商 prompt 缓存降本,不做业务层结果缓存(个性化内容缓存意义不大) |
| 用户配额 | Redis 计数器 + PG 落账 | 周期 | 周期切换重置 |
知识库版本号是关键设计:所有检索缓存键都带上全局 kb_version,知识库发布时版本号 +1,所有旧缓存自然失效,不需要精确追踪哪些缓存受影响。
11. 安全设计
11.1 认证与授权
- JWT 短期 access token(15 分钟)+ 服务端可撤销的 refresh token;登出/注销时撤销全部会话。
- 所有资源访问必须校验归属(
report.user_id == current_user.id),不能只靠 ID 不可猜。越权是这类应用最常见的漏洞。 - 后台采用独立账号体系与独立域名/网段,不与用户端共用鉴权。
11.2 数据安全
| 数据 | 措施 |
|---|---|
| 人脸原图 | 临时桶、服务端加密、2 小时生命周期、处理后立即删除、访问全程走预签名 URL |
| 诊断结论 | 属敏感个人信息衍生数据,数据库加密存储敏感字段,日志中禁止打印 |
| 结果图 | 私有桶 + CDN 签名 URL(短期有效)+ 防盗链,禁止公开可读 |
| 密钥 | 全部走 SSM/KMS,禁止进代码、镜像、环境变量明文 |
| 日志 | 脱敏中间件:手机号、UnionID、图片 URL、诊断结论一律脱敏或不记录 |
| 备份 | PG 自动备份 + 定期恢复演练(没演练过的备份等于没有备份) |
11.3 防刷与滥用
| 风险 | 措施 |
|---|---|
| 免费额度被批量刷取 | 设备指纹 + 华为账号绑定;同设备多账号限制;异常注册风控 |
| 换装接口被外部盗用 | 内网隔离 + 服务间令牌;对外接口强制登录 + 配额消耗 |
| 恶意图片攻击 | 上传前校验类型/尺寸/像素上限;解码走安全库;审核前置 |
| Prompt 注入 | 用户输入与系统提示词严格分区;输出侧校验;禁止用户内容影响工具调用决策 |
| 爬取知识库 | 对外接口只暴露必要字段,加频控与签名 |
11.4 降级与熔断
每个外部依赖都要有明确的降级行为(见 §2 表格)。唯一不允许降级的是内容审核:审核服务不可用时按拦截处理(fail-close)。其他能力遵循 fail-soft,保证核心体验可用。
12. 性能与容量
12.1 性能目标(NFR)
| 链路 | 指标 | 目标 |
|---|---|---|
| 普通 API | p95 响应 | < 300ms |
| Agent 首字 | p95 | < 2s |
| Agent 整轮 | p95 | < 8s |
| 初级报告生成 | p50 / p95 | < 45s / < 90s |
| 全面报告生成 | p95 | < 180s(允许后台生成 + 推送) |
| 换装出图 | p95 | < 40s(受 GPU 容量约束,排队时间单独统计) |
| 知识检索 | p95 | < 200ms |
| 长图渲染 | p95 | < 8s |
| 可用性 | 月度 | ≥ 99.5%(V1) |
排队时间与执行时间必须分开统计。混在一起看会得出错误结论:明明是 GPU 容量不足(该扩容),却被误判为推理慢(去优化模型)。
12.2 容量假设与压力点
基线取 1 万 DAU。与产品方案 §8.1.1 的口径关系需说明清楚,否则会被误读为架构容量低于业务目标:
- 产品方案的 V1 业务目标是 MAU 15 万,且明确不以 DAU 作为考核指标(形象诊断是低频决策型工具,改用季度回访率)。
- 低频工具的 DAU/MAU 通常在 5
10%,因此 MAU 15 万对应 DAU 约 **0.751.5 万** —— 1 万 DAU 基线与业务目标是吻合的,不是保守估计。 - 本基线的作用是确认架构不会在早期撞墙;实际压测值由
T-P5-06确定,Agent 并发上限亦由此推导(穿搭 Agent §7.1)。
| 资源 | 估算 | 压力点 |
|---|---|---|
| API QPS | 峰值约 100~200 | 单机 FastAPI 多 worker 足够 |
| 报告生成 | 首日集中,后续每日新增用户量级 | CPU 队列 + 视觉分析,可水平扩 worker |
| 换装出图 | 最大瓶颈 | GPU 数量决定吞吐,靠队列削峰 + 配额限制需求 |
| PG 连接 | 需连接池(asyncpg pool + 上限保护) | 长事务与 Worker 争用,必须分离读写池 |
| 向量检索 | 十万级向量 | pgvector HNSW 足够,索引重建走低峰 |
13. 成本模型
架构上的硬性要求:每一次付费调用都必须落 ob_ai_call,并可归因到用户与功能。 没有这张表,成本优化只能靠猜。
⚠️ V1 不收费让这张表的地位更高,不是更低(
TD-28)。V1 没有收入,因此成本是唯一的财务变量;同时ob_ai_call是 V2 定价的唯一数据来源 —— 单次真实成本与人均使用频次都从这里出。埋点缺失的功能,在 V2 定价时就是一个必须靠猜的空洞。这一项不允许"先上线后补埋点"。
13.1 三个必须分开的成本口径
⚠️ 本节原有一处错误论证,已更正。原文称「月度成本上限 = MAU × 单用户配额上限 × 单次成本,是可事前算准的确定值」。这不成立:按人头发配额约束的是「单用户能用多少」,而总成本 = MAU × 人均,MAU 本身没有上限。 配额防的是单用户刷爆,防不了总量涨爆。
| 口径 | 公式 | MAU 15 万时 | 用途 |
|---|---|---|---|
| 理论最坏 | MAU × 防刷硬上限成本(¥236/月) |
≈ ¥3,540 万/月 | 只说明"配额不是预算工具",无实际预算意义 |
| 预期成本 | Σ(各功能月用量推算 × 单次成本) |
¥7~12 万/月 | 唯一的预算口径,含使用率假设 |
| 真实封顶 | 全局日成本熔断阈值 × 30 | ¥15 万/月 | 唯一与用户规模无关的硬闸门 |
预期成本的完整算式与其隐含的使用率假设见产品方案 §5.3.1;单用户三档成本见产品方案 §5.2.1。单一事实源在产品方案,本节不重复数值。
13.2 单位成本建模
⚠️ LLM 单价自 2026-08-17 起分峰谷两档,本节的所有
C_llm必须按调用时刻取价。 DeepSeek 高峰时段(北京时间 09:00-12:00、14:00-18:00)单价为空闲时段的 2 倍, 且新价目相对旧价目输出侧涨约 2.25~4.5 倍。两个直接后果:
ob_ai_call.cost_cents必须在调用当时算好落库,不能事后按「当前单价」回算 —— 否则同一条记录在不同时间重算会得到不同结果,V2 定价的依据就不可复现。 实现上由两套价目表 + 时段判定完成(T-P1-25c),并用rate_tier记录当次档位。- §13.1 的预期成本与熔断阈值需按新单价重算一次。原数值基于旧单价推得, 而报告生成集中在白天高峰 —— 不重算会让熔断阈值落在错误的位置。
可控杠杆有两个:一是缓存前缀纪律(固定系统前缀命中缓存后输入侧大幅降价,已实测 单次命中 128/240 输入 token);二是把非实时的文案生成挪到空闲时段,直接省一半。
单位成本核算公式(数值需在 Phase 0 验证任务中实测填入,此处为建模框架):
| 功能 | 成本构成 | 建模公式 |
|---|---|---|
| 初级报告 | 人脸 API + 分割 API + 自建推理 + LLM + 审核 + 渲染 | ≈ C_face + C_seg + C_llm(in≈8k, out≈3k) + C_mod×n + C_render |
| 全面报告 | 初级 + 稠密关键点 + 更长文案 | ≈ 初级 × 3~4 |
| Agent 单轮 | LLM(缓存命中后输入成本大幅下降)+ 检索 + 双向审核 | ≈ C_llm(in≈4.2k 多数缓存, out≈0.8k) + C_mod×2。思考模式须显式关闭(T-P1-24a):开启后思考 token 按输出价计费,实测同一问答耗时从 1.8s 涨到 13.5s |
| 换装单次 | GPU 秒数 + 审核 + 存储 + CDN | ≈ gpu_seconds × 单价 + C_mod + C_storage |
| 抠图单件 | GPU 秒数(离线批量摊薄) | 批量跑,按批次成本 / 件数 |
| 短信验证码 | 单条约 ¥0.03~0.045(TD-32) |
发送条数 × 单价。量级上可忽略,但必须计量并告警 —— 被刷时它是直接的现金损失,且不像 GPU 成本那样有配额兜着 |
13.3 三道成本闸门(按拦截范围从大到小)
| # | 闸门 | 拦截范围 | 触发后行为 | 需求编号 |
|---|---|---|---|---|
| 1 | 全局日成本熔断 | 平台总量 | 自动降级,不是仅告警 | NFR-COST-02 |
| 2 | 场景级日预算 | 单个功能 | 该功能切轻量模型或暂停 | NFR-COST-02 |
| 3 | 用户级配额 | 单个用户 | 服务端拒绝 | FR-BILL-06 |
第 1 道是新增的,也是唯一真正封顶的一道(原设计只有"预警",见 §13.1 的更正)。实现要求:
- 计数:Redis 日累计成本键(
cost:daily:{yyyymmdd}),由 LLM 网关与 GPU 任务在成本产生后立即累加,日切自动过期。落库对账以ob_ai_call为准,Redis 只做实时判定。 - 分级降级顺序(按"用户感知损失最小"排序,阈值百分比可配置):
| 日成本水位 | 动作 | 用户可感知的变化 |
|---|---|---|
| 60% | 告警,不影响用户 | 无 |
| 80% | 停高清导出、停报告重新生成 | 两个低频次要功能不可用 |
| 90% | 停发型试戴;换装强制降画质 + 排队 | 次要生成能力不可用 |
| 100% | 仅保留在途任务与纯文本对话;新的生成类请求全部拒绝 | 「今日服务繁忙,请明天再试」 |
- 在途任务必须放行:成本已经付出,中断只会浪费掉且让用户白等。熔断只拦新请求。
- 关键场景需显式豁免:少数调用一旦失败会让整个链路无法继续(例如意图识别 —— 拿不到意图就无法路由,
用户看到的是功能完全坏掉而不是降级)。这类场景在场景配置上标记为
critical,不受熔断拒绝, 但仍照常计入日成本。豁免清单必须极短且逐个论证,否则熔断会被绕空。 - 必须与配额解耦:熔断拒绝的请求不得扣减用户配额(走
refund流水,FR-BILL-05),否则用户会因平台原因损失额度。这是最容易写错的一处。 - 必须可人工干预:运营后台需有熔断状态查看与手动解除开关(
FR-ADM-*),不能只能等日切。
14. 部署架构
14.1 V1 部署(单可用区,Docker Compose)
腾讯云 VPC
├─ CLB(公网入口,TLS 终止)
├─ CVM-app(2~4 核)
│ ├─ nginx
│ ├─ api (FastAPI × N workers)
│ ├─ admin (极简运营后台)
│ ├─ worker-cpu (Celery: cpu 队列)
│ ├─ worker-io (Celery: io 队列)
│ ├─ beat (定时任务,单实例)
│ └─ vision-svc (ONNX 推理)
├─ CVM-gpu(按需启停)
│ ├─ tryon-svc (换装/发型推理)
│ ├─ worker-gpu (Celery: gpu 队列)
│ └─ matting (批量抠图,仅素材生产期运行)
├─ 云数据库 PostgreSQL(含 pgvector)
├─ 云数据库 Redis
├─ COS × 4 桶 + CDN
└─ CLS / 监控告警
GPU 实例按需启停是 V1 最有效的成本手段:换装量小的时候没必要 7×24 常驻。实现方式是队列有积压时拉起、空闲一段时间后释放(配合任务中心的排队体验,用户可接受冷启动的额外等待)。
14.2 环境
| 环境 | 用途 | 数据 |
|---|---|---|
| local | 本地开发 | Docker Compose 起 PG/Redis/COS 模拟;AI 能力走 mock 或少量真实调用 |
| dev | 联调 | 独立库;外部 API 用测试账号与低配额 |
| staging | 预发/验收 | 结构与生产一致,保留可供监管抽查的测试通道与虚拟测试账号(备案已完成,但变更备案与日常抽查仍需,TD-19) |
| prod | 生产 | — |
14.3 发布
- 容器镜像 + 标签化版本;数据库迁移在发布流程中作为独立步骤执行(先迁移后发布,保证向后兼容)。
- 迁移必须向后兼容(加列不删列、先双写后切读),避免回滚时数据不兼容。
- 灰度:按用户哈希分流;报告与 Agent 这类核心链路的 Prompt/规则变更必须先灰度 + 看黄金集回归。
15. 可观测性
| 类型 | 工具 | 关键内容 |
|---|---|---|
| 链路追踪 | OpenTelemetry | 一次报告生成的完整 span 树(含各外部调用耗时),排障必需 |
| 指标 | Prometheus + Grafana | 三张看板:业务漏斗、AI 成本、任务健康(TD-24) |
| 日志 | CLS | 结构化 JSON,带 request_id/user_id/task_id 贯穿 |
| 审计 | cp_audit_log |
后台操作、知识库变更、权限变更 |
| 告警 | 云监控 + 企业微信/飞书 | 分级:P0(服务不可用/审核失效)、P1(任务失败率超阈值/成本突增)、P2(延迟劣化) |
必须能一键回答的三个问题(这是可观测性的验收标准):
- 「某用户说他的报告生成失败了」→ 用
user_id找到 task,看到失败在哪一步、错误码、配额是否已退还。 - 「昨天成本涨了 3 倍」→ 按功能/模型分组的成本对比,定位到具体场景。
- 「换装最近变慢了」→ 区分是排队变长(扩容)还是推理变慢(模型/资源)。
16. 工程规范
16.1 仓库结构
meida_backend/ # 后端仓库。文档在独立仓库 meida,鸿蒙端尚未建库
├─ apps/
│ ├─ api/ # FastAPI 应用(路由、依赖注入、中间件)
│ ├─ worker/ # Celery 任务定义
│ └─ admin/ # 极简运营后台
├─ services/
│ ├─ vision/ # 视觉推理服务
│ ├─ matting/ # 抠图批处理
│ └─ tryon/ # 换装服务(改造纳管)
├─ packages/
│ ├─ domain/ # 领域模型与业务规则(纯逻辑,无 IO、无框架,可纯单测)
│ ├─ shared/ # 配置、日志、错误、DB、COS、可观测
│ ├─ llm/ # LLM 网关
│ │ # ── 以下为 §4.1 的 11 个业务域,每域一个包 ──
│ ├─ identity/ # 账号与会话
│ ├─ billing/ # 配额与计量
│ ├─ task/ # 任务中心
│ ├─ profile/ # 用户画像
│ ├─ memory/ # 记忆系统
│ ├─ kb/ # 知识库
│ ├─ retrieval/ # 混合检索
│ ├─ rules/ # 规则引擎
│ ├─ report/ # 美学报告
│ ├─ agent/ # 穿搭 Agent
│ ├─ tryon/ # 换装与发型编排
│ ├─ wardrobe/ # 衣橱
│ ├─ notify/ # 通知推送
│ └─ compliance/ # 审核 / 水印 / 元数据标识
├─ prompts/ # Prompt 模板(版本化,随代码走)
├─ migrations/ # Alembic
├─ tests/
│ ├─ unit/ # 领域逻辑
│ ├─ integration/ # 带 DB/Redis(testcontainers)
│ └─ golden/ # 黄金集回归(检索/规则/报告)
├─ deploy/ # Dockerfile / compose / CI 配置
└─ scripts/
文档集在 meida 仓库(docs/),不进本仓库。鸿蒙客户端独立建库,不预置空目录。
包内约定:每个业务域包统一为 service.py(用例编排,对外唯一入口)+ repo.py(该域自己的表访问)
- 可选
schemas.py。§4.2 的「禁止直接查对方的表」因此落为一条可机检规则: 跨域只允许 import 对方的service,不允许 import 对方的repo。
三层的判别标准(决定新代码该放哪一层):
| 层 | 判别标准 | 反例 |
|---|---|---|
packages/domain |
纯函数与不变量,不 import 任何框架、不做 IO | 把 SQLAlchemy 模型放进来 |
packages/<域> |
该域的用例编排与自己的表访问 | 在 agent/ 里直接读 bl_quota 表 |
apps/ |
HTTP / Celery 的适配,不含业务判断 | 在路由函数里写配额判定公式 |
这三条边界由 import-linter 契约强制,不靠 code review。分层规则一旦只靠人守,第一次赶工就会破。
16.2 AI 辅助开发下的质量防线
既然主要由 AI 编码,人工 review 的带宽会成为瓶颈,因此必须把规范变成机器可执行的检查:
| 防线 | 内容 | 卡关方式 |
|---|---|---|
| 契约先行 | 接口先写 Pydantic schema 与 OpenAPI,再写实现 | 客户端类型由 OpenAPI 生成,schema 变更即可见 |
| 静态检查 | ruff(lint+format)+ mypy strict(覆盖 packages/domain、packages/llm) |
CI 失败即阻断合并 |
| 分层依赖检查 | 用 import-linter 之类的工具声明层间依赖规则 | 跨域直接访问对方表 → CI 报错 |
| 黄金集回归 | 检索准确率、规则命中正确性、报告结构完整性、禁忌违反数=0 | 每次 PR 跑,指标退化即阻断 |
| 迁移检查 | Alembic 迁移必须可逆或明确标注不可逆 | Review 检查清单 |
| 敏感信息扫描 | 密钥/Token 扫描 | pre-commit + CI |
黄金集是最重要的一道:AI 改代码时最容易破坏的是那些「没有断言保护的隐性行为」。检索链路和规则引擎必须有一套固定输入 → 期望输出的样本集,这既是质量防线,也是《美学知识库-建设方案》中质量保障章节的落地形式。
17. 架构演进路线
| 阶段 | 架构形态 | 触发条件 |
|---|---|---|
| V1 | 模块化单体 + 独立 AI 服务;单机 Compose;PG 单库 | — |
| V1.5 | 读写分离;Redis 集群;GPU 自动伸缩 | 报告/换装量上升,PG 读压力显现 |
| V2 | 拆出 llm-gateway、retrieval-svc 为独立服务;引入 TKE |
需要独立发布节奏或独立扩容 |
| V2 | 多端(iOS/Android)→ 重新评估跨端框架 | 明确的多端商业需求(TD-01) |
| V3 | 向量迁 Qdrant/Milvus;事件流引入消息队列;数仓引入 ClickHouse | 向量 > 500 万 / 跨域事件复杂度上升 / 需要即席分析 |
演进纪律:每一次架构升级都必须先出现可测量的瓶颈,再动手。提前架构化是这个阶段最大的浪费。
18. 架构决策记录(ADR 索引)
| ADR | 决策 | 详见 |
|---|---|---|
| ADR-01 | 客户端用鸿蒙原生 ArkTS,不用跨端框架 | TD-01 |
| ADR-02 | 业务域用模块化单体,AI 能力独立服务 | §3.2 |
| ADR-03 | 向量检索用 pgvector,不引入专用向量库 | TD-08 |
| ADR-04 | 图谱用 PG 邻接表 + 递归 CTE | TD-09 |
| ADR-05 | 规则引擎自研数据驱动决策表 | TD-10 |
| ADR-06 | 人体关键点自建(腾讯云已下线) | TD-05 |
| ADR-07 | 体型测算用掩膜宽度剖面法 | TD-06 |
| ADR-08 | 抠图用 BiRefNet,禁用 RMBG-2.0 | TD-07 |
| ADR-09 | 自建 LLM 网关,双供应商路由 | TD-04 |
| ADR-10 | 人脸原图不落盘,只存派生特征 | TD-20 |
| ADR-11 | 统一合规出口,所有生成物必经 | TD-21 |
| ADR-12 | 报告长图服务端渲染 | TD-12 |
| ADR-13 | 配额预扣-确认-退还三段式;V1 与 V2 共用账本表 | §7.4、TD-28 |
| ADR-14 | 统一任务中心承载全部长耗时任务 | §7 |
19. 技术风险登记
| 风险 | 影响 | 概率 | 应对 |
|---|---|---|---|
| 已消除 | ✅ 备案已完成,此风险关闭(TD-19) |
||
| 备案范围与实际调用的模型不一致 | 高:超范围提供服务 | 中 | 核实备案覆盖的模型清单;LLM 网关已做多路由抽象,必要时 V1 先单路由(TD-19 残留项) |
| 素材自采产能不足 | 高:tryon_ready < 300 件则「建议→试穿」闭环不成立 |
中高 | 备案解除后这是最长工序(TD-26)。按周产能倒排,未达标则功能降级为「加入搭配」 |
| 全职顾问到岗延迟 | 中:规则库启动即延后,M2 顺延 | 中 | Phase 0 最早启动招聘(TD-27、T-P0-20) |
| 团队缺乏 ArkTS 经验 | 前端进度不确定 | 中 | 第一个任务就打通端到端骨架,尽早暴露问题 |
| 体型测算准确率不达标 | 报告核心结论不可信 | 中 | Phase 0 验证任务(≥80% 一致率);不达标则降级表述 + 引入人工校准 |
| 换装成功率依赖白底图素材 | 核心闭环卡住 | 高 | 素材生产列为 Phase 1 前置任务,先小批量走通再规模化 |
| GPU 成本随活跃度线性增长 | 单位经济恶化 | 低(V1) | V1 不收费,配额为硬上限,月度成本上限 = MAU × 单用户配额成本,可事前算准。V2 引入付费后此风险重新升级 |
| 大模型调价或能力变动 | 成本与质量波动 | 中 | 网关抽象 + 双供应商 + 场景配置化 |
| 云 API 下线(已发生一次) | 能力缺口 | 中 | 核心能力(关键点、抠图)自建;对云能力保留替换预案 |
| AI 标识元数据在链路中被剥离 | 合规不达标 | 中 | 专项测试覆盖 CDN/压缩/客户端保存全路径 |
| 知识库内容生产跟不上 | 建议质量低、可解释性弱 | 高 | 知识缺口埋点驱动优先级;顾问资源尽早接洽(TD-27) |
20. 与需求文档的映射
| 本文章节 | 对应需求文档 |
|---|---|
| §7.1 报告生成链路 | 《美学报告-需求文档》§6 生成流程 |
| §7.2 Agent 链路、§8.3 混合检索 | 《穿搭Agent-需求文档》§3.4、《美学知识库-建设方案》§6 |
| §6.2 profile 域、§8.4 记忆读写 | 《用户系统与记忆系统-需求文档》§3 |
| §6.2 kb 域、§8.3 | 《美学知识库-建设方案》§2~§6 |
| §7.4 配额与支付 | 《用户系统与记忆系统-需求文档》§2.4/2.5、《需求规格说明》§2.9 |
| §7.5、§11 | 《合规与安全方案》全文 |
| §13 成本模型 | 《产品方案》§5.2 成本结构 |
相关文档
- 文档索引
- 需求规格说明(本文实现的需求编号来源)
- 技术选型与调研报告(本文每项技术选择的理由、备选与切换条件)
- 合规与安全方案(§7.5 统一合规出口与 §11 安全设计的义务来源)
- 开发任务拆解(按本文的模块与链路切分的 WBS)
- 美搭 · 智能穿搭美学助手 产品方案
变更记录
| 日期 | 版本 | 变更 |
|---|---|---|
| 2026-08-14 | v1.4 | 文档 ↔ 代码交叉评审回写。① §16.1 根目录 meida/ → meida_backend,并去掉本仓库并不存在的 docs/、client-harmony/。② §4.1 补「域与包不是一一对应」:产品职责上记忆归 profile,代码上独立 packages/memory。③ bl_quota 列清单补 limit_value(本周期 base_limit 快照),并给出 quota_key 与产品方案 §3.2 的一一映射;游客 key 与记忆条数明确不进种子。④ ob_ai_call 补保留口径(定价完成前不可按日志周期删除) |
| 2026-08-13 | v1.3 | 脚手架落地后按实现回写,五处修正。① §16.1 仓库结构补全 11 个业务域包 —— 原树只列了 7 个 packages/,§4.1 定义的 11 个业务域在树里无处安放,实现时只能猜;现补全并加「包内约定」(每域 service.py + repo.py,跨域只许 import 对方 service)与三层判别标准表。② §6.2 ob_ai_call 补 prompt_id/prompt_version/tokens_reasoning/rate_tier 四列 —— §8.1 要求 Prompt 版本化可复现,但表里没有版本列,等于要求做不到的事;思考 token 混进 tokens_out 会掩盖开关思考模式的成本差异。③ §9.1 澄清基础路径 —— 正文写 /api/v1、任务描述写 /v1,实现两者都要能用;现明确 /api/v1 为规范写法、/v1/* 仅作兼容别名。④ §13.2 新增峰谷计价约束 —— DeepSeek 自 8-17 起高峰单价翻倍且新价目输出侧涨 2.25~4.5 倍,成本须在调用当时算好落库(不可事后回算),§13.1 的预算与熔断阈值需按新价重算;Agent 单轮成本注明思考模式须显式关闭。⑤ §13.3 补「关键场景显式豁免」 —— 意图识别这类调用失败会让链路整体不可用,须标记 critical 不受熔断拒绝,但仍计入成本;豁免清单必须极短,否则熔断被绕空 |
| 2026-08-13 | v1.2 | 文档一致性评审修订,其中三处是会直接卡住开发的实质缺陷。① §7.4 补齐 billing 域四张表的职责边界 —— bl_quota 与 bl_wallet 此前看起来像两套重复机制,task_bonus 的来源也没定义;现明确「V1 判定看 bl_quota、V2 判定看 bl_wallet、bl_ledger 两版都只是流水」,并把 task_bonus 物化为 bl_quota.bonus_granted 字段(避免每次判定聚合流水),补充三处最易写错的地方(熔断须在预扣之前、used 增减须与流水同事务、周期重置靠 period_key 而非定时清零)。② §13 成本模型重写 —— 推翻「月度成本上限 = MAU × 单用户配额上限」这个错误论证(配额约束单用户,总成本 = MAU × 人均,MAU 无上限),改为「理论最坏 / 预期成本 / 真实封顶」三口径,并新增 §13.3 四档熔断降级(含在途任务放行、被熔断请求不得扣配额、可人工解除)。③ §7.2 token 预算改为嵌套表,合计 3700→4200(原表漏算会话近轮原文 400),并明确硬禁忌永不裁剪。另:pf_memory.status/source、id_consent.consent_type 枚举与用户系统对齐(personalization 不可省略,否则记忆写入无合规依据);手机号存储位置明确为 id_user_identity;诊断维度 34→32;points_reserved→quota_reserved;外部依赖表补入腾讯云短信并说明登录双通道不得互相依赖;§12.2 说明 1 万 DAU 基线与 MAU 15 万目标的口径关系 |
| 2026-08-13 | v1.1 | 按业务决策收口调整。billing 域降级为「配额与计量」:V1 移除支付、订单、会员、IAP;bl_point_tx 改名 bl_ledger 并新增 tx_type 字段,使 V1 配额账本与 V2 点数账本共用同一张表(V2 免数据迁移);新增 bl_quota_rule 配额规则表。§7.4 拆为「V1 配额三段式」与「V2 IAP 支付」。§13 成本模型补充「V1 成本上限可事前算准」的公式,并强调 ob_ai_call 是 V2 定价的唯一数据来源、不允许后补。§19 风险登记:备案风险关闭,新增备案范围一致性、素材自采产能、顾问到岗三项风险 |
| 2026-08-13 | v1.0 | 首次输出。确立「模块化单体 + 独立 AI 服务」架构、十个业务域数据模型、五条关键链路、统一合规出口、V1 单机 Docker Compose 部署与成本模型 |