From 1eb28e83c5f6da05c0e435628d87ac3b16f0b24f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=84=AD=E6=B2=9B=E8=BB=92?= Date: Tue, 7 Oct 2025 05:12:15 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=BC=B7=E5=8C=96=20Quiz=20Option?= =?UTF-8?q?=20=E7=94=9F=E6=88=90=E6=A9=9F=E5=88=B6=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E9=87=8D=E8=A4=87=E8=A9=9E=E5=BD=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 加強 AI 生成後的詞彙過濾,確保不包含目標詞彙 - 改進 fallback 選項品質,使用更具挑戰性的詞彙池 - 添加詳細日誌追蹤選項生成過程(✅📚💡🤖) - 修復資料庫重複詞彙問題,確保選項品質 測試驗證:happy -> efficient, essential, fundamental 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../Options/OptionsVocabularyService.cs | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/backend/DramaLing.Api/Services/Vocabulary/Options/OptionsVocabularyService.cs b/backend/DramaLing.Api/Services/Vocabulary/Options/OptionsVocabularyService.cs index d4d5529..d8f4dcb 100644 --- a/backend/DramaLing.Api/Services/Vocabulary/Options/OptionsVocabularyService.cs +++ b/backend/DramaLing.Api/Services/Vocabulary/Options/OptionsVocabularyService.cs @@ -79,7 +79,7 @@ public class OptionsVocabularyService : IOptionsVocabularyService if (_cache.TryGetValue(cacheKey, out List? cachedOptions) && cachedOptions != null) { - _logger.LogInformation("Using cached distractors for '{Word}': {Distractors}", + _logger.LogInformation("✅ Using cached distractors for '{Word}': {Distractors}", targetWord, string.Join(", ", cachedOptions.Select(d => d.Word))); return cachedOptions.Take(count).ToList(); } @@ -104,11 +104,14 @@ public class OptionsVocabularyService : IOptionsVocabularyService // 快取結果 _cache.Set(cacheKey, selectedExisting, TimeSpan.FromHours(1)); - _logger.LogInformation("Using existing distractors for '{Word}': {Distractors}", + _logger.LogInformation("📚 Using existing database distractors for '{Word}': {Distractors}", targetWord, string.Join(", ", selectedExisting.Select(d => d.Word))); return selectedExisting; } + _logger.LogInformation("💡 Insufficient database options ({ExistingCount}/{RequiredCount}), proceeding to AI generation for '{Word}'", + existingOptions.Count, count, targetWord); + // 3. 使用 AI 生成新的混淆選項 var aiGeneratedOptions = await GenerateOptionsWithAI(targetWord, cefrLevel, partOfSpeech, count); @@ -122,7 +125,7 @@ public class OptionsVocabularyService : IOptionsVocabularyService _cache.Set(cacheKey, aiGeneratedOptions, TimeSpan.FromHours(1)); } - _logger.LogInformation("Successfully generated {Count} AI distractors for '{Word}': {Distractors}", + _logger.LogInformation("🤖 Successfully generated {Count} AI distractors for '{Word}': {Distractors}", aiGeneratedOptions.Count, targetWord, string.Join(", ", aiGeneratedOptions.Select(d => d.Word))); @@ -247,8 +250,14 @@ Please respond with ONLY a JSON array of strings, like: [""word1"", ""word2"", " return GetFallbackDistractors(targetWord, cefrLevel, partOfSpeech, count); } - // 轉換為 OptionsVocabulary 實體 - var options = generatedWords.Take(count).Select(word => new OptionsVocabulary + // 轉換為 OptionsVocabulary 實體,並過濾掉與目標詞彙相同的詞彙 + var targetWordLower = targetWord.ToLower(); + var filteredWords = generatedWords + .Where(word => !string.IsNullOrWhiteSpace(word) && + word.Trim().ToLower() != targetWordLower) + .Take(count); + + var options = filteredWords.Select(word => new OptionsVocabulary { Id = Guid.NewGuid(), Word = word.Trim(), @@ -259,6 +268,9 @@ Please respond with ONLY a JSON array of strings, like: [""word1"", ""word2"", " UpdatedAt = DateTime.UtcNow }).ToList(); + _logger.LogInformation("Filtered AI generated words from {OriginalCount} to {FilteredCount} options for '{TargetWord}'", + generatedWords.Length, options.Count, targetWord); + // 計算字數長度 foreach (var option in options) { @@ -283,20 +295,27 @@ Please respond with ONLY a JSON array of strings, like: [""word1"", ""word2"", " string partOfSpeech, int count) { - // 根據詞性提供不同的固定選項 + // 根據詞性提供不同的固定選項(避免常見目標詞彙) var fallbackWords = partOfSpeech.ToLower() switch { - "verb" => new[] { "create", "destroy", "change", "develop", "improve", "reduce" }, - "noun" => new[] { "example", "result", "problem", "method", "system", "process" }, - "adjective" => new[] { "important", "different", "special", "general", "common", "simple" }, - "adverb" => new[] { "quickly", "carefully", "easily", "slowly", "clearly", "directly" }, - _ => new[] { "option", "choice", "answer", "question", "test", "study" } + "verb" => new[] { "accomplish", "construct", "transform", "establish", "generate", "implement", "maintain", "organize", "validate", "coordinate" }, + "noun" => new[] { "solution", "approach", "framework", "component", "structure", "mechanism", "principle", "strategy", "technique", "procedure" }, + "adjective" => new[] { "efficient", "comprehensive", "innovative", "sophisticated", "fundamental", "essential", "remarkable", "outstanding", "significant", "substantial" }, + "adverb" => new[] { "thoroughly", "efficiently", "precisely", "consistently", "systematically", "effectively", "accurately", "appropriately", "specifically", "particularly" }, + _ => new[] { "element", "aspect", "feature", "component", "factor", "criteria", "parameter", "attribute", "characteristic", "specification" } }; var targetWordLower = targetWord.ToLower(); - return fallbackWords + var selectedWords = fallbackWords .Where(word => word.ToLower() != targetWordLower) - .Take(count) + .Take(count * 2) // 取更多選項用於隨機選擇 + .OrderBy(x => Guid.NewGuid()) // 隨機排序 + .Take(count); + + _logger.LogInformation("Using fallback distractors for '{TargetWord}': {FallbackWords}", + targetWord, string.Join(", ", selectedWords)); + + return selectedWords .Select(word => new OptionsVocabulary { Id = Guid.NewGuid(),