feat(ai): 新增 AI 模块并集成至备课/错题集/试卷/改题四大业务场景

- 新增 src/modules/ai 独立模块,遵循三层架构(actions → services → shared/lib/ai)
- 通过 AiClientProvider + useAiClient 实现 React Context 依赖注入,业务组件零直接 import
- 6 个 Server Actions 均调用 requirePermission() 权限校验,返回 ActionState<T>
- withAiTracking 统一埋点,覆盖 chat/similar_question/grading_assist/lesson_content/question_variant/weakness_analysis
- 集成场景:作业批改 AiGradingAssist、错题集 AiErrorBookAnalysis、备课 AiLessonContentGenerator、试卷 AiQuestionVariantGenerator
- 全量 i18n(en/zh-CN ai.json),Error Boundary + Skeleton 边界处理
- 同步架构图 004/005,新增审计报告 ai-module-audit-report.md
This commit is contained in:
SpecialX
2026-06-23 00:52:39 +08:00
parent ec87cd9efa
commit 21c5eba96c
40 changed files with 4885 additions and 169 deletions

134
src/modules/ai/schema.ts Normal file
View File

@@ -0,0 +1,134 @@
import { z } from "zod"
// ---------------------------------------------------------------------------
// 基础校验
// ---------------------------------------------------------------------------
export const AiChatMessageSchema = z.object({
role: z.enum(["system", "user", "assistant"]),
content: z.string().min(1).max(8000),
})
export const AiChatInputSchema = z.object({
messages: z.array(AiChatMessageSchema).min(1).max(50),
providerId: z.string().min(1).optional(),
})
// ---------------------------------------------------------------------------
// 业务场景校验
// ---------------------------------------------------------------------------
export const SimilarQuestionInputSchema = z.object({
questionText: z.string().min(1).max(4000),
questionType: z.string().min(1),
subject: z.string().optional(),
knowledgePointIds: z.array(z.string()).optional(),
count: z.number().int().min(1).max(10).optional(),
})
export const GradingInputSchema = z.object({
questionText: z.string().min(1).max(4000),
questionType: z.string().min(1),
studentAnswer: z.string().min(1).max(8000),
correctAnswer: z.string().optional(),
maxScore: z.number().int().min(1).max(100),
subject: z.string().optional(),
})
export const LessonContentInputSchema = z.object({
topic: z.string().min(1).max(500),
subject: z.string().optional(),
grade: z.string().optional(),
textbookId: z.string().optional(),
chapterId: z.string().optional(),
contentType: z.enum(["activity", "assessment", "question", "material"]),
additionalContext: z.string().max(2000).optional(),
})
export const QuestionVariantInputSchema = z.object({
originalQuestion: z.object({
text: z.string().min(1).max(4000),
type: z.string().min(1),
difficulty: z.number().int().min(1).max(5).optional(),
options: z
.array(
z.object({
id: z.string().min(1),
text: z.string().min(1),
isCorrect: z.boolean().optional(),
})
)
.optional(),
answer: z.string().optional(),
}),
subject: z.string().optional(),
variantType: z.enum(["same_knowledge_point", "different_difficulty", "different_format"]),
})
export const WeaknessAnalysisInputSchema = z.object({
studentId: z.string().min(1),
subjectId: z.string().optional(),
errorItems: z
.array(
z.object({
questionText: z.string().min(1),
questionType: z.string().min(1),
knowledgePointIds: z.array(z.string()).optional(),
errorCount: z.number().int().min(1),
masteryLevel: z.number().int().min(0).max(5),
})
)
.min(1)
.max(100),
})
// ---------------------------------------------------------------------------
// AI 返回结果校验(用于解析 AI JSON 输出)
// ---------------------------------------------------------------------------
export const SimilarQuestionResultSchema = z.object({
text: z.string().min(1),
type: z.string().min(1),
difficulty: z.number().int().min(1).max(5).optional(),
options: z.array(z.object({ id: z.string(), text: z.string() })).optional(),
answer: z.string().optional(),
explanation: z.string().optional(),
})
export const SimilarQuestionListSchema = z.array(SimilarQuestionResultSchema)
export const GradingSuggestionSchema = z.object({
suggestedScore: z.number().min(0),
confidence: z.number().min(0).max(1),
feedback: z.string(),
reasoning: z.string(),
})
export const LessonContentResultSchema = z.object({
title: z.string().min(1),
content: z.string().min(1),
metadata: z.record(z.string(), z.unknown()).optional(),
})
export const QuestionVariantResultSchema = z.object({
text: z.string().min(1),
type: z.string().min(1),
difficulty: z.number().int().min(1).max(5),
options: z
.array(z.object({ id: z.string(), text: z.string(), isCorrect: z.boolean() }))
.optional(),
answer: z.string().optional(),
explanation: z.string().optional(),
})
export const WeaknessAnalysisResultSchema = z.object({
weakAreas: z.array(
z.object({
area: z.string().min(1),
severity: z.enum(["high", "medium", "low"]),
suggestion: z.string().min(1),
})
),
studyPlan: z.string().min(1),
recommendedResources: z.array(z.string()),
})