Merge branch 'master' into stdb

This commit is contained in:
2026-04-20 05:40:04 +00:00
277 changed files with 35204 additions and 21510 deletions

View File

@@ -15,6 +15,8 @@
- [FUNCTION_RUNTIME_FULL_TEST_AUDIT_2026-04-16.md](./FUNCTION_RUNTIME_FULL_TEST_AUDIT_2026-04-16.md)Function 运行时完整测试、服务端承接验证与当前门禁缺口。
- [ITEM_AND_BUILD_PRD_AUDIT_2026-04-05.md](./ITEM_AND_BUILD_PRD_AUDIT_2026-04-05.md):物品生成与 Build 标签系统对 PRD 的落地情况。
- [CUSTOM_WORLD_CREATOR_TOOL_AUDIT_2026-04-08.md](./CUSTOM_WORLD_CREATOR_TOOL_AUDIT_2026-04-08.md):自定义世界创作工具当前问题、体验断层和优化优先级审计。
- [engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md](./engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md):对 `2026-04-19` 工程清理审计的当前仓库复核,区分已完成项、仍存边界问题和新的热点迁移。
- [engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md](./engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md):未引用垃圾、旧入口残留、前后端双份真相与后端迁移项的专项审计。
## 推荐使用方式

View File

@@ -124,7 +124,7 @@
工具链:
- `scripts/dev-server/localApiPlugins.ts``1504`
- `scripts/dev-server/*.ts`:已于 `2026-04-19` 删除,旧 Vite 本地 API 链路不再保留实现代码
#### 影响
@@ -145,27 +145,27 @@
---
### P1-2继续收口 editor / assets 工具链边界
### P1-2继续收口 editor / assets 工具链边界(旧链路已删除)
这项的重要性正在上升。
#### 证据
- `docs/technical/EDITOR_ASSET_API_MIGRATION_2026-04-08.md` 已说明 editor/assets API 已经迁到 `server-node`,方向是对的。
- 但当前仓库里仍保留一个 `1504` 行的 `scripts/dev-server/localApiPlugins.ts`
- `scripts/dev-server/*.ts` 旧 Vite 本地 API 实现代码已于 `2026-04-19` 删除,仓库里不再保留并行实现
- 目录 `temp-build-goal-check/` 当前包含 `15099` 个文件,已经开始干扰 lint 和本地开发信号。
- 相关日志里还出现了大量指向 `temp-build-goal-check` 的页面 reload 与 `ENOENT` 噪音。
#### 影响
- 旧工具链虽然“不再是主入口”,但它们还在继续占据认知空间和仓库噪音预算
- 新旧 editor/assets 路径长期并存,会导致维护者很难快速判断哪条链才是正式路径
- editor/assets 正式入口已经收口到 `server-node`,这部分双链路问题已解除
- 当前更大的噪音来源已经转移到临时构建目录、检查目录和历史日志残留
#### 当前建议
1. 明确把旧 Vite 插件链标记为迁移参考,避免继续被误用
1. 保持 `scripts/dev-server/README.md` 作为迁移结果标记,不要恢复旧 Vite `/api/*` 本地插件链
2. 将临时构建目录、检查目录、导出目录统一移出主工程扫描面。
3. 对 editor/assets 正式入口补一份“唯一推荐入口”文档或 README 更新,减少后续回流。
3. 继续以 `server-node/src/modules/editor/**``server-node/src/modules/assets/**``src/editor/shared/editorApiClient.ts` 作为唯一推荐入口,减少后续回流。
---

View File

@@ -0,0 +1,608 @@
# 工程清理与后端边界审计2026-04-19
更新时间:`2026-04-20`
## 0.1 执行回填2026-04-19
本文审计项 `3.2``4.4` 已于 `2026-04-19` 当日完成首轮处置:
1. 已删除 `scripts/dev-server/localApiPlugins.ts`
2. 已删除 `scripts/dev-server/characterAssetStudioPlugins.ts`
3. 已删除 `scripts/dev-server/qwenSpriteSheetToolPlugins.ts`
4. `scripts/dev-server/` 目录仅保留迁移说明,不再保留旧 Vite 本地 API 实现代码
5. 当前正式入口统一为 `scripts/dev-node.mjs + vite proxy + server-node/src/modules/**`
本文其余段落保留为本次审计时的原始问题快照,用于解释为什么要做这轮删除。
## 0.2 执行回填2026-04-19仓库噪音产物
本文审计项 `3.1` 已于 `2026-04-19` 当日完成首轮处置:
1. 已从版本库删除以下根目录历史扫描/截图产物:
- `npc-editor-dom.html`
- `npc-editor-shot.png`
- `temp-write-check.txt`
- `tmp_character_presets_scan.txt`
- `tmp_jsx_text_scan.txt`
- `tmp_runtime_text_scan.txt`
- `tmp_text_candidates.txt`
- `tmp_text_candidates_refined.txt`
- `tmp_visible_props_scan.txt`
- `tmp_volc_seedance_doc.html`
2. 已从版本库删除 `scripts/__pycache__/generate-build-tag-similarity.cpython-313.pyc`
3. 已清理本地工作区中的 `.codex-*.log``.preview.*``npc-editor-console.log``temp-build-goal-check/`,清理前对应体量约为:
- 根目录噪音文件 `60` 个,约 `49.94 MB`
- `temp-build-goal-check/``15620` 个条目,约 `158.85 MB`
4. 已补齐 `.gitignore``.prettierignore``.eslintrc.cjs` 的忽略口径,显式覆盖 `tmp_*``tmp/``npc-editor-*``temp-write-check.txt``temp-build-goal-check/``__pycache__/`
5. `scripts/dev-server/localApiPlugins.ts` 之外的后端边界收口项不在本轮噪音清理范围内,后续继续按本文第二至第四阶段推进。
## 0.3 执行回填2026-04-19运行时边界第一轮收口
本文审计项 `4.1``5.1` 已于 `2026-04-19` 当日完成一轮工程收口:
1. `RuntimeStoryOptionView` 现在由后端直接附带 `interaction` 元数据。
2. `server-node/src/modules/story/runtimeSession.ts` 已成为 runtime option interaction 的唯一构建位置。
3. `src/services/runtimeStoryService.ts` 不再根据 `currentEncounter + functionId` 在前端本地重建一份 interaction 映射。
4. `/api/custom-world/scene-image` 已补齐服务端 prompt 兜底组装能力,允许前端只提交 `profile + landmark + userPrompt` 上下文。
5. `src/services/aiService.ts` 的场景图 SDK 已改为直接调用后端接口,不再为了该链路动态加载 `src/services/ai.ts`
## 0.4 执行回填2026-04-19自定义世界后端边界第二轮收口
本文审计项 `5.2` 与“第三阶段第 4 条:清理 `server-node -> src/**` 的反向依赖”已于 `2026-04-19` 当日完成第二轮工程收口:
1. `server-node/src/modules/custom-world/` 已新增服务端自持 runtime 模块,承接:
- `creator intent` 归一化
- `anchorPack / lockState` 推导
- custom world framework/profile compile 与 normalize
2. `server-node/src/modules/ai/customWorldOrchestrator.ts``server-node/src/services/customWorldAgentFoundationDraftService.ts` 已不再运行时依赖:
- `src/services/customWorld.js`
- `src/services/customWorldBuilder.js`
- `src/services/customWorldCreatorIntent.js`
- `src/types.js`
3. `server-node/src/prompts/customWorldPrompts.ts` 已成为后端自持的 custom world prompt source`scene image``foundation draft` 相关 builder 不再从前端 `src/prompts/customWorldPrompts.ts` 反向 import。
4. 本轮只迁移 prompt source 位置,没有改动任何 custom world 提示词正文,也没有改动功能需求。
## 0.5 执行回填2026-04-20NPC 待接委托正式接取收口)
本文审计项 `5.3` 已于 `2026-04-20` 完成一轮补充收口:
1. `src/hooks/story/npcEncounterActions.ts` 中“聊天里的待接委托正式接取”已不再由前端本地直接写入:
- `quests`
- `runtimeStats.questsAccepted`
- `npcChatState.pendingQuestOffer`
2. `server-node/src/modules/quest/questStoryActionService.ts` 现在会优先读取服务端快照里已保存的 `pendingQuestOffer.quest`,按当前聊天态中已经展示给玩家的那份委托完成正式接取。
3. `server-node/src/modules/story/storyActionService.ts` 已补齐待接委托接取后的聊天态投影:
- 保留 NPC 对话展示模式
- 清空 `pendingQuestOffer`
- 回到既有的三条自由追问建议
4. 本轮没有新增任何 runtime functionId也没有改动任务生成提示词或任务需求只是把既有“接任务”正式结算权收回到后端。
## 0.6 执行回填2026-04-20NPC 聊天任务草案与浏览器 LLM fallback 收口)
本文审计项 `5.1``5.3` 已于 `2026-04-20` 完成一轮补充收口:
1. `server-node/src/modules/ai/chatOrchestrator.ts` 现在会基于 `NPC chat turn` 的运行时上下文,在后端判断是否触发 `pendingQuestOffer`,并把 quest draft 与引导文案一并回填给前端。
2. `src/hooks/story/npcEncounterActions.ts` 不再在 NPC 单轮聊天完成后本地调用 `generateQuestForNpcEncounter(...)` 再决定是否挂出待接委托。
3. `src/services/questDirector.ts` 浏览器端在后端失败时不再退回本地 LLM 生成 quest draft而是直接走 deterministic fallback compile。
4. `src/services/runtimeItemAiDirector.ts` 浏览器端在后端失败时不再退回本地 LLM 生成 runtime item intent而是直接返回 deterministic fallback intents。
5. 本轮仍未改动任何业务提示词正文,也没有改动 quest / runtime item 的需求能力面,只是继续清理浏览器里的正式 AI orchestration 残留。
## 0. 审计目标
本次审计只回答四类问题:
1. 项目里哪些内容已经是高置信度的垃圾、临时产物或无入口代码。
2. 哪些实现属于双份真相、重复映射或旧链路残留。
3. 哪些前端代码仍然承担了应迁移到 Express 后端的职责。
4. 哪些文件已经大到会持续拖累迭代效率,需要优先拆分。
---
## 1. 结论先行
当前仓库的主要问题不是“有一些小工具没人用”,而是四类结构性噪音同时存在:
1. **仓库噪音产物仍然很多。**
根目录残留了大量 `.codex-*.log``tmp_*`、旧截图/HTML以及 `temp-build-goal-check/` 这类大体量检查产物,已经不是单个文件层面的脏数据,而是在持续污染工程视野。
2. **旧入口和新入口并存,形成了明显的冗余链路。**
`scripts/dev-server/localApiPlugins.ts` 已经退出当前正式开发入口,但仍保留了 LLM proxy、JSON 写盘、资产发布等整套旧 Vite 本地 API 机制。
3. **前端仍然承载了过多运行时规则与 AI 编排。**
`src/services/ai.ts``src/services/customWorld.ts``src/hooks/story/npcEncounterActions.ts` 这类文件,仍在浏览器里承担 prompt 组装、规则判定、奖励结算、剧情推进等职责。
4. **后端边界还没有真正闭合。**
`server-node` 虽然已经承接了大量路由和运行时动作,但仍直接 import `src/services/customWorld*.ts``src/types.ts`,说明后端领域层还没有完全从前端目录中独立出来。
一句话判断:
**这轮优先级不该再是继续堆功能,而是先清仓库噪音与无入口孤岛,再把前后端双份真相收口,最后拆新的巨型热点文件。**
---
## 2. 本次审计方法与口径
### 2.1 方法
本次审计结合了四类证据:
1. 文档基线:
- `docs/audits/engineering/CURRENT_ENGINEERING_OPTIMIZATION_PRIORITIES_2026-04-10.md`
- `docs/planning/EXPRESS_BACKEND_REFACTOR_PLAN_2026-04-08.md`
- `docs/planning/EXPRESS_BACKEND_PARALLEL_WORKSTREAM_PLAN_2026-04-08.md`
- `scripts/dev-server/README.md`
2. 当前入口核对:
- `src/main.tsx`
- `src/routing/appRoutes.tsx`
- `src/App.tsx`
- `package.json`
- `server-node/package.json`
3. 静态依赖扫描:
-`src/``server-node/src/``packages/shared/src/``scripts/``650` 个 TS/JS 文件做本地依赖图扫描。
4. 定向 grep
- 核对旧 dev 插件入口、后端跨层 import、localStorage 使用、运行时快照双写、重复映射代码。
### 2.2 口径说明
为避免误判,本次审计明确排除了两类对象:
1. **包脚本入口**:例如 `scripts/build-gate.mjs``scripts/check-encoding.mjs``server-node/build.mjs` 这类由 `package.json` 直接执行的脚本,不因“无 import”而判为垃圾。
2. **字符串路径消费的资源**:例如 `src/data/itemOverrides.json``src/data/monsterOverrides.json` 会被校验脚本和 editor route 以文件路径读取,不按“无 import”处理。
另外,当前工作区存在未提交改动,因此本次结论以**已纳入当前主链且能确认未接线/重复/越界的内容**为主,不把明显的当日 WIP 文件计入垃圾结论。
---
## 3. 高置信度垃圾、临时产物与无入口代码
## 3.1 仓库噪音产物已经到了需要集中清理的程度
### 证据
| 项目 | 当前证据 | 判断 |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| 根目录日志/临时文件 | 根目录命中 `60``.codex-*.log``.preview.*``tmp_*``npc-editor-*``temp-write-check.txt`,合计约 `52.36 MB` | 已经不是偶发临时文件,而是长期堆积的开发残留 |
| `temp-build-goal-check/` | 当前包含 `15099` 个文件,合计约 `166.56 MB` | 大体量检查产物,应该移出主工程视野 |
| Python 缓存 | 当前存在 `scripts/__pycache__/` | 纯缓存产物,不应长期留在仓库工作区中 |
### 影响
1. 根目录信噪比明显下降,真实工程文件被大量一次性产物淹没。
2. `temp-build-goal-check/` 虽然已被 `.gitignore``vite.config.ts` 的 watch 忽略模式覆盖,但 `.eslintrc.cjs``ignorePatterns` 里没有对应口径,仍存在工具口径不一致问题。
3. 这类目录会持续干扰检索、review、lint 判断和本地扫描速度。
### 建议
1. 把根目录临时日志、扫描 txt/html、旧截图统一迁到单独的 `tmp/` 或本地缓存目录,默认不留在仓库根目录。
2.`temp-build-goal-check/` 改成真正的外置检查产物目录,或者在 lint/脚本口径上一起排除。
3. 清理 `scripts/__pycache__/`,并统一补上 Python 缓存忽略规则。
---
## 3.2 旧 Vite 本地 API 插件链已经退出主入口,但仍保留整套旧实现
### 证据
1. `scripts/dev-server/README.md` 已明确写明:`scripts/dev-server/**` 不再是当前开发入口,只保留为迁移参考。
2. `scripts/dev-server/localApiPlugins.ts` 当前仍有 `1664` 行。
3. 仓库内已经找不到 `localApiPlugins` 的实际代码入口引用,当前只剩文档引用。
4. 该文件内部仍然同时定义和拼装:
- `createLlmProxyPlugin`
- `createJsonFileEditorPlugin`
- `createCustomWorldSceneImagePlugin`
- `createCharacterVisualPublishPlugin`
- `createCharacterAnimationPublishPlugin`
- `createCharacterAssetStudioPlugins`
- `createQwenSpriteSheetToolPlugins`
### 判断
这不是“一个小工具暂时没用”,而是**整条旧 editor/assets 本地 API 链路仍然完整保留在仓库里**。它在工程上已经属于高置信度的历史残留。
### 建议
1. 如果只保留迁移证据,建议把 `scripts/dev-server/localApiPlugins.ts` 和相关说明迁到 `docs/reference/` 或单独的 `archive/` 目录。
2. 如果确实还要保留参考代码,至少要在文件顶部加更强的“只读参考、禁止继续扩展”标识,并从主工程扫描面上进一步隔离。
3. 不建议继续在这条旧链路里新增任何 `/api/*` 能力。
---
## 3.3 当前存在一批“无运行时入口”或“仅测试引用”的孤岛模块
### 高置信度无入口/仅测试引用清单
| 模块 | 证据 | 判断 |
| --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| `src/components/GameShell.tsx` | 文件体量 `761` 行;当前 `src/App.tsx` 只接入 `components/game-shell/GameShellRuntime.tsx`;仓库内无其它 import | 旧版壳层残留 |
| `src/components/custom-world-home/CustomWorldCreationHub.tsx` | 仅被 `CustomWorldCreationHub.test.tsx``CustomWorldCreationHub.interaction.test.tsx` 引用;`src/routing/appRoutes.tsx` 只有 `game``qwen-sprite-tool` 两条路由 | 已做出 UI但未进入正式入口 |
| `src/components/custom-world-home/CustomWorldCreationLauncherModal.tsx` | 当前无运行时引用 | 同属未接线入口壳层 |
| `src/components/custom-world-agent/*``9` 个子模块 | 当前合计约 `826` 行;典型文件包括 `CustomWorldAgentLauncherModal.tsx``CustomWorldAgentDraftDrawer.tsx``CustomWorldAgentLockBar.tsx``CustomWorldAgentQuickActions.tsx``CustomWorldAgentSummaryPanel.tsx`;部分文件完全无引用,部分仅被测试引用 | 处于“做了一部分 UI但未进入主链”的孤岛状态 |
| `src/hooks/story/storyBootstrap.ts` | `250` 行,仓库内只定义不消费 | 已被新流程替代的可能性高 |
| `src/hooks/useEquipmentFlow.ts` / `useForgeFlow.ts` / `useInventoryFlow.ts` | 合计约 `393` 行,当前无运行时引用 | 旧流转层残留 |
| `src/editor/shared/cloneValue.ts` / `EditorEmptyState.tsx` / `EditorSelectionCard.tsx` / `useJsonSave.ts` | 当前无运行时引用 | editor 旧共享层碎片 |
| `src/services/customWorldPresentation.stub.ts` | 当前无引用,且文件本身就是 stub | 高置信度占位残留 |
| `src/services/typewriter.ts` | 当前无引用,仅提供一个 `getTypewriterDelay` | 已被其它链路内联实现替代 |
| `src/data/buildTagSimilarity.generated.ts` | 当前 `823` 行,仅能被生成脚本自身检索到,没有消费方 | 生成产物未接入任何业务链路 |
| `src/data/customWorldCharacterLoadout.stub.ts` | 当前无引用,且实现只返回空数组 | 占位残留 |
| `src/components/DeveloperTeamModal.tsx` / `src/components/LazySkillEffectPreview.tsx` | 当前无运行时引用 | 小体量零散孤岛 |
### 判断
这批文件不一定都应该“立刻删除”,但它们已经满足两个至少其一:
1. 当前正式入口完全不消费。
2. 只剩测试在消费,本体没有真实运行时位置。
所以它们至少都应该进入以下三选一处理:
1. 立即归档/删除。
2. 明确接回正式入口。
3. 改名或迁目录,标明“实验稿/参考稿/未接线”身份。
### 特别提醒
`src/components/custom-world-home/``src/components/custom-world-agent/` 这两组文件里,存在**已经有一定 UI 完成度、但没有进入真实路由/流程**的情况。
这类文件最危险的点不是体量,而是会让后来者误以为“这块功能已经在主链上”。
---
## 4. 冗余实现与双份真相
## 4.1 Story option interaction 映射在前后端各维护了一份
### 证据
1. 前端 `src/services/runtimeStoryService.ts``buildRuntimeOptionInteraction` 维护了 `npcActionMap``treasureActionMap`
2. 后端 `server-node/src/modules/story/storyActionService.ts``buildStoryOptionInteraction` 维护了几乎同构的一份 `npcActionMap``treasureActionMap`
### 风险
1. 任何一个 functionId 增删改,前后端都要同步。
2. 一边先改、一边漏改时,表现层和运行时层会出现静默漂移。
### 建议
把 interaction/view model 映射收口到后端,前端只消费后端返回的结构,不再根据 `functionId` 本地重建一遍交互语义。
---
## 4.2 浏览历史已经有后端接口,但前端仍维护本地真相与迁移状态
### 证据
1. `src/components/game-shell/PreGameSelectionFlow.tsx` 中,`appendBrowseHistoryEntry` 先调用 `writePlatformBrowseHistory` 写本地,再调用 `upsertProfileBrowseHistory` 写后端。
2. 同文件启动阶段又会先读 `readPlatformBrowseHistory`,再根据 `hasPendingPlatformBrowseHistoryMigration` 把本地历史同步回后端。
3. 后端 `server-node/src/routes/runtimeRoutes.ts` 已经提供了 `/profile/browse-history` 路由,而前端 `src/services/storageService.ts` 也已有对应 API SDK。
### 判断
当前浏览历史并不是单纯的“本地缓存”,而是**本地存储 + 远端持久化 + 迁移标记**三套状态并存。
### 建议
1. 后端结果作为唯一真相源。
2. 前端如果要保留缓存,只保留一个明确的 cache wrapper不再把它做成独立状态系统。
3. `markPlatformBrowseHistoryMigrated` 这种迁移标记应尽量在后端一次性收口,而不是长期停留在正式前端逻辑里。
---
## 4.3 运行时快照依然由前端先落本地,再与后端会话互相回填
### 证据
1. `src/hooks/story/runtimeStoryCoordinator.ts` 在读状态和提交 action 前都会先调用 `putSaveSnapshot`
2. 同文件以及 `src/services/runtimeStoryService.ts` 又会在响应后多次 `rehydrateSavedSnapshot`
3. 这意味着浏览器仍然在“后端 action 之前”先写一份自己的快照解释。
### 判断
这条链路说明当前运行时还处在**前端快照解释权没有完全退出**的过渡状态。
### 建议
1. 前端逐步退化为 view model 消费层。
2. 运行时快照、版本迁移、恢复解释权继续往后端收口。
3. 前端保留最小必要的离线展示缓存,但不再成为正式运行时状态真相来源。
---
## 4.4 旧 Vite 本地 API 与正式 Express 路由仍然形成重复能力面
### 证据
1. `scripts/dev-server/localApiPlugins.ts` 里仍有 JSON 编辑、场景图生成、角色视觉发布、角色动作发布等插件。
2. 当前正式路径已经迁到:
- `server-node/src/modules/editor/**`
- `server-node/src/modules/assets/**`
3. `scripts/dev-server/README.md` 已明确说明旧链路只保留为迁移参考。
### 判断
这属于典型的**旧能力未删除,新能力已落地,双链路长期并存**。
### 建议
尽快把旧 Vite 本地 API 参考实现移出主工程扫描面,避免后续继续被误用或被误认为正式入口。
---
## 5. 需要迁移到后端的代码
## 5.1 `src/services/ai.ts` 仍然承担了过多正式运行时职责
### 当前职责
`src/services/ai.ts` 当前约 `2632` 行,仍然同时承担:
1. function 可用性与 option 构造相关逻辑。
2. NPC 对话 / 招募 prompt 构造。
3. 自定义世界生成 prompt 与 JSON 修复请求。
4. 直接调用 `requestPlainTextCompletion` / `streamPlainTextCompletion`
5. 浏览器内 fallback 与响应解析。
### 判断
这不是单纯的“前端请求 SDK”而是**前端仍在承担正式运行时 AI orchestration**。
### 建议迁移方向
1. prompt 组装、模型调用、超时重试、JSON repair 继续收口到 `server-node/src/modules/ai/**`
2. 前端只保留轻量 SDK 和展示态拼装。
3. fallback 如果必须保留,也应明确区分“开发兜底”与“正式运行时”。
---
## 5.2 `src/services/customWorld.ts` 仍然是前端侧的大型规则中心
### 当前职责
`src/services/customWorld.ts` 当前约 `2413` 行,仍然承担:
1. 世界框架与角色/地标 outline 归一化。
2. 世界属性 schema 生成。
3. `ownedSettingLayers` 归一化。
4. 最终世界 profile 校验。
5. fallback story graph/theme pack 生成。
### 当前越界证据
后端目前直接从以下文件 import 这些能力:
1. `server-node/src/modules/ai/customWorldOrchestrator.ts`
2. `server-node/src/services/customWorldAgentFoundationDraftService.ts`
它们仍直接引用:
1. `src/services/customWorld.js`
2. `src/services/customWorldBuilder.js`
3. `src/services/customWorldCreatorIntent.js`
4. `src/types.js`
### 判断
这说明自定义世界的核心领域规则仍然以**前端目录为事实源**,后端只是在反向复用。
### 建议迁移方向
1. `types/schema/contracts` 抽到 `packages/shared`
2. 规则编译、校验、fallback 与 AI 编排迁到 `server-node`
3. 前端只保留编辑器表现层和字段草稿态。
---
## 5.3 `src/hooks/story/npcEncounterActions.ts` 仍在浏览器里做任务、奖励、战斗与招募结算
### 当前职责
`src/hooks/story/npcEncounterActions.ts` 当前约 `1623` 行,仍然直接编排:
1. `quest_accept` / `quest_turn_in`
2. 招募、切磋、离开、帮助奖励
3. 掉落/背包写入
4. HP / MP / cooldown 奖励变化
5. NPC 亲和度变化
6. 战斗场景切换与遭遇状态推进
### 判断
这条链已经明显超出“前端表现协调层”的边界,仍属于**正式运行时规则在前端执行**。
### 建议迁移方向
1. quest 信号推进 -> `server-node/src/modules/quest/**`
2. 奖励与背包变更 -> `server-node/src/modules/inventory/**`
3. 招募/关系变化 -> `server-node/src/modules/npc/**`
4. 战斗结算 -> `server-node/src/modules/combat/**`
前端应该只保留选项触发、加载态、动画态和最终结果展示。
---
## 5.4 `src/services/apiClient.ts` 仍保留了本地 token 与自动登录凭证存储
### 证据
`src/services/apiClient.ts` 当前仍把以下内容放在 `window.localStorage`
1. access token
2. 自动登录用户名
3. 自动登录密码
### 判断
这既是安全面问题,也是边界问题。
在“后端负责鉴权、前端只做表现”的目标下,正式凭证体系不应长期依赖浏览器本地保存账号密码。
### 建议迁移方向
1. 正式态优先走服务端 session / HttpOnly cookie。
2. 自动登录不要继续保存明文用户名/密码。
3. 前端仅保留最小必要的登录态感知,不保留额外认证真相。
---
## 6. 需要优先优化和拆分的代码
## 6.1 `src/components/CustomWorldEntityEditorModal.tsx`
### 当前状态
文件体量约 `4487` 行,已同时吞下:
1. 世界营地编辑
2. playable NPC 编辑
3. story NPC 编辑
4. 地标与世界地图布局
5. 场景图生成
6. 技能编辑
7. 初始物品编辑
8. 资产工作台串联
9. 多层 modal 开关与保存逻辑
### 判断
这是当前前端最明显的“巨型工作台单体文件”。
### 建议拆分方向
1. 按实体拆:营地 / playable NPC / story NPC / 地标。
2. 按能力拆:基础信息 / 关系 / 技能 / 初始物品 / 视觉资产。
3. 把 AI 生成与资产工作流进一步外置成独立 coordinator。
---
## 6.2 `server-node/src/modules/assets/characterAssetRoutes.ts`
### 当前状态
文件体量约 `3579` 行,已同时承担:
1. route 注册
2. 请求解析
3. LLM prompt bundle 生成
4. JSON 解析与修复
5. 文件系统写盘
6. visual publish
7. animation publish
8. 资产目录管理
### 直接证据
文件内同时存在:
1. `mkdir` / `writeFile`
2. `UpstreamLlmClient`
3. `parseJsonResponseText`
4. 多条 publish 路径
5. 大量本地文件落盘逻辑
### 建议拆分方向
1. route 层
2. prompt bundle service
3. file publish service
4. animation persistence service
5. asset metadata service
---
## 6.3 `src/services/ai.ts`
### 当前状态
文件体量约 `2632` 行,同时承载运行时 story、自定义世界、NPC 对话、招募等多条链路。
### 建议
即使短期内不能全部迁后端,也应该先按职责拆成:
1. runtime story client
2. npc dialogue client
3. recruit dialogue client
4. custom world generation client
5. parser / fallback / error helpers
---
## 6.4 `src/services/customWorld.ts`
### 当前状态
文件体量约 `2413`已经变成世界生成、校验、归一化、fallback 的综合体。
### 建议
至少拆成:
1. 世界框架与 outline schema
2. profile normalize / validate
3. role / landmark 编译器
4. fallback builder
5. world rule helpers
---
## 6.5 `src/hooks/story/npcEncounterActions.ts`
### 当前状态
文件体量约 `1623` 行,已经不是单纯 hook而是前端运行时 action resolver。
### 建议
按动作域拆开:
1. npc chat / recruit
2. npc help / affinity
3. quest accept / turn-in
4. battle entry / exit
5. async streaming / typewriter / presentation glue
---
## 7. 推荐执行顺序
### 第一阶段:先清仓库噪音和旧入口残留
1. 清根目录日志、扫描文件、旧截图、`__pycache__`
2. 迁出 `temp-build-goal-check/`
3. 明确处置 `scripts/dev-server/localApiPlugins.ts`
### 第二阶段:再处理无入口孤岛模块
1. 逐个确认 `GameShell.tsx`、custom-world-home、custom-world-agent、旧 flow hooks 是要接回还是归档
2. 对确认不再使用的 stub / helper / generated dead file 直接清理
### 第三阶段:把双份真相收口
1. runtime option interaction 映射只保留一份
2. 浏览历史以后端为真相源
3. 运行时快照解释权继续后移
4. 清理 `server-node -> src/**` 的反向依赖
### 第四阶段:最后拆巨型热点文件
1. `CustomWorldEntityEditorModal.tsx`
2. `characterAssetRoutes.ts`
3. `ai.ts`
4. `customWorld.ts`
5. `npcEncounterActions.ts`
---
## 8. 本文依据
文档依据:
1. `docs/audits/engineering/CURRENT_ENGINEERING_OPTIMIZATION_PRIORITIES_2026-04-10.md`
2. `docs/planning/EXPRESS_BACKEND_REFACTOR_PLAN_2026-04-08.md`
3. `docs/planning/EXPRESS_BACKEND_PARALLEL_WORKSTREAM_PLAN_2026-04-08.md`
4. `scripts/dev-server/README.md`
当前仓库扫描依据:
1. `src/main.tsx`
2. `src/routing/appRoutes.tsx`
3. `src/App.tsx`
4. `package.json`
5. `server-node/package.json`
6. `vite.config.ts`
7. `.eslintrc.cjs`
8. `git grep` 对关键模块引用、后端跨层 import、localStorage、旧 dev 插件入口的扫描结果

