feat: AI Runtime 总体架构文档 (API-AI-000)
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 42s

定义 API / Rust Heavy Runtime / document runtime / iOS / Admin 职责边界、
Docker 内部网络部署、Job 异步任务流、用户 Key 与平台 Key 管理、失败重试与
熔断、结果落库边界、RAG 共存策略与后续扩展预留。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-11 20:33:22 +08:00
parent 87cfa2c20d
commit 804c414901

View File

@ -0,0 +1,295 @@
# AI Runtime 总体架构
## 1. 概述
M-API-AI-RUNTIME 里程碑新增 Rust Heavy Runtime 作为内部重任务执行器,完成 DeepSeek 调用、学习状态分析、题目/卡片候选生成。主 API 保持业务权威层地位不变。
### 架构图
```
iOS / Admin / Web
主 APINestJS
AiRuntimeJob / LearningAnalysisSnapshot
Rust Heavy Runtime内部 HTTP
DeepSeek API
Rust Heavy Runtime 输出结构化结果
主 API 二次校验 / 落库
iOS 展示 / Admin 诊断
```
## 2. 职责边界
### 2.1 API本仓库
| 负责 | 不负责 |
|------|--------|
| 用户权限与会员额度 | 直接调用 DeepSeek |
| 学习数据读取与聚合 | Prompt 渲染 |
| 用户 DeepSeek key 加密存储 | 模型输出校验 |
| AI Job 创建与调度 | 任务消费 |
| Snapshot 构建 | 结构化 JSON 解析 |
| Runtime 内部接口提供 | 题目/卡片生成 |
| Runtime 结果校验与落库 | |
| iOS / Admin 对外 API | |
| 平台预算与熔断 | |
### 2.2 Rust Heavy Runtime
| 负责 | 不负责 |
|------|--------|
| Job Worker 消费 | 用户登录与会员 |
| DeepSeek 调用 | 直接写业务主表 |
| Prompt 渲染 | 对公网暴露接口 |
| 结构化 JSON 校验 | 处理 readingTargetType |
| 题目/卡片候选生成 | 用户数据查询 |
| 调用日志回传 | |
### 2.3 Rust Document Runtime
| 负责 | 不负责 |
|------|--------|
| 文档解析与阅读状态 | readingTargetType |
| 阅读事件 V2 生成 | userId |
| EventBuffer | 上传 API |
### 2.4 iOS
| 负责 | 不负责 |
|------|--------|
| 阅读页 UI | 直接调 DeepSeek |
| heartbeat tick 控制 | 直接调 Rust Heavy Runtime |
| 补充 readingTargetType | |
| 本地上传队列 + ack | |
### 2.5 Admin
| 负责 | 不负责 |
|------|--------|
| 管理页面 | 直接调 Rust Runtime |
| 成本统计与诊断 | |
## 3. Docker 部署
```
┌─────────────────────── Docker internal network ───────────────────────┐
│ │
│ ┌─────────┐ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ MySQL │ │ api (NestJS) │ │ heavy-runtime (Rust) │ │
│ │ :3306 │ │ :3000 │ │ :8080 │ │
│ │ │ │ → 公网暴露 │ │ → 不暴露公网 │ │
│ └─────────┘ └────────┬─────────┘ └──────────┬───────────┘ │
│ │ │ │
│ ├── 调 POST /internal/runtime/* ──→ │
│ │ │ │
│ │←── Runtime 调 internal API ──┤ │
│ │
└──────────────────────────────────────────────────────────────────────┘
```
### docker-compose 要点
```yaml
services:
api:
environment:
RUNTIME_INTERNAL_BASE_URL: http://heavy-runtime:8080
RUNTIME_SERVICE_TOKEN: ${RUNTIME_SERVICE_TOKEN}
depends_on:
- heavy-runtime
networks:
- zhixi-internal
heavy-runtime:
environment:
API_INTERNAL_BASE_URL: http://api:3000
RUNTIME_SERVICE_TOKEN: ${RUNTIME_SERVICE_TOKEN}
DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY}
expose:
- "8080"
networks:
- zhixi-internal
networks:
zhixi-internal:
driver: bridge
```
关键约束:
- heavy-runtime 不映射公网端口
- 服务间通过 Docker 内部 DNS 发现(`http://heavy-runtime:8080`
- service token 通过环境变量注入
## 4. 通信方式
### 4.1 主任务流Job 异步
```
API 创建 AiRuntimeJob(status=pending)
→ Runtime poll /internal/runtime/jobs/poll
→ Runtime lock job
→ Runtime 获取 Snapshot + credential
→ Runtime 执行 DeepSeek 调用
→ Runtime 提交 result 到 /internal/runtime/jobs/{jobId}/result
→ API 校验并落库
```
### 4.2 内部接口
| 接口 | 调用方 | 说明 |
|------|--------|------|
| `POST /internal/runtime/jobs/poll` | Runtime | 拉取 pending job |
| `POST /internal/runtime/jobs/{jobId}/lock` | Runtime | 锁定 job |
| `POST /internal/runtime/jobs/{jobId}/heartbeat` | Runtime | 心跳续约 |
| `GET /internal/runtime/jobs/{jobId}/snapshot` | Runtime | 获取快照 |
| `POST /internal/runtime/model-credentials/resolve` | Runtime | 获取模型 key |
| `POST /internal/runtime/jobs/{jobId}/result` | Runtime | 提交成功结果 |
| `POST /internal/runtime/jobs/{jobId}/fail` | Runtime | 提交失败 |
| `POST /internal/runtime/invocation-logs` | Runtime | 提交调用日志 |
| `GET /internal/runtime/health` | API | Runtime 健康检查 |
### 4.3 鉴权
- 复用已有 `InternalAuthGuard`,使用 `x-internal-api-key` header
- 新增 `X-Runtime-Instance-Id` header 用于追踪
- 普通用户 JWT 不可访问 internal 接口
- service token 不可访问普通用户 API
## 5. AI Job 生命周期
### 5.1 状态机
```
pending ──→ locked ──→ running ──→ succeeded
│ │ │
│ │ └──→ failed (retryable) ──→ pending
│ │ └──→ failed (non-retryable)
│ │ └──→ expired
│ │
│ └──→ expired (lockUntil 超时)
└──→ cancelled
```
### 5.2 Job 类型
| 类型 | 说明 | 输出 |
|------|------|------|
| `learning_state_analysis` | 学习状态分析 | AiLearningAnalysis |
| `weak_point_analysis` | 薄弱点分析 | WeakPointCandidate[] |
| `next_action_planning` | 下一步建议 | NextActionRecommendation[] |
| `quiz_generation` | 题目生成 | QuizQuestion[] |
| `flashcard_generation` | 卡片生成 | Flashcard[] |
### 5.3 锁定与超时
```
lockUntil = now + 60s (推荐初始值)
heartbeat 延长 lockUntil
lockUntil 超时后其他 Runtime 可接管
maxRetryCount = 3默认
```
## 6. 用户 Key 与平台 Key
### 6.1 两种模式
| 模式 | Key 来源 | 消费方 |
|------|---------|--------|
| `platform_key` | 环境变量 `DEEPSEEK_API_KEY` 或 API 配置 | 平台预算 |
| `user_deepseek_key` | 用户在 iOS 填写API 加密存储 | 用户自己 |
### 6.2 Key 流转
```
用户填 key → API 加密落库(UserModelCredential)
→ Runtime resolve → API 解密返回明文 → 仅内存使用 → 不写日志
→ ModelInvocationLog 只记录 credentialId不记录 key
```
### 6.3 安全规则
- encryptedApiKeyAES-256-GCM 加密,密钥来自环境变量
- 明文 key 不落库、不进日志、不返回前端、Admin 不可查看
- maskedKey`sk-****xxxx`
## 7. 失败与重试
### 7.1 retryable 错误
```
MODEL_TIMEOUT, MODEL_RATE_LIMIT, NETWORK_ERROR, TEMPORARY_PROVIDER_ERROR
→ retryable=true, job 回到 pending, retryCount+1
```
### 7.2 non-retryable 错误
```
INVALID_SNAPSHOT, INVALID_SCHEMA, INVALID_CREDENTIAL
→ retryable=false, job 直接 failed
```
### 7.3 熔断
连续失败 N 次 platform_key job → 平台熔断 open → 拒绝新的 platform_key job → half_open 后试探
### 7.4 取消
- pending job用户/Admin 可直接取消
- running job标记 cancelRequestedRuntime 下次 heartbeat 获知
## 8. 结果落库边界
### Runtime 提交 → API 校验 → 落业务表
```
Runtime submit result
→ API RuntimeOutputBusinessValidator 二次校验
→ 源数据归属校验 (sourceBlockIds, knowledgePointId)
→ 去重 (exact hash)
→ 写入对应业务表 (AiLearningAnalysis / QuizQuestion / Flashcard / ...
→ 更新 job 状态 succeeded
```
Runtime 不直接写AiLearningAnalysis, QuizQuestion, Flashcard, WeakPointCandidate, NextActionRecommendation, QuestionGenerationPlan, FlashcardGenerationPlan。这些只由 API 写。
## 9. 与现有 M1 RAG/Chat 的共存
1. 本批不迁移现有 M1 RAG。
2. 现有 Chat / RAG 问答链路AiGatewayService → DeepSeek继续运行。
3. 新 AI Runtime 只做:学习状态分析、题目/卡片候选生成。
4. 两者不共享 Job 队列。
5. 若 Chat 结果和 AI Analysis 结果同时存在,前端应区分展示:
- 对话回答:来自 Chat/RAG时效性高
- 学习分析建议:来自 AI Runtime基于 Snapshot 聚合
6. 后续 RAG 迁移需要独立里程碑。
## 10. 后续扩展预留
| 方向 | 本批 | 后续 |
|------|------|------|
| RAG 迁移 | ❌ | 独立里程碑 |
| 批量知识库分析 | ❌ (只做 user/material 级) | parentJob + childJob |
| 多模型供应商 | ❌ (只做 DeepSeek) | ModelProvider trait |
| A/B 测试 | ❌ | promptVersion 字段已预留 |
| Prompt 热更新 | ❌ (静态模板) | PromptRegistry |
| 用户自定义 baseUrl | ❌ | 需安全审计 |
## 11. 验收清单
- [ ] 文档明确 API 是业务权威层
- [ ] 文档明确 Runtime 是内部重任务执行器
- [ ] 文档明确 Runtime 不对公网暴露
- [ ] 文档明确 Docker 内部网络部署方式
- [ ] 文档明确本批不迁移 RAG
- [ ] 文档明确 AI Job 异步流程
- [ ] 文档明确 Runtime 结果最终由 API 校验和落库
- [ ] 文档明确后续 iOS / Admin 只访问 API不访问 Runtime
- [ ] 文档明确后续 RAG 迁移只是预留
- [ ] 文档能指导 API-AI-001~072 的实现