# r2-architect-reviews-others.md · 架构师评其他主笔 **作者视角**: 架构师(R1 已写 r1-architect.md)从架构/数据模型/性能预算/模块边界角度,审视 PM 和工程师的 R1 draft **日期**: 2026-05-23 **5 个评审角度**: 1. PM 的成功指标是否能在我设计的数据模型里取到 2. PM 的 US-N 是否需要新实体/接口但 PM 没意识到 3. 工程师的边界异常是否符合数据一致性约束 4. 工程师的性能要求是否跟我的性能预算一致 5. 工程师 §5 是否符合我设计的模块划分边界(是否跨模块直接调用) --- ## 架构师评 PM 章节(r1-pm.md) ### [需要改] P-A1 · §8.1 "v1 在虚拟数据回测 +10%" 这个北极星指标在我的数据模型里取不到精确数值 PM §8.1 北极星表写:"系统建议带来的总收益提升(相对保守策略)→ 6 月目标 +10%(v1 在虚拟数据回测)"。但我的 `decision_recommendation` 表里只存 `expected_profit_contrib`(**事前**期望值),并没有"如果走系统建议"vs"如果走保守策略"两条**反事实**轨迹的对照。回测要拿到 +10% 数字,必须额外建一张 `backtest_trajectory` 表(per 策略 × per day × per SKU 的 inv/sold/short/cost),这是 FR-11 的内部产出但我 §4.3 schema 草案漏写了,PM 拿这个数字当北极星就把这个隐性依赖摆上了台面 → **R2 我需要补 `backtest_trajectory` schema,PM 需要在 §10.4 兼容性里把"回测数据保留期"补上**(我现行 §4.4.5 保留策略表没这项)。 ### [需要改] P-A2 · §8.3 "事后正确率 ≥ 60%" 的数据延迟跟 §1.2 "缺货天数 -50%" 的统计窗口对不上 PM §8.3 说"事后正确率"按 FR-12 算(工程 §5.13 写"复盘需要 7-14 天后的实际销量");而 §8.1 北极星"缺货天数 -50%" 是月度统计。**问题**:v1 上线 + 4 周里,前 14 天根本没有任何"完整复盘"数据可用(销量未回流),意味着 PM 的"4 周验收"实际只有最后 2 周的 N≈14 条数据样本,"≥ 60%"的统计意义极弱。这不是数据模型问题,是**指标定义与数据生成周期不匹配**。**R2 我建议**:把 §8.3 的"上线后 + 4 周验收"延长到 + 8 周(首次完整复盘窗口 = 4 周决策 + 2 周销量回流缓冲 + 2 周观察),或者明示"前 14 天不计入分母"。 ### [冲突] P-A3 · §10.2.2 "预警延迟 < 1 小时" vs 我的 §6.3.3 实时监控扫描周期 PM §10.2.2 通过标准写"≤ 1 小时延迟";PM §8.1.1 体验指标里同行写"< 1 小时"v1 目标 / "< 15 分钟" v1.5 目标。我 §6.3.3 时序图把"Monitor 每 15 min 扫描 inventory + sales"作为基准(暗含 SLA ≤ 15min + 推送延迟)。**这跟工程师 §5.9 FR-8 边界数值"触发到推送 SLA ≤ 15 min"是完全一致的**,反而 PM 自己 §10.2.2 的"≤ 1 小时"是**最宽松的**那个。三方现在的口径:架构 15 min / 工程 15 min / PM 业务接受 1 小时。**R2 拍板建议**:v1 统一承诺 ≤ 30 min(监控 15 min + 推送 + 渠道重试 buffer),PM §10.2.2 改 30 min,§8.1.1 v1 目标改"< 30 分钟"。PM 边界 B-3 自己也注意到了这个不一致。 ### [OPEN_QUESTION] P-A4 · US-3 "用户改数量立即重算 landed cost" 跟 §3.5 场景法鲁棒优化的 stage-1 决策语义有冲突 PM US-3:用户调数量后系统**立刻**重算这条的 landed cost 和总收益影响。工程 §5.7 FR-6 写"前端送参数 → 后端 /preview-decision API(< 200ms)"。但我 §3.5.2 两阶段决策里,stage-1 决策(t=0 的 x_sea / x_air)是**所有 5 个场景共享并满足 non-anticipativity 约束**的——单独修改一条决策的数量,理论上会破坏 stage-2 在其他场景下的可行性(库存平衡 C1、资金池 C6 都是跨 SKU 跨场景耦合的)。**架构上能做的"立即重算"实际上只是固定其他决策不变、单 SKU 重算 landed cost 这一项的成本变化**,**不是**重新求一次全局最优。这跟用户的认知"我改了系统帮我重新算"有差距。**R2 决策**:要么 PM US-3 改文案为"实时显示**这一条决策**的成本变化,全局最优需等下次批跑";要么工程师在 /preview-decision 里明示"仅 single-SKU re-evaluation, not global re-optimization",并在 UI 加 tooltip。 ### [需要改] P-A5 · US-12 "助理 24h 否决权"在 PM 的状态机 §10.3.1 跟我的 §6.2.1 状态机定义不一致 PM §10.3.1 状态机分支只画了"[待助理执行] → [助理 24h 内退回] → [CEO 复核] → [接受/取消]";我 §6.2.1 状态机里画了 `dispatched --> assistant_rejected --> pending`(退回到 pending 而不是 "CEO 复核"独立状态);工程 §5.8 FR-7 写"原决策恢复 pending_review"(**第三个状态名**)。**三个文档三种状态命名**:PM 写"CEO 复核"、架构写"pending"、工程写"pending_review"。**R2 我建议**:状态机以工程师 §5.8 的实现 = 真理来源,统一为 `pending_review`(语义最清晰:区分"全新待决策"和"被助理退回需复核"两种 pending),PM §10.3.1 / 我的 §6.2.1 改字。这是典型的"PM 不知道这是一个独立状态"的情况——它需要在 §4.3.6 `decision_recommendation.status` enum 里新增 `pending_review` 取值,我现行 schema 漏了。 ### [需要改] P-A6 · US-6 "60/90 天上游下单预警" 暗含一个 PM 没意识到的新实体:工厂/供应商主数据 PM US-6 + §10.5 影响域均提到"工厂该下单了"。工程 §5.16 FR-15 写"v1 仅提示'建议尽快下单 X 件'"避开了这个问题。但 PM 的 US-6 文案"留出工厂产能和海运排期"意味着系统需要知道**这个 SKU 是哪家工厂生产、工厂当前产能、起订量、产周期**——这是一个全新实体 `supplier` + 一个 join `sku_supplier`(含 lead_time / MOQ / capacity)。我 §4.2 ER 图和 §4.3 schema 里**完全没有这个实体**。**R2 决策**:要么把 PM US-6 v1 退化到"只输出'X 天后该下单',不算工厂数量/排期"(与工程 §5.16 现状一致,US-6 文案改);要么 R2 我补 `supplier` / `sku_supplier` schema 进 §4.3,工程师补 FR-15 实现细节。我的 §9.C 可扩展性表也漏了"供应商数 1→N"维度。 ### [需要改] P-A7 · §10.4 兼容性表"货币:美元 + 人民币双币种显示" vs 我的 §4.4.3 "内部建模币种 USD" PM §10.4:业务层要求双币种显示。我 §4.4.3 写"内部建模币种 USD(结算货币)",PM 没意识到我已经把 RMB 当作**展示层折算值** + 保留 fx_rate 字段。**这本身没冲突**,但 PM §10.4 兼容性表条目隐含"汇率源 / 汇率更新频率 / 显示精度"三件事,工程 §5.1 通用约定写了"汇率每日 9:00 UTC 拉取,缓存 24h"。问题来了:PM US-1 "5 分钟内拍板"是早上 9:00 BJ 看日报;而汇率"9:00 UTC = 17:00 BJ"——**用户 9:00 BJ 看到的所有 CNY 折算值是昨天 17:00 UTC 之后已经过了 16 小时的汇率**。如果汇率单日波动 0.5%(FBA 业务现金流千万级时不算小),PM 的"避免拍脑袋错误的金额" B1 收益估算需要把这个误差源摆上来。**R2 建议**:工程师 §5.1 把汇率拉取改成"美西时间 23:00 拉取(与日切对齐)",PM 在 §10.4 兼容性"货币"行补一句"展示汇率为日批跑时刻汇率,盘中波动不重新折算"。 --- ## 架构师评 工程师章节(r1-engineer.md) ### [冲突] E-A1 · §5.5 FR-4 求解器选型 SCIP/Gurobi 跟我 §3.2.2 选型 CP-SAT 直接矛盾 工程师 §5.5 写"v1 默认开源 SCIP(Python via pyscipopt);如 license 允许 fallback Gurobi"。**我 §3.2.2 明确推荐 CP-SAT 而不是 SCIP**,理由是 CP-SAT 对启动成本/最小批量这类逻辑约束表达自然 + 并行调度好 + 我在 §3.5.4 已经按 CP-SAT 8 worker 的性能特征算了"5 场景 900K 变量 10-30 min"的性能预算。**工程师章节如果用 SCIP,§3.5.4 性能预算(10-30 min)和 §3.2.4 降级方案(CP-SAT warm start hint)全部失效**。这不是小笔修——SCIP 的 warm-start API、并行 worker 模型、MILP gap 报告都跟 CP-SAT 完全不同。**R2 必须拍板**:要么以我 §3.2.2 为准(工程 §5.5 改 CP-SAT),要么工程师重新跑性能 spike 论证 SCIP 在 5 场景 900K 变量下的实际时间,并重写 §3.5.4 性能预算。我倾向前者——工程师在 §5.5 的 SCIP 选择没有性能数据支撑(只是"如 license 允许 fallback Gurobi"的免费首选反射)。 ### [冲突] E-A2 · §5.5 FR-4 "维度控制:200 SKU × 13 周 × 2 物流 = 5200 整数变量" 跟我 §3.1.1 决策变量定义不一致 工程师 §5.5 写:决策变量是"SKU × 时间窗 t × 物流方式 m",时间窗 t **用周聚合(13 周 = 90 天)降维**到 5200 整数变量。**我 §3.1.1 写的是 200 SKU × 90 天 × 2 物流 = 36000 变量(按日级)**,工程师把它压缩到周级是为了求解性能。**问题**:周级决策跟 PM US-3 "改物流方式立刻重算 landed cost"、PM §10.2.2 "缺货预警 ≤ 1 小时 + 一键空运补货"语义冲突——一键空运补货是**精确到日**的决策,但优化引擎只能输出"第 N 周空运 X 件",**这意味着 5.5 §交互逻辑 5 "后处理:将 t(周)拆分回当周内的最早可发日"是事后启发式拆分,不是数学最优**。我 §3.1.1 假设的"t=0 真正下单"在工程师的周聚合下变成"第 0 周真正下单,但具体哪一天靠后处理",**这是我没意识到的工程妥协**。R2 必须二选一:(a) 接受周级 + PM US-3/US-4 文案改"按周决策";(b) 保留日级 + 工程师在 §5.5 加 SKU 分组并行求解(我 §3.2.4 第二档降级方案)扛性能。 ### [需要改] E-A3 · §5.7 FR-6 "前端送参数 → 后端 /preview-decision API(< 200ms)" 跟我 §3.2.3 性能预算 5 分钟矛盾 工程师 §5.7 写"调整数量"实时重算 landed cost,要求 API < 200ms。**单条决策的 landed cost 评估**确实可以 < 200ms(就是把 §3.1.2 的目标函数 8 个分量按新数量算一遍,不调求解器)。**但工程师 §5.7 还写"实时重算 landed cost / 预估收益 / 置信度变化"** —— "置信度变化" 来自我 §3.4 灵敏度分析,FR-5 §5.6 边界数值写"单场景求解时长(heuristic)≤ 120 s"。**200ms 内不可能算出新的置信度**。**R2 修复**:要么工程师 §5.7 砍掉"置信度变化"实时重算,只算 landed cost + 预估收益(这俩是闭式),置信度沿用上次批跑结果并加 tooltip"置信度按批跑值,调整数量后此值仅供参考";要么前端 debounce 后异步触发灵敏度 heuristic 跑 30-60s 再更新。 ### [冲突] E-A4 · §5.6 FR-5 "灵敏度跑 27 场景 < 60 min(warm-start MIP)" 跟我 §3.2.3 "灵敏度多场景 < 30 分钟" 矛盾 工程师 §5.6 边界数值:"总 batch 时长上限 3600 s(1h)";目标 "27 场景 < 60min"。**我 §3.2.3 性能预算表里"灵敏度多场景(参数 ±20%, 5-7 个场景)< 30 分钟,硬超时 1 小时"**——我假设的是 **5-7 场景** OAT,**工程师扩到 27 场景**(3 关键参数 × 3³ = 27)。3³=27 是 3 参数 × 3 档(low/mid/high)的全组合,这不是 OAT 而是 full factorial。**R2 决策**:要么以我 §3.4 OAT + 关键参数对组合扫描为准(5-7 场景),工程师 §5.6 改"3 关键参数 OAT 6 场景 + 关键 2 参数 9 组合 = 15 场景";要么接受工程师的 27 场景但**必须把 §3.2.3 性能预算的"灵敏度多场景"行从 30 min 改到 60 min**,并验证 CP-SAT warm-start 在 27 场景下的实际效果(我没做这个 spike)。 ### [需要改] E-A5 · §5.3 FR-2 "用 LightGBM 分位数回归 quantile 0.1/0.5/0.9 三个模型同训" 输出格式跟我 §4.3.5 预测点 schema 不一致 工程师 §5.3 交互逻辑 1:分位数回归输出 `{p10, p50, p90}` 三档;§5.3 边界数值"区间显示精度:销量单位件,p50 取整;区间下界 max(0, floor(p10))"。**我 §4.3.5 `forecast_point` schema 写的是 `p10, p25, p50, p75, p90` 五档**(因为我 §3.5.1 场景生成需要 5 个分位点构 S1-S5 场景)。工程师只训 3 个 quantile 模型 → **我的 §3.5.1 场景法鲁棒优化无法直接对接,缺 p25 / p75**。**R2 必须修复**:要么工程师 §5.3 改训 5 个 quantile 模型 0.10/0.25/0.50/0.75/0.90(计算成本基本翻倍,我 §3.2.3 预测训练预算需要复核);要么我 §3.5.1 降级到 3 场景方案(P10/P50/P90),权重 0.25/0.50/0.25,跟我 §3.5.4 "退化到 3 场景"的退化方案合并。我倾向后者——v1 用 3 场景,5 场景留 v1.5。 ### [需要改] E-A6 · §5.2 FR-1 "Schema 字段映射器:v1 预填 FBA API 标准 schema,修改保存后版本号 +1,旧版本仍可回滚" 跟我 §4.4.4 "强一致区"边界冲突 工程师 §5.2 把 Schema 映射做成**用户可改 + 多版本回滚**的运行时实体。我 §4.4.4 写"强一致区:决策建议、用户动作、参数版本"——但**没写 schema 映射版本**。如果用户改 Schema 映射 → 下游所有 forecast_point / decision_recommendation 的输入字段名变化 → 旧的 decision_recommendation 可能引用已经被映射改名的字段,**回测不可复现**(违反我 §4.4.4 "快照不可变"原则)。**R2 必须修复**:要么工程师 §5.2 把"Schema 映射器"权限收紧到 v1.5(v1 锁定为 FBAStubAdapter 一套硬编码 schema),要么我 §4.3 补 `schema_mapping_version` 实体并把它加入 §4.4.4 强一致区 + 决策跑批时绑定 mapping 版本(跟 param_version 一样)。我倾向前者——v1 自用 200 SKU 不需要 schema 版本灵活性。 ### [冲突] E-A7 · §5.4 FR-3 "单次目标函数评估 < 5 ms(36000 变量)" 跟我 §3.1.1 决策变量数定义口径不一致 工程师 §5.4 边界数值:"单次目标函数评估 < 5 ms(200 SKU × 90 天 × 2 物流方式 = 36000 变量)"。**这跟工程师自己 §5.5 FR-4 写的"5200 整数变量"(周聚合)矛盾**——同一份文档里目标函数按日级 36000 算 5ms 预算,求解器按周级 5200 算性能预算。我 §3.1.1 的 72000 变量(含 y 二元变量 + x 连续变量)口径又是第三个。**R2 必须统一口径**:日级 vs 周级二选一后,36000 / 5200 / 72000 三个数字要全部对齐。我倾向**日级 + 把 y 用大 M 跟 x 耦合(我 §3.1.3 C7 已经这么写了)**,最终决策变量数 = 200 × 90 × 2 × 2 = 72000,工程师 §5.4 和 §5.5 都按此口径改写。 ### [需要改] E-A8 · §5.8 FR-7 助理 WebSocket 实时刷新 vs 我 §6.3.2 时序图的 API 推送语义 工程师 §5.8 交互逻辑 2:"状态回写:助理操作后 webhook 推送到 CEO 视图实时刷新(WebSocket / SSE)"。**我 §6.3.2 时序图画的是 AssistUI → API → 通知 U 的请求-响应模式**,没有 WebSocket。**问题**:引入 WebSocket = 新的基础设施模块(连接管理、断线重连、消息总线、状态同步),跟我 §9.A 风险表 A9 "单点数据接入层(无冗余)"的简洁架构假设不符——WebSocket 网关又是一个单点。**R2 决策建议**:v1 用"轮询 30s + 用户手动刷新"(工程师 §5.8 异常场景里已经写了"WebSocket 断连 → 自动重连 + 状态轮询兜底 30s 一次",那 v1 干脆只保留轮询),WebSocket 留 v1.5。砍这个能省一个完整子系统的工时。 ### [需要改] E-A9 · §5.12 FR-11 三策略回测的资金池约束跟我 §4.5.3 影子模式资金池隔离对不上 工程师 §5.12 三策略:系统 / 保守(≥60 天可卖, 全海运)/ 激进(≥14 天, 缺货前 5 天空运)。**这三个策略**在跑回测时**共用同一份资金池约束**还是**各自独立**?工程师没写。如果共用,那"激进策略"消耗资金导致"系统策略"在某些 SKU 上 infeasible,对比就失真;如果独立,三个策略各自有"自己的资金池"=三个 tenant,但我 §4.5.2 tenant_id 设计是用来分 mock/real/shadow 的,**不是用来分回测策略的**。**R2 必须修复**:要么工程师 §5.12 明示"三策略在同一资金池独立求解(不互相消耗),但缺货损失分别累计";要么我 §4.5.2 tenant_id 模型扩展支持 `backtest_strategy_id` 子隔离(影响 §4.3 schema 和 §6.1 流程图)。 ### [OPEN_QUESTION] E-A10 · §5.9 FR-8 "推送渠道 fallback 到下一个渠道" 跟我 §6.2 状态机里没有"已推送但未读"状态 工程师 §5.9 交互逻辑 4:"每渠道独立模板;失败 fallback 到下一个渠道"。**问题 1**:fallback 顺序是什么、谁定?工程师没写。**问题 2**:如果企微推送 API 返 200(送达成功)但 CEO 没读,要不要 fallback 到 Bark/TG?工程师没写。**问题 3**:我 §6.2 三个状态机里**没有"alert 已推送但未确认"状态**——alert 是 fire-and-forget 还是需要 ACK?**R2 拍板建议**:v1 接受 fire-and-forget(推送成功 = 任务完成,不等 ACK),fallback 仅在 transport 层(API 返 5xx 才 fallback),PM US-4 "在 1 小时内收到预警推送"的承诺以"推送 API 返成功"为度量点,不以"用户已读"为度量点。这跟 PM 边界 B-4 "v1 先实现 1 个推送渠道"也呼应——v1 砍 fallback 链能省一层复杂度。 ### [需要改] E-A11 · §5.2 FR-1 "snapshot_id = uuid + sha256(payload)" 跟我 §4.4.4 "ingestion_run_id 追溯" 双 ID 体系冗余且语义重叠 工程师 §5.2 交互逻辑 2 给每个 snapshot 打 `snapshot_id = uuid + sha256(payload)`。我 §4.3.2 inventory_snapshot 表用 `ingestion_run_id` 追溯到哪次拉取。**这是两个 ID 体系做同一件事**(追溯数据来源)。工程师的 sha256 哈希是为了内容寻址(去重 + 缓存命中),我的 ingestion_run_id 是为了审计运行批次。**R2 决策建议**:保留两个但分工明确——`ingestion_run_id`(行级,每行写入哪次拉取)+ `snapshot_content_hash`(运行级,整批数据的内容指纹用于求解器缓存 §5.5 交互逻辑 7 命中判断)。工程师 §5.2 把 `snapshot_id` 拆成这两个字段。我 §4.3.2 schema 补 `snapshot_content_hash` 字段。 --- **END r2-architect-reviews-others.md**