View File

@@ -0,0 +1,384 @@
# 工程清理与后端边界复核审计2026-04-20
更新时间:`2026-04-20`
## 0. 审计目标
这份文档不是重复 `2026-04-19` 的原始扫描,而是基于当前仓库状态做一轮复核,重点回答三个问题:
1. 昨天审计里已经提出的问题,哪些今天已经真正落地。
2. 哪些结论在当前代码里仍然成立,哪些表述需要纠正。
3. 当前工程热点和边界问题有没有发生迁移。
---
## 1. 结论先行
`2026-04-19` 那份基线相比,当前仓库已经有一批明确进展:
1. **旧 Vite 本地 API 链路已经真正出清。**
`scripts/dev-server/` 当前只剩一份 `README.md`,旧的 `localApiPlugins.ts`、角色资产插件、精灵表插件都不在仓库里了。
2. **根目录噪音产物已经清理完成。**
当前根目录临时日志/扫描产物扫描结果为空,`temp-build-goal-check/` 也不存在。
3. **`server-node -> src/**` 反向依赖已经收掉。**
当前复核没有再发现 `server-node/src/**` 直接 import 前端 `src/**` 的情况。
4. **runtime option interaction 已经收口成后端单一真相。**
这部分现在由 `server-node/src/modules/story/runtimeSession.ts` 统一构造,前端 `src/services/runtimeStoryService.ts` 不再本地再建一份映射表。
但这不代表边界问题已经结束,当前剩余问题主要集中在三块:
1. **前端仍保留运行时镜像与登录凭证本地真相。**
`runtimeStoryCoordinator.ts` 仍会先写本地快照,`apiClient.ts` 仍把 token/自动登录凭证放在 `localStorage`
2. **NPC 聊天任务链路还没有完全后端化。**
“聊天后挂出待接委托”已经移到后端,但“更换待接委托”这条分支仍由前端 `npcEncounterActions.ts` 触发 `generateQuestForNpcEncounter(...)`
3. **未接线孤岛和热点文件问题仍然明显。**
一批 UI/Hook/Prompt 残留模块还没有正式入口;同时热点已经从已删除的旧插件链路,转移到 `CustomWorldEntityEditorModal.tsx``storyPromptBuilders.ts``runtimeProfile.ts``PreGameSelectionFlow.tsx``PlatformHomeView.tsx` 等新中心。
一句话判断:
**当前仓库已经完成“清垃圾、拆旧入口、切断后端反向依赖”的第一阶段,但还没有完成“前端退出运行时真相”和“未接线孤岛归档”的第二阶段。**
---
## 2. 已完成项复核
## 2.1 旧 dev-server 链路已经不是“逻辑上废弃”,而是“代码上删除”
### 当前证据
| 项目 | 当前状态 | 结论 |
| --- | --- | --- |
| `scripts/dev-server/` | 当前只剩 `README.md` 一份说明文件 | 旧 Vite 本地 API 链路已从仓库代码层出清 |
| `scripts/dev-server/README.md` | 已明确声明当前正式入口为 `scripts/dev-node.mjs + server-node/src/modules/**` | 文档与代码状态一致 |
### 结论
`2026-04-19` 文档里关于旧本地 API 插件链路的清理结论,在当前仓库里已经可以确认成立,不再只是“计划删除”。
---
## 2.2 根目录噪音产物已经从当前工作区移除
### 当前证据
| 项目 | 当前状态 | 结论 |
| --- | --- | --- |
| 根目录历史日志/扫描产物 | 本轮扫描结果为空 | 之前的 `.codex-*.log``tmp_*`、旧截图/HTML 不再占据当前工作区 |
| `temp-build-goal-check/` | 当前不存在 | 大体量检查产物已移出当前仓库视野 |
### 结论
`2026-04-19` 文档中关于“仓库噪音产物”的问题,在当前工作区层面已经完成首轮治理。
这部分不再是当前工程第一优先级。
---
## 2.3 `server-node -> src/**` 反向依赖已清零
### 当前证据
本轮用脚本复核 `server-node/src/**` 中所有 `import` 后,当前结果为:
`NO_DIRECT_SERVER_TO_FRONTEND_SRC_IMPORTS`
同时,仓库里已经看不到类似下面这类旧反向依赖:
1. `server-node -> src/services/customWorld.js`
2. `server-node -> src/services/customWorldBuilder.js`
3. `server-node -> src/services/customWorldCreatorIntent.js`
4. `server-node -> src/types.js`
### 结论
`2026-04-19` 文档里“清理 `server-node -> src/**` 反向依赖”的阶段性目标,在当前仓库里已经真正落地。
---
## 2.4 runtime option interaction 已经收口到后端
### 当前证据
1. `server-node/src/modules/story/runtimeSession.ts` 当前仍保留 `buildOptionInteraction(...)`,负责构造:
- `npcActionMap`
- `treasureActionMap`
2. `src/services/runtimeStoryService.ts` 当前只做:
- 直接读取 `option.interaction`
- 把后端返回的 interaction 投影成 `StoryOption`
3. 前端文件里已经找不到旧的 `buildRuntimeOptionInteraction` / `npcActionMap` / `treasureActionMap` 实现。
### 结论
这项收口已经成立,当前不会再出现“前后端各维护一份 interaction 映射表”的旧问题。
---
## 2.5 浏览器端的 quest/runtime item 本地 LLM fallback 已移除
### 当前证据
1. `src/services/questDirector.ts`
- 浏览器路径先请求 `/api/runtime/quests/generate`
- 后端失败时只走 deterministic fallback compile
2. `src/services/runtimeItemAiDirector.ts`
- 浏览器路径先请求 `/api/runtime/items/runtime-intent`
- 后端失败时只返回 deterministic fallback intents
3. 这两个文件虽然仍保留 `requestChatMessageContent(...)` 分支,但那是非浏览器分支,不再是浏览器端正式兜底链路。
### 结论
`2026-04-19` 文档里关于“浏览器本地 LLM fallback”这部分当前应更新为
**浏览器端本地 LLM fallback 已移除,但这两个模块仍然是双环境混合实现,还没有彻底后端化。**
---
## 3. 需要纠正的旧文档表述
## 3.1 NPC 任务链路不是“全部后端化”,而是“挂单已后移、换单仍前触发”
### 需要纠正的点
`2026-04-19` 文档中的回填里有一条表述是:
`src/hooks/story/npcEncounterActions.ts` 不再在 NPC 单轮聊天完成后本地调用 `generateQuestForNpcEncounter(...)` 再决定是否挂出待接委托。”
### 当前代码状态
这句话对“聊天后挂出待接委托”这条主链是成立的,因为当前后端 `server-node/src/modules/ai/chatOrchestrator.ts` 已经会回填 `pendingQuestOffer`
但它对整条 NPC 任务链路来说并不完整,因为当前前端仍保留这条分支:
1. `src/hooks/story/npcEncounterActions.ts`
2. `replacePendingNpcQuestOffer()`
3. `generateQuestForNpcEncounter(...)`
也就是:
**待接委托的“正式挂出”已后端化,但“更换委托”仍然由前端动作流发起。**
### 当前应改成的结论
更准确的描述应该是:
1. NPC 单轮聊天里“是否挂出待接委托”的决定权已收回后端。
2. 但待接委托的“换单/重抽”分支仍通过前端 `npcEncounterActions.ts -> questDirector.ts` 发起。
---
## 4. 当前仍然成立的遗留问题
## 4.1 未接线/仅测试引用孤岛模块仍然明显
本轮依赖图复核后,当前仍能确认一批高置信度孤岛模块:
| 模块 | 当前状态 | 说明 |
| --- | --- | --- |
| `src/components/GameShell.tsx` | `765` 行,无运行时引用 | 旧版壳层残留仍在 |
| `src/components/custom-world-home/CustomWorldCreationHub.tsx` | `161` 行,仅测试引用 | UI 已有完成度,但仍未进入正式入口 |
| `src/components/custom-world-home/CustomWorldCreationLauncherModal.tsx` | `147` 行,无运行时引用 | 未接线入口壳层 |
| `src/components/custom-world-agent/CustomWorldAgentLauncherModal.tsx` | `91` 行,无运行时引用 | agent UI 孤岛仍在 |
| `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx` | `116` 行,无运行时引用 | agent UI 孤岛仍在 |
| `src/hooks/story/storyBootstrap.ts` | `250` 行,无运行时引用 | 旧 bootstrap hook 仍未归档 |
| `src/hooks/useEquipmentFlow.ts` | `134` 行,无运行时引用 | 旧 flow hook 残留 |
| `src/hooks/useForgeFlow.ts` | `159` 行,无运行时引用 | 旧 flow hook 残留 |
| `src/hooks/useInventoryFlow.ts` | `100` 行,无运行时引用 | 旧 flow hook 残留 |
| `src/services/customWorldPresentation.stub.ts` | `55` 行,无运行时引用 | 占位 stub 仍在 |
| `src/services/typewriter.ts` | `7` 行,无运行时引用 | 小型 helper 残留 |
| `src/prompts/customWorldOrchestratorPrompts.ts` | `9` 行,无运行时引用 | prompt source 已迁走后留下的孤岛 |
| `src/prompts/storyOrchestratorPrompts.ts` | `6` 行,无运行时引用 | prompt source 已迁走后留下的孤岛 |
| `src/data/buildTagSimilarity.generated.ts` | `823` 行,无运行时引用 | 生成产物未接入正式业务链路 |
### 说明
`src/data/itemOverrides.json``src/data/monsterOverrides.json` 这类文件虽然没有 import 引用,但会被脚本和 editor route 以路径消费,所以不计入垃圾判断。
### 结论
仓库已经完成“删旧插件”,但还没有完成“清未接线孤岛”。
当前这批模块应该进入明确处置表:
1. 直接归档/删除
2. 正式接回入口
3. 改名/迁目录,标记为实验稿
---
## 4.2 前端仍保留运行时镜像真相
### 当前证据
1. `src/hooks/story/runtimeStoryCoordinator.ts`
- 仍会在读状态和提交动作前先 `putSaveSnapshot(...)`
- 仍会在响应后多次 `rehydrateSavedSnapshot(...)`
2. `src/services/runtimeStoryService.ts`
- 仍对响应快照做 `rehydrateSavedSnapshot(...)`
### 结论
当前运行时已经不是“前端主算”,但仍然是:
**前端先写一份本地镜像,再和后端会话互相回填。**
这说明前端还没有完全退出正式运行时状态解释层。
---
## 4.3 前端仍保留本地登录凭证真相
### 当前证据
`src/services/apiClient.ts` 当前仍把以下内容写入 `window.localStorage`
1. `ACCESS_TOKEN_KEY`
2. `AUTO_AUTH_USERNAME_KEY`
3. `AUTO_AUTH_PASSWORD_KEY`
对应代码仍包括:
1. `window.localStorage.getItem(...)`
2. `window.localStorage.setItem(...)`
3. `window.localStorage.removeItem(...)`
### 结论
这一点和“前端只做表现、后端负责鉴权”的目标仍然不一致。
尤其是自动登录用户名/密码继续存本地,风险和边界问题都还在。
---
## 4.4 quest/runtime item 仍是双环境混合实现
### 当前证据
1. `src/services/questDirector.ts`
- 浏览器路径走 `requestJson('/api/runtime/quests/generate')`
- 非浏览器路径仍有 `requestChatMessageContent(...)`
2. `src/services/runtimeItemAiDirector.ts`
- 浏览器路径走 `requestJson('/api/runtime/items/runtime-intent')`
- 非浏览器路径仍有 `requestChatMessageContent(...)`
3. `src/hooks/story/npcEncounterActions.ts`
- 当前仍 import `generateQuestForNpcEncounter`
- `replacePendingNpcQuestOffer()` 仍会调用它
### 结论
浏览器兜底已经收掉,但模块职责仍然是混合的:
1. 同一个文件同时承担前端 SDK 和非浏览器编排逻辑
2. NPC 换单动作仍由前端发起服务调用
这部分还不能算真正后端化完成。
---
## 4.5 `src/services/ai.ts` 仍然是浏览器端正式 AI orchestration 热点
### 当前证据
`src/services/ai.ts` 当前约 `2608` 行,仍直接使用:
1. `requestChatMessageContent`
2. `requestPlainTextCompletion`
3. `streamPlainTextCompletion`
### 结论
这说明浏览器侧的大型 AI orchestration 仍然没有真正退出主工程。
虽然部分链路已经迁走,但整体边界还没有收完。
---
## 5. 当前热点已经发生迁移
## 5.1 当前主要大文件快照
| 文件 | 当前行数 | 判断 |
| --- | --- | --- |
| `src/components/CustomWorldEntityEditorModal.tsx` | `4898` | 仍是前端最大热点 |
| `server-node/src/modules/assets/characterAssetRoutes.ts` | `3181` | 仍是后端资产链路最大热点 |
| `src/services/ai.ts` | `2608` | 浏览器 AI orchestration 热点仍在 |
| `src/data/npcInteractions.ts` | `2409` | 仍是大型规则数据中心 |
| `server-node/src/services/customWorldAgentFoundationDraftService.ts` | `1902` | custom world agent 后端热点上升 |
| `src/prompts/storyPromptBuilders.ts` | `1882` | prompt source 已成为新的前端热点 |
| `server-node/src/modules/custom-world/runtimeProfile.ts` | `1735` | custom world runtime 编译中心已转到后端 |
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `1547` | 平台/入口流程热点上升 |
| `src/components/game-shell/PlatformHomeView.tsx` | `1522` | 平台首页热点上升 |
| `src/services/customWorld.ts` | `1489` | 仍然大,但已明显缩小 |
| `src/hooks/story/npcEncounterActions.ts` | `1434` | 仍然是前端 action 热点 |
---
## 5.2 热点变化判断
`2026-04-19` 相比,当前热点不是单纯“没变”,而是出现了明显迁移:
1. `characterAssetRoutes.ts``3579` 行降到 `3181` 行,说明资产路由已经有过一轮拆分,但仍然偏大。
2. `src/services/customWorld.ts``2413` 行降到 `1489` 行,说明自定义世界规则已拆出一部分。
3. `src/hooks/story/npcEncounterActions.ts``1623` 行降到 `1434` 行,说明 NPC 运行时逻辑也有收口。
4. 新的复杂度中心开始转移到:
- `src/prompts/storyPromptBuilders.ts`
- `server-node/src/modules/custom-world/runtimeProfile.ts`
- `src/components/game-shell/PreGameSelectionFlow.tsx`
- `src/components/game-shell/PlatformHomeView.tsx`
### 结论
当前问题已经不再是“原来的热点完全没动”,而是:
**部分旧热点正在缩小,但复杂度正在向 prompt source、custom world runtime profile、平台入口壳层继续迁移。**
---
## 6. 最新建议执行顺序
### 第一阶段:先清理当前仍明确无入口的孤岛
1. 处理 `GameShell.tsx`
2. 处理 `custom-world-home/*`
3. 处理 `custom-world-agent/*`
4. 处理 `storyBootstrap.ts``useEquipmentFlow.ts``useForgeFlow.ts``useInventoryFlow.ts`
5. 处理已脱钩的 `src/prompts/*OrchestratorPrompts.ts`
### 第二阶段:再收运行时和鉴权真相
1. 收掉 `runtimeStoryCoordinator.ts` 的本地快照前置写入
2. 收掉 `apiClient.ts` 中的自动登录用户名/密码本地持久化
3. 优先把 token/session 统一到服务端鉴权边界
### 第三阶段:补完 NPC 任务链路的后端化
1. 把“更换待接委托”从 `npcEncounterActions.ts -> questDirector.ts` 继续迁到后端
2.`questDirector.ts` / `runtimeItemAiDirector.ts` 拆成明确的后端服务与前端 SDK 两层
### 第四阶段:最后拆新热点
1. `CustomWorldEntityEditorModal.tsx`
2. `characterAssetRoutes.ts`
3. `storyPromptBuilders.ts`
4. `runtimeProfile.ts`
5. `PreGameSelectionFlow.tsx`
6. `PlatformHomeView.tsx`
---
## 7. 本文依据
文档依据:
1. `docs/audits/engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md`
2. `docs/audits/engineering/CURRENT_ENGINEERING_OPTIMIZATION_PRIORITIES_2026-04-10.md`
3. `docs/planning/EXPRESS_BACKEND_REFACTOR_PLAN_2026-04-08.md`
4. `docs/planning/EXPRESS_BACKEND_PARALLEL_WORKSTREAM_PLAN_2026-04-08.md`
当前仓库复核依据:
1. `scripts/dev-server/README.md`
2. `server-node/src/modules/story/runtimeSession.ts`
3. `src/services/runtimeStoryService.ts`
4. `src/hooks/story/runtimeStoryCoordinator.ts`
5. `src/hooks/story/npcEncounterActions.ts`
6. `src/services/questDirector.ts`
7. `src/services/runtimeItemAiDirector.ts`
8. `src/services/apiClient.ts`
9. 当前依赖图扫描结果与当前大文件体量扫描结果

View File

@@ -4,16 +4,25 @@
## 当前推荐入口
1. [ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md)
1. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md)
这一版是对 `2026-04-19` 基线的当前仓库复核,明确哪些问题已经处理、哪些表述需要纠正、热点又迁移到了哪里。
2. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md)
这一版保留原始问题快照和执行回填,适合回看“为什么会有这轮清理与边界收口”。
3. [ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md)
这一版最适合作为当前工程基线,重点从“是否真正绿色”“门禁有没有覆盖真实风险”来判断仓库状态。
2. [ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md)
4. [ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md)
适合回看运行时主链路、Story/Combat 边界、分层过渡期问题。
3. [ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md)
5. [ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md)
适合看第一轮系统性工程扫描,了解最早的问题基线。
## 融合结论
- 当前仓库已经完成“旧 dev 插件链路删除、根目录噪音清理、`server-node -> src/**` 反向依赖切断”这批第一阶段任务。
- 当前的新重点已经进一步收敛到三类:未接线孤岛模块、前端残留的运行时/鉴权真相、热点向 prompt/runtime profile/平台入口壳层迁移。
- 三轮结论是一致收敛的:问题不在“有没有开始工程化”,而在“工程化是否真正覆盖了最危险的主链路”。
- 最新一轮已经把关注点集中到质量门禁、真实绿色基线、关键模块豁免和 build warning 上。
- `2026-04-19` 这一轮把问题压实到了四类:仓库噪音、旧 dev 入口残留、前端越界运行时逻辑、巨型热点文件。
- `2026-04-20` 这一轮进一步确认:前两类已经阶段性完成,当前真正剩下的是边界尾巴和新热点迁移。
- 如果只是为了判断现在先做什么,直接从 `2026-04-01` 开始即可。
- 如果是要做长期重构方案,再按 `2026-03-29 -> 2026-03-30 -> 2026-04-01` 的顺序回看演进。
- 如果是要看当前清理和边界收口的最新状态,优先看 `2026-04-20`
- 如果是要做长期重构方案,再按 `2026-03-29 -> 2026-03-30 -> 2026-04-01 -> 2026-04-19 -> 2026-04-20` 的顺序回看演进。

View File

