diff --git a/VideoAnalysisCore/AICore/ChatGPT/Dto/CallGPTRes.cs b/VideoAnalysisCore/AICore/ChatGPT/Dto/CallGPTRes.cs index 4890494..9b610dc 100644 --- a/VideoAnalysisCore/AICore/ChatGPT/Dto/CallGPTRes.cs +++ b/VideoAnalysisCore/AICore/ChatGPT/Dto/CallGPTRes.cs @@ -20,7 +20,7 @@ namespace VideoAnalysisCore.AICore.ChatGPT.Dto public TaskRes(TotalCaptionsDto captions) { this.TeacherSpeaking = captions.TeacherSpeaking; - this.TimeBase = captions.TimeBase; + this.TimeBase = captions.TimeBase?.ToList(); this.StudentSpeaking = captions.StudentSpeaking; } /// @@ -45,11 +45,11 @@ namespace VideoAnalysisCore.AICore.ChatGPT.Dto /// /// 时间段概览 /// - public IEnumerable? TimeOverview { get; set; } + public TimeBase[]? TimeOverview { get; set; } /// /// 视频时间轴 /// - public IEnumerable? TimeBase { get; set; } + public List? TimeBase { get; set; } /// /// GPT模型id /// diff --git a/VideoAnalysisCore/AICore/ChatGPT/KIMI/KIMI_GPT.cs b/VideoAnalysisCore/AICore/ChatGPT/KIMI/KIMI_GPT.cs index 67102ad..b312330 100644 --- a/VideoAnalysisCore/AICore/ChatGPT/KIMI/KIMI_GPT.cs +++ b/VideoAnalysisCore/AICore/ChatGPT/KIMI/KIMI_GPT.cs @@ -60,14 +60,14 @@ namespace VideoAnalysisCore.AICore.ChatGPT.KIMI criteriaBuilder.Append(item.Id); criteriaBuilder.Append(":"); criteriaBuilder.Append(item.NamePrompt); - criteriaBuilder.Append("? 请基于字幕内容对问题进行精确打分"); + criteriaBuilder.Append("? 请基于解释精确打分"); criteriaBuilder.Append("0-"); criteriaBuilder.Append((int)(item.TotalScore * 10)); criteriaBuilder.Append("分"); criteriaBuilder.Append((int)(item.PassScore * 10)); criteriaBuilder.Append("分为及格"); criteriaBuilder.Append(":"); - criteriaBuilder.Append("array=[得分(得分不能超过满分/如果字幕内容不符合问题则给低分),问题的回答,问题的详细改进意见,问题的详细扣分原因]|"); + criteriaBuilder.Append("array=[得分,问题的回答,问题的详细改进意见,问题的详细扣分原因] 限制:给出的得分不要超过满分|"); } //拼接枚举提问 @@ -84,7 +84,7 @@ namespace VideoAnalysisCore.AICore.ChatGPT.KIMI criteriaBuilder.Append("|"); } - var resFormat = """[{"问题编号":number,"结果":array|bool|object,"问题解释":string}]"""; + var resFormat = """{"问题编号":number,"结果":array|bool|object,"问题解释":string}"""; var postMessages = $"你是一个教学经验老道老师对教学工作有着深入的理解和丰富的经验,能够准确把握教学大纲的要求和教学重点。" + $"熟练掌握各种教学管理方法和手段,能够制定科学合理的教学计划和教学评估体系。" + @@ -92,7 +92,8 @@ namespace VideoAnalysisCore.AICore.ChatGPT.KIMI $"以下是一段音频的字幕,分析这段字幕 字幕格式(说话人:开始秒:结束秒:内容|下一段字幕)." + $"字幕列表 {captions.Captions} " + $"基于字幕内容回答提出的所有问题 问题格式(问题编号:问题描述:结束秒:结果类型|下一个问题)" + - $"返回固定的JSON数组格式({resFormat})"; + $"问题列表 {criteriaBuilder} " + +$"返回固定的JSON数组格式({resFormat})"; var reqTokenCount = await moonshotClient.GetAsTiMateTokenCount(postMessages); @@ -112,15 +113,23 @@ namespace VideoAnalysisCore.AICore.ChatGPT.KIMI }; RedisExpand.SetTaskGPTReqCached(task, chatRep); - var chatResp = await moonshotClient.Chat(chatRep); - RedisExpand.SetTaskGPTCached(task, chatResp); - if (chatResp is null || chatResp.error != null) - throw new Exception($"KIMI模型返回异常 Chat 返回参数: " + - $" {JsonSerializer.Serialize(chatResp)}"); - var chatResContent = chatResp?.choices.FirstOrDefault()?.message.content.Trim(); - chatResContent = chatResContent?.Replace("字幕内容", "课堂情况"); + var chatResp = await moonshotClient.ChatSSE(chatRep); + //chatResp = await moonshotClient.Chat(chatRep); + var chatResContent = chatResp?.res; if (string.IsNullOrEmpty(chatResContent)) throw new Exception("KIMIGPT返回message无效结果"); + if(chatResp !=null) + RedisExpand.SetTaskGPTCached(task, new object[]{ chatResp.Value.res, chatResp.Value.u }); + + chatResContent = chatResContent?.Replace("字幕内容", "课堂情况"); + chatResContent = chatResContent?.Replace("\n", ""); + chatResContent = chatResContent?.Replace("}{", "},{"); + chatResContent = chatResContent?.Replace("}|{", "},{"); + chatResContent = chatResContent?.Trim(); + if (!chatResContent.StartsWith("[")) + chatResContent = "[" + chatResContent; + if (!chatResContent.EndsWith("]")) + chatResContent = chatResContent+ "]"; var questionRes = JsonSerializer.Deserialize(chatResContent); var gptRes = new TaskRes(captions); if (questionRes is null) @@ -176,7 +185,7 @@ namespace VideoAnalysisCore.AICore.ChatGPT.KIMI foreach (var s in arr) s.TimeBaseType = r; } - var totalTokens = chatResp?.usage.total_tokens ?? 0; + var totalTokens = chatResp?.u.total_tokens ?? 0; if (totalTokens > 1) { var tid = long.Parse(task); diff --git a/VideoAnalysisCore/AICore/ChatGPT/KIMI/MoonshotClient.cs b/VideoAnalysisCore/AICore/ChatGPT/KIMI/MoonshotClient.cs index 8e66096..3baabe7 100644 --- a/VideoAnalysisCore/AICore/ChatGPT/KIMI/MoonshotClient.cs +++ b/VideoAnalysisCore/AICore/ChatGPT/KIMI/MoonshotClient.cs @@ -9,6 +9,9 @@ using System.Net.Http.Json; using AntDesign; using OneOf.Types; using System.Net; +using Azure; +using System.Reflection.PortableExecutable; +using static System.Runtime.InteropServices.JavaScript.JSType; /// /// https://platform.moonshot.cn/docs/api-reference @@ -47,7 +50,56 @@ namespace VideoAnalysisCore.AICore.ChatGPT.KIMI { var chatResp = await PostJsonStreamAsync("/v1/chat/completions", requestBody); return await chatResp.Content.ReadFromJsonAsync(); - + + } + + /// + /// ChatSSE + /// + /// + /// Return HttpResponseMessage for SSE + public async Task<(Usage u, string res)?> ChatSSE(ChatReq chatReq) + { + chatReq.stream = true; + var requestBody = System.Text.Json.JsonSerializer.Serialize(chatReq); + var chatResp = await PostJsonStreamAsync("/v1/chat/completions", requestBody); + using var stream = await chatResp.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(stream, Encoding.UTF8); + string line; + StringBuilder messageBuilder = new StringBuilder(); + ChatResSSE lastChat = new ChatResSSE(); + while ((line = await reader.ReadLineAsync()) != null) + { + if (line.EndsWith("[DONE]")) + { + // 表示一条消息结束 + string message = messageBuilder.ToString(); + messageBuilder.Clear(); + var u = lastChat?.choices?.FirstOrDefault()?.usage; + if (u == null || string.IsNullOrEmpty(message)) + return null; + return (u, message); + } + else if (line.StartsWith("data:")) + { + try + { + var data = System.Text.Json.JsonSerializer.Deserialize(line.Substring("data:".Length).Trim()); + lastChat = data; + var str = data?.choices.FirstOrDefault()?.delta.content; + if (!string.IsNullOrEmpty(str)) + messageBuilder.Append(str); + } + catch (Exception e) + { + Console.WriteLine("异常 ChatSSE=>" ); + Console.WriteLine(line); + Console.WriteLine(e.Message); + Console.WriteLine(e.StackTrace); + } + } + } + return null; } /// @@ -55,11 +107,22 @@ namespace VideoAnalysisCore.AICore.ChatGPT.KIMI /// /// /// Return HttpResponseMessage for SSE - public async Task Chat(ChatReq chatReq) + public async Task<(Usage u, string res)?> Chat(ChatReq chatReq) { var requestBody = System.Text.Json.JsonSerializer.Serialize(chatReq); var chatResp = await PostJsonStreamAsync("/v1/chat/completions", requestBody); - return await chatResp.Content.ReadFromJsonAsync(); + var res = await chatResp.Content.ReadFromJsonAsync(); + var chatResContent = res?.choices.FirstOrDefault()?.message.content.Trim(); + + if (res is null || res.error != null) + throw new Exception($"KIMI模型返回异常 Chat 返回参数: " + + $" {System.Text.Json.JsonSerializer.Serialize(res)}"); + + if (string.IsNullOrEmpty(chatResContent)) + return null; + + + return (res.usage, chatResContent); } /// @@ -206,7 +269,7 @@ namespace VideoAnalysisCore.AICore.ChatGPT.KIMI var content = new StringContent(json, Encoding.UTF8, "application/json"); return await client.PostAsync(uriBuilder.Uri, content); - } + } catch (Exception e) { errorMSG[i] = e; @@ -219,7 +282,7 @@ namespace VideoAnalysisCore.AICore.ChatGPT.KIMI } Thread.Sleep(1000); } - throw errorMSG.Last(s=>s!=null); + throw errorMSG.Last(s => s != null); } private HttpRequestMessage ToHttpRequest(string path) diff --git a/VideoAnalysisCore/AICore/ChatGPT/KIMI/MoonshotModel.cs b/VideoAnalysisCore/AICore/ChatGPT/KIMI/MoonshotModel.cs index 0d54c0c..5697d68 100644 --- a/VideoAnalysisCore/AICore/ChatGPT/KIMI/MoonshotModel.cs +++ b/VideoAnalysisCore/AICore/ChatGPT/KIMI/MoonshotModel.cs @@ -148,6 +148,19 @@ public string type { get; set; } = string.Empty; } + public class ChatResSSE + { + public string id { get; set; } + public int created { get; set; } + /// + /// 模型id + /// + public string model { get; set; } + /// + /// 对话 + /// + public ChoiceSSE[] choices { get; set; } + } public class ChatRes { public ChatResError? error { get; set; } @@ -185,6 +198,16 @@ public int total_tokens { get; set; } } + public class ChoiceSSE + { + public int index { get; set; } + public Message delta { get; set; } + public string finish_reason { get; set; } + /// + /// token使用情况 + /// + public Usage usage { get; set; } + } public class Choice { public int index { get; set; } diff --git a/VideoAnalysisCore/Common/RedisExpand.cs b/VideoAnalysisCore/Common/RedisExpand.cs index 22250e9..58df67f 100644 --- a/VideoAnalysisCore/Common/RedisExpand.cs +++ b/VideoAnalysisCore/Common/RedisExpand.cs @@ -105,7 +105,7 @@ namespace VideoAnalysisCore.Common /// public static void SetTaskGPTCached(object taskId, object? data) { - Redis.Set(RedisExpandKey.TaskGPT(taskId), data, 3600 * 24); + Redis.Set(RedisExpandKey.TaskGPT(taskId)+":Res_" + DateTime.Now.ToString("yyyy/MM/dd_HH/mm/ss"), data, 3600 * 24); } public static void SetTaskGPTReqCached(object taskId, object? data) { diff --git a/VideoAnalysisCore/Enum/QuestionTypeEnum.cs b/VideoAnalysisCore/Enum/QuestionTypeEnum.cs index f5811be..61c311d 100644 --- a/VideoAnalysisCore/Enum/QuestionTypeEnum.cs +++ b/VideoAnalysisCore/Enum/QuestionTypeEnum.cs @@ -11,16 +11,16 @@ namespace VideoAnalysisCore.Enum { [Display(Prompt = "分析授课中使用的高频词" + - "10个频率从高到低 结果类型[]")] + "10个频率从高到低 :结果类型[]")] 高频词 = 5001, [Display(Prompt = "基于字幕内容精准的提取出课堂上全部的事件" + - ",分析事件的时间片段提取事件的的内容概览,字幕开始秒,结束秒.作为返回结果.每个片段不低于120秒 结果类型[{Start:开始秒,End:结束秒,Content:概览}]")] + ",分析事件的时间片段提取事件的的内容概览,字幕开始秒,结束秒.作为返回结果.每个片段不低于120秒 :结果类型[{Start:开始秒,End:结束秒,Content:概览}]")] 时间段概览 = 5002, [Display(Prompt = "统计授课中教师回答类型的次数 回答类型" + - "[重复回答,老师追问,简单性表扬,老师补充答案,表扬并补充答案] 结果类型{回答类型:次数}")] + "[重复回答,老师追问,简单性表扬,老师补充答案,表扬并补充答案] :结果类型{回答类型:次数}")] 提问类型 = 5003, [Display(Prompt = " 分析授课中教师提到 以下类型" + - "[独立学习,小组合作,随堂练习]的时间段,精准提取出其中字幕开始秒,结束秒 结果类型[{Start:开始秒(number),End:结束秒(number),Content:类型(string)}/null]")] + "[独立学习,小组合作,随堂练习]的时间段,精准提取出其中字幕开始秒,结束秒 :结果类型[{Start:开始秒(number),End:结束秒(number),Content:类型(string)}/null]")] 额外课堂情况 = 5004, } }