Files
Genarrative/server-node/src/prompts/customWorldEntityPrompts.ts
2026-04-19 20:33:18 +08:00

250 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
type ParsedRole = {
id: string;
name: string;
title: string;
role: string;
description: string;
visualDescription: string;
actionDescription: string;
sceneVisualDescription: string;
backstory: string;
personality: string;
motivation: string;
tags: string[];
};
type ParsedLandmarkConnection = {
targetLandmarkId: string;
summary: string;
relativePosition: string;
};
type ParsedLandmark = {
id: string;
name: string;
description: string;
visualDescription: string;
dangerLevel: string;
sceneNpcIds: string[];
connections: ParsedLandmarkConnection[];
};
type ParsedProfile = {
name: string;
settingText: string;
summary: string;
tone: string;
playerGoal: string;
playableNpcs: ParsedRole[];
storyNpcs: ParsedRole[];
landmarks: ParsedLandmark[];
};
export const CUSTOM_WORLD_ENTITY_GENERATOR_SYSTEM_PROMPT =
'你是游戏世界编辑器的实体生成器。你必须只返回可解析 JSON不要输出解释、前言或 Markdown。';
function buildRoleReferenceText(roles: ParsedRole[], emptyText: string) {
if (roles.length === 0) {
return emptyText;
}
return roles
.slice(0, 12)
.map(
(role, index) =>
`${index + 1}. ${role.name} / ${role.title || role.role} / 身份:${
role.role || '未写'
} / 描述:${role.description || '未写'} / 背景:${
role.backstory || '未写'
} / 性格:${role.personality || '未写'} / 动机:${
role.motivation || '未写'
} / 形象:${role.visualDescription || '未写'} / 动作表现:${
role.actionDescription || '未写'
} / 场景画面:${role.sceneVisualDescription || '未写'} / 标签:${
role.tags.join('、') || '暂无'
}`,
)
.join('\n');
}
function buildLandmarkReferenceText(profile: ParsedProfile) {
if (profile.landmarks.length === 0) {
return '当前还没有场景设定。';
}
const storyNpcById = new Map(profile.storyNpcs.map((npc) => [npc.id, npc]));
const landmarkById = new Map(
profile.landmarks.map((landmark) => [landmark.id, landmark]),
);
return profile.landmarks
.slice(0, 12)
.map((landmark, index) => {
const sceneNpcNames = landmark.sceneNpcIds
.map((npcId) => storyNpcById.get(npcId)?.name ?? '')
.filter(Boolean)
.join('、');
const connectionNames = landmark.connections
.map((connection) => {
const targetName =
landmarkById.get(connection.targetLandmarkId)?.name ||
connection.targetLandmarkId;
return `${targetName}${connection.relativePosition} / ${
connection.summary || '无说明'
}`;
})
.join('、');
return `${index + 1}. ${landmark.name} / 危险度:${
landmark.dangerLevel || 'medium'
} / 描述:${landmark.description || '未写'} / 画面:${
landmark.visualDescription || '未写'
} / 场景角色:${
sceneNpcNames || '暂无'
} / 连接:${connectionNames || '暂无'}`;
})
.join('\n');
}
export function buildPlayablePrompt(profile: ParsedProfile) {
return [
`世界名:${profile.name}`,
`当前世界设定:${profile.settingText || profile.summary || '未提供额外设定。'}`,
`世界摘要:${profile.summary || '未填写'}`,
`世界基调:${profile.tone || '未填写'}`,
`玩家主线目标:${profile.playerGoal || '未填写'}`,
`当前可扮演角色设定:\n${buildRoleReferenceText(profile.playableNpcs, '当前还没有可扮演角色。')}`,
`当前场景角色设定:\n${buildRoleReferenceText(profile.storyNpcs, '当前还没有场景角色。')}`,
`当前场景设定:\n${buildLandmarkReferenceText(profile)}`,
'请基于上面全部上下文,生成 1 名新的“可扮演角色”。',
'要求:',
'- 必须与当前世界设定、已有可扮演角色、已有场景角色、已有场景形成互补,不要重复定位。',
'- 必须保留明确的协作价值、成长空间和入队理由。',
'- 不要生成泛用模板角色,必须让角色与当前世界的具体势力、地点、冲突或禁忌发生绑定。',
'- visualDescription 只写与角色设定相关的外形、服装、材质、武器、体态、色彩和识别特征,禁止写角色以外的周边环境等与角色不想管的设定。',
'- actionDescription 只写这个角色的动作表现与战斗气质,不要写镜头切换或参数。',
'- sceneVisualDescription 只写该角色首次登场或主要活动区域的场景画面感,不要写“提示词”字样。',
'- 只返回 JSON不要输出解释或 Markdown。',
'JSON 结构:',
'{',
' "playableNpc": {',
' "name": "角色名",',
' "title": "称号",',
' "role": "身份",',
' "description": "一句到两句定位描述",',
' "visualDescription": "角色形象描述",',
' "actionDescription": "动作表现描述",',
' "sceneVisualDescription": "角色关联场景画面描述",',
' "backstory": "背景经历",',
' "personality": "性格特点",',
' "motivation": "当前动机",',
' "combatStyle": "战斗风格",',
' "initialAffinity": 22,',
' "relationshipHooks": ["关系钩子1", "关系钩子2", "关系钩子3"],',
' "tags": ["标签1", "标签2", "标签3"],',
' "publicSummary": "公开背景摘要",',
' "chapterTeasers": ["表层来意", "旧事裂痕", "隐藏执念", "最终底牌"],',
' "chapterContents": ["对应正文1", "对应正文2", "对应正文3", "对应正文4"],',
' "skills": [',
' { "name": "技能1", "summary": "说明", "style": "风格" },',
' { "name": "技能2", "summary": "说明", "style": "风格" },',
' { "name": "技能3", "summary": "说明", "style": "风格" }',
' ],',
' "initialItems": [',
' { "name": "物品1", "category": "武器", "quantity": 1, "rarity": "rare", "description": "说明", "tags": ["标签"] },',
' { "name": "物品2", "category": "专属物品", "quantity": 1, "rarity": "rare", "description": "说明", "tags": ["标签"] },',
' { "name": "物品3", "category": "消耗品", "quantity": 2, "rarity": "uncommon", "description": "说明", "tags": ["标签"] }',
' ]',
' }',
'}',
].join('\n');
}
export function buildStoryPrompt(profile: ParsedProfile) {
return [
`世界名:${profile.name}`,
`当前世界设定:${profile.settingText || profile.summary || '未提供额外设定。'}`,
`世界摘要:${profile.summary || '未填写'}`,
`世界基调:${profile.tone || '未填写'}`,
`玩家主线目标:${profile.playerGoal || '未填写'}`,
`当前可扮演角色设定:\n${buildRoleReferenceText(profile.playableNpcs, '当前还没有可扮演角色。')}`,
`当前场景角色设定:\n${buildRoleReferenceText(profile.storyNpcs, '当前还没有场景角色。')}`,
`当前场景设定:\n${buildLandmarkReferenceText(profile)}`,
'请基于上面全部上下文,生成 1 名新的“场景角色”。',
'要求:',
'- 必须与当前世界设定、已有可扮演角色、已有场景角色、已有场景形成互补,不要重复定位。',
'- 必须像能直接进入游戏的场景角色,而不是抽象设定条目。',
'- 角色应与具体场景、关系链或局势变化发生绑定。',
'- visualDescription 只写与角色设定匹配的外形、服装、材质、武器、体态、色彩和识别特征,不要写“提示词”、镜头参数或构图规则。',
'- actionDescription 只写这个角色的动作表现与战斗气质,不要写镜头切换或参数。',
'- sceneVisualDescription 只写该角色首次登场或主要活动区域的场景画面感,不要写“提示词”字样。',
'- 只返回 JSON不要输出解释或 Markdown。',
'JSON 结构:',
'{',
' "storyNpc": {',
' "name": "角色名",',
' "title": "称号",',
' "role": "身份",',
' "description": "一句到两句定位描述",',
' "visualDescription": "角色形象描述",',
' "actionDescription": "动作表现描述",',
' "sceneVisualDescription": "角色关联场景画面描述",',
' "backstory": "背景经历",',
' "personality": "性格特点",',
' "motivation": "当前动机",',
' "combatStyle": "战斗风格",',
' "initialAffinity": 6,',
' "relationshipHooks": ["关系钩子1", "关系钩子2", "关系钩子3"],',
' "tags": ["标签1", "标签2", "标签3"],',
' "publicSummary": "公开背景摘要",',
' "chapterTeasers": ["表层来意", "旧事裂痕", "隐藏执念", "最终底牌"],',
' "chapterContents": ["对应正文1", "对应正文2", "对应正文3", "对应正文4"],',
' "skills": [',
' { "name": "技能1", "summary": "说明", "style": "风格" },',
' { "name": "技能2", "summary": "说明", "style": "风格" },',
' { "name": "技能3", "summary": "说明", "style": "风格" }',
' ],',
' "initialItems": [',
' { "name": "物品1", "category": "武器", "quantity": 1, "rarity": "rare", "description": "说明", "tags": ["标签"] },',
' { "name": "物品2", "category": "专属物品", "quantity": 1, "rarity": "rare", "description": "说明", "tags": ["标签"] },',
' { "name": "物品3", "category": "消耗品", "quantity": 2, "rarity": "uncommon", "description": "说明", "tags": ["标签"] }',
' ]',
' }',
'}',
].join('\n');
}
export function buildLandmarkPrompt(profile: ParsedProfile) {
return [
`世界名:${profile.name}`,
`当前世界设定:${profile.settingText || profile.summary || '未提供额外设定。'}`,
`世界摘要:${profile.summary || '未填写'}`,
`世界基调:${profile.tone || '未填写'}`,
`玩家主线目标:${profile.playerGoal || '未填写'}`,
`当前可扮演角色设定:\n${buildRoleReferenceText(profile.playableNpcs, '当前还没有可扮演角色。')}`,
`当前场景角色设定:\n${buildRoleReferenceText(profile.storyNpcs, '当前还没有场景角色。')}`,
`当前场景设定:\n${buildLandmarkReferenceText(profile)}`,
'请基于上面全部上下文,生成 1 个新的“场景”。',
'要求:',
'- 必须与当前世界设定、已有可扮演角色、已有场景角色、已有场景形成互补,不要重复。',
'- 必须给出适合出现在这个新场景里的 sceneNpcNames且只能从已有场景角色里选择至少 3 个名字。',
'- 必须给出 connections且 targetLandmarkName 只能引用已有场景名,不要连向自己。',
'- visualDescription 只写这个场景的空间层次、地面、主体建筑或自然景观、氛围、色彩和可见装置,不要写“提示词”、镜头参数或构图规则。',
'- 只返回 JSON不要输出解释或 Markdown。',
'JSON 结构:',
'{',
' "landmark": {',
' "name": "场景名",',
' "description": "场景描述",',
' "visualDescription": "场景画面描述",',
' "dangerLevel": "low|medium|high|extreme",',
' "sceneNpcNames": ["场景角色1", "场景角色2", "场景角色3"],',
' "connections": [',
' { "targetLandmarkName": "已有场景名", "relativePosition": "forward", "summary": "通路说明" },',
' { "targetLandmarkName": "已有场景名", "relativePosition": "inside", "summary": "通路说明" }',
' ]',
' }',
'}',
].join('\n');
}