Files
meida/docs/项目整体评审报告-2026-08-14.md
T
xslandCursor 2934954eb2 按交叉评审对齐文档与代码口径,并挡住 reference 下的密钥文件
表名、配额种子和字段名以产品方案与架构为准;文档仓库补 .gitignore,避免 git add -A 把 API Key 推进远端。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 11:28:35 +08:00

213 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 「美搭」项目整体评审报告
> 评审日期: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 的配额数值差异存在"开发期有意放宽"的可能解释,需负责人确认意图后二选一处理。