@@ -0,0 +1,471 @@
|
||||
# AI 原生 NPC 单轮聊天会话迭代 PRD
|
||||
|
||||
更新时间:`2026-04-18`
|
||||
|
||||
## 0. 文档目的
|
||||
|
||||
本 PRD 只定义 `npc_chat` 在冒险主面板中的这一轮迭代落地方式。
|
||||
|
||||
目标不是新建一套聊天系统,而是在保持现有游戏 UI 外壳、剧情面板结构、消息流位置不变的前提下,把 `npc_chat` 从“触发一次后回到普通剧情流”升级为“进入可持续续写的单轮聊天会话”。
|
||||
|
||||
本次文档必须直接指导编码,避免需求落地漂移。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话定义
|
||||
|
||||
玩家点击 `npc_chat` 后,进入 NPC 聊天模式;每次只完成一轮“玩家输入 -> NPC 回复 -> 关系变化消息 -> 下一轮 3 个建议选项 + 1 个自定义输入”,直到玩家主动退出聊天。
|
||||
|
||||
---
|
||||
|
||||
## 2. 背景与问题
|
||||
|
||||
当前 `npc_chat` 更接近“触发一段剧情性对话”,而不是“持续聊天”:
|
||||
|
||||
1. 玩家点击聊天后,常常是一次性生成较长结果,再回到普通冒险选项。
|
||||
2. 缺少稳定的续聊状态,无法把多轮聊天作为一个连续会话维持。
|
||||
3. 好感度变化更多停留在逻辑层,玩家在消息流中感知不明显。
|
||||
4. 没有单独的退出聊天控制,用户只能被动等系统切回普通状态。
|
||||
5. 选项形态仍偏剧情动作,不够像聊天接话。
|
||||
|
||||
这会导致 `npc_chat` 的体验不像“和角色对话”,而像“触发一个剧情功能”。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本次目标
|
||||
|
||||
本次迭代必须同时满足以下目标:
|
||||
|
||||
1. `npc_chat` 触发后进入聊天交互态。
|
||||
2. 聊天态沿用当前主冒险面板,不新增页面、不弹新系统。
|
||||
3. 每次用户只推进一轮对话。
|
||||
4. 每轮结束后稳定出现 `3` 个建议续聊选项。
|
||||
5. 每轮结束后稳定出现 `1` 个自定义输入框。
|
||||
6. 玩家选择建议项或提交自定义输入后,继续在同一消息队列中续写。
|
||||
7. 好感度增减必须作为“系统消息”插入到对话消息队列中。
|
||||
8. NPC 回复必须支持流式传输,并在前端边接收边解析显示。
|
||||
9. 背包按钮所在行的最右侧必须新增“退出聊天”按钮。
|
||||
10. 退出聊天后恢复普通冒险态,不保留当前聊天输入框与聊天建议项。
|
||||
|
||||
---
|
||||
|
||||
## 4. 明确不做
|
||||
|
||||
本次不做:
|
||||
|
||||
1. 不新建独立聊天页面。
|
||||
2. 不重做现有主面板 UI 结构。
|
||||
3. 不引入多 NPC 并行聊天。
|
||||
4. 不做聊天记录存档面板。
|
||||
5. 不做复杂关系公式配置后台。
|
||||
6. 不做语音输入、表情、附件等扩展输入能力。
|
||||
7. 不把前端改成承担关系计算或剧情判定。
|
||||
|
||||
---
|
||||
|
||||
## 5. 核心体验
|
||||
|
||||
## 5.1 进入聊天
|
||||
|
||||
当玩家点击 `npc_chat` 选项时:
|
||||
|
||||
1. 保持当前冒险面板布局不变。
|
||||
2. 中部内容区切换为聊天消息流展示模式。
|
||||
3. 底部选项区切换为聊天建议区。
|
||||
4. 底部附加一个自定义输入框与发送按钮。
|
||||
5. 顶层不跳转、不弹窗、不覆盖成新页面。
|
||||
|
||||
## 5.2 单轮推进
|
||||
|
||||
单轮定义固定为:
|
||||
|
||||
1. 玩家通过“建议选项”或“自定义输入”提交一句话。
|
||||
2. 玩家消息立即进入消息队列。
|
||||
3. NPC 回复开始流式显示。
|
||||
4. 流式结束后,如果有关系变化,插入一条系统消息。
|
||||
5. 系统刷新下一轮 `3` 个建议选项。
|
||||
6. 系统保留自定义输入入口,等待下一轮。
|
||||
|
||||
## 5.3 退出聊天
|
||||
|
||||
玩家点击“退出聊天”后:
|
||||
|
||||
1. 当前聊天态结束。
|
||||
2. 面板底部恢复普通冒险选项区域。
|
||||
3. 本轮聊天输入框、聊天建议项消失。
|
||||
4. 当前故事内容回到普通故事展示逻辑。
|
||||
|
||||
退出聊天不触发额外确认弹窗。
|
||||
|
||||
---
|
||||
|
||||
## 6. UI 设计要求
|
||||
|
||||
## 6.1 保持不变的部分
|
||||
|
||||
以下 UI 外壳保持当前实现:
|
||||
|
||||
1. 主冒险面板整体框架。
|
||||
2. 对话消息区所在位置。
|
||||
3. 底部操作区的整体层级。
|
||||
4. 队伍按钮、背包按钮的视觉风格。
|
||||
|
||||
## 6.2 必须变化的部分
|
||||
|
||||
### 消息区
|
||||
|
||||
1. 聊天态下,消息区按时间顺序展示:
|
||||
- 玩家消息
|
||||
- NPC 消息
|
||||
- 系统关系变化消息
|
||||
2. 系统关系变化消息必须和普通消息共用同一消息流容器。
|
||||
3. 系统关系变化消息视觉上应与玩家/NPC 气泡有明确区分。
|
||||
|
||||
### 底部按钮区
|
||||
|
||||
1. 左侧仍保留队伍、背包按钮。
|
||||
2. 右侧在聊天态下展示“退出聊天”按钮。
|
||||
3. “退出聊天”按钮必须位于该行最右侧。
|
||||
4. 非聊天态下,该位置仍保持原有刷新选项按钮逻辑。
|
||||
|
||||
### 选项区
|
||||
|
||||
1. 聊天态下只展示 `3` 个续聊选项。
|
||||
2. 聊天态下不展示普通剧情选项附带的说明文案、目标提示、影响摘要。
|
||||
3. 聊天态下选项文案本身就是“下一句怎么接”。
|
||||
|
||||
### 输入区
|
||||
|
||||
1. 聊天态下在 3 个建议项下方展示输入框。
|
||||
2. 输入框右侧固定展示发送按钮。
|
||||
3. 输入框在请求进行中禁用。
|
||||
4. 点击发送或回车提交时,进入下一轮。
|
||||
|
||||
## 6.3 移动端要求
|
||||
|
||||
1. 移动端优先保证输入框与发送按钮可点击。
|
||||
2. 三个建议选项必须保持纵向堆叠,不做横向卡片排布。
|
||||
3. “退出聊天”按钮在小屏下仍需完整可见,不允许被背包按钮挤出。
|
||||
4. 输入框与发送按钮在窄屏下优先保证输入框宽度,其次压缩按钮内边距。
|
||||
|
||||
---
|
||||
|
||||
## 7. 前后端职责边界
|
||||
|
||||
遵循“前端只负责表现,逻辑和数据放后端”原则。
|
||||
|
||||
## 7.1 前端职责
|
||||
|
||||
前端只负责:
|
||||
|
||||
1. 进入/退出聊天态的 UI 切换。
|
||||
2. 渲染当前消息队列。
|
||||
3. 发送玩家本轮输入。
|
||||
4. 接收流式事件并实时更新 NPC 当前回复文本。
|
||||
5. 渲染系统关系变化消息。
|
||||
6. 渲染下一轮 `3` 个建议项与自定义输入框。
|
||||
|
||||
前端不负责:
|
||||
|
||||
1. 生成 NPC 回复文本。
|
||||
2. 生成建议续聊选项。
|
||||
3. 计算关系增减。
|
||||
4. 解析剧情逻辑分支。
|
||||
|
||||
## 7.2 后端职责
|
||||
|
||||
后端负责:
|
||||
|
||||
1. 接收 NPC 单轮聊天请求。
|
||||
2. 结合当前世界、角色、NPC 状态、历史消息构建 prompt。
|
||||
3. 流式生成 NPC 回复。
|
||||
4. 解析回复内容。
|
||||
5. 生成下一轮 3 个建议续聊选项。
|
||||
6. 计算并返回本轮关系增减。
|
||||
7. 通过 SSE 向前端发送流式事件与最终结果。
|
||||
|
||||
---
|
||||
|
||||
## 8. 数据结构要求
|
||||
|
||||
## 8.1 前端故事态扩展
|
||||
|
||||
`StoryMoment` 需要具备聊天态附加状态:
|
||||
|
||||
```ts
|
||||
type StoryNpcChatState = {
|
||||
npcId: string;
|
||||
npcName: string;
|
||||
turnCount: number;
|
||||
customInputPlaceholder?: string;
|
||||
};
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
1. 仅当当前故事处于 NPC 聊天模式时写入。
|
||||
2. `turnCount` 表示已完成的轮数。
|
||||
3. `npcId` 用于保证只续写当前聊天对象。
|
||||
|
||||
## 8.2 聊天消息结构
|
||||
|
||||
消息队列需要支持系统消息:
|
||||
|
||||
```ts
|
||||
type StoryDialogueTurn = {
|
||||
speaker: 'player' | 'npc' | 'companion' | 'system';
|
||||
speakerName?: string;
|
||||
text: string;
|
||||
affinityDelta?: number;
|
||||
};
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
1. `system` 只用于关系变化、系统反馈类消息。
|
||||
2. `affinityDelta` 仅在关系变化消息中写入。
|
||||
|
||||
## 8.3 单轮接口契约
|
||||
|
||||
请求:
|
||||
|
||||
```ts
|
||||
type NpcChatTurnRequest = {
|
||||
worldType: WorldType;
|
||||
player: Character;
|
||||
encounter: Encounter;
|
||||
history: StoryMoment[];
|
||||
dialogue: StoryDialogueTurn[];
|
||||
playerMessage: string;
|
||||
npcState: {
|
||||
affinity: number;
|
||||
chattedCount: number;
|
||||
recruited: boolean;
|
||||
};
|
||||
context: StoryGenerationContext;
|
||||
};
|
||||
```
|
||||
|
||||
返回完成事件载荷:
|
||||
|
||||
```ts
|
||||
type NpcChatTurnResult = {
|
||||
npcReply: string;
|
||||
affinityDelta: number;
|
||||
affinityText: string;
|
||||
suggestions: string[];
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 流式协议
|
||||
|
||||
本次统一使用 SSE。
|
||||
|
||||
## 9.1 事件类型
|
||||
|
||||
后端至少输出以下事件:
|
||||
|
||||
1. `reply_delta`
|
||||
2. `complete`
|
||||
3. `error`
|
||||
|
||||
## 9.2 事件含义
|
||||
|
||||
### `reply_delta`
|
||||
|
||||
用途:
|
||||
|
||||
1. 逐步推送 NPC 当前回复文本增量。
|
||||
2. 前端收到后立即更新消息流中“当前 NPC 气泡”的文本。
|
||||
|
||||
### `complete`
|
||||
|
||||
用途:
|
||||
|
||||
1. 标记本轮流式输出结束。
|
||||
2. 一次性返回最终结果对象:
|
||||
- `npcReply`
|
||||
- `affinityDelta`
|
||||
- `affinityText`
|
||||
- `suggestions`
|
||||
|
||||
### `error`
|
||||
|
||||
用途:
|
||||
|
||||
1. 标记本轮失败。
|
||||
2. 前端停止流式态并回退到可继续输入的状态。
|
||||
|
||||
## 9.3 前端解析规则
|
||||
|
||||
1. 当收到第一个 `reply_delta` 时,若消息流末尾还没有 NPC 临时消息,前端先插入一条空的 NPC 消息。
|
||||
2. 每次收到 `reply_delta`,替换该临时 NPC 消息文本。
|
||||
3. 收到 `complete` 后,把临时 NPC 消息固化为最终文本。
|
||||
4. 如果 `affinityDelta !== 0`,在 NPC 消息后追加一条系统关系消息。
|
||||
5. 之后再刷新下一轮建议选项。
|
||||
|
||||
---
|
||||
|
||||
## 10. 关系变化显示规则
|
||||
|
||||
## 10.1 显示位置
|
||||
|
||||
关系变化必须作为一条独立消息插入消息队列,位置在本轮 NPC 回复之后、下一轮建议项之前。
|
||||
|
||||
## 10.2 文案规则
|
||||
|
||||
1. 有增长时显示正向文案,例如:`关系升温 好感 +3`
|
||||
2. 有下降时显示负向文案,例如:`关系转冷 好感 -2`
|
||||
3. 无变化时不强制插入系统消息。
|
||||
|
||||
## 10.3 视觉规则
|
||||
|
||||
1. 关系变化消息居中显示。
|
||||
2. 关系变化消息使用不同于普通对话气泡的视觉样式。
|
||||
3. 正向变化可使用更暖色的边框/底色。
|
||||
4. 负向变化与中性反馈使用系统消息样式。
|
||||
|
||||
---
|
||||
|
||||
## 11. 交互流程
|
||||
|
||||
## 11.1 进入流程
|
||||
|
||||
```text
|
||||
玩家点击 npc_chat
|
||||
-> 系统进入聊天态
|
||||
-> 当前故事切换为聊天消息模式
|
||||
-> 展示本轮可选接话
|
||||
-> 展示自定义输入框
|
||||
```
|
||||
|
||||
## 11.2 单轮流程
|
||||
|
||||
```text
|
||||
玩家点击建议项或提交输入
|
||||
-> 玩家消息立即入队
|
||||
-> 前端发起 /runtime/chat/npc/turn/stream
|
||||
-> 后端流式返回 NPC 回复
|
||||
-> 前端边接收边渲染 NPC 当前回复
|
||||
-> complete 返回最终结果
|
||||
-> 前端插入关系变化系统消息
|
||||
-> 前端刷新下一轮 3 个建议项
|
||||
-> 等待下一轮输入
|
||||
```
|
||||
|
||||
## 11.3 退出流程
|
||||
|
||||
```text
|
||||
玩家点击退出聊天
|
||||
-> 清理当前聊天态标记
|
||||
-> 当前故事回到普通冒险展示
|
||||
-> 恢复普通选项区
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 状态更新规则
|
||||
|
||||
每完成一轮聊天,系统必须更新:
|
||||
|
||||
1. `npcState.affinity`
|
||||
2. `npcState.chattedCount`
|
||||
3. `npcState.relationState`
|
||||
4. `npcState.stanceProfile`
|
||||
5. `storyHistory`
|
||||
6. `currentStory.dialogue`
|
||||
7. `currentStory.npcChatState.turnCount`
|
||||
|
||||
要求:
|
||||
|
||||
1. 当前轮玩家输入和 NPC 回复都要进入故事历史。
|
||||
2. 关系变化消息属于展示型系统消息,可进入当前对话队列,但不要求作为独立剧情行动历史参与模型推理。
|
||||
|
||||
---
|
||||
|
||||
## 13. 异常与兜底
|
||||
|
||||
## 13.1 流式失败
|
||||
|
||||
如果流式失败:
|
||||
|
||||
1. 当前轮玩家消息保留。
|
||||
2. 不保留半截乱码式 NPC 文本。
|
||||
3. 前端恢复可继续输入状态。
|
||||
4. 使用后端或前端兜底建议项,保证仍有 3 个建议续聊选项可选。
|
||||
|
||||
## 13.2 建议项不足
|
||||
|
||||
如果后端返回建议项不足 `3` 条:
|
||||
|
||||
1. 由后端优先补齐兜底话术。
|
||||
2. 前端只展示最多 `3` 条。
|
||||
|
||||
## 13.3 空输入
|
||||
|
||||
空输入、纯空格输入不发请求。
|
||||
|
||||
---
|
||||
|
||||
## 14. 代码落点
|
||||
|
||||
本次迭代应优先落在现有链路:
|
||||
|
||||
### 前端
|
||||
|
||||
1. `src/hooks/story/npcEncounterActions.ts`
|
||||
2. `src/hooks/story/useStoryInteractionCoordinator.ts`
|
||||
3. `src/hooks/story/useStoryFlowCoordinator.ts`
|
||||
4. `src/hooks/useStoryGeneration.ts`
|
||||
5. `src/services/aiService.ts`
|
||||
6. `src/components/AdventurePanel.tsx`
|
||||
7. `src/components/GameShell.tsx`
|
||||
|
||||
### 共享契约
|
||||
|
||||
1. `packages/shared/src/contracts/story.ts`
|
||||
|
||||
### 后端
|
||||
|
||||
1. `server-node/src/routes/runtimeRoutes.ts`
|
||||
2. `server-node/src/services/chatService.ts`
|
||||
3. `server-node/src/modules/ai/chatPromptBuilders.ts`
|
||||
4. `server-node/src/modules/ai/chatOrchestrator.ts`
|
||||
|
||||
要求:
|
||||
|
||||
1. 复用现有故事流、运行时接口和主面板。
|
||||
2. 不另起新页面、新 store、新聊天系统。
|
||||
|
||||
---
|
||||
|
||||
## 15. 验收标准
|
||||
|
||||
以下全部满足才算通过:
|
||||
|
||||
1. 点击 `npc_chat` 后,面板进入聊天态而不是跳去新页面。
|
||||
2. 当前消息区能连续展示玩家消息、NPC 消息、系统关系消息。
|
||||
3. 每轮结束后稳定出现 `3` 个建议项。
|
||||
4. 每轮结束后稳定出现 `1` 个自定义输入框。
|
||||
5. 点击建议项可继续下一轮。
|
||||
6. 输入自定义文本后点击发送或回车可继续下一轮。
|
||||
7. NPC 回复支持流式逐字/逐段显示。
|
||||
8. 好感度变化会以消息形式插入聊天队列。
|
||||
9. 背包按钮所在行最右侧可见“退出聊天”按钮。
|
||||
10. 点击“退出聊天”后恢复普通冒险态。
|
||||
11. 聊天态下不显示普通剧情选项的说明、目标提示、影响摘要。
|
||||
12. 移动端下输入框、发送按钮、退出按钮均可正常操作。
|
||||
|
||||
---
|
||||
|
||||
## 16. 后续可扩展方向
|
||||
|
||||
本次上线后,再考虑后续迭代:
|
||||
|
||||
1. 更精细的关系变化公式。
|
||||
2. 基于 NPC 性格的建议续聊风格差异。
|
||||
3. 聊天摘要沉淀到长期记忆。
|
||||
4. 聊天中触发支线、任务、物品、邀约等事件。
|
||||
5. 退出聊天后保留“最近一次聊天摘要”。
|
||||
Reference in New Issue
Block a user