diff --git a/QYZH.InteractiveMagazine.WorkService/Consumers/JournalTaskReceiveConsumer.cs b/QYZH.InteractiveMagazine.WorkService/Consumers/JournalTaskReceiveConsumer.cs index fa37c71..1f10f2a 100644 --- a/QYZH.InteractiveMagazine.WorkService/Consumers/JournalTaskReceiveConsumer.cs +++ b/QYZH.InteractiveMagazine.WorkService/Consumers/JournalTaskReceiveConsumer.cs @@ -25,6 +25,7 @@ public class JournalTaskReceiveConsumer( private const long DefaultMaxImageBytes = 10 * 1024 * 1024; private const float DefaultCompletionThreshold = 80; private const float DefaultCommunityScoreThreshold = 90; + private const string InvalidAnswerResult = "作答内容不符合要求"; private const string AiProcessingMessage = "AI批阅中,请稍后"; private static readonly object AiSemaphoreLock = new(); private static SemaphoreSlim? aiSemaphore; @@ -359,7 +360,7 @@ public class JournalTaskReceiveConsumer( prompt.AppendLine(scoreUnit.Questions.Count > 1 ? "这是同一跨页题目的多页作答,请综合全部学生作答图片评分。" : "这是单页题目作答,请根据学生作答图片评分。"); - prompt.AppendLine("参考答案不是必有;若无参考答案,以题目 Prompt 和学生答案为准评分。"); + prompt.AppendLine("以题目 Prompt 和学生答案为准评分,若有参考答案请结合参考答案。"); if (referenceAnswerTexts.Count > 0) { prompt.AppendLine("参考答案文本:"); @@ -368,17 +369,10 @@ public class JournalTaskReceiveConsumer( prompt.AppendLine($"{i + 1}. {referenceAnswerTexts[i]}"); } } - else - { - prompt.AppendLine("参考答案文本:无"); - } prompt.AppendLine(); prompt.AppendLine("题目信息:"); 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($" 评分Prompt:{context.Task.Prompt}"); prompt.AppendLine($" 成长值上限:{context.Task.GrowthPoint}"); @@ -394,6 +388,8 @@ public class JournalTaskReceiveConsumer( prompt.AppendLine("- 看不清、缺页、无法识别或答案明显不完整时,降低 Completion,不要猜测高分。"); prompt.AppendLine("- Completion 表示作答完整度,范围 0-100。"); prompt.AppendLine("- Result 返回 50 字内中文评语。"); + prompt.AppendLine("若作答内容与题目要求不符、答非所问、空白、仅抄题或无法形成有效答案,Score/GrowthPoint/Points/Comprehension/Judgment/Expression/Persuasiveness 均返回 0。"); + prompt.AppendLine("若作答内容不符合要求,Result 必须且只能返回:作答内容不符合要求。不要补充原因、建议或其他文字。"); prompt.AppendLine(); prompt.AppendLine("只返回如下 JSON 字段:"); prompt.AppendLine("{"); @@ -424,7 +420,7 @@ public class JournalTaskReceiveConsumer( { cancellationToken.ThrowIfCancellationRequested(); - await using var imageStream = ossService.GetObjectStream(imageUrl); + await using var imageStream = await GetImageStreamAsync(imageUrl, cancellationToken); if (imageStream == null) { throw new InvalidOperationException($"答案图片读取失败,TaskId: {question.Id}, Url: {imageUrl}"); @@ -474,7 +470,7 @@ public class JournalTaskReceiveConsumer( try { - await using var imageStream = ossService.GetObjectStream(imageUrl); + await using var imageStream = await GetImageStreamAsync(imageUrl, cancellationToken); if (imageStream == null) { throw new InvalidOperationException($"参考答案图片读取失败,TaskId: {taskId}, Url: {imageUrl}"); @@ -590,6 +586,18 @@ public class JournalTaskReceiveConsumer( throw new InvalidOperationException($"AI评分调用失败,TaskId: {taskId}", lastException); } + private async Task 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) { var status = (int)statusCode; @@ -732,10 +740,22 @@ public class JournalTaskReceiveConsumer( Expression = Clamp(scoreResult.Expression, 0, task.Expression), Persuasiveness = Clamp(scoreResult.Persuasiveness, 0, task.Persuasiveness), 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) { if (float.IsNaN(value) || float.IsInfinity(value))