21 KiB
AI 原生游戏任务系统 PRD
更新时间:2026-04-02
0. 设计目标
这份方案针对当前仓库,回答的是:
如果要把现有“接受任务 -> 推进 -> 完成 -> 交付”的轻量任务链路,升级成真正的 AI 原生任务系统,应该怎样设计,才能既让任务更像从当前剧情里长出来,又不把规则、奖励和平衡交给模型失控决定?
这里的“AI 原生”不是指:
- 让模型随时随地自由生成一个任务标题和奖励
- 让模型直接修改任务状态、数值或掉落
- 让任务系统变成纯文案系统
这里的“AI 原生”指的是:
- AI 负责理解当前故事局面,生成任务语义、动机、话术、阶段转折和关系变化
- 本地规则负责验证任务是否合法、如何推进、何时完成、奖励如何结算、存档如何兼容
- 任务系统不再只是“静态模板挂在 NPC 身上”,而是“从当前局面推导出来的可控任务合约”
一句话总结:
AI 决定这份任务在叙事上是什么,本地规则决定它在系统上如何成立。
1. 当前系统现状
从当前仓库实现看,任务系统已经有一个可工作的最小闭环:
src/data/questFlow.ts- 负责构造任务、接受任务、推进任务、完成任务、交付任务
src/hooks/story/npcEncounterActions.ts- 负责 NPC 交互里的
quest_accept/quest_turn_in
- 负责 NPC 交互里的
src/hooks/useStoryGeneration.ts- 负责在战斗、宝藏、NPC 交互后更新 quest progress
src/types/story.ts- 当前
QuestLogEntry是运行时任务主结构
- 当前
src/components/AdventurePanel.tsx- 负责任务 UI 展示与奖励领取
当前系统已经支持:
- NPC 提供任务
- 玩家接受任务
- 击败怪物 / 调查宝藏 / 与 NPC 切磋 三类目标推进
- 任务完成后领取奖励
- 奖励影响货币、背包和 NPC affinity
这说明基础闭环是成立的,问题不在“有没有任务系统”,而在于:
- 当前任务更像“局部规则脚手架”,还不是“AI 原生任务系统”
- AI 目前主要参与任务文案氛围和选项重写,没有真正参与任务语义生成
- 任务结构太扁平,无法承载阶段变化、关系转折、后续分支和任务记忆
2. 当前系统的核心限制
2.1 任务来源仍然偏静态
当前 buildQuestForEncounter(...) 的生成逻辑,本质还是:
- 看当前 scene 有没有 hostile npc
- 没有就看 monsterIds
- 再没有就看 treasureHints
- 最后 fallback 到 spar
这套逻辑稳定,但仍然是:
本地 if/else 在选任务类型,AI 只是在外围写故事。
结果是:
- 任务叙事贴合度有限
- 任务动机比较像通用模板
- 很难让任务明显体现“为什么偏偏是这个 NPC、这个时刻、这个场景、这个玩家”
2.2 任务结构只有“单目标单阶段”
当前 QuestLogEntry.objective 只有:
kindtargetMonsterIdtargetNpcIdtargetSceneIdrequiredCount
它适合最小任务闭环,但不适合承载:
- 多阶段任务
- 前置调查 -> 中段确认 -> 最终结算
- NPC 关系驱动的任务升级
- 同一任务在不同世界/场景下的变体
- 任务完成方式差异带来的后续影响
2.3 任务状态过粗
当前状态只有:
activecompletedturned_in
这会导致任务系统无法表达:
- 已发现但未正式接取
- 已接受但未激活下一阶段
- 已满足隐藏条件但未显式揭示
- 已失败 / 已过期 / 已被替代
- 已分支到不同结局
2.4 奖励是固定规则,不随任务语义演化
当前奖励主要由 buildQuestReward(worldType, roleText) 生成。
优点:
- 稳定
- 易控
- 好测试
问题:
- 奖励和任务语义绑定弱
- 很难体现“这个 NPC 因为什么给你这份奖励”
- 不能承载 AI 原生任务中更重要的“关系、线索、世界信息、后续机会”
2.5 AI 没有任务层专属 contract
当前 StoryGenerationContext 很强,但任务相关信息还没有独立建模。于是:
- prompt 里只能把任务作为周边信息描述
- AI 无法明确返回“任务意图”
- 本地也无法验证 AI 给出的任务建议是否合法
这意味着任务系统还没有形成:
专门面向任务设计的 AI <-> Rule contract
3. 设计原则
3.1 AI 负责叙事,本地负责规则
这是本项目已经验证有效的核心边界,这次任务系统继续沿用。
AI 负责:
- 为什么现在出现这份任务
- 任务在关系和剧情上的意义
- 任务目标的话术表达
- 阶段推进时的语义转折
- 奖励在叙事上的来源解释
本地负责:
- 任务是否能生成
- 任务目标类型是否合法
- 可推进事件有哪些
- 任务进度如何累计
- 奖励数值、掉落、affinity、货币如何结算
- 存档结构与兼容
3.2 任务必须是“当前局面的产物”
AI 原生任务不应是抽象模板,而应明确绑定:
- 当前 scene / landmark / threat
- 当前 NPC 的身份、态度、关系阶段
- 最近几条 story history
- 玩家当前状态、build 缺口、队伍关系
- 当前世界观与 custom world profile
如果一份任务脱离这些上下文也成立,那它就还不是“AI 原生任务”。
3.3 任务应优先表达“意图”,再编译成“合约”
不要让 AI 直接返回最终 QuestLogEntry。
更稳的方式是:
- AI 先返回任务意图
QuestIntent - 本地把它编译成可执行的
QuestContract - 再由运行时把
QuestContract映射成 UI 所需的QuestLogEntry
这样能保证:
- AI 足够自由地表达语义
- 本地仍然保留最终裁决权
- 系统容易测试、回放和做 fallback
3.4 任务系统必须兼容“AI 不可用”
这个项目所有关键玩法都应该支持 fallback。
所以 AI 原生任务系统必须允许:
- AI 在线时,任务更贴剧情、更像当前局面的自然产物
- AI 离线时,任务仍可退回 deterministic builder
也就是说:
AI 提升任务质量,但不成为任务闭环的单点故障。
4. 建议的系统分层
建议把新任务系统拆成 5 层。
4.1 任务上下文采样层
先从当前运行时抽取一个统一的任务上下文:
type QuestGenerationContext = {
worldType: WorldType | null;
customWorldProfile?: CustomWorldProfile | null;
currentSceneId?: string | null;
currentSceneName?: string | null;
currentSceneDescription?: string | null;
issuerNpcId?: string | null;
issuerNpcName?: string | null;
issuerNpcContext?: string | null;
issuerAffinity?: number | null;
issuerDisclosureStage?: NpcDisclosureStage | null;
issuerWarmthStage?: NpcWarmthStage | null;
encounterKind?: "npc" | "monster" | "treasure" | "none";
recentStoryMoments: StoryMoment[];
playerCharacter: Character;
playerHp: number;
playerMaxHp: number;
playerMana: number;
playerMaxMana: number;
playerInventory: InventoryItem[];
playerEquipment: EquipmentLoadout;
activeCompanions: CompanionState[];
rosterCompanions: CompanionState[];
currentQuestSummary: Array<{
id: string;
title: string;
status: QuestStatus;
issuerNpcId: string;
}>;
};
这一层的目标不是直接生成任务,而是先回答:
这次任务生成到底发生在什么局面里?
4.2 AI 任务意图层
让 AI 只返回“任务意图”,而不是最终任务实例:
type QuestIntent = {
narrativeType: "bounty" | "escort" | "investigation" | "retrieval" | "relationship" | "trial";
dramaticNeed: string;
issuerGoal: string;
playerHook: string;
worldReason: string;
recommendedObjectiveKinds: Array<
"defeat_monster" | "inspect_treasure" | "spar_with_npc" | "deliver_item" | "reach_scene" | "talk_to_npc"
>;
urgency: "low" | "medium" | "high";
intimacy: "transactional" | "cooperative" | "trust_based";
rewardTheme: "currency" | "resource" | "relationship" | "intel" | "rare_item";
followupHooks: string[];
};
重点是:
- AI 告诉系统这份任务“像什么”
- 不直接决定最终字段值
- 不直接操作运行时状态
4.3 本地任务编译层
本地根据 QuestIntent + QuestGenerationContext 编译出真正可执行的任务合约:
type QuestContract = {
id: string;
issuerNpcId: string;
questArchetype: QuestIntent["narrativeType"];
title: string;
description: string;
summary: string;
steps: QuestStep[];
rewards: QuestRewardPackage;
narrativeBindings: QuestNarrativeBinding;
failPolicy: "never" | "leave_scene" | "issuer_hostile" | "time_window";
};
type QuestStep = {
id: string;
kind:
| "defeat_monster"
| "inspect_treasure"
| "spar_with_npc"
| "deliver_item"
| "reach_scene"
| "talk_to_npc";
targetSceneId?: string;
targetNpcId?: string;
targetMonsterId?: string;
targetItemId?: string;
requiredCount: number;
progress: number;
revealText: string;
completeText: string;
};
这层的职责是:
- 将 AI 的语义建议裁剪到系统允许的任务范式内
- 确保每一个 step 都能被现有事件流推进
- 给每一个任务生成稳定 id、稳定状态和可存档结构
4.4 任务运行时推进层
运行时只认本地任务合约和事件。
建议新增统一的任务推进事件:
type QuestProgressSignal =
| { kind: "monster_defeated"; sceneId?: string | null; monsterId: string }
| { kind: "treasure_inspected"; sceneId?: string | null }
| { kind: "npc_spar_completed"; npcId: string }
| { kind: "npc_talk_completed"; npcId: string }
| { kind: "scene_reached"; sceneId: string }
| { kind: "item_delivered"; npcId: string; itemId: string; quantity: number };
然后统一走:
applyQuestProgressSignal(contract, signal)
而不是继续把推进逻辑分散到多处手写 helper。
这样做的价值是:
- 后续加新任务种类时,只需扩展 signal 与 compiler
useStoryGeneration只负责发 signal,不负责理解具体任务细节- 测试可以直接覆盖“某信号输入 -> 某任务状态输出”
4.5 任务叙事回写层
AI 原生任务真正有价值的一层,不只是“生成任务”,而是“在任务推进时改写故事”。
建议在以下节点允许 AI 回写叙事:
- 任务生成时
- 新阶段解锁时
- 任务完成但未交付时
- 交付奖励时
- 完成方式影响关系时
这层应该只接收:
- 已经确定的任务 contract
- 已经发生的规则结果
而不是允许 AI 反向篡改规则结果。
5. 建议的数据结构升级
建议不要直接推翻当前 QuestLogEntry,而是在现有结构上新增一层 metadata。
5.1 新增任务元数据
type QuestNarrativeBinding = {
origin: "fallback_builder" | "ai_compiled";
narrativeType: "bounty" | "escort" | "investigation" | "retrieval" | "relationship" | "trial";
dramaticNeed: string;
issuerGoal: string;
playerHook: string;
worldReason: string;
followupHooks: string[];
};
type QuestRewardPackage = {
currency: number;
affinityBonus: number;
items: InventoryItem[];
intel?: {
codexEntry?: string;
rumorText?: string;
unlockedSceneId?: string;
};
};
5.2 扩展现有 QuestLogEntry
interface QuestLogEntry {
id: string;
issuerNpcId: string;
issuerNpcName: string;
sceneId: string | null;
title: string;
description: string;
summary: string;
objective: LegacyQuestObjective;
progress: number;
status: QuestStatus;
completionNotified?: boolean;
reward: QuestReward;
rewardText: string;
narrativeBinding?: QuestNarrativeBinding;
steps?: QuestStep[];
activeStepId?: string | null;
visibleStage?: number;
hiddenFlags?: string[];
}
这样做的原因是:
- UI 可以先继续读旧字段
- 新逻辑逐步切到
steps - 旧存档仍然能兼容
5.3 扩展任务状态
建议未来把任务状态扩展为:
type QuestStatus =
| "discovered"
| "active"
| "ready_to_turn_in"
| "completed"
| "turned_in"
| "failed"
| "expired";
MVP 阶段不一定要一次性全上,但至少建议引入:
discoveredready_to_turn_in
因为 AI 原生任务里,“被感知到”与“正式接取”往往不是同一刻。
6. AI 任务 contract 设计
6.1 新增专用 prompt contract
建议新增专用任务生成接口,而不是继续把任务生成混在通用 story completion 里。
建议新增:
src/services/questDirector.tssrc/services/questPrompt.tssrc/services/questTypes.ts
AI 返回格式建议如下:
{
"intent": {
"narrativeType": "investigation",
"dramaticNeed": "NPC 怀疑附近遗迹并不安全,但不敢直接深入",
"issuerGoal": "确认遗迹是否值得继续接近",
"playerHook": "玩家当前正好在此地,并且具备应对未知风险的能力",
"worldReason": "最近场景和对话都指向此处存在被掩盖的旧痕迹",
"recommendedObjectiveKinds": ["inspect_treasure", "talk_to_npc"],
"urgency": "medium",
"intimacy": "cooperative",
"rewardTheme": "intel",
"followupHooks": ["遗迹背后的旧势力", "NPC 曾经来过这里"]
}
}
重点规则:
- 只允许输出意图,不允许直接输出奖励数值
- 只允许使用本地支持的 objective kinds
- 只描述建议,不越权改状态
6.2 AI 负责的字段
AI 可以负责:
title的语义方向description的叙事质感summary的自然语言表达dramaticNeedissuerGoalplayerHookworldReasonfollowupHooks
AI 不负责:
idrequiredCountstatusprogresscurrencyaffinityBonus- 直接生成超出规则支持范围的 objective
6.3 本地编译时的裁决逻辑
本地应当做如下校验:
- objective kind 是否在 allowlist 内
- 当前 scene / npc / monster / treasure 是否真的存在
- 当前 NPC 是否已存在未完成任务
- 当前任务是否会与现有任务重复或冲突
- 奖励是否符合 rarity / economy / relationship budget
如果 AI 返回不合法:
- 降级到 deterministic builder
- 但仍可保留 AI 提供的部分叙事字段作为文案参考
7. 任务生成时机设计
建议不是每次点 NPC 都无条件生成任务,而是让系统先判断“是否值得生成任务机会”。
7.1 建议的触发点
适合生成任务机会的节点:
- 初次与某 NPC 深入互动
- 某 NPC affinity 达到新阶段
- 某场景首次发现异常实体或藏宝线索
- 某条最近剧情暗示了未解决的局面
- 玩家在当前区域停留较久且缺少明确目标
7.2 不建议触发的节点
以下节点不建议频繁生成任务:
- 每次普通聊天都生成任务
- 每次旅行都自动冒出新任务
- 当前已有多个未完成任务时继续塞新任务
- AI 仅凭最近一句话就突然变出高强度委托
7.3 任务机会判断器
建议先做本地 questOpportunityEvaluator:
type QuestOpportunity = {
shouldOffer: boolean;
reason: string;
suggestedIssuerNpcId?: string;
suggestedThreatType?: "monster" | "treasure" | "relationship" | "travel";
};
这层先判断“该不该生成”,再决定“交给 AI 生成什么”。
这样能避免模型过度产出任务。
8. 任务推进设计
8.1 从“单字段进度”升级到“步骤机”
当前任务推进是:
- 一个 objective
- 一个 progress
建议升级为:
- 多 step
- 每个 step 自己推进
- active step 决定当前 UI 展示
例如:
[
{
id: "step_investigate_ruins",
kind: "inspect_treasure",
targetSceneId: "ruins_gate",
requiredCount: 1
},
{
id: "step_report_back",
kind: "talk_to_npc",
targetNpcId: "npc_scholar_lin",
requiredCount: 1
}
]
这样任务就能表达:
- 先调查
- 再回报
- 再领取奖励
8.2 允许“隐式推进,显式揭示”
AI 原生任务最有意思的一点,是任务可以先发生推进,再在叙事上被揭示。
例如:
- 玩家在探索时先完成了调查
- 回去交谈时 NPC 才明确说出“你已经替我确认了那里的情况”
这意味着系统上应允许:
- step 已完成
- 但
visibleStage仍未切换到下一步,直到触发 reveal
这样任务会更像故事,而不是机械 checklist。
9. 奖励设计
9.1 奖励不只是一包货币和道具
AI 原生任务里,奖励至少应拆成 4 类:
- 经济奖励
- currency
- item bundles
- 关系奖励
- affinity
- disclosure stage / warmth stage 解锁
- 信息奖励
- rumor
- codex entry
- 场景/势力线索
- 机会奖励
- 解锁新 scene
- 解锁新 NPC 交互
- 解锁后续任务机会
9.2 AI 负责解释奖励来源,不负责定奖励数值
建议奖励设计继续遵守:
- AI 解释“为什么这个 NPC 给这些”
- 本地决定“究竟给多少”
例如:
- AI 可说:这是 NPC 私藏的旧护符、只愿交给值得信任的人
- 本地则决定:这是
rare relic + affinityBonus 12 + currency 72
10. UI 表达建议
当前 AdventurePanel 已经有不错的任务面板基础,AI 原生任务系统建议补的不是“更多卡片”,而是“更多阶段感”。
建议新增以下 UI 信息:
- 任务来源语义
- 谁委托
- 为什么此刻委托
- 当前阶段标题
- 当前不是只看进度条,还要看“现在在做哪一步”
- 任务关系状态
- 交易型 / 协作型 / 信任型
- 任务后续钩子
- 这件事可能会牵出什么
UI 上不建议直接暴露给玩家的内部字段:
dramaticNeedfollowupHooks原始数组- 所有 hidden flags
这些字段应该被整理成更自然的面板文案。
11. 与当前仓库的接入点建议
11.1 第一批建议改动的文件
建议先从以下文件接入:
src/types/story.ts- 扩展
QuestLogEntry
- 扩展
src/services/aiTypes.ts- 增加任务生成上下文
src/data/questFlow.ts- 增加 contract compiler 和 step progression
src/hooks/story/npcEncounterActions.ts- 将
quest_accept改为“机会判断 -> AI 意图 -> 本地编译”
- 将
src/services/prompt.ts- 不直接负责任务生成,拆出
questPrompt.ts
- 不直接负责任务生成,拆出
11.2 第一批不建议碰的区域
这轮不建议一开始就深入改:
AdventurePanel的整体结构useStoryGeneration的全部 orchestrator- 所有现存 AI story prompt
原因是任务系统已经是主链路的一部分,第一步应该先把 contract 立住,而不是把整个 story 系统一起重写。
12. MVP 落地顺序
阶段 A:先做任务 contract,不改全部表现
新增:
src/services/questTypes.tssrc/services/questDirector.tssrc/services/questPrompt.ts
目标:
- AI 能返回
QuestIntent - 本地能编译成
QuestContract - 旧 UI 仍然照常显示
阶段 B:把 NPC 接任务改成 AI 原生生成
先只改:
quest_accept
此时:
- 仍沿用现有三种基础 objective kind
- 但 title / description / rewardText / followupHooks 开始变成上下文生成
这是最稳的切入点,因为:
- 影响面小
- 可回退
- 容易观察质量提升
阶段 C:把任务推进改成 step + signal
重点改:
applyQuestProgressFromMonsterVictoryapplyQuestProgressFromTreasureapplyQuestProgressFromSpar
把它们收口到统一 signal reducer。
阶段 D:把交付奖励与后续机会联动起来
让 quest_turn_in 不再只是:
- 发货币
- 发道具
- 加 affinity
而是还能:
- 写入 rumor / intel
- 解锁后续任务机会
- 改变 NPC 对话阶段
13. 为什么这套方案适合当前仓库
这套方案不是重做一套新玩法,而是顺着仓库已经验证过的边界继续深化:
- 仓库已经验证“AI 负责叙事,本地负责规则”是正确方向
- 仓库已经有现成的 quest UI、quest status、quest reward、npc affinity 链路
- 当前
useStoryGeneration已经在汇总 story/combat/treasure/npc 事件,天然适合继续发 quest signal - 当前
StoryGenerationContext已经足够强,只是还缺一个任务专用 contract
所以这次真正缺的不是“再加一个任务面板”,而是:
把任务从‘规则附属物’升级成‘叙事与规则之间的正式中介层’。
14. 最后结论
对这个项目来说,理想的 AI 原生任务系统,不应该做成“AI 随机发委托”,而应该做成:
- 系统先判断当前局面是否值得生成任务机会
- AI 根据世界、场景、NPC、最近剧情和玩家状态,生成任务意图
- 本地将任务意图编译成稳定、可测试、可持久化的任务合约
- 运行时通过统一 signal 推进任务步骤
- AI 在任务生成、阶段切换、完成交付时回写叙事质感
- 奖励既包括资源,也包括关系、情报和后续机会
这样生成出来的任务,才会同时满足三件事:
- 像从当前剧情里自然长出来的
- 像系统里可验证、可推进、可存档的
- 像 AI 原生游戏真正该有的任务结构