优化 ai分析流程

This commit is contained in:
小肥羊 2025-05-09 18:14:33 +08:00
parent c616581918
commit 7cbd4588f5
5 changed files with 158 additions and 126 deletions

View File

@ -24,14 +24,14 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
//public static string Host = AppCommon.Config.ChatGpt.aliyun.Host; //public static string Host = AppCommon.Config.ChatGpt.aliyun.Host;
//public static string ApiKey = AppCommon.Config.ChatGpt.aliyun.ApiKey; //public static string ApiKey = AppCommon.Config.ChatGpt.aliyun.ApiKey;
//private readonly string Path = ""; private readonly string Path = "";
//public static string Host = AppCommon.Config.ChatGpt.DeepSeek.Host; public static string Host = AppCommon.Config.ChatGpt.DeepSeek.Host;
//public static string ApiKey = AppCommon.Config.ChatGpt.DeepSeek.ApiKey; public static string ApiKey = AppCommon.Config.ChatGpt.DeepSeek.ApiKey;
public static string Host = AppCommon.Config.ChatGpt.ChatGpt.Host; //public static string Host = AppCommon.Config.ChatGpt.ChatGpt.Host;
public static string ApiKey = AppCommon.Config.ChatGpt.ChatGpt.ApiKey; //public static string ApiKey = AppCommon.Config.ChatGpt.ChatGpt.ApiKey;
private readonly string Path = "v1/chat/completions"; //private readonly string Path = "v1/chat/completions";
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;

View File

