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:
83
src/modules/ai/services/usage-tracker.ts
Normal file
83
src/modules/ai/services/usage-tracker.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import "server-only"
|
||||
|
||||
import { trackEvent, type EventName } from "@/shared/lib/track-event"
|
||||
|
||||
export type AiUsageEvent = {
|
||||
userId: string
|
||||
capability: "chat" | "similar_question" | "grading_assist" | "lesson_content" | "question_variant" | "weakness_analysis"
|
||||
providerId?: string
|
||||
model?: string
|
||||
success: boolean
|
||||
durationMs: number
|
||||
tokenUsage?: number
|
||||
errorMessage?: string
|
||||
}
|
||||
|
||||
const AI_EVENT_MAP: Record<AiUsageEvent["capability"], EventName> = {
|
||||
chat: "ai.chat",
|
||||
similar_question: "ai.similar_question",
|
||||
grading_assist: "ai.grading_assist",
|
||||
lesson_content: "ai.lesson_content",
|
||||
question_variant: "ai.question_variant",
|
||||
weakness_analysis: "ai.weakness_analysis",
|
||||
}
|
||||
|
||||
/**
|
||||
* AI 使用埋点
|
||||
*
|
||||
* 记录每次 AI 调用的元数据,用于监控、成本分析与异常排查。
|
||||
* 非阻塞,失败不影响主流程。
|
||||
*/
|
||||
export const trackAiUsage = (event: AiUsageEvent): void => {
|
||||
const eventName = AI_EVENT_MAP[event.capability]
|
||||
void trackEvent({
|
||||
event: eventName,
|
||||
userId: event.userId,
|
||||
targetType: event.capability,
|
||||
properties: {
|
||||
providerId: event.providerId,
|
||||
model: event.model,
|
||||
success: event.success,
|
||||
durationMs: event.durationMs,
|
||||
tokenUsage: event.tokenUsage,
|
||||
errorMessage: event.errorMessage,
|
||||
},
|
||||
}).catch(() => {
|
||||
// 静默失败:埋点不应影响业务流程
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 测量 AI 调用耗时并自动埋点
|
||||
*/
|
||||
export const withAiTracking = async <T>(
|
||||
userId: string,
|
||||
capability: AiUsageEvent["capability"],
|
||||
providerId: string | undefined,
|
||||
fn: () => Promise<{ result: T; model?: string; tokenUsage?: number }>
|
||||
): Promise<T> => {
|
||||
const start = Date.now()
|
||||
try {
|
||||
const { result, model, tokenUsage } = await fn()
|
||||
trackAiUsage({
|
||||
userId,
|
||||
capability,
|
||||
providerId,
|
||||
model,
|
||||
success: true,
|
||||
durationMs: Date.now() - start,
|
||||
tokenUsage,
|
||||
})
|
||||
return result
|
||||
} catch (error) {
|
||||
trackAiUsage({
|
||||
userId,
|
||||
capability,
|
||||
providerId,
|
||||
success: false,
|
||||
durationMs: Date.now() - start,
|
||||
errorMessage: error instanceof Error ? error.message : String(error),
|
||||
})
|
||||
throw error
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user