Compare commits
No commits in common. "53951aa870caa0c0fcfbcaf3d0a9e0d3ff878a6a" and "f8a758ff40afe0aca7f5c0b1bd65eab42e0aaac1" have entirely different histories.
53951aa870
...
f8a758ff40
|
|
@ -18,9 +18,7 @@ namespace Learn.VideoAnalysis.Expand
|
||||||
|
|
||||||
Console.WriteLine($"{DateTime.Now}=>初始化 Coravel");
|
Console.WriteLine($"{DateTime.Now}=>初始化 Coravel");
|
||||||
service.AddScheduler();
|
service.AddScheduler();
|
||||||
#if !DEBUG
|
|
||||||
service.AddTransient<TaskFileClearJob>();
|
service.AddTransient<TaskFileClearJob>();
|
||||||
#endif
|
|
||||||
service.AddTransient<NodePackageJob>();
|
service.AddTransient<NodePackageJob>();
|
||||||
}
|
}
|
||||||
public static void UseCoravelExpand(this IApplicationBuilder provider)
|
public static void UseCoravelExpand(this IApplicationBuilder provider)
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,6 @@ namespace Learn.VideoAnalysis
|
||||||
AppCommon.Services = app.Services;
|
AppCommon.Services = app.Services;
|
||||||
app.UseMiddleware<BasicAuthMiddleware>("Swagger");
|
app.UseMiddleware<BasicAuthMiddleware>("Swagger");
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
//¿ªÆôredis¶ÓÁзþÎñ
|
|
||||||
_ = app.Services.GetRequiredService<RedisInit>();
|
_ = app.Services.GetRequiredService<RedisInit>();
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,7 @@ export default {
|
||||||
component: () => import("@/views/welcome/runningTask.vue"),
|
component: () => import("@/views/welcome/runningTask.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "进行中任务",
|
title: "进行中任务",
|
||||||
showLink: true,
|
showLink: true
|
||||||
keepAlive:true,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -37,8 +36,7 @@ export default {
|
||||||
component: () => import("@/views/welcome/showTask.vue"),
|
component: () => import("@/views/welcome/showTask.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "预览任务",
|
title: "预览任务",
|
||||||
showLink: false,
|
showLink: false
|
||||||
keepAlive:true,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -47,8 +45,7 @@ export default {
|
||||||
component: () => import("@/views/welcome/errorTask.vue"),
|
component: () => import("@/views/welcome/errorTask.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "错误任务",
|
title: "错误任务",
|
||||||
showLink: true,
|
showLink: true
|
||||||
keepAlive:true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,6 @@ function previewTask(row: any) {
|
||||||
meta: {
|
meta: {
|
||||||
title: `任务预览` + row.id.toString().slice(-4),
|
title: `任务预览` + row.id.toString().slice(-4),
|
||||||
dynamicLevel: 3,
|
dynamicLevel: 3,
|
||||||
keepAlive: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// 路由跳转
|
// 路由跳转
|
||||||
|
|
|
||||||
|
|
@ -155,13 +155,9 @@ function timeupdateVideo() {
|
||||||
let subtitleI = subtitles.value.findIndex(
|
let subtitleI = subtitles.value.findIndex(
|
||||||
(subtitle) => currentTime >= subtitle.start && currentTime <= subtitle.end
|
(subtitle) => currentTime >= subtitle.start && currentTime <= subtitle.end
|
||||||
);
|
);
|
||||||
// 更新字幕 AI优化字幕
|
|
||||||
let subtitleI1 = subtitles1.value.findLastIndex(
|
|
||||||
(subtitle) => currentTime >= subtitle.start
|
|
||||||
);
|
|
||||||
if (subtitleI > -1 && currentSubtitle.value !== subtitles.value[subtitleI].text) {
|
if (subtitleI > -1 && currentSubtitle.value !== subtitles.value[subtitleI].text) {
|
||||||
currentSubtitle.value = subtitles.value[subtitleI].text;
|
currentSubtitle.value = subtitles.value[subtitleI].text;
|
||||||
currentSubtitle1.value = subtitles1.value[subtitleI1]?.text || "";
|
currentSubtitle1.value = subtitles1.value[subtitleI]?.text || "";
|
||||||
} else if (subtitleI == -1) {
|
} else if (subtitleI == -1) {
|
||||||
currentSubtitle.value = "";
|
currentSubtitle.value = "";
|
||||||
currentSubtitle1.value = "";
|
currentSubtitle1.value = "";
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,12 @@ namespace VideoAnalysisCore.AICore.GPT
|
||||||
public float? max_tokens { get; set; } = 8000;
|
public float? max_tokens { get; set; } = 8000;
|
||||||
public float? max_completion_tokens { get; set; } = 8000;
|
public float? max_completion_tokens { get; set; } = 8000;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// 要使用的采样温度,介于 0 和 2 之间。较高的值(如 0.8)将使输出更加随机,而较低的值(如 0.2)将使其更加集中和确定。 我们通常建议更改此项或同时更改两者。top_p
|
||||||
|
/// <para> 默认为 1</para>
|
||||||
|
/// <para> <see cref="ChatRequest.top_p"/>联动</para>
|
||||||
|
/// </summary>
|
||||||
|
public float? temperature { get; set; } = 0.2f;
|
||||||
|
/// <summary>
|
||||||
/// 一种替代温度采样的方法,称为原子核采样, 其中,模型考虑具有top_p概率的标记的结果 质量。所以 0.1 表示仅包含前 10% 概率质量的代币 被考
|
/// 一种替代温度采样的方法,称为原子核采样, 其中,模型考虑具有top_p概率的标记的结果 质量。所以 0.1 表示仅包含前 10% 概率质量的代币 被考
|
||||||
/// <para>建议与<see cref="ChatRequest.temperature"/>联动</para>
|
/// <para>建议与<see cref="ChatRequest.temperature"/>联动</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
|
||||||
title = title,
|
title = title,
|
||||||
max_tokens = max_tokens,
|
max_tokens = max_tokens,
|
||||||
stream = true,
|
stream = true,
|
||||||
|
temperature = 0.2f,
|
||||||
messages = messageArr
|
messages = messageArr
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
|
||||||
model = model ?? ChatGPTType.Deepseek_Chat,
|
model = model ?? ChatGPTType.Deepseek_Chat,
|
||||||
max_tokens = model == ChatGPTType.Deepseek_Reasoner ? 32000 : max_tokens,
|
max_tokens = model == ChatGPTType.Deepseek_Reasoner ? 32000 : max_tokens,
|
||||||
stream = true,
|
stream = true,
|
||||||
|
temperature = 0.2f,
|
||||||
messages = messageArr
|
messages = messageArr
|
||||||
};
|
};
|
||||||
return await base.ChatAsync<T>(chatReq);
|
return await base.ChatAsync<T>(chatReq);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -34,10 +34,9 @@ namespace VideoAnalysisCore.AICore.GPT.Dto
|
||||||
}
|
}
|
||||||
public class VideoKnowPointDto
|
public class VideoKnowPointDto
|
||||||
{
|
{
|
||||||
|
public float KnowPointWeight { get; set; }
|
||||||
public string KnowPoint { get; set; }
|
public string KnowPoint { get; set; }
|
||||||
public string KnowPointId { get; set; }
|
public string KnowPointId { get; set; }
|
||||||
public float KnowSourceTime { get; set; }
|
|
||||||
public float KnowPointWeight { get; set; }
|
|
||||||
public string KnowSource { get; set; }
|
public string KnowSource { get; set; }
|
||||||
}
|
}
|
||||||
public class VideoKnowRes
|
public class VideoKnowRes
|
||||||
|
|
@ -66,6 +65,10 @@ namespace VideoAnalysisCore.AICore.GPT.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual string? KnowPoint { get; set; }
|
public virtual string? KnowPoint { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// 知识点权重
|
||||||
|
/// </summary>
|
||||||
|
public virtual float? KnowPointWeight { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// 知识点ID
|
/// 知识点ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual string? KnowPointId { get; set; }
|
public virtual string? KnowPointId { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 视频分析工作流1
|
/// 视频分析工作流1
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GTP_Analysis_1 : IBserGPTWorkflow
|
public class GTP_Analysis_1 : IBserGPTWorkflow
|
||||||
{
|
{
|
||||||
private readonly GeminiGPTClient geminiClient;
|
private readonly GeminiGPTClient geminiClient;
|
||||||
private readonly DeepSeekGPTClient deepSeekClient;
|
private readonly DeepSeekGPTClient deepSeekClient;
|
||||||
|
|
@ -187,7 +187,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
$"字幕列表 {rCaptionArr}。" +
|
$"字幕列表 {rCaptionArr}。" +
|
||||||
$"输出格式 json字符串 对象格式{fileNameResFormat}";
|
$"输出格式 json字符串 对象格式{fileNameResFormat}";
|
||||||
var task = taskInfo.Id.ToString();
|
var task = taskInfo.Id.ToString();
|
||||||
var fileNameInfoRes = await geminiClient.ChatAsync<FileNameInfo>
|
var fileNameInfoRes = await chatGPTClient.ChatAsync<FileNameInfo>
|
||||||
(task, fileNamePostMessages, "授课章节");
|
(task, fileNamePostMessages, "授课章节");
|
||||||
taskInfo.Sections = fileNameInfoRes.授课章节;
|
taskInfo.Sections = fileNameInfoRes.授课章节;
|
||||||
await videoTaskDB.AsUpdateable()
|
await videoTaskDB.AsUpdateable()
|
||||||
|
|
@ -309,7 +309,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
await Parallel.ForAsync(0, totalCount,
|
await Parallel.ForAsync(0, totalCount,
|
||||||
new ParallelOptions()
|
new ParallelOptions()
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
{ MaxDegreeOfParallelism = 4 },
|
{ MaxDegreeOfParallelism = 1 },
|
||||||
#else
|
#else
|
||||||
{ MaxDegreeOfParallelism = 9 },
|
{ MaxDegreeOfParallelism = 9 },
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -326,23 +326,25 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
var postMessages =
|
var postMessages =
|
||||||
$$"""
|
$$"""
|
||||||
# Role
|
# Role
|
||||||
你是一位{{subject}}学科的教育专家,有着资深字幕校对经验。你的任务是将{{sections}}内容的原始语音识别(STT)JSON 数据清洗为高质量教学文本。
|
你是一位{{subject}}学科的教育专家与资深校对。你的任务是将{{sections}}内容的原始语音识别(STT)JSON 数据清洗为高质量教学文本。
|
||||||
# Input & Output Protocol
|
# Input & Output Protocol
|
||||||
输入和输出均为严格的 JSON 数组格式:`[{"t": number, "r": string}]`。
|
输入和输出均为严格的 JSON 数组格式:`[{"t": number, "r": string}]`。
|
||||||
`t` (Time): 绝对锚点,代表时间戳。严禁修改、严禁排序、严禁删除。
|
* `t` (Time): 绝对锚点,代表时间戳。严禁修改、严禁排序、严禁删除。
|
||||||
`r` (Result): 字幕内容,仅对此字段进行清洗。
|
* `r` (Result): 字幕内容,仅对此字段进行清洗。
|
||||||
Processing Rules (按优先级执行)
|
# Processing Rules (按优先级执行)
|
||||||
1. 结构铁律 (最高优先级):
|
1. 结构铁律 (最高优先级):
|
||||||
长度一致:输入 N 条,输出必须 N 条。
|
长度一致:输入 N 条,输出必须 N 条。
|
||||||
时间锁定:输出的第 i 条 `t` 必须等于输入的第 i 条 `t`。
|
时间锁定:输出的第 i 条 `t` 必须等于输入的第 i 条 `t`。
|
||||||
原地修正:禁止跨行移动文本。即使一句话被切分在两行,也不要合并它们,只需分别修正各自行的错别字即可。
|
原地修正:禁止跨行移动文本。即使一句话被切分在两行,也不要合并它们,只需分别修正各自行的错别字即可。
|
||||||
2. 内容清洗标准:
|
|
||||||
口语过滤:删除“那个、然后、嗯、啊、就是”等无意义填充词。若该行被清空,`r` 设为 `""`,保留对象。
|
2. 上下文关联修正 (核心任务):
|
||||||
LaTeX 格式化:数学/科学公式必须转换为 LaTeX (如 `$\\frac{a}{b}$`),JSON 中反斜杠需双重转义 (`\\\\`)。
|
|
||||||
3. 上下文关联修正 (核心任务):
|
|
||||||
全局理解,局部修正:请阅读前后多行内容来确定当前行特定词汇的含义,解决同音错别字。
|
全局理解,局部修正:请阅读前后多行内容来确定当前行特定词汇的含义,解决同音错别字。
|
||||||
案例:如果上一行是“求函数的”,当前行是“集值”,结合数学上下文应修正为“极值”。
|
案例:如果上一行是“求函数的”,当前行是“集值”,结合数学上下文应修正为“极值”。
|
||||||
逻辑:修正依据是“上下文语义”,但修正范围仅限“当前行文本”。
|
逻辑:修正依据是“上下文语义”,但修正范围仅限“当前行文本”。
|
||||||
|
|
||||||
|
3. 内容清洗标准:
|
||||||
|
口语过滤:删除“那个、然后、嗯、啊、就是”等无意义填充词。若该行被清空,`r` 设为 `""`,保留对象。
|
||||||
|
LaTeX 格式化:数学/科学公式必须转换为 LaTeX (如 `$\\frac{a}{b}$`),JSON 中反斜杠需双重转义 (`\\\\`)。
|
||||||
# Task Data
|
# Task Data
|
||||||
待处理数据 (共 {{cStrArr.Count()}} 条):
|
待处理数据 (共 {{cStrArr.Count()}} 条):
|
||||||
{{nowCaptionStr}}
|
{{nowCaptionStr}}
|
||||||
|
|
@ -434,13 +436,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
/// <summary>
|
|
||||||
/// 作业内容检查
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="taskInfo"></param>
|
|
||||||
/// <param name="captions"></param>
|
|
||||||
/// <param name="sections"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task<VideoKnowRes?> DetectHomeworkAssignment(VideoTask taskInfo, TotalCaptionsDto captions, string sections)
|
private async Task<VideoKnowRes?> DetectHomeworkAssignment(VideoTask taskInfo, TotalCaptionsDto captions, string sections)
|
||||||
{
|
{
|
||||||
if (captions is null || string.IsNullOrWhiteSpace(captions.Captions))
|
if (captions is null || string.IsNullOrWhiteSpace(captions.Captions))
|
||||||
|
|
@ -725,8 +721,6 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
throw new Exception("未能找到对应知识点=>" + sections);
|
throw new Exception("未能找到对应知识点=>" + sections);
|
||||||
List<KnowledgeInfo>? knowledgeInfos = new List<KnowledgeInfo>();
|
List<KnowledgeInfo>? knowledgeInfos = new List<KnowledgeInfo>();
|
||||||
var kInfo = await knowledgeInfoDB.GetByIdAsync(know.Parent_Id);
|
var kInfo = await knowledgeInfoDB.GetByIdAsync(know.Parent_Id);
|
||||||
if (know.Parent_Id == 0)
|
|
||||||
kInfo = know;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
knowledgeInfos = await knowledgeInfoDB.AsQueryable()
|
knowledgeInfos = await knowledgeInfoDB.AsQueryable()
|
||||||
|
|
@ -802,10 +796,6 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
Stage = s.First().Stage,
|
Stage = s.First().Stage,
|
||||||
Theme = s.First().Theme,
|
Theme = s.First().Theme,
|
||||||
VideoTaskId = taskInfo.Id,
|
VideoTaskId = taskInfo.Id,
|
||||||
CourseLevel = taskInfo.CourseLevel,
|
|
||||||
TextBookVersionId = taskInfo.TextBookVersionId,
|
|
||||||
GradeSemester= taskInfo.GradeSemester,
|
|
||||||
GradeId = taskInfo.GradeId,
|
|
||||||
}).ToList();
|
}).ToList();
|
||||||
//尝试追加 作业布置分段
|
//尝试追加 作业布置分段
|
||||||
if (homework != null && (!questionRes.Any(s => s.Stage == StageEnum.作业布置.ToString())))
|
if (homework != null && (!questionRes.Any(s => s.Stage == StageEnum.作业布置.ToString())))
|
||||||
|
|
@ -856,8 +846,6 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
if (know is null)
|
if (know is null)
|
||||||
throw new Exception("未能找到对应知识点=>" + sections);
|
throw new Exception("未能找到对应知识点=>" + sections);
|
||||||
var kInfo = await knowledgeInfoDB.GetByIdAsync(know.Parent_Id);
|
var kInfo = await knowledgeInfoDB.GetByIdAsync(know.Parent_Id);
|
||||||
if (know.Parent_Id == 0)
|
|
||||||
kInfo = know;
|
|
||||||
var knowledgeInfos = await knowledgeInfoDB.AsQueryable()
|
var knowledgeInfos = await knowledgeInfoDB.AsQueryable()
|
||||||
.ToChildListAsync(s => s.Parent_Id, kInfo.Parent_Id == 0 ? kInfo.Id : kInfo.Parent_Id);
|
.ToChildListAsync(s => s.Parent_Id, kInfo.Parent_Id == 0 ? kInfo.Id : kInfo.Parent_Id);
|
||||||
//开始分析复习课 试题
|
//开始分析复习课 试题
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
|
||||||
model = model,
|
model = model,
|
||||||
max_tokens = max_tokens,
|
max_tokens = max_tokens,
|
||||||
stream = true,
|
stream = true,
|
||||||
|
temperature = 0.2f,
|
||||||
messages = messageArr,
|
messages = messageArr,
|
||||||
max_completion_tokens= 12288,
|
max_completion_tokens= 12288,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using UserCenter.Model.Enum;
|
|
||||||
using VideoAnalysisCore.Common;
|
using VideoAnalysisCore.Common;
|
||||||
using VideoAnalysisCore.Model;
|
using VideoAnalysisCore.Model;
|
||||||
using VideoAnalysisCore.Model.Enum;
|
using VideoAnalysisCore.Model.Enum;
|
||||||
|
|
@ -50,7 +49,7 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="numThreads">默认6线程</param>
|
/// <param name="numThreads">默认6线程</param>
|
||||||
/// <param name="useGPU">是否使用gpu 报错请看安装CUDA环境 <see cref="https://k2-fsa.github.io/sherpa/onnx/pretrained_models/whisper/large-v3.html#run-with-gpu-float32"/></param>
|
/// <param name="useGPU">是否使用gpu 报错请看安装CUDA环境 <see cref="https://k2-fsa.github.io/sherpa/onnx/pretrained_models/whisper/large-v3.html#run-with-gpu-float32"/></param>
|
||||||
public void Init(SubjectEnum? subject = null, int numThreads = 10, bool useGPU = false, bool useHotwords = false)
|
public void Init(int numThreads = 6, bool useGPU = false, bool useHotwords = false)
|
||||||
{
|
{
|
||||||
Console.WriteLine("初始化 FunASRNano");
|
Console.WriteLine("初始化 FunASRNano");
|
||||||
OfflineRecognizerConfig config = new OfflineRecognizerConfig();
|
OfflineRecognizerConfig config = new OfflineRecognizerConfig();
|
||||||
|
|
@ -64,23 +63,17 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
//将非结构化数据(文本、图像、音频等)转换为低维稠密向量
|
//将非结构化数据(文本、图像、音频等)转换为低维稠密向量
|
||||||
config.ModelConfig.FunAsrNano.EncoderAdaptor = Path.Combine(topFolder, "encoder_adaptor.int8.onnx");
|
config.ModelConfig.FunAsrNano.EncoderAdaptor = Path.Combine(topFolder, "encoder_adaptor.int8.onnx");
|
||||||
//接入的大语言模型
|
//接入的大语言模型
|
||||||
//config.ModelConfig.FunAsrNano.LLM = Path.Combine(topFolder ,"llm.fp16.onnx");
|
config.ModelConfig.FunAsrNano.LLM = Path.Combine(topFolder, "llm.fp16.onnx");
|
||||||
config.ModelConfig.FunAsrNano.LLM = Path.Combine(topFolder, "int8-2025-12-30", "llm.int8.onnx");
|
|
||||||
//插入预训练模型(如Transformer)的小型可训练模块 (如语音识别、情感分析)
|
//插入预训练模型(如Transformer)的小型可训练模块 (如语音识别、情感分析)
|
||||||
config.ModelConfig.FunAsrNano.Embedding = Path.Combine(topFolder, "embedding.int8.onnx");
|
config.ModelConfig.FunAsrNano.Embedding = Path.Combine(topFolder, "embedding.int8.onnx");
|
||||||
//分词器
|
//分词器
|
||||||
config.ModelConfig.FunAsrNano.Tokenizer = Path.Combine(topFolder, "Qwen3-0.6B");
|
config.ModelConfig.FunAsrNano.Tokenizer = Path.Combine(topFolder, "Qwen3-0.6B");
|
||||||
//提示词
|
//提示词
|
||||||
config.ModelConfig.FunAsrNano.SystemPrompt = "You are a professional video audio transcription assistant.";
|
config.ModelConfig.FunAsrNano.SystemPrompt = "You are a professional video audio transcription assistant.";
|
||||||
//加上学科为空的处理
|
config.ModelConfig.FunAsrNano.UserPrompt = "这是一堂中国的课堂视频音频,请你帮我分析出它讲述的内容!";
|
||||||
if (subject != null)
|
|
||||||
config.ModelConfig.FunAsrNano.UserPrompt = $"这是一堂中国{subject}的课堂视频音频,请你帮我分析出它讲述的内容!";
|
|
||||||
else
|
|
||||||
config.ModelConfig.FunAsrNano.UserPrompt = "这是一堂中国课堂的视频音频,请你帮我分析出它讲述的内容!";
|
|
||||||
config.ModelConfig.FunAsrNano.MaxNewTokens = 512;
|
config.ModelConfig.FunAsrNano.MaxNewTokens = 512;
|
||||||
config.ModelConfig.FunAsrNano.Temperature = 1E-06f;
|
config.ModelConfig.FunAsrNano.Temperature = 1E-06f;
|
||||||
config.ModelConfig.FunAsrNano.TopP = 0.7f;
|
config.ModelConfig.FunAsrNano.TopP = 0.8f;
|
||||||
//种子
|
|
||||||
config.ModelConfig.FunAsrNano.Seed = 42;
|
config.ModelConfig.FunAsrNano.Seed = 42;
|
||||||
|
|
||||||
//模型类型
|
//模型类型
|
||||||
|
|
@ -88,10 +81,10 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
config.ModelConfig.NumThreads = numThreads;
|
config.ModelConfig.NumThreads = numThreads;
|
||||||
config.ModelConfig.Provider = "cpu";
|
config.ModelConfig.Provider = "cpu";
|
||||||
//需要使用GPU
|
//需要使用GPU
|
||||||
if (useGPU)
|
if (!useGPU)
|
||||||
config.ModelConfig.Provider = "cuda";
|
config.ModelConfig.Provider = "cuda";
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
//config.ModelConfig.Debug = 1;
|
config.ModelConfig.Debug = 1;
|
||||||
#endif
|
#endif
|
||||||
OR = new OfflineRecognizer(config);
|
OR = new OfflineRecognizer(config);
|
||||||
}
|
}
|
||||||
|
|
@ -113,17 +106,15 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="task"></param>
|
/// <param name="task"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task RunTask(string task)
|
public Task RunTask(string task)
|
||||||
{
|
{
|
||||||
var taskInfo = serviceProvider.GetRequiredService<Repository<VideoTask>>().GetById(task);
|
|
||||||
if(taskInfo is null)
|
|
||||||
throw new Exception("task 未找到");
|
|
||||||
var filePath = Path.Combine(task.LocalPath(), "task.wav");
|
var filePath = Path.Combine(task.LocalPath(), "task.wav");
|
||||||
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
||||||
throw new Exception("task 音频路径未找到");
|
throw new Exception("task 音频路径未找到");
|
||||||
if (OR is null) Init(taskInfo.Subject);
|
if (OR is null) Init();
|
||||||
serviceProvider.GetRequiredService<SherpaVad>()
|
serviceProvider.GetRequiredService<SherpaVad>()
|
||||||
.TaskHandle(new WaveReader(filePath), task, SoundHandle, SherpaVadVersion.ten_vad_324);
|
.TaskHandle(new WaveReader(filePath), null, SoundHandle, SherpaVadVersion.ten_vad_324);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
VADModelConfig.SileroVad = new SileroVadModelConfig();
|
VADModelConfig.SileroVad = new SileroVadModelConfig();
|
||||||
VADModelConfig.SileroVad.Model = path;
|
VADModelConfig.SileroVad.Model = path;
|
||||||
//(阈值 / 灵敏度) 含义:判定为“语音”的置信度。取值范围通常在 0 到 1 之间。
|
//(阈值 / 灵敏度) 含义:判定为“语音”的置信度。取值范围通常在 0 到 1 之间。
|
||||||
VADModelConfig.SileroVad.Threshold = 0.25f;
|
VADModelConfig.SileroVad.Threshold = 0.3f;
|
||||||
//(最小静音长度)秒。 含义:“要沉默多久,我才认为这句话说完了?”
|
//(最小静音长度)秒。 含义:“要沉默多久,我才认为这句话说完了?”
|
||||||
VADModelConfig.SileroVad.MinSilenceDuration = 0.2f;
|
VADModelConfig.SileroVad.MinSilenceDuration = 0.2f;
|
||||||
// (最小语音长度)秒 含义:“这段声音至少要多长,我才认为它是有效的说话?”
|
// (最小语音长度)秒 含义:“这段声音至少要多长,我才认为它是有效的说话?”
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ namespace VideoAnalysisCore.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sp = spList.Distinct().ToList();
|
var sp = spList.Distinct().ToList();
|
||||||
if (sp.Count > 0 && !string.IsNullOrWhiteSpace(segment.Text))
|
if (sp.Count > 0)
|
||||||
results.Add(segment, sp);
|
results.Add(segment, sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,14 +110,12 @@ namespace VideoAnalysisCore.Common
|
||||||
|
|
||||||
public FFMPGEHandle FFMPGE { get; set; }
|
public FFMPGEHandle FFMPGE { get; set; }
|
||||||
public SenseVoice senseVoice { get; set; }
|
public SenseVoice senseVoice { get; set; }
|
||||||
public FunASRNano funASRNano { get; set; }
|
|
||||||
public RedisManager redisManager { get; set; }
|
public RedisManager redisManager { get; set; }
|
||||||
|
|
||||||
public RedisInit(FFMPGEHandle fFMPGE, SenseVoice senseVoice, RedisManager redisManager, FunASRNano funASRNano)
|
public RedisInit(FFMPGEHandle fFMPGE, SenseVoice senseVoice, RedisManager redisManager)
|
||||||
{
|
{
|
||||||
FFMPGE = fFMPGE;
|
FFMPGE = fFMPGE;
|
||||||
this.senseVoice = senseVoice;
|
this.senseVoice = senseVoice;
|
||||||
this.funASRNano = funASRNano;
|
|
||||||
this.redisManager = redisManager;
|
this.redisManager = redisManager;
|
||||||
Init();
|
Init();
|
||||||
redisManager.InitChannel();
|
redisManager.InitChannel();
|
||||||
|
|
@ -140,7 +138,6 @@ namespace VideoAnalysisCore.Common
|
||||||
});
|
});
|
||||||
SubscribeList.Add(RedisChannelEnum.分离音频, FFMPGE.RunAsync);
|
SubscribeList.Add(RedisChannelEnum.分离音频, FFMPGE.RunAsync);
|
||||||
SubscribeList.Add(RedisChannelEnum.解析字幕, senseVoice.RunTask);
|
SubscribeList.Add(RedisChannelEnum.解析字幕, senseVoice.RunTask);
|
||||||
//SubscribeList.Add(RedisChannelEnum.解析字幕, funASRNano.RunTask);
|
|
||||||
//SubscribeList.Add(RedisChannelEnum.解析说话人,Speaker.Run);
|
//SubscribeList.Add(RedisChannelEnum.解析说话人,Speaker.Run);
|
||||||
SubscribeList.Add(RedisChannelEnum.AI课程类型, async (task) =>
|
SubscribeList.Add(RedisChannelEnum.AI课程类型, async (task) =>
|
||||||
{
|
{
|
||||||
|
|
@ -433,20 +430,15 @@ namespace VideoAnalysisCore.Common
|
||||||
var oldTaskCount = Redis.LLen(RedisExpandKey.IDTask);
|
var oldTaskCount = Redis.LLen(RedisExpandKey.IDTask);
|
||||||
//重试任务并发过多可能会导致程序崩溃
|
//重试任务并发过多可能会导致程序崩溃
|
||||||
// 未能重新分析的中断任务 则单独开一个网页来处理
|
// 未能重新分析的中断任务 则单独开一个网页来处理
|
||||||
if (oldTaskCount > 0)
|
if (oldTaskCount > 0 )
|
||||||
{
|
{
|
||||||
//获取所有未完成的任务
|
var oldTaskArr = Redis.LRange(RedisExpandKey.IDTask, 0, oldTaskCount);
|
||||||
var oldTaskArr = Redis.LRange(RedisExpandKey.IDTask, 0, -1);
|
//不自动清理未完成任务 等待执行完毕/失败后自动清理
|
||||||
Console.WriteLine($"{DateTime.Now:HH:mm:ss}-------------> 发现 {oldTaskArr.Length} 个未完成任务,准备恢复...");
|
//Redis.LTrim(RedisExpandKey.IDTask, 1, 0);//删除 redis 列表
|
||||||
|
//最多执行5个上次中断/或者未完成的任务
|
||||||
//使用信号量限制并发数(5),防止崩溃
|
foreach (var oldTask in oldTaskArr.Take(5))
|
||||||
using var semaphore = new System.Threading.SemaphoreSlim(5);
|
|
||||||
var retryTaskArr = new List<Task>();
|
|
||||||
|
|
||||||
foreach (var oldTask in oldTaskArr)
|
|
||||||
{
|
{
|
||||||
await semaphore.WaitAsync();
|
_ = Task.Run(async () =>
|
||||||
var res = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -458,21 +450,10 @@ namespace VideoAnalysisCore.Common
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await SetTaskErrorMessage(long.Parse(oldTask), ex);
|
await SetTaskErrorMessage(long.Parse(oldTask), ex);
|
||||||
Console.WriteLine($"恢复任务 {oldTask} 失败: {ex.Message}");
|
throw;
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
semaphore.Release();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
retryTaskArr.Add(res);
|
|
||||||
}
|
}
|
||||||
//等待所有 重试任务完成后接收新任务
|
|
||||||
await Task.WhenAll(retryTaskArr);
|
|
||||||
|
|
||||||
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-------------> 所有未完成任务处理完毕!");
|
|
||||||
ReceivingTaskAsync();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -128,23 +128,11 @@ namespace VideoAnalysisCore.Controllers.Dto
|
||||||
/// 用户中心的云校id
|
/// 用户中心的云校id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long? UserCenterCloudSchoolId { get; set; }
|
public long? UserCenterCloudSchoolId { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// 教材层次
|
|
||||||
/// </summary>
|
|
||||||
public CourselevelTypeEnum? CourseLevel { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 年级
|
|
||||||
/// </summary>
|
|
||||||
public GradeEnum? GradeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 教育阶段
|
/// 教育阶段
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EducationStageEnum StageId { get; set; }
|
public EducationStageEnum StageId { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// 年级册(上册/下册)
|
|
||||||
/// </summary>
|
|
||||||
public GradeSemesterEnum? GradeSemester { get; set; }
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 视频处理 请求
|
/// 视频处理 请求
|
||||||
|
|
|
||||||
|
|
@ -115,10 +115,6 @@ namespace VideoAnalysisCore.Controllers
|
||||||
CloudSchoolId =s.UserCenterCloudSchoolId,
|
CloudSchoolId =s.UserCenterCloudSchoolId,
|
||||||
Area = s.Area,
|
Area = s.Area,
|
||||||
HostIP = s.HostIP,
|
HostIP = s.HostIP,
|
||||||
StageId = s.StageId,
|
|
||||||
GradeId = s.GradeId,
|
|
||||||
GradeSemester = s.GradeSemester,
|
|
||||||
TextBookVersionId = s.TextBookVersionId,
|
|
||||||
};
|
};
|
||||||
nodePackages.Add(np);
|
nodePackages.Add(np);
|
||||||
if (videoIdArr.Contains(s.VideoCode)) continue;
|
if (videoIdArr.Contains(s.VideoCode)) continue;
|
||||||
|
|
@ -138,10 +134,6 @@ namespace VideoAnalysisCore.Controllers
|
||||||
PPTVideoUrl = pptUrl,
|
PPTVideoUrl = pptUrl,
|
||||||
VideoType = s.CourseType,
|
VideoType = s.CourseType,
|
||||||
CloudSchoolId = s.UserCenterCloudSchoolId,
|
CloudSchoolId = s.UserCenterCloudSchoolId,
|
||||||
TextBookVersionId = s.TextBookVersionId,
|
|
||||||
GradeSemester =s .GradeSemester,
|
|
||||||
CourseLevel =s.CourseLevel,
|
|
||||||
GradeId = s.GradeId,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await nodePackageInfoDB.InsertRangeAsync(nodePackages);
|
await nodePackageInfoDB.InsertRangeAsync(nodePackages);
|
||||||
|
|
|
||||||
|
|
@ -153,8 +153,8 @@ namespace VideoAnalysisCore.Controllers
|
||||||
public IActionResult AudioRecognition(IFormFile file)
|
public IActionResult AudioRecognition(IFormFile file)
|
||||||
{
|
{
|
||||||
using var s = file.OpenReadStream();
|
using var s = file.OpenReadStream();
|
||||||
var res = senseVoice.RunTask(s);
|
senseVoice.RunTask(s);
|
||||||
return Ok(res);
|
return Ok();
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 语音识别
|
/// 语音识别
|
||||||
|
|
@ -266,7 +266,7 @@ namespace VideoAnalysisCore.Controllers
|
||||||
TagId = req.TagId,
|
TagId = req.TagId,
|
||||||
PPTVideoCode = req.PPTVideoCode,
|
PPTVideoCode = req.PPTVideoCode,
|
||||||
PPTVideoUrl = req.PPTVideoUrl,
|
PPTVideoUrl = req.PPTVideoUrl,
|
||||||
VideoType = req.VideoType,
|
VideoType = req.VideoType
|
||||||
};
|
};
|
||||||
//入库
|
//入库
|
||||||
await baseService.InsertAsync(task);
|
await baseService.InsertAsync(task);
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace VideoAnalysisCore.Model.Enum
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 课程层次
|
|
||||||
/// </summary>
|
|
||||||
public enum CourselevelTypeEnum
|
|
||||||
{
|
|
||||||
一层次 = 1,
|
|
||||||
二层次 = 2,
|
|
||||||
三层次 = 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace VideoAnalysisCore.Model.Enum
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 年级层次
|
|
||||||
/// </summary>
|
|
||||||
public enum GradeSemesterEnum
|
|
||||||
{
|
|
||||||
上册 = 1,
|
|
||||||
下册 = 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -83,25 +83,6 @@ namespace VideoAnalysisCore.Model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Area { get; set; }
|
public string Area { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 年级
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsNullable = true)]
|
|
||||||
public GradeEnum? GradeId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 教育阶段
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsNullable = true)]
|
|
||||||
public EducationStageEnum StageId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 年级册(上册/下册)
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsNullable = true)]
|
|
||||||
public GradeSemesterEnum? GradeSemester { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 学科网的教材版本Id
|
|
||||||
/// </summary>
|
|
||||||
public long TextBookVersionId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 请求区域
|
/// 请求区域
|
||||||
/// <para>回调添加到Headers</para>
|
/// <para>回调添加到Headers</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -145,26 +145,5 @@ namespace VideoAnalysisCore.Model
|
||||||
[SugarColumn(IsNullable = true)]
|
[SugarColumn(IsNullable = true)]
|
||||||
public long? CloudSchoolId { get; set; }
|
public long? CloudSchoolId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 教材层次
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsNullable = true)]
|
|
||||||
public CourselevelTypeEnum? CourseLevel { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 年级
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsNullable = true)]
|
|
||||||
public GradeEnum? GradeId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 年级册(上册/下册)
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsNullable = true)]
|
|
||||||
public GradeSemesterEnum? GradeSemester { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 学科网的教材版本Id
|
|
||||||
/// </summary>
|
|
||||||
public long TextBookVersionId { get; set; }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@ namespace VideoAnalysisCore.Model
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 知识点来源 视频秒,来源原因
|
/// 知识点来源 视频秒,来源原因
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 500, IsNullable = true)]
|
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 课程阶段
|
/// 课程阶段
|
||||||
|
|
@ -76,26 +75,5 @@ namespace VideoAnalysisCore.Model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(IsNullable = true)]
|
[SugarColumn(IsNullable = true)]
|
||||||
public virtual string? TextbookSource { get; set; }
|
public virtual string? TextbookSource { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 教材层次
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsNullable = true)]
|
|
||||||
public CourselevelTypeEnum? CourseLevel { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 年级
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsNullable = true)]
|
|
||||||
public GradeEnum? GradeId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 年级册(上册/下册)
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsNullable = true)]
|
|
||||||
public GradeSemesterEnum? GradeSemester { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 学科网的教材版本Id
|
|
||||||
/// </summary>
|
|
||||||
public long TextBookVersionId { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="org.k2fsa.sherpa.onnx" Version="1.12.22" />
|
<PackageReference Include="org.k2fsa.sherpa.onnx" Version="1.12.21" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||||
<PackageReference Include="SqlSugar.IOC" Version="2.0.0" />
|
<PackageReference Include="SqlSugar.IOC" Version="2.0.0" />
|
||||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.205" />
|
<PackageReference Include="SqlSugarCore" Version="5.1.4.205" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue