Files
Genarrative/src/prompts/runtimeItemPrompts.ts
高物 50759f3c1e
Some checks failed
CI / verify (push) Has been cancelled
1
2026-04-20 09:54:17 +08:00

120 lines
4.8 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.
import {
buildRuntimeItemAiIntent,
buildRuntimeItemAiPromptInput,
} from '../data/runtimeItemNarrative';
import type {
RuntimeItemGenerationContext,
RuntimeItemPlan,
RuntimeRelationAnchor,
} from '../types';
import { buildRuntimeItemStoryFingerprint } from '../services/storyEngine/carrierNarrativeCompiler';
import { buildCarrierVisibilitySlice } from '../services/storyEngine/visibilityEngine';
function describeRelationAnchor(anchor: RuntimeRelationAnchor) {
switch (anchor.type) {
case 'npc':
return `NPC:${anchor.npcName}`;
case 'scene':
return `场景:${anchor.sceneName}`;
case 'monster':
return `怪物:${anchor.monsterName}`;
case 'quest':
return `任务:${anchor.questName}`;
case 'faction':
return `势力:${anchor.factionName}`;
default:
return `地标:${anchor.landmarkName}`;
}
}
function describeCarrierFactId(factId: string) {
if (factId === 'visibleClue') return '可见线索';
if (factId === 'currentAppearanceReason') return '当前出现理由';
if (factId === 'witnessMark') return '见证痕';
if (factId === 'unresolvedQuestion') return '未完成问题';
if (factId.startsWith('thread:')) return `线程索引(${factId.slice('thread:'.length)})`;
return factId;
}
function describePlan(
context: RuntimeItemGenerationContext,
plan: RuntimeItemPlan,
index: number,
) {
const promptInput = buildRuntimeItemAiPromptInput(context, plan);
const fallbackIntent = buildRuntimeItemAiIntent(context, plan);
const fallbackFingerprint = buildRuntimeItemStoryFingerprint({
context,
plan,
intent: fallbackIntent,
});
const visibilitySlice = buildCarrierVisibilitySlice({
activeThreadIds: context.activeThreadIds,
storyFingerprint: fallbackFingerprint,
});
return [
`物品 ${index + 1}`,
`- slot: ${plan.slot}`,
`- 物品类型: ${promptInput.desiredItemKind}`,
`- 持续性: ${promptInput.permanence}`,
`- 关系锚点: ${describeRelationAnchor(plan.relationAnchor)}`,
`- 世界摘要: ${promptInput.worldSummary}`,
`- 场景摘要: ${promptInput.sceneSummary || '无'}`,
`- 遭遇摘要: ${promptInput.encounterSummary || '无'}`,
`- 相关人物: ${promptInput.relatedNpcSummary}`,
`- 当前激活线程: ${promptInput.activeThreadSummary || '暂无'}`,
`- 近期剧情: ${promptInput.recentStorySummary}`,
`- 玩家当前 build: ${promptInput.playerBuildDirection.join('、') || '均衡'}`,
`- 玩家待补缺口: ${promptInput.playerBuildGaps.join('、') || '无明显缺口'}`,
`- 本次目标方向: ${plan.targetBuildDirection.join('、') || '均衡'}`,
`- 物件可直写线索: ${visibilitySlice.sayableFactIds.map(describeCarrierFactId).join('、') || '无'}`,
`- 物件只宜暗写: ${visibilitySlice.inferredFactIds.map(describeCarrierFactId).join('、') || '无'}`,
].join('\n');
}
export const RUNTIME_ITEM_INTENT_SYSTEM_PROMPT = `你是 AI 原生叙事 RPG 的运行时物品导演。
你只返回 JSON不要输出 Markdown、解释或代码块。
输出结构:
{
"intents": [
{
"shortNameSeed": "中文短种子",
"sourcePhrase": "中文来源短语",
"reasonToAppear": "中文出现理由",
"relationHooks": ["中文关系钩子"],
"desiredBuildTags": ["中文 build 标签"],
"desiredFunctionalBias": ["heal|mana|cooldown|guard|damage"],
"tone": "grim|mysterious|martial|ritual|survival",
"visibleClue": "玩家第一眼能抓到的痕迹",
"witnessMark": "它见证过什么的使用痕",
"unfinishedBusiness": "背后仍未结清的问题",
"hiddenHook": "更深一层但别直接讲穿的钩子",
"reactionHooks": ["以后谁会对它起反应"],
"namingPattern": "命名范式建议"
}
]
}
规则:
- intents 数量必须与输入物品数量完全一致,顺序也必须一致。
- 所有自然语言字段都必须使用中文。
- 物品意图必须贴合当前场景、关系锚点、近期剧情和玩家当前 build。
- visibleClue / witnessMark / unfinishedBusiness / hiddenHook 要优先围绕当前激活线程、相关角色压力和旧痕来写。
- desiredBuildTags 要优先围绕玩家当前 build 与待补缺口,不要写空数组。
- desiredFunctionalBias 只能从给定枚举里选 1 到 2 个。
- reasonToAppear 必须解释为什么这件东西会在当前局势里出现,而不是泛泛描述。`;
export function buildRuntimeItemIntentPrompt(params: {
context: RuntimeItemGenerationContext;
plans: RuntimeItemPlan[];
}) {
return [
`生成渠道:${params.context.generationChannel}`,
`以下每个物品都需要给出一条可编译的运行时物品意图。`,
...params.plans.map((plan, index) => describePlan(params.context, plan, index)),
'请严格返回 JSON。',
].join('\n\n');
}