api-server/docs/architecture/m-ai-08-learning-analysis-migration-contract.md
wangdl 5628e6db90
All checks were successful
Deploy API Server / build-and-unit (push) Successful in 36s
Deploy API Server / current-integration (push) Successful in 30s
Deploy API Server / backward-compat (push) Successful in 0s
Deploy API Server / deploy (push) Successful in 1m2s
docs(M-AI-08-01): AI workflow inventory + learning analysis migration contract
Inventory: 17 AI workflows cataloged. 6 migrated, 4 in M-AI-08 scope,
5 deferred, 2 legacy pending retirement. Confirmed M-AI-08 as final
core business migration milestone.

Contract: 4 Runtime job types (learning_state/weak_point/next_action/
flashcard) to be merged into unified learning_analysis Job. Data source
matrix, snapshot schema, evidence schema, topology frozen.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-22 21:31:29 +08:00

463 lines
15 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.

# M-AI-08 学习综合分析迁移契约
> 审计日期2026-06-22
> 契约状态:冻结(待 M-AI-08-01 验收)
> 对应里程碑M-AI-08 学习综合分析、学习建议与 AI 迁移闭环
---
## 目录
1. [当前链路审计](#1-当前链路审计)
2. [目标链路](#2-目标链路)
3. [数据源矩阵](#3-数据源矩阵)
4. [聚合窗口](#4-聚合窗口)
5. [Snapshot Schema](#5-snapshot-schema)
6. [Output Schema](#6-output-schema)
7. [Evidence Schema](#7-evidence-schema)
8. [触发模型](#8-触发模型)
9. [幂等契约](#9-幂等契约)
10. [Artifact 矩阵](#10-artifact-矩阵)
11. [权限与隐私](#11-权限与隐私)
12. [Feature Flag](#12-feature-flag)
13. [回滚流程](#13-回滚流程)
14. [架构异常与不确定项](#14-架构异常与不确定项)
---
## 1. 当前链路审计
### 1.1 四种 Runtime 作业类型
| Job Type | 产出实体 | promptVersion | outputSchemaVersion | 现状 |
|----------|---------|---------------|---------------------|------|
| `learning_state_analysis` | AiLearningAnalysis | learning_state_v1 | analysis_output_v1 | Runtime 轮询 |
| `weak_point_analysis` | WeakPointCandidate | weak_point_v1 | weak_point_output_v1 | Runtime 轮询 |
| `next_action_planning` | NextActionRecommendation | next_action_v1 | next_action_output_v1 | Runtime 轮询 |
| `flashcard_generation` | Flashcard | flashcard_gen_v1 | flashcard_output_v1 | Runtime 轮询 |
### 1.2 当前时序Runtime 轮询路径)
```text
POST /api/ai/jobs { jobType: "learning_state_analysis", targetType, targetId }
→ UserAiService.createAnalysisJob()
`src/modules/ai-runtime/user-ai.service.ts:199`
├─ settings 检查 (:201-206)
├─ jobType 验证 (:210-214)
├─ 幂等检查 (:225-233)
├─ 配额+预算 (:252-267)
├─ SnapshotBuilderService.buildSnapshot() (:270)
│ `src/modules/ai-runtime/snapshot-builder.service.ts:75`
│ ├─ fetchBehaviorData() — DailyLearningActivity, LearningSession
│ ├─ aggregateProgress() — MaterialReadingProgress
│ ├─ aggregateContent() — KnowledgeItem可选
│ ├─ calculateScores() — Quiz/Review/ActiveRecall/Feynman metrics
│ ├─ computeSignals() — engagement/consistency/streaks/patterns
│ └─ buildDeviceContext() — 平台/时区/设备
├─ PriorityRulesService.computeJobPriority() (:279)
└─ aiRuntimeJob.create (:282-299)
外部 Runtime 轮询
→ POST /internal/runtime/jobs/poll → pollJobs() (:22)
→ POST /internal/runtime/jobs/:id/lock → lockJob() (:85)
→ GET /internal/runtime/jobs/:id/snapshot → getSnapshot() (:153)
→ Runtime 执行 AI 调用prompt/schema 外部管理)
→ POST /internal/runtime/jobs/:id/result → submitResult() (:220)
└─ persistResult() (:286)
├─ learning_state_analysis → AiLearningAnalysis.create (:295-311)
├─ weak_point_analysis → WeakPointCandidate.create (:313-337)
└─ next_action_planning → NextActionRecommendation.create (:339-365)
```
### 1.3 数据模型
**AiLearningAnalysis** (`schema.prisma:2173-2194`):
```text
id, userId, jobId, snapshotId, targetType, targetId,
learningState, summary, riskLevel, confidence, evidence (Json),
nextActionIds (Json), promptVersion, schemaVersion
```
**WeakPointCandidate** (`schema.prisma:2198-2216`):
```text
id, userId, jobId, snapshotId, targetType, targetId,
knowledgePointId, title, reason, confidence, evidence (Json),
status (active/resolved)
```
**NextActionRecommendation** (`schema.prisma:2220-2239`):
```text
id, userId, jobId, snapshotId, actionType, targetType, targetId,
title, reason, priority, estimatedMinutes, deviceSuitability,
status (active/resolved)
```
**LearningAnalysisSnapshot** (`schema.prisma:2058-2083`):
```text
id, userId, scopeType, scopeId, snapshotVersion, sourceDataVersion,
privacyScope, userProfile, aiSettings, deviceContext,
learningBehaviorSummary, materialProgressSummary, contentStructureSummary,
behaviorSignals, scoreSignals, constraints, allowedModelFields
```
### 1.4 拓扑(冻结)
审计确认:`learning_state_analysis``weak_point_analysis``next_action_planning` 三者**各自独立调用模型**,由外部 Runtime 分别执行。
**M-AI-08 迁移决策**:将三者合并为一个 `learning_analysis` Job一次模型调用同时产出综合分析 + 薄弱点 + 建议。
```text
单一 learning_analysis Job
→ 一次 AiGateway 调用
→ LearningAnalysisProjector
→ AiLearningAnalysis综合分析
→ WeakPointCandidate × N薄弱点
→ NextActionRecommendation × N建议
→ AiJobArtifact × N
```
`flashcard_generation` 保持独立(不属于"学习分析"范畴)。
### 1.5 数据来源分类
| 数据 | 来源 | 可信度 | Snapshot 处理 |
|------|------|:---:|------|
| userId | JWT | 可信 | 直接包含 |
| learningGoal | UserLearningProfile | 可信 | 直接包含 |
| dailyAvailableMinutes | UserLearningProfile | 可信 | 聚合 |
| qualityPreference | UserAiSettings | 可信 | 直接包含 |
| 学习时长 | DailyLearningActivity | 可信 | **仅聚合后进入** |
| 活跃天数 | DailyLearningActivity | 可信 | 聚合指标 |
| 复习完成率 | ReviewCard + ReviewLog | 可信 | 聚合指标 |
| Quiz 正确率 | QuizAttempt | 可信 | 聚合指标 |
| Active Recall 表现 | AiAnalysisResult | 可信 | 聚合指标 |
| Feynman 弱点 | FocusItem + AiAnalysisResult | 可信 | 聚合指标 |
| 知识点进度 | KnowledgeItem + MaterialReadingProgress | 可信 | 列表(限量+截断) |
| 设备/时区 | DeviceContext服务端推断 | 可信 | 直接包含 |
| 学习时长(客户端声明) | — | **不可信** | **禁止进入** |
| 掌握度(客户端声明) | — | **不可信** | **禁止进入** |
| JWT / API Key | Request Header | — | **禁止进入** |
| 完整对话历史 | — | — | **禁止进入** |
### 1.6 副作用矩阵
| 副作用 | 当前 | Unified |
|--------|------|---------|
| 配额消耗 | quota.incrementJobCount | 保持 |
| UsageLog | Runtime 记录 | Engine 记录 |
| 更新用户画像 | 否 | 否 |
| 更新知识点掌握度 | 否 | 否 |
| 创建通知 | 否 | 否 |
| 自动生成复习卡 | 否 | 否 |
| 自动执行建议 | 否 | 否(禁止) |
| Snapshot 创建 | SnapshotBuilderService | SnapshotBuilder复用 |
---
## 2. 目标链路
```text
手动/定时触发
→ LearningAnalysisExecutionRouter
├─ [legacy] → UserAiService.createAnalysisJob()
└─ [unified] →
LearningAnalysisSnapshotBuilder.build()
→ AiJobCreationService.createJob()
→ Job + Snapshot + Outbox
→ BullMQ (ai-background)
→ Worker → AiJobExecutionEngine
EXECUTE: LearningAnalysisExecutor (AiGateway)
VALIDATE: SchemaValidator + EvidenceValidator + BusinessValidator
PROJECT: LearningAnalysisProjector
→ AiLearningAnalysis + WeakPointCandidate × N
+ NextActionRecommendation × N + Artifact
→ markSucceeded
```
---
## 3. 数据源矩阵
| 数据源 | Prisma 模型 | Snapshot 包含方式 |
|--------|-----------|-----------------|
| 用户学习目标 | UserLearningProfile | 直接字段 |
| AI 设置 | UserAiSettings | 直接字段 |
| 每日学习活动 | DailyLearningActivity | 聚合totalDuration/activeDays/sessionCount |
| 学习会话 | LearningSession | 聚合avgSessionDuration/completionRate |
| 阅读进度 | MaterialReadingProgress | 列表(限量 50仅标题+进度) |
| 知识项 | KnowledgeItem | 列表(限量 200仅标题+摘要) |
| 复习记录 | ReviewCard + ReviewLog | 聚合dueCount/completedCount/accuracy/overdue |
| Quiz 结果 | QuizAttempt + QuizAnswer | 聚合accuracy/byType/byKnowledgeItem |
| Active Recall | AiAnalysisResult | 聚合count/avgScore/weaknesses |
| Feynman 评估 | AiAnalysisResult + FocusItem | 聚合count/weaknesses/focusItems |
| 设备上下文 | 服务端推断 | 直接字段platform/timezone |
| 连续学习 | StreakRecord | 聚合指标 |
---
## 4. 聚合窗口
| 参数 | 默认值 | 说明 |
|------|--------|------|
| windowStart | 请求时确定 | Snapshot 固定,不漂移 |
| windowEnd | 请求时确定 | 与 windowStart 配对 |
| timezone | 服务端推断 | 用户设备时区 |
| sourceCutoffAt | 请求时 | 数据截止时间 |
| aggregationVersion | 语义版本 | 算法版本,变化时重新分析 |
| 行为窗口 | 7 天 | 学习行为数据 |
| 成绩窗口 | 30 天 | Quiz/复习/Active Recall 成绩 |
---
## 5. Snapshot Schema
```typescript
interface LearningAnalysisSnapshot {
schemaVersion: string; // "learning-analysis-v1"
snapshot: {
userId: string;
triggerType: 'manual' | 'scheduled';
operationId: string;
windowStart: string; // ISO8601
windowEnd: string;
timezone: string; // IANA
aggregationVersion: string;
sourceCutoffAt: string;
learningGoal?: string;
qualityPreference?: string;
dailyAvailableMinutes?: number;
studyMetrics: {
totalStudyDuration: number;
activeDays: number;
sessionCount: number;
averageSessionDuration: number;
completionRate: number;
};
reviewMetrics: {
reviewDueCount: number;
reviewCompletedCount: number;
reviewAccuracy: number;
overdueCount: number;
retentionTrend: number; // -1 to 1
};
quizMetrics: {
quizCount: number;
attemptCount: number;
accuracy: number;
accuracyByQuestionType: Record<string, number>;
};
activeRecallMetrics: {
count: number;
avgScore: number;
weaknessCount: number;
};
feynmanMetrics: {
count: number;
weaknessCount: number;
focusItemCount: number;
};
knowledgeProgress: {
totalItems: number;
completedItems: number;
inProgressItems: number;
weakItems: Array<{ id: string; title: string }>;
};
dataQuality: {
availableSources: string[];
missingSources: string[];
sampleSize: number;
coverageStart?: string;
coverageEnd?: string;
insufficientDataReasons: string[];
};
promptKey: string;
promptVersion: string;
modelTier: string;
inputSchemaVersion: string;
outputSchemaVersion: string;
createdAt: string;
};
}
```
---
## 6. Output Schema
```typescript
interface LearningAnalysisOutput {
summary: string;
strengths: Array<{
title: string;
description: string;
evidenceRefs: EvidenceRef[];
}>;
weaknesses: Array<{
title: string;
description: string;
knowledgePointId?: string;
evidenceRefs: EvidenceRef[];
}>;
trends: Array<{
metricKey: string;
direction: 'improving' | 'declining' | 'stable';
description: string;
evidenceRefs: EvidenceRef[];
}>;
risks: Array<{
title: string;
severity: 'low' | 'medium' | 'high';
description: string;
evidenceRefs: EvidenceRef[];
}>;
recommendations: Array<{
actionType: string;
title: string;
reason: string;
priority: number;
estimatedMinutes?: number;
targetType?: string;
targetId?: string;
evidenceRefs: EvidenceRef[];
}>;
confidence: number; // 0.0-1.0
dataQuality: {
overall: 'sufficient' | 'limited' | 'insufficient';
};
insufficientData: boolean;
}
```
---
## 7. Evidence Schema
每个结论必须附带 evidenceRefs
```typescript
interface EvidenceRef {
sourceType: 'study_metric' | 'review_metric' | 'quiz_metric'
| 'active_recall' | 'feynman' | 'knowledge_progress';
metricKey: string; // e.g. "reviewAccuracy", "quizAccuracy"
entityId?: string; // knowledgeItemId / quizId / analysisResultId
windowStart: string;
windowEnd: string;
}
```
---
## 8. 触发模型
| 触发类型 | 幂等键 | 说明 |
|---------|--------|------|
| manual | `learning-analysis:manual:<operationId>` | 用户手动触发,同一操作重试相同 Job |
| scheduled | `learning-analysis:scheduled:<userId>:<windowStart>:<windowEnd>:<policyVersion>` | 定时触发,同一窗口唯一 |
---
## 9. 幂等契约
### 请求级
- 同一 operationId → 同一 Job
- 同一 scheduled window → 同一 Job
- 禁止 Date.now()/random 回退
### 投影级
- analysisId = deterministic(jobId)
- recommendationId = deterministic(jobId + ordinal)
- 入口 Artifact 检查 + P2002 catch
---
## 10. Artifact 矩阵
| 实体 | artifactType | artifactId |
|------|-------------|-----------|
| AiLearningAnalysis | `learning_analysis` | analysis.id |
| WeakPointCandidate | `weak_point` | candidate.id |
| NextActionRecommendation | `recommendation` | recommendation.id |
| Flashcard (单独) | `flashcard` | flashcard.id |
M-AI-02 已冻结 `learning_analysis``recommendation` 等 artifactType。
---
## 11. 权限与隐私
- 所有数据必须属于同一 userId
- 禁止其他用户数据进入 Snapshot
- 禁止跨用户 evidenceRefs
- 公开错误不泄漏 Snapshot/validatedOutput
- Admin 可查看所有(现有权限制)
---
## 12. Feature Flag
```text
Flag Name: LEARNING_ANALYSIS_ENGINE_MODE
Values: legacy | unified
Default: legacy
```
---
## 13. 回滚流程
```text
unified → legacy:
1. 修改 Flag → legacy
2. 新触发走 Legacy Runtime 轮询
3. 已创建 Unified Job 继续完成
4. 已生成分析保留
5. 无需数据库回滚
```
---
## 14. 架构异常与不确定项
### 阻塞项
| # | 异常 | 说明 | 处理 |
|---|------|------|------|
| A1 | 无 learning_state/weak_point/next_action Prompt 模板 | 当前由 Runtime 管理 | M-AI-08-03 需新增内联 Prompt |
| A2 | 无对应 Output Schema | 同上 | M-AI-08-03 需新增 Zod Schema |
| A3 | flashcard_generation prompt/schema 缺失 | 同 Quiz 模式 | 延期或并入 M-AI-08 |
### 不确定项
| 项 | 问题 | 建议 |
|----|------|------|
| 三个 Job Type 合并策略 | 当前三者独立,合并为一个 learning_analysis 需验证业务合理性 | M-AI-08-01 中确认后冻结 |
| flashcard_generation 归属 | 语义上更接近 Quiz 生成而非学习分析 | 建议纳入 M-AI-08 作为独立 Definition |
---
## 附录:关键文件索引
| 用途 | 路径 | 关键行号 |
|------|------|---------|
| 分析入口 | `src/modules/ai-runtime/user-ai.service.ts` | `:199` createAnalysisJob, `:11-17` JOB_TYPE_CONFIG |
| Runtime 持久化 | `src/modules/ai-runtime/internal/runtime-internal.service.ts` | `:286` persistResult, `:295-365` 四种类型分支 |
| Snapshot 构建 | `src/modules/ai-runtime/snapshot-builder.service.ts` | `:75` buildSnapshot, `:259` aggregateBehavior |
| Priority 规则 | `src/modules/ai-runtime/priority-rules.service.ts` | `:45` computePriorityRules |
| AiLearningAnalysis | `prisma/schema.prisma` | `:2173-2194` |
| WeakPointCandidate | `prisma/schema.prisma` | `:2198-2216` |
| NextActionRecommendation | `prisma/schema.prisma` | `:2220-2239` |
| LearningAnalysisSnapshot | `prisma/schema.prisma` | `:2058-2083` |
| UserLearningProfile | `prisma/schema.prisma` | `:1912-1932` |
| UserAiSettings | `prisma/schema.prisma` | `:1936-1953` |
| 学习趋势 Workflow | `src/modules/ai/workflows/learning-trend.workflow.ts` | `:30` execute |
| 学习趋势 Prompt | `src/modules/ai/prompts/learning-trend.prompt.ts` | `:1` system prompt |