@@ -0,0 +1,870 @@
# 等级成长、章节经验节奏与 NPC 自动定级设计
更新时间:`2026-04-20`
## 实现进度2026-04-20 第一批)
当前仓库已按本设计先落地第一批稳定能力:
1. 已新增 `playerProgression` 正式成长状态,包含等级、当前等级经验、总经验与下级阈值。
2. 已新增等级基准与经验结算服务,并接入前后端存档归一化,旧存档默认回填为 `Lv.1 / 0 XP`
3. 已给 `QuestReward` 补上 `experience`,新生成任务会按当前等级与任务结构给出任务经验。
4. 已将 Express 后端 `npc_quest_turn_in` 接入经验发放与升级处理,任务交付结果会反馈 `经验 +N` 与升级信息。
5. 已在冒险主面板补充最小等级展示:`Lv.` 与细经验条;任务奖励面板可看到经验数值。
6. 已收回任务日志里的直接领奖入口,任务奖励结算当前以 NPC 交付链路为准。
本轮仍未落地的部分:
1. 击败敌对 NPC 经验。
2. 章节经验预算 / ledger 统计。
3. 按章节自动定级 NPC 与运行时敌对经验掉落。
## 0. 目标
这次设计解决 5 个必须同时成立的问题:
1. 玩家需要正式拥有 `等级 / 当前经验 / 总经验 / 升级` 这条成长主链。
2. 经验只从两类明确来源进入:
- 完成任务
- 击败敌对 NPC
3. 同等级实体必须具备同一档 `参考强度`,不能再靠散落在各处的静态数值各自漂移。
4. 系统需要能按章节评估玩家经验获取速度,而不是只在整体通关后回看“升太快/升太慢”。
5. 不同章节里的 NPC 需要按章节目标等级自动定级,保证这一章的敌我强度、经验产出和升级节奏互相闭合。
一句话结论:
**等级必须成为后端统一裁决的成长基线;章节必须先产出“目标玩家等级带 + 经验预算”,再由这套预算反推任务经验、击杀经验和本章 NPC 自动等级。**
---
## 1. 基于当前仓库的判断
结合当前代码与文档,现状已经有足够好的骨架,但等级系统这一层还完全缺位。
### 1.1 已经具备的基础
1. `src/data/questFlow.ts`
- 已有 `QuestLogEntry / QuestStep / QuestProgressSignal / chapter quest`
- 已经能把场景章节任务接到运行时主链。
2. `server-node/src/modules/quest/questStoryActionService.ts`
- 已经把 `接任务 / 交任务` 收回后端。
- 任务结算时已经集中处理货币、背包、好感变化。
3. `server-node/src/modules/quest/questRuntimeSignalService.ts`
- 已经会在 `npc_chat / 击败敌对 NPC / 宝藏 / 切磋` 后投递 quest signal。
4. `src/services/storyEngine/chapterDirector.ts`
- 已经能用当前场景章节任务推导 `opening -> expansion -> turning_point -> climax -> aftermath`
5. `src/types/customWorld.ts`
- 已经有 `sceneChapterBlueprints`,说明章节顺序、幕推进和 NPC 编排已经有正式挂点。
6. `src/types/attributes.ts``src/data/hostileNpcPresets.ts`
- 已经有统一属性画像、怪物/NPC 统一实体方向。
- 当前敌对实体已有 `baseStats / attributeProfile / behaviorVectors`,可以继续向“同级同参考强度”收束。
### 1.2 当前缺口
当前最核心的缺口有 6 个:
1. `GameState` 没有玩家等级成长状态。
2. `QuestReward` 没有经验字段。
3. `SceneHostileNpc / SceneNpc` 没有正式等级和击杀经验字段。
4. 当前 hostile preset 的 `hp/maxHp` 仍是静态绝对值,不受章节节奏控制。
5. 章节系统没有“本章目标入场等级 / 出章等级 / 经验预算”的结构。
6. 没有“按章节自动定级”的编译器,也没有“本章经验是否超发/欠发”的记账面板。
一句话总结:
**现在仓库里已经有章节、任务、NPC 和属性系统,但还没有“成长预算层”,所以强度、奖励和章节节奏仍然缺少同一把尺。**
---
## 2. 核心决策
## 2.1 等级、经验与 NPC 定级全部由 Express 后端裁决
必须坚持:
1. 前端只展示 `等级 / 经验条 / 升级结果 / NPC 等级徽标`
2. 经验发放、升级、章节经验预算、NPC 自动定级全部在 Express 后端计算。
3. 前端不本地推演“这次应该升几级”“这个 NPC 应该是多少级”。
推荐新增领域目录:
- `server-node/src/modules/progression/`
建议首批模块:
- `levelBenchmarks.ts`
- `playerProgressionService.ts`
- `chapterProgressionPlanner.ts`
- `chapterExperienceLedger.ts`
- `npcLevelResolver.ts`
- `progressionRuntimeSignalService.ts`
## 2.2 MVP 经验来源只认两类事件
首版只允许两类正式经验来源:
1. `quest_turned_in`
- 任务真正交付时发经验。
- 不在“接任务”“任务 ready_to_turn_in”时发经验。
2. `hostile_npc_defeated`
- 仅限敌对 NPC / 怪物胜利结算后发经验。
- 不对 `npc_spar_completed`、普通聊天、观察、宝藏直接发经验。
这样做的原因是:
1. 最容易和当前后端任务/战斗链路接上。
2. 经验来源清晰,便于做章节预算。
3. 避免系统一开始就被碎片经验源冲散。
## 2.3 同等级 = 同参考强度
这是本次设计最重要的规则:
1. 等级是所有可比较实体共享的强度基线。
2. 同等级玩家、敌对 NPC、可战斗剧情 NPC必须共享同一档 `参考强度`
3. 世界属性 schema 只决定“强在哪种风格上”,不决定“同级谁天然强一截”。
也就是说:
- `Lv.8` 的重甲敌人和 `Lv.8` 的迅捷刺客可以打法不同
- 但两者的 `参考强度预算` 必须是同一档
真正的强弱差只允许来自:
1. 等级差
2. 装备 / Build / Buff / Debuff
3. 章节中明确声明的 `boss / elite` 角色通过更高等级体现,而不是同级偷加隐藏倍数
## 2.4 章节先出经验预算,再反推等级
章节设计从这次开始必须按下面顺序计算:
```text
章节顺序
-> 本章玩家目标入场等级 / 出章等级
-> 本章总经验预算
-> 任务经验份额 / 击杀经验份额
-> 本章 NPC 自动等级
-> 本章实际经验记账与偏差评估
```
不能反过来先手写一堆 NPC 强度,再看玩家能不能接住。
## 2.5 UI 只做极简表达
为了符合当前项目“UI 不默认堆规则说明”的约束,前台只建议新增 4 个轻量展示:
1. 玩家信息区:
- `Lv. X`
- 一条细经验条
2. 敌对 NPC 名牌:
- `Lv. X`
3. 任务交付结果:
- `经验 +N`
4. 升级提示:
- 单条 toast 或单行系统反馈
不在界面里默认放:
- 经验公式说明
- 章节经验预算说明
- 等级规则解释文案
---
## 3. 数据结构设计
## 3.1 玩家成长状态
建议新增:
```ts
export interface PlayerProgressionState {
level: number;
currentLevelXp: number;
totalXp: number;
xpToNextLevel: number;
pendingLevelUps?: number;
lastGrantedSource?: 'quest' | 'hostile_npc' | null;
}
```
挂载位置建议:
- `src/types/game.ts`
- `GameState.playerProgression`
原则:
1. 这不是 `runtimeStats` 的一部分。
2. `runtimeStats` 继续做统计计数。
3. `playerProgression` 是正式玩法状态。
## 3.2 等级基准表
建议新增:
```ts
export interface LevelBenchmark {
level: number;
xpToNextLevel: number;
cumulativeXpRequired: number;
referenceStrength: number;
baseHp: number;
baseMana: number;
baselineDamageScale: number;
}
```
单一真相源建议放在:
- `server-node/src/modules/progression/levelBenchmarks.ts`
前端只通过后端投影拿结果,不自己保存第二份表。
## 3.3 实体等级档案
建议新增:
```ts
export type ProgressionRole =
| 'guide'
| 'ambient'
| 'support'
| 'hostile_standard'
| 'hostile_elite'
| 'hostile_boss'
| 'rival';
export interface EntityLevelProfile {
level: number;
referenceStrength: number;
chapterId?: string | null;
chapterIndex?: number | null;
progressionRole: ProgressionRole;
source: 'chapter_auto' | 'preset_override' | 'manual';
}
```
建议接入:
- `src/types/scene.ts`
- `SceneNpc.levelProfile?: EntityLevelProfile`
- `SceneHostileNpc.levelProfile?: EntityLevelProfile`
## 3.4 任务奖励扩展
建议扩展:
```ts
export interface QuestReward {
affinityBonus: number;
currency: number;
experience: number;
items: InventoryItem[];
storyHint?: string;
intel?: { ... };
}
```
说明:
1. 经验是任务奖励的一等字段。
2. 经验文本不走 story hint 兜底。
3. 任务经验由后端编译,不交给 AI 决定。
## 3.5 敌对 NPC 经验掉落
建议扩展:
```ts
export interface SceneHostileNpc {
...
experienceReward?: number;
}
```
首版只给运行时敌对 NPC 挂经验值,不强行把它沉到所有 preset 原始数据中。
原因:
1. 经验应该跟章节定级一起编译。
2. 同一个 hostile preset 出现在不同章节时,等级和经验都应不同。
3. 静态 preset 继续只表达“风格”和“原型”,不再表达最终强度。
## 3.6 章节成长计划
建议新增运行时编译结果:
```ts
export interface ChapterProgressionPlan {
chapterId: string;
chapterIndex: number;
totalChapters: number;
entryPseudoLevel: number;
exitPseudoLevel: number;
entryLevel: number;
exitLevel: number;
totalXpBudget: number;
questXpBudget: number;
hostileXpBudget: number;
expectedHostileDefeatCount: number;
paceBand: 'opening_fast' | 'steady' | 'pressure' | 'finale_dense';
}
```
建议作为后端运行时编译结果缓存,不作为创作者直接编辑字段。
## 3.7 章节经验记账
建议新增:
```ts
export interface ChapterExperienceLedger {
chapterId: string;
chapterIndex: number;
levelAtEntry: number;
levelAtExit?: number | null;
plannedTotalXp: number;
plannedQuestXp: number;
plannedHostileXp: number;
actualQuestXp: number;
actualHostileXp: number;
expectedHostileDefeatCount: number;
actualHostileDefeatCount: number;
}
```
用途:
1. 评估每一章经验速度。
2. 判断本章是否超发/欠发。
3. 为下一轮调参提供依据。
---
## 4. 等级曲线与参考强度
## 4.1 首版等级目标
首版建议:
1. 系统支持 `Lv.1 ~ Lv.20`
2. 当前主线正常通章目标不是满级
3. 标准单轮战役通关目标等级建议落在 `Lv.14 ~ Lv.15`
这样做的原因是:
1. 级差足够表达章节成长
2. 不会让前期升级过细、后期又没有空间
3. 还保留后续营地、精英支线、长期养成的余量
## 4.2 升级经验公式
建议基线公式:
```ts
xpToNextLevel(level) = 60 + 20 * (level - 1) + 8 * (level - 1) * (level - 1);
```
由此生成 `LevelBenchmark[]`,不在业务代码里散落重复公式。
说明:
1. 前期升级快,便于建立成长反馈
2. 中后期门槛逐步拉开,避免章节尾段失控
3. 可直接序列化成常量表用于测试
## 4.3 参考强度公式
建议基线公式:
```ts
referenceStrength(level) =
100 + 16 * (level - 1) + 6 * (level - 1) * (level - 1);
```
并同步产出:
```ts
baseHp(level);
baseMana(level);
baselineDamageScale(level);
```
重要约束:
1. `referenceStrength` 是同级比较标尺。
2. style 只允许在同一档预算内重分布,不允许抬高总强度。
3. `elite / boss` 不允许用同级隐藏倍率偷强度,必须通过更高等级体现。
## 4.4 现有静态数值如何迁移
当前 `src/data/hostileNpcPresets.ts` 里的:
- `baseStats.hp`
- `baseStats.maxHp`
- `speed`
- `attackRange`
不建议继续全部视为最终强度。
迁移原则:
1. `attackRange / speed` 继续保留为战斗风格参数。
2. `hp / maxHp` 改为“风格形状参考”,最终值由 `等级基准 + 风格分布` 决定。
3. 现有 preset 的高血量、高机动、高压制,只用于决定“同级下怎么分布”,不改变同级总参考强度。
---
## 5. 经验发放规则
## 5.1 任务经验
任务经验只在 `turn_in` 时发放。
建议公式:
```ts
baseQuestXp(targetLevel) = xpToNextLevel(targetLevel) * 0.45;
questXp =
baseQuestXp(targetLevel) *
stepCountMultiplier *
narrativeTypeMultiplier *
urgencyMultiplier;
```
建议倍率:
| 条件 | 倍率 |
| ------------------------------------------ | ------ |
| `steps = 1` | `0.85` |
| `steps = 2` | `1.0` |
| `steps >= 3` | `1.12` |
| `investigation / retrieval / relationship` | `1.0` |
| `trial / bounty` | `1.08` |
| `urgency = high` | `1.05` |
最终规则:
1. 结果四舍五入到 `5` 的倍数。
2. 章节主任务优先从本章 `questXpBudget` 出数。
3. 普通 NPC 支线如果不绑定章节,则按 `targetLevel` 单独计算。
## 5.2 击败敌对 NPC 经验
建议公式:
```ts
baseKillXp(targetLevel) = xpToNextLevel(targetLevel) * 0.08;
killXp =
baseKillXp(targetLevel) *
stageMultiplier *
levelDeltaMultiplier *
repeatPenalty;
```
建议倍率:
| 条件 | 倍率 |
| -------------------------------- | ----------------- |
| `opening` | `0.9` |
| `expansion` | `1.0` |
| `turning_point` | `1.05` |
| `climax` | `1.15` |
| 玩家高于目标 `2` 级 | `0.7` |
| 玩家高于目标 `4` 级 | `0.3` |
| 玩家低于目标 `2` 级 | `1.15` |
| 同章同类敌对实体超过预计击杀数后 | `0.5 -> 0.2 -> 0` |
解释:
1. 同章重复刷怪必须衰减。
2. 击杀经验要响应等级差,避免低章 farming。
3. 高潮压轴敌人可以给更多经验,但仍受章节预算约束。
## 5.3 经验发放顺序
推荐统一顺序:
```text
规则动作成功
-> 生成经验 grant
-> 写入 playerProgression.totalXp / currentLevelXp
-> 处理升级
-> 回写章节 ledger
-> 生成前端提示
```
不要把经验结算拆在前端多个回调里各自加一次。
---
## 6. 章节经验速度评估
## 6.1 章节顺序来源
章节索引 `chapterIndex` 建议按下面顺序解析:
1.`campaign pack` 时,优先用 campaign 正式顺序
2. 否则有 `sceneChapterBlueprints` 时,用蓝图顺序
3. 再否则,对 `landmarks` 从营地出发做最短路径排序
4. 若存在并列,则回退到稳定的 landmark 原始顺序
这样才能给每章一个稳定的“这是第几章”。
## 6.2 目标等级带
建议先计算“伪等级进度”,再换算成经验预算:
```ts
chapterBoundaryPseudoLevel(i) =
1 + curve(i / totalChapters) * (terminalStoryLevel - 1);
```
建议 `curve` 用轻微前快后稳的函数:
```ts
curve(progress) = Math.pow(progress, 0.92);
```
随后:
```ts
entryPseudoLevel = chapterBoundaryPseudoLevel(chapterIndex - 1);
exitPseudoLevel = chapterBoundaryPseudoLevel(chapterIndex);
chapterXpBudget =
xpForPseudoLevel(exitPseudoLevel) - xpForPseudoLevel(entryPseudoLevel);
```
这样做的好处是:
1. 每一章都有明确的入章/出章目标
2. 等级增幅随章节自然变慢
3. 经验速度评估可以直接落成表格
## 6.3 章节经验份额
默认建议:
| 章节类型 | 任务经验占比 | 击杀经验占比 |
| --------------- | ------------ | ------------ |
| 调查/关系型章节 | `75%` | `25%` |
| 平衡型章节 | `65%` | `35%` |
| 战斗/试炼型章节 | `55%` | `45%` |
章节类型判定可由下面几项共同决定:
1. `SceneChapterBlueprint.acts` 数量
2. 当前章节 hostile NPC 数量
3. 当前章节任务 step 中战斗目标占比
4. `dangerLevel`
5. linked thread 是否为主线高压线程
## 6.4 实际速度评估规则
每章结束后,至少计算下面三个值:
1. `actualTotalXp / plannedTotalXp`
2. `actualHostileXp / plannedHostileXp`
3. `levelAtExit - plannedExitLevel`
建议判定:
| 偏差 | 判断 |
| ----------- | -------- |
| `±10%` 内 | 正常 |
| `10% ~ 20%` | 需观察 |
| `> 20%` | 必须调参 |
这就是“评估每一章获得经验速度”的正式口径,不再用主观感觉判断。
---
## 7. NPC 自动定级规则
## 7.1 默认角色分类
建议默认按当前幕和敌我属性推导 `progressionRole`
1. 当前幕 `primaryNpcId`
- 若 hostile`hostile_elite``hostile_boss`
- 若非 hostile`guide``rival`
2. 非主角色 hostile NPC
- `hostile_standard`
3. 非主角色友方 NPC
- `support``ambient`
如需修正,再允许章节蓝图加可选 override但不要求创作者每次手填。
## 7.2 等级锚点
每章先得到:
1. `entryLevel`
2. `exitLevel`
然后按当前阶段得到阶段锚点:
| 阶段 | 目标锚点 |
| --------------- | ----------------------------- |
| `opening` | 接近 `entryLevel` |
| `expansion` | `entryLevel ~ exitLevel` 中段 |
| `turning_point` | 接近 `exitLevel` |
| `climax` | `exitLevel` |
| `aftermath` | `exitLevel - 1` 或持平 |
## 7.3 最终定级
建议公式:
```ts
baseStageLevel = interpolate(entryLevel, exitLevel, stageProgress);
npcLevel = round(baseStageLevel) + roleOffset(progressionRole);
```
建议 offset
| role | offset |
| ------------------ | -------- |
| `ambient` | `-1` |
| `support` | `0` |
| `guide` | `0` |
| `rival` | `0 ~ +1` |
| `hostile_standard` | `0` |
| `hostile_elite` | `+1` |
| `hostile_boss` | `+2` |
约束:
1. 统一 clamp 到 `1 ~ terminalStoryLevel + 2`
2. 不允许出现“第 3 章普通怪高于第 6 章精英”的跨章倒挂
3. `hostile_boss` 如果需要更强,必须给更高等级,不准同级偷倍数
## 7.4 同级不同风格
NPC 等级确定后,再把 `referenceStrength` 套到具体风格:
1. 重装型:
- 生命占比更高
- 爆发占比更低
2. 迅捷型:
- 生命占比更低
- 出手与压制占比更高
3. 控场型:
- 法力/控制预算更高
但这一步只能做“分布调整”,不能改变同级总参考强度。
---
## 8. 与当前仓库的接入点
## 8.1 第一批必须改的类型
1. `src/types/game.ts`
- 新增 `playerProgression`
2. `src/types/story.ts`
- `QuestReward.experience`
3. `src/types/scene.ts`
- `SceneNpc.levelProfile`
- `SceneHostileNpc.levelProfile`
- `SceneHostileNpc.experienceReward`
4. `packages/shared/src/contracts/story.ts`
- 如果需要让前后端合同正式共享等级展示字段,在这里补最小契约
## 8.2 第一批必须改的后端模块
1. `server-node/src/modules/quest/questStoryActionService.ts`
- `resolveQuestTurnInAction(...)` 里追加任务经验发放
2. `server-node/src/modules/quest/questRuntimeSignalService.ts`
- 保持 quest signal 职责
- 不直接负责经验裁决,只把可用信号交给 progression 模块
3. `server-node/src/modules/combat/**`
- 在胜利结算后发 hostile NPC 经验
4. `server-node/src/modules/story/**`
- 在切章、进场、恢复场景时接入章节成长计划与 ledger
5. 新增 `server-node/src/modules/progression/**`
- 成为等级、经验、章节定级唯一真相源
## 8.3 第一批不建议重写的部分
这轮不建议一开始就重写:
1. 整套前端战斗 UI
2. 整套属性系统
3. Quest UI 大面板结构
4. 所有 hostile preset 原始配置文件
更稳的做法是:
1. 先让后端算出等级与经验
2. 再把结果投影到现有运行时字段
3. 最后再逐步清理旧静态强度残留
---
## 9. 迁移策略
## 9.1 旧存档兼容
旧存档没有 `playerProgression` 时:
1. 默认初始化为 `Lv.1`
2. `totalXp = 0`
3. `currentLevelXp = 0`
4. `xpToNextLevel = benchmark[1].xpToNextLevel`
如果后续希望更平滑,可在第二轮增加“按当前章节进度反推起始等级”的迁移脚本,但首版先不要让迁移复杂化。
## 9.2 旧 hostile preset 兼容
旧 preset 里的 `hp/maxHp` 首版处理建议:
1. 先保留原字段作为 style hint
2. 运行时用 level benchmark 覆盖最终 `hp/maxHp`
3. 保证当前素材和行为标签不需要重做
## 9.3 旧任务兼容
旧任务没有 `reward.experience` 时:
1. 默认按 `0` 处理
2. 仅新生成或重新编译的任务带经验
3. 章节主任务优先切到新编译链
---
## 10. 开发顺序
## 阶段 A先把等级状态立住
先做:
1. `PlayerProgressionState`
2. `LevelBenchmark[]`
3. 经验加点与升级服务
验收:
1. 后端能正确加经验与升级
2. 前端能稳定展示 `Lv. X / 经验条`
## 阶段 B接任务经验
先做:
1. `QuestReward.experience`
2. `quest turn-in` 经验发放
3. 任务结果文案里补 `经验 +N`
验收:
1. 交付任务后能加经验
2. 升级时能正确连跳
## 阶段 C接章节预算与 NPC 自动定级
先做:
1. `ChapterProgressionPlan`
2. `npcLevelResolver`
3. runtime hostile NPC 经验值生成
验收:
1. 进入不同章节时 NPC 等级自动变化
2. 同级不同风格但参考强度一致
## 阶段 D接击败敌对 NPC 经验与章节 ledger
先做:
1. hostile defeat 经验
2. `ChapterExperienceLedger`
3. 章节偏差评估输出
验收:
1. 每章都能看到计划/实际经验偏差
2. 重复刷同章敌对 NPC 不会破坏曲线
---
## 11. 验收标准
做到下面这些,才算这次等级系统设计真正落地:
1. 玩家正式拥有 `等级 + 经验 + 升级` 主链。
2. 经验来源只通过后端发放,前端不本地算经验。
3. 同等级实体共享同一档 `参考强度`
4. 每章都能生成 `入章等级 / 出章等级 / 经验预算`
5. 每章的 NPC 都能按章节自动定级。
6. 完成任务、击败敌对 NPC 都能稳定获得经验。
7. 章节结束后能评估“这一章经验速度是否正常”。
8. 现有任务、章节、属性和 hostile NPC 主链不被推翻,只是在其上新增成长预算层。
---
## 12. 最后结论
这次等级系统设计的重点,不是简单在 UI 上加一个 `Lv.1`,而是把当前仓库里已经存在的:
1. 章节闭环
2. 任务结算
3. 敌对 NPC 胜利事件
4. 统一属性与 hostile preset
收束到一条新的成长主链:
**章节先给出目标等级与经验速度,系统再按这套速度自动设置 NPC 等级,并把任务交付与击败敌对 NPC 统一变成可控的经验入口。**
这样之后,等级不再只是一个展示数字,而会真正变成:
- 玩家成长速度的刻度
- 同级参考强度的刻度
- 章节节奏是否合理的刻度
- 不同章节 NPC 强度自动落位的刻度

View File

@@ -0,0 +1,316 @@
# 高好感角色聊天内委托触发与领取流程设计
更新时间:`2026-04-19`
## 0. 目标
这次迭代解决的是一个很具体的体验断层:
1. 当前角色委托主要还是从 NPC 互动菜单里直接出现,缺少“先聊上 1-2 轮,再顺着上下文自然托付任务”的过渡。
2. 聊天态虽然已经有多轮对话、自定义输入和选项建议,但没有“临时任务 offer”这一层中间状态。
3. 现有任务详情面板已经能看任务、领奖励,但还不能承接“任务尚未正式入日志,只是对方刚提出委托”的场景。
目标不是新造一套聊天任务系统,而是把现有:
- `npc_chat` 多轮聊天流
- `generateQuestForNpcEncounter(...)` 任务生成链
- `QuestLogEntry` 任务日志结构
- `AdventurePanelOverlays` 任务详情面板
串成一条更自然的“聊天内委托”链路。
一句话目标:
**当玩家与好感度大于 0 的角色聊天时,先寒暄 1-2 轮,再由角色顺着上下文提出委托;玩家可查看、换任务、放弃任务,确认领取后任务才正式进入日志,并恢复自由聊天。**
## 1. 这次不做什么
为了避免系统边界漂移,这次明确不做下面这些事:
1. 不重写任务生成器。
- “任务”和“更换任务”都必须复用现有 `generateQuestForNpcEncounter(...)` 链路。
- 也就是说,仍然走现在的 `evaluateQuestOpportunity -> AI / fallback quest intent -> compileQuestIntentToQuest`
2. 不把聊天态任务 offer 直接视为已接任务。
- 对方刚把委托提出来时,它还只是 `pending offer`,不应立即写进 `gameState.quests`
- 只有玩家点击“领取任务”后,才正式调用现有任务接取写入逻辑。
3. 不在 UI 默认堆说明文字。
- 聊天态只切换三项操作:
- `查看任务`
- `更换任务`
- `放弃任务`
- 不额外在主界面堆功能说明。
4. 不改成必须走服务端聊天。
- 当前多轮 NPC 聊天仍沿用前端本地的 `handleNpcChatTurn(...)` 流程。
- 这次只是在聊天流程里插入任务 offer 状态。
## 2. 核心流程
## 2.1 触发条件
只有同时满足下面条件时,聊天中才允许提出委托:
1. 当前遭遇是角色型 NPC。
- `encounter.characterId` 存在。
2. 当前好感度大于 `0`
3. 当前角色没有未结清任务。
- 复用 `getQuestForIssuer(...)` 判断。
4. 当前聊天里还没有待处理的任务 offer。
5. 已经完成前置寒暄轮次。
- 默认要求先完成 `1-2` 轮自然聊天。
- 建议规则:
- `affinity >= 30` 时,完成 `1` 轮后即可进入委托时机。
- `0 < affinity < 30` 时,完成 `2` 轮后再进入委托时机。
## 2.2 聊天轮次切换
正常聊天时:
- 保持现有三条 `npc_chat` 建议选项
- 保持自定义输入可用
当触发委托时:
1. 先正常生成本轮 NPC 回复。
2. 随后调用现有 `generateQuestForNpcEncounter(...)` 生成一份 `pending quest offer`
3. 在当前轮次追加一段 NPC 委托台词。
4. 把当前轮次选项替换成:
- 第一项:`查看任务`
- 第二项:`更换任务`
- 第三项:`放弃任务`
5. 临时隐藏自定义输入。
这意味着聊天态此时进入一个短暂的“任务处理态”,直到玩家:
- 查看并领取
- 更换
- 放弃
其中任一分支结算完成后,再恢复自由聊天。
## 2.3 查看任务
点击 `查看任务` 时:
1. 不立即写入任务日志。
2. 直接复用现有任务详情弹层展示 `pending quest offer` 的详情。
3. 任务详情面板在这类任务上新增主按钮:
- `领取任务`
这一步的关键是:
**查看任务只是看,不等于接。**
## 2.4 领取任务
点击 `领取任务` 时:
1. 使用当前 `pending quest offer``QuestLogEntry`,调用现有任务接取写入逻辑,把任务正式写入 `gameState.quests`
2. 同时把这轮动作写回聊天:
- 追加玩家一句明确接受委托的话。
- 可追加一条简短 NPC 确认回应,或直接用现有结果文案转成对话语义。
3. 更新 `storyHistory`,确保后续聊天上下文知道“这份委托已经接下”。
4. 清空 `pending quest offer`
5. 恢复正常 `npc_chat` 建议选项与自定义输入。
## 2.5 更换任务
点击 `更换任务` 时:
1. 必须再次调用现有 `generateQuestForNpcEncounter(...)`
2. 旧的 `pending quest offer` 被新的覆盖。
3. 当前聊天追加一条“对方换了个委托”的回应。
4. 仍然维持任务处理态:
- 继续显示
- `查看任务`
- `更换任务`
- `放弃任务`
- 自定义输入仍隐藏
这里的关键约束是:
**更换任务不是本地改标题或改描述,而是重新走现有任务生成链。**
## 2.6 放弃任务
点击 `放弃任务` 时:
1. 直接丢弃当前 `pending quest offer`
2. 在对话里补一条“玩家暂时不接”的回应。
3. 恢复自由聊天:
- 再次显示正常 `npc_chat` 建议
- 恢复自定义输入
放弃这里只作用于“待领取委托”,不会影响已经入日志的正式任务。
## 3. 数据与状态设计
## 3.1 聊天态新增待领取任务状态
建议把这次临时状态挂在 `StoryNpcChatState` 上,而不是直接写入 `GameState.quests`
```ts
interface StoryNpcQuestOfferState {
quest: QuestLogEntry;
}
interface StoryNpcChatState {
npcId: string;
npcName: string;
turnCount: number;
customInputPlaceholder?: string;
pendingQuestOffer?: StoryNpcQuestOfferState | null;
}
```
这样有 3 个好处:
1. 任务 offer 只属于当前聊天上下文,不污染正式任务日志。
2. AdventurePanel 可以直接从 `currentStory.npcChatState` 判断是否进入任务处理态。
3. 任务详情面板可以直接读取这份 `QuestLogEntry` 展示,而不用再造一套展示结构。
## 3.2 任务处理态的选项表达
建议不要把“查看 / 更换 / 放弃”接进服务端 runtime action。
原因是:
1. `查看任务` 只是 UI 行为,不需要服务端结算。
2. `更换任务``放弃任务` 都是当前聊天态内部状态流转。
3. 这三项更适合作为本地聊天态专用选项,由 `AdventurePanel + npcEncounterActions` 协同处理。
建议做成 3 个本地专用 `StoryOption`
```ts
{
functionId: 'npc_chat_quest_offer_view',
actionText: '查看任务',
runtimePayload: { npcChatQuestOfferAction: 'view' }
}
```
其余两个同理:
- `replace`
- `abandon`
## 3.3 接取后的正式写入
正式领取后才进入任务日志:
```ts
nextState = {
...state,
quests: acceptQuest(state.quests, pendingQuest.quest),
runtimeStats: incrementGameRuntimeStats(state.runtimeStats, {
questsAccepted: 1,
}),
}
```
也就是说:
- `pending offer` 不计入 `questsAccepted`
- 真正点击 `领取任务` 才计数
## 4. UI 落点
## 4.1 聊天面板
`AdventurePanel` 中增加一个判断:
1. `currentStory.npcChatState?.pendingQuestOffer` 存在时:
- 只显示三项任务处理选项
- 隐藏自定义输入
2. 不存在时:
- 保持现有聊天输入与 `npc_chat` 建议
## 4.2 任务详情弹层
`AdventurePanelOverlays` 里的任务详情弹层继续复用,但要区分两种任务来源:
1. 已在任务日志中的正式任务
- 保持现有逻辑
- 完成后仍显示 `领取奖励`
2. 聊天里的 `pending quest offer`
- 底部显示 `领取任务`
- 不显示 `领取奖励`
点击 `领取任务` 后:
- 关闭详情弹层
- 回到聊天界面
- 当前聊天追加“我愿意接下”这一步
## 5. 代码改动建议
建议落地在这些文件:
1. `src/types/story.ts`
- 扩展 `StoryNpcChatState`
2. `src/hooks/story/npcEncounterActions.ts`
- 增加聊天内任务 offer 触发判断
- 接入 `generateQuestForNpcEncounter(...)`
- 增加
- 更换任务
- 放弃任务
- 领取任务
对应的本地状态流转
3. `src/hooks/story/useStoryInteractionCoordinator.ts`
- 向上暴露聊天内任务 offer 的操作方法
4. `src/hooks/useStoryGeneration.ts`
- 把聊天内任务 offer UI 能力透传给面板
5. `src/components/AdventurePanel.tsx`
- 聊天态隐藏 / 恢复输入
- 拦截 `查看任务 / 更换任务 / 放弃任务`
- 让 pending quest 也能进入任务详情弹层
6. `src/components/adventure-panel/AdventurePanelOverlays.tsx`
- 为 pending quest 增加 `领取任务` 按钮
7. `src/components/AdventurePanel.test.tsx`
- 补聊天态输入隐藏测试
8. `src/hooks/story/npcEncounterActions.test.ts`
- 补任务 offer 触发 / 更换 / 接取测试
## 6. 验收标准
做到以下几点,才算这次需求成立:
1. 与好感度大于 `0` 的角色聊天时,不会一上来立刻塞任务,前 `1-2` 轮先正常寒暄。
2. 达到委托时机后,系统会调用现有 `generateQuestForNpcEncounter(...)` 生成一份待领取任务。
3. 当前聊天轮次会出现一段明确的委托台词。
4. 这一轮聊天选项会切成:
- `查看任务`
- `更换任务`
- `放弃任务`
5. 任务处理态下,自定义输入会被临时隐藏。
6. 点击 `查看任务` 会弹出现有任务详情面板。
7. 点击 `领取任务` 后,任务才正式进入任务日志,并在对话里体现“玩家愿意接下”。
8. 领取完成后,聊天会恢复正常输入与自由继续对话。
9. 点击 `更换任务` 时,必须重新调用现有任务生成链,而不是本地改文案。
## 7. 一句话收束
这次要做的,不是“让聊天里多一个任务按钮”,而是把:
- 高好感聊天
- 上下文化任务生成
- 临时任务 offer
- 任务详情查看
- 正式领取后回流聊天
整合成一个更自然的叙事交接过程。

View File

