对标 Khanmigo/Duolingo Max/Squirrel AI/Century Tech 实现: - SSE 流式响应:createAiChatCompletionStream AsyncGenerator + /api/ai/chat/stream SSE 端点 + useAiChatStream hook(AbortController 停止生成 + localStorage 持久化) - Markdown 渲染:AiMarkdownRenderer(react-markdown + remark-gfm + 代码块/表格/列表 + hover 复制按钮) - 全局 AI 助手:AiAssistantWidget 浮动按钮 + Sheet 侧抽屉 + usePathname 路由推断上下文(7 类场景系统提示)+ dashboard layout 全局注入 AiClientProvider - 内容安全:content-safety.ts 多层过滤(输入/输出安全过滤 + 每日限制 student 50/teacher 200/parent 30/admin 500 + 学生苏格拉底模式),COPPA/FERPA K12 合规 - 多角色 AI 覆盖:家长端 AiChildSummary(学情摘要)+ 管理员端 AiUsageDashboard(使用监控)+ 学生端 AiStudyPath(个性化学习路径) - i18n 修复:8 处错误键引用 + zh-CN/en ai.json 全面扩展 - 架构文档 004/005 同步更新
206 lines
6.2 KiB
TypeScript
206 lines
6.2 KiB
TypeScript
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()),
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 家长学情摘要校验
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const ChildSummaryInputSchema = z.object({
|
|
studentId: z.string().min(1),
|
|
studentName: z.string().optional(),
|
|
grade: z.string().optional(),
|
|
recentGrades: z
|
|
.array(
|
|
z.object({
|
|
subject: z.string().min(1),
|
|
score: z.number(),
|
|
maxScore: z.number(),
|
|
trend: z.enum(["up", "down", "stable"]),
|
|
})
|
|
)
|
|
.optional(),
|
|
attendanceRate: z.number().min(0).max(1).optional(),
|
|
errorBookSummary: z
|
|
.object({
|
|
totalErrors: z.number().int().min(0),
|
|
topWeakSubjects: z.array(z.string()),
|
|
masteryTrend: z.enum(["improving", "declining", "stable"]),
|
|
})
|
|
.optional(),
|
|
homeworkCompletionRate: z.number().min(0).max(1).optional(),
|
|
})
|
|
|
|
export const ChildSummaryResultSchema = z.object({
|
|
overallAssessment: z.string().min(1),
|
|
strengths: z.array(z.string()),
|
|
areasForImprovement: z.array(z.string()),
|
|
familyTutoringSuggestions: z.array(z.string()),
|
|
nextSteps: z.array(z.string()),
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 学习路径推荐校验
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const StudyPathInputSchema = z.object({
|
|
studentId: z.string().min(1),
|
|
subject: z.string().optional(),
|
|
currentMastery: z
|
|
.array(
|
|
z.object({
|
|
knowledgePoint: z.string().min(1),
|
|
masteryLevel: z.number().min(0).max(5),
|
|
errorCount: z.number().int().min(0),
|
|
})
|
|
)
|
|
.optional(),
|
|
learningGoal: z.string().optional(),
|
|
})
|
|
|
|
export const StudyPathResultSchema = z.object({
|
|
currentLevel: z.string().min(1),
|
|
learningPath: z.array(
|
|
z.object({
|
|
step: z.number().int().min(1),
|
|
knowledgePoint: z.string().min(1),
|
|
status: z.enum(["mastered", "in_progress", "needs_work"]),
|
|
recommendedAction: z.string().min(1),
|
|
estimatedTime: z.string().min(1),
|
|
})
|
|
),
|
|
summary: z.string().min(1),
|
|
motivation: z.string().min(1),
|
|
})
|