package ai import ( "time" "gorm.io/gorm" "eai_agentplatform/backend/internal/model" "eai_agentplatform/backend/internal/store" ) // ────────────────────────────────────────────── // 按用户算力点计费 // ────────────────────────────────────────────── // AI 用量类型常量 const ( UsageKindAIChat = "ai_chat" // PathCoach 对话(每轮扣 1 点) UsageKindTextGen = "text_gen" // 快捷动作(情景演练/查佣金/产品对比,扣 1 点) UsageKindImageGen = "image_gen" // 文生图(按次扣点) UsageKindEmbed = "embed" // 知识检索内部 embedding(不扣点,仅审计) UsageKindEssayGrade = "essay_grade" // 简答题 LLM 评分(系统自动,不扣点,仅审计) // 以下 5 类为工具型 AI 调用,当前只审计、不扣点: // 它们不在 UsageKindCredits 表里,ComputeCredits 查不到即返回 0。 // 将来若要计费,只需把它们加进 UsageKindCredits,扣点逻辑自动生效。 UsageKindBatchExtract = "batch_extract" UsageKindContractReview = "contract_review" UsageKindAudioTranscribe = "audio_transcribe" UsageKindCopyProofread = "copy_proofread" UsageKindDocumentTranslate = "document_translate" ) // UsageKindCredits 各用量类型扣点成本 var UsageKindCredits = map[string]int{ UsageKindAIChat: 1, UsageKindTextGen: 1, UsageKindImageGen: 1, UsageKindEmbed: 0, UsageKindEssayGrade: 0, } // ComputeCredits 扣点决策:仅「成功」才扣点;失败不扣。 func ComputeCredits(usageKind string, success bool) int { if !success { return 0 } return UsageKindCredits[usageKind] } // LogEntry 一次 AI 调用的审计入参 type LogEntry struct { UserID uint UsageKind string // SpecialistKey 本次调用以哪个专员的身份进行;空 = 通用助手 SpecialistKey string Provider string AIRouteID string Model string TokensInput int TokensOutput int Success bool ErrorMessage string LatencyMs int } // LogCall 写 ai_call_log;成功且需扣点时从用户余额扣点。审计写入失败不阻断主流程。 func LogCall(e LogEntry) { // 数据库尚未初始化(如测试、独立 worker、无 DB 的嵌入场景)时直接跳过审计, // 绝不因写入 ai_call_log 的指针解引用而 panic 拖垮 AI 主链路。 if store.DB == nil { return } credits := ComputeCredits(e.UsageKind, e.Success) status := "success" if !e.Success { status = "failed" } rec := model.AiCallLog{ UserID: e.UserID, UsageKind: e.UsageKind, SpecialistKey: e.SpecialistKey, Provider: e.Provider, AIRouteID: e.AIRouteID, Model: e.Model, TokensInput: e.TokensInput, TokensOutput: e.TokensOutput, CreditsCharged: credits, Status: status, ErrorMessage: e.ErrorMessage, LatencyMs: e.LatencyMs, CreatedAt: time.Now(), } if err := store.DB.Create(&rec).Error; err != nil { return } if credits > 0 { store.DB.Model(&model.User{}).Where("id = ?", e.UserID). UpdateColumn("ai_points", gorm.Expr("ai_points - ?", credits)) } }