# ADR-001：统一 AI 架构迁移决策

## 状态

已接受（2026-06-19）

## 背景

当前知习项目存在两套独立的 AI 分析系统，以及一套规则出题系统：

- **系统 A（BullMQ + TypeScript，运行中）**：处理 active-recall 和 feynman-evaluation 分析，通过 `AiAnalysisWorker` 在 NestJS 进程内执行，写入 `AiAnalysisJob`/`AiAnalysisResult` 表，创建 `FocusItem`，发布 `ai.analysis.completed` 事件驱动 `ReviewCardSubscriber` 生成复习卡片。
- **系统 B（Rust + HTTP 轮询，闲置）**：`zhixi-heavy-runtime` 独立进程，支持 5 种分析类型（learning_state_analysis、weak_point_analysis、next_action_planning、quiz_generation、flashcard_generation），通过轮询 `AiRuntimeJob` 表获取任务，调用 DeepSeek API，结果写入多个相互独立的领域表。
- **系统 C（规则出题，运行中）**：`QuizService.create()` 通过纯文本拼接生成测验题目，不调用任何 AI 模型。

系统 B 存在以下关键问题：
1. **无任务生产者** — 唯一创建 `AiRuntimeJob` 的入口 `POST /ai/jobs` 无人调用
2. **Snapshot 结构不兼容** — Rust 端 `LearningAnalysisSnapshot` 结构体与 API 端 `SnapshotBuilderService` 产出的 JSON 结构完全不匹配
3. **Rust 无重计算** — 所有 Pipeline 的独有业务逻辑总和不足 50 行，实际是一个 HTTP 代理层
4. **数据断层** — Rust 结果写入的表（`AiLearningAnalysis`、`WeakPointCandidate`、`NextActionRecommendation`）无 iOS/Admin 消费端

同时，当前 API 进程内注册 BullMQ Processor 的部署拓扑存在以下问题：
- API 进程与 Worker 进程边界模糊，Docker 与生产环境配置不一致
- `AppModule` 中同时注册 `AiAnalysisWorker`、`DocumentImportWorker`、`NotificationWorker`
- `WorkerModule` 和 `worker.main.ts` 已存在但未独立使用

## 决策

采用 **8 批次渐进式迁移**，将 AI 分析能力从 BullMQ + Rust 双系统统一到单一 BullMQ + TypeScript 架构。

### 目标架构拓扑

```
                ┌─────────────┐
                │   iOS App   │
                └──────┬──────┘
                       │ HTTP
                ┌──────▼──────┐
                │  zhixi-api  │  NestJS (仅 Controller + Queue Producer)
                │  :3000      │  不注册任何 @Processor()
                └──────┬──────┘
                       │ Redis (BullMQ)
                ┌──────▼──────┐
                │ zhixi-worker│  NestJS (仅 @Processor())
                │  (无端口)    │  AiAnalysisWorker + DocumentImportWorker
                └──────┬──────┘  + NotificationWorker + AuditLogProcessor
                       │ HTTP    + FileCleanupProcessor
                ┌──────▼──────┐
                │  DeepSeek    │
                │  API         │
                └──────────────┘
```

### API Process 职责

- 暴露所有 HTTP Controller
- 注入 Queue Producer（`QueueService.add()`）
- 不注册任何 `@Processor()` 或 `WorkerHost`
- 不启动 BullMQ Worker 实例

### Worker Process 职责

- 注册所有 `@Processor()` 和 `WorkerHost`
- 消费 BullMQ 队列
- 调用 AI Workflow → DeepSeek API
- 写入分析结果表
- 发布领域事件
- 不暴露 HTTP 端口

### Heavy Runtime 当前处置

- 第一阶段保持部署但不修改
- 不停止 Rust 进程
- 不删除 `AiRuntimeJob` 及相关表
- M-AI-08 正式退场

## 迁移批次

