feat(ai): V2 深度增强 — SSE 流式/全局助手/内容安全/多角色覆盖

对标 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 同步更新
This commit is contained in:
SpecialX
2026-06-23 01:34:37 +08:00
parent a60105455e
commit 4da9194a5e
27 changed files with 3522 additions and 172 deletions

View File

@@ -9,6 +9,8 @@ import {
QUESTION_VARIANT_SYSTEM_PROMPT,
SIMILAR_QUESTION_SYSTEM_PROMPT,
WEAKNESS_ANALYSIS_SYSTEM_PROMPT,
CHILD_SUMMARY_SYSTEM_PROMPT,
STUDY_PATH_SYSTEM_PROMPT,
} from "./prompt-templates"
import { withAiTracking } from "./usage-tracker"
import {
@@ -17,6 +19,8 @@ import {
QuestionVariantResultSchema,
SimilarQuestionListSchema,
WeaknessAnalysisResultSchema,
ChildSummaryResultSchema,
StudyPathResultSchema,
} from "../schema"
import type {
AiChatMessage,
@@ -33,6 +37,10 @@ import type {
SimilarQuestionResult,
WeaknessAnalysisInput,
WeaknessAnalysisResult,
ChildSummaryInput,
ChildSummaryResult,
StudyPathInput,
StudyPathResult,
} from "../types"
// ---------------------------------------------------------------------------
@@ -320,6 +328,76 @@ export class DefaultAiService implements AiService {
return { result: validated.data }
})
}
async generateChildSummary(input: ChildSummaryInput): Promise<ChildSummaryResult> {
return withAiTracking(this.userId, "weakness_analysis", undefined, async () => {
const userLines = [
`Student ID: ${input.studentId}`,
input.studentName ? `Student Name: ${input.studentName}` : "",
input.grade ? `Grade: ${input.grade}` : "",
input.recentGrades && input.recentGrades.length > 0
? `Recent Grades:\n${JSON.stringify(input.recentGrades, null, 2)}`
: "",
input.attendanceRate !== undefined
? `Attendance Rate: ${(input.attendanceRate * 100).toFixed(1)}%`
: "",
input.errorBookSummary
? `Error Book Summary:\n${JSON.stringify(input.errorBookSummary, null, 2)}`
: "",
input.homeworkCompletionRate !== undefined
? `Homework Completion Rate: ${(input.homeworkCompletionRate * 100).toFixed(1)}%`
: "",
].filter((line) => line.length > 0)
const { content } = await callAi(
buildChatMessages(CHILD_SUMMARY_SYSTEM_PROMPT, userLines.join("\n\n")),
{ temperature: 0.4, maxTokens: 2000 }
)
const parsed = extractJson(content)
const validated = ChildSummaryResultSchema.safeParse(parsed)
if (!validated.success) {
return {
result: {
overallAssessment: "Unable to generate summary at this time.",
strengths: [],
areasForImprovement: [],
familyTutoringSuggestions: [],
nextSteps: [],
},
}
}
return { result: validated.data }
})
}
async recommendStudyPath(input: StudyPathInput): Promise<StudyPathResult> {
return withAiTracking(this.userId, "weakness_analysis", undefined, async () => {
const userLines = [
`Student ID: ${input.studentId}`,
input.subject ? `Subject: ${input.subject}` : "",
input.currentMastery && input.currentMastery.length > 0
? `Current Mastery:\n${JSON.stringify(input.currentMastery, null, 2)}`
: "",
input.learningGoal ? `Learning Goal: ${input.learningGoal}` : "",
].filter((line) => line.length > 0)
const { content } = await callAi(
buildChatMessages(STUDY_PATH_SYSTEM_PROMPT, userLines.join("\n\n")),
{ temperature: 0.5, maxTokens: 2000 }
)
const parsed = extractJson(content)
const validated = StudyPathResultSchema.safeParse(parsed)
if (!validated.success) {
return {
result: {
currentLevel: "Analysis unavailable",
learningPath: [],
summary: "Unable to generate learning path at this time.",
motivation: "Keep learning!",
},
}
}
return { result: validated.data }
})
}
}
/**