/** * M-PALACE · 背景生成引擎 * 接收锚点答案 + 人格侧面初始数据 * → 从宫廷数据库匹配 * → 生成逻辑自洽的专属背景 * * 生成流程: * 锚点1(世界观)→ 选择 templates/ 基础模板 * 锚点2(身份) → 确定主角定位 + 初始人格快照 * → 查询 palace-db 四大维度 → 调用模型 → 输出世界状态 */ const fs = require('fs'); const path = require('path'); const DATA_DIR = path.join(__dirname, '..', '..', 'data'); const TEMPLATES_DIR = path.join(DATA_DIR, 'templates'); const PALACE_DB_PATH = path.join(DATA_DIR, 'palace-db.json'); let _palaceDB = null; function loadPalaceDB() { if (_palaceDB) return _palaceDB; _palaceDB = JSON.parse(fs.readFileSync(PALACE_DB_PATH, 'utf-8')); return _palaceDB; } function loadTemplate(worldview) { const map = { 'ancient-china': 'ancient-china.json', '古代中国': 'ancient-china.json', 'fantasy': 'fantasy-dynasty.json', '架空王朝': 'fantasy-dynasty.json' }; const file = map[worldview] || 'ancient-china.json'; return JSON.parse(fs.readFileSync(path.join(TEMPLATES_DIR, file), 'utf-8')); } /** * 从模板中随机选取一项 */ function pick(arr) { return arr[Math.floor(Math.random() * arr.length)]; } /** * 基于人格侧面决定 NPC 映射 * 核心机密:每个 NPC 对应玩家 1~2 个人格侧面的外化 */ function generateNPCMappings(scores) { const npcs = []; const dims = Object.entries(scores).sort((a, b) => b[1] - a[1]); // 最强维度 → 镜像对手 const strongest = dims[0]; npcs.push({ archetype: 'mirror', mapped_dims: [strongest[0]], role_hint: 'rival', description: '与你最相似的对手' }); // 最弱维度 → 放大威胁 const weakest = dims[dims.length - 1]; npcs.push({ archetype: 'shadow', mapped_dims: [weakest[0]], role_hint: 'threat', description: '让你恐惧的存在' }); // 温情相关 → 情感锚点 if (scores.warmth >= 40) { npcs.push({ archetype: 'anchor', mapped_dims: ['warmth', 'loyalty'], role_hint: 'ally', description: '需要被保护的弱势角色' }); } // 多疑相关 → 暧昧盟友 if (scores.suspicion >= 45) { npcs.push({ archetype: 'trickster', mapped_dims: ['suspicion', 'cunning'], role_hint: 'ambiguous', description: '行为暧昧的盟友' }); } // 确保至少 3 个 NPC if (npcs.length < 3) { npcs.push({ archetype: 'catalyst', mapped_dims: [dims[2][0]], role_hint: 'neutral', description: '推动局势变化的中立角色' }); } return npcs; } /** * 基于人格分数查询 palace-db,确定初始格局 */ function queryInitialSetup(scores) { const db = loadPalaceDB(); // 权力维度:基于 ambition+cunning const powerLevel = (scores.ambition + scores.cunning) / 2; const factionCount = powerLevel > 60 ? 3 : 2; // 后宫地位:基于 vanity+warmth const statusLevel = (scores.vanity + scores.warmth) / 2; // 情感关系:基于 warmth+loyalty const emotionIntensity = (scores.warmth + scores.loyalty) / 2; // 矛盾冲突:基于 aggression+suspicion const conflictLevel = (scores.aggression + scores.suspicion) / 2; const conflictType = conflictLevel > 60 ? '争宠' : '自保'; return { factions: db.power.factions.slice(0, factionCount), initialEvent: pick(db.power.events), startingRank: db.status.ranks[Math.min(Math.floor(statusLevel / 15), db.status.ranks.length - 1)], emotionType: emotionIntensity > 60 ? pick(db.emotion.types.slice(0, 3)) : pick(db.emotion.types.slice(3)), emotionTrigger: pick(db.emotion.triggers), conflictType: conflictType, escalation: db.conflict.escalation[0] }; } /** * 构建世界生成 prompt(供模型调用) */ function buildWorldPrompt(template, role, setup, npcMappings) { const dynasty = template.setting.dynasty; const era = pick(template.setting.era_prefix); const roleConfig = template.roles[role] || template.roles['妃子']; const title = pick(roleConfig.title_options); const npcDescriptions = npcMappings.map((npc, i) => { return `NPC${i + 1}(${npc.role_hint}型):${npc.description}`; }).join('\n'); return { system: [ '你是一个古风宫斗叙事生成器。', '要求:文笔古风典雅,有呼吸感,不要白话流水账。', '使用通感手法,注重氛围营造。', '所有NPC的存在都有隐藏的人格映射逻辑,但不能向玩家揭示。' ].join('\n'), user: [ `请为以下设定生成宫廷世界开篇:`, `王朝:${dynasty}`, `年号:${era}`, `玩家身份:${role}(${title})`, `初始阵营格局:${setup.factions.join('、')}`, `开局事件:${setup.initialEvent}`, `情感基调:${setup.emotionType}`, `冲突类型:${setup.conflictType}`, ``, `需要生成的NPC:`, npcDescriptions, ``, `输出格式(JSON):`, `{`, ` "dynasty_name": "王朝名",`, ` "era_name": "年号",`, ` "background": "一段时代背景描述(古风文笔·100-200字)",`, ` "player_intro": "主角身世与处境(古风文笔·100-200字)",`, ` "npcs": [{ "name": "角色名", "title": "头衔", "personality": "性格", "relation_to_player": "与主角关系", "hidden_archetype": "mirror|shadow|anchor|trickster|catalyst" }],`, ` "opening_event": "开局事件描述(古风文笔·150-300字)",`, ` "opening_choices": ["选项A", "选项B", "选项C"]`, `}` ].join('\n'), meta: { dynasty, era, title, role } }; } /** * 主入口:生成完整背景(不依赖外部模型时,使用本地模板拼接) */ function generate(worldview, role, personaScores) { const template = loadTemplate(worldview); const setup = queryInitialSetup(personaScores); const npcMappings = generateNPCMappings(personaScores); const prompt = buildWorldPrompt(template, role, setup, npcMappings); // 构造本地 fallback 世界(不依赖外部模型时使用) const dynasty = prompt.meta.dynasty; const era = prompt.meta.era; const roleConfig = template.roles[role] || template.roles['妃子']; const title = prompt.meta.title; const localWorld = { dynasty_name: dynasty, era_name: era, background: `${dynasty}${era}年间,朝堂暗流涌动。${setup.factions.join('与')}明争暗斗,帝位之下,人心叵测。宫墙之内,灯火通明处未必安宁,暗影幢幢处未必无情。`, player_intro: `你是${dynasty}${title},${pick(roleConfig.family_options || ['名门'])}出身。入宫数载,${setup.conflictType === '争宠' ? '虽有圣眷,却树敌颇多' : '小心翼翼,只求自保平安'}。${setup.emotionType}的暗线,早已悄然铺开。`, npcs: npcMappings.map(function (npc, i) { const names = ['沈婉仪', '赵昭仪', '陈太傅', '李将军', '刘公公']; return { name: names[i] || '无名氏', title: pick(template.setting.era_prefix) + '年间人物', personality: npc.description, relation_to_player: npc.role_hint, hidden_archetype: npc.archetype }; }), opening_event: `夜半三更,${template.setting.inner_palace}传来急召。太后身边的掌事姑姑面色如常,语气却带着不容拒绝的冷意。你知道,这一趟,去与不去,都是棋局的一部分。`, opening_choices: [ '立刻更衣前往,恭顺以对', '称身体不适,推迟半个时辰', '先派人去打听太后的意图' ] }; return { world: localWorld, npcMappings: npcMappings, setup: setup, prompt: prompt, template: template }; } module.exports = { generate, loadTemplate, loadPalaceDB, generateNPCMappings, queryInitialSetup, buildWorldPrompt };