表名、配额种子和字段名以产品方案与架构为准;文档仓库补 .gitignore,避免 git add -A 把 API Key 推进远端。 Co-authored-by: Cursor <cursoragent@cursor.com>
19 KiB
「美搭」项目整体评审报告
评审日期:2026-08-14 | 评审人:AI 辅助评审 评审范围:两个仓库全量 —— 文档仓库
G:\meida(13 份文档 + reference 资料)与后端仓库meida_backend(WSL/home/xsl/meida_backend,HEADc97f8ae,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 做得好的(应保持)
- 单一事实源纪律真的执行了,不只是口号。抽查验证:FR-BILL 引用产品方案 §3.2 而不复述数值;用户系统 §2.4.1 明确"本节不再复述数值";架构 §7.2 token 预算收口 4200/1400 并写明修正原因(原表漏算近轮原文)。
- 上轮评审(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 原文更正并留痕)。全部落地。
- 风险记录是真实调研出来的,不是模板:腾讯云人体关键点接口下线(2025-10-30)迫使自建 RTMPose、RMBG-2.0 CC BY-NC 许可禁用改 BiRefNet、AI 标识元数据会被 CDN/压缩剥离、DeepSeek 2026-08-17 峰谷涨价(空闲 ¥1.5/¥4.5、高峰 ¥3/¥9 每百万 token)——且都给出了应对与任务编号。
- 对代理指标的诚实:明确"预约 ≠ 付费"、V1 不收费意味着付费意愿不可证伪、"配额封不住总成本"的自我纠错(架构 §13 改为三口径)。这类不自欺的表述是文档可信度的来源。
- 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 做得好的(生产级,保持)
- 架构纪律由机器强制,不靠自觉:import-linter 四契约(分层单向、domain 禁框架/IO、业务包禁直连 provider、apps 互不依赖)进 CI;
packages/domain是纯算术(雪花 ID 含时钟回拨处理、配额三段式、任务状态机),mypy strict。 - 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、结构化输出重试把模型回答带回修正。
- 峰谷计价赶在 2026-08-17 生效前实现(
- 配额三段式正确:reserve/commit/refund + 幂等键派生 +
balance_after流水可重算对账;DeepSeek 超时自动退还配额(集成测试覆盖)。 - 错误码体系:4xxxx 客户端 / 5xxxx 服务端 / 6xxxx 外部依赖(把"我们的 bug"与"厂商故障"分开),配额耗尽文案经测试断言不含价格字样(FR-BILL-07 的验收在测试里真实生效)。
- 占位是显式的、带启用前置的:智谱 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)。
立即处理(按顺序):
- 把该密钥从文件中移除,改放后端
.env(后端仓库的.gitignore已正确排除.env,且 CI 有 detect-secrets + gitleaks 双扫——文档仓库没有这层防护); - 若该密钥已在任何地方暴露过,去 DeepSeek 控制台作废重发;
- 在文档仓库加
.gitignore(至少覆盖reference/*.txt或明确列该文件); - 顺带清理:
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 的配额数值差异存在"开发期有意放宽"的可能解释,需负责人确认意图后二选一处理。