# 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; }; 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:` | 用户手动触发,同一操作重试相同 Job | | scheduled | `learning-analysis:scheduled::::` | 定时触发,同一窗口唯一 | --- ## 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 |