# M-AI-05 GATE 独立审核报告 > 审核日期:2026-06-21 > 角色:独立审核代理(非 M-AI-05 开发执行者) > 基线:M-AI-04 GATE PASS (`92446b9`) > HEAD:`a532b51` --- ## 1. Commit 范围 ``` M-AI-04 基线:92446b9 M-AI-05 HEAD:a532b51 Commits: 4f74c09 docs: record P2-06/P2-07 as technical debt a532b51 fix(P2-06): remove @Optional() silent skip ``` **M-AI-05 交付物**(16 untracked files): | 类别 | 文件 | 行数 | |------|------|------| | 契约 | `docs/architecture/m-ai-05-feynman-migration-contract.md` | 737 | | Definition | `feynman-job-definition.ts` + `spec.ts` | 77 + 126 | | Snapshot | `feynman-snapshot-builder.ts` | 202 | | Registration | `feynman-registration.service.ts` | 42 | | Executor | `feynman-executor.ts` + `spec.ts` | 100 + 360 | | Validator | `feynman-validator.ts` | 299 | | Projector | `feynman-projector.ts` + `spec.ts` | 217 + 391 | | Router | `feynman-execution-router.ts` + `spec.ts` | 194 + 273 | | Observability | `feynman-observability.service.ts` + `spec.ts` | 179 + 146 | | E2E | `test/m-ai-05-feynman.e2e-spec.ts` | 521 | **范围越界检查**: | 检查项 | 结果 | |--------|:---:| | Prisma Schema 修改 | 无 | | 新增 Migration | 无 | | Active Recall 重构 | 无 | | Quiz 迁移 | 无 | | Heavy Runtime 修改 | 无 | | 旧 `ai-analysis` 队列删除 | 无 | | 旧 Feynman Worker 删除 | 无 | | 客户端接口重构 | 无 | | 复习算法重新设计 | 无 | --- ## 2. 测试矩阵 | 测试套件 | 通过 | 失败 | |----------|------|:---:| | Feynman Job Definition | 26 | 0 | | Feynman Executor + Validator | 29 | 0 | | Feynman Projector | 16 | 0 | | Feynman Execution Router | 14 | 0 | | Feynman Observability | 19 | 0 | | **Feynman 合计** | **104** | **0** | | Active Recall (回归) | 91 | 0 | | **总计** | **195** | **0** | --- ## 3. 入口与 Feature Flag ### 3.1 真实入口 ``` POST /api/ai-analysis/feynman (不变) ``` 调用链: ``` AiAnalysisController.evaluateFeynman() → @Optional() feynmanRouter ├─ Router 存在 → FeynmanExecutionRouter.evaluateFeynman() │ ├─ 参数校验(title/content/explanation 必填) │ ├─ FeatureFlag FEYNMAN_ENGINE_MODE │ │ ├─ disabled → legacyService.evaluateFeynman() │ │ └─ enabled → Unified 路径 │ └─ Unified: SnapshotBuilder.build() → AiJobCreationService.createJob() └─ Router 不存在 → legacyService.evaluateFeynman() ``` ### 3.2 分支互斥 | 检查项 | 状态 | |--------|:---:| | Legacy/Unified 互斥 | ✅ Router `if/else` | | Unified 失败不 fallback | ✅ Router catch 不调用 legacyService | | 无双执行 | ✅ 无同时创建 Legacy + Unified Job | | FeatureFlag 默认 legacy | ✅ 不存在/disabled→false | | FeatureFlag 查询失败→legacy | ✅ catch→return false | ### 3.3 禁止项 | 禁止项 | 状态 | |--------|:---:| | Controller 直接 BullMQ | ✅ 无 | | 直接插入 Outbox | ✅ 通过 CreationService | | 绕过 Registry | ✅ `registry.get('feynman_evaluation')` | | 绕过 SnapshotBuilder | ✅ Router 预构建 Snapshot | --- ## 4. Definition 与 Snapshot ### 4.1 Definition | 字段 | 值 | Registry 校验 | |------|-----|:---:| | `jobType` | `feynman_evaluation` | ✅ | | `queueName` | `ai-interactive` | ✅ | | `timeoutMs` | 180000 | ✅ | | `maxRetries` | 3 | ✅ | | `promptKey` | `feynman-evaluation` | ✅ | | `projectorKey` | `feynman_evaluation_projector` | ✅ | | `credential.allowedModes` | `['platform_key']` | ✅ | ### 4.2 Snapshot 15 字段:`userId, knowledgeItemId, knowledgeItemTitle, knowledgeItemContent, userExplanation, submissionId, knowledgeBaseId, referenceMaterials[], promptKey, promptVersion, modelTier, inputSchemaVersion, outputSchemaVersion, createdAt` - **contentHash 稳定**:键排序 + 时间截断到秒 ✅ - **无敏感字段**:无 JWT/API Key/Cookie/DB URL ✅ - **prompt/model 值来自 Registry**:非硬编码 ✅ - **所有权校验**:`knowledgeItem.userId !== input.userId` → ForbiddenException ✅ --- ## 5. 幂等 ### 5.1 幂等键 ``` feynman: ``` `submissionId` 优先级:`sessionId:answerId` > `sessionId` > SHA256(title|content|explanation)[:16] 禁止:时间戳、随机 UUID、每次重新生成。✅ ### 5.2 验证结果 | 场景 | 测试 | |------|:---:| | 相同 submissionId → 同 jobId | E2E 场景 3 ✅ | | 只有一个 Snapshot | DB `count = 1` ✅ | | Projector 重复执行 → 返回已有 Artifact | spec ✅ | | AiAnalysisResult upsert | `fe_{jobId}` deterministic ID ✅ | | FocusItem findFirst + create | spec ✅ | --- ## 6. Executor 与验证 ### 6.1 Executor | 职责 | 状态 | |------|:---:| | 仅注入 `AiGatewayService` | ✅ | | 无 PrismaService | ✅ | | 消息构造与 Legacy 一致 | ✅ | | `timeoutMs` → AiGateway AbortController | ✅ | ### 6.2 验证层 | 层 | 覆盖 | |----|:---:| | Zod Schema | 10 字段类型/范围/必填 | | BusinessValidator | score[0,100]、clarityLevel 5 枚举、summary 非空、4 数组字段 ≤10×≤500、boolean/enum 检查、代码块检测、模型指令检测 | | ReferenceValidator | URL/email 检测 | 29 测试覆盖全部验证路径。 --- ## 7. Projector 与复习产物 ### 7.1 事务原子性 同一 `tx` 内:`AiAnalysisResult (upsert) + FocusItem × N + AiJobArtifact × (1+N)` ReviewCard 按契约 §12 方案 A — EventBus 异步生成(不在事务内)。 ### 7.2 幂等与失败回滚 | 场景 | 测试 | |------|:---:| | 入口幂等(已有 Artifact→返回) | ✅ | | FocusItem findFirst + create 去重 | ✅ | | Artifact P2002 幂等 | ✅ | | Result 失败 → 后续不执行 | ✅ | | FocusItem 失败 → 异常传播 | ✅ | ### 7.3 Bug 修复 | 字段 | Legacy | Unified | |------|--------|---------| | `knowledgeBaseId` | 恒为 `'unknown'` | 从 Snapshot 读取真实值 ✅ | | `knowledgeItemId` | 未设置 | 从 Snapshot 读取 ✅ | --- ## 8. 权限、状态与安全 ### 8.1 权限 | 检查项 | 状态 | |--------|:---:| | SnapshotBuilder 校验 knowledgeItem 所有权 | ✅ | | E2E 场景 7 跨用户测试 | ⚠️ 断言需修正 (201→403) | ### 8.2 状态兼容 Shadow Write:`pending→queued, processing→running, completed→succeeded, failed→failed` ### 8.3 响应脱敏 不含 `internalErrorMessage` / `validatedOutput` / `Snapshot` / Provider 原始响应 / 堆栈 / Credential。E2E 场景 14 验证。 ### 8.4 BullMQ Payload ```json { "jobId": "" } ``` E2E 验证 `Object.keys(payload).length === 1` ✅ --- ## 9. 真实运行与 CI ### 9.1 E2E 场景覆盖 | # | 场景 | HTTP 层 | Worker 层 | |---|------|:---:|:---:| | 1 | Legacy 成功 | ✅ | — | | 2 | Unified HTTP→Job+Snapshot+Outbox | ✅ | — | | 3 | 重复提交幂等 | ✅ | — | | 4 | 重复消费幂等 | — | ⚠️ Projector spec | | 5 | 重复消费不重复 FocusItem | — | ⚠️ Projector spec | | 6 | 重复消费不重复 ReviewCard | — | ⚠️ 方案 A 异步 | | 7 | 跨用户权限 | ⚠️ 断言需修正 | — | | 8 | Unified 失败不 fallback | ✅ | — | | 9 | Provider 失败 | — | ⚠️ Engine spec | | 10 | Projector 失败 | — | ⚠️ Projector spec | | 11 | 旧查询兼容 | ✅ | — | | 12 | 复习页面查询 | — | ⚠️ Worker 依赖 | | 13 | FeatureFlag 回滚 | ✅ | — | | 14 | 错误脱敏 | ✅ | — | **9/14 HTTP 层覆盖,5/14 单元测试等效。** ### 9.2 Fail-Closed - `throw new Error` on infra unavailable ✅ - 零 `itIfInfra` / `soft-pass` / `|| true` on test commands ✅ - CI 触发路径含 `test/m-ai-05` ✅ --- ## 10. Legacy 回归 | 检查项 | 状态 | |--------|:---:| | `AiAnalysisWorker` 未修改 | ✅ | | `ai-analysis` 队列保留 | ✅ | | Legacy Feynman 路径保留 | ✅ | | Controller `@Optional()` fallback | ✅ | | E2E 场景 1/13 验证 Legacy | ✅ | --- ## 11. 问题列表 ### P0 **无。** ### P1 **无。** `@Optional()` Worker 静默跳过已在 `a532b51` 修复。 ### P2 | ID | 问题 | 影响 | |----|------|------| | P2-01 | E2E 场景 7 期望 `201` 应为 `403` | 权限测试断言不精确 | | P2-02 | Engine `if (jobType === 'feynman_evaluation')` 4 处 | 通用 Engine 感知业务 jobType | | P2-03 | E2E Worker 依赖场景需 CI Docker | 本地无法验证 | ### P3 Worker SIGKILL、多 Dispatcher 压测、性能优化。**不阻塞。** --- ## 12. 无法确认项 1. CI Docker MySQL/Redis 实际就绪状态 2. E2E Worker 进程全链路执行(需 CI 环境) --- ## 13. 最终结论 ``` M-AI-05-GATE:CONDITIONAL PASS 是否允许进入 M-AI-06:是 是否允许生产白名单 Feynman Unified:否 是否允许停止 Legacy:否 ``` **升级为 PASS 的条件**:CI Docker 环境就绪 + E2E Worker 场景通过 + P2-01 修正。