From 2934954eb2fdea5773fbfbff13deacd73ee5e0b7 Mon Sep 17 00:00:00 2001 From: xiangsilian Date: Fri, 14 Aug 2026 11:28:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=89=E4=BA=A4=E5=8F=89=E8=AF=84=E5=AE=A1?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=E6=96=87=E6=A1=A3=E4=B8=8E=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=8F=A3=E5=BE=84=EF=BC=8C=E5=B9=B6=E6=8C=A1=E4=BD=8F=20refere?= =?UTF-8?q?nce=20=E4=B8=8B=E7=9A=84=E5=AF=86=E9=92=A5=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 表名、配额种子和字段名以产品方案与架构为准;文档仓库补 .gitignore,避免 git add -A 把 API Key 推进远端。 Co-authored-by: Cursor --- .gitignore | 14 ++ docs/README.md | 12 +- docs/Sprint执行计划.md | 2 +- docs/合规与安全方案.md | 4 +- docs/开发任务拆解.md | 7 +- docs/技术架构文档.md | 31 +++- docs/用户系统与记忆系统-需求文档.md | 64 +++++---- docs/项目整体评审报告-2026-08-14.md | 212 ++++++++++++++++++++++++++++ reference/README.md | 7 + 9 files changed, 310 insertions(+), 43 deletions(-) create mode 100644 .gitignore create mode 100644 docs/项目整体评审报告-2026-08-14.md create mode 100644 reference/README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a5d294 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# 密钥与本地环境。文档仓库没有 detect-secrets,只能靠忽略规则兜底。 +# `git add -A` 不得把 reference 下的 txt / key 推进远端。 +reference/*.txt +reference/*.key +reference/*.pem +.env +.env.* +!.env.example + +# 本地草稿与工具缓存 +.scratch/ +.DS_Store +Thumbs.db +*.swp diff --git a/docs/README.md b/docs/README.md index e8d5ded..7dff34f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,7 @@ # 「美搭」文档索引 > 智能穿搭美学助手 · 文档体系导航 -> 最后更新:2026-08-13 +> 最后更新:2026-08-14 --- @@ -234,16 +234,18 @@ **第三批 —— 后端脚手架落地后的回写**:`meida_backend` 仓库跑通真实纵向切片后,两项文档未覆盖的问题被实测暴露并回写为 `T-P1-24a`(思考模式)与 `T-P1-25c`(峰谷计价);同时新增《Sprint 执行计划》承接排期与已交付部分的验收状态。 +**第四批 —— 文档 ↔ 代码交叉评审**(见《[项目整体评审报告](./项目整体评审报告-2026-08-14.md)》):以产品方案 §3.2 修正配额种子;`points_reserved` → `quota_reserved`、`bonus_value` → `bonus_granted`;用户系统表名与架构对齐;文档仓库补 `.gitignore` 挡住 `reference/*.txt`。 + | 文档 | 版本 | 主要变更 | | --- | :---: | --- | | [产品方案](./鸿蒙美搭-智能穿搭美学助手-产品方案.md) | v6.1 | §5.0「V1 不收费只限量」;§3.2 配额表补入**游客列**并成为唯一事实源;§4.1 市场数据换官方信源并纠正鸿蒙可触达口径;§8.1.1 用户目标下调且**以季度回访率替代 DAU**;**§5.2.1/§5.3.1 成本口径重写为三档并推翻「配额封顶总成本」的错误论证**;§8.3.1 性能指标改为引用 `NFR-PERF-*`;§8.3.3 准确率拆为验证/上线两档;删除全部日历周期承诺 | | [需求规格说明](./需求规格说明.md) | v1.2 | BILL 域改为「配额与计量」,V1 移除全部支付;**§4.1 成为性能指标唯一事实源**(修正 Agent 整轮 8s→15s、可用性 99.9%→99.5%,换装改两段式);`NFR-COST-02` 升级为**自动熔断**;新增 `FR-ACC-14` 短信防刷、`FR-ACC-15` 清空照片、`FR-RPT-19` 结论修正入口、`FR-RPT-20` 预约留资、`FR-BILL-12` 会员页留资 | -| [技术架构文档](./技术架构文档.md) | v1.3 | billing 域降级为配额与计量;**§7.4 新增四张表职责边界与 `task_bonus` 物化方案**(此前开发会卡在这里);**§7.2 token 预算改为嵌套表并修正合计 3700→4200**;**§13 成本模型重写为三口径 + §13.3 四档熔断降级**;`pf_memory`/`id_consent` 枚举与用户系统对齐;手机号存储位置明确;`points_reserved`→`quota_reserved`;v1.3 按脚手架实现回写五处:**§16.1 补全 11 个业务域包与三层判别标准**、**`ob_ai_call` 补 4 列**(Prompt 版本/思考 token/峰谷档位)、**§9.1 澄清 `/api/v1` 为规范写法**、**§13.2 新增峰谷计价约束**、**§13.3 补关键场景豁免** | +| [技术架构文档](./技术架构文档.md) | v1.4 | billing 域降级为配额与计量;**§7.4 新增四张表职责边界与 `task_bonus` 物化方案**(此前开发会卡在这里);**§7.2 token 预算改为嵌套表并修正合计 3700→4200**;**§13 成本模型重写为三口径 + §13.3 四档熔断降级**;`pf_memory`/`id_consent` 枚举与用户系统对齐;手机号存储位置明确;`points_reserved`→`quota_reserved`;v1.3 按脚手架实现回写五处:**§16.1 补全 11 个业务域包与三层判别标准**、**`ob_ai_call` 补 4 列**(Prompt 版本/思考 token/峰谷档位)、**§9.1 澄清 `/api/v1` 为规范写法**、**§13.2 新增峰谷计价约束**、**§13.3 补关键场景豁免** | | [技术选型与调研报告](./技术选型与调研报告.md) | v1.2 | `TD-26`~`TD-31` 六项业务决策收口;`TD-19` 备案已办结;**补 `TD-00` 定义**(此前被引用却未定义);**新增 `TD-32` 短信通道**(此前 P0 需求无选型)、**`TD-33` 天气数据源**(V1 不接,给降级方案) | -| [合规与安全方案](./合规与安全方案.md) | v1.2 | §2.2.0 备案后四项持续义务;公示要求升为开发任务;**人脸原图保留期统一为 2 小时**(此前与产品方案的 24h 冲突) | -| [开发任务拆解](./开发任务拆解.md) | v1.3 | **关键路径转移**:内容与素材生产升为最优先;**M4 拆为 a/b 两块**消除依赖图歧义;新增 `T-P1-12b/12c` 短信、`T-P1-16b` 清空照片、`T-P1-25b` 熔断、`T-P3-21/22` 两个埋点、`T-P5-17/18/19`;v1.3 补 **`T-P1-24a` 思考模式显式配置**与 **`T-P1-25c` 峰谷计价**(均由后端实测暴露) | +| [合规与安全方案](./合规与安全方案.md) | v1.3 | §2.2.0 备案后四项持续义务;公示要求升为开发任务;**人脸原图保留期统一为 2 小时**(此前与产品方案的 24h 冲突);v1.3 补 prod 无 `dev-token` | +| [开发任务拆解](./开发任务拆解.md) | v1.4 | **关键路径转移**:内容与素材生产升为最优先;**M4 拆为 a/b 两块**消除依赖图歧义;新增 `T-P1-12b/12c` 短信、`T-P1-16b` 清空照片、`T-P1-25b` 熔断、`T-P3-21/22` 两个埋点、`T-P5-17/18/19`;v1.3 补 **`T-P1-24a` 思考模式显式配置**与 **`T-P1-25c` 峰谷计价**(均由后端实测暴露) | | [Sprint 执行计划](./Sprint执行计划.md) | v1.0 | 新增。按依赖就绪切分六个 Sprint,标注 `meida_backend` 已交付任务的实测证据,并把「内容产能」明确为倒排上线时间的唯一依据 | | [美学知识库-建设方案](./美学知识库-建设方案.md) | v1.2 | §4.2.1 素材自采流水线;§5.1.1 全职顾问工作流;**§9.1 成为规模唯一事实源**(V1 硬指标与长期目标分列);**§7 黄金集拆为四个子集共 330 例**;修正脸型 8→7、局部特征 12→13 | | [美学报告-需求文档](./美学报告-需求文档.md) | v1.2 | 五行模块移除(维度 34→32);**§9.4 成为准确率门槛唯一事实源**(验证/上线两档);**清除全部价格残留**(¥29.9、预计价格、点击价格档位);**维度覆盖改为准确口径**(初级 21/32,原「40+ 项」不存在) | -| [用户系统与记忆系统-需求文档](./用户系统与记忆系统-需求文档.md) | v1.2 | §2.4 重构为 V1 配额 + V2 规划;一键登录降级;**§3.8 记忆注入分项被采纳为全局事实源**;配额数值改为引用产品方案 §3.2 | +| [用户系统与记忆系统-需求文档](./用户系统与记忆系统-需求文档.md) | v1.3 | §2.4 重构为 V1 配额 + V2 规划;一键登录降级;**§3.8 记忆注入分项被采纳为全局事实源**;配额数值改为引用产品方案 §3.2;v1.3 **表名与架构对齐**(`id_user` / `id_user_identity` / `id_consent` / `pf_memory_event`) | | [穿搭 Agent-需求文档](./穿搭Agent-需求文档.md) | v1.2 | **语音输入由 P0 改为 P1/V2**(此前与 `FR-AGT-17` 冲突);§2.1 能力表补版本与编号列;token 预算与性能指标改为引用;**天气类问题明确不得编造数值**;意图识别准确率拆两档;删除无依据的「1000+ 并发」 | diff --git a/docs/Sprint执行计划.md b/docs/Sprint执行计划.md index 9798792..c132ed9 100644 --- a/docs/Sprint执行计划.md +++ b/docs/Sprint执行计划.md @@ -38,7 +38,7 @@ | `T-P1-01` 仓库结构与包边界 | ✅ | `apps / packages / services` 三层;**四条 import-linter 契约全绿**,`packages/domain` 禁 framework 与 IO 由机器强制 | | `T-P1-02` CI 流水线 | ✅ | ruff + mypy(domain/llm 为 strict) + pytest + lint-imports + detect-secrets + gitleaks + **迁移 upgrade→downgrade→upgrade 往返** 六道防线 | | `T-P1-03` 本地开发环境 | 🟡(缺 COS mock) | `make up` 起 pgvector + Redis,端口 55432/56379 避冲突;`make install` 一条命令装齐 | -| `T-P1-06` Alembic 基线 | ✅ | `0001` 建 9 张表并创建 `vector` 扩展;正反向往返实测通过 | +| `T-P1-06` Alembic 基线 | ✅ | `0001` 建 **8** 张表(`id_user` / `id_user_identity` / `id_consent` / `bl_quota_rule` / `bl_quota` / `bl_ledger` / `tk_task` / `ob_ai_call`)并创建 `vector` 扩展;正反向往返实测通过 | | `T-P1-07` 统一响应与错误码 | ✅ | `code/message/data/request_id` 信封;每个错误码有文案,且**配额文案经测试断言不含价格字样** | | `T-P1-08` 可观测性 | 🟡(缺 CLS 与看板) | structlog JSON + request_id 贯穿;**日志脱敏有专项测试**(手机号/令牌/密钥/诊断结论);Prometheus 指标就绪,OTel 留开关 | | `T-P1-19` 任务中心 | 🟡(仅骨架任务) | `tk_task` + 状态机(非法跃迁有测试)+ 幂等键;`POST /tasks/ping`、`GET /tasks/{id}`、列表可用 | diff --git a/docs/合规与安全方案.md b/docs/合规与安全方案.md index d2ea2dc..071a62f 100644 --- a/docs/合规与安全方案.md +++ b/docs/合规与安全方案.md @@ -1,6 +1,6 @@ # 「美搭」合规与安全方案 -> 版本:**v1.2** | 更新日期:2026-08-13 | 状态:待法务/合规复核 +> 版本:**v1.3** | 更新日期:2026-08-14 | 状态:待法务/合规复核 > > **为什么这份文档单独存在**:本产品同时踩在三条监管线上——处理**人脸信息**(敏感个人信息)、提供**生成式 AI 服务**、产出**AI 生成合成内容**。这三条线各自有独立的法定义务,把它们混在技术文档里会被当成技术细节忽略,所以单独成文。 > @@ -363,6 +363,7 @@ AI 生成内容仅供参考,不构成专业建议 - [ ] 注销流程 7 日内彻底删除,且有执行记录 - [ ] 越权访问测试全部拦截 - [ ] 日志中无敏感信息(人工抽查 + 自动扫描) +- [ ] **生产环境确认无 `dev-token` 路由**(`DEV_AUTH_ENABLED` 与 `is_local` 双开关均须为关;发布包探测 `/api/v1/auth/dev-token` 应 404) - [ ] 越界问题(医疗/命理/他人评价)拒答测试通过 —— **命理模块已决定不做(`TD-31`),但对话侧仍须拒答** - [ ] 贬损性表述检测生效(用负面样本集验证) - [ ] 备案编号公示位在发布包中可见且内容正确 @@ -424,6 +425,7 @@ AI 生成内容仅供参考,不构成专业建议 | 日期 | 版本 | 变更 | | --- | --- | --- | +| 2026-08-14 | v1.3 | §5.3 补「生产环境确认无 `dev-token` 路由」 | | 2026-08-13 | v1.2 | **文档一致性评审修订**。**本文的「人脸原图保留期 ≤ 2 小时」被确认为全局唯一事实源** —— 产品方案 §7.1.4 与美学报告 §9.1 原写「≤ 24h 仅用于任务重试」,已按本文更正(2 小时已足够覆盖重试窗口:最长任务 320s、重试上限 3 次;24h 只是白担合规风险,保留期越长越可能被认定为"存储了人脸信息"从而触发 10 万人备案义务)。§5.3 检查清单补入 `FR-ACC-15`「清空我的照片」(涉及人脸数据删除,此前无 FR 也未纳入清单);防刷项补入配额硬限制与短信通道防刷(`FR-ACC-14`);措辞「退点」→「退还配额」 | | 2026-08-13 | v1.1 | **主线二状态更新为「备案已完成」**。新增 §2.2.0「备案完成后的四项持续义务」(编号公示、备案范围与实际调用一致、变更备案 30 工作日、材料维护),并标出唯一开放风险 —— 备案覆盖的模型清单是否含 DeepSeek + GLM 两家,若只含一家则双路由属超范围。§2.2.3 时间线改为已完成说明,指出关键路径已转移到素材与规则库生产。§2.2.4 公示要求从"待办材料"升为"必须落地的开发任务",补充三条实现要求(入口硬编码、编号可下发、文案与网关路由同源)。§5.1 检查清单标记已完成项,移除华为企业开发者认证(V1 不需要);§5.3 移除支付类合规项 | | 2026-08-13 | v1.0 | 首次输出。确立三条主线:① 人脸原图不落盘以规避 10 万人备案阈值;② 大模型登记 + 算法备案须在 Phase 0 启动(周期 2~4 个月);③ AI 生成内容显式 + 隐式双重标识,经统一合规出口收口 | diff --git a/docs/开发任务拆解.md b/docs/开发任务拆解.md index 286f949..8fee578 100644 --- a/docs/开发任务拆解.md +++ b/docs/开发任务拆解.md @@ -1,6 +1,6 @@ # 「美搭」开发任务拆解(WBS) -> 版本:**v1.3** | 更新日期:2026-08-13 | 状态:待评审 +> 版本:**v1.4** | 更新日期:2026-08-14 | 状态:待评审 > > **本文的定位**:把需求与架构翻译成可指派、可验收的任务清单,并明确任务之间的依赖与关键路径。 > @@ -187,9 +187,9 @@ M2 知识库 M3 报告闭环 M4-a 换装/发型 │ | T-P1-24 | DEV | **LLM 网关**(路由/降级/熔断/结构化输出/Prompt 版本/成本记录) | 模块 | 主备切换 < 3s;成本 100% 记录 | FR-SYS-04 | | **T-P1-24a** | DEV | **思考模式按场景显式配置** | 场景配置 + provider 参数 | 每个场景**显式**给出 `thinking` 值,不依赖供应商默认;对话与结构化抽取场景一律关闭;单独记录 `tokens_reasoning` | `NFR-PERF-02b`、`NFR-COST-01`、TD-04 | | T-P1-25 | DEV | 成本归因表与日成本预警 | 表 + 告警 | 可按功能/模型/用户聚合;突增告警生效。**不允许后置到上线后补**(V2 定价的唯一数据来源) | FR-BILL-08、NFR-COST-01、FR-SYS-07 | -| T-P1-25b | DEV | **全局日成本熔断**(四档自动降级) | 模块 + 后台开关 | 注入成本可复现 60/80/90/100% 四档降级;**在途任务放行**;**被熔断拒绝的请求配额净扣减 = 0**;后台可查状态并手动解除 | NFR-COST-02、架构 §13.3 | +| T-P1-25b | DEV | **全局日成本熔断**(四档自动降级) | 模块 + 后台开关 | 注入成本可复现 60/80/90/100% 四档降级;**在途任务放行**;**被熔断拒绝的请求配额净扣减 = 0**(须有集成断言覆盖「reserve 之后才触发熔断」这条路径,不能只测「熔断在 reserve 之前」);后台可查状态并手动解除;**日预算阈值须按 8-17 峰谷新价 + 实测 token 分布重算**,`.env` 里的 `100000` 分是占位不是验收值 | NFR-COST-02、架构 §13.3 | | **T-P1-25c** | DEV | **峰谷单价按调用时刻计价** | 两套价目表 + 时段判定 | 高峰(北京时间 09:00-12:00、14:00-18:00)与空闲各一套单价;成本在**调用当时**算好落库,事后不按当前价重算;跨时段有测试 | `NFR-COST-01`、TD-04 | -| T-P1-26 | DEV | **配额账本**(余额、流水、预扣三段式、周期重置、规则配置化) | 模块 + 接口 | 余额可由流水重算;失败自动退还;**表结构含 `tx_type` 字段,V2 上支付无需迁移**;服务端硬校验不可绕过 | FR-BILL-01/02/04/05/06 | +| T-P1-26 | DEV | **配额账本**(余额、流水、预扣三段式、周期重置、规则配置化) | 模块 + 接口 | 余额可由流水重算;失败自动退还;**表结构含 `tx_type` 字段,V2 上支付无需迁移**;服务端硬校验不可绕过;**`bl_quota_rule` 种子必须与产品方案 §3.2 一致**(含 `hairstyle` / `hd_export` / `report_basic` / `report_regen`),禁止开发期另写一套数字 | FR-BILL-01/02/04/05/06 | | T-P1-26b | DEV | 额度耗尽文案与提示 | 端侧组件 | **不出现任何价格、购买、会员、升级字样**;消耗前提示剩余量 | FR-BILL-07 | | T-P1-27 | DEV | 图片直传(预签名 URL)与临时桶生命周期 | 接口 + 桶策略 | 图片不经应用服务器;2 小时自动清理 | FR-SYS-03、CR-02 | | T-P1-28 | DEV | 推送接入(Push Kit + Token 管理) | 模块 | 到达率 ≥ 90%;文案无敏感结论 | FR-TASK-04 | @@ -429,6 +429,7 @@ M2 知识库 M3 报告闭环 M4-a 换装/发型 │ | 日期 | 版本 | 变更 | | --- | --- | --- | +| 2026-08-14 | v1.4 | 交叉评审回写:`T-P1-25b` 验收补「reserve 之后熔断」集成断言与日预算按新价重算;`T-P1-26` 明确种子必须与产品方案 §3.2 一致 | | 2026-08-13 | v1.3 | **后端脚手架落地后的回写**。新增两项由真机实测暴露、原 WBS 完全未覆盖的任务:**`T-P1-24a` 思考模式按场景显式配置** —— `deepseek-v4-flash` **默认开启**思考模式,实测同一单句问答开启 13.5s、关闭 1.8s,思考 token 还按输出价计费,漏配会同时打破 `NFR-PERF-02b` 与成本模型;**`T-P1-25c` 峰谷单价按调用时刻计价** —— DeepSeek 自 2026-08-17 起高峰单价为空闲时段 2 倍,用单一均价记账会让 `ob_ai_call` 与真实账单差出近一倍,而它是 V2 定价的唯一依据。另:`T-P1-01`/`02`/`06`/`07`/`24`/`25` 已在 `meida_backend` 仓库交付并实测通过,各任务的完成状态见《Sprint 执行计划》§1.1 | | 2026-08-13 | v1.2 | **文档一致性评审修订,补齐 9 项"需求已定但无任务认领"的缺口**。新增任务:`T-P1-12b` 短信验证码登录、`T-P1-12c` 短信防刷、`T-P1-16b` 清空我的照片、`T-P1-25b` **全局日成本熔断**(四档降级,验收含「配额净扣减 = 0」)、`T-P3-21` **报告结论认同/修正入口**、`T-P3-22` **全面报告预约留资**、`T-P5-17` 会员页留资、`T-P5-18` CLS 日志留存 ≥6 个月(`CR-11` 此前无人认领)、`T-P5-19` 熔断四档演练。其中 `T-P3-21`/`T-P3-22` 尤其关键 —— 前者是「结论认同率 > 70%、修正率 < 15%」的唯一数据来源,后者是 V1 唯一的商业化验证数据来源,**缺了会让 V1 白跑一遍**。另:**依赖图中的 M4 拆为 `M4-a` 换装/发型(可与 M3 并行)与 `M4-b` Agent 对话(须等 M2+M3)**,原图两处都标 M4 易误读为同一批工作;`T-P3-17` 注入预算 500→1400、`T-P4-03` 总输入 3700→4200;`T-P4-14` 明确输入校验须在预扣配额之前 | | 2026-08-13 | v1.1 | **关键路径转移**。备案已完成,原 `T-P0-01`~`T-P0-08` 申报类任务关闭,替换为备案后的落地与核实任务(模型清单核实、编号公示位实现、公示与网关路由同源校验)。§2.3 内容与素材生产升为新的关键路径,`T-P0-20` 顾问到岗与 `T-P0-21` 自采流水线成为全项目最优先任务,并要求输出**每周产能数据**用于倒排。配额相关任务去除支付语义(`T-P1-26` 强调 `tx_type` 字段使 V2 免迁移,新增 `T-P1-26b` 文案约束、`T-P5-15` 对账、`T-P5-16` 价格字样扫描)。诊断维度改 32。Phase 6 新增企业开发者认证、一键登录、**小范围真实收费灰度**,并明确 `T-P6-16` 定价的四项前置输入 | diff --git a/docs/技术架构文档.md b/docs/技术架构文档.md index 3bfc9f7..8af7086 100644 --- a/docs/技术架构文档.md +++ b/docs/技术架构文档.md @@ -1,6 +1,6 @@ # 「美搭」技术架构文档 -> 版本:**v1.3** | 更新日期:2026-08-13 | 状态:待技术评审 +> 版本:**v1.4** | 更新日期:2026-08-14 | 状态:待技术评审 > > **本文的定位**:把产品需求翻译成可直接拆解为开发任务的技术方案。读完这份文档,应该能回答三个问题——系统由哪些部分组成、数据长什么样、每条关键链路怎么跑通。 > @@ -185,6 +185,8 @@ | `notify` | 推送、应用内消息、Token 管理 | `push()` `notify_task_done()` | — | | `wardrobe` | 我的衣橱(V2) | — | — | +> **域与包不是一一对应。** 上表按产品职责切(`profile` 含记忆);§16.1 的 `packages/` 按发布与依赖切。记忆有独立表、独立生命周期,且会被 agent / report 同时写入,因此代码上是独立的 `packages/memory`,产品职责仍归 profile 域。跨域只许 import `memory` 的 `service`,不许直接读 `pf_memory`。 + ### 4.2 依赖规则 ``` @@ -344,11 +346,24 @@ kb ← agent / report(读取) | 表 | 关键字段 | V1 | 说明 | | --- | --- | :--: | --- | -| `bl_quota` | `user_id`, `quota_key`, `period_key`, `used`, **`bonus_granted`**, `updated_at`;UNIQUE(`user_id`,`quota_key`,`period_key`) | ✅ | **V1 扣减判定的唯一依据**。周期配额计量(日/月/终身),按 `period_key` 自然重置,**不要写定时清零任务**。`bonus_granted` 物化本周期已获任务奖励,避免每次判定都聚合流水 | -| `bl_quota_rule` | `quota_key`, `period_type`, `base_limit`, `task_bonus_max`, `hard_cap` | ✅ | 配额规则配置化(`FR-BILL-04`),改配置不发版。数值来源:产品方案 §3.2 配额表(唯一事实源) | +| `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_token` UNIQUE, `status`, `verified_at`, `raw_receipt` JSONB | ❌ V2 | `purchase_token` 唯一约束防重复发货(`TD-16`) | @@ -368,7 +383,7 @@ kb ← agent / report(读取) | 表 | 关键字段 | 说明 | | --- | --- | --- | -| `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` | +| `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 诊断维度的存储形态 @@ -996,7 +1011,7 @@ get_memory_snapshot(user_id, budget_tokens) ### 16.1 仓库结构 ``` -meida/ +meida_backend/ # 后端仓库。文档在独立仓库 meida,鸿蒙端尚未建库 ├─ apps/ │ ├─ api/ # FastAPI 应用(路由、依赖注入、中间件) │ ├─ worker/ # Celery 任务定义 @@ -1031,10 +1046,11 @@ meida/ │ ├─ integration/ # 带 DB/Redis(testcontainers) │ └─ golden/ # 黄金集回归(检索/规则/报告) ├─ deploy/ # Dockerfile / compose / CI 配置 -├─ docs/ # 本文档集 -└─ client-harmony/ # 鸿蒙 ArkTS 工程 +└─ scripts/ ``` +文档集在 `meida` 仓库(`docs/`),不进本仓库。鸿蒙客户端独立建库,不预置空目录。 + **包内约定**:每个业务域包统一为 `service.py`(用例编排,对外唯一入口)+ `repo.py`(该域自己的表访问) + 可选 `schemas.py`。§4.2 的「禁止直接查对方的表」因此落为一条可机检规则: **跨域只允许 import 对方的 `service`,不允许 import 对方的 `repo`。** @@ -1149,6 +1165,7 @@ meida/ | 日期 | 版本 | 变更 | | --- | --- | --- | +| 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 风险登记:备案风险关闭,新增备案范围一致性、素材自采产能、顾问到岗三项风险 | diff --git a/docs/用户系统与记忆系统-需求文档.md b/docs/用户系统与记忆系统-需求文档.md index 1f5680b..bf2f223 100644 --- a/docs/用户系统与记忆系统-需求文档.md +++ b/docs/用户系统与记忆系统-需求文档.md @@ -54,32 +54,43 @@ ### 2.2 用户数据模型 -#### 用户主表 `user` +> 表名与列清单以《技术架构文档》§6.2 为唯一事实源。本节只写产品语义,**不再自造表名**。 -| 字段 | 类型 | 说明 | -|------|------|------| -| `user_id` | string | 全局唯一 ID(雪花号) | -| `phone` | string | 手机号(加密存储,脱敏展示) | -| `huawei_open_id` | string | 华为账号标识 | -| `nickname` / `avatar` | string | 昵称、头像 | -| `gender` | enum | 女 / 男 / 不便告知 | -| `birthday` | date | 生日。**用途限于生日权益与年龄段判断**,五行推算已取消(`TD-31`) | -| `member_level` | enum | free / member / premium。**V1 恒为 `free`**,字段预留 | -| `member_expire_at` | datetime | 会员到期时间。**V1 恒为空**,字段预留 | -| ~~`points_balance`~~ | — | **不放在主表**。余额统一存 `bl_wallet.balance`,以 `bl_ledger` 流水为真相(架构 §6.2)。在主表冗余余额会引入两处数据不一致的风险,而余额是最不能出错的字段 | -| `register_channel` | string | 注册渠道(归因) | -| `status` | enum | active / frozen / deleting / deleted | -| `created_at` / `last_active_at` | datetime | — | +#### 账号主表 `id_user` -#### 授权记录表 `user_consent`(合规必需) +内部主键与账号状态,与外部身份解耦。 | 字段 | 说明 | |------|------| -| `consent_type` | 隐私政策 / 人脸信息处理 / 个性化推荐 / 推送通知 | -| `version` | 协议版本号 | +| `user_id` | 雪花号,全局唯一。**不用手机号或 UnionID 做主键** | +| `nickname` / `avatar_key` | 昵称、头像 | +| `register_source` | 注册渠道(归因) | +| `status` | `active` / `frozen` / `deleting` / `deleted` | +| `created_at` / `last_active_at` | — | + +**不在本表的字段(此前误画在主表上)**: + +| 数据 | 实际位置 | 原因 | +| --- | --- | --- | +| 手机号 | `id_user_identity`(`provider='phone'`,`phone_enc` + `phone_hash`) | 登录凭证,不是用户属性;加密与检索要和华为身份走同一张映射表 | +| 华为账号 | `id_user_identity`(`provider='huawei'`,`union_id` / `open_id`) | 同上 | +| 性别 / 生日 | 画像与问卷(`pf_profile` / 报告问卷),不进账号主表 | 生日用途限于权益与年龄段,五行推算已取消(`TD-31`) | +| 会员档位 / 到期 | V2 的 `bl_membership`;V1 不建 | V1 全员同一套配额,主表预留会员字段会造成「看起来能升级」的假接口 | +| 余额 | `bl_wallet.balance`,以 `bl_ledger` 为真相 | 主表冗余余额会引入两处不一致 | + +#### 登录身份表 `id_user_identity` + +一个用户可有多条外部身份。`UNIQUE(provider, union_id)`。手机号检索用 `phone_hash`,展示用解密后掩码。 + +#### 授权记录表 `id_consent`(合规必需) + +| 字段 | 说明 | +|------|------| +| `consent_type` | `privacy` / `face` / `personalization` / `push` / `minor`。**人脸必须独立成行** | +| `doc_version` | 协议版本号 | | `granted` | 是否同意 | | `granted_at` / `revoked_at` | 授权与撤回时间 | -| `evidence` | 授权来源页面与操作记录 | +| `client_ip` / `ua` | 授权来源留证 | > 人脸信息属敏感个人信息,必须**单独授权**并可**单独撤回**;撤回后停止相关功能并删除派生数据。 @@ -340,15 +351,15 @@ ### 3.9 存储与技术形态 -> 选型结论见《[技术选型与调研报告](./技术选型与调研报告.md)》`TD-02`/`TD-08`;表结构见《[技术架构文档](./技术架构文档.md)》§5.2。 +> 选型结论见《[技术选型与调研报告](./技术选型与调研报告.md)》`TD-02`/`TD-08`;表结构见《[技术架构文档](./技术架构文档.md)》§6.2。 | 数据 | 存储 | 说明 | |------|------|------| -| L0 / L1 / L2 结构化画像 | PostgreSQL | 强一致、可索引、可审计 | +| L0 / L1 / L2 结构化画像 | PostgreSQL(`pf_profile` / `pf_diagnosis` / `pf_memory`) | 强一致、可索引、可审计 | | L3 事件与对话摘要 | PostgreSQL + pgvector | 同一库内完成硬过滤与语义召回,不引入专用向量库 | -| L4 会话状态 | Redis(带 TTL) | 高频读写 | +| L4 会话状态 | Redis(带 TTL)+ `ag_conversation.rolling_summary` | 高频读写 | | L5 衣橱单品 | PostgreSQL + COS(图片)+ pgvector(图像向量) | 支持图搜与属性筛选 | -| 变更审计 | 追加写日志表 `memory_audit` | 记录每次记忆变更的 before/after 与操作者 | +| 变更审计 | 追加写 `pf_memory_event` | 记录每次记忆变更的 before/after 与操作者 | - 端侧仅缓存画像**摘要**,支持弱网/离线时的基础个性化展示;**不缓存记忆明文**(含体型尺寸等敏感项) - 所有记忆读写走统一 `MemoryService`,禁止各业务模块直连表,保证冲突消解与审计逻辑唯一 @@ -436,7 +447,7 @@ | **V1 是否收费** | **完全不收费**(`TD-28`)。无会员、无点数购买、无 IAP。用配额硬限制控成本,额度用尽只有「等恢复」与「做任务换额度」两条路径,文案不出现价格字样 | `TD-28`、`FR-BILL-07` | | **登录方式** | V1 用「华为账号授权登录 + 手机号验证码」双通道;**一键登录降级为 P1 推至 V2**(需企业资质,属体验优化非能力缺口) | `TD-29`、`FR-ACC-13` | -| **余额字段位置** | 余额存 `bl_wallet`,**不在 `user` 主表冗余** `points_balance`;以 `bl_ledger` 流水为真相 | 架构 §6.2 | +| **余额字段位置** | 余额存 `bl_wallet`,**不在 `id_user` 冗余** `points_balance`;以 `bl_ledger` 流水为真相 | 架构 §6.2 | | **配额账本与点数账本的关系** | **同一张表**。V1 用 `system_grant`/`consume`/`refund` 三种 `tx_type`,V2 增加 `purchase`/`gift`,V2 上支付**无需数据迁移** | 架构 §6.2、§7.4 | ### 6.2 仍待业务输入 @@ -461,12 +472,13 @@ --- -**文档版本**:v1.2 +**文档版本**:v1.3 **创建日期**:2026-08-11 -**更新日期**:2026-08-13 +**更新日期**:2026-08-14 **作者**:产品团队 **状态**:待评审 **变更说明**: +- v1.3(2026-08-14):§2.2 / §3.9 表名与架构 §6.2、实际代码对齐。原 `user` / `user_consent` / `memory_audit` 以及把手机号、华为账号画在主表上,是概念模型残留;现改为 `id_user` / `id_user_identity` / `id_consent` / `pf_memory_event`,并写明性别生日归画像、会员归 V2 `bl_membership` - v1.2 rev.2(2026-08-13):文档一致性评审修订 - **§3.8 的记忆注入分项预算被采纳为全局唯一事实源**。评审发现架构 §7.2 与本节存在两套口径(会话 400 vs 800、合计 3700 vs 1400),**错在架构表** —— 本节的 800 是「最近 6 轮原文 + 更早轮次滚动摘要」,架构表只算了滚动摘要、漏掉近轮原文。故以本节分项为准,架构侧总输入合计由 3700 修正为 **4200**;`FR-MEM-04` 的「≤ 500」为误写,已改为 ≤ 1400(V1 实际 ≤ 1200,L3 为 V2 能力) - **§2.4.1 配额数值改为引用产品方案 §3.2**,不再复述。同时**游客额度已补入产品方案 §3.2 的配额表**(此前只写在本文 §2.1 的叙述里,导致 `FR-ACC-02` 的验收标准无法判定),并明确它是**设备维度的终身额度而非每日额度** diff --git a/docs/项目整体评审报告-2026-08-14.md b/docs/项目整体评审报告-2026-08-14.md new file mode 100644 index 0000000..a2b2bc3 --- /dev/null +++ b/docs/项目整体评审报告-2026-08-14.md @@ -0,0 +1,212 @@ +# 「美搭」项目整体评审报告 + +> 评审日期:2026-08-14 | 评审人:AI 辅助评审 +> 评审范围:**两个仓库全量** —— 文档仓库 `G:\meida`(13 份文档 + reference 资料)与后端仓库 `meida_backend`(WSL `/home/xsl/meida_backend`,HEAD `c97f8ae`,10 个提交) +> 评审方法:文档逐份通读 + 后端代码结构/核心模块审查 + **文档 ↔ 代码交叉核对**(表名、字段名、种子数据、API 路径、配额数值)+ 对《文档评审报告-2026-08-13》所列 P0 修复项的落地抽查 +> 本文性质:评审反馈,不修改任何被评文档与代码;问题定位到文档章节或代码文件,供修订时对照 + +--- + +## 0. 总体结论 + +**这个项目的整体质量显著高于同阶段的常见水平,且"自我认知"准确 —— 文档没有夸大、代码没有虚报。主要风险不在工程能力,而在三处:一个躺在工作区里的 API 密钥、一批"文档说改了但代码没改(或反之)"的漂移点、以及既定的内容产能关键路径。** + +分项结论: + +| 维度 | 评级 | 一句话 | +| --- | --- | --- | +| 文档体系 | **A-** | 四层结构 + 单一事实源纪律执行到位,上轮评审的 P0 修复**抽查全部落地**;遗留少量表名/仓库名漂移 | +| 后端架构与工程化 | **A** | import-linter 四契约、CI 六道防线、迁移往返、双密钥扫描 —— "用机器替 review 守纪律"的做法少见地完整 | +| 后端实现完成度 | **B(符合预期)** | 工程地基生产级 + 一条真花钱验证过的纵向切片;业务域 9 个包刻意占位,与 Sprint 计划的自我标注(🟡)一致,无虚报 | +| 文档 ↔ 代码一致性 | **C+** | **配额种子数据与唯一事实源不符、`points_reserved` 改名"假回写"** 两处必须修;另有若干小漂移 | +| 安全卫生 | **⚠️ 有一个 P0** | `reference/deepseek.txt` 存有 API 密钥,未被 .gitignore 覆盖,`git add -A` 即入库 | + +--- + +## 1. 项目现状全景 + +### 1.1 文档仓库(G:\meida) + +13 份文档构成四层体系(为什么做 → 做什么 → 怎么做 → 谁来做/何时做),7 套编号(FR/NFR/CR/TD/ADR/T/R),README §4.1 建立"单一事实源索引"锁定 13 类关键数字的唯一定义位置。关键业务前提已全部收口:备案已完成(转持续义务)、V1 完全不收费(配额为唯一成本闸门)、素材全部自采(成为新关键路径)、诊断维度 32 项、五行/命理永久不做。 + +### 1.2 后端仓库(meida_backend) + +| 层 | 状态 | +| --- | --- | +| `apps/`(api / worker / admin) | api ✅(10 个路由,1 条纵向切片)、worker ✅(ping 任务 + 4 队列)、admin 🟦 占位 | +| `packages/`(20 个包) | domain / llm / shared / identity / billing / task / agent ✅ 实现;compliance ⚠️ 仅接口(全 `NotImplementedError`);kb / memory / notify / profile / report / retrieval / rules / tryon / wardrobe 🟦 占位(README + 空 `__init__`) | +| `services/`(vision / matting / tryon) | 🟦 全占位 | +| 数据库 | 2 个迁移:0001 建 **8 张表** + pgvector 扩展(向量列未建);0002 给 `ob_ai_call` 加 `tokens_reasoning`/`rate_tier` | +| 测试 | 142 个测试函数(16 个单测文件 + 1 个集成切片);`scripts/smoke.sh` 真调 DeepSeek 端到端 | +| 工程化 | ruff + mypy(domain/llm strict)+ import-linter(4 契约)+ pytest + alembic 往返 + detect-secrets/gitleaks,CI 单 job 六道防线 | + +**已打通的真实链路**:`POST /api/v1/agent/chat` → JWT 校验 → 配额预扣(`bl_ledger` reserved 行)→ 真实调用 DeepSeek(场景路由 / 熔断器 / 峰谷计价 / 思考模式显式关闭)→ 成本行落 `ob_ai_call`(独立事务)→ 配额 commit / 失败自动 refund。 + +--- + +## 2. 文档体系评审 + +### 2.1 做得好的(应保持) + +1. **单一事实源纪律真的执行了,不只是口号**。抽查验证:FR-BILL 引用产品方案 §3.2 而不复述数值;用户系统 §2.4.1 明确"本节不再复述数值";架构 §7.2 token 预算收口 4200/1400 并写明修正原因(原表漏算近轮原文)。 +2. **上轮评审(2026-08-13)的修复可信**。抽查 5 项 P0:维度 34→32(架构 §6.2 已改)、可用性 99.9%→99.5%(全库无 99.9% 残留)、人脸保留期 24h→2h(美学报告 §9.1 显式标注废弃原因)、token 两套口径→4200/1400(含错因分析)、流水类型两种→三种(FR-BILL-02 原文更正并留痕)。**全部落地**。 +3. **风险记录是真实调研出来的**,不是模板:腾讯云人体关键点接口下线(2025-10-30)迫使自建 RTMPose、RMBG-2.0 CC BY-NC 许可禁用改 BiRefNet、AI 标识元数据会被 CDN/压缩剥离、DeepSeek 2026-08-17 峰谷涨价(空闲 ¥1.5/¥4.5、高峰 ¥3/¥9 每百万 token)——且都给出了应对与任务编号。 +4. **对代理指标的诚实**:明确"预约 ≠ 付费"、V1 不收费意味着付费意愿不可证伪、"配额封不住总成本"的自我纠错(架构 §13 改为三口径)。这类不自欺的表述是文档可信度的来源。 +5. **Sprint 计划对已交付部分的验收标注与代码实况相符**(🟡 标注诚实,见 §4.3 核对)。 + +### 2.2 遗留问题 + +| # | 级别 | 问题 | 位置 | 说明 | +| --- | --- | --- | --- | --- | +| D-1 | **P1** | **用户系统文档表名未与架构/代码对齐** | 用户系统 §2.2、§3.9 | 仍用 `user`/`user_consent`/`memory_audit` 表名叙述,`phone`/`huawei_open_id` 画在主表;而架构 §6.2 与实际代码是 `id_user`/`id_consent`/`pf_memory_event`,手机号在 `id_user_identity`(phone_enc + phone_hash)。上轮修了枚举与置信度(已对齐),**表名这层漏了** | +| D-2 | P2 | 架构 §16.1 仓库根目录写 `meida/`,实际为 `meida_backend` | 技术架构文档:999 | 其他文档(任务拆解、Sprint 计划)均已用 `meida_backend` | +| D-3 | P2 | Sprint 计划 §1.1 写"`0001` 建 **9** 张表",实际 **8** 张 | Sprint执行计划.md §1.1 | 迁移文件 `op.create_table` 计数 = 8 | +| D-4 | P2 | memory 是 profile 域子模块还是独立包,架构 §4.1 与 §16.1 口径不一 | 技术架构文档 | §4.1 列 11 个业务域(profile 含记忆),§16.1 的 packages 把 `profile/` 与 `memory/` 列为两个包;实际代码是两个独立包(与 §16.1 一致)。建议在 §4.1 补一句判别口径 | +| D-5 | 备注 | 鉴权细节(refresh token 轮换、JWT claims、服务间令牌签发)与 API 限流阈值在文档层仍偏薄 | 架构 §9/§11 | 属 T-P1-13 与压测(T-P5-06)范围,开发前需细化 | + +--- + +## 3. 后端代码评审 + +### 3.1 做得好的(生产级,保持) + +1. **架构纪律由机器强制,不靠自觉**:import-linter 四契约(分层单向、domain 禁框架/IO、业务包禁直连 provider、apps 互不依赖)进 CI;`packages/domain` 是纯算术(雪花 ID 含时钟回拨处理、配额三段式、任务状态机),mypy strict。 +2. **LLM 网关是全仓库最完整的一块,且踩点准确**: + - **峰谷计价赶在 2026-08-17 生效前实现**(`PeakAwareRateTable` 按调用时刻北京时间判档,`rate_tier` 与 `cost_cents` 同刻落库,事后不重算); + - **思考模式显式开关**(V4 默认开启,实测开启 13.5s / 关闭 1.8s,思考 token 按输出价计费——四个场景全部显式关闭); + - 成本行**独立事务落库**(请求回滚时成本行仍存活)+ Prometheus 双写; + - prompt 缓存前缀纪律由 `validate_cache_prefix()` 静态扫描强制(volatile token 检测),strict 模式直接抛错; + - 4xx 不可重试(避免在备用 provider 烧两次钱)、熔断器三态可注入 clock、结构化输出重试把模型回答带回修正。 +3. **配额三段式正确**:reserve/commit/refund + 幂等键派生 + `balance_after` 流水可重算对账;DeepSeek 超时自动退还配额(集成测试覆盖)。 +4. **错误码体系**:4xxxx 客户端 / 5xxxx 服务端 / **6xxxx 外部依赖**(把"我们的 bug"与"厂商故障"分开),配额耗尽文案经测试断言**不含价格字样**(FR-BILL-07 的验收在测试里真实生效)。 +5. **占位是显式的、带启用前置的**:智谱 provider 与 compliance 全部 `raise NotImplementedError` 并注明启用条件(备案覆盖核实、审核+水印+元数据齐备)。"宁可不实现也不半实现"(half-working moderation looks like a working one)这个判断正确。 + +### 3.2 代码问题与风险 + +| # | 级别 | 问题 | 位置 | 说明与建议 | +| --- | --- | --- | --- | --- | +| C-1 | **P1** | **CI 不跑集成切片** | `.github/workflows/ci.yml:88` | CI 的 pytest 步骤未设 `MEIDA_RUN_INTEGRATION=1`,而 pytest 默认 `-m 'not integration'`——**集成切片在 CI 里被静默跳过**。讽刺的是 CI 明明起了 pgvector + Redis service 容器(目前只服务于 alembic 往返)。建议在 CI 加一步 `MEIDA_RUN_INTEGRATION=1 uv run pytest -m integration`(容器已就位,零额外成本),否则纵向切片回归只靠本地 `make integration` | +| C-2 | **P1** | **四档熔断降级未实现,且当前熔断位置与 NFR-COST-02 的退款要求需验证** | `packages/llm/budget.py` | 当前只有单一日预算阈值(`LLM_DAILY_BUDGET_CENTS`)+ critical 场景豁免;60/80/90/100% 四档降级是 T-P1-25b 待补(Sprint 计划已列,属实)。需注意:**若 `DailyBudgetExceededError` 在配额 reserve 之后抛出,必须走 refund**——建议给 T-P1-25b 的验收加一条"被拒请求账本净变动 = 0"的集成断言(Sprint §1.2 已写此验收,落实现时别丢) | +| C-3 | **P1** | **日预算阈值未按 8-17 峰谷新价重算** | `.env.example`(`LLM_DAILY_BUDGET_CENTS=100000`) | Sprint §1.3 已自我提醒(输出侧涨 2.25~4.5 倍),此处登记为待办:**8-17 之后用实测 token 分布重算**,否则熔断会在错误位置触发 | +| C-4 | P2 | 集成测试仅 1 个文件 3 个用例,FakeProvider 替换真实 provider | `tests/integration/` | 真实 provider 行为(超时映射、429、reasoning token 解析)只有单测级的 respx mock 覆盖。可接受,但 C-1 修复后建议补"预算熔断 + 退款"与"幂等键重放"两个集成用例 | +| C-5 | P2 | `ob_ai_call` 无保留/归档策略 | 迁移 0001 | 成本明细表只增不减,长期需要分区或归档策略(V2 定价依赖它,不能简单清理)。可在架构 §6.2 补一句保留口径 | +| C-6 | 备注 | dev-token 双开关(`DEV_AUTH_ENABLED` && `is_local`)设计正确,但上线检查清单应含"确认 prod 无 dev-token 路由" | `apps/api/routers/auth.py` | 建议纳入 T-P5 合规检查清单 | + +### 3.3 与 Sprint 计划自我标注的核对(抽验) + +Sprint §1.1 的 🟡 标注与代码实况**逐条相符**:T-P1-03 确缺 COS mock、T-P1-08 确无 CLS 看板、T-P1-19 确只有 ping 骨架任务、T-P1-26 确无周期重置后台任务(设计上靠 `period_key` 自然重置,"缺后台"实际指任务奖励发放 `bonus` 递增链路未建)。**没有虚报完成度。** + +--- + +## 4. 文档 ↔ 代码一致性核对(本次评审的核心增量) + +上轮评审只做了"文档 ↔ 文档"比对;本轮首次把**实际代码**纳入核对。以下按严重程度排列。 + +### 4.1 🔴 必须修的漂移 + +#### X-1 配额种子数据与"唯一事实源"不符(P0) + +README §4.1 与产品方案 §3.2 均声称:**`bl_quota_rule` 的初始化数据一律引用产品方案 §3.2 配额表**。但迁移 `0001_initial_foundation.py` 的种子数据(约 207-235 行)与该表对照: + +| 能力 | 文档(产品方案 §3.2) | 代码种子(bl_quota_rule) | 差异 | +| --- | --- | --- | --- | +| AI 对话 | 每日 **10** 轮 / +10 / 硬上限 **40** | `agent_chat`: day / **20** / 10 / **50** | **base 是文档 2 倍,hard 多 10** | +| 换装 | 每日 3 / +3 / 硬上限 **8** | `tryon`: day / 3 / 3 / **10** | hard 不符(10 vs 8) | +| 报告 | 首次 **1 次(终身)**;重新生成每 30 天 1 次 / +1 / **每日 ≤ 2** | `report_basic`: **month** / 1 / 1 / **3** | 把"首次终身"与"每 30 天重新生成"两个口径揉成一个 key;周期口径(每日 ≤2 vs hard 3)不符 | +| 发型/发色试戴 | 每日 3 / +2 / ≤6 | **无此 quota_key** | 缺失 | +| 高清导出 | 每日 1 / — / ≤3 | **无此 quota_key** | 缺失 | +| 游客配额 | 换装 1 / 试戴 1 / 报告 1 / 对话 3(设备维度终身) | **无承载** | 缺失(设备维度计量链路未建,V1 骨架可接受,但应显式登记) | + +**处理建议**:二选一并留痕——① 按纪律以产品方案 §3.2 为准修正种子(现在表里没有生产数据,改动成本≈0);② 若 20/50 是开发期故意放宽,应通过 `bl_quota_rule` 的**运行时配置覆盖**实现(该表本就是配置表),并在文档变更记录登记理由,不能让种子数据成为第二套口径。 + +#### X-2 `points_reserved` → `quota_reserved` 是"假回写"(P0) + +架构文档 v1.3 变更记录与 README §8 均声称已把 `tk_task.points_reserved` 改名为 `quota_reserved`(清除 V2"点数"措辞残留,P2-4 修复项)。**但实际迁移与 ORM 仍是 `points_reserved`**(`migrations/versions/0001_initial_foundation.py:153`、`packages/shared/models.py`)。文档声称与实现相反——要么改代码(推荐,趁表里无数据),要么回退文档表述。 + +#### X-3 `bl_quota` 列名与架构文档不一致(P1) + +架构 §6.2/§7.4 明确:`bl_quota` 列为 `used`、**`bonus_granted`**(物化任务奖励,判定公式 `base_limit + bonus_granted`);实际代码列为 `used`、`limit_value`、**`bonus_value`**——名字不同、语义相近,且文档列清单里没有 `limit_value`、代码里没有文档写的列。任务奖励发放链路(T-P1-26 的剩余部分)实现时会直接撞上这处歧义。 + +### 4.2 🟡 核对通过的关键点(列出来给后续评审省时间) + +| 核对项 | 结论 | +| --- | --- | +| API 基础路径 `/api/v1`(规范)+ `/v1`(别名) | ✅ 代码用中间件实现别名,与架构 §9.1 v1.3 澄清一致 | +| 表名域前缀(id_/bl_/tk_/ob_) | ✅ 代码与架构 §6.2 一致(不一致的是用户系统文档,见 D-1) | +| 流水类型 `system_grant/consume/refund` 三种 | ✅ 代码与 FR-BILL-02 修正后口径一致 | +| `ob_ai_call` 四列(prompt 版本/思考 token/峰谷档位) | ✅ 0002 迁移 + ORM + recorder 均落地,与架构 v1.3 回写一致 | +| 峰谷时段(09-12/14-18 北京时间)与单价结构 | ✅ `cost.py`/`.env.example` 与 Sprint §1.3、架构 §13.2 一致 | +| 思考模式四场景显式关闭 | ✅ `routing.py` V1 全 `thinking=False`,与 T-P1-24a 一致 | +| 错误码文案不含价格字样(FR-BILL-07) | ✅ 有专项测试断言 | +| 日预算 key 按 UTC+8 日滚动 | ✅ 与 FR-BILL-01"跨时区按 UTC+8 统一"一致 | + +--- + +## 5. 🔴 安全问题(独立成节,优先级最高) + +**`G:\meida\reference\deepseek.txt` 是一个 35 字符的 DeepSeek API 密钥**(`sk-` 开头),当前状态: + +- 未被 git 跟踪,但**也未被 `.gitignore` 覆盖** —— 一次 `git add -A && git push` 就会把它推到远端; +- 该仓库已有 remote(origin)。 + +**立即处理**(按顺序): +1. 把该密钥从文件中移除,改放后端 `.env`(后端仓库的 `.gitignore` 已正确排除 `.env`,且 CI 有 detect-secrets + gitleaks 双扫——文档仓库没有这层防护); +2. 若该密钥已在任何地方暴露过,去 DeepSeek 控制台**作废重发**; +3. 在文档仓库加 `.gitignore`(至少覆盖 `reference/*.txt` 或明确列该文件); +4. 顺带清理:`reference/` 下的临时资料建议与正式文档分离,避免下次再混入敏感物。 + +这也暴露一个流程缺口:**文档仓库没有 pre-commit/密钥扫描**(后端仓库有)。文档仓库虽不含代码,但正是密钥、备案材料这类敏感物容易先落在文档区的地方。 + +--- + +## 6. 风险总览(按当前时点重排) + +| 风险 | 等级 | 现状与依据 | +| --- | --- | --- | +| 密钥泄漏(§5) | 🔴 立即 | 未处理即存在 | +| 配额/字段漂移(X-1/X-2/X-3) | 🔴 本周 | 表里尚无生产数据,修错成本每周上升 | +| 备案模型清单未核实(T-P0-01) | 🔴 既定 | README §0.2 第 1 项;影响双路由合法性 | +| 内容产能(顾问到岗 + 300 件自采) | 🔴 既定关键路径 | 无新技术判断,按 Sprint §6 倒排执行 | +| 8-17 峰谷涨价 | 🟡 已知 | 代码已实现峰谷计价;剩"重算日预算阈值"(C-3) | +| 集成回归缺口(C-1) | 🟡 | CI 容器已就位,修复成本低 | +| 四档熔断未实现(C-2) | 🟡 | T-P1-25b 已列,注意退款验收 | +| 算法验证门槛(体型/季型/脸型) | 🟡 既定 | Sprint 1 结论不达标要换方案,不是硬做 | + +--- + +## 7. 建议行动清单(按优先级) + +**立即(今天)** +- [ ] 处理 `reference/deepseek.txt` 密钥:移除 + 视暴露情况作废重发 + 补文档仓库 `.gitignore` + +**本周(Sprint 0 内)** +- [ ] 决策并执行 X-1:以产品方案 §3.2 修配额种子(含补 hairstyle/hd_export 两个 key),或以配置覆盖 + 文档留痕的方式登记偏差 +- [ ] X-2:`points_reserved` → `quota_reserved`(迁移 + ORM + repo 同步改,趁无数据) +- [ ] C-1:CI 增加 `MEIDA_RUN_INTEGRATION=1 pytest -m integration` 步骤 +- [ ] X-3/D-1:对齐 `bl_quota` 列名与用户系统文档表名(两处都是"文档 vs 代码"各改一半) + +**8-17 峰谷生效后** +- [ ] C-3:用实测 token 分布重算 `LLM_DAILY_BUDGET_CENTS` 与四档阈值 + +**随下一轮文档更新** +- [ ] D-2(仓库根目录名)、D-3(9→8 张表)、D-4(memory 包归属口径)、C-5(ob_ai_call 保留策略)、C-6(prod 无 dev-token 加入上线清单) + +--- + +## 附 A:评审覆盖清单 + +| 对象 | 方式 | 深度 | +| --- | --- | --- | +| docs/ 全部 13 份文档 | 通读(README/评审报告/Sprint/需求规格 §4-5/产品方案 §3.2 逐行;其余 8 份结构化摘要 + 关键节抽读) | 中-高 | +| 上轮评审 P0 修复项 | 抽查 5 项(34→32、99.9%、24h、4200/1400、三种流水) | 全部落地 | +| meida_backend 全部 10 提交 | 结构化探索 + 配置/迁移/网关/worker/测试逐模块 | 中-高 | +| 迁移种子数据、字段名、表数 | 直接读迁移文件逐行核对 | 高 | +| CI/测试门控 | 读 ci.yml + Makefile + 测试门控逻辑 | 高 | +| reference/ | 清点(发现密钥文件) | 高 | + +**未覆盖**(后续评审可补):后端单测断言质量逐条审读;`.env` 实际内容(仅看 example,避免接触真实密钥);鸿蒙端侧代码(尚无仓库);`reference/个人形象报告.pdf` 内容与美学报告维度字典的映射关系。 + +--- + +**评审人**:AI 辅助评审(ZCode) +**说明**:文档侧结论基于文本交叉比对;代码侧结论基于 HEAD `c97f8ae`。X-1 的配额数值差异存在"开发期有意放宽"的可能解释,需负责人确认意图后二选一处理。 diff --git a/reference/README.md b/reference/README.md new file mode 100644 index 0000000..a6fe57a --- /dev/null +++ b/reference/README.md @@ -0,0 +1,7 @@ +# reference/ + +本目录只放**不含密钥**的参考资料(公开文档摘录、第三方接口说明的本地副本)。 + +- 密钥、Token、备案原件扫描件**不要放这里**。DeepSeek / 智谱等 API Key 只写在后端仓库的 `.env`(该仓库已 gitignore,且 CI 有 detect-secrets + gitleaks)。 +- `*.txt` / `*.key` / `*.pem` 已被仓库根目录 `.gitignore` 排除。 +- 需要给同事看的正式结论写进 `docs/`,不要在这里堆积会过期的草稿。