fix(ai): V3 长期问题修复+规则合规+竞品对标
## P1 安全加固 - 原子化每日限额(tryConsumeDailyQuota)解决 TOCTOU 竞态 - 流式端点补齐 Zod 校验 + rate limit + 服务端强制 systemPrompt - 配额回退机制(refundDailyQuota):过滤/失败不扣配额 - PII 最小化:移除 AI prompt 中的学生姓名 ## P1 数据一致性 - 修复 capability 埋点缺失 child_summary/study_path 类型 - 创建 data-access.ts:真实统计聚合替代硬编码零 - 修复 generateChildSummary/recommendStudyPath 的 capability 标记 ## P2 可靠性 - AI 调用重试机制(withRetry 指数退避,429/5xx,2 次重试) - 30s 超时配置 - 流式 controller 安全 enqueue(防已关闭抛错) - localStorage 防抖持久化(500ms,流式过程中跳过) ## P2 TypeScript/规则合规 - 移除 as 断言(VariantType 类型守卫、Permission 类型、StreamErrorKey) - 补齐返回类型标注(POST/getStatusFromError/DashboardLayout) - 拆分 use-ai-chat-stream hook(190→107 行,函数体≤80 行) - 抽取 stream-utils.ts(SSE 解析/错误映射/消息工具) - Tailwind 任意值添加注释说明(max-w-[80%] 聊天气泡) ## P3 竞品对标 - 苏格拉底式辅导强化(对标 Khanmigo): - SOCRATIC_TUTOR_SYSTEM_PROMPT 3 级提示升级 - 强化 STUDENT_BLOCKED_PATTERNS 正则(中英文答案拦截) - validateSocraticOutput 服务端校验(问号结尾+连续陈述句限制) - socratic_warning SSE 事件类型 - 知识图谱集成(对标 Squirrel AI): - StudyPathInput 新增 knowledgeGraph/textbookId 字段 - recommendStudyPathAction 自动从 textbooks 模块获取图谱+掌握度 - STUDY_PATH_SYSTEM_PROMPT 增加前置依赖链规则 - WEAKNESS_ANALYSIS_SYSTEM_PROMPT 增加 rootCause 字段 ## 架构文档同步 - 004 更新 AI 模块章节(V3 标记/新导出/依赖关系/安全机制/文件清单) - 005 更新 modules.ai 节点(dependsOn/exports/dataAccess/streamUtils/dependencyMatrix)
This commit is contained in:
@@ -330,10 +330,10 @@ export class DefaultAiService implements AiService {
|
||||
}
|
||||
|
||||
async generateChildSummary(input: ChildSummaryInput): Promise<ChildSummaryResult> {
|
||||
return withAiTracking(this.userId, "weakness_analysis", undefined, async () => {
|
||||
return withAiTracking(this.userId, "child_summary", undefined, async () => {
|
||||
// PII 最小化:不传学生真实姓名,用 ID 替代(COPPA/FERPA 合规)
|
||||
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)}`
|
||||
@@ -370,7 +370,7 @@ export class DefaultAiService implements AiService {
|
||||
}
|
||||
|
||||
async recommendStudyPath(input: StudyPathInput): Promise<StudyPathResult> {
|
||||
return withAiTracking(this.userId, "weakness_analysis", undefined, async () => {
|
||||
return withAiTracking(this.userId, "study_path", undefined, async () => {
|
||||
const userLines = [
|
||||
`Student ID: ${input.studentId}`,
|
||||
input.subject ? `Subject: ${input.subject}` : "",
|
||||
@@ -379,6 +379,21 @@ export class DefaultAiService implements AiService {
|
||||
: "",
|
||||
input.learningGoal ? `Learning Goal: ${input.learningGoal}` : "",
|
||||
].filter((line) => line.length > 0)
|
||||
|
||||
// 知识图谱上下文注入(V3:对标 Squirrel AI 纳米级知识图谱)
|
||||
if (input.knowledgeGraph && input.knowledgeGraph.nodes.length > 0) {
|
||||
const graphLines = [
|
||||
"Knowledge Graph:",
|
||||
"Nodes (id | name | level | mastery 0-100):",
|
||||
...input.knowledgeGraph.nodes.map(
|
||||
(n) => ` ${n.id} | ${n.name} | L${n.level} | ${n.masteryLevel ?? "unassessed"}`
|
||||
),
|
||||
"Prerequisite edges (from -> to, meaning 'from' must be mastered before 'to'):",
|
||||
...input.knowledgeGraph.edges.map((e) => ` ${e.from} -> ${e.to}`),
|
||||
]
|
||||
userLines.push(graphLines.join("\n"))
|
||||
}
|
||||
|
||||
const { content } = await callAi(
|
||||
buildChatMessages(STUDY_PATH_SYSTEM_PROMPT, userLines.join("\n\n")),
|
||||
{ temperature: 0.5, maxTokens: 2000 }
|
||||
|
||||
Reference in New Issue
Block a user