diff --git a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekClient.cs b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekClient.cs index 0608794..eb13cb4 100644 --- a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekClient.cs +++ b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekClient.cs @@ -24,14 +24,14 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek //public static string Host = AppCommon.Config.ChatGpt.aliyun.Host; //public static string ApiKey = AppCommon.Config.ChatGpt.aliyun.ApiKey; - //private readonly string Path = ""; - //public static string Host = AppCommon.Config.ChatGpt.DeepSeek.Host; - //public static string ApiKey = AppCommon.Config.ChatGpt.DeepSeek.ApiKey; + private readonly string Path = ""; + public static string Host = AppCommon.Config.ChatGpt.DeepSeek.Host; + public static string ApiKey = AppCommon.Config.ChatGpt.DeepSeek.ApiKey; - public static string Host = AppCommon.Config.ChatGpt.ChatGpt.Host; - public static string ApiKey = AppCommon.Config.ChatGpt.ChatGpt.ApiKey; - private readonly string Path = "v1/chat/completions"; + //public static string Host = AppCommon.Config.ChatGpt.ChatGpt.Host; + //public static string ApiKey = AppCommon.Config.ChatGpt.ChatGpt.ApiKey; + //private readonly string Path = "v1/chat/completions"; private readonly IHttpClientFactory _httpClientFactory; diff --git a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs index cdf98f1..07d5348 100644 --- a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs +++ b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs @@ -13,6 +13,8 @@ using VideoAnalysisCore.Model.蓝鲸智库; using VideoAnalysisCore.Model.Enum; using Mapster; using System.Linq; +using System.Security.Cryptography; +using static System.Collections.Specialized.BitVector32; namespace VideoAnalysisCore.AICore.GPT.DeepSeek { @@ -432,38 +434,82 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task); return gptRes; } - - /// - /// 获取知识点 + /// 获取内容对应的章节 /// - /// 任务id /// - public async Task GetKnow(string task) + public async Task> GetVideoKnow(List questionRes,VideoTask taskInfo, string sections, int course_Id) + { + + var know = await knowledgeInfoDB.GetFirstAsync(s => s.Course_Id == course_Id && s.Name == sections); + if (know is null) + throw new Exception("未能找到对应知识点=>" + sections); + + var subject = taskInfo.Subject.ToString(); + + var kInfo = await knowledgeInfoDB.GetByIdAsync(know.Parent_Id); + var knowledgeInfos = await knowledgeInfoDB.AsQueryable() + .ToChildListAsync(s => s.Parent_Id, kInfo.Parent_Id == 0 ? kInfo.Id : kInfo.Parent_Id); + var knows = string.Join(',', knowledgeInfos.Select(s => s.Id + "|" + s.Name)); + var knowDic = knowledgeInfos + .OrderBy(s => s.Id) + .GroupBy(s => s.Name) + .ToDictionary(s => s.First().Name, s => s.First().Id); + questionRes = questionRes.Where(s => s != null).OrderBy(s => s.StartTime).ToList(); + var thems = JsonSerializer.Serialize(questionRes.Adapt());// string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme)); + var checkResFormat1 = """[{"StartTime":开始秒(number),"KnowPoint":知识点名称(string),"KnowPointId":知识点Id(string)}]"""; + var knowMessages = + $"我针对{subject}课堂授课视频分析出了视频的授课阶段片段。" + + $"现在需要你通过每个片段的内容总结来分配正确的知识点(单个片段允许多个知识点用逗号','分割)。" + + $"这是我的分段 {thems}。" + + $"课堂内容与{sections}章节相关" + + $"最后请确保分配的知识点是用户提供的,并且一定正确合理!" + + $"输出内容只返回json格式({checkResFormat1})" + + $" 格式 (方法点Id|方法点名称) " + + $"提供的知识点名称({knows})。"; + Console.WriteLine(DateTime.Now + "=>2.开始分析视频内容知识点"); + var konwRes = await ChatAsync(taskInfo.Id.ToString(), knowMessages, null); + + for (int i = 0; i < konwRes.Count(); i++) + questionRes[i].KnowPoint = konwRes[i].KnowPoint; + + + + //todo 未包含的知识点片段 如何处理 + return questionRes + .Where(s => !string.IsNullOrEmpty(s.KnowPoint)) + .SelectMany( + s => + { + var ks = s.KnowPoint.Split(",").Distinct(); + return ks.Where(x => knowDic.ContainsKey(x)) + .Select(x => new VideoKonwPoint() + { + Content = s.Content, + Theme = s.Theme, + StartTime = s.StartTime, + EndTime = s.EndTime, + KnowPoint = x, + KnowPointId = knowDic[x].ToString(), + TagId = taskInfo.TagId, + VideoTaskId = taskInfo.Id, + }); + }).ToList(); + + + } + /// + /// 获取内容对应的章节 + /// + /// + public async Task GetSections(VideoTask taskInfo,int course_Id) { - var taskId = long.Parse(task); - var taskInfo = await videoTaskDB.AsQueryable() - .Where(s => s.Id == taskId) - .FirstAsync(); - var subject = "数学"; - var Course_Id = 27; - switch (taskInfo.Type)//处理不同任务类型的知识点树 - { - case TaskTypeEnum.蓝鲸智库_中职视频分段: - Course_Id = 51; - break; - case TaskTypeEnum.蓝鲸智库_视频分段: - default: - Course_Id = 27; - break; - } var xkwKnows = await knowledgeInfoDB.AsQueryable() - .Where(s => s.Course_Id == Course_Id - && (s.Depth == 3 - || s.Depth == 2)) - .Select(s => s.Name).ToArrayAsync(); + .Where(s => s.Course_Id == course_Id + && (s.Depth == 3 + || s.Depth == 2)) + .Select(s => s.Name).ToArrayAsync(); string title = taskInfo.MediaName; - var speakerArr = JsonSerializer.Deserialize(taskInfo.Speaker); var captionsArr = JsonSerializer.Deserialize(taskInfo.Captions); var fileNameResFormat = "{授课章节: string|null}"; @@ -478,29 +524,49 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek $"以下是包含时间的视频字幕文本。" + $"字幕列表 {rCaptionArr}。" + $"输出格式 json字符串 对象格式{fileNameResFormat}"; - + var task = taskInfo.Id.ToString(); var fileNameInfoRes = await ChatAsync (task, fileNamePostMessages, null); -#if DEBUG - //fileNameInfoRes = new FileNameInfo() { 授课章节 = "一元二次不等式" }; -#endif - var captions = ExpandFunction.GetSpeakerCaptions(captionsArr, speakerArr); - var maxVideoTime = captions?.TimeBase?.LastOrDefault()?.End ?? 0; - var criteriaBuilder = new StringBuilder(); - var know = await knowledgeInfoDB.GetFirstAsync(s => s.Course_Id == Course_Id && s.Name == fileNameInfoRes.授课章节); - if (know is null) - throw new Exception("未能找到对应知识点=>" + fileNameInfoRes.授课章节); - + await videoTaskDB.AsUpdateable() + .SetColumns(it => it.Sections == fileNameInfoRes.授课章节) + .Where(it => it.Id == taskInfo.Id) + .ExecuteCommandAsync(); await RedisExpand.Redis .HMSetAsync(RedisExpandKey.Task(task), "学科章节", fileNameInfoRes.授课章节); - //提升到父级 - var kInfo = await knowledgeInfoDB.GetByIdAsync(know.Parent_Id); - var knowledgeInfos = await knowledgeInfoDB.AsQueryable().ToChildListAsync(s => s.Parent_Id, kInfo.Parent_Id == 0 ? kInfo.Id : kInfo.Parent_Id); - var knows = string.Join(',', knowledgeInfos.Select(s => s.Id + "|" + s.Name)); - var knowDic = knowledgeInfos - .OrderBy(s => s.Id) - .GroupBy(s => s.Name) - .ToDictionary(s => s.First().Name, s => s.First().Id); + + return fileNameInfoRes?.授课章节; + } + /// + /// 获取知识点 + /// + /// 任务id + /// + public async Task GetKnow(string task) + { + var taskId = long.Parse(task); + var taskInfo = await videoTaskDB.AsQueryable() + .Where(s => s.Id == taskId) + .FirstAsync(); + var subject = taskInfo.Subject.ToString(); + var Course_Id = 27; + switch (taskInfo.Type)//处理不同任务类型的知识点树 + { + case TaskTypeEnum.蓝鲸智库_中职视频分段: + Course_Id = 51; + break; + case TaskTypeEnum.蓝鲸智库_视频分段: + default: + Course_Id = 27; + break; + } + + var captionsArr = JsonSerializer.Deserialize(taskInfo.Captions); + //处理视频授课章节 + var sections = await GetSections(taskInfo, Course_Id); + //合并字幕 + var captions = ExpandFunction.GetSpeakerCaptions(captionsArr); + var maxVideoTime = captions?.TimeBase?.LastOrDefault()?.End ?? 0; + var questionRes = new List(); while (true) { @@ -523,7 +589,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek var resFormat = """[{"StartTime":开始秒(number),"EndTime":结束秒(number),"Stage":阶段(string),"Theme":主题(string),"Content":内容总结(string)}]"""; var postMessages = $"请通过视频字幕内容分析出视频中{subject}课堂的授课阶段。" + - $"课堂内容与{fileNameInfoRes.授课章节}章节相关。" + + $"课堂内容与{sections}章节相关。" + $"{keyFrameArr}" + $"完整的课堂标准流程包含以下5个阶段:课程引入/新知讲解/例题精讲/课堂练习/知识总结。" + $"通过授课阶段的主要讲解内容分析出对应的授课阶段内容总结。" + @@ -548,36 +614,10 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek } } if (questionRes.Count == 0) continue; - questionRes = questionRes.OrderBy(s => s.StartTime).ToList(); - var thems = JsonSerializer.Serialize(questionRes.Adapt());// string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme)); - var checkResFormat1 = """[{"StartTime":开始秒(number),"KnowPoint":知识点名称(string),"KnowPointId":知识点Id(string)}]"""; - var knowMessages = - $"我针对{subject}课堂授课视频分析出了视频的授课阶段片段。" + - $"现在需要你通过每个片段的内容总结来分配正确的知识点(单个片段允许多个知识点用逗号','分割)。" + - $"这是我的分段 {thems}。" + - $"课堂内容与{fileNameInfoRes.授课章节}章节相关" + - $"最后请确保分配的知识点是用户提供的,并且一定正确合理!" + - $"输出内容只返回json格式({checkResFormat1})" + - $" 格式 (方法点Id|方法点名称) " + - $"提供的知识点名称({knows})。"; - Console.WriteLine(DateTime.Now + "=>2.开始分析视频内容知识点"); - var konwRes = await ChatAsync(task, knowMessages, null); - - for (int i = 0; i < konwRes.Count(); i++) - questionRes[i].KnowPoint = konwRes[i].KnowPoint; - VideoKnowRes lastVideoKnow = null; - for (int i = 0; i < questionRes.Count(); i++) - { - var item = questionRes[i]; - // 阶段类型相等,且范围包含上一阶段 - if (lastVideoKnow != null && lastVideoKnow.Stage == item.Stage && item.StartTime < lastVideoKnow.EndTime) - { - questionRes[i] = null; - lastVideoKnow.EndTime= item.EndTime; - } - lastVideoKnow = questionRes[i]; - } - thems = JsonSerializer.Serialize(questionRes.Adapt()); + //处理分段 知识点 + var insertData = await GetVideoKnow(questionRes, taskInfo, sections, Course_Id); + //校验结果质量 + var thems = JsonSerializer.Serialize(questionRes.Adapt()); var checkResFormat = """{"Score":打分(number),"Evaluation":评价(string)""";//,"Data":优化后的分段(array)}"""; var checkMessage = "我为视频的讲解内容做了一些分段,希望你能通读字幕内容后检查下的分段是否符合我的要求?" + $"检查这些分段的时间是否合理 与相邻的时间段间隔是否处于合理区间30~900秒之间?" + @@ -587,11 +627,14 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek $"后续的内容是包含时间戳的视频字幕的固定格式文本。" + $"字幕格式(说话人:开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" + $"最后输出格式为json({checkResFormat})"; - Console.WriteLine(DateTime.Now + "=>3.开始检查视频分段结果"); var checkRes = await ChatAsync(task, checkMessage, null); + if (checkRes != null && checkRes.Score >= 80) { + //写入知识点 + await videoKonwPointDB.DeleteAsync(s => s.VideoTaskId == taskInfo.Id); + await videoKonwPointDB.InsertRangeAsync(insertData); break; } else @@ -608,29 +651,6 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek } - //todo 未包含的知识点片段 如何处理 - var insertData = questionRes - .Where(s => !string.IsNullOrEmpty(s.KnowPoint)) - .SelectMany( - s => - { - var ks = s.KnowPoint.Split(",").Distinct(); - return ks.Where(x => knowDic.ContainsKey(x)) - .Select(x => new VideoKonwPoint() - { - Content = s.Content, - Theme = s.Theme, - StartTime = s.StartTime, - EndTime = s.EndTime, - KnowPoint = x, - KnowPointId = knowDic[x].ToString(), - TagId = taskInfo.TagId, - VideoTaskId = taskInfo.Id, - }); - }).ToList(); - await videoKonwPointDB.DeleteAsync(s => s.VideoTaskId == taskId); - await videoKonwPointDB.InsertRangeAsync(insertData); - @@ -640,6 +660,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek var gptRes = new TaskRes(captions); await RedisExpand.Redis .HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes); + RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task); return gptRes; } diff --git a/VideoAnalysisCore/Common/AppCommon.cs b/VideoAnalysisCore/Common/AppCommon.cs index b4e0e11..211429b 100644 --- a/VideoAnalysisCore/Common/AppCommon.cs +++ b/VideoAnalysisCore/Common/AppCommon.cs @@ -178,7 +178,7 @@ namespace VideoAnalysisCore.Common /// /// 获取Task处理后的 说话人字幕 /// - public static TotalCaptionsDto GetSpeakerCaptions(SenseVoiceRes[] captionsArr, OfflineSpeakerRes[] speakerArr) + public static TotalCaptionsDto GetSpeakerCaptions(SenseVoiceRes[] captionsArr, OfflineSpeakerRes[]? speakerArr=null) { if (captionsArr is null || captionsArr.Length == 0) //|| speakerArr is null || speakerArr.Length == 0) diff --git a/VideoAnalysisCore/Job/NodePackageJob.cs b/VideoAnalysisCore/Job/NodePackageJob.cs index 52ca910..b895455 100644 --- a/VideoAnalysisCore/Job/NodePackageJob.cs +++ b/VideoAnalysisCore/Job/NodePackageJob.cs @@ -62,31 +62,36 @@ namespace VideoAnalysisCore.Job return; //var responseMessage = await new HttpClient() // .PostAsJsonAsync(AppCommon.Config.Subsystem.蓝鲸智库.APIUrl + "/api/callback/platform/videosAnalyze", postData); - foreach (var item in taskArr) { + foreach (var item in taskArr) + { HttpResponseMessage responseMessage = null; - if (!string.IsNullOrWhiteSpace(item.CallBackUrl)) - { - responseMessage = await new HttpClient() - .PostAsJsonAsync(item.CallBackUrl, postData); - } - else + try { + var postUrl = !string.IsNullOrWhiteSpace(item.CallBackUrl) + ? item.CallBackUrl + : AppCommon.Config.Subsystem.蓝鲸智库.APIUrl + "/api/callback/platform/videosAnalyze"; responseMessage = await new HttpClient() - .PostAsJsonAsync(AppCommon.Config.Subsystem.蓝鲸智库.APIUrl + "/api/callback/platform/videosAnalyze", postData); - } - if (responseMessage.IsSuccessStatusCode) - { - var res = await responseMessage.Content.ReadAsStringAsync(); - Console.WriteLine($"{DateTime.Now} 执行=>文件包任务 回调结果 {res}"); + .PostAsJsonAsync(postUrl, postData); + if (responseMessage.IsSuccessStatusCode) + { + var res = await responseMessage.Content.ReadAsStringAsync(); + Console.WriteLine($"{DateTime.Now} 执行=>文件包任务 回调结果 {res}"); - await nodePackageInfoDB.AsUpdateable(postData) - .UpdateColumns(it => new { it.SuccessTime }) - .ExecuteCommandAsync(); + await nodePackageInfoDB.AsUpdateable(postData) + .UpdateColumns(it => new { it.SuccessTime }) + .ExecuteCommandAsync(); + } + else + { + var res = await responseMessage.Content.ReadAsStringAsync(); + Console.WriteLine($"{DateTime.Now} 执行=>文件包任务 回调失败!!! {responseMessage.StatusCode} {res}"); + } } - else + catch (Exception ex) { - var res = await responseMessage.Content.ReadAsStringAsync(); + var res = await responseMessage?.Content?.ReadAsStringAsync()??string.Empty; Console.WriteLine($"{DateTime.Now} 执行=>文件包任务 回调失败!!! {responseMessage.StatusCode} {res}"); + Console.WriteLine(ex); } await Task.Delay(1000); } diff --git a/VideoAnalysisCore/Model/VideoTask.cs b/VideoAnalysisCore/Model/VideoTask.cs index 80b4480..42e5d07 100644 --- a/VideoAnalysisCore/Model/VideoTask.cs +++ b/VideoAnalysisCore/Model/VideoTask.cs @@ -71,7 +71,7 @@ namespace VideoAnalysisCore.Model /// /// 授课视频对应PPT视频ID /// - [SugarColumn(Length = 50, ColumnDataType = "varchar", IsNullable = true)] + [SugarColumn(Length = 255, ColumnDataType = "varchar", IsNullable = true)] public string? PPTVideoCode { get; set; } /// /// 授课视频对应PPT视频关键帧 @@ -117,5 +117,11 @@ namespace VideoAnalysisCore.Model [SugarColumn( IsNullable = true)] public DateTime? EndTime { get; set; } + /// + /// 授课章节 + /// + [SugarColumn(IsNullable = true)] + public string? Sections { get; set; } + } }