From deb7f80bce1360a48bf2f1532fba6841f252b680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=82=A5=E7=BE=8A?= <1048382248@qq.com> Date: Fri, 19 Dec 2025 17:41:24 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=20=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=BB=A3=E7=A0=81=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VideoAnalysisCore/AICore/GPT/BserGPT.cs | 6 +- .../AICore/GPT/ChatGPT/ChatGPTClient.cs | 7 +- VideoAnalysisCore/AICore/GPT/ChatGPTType.cs | 4 ++ VideoAnalysisCore/AICore/GPT/GPTClient.cs | 2 +- .../DeepSeek_GPT.cs => GTP_Analysis_1.cs} | 65 +++++++---------- .../AICore/GPT/Gemini/GeminiGPTClient.cs | 69 +++++++++++++++++++ VideoAnalysisCore/Common/RedisExpand.cs | 12 ++-- 7 files changed, 112 insertions(+), 53 deletions(-) rename VideoAnalysisCore/AICore/GPT/{DeepSeek/DeepSeek_GPT.cs => GTP_Analysis_1.cs} (85%) create mode 100644 VideoAnalysisCore/AICore/GPT/Gemini/GeminiGPTClient.cs diff --git a/VideoAnalysisCore/AICore/GPT/BserGPT.cs b/VideoAnalysisCore/AICore/GPT/BserGPT.cs index 48db489..4fa3ca3 100644 --- a/VideoAnalysisCore/AICore/GPT/BserGPT.cs +++ b/VideoAnalysisCore/AICore/GPT/BserGPT.cs @@ -15,7 +15,7 @@ using System.Text.Json.Serialization; namespace VideoAnalysisCore.AICore.GPT { - public interface IBserGPT + public interface IBserGPTWorkflow { /// /// 访问GPT @@ -190,8 +190,8 @@ namespace VideoAnalysisCore.AICore.GPT public static void AddGPTService(this IServiceCollection services) { services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); } diff --git a/VideoAnalysisCore/AICore/GPT/ChatGPT/ChatGPTClient.cs b/VideoAnalysisCore/AICore/GPT/ChatGPT/ChatGPTClient.cs index 261eacc..6a47fda 100644 --- a/VideoAnalysisCore/AICore/GPT/ChatGPT/ChatGPTClient.cs +++ b/VideoAnalysisCore/AICore/GPT/ChatGPT/ChatGPTClient.cs @@ -15,7 +15,10 @@ using System.Text.Json; namespace VideoAnalysisCore.AICore.GPT.ChatGPT { - public class ChatGPTClient : GPTClient + /// + /// 接入oaibest + /// + public class BestAIClient : GPTClient { public override GptConfig Config { get; set; } = AppCommon.Config.ChatGpt.ChatGpt; @@ -23,7 +26,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT private readonly IHttpClientFactory _httpClientFactory; private readonly RedisManager redisManager; - public ChatGPTClient(IHttpClientFactory httpClientFactory, RedisManager redisManager) : base(httpClientFactory, redisManager) + public BestAIClient(IHttpClientFactory httpClientFactory, RedisManager redisManager) : base(httpClientFactory, redisManager) { _httpClientFactory = httpClientFactory; this.redisManager = redisManager; diff --git a/VideoAnalysisCore/AICore/GPT/ChatGPTType.cs b/VideoAnalysisCore/AICore/GPT/ChatGPTType.cs index c21c81d..f5aaef5 100644 --- a/VideoAnalysisCore/AICore/GPT/ChatGPTType.cs +++ b/VideoAnalysisCore/AICore/GPT/ChatGPTType.cs @@ -16,5 +16,9 @@ namespace VideoAnalysisCore.AICore.GPT public static string Deepseek_Chat = "deepseek-chat"; + public static string Gemini_3_Chat_thinking = "gemini-3-pro-preview-thinking"; + public static string Gemini_3_Chat = "gemini-3-pro-preview"; + + } } diff --git a/VideoAnalysisCore/AICore/GPT/GPTClient.cs b/VideoAnalysisCore/AICore/GPT/GPTClient.cs index 7ce403f..441e737 100644 --- a/VideoAnalysisCore/AICore/GPT/GPTClient.cs +++ b/VideoAnalysisCore/AICore/GPT/GPTClient.cs @@ -167,7 +167,7 @@ namespace VideoAnalysisCore.AICore.GPT var chatResp = await Chat(chatRep); var chatResContent = chatResp?.res; if (string.IsNullOrEmpty(chatResContent)) - throw new Exception("GPT返回message无效结果"); + throw new Exception($"GPT返回message无效结果 => res:{chatResp?.res} reasoning:{chatResp?.reasoning}"); if (chatResp != null) { redisCached[1] = new object[] { chatResp.Value.res, chatResp.Value.u, chatResp.Value.reasoning }; diff --git a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs b/VideoAnalysisCore/AICore/GPT/GTP_Analysis_1.cs similarity index 85% rename from VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs rename to VideoAnalysisCore/AICore/GPT/GTP_Analysis_1.cs index 5e11d2f..d03f6f2 100644 --- a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs +++ b/VideoAnalysisCore/AICore/GPT/GTP_Analysis_1.cs @@ -26,12 +26,13 @@ using System.Text.RegularExpressions; namespace VideoAnalysisCore.AICore.GPT.DeepSeek { /// - /// chatgpt 文本模型 + /// 视频分析工作流1 /// - public class DeepSeek_GPT : IBserGPT + public class GTP_Analysis_1 : IBserGPTWorkflow { + private readonly GeminiGPTClient geminiClient; private readonly DeepSeekGPTClient chatClient; - private readonly ChatGPTClient chatGPTClient; + private readonly BestAIClient chatGPTClient; private readonly Repository criteriaDB; private readonly RedisManager redisManager; private readonly Repository videoTaskDB; @@ -46,9 +47,9 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek /// /// /// - public DeepSeek_GPT(DeepSeekGPTClient moonshotClient, Repository criteria, Repository videoTaskDB, + public GTP_Analysis_1(DeepSeekGPTClient moonshotClient, Repository criteria, Repository videoTaskDB, Repository knowledgeInfoDB, Repository videoKonwPointDB, SimpLetexClient simpLetexClient, - Repository videoQuestionDB, OssClient ossClient, Repository videoQuestionKonwDB, RedisManager redisManager, ChatGPTClient chatGPTClient) + Repository videoQuestionDB, OssClient ossClient, Repository videoQuestionKonwDB, RedisManager redisManager, BestAIClient chatGPTClient, GeminiGPTClient geminiClient) { chatClient = moonshotClient; criteriaDB = criteria; @@ -61,6 +62,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek this.videoQuestionKonwDB = videoQuestionKonwDB; this.redisManager = redisManager; this.chatGPTClient = chatGPTClient; + this.geminiClient = geminiClient; } /// /// 获取分段内容对应的章节知识点 @@ -297,8 +299,8 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek try { var keyFrameArr = string.IsNullOrEmpty(taskInfo?.PPTVideoCode) || string.IsNullOrEmpty(taskInfo?.PPTKeyFrame) - ? string.Empty - : $"授课PPT发生了变化的时间是{taskInfo.PPTKeyFrame},所以在这些时间段附近应该发生了授课内容得变化,授课阶段结果尽量参考这些时间节点(PPT与字幕有一定的延时)。"; + ? $"" + : $"授课PPT发生了变化的时间是{taskInfo.PPTKeyFrame},基于PPT变化时间点,将字幕内容分割成时间段。每个时间段的起始和结束应接近这些时间点(例如,以时间点为中心,扩展至内容自然过渡处)。"; var resFormat = """[{"StartTime":开始秒(number),"EndTime":结束秒(number),"Stage":阶段(string),"Theme":主题(string),"Content":内容总结(string)}]"""; var postMessages = string.Empty; @@ -306,46 +308,27 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek { case AttachmentsInfoType.其他资料: case AttachmentsInfoType.新课: - postMessages = - $"请通过视频字幕内容分析出视频中课堂的授课阶段。" + - $"课堂内容与{taskInfo.Subject}学科下的{sections}章节相关。" + - $"{keyFrameArr}" + - $"完整的课堂标准流程包含以下5个阶段:课程引入/新知讲解/例题精讲/课堂练习/知识总结。" + - $"每个类型的授课阶段允许有多个 例如 多个新知讲解/例题精讲..." + - $"1.初步划分阶段:基于PPT变化时间点,将字幕内容分割成时间段。每个时间段的起始和结束应接近这些时间点(例如,以时间点为中心,扩展至内容自然过渡处)。" + - $"2.内容分析:对每个时间段,提取主要讲解内容:识别关键词(如“例题”“证明”“练习”“总结”)和内容结构。" + - $"3.判断阶段类型:如果内容以解题为主,归类为“例题精讲”;如果涉及新知识讲解,归类为“新知讲解”;以此类推。" + - $"4.生成内容总结与主题:内容总结:用1-2句话简述该阶段的核心讲解内容(例如,“通过例题演示柯西不等式在求最值中的应用”)。" + - $"5.阶段主题:基于内容总结,提炼一个具体主题(例如,“柯西不等式的基本应用”)。" + - $"6.时长检查与调整:计算每个阶段的时长(结束时间减开始时间)。如果阶段时长低于50秒,则合并相邻的类似内容阶段(例如,将两个连续的例题讲解合并为一个阶段),或扩展时间段以确保最低50秒。调整时需保持内容连贯性。" + - $"7.输出要求:最终分析结果应列出每个阶段的开始时间、结束时间、阶段类型、主题和详细的内容总结(50~100字不包含提示词内容),确保划分合理、无重叠,且时长符合要求。" + - $"输出内容只返回json格式({resFormat})" + - $"字幕格式(开始秒:内容|下一段字幕).以下是包含时间的视频字幕文本。" + - $"字幕列表 {captions.Captions} 字幕结束!"; - break; case AttachmentsInfoType.复习: - postMessages = - $"请通过视频字幕内容分析出视频中课堂的授课阶段。" + - $"课堂内容与{taskInfo.Subject}学科下的{sections}章节相关。" + - $"{keyFrameArr}" + - $"完整的课堂标准流程包含以下5个阶段:课程引入/新知讲解/例题精讲/课堂练习/知识总结。" + - $"但本堂课是习题课,所以大部分阶段是不同的例题讲解内容。" + - $"1.初步划分阶段:基于PPT变化时间点,将字幕内容分割成时间段。每个时间段的起始和结束应接近这些时间点(例如,以时间点为中心,扩展至内容自然过渡处)。" + - $"2.内容分析:对每个时间段,提取主要讲解内容:识别关键词(如“例题”“证明”“练习”“总结”)和内容结构。" + - $"3.判断阶段类型:如果内容以解题为主,归类为“例题精讲”;如果涉及新知识讲解,归类为“新知讲解”;以此类推。" + - $"4.生成内容总结与主题:内容总结:用1-2句话简述该阶段的核心讲解内容(例如,“通过例题演示柯西不等式在求最值中的应用”)。" + - $"5.阶段主题:基于内容总结,提炼一个具体主题(例如,“柯西不等式的基本应用”)。" + - $"6.时长检查与调整:计算每个阶段的时长(结束时间减开始时间)。如果阶段时长低于50秒,则合并相邻的类似内容阶段(例如,将两个连续的例题讲解合并为一个阶段),或扩展时间段以确保最低50秒。调整时需保持内容连贯性。" + - $"7.输出要求:最终分析结果应列出每个阶段的开始时间、结束时间、阶段类型、主题和详细的内容总结(50~100字不包含提示词内容),确保划分合理、无重叠,且时长符合要求。" + - $"输出格式要求:内容只返回json格式({resFormat})" + - $"字幕格式(开始秒:内容|下一段字幕).以下是包含时间的视频字幕文本。" + - $"字幕列表 {captions.Captions} 字幕结束!"; - break; case AttachmentsInfoType.活动: case AttachmentsInfoType.班会: + break; default: throw new Exception("无效的课程类型"); } + postMessages = + $"请通过视频字幕内容分析出视频中课堂的授课阶段。" + + $"课堂内容与{taskInfo.Subject}学科下的{sections}章节相关。" + + $"完整的课堂标准流程包含以下5个阶段:课程引入/新知讲解/例题精讲/课堂练习/知识总结。" + + (taskInfo?.VideoType == AttachmentsInfoType.复习 ? $"但本堂课是习题课,所以大部分阶段是不同的例题讲解内容。" : string.Empty) + + $"初步划分阶段:{keyFrameArr}" + + $"内容分析:对每个时间段,提取主要讲解内容:识别关键词(如“例题”“证明”“练习”“总结”)和内容结构。" + + $"判断阶段类型:如果内容以解题为主,归类为“例题精讲”;如果涉及新知识讲解,归类为“新知讲解”;以此类推。" + + $"内容总结:简述该阶段的核心讲解内容70~200字,确保内容与阶段时间内授课内容符合。" + + $"阶段主题:基于内容总结,提炼一个恰当的主题(例如,“柯西不等式的基本应用”)。" + + $"输出要求:确保阶段划分合理、无重叠,且时长符合要求" + + $"输出格式要求:内容只返回json格式({resFormat})" + + $"字幕格式(开始秒:内容|下一段字幕).以下是包含时间的视频字幕文本。" + + $"字幕列表 {captions.Captions} 字幕结束!"; await redisManager.AddTaskLog(taskInfo.Id, $"开始分析视频内容 {tryCount}"); //return await chatGPTClient.ChatAsync(taskInfo.Id.ToString(), postMessages, "分析字幕"); diff --git a/VideoAnalysisCore/AICore/GPT/Gemini/GeminiGPTClient.cs b/VideoAnalysisCore/AICore/GPT/Gemini/GeminiGPTClient.cs new file mode 100644 index 0000000..2d18ef4 --- /dev/null +++ b/VideoAnalysisCore/AICore/GPT/Gemini/GeminiGPTClient.cs @@ -0,0 +1,69 @@ +using VideoAnalysisCore.Common; +using System.Net.Http.Headers; +using System.Text; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using System.Net.Http; +using Newtonsoft.Json; +using System.Net.Http.Json; +using System.Net; +using VideoAnalysisCore.AICore.GPT.DeepSeek; +using VideoAnalysisCore.AICore.GPT; +using System.Text.Json; + + +namespace VideoAnalysisCore.AICore.GPT.ChatGPT +{ + + public class GeminiGPTClient : GPTClient + { + + public override GptConfig Config { get; set; } = AppCommon.Config.ChatGpt.ChatGpt; + + private readonly IHttpClientFactory _httpClientFactory; + private readonly RedisManager redisManager; + + public GeminiGPTClient(IHttpClientFactory httpClientFactory, RedisManager redisManager) : base(httpClientFactory, redisManager) + { + _httpClientFactory = httpClientFactory; + this.redisManager = redisManager; + } + + + /// + /// 请求AI + /// + /// 返回JSON类型 + /// 任务id + /// 提示词 + /// 任务类型 + /// GPT版本 + /// 最大token 不设置默认最大值 16000/8000 + /// + /// + public async Task ChatAsync(string task, string postMessages, string title, string model = null, int max_tokens = 8000) + { + Message[] messageArr = [ + new Message(postMessages,"user"), + ]; + model = model ?? ChatGPTType.Gemini_3_Chat; + messageArr = messageArr.Where(s => s != null).ToArray(); + var chatReq = new ChatRequest + { + taskId = task, + model = model, + max_tokens =12000, + stream = true, + temperature = 0.2f, + messages = messageArr + }; + + chatReq.modalities = null; + chatReq.max_tokens = null; + chatReq.top_p = null; + + return await base.ChatAsync(chatReq); + } + + } +} \ No newline at end of file diff --git a/VideoAnalysisCore/Common/RedisExpand.cs b/VideoAnalysisCore/Common/RedisExpand.cs index 6b442b9..3692422 100644 --- a/VideoAnalysisCore/Common/RedisExpand.cs +++ b/VideoAnalysisCore/Common/RedisExpand.cs @@ -137,26 +137,26 @@ namespace VideoAnalysisCore.Common SubscribeList.Add(RedisChannelEnum.AI课程类型, async (task) => { using var scope = AppCommon.Services?.CreateScope(); - if (scope is null || scope.ServiceProvider.GetService() is null) + if (scope is null || scope.ServiceProvider.GetService() is null) throw new Exception("IBserGPT 未注入"); else - await scope.ServiceProvider.GetService()?.GetVideoType(task); + await scope.ServiceProvider.GetService()?.GetVideoType(task); }); SubscribeList.Add(RedisChannelEnum.AI模型分析, async (task) => { using var scope = AppCommon.Services?.CreateScope(); - if (scope is null || scope.ServiceProvider.GetService() is null) + if (scope is null || scope.ServiceProvider.GetService() is null) throw new Exception("IBserGPT 未注入"); else - await scope.ServiceProvider?.GetService()?.GetKnow(task); + await scope.ServiceProvider?.GetService()?.GetKnow(task); }); SubscribeList.Add(RedisChannelEnum.AI分析试题, async (task) => { using var scope = AppCommon.Services?.CreateScope(); - if (scope is null || scope.ServiceProvider.GetService() is null) + if (scope is null || scope.ServiceProvider.GetService() is null) throw new Exception("IBserGPT 未注入"); else - await scope.ServiceProvider?.GetService()?.GetVideoQuestion(task); + await scope.ServiceProvider?.GetService()?.GetVideoQuestion(task); }); SubscribeList.Add(RedisChannelEnum.结束任务, redisManager.TaskEnd);