| 批次 | 名称 | 范围 |
|------|------|------|
| M-AI-01 | Worker 进程边界与部署收口 | 拆分 API/Worker 进程，不改变业务 |
| M-AI-02 | 统一 AiJob 数据库 Expand | 扩展 `AiAnalysisJob`，新增 Artifact/Snapshot/Outbox |
| M-AI-03 | 统一 Job Engine、Registry 与 Outbox | AiJobService + Registry + 双队列 |
| M-AI-04 | Active Recall 端到端迁移 | 切换生产者，iOS 展示真实结果 |
| M-AI-05 | Feynman 与 ReviewCard 可靠链路 | Outbox 替代进程内 EventEmitter |
| M-AI-06 | AI Quiz 端到端迁移 | 迁移 Rust Prompt/Schema，异步生成 |
| M-AI-07 | 后台学习分析与推荐闭环 | learning_state/weak_point/next_action |
| M-AI-08 | Heavy Runtime 退场与 Legacy Contract | 停止 Rust，归档表 |

## 第一阶段非目标（M-AI-01）

1. 不修改 `AiAnalysisJob` / `AiRuntimeJob` 表结构
2. 不新建统一 AiJob 模型
3. 不迁移 Active Recall、Feynman 或 Quiz 业务链路
4. 不停止 `zhixi-heavy-runtime` 服务
5. 不修改 iOS 代码
6. 不删除任何数据库表
7. 不修改 AI Prompt、模型或业务结果结构
8. 不新增 Prisma Migration

## 架构约束

以下约束在所有批次中持续生效：

1. `AppModule` 不允许注册 `@Processor()` 或 `WorkerHost`
2. `WorkerModule` 不允许暴露公开 HTTP Controller
3. 不允许新建第三套 AI Job 模型（除 M-AI-02 统一扩展外）
4. 不允许顺带修改 Prompt、模型选择或业务结果 Schema（各自独立 Issue）
5. 没有 iOS/Admin 消费者的任务类型不得启用自动生成
6. 自动触发必须有频率限制、幂等保护、成本上限

## 回滚原则

每个 Issue 必须提供独立回滚方案：

1. 代码层面：回滚提交或 revert PR
2. 数据库：本批次不涉及 Schema 变更，无需数据回滚
3. 部署：恢复旧 AppModule Processor 注册，停止独立 Worker service
4. 队列：验证无双重消费

## 现有 Processor 清单

| Processor | 队列 | 文件 |
|-----------|------|------|
| `AiAnalysisWorker` | `ai-analysis` | `workers/ai-analysis.worker.ts` |
| `DocumentImportWorker` | `document-import` | `workers/document-import.worker.ts` |
| `NotificationWorker` | `notification` | `workers/notification.worker.ts` |
| `AuditLogProcessor` | `audit-logs` | `modules/admin-audit-log/audit-log.processor.ts` |
| `FileCleanupProcessor` | `file-cleanup` | `modules/files/file-cleanup.processor.ts` |

## 模块依赖图（当前）

```
AppModule
├── WorkerModule (已存在但未独立使用)
│   ├── AiAnalysisWorker
│   ├── DocumentImportWorker
│   └── NotificationWorker
├── QueueModule
│   ├── BullModule.forRootAsync (Redis 连接)
│   └── BullModule.registerQueue (6 个队列)
├── AiAnalysisModule
│   ├── AiAnalysisController (HTTP 端点)
│   └── AiAnalysisService (BullMQ Producer)
├── ActiveRecallModule
│   └── ActiveRecallService (BullMQ Producer 调用者)
├── ReviewModule
│   ├── ReviewController (HTTP 端点)
│   ├── ReviewCardSubscriber (监听 ai.analysis.completed)
│   └── ReviewService
├── AiRuntimeModule
│   ├── UserAiController (HTTP 端点)
│   └── RuntimeInternalController (Rust 内部端点)
├── AdminAuditLogModule
│   └── AuditLogProcessor (@Processor)
└── FilesModule
    └── FileCleanupProcessor (@Processor)
```

## 后续 Issue 引用

后续 M-AI-01 Issue 必须引用本文档（ADR-001）作为架构约束依据。

## 证据

- 当前部署：`docker-compose.yml:1-70` — API 和 Worker 共享同一 NestJS 实例
- Worker 模块：`worker.module.ts:1-20` — 已定义但未独立使用
- Worker 入口：`worker.main.ts:1-10` — 已存在但未被 Docker/生产独立调用
- 现有架构文档：`docs/ai-runtime-architecture.md` — 描述 Rust Heavy Runtime 架构
- BullMQ 版本：`bullmq@5.77.1`、`@nestjs/bullmq@11.0.4`
