refactor(JournalTaskConsumer): 优化AI评分prompt与图片加载逻辑
1. 新增无效作答固定返回文案常量 2. 调整prompt提示词,补充无效作答评分规则与返回要求 3. 移除冗余的参考答案无结果提示与题目基础信息打印 4. 封装图片流获取方法,兼容OSS和HTTP链接 5. 新增结果标准化处理,统一无效作答返回格式
This commit is contained in:
@ -25,6 +25,7 @@ public class JournalTaskReceiveConsumer(
|
|||||||
private const long DefaultMaxImageBytes = 10 * 1024 * 1024;
|
private const long DefaultMaxImageBytes = 10 * 1024 * 1024;
|
||||||
private const float DefaultCompletionThreshold = 80;
|
private const float DefaultCompletionThreshold = 80;
|
||||||
private const float DefaultCommunityScoreThreshold = 90;
|
private const float DefaultCommunityScoreThreshold = 90;
|
||||||
|
private const string InvalidAnswerResult = "作答内容不符合要求";
|
||||||
private const string AiProcessingMessage = "AI批阅中,请稍后";
|
private const string AiProcessingMessage = "AI批阅中,请稍后";
|
||||||
private static readonly object AiSemaphoreLock = new();
|
private static readonly object AiSemaphoreLock = new();
|
||||||
private static SemaphoreSlim? aiSemaphore;
|
private static SemaphoreSlim? aiSemaphore;
|
||||||
@ -359,7 +360,7 @@ public class JournalTaskReceiveConsumer(
|
|||||||
prompt.AppendLine(scoreUnit.Questions.Count > 1
|
prompt.AppendLine(scoreUnit.Questions.Count > 1
|
||||||
? "这是同一跨页题目的多页作答,请综合全部学生作答图片评分。"
|
? "这是同一跨页题目的多页作答,请综合全部学生作答图片评分。"
|
||||||
: "这是单页题目作答,请根据学生作答图片评分。");
|
: "这是单页题目作答,请根据学生作答图片评分。");
|
||||||
prompt.AppendLine("参考答案不是必有;若无参考答案,以题目 Prompt 和学生答案为准评分。");
|
prompt.AppendLine("以题目 Prompt 和学生答案为准评分,若有参考答案请结合参考答案。");
|
||||||
if (referenceAnswerTexts.Count > 0)
|
if (referenceAnswerTexts.Count > 0)
|
||||||
{
|
{
|
||||||
prompt.AppendLine("参考答案文本:");
|
prompt.AppendLine("参考答案文本:");
|
||||||
@ -368,17 +369,10 @@ public class JournalTaskReceiveConsumer(
|
|||||||
prompt.AppendLine($"{i + 1}. {referenceAnswerTexts[i]}");
|
prompt.AppendLine($"{i + 1}. {referenceAnswerTexts[i]}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
prompt.AppendLine("参考答案文本:无");
|
|
||||||
}
|
|
||||||
prompt.AppendLine();
|
prompt.AppendLine();
|
||||||
prompt.AppendLine("题目信息:");
|
prompt.AppendLine("题目信息:");
|
||||||
foreach (var context in scoreUnit.Questions)
|
foreach (var context in scoreUnit.Questions)
|
||||||
{
|
{
|
||||||
prompt.AppendLine($"- TaskId:{context.Task.Id}");
|
|
||||||
prompt.AppendLine($" 页码:{context.Page?.PageNum ?? 0}");
|
|
||||||
prompt.AppendLine($" 题号:{context.Task.No}");
|
|
||||||
prompt.AppendLine($" 题目内容:{context.Task.Task}");
|
prompt.AppendLine($" 题目内容:{context.Task.Task}");
|
||||||
prompt.AppendLine($" 评分Prompt:{context.Task.Prompt}");
|
prompt.AppendLine($" 评分Prompt:{context.Task.Prompt}");
|
||||||
prompt.AppendLine($" 成长值上限:{context.Task.GrowthPoint}");
|
prompt.AppendLine($" 成长值上限:{context.Task.GrowthPoint}");
|
||||||
@ -394,6 +388,8 @@ public class JournalTaskReceiveConsumer(
|
|||||||
prompt.AppendLine("- 看不清、缺页、无法识别或答案明显不完整时,降低 Completion,不要猜测高分。");
|
prompt.AppendLine("- 看不清、缺页、无法识别或答案明显不完整时,降低 Completion,不要猜测高分。");
|
||||||
prompt.AppendLine("- Completion 表示作答完整度,范围 0-100。");
|
prompt.AppendLine("- Completion 表示作答完整度,范围 0-100。");
|
||||||
prompt.AppendLine("- Result 返回 50 字内中文评语。");
|
prompt.AppendLine("- Result 返回 50 字内中文评语。");
|
||||||
|
prompt.AppendLine("若作答内容与题目要求不符、答非所问、空白、仅抄题或无法形成有效答案,Score/GrowthPoint/Points/Comprehension/Judgment/Expression/Persuasiveness 均返回 0。");
|
||||||
|
prompt.AppendLine("若作答内容不符合要求,Result 必须且只能返回:作答内容不符合要求。不要补充原因、建议或其他文字。");
|
||||||
prompt.AppendLine();
|
prompt.AppendLine();
|
||||||
prompt.AppendLine("只返回如下 JSON 字段:");
|
prompt.AppendLine("只返回如下 JSON 字段:");
|
||||||
prompt.AppendLine("{");
|
prompt.AppendLine("{");
|
||||||
@ -424,7 +420,7 @@ public class JournalTaskReceiveConsumer(
|
|||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
await using var imageStream = ossService.GetObjectStream(imageUrl);
|
await using var imageStream = await GetImageStreamAsync(imageUrl, cancellationToken);
|
||||||
if (imageStream == null)
|
if (imageStream == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"答案图片读取失败,TaskId: {question.Id}, Url: {imageUrl}");
|
throw new InvalidOperationException($"答案图片读取失败,TaskId: {question.Id}, Url: {imageUrl}");
|
||||||
@ -474,7 +470,7 @@ public class JournalTaskReceiveConsumer(
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await using var imageStream = ossService.GetObjectStream(imageUrl);
|
await using var imageStream = await GetImageStreamAsync(imageUrl, cancellationToken);
|
||||||
if (imageStream == null)
|
if (imageStream == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"参考答案图片读取失败,TaskId: {taskId}, Url: {imageUrl}");
|
throw new InvalidOperationException($"参考答案图片读取失败,TaskId: {taskId}, Url: {imageUrl}");
|
||||||
@ -590,6 +586,18 @@ public class JournalTaskReceiveConsumer(
|
|||||||
throw new InvalidOperationException($"AI评分调用失败,TaskId: {taskId}", lastException);
|
throw new InvalidOperationException($"AI评分调用失败,TaskId: {taskId}", lastException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<Stream?> GetImageStreamAsync(string imageUrl, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (Uri.TryCreate(imageUrl, UriKind.Absolute, out var uri)
|
||||||
|
&& (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))
|
||||||
|
{
|
||||||
|
var httpClient = httpClientFactory.CreateClient();
|
||||||
|
return await httpClient.GetStreamAsync(uri, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ossService.GetObjectStream(imageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool ShouldRetry(System.Net.HttpStatusCode statusCode)
|
private static bool ShouldRetry(System.Net.HttpStatusCode statusCode)
|
||||||
{
|
{
|
||||||
var status = (int)statusCode;
|
var status = (int)statusCode;
|
||||||
@ -732,10 +740,22 @@ public class JournalTaskReceiveConsumer(
|
|||||||
Expression = Clamp(scoreResult.Expression, 0, task.Expression),
|
Expression = Clamp(scoreResult.Expression, 0, task.Expression),
|
||||||
Persuasiveness = Clamp(scoreResult.Persuasiveness, 0, task.Persuasiveness),
|
Persuasiveness = Clamp(scoreResult.Persuasiveness, 0, task.Persuasiveness),
|
||||||
Completion = Clamp(scoreResult.Completion, 0, 100),
|
Completion = Clamp(scoreResult.Completion, 0, 100),
|
||||||
Result = TrimResult(scoreResult.Result)
|
Result = NormalizeResult(scoreResult.Result)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string NormalizeResult(string? result)
|
||||||
|
{
|
||||||
|
var trimmedResult = TrimResult(result);
|
||||||
|
if (trimmedResult.Contains("不符合要求", StringComparison.Ordinal) ||
|
||||||
|
trimmedResult.Contains("不符", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return InvalidAnswerResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmedResult;
|
||||||
|
}
|
||||||
|
|
||||||
private static float Clamp(float value, float min, float max)
|
private static float Clamp(float value, float min, float max)
|
||||||
{
|
{
|
||||||
if (float.IsNaN(value) || float.IsInfinity(value))
|
if (float.IsNaN(value) || float.IsInfinity(value))
|
||||||
|
|||||||
Reference in New Issue
Block a user