@ -13,6 +13,8 @@ using VideoAnalysisCore.Model.蓝鲸智库;
using VideoAnalysisCore.Model.Enum; using VideoAnalysisCore.Model.Enum;
using Mapster; using Mapster;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using static System.Collections.Specialized.BitVector32;
namespace VideoAnalysisCore.AICore.GPT.DeepSeek namespace VideoAnalysisCore.AICore.GPT.DeepSeek
{ {
@ -432,38 +434,82 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task); RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task);
return gptRes; return gptRes;
} }
/// <summary> /// <summary>
/// 获取知识点 /// 获取内容对应的章节
/// </summary> /// </summary>
/// <param name="task">任务id</param>
/// <returns></returns> /// <returns></returns>
public async Task<TaskRes> GetKnow(string task) public async Task<List<VideoKonwPoint>> GetVideoKnow(List<VideoKnowRes> 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<VideoKnowQueryDto[]>());// 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<VideoKnowRes[]>(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();
}
/// <summary>
/// 获取内容对应的章节
/// </summary>
/// <returns></returns>
public async Task<string?> 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() var xkwKnows = await knowledgeInfoDB.AsQueryable()
.Where(s => s.Course_Id == Course_Id .Where(s => s.Course_Id == course_Id
&& (s.Depth == 3 && (s.Depth == 3
|| s.Depth == 2)) || s.Depth == 2))
.Select(s => s.Name).ToArrayAsync(); .Select(s => s.Name).ToArrayAsync();
string title = taskInfo.MediaName; string title = taskInfo.MediaName;
var speakerArr = JsonSerializer.Deserialize<OfflineSpeakerRes[]>(taskInfo.Speaker);
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions); var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions);
var fileNameResFormat = "{授课章节: string|null}"; var fileNameResFormat = "{授课章节: string|null}";
@ -478,29 +524,49 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
$"以下是包含时间的视频字幕文本。" + $"以下是包含时间的视频字幕文本。" +
$"字幕列表 {rCaptionArr}。" + $"字幕列表 {rCaptionArr}。" +
$"输出格式 json字符串 对象格式{fileNameResFormat}"; $"输出格式 json字符串 对象格式{fileNameResFormat}";
var task = taskInfo.Id.ToString();
var fileNameInfoRes = await ChatAsync<FileNameInfo> var fileNameInfoRes = await ChatAsync<FileNameInfo>
(task, fileNamePostMessages, null); (task, fileNamePostMessages, null);
#if DEBUG await videoTaskDB.AsUpdateable()
//fileNameInfoRes = new FileNameInfo() { 授课章节 = "一元二次不等式" }; .SetColumns(it => it.Sections == fileNameInfoRes.)
#endif .Where(it => it.Id == taskInfo.Id)
var captions = ExpandFunction.GetSpeakerCaptions(captionsArr, speakerArr); .ExecuteCommandAsync();
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 RedisExpand.Redis await RedisExpand.Redis
.HMSetAsync(RedisExpandKey.Task(task), "学科章节", fileNameInfoRes.); .HMSetAsync(RedisExpandKey.Task(task), "学科章节", fileNameInfoRes.);
//提升到父级
var kInfo = await knowledgeInfoDB.GetByIdAsync(know.Parent_Id); return fileNameInfoRes?.;
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)); /// <summary>
var knowDic = knowledgeInfos /// 获取知识点
.OrderBy(s => s.Id) /// </summary>
.GroupBy(s => s.Name) /// <param name="task">任务id</param>
.ToDictionary(s => s.First().Name, s => s.First().Id); /// <returns></returns>
public async Task<TaskRes> 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<SenseVoiceRes[]>(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<VideoKnowRes>(); var questionRes = new List<VideoKnowRes>();
while (true) while (true)
{ {
@ -523,7 +589,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
var resFormat = """[{"StartTime":开始秒(number),"EndTime":结束秒(number),"Stage":阶段(string),"Theme":主题(string),"Content":内容总结(string)}]"""; var resFormat = """[{"StartTime":开始秒(number),"EndTime":结束秒(number),"Stage":阶段(string),"Theme":主题(string),"Content":内容总结(string)}]""";
var postMessages = var postMessages =
$"请通过视频字幕内容分析出视频中{subject}课堂的授课阶段。" + $"请通过视频字幕内容分析出视频中{subject}课堂的授课阶段。" +
$"课堂内容与{fileNameInfoRes.授课章节}章节相关。" + $"课堂内容与{sections}章节相关。" +
$"{keyFrameArr}" + $"{keyFrameArr}" +
$"完整的课堂标准流程包含以下5个阶段课程引入/新知讲解/例题精讲/课堂练习/知识总结。" + $"完整的课堂标准流程包含以下5个阶段课程引入/新知讲解/例题精讲/课堂练习/知识总结。" +
$"通过授课阶段的主要讲解内容分析出对应的授课阶段内容总结。" + $"通过授课阶段的主要讲解内容分析出对应的授课阶段内容总结。" +
@ -548,36 +614,10 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
} }
} }
if (questionRes.Count == 0) continue; if (questionRes.Count == 0) continue;
questionRes = questionRes.OrderBy(s => s.StartTime).ToList(); //处理分段 知识点
var thems = JsonSerializer.Serialize(questionRes.Adapt<VideoKnowQueryDto[]>());// string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme)); var insertData = await GetVideoKnow(questionRes, taskInfo, sections, Course_Id);
var checkResFormat1 = """[{"StartTime":开始秒(number),"KnowPoint":知识点名称(string),"KnowPointId":知识点Id(string)}]"""; //校验结果质量
var knowMessages = var thems = JsonSerializer.Serialize(questionRes.Adapt<VideoKnowQueryDto[]>());
$"我针对{subject}课堂授课视频分析出了视频的授课阶段片段。" +
$"现在需要你通过每个片段的内容总结来分配正确的知识点(单个片段允许多个知识点用逗号','分割)。" +
$"这是我的分段 {thems}。" +
$"课堂内容与{fileNameInfoRes.授课章节}章节相关" +
$"最后请确保分配的知识点是用户提供的,并且一定正确合理!" +
$"输出内容只返回json格式({checkResFormat1})" +
$" 格式 (方法点Id|方法点名称) " +
$"提供的知识点名称({knows})。";
Console.WriteLine(DateTime.Now + "=>2.开始分析视频内容知识点");
var konwRes = await ChatAsync<VideoKnowRes[]>(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<VideoKnowQueryDto[]>());
var checkResFormat = """{"Score":打分(number),"Evaluation":评价(string)""";//,"Data":优化后的分段(array)}"""; var checkResFormat = """{"Score":打分(number),"Evaluation":评价(string)""";//,"Data":优化后的分段(array)}""";
var checkMessage = "我为视频的讲解内容做了一些分段,希望你能通读字幕内容后检查下的分段是否符合我的要求?" + var checkMessage = "我为视频的讲解内容做了一些分段,希望你能通读字幕内容后检查下的分段是否符合我的要求?" +
$"检查这些分段的时间是否合理 与相邻的时间段间隔是否处于合理区间30~900秒之间?" + $"检查这些分段的时间是否合理 与相邻的时间段间隔是否处于合理区间30~900秒之间?" +
@ -587,11 +627,14 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
$"后续的内容是包含时间戳的视频字幕的固定格式文本。" + $"后续的内容是包含时间戳的视频字幕的固定格式文本。" +
$"字幕格式(说话人:开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" + $"字幕格式(说话人:开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" +
$"最后输出格式为json({checkResFormat})"; $"最后输出格式为json({checkResFormat})";
Console.WriteLine(DateTime.Now + "=>3.开始检查视频分段结果"); Console.WriteLine(DateTime.Now + "=>3.开始检查视频分段结果");
var checkRes = await ChatAsync<CheckMessageDto>(task, checkMessage, null); var checkRes = await ChatAsync<CheckMessageDto>(task, checkMessage, null);
if (checkRes != null && checkRes.Score >= 80) if (checkRes != null && checkRes.Score >= 80)
{ {
//写入知识点
await videoKonwPointDB.DeleteAsync(s => s.VideoTaskId == taskInfo.Id);
await videoKonwPointDB.InsertRangeAsync(insertData);
break; break;
} }
else 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); var gptRes = new TaskRes(captions);
await RedisExpand.Redis await RedisExpand.Redis
.HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes); .HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes);
RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task); RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task);
return gptRes; return gptRes;
} }

