From 401f4bc5d7cc61ed62eba0e8bb159b9a766a17cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 09:59:48 +0000 Subject: [PATCH 1/4] Initial plan From 8627cb1d7fa1bc897fe567c0a99573f871844480 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:07:27 +0000 Subject: [PATCH 2/4] Enhance puzzle generation with story-based questions, unique answers, and clear next steps Co-authored-by: WilliamsMiao <181537281+WilliamsMiao@users.noreply.github.com> --- backend/ai-service/AIService.js | 61 ++++++++++++++++++++----------- backend/game-engine/GameEngine.js | 13 ++++++- backend/server.js | 19 ++++++++-- backend/storage/database.js | 9 +++-- 4 files changed, 71 insertions(+), 31 deletions(-) diff --git a/backend/ai-service/AIService.js b/backend/ai-service/AIService.js index c9046c6..6de5ce6 100644 --- a/backend/ai-service/AIService.js +++ b/backend/ai-service/AIService.js @@ -494,11 +494,13 @@ ${chapterContent} 2. 为每个玩家分配独特的线索(每人2-3条) ## 设计原则: +- 核心谜题**必须严格基于当前章节的故事内容**,直接关联剧情中的事件、人物、线索 +- 谜题答案必须是**唯一、明确、具体的答案**(如人名、地点、物品、时间等),不能模糊或有多种解释 +- 答案必须能够通过关键词精确验证(提供3-5个核心关键词,必须包含在正确答案中) - 核心谜题必须需要多人信息整合才能解决 - 每个玩家的线索都是解谜的一部分,但单独无法得出答案 - 线索之间要有关联性,鼓励玩家互相交流 - 有些线索可以是误导性的,增加推理难度 -- 谜题答案必须明确,能够验证对错 ## 线索类型: - 目击证词:玩家"看到"或"听到"的信息 @@ -512,11 +514,12 @@ ${players.map((p, i) => `${i+1}. ${p.username}(ID: ${p.id})`).join('\n')} ## 返回格式(严格JSON): { "puzzle": { - "question": "核心谜题问题(让玩家思考和推理的问题)", - "correct_answer": "正确答案(简洁明确)", - "answer_keywords": "关键词1|关键词2|关键词3(用于判断答案是否正确)", + "question": "核心谜题问题(必须基于章节内容的具体问题,如'是谁偷走了XX'、'凶手使用的作案工具是什么'等)", + "correct_answer": "正确答案(必须是唯一、明确的答案,如具体的人名、物品名、地点名等)", + "answer_keywords": "关键词1|关键词2|关键词3|关键词4|关键词5(至少3-5个核心关键词,用于精确判断答案正确性)", "difficulty": 3, - "hints": ["提示1", "提示2", "提示3"] + "hints": ["提示1", "提示2", "提示3"], + "next_steps": "玩家答对后的明确指示(如:'前往书房调查'、'询问管家关于XX的事'、'检查花园的痕迹'等具体行动指引)" }, "playerClues": { "玩家ID": [ @@ -537,11 +540,14 @@ ${storyContext.background || ''} 当前章节内容: ${chapterContent} -请为这${playerCount}个玩家设计谜题和线索。确保: -1. 每个玩家得到2-3条独特线索 -2. 线索内容不能重复 -3. 必须整合所有人的线索才能解开谜题 -4. 返回严格的JSON格式`; +请为这${playerCount}个玩家设计谜题和线索。**重要要求**: +1. 谜题必须**严格基于上述章节内容**,从章节中的具体情节、对话、发现中提炼 +2. 答案必须是**唯一、明确的**(如具体的人名、物品、地点等),不能有歧义 +3. 每个玩家得到2-3条独特线索 +4. 线索内容不能重复 +5. 必须整合所有人的线索才能解开谜题 +6. 提供明确的"下一步行动指示"(next_steps),告诉玩家答对后应该做什么 +7. 返回严格的JSON格式`; try { const response = await this.requestQueue.enqueue( @@ -594,7 +600,8 @@ ${chapterContent} correct_answer: '需要根据线索推理', answer_keywords: '凶手|动机|真相', difficulty: 3, - hints: ['注意时间线的矛盾', '有人在撒谎', '物证不会说谎'] + hints: ['注意时间线的矛盾', '有人在撒谎', '物证不会说谎'], + next_steps: '继续调查其他可疑人员,收集更多证据' }; const playerClues = {}; @@ -722,7 +729,7 @@ ${chapterContent.substring(0, 1000)} const answerLower = playerAnswer.toLowerCase(); const correctAnswerLower = (puzzle.correct_answer || '').toLowerCase(); - // 检查关键词匹配 + // 检查关键词匹配 - 要求更严格的匹配 const matchedKeywords = keywords.filter(k => answerLower.includes(k)); const keywordMatch = matchedKeywords.length / Math.max(keywords.length, 1); @@ -730,27 +737,39 @@ ${chapterContent.substring(0, 1000)} const correctAnswerParts = correctAnswerLower.split(/[,。、\s]+/).filter(p => p.length > 1); const answerMatch = correctAnswerParts.filter(p => answerLower.includes(p)).length / Math.max(correctAnswerParts.length, 1); - const confidence = (keywordMatch * 0.6 + answerMatch * 0.4); - const isCorrect = confidence >= 0.5; // 50%匹配度视为正确 + // 提高正确判断的阈值,确保答案更加准确 + const confidence = (keywordMatch * 0.7 + answerMatch * 0.3); + const isCorrect = confidence >= 0.7; // 提高到70%匹配度视为正确,确保答案唯一性 let feedback = ''; + let nextSteps = puzzle.next_steps || ''; + if (isCorrect) { - if (confidence >= 0.8) { - feedback = '🎉 完全正确!你成功解开了这个谜题!'; + if (confidence >= 0.85) { + feedback = '🎉 完全正确!你成功解开了这个谜题!\n\n'; + } else { + feedback = '✅ 正确!你的推理方向完全对了!\n\n'; + } + + // 添加明确的下一步指示 + if (nextSteps) { + feedback += `📍 **下一步行动**:${nextSteps}\n\n`; + feedback += '💡 当所有玩家都解开谜题后,故事将自动推进到下一章节。'; } else { - feedback = '✅ 基本正确!你的推理方向是对的!'; + feedback += '💡 等待其他玩家完成解谜,故事即将继续推进...'; } - } else if (confidence >= 0.3) { - feedback = '🤔 接近了,但还差一点...再想想?'; + } else if (confidence >= 0.4) { + feedback = '🤔 答案接近了,但还不够准确...请再仔细思考一下?\n\n💭 提示:答案应该更加具体和明确。'; } else { - feedback = '❌ 这个答案似乎偏离了方向,需要更多线索吗?'; + feedback = '❌ 这个答案似乎偏离了方向。\n\n💡 建议:回顾你获得的线索,或者向故事机询问更多提示。'; } return { isCorrect, confidence, matchedKeywords, - feedback + feedback, + nextSteps: isCorrect ? nextSteps : null }; } diff --git a/backend/game-engine/GameEngine.js b/backend/game-engine/GameEngine.js index 6126ccc..fb239e5 100644 --- a/backend/game-engine/GameEngine.js +++ b/backend/game-engine/GameEngine.js @@ -1088,7 +1088,8 @@ ${playerRoles.map(r => `- ${r.characterName}(${r.occupation}): ${r.personalG puzzleQuestion: puzzle.question, correctAnswer: puzzle.correct_answer, answerKeywords: puzzle.answer_keywords, - difficulty: puzzle.difficulty || 3 + difficulty: puzzle.difficulty || 3, + nextSteps: puzzle.next_steps || '' }); console.log(`[故事机初始化] 谜题已保存,ID: ${puzzleId}`); @@ -1320,6 +1321,9 @@ ${playerRoles.map(r => `- ${r.characterName}(${r.occupation}): ${r.personalG throw new Error('当前章节不存在'); } + // 获取谜题信息以包含在推进消息中 + const puzzle = await database.getChapterPuzzle(currentChapterId); + // 生成下一章节 const nextChapter = await this.generateNextChapter(story, currentChapter); @@ -1333,7 +1337,12 @@ ${playerRoles.map(r => `- ${r.characterName}(${r.occupation}): ${r.personalG return { ready: true, newChapter: nextChapter, - interactionResult + interactionResult, + puzzleInfo: puzzle ? { + question: puzzle.puzzle_question, + correctAnswer: puzzle.correct_answer, + nextSteps: puzzle.next_steps + } : null }; } diff --git a/backend/server.js b/backend/server.js index 2c450d8..02fc8b3 100644 --- a/backend/server.js +++ b/backend/server.js @@ -586,15 +586,26 @@ class StoryWeaverServer { // 处理章节推进结果(所有玩家解开谜题后) if (result.progressionResult && result.progressionResult.ready) { - const { newChapter, interactionResult } = result.progressionResult; + const { newChapter, interactionResult, puzzleInfo } = result.progressionResult; console.log(`[章节推进广播] 所有玩家解开谜题,推进到第 ${newChapter.chapterNumber} 章`); - // 1. 先广播解谜成功消息 + // 1. 先广播解谜成功消息(包含答案和下一步指示) + let successMessage = '🎉 恭喜!所有玩家都成功解开了本章谜题!\n\n'; + if (puzzleInfo) { + successMessage += `✅ **谜题**:${puzzleInfo.question}\n`; + successMessage += `🎯 **正确答案**:${puzzleInfo.correctAnswer}\n\n`; + if (puzzleInfo.nextSteps) { + successMessage += `📍 **下一步行动**:${puzzleInfo.nextSteps}\n\n`; + } + } + successMessage += '📖 故事将继续发展,新的章节即将展开...'; + io.to(roomId).emit('puzzle_all_solved', { - message: '🎉 恭喜!所有玩家都成功解开了本章谜题!', + message: successMessage, chapterNumber: newChapter.chapterNumber - 1, - nextChapterNumber: newChapter.chapterNumber + nextChapterNumber: newChapter.chapterNumber, + puzzleInfo: puzzleInfo }); // 2. 广播新章节 diff --git a/backend/storage/database.js b/backend/storage/database.js index 03d1176..83c013c 100644 --- a/backend/storage/database.js +++ b/backend/storage/database.js @@ -248,6 +248,7 @@ class Database { correct_answer TEXT NOT NULL, answer_keywords TEXT, difficulty INTEGER DEFAULT 3, + next_steps TEXT, solved INTEGER DEFAULT 0, solved_by TEXT, solved_at DATETIME, @@ -1266,11 +1267,11 @@ class Database { * @param {Object} puzzle - 谜题数据 */ async createChapterPuzzle(puzzle) { - const { id, chapterId, storyId, puzzleQuestion, correctAnswer, answerKeywords, difficulty = 3 } = puzzle; + const { id, chapterId, storyId, puzzleQuestion, correctAnswer, answerKeywords, difficulty = 3, nextSteps = '' } = puzzle; await this.db.run( - `INSERT INTO chapter_puzzles (id, chapter_id, story_id, puzzle_question, correct_answer, answer_keywords, difficulty) - VALUES (?, ?, ?, ?, ?, ?, ?)`, - [id, chapterId, storyId, puzzleQuestion, correctAnswer, answerKeywords, difficulty] + `INSERT INTO chapter_puzzles (id, chapter_id, story_id, puzzle_question, correct_answer, answer_keywords, difficulty, next_steps) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [id, chapterId, storyId, puzzleQuestion, correctAnswer, answerKeywords, difficulty, nextSteps] ); } From c534462b7f84f90ce55edba363d05f895ce65957 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:10:56 +0000 Subject: [PATCH 3/4] Add database migration for next_steps column and update validation test Co-authored-by: WilliamsMiao <181537281+WilliamsMiao@users.noreply.github.com> --- backend/ai-service/AIService.js | 3 ++- backend/storage/database.js | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/backend/ai-service/AIService.js b/backend/ai-service/AIService.js index 6de5ce6..7a00ac1 100644 --- a/backend/ai-service/AIService.js +++ b/backend/ai-service/AIService.js @@ -738,8 +738,9 @@ ${chapterContent.substring(0, 1000)} const answerMatch = correctAnswerParts.filter(p => answerLower.includes(p)).length / Math.max(correctAnswerParts.length, 1); // 提高正确判断的阈值,确保答案更加准确 + // 使用70%作为阈值,确保答案包含足够的关键信息以保证唯一性 const confidence = (keywordMatch * 0.7 + answerMatch * 0.3); - const isCorrect = confidence >= 0.7; // 提高到70%匹配度视为正确,确保答案唯一性 + const isCorrect = confidence >= 0.7; // 70%匹配度,确保答案唯一且可验证 let feedback = ''; let nextSteps = puzzle.next_steps || ''; diff --git a/backend/storage/database.js b/backend/storage/database.js index 83c013c..e90d193 100644 --- a/backend/storage/database.js +++ b/backend/storage/database.js @@ -414,6 +414,30 @@ class Database { await this.db.run(`CREATE INDEX IF NOT EXISTS idx_player_interactions_story ON player_interactions(story_id)`); console.log('数据库表初始化完成'); + + // 运行迁移 + await this.runMigrations(); + } + + /** + * 运行数据库迁移 + * 处理新增字段等数据库结构变更 + */ + async runMigrations() { + try { + // 检查 chapter_puzzles 表是否有 next_steps 列 + const tableInfo = await this.db.all('PRAGMA table_info(chapter_puzzles)'); + const hasNextSteps = tableInfo.some(col => col.name === 'next_steps'); + + if (!hasNextSteps) { + console.log('执行迁移:添加 next_steps 列到 chapter_puzzles 表'); + await this.db.run('ALTER TABLE chapter_puzzles ADD COLUMN next_steps TEXT DEFAULT ""'); + console.log('迁移完成:next_steps 列已添加'); + } + } catch (error) { + console.error('数据库迁移失败:', error); + // 不抛出错误,允许应用继续运行 + } } async close() { From ea7c285ad51109fb8329e18a089efa4bf8e4b39d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:12:43 +0000 Subject: [PATCH 4/4] Refactor validation constants and fix database schema consistency Co-authored-by: WilliamsMiao <181537281+WilliamsMiao@users.noreply.github.com> --- backend/ai-service/AIService.js | 20 ++++++++++++++------ backend/storage/database.js | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/backend/ai-service/AIService.js b/backend/ai-service/AIService.js index 7a00ac1..4db6348 100644 --- a/backend/ai-service/AIService.js +++ b/backend/ai-service/AIService.js @@ -725,6 +725,15 @@ ${chapterContent.substring(0, 1000)} * @returns {Object} { isCorrect, confidence, feedback } */ async validatePuzzleAnswer(playerAnswer, puzzle) { + // 验证配置常量 + const VALIDATION_CONFIG = { + KEYWORD_WEIGHT: 0.7, + ANSWER_WEIGHT: 0.3, + CORRECT_THRESHOLD: 0.7, + HIGH_CONFIDENCE_THRESHOLD: 0.85, + NEAR_THRESHOLD: 0.4 + }; + const keywords = (puzzle.answer_keywords || '').split('|').map(k => k.trim().toLowerCase()); const answerLower = playerAnswer.toLowerCase(); const correctAnswerLower = (puzzle.correct_answer || '').toLowerCase(); @@ -737,16 +746,15 @@ ${chapterContent.substring(0, 1000)} const correctAnswerParts = correctAnswerLower.split(/[,。、\s]+/).filter(p => p.length > 1); const answerMatch = correctAnswerParts.filter(p => answerLower.includes(p)).length / Math.max(correctAnswerParts.length, 1); - // 提高正确判断的阈值,确保答案更加准确 - // 使用70%作为阈值,确保答案包含足够的关键信息以保证唯一性 - const confidence = (keywordMatch * 0.7 + answerMatch * 0.3); - const isCorrect = confidence >= 0.7; // 70%匹配度,确保答案唯一且可验证 + // 计算置信度并判断正确性 + const confidence = (keywordMatch * VALIDATION_CONFIG.KEYWORD_WEIGHT + answerMatch * VALIDATION_CONFIG.ANSWER_WEIGHT); + const isCorrect = confidence >= VALIDATION_CONFIG.CORRECT_THRESHOLD; let feedback = ''; let nextSteps = puzzle.next_steps || ''; if (isCorrect) { - if (confidence >= 0.85) { + if (confidence >= VALIDATION_CONFIG.HIGH_CONFIDENCE_THRESHOLD) { feedback = '🎉 完全正确!你成功解开了这个谜题!\n\n'; } else { feedback = '✅ 正确!你的推理方向完全对了!\n\n'; @@ -759,7 +767,7 @@ ${chapterContent.substring(0, 1000)} } else { feedback += '💡 等待其他玩家完成解谜,故事即将继续推进...'; } - } else if (confidence >= 0.4) { + } else if (confidence >= VALIDATION_CONFIG.NEAR_THRESHOLD) { feedback = '🤔 答案接近了,但还不够准确...请再仔细思考一下?\n\n💭 提示:答案应该更加具体和明确。'; } else { feedback = '❌ 这个答案似乎偏离了方向。\n\n💡 建议:回顾你获得的线索,或者向故事机询问更多提示。'; diff --git a/backend/storage/database.js b/backend/storage/database.js index e90d193..1a5739c 100644 --- a/backend/storage/database.js +++ b/backend/storage/database.js @@ -248,7 +248,7 @@ class Database { correct_answer TEXT NOT NULL, answer_keywords TEXT, difficulty INTEGER DEFAULT 3, - next_steps TEXT, + next_steps TEXT DEFAULT "", solved INTEGER DEFAULT 0, solved_by TEXT, solved_at DATETIME,