diff --git a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekClient.cs b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekClient.cs index 8cc8976..9cb2b7b 100644 --- a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekClient.cs +++ b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekClient.cs @@ -21,18 +21,18 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek public class DeepSeekGPTClient { - private readonly string Path = "v1/chat/completions"; - public static string Host = AppCommon.Config.ChatGpt.aliyun.Host; - public static string ApiKey = AppCommon.Config.ChatGpt.aliyun.ApiKey; + //private readonly string Path = "v1/chat/completions"; + //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; - //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; @@ -49,7 +49,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek /// Return HttpResponseMessage for SSE public async Task<(Usage u, string res,string reasoning)?> Chat(ChatRequest chatReq) { - chatReq.model = "deepseek-r1"; + //chatReq.model = "deepseek-r1"; if (chatReq.stream) return await ChatSSE(chatReq); postStar: var requestBody = System.Text.Json.JsonSerializer.Serialize(chatReq); @@ -68,23 +68,30 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek if (res is null || res.error != null) throw new Exception($" GPT模型返回异常 返回参数: " + $" {System.Text.Json.JsonSerializer.Serialize(res)}"); - var chatResContent = res?.choices.FirstOrDefault()?.message.content.Trim(); - var chatResReasoning = string.Empty; - if (chatResContent.StartsWith("")+8); - } - else - chatResReasoning = res?.choices.FirstOrDefault()?.message.reasoning_content?.Trim(); - + var d = thinkMSG(res?.choices.FirstOrDefault()?.message); + var chatResContent = d.m1; + var chatResReasoning = d.m2; + if (string.IsNullOrEmpty(chatResContent)) return null; return (res.usage, chatResContent, chatResReasoning); } + private (string m1, string m2) thinkMSG(Message? m) + { + var chatResContent = m?.content.Trim(); + var chatResReasoning = string.Empty; + if (chatResContent.StartsWith("") + 8); + } + else + chatResReasoning = m?.reasoning_content?.Trim(); + return (chatResContent, chatResReasoning); + } private HttpResponseMessage PostJsonStream(string path, string json) { var uriBuilder = new UriBuilder(Host + Path); @@ -132,9 +139,10 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek /// Return HttpResponseMessage for SSE public async Task<(Usage u, string res, string reasoning)?> ChatSSE(ChatRequest chatReq) { - chatReq.stream = true; var requestBody = System.Text.Json.JsonSerializer.Serialize(chatReq); var chatResp = PostJsonStream(string.Empty, requestBody); + if (!chatResp.IsSuccessStatusCode) + throw new Exception("GPT请求状态异常=>"+ chatResp.StatusCode); using var stream = chatResp.Content.ReadAsStream(); using var reader = new StreamReader(stream, Encoding.UTF8); string line; @@ -142,16 +150,25 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek var messageBuilder1 = new StringBuilder(); var lastChat = new ChatResSSE(); var splitCount = "data:".Length; - while (true) + var maxLoop = 60*100; + while (maxLoop>0) { line = reader.ReadLine(); - if (line is null || string.IsNullOrEmpty(line)) continue; + if (line is null || string.IsNullOrEmpty(line)|| line.StartsWith(": keep-alive")) { + Thread.Sleep(100); + maxLoop--; + continue; + } else if (line.EndsWith("[DONE]")) { // 表示一条消息结束 string message = messageBuilder.ToString(); string message2 = messageBuilder1.ToString(); messageBuilder.Clear(); + messageBuilder1.Clear(); + var d =thinkMSG(new Message() { content = message, reasoning_content = message2 }); + message = d.m1; + message2 = d.m2; var u = lastChat?.usage; if (u == null || string.IsNullOrEmpty(message)) return null; @@ -180,6 +197,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek } } } + return null; } } diff --git a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekModel.cs b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekModel.cs index 8f47c71..20aaaef 100644 --- a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekModel.cs +++ b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekModel.cs @@ -42,7 +42,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek /// /// 一个对象,用于指定模型必须输出的格式。设置为 enable 结构化输出,确保模型与您提供的 JSON 匹配 图式。 /// - public object response_format { get; set; } = new { type = "json_object" }; + //public object response_format { get; set; } = new { type = "json_object" }; /// /// 流式返回 /// @@ -58,6 +58,10 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek } public class Message { + public Message() + { + + } public Message(string content, string role) { this.role = role; diff --git a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs index 048f982..9071b39 100644 --- a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs +++ b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs @@ -72,14 +72,18 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek throw new Exception("未能找到对应知识点=>"+ fileNameInfoRes.授课章节); var knowledgeInfos = await knowledgeInfoDB.AsQueryable().ToChildListAsync(s => s.Parent_Id, know.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); var postMessages = $"你的任务是分析视频字幕内容并提取出中国高考考试试题方法点,然后根据步骤分析出知识片段" + $"按以下步骤完成:" + $"1.识别方法点:提取字幕内容中与{subject}考试属于{fileNameInfoRes.授课章节}章节相关的方法点。" + - $"2.关联合并知识内容相似的知识点来合并为知识片段。如果知识片段时长超过20分钟则考虑拆封为2个更加细微贴切的知识片段" + + $"2.关联合并知识内容相似的知识点来合并为知识片段。如果知识片段时长超过12分钟则考虑拆封为2个更加细微贴切的知识片段" + $"3.分配空余未使用的时间段到内容相近的知识片段时间区间来获取更加详细的上下文,但是请避免知识片段之间时间重合。" + $"4.分析总结:基于提取出的知识片段内容称来匹配我提供的知识点,来作为片段的知识点(请确保匹配的知识点是我提供的,否者片段知识点值为空字符串)" + - $"提供的方法点名称({knows})。 格式 (方法点Id|方法点名称) 如果一个知识片段出现多个知识点那么知识点ID与知识点名称都用逗号','分割" + + $"提供的方法点名称({knows})。 格式 (方法点Id|方法点名称) 如果一个知识片段出现多个知识点那么知识点Id与知识点名称都用逗号','分割" + $"输入:包含时间戳的视频字幕文本。" + $"以下是包含时间的视频字幕文本。" + $"字幕格式(说话人:开始秒:结束秒:内容|下一段字幕).字幕列表 {captions.Captions}" + @@ -102,17 +106,23 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek } await videoKonwPointDB.DeleteAsync(s => s.VideoTaskId == taskId); - var data = questionRes.Select(s => new VideoKonwPoint() - { - Content = s.Content, - Theme = s.Theme, - StartTime = s.StartTime, - EndTime =s.EndTime, - KnowPoint = s.KnowPoint, - KnowPointId = s.KnowPointId, - TagId = taskInfo.TagId, - VideoTaskId = taskInfo.Id, - }).ToList(); + var data = questionRes.SelectMany( + s => + { + var ks = s.KnowPoint.Split(","); + 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.InsertRangeAsync(data);