View File

@ -178,7 +178,7 @@ namespace VideoAnalysisCore.Common
/// <summary> /// <summary>
/// 获取Task处理后的 说话人字幕 /// 获取Task处理后的 说话人字幕
/// </summary> /// </summary>
public static TotalCaptionsDto GetSpeakerCaptions(SenseVoiceRes[] captionsArr, OfflineSpeakerRes[] speakerArr) public static TotalCaptionsDto GetSpeakerCaptions(SenseVoiceRes[] captionsArr, OfflineSpeakerRes[]? speakerArr=null)
{ {
if (captionsArr is null || captionsArr.Length == 0) if (captionsArr is null || captionsArr.Length == 0)
//|| speakerArr is null || speakerArr.Length == 0) //|| speakerArr is null || speakerArr.Length == 0)

View File

@ -62,31 +62,36 @@ namespace VideoAnalysisCore.Job
return; return;
//var responseMessage = await new HttpClient() //var responseMessage = await new HttpClient()
// .PostAsJsonAsync(AppCommon.Config.Subsystem.蓝鲸智库.APIUrl + "/api/callback/platform/videosAnalyze", postData); // .PostAsJsonAsync(AppCommon.Config.Subsystem.蓝鲸智库.APIUrl + "/api/callback/platform/videosAnalyze", postData);
foreach (var item in taskArr) { foreach (var item in taskArr)
{
HttpResponseMessage responseMessage = null; HttpResponseMessage responseMessage = null;
if (!string.IsNullOrWhiteSpace(item.CallBackUrl)) try
{
responseMessage = await new HttpClient()
.PostAsJsonAsync(item.CallBackUrl, postData);
}
else
{ {
var postUrl = !string.IsNullOrWhiteSpace(item.CallBackUrl)
? item.CallBackUrl
: AppCommon.Config.Subsystem..APIUrl + "/api/callback/platform/videosAnalyze";
responseMessage = await new HttpClient() responseMessage = await new HttpClient()
.PostAsJsonAsync(AppCommon.Config.Subsystem..APIUrl + "/api/callback/platform/videosAnalyze", postData); .PostAsJsonAsync(postUrl, postData);
} if (responseMessage.IsSuccessStatusCode)
if (responseMessage.IsSuccessStatusCode) {
{ var res = await responseMessage.Content.ReadAsStringAsync();
var res = await responseMessage.Content.ReadAsStringAsync(); Console.WriteLine($"{DateTime.Now} 执行=>文件包任务 回调结果 {res}");
Console.WriteLine($"{DateTime.Now} 执行=>文件包任务 回调结果 {res}");
await nodePackageInfoDB.AsUpdateable(postData) await nodePackageInfoDB.AsUpdateable(postData)
.UpdateColumns(it => new { it.SuccessTime }) .UpdateColumns(it => new { it.SuccessTime })
.ExecuteCommandAsync(); .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($"{DateTime.Now} 执行=>文件包任务 回调失败!!! {responseMessage.StatusCode} {res}");
Console.WriteLine(ex);
} }
await Task.Delay(1000); await Task.Delay(1000);
} }

View File

@ -71,7 +71,7 @@ namespace VideoAnalysisCore.Model
/// <summary> /// <summary>
/// 授课视频对应PPT视频ID /// 授课视频对应PPT视频ID
/// </summary> /// </summary>
[SugarColumn(Length = 50, ColumnDataType = "varchar", IsNullable = true)] [SugarColumn(Length = 255, ColumnDataType = "varchar", IsNullable = true)]
public string? PPTVideoCode { get; set; } public string? PPTVideoCode { get; set; }
/// <summary> /// <summary>
/// 授课视频对应PPT视频关键帧 /// 授课视频对应PPT视频关键帧
@ -117,5 +117,11 @@ namespace VideoAnalysisCore.Model
[SugarColumn( IsNullable = true)] [SugarColumn( IsNullable = true)]
public DateTime? EndTime { get; set; } public DateTime? EndTime { get; set; }
/// <summary>
/// 授课章节
/// </summary>
[SugarColumn(IsNullable = true)]
public string? Sections { get; set; }
} }
} }