@@ -0,0 +1,193 @@
# 平台首页公开浏览与登录弹窗拦截设计
更新时间:`2026-04-19`
## 0. 背景
当前仓库里的账号 PRD 默认要求“未登录先登录,再进入平台”。
这次产品策略调整为:
- 用户进入平台后,默认可以直接浏览首页
- 只有在尝试进入作品、进入世界、开始创作等受保护动作时,才检查登录
- 登录界面不再是完整页面,而是覆盖在当前平台上的轻量弹窗
这份设计只覆盖当前一次前台入口改造,目标是把边界写清楚到可以直接编码,不再让登录策略和平台首页互相冲突。
---
## 1. 本次目标
1. 未登录用户可以正常进入平台首页并浏览公开内容。
2. 点击作品卡片时,若未登录,弹出登录弹窗;登录成功后继续进入刚才点击的作品。
3. 打开创作类型选择后,点击具体游戏类型开始创作时,若未登录,弹出登录弹窗;登录成功后继续刚才的创作动作。
4. 登录 UI 改成极简弹窗,只保留窗口标题、必要输入框、必要按钮、错误态与关闭能力。
5. 未登录态下不要继续请求“我的作品 / 个人看板 / 云端浏览历史 / 云端存档列表”这类受保护数据,避免首页公开态出现无意义报错。
---
## 2. 公开态与受保护动作边界
## 2.1 未登录允许访问
- 平台首页主视图
- 精选推荐
- 最新发布
- 创作类型选择弹窗本身的展示
- 本地浏览历史展示(若存在)
说明:
- “允许访问”只代表允许看,不代表允许进入作品详情、开始世界或创建内容。
- 首页公开态必须保持可读,不因账号接口 401 出现整屏报错。
## 2.2 未登录必须拦截
- 点击任意作品卡片
- 点击作品详情中的“开始游戏”
- 点击作品详情中的“继续创作 / 发布 / 下架 / 删除”等作者动作
- 点击创作类型卡片,开始进入具体创作工作台
- 其他后续新增的“进入世界 / 开始正式创作”入口
拦截方式统一为:
- 保持当前页面上下文不跳走
- 直接弹出登录弹窗
- 登录成功后自动继续刚才被拦截的动作
---
## 3. 登录弹窗设计
## 3.1 展示形态
- 使用居中的 modal 覆盖层
- 背景保留平台当前页面,只加遮罩和轻微模糊
- 移动端优先,弹窗宽度贴近屏幕边缘,底部和顶部留出安全边距
- 桌面端保持紧凑,不做双栏 hero不再单独占满整页
## 3.2 内容约束
弹窗内默认只保留:
- 标题:`登录账号`
- 手机号输入框
- 验证码输入框
- 获取验证码按钮
- 登录主按钮
- 微信登录按钮(当后端开放时)
- 图形验证码输入区(仅后端要求时出现)
- 错误提示
- 关闭按钮
明确不再保留:
- 品牌副标题
- 功能介绍段落
- 规则说明卡片
- “先登录再同步进度”这类描述性文案
- 占据视觉主体的装饰信息块
## 3.3 登录成功后的行为
- 手机号登录成功后,关闭弹窗
- 当前平台页面不刷新
- 若用户是被某个受保护动作拦截进入登录,则自动恢复该动作
- 若用户只是主动点“登录”按钮,则关闭弹窗并停留在当前页面
## 3.4 关闭行为
- 用户主动关闭弹窗时,只关闭弹窗,不改变当前平台页面
- 不清空首页浏览状态
- 不自动跳转到其他 tab
---
## 4. 前端状态约束
## 4.1 AuthGate
`AuthGate` 需要从“未登录整页拦截器”调整为“平台级账号状态提供器”:
- `checking / recovering`:仍可显示加载态,避免首屏闪烁
- `unauthenticated`:渲染平台内容,同时允许按需打开登录弹窗
- `ready`:渲染平台内容和账号能力
- `pending_bind_phone`:继续保留当前绑定手机号流程,不在这次入口改造里拆散
同时需要在 context 中提供:
- 当前用户
- 打开登录弹窗
- 打开账号面板
- `requireAuth(action)` 能力
`requireAuth(action)` 约束:
- 已登录:直接执行 `action`
- 未登录:弹出登录弹窗,并缓存 `action`
- 登录成功:自动执行缓存的 `action`
## 4.2 平台首页数据加载
`PreGameSelectionFlow` 在未登录时只读取:
- 公开作品广场
- 本地浏览历史
公开作品广场前端请求约束:
- `listCustomWorldGallery`
- `getCustomWorldGalleryDetail`
这两类公开请求必须走“公开只读请求”通道:
- 不主动附带 `Authorization`
- 不因本地 access token 失效去触发 `/api/auth/refresh`
- refresh cookie 缺失、refresh 失败、账号状态过期时,不能把首页公开作品广场一起拖成错误态
未登录时不读取:
- 自定义世界库
- 个人看板
- 云端浏览历史
- 云端运行时设置
- 云端存档快照
- 云端存档列表
未登录态的对应前台表现:
- “我的创作”显示空态,不显示账号接口错误
- “个人页”显示未登录态入口,可手动打开登录弹窗
- 音量等运行时设置继续使用本地缓存,不触发 `/api/runtime/settings`
- 未登录态不显示“继续远端存档”能力,也不触发 `/api/runtime/save/snapshot`
- 未登录态的“存档”Tab 只展示登录引导,不触发 `/api/runtime/profile/save-archives`
---
## 5. 代码落点
本次实现最少要覆盖:
- `src/components/auth/AuthGate.tsx`
- `src/components/auth/AuthUiContext.ts`
- `src/components/auth/LoginScreen.tsx`
- `src/components/game-shell/PreGameSelectionFlow.tsx`
- `src/components/game-shell/PlatformHomeView.tsx`
- `src/components/game-shell/PlatformCreationTypeModal.tsx`
测试至少覆盖:
- 未登录时平台首页仍能渲染
- 未登录点击作品卡片会打开登录弹窗
- 未登录点击创作类型卡片会打开登录弹窗
- 登录成功后会继续刚才被拦截的动作
---
## 6. 验收标准
1. 用户首次进入平台时,不会先看到整页登录页,而是能看到首页内容。
2. 未登录点击作品时,直接弹出登录弹窗,登录后自动进入对应作品流。
3. 未登录选择 RPG 创作类型时,直接弹出登录弹窗,登录后自动进入创作工作台。
4. 登录弹窗内没有介绍性大段文字,只剩必要输入与按钮。
5. 未登录态首页不会因个人接口失败而出现“读取个人看板失败”“读取作品库失败”之类报错。

View File

@@ -0,0 +1,118 @@
# 平台层 UI 去像素化刷新设计
更新时间:`2026-04-20`
## 1. 目标
本次刷新只覆盖平台层功能 UI不改游戏内 HUD、战斗、地图、剧情面板等像素风界面。
目标有 5 个:
1. 平台层正文与功能信息不再使用像素字体
2. 平台层不再使用像素九宫格边框、像素图标、像素背景纹理这类平台 chrome
3. 原有紫蓝深色方案沉淀为平台暗色主题
4. 新参考图沉淀为平台亮色主题:白色主面板、粉橘主强调、暖白背景、高亮图卡
5. 平台默认使用亮色主题,移动端保持现有布局结构不变,桌面端允许在不改变业务入口的前提下重组为控制台式平台壳层
## 2. 覆盖范围
本次统一按 `!gameState.worldType` 的平台态处理,覆盖:
- 平台首页 `PlatformHomeView`
- 作品详情 `PlatformWorldDetailView`
- 创作类型弹窗 `PlatformCreationTypeModal`
- 平台创作链路中的生成页、结果页、目录页、编辑弹窗
明确不覆盖:
- 进入世界后的游戏内 UI
- 地图、战斗、剧情面板、角色面板、背包面板等像素 RPG 界面
- 世界内容本身的数据图片、角色主图、场景图等作品内容素材
说明:
- “不再引用像素素材”指平台 chrome 不再依赖像素框、像素按钮、像素关闭图标、像素底纹等 UI 资源
- 作品内容图仍可展示,但平台层不再用 `image-rendering: pixelated` 强化像素感
## 3. 视觉原则
### 3.1 风格来源
直接对齐现有登录页和绑定手机号页的成熟样式,并吸收本次参考图的桌面端气质:
- 暗色主题:顶部与边缘的紫蓝径向高光 + 深色纵向渐变背景
- 亮色主题:暖白控制台外壳 + 粉橘主强调 + 轻紫细节高光
- 大圆角卡片
- 半透明玻璃质感
- 平台正文与功能信息统一使用 `Inter + Noto Serif SC`
- 左上角品牌区允许使用专用像素字标组件或直接使用 `Fusion Pixel` 文本,但仅限品牌 logo不向正文、按钮、标签扩散
- 品牌 logo 只能复用游戏现有 `Fusion Pixel`,不允许再引入第二套像素字体文件
主题基准:
- 暗色主题:
底色以深靛蓝、深紫黑为主,高光以亮紫、蓝青为主
- 亮色主题:
底色以暖白、浅粉白、浅橘白为主,强调色以高饱和粉色、橘粉色为主,局部可带少量紫色作装饰
- 平台默认主题使用亮色主题;暗色主题保留为可切换方案,不作为当前默认展示
### 3.2 排版
- 平台层正文、按钮、说明、功能标签统一使用非像素字体
- 左上角 `叙世 / GENARRATIVE` 品牌字标允许单独做成像素化 logo
- `GENARRATIVE``叙世` 都优先直接使用游戏内同款 `Fusion Pixel`
- 品牌字标默认保持正常像素字观感,禁止再叠双层粗阴影或手动加粗到影响识别
- 品牌字标直接使用字体文件内原字形,不额外做运行时描字、轮廓拼字或伪粗体处理
- 主标题保留明显层级,但不再做像素描边效果
- 微型标签维持高字距英文/中文短标签,用来保留产品感和秩序感
### 3.3 组件约束
- 面板:使用玻璃卡片,不再用九宫格像素框
- 按钮:使用圆角胶囊按钮或渐变主按钮,不再用像素按钮框
- 图标:优先使用 `lucide-react`
- Tab移动端底部结构不变但图标与底座改成非像素风桌面端切换为左侧纵向导航轨道
- 弹窗:沿用登录页的圆角浮层和半透明遮罩,不再使用像素弹窗边框
- 桌面壳层:首页允许增加顶部工具栏、左侧导航轨、中央内容舞台与右侧趋势面板的组合
- 登录页、绑定手机号、账户弹窗、平台详情、创作生成页、结果页、编辑弹窗都必须共享同一套平台主题 token禁止再各自写一套独立旧色板
- 创作中心、Agent 工作台、草稿详情抽屉、资产工坊、启动弹窗、生成弹窗这类二三级平台面板必须显式挂载平台主题壳层或平台 remap 容器,禁止直接在局部面板里写死旧深色 modal 底和旧输入框底色
- 平台“我的”页中的“设置”入口必须打开真正的设置面板;账号信息、设备管理、安全状态属于设置面板中的分区,不允许再把账号信息弹层直接充当设置页
- 设置面板必须支持平台亮色 / 暗色主题切换,并复用同一套平台 token 驱动登录页、首页、详情页与二三级面板
- 首页移动端底部 Tab 与桌面侧边导航的图标底座、图标颜色、文字状态必须全部由平台 token 驱动;暗色主题下不得出现过浅底座和错误文字色,亮色主题下不得残留旧灰蓝 inactive 状态
- 首页、存档页、作品详情这类平台主导航与局部 Tab 的 active fill、active shadow、icon shell fill 必须全部来自主题 token暗色主题禁止继续复用亮色主题的粉橘高光、白色 active 底座
- 创作链路中的吸顶返回栏、目录 Tab 条、搜索工具条也必须走平台亮暗主题 token暗色主题禁止继续写死暖白渐变或浅粉背景作为顶部衬底
- “我的”页账号主卡必须跟随平台亮 / 暗主题联动,不允许继续写死浅色渐变卡面与 `slate` 系按钮
## 4. 交互与布局约束
- 移动端保持原有页面布局层级、区块顺序、操作入口位置不变
- 桌面端首页允许参考图示重组为“顶部工具栏 + 左侧纵向导航 + 主 Hero 卡 + 右侧趋势列表 + 下方内容卡组”
- 桌面端的重组只改变视觉排布;自 `2026-04-19` 起平台主入口调整为“首页 / 创作 / 存档 / 我的”,四个入口的操作路径都必须保持清晰稳定
- 移动端优先,底部 tab 与主卡片点击区域不能缩小
- 不在平台 UI 面板里额外堆砌规则说明
- 所有视觉替换必须是局部补丁,不做无必要的大规模结构重写
## 5. 实现约束
- 平台态从 `fusion-pixel-app` 中隔离,避免被全局像素字体覆盖
- 品牌区禁止新增额外像素字体包;平台层只允许保留现有 `public/fusion-pixel.ttf` 这一份像素字体资源
- 平台态背景不再使用 `/UI/Background_fill.png`
- 新样式优先沉淀为平台专用 class / theme token避免把游戏内像素 class 改坏
- 平台默认挂载亮色主题 class旧紫蓝方案保留为暗色主题 class
- 亮色主题需要补齐统一的 overlay、progress track、status pill token登录弹层与二三级功能面板禁止继续沿用旧深色遮罩与紫蓝强调残留
- 亮色主题下平台壳层与各个 Tab 页的 page stage 必须以暖白底为主,禁止继续让高饱和深粉底或旧深色底透成页面主背景
- 亮色主题下平台主内容区、page stage、移动端底部 Tab 容器都必须使用接近实色的暖白底,禁止继续用高透明度浅色层叠在深底上造成整体发灰
- 平台态中仍保留旧 Tailwind 深色类的历史组件,必须通过平台 remap 容器或平台专用 class 统一收口,不能放任 `bg-[#111318]``bg-black/*``bg-white/*` 这类旧类在亮色主题下直接裸露
- 编辑弹窗保留业务结构与表单逻辑,只替换壳层样式
## 6. 验收标准
达到以下结果才算完成:
1. 除左上角品牌像素字标外,平台首页、详情、登录、绑定手机号、账户弹窗、创作入口、创作结果页不再出现像素字体
2. 平台层按钮、面板、关闭按钮、底部 tab 不再依赖像素 UI 素材
3. 平台默认展示亮色主题,暗色主题保留为独立主题方案
4. 平台层二三级面板、表单、状态卡、弹窗与登录体系不再残留旧金橙 / 青蓝 / 深黑混搭方案
5. 平台层世界封面与角色预览不再使用 `pixelated` 渲染
6. 游戏内像素 UI 保持原样,不出现误改
7. 手机端布局保持稳定,桌面端在参考图方向下完成控制台化重组

View File

@@ -10,9 +10,11 @@
- [CUSTOM_WORLD_TEMPLATE_DECOUPLING_AND_CROSS_GENRE_GENERALIZATION_DESIGN_2026-04-08.md](./CUSTOM_WORLD_TEMPLATE_DECOUPLING_AND_CROSS_GENRE_GENERALIZATION_DESIGN_2026-04-08.md):把自定义世界从武侠/仙侠模板依赖迁到跨题材通用设定层的优化设计。
- [CUSTOM_WORLD_SELF_OWNED_SETTING_LAYER_OPTIMIZATION_2026-04-08.md](./CUSTOM_WORLD_SELF_OWNED_SETTING_LAYER_OPTIMIZATION_2026-04-08.md):把模板依赖逐步迁成自定义世界自有设定层,并保证不破坏当前生成流程的优化方案。
- [AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md](./AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md):运行时物品生成系统重设计。
- [LEVEL_PROGRESS_AND_CHAPTER_NPC_AUTO_SCALING_DESIGN_2026-04-20.md](./LEVEL_PROGRESS_AND_CHAPTER_NPC_AUTO_SCALING_DESIGN_2026-04-20.md):等级成长、章节经验节奏与 NPC 自动定级设计。
- [RPG_NARRATIVE_PLANNING_FULL_PIPELINE_WORKFLOW_2026-04-12.md](./RPG_NARRATIVE_PLANNING_FULL_PIPELINE_WORKFLOW_2026-04-12.md):专业剧情策划构建 RPG 游戏全剧情的工作流程与交付模板。
- [EQUIPMENT_BUILD_AND_FORGE_LOOP_SYSTEM_DESIGN.md](./EQUIPMENT_BUILD_AND_FORGE_LOOP_SYSTEM_DESIGN.md):配装构筑与合成/锻造闭环设计。
- [COMPANION_FIRST_CONTACT_RELATIONSHIP_AND_PRIVATE_CHAT_DESIGN_2026-04-04.md](./COMPANION_FIRST_CONTACT_RELATIONSHIP_AND_PRIVATE_CHAT_DESIGN_2026-04-04.md):角色首遇感、关系分层解锁、私聊系统设计。
- [NPC_HIGH_AFFINITY_CHAT_QUEST_OFFER_FLOW_2026-04-19.md](./NPC_HIGH_AFFINITY_CHAT_QUEST_OFFER_FLOW_2026-04-19.md):高好感角色在聊天内自然提出委托,并支持查看、更换、放弃、领取的流程设计。
- [SCENE_CHAPTER_LOOP_AND_FIRST_ENTRY_CHAPTER_QUEST_DESIGN_2026-04-08.md](./SCENE_CHAPTER_LOOP_AND_FIRST_ENTRY_CHAPTER_QUEST_DESIGN_2026-04-08.md):把每个场景收束成章节单元,并在首进场景时开启章节任务的设计稿。
- [SCENE_CHAPTER_BENCHMARK_GAP_AND_AI_NATIVE_EXPERIENCE_SUPPLEMENT_2026-04-08.md](./SCENE_CHAPTER_BENCHMARK_GAP_AND_AI_NATIVE_EXPERIENCE_SUPPLEMENT_2026-04-08.md):对标仙剑、博德之门、黑神话,分析单场景章节的体验缺口,并给出 AI 原生补强方案。
- [npc-conversation-situation-draft.md](./npc-conversation-situation-draft.md)NPC 对话阶段和情景注入草案。
@@ -27,6 +29,8 @@
- 做自定义世界去模板依赖、跨题材泛化、兼容迁移设计时,优先看新增的去模板化优化设计稿。
- 做“模板依赖如何真正变成自定义世界自有设定层”的具体迁移方案时,优先看新增的自有设定层优化方案。
- 做角色关系、同伴互动、对话表现时,先看后两份。
- 做“高好感聊天里如何顺着上下文自然抛出委托、并让任务在聊天内领取”的需求时,优先看新增的聊天委托流程设计稿。
- 做剧情引擎章节化、场景闭环、章节任务接入时,优先看新增的场景章节设计稿。
- 做“单章节体验还缺什么、该补哪种情感 / 抉择 / 试炼模块”时,优先看新增的章节对标补强设计稿。
- 做等级成长、任务/击败敌对 NPC 发经验、章节经验速度评估、NPC 自动定级时,优先看新增的等级系统设计稿。
- 如果要判断是否符合目标,再和 `docs/prd/` 中对应 PRD 对照阅读。

View File

@@ -113,4 +113,12 @@
---
## 8. 2026-04-18 补充记录
- `GameShellRuntime` 进入游戏壳时,会主动隐藏认证层提供的右上角全局账号信息条。
- 原因不是账号功能下线,而是这个悬浮条会遮挡冒险主场景内容,移动端更明显。
- 账号相关入口保留在平台首页 / 个人页内部按钮与账号弹窗,不再占用游戏 HUD 区域。
---
*文档目的:交接给下一个 Agent 时,优先读本文件 + `UI_CODING_STANDARD.md`,再改 `uiAssets.ts` / `App.tsx` / `index.css`。*

View File

@@ -89,6 +89,12 @@
- 在底部工具区,队伍/背包改成 icon 后更紧凑。
- 但必须保留 `aria-label`,保证语义清晰、后续也方便测试。
### 4.5 冒险主场景不要挂右上角账号悬浮条
- 冒险页右上角属于画面演出和战斗/剧情信息的高频观察区。
- 全局账号信息条挂在这里,会直接压住场景、敌人血条或顶部提示,手机端尤其明显。
- 结论:
账号入口应收回平台首页、个人页或设置面板,不要在实际冒险主场景常驻悬浮显示。
## 5. 队伍面板经验
### 5.1 移动端成员列表不能太“卡片化”

View File

@@ -76,6 +76,19 @@
- 流程层优先按“职责”拆,不按“文件长度”拆。
- 状态修改逻辑尽量集中到 hook 内,不要散落在多个组件按钮回调里。
## 3.1 AI 草稿数据进列表前,要先补本地稳定标识
自定义世界、角色草稿、澄清问题、生成结果卡片这类数据,在草稿态或兼容旧数据时,`id` 可能为空。
经验:
- React 列表的 `key` 不要直接裸用这类可能为空的 `id`
- 当前选中态、草稿缓存、轮播焦点也不要直接绑空 `id`,否则会出现“点了第二张卡,结果还是第一张卡被选中”的错位。
- 更稳的做法是:
- 业务数据层尽量补齐真实 id
- UI 层再补一层本地稳定 `selectionKey` / fallback render key
- fallback 至少带上 `index + 名称种子`,保证当前列表内唯一
## 4. AI 只适合生成叙事,不适合决定关键规则
实践中最稳定的策略是:

View File

@@ -1,6 +1,13 @@
# 账号系统与登录入口重构 PRD
更新时间:`2026-04-09`
更新时间:`2026-04-19`
> 2026-04-19 入口策略补充:
> 平台首页现调整为“未登录也可浏览公开首页”,不再要求用户先登录才能进入平台。
> 登录拦截点改为“点击作品进入详情/世界”与“选择游戏类型开始创作”等受保护动作触发时再弹出登录弹窗。
> 本次入口策略与弹窗约束以
> [`docs/design/PLATFORM_HOME_PUBLIC_BROWSE_AND_LOGIN_MODAL_GATING_DESIGN_2026-04-19.md`](../design/PLATFORM_HOME_PUBLIC_BROWSE_AND_LOGIN_MODAL_GATING_DESIGN_2026-04-19.md)
> 为准;本 PRD 中“先登录再进入开始界面”的旧表述不再作为当前前台入口实现依据。
## 0. 目标
@@ -175,7 +182,7 @@ MVP 阶段建议采用最稳妥规则:
4. 微信后强制绑定手机号
5. 账号会话管理
6. 账号与存档/自定义世界/运行时设置统一绑定
7. 基础账号中心与退出登录
7. 基础账号中心、平台设置面板与退出登录
## 3.2 本期不做
@@ -460,6 +467,7 @@ MVP 阶段建议至少提供一个轻量账号中心,包含:
- 已绑定手机号(脱敏展示)
- 微信绑定状态
- 最近登录时间
- 平台设置面板中的亮色 / 暗色主题切换
- 退出登录
二期可以再补:

View File

