表名、配额种子和字段名以产品方案与架构为准;文档仓库补 .gitignore,避免 git add -A 把 API Key 推进远端。 Co-authored-by: Cursor <cursoragent@cursor.com>
213 lines
19 KiB
Markdown
213 lines
19 KiB
Markdown
# 「美搭」项目整体评审报告
|
||
|
||
> 评审日期: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 的配额数值差异存在"开发期有意放宽"的可能解释,需负责人确认意图后二选一处理。
|