@@ -1,6 +1,6 @@
# AI 角色形象与角色动画 MVP PRD
更新时间:`2026-04-04`
更新时间:`2026-04-19`
## 0. 一句话结论
@@ -254,49 +254,49 @@ MVP 支持三种主形象输入方式:
MVP 必须与当前项目可扮演角色动作槽位对齐。
第一版要求以下基础动作槽位不能为空
当前落地实现补充约束(`2026-04-20`
| 动作槽位 | 是否必填 | 备注 |
| --- | --- | --- |
| `idle` | 必填 | 循环动作 |
| `acquire` | 必填 | 可由短变体衍生 |
| `attack` | 必填 | 一次性动作 |
| `run` | 必填 | 循环动作 |
| `jump` | 必填 | 一次性动作 |
| `double_jump` | 必填 | 可由跳跃二次变体生成 |
| `jump_attack` | 必填 | 一次性动作 |
| `dash` | 必填 | 一次性动作 |
| `hurt` | 必填 | 一次性动作 |
| `die` | 必填 | 一次性动作 |
| `climb` | 必填 | 可由模板生成 |
| `wall_slide` | 必填 | 可由攀爬停帧变体生成 |
- 角色资产工坊固定生成入口仍为 `idle / run / attack / die`
- `run / attack` 是固定基础必生成动作
- `idle / die` 改为固定可选动作,不再作为发布硬门槛
- `idle` 未生成时默认直接使用主图静止显示
- `die` 未生成时默认播放一段基于主图的倒地过渡动画,并最终停在翻转倒地姿态
- 角色已配置的每个技能,都必须在技能编辑面板里补出对应动作预览
- 图生视频默认走火山方舟 `Seedance` 首尾帧方案
- 接口请求体中的两张参考图分别固定为 `first_frame / last_frame`
- 固定参数为 `1:1``480p``4 秒`、单次 `1` 个视频
- 提示词中的动作名统一传英文动作名
这里“不能为空”指的是
第一版动作生成按下面两层规则落地
- 每个槽位必须最终指向一套可播放的资源
- 允许少量槽位由近似动作衍生
- 但不允许在运行时读到空动画映射
| 类别 | 动作槽位 | 是否必填 | 备注 |
| -------- | ------------------------------- | -------- | -------------------------------------------------- |
| 基础动作 | `run` | 必填 | 角色移动主循环动作 |
| 基础动作 | `attack` | 必填 | 角色普通攻击主动作 |
| 技能动作 | `skills[*].actionPreviewConfig` | 必填 | 当前角色每个已配置技能都要有独立动作资源 |
| 可选动作 | `idle` | 可选 | 缺失时默认走主图静止待机 |
| 可选动作 | `die` | 可选 | 缺失时默认走主图倒地过渡动画,最终停在翻转倒地姿态 |
这里“必生成”指的是:
- `run / attack` 必须最终指向可播放资源
- 每个已配置技能都必须带独立 `actionPreviewConfig`
- 发布判定不再要求 `idle / die` 一定存在动画映射
- 运行时仍然不能出现无可用表现;`idle / die` 的缺口由默认兜底承担
## 8.2 技能动作要求
本期不要求自动补齐
本期不要求把整套固定技能枚举一次性自动补齐,但对“角色当前实际配置的技能”改为必做
- `skill1`
- `skill1_jump`
- `skill1_bullet`
- `skill1_bullet_fx`
- `skill2`
- `skill2_jump`
- `skill3`
- `skill3_jump`
- `skill3_bullet`
- `skill3_bullet_fx`
- `skill4`
- 不要求预先把 `skill1 / skill2 / skill3 / skill4` 这套历史枚举全部补满
- 只要求当前角色 `skills` 数组里的每个技能都生成独立动作预览
- 技能动作生成入口继续放在技能编辑面板逐个处理,不塞进固定四按钮里
结论:
- 技能动作本期可选
- 基础动作本期必做
- 技能动作从“固定枚举可选”调整为“按角色已配技能必做”
- 固定基础动作收敛为 `run / attack`
- `idle / die` 保留为可选增强动作
## 8.3 动作生成方式
@@ -345,7 +345,6 @@ MVP 支持两种方式:
- `attack`
- `jump_attack`
- `hurt`
- `die`
要求末帧清晰,不与下一动作切换冲突。
@@ -490,7 +489,7 @@ type GeneratedCharacterAnimationAsset = {
- `src/components/CharacterAnimator.tsx`
- `src/types/characters.ts`
- `src/data/characterOverrides.json`
- `scripts/dev-server/localApiPlugins.ts`
- `server-node/src/modules/assets/**`
建议新增:
@@ -598,7 +597,7 @@ type GeneratedCharacterAnimationAsset = {
目标:
-基础动作槽位全部非空,并可一键发布
-必生成动作全部就绪,并为 `idle / die` 提供明确默认兜底
产出:
@@ -634,4 +633,3 @@ type GeneratedCharacterAnimationAsset = {
- 路径清晰
- 能真正进入当前仓库
- 后续可以在此基础上再加技能动作、剧情演出和多供应商增强路线

View File

@@ -200,7 +200,7 @@
`CustomWorldAgentDraftDetailPanel` 中,当当前卡类型为:
```ts
kind === 'character'
kind === 'character';
```
显示按钮:
@@ -239,11 +239,19 @@ kind === 'character'
基于主图生成当前工坊支持的核心动作:
1. `run`
2. `attack`
可选增强动作:
1. `idle`
2. `run`
3. `attack`
4. `hurt`
5. `die`
2. `die`
补充约束:
1. `run / attack` 为固定必生成动作
2. 角色已配置技能时,对应技能动作也属于必生成动作
3. `idle / die` 只作为可选增强,缺失时分别走主图静止 / 主图倒地过渡动画兜底,死亡动画最终停在翻转倒地姿态
### 阶段 D动作发布
@@ -350,15 +358,15 @@ type CustomWorldRoleAssetStatus =
发布主图成功后,必须写回:
```ts
imageSrc
generatedVisualAssetId
imageSrc;
generatedVisualAssetId;
```
发布动作成功后,必须写回:
```ts
generatedAnimationSetId
animationMap
generatedAnimationSetId;
animationMap;
```
### 明确要求
@@ -440,8 +448,8 @@ type SyncRoleAssetsResult = {
### 输入
```ts
buildRoleAssetStudioContext(snapshot, roleId)
applyRoleAssetPublishResult(snapshot, payload)
buildRoleAssetStudioContext(snapshot, roleId);
applyRoleAssetPublishResult(snapshot, payload);
```
### 说明
@@ -465,8 +473,8 @@ applyRoleAssetPublishResult(snapshot, payload)
### 导出函数建议
```ts
rebuildRoleAssetCoverage(draftProfile)
mergeRoleAssetIntoDraftProfile(draftProfile, payload)
rebuildRoleAssetCoverage(draftProfile);
mergeRoleAssetIntoDraftProfile(draftProfile, payload);
```
## 10.3 修改 `customWorldAgentOrchestrator.ts`
@@ -598,7 +606,7 @@ showRoleAssetStudio: boolean;
3. 统一回调:
```ts
onPublishSuccess(payload)
onPublishSuccess(payload);
```
### `onPublishSuccess` 最小字段

View File

@@ -39,9 +39,11 @@
目标用户分三类:
1. 轻创作者
- 有世界灵感,但不擅长结构化填表
2. 中度创作者
- 愿意精修角色、地点、主线第一幕,但不想维护大量底层字段
3. 重度创作者
@@ -138,37 +140,48 @@
本次 PRD 必须复用以下现有基础:
1. `src/services/customWorldCreatorIntent.ts`
- 已有创作者意图、锚点包、锁定状态的基础结构
2. `src/types/customWorld.ts`
- 已有 `creatorIntent / anchorPack / lockState / generationMode / generationStatus`
3. `src/services/aiService.ts`
- 已有自定义世界 session 与生成 API 客户端
4. `server-node/src/services/customWorldSessionStore.ts`
- 已有澄清问题与 session 的基础概念
5. `server-node/src/services/customWorldGenerationService.ts`
- 已有分阶段生成骨架
6. `src/components/game-shell/PreGameSelectionFlow.tsx`
- 已有世界创建流程入口
7. `src/components/CustomWorldResultView.tsx`
- 已有结果页壳层
8. `src/components/CustomWorldRoleAssetStudioModal.tsx`
- 已有角色主图与核心动作资产工坊原型
9. `src/services/ai.ts`
- 已有 `generateCustomWorldSceneImage(...)` 场景图生成入口
10. `server-node/src/modules/assets/characterAssetRoutes.ts`
- 已有角色主图发布、角色动作发布、动作模板等资产路由
- 已有角色主图发布、角色动作发布、动作模板等资产路由
11. `server-node/src/routes/runtimeRoutes.ts`
- 已有 `/custom-world/scene-image` 场景背景图生成路由
- 已有 `/custom-world/scene-image` 场景背景图生成路由
## 3.2 必须替换或重构的现有行为
@@ -220,6 +233,7 @@
最终必须输出两类产物:
1. 创作工作产物
- 世界圣经摘要
- 关键角色卡
- 关键地点卡
@@ -478,9 +492,11 @@ type CustomWorldAssetPriorityTier = 'hero' | 'featured' | 'supporting';
判定规则:
1. `hero`
- 所有 `playableNpcs`
2. `featured`
- 被锁定的 `storyNpcs`
- 主线第一幕直接关联的 `storyNpcs`
- 势力代表角色
@@ -499,6 +515,7 @@ type CustomWorldScenePriorityTier = 'key' | 'supporting';
判定规则:
1. `key`
- `camp`
- 被锁定的 `landmark`
- 主线第一幕直接关联的 `landmark`
@@ -530,32 +547,43 @@ type CustomWorldScenePriorityTier = 'key' | 'supporting';
### 动作抽卡策略
角色动作不能一开始就把完整核心动作集全部抽出来
角色动作不能一开始就把所有动作一次性抽完
必须采用两段式
必须采用“先必需、再增强”的两层策略
#### 阶段 A动作试片
#### 阶段 A基础必需动作
每个角色先生成:
每个角色先生成:
1. `idle`
1. `run`
2. `attack`
用途:
1. 检查角色一致性是否稳定
2. 检查动作风格是否匹配
2. 检查移动和出手两条主动作是否可用
3. 检查武器、衣摆和轮廓是否容易漂移
#### 阶段 B完整核心动作集
#### 阶段 B技能动作补齐
只有当动作试片确认通过后,才允许生成:
当角色基础动作通过后,再逐个补当前角色已经配置的技能动作。
1. `run`
2. `hurt`
3. `die`
要求:
加上已确认的 `idle / attack`,组成当前阶段完整核心动作集。
1. 每个技能都必须有独立 `actionPreviewConfig`
2. 技能动作入口放在技能编辑面板,不并入固定四按钮
#### 可选增强动作
以下动作不再作为发布硬门槛,可按需要补:
1. `idle`
2. `die`
默认兜底:
1. `idle` 缺失时使用主图静止
2. `die` 缺失时使用主图倒地过渡动画,最终停在翻转倒地姿态
### 场景图抽卡策略
@@ -616,16 +644,26 @@ type CustomWorldScenePriorityTier = 'key' | 'supporting';
发布前,每个角色至少需要以下动作槽位可用:
1. `idle`
2. `run`
3. `attack`
4. `hurt`
5. `die`
1. `run`
2. `attack`
3. 当前角色 `skills` 中每个技能的 `actionPreviewConfig`
判定方式:
1. `generatedAnimationSetId` 非空
2. `animationMap`以上 5 个槽位都存在有效映射
2. `animationMap`至少存在有效的 `run / attack`
3. `skills` 数组里的每个技能都带有效 `actionPreviewConfig`
可选动作:
1. `idle`
2. `die`
说明:
1. `idle / die` 不再是发布硬门槛
2. `idle` 缺失时运行时默认使用主图静止
3. `die` 缺失时运行时默认播放主图倒地过渡动画,最终停在翻转倒地姿态
说明:
@@ -664,34 +702,34 @@ type CustomWorldAgentStage =
## 6.2 状态迁移规则
| 当前阶段 | 触发 | 下一阶段 |
| --- | --- | --- |
| `collecting_intent` | 最小锚点不足Agent 追问 | `clarifying` |
| `clarifying` | 用户补齐锚点 | `foundation_review` |
| `collecting_intent` | 用户信息已足够并请求底稿 | `foundation_review` |
| `foundation_review` | 用户精修关键对象 | `object_refining` |
| `object_refining` | 用户请求生成角色或场景资产 | `visual_refining` |
| `visual_refining` | 关键角色与场景资产进入可用状态 | `long_tail_review` |
| `object_refining` | 用户明确跳过人工精修并走自动补齐 | `long_tail_review` |
| `long_tail_review` | 用户请求发布 | `ready_to_publish` |
| `ready_to_publish` | 发布成功 | `published` |
| 任意阶段 | 发生不可恢复错误 | `error` |
| 当前阶段 | 触发 | 下一阶段 |
| ------------------- | -------------------------------- | ------------------- |
| `collecting_intent` | 最小锚点不足Agent 追问 | `clarifying` |
| `clarifying` | 用户补齐锚点 | `foundation_review` |
| `collecting_intent` | 用户信息已足够并请求底稿 | `foundation_review` |
| `foundation_review` | 用户精修关键对象 | `object_refining` |
| `object_refining` | 用户请求生成角色或场景资产 | `visual_refining` |
| `visual_refining` | 关键角色与场景资产进入可用状态 | `long_tail_review` |
| `object_refining` | 用户明确跳过人工精修并走自动补齐 | `long_tail_review` |
| `long_tail_review` | 用户请求发布 | `ready_to_publish` |
| `ready_to_publish` | 发布成功 | `published` |
| 任意阶段 | 发生不可恢复错误 | `error` |
## 6.3 阶段显示规则
前端顶部摘要区必须展示当前阶段中文标签:
| 阶段 | 展示文案 |
| --- | --- |
| 阶段 | 展示文案 |
| ------------------- | ------------ |
| `collecting_intent` | 收集世界锚点 |
| `clarifying` | 补充关键设定 |
| `clarifying` | 补充关键设定 |
| `foundation_review` | 校对世界底稿 |
| `object_refining` | 精修关键对象 |
| `visual_refining` | 生成视觉资产 |
| `long_tail_review` | 补全长尾内容 |
| `ready_to_publish` | 准备发布 |
| `published` | 已发布 |
| `error` | 处理异常 |
| `object_refining` | 精修关键对象 |
| `visual_refining` | 生成视觉资产 |
| `long_tail_review` | 补全长尾内容 |
| `ready_to_publish` | 准备发布 |
| `published` | 已发布 |
| `error` | 处理异常 |
---
@@ -980,7 +1018,15 @@ interface SendCustomWorldAgentMessageResponse {
type CustomWorldAgentActionRequest =
| { action: 'lock_cards'; cardIds: string[] }
| { action: 'unlock_cards'; cardIds: string[] }
| { action: 'regenerate_scope'; scope: 'focus_card' | 'long_tail_npcs' | 'long_tail_landmarks' | 'sidequest_seeds'; targetCardId?: string | null }
| {
action: 'regenerate_scope';
scope:
| 'focus_card'
| 'long_tail_npcs'
| 'long_tail_landmarks'
| 'sidequest_seeds';
targetCardId?: string | null;
}
| { action: 'draft_foundation' }
| { action: 'generate_role_assets'; roleIds: string[] }
| {
@@ -1909,9 +1955,12 @@ Agent 会话每次 operation 完成后自动保存 session snapshot。
15. `src/components/custom-world-agent/CustomWorldAgentDraftDetailPanel.tsx`
16. `src/components/custom-world-agent/CustomWorldSceneAssetStudioModal.tsx`
17. `src/components/CustomWorldRoleAssetStudioModal.tsx`
- 改成 Agent 可调用版
- 改成 Agent 可调用版
18. `src/components/asset-studio/characterAssetWorkflowPersistence.ts`
- 继续复用现有资产接口客户端
- 继续复用现有资产接口客户端
## 15.3 backend

View File

@@ -0,0 +1,233 @@
# AI Native 战斗单行为 Function PRD2026-04-18
## 1. 目标
本次迭代把战斗中的 function 从“战术风格 function”收敛为“单次可直接结算的原子行为 function”。
核心目标:
1. 战斗中一次点击只完成一个明确行为,不再做连续多轮击打、连续多 actor 轮转的 function 设计。
2. 战斗中除逃跑外,不再为每次动作额外触发剧情推理,而是直接结算数值并刷新下一轮战斗选项。
3. 只有在逃跑成功或战斗正式结束后,才触发一次剧情推理,生成脱战后的 storyText 与后续剧情选项。
---
## 2. 新战斗 option 池
`inBattle = true` 时,默认战斗选项池固定收敛为以下结构,顺序按下列规则输出:
1. `battle_attack_basic`
2. `battle_recover_breath`
3. `inventory_use`
4. `battle_use_skill`
5. `battle_escape_breakout`
其中第 4 项不是单个 option而是“每个技能一个 option 实例”。
### 2.1 普通攻击
- functionId`battle_attack_basic`
- 含义:不消耗灵力的基础攻击。
- 结算:直接结算一次基础伤害。
- 不触发剧情推理。
### 2.2 恢复
- functionId`battle_recover_breath`
- 含义:本回合做恢复与节奏调整。
- 结算:直接恢复血量/灵力,并推进技能冷却。
- 不触发剧情推理。
### 2.3 使用物品
- functionId`inventory_use`
- 含义:战斗中直接使用一个可结算的消耗品。
- 本期战斗选项池只给一个“推荐可用物品” option不展开整包物品列表。
- option 必须携带 `runtimePayload.itemId`
- 若当前没有可用消耗品,则仍保留该项,但以 disabled 态展示。
- 不触发剧情推理。
### 2.4 使用技能
- functionId`battle_use_skill`
- 每个角色技能都生成一个独立 option。
- option 文案直接对应技能名,不再包装成“稳扎试探 / 破架 / 终结窗口”这类抽象战术文案。
- option 必须携带 `runtimePayload.skillId`
- 若技能因蓝量不足或冷却中不可用,仍保留该 option但以 disabled 态展示。
- 点击后直接结算该技能本次效果,不触发剧情推理。
### 2.5 逃跑
- functionId`battle_escape_breakout`
- 含义:立即尝试脱离当前战斗。
- 结算:直接处理脱战结果。
- 逃跑成功后必须触发剧情推理。
---
## 3. 旧战斗 function 的处理
以下旧 function 不再进入默认战斗选项池:
- `battle_all_in_crush`
- `battle_guard_break`
- `battle_probe_pressure`
- `battle_feint_step`
- `battle_finisher_window`
兼容规则:
- 后端仍允许解析这些旧 functionId避免旧存档 / 旧 currentStory 点击时报错。
- 兼容结算统一按“单次攻击型行为”处理,不再保留旧的战术风格分支。
- 新生成的新选项、新 currentStory、新 viewModel 不再继续下发这些旧 function。
---
## 4. 单行为结算规则
### 4.1 单次点击的边界
一次点击只允许完成一次玩家声明行为:
- 普通攻击
- 恢复
- 使用物品
- 使用某个具体技能
- 逃跑
不再允许一次点击里继续串:
- 多轮连续攻击
- 多个技能连续释放
- 多个角色依次轮转
- 为了“表现完整”再补一整串额外战斗回合
### 4.2 回合感保留
虽然不再做连续多轮击打,但每个战斗动作仍然视为消耗了一个战斗回合,因此:
- 技能冷却要按“本次动作结束后”推进
- 恢复类动作可额外提供冷却推进收益
- 物品动作在战斗态下也算一次战斗回合
- 战斗中使用物品要先结算物品恢复 / buff / 额外冷却收益,再结算这一回合是否承受敌方单次反击
### 4.3 结果文本
ongoing battle 的本地/后端结果文本只负责说明这一次动作结算结果,不负责续写新的剧情段落。
例如:
- 你挥出一记普通攻击,命中前方敌人。
- 你稳住呼吸,恢复了部分气血与灵力。
- 你立刻服下疗伤药,当前状态回升。
- 你释放了【试锋斩】,直接压低了对方血线。
---
## 5. 剧情推理触发边界
### 5.1 不触发剧情推理的情况
当动作执行后仍处于战斗中时,以下 function 不触发剧情推理:
- `battle_attack_basic`
- `battle_recover_breath`
- `inventory_use`
- `battle_use_skill`
- 旧攻击类兼容 function
此时系统行为为:
1. 直接结算动作
2. 更新 HP / MP / CD / 物品 / 战斗状态
3. 直接刷新新一轮战斗选项
4. `storyText` 直接使用本次结算结果文本,不请求 AI 续写
### 5.2 必须触发剧情推理的情况
以下情况必须触发剧情推理:
1. `battle_escape_breakout` 执行后成功脱战
2. 任意战斗动作执行后,战斗正式结束
战斗正式结束包括:
- 敌方被击败
- 切磋结束
- 玩家被系统判定为本轮战斗已断开
此时系统行为为:
1. 先完成数值结算与状态落地
2. 再以“本次动作 + 本次战斗结果”为上下文触发一次剧情推理
3. 生成脱战后的 `storyText` 与非战斗态 options
---
## 6. 前后端数据约束
### 6.1 Option 扩展字段
为了支持“单 functionId + 多实例技能/物品 option”战斗 option 允许携带以下运行时字段:
- `runtimePayload`
- `skillId?: string`
- `itemId?: string`
- `disabled?: boolean`
- `disabledReason?: string`
### 6.2 前端职责
- 前端只负责展示 option、透传 `runtimePayload`、展示 disabled 态
- 前端不再自己推导战斗中“是否需要剧情推理”
- 前端不再把技能 option 重写成抽象战术描述
### 6.3 后端职责
- 后端负责生成战斗 option 池
- 后端负责解析 `skillId / itemId`
- 后端负责决定 battle ongoing / battle end / escape 后是否触发剧情推理
---
## 7. 本次落地范围
本期必须落地:
1. 后端 runtime 战斗 option 池切换到单行为模型
2. 后端 combat resolution 支持普通攻击 / 指定技能 / 恢复 / 战斗物品 / 逃跑
3. 后端只在逃跑或战斗结束后做剧情推理
4. 前端支持透传战斗 option 的 `runtimePayload`
5. 前端支持 disabled battle option 展示
6. 文档、测试同步更新
补充落地备注2026-04-20
- `inventory_use` 在战斗中按战斗动作结算,而不是按非战斗库存动作直接短路返回
- 战斗态 `inventory_use` 使用后要消费物品、累计 `itemsUsed`、推进 1 回合基础冷却,再叠加物品自带的 `cooldownReduction`
- 若物品动作结算后战斗仍在继续,`storyText` 直接等于本次战斗结果文本,不触发 AI 续写
本期不做:
1. 新增复杂目标选择 UI
2. 一次展开完整背包的战斗 item 子面板
3. 重做整套战斗演出系统
4. 把所有旧本地 battle plan 彻底删除到只剩后端一条链
---
## 8. 验收口径
满足以下条件视为本次需求完成:
1. 战斗中不再出现 `battle_all_in_crush / battle_guard_break / battle_probe_pressure / battle_feint_step / battle_finisher_window` 作为默认候选项。
2. 战斗默认候选项能看到:
- 普通攻击
- 恢复
- 使用物品
- 每个技能一个独立技能项
- 逃跑
3. 点击普通攻击 / 恢复 / 使用物品 / 技能时,不请求新的剧情推理,直接返回结算结果并刷新下一轮战斗 options。
4. 点击逃跑成功后,会请求一次剧情推理并切回脱战后的剧情 options。
5. 任意攻击或技能把敌人打死后,会请求一次剧情推理并切回脱战后的剧情 options。
6. 旧存档里残留旧 battle functionId 时,不会因为 function 不识别而报错。

View File

@@ -1,6 +1,6 @@
# AI 原生自定义世界创作页面 PRD
更新时间:`2026-04-13`
更新时间:`2026-04-20`
## 0. 文档目的
@@ -314,9 +314,11 @@ UI 主标题建议:
按优先级取:
1. `draftProfile.camp.imageSrc`
2. `draftProfile` 中可解析的营地
3. 角色主图或默认创作占位图
1. `draftProfile.cover.imageSrc`,当 `sourceType``uploaded / generated`
2. `draftProfile.camp.imageSrc` 作为默认封面底
3. 默认封面底图上叠加 `draftProfile.cover.characterRoleIds` 对应的角色主形象
4. 若未显式指定角色,则按 `playableNpcs` 顺序取前 `3` 个有主图的角色
5. 若开局场景图为空,则回退到第一张场景图;再不行才回退到首个角色主图或默认占位图
### 草稿卡片主操作
@@ -358,9 +360,78 @@ UI 主标题建议:
按优先级取:
1. 营地图
2. 第一可扮演角色立绘
3. 默认已发布作品占位图
1. `CustomWorldProfile.cover.imageSrc`,当 `sourceType``uploaded / generated`
2. 开局场景图作为默认封面底图
3. 默认封面底图上叠加 `cover.characterRoleIds` 指定的角色主形象
4. 若未显式指定角色,则按 `playableNpcs` 顺序取前 `3` 个有主图的角色
5. 若默认底图不可用,再回退到第一可扮演角色立绘或默认占位图
## 7.3 作品封面属性
作品必须新增显式封面属性,作为作者可编辑的作品资产,而不再只靠“卡片展示时临时猜一张图”。
建议字段:
```ts
type CustomWorldCoverSourceType = 'default' | 'uploaded' | 'generated';
interface CustomWorldCoverProfile {
sourceType: CustomWorldCoverSourceType;
imageSrc?: string | null;
characterRoleIds?: string[];
}
```
字段含义:
1. `sourceType = default`
- 表示继续使用系统默认封面布局
- `imageSrc` 不作为最终封面图使用
- 底图固定取“开局场景图”
- 前景角色取 `characterRoleIds`
2. `sourceType = uploaded`
- 表示作者直接上传了一张最终封面
- 卡片与详情页直接显示 `imageSrc`
- 不再叠加默认角色前景
3. `sourceType = generated`
- 表示作者通过 AI 生成了一张最终封面
- 卡片与详情页直接显示 `imageSrc`
- 不再叠加默认角色前景
## 7.4 默认封面布局
默认封面布局不是单纯“取开局场景图”,而是:
```text
开局场景图
+ 前景主角色主形象 2~3 个
+ 用于列表卡片和作品详情的统一封面预览
```
明确规则:
1. 默认封面底图固定优先取 `camp.imageSrc`
2. 默认前景角色固定从 `playableNpcs` 中取前 `3` 个有主图的角色
3. 若作者在 `cover.characterRoleIds` 中显式指定角色,则优先按指定顺序展示
4. 前端只负责把后端给出的“底图 + 角色主图列表”渲染成封面,不在前端做封面规则推理
5. 已上传或已生成的最终封面,直接作为成品图显示,不再做默认布局叠加
## 7.5 作者操作
作者在作品编辑态至少支持 4 个动作:
1. `使用默认封面`
2. `上传封面`
3. `AI 生成封面`
4. `重置为默认`
约束:
1. 上传和 AI 生成都必须把最终图片落到后端资产目录,前端不能长期持有 Data URL 作为作品封面
2. 重置为默认后,`sourceType` 回到 `default`
3. 草稿与已发布作品都读取同一份封面属性,不允许出现“草稿页是一个封面、发布后又自动换另一张”的漂移
### 已发布卡片主操作
@@ -395,6 +466,8 @@ interface CustomWorldWorkSummary {
subtitle: string;
summary: string;
coverImageSrc?: string | null;
coverRenderMode?: 'image' | 'scene_with_roles';
coverCharacterImageSrcs?: string[];
updatedAt: string;
publishedAt?: string | null;
stage?: string | null;
@@ -447,6 +520,25 @@ interface CustomWorldWorkSummary {
仅已发布作品为 `true`
### `coverRenderMode / coverCharacterImageSrcs`
用于支撑默认封面布局。
规则:
1. 当作品封面为上传或 AI 生成成图时:
- `coverRenderMode = image`
- `coverCharacterImageSrcs = []`
2. 当作品封面为默认布局时:
- `coverRenderMode = scene_with_roles`
- `coverImageSrc = 开局场景图`
- `coverCharacterImageSrcs = 需要叠加的角色主图列表`
一句话:
**后端负责告诉前端“这张封面该怎么画”,前端只负责把它画出来。**
---
## 9. 后端接口设计
@@ -484,6 +576,29 @@ interface ListCustomWorldWorksResponse {
- 当前用户作品量预计不大
- 先把结构做稳,比先做分页更重要
### 公开浏览与登录边界
创作首页与世界选择页必须拆分两类数据:
1. 公开浏览数据
2. 当前用户私有数据
其中以下接口必须定义为公开只读:
- `GET /api/runtime/custom-world-gallery`
- `GET /api/runtime/custom-world-gallery/:ownerUserId/:profileId`
明确约束:
1. 未登录用户进入世界选择页时,也必须能读取公开作品广场
2. 公开作品广场读取不能依赖 access token也不能因为 refresh 失败返回 401
3. 已发布作品详情允许未登录用户查看
4. 只有“继续创作 / 发布 / 下架 / 删除 / 查看我的草稿 / 查看我的统计”等私有能力必须要求登录
也就是说:
**平台首页要支持“先浏览公开作品,再决定是否登录进入世界或开始创作”。**
## 9.3 数据来源
### 草稿来源
@@ -677,8 +792,9 @@ type SelectionStage =
1. 新建作品区位于首屏
2. tabs 横向可滚
3. 作品卡优先单列
3. 平台“创作”页中的“我的创作”列表在移动端至少双列展示,不能继续沿用横向滚动卡片的固定宽度
4. 不使用桌面化大表格
5. 双列卡片必须采用紧凑栅格布局,标题、状态、时间允许换行或截断,但不能横向溢出或出现参差错位
## 12.2 页面保持清爽

View File

@@ -0,0 +1,728 @@
# AI 原生场景多幕配置与 NPC 相遇聊天流程 PRD
更新时间:`2026-04-20`
## 0. 文档目的
这份 PRD 用于把下面几条已经存在但还没真正接成一条产品主链的设计,收束成一次可直接编码的迭代:
- `docs/prd/AI_NATIVE_AGENT_FIRST_CUSTOM_WORLD_CREATOR_PRD_2026-04-12.md`
- `docs/prd/AI_NATIVE_SCENE_CHAPTER_GAMEPLAY_PRD_AND_EXECUTION_PLAN_2026-04-08.md`
- `docs/prd/AI_NATIVE_NPC_CHAT_SINGLE_TURN_SESSION_PRD_2026-04-18.md`
- `docs/design/NPC_HIGH_AFFINITY_CHAT_QUEST_OFFER_FLOW_2026-04-19.md`
- `docs/design/SCENE_CHAPTER_LOOP_AND_FIRST_ENTRY_CHAPTER_QUEST_DESIGN_2026-04-08.md`
本次要解决的不是再新建一套场景系统或聊天系统,而是把现有:
1. 创作工作区
2. 场景章节闭环
3. NPC 多轮聊天
4. 场景背景资产
5. 好感度关系流
接成一条新的稳定流程:
**每个场景由创作者在工具中配置为 `2~5` 幕;每一幕都绑定独立背景图和相遇 NPC 顺序;每一幕的第一个 NPC 视为主角色;运行时按幕切换背景和可遇对象,并根据主角色当前好感度裁决聊天轮数与第 5 轮收束方式。**
这份文档必须能直接指导后续创作工具和游戏流程改造,避免需求落地漂移。
---
## 1. 一句话定义
把当前“一个场景只有一层平铺内容”的创作与运行方式,升级成“一个场景内有多幕推进、每幕有独立视觉和主角色相遇规则”的章节内流程。
---
## 2. 本次目标
本次迭代必须同时满足以下目标:
1. 创作者可以在现有创作页面中为每个场景章节配置多幕内容。
2. 每一幕都必须绑定一张正式背景图。
3. 每一幕都可以配置玩家会遇到哪些 NPC并且保留顺序。
4. 每一幕配置的第一个 NPC 必须被系统认定为该幕主角色。
5. 运行时进入某一幕时,背景图和可遇 NPC 必须随幕切换。
6. 当前幕主角色的聊天轮数必须按好感度裁决,而不是继续完全沿用统一规则。
7. 好感度大于 `0` 的主角色,在相遇后进入无限轮聊天态,直到玩家主动退出。
8. 好感度小于 `0` 的主角色,在相遇后最多只允许聊天 `5` 轮,第 `5` 轮必须输出一段为后续剧情开展铺垫的收束回应。
9. 前端继续只负责展示,幕切换、聊天限制、幕进度与数据裁决全部由 Express 后端负责。
10. 默认复用现有创作页面、草稿抽屉、详情弹层、场景章节和聊天流程,不新开独立系统或新页面。
---
## 3. 明确不做
本次明确不做下面这些事:
1. 不新建独立的“场景编辑器”页面。
2. 不把幕推进逻辑放到前端本地计算。
3. 不让创作者直接编辑底层运行时 `ChapterState` 或聊天状态对象。
4. 不做多 NPC 并行聊天。
5. 不做每一幕的复杂分支树可视化编辑器。
6. 不把“规则说明文案”默认堆到创作页或游戏 UI 面板里。
7. 不把“点击配置”实现成在当前卡片下面继续展开大段内容。
8. 不重写现有高好感委托链路,只在本次规则下明确它什么时候还能触发。
---
## 4. 现状判断
## 4.1 创作工具侧现状
当前仓库已经具备下面这些基础:
1. `packages/shared/src/contracts/customWorldAgent.ts`
- 已存在 `scene_chapter` 草稿卡 kind。
2. `server-node/src/services/customWorldAgentDraftCompiler.ts`
- 已经能编译世界、第一幕、线程、势力、角色、地点等草稿卡。
3. `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx`
- 已有草稿抽屉,但还没有把 `scene_chapter` 正式纳入抽屉分组。
4. 现有场景背景图生成与发布链已存在。
但当前仍有 4 个缺口:
1. 场景章节没有“幕”这一层结构化对象。
2. 背景图是场景级资产,不是幕级资产。
3. NPC 与场景的关系主要还是地点级归属,不是幕级相遇编排。
4. 创作者无法在创作页里明确控制“这一幕谁先出场、谁是主角色”。
## 4.2 游戏运行侧现状
当前运行时已经具备下面这些基础:
1. `src/data/questFlow.ts`
- 已有 `scene chapter quest``buildSceneChapterId(...)`
2. `src/services/storyEngine/chapterDirector.ts`
- 已能按场景章节输出 `ChapterState`
3. `src/hooks/story/npcEncounterActions.ts`
- 已有 `npc_chat` 多轮聊天、`turnCount``pendingQuestOffer` 等状态。
4. `packages/shared/src/contracts/story.ts`
- 已有 `NpcChatTurnRequest` / `NpcChatTurnResult` 契约。
但当前仍有 5 个问题:
1. 场景内部仍偏单层推进,缺少“第几幕”的明确状态。
2. 场景背景不会随幕切换。
3. 场景可遇 NPC 不会随幕切换。
4. 主角色没有从配置顺序直接编译成运行时规则。
5. 负好感主角色聊天仍没有“最多 5 轮且第 5 轮收束铺垫”的规则。
一句话总结:
**现在我们有场景章节也有聊天系统但还没有“场景多幕蓝图”这一层把创作配置、背景资产、NPC 相遇顺序和聊天规则真正串起来。**
---
## 5. 核心决策
## 5.1 场景章节与场景幕的关系
本次新增一个明确约束:
- `场景章节` 仍然是场景级闭环容器
- `场景幕` 是场景章节内部的有序分段
关系定义如下:
| 层级 | 作用 |
| --- | --- |
| `scene chapter` | 表示这一整个场景在剧情上的一章 |
| `scene act` | 表示这章内部的第几幕、当前视觉和当前相遇主体 |
每个场景章节必须至少有 `2` 幕,最多 `5` 幕。
## 5.2 多幕数量与章节阶段映射
为了不引入第二套完全独立的运行时章节体系,本次规定场景幕按数量映射到现有 `ChapterState.stage`
| 幕数 | 编译规则 |
| --- | --- |
| `2` 幕 | 幕 1=`opening + expansion`,幕 2=`turning_point + climax + aftermath` |
| `3` 幕 | 幕 1=`opening`,幕 2=`expansion + turning_point`,幕 3=`climax + aftermath` |
| `4` 幕 | 幕 1=`opening`,幕 2=`expansion`,幕 3=`turning_point`,幕 4=`climax + aftermath` |
| `5` 幕 | 与 `opening / expansion / turning_point / climax / aftermath` 一一对应 |
这意味着:
1. 创作者在工具里编辑的是“第几幕”。
2. 运行时仍然只认现有章节阶段枚举。
3. `chapterDirector` 可以继续复用,只是数据来源从“纯 quest 推导”升级成“quest + 幕蓝图联合推导”。
## 5.3 主角色定义
每一幕配置的 `encounterNpcIds` 必须是有序数组。
规则固定为:
1. `encounterNpcIds[0]` 就是当前幕主角色。
2. 运行时会把它编译成 `primaryNpcId`
3. 主角色承担该幕默认的首次相遇、聊天轮数裁决和幕推进优先级。
4. 其余 NPC 视为辅助相遇角色,不直接承担本次“好感度聊天轮数规则”。
---
## 6. 数据结构要求
## 6.1 创作草稿层新增结构
建议在现有 `CustomWorldFoundationDraftProfile` 之上新增下面两层:
```ts
type SceneActAdvanceRule =
| 'after_primary_contact'
| 'after_active_step_complete'
| 'after_chapter_resolution';
interface CustomWorldFoundationDraftSceneAct {
id: string;
title: string;
summary: string;
stageCoverage: Array<
| 'opening'
| 'expansion'
| 'turning_point'
| 'climax'
| 'aftermath'
>;
backgroundImageSrc?: string | null;
backgroundAssetId?: string | null;
encounterNpcIds: string[];
primaryNpcId: string;
linkedThreadIds: string[];
actGoal: string;
transitionHook: string;
advanceRule: SceneActAdvanceRule;
}
interface CustomWorldFoundationDraftSceneChapter {
id: string;
sceneId: string;
sceneName: string;
title: string;
summary: string;
linkedThreadIds: string[];
linkedLandmarkIds: string[];
acts: CustomWorldFoundationDraftSceneAct[];
}
```
硬要求:
1. `primaryNpcId` 必须等于 `encounterNpcIds[0]`,不允许单独填写成别的角色。
2. 每幕必须至少有 `1` 个 NPC。
3. 每幕必须有 `backgroundImageSrc``backgroundAssetId`
4. `advanceRule` 由系统按幕位置默认编译,第一版不要求创作者手改。
## 6.2 发布到运行时的蓝图结构
创作草稿在发布时必须进一步编译成运行时蓝图:
```ts
interface SceneActBlueprint {
id: string;
sceneId: string;
title: string;
stageCoverage: Array<
| 'opening'
| 'expansion'
| 'turning_point'
| 'climax'
| 'aftermath'
>;
backgroundImageSrc?: string | null;
encounterNpcIds: string[];
primaryNpcId: string;
advanceRule:
| 'after_primary_contact'
| 'after_active_step_complete'
| 'after_chapter_resolution';
actGoal: string;
transitionHook: string;
}
interface SceneChapterBlueprint {
id: string;
sceneId: string;
title: string;
summary: string;
acts: SceneActBlueprint[];
}
```
建议把它挂入 `CustomWorldProfile` 的新字段中:
```ts
sceneChapterBlueprints?: SceneChapterBlueprint[] | null;
```
原因:
1. 现有 `landmarks` 只足够表达地点,不足够表达幕顺序。
2. 现有 `ChapterState` 是运行时状态,不适合直接兼做创作者蓝图。
3. 独立蓝图层更适合后端编译和发布校验。
## 6.3 聊天状态扩展
建议在现有 `StoryNpcChatState` 上新增有限聊天需要的状态:
```ts
interface StoryNpcChatState {
npcId: string;
npcName: string;
turnCount: number;
customInputPlaceholder?: string;
pendingQuestOffer?: {
quest: QuestLogEntry;
} | null;
sceneActId?: string | null;
turnLimit?: number | null;
remainingTurns?: number | null;
limitReason?: 'negative_affinity' | null;
forceExitAfterTurn?: boolean;
}
```
要求:
1. 正常无限聊天时,`turnLimit``remainingTurns``null`
2. 负好感主角色聊天时,`turnLimit=5`
3.`5` 轮结束后,`forceExitAfterTurn=true`,由后端明确告知前端结束当前聊天态。
## 6.4 NPC 聊天返回契约扩展
建议扩展 `NpcChatTurnResult`
```ts
type NpcChatTurnResult = {
npcReply: string;
affinityDelta: number;
affinityText: string;
suggestions: string[];
chatDirective?: {
turnLimit?: number | null;
remainingTurns?: number | null;
forceExit?: boolean;
closingMode?: 'free' | 'foreshadow_close';
};
};
```
这部分必须由后端给出,不允许前端自己猜。
---
## 7. 创作工具需求
## 7.1 入口与承载方式
本次必须继续复用现有:
1. `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx`
2. `src/components/custom-world-agent/CustomWorldDraftCardDetailModal.tsx`
3. `src/components/custom-world-agent/CustomWorldDraftEditPanel.tsx`
不新建独立页面。
新增规则:
1. 草稿抽屉必须正式支持 `scene_chapter` 分组。
2. `scene_chapter` 分组应位于 `chapter` 后、`thread` 前。
3. 点开 `scene_chapter` 草稿卡后,进入现有详情弹层和编辑面板体系。
4. 创作页面卡片摘要后续可增加 `sceneChapterCount`,但第一版不是阻塞项。
## 7.2 场景章节卡展示要求
每张 `scene_chapter` 草稿卡至少展示:
1. 场景名称
2. 章节标题
3. 幕数量
4. 已就绪背景图数量
5. 关联 NPC 数量
6. 关联线程数量
7. 当前风险数
详情页必须至少展示:
1. 场景摘要
2. 幕结构总览
3. 每幕的背景缩略图
4. 每幕的主角色
5. 每幕的辅助 NPC
6. 每幕目标
7. 每幕过渡钩子
## 7.3 幕编辑交互
每个场景章节卡的编辑区必须支持下面这些操作:
1. 新增幕
2. 删除幕
3. 调整幕顺序
4. 编辑幕标题
5. 编辑幕摘要
6. 绑定幕背景图
7. 配置幕相遇 NPC 顺序
8. 编辑幕目标
9. 编辑幕过渡钩子
交互要求:
1. 幕列表在桌面端纵向堆叠,在移动端同样保持纵向,不做复杂双列。
2. 每幕是独立卡片,不把所有字段一次性铺满。
3. 点击“配置背景图”时必须打开独立面板或独立弹层,不允许在当前卡片下方内联展开。
4. 点击“配置相遇 NPC”时必须打开独立面板或独立弹层不允许在当前卡片下方内联展开。
5. 默认不展示大段规则说明文字。
## 7.4 幕背景图配置
背景图配置必须复用现有场景图资产链,而不是另造上传体系。
要求如下:
1. 一幕只绑定一张正式背景图。
2. 可从已生成场景图中选择,也可调用现有场景图生成链生成。
3. 幕背景图和场景总背景图不是同一个概念,允许不同幕使用不同图。
4. 发布前如果存在未绑定背景图的幕,必须阻止发布。
5. 幕切换时运行时优先使用幕背景图,而不是地点默认图。
## 7.5 幕相遇 NPC 配置
NPC 配置面板必须支持:
1. 从当前世界的 `playableNpcs + storyNpcs` 中选择角色
2. 只展示与当前场景相关的优先推荐角色
3. 支持排序
4. 第一位角色明确标记为“主角色”
5. 允许同一角色出现在多个不同幕
硬约束:
1. 每幕至少 `1` 名 NPC。
2. 第一位 NPC 不能为空。
3. 不允许把不存在于当前世界角色池中的 id 写入幕配置。
4. 若主角色未与当前场景或线程建立任何关联,给出发布警告。
## 7.6 创作校验
`CustomWorldQualityFinding` 至少新增下面这些检查项:
1. `scene_chapter_missing_act`
2. `scene_act_missing_background`
3. `scene_act_missing_primary_npc`
4. `scene_act_missing_encounter_npc`
5. `scene_act_primary_npc_not_first`
6. `scene_act_unlinked_thread`
7. `scene_act_unpublished_background`
发布阻断项:
1. 幕数小于 `2`
2. 任意一幕没有背景图
3. 任意一幕没有 NPC
4. 任意一幕的第一 NPC 为空
---
## 8. 游戏流程需求
## 8.1 幕运行时状态
运行时必须为每个场景章节维护独立幕进度:
```ts
interface SceneActRuntimeState {
sceneId: string;
chapterId: string;
currentActId: string;
currentActIndex: number;
completedActIds: string[];
visitedActIds: string[];
}
```
建议挂入当前 story engine memory 中,和现有 `openedSceneChapterIds` 并存。
## 8.2 进入场景时的流程
当玩家进入一个有 `SceneChapterBlueprint` 的场景时:
1. 后端定位当前场景对应的 `scene chapter blueprint`
2. 如果该场景首次进入,则激活第 `1`
3. 如果该场景未完成且已有幕进度,则恢复到当前未完成幕
4. 把当前幕的背景图写入前端展示模型
5. 把当前幕的 `encounterNpcIds` 作为本幕优先相遇池
6. 把当前幕的 `stageCoverage` 交给 `chapterDirector` 参与裁决,并结合 quest 进度输出单一 `ChapterState.stage`
## 8.3 幕推进规则
第一版不要求创作者手填推进条件,而是由系统按幕位置默认编译:
1.`1` 幕默认 `after_primary_contact`
- 玩家与主角色发生首次有效接触后可进入下一幕判定
2. 中间幕默认 `after_active_step_complete`
- 当前场景章节任务 active step 完成后进入下一幕判定
3. 最后一幕默认 `after_chapter_resolution`
- 当前场景章节任务完成或进入可收束状态后结束本场景章节
要求:
1. 幕推进由后端统一裁决。
2. 前端只接收“幕已切换”的结果,不自行判断。
3. 幕切换后必须触发背景切换与相遇池更新。
## 8.4 幕切换表现
游戏前台在幕切换时必须至少做到:
1. 显示当前幕标题
2. 更新背景图
3. 更新当前可遇 NPC
4. 给出一条轻量系统提示,说明进入了新一幕
注意:
1. 不新建独立页面。
2. 不弹全屏说明面板。
3. 移动端优先保证幕标题与背景切换不遮挡底部操作区。
---
## 9. NPC 相遇与聊天规则
## 9.1 规则适用范围
本次新增的“按好感度控制聊天轮数”规则,只对**当前幕主角色**生效。
也就是说:
1. 当前幕 `primaryNpcId` 命中的角色,使用本次新规则。
2. 当前幕其他辅助 NPC第一版继续沿用现有 `npc_chat` 通用流程。
3. 辅助 NPC 的聊天不直接推进幕进度,除非后端另有章节 step 裁决。
## 9.2 主角色好感度大于 0
当当前幕主角色对玩家的当前好感度 `> 0` 时:
1. 玩家与其相遇后可以进入聊天态。
2. 聊天轮数无限制。
3. 继续沿用现有:
- `3` 个续聊建议项
- `1` 个自定义输入框
- 主动退出聊天
4. 只要满足 `docs/design/NPC_HIGH_AFFINITY_CHAT_QUEST_OFFER_FLOW_2026-04-19.md` 中的条件,仍然允许在聊天内抛出委托。
## 9.3 主角色好感度等于 0
为了避免编码边界歧义,本 PRD 先明确:
1. `affinity = 0` 视为中立档,不归入负好感限制分支。
2. 中立档允许进入正常多轮聊天。
3. 中立档不自动享受“高好感委托时机”。
这意味着:
- `> 0`:无限聊,且可进入高好感委托逻辑
- `= 0`:无限聊,但不进入高好感委托逻辑
- `< 0`:最多 `5`
## 9.4 主角色好感度小于 0
当当前幕主角色对玩家的当前好感度 `< 0` 时,必须进入 `limited hostile chat mode`
1. 允许进入聊天,但最多 `5` 轮。
2. 聊天状态中必须显示剩余轮数。
3.`1~4` 轮仍然走正常“玩家一句 -> NPC 一句 -> 建议项刷新”的基本结构。
4.`5` 轮不是普通续聊,而是强制收束轮。
5.`5` 轮必须输出一段带方向的收束回应,为后续剧情开展铺垫。
6.`5` 轮结束后:
- 自定义输入框隐藏
- 当前聊天态结束
- 恢复普通冒险态或进入后续 action 选择
## 9.5 第 5 轮的“铺垫”定义
“为开展铺垫”在本次 PRD 中必须被明确解释为:
**NPC 在第 5 轮必须抛出一个明确的后续方向,不能只用一句敌意台词把对话硬截断。**
可接受的铺垫结果包括:
1. 抛出新的威胁或最后通牒
2. 指向某个地点、人物或线索
3. 把矛盾推向对峙、交易、追踪或战斗
4. 暗示自己下一步行动去向
5. 给玩家一个必须接住的悬念或条件
不可接受的结果:
1. 纯重复敌意表达
2. 没有任何新方向的信息
3. 第 5 轮结束后界面直接空掉,没有后续承接
## 9.6 对当前负好感拦截逻辑的调整
当前若主角色属于本幕 `primaryNpcId`,则需要覆盖现有“负好感直接不给持续聊天”的逻辑。
新规则如下:
1. 如果它是当前幕主角色,即使当前好感度 `< 0`,也允许进入有限聊天态。
2. 只有在完成第 `5` 轮铺垫收束后,才切回普通探索/对峙流程。
3. 如果该 NPC 不是当前幕主角色,仍可沿用现有负好感拦截逻辑。
---
## 10. 前端表现要求
## 10.1 创作页
创作页必须保持清爽,不默认塞规则说明。
必须做到:
1. `scene_chapter` 卡片可见
2. 幕列表可编辑
3. 背景图选择和 NPC 选择都走独立面板
4. 移动端仍能完成幕排序、背景选择、NPC 排序
## 10.2 游戏主面板
Adventure 主面板在本次迭代中至少增加下面这些表现:
1. 当前幕标题或幕序号标签
2. 当前幕背景图切换
3. 主角色负好感聊天时的“剩余轮数”轻量提示
4. 第 5 轮结束后的过渡系统消息
禁止:
1. 默认展示大段规则介绍
2. 把幕配置说明直接写进玩家面板
3. 为了展示幕切换而新建独立剧情页面
---
## 11. 前后端职责边界
## 11.1 前端职责
前端只负责:
1. 渲染 `scene_chapter` 草稿卡与幕编辑 UI
2. 发起背景图配置和 NPC 配置请求
3. 渲染当前幕背景和幕标题
4. 渲染负好感聊天剩余轮数
5. 根据后端返回切换幕、退出聊天、展示后续 options
前端不负责:
1. 计算主角色是谁
2. 计算好感度轮数限制
3. 判定什么时候切幕
4. 决定第 5 轮要输出什么铺垫
5. 本地拼接下一幕 encounter 池
## 11.2 后端职责
后端必须负责:
1. 把创作页幕配置编译成运行时蓝图
2. 校验每幕背景与 NPC 配置完整性
3. 维护 `SceneActRuntimeState`
4. 进入场景时确定当前幕
5. 输出当前幕背景与 encounter 池
6. 裁决主角色聊天轮数限制
7. 在第 `5` 轮生成铺垫式收束回应
8. 在满足条件时推进到下一幕
---
## 12. 影响模块
本 PRD 落地时,至少会影响下面这些模块:
1. `packages/shared/src/contracts/customWorldAgent.ts`
- 新增场景多幕草稿结构
2. `src/types/customWorld.ts`
- 新增发布态 `sceneChapterBlueprints`
3. `server-node/src/services/customWorldAgentDraftCompiler.ts`
- 编译 `scene_chapter` 草稿卡
4. `server-node/src/services/customWorldAgentDraftEditService.ts`
- 支持场景幕的增删改排序
5. `server-node/src/services/customWorldAgentQualityService.ts`
- 增加幕背景和幕 NPC 校验
6. `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx`
- 展示 `scene_chapter` 分组
7. `src/components/custom-world-agent/CustomWorldDraftCardDetailModal.tsx`
- 展示幕详情
8. `src/components/custom-world-agent/CustomWorldDraftEditPanel.tsx`
- 新增幕编辑 UI
9. `src/data/questFlow.ts`
- 让 scene chapter quest 感知当前幕
10. `src/services/storyEngine/chapterDirector.ts`
- 用当前幕映射章节阶段和摘要
11. `src/hooks/story/npcEncounterActions.ts`
- 新增主角色有限聊天与第 5 轮收束逻辑
12. `packages/shared/src/contracts/story.ts`
- 扩展 `NpcChatTurnResult`
13. `src/services/aiService.ts`
- 透传有限聊天新字段
14. `server-node/src/modules/ai/chatOrchestrator.ts`
- 生成第 `5` 轮铺垫式收束结果
---
## 13. 验收标准
当下面这些结果都成立时,视为本次 PRD 已被正确落地:
1. 创作者可以在现有创作工作区中创建并编辑 `scene_chapter`
2. 每个场景章节都可以配置 `2~5` 幕。
3. 每一幕都可以绑定独立背景图。
4. 每一幕都可以配置有序 NPC 列表,第一位自动成为主角色。
5. 发布时缺少幕背景或幕 NPC 会被明确拦截。
6. 玩家进入场景后,当前幕背景图能正确显示。
7. 当前幕可遇 NPC 会按幕配置切换。
8. 当前幕主角色好感度 `> 0` 时可以无限续聊。
9. 当前幕主角色好感度 `< 0` 时最多只聊 `5` 轮。
10.`5` 轮结束后一定会出现为后续剧情开展铺垫的收束结果,而不是直接硬断。
11. 高好感委托链仍只在正好感聊天中触发。
12. 桌面端和移动端都能完成幕配置与幕切换使用。
---
## 14. 本稿默认假定
为了避免下一步编码时再出现语义歧义,这份 PRD 先明确采用下面两条默认假定:
1. `affinity = 0` 先按中立档处理:允许无限聊,但不进入高好感委托分支。
2. “第 5 轮为开展铺垫”先解释为“为后续剧情推进、对峙、追踪、交易或战斗制造明确下一跳”,而不是限定为必须开战。
如果后续你希望把:
- `affinity = 0` 改成也只聊 `5`
-`5` 轮明确收束到“开战前摇”
可以在下一版实现文档中单独收紧,不影响本稿主结构。

View File

@@ -17,17 +17,20 @@
## 1. 当前“我的”Tab 功能拆分
当前页面可拆成以下 `9` 个独立功能
说明
-`2026-04-19` 起,“最近游玩 / 历史浏览”已从“我的”页迁出,改为平台一级主 Tab“存档”。
- 对应母文档见 [PLATFORM_SAVE_TAB_PRD_2026-04-19.md](/E:/Repos/Genarrative/docs/prd/PLATFORM_SAVE_TAB_PRD_2026-04-19.md)。
当前“我的”页保留以下 `7` 个独立功能:
1. 账号资料与身份卡
2. 会员中心与充值
3. 我的数据看板
4. 最近游玩
5. 历史浏览
6. 邀请好友
7. 填邀请码
8. 玩家社区
9. 设置与账号安全
4. 邀请好友
5. 填邀请码
6. 玩家社区
7. 设置与账号安全
---
@@ -36,12 +39,11 @@
1. [MY_TAB_PROFILE_IDENTITY_CARD_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_PROFILE_IDENTITY_CARD_PRD_2026-04-16.md)
2. [MY_TAB_MEMBERSHIP_CENTER_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_MEMBERSHIP_CENTER_PRD_2026-04-16.md)
3. [MY_TAB_DATA_DASHBOARD_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_DATA_DASHBOARD_PRD_2026-04-16.md)
4. [MY_TAB_RECENT_PLAY_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_RECENT_PLAY_PRD_2026-04-16.md)
5. [MY_TAB_BROWSE_HISTORY_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_BROWSE_HISTORY_PRD_2026-04-16.md)
6. [MY_TAB_INVITE_FRIENDS_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_INVITE_FRIENDS_PRD_2026-04-16.md)
7. [MY_TAB_INVITE_CODE_REDEMPTION_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_INVITE_CODE_REDEMPTION_PRD_2026-04-16.md)
8. [MY_TAB_PLAYER_COMMUNITY_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_PLAYER_COMMUNITY_PRD_2026-04-16.md)
9. [MY_TAB_SETTINGS_AND_SECURITY_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_SETTINGS_AND_SECURITY_PRD_2026-04-16.md)
4. [PLATFORM_SAVE_TAB_PRD_2026-04-19.md](/E:/Repos/Genarrative/docs/prd/PLATFORM_SAVE_TAB_PRD_2026-04-19.md)
5. [MY_TAB_INVITE_FRIENDS_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_INVITE_FRIENDS_PRD_2026-04-16.md)
6. [MY_TAB_INVITE_CODE_REDEMPTION_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_INVITE_CODE_REDEMPTION_PRD_2026-04-16.md)
7. [MY_TAB_PLAYER_COMMUNITY_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_PLAYER_COMMUNITY_PRD_2026-04-16.md)
8. [MY_TAB_SETTINGS_AND_SECURITY_PRD_2026-04-16.md](/E:/Repos/Genarrative/docs/prd/MY_TAB_SETTINGS_AND_SECURITY_PRD_2026-04-16.md)
---
@@ -51,20 +53,19 @@
1. 账号资料与身份卡
2. 设置与账号安全
3. 最近游玩
4. 历史浏览
5. 我的数据看板
6. 会员中心与充值
7. 邀请好友
8. 填邀请码
9. 玩家社区
3. 我的数据看板
4. 平台存档 Tab
5. 会员中心与充值
6. 邀请好友
7. 邀请
8. 玩家社区
原因:
- `1 + 2` 复用现有账号系统最多,最容易先落地
- `3 + 4 + 5` 直接增强“我的”页内容密度,短期收益高
- `6 + 7` 涉及商业化和关系绑定,依赖结算与奖励台账
- `8` 最适合放在平台内容层能力稳定后再做
- `3 + 4` 直接增强账号资产与回流体验,短期收益高
- `5 + 6` 涉及商业化和关系绑定,依赖结算与奖励台账
- `7` 最适合放在平台内容层能力稳定后再做
---

View File

@@ -1,6 +1,6 @@
# “我的”Tab 设置与账号安全 PRD
更新时间:`2026-04-16`
更新时间:`2026-04-20`
## 0. 目标
@@ -56,7 +56,12 @@
## 3. 信息架构
设置中心建议固定为段:
设置中心首层固定为段:
1. 主题外观
2. 账号信息
其中“账号信息”二级面板固定承载以下内容:
1. 账号概况
2. 当前安全状态
@@ -64,6 +69,17 @@
4. 更换手机号
5. 账号操作记录
交互层级要求补充为:
1. 设置首页只展示“主题外观”“账号信息”两个分区入口与危险操作,不在首页内联展开具体详情
2. 点击任一分区入口后,必须进入独立二级面板
3. 安全状态、登录设备、操作记录不再作为首页独立入口,统一归入“账号信息”二级面板
4. 更换手机号属于独立操作面板,不允许在账号信息面板内直接展开表单
5. 设置首页头部只保留一套主标题,不允许在内容区再重复放置“设置首页”“选择要管理的内容”这类二次标题块
6. 子面板导航动作必须单一明确;同一层面板内有“返回”时,不再同时展示“关闭”
7. 设置首页与各级子面板都必须定义单一滚动容器,列表内容必须可稳定滚动,禁止外层与内层同时争夺滚动
8. 二级或三级面板打开后,下层内容必须进入不可交互状态,并把焦点主动转移到当前面板内;禁止对仍保留焦点的祖先节点使用 `aria-hidden`
底部保留两个危险操作按钮:
1. 退出登录
@@ -80,10 +96,15 @@
- 登录方式
- 手机号脱敏值
- 微信绑定状态
- 账号状态
这里只看信息,不做大编辑动作。
标题约束:
- 设置首页标题固定表达“设置”或“设置与账号安全”
- 设置首页标题区域不展示手机号,也不允许把手机号当作主标题替代昵称
- 手机号只允许出现在账号概况信息项中,以脱敏值展示
## 4.2 当前安全状态
展示当前账号命中的风控保护:
@@ -188,8 +209,14 @@
1. 设置继续采用当前账号弹窗基础形态即可
2. 移动端优先底部弹层,桌面端可居中弹窗
3. 更换手机号区域默认折叠
4. 危险操作按钮与普通按钮必须明显区分
3. 设置首页只保留“主题外观”“账号信息”两个入口,不再单独展示安全状态、登录设备、操作记录入口
4. “账号信息”二级面板直接承载账号概况、安全状态、登录设备、操作记录四块内容,移动端优先纵向滚动,桌面端保持同一面板内稳定扫读
5. 更换手机号必须通过独立操作面板完成,不再使用当前面板内联展开表单
6. 危险操作按钮与普通按钮必须明显区分
7. 设置首页标题处禁止展示手机号、脱敏手机号或手机号形态的 displayName
8. 设置首页不额外堆砌规则说明文案,标题下直接进入可操作内容
9. 子面板采用覆盖式独立面板承载详情,返回上一级时恢复首页,不在同层同时出现双导航动作
10. 面板切换必须保证键盘焦点始终停留在当前活跃面板内,返回上一级后焦点恢复到触发入口
---

View File

@@ -0,0 +1,258 @@
# 平台“存档”Tab PRD
更新时间:`2026-04-20`
## 0. 目标
把原本堆在“我的”页中的“最近游玩 / 历史浏览”移出,新增平台一级主 Tab“存档”用于承载当前账号在平台里玩过的所有游戏留下的最近可恢复存档。
这次改动的核心目标不是做复杂多槽位存档系统,而是先落地一个稳定、可跨设备同步、可直接继续游玩的账号级存档入口。
---
## 1. 信息架构调整
## 1.1 平台主导航
平台主导航从:
- 首页
- 创作
- 我的
调整为:
- 首页
- 创作
- 存档
- 我的
移动端底部导航与桌面端左侧纵向导航都必须同步调整。
## 1.2 “我的”页调整
“我的”页删除以下内容块:
- 最近游玩
- 历史浏览
“我的”页保留:
- 账号资料与身份卡
- 数据看板
- 常用功能
- 设置与账号安全
说明:
- 历史浏览本期直接从“我的”页移除,不再占据个人页首屏空间。
- 存档能力统一收口到平台一级“存档”Tab不再同时在“我的”页重复展示。
---
## 2. 存档定义
## 2.1 本期存档口径
本期“存档”Tab 展示的是:
- 当前账号在每个已游玩游戏 / 世界下保留的最近一个可恢复存档
不是:
- 同一游戏下的完整多槽位存档管理页
- 手动重命名 / 置顶 / 删除存档系统
## 2.2 世界唯一键
服务端必须按 `worldKey` 聚合最近存档:
- 自定义世界:`custom:<profileId>`
- 内建世界:`builtin:<worldType>`
同一账号、同一 `worldKey` 只保留最近一次成功保存的可恢复存档。
## 2.3 生命周期
1. 玩家每次成功写入运行时快照时,同步刷新该世界的最近存档记录。
2. 删除当前活动快照时,不删除历史存档归档。
3. 点击“继续游玩”时,从该世界最近存档恢复为当前活动快照,再进入游戏。
---
## 3. 界面设计
## 3.1 存档 Tab 首屏结构
页面首屏直接展示存档列表,不再单独保留顶部“最近存档”摘要卡。
列表容器本身需要承担首屏入口作用:
- 用户进入“存档”Tab 后第一屏就看到可恢复存档列表
- 不额外重复展示首个存档的大卡摘要
- 存档数量、排序状态如需表达,应收敛在列表标题或轻量状态信息中
不要在 UI 中默认堆规则说明文案,只保留简洁的状态表达。
## 3.2 列表排序
列表按 `lastPlayedAt` 倒序。
最近更新的存档始终在最前面。
## 3.3 列表项字段
每个列表项必须展示:
- 游戏名称
- 最后游玩时间
- 游戏信息
### 3.3.1 移动端卡片布局约束
- 移动端列表卡片中的封面只能作为独立缩略图或弱化背景层使用,不能直接占满整张卡片并压在正文信息下方。
- 标题、时间、摘要所在的信息区必须保持 `min-width: 0` 的可收缩布局,长标题不能把正文挤出屏幕外。
- 世界名称最多展示 2 行,游戏信息最多展示 3 行,超出后截断,不允许横向溢出。
- 时间标签、状态标签在窄屏下必须允许换行或独立成行,不能为了保持单行导致卡片内容错位。
- 列表卡片缩略图区域比例固定,文本区与缩略图区在移动端需要保持稳定对齐,避免出现上下参差和视觉歪斜。
其中“游戏信息”优先级如下:
1. `continueGameDigest`
2. 当前故事文本摘要
3. 世界简介 / 场景简介
4. 若都没有则给出简洁兜底文案
可附带展示封面,但封面不是必填验收项。
## 3.4 点击行为
点击列表项后:
1. 调用后端恢复接口
2. 将所选存档切换为当前活动快照
3. 直接进入游戏继续游玩
前端不允许自行拼装恢复上下文。
## 3.5 空状态
### 已登录但无存档
- 展示轻量空态
- 引导去首页开始游玩
### 未登录
- 展示登录态空壳
- 不请求受保护的云端存档列表接口
---
## 4. 默认进入逻辑
当满足以下条件时玩家进入网站后的平台首页默认进入“存档”Tab
1. 当前处于登录状态
2. 当前账号至少存在一个存档
否则:
- 仍默认进入“首页”Tab
注意:
- 这个默认进入逻辑只在平台首屏初始化时执行,不能覆盖用户手动切换后的选择。
---
## 5. 后端设计
## 5.1 新增数据表
建议新增 `profile_save_archives`
- `user_id`
- `world_key`
- `owner_user_id`
- `profile_id`
- `world_type`
- `world_name`
- `world_subtitle`
- `summary_text`
- `cover_image_src`
- `saved_at`
- `bottom_tab`
- `game_state_json`
- `current_story_json`
- `updated_at`
约束:
- 主键:`user_id + world_key`
- 排序索引:`user_id + saved_at desc`
## 5.2 写入规则
每次 `/api/runtime/save/snapshot` 成功写入后:
1. 正常更新当前活动快照
2. 同步 upsert 对应 `world_key` 的存档归档
3. 继续保留原有个人看板 / 已玩作品同步逻辑
## 5.3 列表接口
### `GET /api/runtime/profile/save-archives`
返回:
- 当前账号全部最近存档
字段至少包含:
- `worldKey`
- `worldName`
- `worldSubtitle`
- `summaryText`
- `coverImageSrc`
- `lastPlayedAt`
- `worldType`
- `profileId`
- `ownerUserId`
## 5.4 恢复接口
### `POST /api/runtime/profile/save-archives/:worldKey/resume`
用途:
- 将指定存档归档恢复为当前活动快照
- 返回恢复后的快照
限制:
- 恢复动作不能重复记账,不得再次累计个人资产流水
- 恢复动作不能重复累计已玩时长
- 恢复动作不能破坏现有快照水合逻辑
---
## 6. 前端实现要求
1. `PlatformHomeView` 新增 `存档` 主 Tab。
2. `PreGameSelectionFlow` 在平台数据加载时同时拉取存档列表。
3. 已登录且有存档时,平台首屏默认选中 `存档` Tab。
4. “我的”页删除“最近游玩 / 历史浏览”两个区块。
5. 点击存档列表项时必须经过后端恢复接口,恢复成功后直接进入游戏。
6. 移动端优先,列表项点击区域不能过小。
---
## 7. 验收标准
1. 已登录账号可以在“存档”Tab 看到所有已玩过世界的最近存档。
2. 列表按最近更新时间倒序。
3. 列表项可看到游戏名称、最后游玩时间和游戏信息。
4. 点击列表项后可直接继续对应游戏。
5. 已登录且至少有一个存档时进入网站默认打开“存档”Tab。
6. 未登录时不请求云端存档列表,也不会出现受保护接口报错。

View File

@@ -0,0 +1,185 @@
# 业务提示词清单2026-04-19
## 1. 目标
这份清单用于回答两个问题:
- 目前业务里到底有哪些提示词还在被使用。
- 哪些提示词已经收口到独立目录,哪些仍散落在前后端与工具链里。
本次统计范围:
- `server-node/src/**`
- `src/**`
- `packages/shared/src/**`
本次“提示词”统计口径包含:
- system prompt
- user prompt builder
- repair prompt
- negative prompt
- 图像 / 动画生成 prompt
- 编辑器里会直接喂给模型的默认 prompt 种子
本次不计入:
- 单纯转发 prompt 的接口入参校验
- 普通剧情文案、UI 文案、剧情预设文本
- 纯测试断言文件
## 2. 当前结论
截至 2026-04-19 本轮收口完成后,业务 prompt 主源已经集中到 3 个目录:
1. `server-node/src/prompts/`
2. `src/prompts/`
3. `packages/shared/src/prompts/`
当前业务模块、路由、服务层里的旧 prompt 文件大多已经退化成两类:
- prompt 调用方
- 薄 re-export 兼容层
目前没有再发现“正式业务 prompt 正文仍长期内联在主流程文件里”的大块散点;剩余位于非 prompt 目录的相关文件,主要是兼容层、测试文件或普通调用方。
## 3. 当前 Prompt 目录清单
### 3.1 后端
| 文件 | 业务域 | 关键导出 |
| --- | --- | --- |
| `server-node/src/prompts/storyPromptBuilders.ts` | 主剧情推进 | `SYSTEM_PROMPT``buildUserPrompt` |
| `server-node/src/prompts/storyOrchestratorPrompts.ts` | 剧情语言修复 | `STORY_LANGUAGE_REPAIR_SYSTEM_PROMPT``buildStoryLanguageRepairPrompt` |
| `server-node/src/prompts/chatPromptBuilders.ts` | 角色私聊 / NPC 对话 / 招募 | `CHARACTER_PANEL_CHAT_*``NPC_CHAT_*`、多个 `build*Prompt` |
| `server-node/src/prompts/questPrompts.ts` | 任务意图 | `QUEST_INTENT_SYSTEM_PROMPT``buildQuestIntentPrompt` |
| `server-node/src/prompts/runtimeItemPrompts.ts` | 运行时物品意图 | `RUNTIME_ITEM_INTENT_SYSTEM_PROMPT``buildRuntimeItemIntentPromptText` |
| `server-node/src/prompts/customWorldOrchestratorPrompts.ts` | 自定义世界主编排 | `CUSTOM_WORLD_GENERATION_JSON_ONLY_SYSTEM_PROMPT``CUSTOM_WORLD_JSON_REPAIR_SYSTEM_PROMPT``buildCustomWorldProfilePrompt``buildCustomWorldProfileRepairPrompt` |
| `server-node/src/prompts/customWorldAgentPrompts.ts` | 世界草稿增补 | `FOUNDATION_JSON_ONLY_SYSTEM_PROMPT``FOUNDATION_JSON_REPAIR_SYSTEM_PROMPT`、多个扩展 prompt |
| `server-node/src/prompts/customWorldEntityPrompts.ts` | 世界编辑器实体生成 | `CUSTOM_WORLD_ENTITY_GENERATOR_SYSTEM_PROMPT``buildPlayablePrompt``buildStoryPrompt``buildLandmarkPrompt` |
| `server-node/src/prompts/customWorldSceneNpcPrompts.ts` | 世界编辑器场景 NPC | `CUSTOM_WORLD_SCENE_NPC_SYSTEM_PROMPT``buildCustomWorldSceneNpcPrompt` |
| `server-node/src/prompts/eightAnchorPrompts.ts` | 八锚点共创 | `BASE_SYSTEM_PROMPT``GLOBAL_HARD_RULES``MODE_RULES``USER_SIGNAL_RULES``buildPromptDynamicStateInferencePrompt``buildEightAnchorSingleTurnPrompt` |
| `server-node/src/prompts/characterAssetPrompts.ts` | 角色形象 / 动作资产生成 | `CHARACTER_PROMPT_BUNDLE_SYSTEM_PROMPT``buildFallbackCharacterPromptBundle``buildCharacterPromptBundleUserPrompt``buildNpcVisualPrompt``buildNpcAnimationPrompt``buildArkCharacterAnimationPrompt` |
### 3.2 前端
| 文件 | 业务域 | 关键导出 |
| --- | --- | --- |
| `src/prompts/storyPromptBuilders.ts` | 剧情推进 | `SYSTEM_PROMPT``buildUserPrompt` |
| `src/prompts/characterChatPrompts.ts` | 角色面板私聊 | `CHARACTER_PANEL_CHAT_*`、多个 `build*Prompt` |
| `src/prompts/questPrompts.ts` | 前端任务意图兜底 | `QUEST_INTENT_SYSTEM_PROMPT``buildQuestIntentPrompt` |
| `src/prompts/runtimeItemPrompts.ts` | 前端物品意图兜底 | `RUNTIME_ITEM_INTENT_SYSTEM_PROMPT``buildRuntimeItemIntentPrompt` |
| `src/prompts/customWorldPrompts.ts` | 自定义世界分阶段生成 + 场景背景图 | 多个 `buildCustomWorld*Prompt``DEFAULT_CUSTOM_WORLD_SCENE_IMAGE_NEGATIVE_PROMPT` |
| `src/prompts/customWorldOrchestratorPrompts.ts` | 世界 JSON 修复 / JSON only | `CUSTOM_WORLD_JSON_REPAIR_SYSTEM_PROMPT``CUSTOM_WORLD_GENERATION_JSON_ONLY_SYSTEM_PROMPT` |
| `src/prompts/storyOrchestratorPrompts.ts` | 剧情中文修复 | `STORY_LANGUAGE_REPAIR_SYSTEM_PROMPT` |
| `src/prompts/customWorldRolePromptDefaults.ts` | 角色资产工作台默认词 | `buildDefaultRolePromptBundle` |
| `src/prompts/customWorldEntityActionPrompts.ts` | 编辑器技能动作词 | `buildSkillActionPrompt` |
| `src/prompts/qwenSpriteSheetToolPrompts.ts` | Qwen 精灵图工具 prompt 模型 | 主 prompt / sheet prompt / repair prompt / negative prompt 系列 |
### 3.3 共享层
| 文件 | 业务域 | 关键导出 |
| --- | --- | --- |
| `packages/shared/src/prompts/qwenSprite.ts` | 共享像素角色主 prompt 模板 | `QWEN_SPRITE_ACTION_TEMPLATES``buildMasterPrompt``buildVideoActionPrompt``getActionTemplateById` |
## 4. 兼容层与调用层
为了避免一次性打断旧引用,当前保留了若干兼容层:
- `src/services/prompt.ts`
- `src/services/characterChatPrompt.ts`
- `src/services/questPrompt.ts`
- `src/services/runtimeItemAiPrompt.ts`
- `server-node/src/services/eightAnchorPromptBuilder.ts`
- `src/tools/qwenSpriteSheetToolModel.ts`
- `src/components/asset-studio/customWorldRolePromptDefaults.ts`
- `packages/shared/src/assets/qwenSprite.ts`
这些文件当前职责是:
- 维持旧路径可用
- re-export 到新的 prompt 目录
它们不再是 prompt 正文主源。
## 5. AI 角色形象生成当前来源
这部分是你点名要求补齐的重点,现在已经收口为:
| 文件 | 角色 | 当前定位 |
| --- | --- | --- |
| `server-node/src/prompts/characterAssetPrompts.ts` | 正式角色资产生成 prompt | 后端角色主图、动作试片、角色场景词主源 |
| `packages/shared/src/prompts/qwenSprite.ts` | 共享角色主 prompt 模板 | 共享给后端资产链使用的基础模板 |
| `src/prompts/qwenSpriteSheetToolPrompts.ts` | 工具链 prompt 模型 | Qwen 精灵图工具主词、分镜词、修帧词、负面词 |
| `src/prompts/customWorldRolePromptDefaults.ts` | 工作台默认词种子 | 角色视觉词、动画词、场景词默认值 |
| `src/prompts/customWorldEntityActionPrompts.ts` | 编辑器动作词 | 技能动作描述 prompt builder |
当前调用关系:
- `server-node/src/modules/assets/characterAssetRoutes.ts` 调用 `server-node/src/prompts/characterAssetPrompts.ts`
- `src/tools/QwenSpriteSheetTool.tsx` 通过兼容层消费 `src/prompts/qwenSpriteSheetToolPrompts.ts`
- `src/components/CustomWorldRoleAssetStudioModal.tsx` 通过兼容层消费 `src/prompts/customWorldRolePromptDefaults.ts`
- `src/components/CustomWorldEntityEditorModal.tsx` 直接调用 `src/prompts/customWorldEntityActionPrompts.ts`
## 6. AI 场景背景生成当前来源
场景背景图 prompt 现在已经从业务流程文件里抽出,统一主源是:
| 文件 | 角色 | 当前定位 |
| --- | --- | --- |
| `src/prompts/customWorldPrompts.ts` | 场景背景图 prompt 主源 | `buildCustomWorldSceneImagePrompt``DEFAULT_CUSTOM_WORLD_SCENE_IMAGE_NEGATIVE_PROMPT` |
| `src/services/ai.ts` | 前端编排调用方 | 组装请求并调用同一份 prompt builder |
| `server-node/src/services/sceneImageService.ts` | 后端执行器调用方 | 在服务端用同一份 prompt builder 生成 prompt再请求上游模型 |
这条链的关键变化是:
- 不再让 `src/services/customWorld.ts` 承担场景图 prompt 正文主源
- 前后端场景图生成改为共用 `src/prompts/customWorldPrompts.ts`
## 7. 本轮完成的原散点收口
本轮已经完成的原散点包括:
- `server-node/src/modules/assets/characterAssetRoutes.ts` 中的角色资产 prompt
- `server-node/src/services/eightAnchorPromptBuilder.ts` 中的八锚点 prompt
- `src/services/customWorld.ts` 中的自定义世界分阶段 prompt 与场景背景图 prompt
- `src/services/ai.ts` 中的世界修复 / 语言修复 / JSON only system prompt
- `src/services/prompt.ts``characterChatPrompt.ts``questPrompt.ts``runtimeItemAiPrompt.ts` 这批前端 prompt 脚本
- `src/tools/qwenSpriteSheetToolModel.ts``src/components/asset-studio/customWorldRolePromptDefaults.ts``src/components/CustomWorldEntityEditorModal.tsx` 里的工具 / 编辑器 prompt 散点
## 8. 当前仍在非 Prompt 目录中的相关文件
仍在非 prompt 目录中的相关文件,当前主要是:
- 调用方
- 兼容层
- 测试
因此现在的工程状态已经从“散点查找”变成“目录集中 + 兼容过渡”。
## 9. 验证结果
本轮收口后已验证:
- `npm run check:encoding`
- `npm --prefix server-node run build`
- `npm run build`
- `npm run server-node:test`
结果:
- 编码检查通过
- 前端构建通过
- 后端构建通过
- `server-node` 测试 143 项全部通过
## 10. 本次盘点后的判断
截至 2026-04-19本仓库的业务 prompt 已经基本完成目录化管理。
当前更准确的结论是:
- 后端正式业务 prompt 主源集中在 `server-node/src/prompts/`
- 前端与编辑器 prompt 主源集中在 `src/prompts/`
- 共享资产 prompt 主源集中在 `packages/shared/src/prompts/`
- 旧服务路径、旧工具路径仍保留为兼容层,但不再承担 prompt 正文维护职责

View File

@@ -2,6 +2,7 @@
## 当前入口
- [BUSINESS_PROMPT_INVENTORY_2026-04-19.md](./BUSINESS_PROMPT_INVENTORY_2026-04-19.md):业务中现存提示词的总清单,覆盖后端主链、前端遗留、自定义世界、角色形象生成、场景背景生成与工具链 prompt。
- [FUNCTION_SCRIPT_CATALOG_2026-04-04.md](./FUNCTION_SCRIPT_CATALOG_2026-04-04.md)Function 独立脚本目录与分类速查。
- [TASK_GENERATION_TRACE_2026-04-08.md](./TASK_GENERATION_TRACE_2026-04-08.md):任务描述、达成条件与奖励生成链路梳理。
- [CUSTOM_WORLD_TEMPLATE_DEPENDENCY_INVENTORY_2026-04-08.md](./CUSTOM_WORLD_TEMPLATE_DEPENDENCY_INVENTORY_2026-04-08.md):自定义世界当前仍依赖哪些模板世界设定的清单。

View File

@@ -265,6 +265,23 @@
但对“第一版角色动作资产生产”来说,它更适合作为增强通道,不建议先做成唯一主依赖。
实现更新(`2026-04-19`
- 当前仓库的 `image-to-video` 角色动作生成入口已切到火山方舟 `Seedance`
- 采用双参考图首尾帧方案:图片 1 约束首帧,图片 2 约束尾帧
- 当前请求体中的两张参考图角色分别固定为 `first_frame / last_frame`
- 当前固定参数为 `1:1``480p``4 秒`、单次 `1` 个视频
- 当前固定动作入口收敛为 `idle / run / attack / die`,不再内置固定 `hurt`
- 提示词里传给视频模型的动作名统一使用英文动作名
实现更新(`2026-04-20`
- `run / attack` 是当前固定动作入口里的基础必生成动作
- `idle / die` 改为可选增强动作,不再作为资产完成度硬门槛
- `idle` 缺失时运行时默认使用主图静止
- `die` 缺失时运行时默认播放一段基于主图的倒地过渡动画,并最终停在翻转倒地姿态
- 技能动作不走固定按钮,但对当前角色 `skills` 中的每个技能都属于必生成动作
## 5.3 补充路线:腾讯云相关能力
腾讯云相关接口里,`提交图片跳舞任务` 提供了:
@@ -422,12 +439,17 @@
- `idle`
- `run`
- `attack`
- `jump`
- `hurt`
- `die`
系统自动选择对应参考视频模板。
其中:
- `run / attack` 属于固定必生成动作
- `idle / die` 属于固定可选动作,未生成时走默认兜底
`jump``hurt` 这类扩展动作不再作为当前编辑器固定按钮,改为后续扩展动作槽位或手动补齐。
### B. 视频驱动
用户上传参考动作视频,系统抽姿态后再生成角色动作。
@@ -498,6 +520,8 @@ flowchart LR
- 文生图时,优先生成与当前项目角色素材视角一致的单人全身图
- 有参考图时,优先做“角色指定 + 风格收敛 + 视角纠偏”
- 用户直接上传素材时,先做校验、裁切、背景清理和尺寸标准化
- 编辑器未上传参考图时,主形象阶段默认附加一张由项目内可扮演角色 idle 帧拼成的风格参考板,用来锁定像素动作角色的轮廓语言、右朝向、体型比例与配色组织,避免模型只放大 Q 版比例却丢掉像素感
- 风格约束优先级里“像素动作角色感”高于“Q 版比例提示”;比例只允许轻度偏大头,不允许退化成普通软萌插画或儿童绘本风
### 角色视角要求
@@ -623,6 +647,16 @@ flowchart LR
8. 生成 Sprite Sheet
9. 输出动画元数据
### 当前工程的抠像补充策略
针对角色动作视频抽帧后常见的“后段帧出现白底”“角色轮廓残留绿幕像素点”问题,当前工程内的背景清理不再只依赖单一绿幕阈值,而是统一改为以下顺序:
1. 先识别边界连通的可移除背景区域,同时覆盖纯绿色绿幕和高亮低色差白底。
2. 再向主体边缘的半透明软边做一轮有限扩张,把压缩后残留的白边、绿边纳入透明化处理。
3. 最后对贴近透明边缘的像素做去污,优先压掉绿色溢色,并把白边/绿边颜色拉回附近前景主体颜色,减少抽帧后的轮廓发白、发绿。
这样可以避免把角色内部的白色高光、白色装备整体误删,同时能更稳定地清理视频模型在末段帧里偶发的白背景和压缩噪点。
### 像素化策略
推荐做法:
@@ -703,7 +737,14 @@ export interface GeneratedCharacterAnimationAsset {
id: string;
characterId: string;
visualAssetId: string;
action: BaseAnimationSlot | 'cast' | 'talk' | 'skill1' | 'skill2' | 'skill3' | 'skill4';
action:
| BaseAnimationSlot
| 'cast'
| 'talk'
| 'skill1'
| 'skill2'
| 'skill3'
| 'skill4';
sourceProvider: 'aliyun-wan' | 'volc-seedance' | 'tencent' | 'local';
sourceMode: 'template' | 'video-drive' | 'audio-drive';
frameCount: number;
@@ -826,7 +867,8 @@ generatedAnimationOverrides?: Partial<Record<AnimationState, string>>;
## 10.3 本地 API 层
当前项目已经有 `scripts/dev-server/localApiPlugins.ts` 这种本地 API 插件机制,应该复用。
`2026-04-19` 起,旧 `scripts/dev-server/localApiPlugins.ts` 已从仓库删除。
当前角色视觉与动画相关 `/api/*` 能力应统一落到 `server-node/src/modules/assets/**``server-node/src/modules/ai/**`,不再复用旧 Vite 本地插件链路。
建议新增:
@@ -912,63 +954,54 @@ draft
### 12.1 基础动作槽位必须非空
第一版要求以下基础动作槽位全部有内容
第一版要求以下动作能力按“必生成 / 可选兜底”拆开
| 动作槽位 | 是否必填 | 建议来源 |
| --- | --- | --- |
| `idle` | 必填 | 模板生成 |
| `acquire` | 必填 | 可由短持物 / 抬手动作生成 |
| `attack` | 必填 | 模板生成 |
| `run` | 必填 | 模板生成 |
| `jump` | 必填 | 模板生成 |
| `double_jump` | 必填 | 可由跳跃二次变体生成 |
| `jump_attack` | 必填 | 跳跃攻击模板 |
| `dash` | 必填 | 冲刺模板 |
| `hurt` | 必填 | 受击模板 |
| `die` | 必填 | 倒地 / 消散模板 |
| `climb` | 必填 | 攀爬模板 |
| `wall_slide` | 必填 | 可由攀爬或停滞帧变体生成 |
当前编辑器固定生成入口补充说明(`2026-04-19`
这里“不能为空”指的是:
- 固定按钮只保留 `idle / run / attack / die`
- `hurt` 不再作为固定生成按钮
- 如果运行时仍需 `hurt` 资源,应通过后续扩展动作槽位或手动补齐
- 每个基础动作槽位必须能挂到一套可播放资产
- 不允许在运行时出现 `null` 或空映射
- 个别低优先动作允许由近似动作衍生,但槽位本身必须有有效资源
| 动作能力 | 是否必填 | 建议来源 |
| ------------------------------- | -------- | ---------------------------------------------------------- |
| `run` | 必填 | 模板生成 |
| `attack` | 必填 | 模板生成 |
| `skills[*].actionPreviewConfig` | 必填 | 技能编辑面板逐个生成 |
| `idle` | 可选 | 模板生成;缺失时默认主图静止 |
| `die` | 可选 | 模板生成;缺失时默认主图倒地过渡动画,最终停在翻转倒地姿态 |
### 12.2 技能动作不是第一版强制项
这里“必填”指的是:
第一版可选:
- `run / attack` 必须能挂到一套可播放资产
- 角色当前每个技能都必须有可播放的 `actionPreviewConfig`
- `idle / die` 不再进入“缺失即阻塞发布”的判断
- 运行时表现仍然不能空白;`idle / die` 的缺口由默认兜底承接
- `skill1`
- `skill1_jump`
- `skill1_bullet`
- `skill1_bullet_fx`
- `skill2`
- `skill2_jump`
- `skill3`
- `skill3_jump`
- `skill3_bullet`
- `skill3_bullet_fx`
- `skill4`
### 12.2 技能动作改为“按角色已配技能强制”
第一版不再要求预留整套固定技能枚举,但要求:
- 当前角色 `skills` 数组里的每个技能都要补出 `actionPreviewConfig`
- 技能动作继续在技能编辑面板逐个生成,不并入固定四按钮
策略建议:
- 基础动作先全量补齐
- 技能动作后续按角色职业差异再补
- 先补 `run / attack`
- 再逐个补当前角色已有技能动作
- `idle / die` 作为可选增强按需要补
- 投射物与特效优先继续复用当前项目已有素材与技能特效系统
### 12.3 第一阶段优先模板
先优先做这些高价值模板:
| 模板 | 推荐时长 | 是否循环 | 说明 |
| --- | --- | --- | --- |
| `idle` | 2s-4s | 是 | 微动作、呼吸 |
| `run` | 2s-3s | 是 | 固定侧向 |
| `attack` | 2s-4s | 否 | 近战基础攻击 |
| `jump` | 1s-2s | 否 | 起跳与空中姿态 |
| `hurt` | 1s-2s | 否 | 受击短动作 |
| `die` | 2s-4s | 否 | 倒下或消散 |
| 模板 | 推荐时长 | 是否循环 | 说明 |
| -------- | -------- | -------- | -------------- |
| `idle` | 2s-4s | 是 | 微动作、呼吸 |
| `run` | 2s-3s | 是 | 固定侧向 |
| `attack` | 2s-4s | 否 | 近战基础攻击 |
| `jump` | 1s-2s | 否 | 起跳与空中姿态 |
| `die` | 2s-4s | 否 | 倒下或消散 |
### 12.4 不建议第一阶段就重投入的动作

View File

@@ -82,7 +82,8 @@
### 2.3 本地 API 插件里已经有 DashScope 接入样板
`scripts/dev-server/localApiPlugins.ts` 里已经接了自定义世界场景图
本文撰写时,旧 `scripts/dev-server/localApiPlugins.ts` 里已经接了自定义世界场景图
截至 `2026-04-19`,该文件已从仓库删除,对应样板能力应改为参考 `server-node/src/modules/assets/**``server-node/src/modules/ai/**`
- 默认 DashScope base URL 已经存在
- 已经有异步任务创建、轮询、下载、落盘、写 manifest 的完整样板
@@ -480,7 +481,7 @@
- 最少改 UI
- 最快复用当前 `CharacterAssetPanel`
- 最容易复用 `localApiPlugins.ts` 里现有 DashScope 异步任务模式
- 最容易复用现已迁入 `server-node` DashScope 异步任务模式
## 9.2 第二轮
@@ -641,5 +642,6 @@
- `src/components/preset-editor/characterAssetStudioPersistence.ts`
- `src/routing/appRoutes.tsx`
- `src/services/ai.ts`
- `scripts/dev-server/localApiPlugins.ts`
- `server-node/src/modules/assets/**`
- `server-node/src/modules/ai/**`
- `docs/technical/AI_CHARACTER_ANIMATION_TECHNICAL_SOLUTION_2026-04-04.md`

View File

@@ -73,12 +73,12 @@
## 5. 旧工具链隔离状态
`scripts/dev-server/**` 中的旧 Vite 本地插件已经不再由 `vite.config.ts` 注入,也不再作为当前开发入口使用。
`2026-04-19` 起,`scripts/dev-server/**` 中的旧 Vite 本地插件实现代码已经从仓库删除,也不再作为当前开发入口使用。
旧文件保留用途
当前保留状态
- 作为历史迁移参考
- 对照旧 DashScope 调用与文件写入逻辑
- `scripts/dev-server/` 目录只保留迁移说明 README
- 旧链路的历史背景由 `docs/audits/engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md` 等审计文档承接
新增编辑器或资产能力时,应优先写入:
@@ -103,4 +103,4 @@
- 前端编辑器组件已通过统一 SDK 或资源 ID 访问编辑器 API。
- Vite 已代理 `/api/editor``/api/assets` 到 Node 后端。
- 写接口已经有环境门禁。
- 旧 Vite 本地插件不再是当前工具链入口
- 旧 Vite 本地插件代码已删除,不再保留并行实现

View File

@@ -16,7 +16,7 @@
当前不再使用:
- Vite 本地 API 插件 `scripts/dev-server/` 作为当前接口入口
- 已删除的旧 Vite 本地 API 插件链路 `scripts/dev-server/*.ts`
## 2. 技术栈
@@ -162,6 +162,11 @@ JWT 现状:
- `POST /api/runtime/items/runtime-intent`
- `POST /api/runtime/quests/generate`
补充说明(`2026-04-19`
- `POST /api/custom-world/scene-image` 现在支持前端仅提交 `profile + landmark + userPrompt` 上下文,由后端统一补齐场景图 prompt 与默认 negative prompt。
- runtime story option 的 `interaction` 元数据现在由后端随 option 一并返回,前端不再本地按 `functionId` 重建 NPC / treasure 交互语义。
编辑器工具:
- `GET /api/editor/catalog/items`
@@ -198,7 +203,11 @@ Story
Custom World
- Node 后端直接复用前端现有多阶段生成编排
- Node 后端当前已自持 `server-node/src/modules/custom-world/**` 运行时模块
- 已承接 `creator intent` 归一化、`anchorPack / lockState` 推导、framework normalize、runtime profile compile
- `customWorldOrchestrator``customWorldAgentFoundationDraftService` 已不再运行时 import 前端 `src/services/customWorld*.ts``src/types.js`
- `server-node/src/prompts/customWorldPrompts.ts` 已承接 foundation draft 与 scene image 使用的 custom world prompt source
- 上述 prompt 迁移只改变源码归属位置,没有改动提示词正文
- 当前保留 `session + answers + SSE progress/result/error` 协议
- 前端已支持接收真实阶段进度对象
@@ -238,4 +247,4 @@ Vite 当前只负责代理,不再提供本地 API 插件。
全部转发到 Node 后端。
`scripts/dev-server/**` 文件仅保留为迁移参考,不再由 `vite.config.ts` 注入
`scripts/dev-server/` 目录现仅保留 README 作为迁移说明,旧本地 API 实现代码已于 `2026-04-19` 删除

View File

@@ -0,0 +1,190 @@
# Prompt 目录收口方案2026-04-19
## 1. 这次调整解决什么问题
此前提示词分散在多个后端、前端和工具文件里:
- `server-node/src/modules/ai/**`
- `server-node/src/modules/quest/**`
- `server-node/src/modules/runtime-item/**`
- `server-node/src/services/customWorld*.ts`
- `server-node/src/services/eightAnchorPromptBuilder.ts`
- `server-node/src/modules/assets/characterAssetRoutes.ts`
- `src/services/**`
- `src/tools/qwenSpriteSheetToolModel.ts`
- `src/components/**`
问题主要有三类:
1. 业务逻辑和 prompt 文本混写,改提示词时容易顺手改坏运行时逻辑。
2. 同一类 prompt 缺少集中入口,排查系统 prompt / user prompt / repair prompt 成本高。
3. 老桥接层、测试和新业务链路同时依赖时,迁移成本高,容易出现导出断裂。
这次收口目标不是“重写全部 AI 链路”,而是把当前正式业务 prompt 主源收到独立目录,业务模块退化成“准备上下文 + 调用 prompt 脚本”。
## 2. 新目录
本轮落地后的目录:
```text
packages/shared/src/prompts/
└─ qwenSprite.ts
server-node/src/prompts/
├─ characterAssetPrompts.ts
├─ chatPromptBuilders.ts
├─ customWorldAgentPrompts.ts
├─ customWorldEntityPrompts.ts
├─ customWorldOrchestratorPrompts.ts
├─ customWorldSceneNpcPrompts.ts
├─ eightAnchorPrompts.ts
├─ questPrompts.ts
├─ runtimeItemPrompts.ts
├─ storyOrchestratorPrompts.ts
└─ storyPromptBuilders.ts
src/prompts/
├─ characterChatPrompts.ts
├─ customWorldEntityActionPrompts.ts
├─ customWorldOrchestratorPrompts.ts
├─ customWorldPrompts.ts
├─ customWorldRolePromptDefaults.ts
├─ questPrompts.ts
├─ qwenSpriteSheetToolPrompts.ts
├─ runtimeItemPrompts.ts
├─ storyOrchestratorPrompts.ts
└─ storyPromptBuilders.ts
```
当前职责划分:
- `chatPromptBuilders.ts`
- 角色私聊 / NPC 聊天 / 招募对话 prompt
- `storyPromptBuilders.ts`
- 主剧情 system prompt 与 user prompt builder
- `storyOrchestratorPrompts.ts`
- 剧情语言修复 prompt
- `questPrompts.ts`
- 任务意图 system prompt 与 user prompt builder
- `runtimeItemPrompts.ts`
- 运行时物品意图 system prompt 与 user prompt 文本装配
- `customWorldOrchestratorPrompts.ts`
- 自定义世界主编排 JSON 生成与 repair prompt
- `customWorldAgentPrompts.ts`
- 世界草稿 JSON prompt、补角色 / 补地点 prompt
- `customWorldEntityPrompts.ts`
- 世界编辑器角色 / 场景实体生成 prompt
- `customWorldSceneNpcPrompts.ts`
- 世界编辑器场景 NPC 生成 prompt
- `characterAssetPrompts.ts`
- 角色主图 / 动作试片 / 角色关联场景 prompt
- `eightAnchorPrompts.ts`
- 八锚点状态推断、模式规则与正式单轮共创 prompt
- `src/prompts/customWorldPrompts.ts`
- 自定义世界分阶段生成 prompt 与场景背景图 prompt
- `src/prompts/qwenSpriteSheetToolPrompts.ts`
- 精灵图工具主词 / 分镜词 / 修帧词 / 负面词
- `src/prompts/customWorldRolePromptDefaults.ts`
- 角色资产工作台默认 prompt 种子
- `src/prompts/customWorldEntityActionPrompts.ts`
- 编辑器技能动作 prompt
- `packages/shared/src/prompts/qwenSprite.ts`
- 共享资产层的基础角色 prompt 模板
## 3. 落地规则
### 3.1 业务模块只做两件事
1. 整理运行时上下文
2. 调用 `server-node/src/prompts/**` 下的脚本输出 prompt
不要在业务模块里继续直接内联大段 system prompt / repair prompt / user prompt 模板文本。
### 3.2 Prompt 文件只放文本相关职责
允许放:
- system prompt 常量
- user prompt builder
- repair prompt builder
- prompt 专用的文本摘要函数
不建议放:
- 运行时状态 mutation
- 仓储读写
- HTTP 处理
- 与 prompt 无关的领域推导
### 3.3 兼容层保留旧导出
本轮对已有纯 prompt builder 文件采取了兼容迁移,旧路径保留为薄 re-export
- `server-node/src/modules/ai/chatPromptBuilders.ts`
- `server-node/src/modules/ai/storyPromptBuilders.ts`
- `server-node/src/services/eightAnchorPromptBuilder.ts`
- `src/services/prompt.ts`
- `src/services/characterChatPrompt.ts`
- `src/services/questPrompt.ts`
- `src/services/runtimeItemAiPrompt.ts`
- `src/tools/qwenSpriteSheetToolModel.ts`
- `src/components/asset-studio/customWorldRolePromptDefaults.ts`
- `packages/shared/src/assets/qwenSprite.ts`
对于 `runtimeQuestModule.ts``runtimeItemModule.ts` 这类被桥接层直接引用的模块,本轮保留原导出名,通过 re-export 指向新 prompt 文件,保证兼容性。
## 4. 后续新增 prompt 的写法
新增提示词时按下面顺序处理:
1. 先判断属于后端、前端/编辑器还是共享工具层。
2. 后端正式业务优先补到 `server-node/src/prompts/*.ts`
3. 前端/编辑器 prompt 优先补到 `src/prompts/*.ts`
4. 可复用的共享资产 prompt 优先补到 `packages/shared/src/prompts/*.ts`
5. 业务模块只传入已经整理好的上下文字段,不在模块内部继续拼长文本。
6. 至少补一条该 prompt 的调用链测试或现有测试断言。
建议命名:
- system prompt`XXX_SYSTEM_PROMPT`
- repair prompt`buildXXXRepairPrompt`
- user prompt`buildXXXPrompt`
- 纯文本装配:`buildXXXPromptText`
## 5. 本轮范围与当前状态
本轮已经收口:
- Story
- Chat
- Quest
- Runtime Item
- Custom World 主编排
- Custom World Agent 草稿增补
- Custom World 编辑器角色 / 场景 / 场景 NPC 生成
- Character Asset
- Eight Anchor
- Scene Image
- 前端剧情 / 私聊 / 任务 / 物品 prompt 兼容层
- 编辑器与工具链 prompt 种子
当前状态:
- 正式业务 prompt 主源已经集中到 prompt 目录。
-`services/``tools/``components/` 下保留的相关文件主要是兼容层或调用方。
- 当前没有再发现需要优先继续抽离的大块业务 prompt 正文。
## 6. 验证方式
本轮调整后建议至少执行:
- `npm run check:encoding`
- `npm run server-node:test`
- `npm --prefix server-node run build`
本轮实测结果:
- `npm run check:encoding` 通过
- `npm --prefix server-node run build` 通过
- `npm run build` 通过
- `npm run server-node:test` 143 项全部通过

View File

@@ -4,6 +4,8 @@
## 文档列表
- [REPO_NOISE_CLEANUP_BASELINE_2026-04-19.md](./REPO_NOISE_CLEANUP_BASELINE_2026-04-19.md):落实工程清理审计第一阶段后的仓库噪音清理范围、忽略规则闭合点与后续约束。
- [PROMPT_DIRECTORY_MANAGEMENT_2026-04-19.md](./PROMPT_DIRECTORY_MANAGEMENT_2026-04-19.md):后端提示词收口到 `server-node/src/prompts/` 的目录方案、兼容策略与后续新增规则。
- [SPACETIME_DEV_URI_HOTFIX_2026-04-20.md](./SPACETIME_DEV_URI_HOTFIX_2026-04-20.md):修复开发默认配置把 Spacetime 连接误指向 Vite `3000` 端口的问题。
- [SPACETIME_AUTH_TOKEN_FALLBACK_HOTFIX_2026-04-20.md](./SPACETIME_AUTH_TOKEN_FALLBACK_HOTFIX_2026-04-20.md):本地 token 失效时自动降级匿名连接,并提示“登录已过期”的热修记录。
- [NODE_DEV_STARTUP_HOTFIX_2026-04-20.md](./NODE_DEV_STARTUP_HOTFIX_2026-04-20.md)`npm run dev` 启动失败的热修记录、根因与验证结果。

View File

@@ -0,0 +1,53 @@
# 仓库噪音清理基线2026-04-19
更新时间:`2026-04-19`
## 1. 背景
本次清理落实以下审计结论:
- `docs/audits/engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md`
目标只覆盖审计文档推荐执行顺序中的第一阶段,也就是先把仓库噪音产物和本地检查残留从主工程面清掉,不在这一轮同时推进前后端边界迁移或无入口模块归档。
## 2. 本次已执行的清理
本轮已从仓库中移除以下高置信度噪音产物:
- 根目录的 `.codex-*` 日志、`.preview.*` 输出、`tmp_*` 扫描文本与 HTML、`npc-editor-*` 截图和调试文件、`temp-write-check.txt`
- 根目录 `temp-build-goal-check/` 大体量检查产物目录
- `scripts/__pycache__/` Python 缓存目录与 `.pyc` 文件
- `.codex-logs/` 中遗留的旧日志与 restore backup 文件
这些文件都不属于正式运行时代码、PRD、设计稿或生产资源继续保留只会污染根目录视野、拖慢扫描和 review并增加误判成本。
## 3. 规则闭合
为避免相同问题反复回流,本次同步补齐了两层规则:
1. `.gitignore`
- 新增 `/.codex-logs/`
- 新增 `*.py[cod]`
- 保持 `.preview.*``tmp_*``tmp/``npc-editor-*``temp-write-check.txt``temp-build-goal-check/``**/__pycache__/` 为忽略项
2. `.eslintrc.cjs`
- 保持 `temp-build-goal-check/**`
- 保持 `.preview.*``tmp_*``tmp/**``npc-editor-*``temp-write-check.txt`
- 保持 `**/__pycache__/**`
这样 Git 与 ESLint 对临时产物的忽略口径保持一致,不会再出现某个目录虽然被 Git 忽略,但仍被 lint 扫进去的情况。
## 4. 后续约束
- 以后所有临时扫描结果、调试截图、HTML 导出、一次性日志,不要直接落在仓库根目录。
- 如需保留本地临时产物,统一放到本地忽略目录,例如 `tmp/``.codex-logs/`
- `temp-build-goal-check/` 仍视为本地检查产物,不作为主工程目录的一部分长期保留。
- Python 脚本执行后产生的 `__pycache__` 不应进入仓库。
## 5. 本轮未处理范围
以下内容仍按审计原计划留待后续阶段处理:
- `scripts/dev-server/localApiPlugins.ts` 及旧 Vite 本地 API 链路的归档
- 无运行时入口或仅测试引用的孤岛模块处置
- 前后端双份真相收口与 `server-node -> src/**` 反向依赖治理
- 巨型热点文件拆分

View File

@@ -0,0 +1,174 @@
# Runtime Story 后端边界迁移记录2026-04-19
更新时间:`2026-04-20`
## 1. 本轮目标
本轮只处理 `docs/audits/engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md` 中已经明确、且可以无需求漂移落地的两类问题:
1. `RuntimeStoryOptionView``interaction` 语义不能再由前端根据 `functionId + currentEncounter` 本地重建。
2. `src/hooks/story/npcEncounterActions.ts``help / leave / fight / spar / quest_turn_in` 等运行时动作不能继续在浏览器里保留旧本地结算分支。
本轮**没有**做以下事情:
1. 没有修改任何功能需求。
2. 没有修改任何业务提示词。
3. 没有扩展新的动作能力面。
4. 没有处理 custom-world 领域规则从 `server-node -> src/services/**` 反向 import 的剩余问题。
## 2. 已落地收口
### 2.1 runtime option interaction 改为后端唯一构建
已完成:
1. `server-node/src/modules/story/runtimeSession.ts`
- `buildOptionView(...)` 直接输出 `interaction`
- `buildAvailableOptions(...)` 直接返回带 `interaction` 的 runtime option
- 删除前一版补丁式附加逻辑,避免后续再出现“双份映射”
2. `server-node/src/modules/story/storyActionService.ts`
- story option view model 直接透传 `option.interaction`
3. `src/services/runtimeStoryService.ts`
- 前端不再根据 `currentEncounter` 重建一份 `interaction`
- 只消费服务端返回的 `option.interaction`
这意味着:
1. `npc_chat / npc_help / npc_fight / npc_leave / npc_trade / npc_gift / npc_quest_*`
2. `treasure_secure / treasure_inspect / treasure_leave`
这些交互语义以后都以后端 runtime session 为准。
### 2.2 npcEncounterActions 收缩为前端壳层
已完成:
1. `src/hooks/story/npcEncounterActions.ts`
- `help`
- `leave`
- `fight`
- `spar`
- `quest_accept`
- `quest_turn_in`
以上动作已统一改为走 `resolveServerRuntimeChoice(...)`
2. 删除本轮迁移后已无消费方的本地 helper / import / 常量残留,避免误以为本地仍承担这部分结算职责。
当前前端在这条链路上只保留:
1. 选项点击分发
2. trade / gift / recruit 的 UI modal 打开
3. NPC 对话壳层与流式展示
4. 服务端返回结果的状态落地与 story 刷新
### 2.3 待接委托正式接取改为后端收口
已完成:
1. `server-node/src/modules/quest/questStoryActionService.ts`
- `npc_quest_accept` 会优先读取服务端快照里 `currentStory.npcChatState.pendingQuestOffer.quest`
- 如果当前聊天态里已经存在待接委托,就按这份已展示给玩家的委托正式接取,不再在后端临时重建另一份任务
2. `server-node/src/modules/story/storyActionService.ts`
- `npc_quest_accept` 在存在待接委托聊天态时,会继续输出 `displayMode: 'dialogue'` 的 current story
- 会清空 `pendingQuestOffer`
- 会恢复既有的三条自由追问建议,避免接任务后 UI 退回普通 story 文本态
3. `src/hooks/story/npcEncounterActions.ts`
- `acceptPendingNpcQuestOffer()` 不再本地直接修改 `quests / runtimeStats / storyHistory`
- 现在只负责触发 `resolveServerRuntimeChoice(...)`,由后端统一落地正式接取结果
这意味着:
1. “NPC 聊天里弹出的待接委托”不再是一份只存在于前端内存里的临时结果
2. 前端不会再一边展示待接委托、一边本地把正式 quest log 写出来
3. 正式接取后的 quest 真相源、聊天态投影与快照持久化统一以后端为准
### 2.4 NPC 聊天待接委托生成与浏览器 LLM fallback 继续后移
已完成:
1. `server-node/src/modules/ai/chatOrchestrator.ts`
- `streamNpcChatTurnFromOrchestrator(...)` 现在会基于前端提交的 `questOfferContext`
- 由后端判断是否应该生成 `pendingQuestOffer`
- quest draft 与引导文案 `introText` 由服务端一并回填到 `complete` 事件
2. `src/services/aiService.ts`
- `streamNpcChatTurn(...)` 已支持把 `questOfferContext` 送入后端
3. `src/hooks/story/npcEncounterActions.ts`
- NPC 单轮聊天结束后不再本地调用 `generateQuestForNpcEncounter(...)`
- 前端改为只消费服务端回填的 `pendingQuestOffer + introText`
4. `src/services/questDirector.ts`
- 浏览器端 quest draft 失败时,不再退回本地 LLM 调用
- 当前改为直接走 deterministic fallback quest compile
5. `src/services/runtimeItemAiDirector.ts`
- 浏览器端 runtime item intent 失败时,不再退回本地 LLM 调用
- 当前改为直接返回 deterministic fallback intents
这意味着:
1. NPC 聊天里“是否触发待接委托”的判定不再由前端依据 `turnCount / affinity / quests` 本地重跑
2. 浏览器不再保留 quest draft 与 runtime item intent 的正式 LLM fallback orchestration
3. 前端在这两条链路上继续退化为服务端结果消费层
## 3. 本轮涉及文件
代码:
1. `server-node/src/modules/story/runtimeSession.ts`
2. `server-node/src/modules/story/storyActionService.ts`
3. `server-node/src/modules/story/storyActionRoutes.test.ts`
4. `src/services/runtimeStoryService.ts`
5. `src/services/runtimeStoryService.test.ts`
6. `src/hooks/story/npcEncounterActions.ts`
7. `src/hooks/story/npcEncounterActions.test.ts`
8. `server-node/src/modules/quest/questStoryActionService.ts`
9. `server-node/src/modules/ai/chatOrchestrator.ts`
10. `server-node/src/modules/ai/orchestrator.test.ts`
11. `src/services/aiService.ts`
12. `src/services/questDirector.ts`
13. `src/services/runtimeItemAiDirector.ts`
说明:
1. 本轮没有改 prompt 文本。
2. 本轮没有新增或删减 runtime functionId。
3. 本轮只是把既有交互语义和动作结算职责收回到后端。
## 4. 验证结果
已执行并通过:
1. `npm run check:encoding`
2. `npx vitest run src/services/runtimeStoryService.test.ts src/hooks/story/npcEncounterActions.test.ts`
3. `npx tsx --test server-node/src/modules/story/runtimeSession.test.ts`
新增的防回退验证包括:
1. 前端 `runtimeStoryService` 会保留并消费服务端返回的 `interaction`
2. `npcEncounterActions``help / leave / fight / spar` 明确走服务端 resolver
3. `npc_quest_turn_in` 会把 `questId` 原样透传到后端
4. 后端 `runtimeSession` 明确直接构建并透传 NPC option interaction
5. 待接委托正式接取会复用快照中的 pending quest而不是在接取瞬间重新造一份 quest
6. 待接委托接取后仍保持 NPC 聊天展示态,并清空 `pendingQuestOffer`
7. NPC chat turn 的 pending quest offer 现在由后端直接产出,前端不再本地二次决定
8. 浏览器端 quest / runtime item 已不再保留直接调用本地 LLM 的 fallback orchestration
补充说明:
1. `server-node/src/modules/story/storyActionRoutes.test.ts` 已补充路由级 interaction 断言。
2. `2026-04-20` 的补充收口改为以 `storyActionRoutes.test.ts` 直接覆盖待接委托正式接取链路,验证快照输入与快照输出是否都以后端为准。
3. 本轮没有改 prompt 文本,也没有新增函数能力面,只是继续把现有 runtime story 结算权后移。
## 5. 暂未处理的后续边界项
这轮故意没有继续扩大的点:
1. `server-node/src/modules/ai/customWorldOrchestrator.ts` 仍直接依赖 `src/services/customWorld*.ts`
2. `server-node/src/services/customWorldAgentFoundationDraftService.ts` 仍直接依赖 `src/services/customWorld*.ts``src/types.ts`
3. `pending quest offer` 的 replace / abandon 仍由前端 UI 壳层协调
4. 浏览历史、本地快照真相源、custom-world 领域规则共享化仍需后续阶段继续推进
原因:
1. 这些点会牵涉 shared contract 抽取与较大范围重组
2. 若与本轮 runtime story 收口混做,容易把“边界迁移”误做成“需求改造”
因此本轮把范围锁在 runtime story 主链,优先交付一条可验证、可继续扩展的后端边界基线。

View File

@@ -0,0 +1,97 @@
# 场景多幕创作与流程改造实施进度 2026-04-20
更新时间:`2026-04-20`
## 1. 本轮落地范围
本轮先完成 `scene_chapter` 的第一批基础链路,让“场景章节 -> 多幕 -> 主角色 -> 幕背景/相遇 NPC”真正进入现有创作工具和草稿系统。
本轮目标不是一次性做完 PRD 全量能力,而是先把下面这条主干打通:
1. 草稿层可以承载 `scene chapter / scene act`
2. 草稿编译器可以把 `scene_chapter` 编译成正式卡片
3. 创作页可以看到、打开、编辑 `scene_chapter`
4. 编辑后的幕信息可以正确写回草稿
5. 运行时共享层先具备读取幕背景、主角色、相遇 NPC 池的基础能力
## 2. 本轮已落地
## 2.1 草稿与运行时结构
已补齐多幕相关结构:
1. `CustomWorldFoundationDraftProfile.sceneChapters`
2. `CustomWorldFoundationDraftSceneChapter`
3. `CustomWorldFoundationDraftSceneAct`
4. `CustomWorldProfile.sceneChapterBlueprints`
5. `StoryEngineMemoryState.currentSceneActState`
同时补齐了地点/营地草稿里的 `imageSrc`,避免幕背景回落时丢失现有场景图资产引用。
## 2.2 scene_chapter 草稿编译
`server-node/src/services/customWorldAgentDraftCompiler.ts` 已完成第一批接入:
1. `scene_chapter` 正式进入草稿编译结果
2. 支持从显式 `sceneChapters` 或地点/章节数据回退生成场景章节卡
3. 每张卡会编译出场景摘要、幕结构总览、每幕背景图、主角色、辅助 NPC、幕目标、过渡钩子
4. 每幕生成动态可编辑 section id
5. 已增加基础警告:
- 幕数不足
- 缺背景图
- 缺相遇 NPC
- 主角色不在第一位
- 缺线程挂钩
- NPC 或线程引用失配
## 2.3 scene_chapter 草稿编辑
`server-node/src/services/customWorldAgentDraftEditService.ts` 已支持:
1. 编辑场景章节标题、摘要
2. 编辑每幕标题、摘要、背景图链接、相遇 NPC、幕目标、过渡钩子
3. `encounterNpcIds` 支持用角色 id 或角色名回写
4. 回写后自动用第一位 NPC 覆盖 `primaryNpcId`
`server-node/src/services/customWorldAgentChangeSummaryService.ts` 也已支持解析 `scene_chapter` 标题。
## 2.4 创作页展示
前端已完成第一批接入:
1. 草稿抽屉正式加入 `scene_chapter` 分组
2. `scene_chapter` 分组顺序位于 `chapter` 后、`thread`
3. 详情面板已支持 `场景章节` 类型标签
4. 幕背景 section 在详情面板里会直接渲染图片预览
5. 编辑面板已支持幕摘要 / 相遇 NPC / 幕目标 / 过渡钩子等动态多行字段
## 2.5 运行时基础层
本轮同步补齐了幕运行的基础读取能力,便于下一轮继续接游戏流程:
1. 当前幕背景图优先覆盖场景默认背景
2. 当前幕相遇 NPC 池可参与场景相遇过滤
3. 当前幕主角色与负好感有限聊天的判定 helper 已建立
4. 场景预览层已能识别“负好感主角色不直接自动开战”的基础分支
## 3. 当前仍未完成
下面这些仍属于 PRD 未完项,需要下一轮继续:
1. 创作页里的“新增幕 / 删除幕 / 调整幕顺序”交互
2. 背景图配置与 NPC 配置的独立面板化交互
3. 发布期 `qualityFindings` / blocker 的正式接入
4. `SceneActRuntimeState` 的完整推进与持久化
5. 当前幕主角色负好感 `5` 轮聊天限制的前后端完整闭环
6.`5` 轮“铺垫式收束”提示与强制退出聊天态
7. 幕切换后的系统提示与 Adventure 面板状态展示
## 4. 下一轮建议顺序
建议下一轮按下面顺序继续:
1. 先补 `SceneActRuntimeState` 初始化与幕推进
2. 再接 `npcEncounterActions / aiService / chatOrchestrator` 的负好感有限聊天闭环
3. 最后补创作页的幕增删改序和独立配置面板
这样可以先把“能跑”补齐,再把“编辑体验”补完整。

View File

@@ -42,7 +42,8 @@
### 2.2 当前 API 层还只是开发期能力
这些接口现在主要由 [scripts/dev-server/localApiPlugins.ts](/E:/Repos/Genarrative/scripts/dev-server/localApiPlugins.ts) 挂在 Vite dev/preview 服务器里。
本文撰写时,这些接口主要由`scripts/dev-server/localApiPlugins.ts` 挂在 Vite dev/preview 服务器里。
截至 `2026-04-19`,这条旧链路已经从仓库删除,当前统一由 `server-node` 承接。
这在本地开发阶段很方便,但生产环境存在几个明显问题:
@@ -500,7 +501,14 @@ flowchart LR
## 11.1 第一优先级:把 Vite 里的 API 能力抽出来
当前 [scripts/dev-server/localApiPlugins.ts](/E:/Repos/Genarrative/scripts/dev-server/localApiPlugins.ts) 里的能力,建议分三类迁移:
该迁移建议已完成,原 `scripts/dev-server/localApiPlugins.ts` 已于 `2026-04-19` 删除。
对应能力现统一收口到以下正式模块:
- `server-node/src/modules/editor/**`
- `server-node/src/modules/assets/**`
- `server-node/src/modules/ai/**`
原旧链路中的能力,可按以下三类理解其迁移归属:
### A. 运行时代理接口