Prune stale docs and update .hermes content

Delete a large set of outdated documentation (many files under docs/ and .hermes/plans/, including audits, design, prd, technical, planning, assets, and todos). Update and consolidate .hermes content: refresh shared-memory pages (decision-log, development-workflow, document-map, pitfalls, project-overview, team-conventions) and several skills/references under .hermes/skills. Also modify AGENTS.md, README.md, UI_CODING_STANDARD.md, docs/README.md and .encoding-check-ignore. Purpose: clean up stale planning/audit material and keep current hermes documentation and related top-level docs in sync.
This commit is contained in:
2026-05-15 06:24:07 +08:00
parent 2eded08bc7
commit 3cb3efb4d0
708 changed files with 4033 additions and 142328 deletions

View File

@@ -1,55 +1,19 @@
# 文档总览
# Genarrative 文档入口
`docs/` 现在按主题拆成了 6 类;旧后端路线文档开始聚合和删除,后续实现以 Rust / SpacetimeDB 当前基线为准
本目录已经在 `2026-05-15` 完成压缩整理:旧 PRD、阶段计划、修复记录、审计报告、技术方案和查询手册中的仍有效内容统一融合到下列当前文档。旧文档不再作为实现依据
## 快速入口
## 当前文档
- [经验沉淀](./experience/README.md)项目开发经验、UI 交接、历史实现经验
- [审计与复盘](./audits/README.md):工程审查、文本/乱码审计、专项落地审计
- [系统设计](./design/README.md):玩法、关系、物品与对话设计
- [技术方案](./technical/README.md):动画、服务端、外部产品形态拆解
- [规划与优先级](./planning/README.md):当前阶段的迭代排序与落地优先级。
- [参考目录](./reference/README.md):脚本/Function 速查入口。
重点补充RPG 创作与运行时脚本职责地图见 [RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md](./reference/RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md)。
- [埋点查询](./tracking/README.md):埋点原始事件与聚合投影的本地 SQL 查询。
- [运营查询](./operations/README.md):任务、领奖、钱包对账等后台核查查询。
- [PRD](./prd/README.md):产品需求与阶段计划;参考 MOKU / 幕间类 AI 文游的陶泥儿 `text-game` 模板口径见 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md](./prd/AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md),视觉小说模板 TXT 玩法平台化接入口径见 [AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md](./prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md),创意互动内容 Agent Phase 1 的 LangChain-Rust PoC、拼图闭环和并行任务拆分见 [CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md](./prd/CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md),幸存者类模板闭环见 [AI_NATIVE_SURVIVOR_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-05.md](./prd/AI_NATIVE_SURVIVOR_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-05.md),后台管理独立前端工程见 [ADMIN_WEB_CONSOLE_PRD_2026-04-30.md](./prd/ADMIN_WEB_CONSOLE_PRD_2026-04-30.md),新增 RPG 开场动画方案见 [AI_NATIVE_RPG_OPENING_ANIMATION_PRD_2026-04-25.md](./prd/AI_NATIVE_RPG_OPENING_ANIMATION_PRD_2026-04-25.md),新增抓大鹅 Match3D 玩法方案见 [AI_NATIVE_MATCH3D_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-04-30.md](./prd/AI_NATIVE_MATCH3D_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-04-30.md),方洞挑战创作、发布与试玩闭环见 [AI_NATIVE_SQUARE_HOLE_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-04.md](./prd/AI_NATIVE_SQUARE_HOLE_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-04.md)。
- [【项目基线】当前产品与工程约束-2026-05-15.md](./【项目基线】当前产品与工程约束-2026-05-15.md)产品定位、平台入口、UI 约束、协作规则和废弃路线
- [【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md](./【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md)server-rs DDD 边界、API 分组、SpacetimeDB schema 变更规则和当前表目录
- [【玩法创作】平台入口与玩法链路-2026-05-15.md](./【玩法创作】平台入口与玩法链路-2026-05-15.md):创作 Tab、草稿架、拼图、抓大鹅、视觉小说、方洞、大鱼、汪汪声浪和儿童向玩法的当前口径
- [【开发运维】本地开发验证与生产运维-2026-05-15.md](./【开发运维】本地开发验证与生产运维-2026-05-15.md)本地启动、测试、编码检查、SpacetimeDB 变更流程、生产运维、埋点和运营查询
生产部署切换到 systemd + Nginx + SpacetimeDB 自托管的总方案见 [PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md](./technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md),该文档也是当前生产 Jenkinsfile 的唯一入口。SpacetimeDB 表结构变更、自动迁移边界和保留旧数据的分阶段迁移流程见 [SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md](./technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md)private 表迁移 JSON 导入导出、HTTP 413 分片导入和旧数据库迁移流水线经验见 [SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md](./technical/SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md) 与 [JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md](./technical/JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md);后台管理独立前端工程技术方案见 [ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md](./technical/ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md)
新增稳定知识优先合并到上述文档。只有上述文档无法容纳某个长期主题时,才新增同目录的 `【标签名】中文标题-YYYY-MM-DD.md` 文档
SpacetimeDB 表结构变更、自动迁移边界和保留旧数据的分阶段迁移流程见 [SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md](./technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md)。
## 维护规则
创作 Agent 问答流式失败时保留已显示回复、并透出更具体上游错误的契约见 [CREATION_AGENT_STREAM_FAILURE_RETENTION_FIX_2026-05-05.md](./technical/CREATION_AGENT_STREAM_FAILURE_RETENTION_FIX_2026-05-05.md)
`maincloud` 相关脚本、环境变量、测试名和文档要求已统一判定为历史残留,后续禁止新增、运行或引用;当前后端 smoke 使用 `npm run api-server``/healthz`,详细规则见 [MAINCLOUD_REFERENCE_REMOVAL_POLICY_2026-05-06.md](./technical/MAINCLOUD_REFERENCE_REMOVAL_POLICY_2026-05-06.md)
基于 LangChain-Rust以“感知—思考—记忆—行动—反思—协作”闭环完成图文创意理解、拼图模板选择、积分范围确认、拼图草稿字段填充、立即试玩和自然语言修订草稿字段的 Agent 方案见 [CREATIVE_INTERACTIVE_CONTENT_AGENT_TECHNICAL_SOLUTION_2026-05-05.md](./technical/CREATIVE_INTERACTIVE_CONTENT_AGENT_TECHNICAL_SOLUTION_2026-05-05.md)。
创意互动内容 Agent Phase 1 的产品边界、实现细节、SpacetimeDB 落点、前端接入和可并行任务拆分见 [CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md](./prd/CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md)。
视觉小说模板接入以 [AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md](./prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md) 为最新口径:只吸收外部 TXT 玩法的模板创作与运行经验,禁止迁入外部平台功能,并删除回放。
AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md](./prd/AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md) 为最新口径:只吸收 MOKU / 幕间类 AI 文游的剧本游乐场、自由行动、AI GM、记忆和模拟器强反馈经验禁止迁入外部社区、支付、榜单、私有存档或回放。
## 推荐阅读顺序
1. 先看 [经验沉淀](./experience/README.md),快速建立这个项目的开发共识。
2. 再看 [工程审查总览](./audits/engineering/README.md) 和 [文本审计总览](./audits/text/README.md),了解当前风险。
3. 需要排期时看 [规划与优先级](./planning/README.md)。
4. 需要补方案时进入 [系统设计](./design/README.md) / [技术方案](./technical/README.md);涉及后端先看 [当前后端实现基线](./technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md),涉及生产发布链路先看 [生产部署计划](./technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md),涉及 SpacetimeDB 表结构变更时再看 [表结构变更约束](./technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md)。
5. 需要对齐目标边界时再进入 [PRD](./prd)。
## 分类规则
- `experience/`:偏方法论、交接经验、长期有效的开发结论。
- `audits/`:偏“现状扫描 / 问题定位 / 是否达标”的审查类文档。
- `design/`:偏玩法机制、叙事关系、系统结构设计。
- `technical/`:偏技术选型、实现路线、竞品/产品形态拆解。
- `planning/`:偏阶段优先级与推进顺序。
- `reference/`:偏目录、速查、检索辅助。
- `tracking/`:偏埋点原始事实和聚合投影查询,不放任务进度或钱包对账。
- `operations/`:偏后台运营核查、对账和排障查询。
## 文档命名规则
后续新增的 Markdown 文档文件名必须以分类标签开头,格式为 `【标签名】中文标题-日期.md`。标签用于跨目录检索,不替代上面的目录分类;历史文档不要求批量重命名,除非本次任务明确涉及该文档。
1. 代码和当前文档冲突时,以代码为准,并立即修正文档
2. 新增 Markdown 文件名必须使用 `【标签名】中文标题-YYYY-MM-DD.md`
3.`server-node`、Express、PostgreSQL、Go 服务端、`maincloud`、人工 `spacetime --root-dir`、Match3D 新草稿 Rodin/GLB、拼图和抓大鹅音频生成入口均不再作为当前实现依据
4. 涉及 SpacetimeDB 表、reducer、procedure、row shape 或 bindings 时,必须同步修改代码、迁移、生成绑定和 [后端架构文档](./【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md) 的表目录。

View File

@@ -1,36 +0,0 @@
# Domain Docs
How the engineering skills should consume this repo's domain documentation when exploring the codebase.
## Layout
This repo uses a **single-context** layout for Matt Pocock engineering skills:
- `CONTEXT.md` at the repo root, when present, is the primary domain glossary/context file.
- `docs/adr/`, when present, contains architecture decision records.
- If either path does not exist, proceed silently; do not block the task just to create it.
## Before exploring, read these
1. Root `CONTEXT.md`, if present.
2. Relevant ADRs under `docs/adr/`, if present.
3. Existing project context that predates this setup:
- `.hermes/README.md`
- `.hermes/shared-memory/project-overview.md`
- `.hermes/shared-memory/team-conventions.md`
- `.hermes/shared-memory/development-workflow.md`
- `.hermes/shared-memory/decision-log.md`
- `.hermes/shared-memory/pitfalls.md`
- Relevant files under `docs/technical/`, `docs/prd/`, `docs/design/`, and `docs/experience/`
Follow `AGENTS.md` when it is more specific than this file. If older docs conflict with current code or newer technical docs, treat current code and newer docs as authoritative and update stale docs when the task requires it.
## Use the glossary's vocabulary
When your output names a domain concept in an issue title, refactor proposal, diagnosis, test name, or implementation plan, use the term as defined in `CONTEXT.md` when available. Do not drift to synonyms the glossary explicitly avoids.
If the concept you need is not in the glossary yet, either use the established vocabulary from `.hermes/shared-memory/` and `docs/`, or note the gap for a future documentation pass.
## Flag ADR conflicts
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding it.

View File

@@ -1,35 +0,0 @@
# Issue tracker: Gitea
Issues and PRDs for this repo live as issues in the self-hosted Gitea remote:
- Remote: `https://git.genarrative.world/GenarrativeAI/Genarrative.git`
- Tracker type: Gitea Issues
## Conventions
- Prefer the Gitea `tea` CLI when it is installed and configured for this host.
- Do not use GitHub `gh` or GitLab `glab` for this repo unless the repository is explicitly migrated to those platforms.
- If `tea` is unavailable, use the Gitea Web UI or Gitea REST API for the same operations.
## Common operations with `tea`
Exact flags can vary by `tea` version. Run `tea issues --help` or `tea issue --help` before using a command in a new environment.
- Create an issue: `tea issues create --title "..." --body "..."`
- Read an issue: `tea issues view <number>`
- List issues: `tea issues list`
- Comment on an issue: use the installed `tea` issue comment command shown by `tea issues --help`; if unavailable, use the Gitea Web UI or REST API.
- Apply labels: use the installed `tea` issue update/edit command shown by `tea issues --help`; if unavailable, use the Gitea Web UI or REST API.
- Close an issue: use the installed `tea` issue close/update command shown by `tea issues --help`; if unavailable, use the Gitea Web UI or REST API.
## When a skill says "publish to the issue tracker"
Create a Gitea issue in `GenarrativeAI/Genarrative` with the requested title, body, labels, and links back to any relevant docs or branch.
## When a skill says "fetch the relevant ticket"
Read the Gitea issue body and comments/notes for the referenced issue number. Include labels and current open/closed state in the working context.
## Authentication
Use the locally configured Gitea credentials for the current developer. Do not commit tokens, cookies, `.env`, or local credential files.

View File

@@ -1,15 +0,0 @@
# Triage Labels
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's Gitea issue tracker.
| Label in mattpocock/skills | Label in our tracker | Meaning |
| -------------------------- | -------------------- | ---------------------------------------- |
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
| `needs-info` | `needs-info` | Waiting on reporter for more information |
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
| `ready-for-human` | `ready-for-human` | Requires human implementation |
| `wontfix` | `wontfix` | Will not be actioned |
When a skill mentions a role, use the corresponding Gitea label string from this table.
If the Gitea repository later adopts Chinese labels or a different naming scheme, edit the right-hand column here rather than letting skills create duplicate labels.

View File

@@ -1,417 +0,0 @@
# Agent 聊天到草稿生成到进入游戏世界链路审计
更新时间:`2026-04-20`
## 0. 审计目标
本次审计只看一条链:
`Agent 聊天 -> 世界草稿生成 -> 结果页/作品库 -> 进入游戏世界`
聚焦回答四类问题:
1. 哪些数据在链路中断掉了
2. 哪些地方在代码里同时存在多条 pipeline
3. 哪些字段、功能、组件已经变成冗余或主链弱消费
4. 哪些能力在 contract、PRD 或代码结构里已经定义,但并没有真正实装到当前游戏主流程
---
## 1. 结论先行
当前系统还没有形成“Agent 会话是唯一真相源、发布后再进入世界”的单一主链,而是处在多条 pipeline 并存、多个桥接层临时粘合的状态。
最关键的结论有 8 条:
1. 当前至少并存 `5` 条相关 pipeline其中真正影响可玩流程的主链至少有 `3` 条。
2. 最大的数据断点是:`CustomWorldAgentSessionSnapshot.draftProfile` 不直接进入 runtime前端 `buildCustomWorldProfileFromAgentDraft()` 会先把它本地编译成 legacy `CustomWorldProfile`,后面的结果页、自动保存、进入世界都只认这个 legacy profile。
3. 服务端内部也存在一次“先编成 legacy runtime profile再转回 foundation draft”的双重编译`draftProfile.legacyResultProfile` 是这个桥接层留下来的强耦合字段。
4. `packages/shared/src/contracts/customWorldAgent.ts``server-node/src/routes/customWorldAgent.ts``server-node/src/services/customWorldAgentOrchestrator.ts` 三层定义不一致,`publish_world / generate_scene_assets / sync_scene_assets / expand_long_tail / lock_cards / unlock_cards / regenerate_scope` 等关键动作没有形成真实可用链路。
5. `CustomWorldResultView.tsx` 仍保留“直接对 legacy profile 生成角色/地点、直接编辑 profile”的旧流程会绕过 Agent session是当前最明显的并行 pipeline 和冗余功能源。
6. “进入世界”和“发布世界”目前是两套平行逻辑。Agent 草稿结果页可以自动保存并直接进入世界,但 `publish_world` action 仍不可用,`qualityFindings / blocker` 校验也没有真正接入。
7. `listCustomWorldWorks()``CustomWorldWorkSummaryService` 已能聚合 Agent 草稿和已发布 profile但平台 `create` tab 仍主要展示 `myEntries`Agent draft session 不能自然回到主入口,恢复创作主要依赖 `activeSessionId`
8. Agent 工作区主 UI 只接了头部、进度、线程、输入框、操作横幅等极简子集PRD 里规划的锁定条、草稿抽屉、详情面板、澄清面板、快捷动作、发布校验结果等大部分还没有真正进入当前游戏主流程。
---
## 2. 目标链路
`docs/prd/AI_NATIVE_AGENT_FIRST_CUSTOM_WORLD_CREATOR_PRD_2026-04-12.md``docs/prd/AI_NATIVE_AGENT_FIRST_EIGHT_ANCHOR_CO_CREATION_FLOW_PRD_2026-04-16.md`,目标链路应当是:
```text
Agent 对话
-> Express 后端维护结构化 eight-anchor / creatorIntent / lockState / draftSnapshot
-> foundation draft
-> 角色资产工坊 / 场景资产工坊
-> sync 回 Agent session draft
-> expand long tail
-> publish_world
-> 服务端执行 quality / blocker 校验
-> 服务端编译最终 CustomWorldProfile
-> 持久化到世界库
-> 进入世界
```
这条目标链路有 4 个硬约束:
1. Express 后端才是真实状态源,前端只负责展示和输入,不负责结构化草稿编译。
2. 未发布的 Agent 草稿不应该直接污染正式世界库,主入口里应该通过“继续创作”恢复。
3. 进入世界前应先经过 `publish_world`,并由发布校验阻止缺角色资产、缺场景资产、缺主线第一幕等 blocker。
4. 结果页不再是旧自定义世界编辑器的平移副本,而应更接近“最终预览 / 发布确认 / 进入世界”的收口层。
---
## 3. 当前真实链路
## 3.1 Agent 会话草稿链
当前新链路实际是:
```text
PreGameSelectionFlow.tsx
-> /api/runtime/custom-world/agent/sessions
-> CustomWorldAgentSessionStore
-> CustomWorldAgentOrchestrator
-> CustomWorldAgentFoundationDraftService
-> CustomWorldAgentAutoAssetService
-> session.draftProfile / draftCards / assetCoverage
-> 前端 buildCustomWorldProfileFromAgentDraft()
-> generatedCustomWorldProfile
-> upsertCustomWorldProfile()
-> handleCustomWorldSelect(profile)
-> runtime
```
关键特点:
1. Agent session 不是 runtime 直接消费的对象。
2. Agent 草稿完成后,会在前端先转成 `CustomWorldProfile`
3. 结果页阶段会自动调用 `upsertCustomWorldProfile()`,把当前 profile 写进 `custom-world-library`
4. “进入世界”按钮直接把这个 profile 送给 `handleCustomWorldSelect(...)`,不需要 `publish_world`
主要证据:
- `src/components/game-shell/PreGameSelectionFlow.tsx`
- `src/services/customWorldAgentDraftResult.ts`
- `src/hooks/useGameFlow.ts`
## 3.2 旧自定义世界 session 链
旧链路仍然完整存在:
```text
aiService.generateCustomWorldProfile()
-> /api/runtime/custom-world/sessions
-> answerCustomWorldSessionQuestion()
-> /generate/stream
-> generateCustomWorldProfile()
-> CustomWorldProfile
-> 结果页 / 作品库 / 进入世界
```
关键特点:
1. `src/services/aiService.ts` 里的 `generateCustomWorldProfile()` 仍然会创建旧 `custom-world/sessions`
2. 前端会先根据 `world_hook / player_premise / opening_situation / core_conflict` 自动补默认回答,再触发流式生成。
3. 这条链已经与 Agent 八锚点链并行存在,且依然可用。
主要证据:
- `src/services/aiService.ts`
- `server-node/src/routes/runtimeRoutes.ts`
- `server-node/src/services/customWorldSessionStore.ts`
## 3.3 已保存 profile / 作品库链
当前作品库链是:
```text
custom-world-library
-> upsert / delete / publish / unpublish
-> PlatformHomeView / saved profile detail
-> CustomWorldResultView
-> handleCustomWorldSelect(profile)
```
关键特点:
1. 这条链直接消费 `CustomWorldProfile`,不依赖 Agent session。
2. Agent 结果页自动保存后,也会落入这条链。
3. `publish/unpublish` 作用在作品库 profile 上,而不是 Agent session 上。
主要证据:
- `server-node/src/routes/runtimeRoutes.ts`
- `src/components/game-shell/PlatformHomeView.tsx`
- `src/components/game-shell/PreGameSelectionFlow.tsx`
## 3.4 结果页 legacy profile 直改链
`CustomWorldResultView.tsx` 仍保留旧能力:
1. `generateCustomWorldPlayableNpc({ profile })`
2. `generateCustomWorldStoryNpc({ profile })`
3. `generateCustomWorldLandmark({ profile })`
4. `CustomWorldEntityEditorModal`
这意味着结果页不仅是预览层还是一套独立的“legacy profile 直改工作台”。这一套能力不会回写 Agent session 的结构化状态,也不会走 Agent action route。
主要证据:
- `src/components/CustomWorldResultView.tsx`
- `src/services/aiService.ts`
## 3.5 创作中心 works 聚合链
后端已经能聚合两类作品:
1. `sourceType: 'agent_session'`
2. `sourceType: 'published_profile'`
但主平台 `create` tab 现在仍主要展示 `myEntries`,没有把 `CustomWorldCreationHub.tsx` 作为主入口接上。
这导致:
1. works 聚合链存在
2. create tab 真实消费的是另一条链
3. Agent draft session 的继续创作入口没有真正收口到主平台
主要证据:
- `server-node/src/services/customWorldWorkSummaryService.ts`
- `src/components/custom-world-home/CustomWorldCreationHub.tsx`
- `src/components/game-shell/PlatformHomeView.tsx`
---
## 4. 数据断点
| 断点 | 当前现状 | 影响 | 主要证据 |
| --- | --- | --- | --- |
| Agent session -> runtime | `buildCustomWorldProfileFromAgentDraft()` 在前端把 `session.draftProfile` 编译成 legacy `CustomWorldProfile`,后续结果页、自动保存、进入世界都只认 profile | 后端不再是最终唯一真相源,前端承担了结构化编译与字段裁决,容易产生字段丢失、语义漂移、状态失真 | `src/components/game-shell/PreGameSelectionFlow.tsx``src/services/customWorldAgentDraftResult.ts` |
| foundation draft 内部双重编译 | `CustomWorldAgentFoundationDraftService` 会先 `buildCompiledCustomWorldProfile(...)`,再 `convertRuntimeProfileToFoundationDraft(...)`,并把结果塞进 `legacyResultProfile` | Agent draft 不是原生生成,而是绕了一次 legacy profile再回 draft后续桥接层依赖这个字段继续工作 | `server-node/src/services/customWorldAgentFoundationDraftService.ts` |
| 创作态元数据进入最终 profile | 前端桥接时会把 `anchorContent / creatorIntent / anchorPack / lockState` 一并塞进 legacy profile同时固定写入 `generationMode: 'fast'``generationStatus: 'key_only'` | 创作态数据污染运行时 profile 存储;`generationMode / generationStatus` 还会覆盖真实阶段语义 | `src/services/customWorldAgentDraftResult.ts` |
| Agent session 元数据在结果页后被截断 | `draftCards / pendingClarifications / suggestedActions / qualityFindings / checkpoints / operations` 大多停留在 session 层;结果页与 runtime 只继续消费 profile | 进入结果页后Agent 会话层的大量结构化上下文被切断,发布门槛、锁定、局部重生成等信息无法自然继承 | `packages/shared/src/contracts/customWorldAgent.ts``server-node/src/services/customWorldAgentSessionStore.ts``src/components/custom-world-agent/CustomWorldAgentWorkspace.tsx` |
| works 聚合 -> 平台 create tab | 后端 `listCustomWorldWorkSummaries(...)` 能返回 draft 与 published但 create tab 仍只渲染 `myEntries` | Agent draft session 无法稳定出现在主入口“我的创作”里,恢复创作入口割裂 | `server-node/src/services/customWorldWorkSummaryService.ts``src/components/game-shell/PlatformHomeView.tsx` |
| 发布状态 -> 可玩状态 | 结果页会自动 `upsertCustomWorldProfile()` 并允许直接 `onEnterWorld`;但 `publish_world` action 仍不可用 | “可玩”与“已发布”没有统一门槛,发布校验无法阻止未完成草稿进入世界 | `src/components/game-shell/PreGameSelectionFlow.tsx``server-node/src/services/customWorldAgentOrchestrator.ts` |
---
## 5. 多条 Pipeline
## 5.1 主链级 pipeline
| pipeline | 真相源 | 当前是否在主流程可达 | 问题 |
| --- | --- | --- | --- |
| Agent 会话草稿链 | `CustomWorldAgentSessionStore` + `draftProfile` | 是 | 后半段通过前端桥接成 legacy profile未形成端到端单一真相源 |
| 旧 custom-world session 链 | `CustomWorldSessionStore` | 是 | 与 Agent 八锚点链重复,且前端仍在补默认回答 |
| 已保存 / 已发布 profile 链 | `custom-world-library` 中的 `CustomWorldProfile` | 是 | 与 Agent draft session 发布链平行存在 |
| 结果页 legacy profile 直改链 | 结果页本地 `profile` | 是 | 绕过 Agent session属于并行编辑器 |
| works 创作中心链 | `listCustomWorldWorks()` 聚合数据 | 否,主平台未接主入口 | 后端已有聚合,但 UI 没真正切过去 |
## 5.2 资产子链 pipeline
资产相关还存在“自动补齐”和“人工工坊写回”并存:
1. `draft_foundation` 后,`CustomWorldAgentAutoAssetService` 会自动补角色主图和幕背景图。
2. 角色资产又存在 `generate_role_assets -> sync_role_assets` 的手动工坊写回链。
3. 场景资产在 contract 层定义了 `generate_scene_assets / sync_scene_assets`,但主 action 链未打通。
这导致当前资产链不是一条统一 pipeline而是
```text
自动补角色 / 自动补幕背景
并存
手动角色工坊 -> sync_role_assets
缺失
手动场景工坊 -> sync_scene_assets
```
主要证据:
- `server-node/src/services/customWorldAgentAutoAssetService.ts`
- `server-node/src/services/customWorldAgentRoleAssetStateService.ts`
- `packages/shared/src/contracts/customWorldAgent.ts`
- `server-node/src/services/customWorldAgentOrchestrator.ts`
---
## 6. 冗余字段与主链悬空字段
这里区分两类:
1. 已经明显承担桥接残留职责的冗余字段
2. 在 contract / session 里存在,但当前主流程几乎不消费的悬空字段
| 字段 | 类型 | 当前状态 | 判断 |
| --- | --- | --- | --- |
| `draftProfile.legacyResultProfile` | 桥接残留字段 | foundation draft 服务端先生成 legacy runtime profile再把它塞回 draft前端桥接又优先读它 | 明显冗余,属于临时兼容字段,不应长期成为主链依赖 |
| `generationMode: 'fast'` | 固定写死字段 | `buildCustomWorldProfileFromAgentDraft()` 固定写入 | 不是草稿真实状态,更像桥接层补丁 |
| `generationStatus: 'key_only'` | 固定写死字段 | `buildCustomWorldProfileFromAgentDraft()` 固定写入 | 同上,会掩盖真实生成阶段 |
| `anchorContent / creatorIntent / anchorPack / lockState` 被直接塞进 legacy profile | 创作态元数据 | 会跟随自动保存一起写进作品库 profile但 runtime 并不以这些字段为正式运行时输入 | 当前更像创作态元数据泄漏进运行时 profile |
| `qualityFindings` | session / contract 字段 | contract、session store、测试里存在但没形成生成、渲染、发布阻断闭环 | 当前主链悬空 |
| `checkpoints` | session 字段 | session store 会记录,但主工作区和结果页没有真实展示入口 | 当前主链悬空 |
| `suggestedActions` | session 字段 | session 会生成,但当前正式工作区没有对应消费面;旧 `QuickActions` 面板已在 `2026-04-21` 判定退出当前版本主链并物理删除 | 当前主链悬空 |
| `pendingClarifications` | session 字段 | session 有数据,但当前正式工作区没有对应消费面;旧澄清面板已在 `2026-04-21` 判定退出当前版本主链并物理删除 | 当前主链悬空 |
| `operations` 历史 | session 字段 | 主工作区只展示当前 `activeOperation` 横幅,不展示完整历史 | 当前主链弱消费 |
| `roleAssetSummaryLabel / cover* / counts` 等 works 字段 | works 聚合字段 | 后端能返回,但主平台 create tab 没走 `works` 入口 | 当前主链弱消费 |
---
## 7. 冗余功能与冗余组件
## 7.1 冗余功能
| 功能 | 当前状态 | 问题 |
| --- | --- | --- |
| 结果页直接生成 playable/story/landmark | `CustomWorldResultView.tsx` 仍可直接调用 AI 生成 | 与 Agent 对象精修链重复,且不会同步回 session |
| 结果页直接编辑 `CustomWorldProfile` | `CustomWorldEntityEditorModal` 仍挂在结果页 | 把结果页继续维持成旧编辑器,而不是 Agent 流程的收口层 |
| 旧 `custom-world/sessions` 世界生成 | `2026-04-20` 审计时仍完整可用;`2026-04-21` 已完成物理删除 | 与 Agent 八锚点世界创建重复;当前遗留问题已转为文档口径清理 |
| 作品库 `publish/unpublish` 与 Agent `publish_world` | 两套“发布”概念并行 | 一套作用于 library profile一套想作用于 Agent session但后者还未打通 |
| 结果页自动保存 | `generatedCustomWorldProfile` 变化时自动 `upsertCustomWorldProfile()` | 让“草稿保存”“作品库存档”“正式发布”语义混在一起 |
## 7.2 冗余或已退出当前版本主链的组件
`src/components/custom-world-agent/CustomWorldAgentWorkspace.tsx` 当前只真正接了:
1. `CustomWorldAgentHeader`
2. `EightAnchorProgressBar`
3. `CustomWorldAgentOperationBanner`
4. `CustomWorldAgentThread`
5. `CustomWorldAgentComposer`
`2026-04-21` 清理前,同目录下还存在一组未接线旧组件:
1. `CustomWorldAgentLockBar.tsx`
2. `CustomWorldAgentDraftDrawer.tsx`
3. `CustomWorldAgentDraftDetailPanel.tsx`
4. `CustomWorldAgentQuickActions.tsx`
5. `CustomWorldAgentSummaryPanel.tsx`
6. `CustomWorldAgentIntentSummaryPanel.tsx`
7. `CustomWorldAgentClarificationPanel.tsx`
8. `CustomWorldGenerateEntityModal.tsx`
其中:
1. `CustomWorldAgentDraftDrawer.tsx` 已在批次 A 清理中删除
2. `CustomWorldAgentLockBar.tsx`
3. `CustomWorldAgentDraftDetailPanel.tsx`
4. `CustomWorldAgentQuickActions.tsx`
5. `CustomWorldAgentSummaryPanel.tsx`
6. `CustomWorldAgentIntentSummaryPanel.tsx`
7. `CustomWorldAgentClarificationPanel.tsx`
8. `CustomWorldGenerateEntityModal.tsx`
已在批次 D 清理中判定为退出当前版本主链,并完成物理删除。
因此这里的审计结论需要更新为:
1. 它们不再属于“待接线组件”
2. 它们属于已确认退场的旧副面板链
3. 当前版本如果还要补 `suggestedActions / pendingClarifications / draftCards` 的消费面,应基于新的主链设计重新定义,而不是默认把旧面板接回来
另外,`src/components/custom-world-home/CustomWorldCreationHub.tsx` 也已存在,但平台 `create` tab 还没有把它接成主入口。
---
## 8. 当前没有真正实装到游戏主流程中的项
| 能力 | 设计 / 定义位置 | 当前状态 |
| --- | --- | --- |
| `publish_world` 真正发布链 | contract、PRD、route、orchestrator | route 能接orchestrator 直接 `throw badRequest('publish_world is not available in phase5')` |
| `generate_scene_assets` | contract、PRD | contract 定义了,但 action route 未接,主链无执行实现 |
| `sync_scene_assets` | contract、PRD | contract 定义了,但 action route 未接,主链无执行实现 |
| `expand_long_tail` | contract、PRD | contract 定义了,但主 action 链未接 |
| `lock_cards / unlock_cards` | contract、PRD | contract 定义了,但 route / UI / orchestrator 主链未接 |
| `regenerate_scope` | contract、PRD | contract 定义了,但 route / UI / orchestrator 主链未接 |
| `qualityFindings` 与 blocker 发布门禁 | contract、PRD、技术进度文档 | 字段存在,但没有真实的生成、展示、阻止发布闭环 |
| 场景资产工坊从 Agent workspace 打开并写回 | PRD | 主工作区未接详情面板与场景资产 action |
| 通过 works 统一恢复 Agent draft / 已发布作品 | works service + creation hub | 后端已有聚合,主平台入口未收口 |
| 发布前只允许预览、发布后再进入世界 | PRD | 当前 Agent 草稿结果页可自动保存并直接进入世界 |
补充说明:
`docs/technical/SCENE_MULTI_ACT_CREATOR_IMPLEMENTATION_PROGRESS_2026-04-20.md` 已明确写到,发布期 `qualityFindings / blocker` 正式接入仍未完成,这与当前代码状态一致。
---
## 9. 优先级建议
## P0先收一条真正的单一主链
建议明确把下面这条定为唯一正式主链:
```text
Agent session
-> 服务端 draft snapshot
-> 服务端质量检查 / 发布动作
-> 服务端编译 final CustomWorldProfile
-> 世界库
-> runtime
```
对应动作:
1. 结果页不再承担“主编辑器”职责,至少对 Agent draft 结果页关闭 legacy profile 直改能力。
2. 用服务端 preview / compile 接口替代前端 `buildCustomWorldProfileFromAgentDraft()` 的最终裁决职责。
3. `publish_world` 打通后,再决定是否允许“发布后立即进入世界”。
## P0把“进入世界”和“发布世界”重新绑回同一门槛
建议收口为:
1. 未发布 Agent 草稿只能继续创作或查看预览。
2. 只有 `publish_world` 成功后,才产出正式 `CustomWorldProfile` 并允许主入口进入世界。
3. `qualityFindings / blocker` 必须在 foundation draft 完成、资产写回后、publish 前持续重跑。
## P1继续做旧 world session 链的文档收口
`2026-04-21` 更新:
`custom-world/sessions` 链已经完成物理删除。
因此这里不再是“保留还是淘汰”的开放问题,而是:
1. 继续清理由这条旧链残留在审计、PRD、知识图谱中的过时口径
2. 把当前正式主链与仍保留的兼容层边界写清楚
## P1把 works 创作中心接回主平台
建议:
1. 平台 `create` tab 改成消费 `listCustomWorldWorks()`
2. 草稿 session 通过“继续创作”恢复。
3. 已发布 profile 通过“进入世界”或“查看详情”进入。
4. `myEntries` 退回为作品库子集,而不是 create tab 的唯一数据源。
## P1为悬空 session 字段重新定义最小闭环
`2026-04-21` 更新:
原文这里建议把旧 `QuickActions / DraftDrawer / DraftDetailPanel / ClarificationPanel` 接回主工作区。
但这些旧副面板已经在当前版本收口判断中被明确认定为:
1. 不属于现行主链
2. 不再作为当前版本默认待落地项
3. 已完成物理删除
因此当前更准确的建议应该是:
1. 如果 `suggestedActions / pendingClarifications / draftCards` 仍要进入正式主流程,需要先重新定义符合当前极简工作区的消费方式
2. 不应再以“把旧副面板接回来”作为默认方案
3. 在没有新主链设计前,这些字段继续标记为“主链悬空”
## P2等主链收口后再清桥接字段
下面这些字段不建议现在立刻删,但应在主链收口后尽快移除:
1. `draftProfile.legacyResultProfile`
2. 前端桥接里固定写死的 `generationMode / generationStatus`
3. 仅为兼容旧编辑器而塞进 legacy profile 的创作态元数据
---
## 10. 一句话总评
当前“Agent 聊天 -> 草稿生成 -> 进入世界”已经能跑通一条可玩链,但它还不是 PRD 要求的“后端单一真相源 + 发布门禁收口”的正式链路,而是 `Agent session``legacy profile``旧 session``作品库` 四层并存、靠前端桥接和结果页兼容能力临时拼起来的过渡态。

View File

@@ -1,332 +0,0 @@
# 角色资产 Prompt 链路审计2026-04-20
更新时间:`2026-04-20`
## 0. 本次审计回答什么问题
本次只回答角色资产相关的 4 个问题:
1. `characterAssetPrompts.ts` 里的 `visualPromptText``animationPromptText`,是不是“生成角色形象 / 动作形象的默认描述”。
2. 生成角色形象的系统提示词在哪个文件,生成默认角色形象描述文本的提示词在哪个文件。
3. 生成角色动作的系统提示词在哪个文件,生成默认角色动作描述文本的提示词在哪个文件。
4. 当前链路里是否存在冗余流程、保留接口或无效代码。
---
## 1. 先说结论
结论不是“只有一套 prompt”而是
**当前角色资产链路仍然有两层 prompt但“默认描述文本”已经统一成单一主源。**
### 1.1 默认描述文本层
这层的目标是:
**先给资产工坊里的输入框一个默认可编辑文本。**
这层不直接拿去生成图片或动作视频。
当前实际主链来源:
- `src/prompts/customWorldRolePromptDefaults.ts`
它会把角色已有字段映射成:
- `visualPromptText`
- `animationPromptText`
- `scenePromptText`
其中:
- `visualPromptText` 优先取 `visualDescription`
- `animationPromptText` 优先取 `actionDescription`
- `scenePromptText` 优先取 `sceneVisualDescription`
这层是**默认描述文本**,不是正式图像模型 prompt。
### 1.2 正式模型 prompt 层
这层的目标是:
**把“默认描述文本”进一步编译成正式给图像模型 / 动作模型的完整 prompt。**
当前主链来源:
- `server-node/src/prompts/characterAssetPrompts.ts`
- `packages/shared/src/prompts/qwenSprite.ts`
也就是说:
1. 前端先有一段短文本
2. 后端再用正式 prompt builder 把它扩成模型真正使用的完整 prompt
---
## 2. 角色形象生成链路
## 2.1 生成角色形象的系统提示词在哪
如果这里问的是“正式生成角色主图时,真正控制模型输出方向的 prompt 主源在哪”,答案是:
- `server-node/src/prompts/characterAssetPrompts.ts`
- `packages/shared/src/prompts/qwenSprite.ts`
更准确说:
1. `buildNpcVisualPrompt`
- 文件:`server-node/src/prompts/characterAssetPrompts.ts`
- 作用:把短描述文本和角色摘要合并
2. `buildMasterPrompt`
- 文件:`packages/shared/src/prompts/qwenSprite.ts`
- 作用:提供正式的角色主图 prompt 骨架
最终角色形象正式生成请求使用的是:
- `buildNpcVisualPrompt(...)`
调用位置:
- `server-node/src/modules/assets/characterAssetRoutes.ts`
即:
**角色主图正式生成的系统提示词主链,不在前端默认值文件,而在后端 `characterAssetPrompts.ts` + 共享 `qwenSprite.ts`。**
## 2.2 生成默认角色形象描述文本的提示词在哪
当前资产工坊默认输入框实际使用:
- `src/prompts/customWorldRolePromptDefaults.ts`
这不是 LLM system prompt而是本地字段映射规则。
换句话说,当前页面上的默认“形象描述”主要来自:
- `role.visualDescription`
- 或回退到 `role.description`
---
## 3. 角色动作生成链路
## 3.1 生成角色动作的系统提示词在哪
当前正式动作生成主链在:
- `server-node/src/prompts/characterAssetPrompts.ts`
- `packages/shared/src/prompts/qwenSprite.ts`
其中分两类:
1. `buildArkCharacterAnimationPrompt`
- 当前图生视频动作链路主入口
2. `buildNpcAnimationPrompt`
- 通用动作视频 prompt builder
3. `buildImageSequencePrompt`
- 连续帧方案动作 prompt builder
4. `buildVideoActionPrompt`
- 共享动作模板骨架,在 `packages/shared/src/prompts/qwenSprite.ts`
当前主动作链路更偏向:
- `buildArkCharacterAnimationPrompt`
调用位置:
- `server-node/src/modules/assets/characterAssetRoutes.ts`
## 3.2 生成默认角色动作描述文本的提示词在哪
当前资产工坊真实默认“动作描述”来源:
- `src/prompts/customWorldRolePromptDefaults.ts`
规则是:
- 优先 `actionDescription`
- 回退 `combatStyle`
这仍然是**默认描述文本层**,不是最终动作模型 prompt。
---
## 4. `characterAssetPrompts.ts` 里的 `visualPromptText` / `animationPromptText` 到底是什么
这两个字段容易混淆,因为它们名字里带 `Prompt`
但当前工程里它们更准确的定位是:
**“默认描述文本 bundle 字段名”,不是最终图像模型请求体里的最终 prompt 名称。**
也就是:
- `visualPromptText`
- 在 UI 里更像“角色形象描述默认文本”
- 之后会再被编译进正式图像 prompt
- `animationPromptText`
- 在 UI 里更像“角色动作描述默认文本”
- 之后会再被编译进正式动作 prompt
所以对你的问题可以直接回答为:
**是,它们在当前语义上确实可以看作“默认角色形象 / 动作描述文本”。**
但需要补一句:
**它们不是最终一步的正式模型系统提示词,而是正式模型 prompt 的上游输入。**
---
## 5. 当前真实调用链
## 5.1 当前资产工坊页面初始默认值主链
当前真实主链:
1. 角色对象已有字段进入前端
2. `src/prompts/customWorldRolePromptDefaults.ts`
3. `CustomWorldRoleAssetStudioModal.tsx`
4. 输入框初始值:
- `visualPromptText`
- `animationPromptText`
这条链:
-
- 本地可控
- 不依赖额外一次 LLM 调用
## 5.2 当前正式角色主图生成主链
1. 前端把输入框里的 `visualPromptText` 提交到后端
2. `server-node/src/prompts/characterAssetPrompts.ts`
- `buildNpcVisualPrompt`
3. `packages/shared/src/prompts/qwenSprite.ts`
- `buildMasterPrompt`
4. 图像模型正式生成
## 5.3 当前正式角色动作生成主链
1. 前端把输入框里的 `animationPromptText` 提交到后端
2. `server-node/src/prompts/characterAssetPrompts.ts`
- `buildArkCharacterAnimationPrompt`
-`buildNpcAnimationPrompt`
-`buildImageSequencePrompt`
3. `packages/shared/src/prompts/qwenSprite.ts`
- `buildVideoActionPrompt`
4. 动作模型正式生成
---
## 6. 冗余流程与当前问题
## 6.1 默认描述文本双链已收口
此前默认描述文本同时存在:
1. 前端本地字段映射
2. 后端 bundle 编译接口
本轮已经统一为:
- `src/prompts/customWorldRolePromptDefaults.ts`
也就是:
**默认描述文本现在只有一条真实主源。**
对应变化:
1. 不再保留后端独立的默认 bundle 编译接口。
2. 不再保留前端对应的 bundle 生成 API 壳层。
3. `server-node/src/prompts/characterAssetPrompts.ts` 只保留正式模型 prompt builder。
判断:
**默认描述文本层的双份真相已经被消除。**
## 6.2 `scenePromptText` 结构存在,但当前资产工坊没有完整承接
当前这套链路里:
- `customWorldRolePromptDefaults.ts` 会返回 `scenePromptText`
- `characterAssetPrompts.ts` 也会返回 `scenePromptText`
但当前资产工坊 UI 里并没有完整对应输入框链路。
这说明:
**场景描述文本在结构层存在,但在当前角色资产工坊里没有形成完整的用户可编辑闭环。**
## 6.3 共享模板与工具模板存在相似实现,但职责不同
仓库里同时有:
- `packages/shared/src/prompts/qwenSprite.ts`
- `src/prompts/qwenSpriteSheetToolPrompts.ts`
它们都提供类似的主图 / 动作模板能力。
但当前定位不同:
- `packages/shared/src/prompts/qwenSprite.ts`
- 正式角色资产主链共享模板
- `src/prompts/qwenSpriteSheetToolPrompts.ts`
- Qwen 工具链 prompt
它们不是同一条业务主链里的重复实现,但确实容易让人误读为“双份正式模板”。
判断:
**这是“职责上可解释,但认知上高混淆”的并行模板,不建议现在直接删,但需要文档明确边界。**
## 6.4 当前没有证据说明正式主图 / 动作 prompt builder 是无效代码
以下 builder 当前都有正式调用点:
- `buildNpcVisualPrompt`
- `buildNpcVisualNegativePrompt`
- `buildArkCharacterAnimationPrompt`
- `buildNpcAnimationPrompt`
- `buildImageSequencePrompt`
因此它们不能算“无效代码”。
真正已经被清理掉的保留链路,是此前未接入主 UI 的默认 bundle 接口:
- `CHARACTER_PROMPT_BUNDLE_SYSTEM_PROMPT`
- `buildCharacterPromptBundleUserPrompt`
- `/api/assets/character-prompts/generate`
这套链路已经不再保留在当前仓库主线中。
---
## 7. 本次建议
如果后续要继续收口,建议按顺序处理:
1. 继续以前端本地映射作为默认描述文本唯一主源。
2.`scenePromptText` 做完整承接,不要继续停留在结构存在但 UI 不消费的状态。
3. 继续保留 `packages/shared/src/prompts/qwenSprite.ts` 与工具链 prompt 分层,但在文档里强制写清“正式主链 / 工具链”边界。
---
## 8. 本次审计覆盖文件
- `server-node/src/prompts/characterAssetPrompts.ts`
- `packages/shared/src/prompts/qwenSprite.ts`
- `server-node/src/modules/assets/characterAssetRoutes.ts`
- `src/prompts/customWorldRolePromptDefaults.ts`
- `src/components/CustomWorldRoleAssetStudioModal.tsx`
- `src/components/asset-studio/characterAssetWorkflowPersistence.ts`
- `src/prompts/qwenSpriteSheetToolPrompts.ts`
---
## 9. 一句话版结论
一句话总结就是:
**当前角色资产系统把“默认描述文本”和“正式模型 prompt”拆成了两层这是合理的默认描述文本层已经统一为前端本地映射单一主源当前剩余主要问题不再是双主源而是 `scenePromptText` 仍未形成完整 UI 闭环。**

View File

@@ -1,444 +0,0 @@
# 自定义世界创作工具问题审计与优化建议
更新时间:`2026-04-08`
## 0. 结论先说
当前自定义世界创作工具已经有了比较强的生成骨架、锚点结构和结果编辑能力,但整体仍处在一个很明显的“半收口状态”:
**设计目标已经走到“陶泥儿主工作台”,数据结构已经支持“锚点化输入”,但实际体验仍然更像“大文本生成器 + 大型结果总表编辑器”。**
如果用一句话概括当前问题,就是:
**高杠杆创作入口还不够强,低杠杆编辑负担还偏重,局部重生成与后端收口也还没有真正闭环。**
---
## 1. 审计范围
本次审计主要对照了三类信息:
1. 目标设计与 PRD
- `docs/prd/AI_NATIVE_CUSTOM_WORLD_CREATION_FLOW_OPTIMIZATION_PRD_2026-04-06.md`
- `docs/design/CUSTOM_WORLD_CREATOR_INPUT_AND_AI_BOUNDARY_DESIGN_2026-04-06.md`
- `docs/design/CUSTOM_WORLD_SELF_OWNED_SETTING_LAYER_OPTIMIZATION_2026-04-08.md`
- `docs/design/CUSTOM_WORLD_TEMPLATE_DECOUPLING_AND_CROSS_GENRE_GENERALIZATION_DESIGN_2026-04-08.md`
2. 当前前端主流程与工作台
- `src/components/SelectionCustomizationModals.tsx`
- `src/components/game-shell/PreGameSelectionFlow.tsx`
- `src/components/CustomWorldGenerationView.tsx`
- `src/components/CustomWorldResultView.tsx`
- `src/components/CustomWorldEntityCatalog.tsx`
- `src/components/CustomWorldEntityEditorModal.tsx`
3. 当前生成链与后端会话层
- `src/services/ai.ts`
- `src/services/aiService.ts`
- `src/services/customWorldCreatorIntent.ts`
- `src/services/customWorld.ts`
- `server-node/src/services/customWorldSessionStore.ts`
- `server-node/src/services/customWorldGenerationService.ts`
- `server-node/src/bridges/legacyAiRuntimeBridge.ts`
---
## 2. 当前主要问题
## 2.1 输入层和意图结构已经脱节
当前数据结构已经支持:
- 世界一句话
- 主题关键词
- 气质约束
- 玩家身份
- 开局处境
- 核心冲突
- 关键势力
- 关键角色
- 关键地点
- 标志性要素
- 禁止事项
但实际入口 `src/components/SelectionCustomizationModals.tsx` 里,陶泥儿主弹窗仍然基本只有:
- 生成模式
- 一块大 textarea
这导致两个直接后果:
1. 设计里已经想清楚的“高杠杆锚点输入”,还没有真正变成主入口。
2. `CustomWorldCreatorIntent` 虽然已经能表达卡片化输入,但 UI 并没有把它转成真正可用的创作工作台。
目前 `card` 模式更多还停留在类型和测试层,没有成为真实用户路径。
可优化点:
- 把当前创建弹窗升级成“快速文本模式 / 创作卡片模式”双入口。
- 快速文本模式保留,但提交后应自动拆出建议锚点,而不是直接把整段文本原封不动送进生成链。
- 卡片模式先只做最关键的 `5~6` 张卡,不要一开始把所有高级字段都铺开。
- 允许空卡提交,但明确区分“已锁定锚点”和“允许 AI 自由补全”的内容。
---
## 2.2 澄清机制已经存在,但没有真正服务陶泥儿主
`server-node/src/services/customWorldSessionStore.ts` 已经支持:
- 根据输入缺口生成澄清问题
- 世界核心不足时追问
- 玩家身份缺失时追问
- 开局处境缺失时追问
- 核心冲突缺失时追问
`src/services/aiService.ts` 当前做法是:
- 创建 session
- 读取问题
- 直接用 fallback 文案自动回答
- 然后继续生成
这意味着:
**系统表面上已经有“先澄清再生成”的能力,但实际体验里,陶泥儿主并没有真正参与这一步。**
结果就是:
- 低信息量输入并没有被真正补强
- 澄清问题变成了内部兜底,而不是创作协作
- 很容易继续生成出“完整但不够像用户想要的世界”
可优化点:
- 把 session question 真正接到前端,作为生成前的二次确认步骤。
- 每次只问 `1~3` 个最关键问题,不要把它做成问卷。
- 支持“一键使用系统建议”,但必须让陶泥儿主可见,而不是静默自动填充。
- 把回答结果回写到 `creatorIntent`,而不是只作为一次性会话答案。
---
## 2.3 新建完成后的工作台闭环没有成立
设计文档里,自定义世界应该是:
`输入 -> 生成 -> 结果页确认/编辑 -> 保存并进入世界`
但当前 `src/components/game-shell/PreGameSelectionFlow.tsx` 里,新建世界生成成功后会:
- 直接保存到世界库
- 清空 `generatedCustomWorldProfile`
- 返回世界列表页
而不是进入 `custom-world-result` 结果工作台。
这会带来几个问题:
1. 新建后的第一时间确认感不够强。
2. 快速模式生成出的“关键对象预览”没有自然承接页。
3. 用户生成完后,如果想继续看结果、继续补全、继续编辑,还要再从世界列表里点一次“编辑”。
这条链路会让“创作中”与“已保存”之间的体验断开。
可优化点:
- 新建完成后默认进入结果工作台,而不是直接跳回世界列表。
- 保存动作留在结果页显式触发。
- 可以增加“自动保存草稿,但仍停留在结果页”的策略,兼顾安全感和连贯性。
- 世界列表更适合作为“已归档内容入口”,不适合作为新建完成页。
---
## 2.4 结果页仍然偏“数据总表”,低杠杆编辑负担偏重
当前结果页已经有 `世界 / 锚点 / 可扮演角色 / 场景角色 / 场景` 五个页签,这是正确方向。
但问题在于,进入编辑器后暴露出来的字段仍然太“底层”了,例如:
- `backstoryReveal`
- 技能列表
- 初始物品
- 场景 NPC 分配
- 场景连接关系
这些字段对少数深度编辑场景有用,但不应该成为默认主编辑内容。
当前 `src/components/CustomWorldEntityEditorModal.tsx` 已经变成一个非常重的综合编辑器,里面同时承担:
- 角色完整档案编辑
- 形象编辑入口
- AI 资产生成入口
- 背景章节编辑
- 技能与初始物品编辑
- 场景内 NPC 分配
- 场景连接维护
这会带来三层问题:
1. 陶泥儿主负担过重
- 很多字段属于“系统编译层”,不属于“创作决策层”。
2. 移动端负担过重
- 大量长表单、长弹窗和多级编辑,对手机创作并不友好。
3. 工程复杂度过高
- 前端工作台承担了太多不同层级的编辑职责。
可优化点:
- 默认只暴露高杠杆编辑:
- 世界核心命题
- 主题与气质
- 玩家身份与开局
- 关键势力
- 关键角色
- 关键地点
- 标志性要素
- 把技能、初始物品、章节 reveal、连接网络等移到“高级模式”或“系统层编辑”。
- 结果页结构从“按对象字段堆表单”改成“按创作价值组织”。
- 移动端优先改成分段式面板或底部工作台,不要把长表单都塞进同一个大 modal。
---
## 2.5 锁定与局部重生成机制还不完整
当前已经有两套看起来相关的能力:
1. `creatorIntent` 里的 `locked`
2. `lockState` 里的角色 / 地点 / 势力锁定字段
但实际重生成时,`src/components/game-shell/PreGameSelectionFlow.tsx` 里的合并逻辑只真正处理了:
- 已锁定角色
- 已锁定地点
而且还是按“名称匹配”保留,不是按稳定 id 或字段级锁定来处理。
这带来的问题很明显:
1. 角色或地点一旦重命名,锁定可能失效。
2. 势力、冲突、世界概述等高价值内容没有真正进入局部重生成保护范围。
3. 当前“锁定能力”更像一个早期过渡实现,还没有形成统一的重生成规则。
同时,结果页的“重新生成”提示文案仍然是“整世界覆盖式”的语义,这也会进一步削弱用户对重生成的信任感。
可优化点:
- 把锁定语义统一收口到后端,以 `lockState` 为唯一事实来源。
- 锁定粒度改成:
- 世界字段锁定
- 势力锁定
- 关键角色锁定
- 关键地点锁定
- 长尾内容可重生成
- 局部重生成至少拆成几类:
- 仅补长尾角色
- 仅补长尾场景
- 仅重做场景网络
- 仅重做支持性 NPC
- 合并逻辑不要再靠名称匹配,改成稳定 id 或锚点映射。
---
## 2.6 快速模式还不够“快”,生成页也还不够“陶泥儿主视角”
当前快速模式的主要区别,是把数量降成:
- 可扮演角色 `3`
- 场景角色 `8`
- 场景 `4`
但主生成链本身仍然会继续跑:
- framework
- theme pack
- story graph
- role narrative
- role dossier
- narrative profile
- finalization
也就是说:
**现在的 fast 更像“缩数量版 full”还不是“先出关键锚点与关键对象的创作预览模式”。**
同时,`src/components/CustomWorldGenerationView.tsx` 当前展示的重点仍然是:
- 当前批次
- 预计等待
- 计时
- 模型阶段
而不是陶泥儿主真正关心的:
- 关键角色有没有成型
- 核心冲突有没有稳定
- 哪些锚点已经锁定
- 当前正在补的是关键对象还是长尾内容
可优化点:
- 快速模式改成真正的“关键锚点预览模式”:
- 先只生成关键角色、关键地点、核心冲突摘要
- 暂不补全所有长尾档案
- 生成页改成“陶泥儿主视角进度”:
- 世界灵魂已确定
- 关键角色已成型
- 关键地点已落地
- 长尾扩展准备开始
- 把技术批次隐藏到二级信息里,默认只展示创作状态。
---
## 2.7 自定义世界底层仍然没有完全脱离模板世界依赖
这部分设计文档和参考清单已经说得比较清楚,代码里也能看到对应痕迹:
- `templateWorldType`
- `WUXIA / XIANXIA` 兼容字段
- 规则层 fallback
- 视觉参考池 fallback
- 角色骨架与怪物池 fallback
当前问题不在于“还没完全去模板化”本身,而在于:
**这层依赖仍然深入到了生成、运行时规则、表现词汇和参考资源,不只是一个兼容字段。**
这会限制:
1. 跨题材表达稳定性
2. 自定义世界的自有设定层独立性
3. 后续真正做到“任何题材都能稳定跑”
可优化点:
- 继续按现有设计稿,把模板依赖逐步迁成:
- 语义锚层
- 规则层
- 表现层
- 原型参考层
- 兼容迁移层
- 在迁移完成前,保留兼容字段,但让新逻辑优先读取 `ownedSettingLayers`
- 明确哪些是“兼容桥”,哪些还是“真实主依赖”,避免继续混用。
---
## 2.8 前后端边界仍处于过渡态,和项目约束还有距离
当前自定义世界已经有了:
- Node 路由
- session
- 流式生成
- 世界库存储接口
这是对的。
但问题是,`server-node/src/services/customWorldGenerationService.ts` 仍然通过 `server-node/src/bridges/legacyAiRuntimeBridge.ts` 去桥接 `src/services/ai.ts`
这说明:
**核心生成逻辑虽然已经被路由包起来了,但真正的生成实现还没有完全成为 Express 侧自己的领域服务。**
同时,前端仍然承担了不少流程语义:
- 锁定内容合并
- 重生成确认
- 结果页覆盖提示
- 工作台状态切换
这和当前仓库“前端只负责表现,逻辑与数据尽量收口到 Express 后端”的方向还有距离。
可优化点:
- 把自定义世界生成链正式下沉到 `server-node` 领域服务。
- 把锁定、局部重生成、澄清会话、结果归档规则都放到后端。
- 前端只负责:
- 输入展示
- 进度展示
- 结果工作台展示
- 明确的用户确认动作
---
## 2.9 移动端工作台仍有明显压迫感
项目文档已经明确要求移动端优先,但当前自定义世界工作台里仍有几个典型问题:
- 大量长 modal
- 多段长表单
- `window.confirm / window.alert` 原生弹框较多
- 结果页与编辑器中同时承载太多操作密度
这些在桌面端还能勉强接受,但在手机上会很容易变成:
- 滚动层级混乱
- 退出成本高
- 修改焦点不明确
- 确认感不稳定
可优化点:
- 移动端改成底部工作台 / 分步面板 / 可折叠分区。
- 用项目内统一确认弹层替换 `window.confirm / window.alert`
- 让“保存”“继续补全”“局部重生成”这些关键动作固定在底部安全区附近。
- 把搜索、批量删除、创建动作做成更轻的操作条,而不是持续挤压正文区。
---
## 3. 优先级建议
### P0先修主链路闭环
- 补卡片化输入入口,至少把关键锚点输入真正开放出来。
- 把澄清问题正式接入陶泥儿主流程,不再静默自动兜底。
- 修正“新建完成后直接回世界列表”的流程,生成后默认进入结果工作台。
- 统一锁定与局部重生成规则,先让“陶泥儿主不怕重生成”成立。
### P1再降低工作台负担
- 结果页默认只展示高杠杆编辑。
- 低杠杆字段进入高级模式。
- 快速模式改成真正的关键对象预览模式。
- 生成页改成陶泥儿主视角进度,而不是模型批次视角。
### P2最后做架构收口与去模板化
- 把生成链、锁定规则、会话澄清彻底收回 Express 后端。
- 持续推进 `ownedSettingLayers` 成为真实主设定层。
- 逐步去掉自定义世界对模板世界的深依赖。
- 针对移动端重做工作台的操作密度和确认路径。
---
## 4. 推荐落地顺序
如果只按“最小投入、最大体验收益”排序,建议按下面四步做:
1. 先改输入与结果闭环
- 卡片化最小入口
- 澄清问题接入
- 新建后进入结果页
2. 再改锁定与局部重生成
- 用稳定 id
- 用统一 lockState
- 增加局部重生成类型
3. 再改结果工作台结构
- 默认高杠杆
- 高级模式收纳低杠杆字段
- 移动端拆分长表单
4. 最后做后端收口与去模板化
- 服务端领域化
- 设定层自有化
- 跨题材泛化
---
## 5. 一句话判断
当前自定义世界创作工具最需要的,不是再继续补更多字段或更多生成步骤,而是:
**把“陶泥儿主先决定灵魂锚点,系统再稳定展开世界”这条主逻辑真正落到 UI、流程和后端边界上。**

View File

@@ -1,621 +0,0 @@
# 世界 Profile 到预设内容与实时生成规则映射审计
更新时间:`2026-04-18`
## 0. 审计目标
本次审计只回答一个问题:
**当前仓库里的世界 profile 设定,是否已经完整、合理地映射到游戏的预设内容与实时生成内容规则中。**
这里的“世界 profile”包含两层
1. `CustomWorldProfile` 顶层世界数据
2. `ownedSettingLayers` 派生设定层
这里的“预设内容”包含:
1. 角色运行时预设
2. 场景预设
3. 默认视觉与怪物匹配
4. 初始装备 / 初始背包 / 经济与术语表现
这里的“实时生成规则”包含:
1. 主剧情 prompt
2. NPC 对话 / 招募 / 私聊 prompt
3. 任务生成
4. 运行时物品生成
5. 故事线程、可见性、叙事 QA 与推进规则
---
## 1. 结论先行
结论不是“完全映射”,而是:
**已完成基础映射,但没有达到“完全且合理”的程度。**
当前状态更准确地说是:
1. `世界基础骨架 -> 角色 / 场景 / 属性 / prompt` 这条主链已经打通。
2. `叙事层 -> 主剧情/NPC 可见性规则` 已经有比较扎实的接入。
3. `规则层 -> UI术语 / 经济 / 属性` 已经接入。
4.`模板兼容层` 仍然过强,跨题材世界会被粗暴压回 `WUXIA/XIANXIA`
5.`后端运行时任务/物品模块` 只拿到了瘦身版 profile没有真正吃到完整世界叙事层。
6.`世界级 items / faction / conflict` 仍然更多是文本种子,而不是可操作的游戏内容对象。
如果按结果判断:
1. **预设内容映射:部分完整,约 70%。**
2. **实时生成规则映射:前端剧情主链较完整,后端运行时子链不完整,整体约 60%。**
3. **跨题材合理性:明显不足。**
---
## 2. 本次审计覆盖的核心文件
类型与编译链:
- `src/types/customWorld.ts`
- `src/services/customWorld.ts`
- `src/services/customWorldBuilder.ts`
- `src/services/customWorldOwnedSettingLayers.ts`
- `src/services/customWorldTheme.ts`
预设内容落地:
- `src/data/characterPresets.ts`
- `src/data/scenePresets.ts`
- `src/data/customWorldCharacterLoadout.ts`
- `src/data/customWorldRuntime.ts`
- `src/data/customWorldVisuals.ts`
- `src/data/customWorldNpcMonsters.ts`
- `src/data/worldAttributeSchemas.ts`
- `src/data/economy.ts`
- `src/services/customWorldPresentation.ts`
实时生成规则:
- `src/hooks/story/storyContextBuilder.ts`
- `src/services/prompt.ts`
- `src/services/characterChatPrompt.ts`
- `src/services/questPrompt.ts`
- `src/services/questDirector.ts`
- `src/services/runtimeItemAiPrompt.ts`
- `src/data/runtimeItemNarrative.ts`
- `src/services/storyEngine/themePack.ts`
- `src/services/storyEngine/worldStoryGraph.ts`
- `src/services/storyEngine/actorNarrativeProfile.ts`
- `src/services/storyEngine/knowledgeGraph.ts`
- `src/services/storyEngine/threadContract.ts`
- `src/services/storyEngine/visibilityEngine.ts`
- `src/services/storyEngine/authorialConstraintPack.ts`
- `src/hooks/story/progressionActions.ts`
后端运行时链:
- `server-node/src/modules/ai/customWorldOrchestrator.ts`
- `server-node/src/modules/runtime-item/runtimeItemModule.ts`
- `server-node/src/modules/quest/runtimeQuestModule.ts`
- `server-node/src/modules/runtime/runtimeSnapshotHydration.ts`
---
## 3. 映射总表
| 设定层/字段 | 映射到预设内容 | 映射到实时生成规则 | 判断 |
| --- | --- | --- | --- |
| `name/subtitle/summary/tone/playerGoal` | 已映射到角色 opening、场景提示、视觉匹配、程序化物品关键词 | 已映射到主剧情 prompt、任务 prompt、物品 prompt、ThemePack/StoryGraph 派生 | 基本成立 |
| `templateWorldType/compatibilityTemplateWorldType` | 强影响角色模板、场景图参考池、怪物池、兼容 schema | 影响 ThemePack fallback 与部分运行时回退 | 已接入,但合理性不足 |
| `majorFactions/coreConflicts` | 主要进入 ThemePack / StoryGraph / tension state未落成具体 faction 实体 | 影响 authorial constraints、线程图谱、任务与剧情语义 | 有映射,但偏文本种子 |
| `camp` | 已映射为开局 camp scene、camp 图、camp 连接 | 通过世界参考文本和开局内容进入 prompt | 成立 |
| `attributeSchema` | 已映射到角色/NPC 属性、战斗面板、属性展示 | 已映射到 prompt 属性描述与运行时计算 | 成立 |
| `ownedSettingLayers.ruleProfile.resourceLabels` | 已映射到 UI 血量/法力/货币等术语 | 主要通过 UI/经济层体现prompt 侧间接使用 | 成立 |
| `ownedSettingLayers.ruleProfile.economyProfile.initialCurrency` | 已映射到初始货币与快照恢复 | 对运行时奖励规则影响弱,更多是初始化 | 成立但范围有限 |
| `playableNpcs` | 已映射到可玩角色预设、技能变体、初始物品、home scene | 已映射到剧情 prompt、私聊 prompt、叙事档案 | 成立 |
| `storyNpcs` | 已映射到场景 NPC、怪物判定、角色运行时预设 | 已映射到遭遇 prompt、任务发布者、叙事可见性 | 成立 |
| `landmarks` | 已映射到 scene presets、连接网络、场景视觉、treasure hints | 已映射到世界参考文本、scene residues、故事线程关联 | 成立 |
| `items` | 生成主链默认清空,世界级 item 几乎未形成正式内容层 | `knowledgeFacts` 可支持 item但主链无内容可用 | 映射明显不足 |
| `themePack/expressionProfile` | 已映射到视觉/命名/技能名/场景语义 | 已映射到 prompt 基调、reveal 风格、故事图谱 | 成立 |
| `referenceProfile.roleArchetypes` | 已映射到角色模板骨架选择 | 运行时规则直接消费较少 | 部分成立 |
| `referenceProfile.sceneBuckets` | 已映射到场景默认图匹配 | 运行时 prompt 直接消费较少 | 部分成立 |
| `referenceProfile.creatureArchetypes` | 已映射到怪物 preset 池筛选 | 运行时规则间接消费 | 部分成立 |
| `storyGraph` | 预设层主要影响 narrative residues 与 faction tension | 已映射到 active threads、constraints、visibility、chapter/campaign | 成立 |
| `narrativeProfile` | 已映射到 scene NPC 简介和遭遇资料 | 已映射到 prompt 可见性、任务/物品关系生成 | 成立 |
| `knowledgeFacts` | 不直接生成预设内容 | 已映射到 visibility slice 与 prompt 裁剪 | 成立 |
| `threadContracts` | 不直接生成预设内容 | 已映射到 story signal / thread update / QA | 成立 |
| `creatorIntent/anchorPack/lockState/anchorContent` | 主要留在创作工作区与结果页整理 | 几乎不直接进入正式游戏运行时 | 创作层有用,运行时映射弱 |
---
## 4. 已经成立的映射链
## 4.1 世界基础骨架已经能稳定进入角色、场景与剧情主链
`CustomWorldProfile` 的基础字段已经不是“只存档不消费”的状态。
它们已经实际进入:
1. 角色开局文案与 opening 动机
2. 角色技能变体
3. 场景预设名称、描述、连接、treasure hints
4. 主剧情 prompt 中的世界补充档案
5. 私聊 / 任务 / 运行时物品 prompt 的世界摘要
这说明:
**世界 profile 的基础文本层已经真正进入游戏主链。**
---
## 4.2 规则层已经落到真实游戏表现
`ownedSettingLayers.ruleProfile` 目前已真实影响:
1. `attributeSchema`
- 角色/NPC 属性计算
- prompt 中的属性描述
- 面板展示
2. `resourceLabels`
- HP/MP/伤害/冷却/货币等 UI 术语
3. `economyProfile.initialCurrency`
- 自定义世界初始货币
- 快照恢复时的默认初始化
这部分不是空壳。
**规则层已经从 profile 进入真实结算和 UI。**
---
## 4.3 叙事层已经进入 prompt 可见性与推进规则
`themePack -> storyGraph -> narrativeProfile -> knowledgeFacts -> visibilitySlice`
这条链已经是当前自定义世界最完整的一条映射链。
它已经支撑:
1. NPC 首遇/低披露 prompt 裁剪
2. 当前线程可见性控制
3. 当前压力、错位、禁区、已解锁章节等信息分层
4. 章节/战役/约束/QA 的继续推进
这说明:
**世界 profile 里的叙事层不只是展示文本,而是真的在控制“模型这轮能知道什么、不能知道什么”。**
---
## 5. 关键问题
## 5.1 高优先级问题:模板兼容层仍然是二元锚点,跨题材世界会被错误压缩
当前主生成链仍要求模型输出:
- `templateWorldType: WUXIA | XIANXIA`
而兼容解析也会把世界最终压回:
1. `arcane -> XIANXIA`
2. 其它几乎全部回到 `WUXIA`
这会直接影响:
1. 角色模板骨架选择
2. 场景默认图参考池
3. 怪物 preset 池
4. 兼容性 fallback
问题不在“有兼容字段”,而在于:
**当前兼容字段仍然过度参与真实内容映射。**
对现代金融、科幻 AI 战争、校园、都市、调查等题材来说,这种二元压缩并不合理。
仓库日志里已经出现了典型样本:
1. 股市世界被要求产出 `XIANXIA`
2. AI 战争世界也被要求产出 `XIANXIA`
3. 魔法科技融合世界被要求产出 `WUXIA`
这意味着:
**世界 profile 虽然支持跨题材文本输入,但底层预设内容映射仍带着明显的“武侠/仙侠残余偏置”。**
相关文件:
- `server-node/src/modules/ai/customWorldOrchestrator.ts`
- `src/services/customWorldTheme.ts`
- `src/data/customWorldVisuals.ts`
- `src/data/customWorldNpcMonsters.ts`
- `src/data/characterPresets.ts`
判断:
**这是当前“合理映射”最大缺口。**
---
## 5.2 高优先级问题:后端运行时任务/物品模块只消费了瘦身版 world profile
前端剧情主链里,`customWorldProfile` 会带着:
1. `themePack`
2. `storyGraph`
3. `knowledgeFacts`
4. `threadContracts`
5. `ownedSettingLayers`
但后端运行时模块里:
1. `runtimeItemModule``customWorldProfile` 只有 `{ name, summary }`
2. `runtimeQuestModule``customWorldProfile` 也只有 `{ name, summary }`
这直接导致后端运行时生成无法真正读取:
1. 世界线程图谱
2. 世界可见性事实
3. 参考原型层
4. 规则层
5. 表达层
结果是:
1. 主剧情/NPC prompt 已经较强依赖世界叙事层
2. 但后端任务/物品生成还只是吃世界摘要
这会把系统拆成两种强度不同的世界消费链:
1. 前端剧情链较“懂世界”
2. 后端运行时奖励链较“不懂世界”
相关文件:
- `src/hooks/story/storyContextBuilder.ts`
- `src/services/runtimeItemAiPrompt.ts`
- `src/services/questPrompt.ts`
- `server-node/src/modules/runtime-item/runtimeItemModule.ts`
- `server-node/src/modules/quest/runtimeQuestModule.ts`
判断:
**世界 profile 到实时生成规则的映射,在后端链路上是不完整的。**
---
## 5.3 高优先级问题:世界级 `items` 没有真正接进主生成链
类型里存在:
- `CustomWorldProfile.items`
构建器也支持:
1. item 归一化
2. `attributeResonance`
3. item knowledge facts
但真正的世界生成主链里:
1. orchestrator prompt 明确要求不要预生成物品档案
2. `attachRuntimeGenerationMetadata(...)` 会把 `items` 直接压成空数组
这会带来两个结果:
1. 世界 profile 的“世界级物品层”几乎为空
2. 运行时背包、掉落、交易更多依赖程序化生成和角色初始物品
于是目前的物品系统更像:
1. 有角色初始物品
2. 有运行时程序化物品
3. 但没有稳定的“世界物品图谱”
这意味着:
**世界 profile 在物品层没有形成完整映射。**
相关文件:
- `server-node/src/modules/ai/customWorldOrchestrator.ts`
- `src/services/customWorldBuilder.ts`
- `src/data/customWorldRuntime.ts`
- `src/data/customWorldCharacterLoadout.ts`
- `src/services/storyEngine/knowledgeGraph.ts`
判断:
**这是“预设内容”和“实时生成规则”共同缺失的一块。**
---
## 5.4 中优先级问题:`majorFactions/coreConflicts` 仍然是文本种子,不是可操作游戏对象
当前 `majorFactions``coreConflicts` 已经被大量消费,但主要消费方式是:
1. 拼进 `ThemePack`
2. 派生 `WorldStoryGraph`
3. 派生 `AuthorialConstraintPack`
4. 派生 `FactionTensionState`
问题在于它们还没有形成:
1. 可索引 faction 实体
2. faction 与 NPC 的显式归属关系
3. faction 与场景/商店/敌对阵营的显式绑定
4. 冲突与任务/势力状态的强约束关系
当前更多是:
**“文本里提到过 -> 图谱做字符串匹配 -> 运行时拿去写 prompt”**
而不是:
**“世界里真的存在这些派系与冲突对象,并驱动交互规则”**
相关文件:
- `src/services/storyEngine/themePack.ts`
- `src/services/storyEngine/worldStoryGraph.ts`
- `src/services/storyEngine/factionTensionState.ts`
判断:
**已有映射,但离“完全落地”为具体游戏内容对象还有明显距离。**
---
## 5.5 中优先级问题:场景预设会额外注入模板怪物,弱化 landmark 的原始设定控制力
`buildCustomScenePresets(profile)` 在每个 landmark scene 中,除了把 `landmark.sceneNpcIds` 指向的角色放进去,还会:
1. 从怪物 preset 池按 scene index 截两只怪
2. 直接拼进 `combinedNpcs`
这意味着即使 profile 本身没有明确要求某个 landmark 出现这些敌对实体,运行时场景仍会被额外补入模板怪物。
这会导致:
1. 场景内容不完全由 `landmark + storyNpcs` 决定
2. 地标设定与实际可战斗内容之间存在偏移
3. 跨题材世界会更容易被模板怪物池拖偏
相关文件:
- `src/data/scenePresets.ts`
- `src/data/customWorldNpcMonsters.ts`
判断:
**landmark 到实际场景实体池的映射,不是完全忠实映射,而是“设定 + 模板补丁”。**
---
## 5.6 中优先级问题:后端运行时物品线程并不是真正世界线程
前端剧情链里的 `activeThreadIds` 来自:
1. `storyEngineMemory`
2. `storyGraph`
3. `knowledgeFacts`
4. `visibilitySlice`
但后端 `runtimeItemModule` 的 loose context 里,`activeThreadIds` 只是:
1. `thread:${encounter.id}`
2.`thread:${scene.id}`
这不是世界线程图谱,而是临时合成 id。
结果是:
1. 名义上后端物品模块也有“active threads”
2. 实际上它拿到的并不是 `WorldStoryGraph` 中的真实线程
这会让运行时物品的“为什么现在出现”更像局部上下文推断,而不是来自世界故事结构。
相关文件:
- `server-node/src/modules/runtime-item/runtimeItemModule.ts`
判断:
**这是实时生成规则层的结构性弱映射。**
---
## 5.7 中优先级问题:`referenceProfile.roleArchetypes` 只从 playableNpcs 派生storyNpcs 覆盖不够
当前 `roleArchetypes` 的编译来源是:
1. `profile.playableNpcs.slice(0, 6)`
而不是:
1. `playableNpcs + storyNpcs` 的综合原型池
这导致两个问题:
1. 世界里的长尾 story NPC 原型没有进入 reference archetype 编译
2. 某些场景角色/怪物/平民的模板骨架选择更多依赖启发式 fallback
这会让:
1. 可玩角色映射较稳定
2. 长尾场景角色映射不够稳定
相关文件:
- `src/services/customWorldOwnedSettingLayers.ts`
- `src/services/customWorldReferenceSignals.ts`
- `src/data/characterPresets.ts`
判断:
**参考层映射存在明显“主角优先、长尾不足”的偏差。**
---
## 5.8 低优先级问题:创作元数据并未进入正式游戏运行时
`creatorIntent / anchorPack / lockState / anchorContent` 当前主要服务于:
1. 创作工作区
2. Agent session
3. 结果页和编辑器
它们对正式运行时的直接作用主要是:
1. 参与 `ownedSettingLayers` 的编译
但不会直接变成:
1. 正式战斗规则
2. 场景交互规则
3. faction 状态
4. 任务目标约束
这不一定是 bug但如果把“世界 profile 设定”理解为所有 profile 元数据,那么:
**创作层数据目前并没有完整进入游戏运行时。**
---
## 6. 分层判断
## 6.1 预设内容映射判断
### 已经合理接入的部分
1. 可玩角色
2. 场景角色
3. 地标场景
4. 属性 schema
5. 资源术语
6. 初始货币
7. camp 开局归处
8. 默认场景图匹配
### 仍然不足的部分
1. 世界级 items
2. faction 实体化
3. 冲突到任务/场景状态的强绑定
4. 跨题材世界的模板偏置问题
5. 地标与怪物注入之间的忠实性
结论:
**预设内容层是“能跑且已有骨架”,但还不是“设定完全落地”。**
---
## 6.2 实时生成规则映射判断
### 已经合理接入的部分
1. 主剧情 prompt
2. NPC 可见性控制
3. 私聊与对话 prompt
4. 叙事线程图谱
5. 事实图谱
6. 作者性约束与 QA
### 仍然不足的部分
1. 后端任务模块 world profile 过瘦
2. 后端物品模块 world profile 过瘦
3. 后端物品线程是伪线程
4. 世界级 item 图谱为空
5. faction/conflict 仍偏语义层,不够规则化
结论:
**实时生成规则层呈现出“前端剧情链强、后端奖励链弱”的不均衡状态。**
---
## 7. 最终判定
如果问题是:
**“世界 profile 设定是否已经完全地、合理地映射到游戏的预设内容、实时生成内容规则中?”**
我的结论是:
**没有。**
更准确地说:
1. 已经完成了主干映射。
2. 但还没有完成全量映射。
3. 也还没有完成跨题材下的合理映射。
当前系统最准确的状态是:
**世界 profile 已经成为真实驱动源之一,但还没有成为所有预设内容与实时规则的一致单一真相源。**
---
## 8. 建议的修复优先级
## P1先补“真实消费不完整”的链路
1. 让后端 `runtimeItemModule` / `runtimeQuestModule` 接收完整 `customWorldProfile` 子集
2. 至少补进:
- `ownedSettingLayers`
- `storyGraph`
- `knowledgeFacts`
- `themePack`
- `majorFactions/coreConflicts`
---
## P1把 `templateWorldType` 退回兼容字段,而不是主导字段
1. 生成期保留兼容输出
2. 运行时优先读取:
- `ownedSettingLayers`
- `themeMode`
- `referenceProfile`
3. 不再让 `WUXIA/XIANXIA` 主导现代/科幻/海洋/裂界世界的视觉与怪物选择
---
## P1补世界级 item 层
1. 允许世界生成阶段产出一批世界级 items seed
2.`knowledgeFacts / runtime item / quest reward / treasure hint` 能挂到这些 seed 上
3. 形成“世界物件图谱”,而不是只有角色初始物品和程序化临时物品
---
## P2把 faction/conflict 从文本种子升级成结构对象
1. faction 实体
2. faction -> NPC 归属
3. faction -> 场景控制
4. conflict -> 任务/线程/场景压力绑定
---
## P2去掉地标场景里的固定模板怪物补丁式注入
1. 优先使用 landmark 自己的敌对角色设计
2. 模板怪物只作为缺口补位
3. 补位也要受 landmark/theme/thread 约束
---
## P3扩充 reference archetype 的来源
1. role archetypes 不只从 playableNpcs 编
2. storyNpcs 也应参与 archetype 归纳
3. 为平民、敌对、怪物、势力成员建立更细 archetype
---
## 9. 一句话总评
**当前世界 profile 已经能驱动游戏,但还没有彻底收束成“所有预设内容与实时生成规则都优先读取它”的单一真相源。主链可用,边链仍散,跨题材合理性仍偏弱。**

View File

@@ -1,437 +0,0 @@
# 当前 Function 设计审计2026-04-03
## 审计范围
本次审计重点阅读并对照了这些位置:
- `docs/experience/ADVENTURE_RUNTIME_DEV_EXPERIENCE.md`
- `docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md`
- `docs/experience/PROJECT_DEVELOPMENT_EXPERIENCE.md`
- `docs/audits/engineering/README.md`
- `src/data/stateFunctions.ts`
- `src/data/npcInteractions.ts`
- `src/data/treasureInteractions.ts`
- `src/hooks/useStoryGeneration.ts`
- `src/hooks/story/npcEncounterActions.ts`
- `src/hooks/story/npcInteraction.ts`
- `src/hooks/story/progressionActions.ts`
- `src/hooks/story/storyGenerationState.ts`
- `src/services/ai.ts`
- `src/services/prompt.ts`
- `src/components/AdventurePanel.tsx`
- `src/components/StateFunctionEditor.tsx`
## 先说结论
当前运行时的“function”不是单一体系而是至少分成了 4 层:
1. `stateFunctions.ts` 里的基础战斗 / 空闲 function。
2. `npcInteractions.ts` 里的 NPC 交互 function。
3. `treasureInteractions.ts` 里的宝藏交互 function。
4. `useStoryGeneration.ts` / `npcInteraction.ts` / 背包装备锻造里额外加出来的“流程控制型 functionId”。
“选完选项触发 function 后没有触发剧情推理”这类现象,当前主要有 3 种来源:
1. **按设计先走本地分流,不会在第一次点击时立刻推理。**
2. **剧情推理其实已经完成,但被“继续冒险”这道 UI 中转门挡住了。**
3. **真正的可见性 bug`story_continue_adventure` 的文案常量已经乱码,导致 UI 提示失效,用户很容易误判为没继续推理。**
---
## 1. 当前 function 体系分层
### 1.1 基础状态 function`src/data/stateFunctions.ts`
这一层才是严格意义上的“状态 function 注册表”,由 `resolveFunctionOption` 统一解析。
当前运行时实际启用 12 个:
- 战斗类 7 个:
- `battle_all_in_crush`
- `battle_guard_break`
- `battle_probe_pressure`
- `battle_feint_step`
- `battle_recover_breath`
- `battle_finisher_window`
- `battle_escape_breakout`
- 空闲类 5 个:
- `idle_explore_forward`
- `idle_travel_next_scene`
- `idle_rest_focus`
- `idle_observe_signs`
- `idle_call_out`
关键设计点:
- 定义源头是 `BATTLE_FUNCTIONS` / `IDLE_FUNCTIONS`
- 最终运行时集合由 `buildStateFunctionDefinitions` 产出。
- 可执行过滤走 `getExecutableFunctions`
- 文案和视觉包装走 `resolveFunctionOption`
- 默认选项池走 `getDefaultFunctionIdsForContext`
注意:
- `idle_follow_clue` 仍然留在源码和 prompt 描述里,但在 `applyRuntimeFunctionAdjustments` 中被直接过滤掉,不会进入运行时 function 集合。
- `src/components/game-shell/useSceneTransitionModel.ts` 里仍然保留了 `idle_follow_clue` 的转场映射,这说明当前 function 清单并不完全一致。
### 1.2 NPC 交互 function`src/data/npcInteractions.ts`
这一层不是通过 `resolveFunctionOption` 生成的,而是 `buildNpcEncounterStoryMoment` 直接拼出 `StoryOption`,并挂上 `interaction.kind = 'npc'`
按功能类型看,当前会出现这些 `functionId`
- `npc_preview_talk`
- `npc_trade`
- `npc_fight`
- `npc_spar`
- `npc_help`
- `npc_chat`(可重复出现 2 个以上,代表不同聊天话题)
- `npc_gift`
- `npc_recruit`
- `npc_quest_accept`
- `npc_quest_turn_in`
- `npc_leave`
关键设计点:
- 这一层的 function 是“眼前 NPC 交互目录”,不是基础状态机目录。
- 真正执行分流不在 `stateFunctions.ts`,而在 `handleNpcInteraction` / `resolveNpcInteractionDecision`
- prompt 层通过 `availableOptions` 把这些 function 当作“固定可选项列表”交给模型,要求模型保留数量和 `functionId`
### 1.3 宝藏交互 function`src/data/treasureInteractions.ts`
这一层同样不是 `resolveFunctionOption` 体系,而是直接构造带 `interaction.kind = 'treasure'` 的选项。
当前有 3 个:
- `treasure_secure`
- `treasure_inspect`
- `treasure_leave`
执行时由 `useTreasureFlow.ts` 接管,最终再回到 `commitGeneratedState` 继续剧情推理。
### 1.4 流程控制 / 面板动作 functionId
这类 `functionId` 会进入 `lastFunctionId``commitGeneratedState`,但不在 `stateFunctions.ts` 注册表里:
- 流程控制:
- `story_continue_adventure`
- `camp_travel_home_scene`
- `story_opening_camp_dialogue`
- 面板动作:
- `inventory_use`
- `equipment_equip`
- `equipment_unequip`
- `forge_craft`
- `forge_dismantle`
- `forge_reforge`
这说明当前项目里“functionId”已经同时承担了 3 个角色:
- 运行时基础状态动作 ID
- NPC / 宝藏交互动作 ID
- 流程 / 面板事件 ID
这能跑,但可追踪性已经开始分裂。
---
## 2. 当前剧情推理触发链路
### 2.1 会立刻触发剧情推理的主链
这类点击后会直接走 AI 续推:
- 普通战斗 / 空闲 function
- `handleChoice`
- `buildResolvedChoiceState`
- `playResolvedChoice`
- `generateNextStep`
- 这些 NPC 交互
- `npc_preview_talk`
- `npc_help`
- `npc_chat`
- `npc_fight`
- `npc_spar`
- `npc_quest_accept`
- `npc_quest_turn_in`
- `npc_leave`
- 宝藏交互确认后
- `treasure_secure`
- `treasure_inspect`
- `treasure_leave`
- 面板动作确认后
- `inventory_use`
- `equipment_equip`
- `equipment_unequip`
- `forge_*`
共性是:
- 要么直接在 `handleChoice` 里调用 `generateNextStep`
- 要么先走 `commitGeneratedState` / `commitGeneratedStateWithEncounterEntry`,再统一调用 `generateStoryForState`
### 2.2 第一次点击不会立刻触发剧情推理的分流
这类最容易被误判成“function 触发了,但剧情没继续”:
- `npc_trade`
- `npc_gift`
- `npc_recruit`(队伍满时必进 modal队伍未满时会先进招募对话流
原因不是漏调,而是当前设计明确先走:
- `resolveNpcInteractionDecision`
- `trade_modal`
- `gift_modal`
- `recruit_modal`
- `recruit_immediate`
也就是说:
- 第一次点击只是**打开模态框 / 进入招募流程**。
- 真正推进剧情推理,要等到:
- `confirmTrade`
- `confirmGift`
- `confirmRecruit`
- `executeRecruitment`
如果产品预期是“用户每点一次选项就必须立即看到剧情继续”,这一层现在不满足。
### 2.3 `npc_chat` 是“先推理,再延迟展示选项”
`npc_chat` 不是普通的“生成下一幕 + 立刻给选项”,而是单独走这条链:
1. `commitNpcChatState` 先流式生成聊天正文。
2. 聊天结束后,再调用一次 `generateNextStep`
3. 新一轮冒险选项不直接显示,而是塞进 `deferredOptions`
4. 当前界面只先显示一个 `story_continue_adventure`
5. 用户再点一次,才把 `deferredOptions` 放出来。
所以这里常见的误判是:
- **剧情推理其实已经做完了。**
- 只是 UI 先让用户“继续冒险”一次,才把新选项展示出来。
---
## 3. 本次排查发现的重点问题
### 3.1 真正最像“没触发剧情推理”的地方:模态框型 NPC function
定位:
- `src/hooks/story/npcEncounterActions.ts:427-449`
- `src/hooks/story/storyGenerationState.ts:41-80`
- `src/hooks/story/npcInteraction.ts:427-585`
现象:
- 点击 `npc_trade` / `npc_gift` / `npc_recruit` 后,故事文本区通常不会立刻变化。
- 当前故事也不会马上追加一段新的 `storyText`
原因:
- 这些 function 的第一次点击只做本地 UI 分流。
- 直到用户在 modal 里确认,才会真正调用 `commitGeneratedState` 继续推理。
判断:
- **这不是单纯 bug而是当前设计本身如此。**
- 但如果玩家从“选项即剧情推进”的心智出发,会非常像“点了没反应”。
建议优先级:高。
### 3.2 最关键的实际 bug`story_continue_adventure` 文案乱码,导致“已推理但未显式提示”
定位:
- `src/hooks/useStoryGeneration.ts:92-96`
- `src/hooks/story/npcEncounterActions.ts:281-400`
- `src/components/AdventurePanel.tsx:534`
- `src/components/AdventurePanel.tsx:860`
- `src/components/AdventurePanel.tsx:879`
现象:
- `npc_chat` 完成后,系统本应展示一个“继续冒险”按钮,提示“剧情推理完成,继续后显示新的冒险选项”。
- 但当前 `CONTINUE_ADVENTURE_ACTION_TEXT` 常量已经写成了乱码:`缁х画鍐掗櫓`
- `AdventurePanel` 又是靠 `option.actionText === '继续冒险'` 来识别这个特殊按钮。
直接后果:
- 特殊提示 UI 不会出现。
- 玩家只会看到一个普通且乱码的单选项。
- 实际上 `deferredOptions` 已经算好了,但用户极容易误会为“剧情没有继续推理”。
判断:
- **这是本次排查里最明确的实现级 bug。**
- 它不会阻止底层推理发生,但会严重破坏“推理已完成”的可见性。
建议优先级:最高。
### 3.3 `StateFunctionEditor` 覆盖不到真正最容易出问题的 function
定位:
- `src/components/StateFunctionEditor.tsx:13-21`
- `src/components/StateFunctionEditor.tsx:488`
- `src/components/StateFunctionEditor.tsx:772-838`
- `src/components/StateFunctionEditor.tsx:992-1211`
现象:
- 编辑器预览只接了 `buildStateFunctionDefinitions` / `getAllStateFunctionDefinitions` / `resolveFunctionOption`
- 也就是它只能预览 `stateFunctions.ts` 那 12 个基础 function。
覆盖不到的关键分支:
- `npc_*`
- `treasure_*`
- `npc_preview_talk`
- `story_continue_adventure`
- modal 分流
- `npc_chat``deferredOptions`
判断:
- 这不是运行时 bug但它解释了为什么这类问题很难在编辑器里提前暴露。
- 当前“Function 编辑器”并没有覆盖到最复杂、最容易让用户感知为异常的 function 链路。
建议优先级:高。
### 3.4 function 清单存在“源码有、运行时无、别处还在引用”的分裂
定位:
- `src/data/stateFunctions.ts:350`
- `src/data/stateFunctions.ts:429-441`
- `src/components/game-shell/useSceneTransitionModel.ts:19-25`
现象:
- `idle_follow_clue` 仍在定义表、提示词描述、若干 `switch` 分支中存在。
- 但最终在 `applyRuntimeFunctionAdjustments` 被过滤掉,不会进入运行时 function 集合。
- `useSceneTransitionModel` 里却还保留着它的转场模式映射。
判断:
- 这不是“没触发剧情推理”的直接原因。
- 但会让维护者误判当前真实 function 清单,也会增加后续继续扩 function 时的混乱。
建议优先级:中。
### 3.5 自动化测试几乎没有覆盖“最容易让人误会没推理”的链路
定位:
- 当前已有测试主要是 `src/hooks/story/storyGenerationState.test.ts`
已覆盖:
- `trade_modal`
- `recruit_modal`
- 地图切场景
未覆盖:
- `npc_chat``deferredOptions -> story_continue_adventure -> 真正显示新选项`
- `npc_trade` / `npc_gift` / `npc_recruit` 确认后是否一定调用 `commitGeneratedState`
- `story_continue_adventure` 文案与 UI 特判是否一致
- `npc_preview_talk -> enterNpcInteraction -> generateStoryForState`
- 宝藏分支确认后是否稳定续推
判断:
- 这类问题之所以能长期存在,一个核心原因就是**最关键的续推分支没有测试兜底**。
建议优先级:高。
---
## 4. 按“首次点击是否立即触发剧情推理”整理
### 4.1 首次点击就会继续推理
- `battle_*`
- `idle_*`
- `npc_preview_talk`
- `npc_help`
- `npc_chat`
- `npc_fight`
- `npc_spar`
- `npc_quest_accept`
- `npc_quest_turn_in`
- `npc_leave`
- `treasure_*`
- `inventory_use`
- `equipment_*`
- `forge_*`
### 4.2 首次点击不会立刻继续推理
- `npc_trade`
- `npc_gift`
- `npc_recruit`(至少会先进入确认 / 对话流)
- `story_continue_adventure`
这里要特别分清:
- `npc_trade` / `npc_gift` / `npc_recruit` 是**先分流,后确认,确认后再推理**。
- `story_continue_adventure` 是**推理已经完成,只是先把结果选项延后展示**。
---
## 5. 建议的修正顺序
### P0
- 修掉 `CONTINUE_ADVENTURE_ACTION_TEXT` 的乱码。
- `AdventurePanel` 不要再靠 `actionText === '继续冒险'` 判定特殊按钮,改成按 `functionId === 'story_continue_adventure'` 判定。
### P1
- 明确产品规则:
- `npc_trade` / `npc_gift` / `npc_recruit` 第一次点击是否就应该写入一条“进入交易 / 送礼 / 招募确认”的剧情反馈。
- 如果答案是“应该”,那这些 modal 型 function 需要补一层轻量 story feedback而不是只弹框。
### P1
-`npc_chat` 补自动化测试:
- 聊天后必须出现 `story_continue_adventure`
- 第二次点击后必须能展示 `deferredOptions`
### P1
-`npc_trade` / `npc_gift` / `npc_recruit` 补自动化测试:
- 第一次点击只分流
- 确认后一定触发 `commitGeneratedState`
- 后续一定能拿到新的 `StoryMoment`
### P2
- 扩展 `StateFunctionEditor` 或补新的“交互 function 预览器”,把 `npc_*` / `treasure_*` / `story_continue_adventure` 也纳入可预演范围。
### P2
- 清理 `idle_follow_clue` 这类“半退场”的 function保证
- 运行时集合
- prompt 描述
- 编辑器
- 转场映射
- 测试
保持一致。
---
## 最后结论
当前最值得优先处理的,不是再继续加新的 function而是先把这 3 个点收拢:
1. **把“首次点击只分流、不推理”的 function 明确标出来。**
2. **把 `npc_chat -> story_continue_adventure -> deferredOptions` 这条延迟展示链做清楚。**
3. **先修掉 `story_continue_adventure` 的乱码和基于文案的 UI 判断。**
如果不先处理这几个点,用户会持续把“按设计延迟”与“真实漏调剧情推理”混在一起感知,后面 function 越多,这类问题会越难排查。

View File

@@ -1,163 +0,0 @@
# Function 需求完整性核查2026-04-14
## 1. 核查范围
本次核查按当前线程上下文,聚焦 function 体系相关文档与实现,不扩大到整个项目全部 PRD。
本次实际对照了这些文档:
- `docs/audits/FUNCTION_DESIGN_AUDIT_2026-04-03.md`
- `docs/reference/FUNCTION_SCRIPT_CATALOG_2026-04-04.md`
- `docs/experience/ADVENTURE_RUNTIME_DEV_EXPERIENCE.md`
- `docs/audits/engineering/README.md`
本次实际核对了这些实现入口:
- `src/data/functionCatalog/**`
- `src/data/stateFunctions.ts`
- `src/data/npcInteractions.ts`
- `src/components/AdventurePanel.tsx`
- `src/hooks/story/choiceActions.ts`
- `src/hooks/story/npcEncounterActions.ts`
- `src/hooks/story/npcInteraction.ts`
- `src/services/runtimeStoryService.ts`
- `server-node/src/modules/story/**`
- `server-node/src/modules/inventory/**`
- `server-node/src/modules/runtime-item/**`
- `server-node/src/modules/quest/**`
## 2. 核查结论
结论先说:
- function 主链路需求已经基本落地,不需要再继续“为了完整而过度迭代”。
- 当前最主要的缺口不是再发明一套新 function 流程,而是把已有链路补齐回归测试,确保后续不会回退。
- 本轮核查后,已把两条仍缺直接测试兜底的核心链路补上。
## 3. 已确认已经落地的需求
### 3.1 function 目录化与分层收口
已实现:
- `state / npc / treasure / flow / panel` 五类 function 已统一收口到 `src/data/functionCatalog/`
- `src/data/functionCatalog/index.ts` 已提供统一导出
- `SERVER_RUNTIME_FUNCTION_IDS` 也已和 catalog 文档映射对齐
判断:
- 这部分需求已完成,不需要继续重做结构。
### 3.2 `story_continue_adventure` 延迟展示链路
已实现:
- `npc_chat``npcEncounterActions.ts` 中先生成聊天正文,再把后续选项写入 `deferredOptions`
- `choiceActions.ts` 在点击 `story_continue_adventure` 时会直接恢复 `deferredOptions`
- `AdventurePanel.tsx` 已按 `functionId` 而不是 `actionText` 识别该特殊按钮
判断:
- 这条主功能链路已经真正存在,不属于“文档写了、实现没跟上”。
### 3.3 modal 型 function 的首次点击分流
已实现:
- `npc_trade`
- `npc_gift`
- `npc_recruit`
这些 function 当前都明确是“首次点击先分流,再在确认后进入真正执行链”。
当前实现路径:
- 首次点击:
- `choiceActions.ts`
- `storyGenerationState.ts`
- `npcInteraction.ts`
- 确认后:
- `trade / gift / quest` 进入 server runtime action
- `recruit` 进入本地招募对白与本地状态提交链
判断:
- 这不是未实现,而是当前架构设计如此。
- 文档里“确认后要继续推进剧情/结算”的要求已经满足。
### 3.4 Task6 function 的服务端化
已实现:
- `inventory_use`
- `equipment_equip`
- `equipment_unequip`
- `forge_craft`
- `forge_dismantle`
- `forge_reforge`
- `npc_trade`
- `npc_gift`
- `npc_quest_accept`
- `npc_quest_turn_in`
- `treasure_secure`
- `treasure_inspect`
- `treasure_leave`
这些 function 已经在前端 `runtimeStoryService.ts`、服务端 `story runtime / inventory / runtime-item / quest` 模块里形成闭环。
判断:
- 这部分不需要再回退到前端本地重写。
## 4. 本轮发现的真实缺口
本轮真正仍未完整落地的,不是功能行为本身,而是下面两条回归保护:
### 4.1 `npc_chat -> story_continue_adventure -> deferredOptions`
问题:
- 文档明确要求这条链路要清楚、可验证。
- 代码已经有,但之前没有直接测试“点击继续冒险后必须展示 deferredOptions”。
本轮已补:
- `src/hooks/story/choiceActions.test.ts`
- 新增 `reveals deferred adventure options when story_continue_adventure is selected`
### 4.2 `AdventurePanel` 对 continue option 的识别方式
问题:
- 文档明确要求不要再靠文案识别 continue option。
- 实现已经改成按 `functionId` 识别,但之前没有组件层测试锁住。
本轮已补:
- `src/components/AdventurePanel.test.tsx`
- 验证 `story_continue_adventure` 即使 actionText 改掉,仍然显示延迟选项提示
- 验证仅 actionText 相同但 functionId 不同,不会误触发提示
## 5. 本轮新增测试
本轮新增:
- `src/components/AdventurePanel.test.tsx`
- `src/hooks/story/choiceActions.test.ts`
- 新增 deferred options 恢复用例
## 6. 验收结论
按当前 function 相关文档要求判断:
- 核心运行时需求:已实现
- 延迟展示链路:已实现
- modal 分流架构:已实现
- Task6 服务端承接:已实现
- 回归测试盲区:本轮已补齐关键缺口
最终建议:
- 这块现在不应该继续过度迭代。
- 后续应以“新增需求再增量补测试”为主,而不是再次重构 function 主链路。
- 除非后续 PRD 明确要求“modal 首次点击也必须立刻生成一段剧情反馈”,否则不建议为了形式统一再强行改写当前分流模型。

View File

@@ -1,232 +0,0 @@
# Function 运行时完整测试审计2026-04-16
## 1. 本次目标
本次不是泛泛地跑一遍前端页面,而是围绕“游戏运行中的 function 主链路”做系统化核查,重点确认下面 4 件事:
- 当前运行时 function 集合是否还能正确构建、过滤、排序和解析。
- function 命中的前端承接链路是否仍然稳定。
- 已服务端化的 runtime action function 是否还能闭环执行。
- 工程门禁是否处于可持续回归的状态。
## 2. 本次实际覆盖范围
本轮重点覆盖了这些实现入口:
- `src/data/stateFunctions.ts`
- `src/data/functionCatalog/**`
- `src/data/npcInteractions.ts`
- `src/hooks/story/**`
- `src/services/runtimeStoryService.ts`
- `src/hooks/useGameFlow.ts`
- `server-node/src/modules/story/**`
- `server-node/src/modules/inventory/**`
- `server-node/src/modules/runtime-item/**`
- `server-node/src/modules/quest/**`
- `scripts/smoke-server-node.ts`
## 3. 实际执行的测试与检查
### 3.1 前端 Vitest 全量测试
执行命令:
```bash
npm.cmd test
```
结果:
- `110` 个测试文件全部通过
- `276` 条测试全部通过
其中与 function 主链路直接相关、并已确认通过的测试包括:
- `src/data/stateFunctions.test.ts`
- `src/data/functionCatalog/functionCatalog.test.ts`
- `src/data/npcInteractions.test.ts`
- `src/hooks/story/choiceActions.test.ts`
- `src/hooks/story/storyGenerationState.test.ts`
- `src/hooks/story/runtimeStoryCoordinator.test.ts`
- `src/components/AdventurePanel.test.tsx`
- `src/services/runtimeStoryService.test.ts`
补充说明:
- 运行 `hostileNpcPresets.test.ts` 时会看到 “network disabled in test” 的日志。
- 这不是本轮失败项,测试已验证在 LLM 不可达时会正确走 deterministic fallback。
### 3.2 内容数据校验
执行命令:
```bash
npm.cmd run check:content
```
结果:
- `check:data` 通过
- `check:overrides` 通过
- `check:smoke` 通过
- 输出为:`Content validation passed. scenes=24 monsters=16 characters=5 functions=12`
结论:
- 当前基础内容数据、override 与 smoke 内容校验没有发现新的 function 数据层问题。
### 3.3 服务端测试
执行命令:
```bash
npm.cmd run server-node:test
```
结果:
- `108` 条服务端测试全部通过
本轮已确认通过的 function 相关服务端能力包括:
- `inventory_use`
- `equipment_equip`
- `npc_trade`
- `npc_gift`
- `npc_quest_accept`
- `npc_quest_turn_in`
- `treasure_inspect`
- 战斗结算与 quest signal 推进
结论:
- 已服务端化的 runtime function 承接链路当前单测层面是稳定的。
### 3.4 服务端 smoke
执行命令:
```bash
npm.cmd run server-node:smoke
```
结果:
- 失败
- 报错:
```text
TypeError: Cannot read properties of undefined (reading 'enabled')
```
### 3.5 TypeScript 类型检查
执行命令:
```bash
npm.cmd run typecheck
```
结果:
- 失败
- 失败位置:
- `src/hooks/useGameFlow.ts:339`
### 3.6 生产构建
执行命令:
```bash
npm.cmd run build
```
结果:
- 通过
结论:
- 当前代码可构建,但并不代表类型门禁与 smoke 门禁同样健康。
## 4. 本轮确认的问题
## P1`server-node:smoke` 已失效,无法完成服务端运行时冒烟验证
- 现象:
- `npm.cmd run server-node:smoke` 无法启动临时 Express 服务,直接在 `createSmsVerificationService` 处报错。
- 直接原因:
- [`scripts/smoke-server-node.ts`](../../scripts/smoke-server-node.ts) 里的 `createSmokeConfig()` 只构造了 `llm``dashScope` 等字段,没有补齐后续新增的 `smsAuth``wechatAuth``authSession` 配置块。
- [`server-node/src/config.ts`](../../server-node/src/config.ts) 中 `AppConfig` 已把这些字段定义为必需项。
- [`server-node/src/services/smsVerificationService.ts`](../../server-node/src/services/smsVerificationService.ts) 在 `createSmsVerificationService` 中直接读取 `config.smsAuth.enabled`,因此 smoke 配置一进入 `createAppContext` 就会崩。
- 影响:
- 本地无法再用 smoke 脚本验证“服务端真实启动 + 认证 + runtime save/settings 回路”。
- 这会让 function 服务端化之后的真实接线路径少掉一层最接近运行时的保护。
- 判断:
- 这是本轮最明确、最稳定复现的真实 bug。
## P1`typecheck` 门禁已破,`useGameFlow` 的 starter inventory 合并存在类型漂移
- 现象:
- `npm.cmd run typecheck` 在 [`src/hooks/useGameFlow.ts`](../../src/hooks/useGameFlow.ts) 第 `339` 行失败。
- 直接原因:
- 同文件中的 `mergeStarterInventoryItems<T extends { category: string; name: string }>` 会优先从显式 starter item 推断出一个“字段更严格”的 `T`
- 但 fallback 侧传入的是 [`InventoryItem`](../../src/types/items.ts) 数组,而 `InventoryItem.description``equipmentSlotId``runtimeMetadata` 等字段本身是可选的。
- 于是 `explicitItems``fallbackItems` 在泛型推断后不再兼容,类型门禁被打穿。
- 影响:
- 当前 starter inventory 初始化链路虽然能跑、也能通过构建,但已经失去 TypeScript 对结构一致性的保护。
- 这类问题后续很容易演变成“自定义世界显式初始物品”和“默认初始物品”在字段形态上继续分叉。
- 判断:
- 这是一个真实的工程 bug优先级高于纯测试文案问题。
## 5. 本轮已确认通过的结论
- state function 运行时构建、过滤、优先级、选项解析当前测试全绿。
- function catalog 文档映射、helper option、trade/gift/recruit modal helper 当前测试全绿。
- 前端 function 主链路相关测试在最新工作区状态下已全部通过。
- 服务端 runtime story action、inventory、runtime-item、quest 承接链路当前测试全绿。
- 内容数据层校验通过。
- 生产构建通过。
## 6. 结论
如果只看“游戏运行中的 function 主链路”,当前结论是:
- 前端 function 运行时逻辑:通过
- 服务端 function action 承接:通过
- 内容数据与 function 基础目录:通过
- 工程回归门禁:不完整
当前最需要处理的不是再扩 function 范围,而是先修复这两个门禁缺口:
1. 修好 `server-node:smoke` 的配置构造,让服务端冒烟恢复可用。
2. 修好 `useGameFlow` 的 starter inventory 类型漂移,让 `typecheck` 回到绿色基线。
## 7. 备注
本轮没有做两类事情:
- 没有接入真实外部 LLM 做在线回归,本轮依赖的是本地测试里已有的 fallback 断言。
- 没有人手逐个点击整局游戏所有 function 的视觉回放,本轮重点是自动化测试、服务端测试、内容校验与 smoke 门禁。
因此,本审计可以说明“当前 function 系统的自动化测试层状况”,但不等于“所有视觉演出与在线模型联动都已人工验证完毕”。
## 8. 执行回填2026-04-28修复聊天任务领取入口
- 问题现象:
- NPC 聊天中的待领取任务,点击“查看任务”进入详情后,再点“领取任务”没有把面板切到正式已接任务状态,表现上像“无法领取”。
- 根因:
- `npcChatQuestOfferUi.acceptPendingOffer()` 会异步把 `npc_quest_accept` 发到服务端。
- 但 [`src/components/rpg-runtime-panels/RpgAdventurePanel.tsx`](../../src/components/rpg-runtime-panels/RpgAdventurePanel.tsx) 里“待领取任务详情弹层”的 `onAcceptPendingNpcQuestOffer` 只返回了 `questId`,没有把它写入共享的 `pendingAcceptedQuestId`
- 结果是本来负责等待 quest 真正进入 `quests` 后再统一收口面板状态的 `useEffect` 根本不会触发。
- 修复:
-`onAcceptPendingNpcQuestOffer` 中补写 `setPendingAcceptedQuestId(acceptedQuestId)`,让待领取任务详情弹层复用普通 `npc_quest_accept` 已有的异步收口链。
- 新增 [`src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx`](../../src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx),覆盖“查看任务 -> 领取任务 -> 服务端异步写回 quest log -> 面板切到正式任务状态”的回归路径。
- 本次回归验证:
- `src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx`
- `src/components/rpg-runtime-panels/RpgAdventurePanel.test.tsx`
- `src/hooks/rpg-runtime-story/npcEncounterActions.test.ts`
- `src/hooks/rpg-runtime-story/choiceActions.test.ts`
- `src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts`
- `src/hooks/rpg-runtime-story/sessionActions.test.ts`
-`57` 条测试通过。

View File

@@ -1,128 +0,0 @@
# Function 测试审计2026-04-14
补充更新:
- 本文记录的 2 个 bug 已在同日完成代码修复。
- 对应测试已经从“稳定复现旧行为”切换为“验证修复后行为”。
## 1. 本次新增测试
本轮新增了两组 function 相关测试:
- `src/data/stateFunctions.test.ts`
- 覆盖 state function 的运行时过滤、优先级、选项解析、排序逻辑。
- `src/data/functionCatalog/functionCatalog.test.ts`
- 覆盖 function 文档映射、flow helper、NPC helper modal 初始化逻辑。
这两组测试都直接挂在现有 `vitest` 体系里,没有新建独立测试框架。
## 2. 本次执行结果
本轮实际执行了以下测试:
```bash
npx vitest run src/data/stateFunctions.test.ts src/data/functionCatalog/functionCatalog.test.ts
npx vitest run src/data/npcInteractions.test.ts src/hooks/story/storyGenerationState.test.ts src/services/runtimeStoryService.test.ts
```
执行结果:
- 新增测试:`2` 个文件,`12` 条测试,全部通过。
- 复跑已有 function 相关测试:`3` 个文件,`17` 条测试,全部通过。
- 修复回归测试:`5` 个文件,`30` 条测试,全部通过。
- 编码检查:`1516` 个文件全部通过。
说明:
- 本轮不是“测试全绿就代表没有问题”。
- 最初定位出的 2 个问题,已经在后续修复回合里转成了回归测试。
## 3. 历史 bug 与修复结果
### 3.1 `battle_recover_breath`
- 所在位置:`src/data/stateFunctions.ts`
- 状态:已修复
- 原始问题表现:
-`inBattle = true`,但当前没有存活敌人时,`battle_recover_breath` 仍会留在可执行 function 列表中。
- 直接原因:
- `matchesCategory``recovery` 分类只判断了是否处于战斗态,没有像 `battle` / `escape` 分类那样额外校验 `hasAliveMonsters(context.monsters)`
- 修复方式:
- 已在 `matchesCategory``recovery` 分支中,为战斗恢复类 function 补上 `hasAliveMonsters(context.monsters)` 判断。
- 修复前影响:
- 在“敌人已死但战斗态尚未清理干净”的边界帧里,界面仍可能出现战斗恢复类选项。
- 这会让 function 池和真实战斗状态产生残留错位。
- 当前验证方式:
- `inBattle = true`
- `monsters = [{ hp: 0, ... }]`
- 调用 `getExecutableFunctions(context)`
- 现在返回结果应为空,不再包含 `battle_recover_breath`
- 对应用例:
- `src/data/stateFunctions.test.ts`
- 用例名:`removes battle_recover_breath when combat has no living monsters`
### 3.2 `npc_trade`
- 所在位置:`src/data/functionCatalog/npc/npcTrade.ts`
- 状态:已修复
- 原始问题表现:
- trade modal 初始化时,`selectedPlayerItemId` 直接取 `state.playerInventory[0]?.id`
- 如果玩家背包第一项数量为 `0`modal 默认会选中一件不可出售物品。
- 直接原因:
- `buildNpcTradeModalState` 没有过滤 `quantity <= 0` 的物品,也没有寻找第一个可交易物品。
- 修复方式:
- 已改为优先选择 `quantity > 0` 的可交易物品。
- 同时对 NPC 库存和玩家背包都使用同一条筛选规则,避免默认选中空物品。
- 修复前影响:
- 交易面板第一次打开时,默认状态可能就是不可确认的。
- 用户需要手动切换到第二件物品,才会进入可提交状态。
- 当前验证方式:
- `playerInventory[0].quantity = 0`
- `playerInventory[1].quantity > 0`
- 调用 `buildNpcTradeModalState(...)`
- 现在 `selectedPlayerItemId` 应该自动落到第一件可交易物品
- 对应用例:
- `src/data/functionCatalog/functionCatalog.test.ts`
- 用例名:`prefers the first tradable player item when zero-quantity items exist`
- `src/hooks/story/storyGenerationState.test.ts`
- 用例名:`skips zero-quantity player items when opening the trade modal`
## 4. 本轮已验证通过的 function 能力
以下内容本轮已通过测试验证,没有发现新的明显问题:
- state function runtime 构建:
- `idle_follow_clue` 已正确从运行时候选池移除。
- `idle_explore_forward` 的运行时文案覆盖仍然生效。
- state function 选项行为:
- 高压战斗下 `battle_recover_breath` 会被正确提权。
- 营地场景会正确隐藏 `idle_explore_forward`
- `idle_travel_next_scene` 会强制使用运行时建议 actionText。
- `battle_all_in_crush` 会保留外部传入的自定义 actionText。
- story option 排序仍保持“前 2 个 model 锁定 + 后续按 priority 排序”。
- flow helper
- `story_continue_adventure`
- `camp_travel_home_scene`
- NPC helper
- `npc_preview_talk`
- `npc_gift`
- `npc_recruit`
- 文档映射:
- 当前 `SERVER_RUNTIME_FUNCTION_IDS` 全部都能在 function catalog 文档中找到对应条目。
- 本轮扫描到的 function `source` 路径全部存在,没有出现失效引用。
## 5. 本轮修复动作
- `battle_recover_breath`
- 已补充战斗恢复类 function 的存活敌人校验,避免战斗边界帧残留非法选项。
- `npc_trade`
- 已把 trade modal 默认选中逻辑改为优先寻找可交易物品,不再直接吃数组第一项。
- 回归测试
- 原先记录旧行为的测试已翻转为修复后预期,并额外补了一条 `storyGenerationState` 接入层测试。
## 6. 备注
这次测试资产的意义分两步:
- 第一步先把 bug 稳定复现出来,避免问题只停留在口头描述。
- 第二步在修复后把断言翻转成“正确行为”,让它们正式成为回归测试。

View File

@@ -1,133 +0,0 @@
# 物品生成系统与 Build 标签系统 PRD 落地审计
审计时间2026-04-05
审计范围:
- `docs/prd/AI_NATIVE_RUNTIME_ITEM_GENERATION_DESIGN.md`
- `docs/prd/RUNTIME_ITEM_GENERATION_CURRENT_SYSTEM_DESIGN.md`
- `docs/prd/BUILD_SYSTEM_ATTRIBUTE_SIMILARITY_PRD_2026-04-02.md`
- `docs/design/EQUIPMENT_BUILD_AND_FORGE_LOOP_SYSTEM_DESIGN.md`
## 结论速览
### 1. 物品生成系统
当前状态可以判定为:**主链已补齐落地**。
已经落地的是:
- 有独立的运行时上下文层、导演层、本地编译层、叙事回写层。
- 宝藏、怪物掉落、通用 NPC 商店已经能走统一的 runtime item director。
- 永久 build 标签物品、限时 build buff 物品、少量数值物品三种骨架都已经能编译出来,并接进现有背包 / 装备 / build 结算。
本轮已补齐的是:
- 新增了 runtime item AI 意图导演与 prompt并接入 NPC 帮助奖励主链,失败时自动回退到本地导演。
- NPC 交易库存改成按玩家当前 build 生成,并通过 `tradeStockSignature` 只在 build 变化时刷新。
- NPC 帮助奖励、委托奖励已经统一接入 runtime item director。
- 怪物掉落已经改成“基础掉落 + 语义掉落”双层叠加。
### 2. Build 标签系统
当前状态可以判定为:**核心已按 PRD 落地,且实现范围比 PRD 更大**。
已经落地的是:
- `BuildTagDefinition.attributeAffinity` 已扩展。
- `buildDamage.ts` 已改成“标签分别匹配角色属性画像”的加法模型,不再做标签两两网络效应。
- Buff / 角色固有 / 武器 / 护甲 / 饰品 / 套装标签都能进入最终倍率。
- Character 面板已经切到“属性适配度”展示,并能拆出单标签的属性贡献明细。
- 有针对新公式的测试覆盖。
还存在的尾项主要是:
- 实现用的是“世界属性 schema 六轴模型”,不是 PRD 文字里的固定四维属性。
- 旧的标签相似度辅助能力没有完全清干净,重铸仍在用 `getSimilarBuildTags` 做候选标签替换。
## 物品生成系统审计
| PRD 项 | 当前实现 | 判定 | 代码证据 |
| --- | --- | --- | --- |
| 上下文采样层 | 已有 `buildRuntimeItemGenerationContext` / `buildQuestRuntimeItemGenerationContext`,会收集场景、遭遇、关联 NPC、最近剧情、玩家 build 标签与 build gap。 | 已落地 | `src/data/runtimeItemContext.ts:157-188``src/data/runtimeItemContext.ts:191-252` |
| AI 意图层 | 已新增 `runtimeItemAiDirector` / `runtimeItemAiPrompt``buildRuntimeItemAiPromptInput` 已进入真实 prompt 组装NPC 帮助奖励主链会先请求 AI 物品意图,再回落到本地意图导演。 | 已补齐 | `src/services/runtimeItemAiPrompt.ts``src/services/runtimeItemAiDirector.ts``src/data/runtimeItemDirector.ts``src/hooks/story/npcEncounterActions.ts` |
| 本地编译层 | 已按 channel / slot / permanence 做 rarity 与预算编译,并产出 `statProfile``useProfile.buildBuffs``buildProfile``runtimeMetadata`。 | 已落地 | `src/data/runtimeItemCompiler.ts:77-101``src/data/runtimeItemCompiler.ts:122-206``src/data/runtimeItemCompiler.ts:245-276` |
| 叙事回写层 | 会把锚点、来源理由、build 倾向回写进物品名和描述。 | 已落地 | `src/data/runtimeItemNarrative.ts:171-189` |
| 永久标签 / 限时标签 / 少量数值三类物品 | 永久物品走 `buildProfile`,限时物品走 `useProfile.buildBuffs`,并可附带少量数值。 | 已落地 | `src/data/runtimeItemCompiler.ts:104-155``src/data/runtimeItemCompiler.ts:157-206` |
| 宝藏入口 | 宝藏奖励已经走 `buildRuntimeItemGenerationContext + buildDirectedRuntimeReward`,并把结果写回 story hint。 | 已落地 | `src/data/treasureInteractions.ts:52-89``src/hooks/useTreasureFlow.ts:45-75` |
| NPC 交易入口 | 通用 NPC 商店已改成按玩家当前 build 生成;初始 NPC 状态、交易模态打开时刷新、场景预览读取都会带上完整 `GameState`,并用 `tradeStockSignature` 避免无意义重刷。角色型 NPC 仍保留既有角色装备/背包模板。 | 已补齐 | `src/data/npcInteractions.ts``src/hooks/story/npcInteraction.ts``src/hooks/useStoryGeneration.ts``src/components/NpcModals.tsx` |
| NPC 帮助 / 关系奖励 | `npc_reward` 已真正接入主链。帮助奖励现在会生成带 `runtimeMetadata` 的 runtime item并保留数值恢复、冷却缩减与 story hint。 | 已补齐 | `src/data/npcInteractions.ts``src/hooks/story/npcEncounterActions.ts` |
| 委托奖励入口 | Quest reward 已改为走 runtime director支持 `buildQuestRuntimeItemGenerationContext`,领取时发放的是 runtime item。主 NPC 接任务流程也已切到 `generateQuestForNpcEncounter`。 | 已补齐 | `src/data/questFlow.ts``src/hooks/story/npcEncounterActions.ts``src/services/questDirector.ts` |
| 怪物掉落双层设计 | 普通世界掉落已改成预设 `lootTable` 基础掉落与 `monster_drop` runtime 语义掉落并存,不再互相覆盖。 | 已补齐 | `src/data/hostileNpcPresets.ts``src/data/hostileNpcPresets.test.ts` |
### 物品系统的具体判断
#### 已经明显符合 PRD 的部分
1. 系统分层已经形成。
`runtimeItemContext -> runtimeItemDirector -> runtimeItemCompiler -> runtimeItemNarrative` 这条链已经非常接近 PRD 里“上下文采样 / AI 意图 / 本地编译 / 叙事回写”的结构。
2. build 导向优先于纯数值。
`buildRuntimeItemContext` 会先算 `playerBuildTags``playerBuildGaps`,导演层再优先把 gap tag 与现有 build tag 拼进 `targetBuildDirection`,编译层才决定数值预算。
3. 奖励已经进入真实玩法结算。
runtime item 生成出的 `buildProfile` 会进入装备 build 结算,`useProfile.buildBuffs` 会在使用物品时写入 `activeBuildBuffs`
#### 本轮补齐后的说明
1. 运行时物品意图已经进入真实主链。
当前至少在 NPC 帮助奖励链路中,已经先走 AI 意图导演,再走本地编译与叙事回写;如果模型不可用,会回退到既有启发式导演,保证玩法不断。
2. 交易库存已经真正读取玩家当前构筑。
通用交易 NPC 的库存会基于玩家当前 build、装备标签和 build gap 生成,而不是继续依赖 NPC 自身偏好标签。
3. 帮助奖励、委托奖励、怪物掉落都已并入统一 runtime director。
现在三条链路都能产出带 relation anchor / source reason / runtime metadata 的 runtime item。
## Build 标签系统审计
| PRD 项 | 当前实现 | 判定 | 代码证据 |
| --- | --- | --- | --- |
| `BuildTagDefinition.attributeAffinity` 扩展 | 类型已扩展,标签注册表也会为每个标签注入 affinity。 | 已落地 | `src/types/build.ts:6-13``src/data/buildTags.ts:66-69` |
| 静态标签亲和度表 | 已有 `buildTagAttributeAffinity.ts`,提供标签到属性轴的静态 affinity 表。 | 已落地 | `src/data/buildTagAttributeAffinity.ts:127-183` |
| 从“标签互相影响”改为“标签分别匹配角色属性” | `buildDamage.ts` 已按单标签计算 `fitScore``bonusDelta` 和属性贡献,最后做加法累积,不再做 pair/cluster 乘法网络。 | 已落地 | `src/data/buildDamage.ts:236-318` |
| 来源系数 | Buff / 角色 / 武器 / 护甲 / 饰品 / 套装都有独立 source coefficient和 PRD 基本一致。 | 已落地 | `src/data/buildDamage.ts:95-114` |
| 最终伤害接入 | `resolvePlayerOutgoingDamage` / `resolveCompanionOutgoingDamage` / `resolveMonsterOutgoingDamage` 都已接 `buildDamageMultiplier`。 | 已落地 | `src/data/buildDamage.ts:498-542` |
| 展示层从“标签协同”改成“属性适配度” | Character 面板已经按标签展示 bonus、来源、主导属性并能打开明细弹窗。 | 已落地 | `src/components/CharacterPanel.tsx:205-247``src/components/CharacterPanel.tsx:497-539` |
| 验收测试 | 已覆盖单标签可拆分、删一个标签不重算其余标签、不同属性角色用同一套装倍率不同、Buff/套装来源正常进入等场景。 | 已落地 | `src/data/buildDamage.test.ts:125-313` |
| 四维属性口径 | PRD 写的是四维固定属性;实现已经提升为 world schema 六轴模型。目标一致,但口径不再一一对应。 | 已落地,但实现已外延扩展 | `src/types/attributes.ts:3-15``src/data/worldAttributeSchemas.ts:4-155` |
| 旧标签相似度清理 | 核心伤害结算已不再依赖旧矩阵,但 `getSimilarBuildTags` 仍存在,重铸继续用它挑候选标签,`generate:build-tags` 脚本也还保留。 | 收尾未完成 | `src/data/buildTags.ts:183-215``src/data/forgeSystem.ts:371-399``package.json:21-24` |
### Build 系统的具体判断
#### 已经符合 PRD 核心目标的部分
1. 可解释性已经建立。
现在每个标签都有自己的 `fitScore``attributeContributions``attributeModifierDeltas`,玩家可以看到“这个标签为什么强、强在哪条属性轴上”。
2. 新增标签不会反向扰动旧标签贡献。
`buildDamage.test.ts` 已专门验证“删掉一个标签,只会移除它自己的 row不会重算其他 row”。
3. 套装标签、Buff 标签、装备标签都能统一进入同一公式。
这点和 PRD 的来源规则一致,且测试已经覆盖。
#### 需要注意但不构成核心未落地的问题
1. 实现已经超出 PRD 的四维设计。
现在不是固定 `strength / agility / intelligence / spirit` 四维,而是通过 `WorldAttributeSchema` 映射成武侠/仙侠/自定义世界都能共用的语义属性轴。这更适合当前仓库的世界化属性系统,但也意味着 PRD 文案需要同步。
2. 旧相似度能力没有完全退场。
核心伤害结算已经不用标签两两矩阵,但锻造重铸仍然会根据标签相似度挑换洗标签,所以“旧体系相关命名/脚本”还没彻底收尾。
## 综合判断
如果按“是否已经把 PRD 的主战场落到代码里”来判断:
- **Build 标签系统:可以认为已经落地。**
- **物品生成系统:可以认为主链已经落地,之前审计出的缺口已在本轮补齐。**
如果接下来继续做收尾,更建议做的是:
1. 把 runtime item AI 意图层继续扩到宝藏、怪物掉落、委托奖励以外的更多同步入口,减少启发式 fallback 的覆盖面。
2. 给 Build 系统补一版文档同步,明确当前实现已经从 PRD 四维模型升级为世界属性 schema 模型,并清理剩余旧相似度脚本/命名。
3. 评估 `origin: 'ai_compiled'` 的语义是否还要细分成“AI 意图 + 本地编译”与“纯本地 fallback”两档方便后续观测与埋点。

View File

@@ -1,33 +0,0 @@
# 审计与复盘
这一组文档聚焦“当前状态是否健康、问题在哪里、和目标设计差多少”。
## 系列总览
- [engineering/README.md](./engineering/README.md):当前工程优化审查与历史结论聚合入口。
- [text/README.md](./text/README.md):文本、英文残留、乱码审计系列的融合入口。
## 专项审计
- [FUNCTION_DESIGN_AUDIT_2026-04-03.md](./FUNCTION_DESIGN_AUDIT_2026-04-03.md)Function 体系分层、职责边界和当前结构问题。
- [FUNCTION_REQUIREMENT_COMPLETENESS_AUDIT_2026-04-14.md](./FUNCTION_REQUIREMENT_COMPLETENESS_AUDIT_2026-04-14.md)Function 相关文档需求与当前实现对齐核查。
- [FUNCTION_TEST_AUDIT_2026-04-14.md](./FUNCTION_TEST_AUDIT_2026-04-14.md)Function 运行时测试补充、已确认 bug 与当前验证结果。
- [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):自定义世界创作工具当前问题、体验断层和优化优先级审计。
- [AGENT_TO_DRAFT_TO_WORLD_PIPELINE_AUDIT_2026-04-20.md](./AGENT_TO_DRAFT_TO_WORLD_PIPELINE_AUDIT_2026-04-20.md)Agent 聊天、草稿生成、作品库存储与进入世界之间的断点、多 pipeline、冗余与未实装项审计。
- [CHARACTER_ASSET_PROMPT_CHAIN_AUDIT_2026-04-20.md](./CHARACTER_ASSET_PROMPT_CHAIN_AUDIT_2026-04-20.md):角色资产默认描述文本、正式图像/动作 prompt、共享模板与保留接口的分层与冗余审计。
- [RPG_RUNTIME_DIRECT_DRAFT_PROFILE_AUDIT_2026-04-25.md](./RPG_RUNTIME_DIRECT_DRAFT_PROFILE_AUDIT_2026-04-25.md)RPG 运行时进入世界时改为直读 Agent session 草稿 profile 的链路检查。
- [RPG_WORLD_DRAFT_EDIT_AUTOSAVE_OVERRIDE_AUDIT_2026-04-28.md](./RPG_WORLD_DRAFT_EDIT_AUTOSAVE_OVERRIDE_AUDIT_2026-04-28.md)RPG 世界草稿结果页编辑后被旧设定覆盖的前端本地态、session 真相源与自动保存链路审计。
- [VN11_NEGATIVE_SCAN_REPORT_2026-05-07.md](./VN11_NEGATIVE_SCAN_REPORT_2026-05-07.md):视觉小说 VN-11 回放删除与外部平台功能误入负向扫描报告。
- [VN12_FULL_CHAIN_ACCEPTANCE_REPORT_2026-05-07.md](./VN12_FULL_CHAIN_ACCEPTANCE_REPORT_2026-05-07.md):视觉小说 VN-12 全链路联调与自动化验收报告。
- [engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md](./engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md)RPG 前端脚本中仍应迁到 `server-rs` / SpacetimeDB 的开局、快照、story engine、战斗、NPC/背包规则与创作残留后门审计。
- [engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_COMPLETION_CHECK_2026-04-28.md](./engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_COMPLETION_CHECK_2026-04-28.md)RPG 前端脚本后端迁移完成度复核标明开局、快照、story engine / prompt context、`camp_travel_home_scene`、战斗、NPC、背包/锻造、结果页保存 normalize 与角色资产 prompt 主链均已收口。
- [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):未引用垃圾、旧入口残留、前后端双份真相与后端迁移项的专项审计。
## 推荐使用方式
1. 先读系列总览,确认“最新结论在哪一份”。
2. 再按需要进入具体日期文档,查看当时的证据和上下文。
3. 做方案设计前,优先把对应审计文档看完,避免重复踩已知问题。

View File

@@ -1,48 +0,0 @@
# RPG 复活后继续冒险链路修复记录 2026-04-28
## 问题现象
RPG 运行态里,角色在战斗死亡并复活后,面板会显示一个“继续前进”入口。
此前这一步只有 `story_continue_adventure` 控制项,没有同步挂出复活后首场景应该展示的 `deferredOptions`。因此玩家点击继续后,系统会把它当作一次普通剧情续推入口,而不是单纯展示“复活后的下一批可选动作”。
在带有场景章节主 NPC 的自定义世界里,这会让玩家看起来像是“刚复活就直接和对面主 NPC 聊天”,造成复活后第一拍体验被主 NPC 对话链抢走。
## 根因结论
根因不在 NPC 聊天函数本身,而在死亡复活链没有沿用 `story_continue_adventure -> deferredOptions` 的延迟展示协议。
- 战后胜利链已经使用 `story_continue_adventure + deferredOptions`
- 死亡复活链此前只保留了 `story_continue_adventure`
- 结果是“继续前进”点击后无法走纯展示分支,只能落回普通续推
## 本次修复
本次在 `src/hooks/rpg-runtime-story/postBattleFlow.ts` 与复活调用链中补齐:
- `buildDeathStory(...)` 现在支持在复活文案上同步挂出 `deferredOptions`
- 这些 `deferredOptions` 复用 `buildFallbackStoryForState(...)` 产出的复活后可用入口
- 点击“继续前进”时只揭示这些入口,不再额外触发一次普通剧情推演
## 当前行为规则
角色死亡复活后:
1. 先显示“你在战斗中倒下,随后重新醒来”
2. 面板只展示一个 `story_continue_adventure`
3. 点击后展示复活后首场景已有的后续动作
4. 不应直接自动推进到主 NPC 聊天执行态
## 回归覆盖
已补两条测试:
- `src/hooks/rpg-runtime-story/choiceActions.test.ts`
- 覆盖本地战斗失败后的复活链
- `src/hooks/rpg-runtime-story/storyChoiceRuntime.test.ts`
- 覆盖服务端战斗失败后的复活链
两条测试都要求复活文案返回:
- `story_continue_adventure`
- 非空 `deferredOptions`

View File

@@ -1,29 +0,0 @@
# RPG 运行时直读世界草稿 Profile 检查 2026-04-25
## 结论
RPG 运行时进入游戏时不应再通过 `resultPreview.preview` 或 legacy runtime profile 做中间转换,主数据源统一为 Agent session 的 `draftProfile`
本次检查确认:
1. Rust 侧 `custom_world_foundation_draft` 已直接产出 `draftProfile`
2. 前端原先 `buildCustomWorldProfileFromAgentSession()` 仍只读取 `session.resultPreview.preview`,这会绕过草稿 profile 中已经存在的角色形象、关系、压力等字段。
3. 角色选择页与游戏内角色本身可以消费 `CustomWorldProfile.playableNpcs[].imageSrc`断点在“session -> profile”的入口而不是角色选择页。
4. “进入世界”按钮原先还会先执行 `sync_result_profile`,把当前结果页旧快照再同步回 session如果结果页 profile 没有最新角色图,会在进入角色选择页前覆盖掉 `draftProfile` 中的正确形象。
## 已修正
- `buildCustomWorldProfileFromAgentSession()` 改为直接归一化 `session.draftProfile`
- `resultPreview` 只保留为发布质量、blocker、预览外壳信息不再作为进入游戏 profile 的数据源。
- Agent 草稿结果进入游戏时直接使用最新 `agentSessionProfile`,不再把当前结果页 profile 回写成新的运行时 profile。
- 前端 `normalizeCustomWorldProfileRecord()` 补齐 rs 草稿角色字段兼容:
- `publicMask/publicIdentity` -> `description/visualDescription/personality` fallback
- `currentPressure/hiddenHook` -> `backstory/actionDescription/sceneVisualDescription` fallback
- `relationToPlayer` -> `motivation/relationshipHooks` fallback
- `imageSrc/generatedVisualAssetId/generatedAnimationSetId/animationMap` 保持直通
## 后续约束
- 新 RPG 运行时链路只允许读取 `draftProfile`
- 不再为进入游戏构造额外 legacy profile也不再把 `resultPreview.preview` 当作运行时真相源。
- 如果草稿中新增角色、场景、物品字段,应优先扩展 `draftProfile` 的归一化读取,而不是增加中间转换结构。

View File

@@ -1,233 +0,0 @@
# RPG 世界草稿编辑后被旧设定覆盖问题审计 2026-04-28
## 1. 问题摘要
当前 RPG 世界草稿结果页存在一条明显的“本地编辑态与 Agent session 真相源分叉”问题链:
1. 用户在结果页编辑世界设定时,前端只更新本地 `generatedCustomWorldProfile`
2. Agent 草稿会话里的 `draftProfile / resultPreview.preview` 并不会随着这次编辑同步更新。
3. 自动保存触发时,系统又会优先重新拉取 session 最新快照,并以 session 返回的 profile 作为最终保存内容。
4. 如果 session 里仍是编辑前旧设定,那么前端刚刚改过的内容就会被旧快照覆盖回来。
所以,这个问题表面看起来像“自动保存覆盖了我刚保存的内容”,本质上是:
- 结果页编辑写到了前端内存态;
- 自动保存落库前又改为优先相信 session 真相;
- 但 session 真相本身没有承接这次编辑。
## 2. 当前主链证据
### 2.1 结果页编辑只改前端本地 profile
`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx:2637-2640`
- `RpgCreationResultView``onProfileChange` 只调用:
- `sessionController.setGeneratedCustomWorldProfile(normalizeAgentBackedProfile(profile))`
- 这里没有触发任何 `update_draft_card``sync_result_profile` 或其它后端写回动作。
这意味着结果页里的“保存修改”目前只是更新前端内存中的 `generatedCustomWorldProfile`
### 2.2 结果页展示数据优先来自 session 预览
`src/services/rpg-creation/rpgCreationPreviewAdapter.ts:27-33`
- `buildCustomWorldProfileFromAgentSession()` 当前优先读取:
- `session.resultPreview.preview`
- 若没有,再回退到:
- `session.draftProfile.legacyResultProfile`
也就是说Agent 结果页最终重新打开、重新同步或重新计算时,主数据源仍是 session 快照,而不是用户刚改过的前端本地对象。
### 2.3 自动保存前会先刷新 session并优先保存 session 返回的最新结果
`src/components/rpg-entry/useRpgCreationResultAutosave.ts:181-236`
- `syncAgentDraftResultProfile()` 在 session 与前端 profile 签名不一致时,不再把前端 profile 回写 session。
- 它只会执行:
- `syncAgentSessionSnapshot(activeAgentSessionId)`
- 然后把拉回来的 session profile 重新塞回:
- `setGeneratedCustomWorldProfile(latestProfile)`
`src/components/rpg-entry/useRpgCreationResultAutosave.ts:326-340`
- 自动保存延迟触发后,如果当前是 Agent 草稿结果页:
1. 先调用 `syncAgentDraftResultProfile(profileToSave)`
2. 再把 `syncedResult.profile ?? profileToSave` 作为最终要入库的 profile
也就是:
- 自动保存不是直接保存用户刚改的前端 profile
- 它会先“向 session 对齐”;
- 对齐后优先保存 session 返回的新 profile。
如果 session 里还是旧设定,那么保存和界面都会一起回滚到旧设定。
## 3. 当前测试口径也在强化这条行为
### 3.1 单测明确要求“不触发 sync_result_profile只保存 session 最新草稿”
`src/components/rpg-entry/useRpgEntryAgentDraftRestore.test.tsx:195-276`
这条测试验证的是:
1. 前端当前持有的是 `oldProfile`
2. `syncAgentSessionSnapshot()` 返回的是 `latestSession`
3. 自动保存最终必须保存 `latestSession.draftProfile`
4. 并且明确断言不应触发 `sync_result_profile`
这说明现有实现不是偶然漏掉了 session 写回,而是被当前测试和实现共同锁定为:
- “自动保存只刷新 session不回写前端结果页编辑态”
### 3.2 交互测试也要求“结果页自动保存优先保存 session 最新快照”
`src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx:2867-3006`
这条测试进一步验证:
1. 结果页最终保存的对象应来自 `syncedSession.resultPreview.preview`
2. 不应触发 `sync_result_profile`
所以现在的覆盖行为不是单点 bug而是当前结果页保存架构的直接产物。
## 4. 系统层面的根因拆解
### 4.1 双真相源并存
当前至少同时存在两份“看起来都像真相”的数据:
1. 前端结果页本地 `generatedCustomWorldProfile`
2. Agent session 内的 `draftProfile / resultPreview.preview`
结果页编辑写前者,自动保存与恢复又优先读后者,所以只要两边没有同事务同步,就一定会出现覆盖风险。
### 4.2 编辑动作没有接入正式后端写回动作
仓库契约和 Rust 模块实际上已经存在两条正式动作:
- `update_draft_card`
- `sync_result_profile`
证据:
- `packages/shared/src/contracts/rpgAgentActions.ts`
- `server-rs/crates/spacetime-module/src/custom_world/mod.rs`
其中:
- `update_draft_card` 适合基于卡片/section 的结构化修改写回 `draftProfile`
- `sync_result_profile` 适合把结果页完整 profile 回写 session 并重建 preview
但当前结果页 `onProfileChange` 没有接入这两条正式链路,导致“编辑成功”只停留在本地内存态。
### 4.3 自动保存策略与编辑链路不一致
自动保存的当前设计目标是正确的:
- 不再轻易把旧前端快照反向污染 session
- 优先保存 session 最新快照,避免把陈旧 preview 写进作品库
但前提应该是:
- 结果页编辑本身已经先进入 session 真相源
现在前提不成立,于是自动保存的“防旧快照污染”策略,反过来变成了“用 session 旧值覆盖用户刚编辑的新值”。
### 4.4 结果页定位混乱
当前结果页同时承担了两种角色:
1. 预览/发布页
2. 可深度编辑的草稿编辑器
但现有数据链路更偏向第 1 种:
- 数据来自 session preview
- 自动保存优先对齐 session preview
而 UI 行为却提供了第 2 种能力:
- 用户可以直接改世界、角色、场景等内容
两种定位没有统一,最终表现就是“能编辑,但编辑不能稳定进入主真相源”。
## 5. 用户视角下的实际故障表现
从用户体感看,当前问题会表现为以下几种:
1. 刚改完字段,短暂显示新内容,随后自动回弹为旧内容。
2. 页面提示“已自动保存”,但重新进入草稿页后仍然是旧设定。
3. 某些字段改了能停留一会儿,触发自动保存或 session 刷新后又被覆盖。
4. 用户会误以为“保存按钮坏了”或“自动保存有缓存问题”,但真实原因是保存目标和展示真相源不一致。
## 6. 影响范围
这个问题不只影响“世界简介文本”,而是整个结果页编辑体系:
1. 世界基础信息编辑
2. 角色编辑
3. 场景编辑
4. 封面/派生内容依赖当前 profile 的场景
5. 结果页返回、恢复、自动打开、发布前预览一致性
只要编辑发生在 `generatedCustomWorldProfile`,但没有进入 session 真相源,就都有同类风险。
## 7. 建议修复方向
### 7.1 第一优先级:统一结果页编辑的唯一真相源
推荐二选一,不要继续混用:
1. 结果页只做预览,不允许直接编辑复杂设定
2. 结果页继续允许编辑,但每次保存必须先落到 session 真相源,再更新本地显示
按当前产品形态,更合理的是第 2 条。
### 7.2 如果保留结果页编辑,建议采用的正式链路
1. 世界/角色/场景编辑保存时,优先调用 Rust/SpacetimeDB 的正式动作写回 session。
2. 能用 `update_draft_card` 的地方尽量走结构化 section 更新。
3. 对无法被 card section 覆盖的完整 profile 级编辑,再评估是否保留 `sync_result_profile`,或新增更细粒度 reducer。
4. session 写回完成后,再重新读取 session 并刷新结果页。
5. 自动保存只负责“把已经写进 session 的最新真相同步到作品库”,不要再承担“猜测该保存前端还是 session”的职责。
### 7.3 自动保存策略需要降级为“作品库存档层”不要兼任“session 真相协调层”
当前自动保存同时承担:
1. 防止旧前端快照污染 session
2. 选择最终保存哪份 profile
3. 刷新结果页显示
职责太重,也导致覆盖问题难以定位。
建议改为:
1. 编辑保存阶段:负责写 session
2. 自动保存阶段:只负责把 session 已确认真相落作品库
3. 结果页渲染阶段:只读 session 最新快照
### 7.4 需要补的测试口径
当前测试主要在保护“不要再触发 `sync_result_profile` 污染 session”。
后续修复后,至少要补以下测试:
1. 用户编辑世界基础字段后session `draftProfile / resultPreview` 会同步更新。
2. 自动保存不会把 session 旧值覆盖到刚编辑的新值上。
3. 刷新页面或重新进入结果页后,看到的是编辑后的新设定。
4. 进入世界、发布世界、继续扩展时,消费的是同一份最新 session 真相。
## 8. 结论
这次问题的核心结论是:
1. 你的判断“像是自动保存问题”是对的,但更准确地说,是“自动保存对齐 session 时,覆盖了未写回 session 的本地编辑态”。
2. 当前结果页编辑没有接上正式的 session 写回链路,这是第一根断点。
3. 当前自动保存被设计成优先信任 session 最新快照,这是第二根断点。
4. 两个断点叠加后,就形成了“编辑后又自动变回原设定”的现象。
如果后续要真正修掉这个问题,重点不该是单独调 debounce 时间或加一个“防抖保存中”提示,而是:
- 把结果页编辑动作重新接回 Rust/SpacetimeDB 的 session 真相源;
- 让自动保存只负责作品库存档,不再替代编辑写回链路做裁决。

View File

@@ -1,377 +0,0 @@
# 安全漏洞扫描报告 2026-05-11
## 扫描范围
- 工作区:`C:/proj/Genarrative/.worktrees/hermes-3337436a`
- 分支:`hermes/hermes-3337436a`
- Git 基线:扫描时存在一个未跟踪计划文件 `.hermes/plans/2026-05-11_205658-security-vulnerability-scan.md`
- 扫描对象:根 Node/Vite/React 依赖、`server-rs` Rust workspace 依赖入口、仓库已跟踪文件中的敏感配置、JS/TS/Rust 源码安全热点。
## 扫描命令与工具状态
已执行:
```bash
pwd
git branch --show-current
git rev-parse --show-toplevel
git status --short
node --version
npm --version
cargo --version
rustc --version
rg --version
npm audit --json
npm audit --audit-level=moderate
git ls-files -z | xargs -0 grep -nIE "(api[_-]?key|secret|password|passwd|token|private[_-]?key|BEGIN (RSA|OPENSSH|EC|DSA)? ?PRIVATE KEY|AKIA[0-9A-Z]{16}|xox[baprs]-|sk-[A-Za-z0-9_-]{20,})"
rg -n "\beval\(|new Function\(|dangerouslySetInnerHTML|innerHTML\s*=|document\.write\(" src apps scripts packages
rg -n "exec\(|execSync\(|spawn\(|spawnSync\(|shell:\s*true|child_process" scripts src apps packages
rg -n "Command::new|std::process|\.unwrap\(|\.expect\(|fs::|File::open|PathBuf|set_header|cors|CorsLayer" server-rs/crates
rg -n "allow_origin|Any|cookie|Authorization|Bearer|refresh|access_token|set_cookie|SameSite|Secure|HttpOnly" server-rs/crates src apps scripts
```
工具版本:
- Node`v22.22.2`
- npm`10.9.7`
- Cargo`cargo 1.95.0 (f2d3ce0bd 2026-03-21)`
- Rustc`rustc 1.95.0 (59807616e 2026-04-14)`
- ripgrep`15.1.0`
- `gitleaks`:未安装,本次未执行。
- `cargo-audit`:未安装,本次未执行;未擅自安装到用户环境。
原始扫描输出保存于:
- `.hermes/plans/assets/security-scan-2026-05-11/npm-audit.json`
- `.hermes/plans/assets/security-scan-2026-05-11/npm-audit.txt`
- `.hermes/plans/assets/security-scan-2026-05-11/secret-grep.txt`
- `.hermes/plans/assets/security-scan-2026-05-11/js-xss-dynamic.txt`
- `.hermes/plans/assets/security-scan-2026-05-11/node-command-exec.txt`
- `.hermes/plans/assets/security-scan-2026-05-11/rust-hotspots.txt`
- `.hermes/plans/assets/security-scan-2026-05-11/auth-cors-hotspots.txt`
注意:`secret-grep.txt` 可能包含敏感片段,提交前应删除或改为脱敏摘要,不建议直接进入 Git。
## 摘要
| 等级 | 数量 | 说明 |
| --- | ---: | --- |
| Critical | 1 | `.env.local` 被 Git 跟踪且含多项非空真实密钥/凭据形态配置。 |
| High | 2 | npm 依赖存在 8 个 high advisory 聚合项Vite dev server 任意文件读取类漏洞需要优先升级。另有 TypeScript ESLint 链路 ReDoS 风险。 |
| Medium | 2 | esbuild dev server 请求读取、PostCSS CSS stringify XSS。 |
| Low | 3 | jsdom/http-proxy-agent/@tootallnate/once 低危链路。 |
| Unknown | 1 | Rust 依赖漏洞未完成扫描,因为本机未安装 `cargo-audit`。 |
| Informational | 多项 | 源码热点扫描命中大量测试/脚本/unwrap/expect需要按入口人工复核。 |
## Critical
### C-1仓库跟踪了 `.env.local`,且包含多项非空真实密钥形态配置
**证据:**
`git ls-files --error-unmatch .env.local` 显示 `.env.local` 已被 Git 跟踪。扫描确认该文件包含多项非空密钥/凭据形态变量,包括但不限于:
- `LLM_API_KEY`
- `ARK_API_KEY`
- `ARK_CHARACTER_VIDEO_API_KEY`
- `DASHSCOPE_API_KEY`
- `VOLCENGINE_ACCESS_KEY_ID`
- `VOLCENGINE_SECRET_ACCESS_KEY`
- `ALIYUN_SMS_ACCESS_KEY_ID`
- `ALIYUN_SMS_ACCESS_KEY_SECRET`
- `GENARRATIVE_LLM_API_KEY`
- `ALIYUN_OSS_ACCESS_KEY_ID`
- `ALIYUN_OSS_ACCESS_KEY_SECRET`
- `GENARRATIVE_ADMIN_PASSWORD`
报告中不记录具体值。
**影响:**
如果该文件已进入远端仓库或被团队成员拉取相关外部服务密钥、OSS/SMS/LLM/后台密码均应视为已泄露。即使后续从当前工作树删除,也不能撤销历史泄露风险。
**建议修复:**
1. 立即轮换 `.env.local` 中出现过的所有真实密钥、访问密钥、后台密码和 token。
2. 从 Git 跟踪中移除 `.env.local`,但不要删除本地私有文件:
```bash
git rm --cached .env.local
```
3. 按项目约束,不要在 `.gitignore` 中新增 `.env.local`;如果仓库已有其他机制管理本地私密 env应遵循既有约定。若没有应先补一份安全说明文档而不是提交真实 `.env.local`。
4. 将必要的占位示例保留在 `.env.example` 或 `deploy/env/api-server.env.example`,确保示例值不是可用密钥。
5. 如该文件已推送到远端历史,评估是否需要历史清理;无论是否清历史,密钥轮换都是必须步骤。
**验证:**
```bash
git ls-files --error-unmatch .env.local
# 预期:返回非 0表示不再跟踪
git diff --cached -- .env.local
# 预期:只显示从索引移除,不输出真实值到公开报告
```
## High
### H-1Vite 依赖存在高危 dev server 任意文件读取/路径遍历类 advisory
**证据:**
`npm audit` 显示:
- package`vite`
- direct dependency
- installed vulnerable range`<=6.4.1`
- severity`high`
- 相关 advisory 包括:
- `GHSA-p9ff-h696-f583`Vite dev server WebSocket 任意文件读取,高危。
- `GHSA-4w7w-66w2-5vf9`optimized deps `.map` 处理路径遍历,中危。
- 多个 `server.fs` / public directory 相关低中危问题。
- `fixAvailable=true`。
**影响:**
主要影响开发服务器和预览环境。如果开发机、测试机或内网联调环境将 Vite dev server 暴露给不可信网络,攻击者可能读取工作区文件或旁路 `server.fs` 限制。
**建议修复:**
1. 优先将 `vite` 升级到 npm audit 推荐的安全版本范围。
2. 升级后执行:
```bash
npm run check:encoding
npm run lint:eslint
npm run typecheck
npm run test
npm run build
```
3. 检查 `scripts/vite-cli.mjs`、`scripts/dev-web-rust.mjs`、Vite 配置中的 dev server host 暴露范围,开发环境避免绑定 `0.0.0.0` 或暴露到公网。
### H-2`@typescript-eslint/*` 链路经 `minimatch` 存在 ReDoS 高危 advisory
**证据:**
`npm audit` 显示:
- direct packages
- `@typescript-eslint/eslint-plugin`,当前范围 `6.16.0 - 7.5.0`high。
- `@typescript-eslint/parser`,当前范围 `6.16.0 - 7.5.0`high。
- transitive packages
- `@typescript-eslint/type-utils`
- `@typescript-eslint/typescript-estree`
- `@typescript-eslint/utils`
- `minimatch`
- `minimatch` advisory
- `GHSA-3ppc-4f35-3m26`
- `GHSA-7r86-cg39-jmmj`
- `GHSA-23c5-xmqv-rm74`
- npm 建议升级到 `@typescript-eslint/* 8.59.3`,属于 SemVer major。
**影响:**
主要影响 lint/构建工具链。如果 CI 或开发命令处理不可信 glob pattern可能造成 ReDoS。生产运行时直接影响较低但 CI 可用性和供应链安全仍应修复。
**建议修复:**
1. 单独开依赖升级分支,将 `@typescript-eslint/eslint-plugin` 与 `@typescript-eslint/parser` 升级到兼容 ESLint 8/TypeScript 5.8 的安全版本。
2. 因为是 major 升级,先阅读迁移说明并运行 ESLint 全量检查。
3. 验证:
```bash
npm run lint:eslint
npm run typecheck
npm run test
```
### H-3`picomatch` ReDoS / glob matching 高危 advisory
**证据:**
`npm audit` 显示:
- package`picomatch`
- severity`high`
- vulnerable range`4.0.0 - 4.0.3`
- advisory
- `GHSA-c2c7-rcm5-vvqj`extglob quantifiers ReDoS高危。
- `GHSA-3v7f-55p6-f55p`POSIX character classes method injection中危。
- `fixAvailable=true`。
**影响:**
主要影响依赖 picomatch 的构建、测试、文件匹配工具链。生产直接影响取决于是否在服务端运行时用它处理用户输入 glob当前未在扫描摘要中发现明显业务入口直接使用。
**建议修复:**
通过升级引入它的 direct dependency 来消除,不建议手工改 lockfile。
## Medium
### M-1`esbuild <=0.24.2` dev server 允许任意网站请求并读取响应
**证据:**
`npm audit` 显示:
- package`esbuild`
- severity`moderate`
- advisory`GHSA-67mh-4wv8-2f99`
- vulnerable range`<=0.24.2`
- `fixAvailable=true`。
**影响:**
主要影响开发服务器场景。若本地开发服务暴露到不可信网络,风险上升。
**建议修复:**
随 Vite / 构建链路升级一并修复,升级后跑前端检查与构建。
### M-2`postcss <8.5.10` CSS stringify XSS advisory
**证据:**
`npm audit` 显示:
- package`postcss`
- severity`moderate`
- advisory`GHSA-qx2v-qp2m-jg93`
- vulnerable range`<8.5.10`
- `fixAvailable=true`。
**影响:**
如果系统把不可信 CSS 内容 stringify 后注入页面,可能触发 XSS。当前项目是否存在这类业务入口需人工复核从依赖角度建议升级。
**建议修复:**
升级 Tailwind/Vite/PostCSS 链路带出的安全版本,并执行前端构建验证。
## Low
### L-1`jsdom` 链路低危 advisory
**证据:**
`npm audit` 显示:
- `jsdom` direct dependencyseverity low。
- transitive`http-proxy-agent`、`@tootallnate/once`。
- npm 建议升级到 `jsdom 29.1.1`SemVer major。
**影响:**
通常影响测试环境。若测试工具处理不可信 URL/代理输入,风险上升。
**建议修复:**
不要和 Vite/TypeScript ESLint 大升级混在一个提交里。单独升级 jsdom 后运行:
```bash
npm run test
```
## Unknown / 未完成项
### U-1Rust 依赖漏洞未完成扫描
**原因:**
本机没有 `cargo-audit`,本次没有擅自安装用户级 Cargo 工具。
**建议:**
如确认允许安装:
```bash
cargo install cargo-audit --locked
cargo audit --manifest-path server-rs/Cargo.toml
```
或在 CI/具备工具的环境执行并回填结果。
## Informational / 源码热点
### I-1JS/TS XSS / 动态执行热点
扫描命中 1 行:
- `src/routing/RouteImageReadyGate.test.ts` 中测试代码使用 `root.innerHTML`。
初步判断为测试环境构造 DOM不是生产漏洞。若后续发现生产代码使用 `dangerouslySetInnerHTML` 或直接 `innerHTML = userInput`,应升级为 High。
### I-2Node 脚本命令执行热点
扫描命中 21 行,主要集中在:
- `scripts/spacetime-migration-common.mjs`
- `scripts/run-bash-script.mjs`
- `scripts/generate-spacetime-bindings.mjs`
- `scripts/dev-web-rust.mjs`
初步判断为项目脚本启动 `spacetime`、bash、Vite 等工具的正常行为。后续人工复核重点:
- 是否使用固定命令和参数数组,而不是拼接 shell 字符串。
- 是否把用户输入直接作为命令或 shell 参数。
- 是否设置 `shell: true`。
### I-3Rust unwrap/expect、文件路径、CORS/Auth 热点较多
扫描命中:
- `rust-hotspots.txt`1348 行。
- `auth-cors-hotspots.txt`1157 行。
这些是热点,不等于漏洞。建议后续按模块分批人工复核:
1. `server-rs/crates/api-server/src/admin.rs`
2. `server-rs/crates/api-server/src/app.rs`
3. `server-rs/crates/platform-auth/src/**`
4. `server-rs/crates/platform-oss/src/**`
5. `server-rs/crates/platform-llm/src/**`
6. `server-rs/crates/spacetime-client/src/**`
重点看:生产 CORS、Cookie 安全属性、token 日志、路径拼接、外部 URL 下载、Data URL 大小限制、OSS 签名边界。
## 推荐修复顺序
1. 立即处理 C-1轮换 `.env.local` 里所有真实密钥,并从 Git 索引移除 `.env.local`。
2. 升级 `vite` 相关依赖,优先消除 dev server 任意文件读取/路径遍历 advisory。
3. 升级 `@typescript-eslint/*`,消除 minimatch 链路 ReDoS因 major 升级,单独提交。
4. 升级 `postcss` / `esbuild` / `picomatch` 的来源依赖。
5. 单独评估 `jsdom` major 升级。
6. 用户确认后安装或使用 CI 执行 `cargo audit`,补齐 Rust 依赖漏洞结论。
7. 对 `auth-cors-hotspots.txt` 和 `rust-hotspots.txt` 做模块级人工审计。
## 修复后的验证命令
```bash
npm run check:encoding
npm run lint:eslint
npm run typecheck
npm run test
npm run build
```
如修改后端安全、Auth、Cookie、CORS 或 API
```bash
cd server-rs && cargo test --workspace
npm run api-server
# 检查 /healthz并执行相关 API/auth smoke
```
如补齐 Rust audit
```bash
cargo audit --manifest-path server-rs/Cargo.toml
```
## 备注
- 本报告没有输出任何真实密钥值。
- `.hermes/plans/assets/security-scan-2026-05-11/secret-grep.txt` 可能包含敏感内容,仅用于本地排查;提交前应删除或替换为脱敏报告。
- 由于 `gitleaks` 未安装,本次密钥扫描只是 grep 兜底,不等价于完整 secrets audit。

View File

@@ -1,32 +0,0 @@
# VN-11 负向扫描报告
生成日期2026-05-07
## 扫描范围
- 工程代码:`src/``packages/shared/src/``server-rs/crates/`
- 文档与共享记忆:`docs/``.hermes/shared-memory/`
- 外部平台误入复核视觉小说前端、service、shared contracts、Rust contracts、module、api-server、SpacetimeDB schema 与 facade 路径
## 扫描结论
- 工程代码回放类直出命中0
- 文档 / 共享记忆回放类命中222
- 视觉小说实现路径外部平台能力疑似误入命中0
## 处理记录
- 已将 `storyEngine` 回归工具的命名从 replay 语义收口为 rerun / 复测语义。
- 已将技能效果预览按钮的内部状态与文案从重播语义收口为重新预览语义。
- 已确认视觉小说工程路径未新增回放路由、DTO、表、按钮、文案、外部平台账号 / 订单 / 会员 / 促销 / 后台 / 公开市场或私有存档能力。
## 文档命中说明
- 文档命中来自历史旧文档、设计复盘、禁止语境、负向验收或本报告记录。VN-11 工程门禁只阻断代码路径新增能力。
## 门禁命令
```bash
npm run check:visual-novel-vn11
```

View File

@@ -1,92 +0,0 @@
# VN-12 全链路联调与自动化验收报告
生成日期2026-05-07
## 结论
- 状态:通过
- 失败项0
- 收口说明VN-12 本次只补验收门禁、关键路径测试和报告记录,未扩展新玩法功能。
## 自动化验收清单
- docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md
- docs/audits/VN11_NEGATIVE_SCAN_REPORT_2026-05-07.md
- src/components/visual-novel-creation/VisualNovelAgentWorkspace.test.tsx
- src/components/visual-novel-result/VisualNovelResultView.test.tsx
- src/components/visual-novel-runtime/VisualNovelRuntimeShell.test.tsx
- src/services/visual-novel-runtime/visualNovelRuntimeClient.test.ts
- src/services/visual-novel-runtime/visualNovelRuntimeSse.test.ts
- server-rs/crates/api-server/src/visual_novel.rs
- server-rs/crates/module-visual-novel/src/application.rs
- server-rs/crates/shared-contracts/src/visual_novel.rs
- package.json
- server-rs/crates/api-server/src/app.rs
- src/services/visual-novel-runtime/visualNovelRuntimeClient.ts
- src/services/visual-novel-runtime/visualNovelRuntimeClient.test.ts
- src/services/visual-novel-runtime/visualNovelRuntimeSse.test.ts
- src/components/visual-novel-creation/VisualNovelAgentWorkspace.test.tsx
- src/components/visual-novel-result/VisualNovelResultView.test.tsx
- src/components/visual-novel-runtime/VisualNovelRuntimeShell.test.tsx
## API smoke
- `/api/creation/visual-novel/sessions`
- `/api/creation/visual-novel/works`
- `/api/runtime/visual-novel/gallery`
- `/api/runtime/visual-novel/works/{profile_id}/runs`
- `/api/runtime/visual-novel/runs/{run_id}/actions/stream`
- `/api/runtime/visual-novel/runs/{run_id}/history`
- `/api/runtime/visual-novel/runs/{run_id}/regenerate`
- `/api/profile/save-archives`
- `/api/profile/save-archives/{world_key}`
- `/api/runtime/save/snapshot`
本次实测:
- `npm run api-server` 可启动 Rust `api-server`
- `GET http://127.0.0.1:3100/healthz` 返回 `200`,响应为 `{"ok":true,"service":"genarrative-api-server"}`
- `GET /api/runtime/visual-novel/gallery` 在当前本地环境返回超时 / `502`,日志显示 `api-server` 连接 `127.0.0.1:3101` SpacetimeDB 数据库 `xushi-p4wfr` 被拒绝;该项按本地 SpacetimeDB 未完整就绪记录为环境阻塞,不新增工程实现。
## 前端关键路径
- 创作工作台:`VisualNovelAgentWorkspace`
- 结果页:`VisualNovelResultView`
- 运行时:`VisualNovelRuntimeShell`
- 运行时 SSE`visualNovelRuntimeSse` / `visualNovelRuntimeClient`
## 桌面 / 移动端检查
- 桌面端:已用 Edge headless 截取 `/creation/visual-novel/agent`,文件为 `docs/audits/VN12_VISUAL_NOVEL_DESKTOP_2026-05-07.png`
- 移动端:已用 Edge headless 截取 `/creation/visual-novel/agent`,文件为 `docs/audits/VN12_VISUAL_NOVEL_MOBILE_2026-05-07.png`
- in-app browser 插件本次未发现可用 IAB backend截图使用本机 Edge headless 兜底完成。
## 校验摘要
- package.json scripts: 通过
- api-server visual novel routes: 通过
- visual novel runtime client routes: 通过
- visual novel runtime client tests: 通过
- visual novel SSE tests: 通过
- visual novel creation tests: 通过
- visual novel result tests: 通过
- visual novel runtime tests: 通过
## 执行命令
```bash
npm run check:visual-novel-vn12 -- --write-report
npm run test -- src/components/visual-novel-creation/VisualNovelAgentWorkspace.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx src/components/visual-novel-runtime/VisualNovelRuntimeShell.test.tsx src/services/visual-novel-runtime/visualNovelRuntimeClient.test.ts src/services/visual-novel-runtime/visualNovelRuntimeSse.test.ts
npm run check:encoding
npm run typecheck
cd server-rs
cargo test -p shared-contracts
cargo test -p module-visual-novel
cargo check -p api-server
```
## 未覆盖风险
- 当前本地 SpacetimeDB 连接未完整就绪,公开 gallery API 的真实数据返回未在本次环境完成;`/healthz` 与编译 / 单测已通过。
- 若接口路由或测试名称后续调整,需要同步更新本门禁脚本与报告模板。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -1,369 +0,0 @@
# 当前工程优化点盘点2026-04-20
更新时间:`2026-04-20`
## 0. 盘点目标
这份文档用于回答一个更直接的问题:
**基于当前仓库状态,接下来最值得投入工程时间的优化点是什么。**
本轮只做文档盘点,不直接修改业务代码;结论同时参考了当前工作区现状。
需要注意,仓库当前存在一批未提交改动,尤其集中在 `custom world``assets``platform shell` 相关模块,所以本文更强调“优先级与切入方式”,而不是要求做大范围整仓改写。
---
## 1. 当前快照
## 1.1 本轮复核方式
本轮主要复核了以下内容:
1. 现有工程优化审计文档与目录索引
2. `package.json``vite.config.ts``.eslintrc.cjs` 等门禁脚本
3. 当前前端、后端、脚本目录的大文件热点
4. 运行时、鉴权、自定义世界、资产链路的边界实现
5. 当前 `typecheck / lint / build` 状态
---
## 1.2 当前门禁结果
| 项目 | 结果 | 当前判断 |
| --- | --- | --- |
| `npm run typecheck` | 失败 | 当前第一优先级问题,类型基线已失真 |
| `npm run lint:eslint` | 失败 | `136` 个 error、`4` 个 warning`95` 个可自动修复 |
| `npm run build` | 通过 | 发布链路未红,但体积压力仍明显存在 |
### 关键说明
当前状态和 `2026-04-10` 那轮“build warning 直接拦截”的状态不同:
1. **构建现在可以通过。**
2. **真正变成第一阻塞项的是 `typecheck` 与 `lint`。**
3. **构建虽然通过但主包、功能包、CSS 体积依然偏重,说明性能类优化仍然值得做。**
---
## 1.3 当前热点文件快照
本轮按源码目录统计的大文件热点如下:
| 文件 | 当前行数 | 判断 |
| --- | --- | --- |
| `src/components/CustomWorldEntityEditorModal.tsx` | `6122` | 当前前端最大热点 |
| `server-node/src/app.test.ts` | `3568` | 后端测试聚合度过高 |
| `server-node/src/modules/assets/characterAssetRoutes.ts` | `2802` | 资产路由职责过重 |
| `src/services/ai.ts` | `2432` | 浏览器侧 AI 编排仍然偏重 |
| `server-node/src/modules/story/storyActionRoutes.test.ts` | `2402` | 运行时路由测试聚合度过高 |
| `src/data/npcInteractions.ts` | `2274` | NPC 规则数据仍然集中 |
| `src/prompts/storyPromptBuilders.ts` | `1728` | prompt 构造成为新的复杂度中心 |
| `server-node/src/modules/custom-world/runtimeProfile.ts` | `1623` | custom world runtime 编译热点 |
| `src/hooks/story/npcEncounterActions.ts` | `1582` | NPC 行动流仍然偏重 |
| `src/components/game-shell/PlatformHomeView.tsx` | `1474` | 平台首页壳层继续膨胀 |
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `1418` | 前置选择流程职责过多 |
| `src/services/customWorld.ts` | `1383` | 自定义世界服务虽然收缩,但仍偏大 |
---
## 2. 结论先行
当前仓库的优化重点,已经不是“继续清旧 Vite 插件链路”或者“继续讨论前后端是否要分离”。
更准确地说,当前最值得做的优化点已经收敛成四类:
1. **先恢复可信的工程基线。**
`typecheck``lint` 当前都是红线,继续扩功能会放大返工成本。
2. **拆掉正在持续膨胀的新热点。**
热点已经从早期运行时主链,迁移到 `custom world``asset routes``platform shell``prompt builders`
3. **继续把前端退出“运行时真相”和“鉴权真相”。**
当前前端仍保留本地快照镜像与自动登录凭证持久化。
4. **补一轮入口归档,减少疑似孤岛模块和大测试聚合文件。**
这部分不一定最急,但会持续拉低仓库可维护性。
一句话判断:
**当前最值得投入的不是横向加功能,而是把质量门禁重新拉绿,再把 custom world / asset / platform 这批新复杂度中心拆开。**
---
## 3. 优化点清单
## 3.1 P0先恢复类型基线
这是当前最优先的工程优化点。
### 证据
`npm run typecheck` 当前失败,主要问题集中在两类:
1. `CustomWorldCampScene` 结构漂移
- `src/components/CustomWorldEntityEditorModal.test.tsx`
- `src/data/customWorldLibrary.ts`
- `src/services/customWorld.ts`
- `src/services/customWorldCamp.ts`
2. 局部实现与类型定义不同步
- `src/components/auth/AccountModal.test.tsx` 的测试数据缺少新增字段
- `src/components/game-canvas/GameCanvasShared.tsx` 引用了未定义的 `DEFAULT_IMAGE_STYLE`
### 影响
1. 类型系统已经不能提供可信回归信号。
2. 自定义世界链路当前正在迭代,如果继续在红线状态叠加修改,后续会反复出现“改 A 崩 B”的情况。
3. 测试 fixture 与正式类型脱节,会让测试文件逐渐失去文档价值。
### 建议
1. 先补一个统一的 `CustomWorldCampScene` 构造/归一化入口,禁止在多个文件里手写不完整字面量。
2.`auth``custom world` 的测试 fixture 改成工厂函数,避免字段新增后多处漏改。
3. 单独清掉 `GameCanvasShared.tsx` 这类“编译即失败”的确定性问题,优先恢复 `typecheck` 绿色基线。
---
## 3.2 P0恢复 lint 可信度,区分机械问题和真实问题
这项和类型基线同级。
### 证据
`npm run lint:eslint` 当前结果是:
- `136` 个 error
- `4` 个 warning
- 其中 `95` 个问题可自动修复
当前 lint 问题明显分成两层:
1. 机械问题
- import 排序
- export 排序
- 未使用导入
2. 真实问题
- `server-node/src/modules/inventory/inventoryStoryActionService.ts` 出现 React Hook 规则错误
- `server-node/src/migrate.ts` 仍触发 `no-console`
- `packages/shared/src/http.ts` 触发 `@typescript-eslint/ban-types`
- 若干文件存在真正未使用变量、转义和规则误配问题
### 影响
1. 当前 lint 信号噪音仍然较高,不利于 review。
2. 真实问题会被大量机械问题掩盖。
3. 团队会更倾向于跳过 lint而不是信任 lint。
### 建议
1. 先跑一轮仅机械修复的清理批次,优先吃掉 import sort、unused imports 这类低风险项。
2. 再单独处理 Hook 误用、共享契约类型、脚本规则豁免这类语义问题。
3. 之后把“自动可修复问题”与“必须人工处理的问题”拆成两个门禁视角,减少下次再次堆积。
---
## 3.3 P1拆 custom world / asset / platform 新热点
这是当前最有性价比的结构性优化点。
### 证据
当前复杂度最高的业务热点,已经集中在这些模块:
1. `src/components/CustomWorldEntityEditorModal.tsx`
2. `server-node/src/modules/assets/characterAssetRoutes.ts`
3. `src/services/ai.ts`
4. `src/prompts/storyPromptBuilders.ts`
5. `server-node/src/modules/custom-world/runtimeProfile.ts`
6. `src/components/game-shell/PlatformHomeView.tsx`
7. `src/components/game-shell/PreGameSelectionFlow.tsx`
8. `src/hooks/story/npcEncounterActions.ts`
### 问题本质
这些文件并不是单纯“代码多”,而是同时承载了多类职责:
1. UI 状态
2. 领域规则
3. 请求编排
4. 文本构造
5. 运行时映射
6. 面板切换与流程控制
### 建议
1. `CustomWorldEntityEditorModal.tsx`
- 先按“实体列表/表单区/资源区/高级设置/预览区”拆组件
- 再把数据准备与提交编排抽成 hook
2. `characterAssetRoutes.ts`
- 拆成 route、prompt payload、job orchestration、产物发布、错误响应五层
3. `PlatformHomeView.tsx``PreGameSelectionFlow.tsx`
- 把页面壳层、数据加载、卡片渲染、弹层控制拆开
4. `storyPromptBuilders.ts``runtimeProfile.ts`
- 把“模板片段”“上下文归一化”“规则裁剪”“最终拼接”分层
---
## 3.4 P1继续控制构建产物体积
构建虽通过,但体积已经给出明显信号。
### 当前证据
本轮 `npm run build` 输出里,几个值得关注的点是:
1. `dist/assets/AuthenticatedApp-*.js``794.77 kB`
2. `dist/assets/index-*.js``197.44 kB`
3. `dist/assets/CustomWorldResultView-*.js``163.38 kB`
4. `dist/assets/ai-*.js``131.73 kB`
5. `dist/assets/PreGameSelectionFlow-*.js``96.39 kB`
6. `dist/assets/index-*.css``201.44 kB`
### 影响
1. 虽然还没触发新的 build gate 红线,但首屏、缓存和移动端体验会继续承压。
2. `AuthenticatedApp` 主包偏大,说明平台壳层仍然装入了过多首屏不必需能力。
3. CSS 体积继续上涨,说明样式正在跨模块相互堆叠。
### 建议
1. 继续把 custom world、asset studio、平台详情页、角色资产工具从主壳层路径中抽离。
2. 审查 `ai.ts``custom world result view``pregame selection` 是否还能再延迟加载。
3. 对全局样式做一次按模块归属清理,减少公共样式无限增长。
---
## 3.5 P1继续收紧前端与后端边界
这项已经不是“要不要做”的问题,而是“还剩多少尾巴没收完”。
### 当前证据
1. `src/services/apiClient.ts`
- 当前仍把 `access token`
- 自动登录用户名
- 自动登录密码
写入 `window.localStorage`
2. `src/hooks/story/runtimeStoryCoordinator.ts`
- 当前仍会在调用后端运行时前先 `putSaveSnapshot(...)`
- 响应后继续 `rehydrateSavedSnapshot(...)`
3. `src/hooks/story/npcEncounterActions.ts`
- 当前仍从前端动作流触发 `generateQuestForNpcEncounter(...)`
- 说明 NPC 任务“换单/重抽”分支尚未完全后端化
### 影响
1. 前端仍保留了一部分运行时真相与鉴权真相。
2. 自动登录凭证持久化在边界和安全上都不理想。
3. 运行时快照前置写入,会让“前端镜像状态”和“后端会话状态”继续纠缠。
### 建议
1. 优先移除自动登录用户名/密码本地持久化,收敛到服务端 session / refresh 机制。
2. 把运行时快照改为“展示缓存”而不是“提交前真相源”。
3. 把 NPC 任务更换动作补齐到后端 runtime/session 边界,不再由前端直接发起生成决策。
---
## 3.6 P2做一次疑似孤岛模块与旧入口归档
这项不一定最紧急,但现在做会明显降低后续维护噪音。
### 当前现象
从当前入口关系看,以下模块值得做一次正式复核:
1. `src/components/GameShell.tsx`
2. `src/components/custom-world-home/CustomWorldCreationHub.tsx`
3. `src/components/custom-world-home/CustomWorldCreationLauncherModal.tsx`
4. `src/components/custom-world-agent/CustomWorldAgentLauncherModal.tsx`
5. `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx`
6. `src/hooks/story/storyBootstrap.ts`
7. `src/hooks/useEquipmentFlow.ts`
8. `src/hooks/useForgeFlow.ts`
9. `src/hooks/useInventoryFlow.ts`
10. `src/services/typewriter.ts`
### 当前判断
这批模块不一定全部是垃圾代码,但至少说明一件事:
**仓库里仍然存在一批“不是正式入口、也没有清晰归档标签”的过渡实现。**
### 建议
把这类模块统一分成三类:
1. 正式保留并接回入口
2. 明确标记为实验稿
3. 直接归档或删除
这样可以减少后续开发时的误判成本。
---
## 3.7 P2拆测试聚合文件恢复测试的定位能力
当前测试文件也已经出现“大一统热点”。
### 证据
1. `server-node/src/app.test.ts``3568`
2. `server-node/src/modules/story/storyActionRoutes.test.ts``2402`
3. `server-node/src/modules/assets/characterAssetRoutes.test.ts``1235`
4. `src/hooks/story/npcEncounterActions.test.ts``1199`
### 影响
1. 失败定位成本高。
2. fixture 复用差,字段一变容易整片测试跟着漂移。
3. 测试文件本身开始变成新的维护热点。
### 建议
1. 按领域动作拆测试文件,而不是继续堆到单一总测文件中。
2. 补 fixture builder / factory减少字面量散落。
3.`runtime / auth / custom world / assets` 这几条链路增加更明确的契约测试分层。
---
## 4. 推荐执行顺序
如果只按工程收益排序,建议按下面的顺序推进:
1. 先修 `typecheck`
2. 再把 `lint` 分成机械修复和语义修复两轮
3. 然后拆 `custom world / asset / platform` 热点
4. 再继续收前端运行时与鉴权边界
5. 最后处理孤岛模块归档和测试拆分
---
## 5. 当前不建议优先做的事
1. 不建议在 `typecheck``lint` 仍为红线时继续横向扩功能。
2. 不建议直接在 `CustomWorldEntityEditorModal.tsx``characterAssetRoutes.ts``PlatformHomeView.tsx` 里继续堆新逻辑。
3. 不建议把 bundle 体积问题简单理解为“先放宽阈值”,当前更适合继续拆职责和延迟加载。
4. 不建议在未确认入口关系前随手删除可疑旧模块,先做归档分类更稳。
---
## 6. 本文依据
文档依据:
1. `docs/audits/engineering/README.md`
2. `docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`
3. `docs/audits/engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md`
4. `docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md`
当前仓库复核依据:
1. `package.json`
2. `.eslintrc.cjs`
3. `vite.config.ts`
4. `scripts/build-gate.mjs`
5. `src/App.tsx`
6. `src/services/apiClient.ts`
7. `src/hooks/story/runtimeStoryCoordinator.ts`
8. `src/hooks/story/npcEncounterActions.ts`
9. 当前源码大文件体量扫描结果
10. `npm run typecheck`
11. `npm run lint:eslint`
12. `npm run build`

View File

@@ -1,606 +0,0 @@
# 工程清理与后端边界审计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/README.md`
- `docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.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/README.md`
2. `docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`
3. `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

@@ -1,382 +0,0 @@
# 工程清理与后端边界复核审计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/README.md`
3. `docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.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

@@ -1,141 +0,0 @@
# 工程死分支清理执行记录 A2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
这份记录对应:
- `docs/planning/ENGINEERING_DEAD_CODE_AND_HIDDEN_BRANCH_CLEANUP_PLAN_2026-04-21.md`
- 其中的 `P0 + 批次 A`
本批次只做一件事:
**先清理高置信度、低耦合、无正式入口的小型孤岛与残留壳子。**
这批对象有一个共同特征:
1. 当前没有正式运行时引用
2. 没有当前主链计划要接回
3. 删除后有明确替代路径,或者本身只是历史占位
因此这批次不碰运行时真相链、不碰鉴权链、不碰任务物品主链,只先做低风险去噪。
---
## 1. 本批次已处理对象
## 1.1 已删除文件
| 文件 | 判定 | 删除原因 | 替代路径 / 当前真相源 | 验证口径 |
| --- | --- | --- | --- | --- |
| `src/services/customWorldPresentation.stub.ts` | 无引用占位 stub | 文件本身就是占位实现,且正式逻辑已由 `customWorldPresentation.ts` 承接 | `src/services/customWorldPresentation.ts` | 符号级检索确认正式调用方都指向正式实现 |
| `src/services/typewriter.ts` | 无引用 helper 残留 | 独立 helper 已失效,正式链路已在 `storyPresentation.ts` / `storyRenderingHelpers.ts` 等处内联或迁移 | `src/hooks/story/storyPresentation.ts``src/hooks/story/storyRenderingHelpers.ts` | `getTypewriterDelay` 调用点未指向该文件 |
| `src/prompts/customWorldOrchestratorPrompts.ts` | 前端孤岛 prompt 壳 | 当前无正式 import正式主编排 prompt 已收口到后端 prompt 目录,前端 `ai.ts` 也保留自己的现行实现 | `server-node/src/prompts/customWorldOrchestratorPrompts.ts``src/services/ai.ts` | 全仓检索仅剩文档引用,无代码消费 |
| `src/prompts/storyOrchestratorPrompts.ts` | 前端孤岛 prompt 壳 | 当前无正式 import剧情语言修复 prompt 已由后端 prompt 目录承接,前端当前执行路径不依赖该文件 | `server-node/src/prompts/storyOrchestratorPrompts.ts``src/services/ai.ts` | 全仓检索仅剩文档引用,无代码消费 |
| `src/components/custom-world-home/CustomWorldCreationLauncherModal.tsx` | 无入口 UI 壳层 | 最近两轮工程审计都确认无运行时引用,当前平台主流程未接这条入口 | 当前平台正式入口链 | 文件级检索确认无组件 import |
| `src/components/custom-world-agent/CustomWorldAgentLauncherModal.tsx` | 无入口 UI 壳层 | Agent 创作主流程已切到当前工作区链路,这个旧 modal 没有接线价值 | 当前 Agent 工作区主链 | 文件级检索确认无组件 import |
| `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx` | 无入口 UI 壳层 | 只有孤立 UI 实现,没有正式调用链,也不在当前结果页 / 工作区主链中 | 当前 Agent 工作区与结果页正式链 | 文件级检索确认无组件 import |
---
## 2. 本批次为什么先删这 7 个
这批文件适合先处理,不是因为它们最大,而是因为它们最清晰:
1. **没有正式入口。**
本轮检索没有发现主工程 import。
2. **删除后不会形成职责空洞。**
要么已有正式替代路径,要么本身只是历史占位。
3. **不会误伤当前重点链路。**
这批不涉及运行时快照、鉴权、任务、物品、AI 正式编排主链。
4. **可以最快降低目录噪音。**
先把真假并存的壳子删掉,后面做批次 B/C/D 时判断成本会更低。
---
## 3. 本批次暂不处理对象
以下对象虽然已进入首轮台账,但本批次暂不删除:
1. `src/components/GameShell.tsx`
2. `src/components/custom-world-home/CustomWorldCreationHub.tsx`
3. `src/hooks/story/storyBootstrap.ts`
4. `src/hooks/useEquipmentFlow.ts`
5. `src/hooks/useForgeFlow.ts`
6. `src/hooks/useInventoryFlow.ts`
7. `src/data/buildTagSimilarity.generated.ts`
暂缓原因分别是:
1. 仍属于旧主流程 / 旧 flow 级别对象,删除前要先核对更多历史依赖和替代路径
2. 部分对象仍有测试引用或更大的上下文耦合
3. `buildTagSimilarity.generated.ts` 虽无正式业务 import但属于生成产物处理前还要确认脚本链与文档链
这批对象更适合进入:
1. `批次 B旧 flow / 旧 shell / 旧 hook`
2. 或独立的数据产物复核批次
---
## 4. 本批次同步更新的文档
本批次除了删文件,还同步做了文档回填:
1. 新增本执行记录,说明本批删了什么、为什么删、哪些对象暂缓
2. 更新 `docs/audits/engineering/README.md`,把这份执行记录加入当前审计入口
这样做的目的,是避免再次出现:
1. 代码删了
2. 但审计入口还是旧状态
3. 后续开发又从旧清单里重复判断一遍
---
## 5. 验证方式
本批次验证采用两层口径:
## 5.1 删除前验证
1. 文件级检索确认无正式 import
2. 符号级检索确认关键导出没有被主链消费
3. 结合 `2026-04-20` 工程审计交叉确认这些对象已被标记为高置信度孤岛
## 5.2 删除后验证
建议至少执行:
1. `npm run check:encoding`
2. `npm run build`
说明:
- 当前仓库已知 `typecheck``lint` 仍处于红线阶段,因此本批不把它们作为“由本批引入的新失败”判断口径
- 本批主要验证目标是:删除小残留后,不产生新的导入断裂和构建断裂
---
## 6. 本批次结果判断
本批次完成后,工程至少获得了 3 个直接收益:
1. `src/prompts/``src/services/``src/components/custom-world-*` 中少了一批无入口孤岛
2. 当前目录里“看起来像正式入口,其实已经废弃”的误导性对象减少
3. 后续可以把精力集中到真正高价值的批次 B/C/D而不是继续被小残留分散判断成本
---
## 7. 下一批建议
建议严格按计划继续往下推进:
1. 批次 B`GameShell``storyBootstrap``useEquipmentFlow``useForgeFlow``useInventoryFlow`
2. 批次 C`runtimeStoryCoordinator``runtimeStoryService``apiClient`
3. 批次 D`npcEncounterActions``questDirector``runtimeItemAiDirector``ai.ts`
一句话总结本批次:
**先把最确定的死分支和占位壳子清掉,让主工程少一些假入口、假主源、假能力,再进入更重的主链收口。**

View File

@@ -1,145 +0,0 @@
# 工程死分支清理执行记录 B2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
这份记录对应清洗计划中的:
- `批次 B旧 flow / 旧 shell / 旧 hook`
本批次聚焦的不是小型 stub而是
**已经退出正式主流程、但仍占着高辨识度命名和旧职责心智的壳层与流程 Hook。**
这类文件如果继续留在仓库里,问题比小 helper 更大,因为它们会持续制造误判:
1. 新人会以为它们还是正式入口
2. 后续开发会误判“应该往这里接逻辑”
3. review 时会多出一层“旧主链是不是还活着”的判断成本
---
## 1. 本批次已处理对象
## 1.1 已删除文件
| 文件 | 判定 | 删除原因 | 替代路径 / 当前真相源 | 验证口径 |
| --- | --- | --- | --- | --- |
| `src/components/GameShell.tsx` | 旧主流程壳层残留 | 当前正式壳层已由 `src/components/game-shell/GameShellRuntime.tsx` 承接,旧文件无正式 import | `src/components/game-shell/GameShellRuntime.tsx``src/hooks/useGameShellRuntime.ts` | 全仓检索未发现对旧 `GameShell` 组件的正式消费 |
| `src/hooks/story/storyBootstrap.ts` | 旧启动流程 Hook 残留 | 当前主剧情启动链已不再调用该 Hook继续保留只会误导人以为它还是故事初始化入口 | 当前 story runtime / coordinator 链 | 全仓检索未发现 `useStoryBootstrap` 消费方 |
| `src/hooks/useEquipmentFlow.ts` | 旧装备流程 Hook 残留 | 当前正式背包与装备链未消费该 Hook属于旧流程实现残留 | 当前 inventory / runtime 正式链 | 符号级检索仅命中定义文件自身 |
| `src/hooks/useForgeFlow.ts` | 旧锻造流程 Hook 残留 | 当前正式锻造入口未通过该 Hook 进入主链,保留会制造旧流程错觉 | 当前 inventory / runtime 正式链 | 符号级检索仅命中定义文件自身 |
| `src/hooks/useInventoryFlow.ts` | 旧背包使用流程 Hook 残留 | 当前主流程未消费该 Hook属于旧状态推进实现残留 | 当前 inventory / runtime 正式链 | 符号级检索仅命中定义文件自身 |
---
## 2. 为什么这批要紧跟批次 A 处理
批次 A 清掉的是“小型假入口”。
批次 B 清掉的是“高辨识度旧主链”。
这批必须紧跟着做,原因是:
1. 它们虽然比 stub 更大,但引用关系同样清楚
2. 它们的误导性比小残留更强
3. 不先处理这批,后面做批次 C/D 时,很容易继续有人拿旧 flow Hook 当候选接线点
一句话讲:
**批次 A 是去噪,批次 B 是拔掉旧路牌。**
---
## 3. 本批次删除后的结构变化
本批次完成后,仓库里的流程心智会更清楚:
1. 游戏壳层正式入口继续收敛到 `src/components/game-shell/**`
2.`GameShell.tsx` 不再和 `GameShellRuntime.tsx` 并存
3. 旧的装备 / 锻造 / 背包单独 flow Hook 不再伪装成还在生效的正式实现
4.`storyBootstrap` 不再和当前 story runtime 链并存
这会直接减少两类误判:
1. “是不是还有旧主流程没迁完”
2. “我是不是应该把新逻辑继续补进这些旧 Hook”
---
## 4. 本批次暂不处理对象
虽然批次 B 已经处理了旧 shell / old flow / old bootstrap但以下对象仍暂缓
1. `src/components/custom-world-home/CustomWorldCreationHub.tsx`
2. `src/data/buildTagSimilarity.generated.ts`
3. 批次 C 的运行时真相链:
- `src/hooks/story/runtimeStoryCoordinator.ts`
- `src/services/runtimeStoryService.ts`
- `src/services/apiClient.ts`
4. 批次 D 的混合执行层:
- `src/hooks/story/npcEncounterActions.ts`
- `src/services/questDirector.ts`
- `src/services/runtimeItemAiDirector.ts`
- `src/services/ai.ts`
暂缓原因很明确:
1. 这些对象要么仍在当前正式链上
2. 要么涉及运行时真相与鉴权边界
3. 不能按“无引用旧壳”同一口径直接删除
---
## 5. 本批次验证方式
## 5.1 删除前验证
1. 全仓检索 `GameShell` 旧组件消费方,确认当前正式壳层已切到 `game-shell/` 目录
2. 全仓检索 `useStoryBootstrap`
3. 全仓检索旧装备 / 锻造 / 背包 flow Hook 导出的 handler 名称
4. 交叉确认当前正式主链入口已存在替代实现
## 5.2 删除后验证
建议至少执行:
1. `npm run check:encoding`
2. `npm run build`
如果这两项通过,说明:
1. 删除没有引入新的导入断裂
2. 主工程构建链仍然成立
---
## 6. 本批次结果判断
本批次完成后,工程获得的直接收益是:
1. 旧主流程壳层不再和现行壳层并存
2. 旧流程 Hook 不再占据 `src/hooks/` 的主路径注意力
3. 当前正式入口和历史残留的边界更清楚
4. 后续开发更不容易把新逻辑接回旧流程壳子
---
## 7. 下一批建议
建议下一步进入真正有结构价值的收口:
1. `批次 C运行时真相收口`
- `runtimeStoryCoordinator`
- `runtimeStoryService`
- `apiClient`
2. `批次 D任务 / 物品 / AI 混合执行层收口`
- `npcEncounterActions`
- `questDirector`
- `runtimeItemAiDirector`
- `ai.ts`
一句话总结本批次:
**这一步不是在“删几个没用 Hook”而是在把已经退场的旧主流程壳层和旧 flow 路牌从主工程里真正拔掉,让现行架构不再和历史壳子并排站着。**

View File

@@ -1,241 +0,0 @@
# 工程死分支清理执行记录 C2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
这份记录对应清洗计划中的:
- `批次 C运行时真相收口`
但这次不是“一口气把运行时真相链全删干净”,而是先做其中最明确、风险最低、最不该继续拖的那一段:
1. **收掉前端本地自动登录用户名 / 密码真相**
2. **把登录恢复改成优先依赖服务端 session / refresh**
同时,这一批也明确记录了一件事:
**运行时快照前置写入链当前还不能直接砍。**
原因不是“不想动”,而是服务端当前 `runtime story` 动作入口仍然以远端快照作为执行基线。
在后端 contract 没先改好之前,前端不能假装自己已经退出这条链。
---
## 1. 本批次已处理对象
## 1.1 已收口的鉴权链
| 文件 | 处理动作 | 本批结论 |
| --- | --- | --- |
| `src/services/apiClient.ts` | 删除本地自动登录用户名 / 密码存取逻辑 | 前端不再保存 auto auth 账号密码 |
| `src/services/authService.ts` | 去掉对本地游客凭证的读写依赖 | 自动游客登录改为仅本次生成凭证,不再长期落本地 |
| `src/components/auth/AuthGate.tsx` | 去掉“必须先有本地 access token 才尝试恢复”的前置假设 | 登录恢复改为优先尝试服务端 `getCurrentAuthUser()` / refresh session |
| `src/services/authService.test.ts` | 改写游客自动登录相关断言 | 验证改为“生成临时凭证并完成登录”,而不是“落本地账号密码” |
| `src/components/auth/AuthGate.test.tsx` | 改写登录恢复 mock | 验证改为“先尝试服务端会话恢复,再决定是否走游客兜底” |
---
## 2. 本批次为什么先做这段
这批优先级高,是因为它同时满足 4 条:
1. **风险明确。**
浏览器保存自动登录用户名 / 密码,本身就不符合“前端只做表现、后端负责鉴权真相”的方向。
2. **替代路径已经存在。**
后端已经有 refresh session cookie 与 `getCurrentAuthUser()`,不是没有可替代能力。
3. **改动边界清楚。**
这一段主要落在前端鉴权恢复逻辑和测试,不会直接波及运行时战斗、任务、物品、剧情主链。
4. **收益直接。**
一旦收掉,前端就少了一份最不该长期保留的高风险真相。
一句话讲:
**这一步先把“浏览器记住游客账号密码再重登”这条假真相链拔掉。**
---
## 3. 本批次明确没做的事
## 3.1 没有直接删除 `runtimeStoryCoordinator.ts` 里的前置 `putSaveSnapshot(...)`
这不是漏做,而是明确暂缓。
当前复核结果是:
1. `server-node/src/modules/story/storyActionService.ts`
2. `server-node/src/routes/runtimeRoutes.ts`
3. `server-node/src/repositories/runtimeRepository.ts`
这条后端链当前仍然通过远端快照读取运行时状态,再执行:
1. `getRuntimeStoryState`
2. `resolveRuntimeStoryAction`
也就是说,当前真实情况不是“前端多写了一份完全没用的镜像”,而是:
**前端在提交动作前先把当前状态写回远端快照,后端再基于这份快照执行业务动作。**
在这个 contract 没先升级为“前端只发 action后端自己持有完整 session 真相”之前,前端不能直接把这一步砍掉。
否则会出现:
1. 动作请求仍在走
2. 但服务端读取到的执行基线不完整
3. 最后不是收口真相,而是把主链打断
## 3.2 没有删除 `runtimeStoryService.ts` / `runtimeStoryCoordinator.ts` 的快照再水合逻辑
这一步本轮也做了复核,结论是:
1. 我曾尝试把 `runtimeStoryCoordinator.ts` 中对服务端返回快照的重复再水合去掉
2. 但对应的 `runtimeStoryCoordinator` 测试立即暴露出:当前后端返回的快照在部分战斗场景下还不是完整水合态
3. 说明前端当前这层再水合仍然有现实职责,不是纯多余代码
所以这一步本批明确结论是:
**暂不删除,等后端快照 contract 先补完整后再做。**
---
## 4. 本批次验证结果
本批次已完成的定向验证:
1. `npx vitest run src/services/authService.test.ts`
2. `npx vitest run src/components/auth/AuthGate.test.tsx`
3. `npx vitest run src/hooks/story/runtimeStoryCoordinator.test.ts`
4. `npm run check:encoding`
结果:
1. `authService` 测试通过
2. `AuthGate` 测试通过
3. `runtimeStoryCoordinator` 测试通过
4. 编码检查通过
另外执行了:
1. `npm run build`
结果:
构建产物生成成功,但 `build-gate` 仍因主包 chunk warning 拦截失败。
当前失败点仍是已知的主包体积问题:
- `AuthenticatedApp-*.js` 超过当前 warning 门槛
这属于仓库当前既有工程问题,不是本批次引入的新断裂。
## 4.1 2026-04-21 补充修正:会话探测 401 自触发循环
在这批收口完成后,前端又暴露出一条更细的鉴权恢复回路问题:
1. `AuthGate` 启动时会调用 `getCurrentAuthUser()` 探测现有会话
2. `/api/auth/me` 返回 `401` 时,`apiClient.ts` 会默认广播一次 `AUTH_STATE_EVENT`
3. `AuthGate` 自己又监听这个事件并重新 `hydrate()`
4. 最终形成 `hydrate -> /auth/me 401 -> emit -> hydrate` 的自触发循环
这条链的问题不在“是否允许 401”而在
**会话探测请求把“未登录态探测”错误地当成了“全局登录态变更”。**
因此这里补了一条更细粒度的约束:
1. `apiClient.ts` 新增 `notifyAuthStateChange` 选项,默认仍保持原有广播行为
2. `getCurrentAuthUser()` 作为会话探测请求,显式关闭这类 401 广播
3. 真实登录、登出、刷新成功后,仍保留全局鉴权变更通知
这样修完后:
1. `AuthGate` 仍会优先尝试服务端会话恢复
2. 无会话时会正常落回未登录分支
3. 不会因为探测型 401 把自己重新唤醒并刷爆控制台
## 4.2 2026-04-22 补充修正:公开认证入口误触发 refresh
在登录弹窗链路继续联调时,又暴露出一条更细的请求边界问题:
1. 用户处于未登录态,浏览器本地没有 access token
2. 点击“获取验证码”会调用 `sendPhoneLoginCode()`
3. `authService.ts` 复用了通用 `requestJson(...)`
4. `apiClient.ts` 在“无本地 token 且未显式关闭 refresh”时会先尝试 `POST /api/auth/refresh`
5. 若当前浏览器本来也没有 refresh session cookie就会先打出一条 `401 Unauthorized`
6. 最终表现成:验证码接口真正发送前,前端控制台先报一次 `/api/auth/refresh 401`
这条链的问题不在“验证码接口失败”,而在:
**登录前公开认证入口被错误当成了需要先补票的受保护请求。**
因此这里再补一条明确约束:
1. `sendPhoneLoginCode()`
2. `loginWithPhoneCode()`
3. `authEntry()`
4. `getAuthLoginOptions()`
5. `startWechatLogin()`
以上这些“获取登录态之前”的公开认证入口,统一显式传入:
1. `skipAuth: true`
2. `skipRefresh: true`
这样修完后:
1. 未登录用户点击“获取验证码”不会先打 `/api/auth/refresh`
2. 公开认证入口不会误带旧 token也不会制造无意义的 401 噪音
3. 真正需要 refresh 的仍然只有已拿到登录态后的受保护请求
本次补修的定向验证:
1. `npx vitest run src/services/authService.test.ts`
2. `npm run check:encoding`
---
## 5. 本批次完成后的实际收益
这一步完成后,工程在鉴权边界上有了两个明确改善:
1. **前端不再保存自动登录用户名 / 密码。**
浏览器只保留 access token本地高风险游客凭证真相已经收掉。
2. **登录恢复逻辑更接近服务端为真相源。**
`AuthGate` 不再假设“没有本地 token 就一定还没登录”,而是优先尝试服务端会话恢复。
这意味着前端鉴权链已经从:
```text
本地用户名/密码 -> 再次 entry -> 拿 token
```
进一步收到了:
```text
refresh session / 当前会话 -> 恢复用户
兜底时才创建一次游客凭证
```
---
## 6. 本批次后续建议
要继续完成批次 C下一步不该直接在前端硬删而应该先补后端 contract
1.`runtime story` 动作链逐步摆脱“前端先写远端快照”的依赖
2. 让服务端自己持有更完整的运行时 session 真相
3. 等后端返回快照已经稳定水合后,再删前端的重复再水合
换句话说,批次 C 的后半段应该拆成:
1. **C-1鉴权真相收口**
本批已完成
2. **C-2运行时快照 contract 后端化**
需要先改后端
3. **C-3前端镜像写入与重复水合退场**
依赖 C-2
---
## 7. 一句话总结
**批次 C 这一轮已经先把“浏览器长期保存游客账号密码”这条最不该存在的鉴权假真相链收掉了;而运行时快照前置写入这条链经过复核确认仍受后端 contract 约束,不能在服务端未先补齐前硬砍。**

View File

@@ -1,56 +0,0 @@
# 工程死分支清理执行记录 D2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
本批次继续清理上一轮复核后剩余的低风险数据产物与测试占位:
1. 未接入业务的生成产物
2. 只为测试替换真实实现的空 stub
3. 支撑这些残留的配置与脚本
---
## 1. 已删除对象
| 文件 | 判定 | 删除原因 | 替代路径 / 当前真相源 |
| --- | --- | --- | --- |
| `src/data/buildTagSimilarity.generated.ts` | 未接入业务的生成产物 | 运行时代码不 importBuild 相似度当前由 `buildTags.ts` 中的属性亲和度逻辑计算 | `src/data/buildTags.ts` |
| `scripts/generate-build-tag-similarity.py` | 已无输出目标的生成脚本 | 只负责生成已删除的矩阵文件,继续保留会误导后续开发恢复旧主源 | `src/data/buildTags.ts` 的手工审表逻辑 |
| `src/data/customWorldCharacterLoadout.stub.ts` | 测试专用空 stub | 只通过 `vitest.config.ts` alias 替换真实实现;真实实现已经稳定存在 | `src/data/customWorldCharacterLoadout.ts` |
---
## 2. 同步更新
本批次同步移除了:
1. `vitest.config.ts` 中指向 `customWorldCharacterLoadout.stub.ts` 的 alias
2. `BUILD_SYSTEM_ATTRIBUTE_SIMILARITY_PRD_2026-04-02.md` 中把旧 generated 矩阵描述为当前文件的表述
3. 清理计划里对 `buildTagSimilarity.generated.ts` 的未处理状态说明
---
## 3. 验证口径
删除前已确认:
1. `buildTagSimilarity.generated.ts` 无运行时代码引用
2. `customWorldCharacterLoadout.stub.ts` 只被 `vitest.config.ts` alias 引用
3. 真实 `customWorldCharacterLoadout.ts` 仍被 `characterPresets.ts``npcInteractions.ts` 使用,不能删除
删除后建议验证:
1. `npm run check:encoding`
2. 与自定义世界开局物品相关的测试
---
## 4. 当前结论
本批次完成后,剩余清理对象已经不再适合按“无引用直接删”推进。后续如果继续清,需要先改 contract 或主链职责:
1. 运行时快照真相链
2. 任务 / 物品 / AI 混合执行层
3. 大型主流程组件继续拆分,而不是直接删除

View File

@@ -1,117 +0,0 @@
# 工程死分支清理执行记录 E2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
本批次承接批次 D继续清掉已经退出 RPG 游戏创作主流程、RPG 运行时玩法主流程、平台基本功能主流程的历史壳层。
本批次不处理仍需后端 contract 先收口的对象,例如:
1. `src/services/questDirector.ts`
2. `src/services/runtimeItemAiDirector.ts`
3. `src/hooks/rpg-runtime-story/runtimeStoryCoordinator.ts`
4. `src/services/apiClient.ts`
这些对象仍属于“前端越界逻辑继续后端化”的后续批次,不按无引用文件直接删除。
---
## 1. 删除判定口径
本批只删除满足下面条件之一的对象:
1. 无运行时入口、无脚本入口、无当前路由挂载。
2. 已有现行正式实现,旧文件只剩 re-export / facade / 兼容命名。
3. 只被测试验证旧壳自身,且该测试不再服务当前主流程门禁。
4. 文档已明确该对象处于“后续只允许收缩、不再接新逻辑”的兼容残留状态。
---
## 2. 本批次已处理对象
| 文件 | 判定 | 删除原因 | 替代路径 / 当前真相源 |
| --- | --- | --- | --- |
| `server-node/src/routes/rpgCreationAgentRoutes.ts` | 旧命名 re-export | 当前后端正式路由直接使用 `customWorldAgent.ts` | `server-node/src/routes/customWorldAgent.ts` |
| `server-node/src/routes/rpgWorldGalleryRoutes.ts` | 空路由骨架 | 世界广场实际列表和详情已经进入世界库路由 | `server-node/src/routes/rpg-entry/rpgWorldLibraryRoutes.ts` |
| `server-node/src/services/RpgAgentOrchestrator.ts` | 旧命名 re-export | 当前正式上下文直接使用 `CustomWorldAgentOrchestrator` | `server-node/src/services/customWorldAgentOrchestrator.ts` |
| `server-node/src/services/RpgAgentSessionStore.ts` | 旧命名 re-export | 当前正式上下文直接使用 `CustomWorldAgentSessionStore` | `server-node/src/services/customWorldAgentSessionStore.ts` |
| `server-node/src/services/customWorldWorkSummaryService.ts` | 旧兼容入口 | 测试和路由已改为直接使用 RPG 命名服务 | `server-node/src/services/RpgWorldWorkSummaryService.ts` |
| `server-node/src/services/customWorldAgentPublishGateService.ts` | 旧发布门禁实现 | 当前 action executor 与作品库发布链已统一走 PublishingService | `server-node/src/services/customWorldAgentPublishingService.ts` |
| `server-node/src/services/customWorldAgentPublishService.ts` | 旧发布实现 | 当前发布链不再编译旧 legacy result profile | `server-node/src/services/customWorldAgentPublishingService.ts` |
| `server-node/src/modules/custom-world/runtime-profile/runtimeProfileCompiler.ts` | 旧 facade | runtime profile 已拆到目录模块并由 `index.ts` / `runtimeProfile.ts` 承接 | `server-node/src/modules/custom-world/runtime-profile/index.ts` |
| `server-node/src/bridges/legacyBuildRuntimeBridge.ts` | 无引用旧桥 | 后端 runtime build / equipment 已直接在正式模块内使用 | `server-node/src/modules/runtime/**` |
| `server-node/src/bridges/legacyRuntimeItemResolutionBridge.ts` | 旧桥 | runtime item 解析服务一并删除,正式运行时使用 `runtimeItemModule.ts` | `server-node/src/modules/runtime-item/runtimeItemModule.ts` |
| `server-node/src/modules/runtime-item/runtimeItemResolutionService.ts` | 无正式入口 wrapper | 只被 barrel 和自身测试引用,未挂入 Express 运行时主链 | `server-node/src/modules/runtime-item/runtimeItemModule.ts` |
| `server-node/src/modules/**/index.ts` | 无引用 barrel | 这些 barrel 没有被当前后端入口消费,反而制造“公共模块入口仍存在”的错觉 | 直接 import 具体正式模块 |
| `server-node/src/routes/rpg-*/index.ts` | 无引用 barrel | 当前 Express app 直接 import 具体 route 文件 | `server-node/src/app.ts` 中的具体路由 |
| `server-node/src/repositories/rpg-*/index.ts` | 无引用 barrel | 当前上下文直接 import 具体 repository | `server-node/src/server.ts` 中的具体仓储 |
| `src/components/DeveloperTeamModal.tsx` | 无入口 UI | 平台主流程没有打开该弹窗的入口 | 无替代 UI删除历史壳 |
| `src/components/LazySkillEffectPreview.tsx` | 无入口 lazy 壳 | 正式技能预览直接使用 `SkillEffectPreview` | `src/components/SkillEffectPreview.tsx` |
| `src/components/npcVisualEditorModel.ts` | 旧 NPC 形象写回模型 | 当前 RPG 创作编辑器使用 `CustomWorldNpcVisualEditor` 与结果页新入口 | `src/components/CustomWorldNpcVisualEditor.tsx``src/components/rpg-creation-editor/**` |
| `src/components/npcVisualEditorPersistence.ts` | 旧 NPC 形象写回持久层 | 只被旧持久化测试引用,正式编辑入口已迁移 | `src/components/rpg-creation-editor/**` |
| `src/components/rpg-creation-*/index.ts` | 无引用 barrel | 当前入口直接 import 具体 facade 文件barrel 没有主流程消费 | 直接 import `RpgCreation*` 具体文件 |
| `src/components/rpg-creation-editor/CustomWorldSceneChapterEditorSection.tsx` | 旧 facade | 当前编辑器 section 直接在 `RpgCreationEntityEditorShared.tsx` 中分发 | `src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx` |
| `src/data/editorValidation.ts` | 旧预设编辑器校验 | 当前主流程和内容门禁不再调用 | `scripts/validate-overrides.ts`、后端 editor API |
| `src/editor/shared/EditorNotice.tsx` | 无入口共享 UI | 只被同批删除的 FormFields 使用 | 无替代 UI删除历史编辑器壳 |
| `src/editor/shared/FormFields.tsx` | 无入口共享 UI | 旧编辑器共享表单未接主流程 | 当前 RPG 编辑器组件内聚在 `rpg-creation-editor/**` |
| `src/editor/shared/SectionCard.tsx` | 无入口共享 UI | 旧编辑器卡片未接主流程 | 当前 RPG 编辑器组件内聚在 `rpg-creation-editor/**` |
| `src/hooks/rpg-runtime-story/npcEncounterActions.ts` | 旧 wrapper | 正式实现已在 `useRpgRuntimeNpcInteraction.ts`,测试已改到正式文件 | `src/hooks/rpg-runtime-story/useRpgRuntimeNpcInteraction.ts` |
| `src/hooks/rpg-runtime-story/openingAdventure.ts` | 旧前端开局特殊流程 | 开局营地对白已由后端 `RpgRuntimeStoryActionDomain` 和当前 story context 承接 | `server-node/src/modules/rpg-runtime-story/RpgRuntimeStoryActionDomain.ts` |
| `src/hooks/rpg-runtime-story/storyCampCompanion.ts` | 旧前端营地同伴 helper | 只剩旧开局流程和自身测试引用,正式开局上下文已迁到当前 runtime story 链 | 后端 runtime story action domain 与 `storyContextBuilder.ts` |
| `src/hooks/rpg-runtime-story/storyRenderingHelpers.ts` | 无入口旧渲染 helper | 当前正式 story presentation 不再 import | `src/hooks/rpg-runtime-story/storyPresentation.ts` |
| `src/prompts/questPrompts.ts` | 前端 prompt 残留 | Quest prompt 真相已迁到后端 | `server-node/src/prompts/questPrompts.ts` |
| `src/prompts/runtimeItemPrompts.ts` | 前端 prompt 残留 | Runtime item prompt 真相已迁到后端 | `server-node/src/prompts/runtimeItemPrompts.ts` |
| `src/services/questPrompt.ts` | 前端 prompt re-export | 只指向同批删除的前端 prompt | `server-node/src/prompts/questPrompts.ts` |
| `src/services/runtimeItemAiPrompt.ts` | 前端 prompt re-export | 只指向同批删除的前端 prompt | `server-node/src/prompts/runtimeItemPrompts.ts` |
| `src/services/storyEngine/contentDependencyGraph.ts` | 实验性孤岛 | 只被自身测试引用,没有主流程消费 | 后续如需要重新设计到后端 story graph 服务 |
---
## 3. 同步调整
1. `customWorldAgentPhase2/3/4` 测试改为直接实例化 `RpgWorldWorkSummaryService`
2. `customWorldWorkSummaryService.integration.test.ts` 改为直接覆盖 `RpgWorldWorkSummaryService`
3. `npcEncounterActions.test.ts` 改为直接覆盖 `useRpgRuntimeNpcInteraction.ts`,不再通过旧 wrapper。
4. `story_opening_camp_dialogue` 的 function catalog 执行路径改为后端 runtime action domain不再指向已删除旧前端文件。
5. NPC function catalog 中 `npc_chat / npc_help / npc_leave / npc_fight / npc_spar / npc_preview_talk` 的 executor 路牌改到现行 `useRpgRuntimeNpcInteraction.ts`
---
## 4. 本批次暂缓对象
以下对象仍然保留,原因是它们不是“无引用死代码”,而是需要下一轮按 contract 或主链职责迁移:
1. `src/services/questDirector.ts`
2. `src/services/runtimeItemAiDirector.ts`
3. `src/services/ai.ts`
4. `src/data/sceneObservation.ts`
5. `server-node/ecosystem.config.cjs`
6. `server-node/src/scripts/syncCustomWorldSavedProfileAssets.ts`
其中 `ecosystem.config.cjs` 被部署脚本直接使用;`sceneObservation.ts` 被内容 smoke 脚本验证;`syncCustomWorldSavedProfileAssets.ts` 是一次性运维脚本,后续要单独按运维脚本治理口径确认是否归档。
---
## 5. 验证口径
本批删除后建议验证:
1. `npm run check:encoding`
2. `npx tsx --test server-node/src/services/customWorldWorkSummaryService.integration.test.ts`
3. `npx vitest run src/hooks/rpg-runtime-story/npcEncounterActions.test.ts`
4. `npm run server-node:build`
5. `npm run build`
如果 `npm run build` 仍被既有 chunk warning 拦截,需要单独记录为既有门禁问题,不归因到本批删除。
---
## 6. 当前结论
本批次进一步删除了“旧命名入口、旧 facade、旧 prompt 前端镜像、无入口编辑器壳层”这批容易误导后续开发的文件。
后续清理不应继续按“静态无引用”直接推进,而应进入两类工作:
1. 运行时 / 任务 / 物品 / AI 的后端 contract 收口。
2. RPG 创作编辑器与运行时热点文件的职责拆分。

View File

@@ -1,91 +0,0 @@
# 工程死分支清理执行记录 F2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
本批次承接批次 E 的验证结果,继续处理删除后暴露出的最后一组高置信残留:
1. 已经没有任何代码入口引用的前端任务生成 director。
2. 只被内容 smoke 牵住、但不再是正式运行时入口的旧观察文案 helper。
3. 带有固定用户、固定 session、固定 profile 的一次性历史同步脚本。
4. 清理后暴露出的 function catalog 契约覆盖缺口。
本批次仍然不按文件名直接删除 `legacy` 命名对象。经核对,`server-node/src/bridges/legacyInventoryRuntimeBridge.ts``legacyNpcTask6Bridge.ts``legacyQuestProgressBridge.ts``legacyQuestRuntimeBridge.ts``legacyRuntimeItemBridge.ts``legacyTreasureRuntimeBridge.ts` 仍被后端战斗、背包、任务、宝藏主链直接引用,不能按历史命名硬删。
---
## 1. 删除判定口径
本批删除对象必须同时满足:
1. 修正 `.js -> .ts` 后端源码解析、前端懒加载入口解析后,仍不可从正式入口到达。
2. 全仓库代码引用扫描没有正式入口引用。
3. 如只被 smoke 或测试牵住,先把 smoke / 测试改到当前正式主链,再删除旧对象。
4. 删除后通过对应门禁验证,没有新增悬空 import。
---
## 2. 本批次已处理对象
| 文件 | 判定 | 删除 / 调整原因 | 替代路径 / 当前真相源 |
| --- | --- | --- | --- |
| `src/services/questDirector.ts` | 无代码入口残留 | 正式 quest 生成已由后端 `/api/runtime/quests/generate``questService.ts` 承接,前端当前没有任何 import | `server-node/src/services/questService.ts``server-node/src/modules/quest/runtimeQuestModule.ts` |
| `src/data/sceneObservation.ts` | 旧观察文案 helper | 只被 `scripts/smoke-content.ts` 引用,正式观察动作已走 `idle_observe_signs` function 与运行时 story continuation | `src/data/functionCatalog/state/idleObserveSigns.ts``src/hooks/rpg-runtime-story/storyChoiceContinuation.ts` |
| `server-node/src/scripts/syncCustomWorldSavedProfileAssets.ts` | 一次性硬编码运维脚本 | 脚本内固定用户、session、profile只服务历史补丁没有 CLI 参数和当前运维入口 | 无替代;如未来需要,按参数化运维脚本重新设计 |
---
## 3. 同步调整
1. `scripts/smoke-content.ts` 不再 import 旧 `sceneObservation.ts`,改为通过 `resolveFunctionOption('idle_observe_signs', ...)` 验证当前正式 function 目录。
2. `packages/shared/src/contracts/rpgRuntimeContracts.test.ts` 不再验证已移除的旧 `story` façade改为直接验证当前拆分契约。
3. `src/data/functionCatalog/` 补齐仍在后端运行时契约中的 function 文档:
- `battle_attack_basic`
- `battle_use_skill`
- `npc_chat_quest_offer_view`
- `npc_chat_quest_offer_replace`
- `npc_chat_quest_offer_abandon`
4. `battle_attack_basic``battle_use_skill` 只作为后端契约文档登记,不进入 `STATE_FUNCTION_DEFINITIONS`,避免前端本地候选池生成缺少 `runtimePayload.skillId` 的假技能 option。
---
## 4. 本批次暂缓对象
以下对象经本批复核后继续保留:
1. `server-node/src/services/customWorldAgentRepositoryTestHelpers.ts`
2. `server-node/src/services/customWorldAgentTestHelpers.ts`
3. `server-node/src/testFixtures/runtimeCharacter.ts`
4. `server-node/src/testHttp.ts`
这些文件不属于正式运行时入口但当前被后端测试、smoke 与路由边界门禁使用。它们不是 RPG 创作 / 运行时玩法主流程代码,但仍是平台基本质量门禁的一部分,不能在“删除冗余业务代码”批次里直接硬删。
另保留:
1. `src/services/runtimeItemAiDirector.ts`
2. `src/services/ai.ts`
3. `src/services/apiClient.ts`
这些文件仍被当前主链或前端 SDK 入口引用,后续如继续压缩,必须先完成对应 contract / SDK 拆分,不按无引用规则删除。
---
## 5. 验证结果
本批已通过:
1. `npx vitest run src/data/functionCatalog/functionCatalog.test.ts packages/shared/src/contracts/rpgRuntimeContracts.test.ts`
2. `npx tsx scripts/smoke-content.ts`
3. `npm run check:encoding`
并额外确认:
1. 全仓库代码中不再引用 `sceneObservation``questDirector``syncCustomWorldSavedProfileAssets`
2. `buildStateFunctionDefinitions()` 中不会出现 `battle_attack_basic` / `battle_use_skill`,这两个 function 只由后端运行时 option 池下发。
---
## 6. 当前结论
本批次后,静态入口扫描中剩余的高置信“不可达源码”已经收敛为测试辅助、测试夹具和 smoke helper。继续删除前需要先重构测试基础设施或迁移剩余前端 SDK而不应再按文件名或历史命名直接硬删。

View File

@@ -1,553 +0,0 @@
# 前端应迁后端逻辑审计2026-04-21
更新时间:`2026-04-21`
## 0. 审计目标
这份文档只回答一个问题:
**当前前端代码里哪些逻辑已经明显越过“前端只做表现Express 后端负责逻辑、数据与存储”的边界,应该继续迁到后端。**
本轮不改业务代码,只做:
1. 基于当前仓库状态给出高置信度候选点
2. 标明代码证据
3. 给出迁移优先级
4. 说明迁移后前端应该保留什么、移走什么
---
## 1. 结论先行
结合当前代码与已有边界文档,前端里仍有 7 类逻辑应该继续后移:
1. **运行时快照前置写入与本地镜像解释**
2. **鉴权 token 的浏览器本地真相**
3. **平台浏览历史的本地真相与迁移状态**
4. **NPC 待接委托“换单”仍由前端直接触发正式生成**
5. **quest/runtime item 的双环境混合编排**
6. **浏览器侧大型 AI orchestration 与 prompt/repair/fallback 主链**
7. **NPC 招募对白之后的正式结算链路**
一句话判断:
**当前前端已经不是最早那种“大量主算”的状态,但仍然保留了运行时镜像、生成编排和部分正式真相。后端边界还需要再收一轮,前端才算真正退回表现层。**
---
## 2. 审计依据
### 2.1 文档依据
1. `docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md`
2. `docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`
3. `docs/technical/RUNTIME_STORY_BACKEND_BOUNDARY_MIGRATION_2026-04-19.md`
4. `docs/audits/engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md`
5. `docs/audits/engineering/CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md`
### 2.2 当前代码依据
1. `src/hooks/story/runtimeStoryCoordinator.ts`
2. `src/services/apiClient.ts`
3. `src/services/platformBrowseHistory.ts`
4. `src/components/game-shell/PreGameSelectionFlow.tsx`
5. `src/hooks/story/npcEncounterActions.ts`
6. `src/services/questDirector.ts`
7. `src/services/runtimeItemAiDirector.ts`
8. `src/services/ai.ts`
---
## 3. 当前高置信度应后移逻辑
## 3.0 本轮已完成后移
以下链路已在本轮或上一轮连续落地中完成后移,不再属于“仍残留在前端”的正式主链:
1. access token 浏览器本地真相
2. browse history 本地真相
3. runtime story 前置 `PUT /runtime/save/snapshot`
4. NPC 待接委托 `replace / abandon / accept`
5. custom world profile 正式浏览器入口
6. `questDirector` / `runtimeItemAiDirector` 浏览器正式编排
7. NPC 招募正式结算
其中 NPC 招募已从“前端本地改 companions / roster / npcStates / storyHistory”收回到后端 runtime action。
## 3.1 运行时快照前置写入仍在前端
### 代码证据
`src/hooks/story/runtimeStoryCoordinator.ts` 当前仍存在以下链路:
1. `syncRuntimeSnapshot(...)`
2. `syncRuntimeSnapshot(...)` 内部直接调用 `putSaveSnapshot(...)`
3. `loadServerRuntimeOptionCatalog(...)` 在请求 `getRuntimeStoryState(...)` 之前先写本地快照
4. `resolveServerRuntimeChoice(...)` 在请求 `resolveRuntimeStoryAction(...)` 之前先写本地快照
对应位置:
1. `src/hooks/story/runtimeStoryCoordinator.ts:21`
2. `src/hooks/story/runtimeStoryCoordinator.ts:25`
3. `src/hooks/story/runtimeStoryCoordinator.ts:36`
4. `src/hooks/story/runtimeStoryCoordinator.ts:99`
### 当前问题
这意味着运行时正式动作发起前,前端仍会先落一份自己的快照真相,再去请求后端。
这条链的问题不是“有没有缓存”,而是:
1. 前端仍在承担正式提交前的状态镜像
2. 快照解释权没有完全收回到后端
3. 运行时主链仍处于“本地镜像 + 服务端会话”并存状态
### 迁移建议
后端继续承接:
1. 运行时快照写入
2. 快照版本解释
3. 动作提交前的状态一致性校验
前端只保留:
1. 当前展示用的 view model
2. 可选的只读恢复缓存
3. 纯表现态的 loading / transition / animation state
### 优先级
`P0`
---
## 3.2 鉴权 token 仍由前端 localStorage 持有真相
### 代码证据
`src/services/apiClient.ts` 当前仍直接访问 `window.localStorage` 保存 access token
1. `getStoredAccessToken()`
2. `setStoredAccessToken(...)`
3. `clearStoredAccessToken(...)`
4. `withAuthorizationHeaders(...)` 直接从本地 token 组装请求头
对应位置:
1. `src/services/apiClient.ts:333`
2. `src/services/apiClient.ts:341`
3. `src/services/apiClient.ts:362`
4. `src/services/apiClient.ts:382`
### 当前问题
第三批清理已经收掉了“自动登录用户名/密码”本地真相,但 access token 仍然由浏览器长期持有。
这在当前项目边界下仍有两个问题:
1. 正式鉴权真相仍没有完全收回后端 session 边界
2. 前端 SDK 仍然负担 token 生命周期的关键部分
### 迁移建议
后端继续承接:
1. session / refresh / cookie 真相
2. 鉴权状态续期
3. token 更新与失效策略
前端只保留:
1. 当前是否已登录的展示态
2. 统一的请求封装
3. 401 后的 UI 响应
### 优先级
`P0`
---
## 3.3 平台浏览历史仍是“前端本地历史 + 后端回填”的双真相
### 代码证据
`src/services/platformBrowseHistory.ts` 当前仍维护一整套本地历史真相:
1. `readPlatformBrowseHistory(...)`
2. `writePlatformBrowseHistory(...)`
3. `hasPendingPlatformBrowseHistoryMigration(...)`
4. `markPlatformBrowseHistoryMigrated(...)`
对应位置:
1. `src/services/platformBrowseHistory.ts:77`
2. `src/services/platformBrowseHistory.ts:103`
3. `src/services/platformBrowseHistory.ts:151`
4. `src/services/platformBrowseHistory.ts:164`
`src/components/game-shell/PreGameSelectionFlow.tsx` 当前仍显式做:
1.`writePlatformBrowseHistory(...)`
2. 再调用 `upsertProfileBrowseHistory(...)`
3. 同步成功后 `markPlatformBrowseHistoryMigrated(...)`
4. 启动阶段读取 `readPlatformBrowseHistory(...)`
5. 根据 `hasPendingPlatformBrowseHistoryMigration(...)` 决定是否补同步
对应位置:
1. `src/components/game-shell/PreGameSelectionFlow.tsx:383`
2. `src/components/game-shell/PreGameSelectionFlow.tsx:392`
3. `src/components/game-shell/PreGameSelectionFlow.tsx:394`
4. `src/components/game-shell/PreGameSelectionFlow.tsx:433`
5. `src/components/game-shell/PreGameSelectionFlow.tsx:466`
### 当前问题
这条链已经不是单纯缓存,而是:
1. 本地历史存储
2. 本地同步标记
3. 后端历史持久化
三套状态同时存在。
### 迁移建议
后端继续承接:
1. 浏览历史唯一持久化真相
2. 历史去重、排序、截断
3. 迁移完成标记
前端只保留:
1. 展示缓存
2. 弱网下的临时 optimistic UI
3. 刷新后重新拉取远端结果
### 优先级
`P1`
---
## 3.4 NPC 待接委托“换单”仍由前端直接发起正式生成
### 代码证据
`src/hooks/story/npcEncounterActions.ts` 当前仍保留:
1. `replacePendingNpcQuestOffer = async () => { ... }`
2. 内部直接调用 `generateQuestForNpcEncounter(...)`
对应位置:
1. `src/hooks/story/npcEncounterActions.ts:1561`
2. `src/hooks/story/npcEncounterActions.ts:1595`
### 当前问题
聊天后是否挂出待接委托已经后移,但“换一份委托”这条分支仍然是:
1. 前端组装上下文
2. 前端决定调用生成
3. 前端直接把结果写回当前 story UI
这仍属于正式运行时任务编排没有收干净。
### 迁移建议
后端继续承接:
1. NPC 待接委托换单决策
2. 是否允许换单
3. 换单后的任务草案生成
4. 对应聊天态快照回填
前端只保留:
1. 点击“换一份委托”
2. loading / error 展示
3. 消费后端返回的新 pending quest offer
### 优先级
`P0`
---
## 3.5 questDirector 仍是前端 SDK 与生成编排混合体
### 代码证据
`src/services/questDirector.ts` 当前同时承担:
1. `generateQuestForNpcEncounter(...)`
2. 浏览器路径 `requestJson('/api/runtime/quests/generate')`
3. 非浏览器路径 `requestChatMessageContent(...)`
4. 本地 `compileQuestIntentToQuest(...)` fallback
对应位置:
1. `src/services/questDirector.ts:213`
2. `src/services/questDirector.ts:242`
3. `src/services/questDirector.ts:267`
4. `src/services/questDirector.ts:256`
5. `src/services/questDirector.ts:281`
6. `src/services/questDirector.ts:293`
### 当前问题
这类文件虽然浏览器正式路径已经优先走后端,但职责仍混在一起:
1. 前端 SDK
2. Quest prompt 编排
3. Quest intent 解析
4. deterministic fallback compile
这会导致边界长期模糊,也让前端仍像“半个服务端”。
### 迁移建议
后端继续承接:
1. quest intent 生成
2. prompt 组装
3. JSON 解析
4. fallback compile
前端只保留:
1. `requestGenerateQuest(...)` 这类轻量 SDK
2. 请求参数组装
3. 结果消费
### 优先级
`P1`
---
## 3.6 runtimeItemAiDirector 仍是前端 SDK 与意图生成混合体
### 代码证据
`src/services/runtimeItemAiDirector.ts` 当前同时承担:
1. `generateRuntimeItemAiIntents(...)`
2. 浏览器路径 `requestJson('/api/runtime/items/runtime-intent')`
3. 非浏览器路径 `requestChatMessageContent(...)`
4. 本地 `buildRuntimeItemAiIntent(...)` fallback
对应位置:
1. `src/services/runtimeItemAiDirector.ts:84`
2. `src/services/runtimeItemAiDirector.ts:94`
3. `src/services/runtimeItemAiDirector.ts:118`
### 当前问题
它和 `questDirector` 是同类问题:
1. 正式浏览器路径已经走后端
2. 但前端文件仍然承担完整生成逻辑认知
3. 文件职责仍然是双环境混合
### 迁移建议
后端继续承接:
1. runtime item intent prompt
2. 模型调用
3. 结果解析与 fallback
前端只保留:
1. 轻量请求 SDK
2. 结果到 UI 的映射
### 优先级
`P1`
---
## 3.7 `src/services/ai.ts` 仍是浏览器侧正式 AI orchestration 热点
### 代码证据
当前 `src/services/ai.ts` 仍直接承担以下正式链路:
1. `requestChatMessageContent(...)`
2. `requestPlainTextCompletionFromClient(...)`
3. `streamPlainTextCompletionFromClient(...)`
4. `generateCustomWorldProfile(...)`
5. `generateInitialStory(...)`
6. `generateNextStep(...)`
7. `streamNpcChatDialogue(...)`
8. `streamNpcRecruitDialogue(...)`
对应位置:
1. `src/services/ai.ts:1732`
2. `src/services/ai.ts:1868`
3. `src/services/ai.ts:2038`
4. `src/services/ai.ts:2339`
5. `src/services/ai.ts:2447`
6. `src/services/ai.ts:2487`
7. `src/services/ai.ts:2529`
8. `src/services/ai.ts:2570`
并且文件内仍保留:
1. JSON repair
2. prompt 组装
3. response normalize
4. fallback/offline 响应
5. 角色聊天建议与摘要生成
### 当前问题
这说明浏览器端并不只是“请求一个后端接口”,而是还在承担:
1. prompt source
2. 生成策略
3. 错误修复
4. fallback 编排
5. 多类业务场景的正式 AI 出口
这与“前端只做表现”存在明确冲突。
### 迁移建议
后端继续承接:
1. story / npc / recruit / custom-world 的 prompt 编排
2. JSON repair
3. fallback 策略
4. streaming orchestration
5. 模型调用与日志
前端只保留:
1. 轻量 AI SDK
2. SSE 文本流展示
3. UI fallback 呈现
### 优先级
`P0`
---
## 3.8 NPC 招募对白之后的正式结算链路已完成后移
### 本轮前状态
迁移前,`src/hooks/story/npcInteraction.ts` 中的 `buildRecruitmentOutcome / executeRecruitment / startRecruitmentSequence` 仍在前端本地正式结算:
1.`npcStates`
2.`companions`
3.`roster`
4.`currentEncounter / inBattle / sceneHostileNpcs`
5. 直接写 `storyHistory`
6. 再触发后续剧情推进
这与“前端只做表现,所有正式逻辑、数据都放到 Express 后端”直接冲突。
### 本轮后状态
本轮已完成:
1. `server-node/src/modules/story/runtimeSession.ts`
- 正式承接完整 `companions`
- 正式承接 `roster`
2. `server-node/src/modules/npc/npcInteractionService.ts`
- `npc_recruit` 已支持正常入队
- `npc_recruit` 已支持满员换队招募
3. `src/hooks/story/npcInteraction.ts`
- 前端只保留招募对白流式展示
- 正式招募结算改为调用后端 runtime action
### 当前判断
这一项已不再属于前端残留正式逻辑。
---
## 4. 可以暂时保留在前端的部分
下面这些内容即使和上述模块同文件出现,也不属于必须后移的对象:
1. 面板开关、loading、error、streaming 文本展示
2. 动画时间线、过场状态、临时 UI 回显
3. 表单草稿、筛选词、排序选项
4. 只影响表现、不影响正式真相的 view model 拼接
迁移时要注意:
**不是把所有前端代码都往后端搬,而是把“正式状态解释、规则裁决、生成编排、持久化真相”搬走。**
---
## 5. 推荐迁移顺序
## 5.1 第一阶段
先收最危险的正式真相:
1. `runtimeStoryCoordinator.ts`
2. `apiClient.ts`
3. `npcEncounterActions.ts` 里的 quest replace 分支
原因:
1. 这三处最直接影响运行时真相和动作主链
2. 不先收这些,前端仍然不是纯表现层
## 5.2 第二阶段
再拆双环境混合服务:
1. `questDirector.ts`
2. `runtimeItemAiDirector.ts`
3. `platformBrowseHistory.ts`
原因:
1. 这几处已经有后端承接基础
2. 迁移成本相对可控
## 5.3 第三阶段
最后继续压缩浏览器 AI orchestration
1. `src/services/ai.ts`
2. 相关 prompt builder / repair helper / offline fallback
原因:
1. 这部分体量大
2. 链路多
3. 更适合在前两阶段把 contract 稳住后集中拆
---
## 6. 建议产出物
如果后续按这份文档继续落地,建议每一批都至少同步产出:
1. 一份落地文档,说明迁移了哪条链
2. 一组 contract/route 变更说明
3. 一组前端 SDK 收缩说明
4. 一组防回退测试
---
## 7. 一句话结论
当前前端最需要继续后移的,不是零散小工具,而是:
**运行时快照前置写入、鉴权 token、本地浏览历史真相、NPC 委托换单、quest/runtime item 双环境混合编排,以及 `src/services/ai.ts` 里仍然留在浏览器的正式 AI orchestration。**

View File

@@ -1,173 +0,0 @@
# 怪物-NPC 脚本统一整改审计
日期2026-04-06
## 核心结论
当前工程仍然没有真正落实“怪物就是初始好感度为负数的 NPC”这一原则。
现状不是“NPC 脚本里支持 hostile 状态”,而是同时存在两条并行链路:
1. `npc / encounter / npcStates / npcInteraction`
2. `monster / hostileNpc / sceneMonsters / sceneHostileNpcs / hostileNpcPresets`
这会直接导致:
- 同一个敌对实体同时拥有 NPC 身份和 monster 身份。
- 场景、战斗、渲染、提示词都在维护两套入口。
- 后续修 bug 时,任何位置、死亡、血条、入场、掉落问题都要同时查两条链路。
本次文档的目标不是立刻改代码,而是先把应该删除的分叉脚本、应该降级成素材层的文件、以及必须合并的字段全部列清楚,作为后续统一改造的依据。
## 当前违背原则的根因
### 1. 场景数据仍然把“怪物”和“NPC”当成两类实体
当前场景层同时维护:
- `ScenePreset.monsterIds`
- `SceneNpc[]`
- `ScenePresetInfo.hostileNpcIds`
- `SceneNpc.monsterPresetId / hostileNpcPresetId`
这意味着场景里一个敌对单位既可以来自 `monsterIds`,也可以来自 `npcs`,甚至会被脚本再生成为 hostile scene npc。
### 2. 运行时仍然存在“怪物专用实体状态”
当前运行时仍然同时维护:
- `GameState.sceneMonsters`
- `GameState.sceneHostileNpcs`
- `SceneHostileNpc / SceneMonster`
这和“怪物本质上只是 hostile NPC”是冲突的。真正统一后运行时只应该保留一套“场景 NPC / 战斗 NPC”状态。
### 3. 战斗脚本仍然把怪物当独立 actor
战斗层目前不是“NPC 战斗,只是 hostile 的那部分会出手”,而是显式写了:
- `TurnActor = 'player' | 'companion' | 'monster'`
- `getClosestMonster`
- `resetCombatPresentation(monsters, ...)`
- `sceneMonsters` 全链路结算
这会强制后面所有视觉、掉落、提示词、AI 上下文都跟着叫 monster。
### 4. 渲染层仍然有 monster 专属显示入口
画布层当前仍然依赖:
- `sceneMonsters`
- `sceneHostileNpcs`
- `monsterPresetId`
- `HostileNpcAnimator`
也就是“敌对 NPC 是否按 NPC 脚本渲染”这件事,到最终显示层仍然没有统一。
## 一级删除清单
下面这些文件属于“业务流程分叉脚本”,不是单纯资源适配层。后续统一时应优先删除或并入 NPC 主链路。
| 文件 | 当前分叉角色 | 处理建议 |
| --- | --- | --- |
| `src/data/monsters.ts` | 对 `hostileNpcs.ts` 的别名出口 | 直接删除,禁止继续保留 monster 专属入口名。 |
| `src/data/hostileNpcs.ts` | 负责 monster 创建、编队、距离、朝向、变化、落位 | 按 hostile NPC 运行时工具重写并并入 NPC 体系;原文件名不应继续保留。 |
| `src/data/sceneEncounterPreviews.ts` | 单独构造 hostile encounter group、auto battle、hostile preview | 删除 monster 专线逻辑,改为“负好感 NPC 预览/入场/转战斗”。 |
| `src/hooks/combat/battlePlan.ts` | 使用 `monster` actor、`sceneMonsters``getClosestMonster` | 改成统一的 hostile NPC combatant 规划脚本monster actor 概念应移除。 |
| `src/hooks/combat/playback.ts` | 使用 `sceneMonsters` 播放怪物战斗演出 | 改成统一 NPC 战斗回放;不再区分 monster 播放器。 |
| `src/components/game-canvas/GameCanvasRuntime.tsx` | 运行时按 `sceneMonsters / sceneHostileNpcs` 双数据源选敌方实体 | 删除双源兜底,统一成单一 hostile NPC 列表。 |
| `src/components/game-canvas/GameCanvasEntityLayer.tsx` | 敌方实体按 monsterPreset 分支渲染 | 改成同一套 NPC 实体渲染,视觉差异仅由 visual preset 决定。 |
| `src/components/game-canvas/GameCanvasShared.tsx` | 定义 `sceneMonsters / sceneHostileNpcs` props 与 monster 专属计算 | 删除这些字段与 helper改成统一 NPC 画布协议。 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | 独立怪物预设编辑面板 | 并回 NPC hostile visual preset 面板,或删除该独立编辑器入口。 |
| `src/components/preset-editor/MonsterPresetTab.tsx` | 独立怪物预设页签 | 与上面一并删除或并入 NPC preset 编辑器。 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | 仍然单独编辑 `monsterIds` | 删除 `monsterIds` 编辑项,只保留场景 NPC 列表。 |
## 二级归并清单
下面这些文件不一定需要物理删除,但它们当前仍然在放大 monster / NPC 分轨,必须在统一改造时一起收口。
| 文件 | 当前问题 | 处理建议 |
| --- | --- | --- |
| `src/data/scenePresets.ts` | 通过 `monsterIds` 再生 hostile scene npc并区分 `getSceneHostileNpcs / getSceneFriendlyNpcs` | 保留场景数据文件本身,但删除 `monsterIds` 体系,让敌对角色直接存在于 `npcs` 中。 |
| `src/data/customWorldNpcMonsters.ts` | 用单独脚本推导“怪物型 NPC”预设 | 可保留为 hostile visual preset 选择器,但不能再生成第二套实体语义。 |
| `src/data/hostileNpcPresets.ts` | 目前既是视觉预设库,也是独立 hostile 流程的数据源 | 降级为 hostile visual/combat preset 库;不再拥有独立实体生命周期。 |
| `src/components/HostileNpcAnimator.tsx` | 当前名字和调用语义都在暗示“独立怪物实体” | 可以保留为贴图播放器,但应改为 hostile NPC 的视觉适配组件,而不是独立物种脚本。 |
| `src/components/AdventureEntityModal.tsx` | 详情弹窗仍会优先查 monster preset / hostileNpcPreset | 统一读取 NPC 档案;视觉差异只通过 hostile preset 补充。 |
| `src/components/SkillEffectPreview.tsx` | 预览器直接使用 `sceneMonsters``createSceneMonstersFromIds` | 改成统一 hostile NPC 预览态。 |
| `src/components/StateFunctionEditor.tsx` | 编辑器里仍然直接造 monster battle preview | 改成 hostile NPC preview。 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | 仍然暴露 `monsterPresetId` 字段 | 改成更明确的 hostile visual preset 字段避免“怪物类型”和“NPC 类型”双语义。 |
| `src/hooks/story/npcEncounterActions.ts` | 虽然入口叫 npc但内部仍然写 `sceneMonsters / sceneHostileNpcs` | 改成统一 hostile NPC 战斗状态字段。 |
| `src/hooks/story/choiceActions.ts` | 仍然有 `buildHostileNpcBattleReward``getResolvedSceneHostileNpcs` 这一层额外概念 | 统一到 hostile NPC 结算工具,不再把“敌对 NPC”和“monster”混称。 |
| `src/hooks/useStoryGeneration.ts` | 给 AI/剧情层传入 `sceneMonsters``sceneHostileNpcs` | 改成统一的 hostile NPC 上下文切片。 |
| `src/services/prompt.ts` | 仍然从 `monsterIds``createSceneMonstersFromIds` 组 prompt | 改成从场景 NPC 列表中筛出 hostile NPC。 |
| `src/services/questDirector.ts` | 仍然依赖 `monsterPresetId` 推导当前敌对目标 | 统一改为基于负好感或 hostile 标记的 NPC。 |
| `src/services/ai.ts` | 仍然混用 `monsterIds`、sceneNpc 的 hostile 判定 | 与场景统一后改成只读 NPC 列表。 |
| `src/services/questTypes.ts` | 仍然把 `hostileNpcIds / monsterIds` 当作 scene 快照字段 | 删除 `monsterIds`,保留 hostile NPC 语义。 |
## 可保留但必须降级为“素材/配置层”的内容
下面这些内容不一定要消失,但不能继续作为独立业务链路存在:
| 文件/内容 | 可以保留的原因 | 必须收口的边界 |
| --- | --- | --- |
| `src/components/HostileNpcAnimator.tsx` | 怪物贴图是特殊资源,需要专门 sprite sheet 播放器 | 只负责画图,不再决定实体类型、战斗身份、交互入口。 |
| `src/data/hostileNpcPresets.ts` | hostile visual/combat preset 仍然有价值 | 只能作为 hostile NPC 的 visual/combat preset 库不再驱动另一套“monster 实体”。 |
| `src/data/hostileNpcOverrides.json` | 资源级 override 仍可继续用 | 不能再配套出独立 hostile 流程。 |
| `src/data/monsterOverrides.json` | 如果只是素材映射,可迁移到 hostile visual preset override | 不应继续以 monster 专属命名长期存在。 |
| `src/data/customWorldNpcMonsters.ts` | 自定义世界里确实需要从文本匹配 hostile visual preset | 只能产出“NPC 使用哪个 hostile visual preset”不能产出独立 monster 身份。 |
## 字段级必须合并的内容
后续改代码时,至少要把下面这些字段和类型一起收口:
| 当前字段/类型 | 问题 | 合并方向 |
| --- | --- | --- |
| `ScenePreset.monsterIds` | 场景里额外保存一份怪物池 | 删除,只保留 `npcs`。 |
| `ScenePresetInfo.hostileNpcIds` | 历史遗留双字段 | 直接由 `npcs.filter(initialAffinity < 0 或 hostile)` 推导。 |
| `Encounter.monsterPresetId` | 把 hostile NPC 再次物种化 | 改成 hostile visual preset 字段,或并入统一 visualRef。 |
| `Encounter.hostileNpcPresetId` | 与 `monsterPresetId` 语义重叠 | 与上面合并为一个字段。 |
| `GameState.sceneMonsters` | 把敌对 NPC 单独塞进 monster 容器 | 改成统一 `sceneNpcCombatants` 或等价单一列表。 |
| `GameState.sceneHostileNpcs` | 历史兼容层,导致双数据源 | 删除。 |
| `SceneHostileNpc / SceneMonster` | 类型名直接固化了分轨 | 改成统一的 hostile NPC / scene combat NPC 类型。 |
| `SceneHostileNpcChange / SceneMonsterChange` | 继续复制同一套变更结构 | 合并成统一 NPC scene change。 |
| `SceneNpc.monsterPresetId / hostileNpcPresetId` | 同一实体上挂两套 preset 入口 | 收敛为一个 hostile visual/combat preset 字段。 |
## 本轮最优先的删除顺序
建议后续真正改代码时,按下面顺序删并,风险最低:
1. 先删字段入口:`monsterIds / sceneHostileNpcs / hostileNpcPresetId`
2. 再删运行时双轨:`src/data/monsters.ts``src/data/hostileNpcs.ts``src/data/sceneEncounterPreviews.ts`
3. 再删战斗双轨:`battlePlan.ts``playback.ts` 里的 `monster` actor 与 `sceneMonsters`
4. 再删画布双轨:`GameCanvasRuntime.tsx``GameCanvasEntityLayer.tsx``GameCanvasShared.tsx`
5. 最后清编辑器和提示词:`MonsterPresetPanel.tsx``MonsterPresetTab.tsx``prompt.ts``questDirector.ts`
## 改造后的目标形态
统一后应只剩下这一套语义:
- 场景中所有可见角色都放在 `npcs`
- 怪物 = `initialAffinity < 0``hostile = true` 的 NPC
- hostile 的视觉差异只来自 hostile visual preset
- 战斗中所有敌方单位都属于 hostile NPC combatant
- AI、任务、渲染、详情、掉落都只读同一套 NPC 数据
如果后面代码里还出现下面这些关键词,基本都说明分轨没有删干净:
- `sceneMonsters`
- `sceneHostileNpcs`
- `monsterIds`
- `hostileNpcPresetId`
- `createSceneMonstersFromIds`
- `getClosestMonster`
- `TurnActor = 'monster'`
## 这份文档的使用方式
后续正式开始改造时,建议把文件分成三批执行:
1. “直接删掉”的入口脚本
2. “改名并并回 NPC 主链路”的桥接脚本
3. “仅保留素材职责”的 renderer / preset 文件
不要继续接受“名字叫 NPC但内部仍然先转成 monster 再跑”的中间态。

View File

@@ -1,50 +0,0 @@
# 工程优化审查总览
这一组只保留仍能指导当前 Rust / SpacetimeDB 主线的工程审查入口。早期连续扫描的有效结论已经合并到本 README 的“融合结论”,不再保留逐日旧稿。
## 当前推荐入口
1. [SERVER_NODE_FREEZE_AND_DEPRECATION_2026-04-24.md](./SERVER_NODE_FREEZE_AND_DEPRECATION_2026-04-24.md)
这一版是旧 Node 后端冻结、第一批物理删除与后续批次边界记录,明确当前工程只保留 Rust / SpacetimeDB 主线入口。
2. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_F_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_F_2026-04-21.md)
这一版是第六批落地记录,聚焦删除无入口 `questDirector`、旧观察文案 helper、一次性硬编码同步脚本并补齐后端运行时 function catalog 契约覆盖。
3. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_E_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_E_2026-04-21.md)
这一版是第五批落地记录,聚焦旧命名 re-export、空路由骨架、旧发布服务、前端 prompt 镜像与无入口编辑器壳层的物理删除。
4. [RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md](./RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md)
这一版专项扫描 `src/` 下 RPG 开头脚本明确运行时开局、快照、story engine、战斗后处理、NPC/背包规则和创作链残留后门中应迁到 `server-rs` 的逻辑。
5. [RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_COMPLETION_CHECK_2026-04-28.md](./RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_COMPLETION_CHECK_2026-04-28.md)
这一版复核 RPG 前端脚本后端迁移完成度确认开局、快照、存档、story engine / prompt context、`camp_travel_home_scene`、NPC、背包/锻造、战斗后处理、结果页保存前 normalize 与角色资产 prompt 主链均已收口。
6. [FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md](./FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md)
这一版是本轮前端越界逻辑专项审计,专门汇总当前仍应继续迁到 `server-rs` 的运行时、鉴权、生成编排与本地真相残留。
7. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_D_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_D_2026-04-21.md)
这一版是第四批落地记录,聚焦未接入业务的数据生成产物、测试专用 stub 与对应配置残留出清。
8. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_C_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_C_2026-04-21.md)
这一版是第三批落地记录,聚焦鉴权真相收口,先移除前端保存自动登录用户名/密码的本地真相,并明确运行时快照前置写入为什么当前还不能硬砍。
9. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_B_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_B_2026-04-21.md)
这一版是第二批落地记录,聚焦旧主流程壳层、旧 bootstrap 和旧 inventory / forge / equipment flow Hook 的正式出清。
10. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_A_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_A_2026-04-21.md)
这一版是第一批落地记录聚焦高置信度小型孤岛、prompt 壳子、stub 和无入口 modal 的首轮清理。
11. [CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md](./CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md)
这一版是面向当前仓库状态的优化点盘点,适合直接拿来排优先级和拆执行批次。
12. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md)
这一版是对 `2026-04-19` 基线的当前仓库复核,明确哪些问题已经处理、哪些表述需要纠正、热点又迁移到了哪里。
13. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md)
这一版保留原始问题快照和执行回填,适合回看“为什么会有这轮清理与边界收口”。
## 融合结论
- 最新专项审计已经把“前端哪些逻辑还该后移到后端”收敛到 6 类:运行时快照、本地 token、本地浏览历史、NPC 委托换单、quest/runtime item 混合编排、浏览器 AI orchestration。
- 工程大清洗已经开始进入实际执行阶段,首批高置信度小型孤岛和残留壳子已开始清理。
- 第二批已经开始清理旧主流程壳层与旧 flow Hook当前主工程的“现行入口”和“历史入口”边界正在变得更清楚。
- 第三批已经先完成鉴权真相收口的一段,前端不再保存自动登录用户名/密码;运行时快照链仍需先补后端 contract再继续往前删。
- 第四批已经继续收掉未接入业务的数据生成产物、测试专用 stub 与对应脚本/配置残留,主工程里的“假数据主源”进一步减少。
- 第五批已经继续收掉旧命名 re-export、空路由骨架、旧发布 service、前端 prompt 镜像与无入口编辑器壳层,主工程里的“假入口”和“假 prompt 主源”进一步减少。
- 第六批已经继续收掉无入口 `questDirector`、旧观察文案 helper、一次性硬编码同步脚本并修复 function catalog 对后端运行时契约的覆盖缺口。
- 当前仓库已经完成“旧 dev 插件链路删除、根目录噪音清理、`server-node -> src/**` 反向依赖切断”这批第一阶段任务。
- 当前如果想直接判断“今天先优化什么”,优先看 `CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md`
- 当前的新重点已经进一步收敛到三类:未接线孤岛模块、前端残留的运行时/鉴权真相、热点向 prompt/runtime profile/平台入口壳层迁移。
- 早期三轮工程扫描的结论已经聚合为一条长期规则:工程化不能只看目录和拆分动作,必须覆盖真实主链、质量门禁、绿色基线、关键模块豁免和 build warning。
- `2026-04-19` 这一轮把问题压实到了四类:仓库噪音、旧 dev 入口残留、前端越界运行时逻辑、巨型热点文件。
- `2026-04-20` 这一轮进一步确认:前两类已经阶段性完成,当前真正剩下的是边界尾巴和新热点迁移。
- 如果是要看当前清理和边界收口的最新状态,优先看 `ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md`
- 如果是要看“当前可执行的优化点清单”,优先看 `CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md`
- 如果是要做长期重构方案,从 `2026-04-19``2026-04-20` 与当前 dead-code batch 记录开始即可。

View File

@@ -1,650 +0,0 @@
# RPG 前端脚本后端迁移审计2026-04-28
## 0. 审计目标
这份文档只回答一个问题:
**当前 `src/` 下 RPG 开头或 RPG 目录内的前端脚本中,哪些逻辑已经越过“前端只做表现”的边界,应该继续迁移到 `server-rs` / SpacetimeDB 后端。**
本轮只做审计与迁移拆分,不改业务代码。
## 1. 审计范围
本轮扫描范围:
1. `src/RpgRuntimeApp.tsx`
2. `src/components/rpg-*/*.ts`
3. `src/components/rpg-*/*.tsx`
4. `src/hooks/rpg-*/*.ts`
5. `src/services/rpg-*/*.ts`
6. `src/services/rpgRuntimeChatTypes.ts`
不把测试文件作为迁移对象,但会参考测试暴露的当前行为。
本轮依据:
1. `docs/reference/RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md`
2. `docs/audits/engineering/FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md`
3. `docs/technical/RPG_PROMPT_FRONTEND_REMOVAL_AND_SERVER_RS_MIGRATION_2026-04-28.md`
4. `docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md`
5. `spacetimedb-concepts` / `spacetimedb-rust` / `spacetimedb-typescript` skill 约束
## 2. 判断标准
### 2.1 应迁后端
只要逻辑满足下面任一项,就不应继续以浏览器前端作为正式真相:
1. 生成或解释 `GameState`、任务、背包、装备、NPC 状态、战斗状态、剧情记忆。
2. 决定开局初始状态、场景落点、遭遇、初始物品、初始装备。
3. 决定 action 是否合法、价格、数量、奖励、掉落、复活、场景推进。
4. 组装正式 AI prompt / story context / fallback generation。
5. 持久化完整运行时快照,或让前端上传整份 `GameState` 作为后端写入依据。
6. 对服务端返回的快照做业务补丁,补齐战斗阵型、场景跳转、任务推进等正式状态。
7. 解释 Agent session / draft / result preview 哪份才是创作真相。
### 2.2 可留前端
下面这些可以继续留在前端:
1. 面板开关、tab、modal、loading、error、按钮禁用展示。
2. 动画播放、镜头、过场、打字机效果、临时视觉态。
3. API client、请求封装、SSE 文本消费。
4. 纯展示 view model 适配,但不能改变正式业务含义。
5. 用户正在编辑的表单草稿,但保存、校验、合并、发布门禁以后端为准。
## 3. 结论先行
当前 RPG 前端脚本中仍有 9 类逻辑应该迁移到后端:
1. `P0` 运行时开局 `GameState` 装配。
2. `P0` runtime story 网关中的“客户端带快照解析”和快照补丁。
3. `P0` 前端自动保存整份运行时快照。
4. `P0` 前端 story engine / chapter / world mutation / prompt context 编排。
5. `P0` 战斗胜负后处理、死亡复活、战斗后章节推进。
6. `P1` NPC 交易、送礼、价格、数量与库存校验。
7. `P1` 背包、装备、锻造可用性与配方视图。
8. `P1` RPG 创作 profile 生成的非浏览器 legacy AI 回退。
9. `P1` RPG 创作结果页自动保存、session/result preview 真相优先级与 legacy preview fallback。
一句话判断:
**前端已经大面积开始调用 server-rs但仍保留了“后端兼容不完整时由前端补真相”的模式这部分需要继续收口否则 SpacetimeDB 表和 reducer 永远无法成为唯一运行时真相源。**
## 4. 高优先级迁移项
## 4.1 `P0` 运行时开局 `GameState` 装配仍在前端
### 代码证据
主要文件:
1. `src/hooks/rpg-session/useRpgSessionBootstrap.ts`
关键逻辑:
1. `PLAYER_BASE_MAX_HP = 180`
2. `mergeStarterInventoryItems(...)`
3. `normalizeExplicitStarterCategory(...)`
4. `inferExplicitStarterSlot(...)`
5. `buildExplicitCustomWorldRoleStarterState(...)`
6. `createInitialGameState()`
7. `handleCustomWorldSelect(...)`
8. `handleCharacterSelect(...)`
对应代码表现:
1.`58` 行到第 `151` 行:前端把角色初始物品编译成 `InventoryItem`,推断装备槽,并直接生成 `runtimeMetadata`
2.`185` 行到第 `235` 行:前端创建完整初始 `GameState`
3.`517` 行到第 `563`前端选择世界时重置运行时上下文、progression、story memory、战斗态。
4.`572` 行到第 `710` 行:前端选角时决定开局场景、开局 NPC、初始 NPC state、初始装备、血蓝、货币、背包、任务、队伍、战斗字段。
### 为什么应迁
这些不是表现层逻辑,而是“新开局正式状态”的创建权。只要开局状态仍由浏览器本地装配,后端就只能被动接收一份客户端快照,无法成为 runtime session 的唯一真相。
### 后端落点
建议收口到:
1. `server-rs/crates/api-server/src/runtime_story.rs`
2. `server-rs/crates/api-server/src/story_sessions.rs`
3. `server-rs/crates/module-runtime/src/lib.rs`
4. `server-rs/crates/module-story/src/lib.rs`
5. `server-rs/crates/module-inventory/src/lib.rs`
6. `server-rs/crates/module-progression/src/lib.rs`
7. `server-rs/crates/module-runtime-story-compat/src/game_state.rs`
SpacetimeDB 方向:
1. 新增或扩展 `begin_rpg_runtime_session` reducer。
2. 由 reducer 基于 `ctx.sender()`、作品 profile id、角色 id 创建 runtime session。
3. 在表中写入初始场景、角色状态、背包、装备、NPC 状态、任务、progression。
4. 前端只调用 reducer / API 并订阅或读取后端返回的 session view model。
注意:外部 AI / 网络调用不能放进 reducer如果开局需要 LLM应由 `api-server` / `platform-llm` 完成生成,再把确定结果写入 SpacetimeDB。
## 4.2 `P0` runtime story 网关仍要求客户端携带快照解析
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts`
关键逻辑:
1.`112` 行到第 `123` 行:`buildRuntimeSnapshotRequest(...)` 把本地 `gameState``currentStory` 一并提交给后端。
2.`125` 行到第 `213` 行:`bridgeServerSceneTravelSnapshot(...)` 在前端补齐服务端旅行快照。
3.`216` 行到第 `317` 行:`bridgeServerNpcBattleSnapshot(...)` 在前端补齐 NPC 战斗阵型。
4.`323` 行到第 `341` 行:拉取 option catalog 时仍基于客户端快照。
5.`387` 行到第 `430` 行:正式动作结算后继续由前端对服务端快照做桥接修补。
### 为什么应迁
这类桥接逻辑意味着服务端返回结果不是完整业务真相,前端还在做二次裁决:
1. 推断下一场景。
2. 补齐场景 preset。
3. 补齐遭遇预览。
4. 补齐任务推进。
5. 补齐 NPC 战斗阵型。
6. 改写 `GameState` 中的战斗、场景、任务字段。
这些都应该由后端 runtime action resolver 在同一个事务边界内完成。
### 后端落点
建议收口到:
1. `server-rs/crates/api-server/src/runtime_story/compat/game_state.rs`
2. `server-rs/crates/api-server/src/runtime_story/compat/npc_actions.rs`
3. `server-rs/crates/api-server/src/runtime_story/compat/equipment_actions.rs`
4. `server-rs/crates/api-server/src/runtime_story/compat/quest_actions.rs`
5. `server-rs/crates/module-runtime-story-compat/src/options.rs`
6. `server-rs/crates/module-runtime-story-compat/src/battle.rs`
7. `server-rs/crates/module-runtime-story-compat/src/view_model.rs`
迁移后前端应该只提交:
1. `sessionId`
2. `functionId`
3. `runtimePayload`
4. 当前 UI 所需的弱表现参数
后端应该基于已存 session state 解析动作,不再要求前端上传完整 `GameState`
### 本轮落地口径2026-04-28
本轮先收口 `runtime story` 网关这条 P0 主链:
1. 前端 `rpgRuntimeStoryGateway.ts` 不再构造或上传 `snapshot.gameState / currentStory`
2. 前端 `rpgRuntimeStoryClient.ts` 的状态读取统一走 `GET /api/runtime/story/state/:sessionId`,动作结算统一走 `POST /api/runtime/story/actions/resolve` 且请求体只包含 `sessionId / clientVersion / action`
3. `idle_travel_next_scene` 的下一场景、场景 preset、遭遇清理、战斗清理与旅行计数由 `server-rs/crates/api-server/src/runtime_story/compat.rs` 在持久化快照上完成。
4. `npc_fight / npc_spar` 的战斗阵型、`sceneHostileNpcs`、战前 encounter 归档与战斗字段由 `server-rs/crates/api-server/src/runtime_story/compat/npc_actions.rs` 在后端完成。
5. 前端只消费后端返回的 hydrated snapshot 和 presentation不再保留 `bridgeServer*Snapshot` 业务补丁。
### 本轮落地验收2026-04-28
1. `rpgRuntimeStoryGateway.ts` 已删除 `buildRuntimeSnapshotRequest``bridgeServerSceneTravelSnapshot``bridgeServerNpcBattleSnapshot` 以及 NPC 战斗/旅行快照补丁依赖option catalog 和 action resolve 都只提交 `sessionId / clientVersion / action / payload`
2. `rpgRuntimeStoryClient.ts` 已移除前端 `RuntimeStorySnapshotRequest` 入参;状态读取固定为 `GET /api/runtime/story/state/:sessionId`,动作结算请求体不再带 `snapshot` 字段。
3. `server-rs` 已覆盖 `idle_travel_next_scene``npc_fight``npc_spar` 的后端快照补齐测试,并将 route 级测试改为从服务端持久化 session 读取,不再通过旧 save 端点上传整份前端快照铺垫。
4. 已执行验收:`cargo check -p api-server --message-format short``cargo test -p api-server runtime_story_ -- --nocapture``npm run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts`
5. 搜索确认:`src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts``src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 不再包含 `/state/resolve``buildRuntimeSnapshotRequest``bridgeServer*``params.snapshot` 或动作请求上传 `snapshot.gameState/currentStory` 的路径。
## 4.3 `P0` 自动保存仍由前端上传整份运行时快照
### 代码证据
主要文件:
1. `src/hooks/rpg-session/useRpgSessionPersistence.ts`
2. `src/services/rpg-runtime/rpgSnapshotClient.ts`
关键逻辑:
1. `useRpgSessionPersistence.ts``12` 行到第 `23` 行:前端判断哪些运行态可以入正式存档。
2.`90` 行到第 `150` 行:前端把 `gameState / bottomTab / currentStory` 作为 payload 写入远端。
3.`216` 行到第 `234` 行:前端监听本地 `gameState` 变化并自动保存。
4.`236` 行到第 `263` 行:手动存档也上传整份本地快照。
5. `rpgSnapshotClient.ts``31` 行到第 `48` 行:`putRpgSaveSnapshot(...)` 直接发送 `SavedGameSnapshotInput`
### 为什么应迁
运行时存档不是普通表单保存它是玩家进度、任务、背包、战斗、NPC、故事状态的正式真相。当前模式仍是“前端本地状态变化 -> 上传整份快照 -> 后端持久化”,后端缺少对状态版本、动作来源、事务一致性的最终解释权。
### 后端落点
建议改成:
1. 后端 action resolver 每次动作结算时事务性写入 session state。
2. 自动保存只变成后端已有 session state 的归档或 checkpoint。
3. 前端最多提交 `sessionId``saveSlot``bottomTab` 等非业务 UI 状态。
4. `runtime_save.rs` 负责快照归档,不再信任浏览器传入的整份 `GameState`
涉及表结构时同步检查并更新 `migration.rs`
## 4.4 `P0` story engine / chapter / world mutation 仍在前端编排
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/progressionActions.ts`
2. `src/hooks/rpg-runtime-story/storyContextBuilder.ts`
3. `src/hooks/rpg-runtime-story/useRpgRuntimeStoryController.ts`
关键逻辑:
1. `progressionActions.ts``193` 行到第 `293` 行:前端生成章节任务并写入 `quests`
2.`295` 行到第 `395` 行:前端收集 story signals、推进 thread、同伴反应、章节、旅程 beat、阵营/世界 mutation、setpiece。
3.`644` 行到第 `782` 行:前端提交生成后状态、追加 story history、执行 AI 后再做 recovery。
4. `storyContextBuilder.ts``1` 行到第 `51` 行:前端引入大量 story engine director / graph / memory / prompt context 组件。
5. `useRpgRuntimeStoryController.ts``80` 行到第 `103` 行:前端把 `generateInitialStory / generateNextStep` 注入本地 story request runtime。
### 为什么应迁
这些逻辑不是 UI而是叙事引擎状态机
1. 章节任务发放。
2. 线索和 thread 更新。
3. 同伴关系变化。
4. 营地事件与 setpiece 触发。
5. 世界 mutation。
6. 生成上下文组织。
7. fallback / recovery。
这些都应作为后端 story session 的 reducer / domain service而不是 React hook 的本地副作用。
### 后端落点
建议收口到:
1. `server-rs/crates/api-server/src/runtime_story/compat/ai.rs`
2. `server-rs/crates/api-server/src/prompt/rpg/runtime_chat.rs`
3. `server-rs/crates/module-story/src/lib.rs`
4. `server-rs/crates/module-progression/src/lib.rs`
5. `server-rs/crates/module-runtime-story-compat/src/core.rs`
6. `server-rs/crates/module-runtime-story-compat/src/view_model.rs`
前端保留:
1. `currentStory` 展示。
2. loading / error。
3. SSE 增量文本。
4. 视觉过场。
## 4.5 `P0` 战斗后处理、死亡复活、章节推进仍在前端补真相
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/storyChoiceRuntime.ts`
2. `src/hooks/rpg-runtime-story/postBattleFlow.ts`
关键逻辑:
1. `storyChoiceRuntime.ts``1` 行到第 `6` 行仍引用掉落、背包合并等本地规则。
2.`304` 行到第 `390` 行:服务端动作返回后,前端仍判断死亡、等待复活、构造复活状态、构造战斗胜利后状态与 story。
3. `postBattleFlow.ts``86` 行到第 `105` 行:前端构造战斗胜利后的 `GameState`
4.`107` 行到第 `158` 行:前端推进 scene act 并构造战斗后 story / deferred options。
5.`161` 行到第 `205` 行:前端决定复活回第一场景、恢复血蓝、清战斗态、重建首幕 encounter preview。
### 为什么应迁
死亡、胜利、切磋完成、复活、掉落、战斗后章节推进都属于正式玩法结果。前端可以播死亡动画和过场,但不能决定:
1. 玩家是否死亡。
2. 玩家在哪里复活。
3. 复活后血蓝。
4. 战斗是否结束。
5. 任务或章节是否推进。
6. deferred options 里有哪些正式可选动作。
### 后端落点
建议收口到:
1. `server-rs/crates/module-combat/src/lib.rs`
2. `server-rs/crates/module-runtime-story-compat/src/battle.rs`
3. `server-rs/crates/module-runtime-story-compat/src/view_model.rs`
4. `server-rs/crates/api-server/src/story_battles.rs`
5. `server-rs/crates/api-server/src/runtime_story/compat/presentation.rs`
前端保留:
1. 根据后端返回的 `presentation.battle` 播动画。
2. 根据后端返回的 `nextStory` 展示文本和选项。
3. 根据后端返回的 `GameState` 或 view model 渲染血条、角色、敌人。
## 5. 中优先级迁移项
## 5.1 `P1` NPC 交易、送礼的价格和库存校验仍在前端
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/npcInteraction.ts`
关键逻辑:
1.`186` 行到第 `218` 行:前端读取 NPC / 玩家物品,计算交易单价、最大数量、数量 clamp。
2.`629` 行到第 `683` 行:前端按本地价格和库存判断买入 / 卖出是否允许,再提交后端。
3.`685` 行到第 `702` 行:前端检查礼物是否存在,并构造送礼 action。
### 当前判断
这条链已经比早期更好:正式交易和送礼会调用 `resolveRpgRuntimeChoice(...)` 走后端。但前端仍在承担“能不能买、能不能卖、价格是多少、数量是否合法”的第一层裁决。
### 迁移建议
后端应该:
1. 计算价格。
2. 校验库存和货币。
3. 校验 NPC 亲和度影响。
4. 原子更新玩家库存、NPC 库存、货币和 NPC state。
5. 返回可展示的交易 view model 与错误原因。
前端可以保留:
1. 当前选择的 itemId / quantity。
2. 后端返回价格的展示。
3. modal 开关和数量输入。
### 本次落地设计2026-04-28
为避免“前端只是少算了一处价格,但仍在提交前裁决库存”的半迁移,本次按下面边界收口:
1. `server-rs` 在 runtime story compat 状态桥中编译 `gameState.runtimeNpcInteraction` 派生 view包含当前 NPC、玩家货币、购买列表、出售列表、赠礼列表、服务端单价、服务端库存上限、赠礼好感增益与不可选原因。
2. `resolve_npc_trade_action(...)``resolve_npc_gift_action(...)` 仍是唯一正式结算入口,提交后重新校验 mode、itemId、quantity、NPC 库存、玩家背包数量与玩家货币;前端展示的 view 只作为 UI 提示,不作为可信输入。
3. 前端 `npcInteraction.ts` 不再读取本地 NPC / 玩家物品计算单价、总价、最大数量,也不再因为本地库存或货币不足提前阻断提交;确认时只提交 `{ mode, itemId, quantity }`,后端返回错误即展示错误。
4. 前端 `NpcModals.tsx` 只渲染 `runtimeNpcInteraction` 中的价格、库存和赠礼增益;按钮禁用仅保留“未选择条目 / 服务端 view 标记不可选 / 当前操作正在提交”的表现态,不再用浏览器本地价格和货币重新裁决。
5. 验收测试必须覆盖后端购买成功、NPC 库存不足拒绝、玩家货币不足拒绝、出售成功、玩家背包数量不足拒绝、赠礼成功、礼物不存在拒绝;前端测试覆盖确认交易/赠礼在本地库存或货币不满足时仍提交后端,由后端错误接管。
## 5.2 `P1` 背包、装备、锻造可用性与配方视图仍在前端
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/inventoryActions.ts`
关键逻辑:
1.`44` 行到第 `52` 行:前端基于 `playerInventory / playerCurrency / worldType` 计算 forge recipe views。
2.`97` 行到第 `180` 行:前端先查找物品 / 装备 / 配方,再提交后端 reducer 风格 action。
### 当前判断
正式动作已经走后端,这是正确方向。但 `forge recipe view` 和本地物品存在性判断仍会让前端表现出“它知道业务规则”的形态。
### 迁移建议
后端应该提供:
1. 当前玩家背包 view。
2. 当前装备 view。
3. 当前可锻造配方 view。
4. 每个动作的 disabled / reason。
前端只渲染后端 view并把用户选择提交给后端。
## 5.3 `P1` RPG 创作 profile 生成仍保留非浏览器 legacy AI 回退
### 迁移回填2026-04-28
已完成本项迁移:
1. `src/services/rpg-creation/rpgCreationGenerationClient.ts` 删除 `LegacyAiModule``loadLegacyAiModule``typeof window === 'undefined'` 分支。
2. `src/services/rpg-creation/rpgCreationGenerationClient.node.test.ts` 锁定 node 环境也只 mock `requestJson`,不再触发前端 legacy AI 模块。
3. `src/services/rpg-creation/index.ts` 删除 `generateLegacyCustomWorldProfile` 旧命名导出。
4. `server-rs/crates/api-server/src/app.rs` 挂载 `POST /api/runtime/custom-world/profile`
5. `server-rs/crates/api-server/src/custom_world.rs` 复用 `generate_custom_world_foundation_draft(...)` 生成 profile并补齐前端结果页所需稳定字段。
6. 迁移说明见 `docs/technical/RPG_CREATION_PROFILE_GENERATION_BACKEND_MIGRATION_2026-04-28.md`
### 迁移前历史证据
主要文件:
1. `src/services/rpg-creation/rpgCreationGenerationClient.ts`
关键逻辑:
1.`9` 行到第 `19` 行:前端 client 仍定义 `LegacyAiModule = typeof import('../ai')` 并动态加载旧 AI 模块。
2.`32` 行到第 `35` 行:非浏览器环境直接调用 `aiClient.generateCustomWorldProfile(...)`
3.`43` 行到第 `51` 行:浏览器环境才请求 `/api/runtime/custom-world/profile`
### 为什么应迁
这与 `RPG_PROMPT_FRONTEND_REMOVAL_AND_SERVER_RS_MIGRATION_2026-04-28.md` 的目标冲突。即使该分支主要服务测试或 SSR它仍保留了“前端可持有 RPG 生成逻辑和 prompt”的技术后门。
### 迁移建议
1. 删除 `import('../ai')` 回退。
2. 非浏览器环境也应通过 `server-rs` client / test mock 调用后端 contract。
3. 若测试需要离线能力,应 mock `requestJson`,不要恢复前端生成链。
后端落点:
1. `server-rs/crates/api-server/src/custom_world.rs`
2. `server-rs/crates/api-server/src/custom_world_foundation_draft.rs`
3. `server-rs/crates/api-server/src/prompt/rpg/foundation_draft.rs`
4. `server-rs/crates/platform-llm/`
## 5.4 `P1` 创作结果页保存与 Agent session 真相优先级仍在前端编排
### 代码证据
主要文件:
1. `src/components/rpg-entry/useRpgCreationResultAutosave.ts`
2. `src/components/rpg-entry/useRpgEntryLibraryDetail.ts`
3. `src/services/rpg-creation/rpgCreationPreviewAdapter.ts`
关键逻辑:
1. `useRpgCreationResultAutosave.ts` 负责 profile signature、自动保存请求去重、`upsertRpgWorldProfile(...)`、Agent session refresh。
2. `useRpgEntryLibraryDetail.ts` 负责判断 draft work 应打开 Agent workspace、生成过程页还是结果页。
3. `rpgCreationPreviewAdapter.ts``resultPreview` 缺失时回退读取 `draftProfile.legacyResultProfile`
### 当前判断
这部分大多是创作 UI 编排,不能简单全迁。但下面三件事属于后端真相:
1. 保存前 profile normalize。
2. Agent session / result preview / legacyResultProfile 的优先级。
3. 发布门禁与草稿是否可进入结果页。
### 迁移建议
后端应该提供一个稳定的 `creation_result_view``work_detail_view`
1. 已标准化 profile。
2. 当前 session / work 状态。
3. 是否可发布。
4. 应打开的前端 stage。
5. 缺失或失败时的恢复指令。
前端只根据 view model 切页面,不再自行解释 session 阶段。
## 5.5 `P1` 角色资产工坊默认 prompt 与缓存合并规则仍在前端
### 本轮落地状态2026-04-28
已完成默认 prompt 与缓存合并规则迁移:
1. 新增 `server-rs/crates/api-server/src/prompt/rpg/role_asset_studio.rs`,作为角色资产工坊默认 prompt、legacy prompt 过滤、逐动作 prompt 缓存合并的后端主源。
2. 新增 `POST /api/runtime/custom-world/asset-studio/role/{character_id}/workflow`,由前端提交当前正在编辑的角色快照,后端返回合并后的 workflow view。
3. 新增 `PUT /api/runtime/custom-world/asset-studio/role/{character_id}/workflow`,复用 OSS JSON 缓存保存,并补齐 `animationPromptTextByKey` 持久化。
4. `RpgCreationRoleAssetStudioModalImpl.tsx` 不再调用 `buildDefaultRolePromptBundle`,也不再包含 legacy prompt 判断和缓存合并函数。
5. 删除 `src/prompts/customWorldRolePromptDefaults.ts` 与旧兼容 re-export避免 `src/` 继续持有角色资产默认 prompt 主源。
### 代码证据
主要文件:
1. `src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModalImpl.tsx`
2. `src/components/rpg-creation-asset-studio/useRoleVisualCandidateWorkflow.ts`
3. `src/components/rpg-creation-asset-studio/useRoleAnimationWorkflow.ts`
关键逻辑:
1. `RpgCreationRoleAssetStudioModalImpl.tsx``54` 行到第 `85` 行:前端合并默认动画 prompt、缓存 prompt、legacy prompt。
2.`564` 行到第 `591` 行:前端调用 `buildDefaultRolePromptBundle(baseRole)` 生成默认视觉 / 动作 prompt。
3.`807` 行到第 `830` 行:前端保存工作流缓存,包括 prompt、草稿、asset id、animation map。
4. `useRoleVisualCandidateWorkflow.ts``useRoleAnimationWorkflow.ts` 把 prompt 文本直接作为生成 payload。
### 当前判断
用户正在编辑的 prompt 草稿可以留在前端表单里,但默认 prompt 生成、legacy prompt 判断、缓存合并、生成参数默认值不应继续散落在 UI modal 内。
### 迁移建议
后端应该提供:
1. `GET /api/runtime/custom-world/asset-studio/role/:id/workflow`
2. `PUT /api/runtime/custom-world/asset-studio/role/:id/workflow`
3. `POST /api/runtime/custom-world/assets/role-visual-candidates`
4. `POST /api/runtime/custom-world/assets/role-animation`
并在 `server-rs/crates/api-server/src/prompt/rpg/` 或资产 prompt 模块中统一默认 prompt 生成。
前端只保留:
1. prompt 文本框。
2. 参考图上传 UI。
3. 候选图 / 动画预览。
4. 用户点击生成、应用、保存的交互入口。
## 6. 可保留在前端的 RPG 脚本
以下脚本当前主要承担表现层或 request client 职责,可以暂时保留:
1. `src/components/rpg-runtime-shell/*`
2. `src/components/rpg-runtime-panels/RpgAdventurePanel.tsx`
3. `src/components/rpg-runtime-panels/RpgAdventurePanelOverlays.tsx`
4. `src/components/rpg-entry/RpgEntryHomeView.tsx`
5. `src/components/rpg-entry/RpgEntryWorldDetailView.tsx`
6. `src/services/rpg-runtime/rpgRuntimeRequest.ts`
7. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts`
8. `src/services/rpg-runtime/rpgSnapshotClient.ts`
9. `src/services/rpg-creation/rpgCreationAgentClient.ts`
10. `src/services/rpg-creation/rpgCreationAssetClient.ts`
11. `src/services/rpg-creation/rpgCreationLibraryClient.ts`
12. `src/services/rpg-creation/rpgCreationRuntimeClient.ts`
但要注意:
1. client 文件可以保留请求封装,但不应继续加入 fallback 生成逻辑。
2. UI 文件可以根据后端 view model 展示 disabled / reason但不能本地重算业务可用性。
3. snapshot client 可以保留读取 / 删除 / 归档入口,但不应让前端上传整份 `GameState` 作为正式真相。
## 7. 推荐迁移顺序
### 第一阶段:收运行时真相
优先迁移:
1. `useRpgSessionBootstrap.ts`
2. `rpgRuntimeStoryGateway.ts`
3. `useRpgSessionPersistence.ts`
目标:
1. 后端创建 session。
2. 后端保存 session state。
3. runtime action 不再依赖客户端完整快照。
4. 前端不再补战斗 / 旅行快照。
### 第二阶段:收 story engine 与战斗后处理
优先迁移:
1. `progressionActions.ts`
2. `storyContextBuilder.ts`
3. `storyChoiceRuntime.ts`
4. `postBattleFlow.ts`
目标:
1. 后端统一处理 story history、thread、chapter、companion reaction、world mutation。
2. 后端统一处理死亡、复活、胜利、切磋完成、战斗后选项。
3. 前端只播放 presentation。
### 第三阶段:收 NPC / 背包 / 锻造可用性
优先迁移:
1. `npcInteraction.ts`
2. `inventoryActions.ts`
目标:
1. 后端提供交易 / 礼物 / 背包 / 锻造 view model。
2. 前端不再重算价格、数量、配方、动作合法性。
### 第四阶段:收创作链残留后门
优先迁移:
1. `rpgCreationGenerationClient.ts`
2. `useRpgCreationResultAutosave.ts`
3. `useRpgEntryLibraryDetail.ts`
4. `rpgCreationPreviewAdapter.ts`
5. `RpgCreationRoleAssetStudioModalImpl.tsx`
目标:
1. 移除 `import('../ai')`
2. 后端输出稳定 result/work view。
3. 后端统一角色资产工坊默认 prompt 和缓存合并规则。
## 8. 后端实现注意事项
### 4.3 落地记录:自动保存 checkpoint 化2026-04-28
本轮已完成 `自动保存仍由前端上传整份运行时快照` 的迁移:
1. `src/hooks/rpg-session/useRpgSessionPersistence.ts` 自动保存与手动保存不再构造 `gameState / currentStory` 上传体,只提交 `sessionId / bottomTab` checkpoint 元数据。
2. `src/services/rpg-runtime/rpgSnapshotClient.ts``PUT /api/runtime/save/snapshot` 请求体改为 `RuntimeSaveCheckpointInput`,前端保存链路只请求后端刷新 checkpoint。
3. `server-rs/crates/api-server/src/runtime_save.rs` 改为读取已存在的服务端 `runtime_snapshot`,校验 `runtimeSessionId` 一致后刷新 `savedAt / bottomTab / runtimeStats.playTimeMs / lastPlayTickAt`,不再信任浏览器传入完整运行态。
4. 旧式 `gameState / currentStory` 上传体会被 `PutRuntimeSaveCheckpointRequest``deny_unknown_fields` 拒绝无服务端快照、session 不一致、preview/test 快照都会返回冲突。
5. 本轮不涉及 SpacetimeDB 表结构变更,因此 `server-rs/crates/spacetime-module/src/migration.rs` 无需调整。
对应测试:
1. `src/services/rpg-runtime/rpgSnapshotClient.test.ts` 覆盖保存请求体不含 `gameState / currentStory`
2. `src/hooks/runtimeAuthGuards.test.tsx` 覆盖自动保存只提交 checkpoint。
3. `server-rs/crates/api-server/src/runtime_save.rs` 覆盖旧式整快照上传拒绝、缺少服务端快照冲突、session 不一致冲突、成功 checkpoint 沿用服务端快照真相。
按 SpacetimeDB 约束,后续落地时要遵守:
1. reducer 不返回数据,前端通过订阅 / 查询读取 view model。
2. reducer 使用 `ctx.sender()` 做鉴权,不信任前端传入身份。
3. reducer 必须确定性,不能访问网络、文件、外部随机。
4. LLM、OSS、图片生成等外部 I/O 放在 `api-server` / `platform-*` crate 中,再把确定结果写回 SpacetimeDB。
5. 前端调用 reducer 使用生成绑定和对象参数,不编辑生成代码。
6. 涉及表结构修改时同步更新 `migration.rs`
7. 修改后端代码后统一执行 `npm run api-server`,并跑对应自动测试。
## 9. 最小验收标准
后续迁移完成后,前端 RPG 脚本应满足:
1. 搜索 `src/hooks/rpg-*` 不再出现创建完整 `GameState` 的逻辑。
2. runtime action 请求不再携带完整 `gameState`
3. 前端不再出现 `bridgeServer*Snapshot` 这类业务补丁函数。
4. 前端不再 `putSnapshot({ gameState, currentStory })`
5. 前端不再动态 `import('../ai')`
6. NPC 交易、送礼、背包、锻造的价格、合法性、结果都以后端 view model 为准。
7. 战斗死亡、复活、胜利后状态都以后端返回为准。
8. 创作结果页打开哪个 stage 由后端 work/session view 指示。
## 10. 一句话结论
当前 RPG 前端脚本最需要迁移的不是 UI 组件,而是仍残留在 hooks / gateway / client 里的:
**开局造状态、运行时带快照解析、前端补服务端快照、自动保存整份 GameState、story engine 编排、战斗后处理、NPC/背包/锻造规则裁决,以及 RPG 创作生成和结果预览的 legacy 后门。**

View File

@@ -1,177 +0,0 @@
# RPG 前端脚本后端迁移完成度核验2026-04-28
## 1. 核验结论
本次按 `RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md` 中列出的应迁后端项逐项检查当前代码。
结论:**应迁移项已全部迁移完成。**
当前状态:
1. `已完成`10 项。
2. `部分完成`0 项。
3. `未发现完全未启动`0 项。
本轮重新核查的变化:
1. 上次残留的 `RPG 创作结果页` 保存前 profile normalize 已完成后端化。
2. `camp_travel_home_scene` 已完成后端收口:正式点击统一走 `/api/runtime/story/actions/resolve`目标场景、encounter preview、`scenesTraveled` 与快照持久化由 `server-rs` 裁决。
## 2. 核验口径
### 2.1 判定为已完成
满足以下条件才记为已完成:
1. 前端不再构造正式业务状态。
2. 前端不再上传完整 `GameState` 作为后端写入依据。
3. 前端不再用本地规则裁决价格、库存、掉落、复活、章节推进、结果页真相优先级。
4. 前端只保留请求封装、UI 展示、表单草稿、loading/error、动画表现和按钮禁用展示。
### 2.2 判定为部分完成
满足以下任一情况记为部分完成:
1. 主链已经迁到 `server-rs`,但旧前端分支仍可被正式入口调用。
2. 后端已经提供 view/action但前端仍保留影响业务真相的 normalize、fallback 或 AI context 编排。
3. 后端只覆盖部分状态机,前端仍负责另一部分正式状态推进。
## 3. 逐项结果
| 原审计项 | 当前状态 | 核验结论 |
| --- | --- | --- |
| `P0` 运行时开局 `GameState` 装配 | 已完成 | 正式开局状态由 `server-rs/crates/api-server/src/runtime_story/compat/bootstrap.rs` 创建并持久化;前端 `useRpgSessionBootstrap.ts` 只保留选择页占位态和 `beginRpgRuntimeStorySession(...)` 调用。 |
| `P0` runtime story 网关客户端快照解析/补丁 | 已完成 | `rpgRuntimeStoryGateway.ts` 不再有 `buildRuntimeSnapshotRequest` / `bridgeServer*Snapshot``rpgRuntimeStoryClient.ts` 读取 `/state/:sessionId`,动作提交 `/actions/resolve`,不再上传完整 `snapshot.gameState/currentStory`。 |
| `P0` 自动保存整份运行时快照 | 已完成 | `useRpgSessionPersistence.ts` / `rpgSnapshotClient.ts` 保存链路只提交 `sessionId/bottomTab` checkpoint`runtime_save.rs` 从服务端已有快照刷新 checkpoint并测试拒绝旧式完整快照上传。 |
| `P0` story engine / chapter / world mutation / prompt context 编排 | 已完成 | 后端已有 `project_story_engine_after_action(...)``build_runtime_story_prompt_context(...)``/story/initial``/story/continue``sessionId` 时只从服务端 snapshot 投影 world / character / history / prompt context`camp_travel_home_scene` 正式点击也已统一进入后端 resolver不再由前端拼装场景迁移、encounter preview 或 runtimeStats。 |
| `P0` 战斗胜负后处理、死亡复活、战斗后章节推进 | 已完成 | `battle_* / inventory_use` 正式点击统一走 `runServerRuntimeChoiceAction(...)` 与后端 `/actions/resolve``storyChoiceContinuation.ts` 对战斗 / 逃脱 / 物品动作加硬保护,不再裁决掉落、任务推进、死亡复活或战后 story`postBattleFlow.ts` 正式状态构造函数已删除。 |
| `P1` NPC 交易/送礼价格数量库存校验 | 已完成 | 前端 `npcInteraction.ts` 已改为消费 `runtimeNpcInteraction` view 并只提交 `{ mode, itemId, quantity }`;后端 `npc_actions.rs` / `npc_support.rs` 负责价格、库存、货币、赠礼好感和原子更新。 |
| `P1` 背包/装备/锻造可用性与配方视图 | 已完成 | 前端 `inventoryActions.ts` 读取 `loadRpgRuntimeInventoryView(...)`,根据后端 action/view 提交;后端 `view_model.rs` / `forge.rs` 生成背包、装备槽、配方、`canCraft/enabled/reason`。 |
| `P1` RPG 创作 profile 生成 legacy AI 回退 | 已完成 | `rpgCreationGenerationClient.ts` 只调用 `/api/runtime/custom-world/profile`,不再动态 `import('../ai')` 或导出 `generateLegacyCustomWorldProfile`。 |
| `P1` 创作结果页保存与 Agent session/result preview 真相优先级 | 已完成 | 后端已提供 `GET /api/runtime/custom-world/agent/sessions/:sessionId/result-view`,统一 `targetStage/profileSource/canAutosaveLibrary/canSyncResultProfile`,前端不再直接读取 `legacyResultProfile`;保存前 canonicalize 已迁到 `server-rs``normalizeRpgEntryAgentBackedProfile(...)` 现在只透传兼容旧导入。 |
| `P1` 角色资产工坊默认 prompt 与缓存合并规则 | 已完成 | 默认 prompt、legacy prompt 过滤、逐动作缓存合并已在 `server-rs/crates/api-server/src/prompt/rpg/role_asset_studio.rs`;前端 modal 只调用 workflow API、保存用户草稿和发起生成/发布。 |
## 4. 已完成的具体收尾点
### 4.1 story engine / prompt context 主链与 `camp_travel_home_scene` 已完成后端收口
当前后端已经处理动作结算后的确定性 story projector
1. `server-rs/crates/module-runtime-story-compat/src/story_engine.rs`
2. `server-rs/crates/api-server/src/runtime_story/compat.rs`
本轮已补齐 prompt context 后端 projector
1. `server-rs/crates/module-runtime-story-compat/src/prompt_context.rs`
2. `server-rs/crates/api-server/src/runtime_story/compat.rs`
3. `server-rs/crates/api-server/src/runtime_chat.rs`
4. `server-rs/crates/api-server/src/runtime_chat_plain.rs`
完成状态:
1. 前端不再决定 `conversationSituation``conversationPressure`、NPC 对话上下文、party relationship notes、scene pressure 文本等正式 prompt context。
2. 前端 story initial / continue 在有 `runtimeSessionId` 时只提交 `sessionId / clientVersion / choice / lastFunctionId / requestOptions` 等轻量字段。
3. 后端从已持久化 runtime snapshot 投影 `worldType / playerCharacter / sceneHostileNpcs / storyHistory / context`,旧 payload 字段只保留兼容。
4. 角色私聊、NPC 对话、NPC 单轮聊天、NPC 招募对话已支持 `sessionId`,有 session 时上下文同样以后端 snapshot 为准。
5. 前端奖励领取、NPC 聊天闭合与旧 `deferredRuntimeState` 兼容分支不再写入 `storyEngineMemory`章节、scene act、thread、world mutation 等正式叙事记忆只以后端快照为准。
本轮验收:
1. `cargo test -p module-runtime-story-compat prompt_context --manifest-path server-rs\Cargo.toml` 覆盖后端 prompt context projector 对场景、NPC 披露阶段、对话压力和关系态度的投影。
2. `cargo test -p shared-contracts runtime_story_ai_request --manifest-path server-rs\Cargo.toml` 覆盖 story AI 请求可只携带 `sessionId` 的共享契约。
3. `cargo test -p api-server runtime_story_initial_uses_server_snapshot_prompt_context_when_session_id_present --manifest-path server-rs\Cargo.toml` 覆盖 `/story/initial` 在 session 模式下以后端快照覆盖浏览器传入的 world / character / context。
4. `npm run test -- src/services/ai.test.ts src/hooks/rpg-runtime-story/storyRequestCoordinator.test.ts src/hooks/rpg-runtime-story/useRpgRuntimeStoryController.test.tsx` 覆盖前端 story / chat 请求在 session 模式下只发送轻量 payload。
5. `npm run test -- src/hooks/rpg-runtime-story/sessionActions.test.ts src/hooks/rpg-runtime-story/choiceActions.test.ts src/hooks/rpg-runtime-story/npcEncounterActions.test.ts` 覆盖前端旧 UI 分支不再回写后端拥有的 `storyEngineMemory`
本轮收尾:
1. `packages/shared/src/contracts/rpgRuntimeStoryAction.ts` 已把 `camp_travel_home_scene` 纳入 `TASK5_RUNTIME_FUNCTION_IDS` / `SERVER_RUNTIME_FUNCTION_IDS`
2. `server-rs/crates/api-server/src/runtime_story/compat.rs``camp_travel_home_scene` resolver 已承接前端旧分支的正式状态职责:解析目标场景、写入 `currentScenePreset`、清理战斗/遭遇残留、递增 `scenesTraveled`、生成 encounter preview并让后续故事和持久化继续走后端 snapshot 主链。
3. 目标场景解析以后端为准:优先接收兼容 payload 中的 `targetSceneId`,其次使用内置角色主场景映射,自定义世界按角色与 landmark 绑定解析,再回退到当前场景前向连接或首个冒险场景。
4. `src/hooks/rpg-runtime-story/choiceActions.ts` 不再调用 `runCampTravelHomeChoice(...)``camp_travel_home_scene` 即使命中旧展示 helper也会按服务端 function id 统一进入 `runServerRuntimeChoiceAction(...)`
5. `src/hooks/rpg-runtime-story/storyChoiceRuntime.ts` 已删除 `runCampTravelHomeChoice(...)`,前端不再保留正式场景迁移构造函数。
已消除风险:
1. `camp_travel_home_scene` 不再由浏览器决定目标场景、运行时统计或 encounter preview。
2. 正式离营状态已经满足“前端只提交 action后端返回 hydrated snapshot”的边界。
本轮验收补充:
1. `cargo test -p api-server runtime_story_route_boundary_camp_travel_home_scene_is_server_owned --manifest-path server-rs\Cargo.toml` 覆盖点击后 hydrated snapshot 进入角色主场景、生成 encounter preview、递增 `scenesTraveled` 并持久化。
2. `cargo test -p api-server runtime_story --manifest-path server-rs\Cargo.toml` 覆盖 runtime story 相关后端回归。
3. `npm run test -- src/hooks/rpg-runtime-story/choiceActions.test.ts` 覆盖 `camp_travel_home_scene` 只调用后端 resolver不触发旧本地旅行分支。
4. `npm run test -- src/hooks/rpg-runtime-story/storyChoiceRuntime.test.ts src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts` 覆盖服务端 runtime choice presentation 与 story client 轻量 payload。
### 4.2 本地战斗 continuation 已收口到后端
当前后端已经有战斗终局收口:
1. `server-rs/crates/module-runtime-story-compat/src/post_battle.rs`
2. `server-rs/crates/module-runtime-story-compat/src/battle.rs`
3. `server-rs/crates/api-server/src/runtime_story/compat.rs`
本轮已完成前端旧正式分支收口:
1. `src/hooks/rpg-runtime-story/choiceActions.ts` 不再保留 `shouldResolveCombatChoiceLocally(...)`,所有服务端 function id 都统一进入 `runServerRuntimeChoiceAction(...)`
2. `src/hooks/rpg-runtime-story/storyChoiceContinuation.ts``battle_* / inventory_use` 以及被分类为 `battle / escape` 的动作加硬保护,误入时只报错回退,不会写掉落、任务、复活或战后 story。
3. `src/hooks/rpg-runtime-story/storyChoiceRuntime.ts` 删除本地敌对 NPC 掉落 reward helper不再调用 `rollHostileNpcLoot(...)` / `addInventoryItems(...)` 构造正式战斗奖励。
4. `src/hooks/rpg-runtime-story/postBattleFlow.ts` 与对应测试删除,前端不再保留 `buildPostBattleVictoryState(...)``buildPostBattleVictoryStory(...)``buildRevivedFirstSceneState(...)``buildDeathStory(...)` 作为正式状态构造入口。
已消除风险:
1. `battle_* / inventory_use` 不再因 `inBattle``currentBattleNpcId`、可见 story option 等状态回落到本地结算。
2. 本地 continuation 不再调用敌对 NPC 掉落、背包合并、敌对 NPC 任务推进。
3. 前端不再构造死亡复活状态、胜利后 story、deferred options 和章节推进。
本轮验收:
1. `choiceActions.test.ts` 覆盖 `battle_use_skill``battle_attack_basic` stale option、`inventory_use` 均只调用后端 resolver不触发 `buildResolvedChoiceState(...)` / `playResolvedChoice(...)`
2. `storyChoiceRuntime.test.ts` 保留服务端 battle presentation 验收,确认胜利 / 失败最终采用服务端 hydrated snapshot。
3. 搜索确认 `src/hooks/rpg-runtime-story` 不再包含 `shouldResolveCombatChoiceLocally``buildPostBattleVictory*``buildRevivedFirstSceneState``buildDeathStory``buildHostileNpcBattleReward`
### 4.3 创作结果页保存前 normalize 已完成后端化
后端已经负责 Agent result-view
1. `server-rs/crates/api-server/src/custom_world.rs`
2. `packages/shared/src/contracts/rpgCreationResultView.ts`
3. `src/services/rpg-creation/rpgCreationAgentClient.ts`
重新核查结果:
1. `src/components/rpg-entry/rpgEntryShared.ts`
2. `src/components/rpg-entry/useRpgCreationResultAutosave.ts`
3. `server-rs/crates/api-server/src/custom_world.rs`
当前完成状态:
1. `normalizeRpgEntryAgentBackedProfile(...)` 现在直接返回原始 `profile`,注释明确保存前 canonicalize 已迁到 `server-rs`
2. `stringifyRpgEntryAgentBackedProfile(...)` 现在只做 `JSON.stringify(profile)`,不再触发前端 normalize。
3. `put_custom_world_library_profile(...)` 写入作品库前调用 `canonicalize_custom_world_library_profile_payload(payload.profile)`
4. `serialize_sync_result_profile_action_payload(...)` 会在 Agent `sync_result_profile` action payload 中对 `profile` 执行 `canonicalize_custom_world_profile_before_save(...)`
5. 后端测试 `sync_result_profile_payload_is_canonicalized_on_server``custom_world_library_profile_payload_is_canonicalized_on_server` 已覆盖保存前 canonicalize。
本项不再计为未迁移残留。
## 5. 已完成项的保留边界
以下前端残留可以保留,不视为未迁移:
1. `useRpgSessionBootstrap.ts``createSelectionGameState()`:只服务选择页占位,正式开局不使用它造运行时真相。
2. `NpcModals.tsx` 的数量 stepper、价格展示和按钮禁用只消费后端 view不重新裁决价格或库存。
3. `inventoryActions.ts``submitInventoryAction(...)`:只读取后端 action 的 `enabled/reason`,不本地计算规则。
4. 角色资产工坊 modal 中的 prompt 输入框与缓存保存:这是用户正在编辑的 UI 草稿,默认 prompt 和合并规则已由后端 workflow 输出。
5. `playServerBattlePresentation(...)`:只播放临时动画态,最终 `GameState/currentStory` 仍以服务端 snapshot 为准。
## 6. 后续建议
本轮核验范围内的应迁项已经收口。后续建议转为质量维护:
1. 继续把 function catalog / 旧文档里“本地规则结算”的历史描述逐批改成当前后端归属,避免误导后续开发。
2. 新增 runtime function 时先补后端 resolver / view / contract再让前端接展示入口保持“前端不造正式状态”的边界。
3. 对仍保留的前端本地 continuation 只允许处理非服务端 function id凡进入 `SERVER_RUNTIME_FUNCTION_IDS` 的动作都应有 route 级测试。
## 7. 一句话结论
**当前迁移已经完成开局、快照、存档、story engine / prompt context 主链、`camp_travel_home_scene` 离营迁移、NPC、背包/锻造、战斗后处理、profile 生成、创作结果页 normalize 和角色资产 prompt 主链;本核验范围内不再保留前端正式状态裁决残留。**

View File

@@ -1,129 +0,0 @@
# server-node 冻结隔离说明2026-04-24
## 1. 当前状态
`server-node/` 已进入冻结隔离状态,不再作为可运行、可扩展、可引用的后端工程使用。
冻结原因:项目后端主线已经切到 `server-rs/` 的 Rust + SpacetimeDB 多 crate 方案,继续保留可执行的 `server-node/` 入口会误导后续开发并增加提示词资产、AI 工作流与运行态逻辑的迁移漂移风险。
## 2. 冻结边界
1. 禁止新增任何以 `server-node/` 为目标的运行脚本、开发入口、CI 入口或工程依赖。
2. 禁止新增从前端、Rust 后端、脚本或配置主动调用 `server-node/` 的逻辑。
3. 禁止在 `server-node/` 内继续新增业务能力;后续能力必须落到 `server-rs/` 对应 crate。
4. 历史文档、审计文档、迁移基线中允许保留 `server-node/` 作为旧系统来源说明,但不得把它描述成当前推荐实现。
5. 第一批物理删除后,提示词资产与提示词相关工作流继续按迁移核对项追踪,不再恢复旧工程目录。
## 3. 删除前迁移核对项
以下资产曾作为删除前核对项。第一批物理删除后,旧实现不再从工作区直接读取;如需继续核对能力缺口,只允许通过历史提交、迁移文档或 `server-rs/` 已迁移实现追溯:
1. `server-node/src/prompts/customWorldEntityPrompts.ts`:自定义世界实体生成 prompt。
2. `server-node/src/prompts/customWorldSceneNpcPrompts.ts`:自定义世界场景 NPC prompt。
3. `server-node/src/prompts/questPrompts.ts`:任务意图识别 prompt。
4. `server-node/src/prompts/runtimeItemPrompts.ts`:运行时物品意图识别 prompt。
5. `server-node/src/prompts/customWorldOrchestratorPrompts.ts`:旧 Custom World JSON 生成与修复 prompt。
6. `src/services/ai.ts``src/prompts/customWorldPrompts.ts` 中仍由前端承载的 AI orchestration / prompt 编排。
## 4. 工程防线
1. 第一批物理删除后,根目录 `package.json` 不再保留 `server-node:*``dev:node``check:server-node-freeze` 等旧入口。
2. Vite 与本地开发脚本默认只指向 Rust `api-server`,不再保留 Node/Rust 后端切换开关。
3. 历史文档允许保留旧 `server-node` 字样,但新增工程入口、脚本、依赖、运行说明不得再指向旧 Node 后端。
4. 若后续需要恢复旧能力,只能迁移到 `server-rs/` 对应 crate 或 Axum facade不恢复 `server-node/` 工程目录。
## 5. 后续处理顺序
1. 继续核对提示词资产与 prompt 工作流是否已完整落到 Rust 主线。
2. 继续把前端残留业务编排迁入 `server-rs/`
3. 清理技术索引中容易误导当前入口的 Node / Express 文案。
4. 保留历史审计材料,但不得把旧 Node 后端描述为当前推荐实现。
## 6. 已确认迁移项
### 6.1 场景幕背景图提示词
2026-04-25 已把旧 Node 自动资产链路中的场景幕背景图提示词包装迁移到 Rust 主线:
1. 旧来源:`server-node/src/services/customWorldAgentAutoAssetService.ts``buildSceneActPrompt(...)`
2. 新主源:`server-rs/crates/api-server/src/custom_world.rs``build_scene_act_background_image_prompt(...)`
3. 使用位置:`generate_draft_foundation_act_backgrounds(...)` 收集 `sceneChapterBlueprints[].acts[]` 后,先构造幕背景图专用提示词,再调用 `generate_custom_world_scene_image_for_profile(...)`
4. 保留语义:世界名、场景名、幕标题、幕摘要、幕目标、过渡钩子、主角色、辅助角色、世界气质、背景描述,以及“只生成环境背景,不出现角色立绘、站位 UI、对白框、按钮或文字”的约束。
5. 迁移边界:`server-node/` 仅作为历史来源说明,不再参与运行;后续调整统一修改 Rust 主源。
## 7. 第一批安全删除记录2026-04-25
本批次开始把冻结隔离升级为物理删除。执行依据是项目后端主线已固定为 `server-rs/` 的 Rust + SpacetimeDB 多 crate 方案,旧 `server-node/` 不再作为可运行、可扩展、可引用的工程目录保留。
### 7.1 删除范围
1. 删除 `server-node/` 目录本体,旧实现只允许通过历史提交、迁移文档和已迁移到 `server-rs/` 的代码追溯。
2. 删除旧 Node 后端专用入口:`scripts/dev-node.mjs``scripts/server-node-frozen.mjs``scripts/check-server-node-freeze.mjs``scripts/server-node-freeze-baseline.json``scripts/smoke-server-node.ts``scripts/smoke-same-origin-stack.ts``scripts/m7-api-compare.ts``scripts/deploy.sh``scripts/update.sh``view-llm-logs.ps1`
3. 根目录 `package.json` 删除 `server-node:*``dev:node``m7:api-compare``check:server-node-freeze` 等旧入口,并移除 `express``@types/express` 依赖。
4. `npm run dev` 改为启动 Rust 本地栈Vite 默认只代理到 Rust `api-server`,不再保留 `GENARRATIVE_BACKEND_STACK` 的 Node/Rust 双栈切换口。
5. 清理 `.gitignore` 中只服务 `server-node/` 的忽略规则,并同步 `README.md``.env.example``server-rs/README.md``scripts/dev-server/README.md`
### 7.2 暂不处理范围
1. 历史 PRD、审计、迁移基线中的 `server-node` 文案暂时保留为历史记录,不在第一批中大规模改写。
2. `backend-rewrite-tasklist/` 中以旧 Node 后端为对照的迁移材料暂时保留,作为后续核对 Rust 主线能力缺口的历史审计输入。
3. `src/services/ai.ts``src/prompts/customWorldPrompts.ts` 的前端残留编排不属于本批 Node 后端删除范围;后续继续按“前端只负责表现,业务逻辑进入 `server-rs/`”单独收口。
### 7.3 后续批次建议
1. 技术文档索引中的 Node / Express 后端条目只保留为历史资料,不再作为当前入口或推荐方案。
2. 后续如继续整理历史文档,只把仍描述 `Express / PostgreSQL` 为当前目标架构的文字修正为“历史阶段口径”。
3. 继续把前端残留业务逻辑迁入 `server-rs`;涉及 SpacetimeDB 的设计、实现、脚本和绑定继续显式使用相关 skill。
### 7.4 本轮安全核对结果
2026-04-25 本轮开始分批删除时,已确认第一批工程入口层面满足以下条件:
1. 工作区根目录下已不存在 `server-node/` 物理目录。
2. `scripts/` 下已不存在旧 Node 后端专用运行、冻结、smoke、API 对比脚本。
3. 根目录 `package.json` 不再包含 `server-node:*``dev:node``m7:api-compare``check:server-node-freeze` 入口。
4. `package.json``package-lock.json` 不再包含 `express``@types/express``pg``postgres` 依赖包。
5. `README.md``scripts/dev-server/README.md``server-rs/README.md``vite.config.ts``scripts/*.mjs``src/``packages/``server-rs/` 未发现仍主动启动或调用 `server-node` 的当前工程入口。
### 7.5 第二批删除边界
第二批不再删除可运行工程代码,而是清理“容易误导当前实现口径”的历史文档索引:
1. 只修正文档中仍把 `server-node`、Express 或 PostgreSQL 描述为当前推荐后端的句子。
2. 保留审计、PRD、迁移基线中作为历史事实、旧实现来源、能力对照的 `server-node` 引用。
3. 不大规模重写包含中文剧情、需求、审计结论的历史文档,避免把真实历史上下文抹平。
4. 若发现某个历史文档仍指导新开发继续写 Node 后端,先把该文档改为“历史阶段口径”,再继续工程处理。
## 8. 开发命令与脚本复核2026-04-26
本轮按“`server-node/` 已完全移除”的状态复核当前开发入口、脚本和工程配置,确认不再保留旧 Node 后端或 Express 运行路径。
### 8.1 已复核范围
1. 根目录 `package.json``package-lock.json`
2. 根目录 `README.md``.env.example``.gitignore``vite.config.ts`
3. `scripts/``.github/``jenkins/``server-rs/` 下的已跟踪文本文件。
### 8.2 复核结论
1. `package.json` 中不存在 `server-node:*``dev:node``m7:api-compare``check:server-node-freeze` 等旧入口。
2. `scripts/` 下不存在 `dev-node.mjs``smoke-server-node.ts``m7-api-compare.ts``smoke-same-origin-stack.ts` 等旧 Node 后端脚本。
3. `package.json``package-lock.json` 中不存在 `express``@types/express``pg``postgres` 依赖。
4. 当前开发入口继续固定为 `npm run dev``npm run dev:web``npm run api-server` 与 Rust / SpacetimeDB 相关脚本,不恢复旧 Node 后端切换开关。
## 9. Caddy 本地服务入口移除2026-04-26
`serve:caddy` 仅服务旧的 dist 本地代理验证链路,不再属于当前 Rust / SpacetimeDB 主开发入口。本轮删除该入口和配套文件,避免开发命令继续暴露第二套本地服务方式。
### 9.1 删除范围
1. 根目录 `package.json` 删除 `serve:caddy`
2. 删除 `scripts/run-caddy-dev.mjs`
3. 删除 `tools/Caddyfile.dev`
4. `.env.example` 删除 `CADDY_API_UPSTREAM` 样例变量。
### 9.2 后续口径
1. 本地完整联调继续使用 `npm run dev`
2. 单独前端联调继续使用 `npm run dev:web` 并通过 Vite 代理到 Rust `api-server`
3. 生产包预览继续使用 Vite `preview`,不恢复 Caddy 专用开发入口。

View File

@@ -1,91 +0,0 @@
# 中文乱码位置清单
更新时间:`2026-03-24`
## 说明
- 本文档用于记录仓库内已确认或高置信度疑似存在中文乱码的位置。
- 当前这份文档是重建版本;原有的 [`docs/audits/text/CHINESE_MOJIBAKE_INVENTORY.md`](/E:/Repos/Genarrative/docs/audits/text/CHINESE_MOJIBAKE_INVENTORY.md) 本身也已经乱码,因此已整体替换。
- 本次整理依据:
- 仓库内旧清单中的完整文件/行号信息
- 本轮人工复核时再次直接看到的明显乱码位置
- 由于仓库内同时存在“文件内容已写坏”和“终端/工具显示失真”两类情况,下面清单优先保留高置信位置,便于后续逐项修复。
## 扫描范围
- 已纳入:`src/``docs/`、根目录文档与元数据文件
- 已排除:`.git/``node_modules/``dist/`、纯图片资源目录
## 高置信位置
### 文档与元数据
- [`docs/experience/AGENT_UI_CHANGELOG.md`](/E:/Repos/Genarrative/docs/experience/AGENT_UI_CHANGELOG.md)1, 3, 7, 9, 11-18, 24, 26-28, 32-33, 37, 39, 41-43, 47, 49, 51, 53-66, 68, 72, 74, 77-79, 83, 87, 89-90, 94, 96, 98-100, 104, 106-112, 116
- [`UI_CODING_STANDARD.md`](/E:/Repos/Genarrative/UI_CODING_STANDARD.md)3, 91, 104, 108, 112, 156, 158, 160-166
- [`metadata.json`](/E:/Repos/Genarrative/metadata.json)2-3
### 组件层
- [`src/components/AdventurePanel.tsx`](/E:/Repos/Genarrative/src/components/AdventurePanel.tsx)57, 65
- [`src/components/CharacterPanel.tsx`](/E:/Repos/Genarrative/src/components/CharacterPanel.tsx)37, 65-66, 91-95, 102-103
- [`src/components/GameCanvas.tsx`](/E:/Repos/Genarrative/src/components/GameCanvas.tsx)240, 462
- [`src/components/GameShell.tsx`](/E:/Repos/Genarrative/src/components/GameShell.tsx)108, 116, 124, 138, 171, 181
- [`src/components/InventoryPanel.tsx`](/E:/Repos/Genarrative/src/components/InventoryPanel.tsx)55, 58, 82-83, 181-184, 189, 191
- [`src/components/MapModal.tsx`](/E:/Repos/Genarrative/src/components/MapModal.tsx)105, 108, 136
- [`src/components/MedievalNpcAnimator.tsx`](/E:/Repos/Genarrative/src/components/MedievalNpcAnimator.tsx)124
- [`src/components/NpcVisualEditor.tsx`](/E:/Repos/Genarrative/src/components/NpcVisualEditor.tsx)65, 69-71, 403, 440, 444, 446, 464, 467, 470, 482, 569, 571, 585, 610, 628, 662, 690, 694-695, 697, 722, 751, 759, 775, 777, 781, 824
- [`src/components/PresetEditor.tsx`](/E:/Repos/Genarrative/src/components/PresetEditor.tsx)34-37, 43-44, 94, 96, 349, 470, 472, 480, 482, 512, 516, 519, 525, 568, 612, 618, 637, 639, 643, 645, 652, 661, 677, 740, 769, 771, 779, 781, 806, 809, 820, 831, 835, 837, 840, 848, 871, 894, 916, 918, 930, 932, 950, 953, 956, 960, 962, 990, 1004, 1006, 1012, 1018, 1024, 1030, 1036, 1064, 1120, 1122, 1130, 1132, 1150, 1153, 1156, 1172-1175, 1180, 1182, 1186, 1188, 1199, 1203-1204, 1208, 1240, 1242
### 数据层
- [`src/data/characterPresets.ts`](/E:/Repos/Genarrative/src/data/characterPresets.ts)97, 102, 104, 107, 129, 132-133, 142, 144, 170, 276, 302, 470, 496, 531, 540, 566, 699-700, 729, 972
- [`src/data/medievalNpcVisuals.ts`](/E:/Repos/Genarrative/src/data/medievalNpcVisuals.ts)103, 115, 117, 119, 136, 154, 156, 161, 167, 174, 177, 189, 226, 235-236, 241, 244-245, 249-254, 256-257, 260, 262, 274, 278, 288, 451-453, 565, 568, 577, 592
- [`src/data/monsterPresets.ts`](/E:/Repos/Genarrative/src/data/monsterPresets.ts)41-42, 54, 60-61, 79-80, 92, 98-99, 117-118, 136-137, 155-156, 171-173, 185, 191-192, 204, 210-211, 229-230, 242, 248-249, 261, 267-268, 280, 286-287, 304-305, 323-324, 335
- [`src/data/monsters.ts`](/E:/Repos/Genarrative/src/data/monsters.ts)112
- [`src/data/npcInteractions.ts`](/E:/Repos/Genarrative/src/data/npcInteractions.ts)68-71, 80, 82-83, 161, 165, 173, 182, 188-190, 196, 198, 205, 231, 241, 245, 255-260, 272, 296, 319-320, 372, 444-445, 449, 451, 453, 507, 569-570, 578-579, 587-588, 597, 605-606, 615, 617-618, 626-627, 634, 641-643, 652, 661, 665, 670, 672, 676
- [`src/data/scenePresets.ts`](/E:/Repos/Genarrative/src/data/scenePresets.ts)115, 120, 122, 128, 133, 135, 141, 146, 148, 154, 159, 161, 167, 172, 174, 180, 185, 187, 192-193, 198, 200, 205-206, 211, 213, 219, 224, 226, 232, 237, 239, 245, 250, 252, 258, 263, 265, 274, 279, 281, 287, 292, 294, 299-300, 305, 307, 313, 318, 320, 326, 331, 333, 339, 344, 346, 352, 357, 359, 364-365, 370, 372, 377-378, 383, 385, 390-391, 396, 398, 404, 409, 411, 417, 422, 424, 509, 523, 525
- [`src/data/stateFunctions.ts`](/E:/Repos/Genarrative/src/data/stateFunctions.ts)72-73, 80, 95-96, 103, 117-118, 125, 139-140, 147, 161-162, 169, 186-187, 194, 209-210, 217, 237-238, 255-256, 273-274, 294, 311-312, 329-330, 420, 430-431, 433-435, 437-438, 440-442, 444-445, 447, 449, 451-452, 454-456, 458, 460-461, 464, 466, 468, 484-485, 487, 489, 491, 493, 601, 618
### Hooks 与服务层
- [`src/hooks/useCombatFlow.ts`](/E:/Repos/Genarrative/src/hooks/useCombatFlow.ts)54, 56-58, 566
- [`src/services/ai.ts`](/E:/Repos/Genarrative/src/services/ai.ts)200-201, 209-210, 234-235, 269-270, 309, 311, 317, 338, 341, 358, 382
- [`src/services/prompt.ts`](/E:/Repos/Genarrative/src/services/prompt.ts)7-8, 10, 13-15, 19-20, 25-40, 43, 55, 61-62, 64, 66, 74-76, 78-79, 83-84, 87-90, 96, 103-104, 112, 115, 157, 159, 161-162, 164-165, 167-168, 170, 172-173
### 其他源码
- [`src/uiAssets.ts`](/E:/Repos/Genarrative/src/uiAssets.ts)54, 115, 122, 129, 142, 173, 180
## 本轮人工复核补充
以下位置是在本轮实现过程中直接再次看到的明显乱码文本,建议优先复查:
- [`src/hooks/useCombatFlow.ts`](/E:/Repos/Genarrative/src/hooks/useCombatFlow.ts)1094, 1554, 1556-1557
## 处理优先级建议
### 第一批
- `src/components/GameShell.tsx`
- `src/components/InventoryPanel.tsx`
- `src/components/CharacterPanel.tsx`
- `src/data/characterPresets.ts`
- `src/data/npcInteractions.ts`
- `src/data/scenePresets.ts`
- `src/services/prompt.ts`
### 第二批
- `src/components/PresetEditor.tsx`
- `src/components/NpcVisualEditor.tsx`
- `src/data/monsterPresets.ts`
- `src/data/stateFunctions.ts`
- `docs/experience/AGENT_UI_CHANGELOG.md`
- `UI_CODING_STANDARD.md`
## 备注
- 当前文档的目标是“先把位置收拢清楚”,不是直接修复乱码。
- 如果你下一步要我继续,我可以基于这份清单继续做两件事之一:
- 逐文件修复中文乱码
- 先做一个“乱码修复优先级 + 替换建议”文档

View File

@@ -1,236 +0,0 @@
# 当前游戏剧情原文整理与质量评测
日期:`2026-04-07`
## 总结先说
- 当前游戏的剧情骨架已经能让玩家在武侠、仙侠两个世界里感到“我正在追一件事”,整体判断为:**部分达到预期**。
- 强项在于场景残痕、地图推进、NPC 保留、线程结构已经开始互相咬合。
- 短板也很明确:强回收、强情感爆点、真正能改写后续理解的长线后果还没有完全跑起来。
## 方法
- 先把当前仓库里的可扮演角色、场景、场景 NPC、宝藏残痕原文整理出来。
- 再用现有 story engine 模块补出 ThemePack、WorldStoryGraph、ActorNarrativeProfile、KnowledgeGraph、ThreadContract、QA Report 和 Release Gate。
- 最后按“玩家真实会感受到什么剧情”重组样章,并对照 PRD 的经典 RPG 体验目标做评测。
## 武侠世界
### 说明
- “原文”部分整理的是当前仓库角色、场景、NPC 和残痕里已经存在的中文文本。
- “引擎整理”部分是根据这些原文,经过 story engine 的主题包、线程图谱、角色叙事档案和 QA 规则重新编译出的结构化结果。
### 项目内原始剧情文本整理
### 可扮演角色原文
- 剑之公主 / 王庭剑姬
角色原文:以迅疾剑技和正面压制见长,适合喜欢凌厉推进的玩家。
背景原文:王庭旁支出身,自幼被当作执剑者培养。一次宫变让她失去旧有庇护,也背上了亲手追回王室誓剑与真相的责任。
表层来意:以迅疾剑技和正面压制见长,适合喜欢凌厉推进的玩家。
- 神箭游侠 / 流风弓卫
角色原文:擅长远距离压制与精准射击,节奏灵活,机动性很强。
背景原文:曾是边境游骑与斥候,被一场伏击逼得离开旧军阵。如今他只信自己亲眼见过的风向与箭路,却仍背着守住边境故土的旧誓。
表层来意:擅长远距离压制与精准射击,节奏灵活,机动性很强。
- 双刃旅者 / 疾影斥候
角色原文:速度快、侵略性强,适合喜欢持续推进和连段压迫的玩家。
背景原文:她在暗巷与帮派追杀中长大,学会靠速度、直觉和先手活下去。表面上轻快利落,心里却一直在追查那封改变命运的密信去向。
表层来意:速度快、侵略性强,适合喜欢持续推进和连段压迫的玩家。
### 场景角色原文
- 神箭游侠 / 流风弓卫
角色原文:擅长远距离压制与精准射击,节奏灵活,机动性很强。
保留线索:曾是边境游骑与斥候,被一场伏击逼得离开旧军…
- 青鳞毒蛇 / 敌对角色
角色原文:身形细长,吐信极快,最喜欢守在草木掩映和石缝交错之地。
保留线索:青鳞毒蛇长期出没于竹林古道。身形细长,吐信…
- 枯藤伏虫 / 敌对角色
角色原文:像一截会蠕动的枯藤,贴地潜行,适合在林地和湿地里伏击。
保留线索:枯藤伏虫长期出没于竹林古道。像一截会蠕动的…
- 樵夫老周 / 樵夫
角色原文:常在竹海边缘砍柴,对附近路数和兽踪了如指掌。
保留线索:樵夫老周长期出没于竹林古道。常在竹海边缘砍…
- 玄甲战锋 / 重装先锋
角色原文:攻守兼备,推进稳健,适合喜欢扎实前排风格的玩家。
保留线索:他长期担任重装前锋,习惯站在最危险的位置替…
- 石背蜗怪 / 敌对角色
角色原文:驮着厚重石壳缓慢爬行,常盘踞在石阶、桥边与潮湿山路上。
保留线索:石背蜗怪长期出没于山门石阶。驮着厚重石壳缓…
### 场景原文整理
- 竹林古道
场景原文:风过竹叶如刀鸣,窄道蜿蜒向深处,最适合藏伏毒物和游侠。
第一残痕:竹根旁半埋的刀鞘
场景角色:神箭游侠(流风弓卫)、青鳞毒蛇(敌对角色)、枯藤伏虫(敌对角色)
- 山门石阶
场景原文:青石阶层层向上,旧山门半开半掩,守山人与伏兽都能藏得很稳。
第一残痕:裂缝里的铜钥
场景角色:玄甲战锋(重装先锋)、石背蜗怪(敌对角色)、岩甲蛛兽(敌对角色)
- 雨夜长街
场景原文:长街积水映灯,屋檐下尽是藏身空隙,最易碰见追踪者与夜行客。
第一残痕:灯檐下浸湿的布包
场景角色:双刃旅者(疾影斥候)、夜牙潜兽(敌对角色)、孢爆菇灵(敌对角色)
- 荒村断垣
场景原文:残墙和空屋挤成一团,风里总像夹着旧哭声与游荡脚步。
第一残痕:断墙后压着的木匣
场景角色:断骨祟灵(敌对角色)、孢爆菇灵(敌对角色)、守村妇人(遗民)
- 古桥渡口
场景原文:桥面潮湿,渡口雾重,来往之人不多,但每个身影都藏着故事。
第一残痕:桥柱缝里的油纸包
场景角色:双刃旅者(疾影斥候)、石背蜗怪(敌对角色)、夜牙潜兽(敌对角色)
### 引擎整理出的明线
- 旧宫旧案仍在牵动江湖局势:旧宫旧案仍在牵动江湖局势,焦点常落在竹林古道。
- 护送线:边关与地宫残痕正在把旧事重新拖回台前,焦点常落在山门石阶。
- 回收线:当前武侠世界不是单点冒险,而是一张由边关军需、渡口风声、地宫旧痕和宫苑旧案交叉拉紧的追查网络。,焦点常落在雨夜长街。
- 分歧对峙线:沿着场景残痕和人物试探,一步步追清边关与宫苑旧案背后的真相,焦点常落在荒村断垣。
### 引擎整理出的暗线
- 神箭游侠的隐线:神箭游侠并不只是流风弓卫,他与旧宫旧案仍在牵动江湖局势之间还有一段未被说破的牵连。
- 青鳞毒蛇的隐线:青鳞毒蛇并不只是敌对角色,他与护送线之间还有一段未被说破的牵连。
- 枯藤伏虫的隐线:枯藤伏虫并不只是敌对角色,他与回收线之间还有一段未被说破的牵连。
- 樵夫老周的隐线:樵夫老周并不只是樵夫,他与分歧对峙线之间还有一段未被说破的牵连。
- 玄甲战锋的隐线:玄甲战锋并不只是重装先锋,他与旧宫旧案仍在牵动江湖局势之间还有一段未被说破的牵连。
### 场景旧痕
- 竹林古道留下的旧痕:表层残痕是“风过竹叶如刀鸣,窄道蜿蜒向深处,最适合藏伏毒物和游侠。”;压着的真相是“神箭游侠并不只是流风弓卫,他与旧宫旧案仍在牵动江湖局势之间还有一段未被说破的牵连。”
- 山门石阶留下的旧痕:表层残痕是“青石阶层层向上,旧山门半开半掩,守山人与伏兽都能藏得很稳。”;压着的真相是“青鳞毒蛇并不只是敌对角色,他与护送线之间还有一段未被说破的牵连。”
- 雨夜长街留下的旧痕:表层残痕是“长街积水映灯,屋檐下尽是藏身空隙,最易碰见追踪者与夜行客。”;压着的真相是“枯藤伏虫并不只是敌对角色,他与回收线之间还有一段未被说破的牵连。”
- 荒村断垣留下的旧痕:表层残痕是“残墙和空屋挤成一团,风里总像夹着旧哭声与游荡脚步。”;压着的真相是“樵夫老周并不只是樵夫,他与分歧对峙线之间还有一段未被说破的牵连。”
- 古桥渡口留下的旧痕:表层残痕是“桥面潮湿,渡口雾重,来往之人不多,但每个身影都藏着故事。”;压着的真相是“玄甲战锋并不只是重装先锋,他与旧宫旧案仍在牵动江湖局势之间还有一段未被说破的牵连。”
### 玩家在游戏中真实感受到的剧情样章
你来到这个武侠世界,是为追查失落王庭誓剑流入江湖的踪迹。此行最重要的目标,是在诸门派与野心家之前找回誓剑,并逼出宫变幕后之人。 第一眼看到的不是纯说明,而是 边关营地 里的环境压迫:营火与旌旗都带着风沙味,士卒、斥候和异兽都可能在这里短暂停留。
走进边关营地时,玩家实际感受到的核心不是“到了新地图”,而是“营火与旌旗都带着风沙味,士卒、斥候和异兽都可能在这里短暂停留。”。这句场景原文会立刻把体验拉回到“旧宫旧案仍在牵动江湖局势”这条明线。神箭游侠表面只是流风弓卫,但他的公开面是“擅长远距离压制与精准射击,节奏灵活,机动性很强。”,真正压在肩上的却是“找出贩卖军情的人,并截回被转移的军械账册”。而像“废营帐里的箭囊”这样的场景残痕,会把玩家往“这里一定还藏着别的事”那种感觉里继续推。如果继续追下去,边关营地背后会逐渐显出“神箭游侠并不只是流风弓卫,他与旧宫旧案仍在牵动江湖局势之间还有一段未被说破的牵连。”这层旧伤。
走进雨夜长街时,玩家实际感受到的核心不是“到了新地图”,而是“长街积水映灯,屋檐下尽是藏身空隙,最易碰见追踪者与夜行客。”。这句场景原文会立刻把体验拉回到“护送线”这条明线。双刃旅者表面只是疾影斥候,但他的公开面是“速度快、侵略性强,适合喜欢持续推进和连段压迫的玩家。”,真正压在肩上的却是“夺回密信,查清究竟是谁把你推上了被追杀的路”。而像“灯檐下浸湿的布包”这样的场景残痕,会把玩家往“这里一定还藏着别的事”那种感觉里继续推。如果继续追下去,雨夜长街背后会逐渐显出“青鳞毒蛇并不只是敌对角色,他与护送线之间还有一段未被说破的牵连。”这层旧伤。
走进古桥渡口时,玩家实际感受到的核心不是“到了新地图”,而是“桥面潮湿,渡口雾重,来往之人不多,但每个身影都藏着故事。”。这句场景原文会立刻把体验拉回到“回收线”这条明线。双刃旅者表面只是疾影斥候,但他的公开面是“速度快、侵略性强,适合喜欢持续推进和连段压迫的玩家。”,真正压在肩上的却是“夺回密信,查清究竟是谁把你推上了被追杀的路”。而像“桥柱缝里的油纸包”这样的场景残痕,会把玩家往“这里一定还藏着别的事”那种感觉里继续推。如果继续追下去,古桥渡口背后会逐渐显出“枯藤伏虫并不只是敌对角色,他与回收线之间还有一段未被说破的牵连。”这层旧伤。
因此,武侠世界目前最容易让玩家产生真实剧情感的地方,不是某一句高光台词,而是“场景描述 -> 人物保留 -> 残痕线索 -> 线程压力”这条连续链路。它已经能让玩家觉得自己在追一件还没完全揭开的事,但离“经典 RPG 式的强收束和强情感爆点”还差最后一层回响回收。
### 质量评测
整体判断:**部分达成预期**
### 维度评测
- 角色记忆点:达成。当前可扮演角色的人设、背景、开局动机和首遇目标已经能形成第一轮代入,玩家能记住“谁在上路、为什么上路”。
- 低关系也有戏:达成。低好感或首遇 NPC 不再只是“更冷淡”,而是能从当前压力、错位说辞和反应钩子里带出暗线存在感。
- 世界互文与旧史厚度达成。场景、NPC、旧痕和线程已经能互相指向同一批旧事不再只是各自独立的设定块。
- 空间与残痕叙事:达成。地点不是纯背景图,场景描述、宝藏线索和 narrative residue 已经能共同承担“空间会说话”的职责。
- 选择后果与主线抓手:部分达成。当前任务抓手和线程合约已经存在,但真正影响关系、理解和后续回响的后果层还没有被完全跑满。
- 长线回响与收束:未达成。从 QA 结果看,当前版本最明显的短板仍是“已经埋下的线,后面有没有被稳定回收”。这一步决定它能不能真正跨到经典 RPG 质感。
### 客观检查
- Narrative QA4 条明线 / 1 条问题。
- Release Gatewarn。当前版本可继续观察但仍有若干 narrative 风险。
- Simulation共跑了 3 条 simulationending family 1 类,单次最高 QA 问题 1 条。
### 当前主要问题
- 有线程合约尚未在 chronicle 中留下足够的回收痕迹。
## 仙侠世界
### 说明
- “原文”部分整理的是当前仓库角色、场景、NPC 和残痕里已经存在的中文文本。
- “引擎整理”部分是根据这些原文,经过 story engine 的主题包、线程图谱、角色叙事档案和 QA 规则重新编译出的结构化结果。
### 项目内原始剧情文本整理
### 可扮演角色原文
- 剑之公主 / 王庭剑姬
角色原文:以迅疾剑技和正面压制见长,适合喜欢凌厉推进的玩家。
背景原文:王庭旁支出身,自幼被当作执剑者培养。一次宫变让她失去旧有庇护,也背上了亲手追回王室誓剑与真相的责任。
表层来意:以迅疾剑技和正面压制见长,适合喜欢凌厉推进的玩家。
- 神箭游侠 / 流风弓卫
角色原文:擅长远距离压制与精准射击,节奏灵活,机动性很强。
背景原文:曾是边境游骑与斥候,被一场伏击逼得离开旧军阵。如今他只信自己亲眼见过的风向与箭路,却仍背着守住边境故土的旧誓。
表层来意:擅长远距离压制与精准射击,节奏灵活,机动性很强。
- 双刃旅者 / 疾影斥候
角色原文:速度快、侵略性强,适合喜欢持续推进和连段压迫的玩家。
背景原文:她在暗巷与帮派追杀中长大,学会靠速度、直觉和先手活下去。表面上轻快利落,心里却一直在追查那封改变命运的密信去向。
表层来意:速度快、侵略性强,适合喜欢持续推进和连段压迫的玩家。
### 场景角色原文
- 神箭游侠 / 流风弓卫
角色原文:擅长远距离压制与精准射击,节奏灵活,机动性很强。
保留线索:曾是边境游骑与斥候,被一场伏击逼得离开旧军…
- 玄甲战锋 / 重装先锋
角色原文:攻守兼备,推进稳健,适合喜欢扎实前排风格的玩家。
保留线索:他长期担任重装前锋,习惯站在最危险的位置替…
- 秘匣书妖 / 敌对角色
角色原文:像会自行翻页的秘典与宝匣,常在仙门、遗迹与禁制附近浮游。
保留线索:秘匣书妖长期出没于云海仙门。像会自行翻页的…
- 噬雾飞蛾 / 敌对角色
角色原文:借雾气遮身,飞行轨迹诡谲,喜欢围着灵光和人影打转。
保留线索:噬雾飞蛾长期出没于云海仙门。借雾气遮身,飞…
- 守门灵官 / 门官
角色原文:站在门阙侧旁观来者,像在等一份迟迟未到的回报。
保留线索:守门灵官长期出没于云海仙门。站在门阙侧旁观…
- 幽烬灵蝠 / 敌对角色
角色原文:翅翼缭绕灰烬般的灵火,常成群出没于洞天、崖壁与灵脉附近。
保留线索:幽烬灵蝠长期出没于悬空仙岛。翅翼缭绕灰烬般…
### 场景原文整理
- 云海仙门
场景原文:云阶在脚下翻涌,门阙后方灵光不断,来客与守门异物都极显眼。
第一残痕:云阶尽头的灵符匣
场景角色:神箭游侠(流风弓卫)、玄甲战锋(重装先锋)、秘匣书妖(敌对角色)
- 悬空仙岛
场景原文:浮岛边缘风大云急,灵禽与飞蛾总绕着岛沿的光带盘旋。
第一残痕:浮岛边缘的灵羽匣
场景角色:幽烬灵蝠(敌对角色)、噬雾飞蛾(敌对角色)、云栖散修(散修)
- 天宫长廊
场景原文:廊柱之间回响着空灵风声,禁制和书妖都喜欢寄在这类高处回廊里。
第一残痕:廊柱暗槽里的玉简
场景角色:剑之公主(王庭剑姬)、玄甲战锋(重装先锋)、秘匣书妖(敌对角色)
- 灵药花圃
场景原文:灵草灵花层层叠开,香气诱人,却也最容易养出食灵的怪物。
第一残痕:药圃深处的灵壶
场景角色:噬灵妖花(敌对角色)、血瞳妖眼(敌对角色)、药圃执事(药师)
- 寒玉洞天
场景原文:洞壁结着寒玉光泽,地面湿滑,水灵和阴性异物都爱停在这里。
第一残痕:寒玉裂隙里的灵髓
场景角色:青腐泥灵(敌对角色)、幽烬灵蝠(敌对角色)、澄潮灵母(敌对角色)
### 引擎整理出的明线
- 灵脉与封印正在失衡:灵脉与封印正在失衡,焦点常落在云海仙门。
- 追索线:宗门旧案与秘境争夺彼此缠住了当下局势,焦点常落在悬空仙岛。
- 封印失衡线:当前仙侠世界由宗门秩序、秘境余波、灵脉封印和古仙残迹共同推着故事前进,玩家每深入一层,都会撞上新的旧事回响。,焦点常落在天宫长廊。
- 宗门旧案线:顺着灵痕、残识和人物保留,一层层摸清宗门旧案与秘境失衡的根源,焦点常落在灵药花圃。
### 引擎整理出的暗线
- 神箭游侠的隐线:神箭游侠并不只是流风弓卫,他与灵脉与封印正在失衡之间还有一段未被说破的牵连。
- 玄甲战锋的隐线:玄甲战锋并不只是重装先锋,他与追索线之间还有一段未被说破的牵连。
- 秘匣书妖的隐线:秘匣书妖并不只是敌对角色,他与封印失衡线之间还有一段未被说破的牵连。
- 噬雾飞蛾的隐线:噬雾飞蛾并不只是敌对角色,他与宗门旧案线之间还有一段未被说破的牵连。
- 守门灵官的隐线:守门灵官并不只是门官,他与灵脉与封印正在失衡之间还有一段未被说破的牵连。
### 场景旧痕
- 云海仙门留下的旧痕:表层残痕是“云阶在脚下翻涌,门阙后方灵光不断,来客与守门异物都极显眼。”;压着的真相是“神箭游侠并不只是流风弓卫,他与灵脉与封印正在失衡之间还有一段未被说破的牵连。”
- 悬空仙岛留下的旧痕:表层残痕是“浮岛边缘风大云急,灵禽与飞蛾总绕着岛沿的光带盘旋。”;压着的真相是“玄甲战锋并不只是重装先锋,他与追索线之间还有一段未被说破的牵连。”
- 天宫长廊留下的旧痕:表层残痕是“廊柱之间回响着空灵风声,禁制和书妖都喜欢寄在这类高处回廊里。”;压着的真相是“秘匣书妖并不只是敌对角色,他与封印失衡线之间还有一段未被说破的牵连。”
- 灵药花圃留下的旧痕:表层残痕是“灵草灵花层层叠开,香气诱人,却也最容易养出食灵的怪物。”;压着的真相是“噬雾飞蛾并不只是敌对角色,他与宗门旧案线之间还有一段未被说破的牵连。”
- 寒玉洞天留下的旧痕:表层残痕是“洞壁结着寒玉光泽,地面湿滑,水灵和阴性异物都爱停在这里。”;压着的真相是“守门灵官并不只是门官,他与灵脉与封印正在失衡之间还有一段未被说破的牵连。”
### 玩家在游戏中真实感受到的剧情样章
你来到这个仙侠世界,是因为王庭圣印坠入了云海裂隙。此行最重要的目标,是寻回圣印,截断那些企图借它开启天门禁制的野心。 第一眼看到的不是纯说明,而是 星舟甲板 里的环境压迫:甲板横在高天之上,风压和星光都很强,飞行异物最爱在这里盘旋。
走进星舟甲板时,玩家实际感受到的核心不是“到了新地图”,而是“甲板横在高天之上,风压和星光都很强,飞行异物最爱在这里盘旋。”。这句场景原文会立刻把体验拉回到“灵脉与封印正在失衡”这条明线。神箭游侠表面只是流风弓卫,但他的公开面是“擅长远距离压制与精准射击,节奏灵活,机动性很强。”,真正压在肩上的却是“找回星图核心,查清是谁击落了你的船队”。而像“舵台后的星图匣”这样的场景残痕,会把玩家往“这里一定还藏着别的事”那种感觉里继续推。如果继续追下去,星舟甲板背后会逐渐显出“神箭游侠并不只是流风弓卫,他与灵脉与封印正在失衡之间还有一段未被说破的牵连。”这层旧伤。
走进悬空仙岛时,玩家实际感受到的核心不是“到了新地图”,而是“浮岛边缘风大云急,灵禽与飞蛾总绕着岛沿的光带盘旋。”。这句场景原文会立刻把体验拉回到“追索线”这条明线。云栖散修表面只是散修,但他的公开面是“常坐在浮岛边缘打坐,对天风和禁制的变化很敏感。”,真正压在肩上的却是“在悬空仙岛守住自己不愿失去的那一层秩序。”。而像“浮岛边缘的灵羽匣”这样的场景残痕,会把玩家往“这里一定还藏着别的事”那种感觉里继续推。如果继续追下去,悬空仙岛背后会逐渐显出“玄甲战锋并不只是重装先锋,他与追索线之间还有一段未被说破的牵连。”这层旧伤。
走进月湖仙洲时,玩家实际感受到的核心不是“到了新地图”,而是“湖光像铺开的镜面,水灵、章灵与花影都可能从月色里浮出来。”。这句场景原文会立刻把体验拉回到“封印失衡线”这条明线。双刃旅者表面只是疾影斥候,但他的公开面是“速度快、侵略性强,适合喜欢持续推进和连段压迫的玩家。”,真正压在肩上的却是“找到残阵核心,并弄明白信里提到的“第二个你”究竟是谁”。而像“湖岸边漂来的玉匣”这样的场景残痕,会把玩家往“这里一定还藏着别的事”那种感觉里继续推。如果继续追下去,月湖仙洲背后会逐渐显出“秘匣书妖并不只是敌对角色,他与封印失衡线之间还有一段未被说破的牵连。”这层旧伤。
因此,仙侠世界目前最容易让玩家产生真实剧情感的地方,不是某一句高光台词,而是“场景描述 -> 人物保留 -> 残痕线索 -> 线程压力”这条连续链路。它已经能让玩家觉得自己在追一件还没完全揭开的事,但离“经典 RPG 式的强收束和强情感爆点”还差最后一层回响回收。
### 质量评测
整体判断:**部分达成预期**
### 维度评测
- 角色记忆点:达成。当前可扮演角色的人设、背景、开局动机和首遇目标已经能形成第一轮代入,玩家能记住“谁在上路、为什么上路”。
- 低关系也有戏:达成。低好感或首遇 NPC 不再只是“更冷淡”,而是能从当前压力、错位说辞和反应钩子里带出暗线存在感。
- 世界互文与旧史厚度达成。场景、NPC、旧痕和线程已经能互相指向同一批旧事不再只是各自独立的设定块。
- 空间与残痕叙事:达成。地点不是纯背景图,场景描述、宝藏线索和 narrative residue 已经能共同承担“空间会说话”的职责。
- 选择后果与主线抓手:部分达成。当前任务抓手和线程合约已经存在,但真正影响关系、理解和后续回响的后果层还没有被完全跑满。
- 长线回响与收束:未达成。从 QA 结果看,当前版本最明显的短板仍是“已经埋下的线,后面有没有被稳定回收”。这一步决定它能不能真正跨到经典 RPG 质感。
### 客观检查
- Narrative QA4 条明线 / 1 条问题。
- Release Gatewarn。当前版本可继续观察但仍有若干 narrative 风险。
- Simulation共跑了 3 条 simulationending family 1 类,单次最高 QA 问题 1 条。
### 当前主要问题
- 有线程合约尚未在 chronicle 中留下足够的回收痕迹。
## 最终结论
- 如果目标只是“让玩家进入游戏后立刻感觉世界里有事正在发生”,当前文本资产已经够用,且部分环节已经明显跑起来了。
- 如果目标是“对标经典单机 RPG 的强角色回响、强关系后果、强主线收束”,当前版本还只能算走到了一半,最该补的是 payoff 和长线回响。
- 也就是说:**当前剧情原文的底座已经部分达到预期,但还没到可以完全放心交给玩家沉浸式吃剧情的程度。**

View File

@@ -1,276 +0,0 @@
# 游戏 UI / 预设 / 编辑器 / npcInteraction / prompt 文本深度审计
日期:`2026-04-02`
## 说明
- 本文档基于当前仓库源码再次深搜,不直接沿用旧审计结论。
- 审计目标:找出“可能出现在游戏 UI、预设、编辑器 UI、生成链路中的文本”里仍然存在的
- `中文乱码`
- `英文直出`
- `中英混用`
- 本轮重点加查:
- `src/data/npcInteractions.ts`
- `src/services/prompt.ts`
- `src/services/characterChatPrompt.ts`
- `src/services/questPrompt.ts`
- 说明口径:
- “会显示给玩家/编辑器使用者”的文本,按高优先级记录。
- “内部英文枚举/键名,但可能泄露到 prompt、编辑器或预览”的内容也单独记录。
- 已经转成中文且当前复查无明显问题的区域,会标记为“复查通过”。
## 结论摘要
- 当前最严重的文本污染源不是单一 UI而是两条链路同时存在问题
- 运行时弹窗 / 实体详情 / NPC 交易招募弹窗
- `prompt` 生成链路
- `src/services/prompt.ts` 是当前最重灾区,既有大面积中文乱码,也混有英文结构词,会直接影响 AI 生成质量。
- `src/data/npcInteractions.ts` 里“话题 actionText / detailText”大体已是正常中文但商人来源匹配、库存种子物品类别/名称仍有明显乱码,而且阶段枚举仍是英文值。
- 预设编辑器仍然是英文和乱码高密度区,尤其是:
- `CharacterPresetPanel.tsx`
- `MonsterPresetPanel.tsx`
- `SceneNpcPresetPanel.tsx`
- `ScenePresetPanel.tsx`
- `StateFunctionEditor.tsx`
- 自定义世界 NPC/场景编辑器、NPC 视觉编辑器大体已转中文,但还残留 `NPC``AI``Shift` 等中英混用。
## 一、游戏主流程 UI 与运行时面板
### 1.1 复查通过
| 文件 | 行号 | 当前状态 | 说明 |
| --- | --- | --- | --- |
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `47-49` | 通过 | 开场联系方式已改成 `QQ群``微信` |
| `src/components/game-shell/GameShellRuntime.tsx` | `147` | 通过 | 场景名兜底已改成 `当前区域` |
| `src/components/adventure-panel/AdventurePanelOverlays.tsx` | `137-158` | 通过 | 任务目标眉标、宝藏踪迹、切磋会话等主文案已是中文 |
### 1.2 仍有问题
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `81``89` | 中英混用 | `正在生成核心NPC...` | `NPC` 仍在中文进度文案里直出 |
| `src/components/game-shell/GameShellOverlays.tsx` | `151` | 中文乱码 | `闃熶紞` / `鑳屽寘` | 队伍/背包弹层标题已写坏 |
| `src/components/game-shell/GameShellRuntime.tsx` | `200-201` | 英文直出 | `GENARRATIVE` | 运行时头部品牌字样仍为英文 |
| `src/components/adventure-panel/AdventurePanelOverlays.tsx` | `140` | 中英混用 | `未知敌对 NPC` | 已非乱码,但仍混入 `NPC` |
## 二、实体详情、NPC 交互弹窗、交易/招募 UI
### 2.1 `AdventureEntityModal.tsx`
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/AdventureEntityModal.tsx` | `111` | 中文乱码 | `鏁屽 NPC` | hostile badge 已坏,同时混入 `NPC` |
| `src/components/AdventureEntityModal.tsx` | `113``137-146` | 英文直出 | `NPC``Player``Companion` | 标题/副标题兜底值仍为英文 |
| `src/components/AdventureEntityModal.tsx` | `221``264``272``283``296``306` | 英文直出 | `Portrait``Status``Relation``Attributes``Encounter``Inventory` | 六个主区块标题都未本地化 |
| `src/components/AdventureEntityModal.tsx` | `266-267` | 英文直出 | `HP``MP` | 状态条标签未本地化 |
| `src/components/AdventureEntityModal.tsx` | `274-275` | 英文直出 | `Affinity``Recruited: Yes/No` | 关系区块仍是英文 |
| `src/components/AdventureEntityModal.tsx` | `298-301` | 英文直出 | `Name``Context``Type``Battle mode` | 遭遇信息区块仍是英文 |
### 2.2 `NpcModals.tsx`
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/NpcModals.tsx` | `251-252` | 英文直出 | `NPC inventory` / `Your inventory` / `items` | 交易栏标题和数量单位仍是英文 |
| `src/components/NpcModals.tsx` | `272` | 英文直出 | `This NPC has nothing to sell right now.` / `You have nothing to sell right now.` | 空状态未本地化 |
| `src/components/NpcModals.tsx` | `290` | 英文直出 | `Purchase total` / `Sale total` | 交易总价标题未本地化 |
| `src/components/NpcModals.tsx` | `297` | 中文乱码 | 大段价格不足提示 | 购买资金不足提示整段已坏 |
| `src/components/NpcModals.tsx` | `303` | 中文乱码 | 大段默认说明 | 未选中物品时的说明区整段已坏 |
| `src/components/NpcModals.tsx` | `350-399` | 英文直出 | `Item details``NPC item``Stock``Value``Purchase price``Buyback price``Slot``Not equippable``Usable immediately``Tags``None` | 物品详情弹窗几乎整屏英文 |
| `src/components/NpcModals.tsx` | `404` | 中文乱码 + 中英混用 | 含 `MP` 的恢复说明 | 物品使用效果提示已坏 |
| `src/components/NpcModals.tsx` | `465` | 中文乱码 | 好感变动提示 | 交易结果反馈文案已坏 |
| `src/components/NpcModals.tsx` | `500-501` | 英文直出 | `Manage companion slot``Select a current companion to rotate out before recruiting this NPC.` | 招募替换同伴弹窗未本地化 |
## 三、预设编辑器与编辑器 UI
### 3.1 复查通过或基本通过
| 文件 | 行号 | 当前状态 | 说明 |
| --- | --- | --- | --- |
| `src/components/CustomWorldEntityEditorModal.tsx` | `242` | 通过 | 图片路径占位文案已中文化,不再暴露 `URL` |
| `src/components/NpcVisualEditor.tsx` | `463` | 基本通过 | 空状态是中文,但仍混入 `NPC` |
| `src/components/CustomWorldNpcVisualEditor.tsx` | `88-91``552-765` | 基本通过 | 主体编辑项、装备类型、素材、姿态等基本都为中文 |
### 3.2 标签、枚举、面板标题问题
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/shared.ts` | `43` | 英文直出 | `NPC` | 预设编辑器顶层页签仍用英文 |
| `src/components/preset-editor/shared.ts` | `62-67` | 英文枚举外露 | `idle``move``attack``die` | 怪物动画候选仍是英文值 |
| `src/components/preset-editor/shared.ts` | `69-74` | 英文枚举外露 | `steady``burst``mobility``finisher``projectile` | 技能风格候选仍是英文值 |
| `src/components/NpcVisualEditor.tsx` | `702-708` | 中英混用 | `NPC 视觉编辑器` | 中文标题中混入 `NPC` |
| `src/components/NpcVisualEditor.tsx` | `718` | 中英混用 | `当前 NPC` | 选择器标签混入 `NPC` |
| `src/components/NpcVisualEditor.tsx` | `976-977` | 中英混用 | `拖动标记微调 NPC 的预览布局。移动时按住 Shift...` | 混入 `NPC``Shift` |
| `src/components/CustomWorldEntityEditorModal.tsx` | `482-483` | 中英混用 | `AI生成NPC形象``NPC 形象 AI 生成功能仍在开发中。` | 模态标题与副标题仍大量混用英文术语 |
| `src/components/CustomWorldEntityEditorModal.tsx` | `635-636` | 中英混用 | `新增 NPC``编辑 NPC` | 主标题和副标题混入 `NPC` |
| `src/components/CustomWorldEntityEditorModal.tsx` | `734``762-763` | 中英混用 | `AI生成``AI生成场景``场景图片 AI 生成功能仍在开发中。` | 场景生成入口仍混入 `AI` |
| `src/components/CustomWorldEntityEditorModal.tsx` | `792-793` | 中英混用 | `npc-...``自定义NPC...` | 新建 NPC 默认 ID/名称混有英文前缀与 `NPC` |
### 3.3 `CharacterPresetPanel.tsx`
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `262``264``277` | 中文乱码 | `瑙掕壊``淇濆瓨瑙掕壊瑕嗙洊` | 选择卡标题、下拉标签、保存按钮均已坏 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `320``326` | 中文乱码 | `鍔ㄧ敾``闁煎啿...` | 动画/世界标签已坏 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `418``427` | 中文乱码 | `涓栫晫``棰勮鎬墿` | 技能预览区字段标签已坏 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `313-314``402-403` | 通过 | `角色详情``技能预览` | 主体段落标题已是正常中文 |
### 3.4 `MonsterPresetPanel.tsx`
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `49-50` | 英文直出 | `Saved monster overrides...``Failed to save monster overrides.` | 保存反馈未本地化 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `58-59` | 中文乱码 | 空状态整段提示 | 无怪物时的空态文案已坏 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `124-126` | 英文直出 | `Section``Editor section.``Field` | 左侧选择卡是占位英文 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `135` | 中文乱码 | `闂?` | 世界与名字之间的分隔符已坏 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `139` | 英文直出 | `Save Monster Overrides` | 保存按钮未本地化 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `158-159` | 英文直出 | `Monster Override Preview``Editor section.` | 右侧预览卡标题/说明未本地化 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `165``218``223``228``240``248``256` | 英文直出 | `Field``Name``Intro Action` | 多个字段标签仍是英文或占位文案 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `335` | 中文乱码 | 一段动画配置标签乱码 | 帧数相关字段标题已坏 |
### 3.5 `SceneNpcPresetPanel.tsx`
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `159` | 英文直出 | `No NPC presets are available.` | 空状态未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `177-193` | 英文直出 | `NPC Library``Browse and select an NPC preset.``NPC ID``Save NPC Overrides` | 选择区整块未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `221-238` | 英文直出 | `Skill Preview``Skill``World` | 技能预览区未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `258-259` | 中文乱码 | 空状态/说明整段乱码 | 预览区已有损坏文本 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `264-268` | 英文直出 | `Visual Preview``Hostile NPCs use monster presets...``Narrative NPCs can preview...` | 视觉预览说明未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `310-357` | 英文直出 | `NPC Details``NPC ID``Name``Role``Avatar``Linked Character ID``Monster Preset ID``Initial Affinity``Description` | 详情字段整体未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `363` | 中文乱码 | 预览说明大段乱码 | NPC 预览描述区已坏 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `371-375` | 英文直出 | `Visual Editor``Hostile NPCs cannot use the visual editor...` | 可视编辑器说明未本地化 |
### 3.6 `ScenePresetPanel.tsx`
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `105-108` | 英文直出 | `Treasure Ahead``Treasure` | 宝藏预览实体名/头像仍是英文 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `132-133` | 英文直出 | `Scene Library``Browse and select a scene preset.` | 左侧选择区未本地化 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `162` | 英文直出 | `Save` | 保存按钮未本地化 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `170-181` | 英文直出 | `Scene Preview``Preview Mode``Monster Preview``NPC Preview``Treasure Preview``Empty` | 预览模式整组未本地化 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `207-226` | 英文直出 | `Hostile NPCs``NPCs``Treasure Hint``None` | 场景摘要卡未本地化 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `233-299` | 英文直出 | `Scene Details``Scene ID``World``Name``Description``Image Source``Forward Scene``Unset``Connected Scene IDs``Monster IDs``Treasure Hints``NPCs In Scene` | 详情编辑区整块未本地化 |
### 3.7 `StateFunctionEditor.tsx` 与共享请求层
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/StateFunctionEditor.tsx` | `1060-1064` | 英文直出 | `Failed to save option behavior overrides``Option behavior overrides saved.` | 保存结果提示未本地化 |
| `src/components/StateFunctionEditor.tsx` | `1138` | 中英混用 | `GameState` | 预览说明里仍混入英文类型名 |
| `src/components/StateFunctionEditor.tsx` | `1143` | 中英混用 | `敌对NPC资源` | `NPC` 混入字段标签 |
| `src/components/StateFunctionEditor.tsx` | `1185` | 英文枚举外露 | `AnimationState` 原始值 | 玩家动作下拉会直接显示英文枚举值 |
| `src/components/StateFunctionEditor.tsx` | `1190` | 英文占位符外露 | `{monster}` | 占位提示面向编辑器用户直接可见 |
| `src/components/StateFunctionEditor.tsx` | `1191` | 英文枚举外露 | `idle``move``attack` | 怪物动画下拉仍使用英文值 |
| `src/components/StateFunctionEditor.tsx` | `1213-1217` | 基本通过 | `稳扎稳打``爆发``机动` 等 | 技能权重显示标签已是中文,但底层 key 仍是英文 |
| `src/editor/shared/jsonClient.ts` | `29``43` | 英文直出 | `Request failed``Save failed` | 通用网络错误兜底未本地化,所有编辑器接口都可能透出 |
## 四、`npcInteractions.ts` 重点复查
### 4.1 复查通过
| 文件 | 行号 | 当前状态 | 说明 |
| --- | --- | --- | --- |
| `src/data/npcInteractions.ts` | `507-657` | 通过 | `actionText` / `detailText` 话题库主体已是中文,适合作为后续清理基线 |
### 4.2 仍有问题
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/data/npcInteractions.ts` | `299-343` | 中文乱码 | `绋€鏈夊搧``鏉愭枡``琛屽晢鎶ょ``绮剧偧閾?``闃叉按琛屽泭``娌抽浘缃楃洏``鍏界毊``鐚庨拱缇藉潬``鎷撴湰鏂囧唽``娈嬬己鍦板浘``姝﹀櫒``鍒跺紡浣╁垁``鎶ょ敳``鎶よ噦``鏃у竷鍗?``闅忚韩鏃х墿` | 商人来源匹配、商品分类、商品名存在明显乱码,会直接流入交易 UI |
| `src/data/npcInteractions.ts` | `401-424` | 英文枚举外露 | `deep``honest``partial``guarded``warm``cooperative``neutral``distant``candid``true_but_incomplete``half_truth``situational_only` | 阶段/回答模式仍使用英文值,虽不一定直接给玩家看,但已进入 prompt 上下文 |
| `src/data/npcInteractions.ts` | `428-500` | 基本通过 | `已经愿意逐步谈到真实来历...` 等 | 描述层已是中文,可作为未来替换英文枚举时的文案来源 |
| `src/data/npcInteractions.ts` | `1199``1209``1243` | 中英混用 | `向该NPC送礼``邀请该NPC加入队伍``离开当前 NPC重新回到探索状态。` | 选项和说明里仍混入 `NPC` |
| `src/data/npcInteractions.ts` | `1250` | 中英混用 | `当前好感为 ...` 同句包含 `NPC` | 遭遇总结文本仍有术语混入 |
## 五、`prompt` 链路重点复查
### 5.1 `src/services/prompt.ts`
这是当前最需要优先清理的文件。问题不是一两处,而是“结构性污染”:
- 前段中文描述已混入乱码。
- 中段人物/遭遇/场景/状态描述大面积乱码。
- 后段选项约束、战斗/观察/营地对话指令大面积乱码。
- 同时混有 `functionId``actionText``npc|treasure|none` 等英文结构词。
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/services/prompt.ts` | `145``151` | 中文乱码 | `锛堣嚜瀹氫箟...``鑷畾涔変笘鐣?...` | 自定义世界描述区已坏 |
| `src/services/prompt.ts` | `170-171``198-216` | 中文乱码 | 冒险开场理由、表层钩子、眼前顾虑、目标等整段乱码 | 角色开场信息会直接污染生成上下文 |
| `src/services/prompt.ts` | `226-233` | 中文乱码 + 英文枚举外露 | `NPC``deep``warm``answerMode` 等 | NPC 会话阶段控制段同时存在乱码和英文枚举 |
| `src/services/prompt.ts` | `378-451` | 中文乱码 + 中英混用 | 遭遇实体、敌对 `NPC`、玩家状态、场景说明等整段乱码 | 玩家、NPC、怪物、场景等核心提示都已受污染 |
| `src/services/prompt.ts` | `547-568` | 中文乱码 + 英文结构词外露 | `functionId``actionText``function` | 选项约束与动作重写规则段落已坏 |
| `src/services/prompt.ts` | `859-910` | 中文乱码 + 中英混用 | 空闲/遭遇函数说明、观察线索、开场营地跟进等整段乱码 | 后半段生成规则几乎不可维护 |
### 5.2 `src/services/characterChatPrompt.ts`
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/services/characterChatPrompt.ts` | `42-54` | 英文直出 | `You are a companion character...``Generate exactly 3 player reply suggestions.``Summarize the evolving relationship...` | 系统 prompt 整段为英文 |
| `src/services/characterChatPrompt.ts` | `57-59` | 英文直出 | `Wuxia``Xianxia``Custom World` | 世界描述仍为英文 |
| `src/services/characterChatPrompt.ts` | `64``69-71``74-75` | 英文直出 | `Custom world reference``female``male``unknown``left``right` | 性别、朝向、扩展说明均为英文 |
| `src/services/characterChatPrompt.ts` | `99-112` | 英文直出 | `Recent story: none.``Earlier story summary``Most recent 3 story rounds` | 历史摘要模板为英文 |
| `src/services/characterChatPrompt.ts` | `123-155` | 英文直出 | `damage``mana``cooldown``arrival reason``current goal``world schema``top attributes` | 角色信息拼接模板整体为英文 |
### 5.3 `src/services/questPrompt.ts`
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/services/questPrompt.ts` | `28-31` | 英文直出 | `issued by``No live quests` | 当前任务摘要模板为英文 |
| `src/services/questPrompt.ts` | `35-37` | 英文直出 | `Active companions``Roster companions` | 同伴摘要模板为英文 |
| `src/services/questPrompt.ts` | `41-52` | 英文直出 | `Player``HP``Mana``Inventory snapshot` | 玩家状态模板为英文 |
| `src/services/questPrompt.ts` | `67-95` | 英文直出 | `You are the quest director...` 等整段 | 任务意图系统 prompt 全英文 |
| `src/services/questPrompt.ts` | `107-123` | 英文直出 | `World``Issuer NPC``Encounter kind``Recent story moments` 等 | 最终拼接 prompt 仍是英文框架 |
### 5.4 `src/services/aiFallbacks.ts`
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/services/aiFallbacks.ts` | `5-17` | 英文直出 | `Player: ...``That question is not casual...``I accept...` | 离线 NPC 对话与招募兜底仍是英文 |
| `src/services/aiFallbacks.ts` | `28-35` | 英文直出 | `I will answer in my own way``I still remember...` | 离线角色私聊回复兜底为英文 |
| `src/services/aiFallbacks.ts` | `40-42` | 英文直出 | 三条候选回复 | 私聊建议兜底为英文 |
| `src/services/aiFallbacks.ts` | `52-57` | 英文直出 | `Player``Recent exchange``warmer toward the player` | 私聊摘要兜底为英文 |
## 六、其他会外溢到 UI / 预览 / 生成链路的文本源
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
| --- | --- | --- | --- | --- |
| `src/hooks/useStoryGeneration.ts` | `578-580` | 通过 | `前往...``离开营地...` | 旅行选项已中文化 |
| `src/hooks/useStoryGeneration.ts` | `599``602` | 英文直出 | `approaches from a short distance away...``steps into view...` | 初始同伴结果文本仍是英文 |
| `src/hooks/useStoryGeneration.ts` | `814` | 英文直出 | `Continue the camp exchange and organize the most natural next actions.` | 营地跟进生成指令仍是英文 |
| `src/data/scenePresets.ts` | `195``297` | 英文枚举外露 | `trade``fight``spar``help``chat``recruit``gift` | 场景函数列表仍是英文 ID |
| `src/data/stateFunctions.ts` | `33` | 英文枚举外露 | `idle|move|attack` | 怪物动画枚举仍是英文 |
| `src/data/stateFunctions.ts` | `130``152``174``196``221``244` | 英文枚举外露 | `steady``burst``mobility``finisher``projectile` | 技能权重 key 仍为英文 |
| `src/data/characterPresets.ts` | `60-76` | 英文枚举外露 | `blunt``wary``evasive``measured``gentle``teasing``dry``steady``direct``fragmented``deflecting` | 对话风格推断值仍为英文,已进入 `npcInteractions` / `prompt` 描述链路 |
## 七、优先级建议
### P0先修否则会持续污染生成结果或直接破坏主界面
1. `src/services/prompt.ts`
2. `src/components/NpcModals.tsx`
3. `src/components/AdventureEntityModal.tsx`
4. `src/data/npcInteractions.ts` 的商店库存种子乱码段
5. `src/components/game-shell/GameShellOverlays.tsx` 的标题乱码
### P1紧接着修编辑器体验当前已经明显受损
1. `src/components/preset-editor/MonsterPresetPanel.tsx`
2. `src/components/preset-editor/SceneNpcPresetPanel.tsx`
3. `src/components/preset-editor/ScenePresetPanel.tsx`
4. `src/components/preset-editor/CharacterPresetPanel.tsx`
5. `src/components/StateFunctionEditor.tsx`
6. `src/editor/shared/jsonClient.ts`
### P2统一术语与风格减少中英混用
1. 全局统一 `NPC``HP``MP``AI``Shift` 是否保留英文缩写
2.`npcInteractions.ts` / `characterPresets.ts` / `stateFunctions.ts` 的英文枚举与键名整理为“内部值 + 中文展示层”
3. 补齐 `aiFallbacks.ts``characterChatPrompt.ts``questPrompt.ts` 的中文 prompt / fallback 版本
## 八、建议的修复顺序
1. 先修 `prompt``npcInteractions`,因为这两处会同时污染 AI 输出和运行时文本。
2. 再修 `NpcModals``AdventureEntityModal``GameShellOverlays`,优先恢复玩家可见界面。
3. 再批量处理四个预设编辑器面板和 `StateFunctionEditor`,最后统一共享请求层兜底文案。

View File

@@ -1,273 +0,0 @@
# 游戏 UI / 预设 / 编辑器文本二次审计(扩展重查版)
日期:`2026-04-02`
说明:
- 本文档用于替换同名上一版审计。上一版确实漏掉了不少内容,尤其是:
- `AdventurePanel` 里的任务概览与奖励文案
- `npcInteractions.ts` 里的 `actionText` / `detailText`
- 自定义世界编辑器、NPC 视觉编辑器里的英文兜底和混合术语
- 预设编辑器里多组仍未本地化的标题、字段名、保存反馈
- 本次以当前仓库实际内容为准,不沿用旧结论;已经修掉的内容不再重复计入。
## 审计范围
- 扫描目录:
- `src/components/`
- `src/data/`
- `src/hooks/`
- `src/services/`
- `src/routing/`
- `src/editor/`
- 关注对象:
- 玩家在主流程、冒险面板、弹窗里会看到的文本
- 预设编辑器、自定义世界编辑器、NPC 视觉编辑器中会直接显示的文本
- 会透传到 UI、弹窗、编辑器预览中的数据层文本源
- 标记类型:
- `英文直出`:面向玩家 / 编辑器用户的英文文本仍直接显示
- `中英混用`:中文 UI 中混入 `NPC``HP``MP``AI``URL``Shift` 等术语
- `异常显示`:明显乱码、截断、异常问号替代、分隔符损坏
## 结论摘要
- 游戏主流程里仍有明显英文残留:`QQ Group``WeChat``GENARRATIVE``Current Area`
- 游戏内任务 / 冒险面板仍有一批英文任务眉标和按钮辅助文案:`BOUNTY TARGET``CACHE TRACE``SPAR SESSION``Inspect reward item ...``Unknown monster`
- `GameShellOverlays.tsx` 仍有整组 loading fallback 出现异常编码。
- 预设编辑器目前仍是问题最密集区域之一,`CharacterPresetPanel``MonsterPresetPanel``SceneNpcPresetPanel``ScenePresetPanel``StateFunctionEditor` 里都有明显英文直出或半成品占位。
- 数据层里仍有大量会透出到 UI / 编辑器的英文值,重点在:
- `npcInteractions.ts`
- `useStoryGeneration.ts`
- `storyGenerationState.ts`
- `npcEncounterActions.ts`
- `sceneObservation.ts`
- `characterPresets.ts`
- `stateFunctions.ts`
## 一、游戏主流程 UI
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `48-49` | 英文直出 | `QQ Group``WeChat` | 开场页联系方式标签仍是英文 |
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `351` | 中英混用 | `contact.label === 'QQ Group' ? 'QQ群' : '微信'` | 逻辑分支仍依赖英文标签 |
| `src/components/game-shell/GameShellOverlays.tsx` | `123` | 异常显示 | `姝e湪鍔犺浇鍐掗櫓璇︽儏...` | 冒险详情 loading fallback 乱码 |
| `src/components/game-shell/GameShellOverlays.tsx` | `162` | 异常显示 | `姝e湪鍔犺浇闃熶紞闈㈡澘` | 队伍面板 loading fallback 乱码 |
| `src/components/game-shell/GameShellOverlays.tsx` | `187` | 异常显示 | `姝e湪鍔犺浇鑳屽寘闈㈡澘` | 背包面板 loading fallback 乱码 |
| `src/components/game-shell/GameShellOverlays.tsx` | `214` | 异常显示 | `姝e湪鍔犺浇闃熶紞钀ュ湴...` | 营地弹窗 loading fallback 乱码 |
| `src/components/game-shell/GameShellOverlays.tsx` | `229` | 异常显示 | `姝e湪鍔犺浇鍦板浘...` | 地图 loading fallback 乱码 |
| `src/components/game-shell/GameShellOverlays.tsx` | `248` | 异常显示 | `姝e湪鍔犺浇瑙掕壊鑱婂ぉ...` | 角色聊天 loading fallback 乱码 |
| `src/components/game-shell/GameShellOverlays.tsx` | `261` | 异常显示 | `姝e湪鍔犺浇 NPC 浜や簰...` | NPC 交互 loading fallback 同时混入 `NPC` |
| `src/components/game-shell/GameShellRuntime.tsx` | `146` | 英文直出 | `Current Area` | 当前场景名缺失时的兜底文案仍是英文 |
| `src/components/game-shell/GameShellRuntime.tsx` | `200` | 英文直出 | `GENARRATIVE` | 顶部 logo 文案仍是英文 |
| `src/components/GameShell.tsx` | `311` | 英文直出 | `Current Area` | 旧壳组件里同样保留英文兜底 |
| `src/components/GameShell.tsx` | `365` | 英文直出 | `GENARRATIVE` | 旧壳组件里同样保留英文 logo |
| `src/components/DeveloperTeamModal.tsx` | `44` | 英文直出 | `aria-label="Close developer team modal"` | 开发团队弹窗关闭按钮辅助文案未本地化 |
## 二、游戏内面板 / 弹窗
### 2.1 冒险与任务面板
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/AdventurePanel.tsx` | `197-198` | 异常显示 | `适合进攻型构``适合防御型构` | 描述文本疑似被截断,正常语义应是“构筑” |
| `src/components/AdventurePanel.tsx` | `206` | 异常显示 | `item.name + ' 奖励物品<E789A9>?';` | 奖励物品描述兜底尾部异常 |
| `src/components/AdventurePanel.tsx` | `216` | 异常显示 | ``${hours}小时 ...<2E>?...秒`` | 时长格式字符串出现异常字符 |
| `src/components/AdventurePanel.tsx` | `219` | 异常显示 | ``${minutes}<7D>?...秒`` | 分钟格式字符串同样异常 |
| `src/components/AdventurePanel.tsx` | `248` | 英文直出 | ``aria-label={`Inspect reward item ${item.name}`}`` | 奖励物品按钮辅助文案是英文 |
| `src/components/AdventurePanel.tsx` | `279` | 英文直出 | `BOUNTY TARGET` | 任务眉标未本地化 |
| `src/components/AdventurePanel.tsx` | `283` | 英文直出 | `Unknown monster` | 目标怪物兜底文案是英文 |
| `src/components/AdventurePanel.tsx` | `290` | 英文直出 | `CACHE TRACE` | 宝藏任务眉标未本地化 |
| `src/components/AdventurePanel.tsx` | `295` | 英文直出 | `Inspect the hidden reward site` | 宝藏任务副文案未本地化 |
| `src/components/AdventurePanel.tsx` | `302` | 英文直出 | `SPAR SESSION` | 切磋任务眉标未本地化 |
### 2.2 NPC / 实体 / 交易相关弹窗
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/AdventureEntityModal.tsx` | `895` | 中英混用 | `label="HP"` | 实体状态估计面板直接显示 `HP` |
| `src/components/AdventureEntityModal.tsx` | `901` | 中英混用 | `label="MP"` | 实体状态估计面板直接显示 `MP` |
| `src/components/NpcModals.tsx` | `252` | 中英混用 | `NPC 商品列表` / `你的背包列表` | 交易列表标题混入 `NPC` |
| `src/components/NpcModals.tsx` | `273` | 中英混用 | `这个 NPC 当前没有可售商品。` | 空状态文案混入 `NPC` |
| `src/components/NpcModals.tsx` | `356` | 中英混用 | `NPC 商品` | 详情弹窗标题混入 `NPC` |
| `src/components/NpcModals.tsx` | `408` | 中英混用 | `效果预览HP +... / MP +... / 冷却 -...` | 数值预览里直接保留 `HP`、`MP` |
### 2.3 自定义世界结果页
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/CustomWorldResultView.tsx` | `58` | 中英混用 | `新增 NPC` | 结果页新增操作标签混入 `NPC` |
## 三、自定义世界 / NPC 视觉编辑
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/CustomWorldEntityEditorModal.tsx` | `242` | 英文直出 | `URL` | 图片地址输入提示词未本地化 |
| `src/components/CustomWorldEntityEditorModal.tsx` | `464` | 英文直出 | `MedievalFantasyCharacters` | 形象编辑副标题里直接暴露素材包英文名 |
| `src/components/CustomWorldEntityEditorModal.tsx` | `482-483` | 中英混用 | `AI生成NPC形象`、`NPC 形象 AI 生成功能仍在开发中。` | 同时混入 `AI`、`NPC` |
| `src/components/CustomWorldEntityEditorModal.tsx` | `635-636` | 中英混用 | `新增 NPC`、`编辑 NPC...` | NPC 档案编辑标题混入英文缩写 |
| `src/components/CustomWorldEntityEditorModal.tsx` | `663` | 中英混用 | `修改形象` | 本行本身无问题,但上下文仍指向 `NPC` 视觉编辑入口 |
| `src/components/CustomWorldEntityEditorModal.tsx` | `734` | 中英混用 | `AI生成` | 按钮文案混入 `AI` |
| `src/components/CustomWorldEntityEditorModal.tsx` | `762-763` | 中英混用 | `AI生成场景`、`场景图片 AI 生成功能仍在开发中。` | 场景生成弹窗混入 `AI` |
| `src/components/CustomWorldNpcVisualEditor.tsx` | `393` | 中英混用 | `AI生成` | 自定义 NPC 视觉编辑器按钮混入 `AI` |
| `src/components/NpcVisualEditor.tsx` | `263-267` | 英文直出 | `Failed to load NPC visual overrides`、`Failed to load NPC layout config` | 编辑器首屏可见的加载失败兜底为英文 |
| `src/components/NpcVisualEditor.tsx` | `284-308` | 英文直出 | `response was invalid, using bundled defaults` 等 | 多组降级提示未本地化 |
| `src/components/NpcVisualEditor.tsx` | `463` | 中英混用 | `请先选择一个 NPC 进行编辑` | 空态文案混入 `NPC` |
| `src/components/NpcVisualEditor.tsx` | `539` | 英文直出 | `Save failed` | 保存失败提示仍是英文 |
| `src/components/NpcVisualEditor.tsx` | `702-708` | 中英混用 | `NPC 视觉编辑器`、`选择并编辑 NPC 的外观...` | 页面标题与简介多次混入 `NPC` |
| `src/components/NpcVisualEditor.tsx` | `718` | 中英混用 | `当前 NPC` | 当前对象字段名混入 `NPC` |
| `src/components/NpcVisualEditor.tsx` | `976-977` | 中英混用 | `拖动标记微调 NPC 的预览布局。移动时按住 Shift...` | 帮助提示同时混入 `NPC` 与 `Shift` |
| `src/components/NpcVisualEditor.tsx` | `1033-1052` | 英文直出 | `Unknown headgear`、`No headgear`、`Unknown main hand`、`No main hand`、`Unknown off hand`、`No off hand` | 预览状态说明仍是英文 |
## 四、预设编辑器
### 4.1 共享配置与选项枚举
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/shared.ts` | `43` | 中英混用 | `label: 'NPC'` | 预设编辑器顶部 tab 仍直接显示 `NPC` |
| `src/components/preset-editor/shared.ts` | `63-66` | 英文直出 | `idle`、`move`、`attack`、`die` | 怪物动画可选项是原始英文值 |
| `src/components/preset-editor/shared.ts` | `70-74` | 英文直出 | `steady`、`burst`、`mobility`、`finisher`、`projectile` | 技能风格可选项是原始英文值 |
### 4.2 角色预设面板
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `91-92` | 英文直出 | `Saved character preset overrides...`、`Failed to save character preset overrides.` | 保存反馈未本地化 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `264-279` | 英文直出 | `Characters`、`Browse the character roster...`、`Field`、`Save Character Overrides` | 左侧选择卡与保存栏均为英文 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `315-322` | 英文直出 | `Character Details`、`Field` | 详情卡标题和字段名未本地化 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `404-430` | 英文直出 | `Skill Preview`、`Preview ranged skills...`、`Preview Monster` | 技能预览区英文直出 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `450-467` | 英文直出 | `Skill Setup`、`Add Skill` | 技能配置区仍是英文 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `498-558` | 英文直出 | 大量 `label="Field"` | 多数字段仍显示占位词 `Field` |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `605` | 英文直出 | `Start Frame` | 动画字段未本地化 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `629`、`729` | 英文直出 | `Section`、`Editor section.` | 两个分区仍是半成品英文占位 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `697-698` | 英文直出 | `Attributes`、`Adjust the core character attributes.` | 属性面板未本地化 |
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `755` | 英文直出 | `Unset` | 场景绑定下拉空值项为英文 |
### 4.3 怪物预设面板
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `49-50` | 英文直出 | `Saved monster overrides...`、`Failed to save monster overrides.` | 保存反馈未本地化 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `124-139` | 英文直出 | `Section`、`Editor section.`、`Field`、`Save Monster Overrides` | 左侧选择区和保存栏未本地化 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `135` | 异常显示 | ``${WORLD_LABELS[monster.worldType]} 闂?${optionMonster.name}`` | 选择列表分隔符损坏 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `158-159` | 英文直出 | `Monster Override Preview`、`Editor section.` | 预览区标题仍是英文 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `192-201` | 英文直出 | `Attack Range`、`Speed`、`HP`、`Max HP` | 预览摘要未本地化 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `212-234` | 英文直出 | `Monster ID`、`Name`、`Intro Action` | 核心字段名仍是英文 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `343` | 英文直出 | `FPS` | 动画字段未本地化 |
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `207`、`275`、`307` | 英文直出 | `Section`、`Editor section.` | 多个分区标题仍为占位英文 |
### 4.4 场景 NPC 预设面板
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `127-128` | 英文直出 | `Saved NPC overrides.`、`Failed to save NPC overrides.` | 保存反馈未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `159` | 英文直出 | `No NPC presets are available.` | 空态文案未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `177-193` | 英文直出 | `NPC Library`、`Browse and select an NPC preset.`、`NPC ID`、`Save NPC Overrides` | 左侧选择区英文直出 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `221-237` | 英文直出 | `Skill Preview`、`Preview ranged skills from the linked character.`、`Skill`、`World` | 技能预览区未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `258` | 异常显示 | `闂?NPC` | 空态或提示文案已损坏 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `264-268` | 英文直出 | `Visual Preview`、`Hostile NPCs use monster presets...` | 视觉预览区标题与说明均未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `310-371` | 英文直出 | `NPC Details`、`Name`、`Role`、`Avatar`、`Linked Character ID`、`Monster Preset ID`、`Initial Affinity`、`Description`、`Visual Editor` | 详情区字段名大面积英文直出 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `374-375` | 英文直出 | `Hostile NPCs cannot use the visual editor...`、`Narrative NPC visual overrides can be previewed here.` | 视觉编辑区说明未本地化 |
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `382-384` | 异常显示 | 三整段乱码说明 + `NPC` | 视觉编辑区空态说明已严重损坏 |
### 4.5 场景预设面板
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `52-53` | 英文直出 | `Saved scene overrides.`、`Failed to save scene overrides.` | 保存反馈未本地化 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `62` | 英文直出 | `No scene presets are available.` | 空态文案未本地化 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `105-107` | 英文直出 | `Treasure Ahead`、`Treasure` | 宝藏预览实体名仍是英文 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `132-171` | 英文直出 | `Scene Library`、`Browse and select a scene preset.`、`Save`、`Scene Preview`、`Preview monsters, NPCs, and treasure...` | 场景面板主框架仍多处英文 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `175-181` | 英文直出 | `Preview Mode`、`Monster Preview`、`NPC Preview`、`Treasure Preview` | 预览模式切换未本地化 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `207-226` | 英文直出 | `Hostile NPCs`、`NPCs`、`None` | 预览摘要区未本地化 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `233-290` | 英文直出 | `Scene Details`、`Edit the selected scene preset.`、`Scene ID`、`World`、`Name`、`Description`、`Image Source`、`Forward Scene`、`Connected Scene IDs`、`Monster IDs`、`Treasure Hints` | 详情编辑区字段名几乎全部英文 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `271` | 英文直出 | `Unset` | 下拉空值项为英文 |
| `src/components/preset-editor/ScenePresetPanel.tsx` | `299` | 英文直出 | `NPCs In Scene` | 分区标题未本地化 |
### 4.6 状态函数编辑器
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/components/StateFunctionEditor.tsx` | `310-325` | 英文直出 | `Preview`、`Treasure` | 预览 NPC / 宝藏 context 里保留英文 |
| `src/components/StateFunctionEditor.tsx` | `1062-1064` | 英文直出 | `Option behavior overrides saved.`、`Failed to save option behavior overrides` | 保存反馈未本地化 |
| `src/components/StateFunctionEditor.tsx` | `1128` | 英文直出 | `无模板` 之外的模板选择逻辑仍有英文结构 | 该区主体中文,但下游模板 ID / 类型依旧偏英文 |
| `src/components/StateFunctionEditor.tsx` | `1185` | 英文直出 | `AnimationState` 原始值直接作为选项标签 | 动画值会直接显示英文枚举 |
| `src/components/StateFunctionEditor.tsx` | `1190` | 英文直出 | `placeholder="可使用 {monster} 占位符"` | 占位符本身暴露英文 key |
| `src/components/StateFunctionEditor.tsx` | `1191` | 英文直出 | `idle`、`move`、`attack` | 怪物动画下拉仍使用英文值 |
## 五、数据层 / 运行时文本源
### 5.1 直接驱动交互按钮、详情文案、结果文本的源头
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/data/npcInteractions.ts` | `444-580` | 重点补录 | 大量 `actionText` / `detailText` | 这是本轮补录重点,属于 NPC 交互选项直接展示源,需要逐条审校 |
| `src/data/npcInteractions.ts` | `327-349` | 英文直出 | `deep`、`honest`、`partial`、`guarded`、`warm`、`cooperative`、`neutral`、`distant`、`candid`、`true_but_incomplete`、`half_truth`、`situational_only` | 这些关系 / 说话风格值虽然是逻辑枚举,但后续很容易在编辑器、调试面板、覆盖配置中直接暴露 |
| `src/hooks/useStoryGeneration.ts` | `576-578` | 英文直出 | `Travel to ...`、`Leave camp and head toward ...` | 旅行选项和详情文案未本地化 |
| `src/hooks/useStoryGeneration.ts` | `654-659` | 英文直出 | `Speak with ...`、`Focus on the person in front of you first...` | 开场对话选项未本地化 |
| `src/hooks/useStoryGeneration.ts` | `827`、`1297` | 英文直出 | `Exchange an opening judgment with ... at camp` | 营地开场交互 actionText 未本地化 |
| `src/hooks/useStoryGeneration.ts` | `1097` | 英文直出 | `Begin the adventure` | 初始 actionText 未本地化 |
| `src/hooks/useStoryGeneration.ts` | `1374`、`1794` | 英文直出 | `Unknown AI error` | AI 兜底错误提示仍是英文 |
| `src/hooks/story/storyGenerationState.ts` | `137-141` | 英文直出 | `You leave ...`、`Travel to ...` | 旅行结果与 actionText 未本地化 |
| `src/hooks/story/npcEncounterActions.ts` | `277` | 英文直出 | `Victory reward: ${lootText}.` | 战斗胜利奖励结算文本未本地化 |
| `src/hooks/story/npcEncounterActions.ts` | `389` | 英文直出 | `NPC dialogue AI is unavailable.` | NPC 对话 AI 失败兜底是英文 |
| `src/hooks/story/characterChat.ts` | `68` | 英文直出 | `Player` | 聊天摘要拼接里保留英文说话人名 |
| `src/hooks/story/characterChat.ts` | `84` | 英文直出 | `Tell me more clearly what you mean.` | 建议起句未本地化 |
| `src/hooks/story/characterChat.ts` | `283` | 英文直出 | `Unknown AI error` | 私聊错误兜底未本地化 |
| `src/data/sceneObservation.ts` | `9-34` | 英文直出 | `You pause to listen...`、`Possible NPCs...`、`Possible hostile NPCs...`、`Possible treasure clues...`、`Boss clue...` | 观察环境的整组文本源仍是英文 |
### 5.2 会透出到编辑器 / 预览 / 覆盖系统的预设值
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
| --- | --- | --- | --- | --- |
| `src/data/scenePresets.ts` | `141` | 中英混用 | `role: '敌对NPC'` | 场景 NPC 角色名混入 `NPC` |
| `src/data/scenePresets.ts` | `194`、`294` | 英文直出 | `['trade', 'fight', 'spar', 'help', 'chat', 'recruit', 'gift']` | 场景 NPC 功能数组是英文原始值,编辑器容易直出 |
| `src/data/stateFunctions.ts` | `33` | 英文直出 | `monsterAnimation?: 'idle' | 'move' | 'attack'` | 动画值原始英文会进入状态函数编辑器 |
| `src/data/stateFunctions.ts` | `130`、`152`、`174`、`196`、`221`、`244` | 英文直出 | `steady`、`burst`、`mobility`、`finisher`、`projectile` | 技能权重 key 为英文原始值 |
| `src/data/stateFunctions.ts` | `372`、`445`、`569`、`575-576` | 中英混用 | 描述里多次直接写 `NPC` | 虽然主体中文,但会透出到说明文字 |
| `src/data/characterPresets.ts` | `54-70` | 英文直出 | `blunt`、`wary`、`evasive`、`measured`、`gentle`、`teasing`、`dry`、`steady`、`direct`、`fragmented`、`deflecting` | 对话风格推断返回值全为英文 |
| `src/data/characterPresets.ts` | `362-382`、`519-539`、`738-758`、`833-853`、`1018-1041` | 英文直出 | `assetFolder`、`folder`、`prefix` 中大量英文,如 `Sword Princess`、`idle`、`Double Jump`、`Wall Slide`、`Attack` | 这些值会被预设编辑器动画区直接展示或编辑 |
| `src/data/characterPresets.ts` | `387-389`、`544-546`、`763-765`、`858-860`、`1046-1048` | 英文直出 | `guardStyle`、`warmStyle`、`truthStyle` 使用英文值 | 会透出到预设编辑器 / 调试视图 |
| `src/data/characterPresets.ts` | `410-501`、`575-720`、`794-1000`、`1069-1197` | 英文直出 | `style`、`delivery`、`phase`、`anchor`、`motion` 使用 `steady`、`mobility`、`ranged`、`travel`、`target`、`projectile` 等英文值 | 角色技能配置层大面积保留英文元数据 |
| `src/data/monsterPresets.ts` | `384-817` | 英文直出 | 多处 `common`、`uncommon`、`rare`、`epic` | 怪物稀有度原始值为英文,若编辑器未做映射会直接暴露 |
| `src/editor/shared/jsonClient.ts` | `29-43` | 英文直出 | `Request failed`、`Save failed` | 编辑器通用 JSON 客户端默认错误文案未本地化 |
## 六、本轮已复查、暂未记录明确问题的文件
以下文件本轮重新检查过,但当前内容里没有继续记录“英文直出 / 异常显示”的明确条目,暂可视为本轮通过:
- `src/routing/appRoutes.tsx`
- `src/components/game-shell/CharacterSelectionFlow.tsx`
- `src/components/game-shell/GameShellStoryPanels.tsx`
- `src/components/CharacterPanel.tsx`
- `src/components/InventoryPanel.tsx`
- `src/components/MapModal.tsx`
- `src/components/CharacterChatModal.tsx`
- `src/components/SelectionCustomizationModals.tsx`
- `src/components/adventure-panel/AdventurePanelOverlays.tsx`
- `src/data/sceneEncounterPreviews.ts`
- `src/data/customWorldVisuals.ts`
- `src/services/customWorldBuilder.ts`
## 建议修复顺序
1. 先修主流程直接暴露给玩家的文本:
- `PreGameSelectionFlow.tsx`
- `GameShellOverlays.tsx`
- `GameShellRuntime.tsx`
- `GameShell.tsx`
- `AdventurePanel.tsx`
2. 再修编辑器里最容易误导内容生产的面板:
- `CharacterPresetPanel.tsx`
- `MonsterPresetPanel.tsx`
- `SceneNpcPresetPanel.tsx`
- `ScenePresetPanel.tsx`
- `StateFunctionEditor.tsx`
3. 最后统一清理数据层文本源和枚举映射:
- `npcInteractions.ts`
- `useStoryGeneration.ts`
- `storyGenerationState.ts`
- `npcEncounterActions.ts`
- `sceneObservation.ts`
- `characterPresets.ts`
- `stateFunctions.ts`

View File

@@ -1,18 +0,0 @@
# 文本与乱码审计总览
这一组只保留当前仍需要执行的文本与乱码审计入口。早期逐日扫描已经聚合到当前结论,不再保留旧稿链路。
## 当前推荐入口
1. [CHINESE_MOJIBAKE_INVENTORY.md](./CHINESE_MOJIBAKE_INVENTORY.md)
偏全局库存清单,适合先确认问题分布范围。
2. [GAME_UI_PRESET_EDITOR_NPC_PROMPT_TEXT_AUDIT_2026-04-02_DEEP_SCAN.md](./GAME_UI_PRESET_EDITOR_NPC_PROMPT_TEXT_AUDIT_2026-04-02_DEEP_SCAN.md)
这是当前最完整的深度审计版本,已经扩到 `prompt``npcInteractions`、运行时弹窗与编辑器深层文本。
3. [GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-04-02.md](./GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-04-02.md)
适合看“扩展重查版”的 UI / 预设 / 编辑器问题面。
## 融合结论
- 早期几轮扫描已经完成使命,核心结论是:不要把乱码当成普通文案改写,先确认真实编码;文本修复要优先处理会进入玩家体验和 AI 生成链路的内容。
- `2026-04-02` 两份文档开始把重点收敛到真正会影响玩家体验和 AI 生成质量的链路。
- 现在做文本修复时,不必从最早一份开始逐篇读;优先看 `CHINESE_MOJIBAKE_INVENTORY``2026-04-02` 两份即可。

View File

@@ -1,650 +0,0 @@
# AI 原生运行时物品生成系统重设计
更新时间:`2026-04-02`
## 0. 这次重设计要解决什么
基于当前仓库已经存在的系统这次不再把“AI 原生物品生成”设计成一套独立玩法,而是把它重做成:
**挂在现有背包、build、宝藏、NPC、任务、自定义世界之上的统一运行时物品导演系统。**
它要解决的核心问题有 4 个:
1. 当前物品入口很多,但缺少统一导演层。
2. 当前奖励能发物品,但和场景背景、相关 NPC、最近事件的贴合度不够高。
3. 当前 build 标签体系已经存在,但运行时奖励还没有围绕“永久标签 / 限时标签 / 小数值补充”形成稳定规则。
4. AI 目前只负责叙事包装,没有真正进入运行时物品生成链路。
这次重设计的目标不是“让 AI 直接生成 `InventoryItem`”,而是:
- 让 AI 负责物品的**叙事意图、关系语义、世界贴合**
- 让本地规则负责物品的**标签、数值、稀有度、存档、平衡**
## 1. 设计结论先说
新的 AI 原生物品系统建议采用:
**上下文采样器 -> AI 物品意图层 -> 本地编译器 -> 渠道分发器 -> 叙事回写器**
其中:
- `InventoryItem` 继续作为唯一成品结构
- `ItemBuildProfile` 继续承载永久 build 标签
- `ItemUseProfile.buildBuffs` 继续承载限时 build 标签
- `ItemStatProfile` 继续承载小数值加成
- `buildTags.ts / buildDamage.ts` 继续承接战斗结算
- `treasureInteractions.ts / npcInteractions.ts / forgeSystem.ts / customWorldRuntime.ts` 改为调用统一导演层
一句话:
**不要再按“宝藏怎么发、NPC 怎么卖、任务怎么奖”各写一套;而是让所有入口共用同一个运行时物品生成管线。**
## 2. 这套系统必须遵守的边界
## 2.1 AI 负责语义,不负责数值
AI 可以决定:
- 这件物品像什么
- 它为什么在这里出现
- 它和哪个 NPC / 势力 / 地标 / 怪物有关
- 它更偏向什么 build 风格
- 它应该是永久物还是限时物
AI 不应该直接决定:
- 最终 `outgoingDamageBonus` 是多少
- 能带几个标签
- 允许不允许进入装备槽
- 价值多少
- 能不能掉落
- 能不能持久化
## 2.2 所有成品都必须编译回当前系统
新的系统不是新物品结构,而是新生成过程。
最终成品仍然必须落到:
- `InventoryItem`
- `ItemStatProfile`
- `ItemUseProfile`
- `ItemBuildProfile`
否则现有:
- 背包
- 装备
- build 计算
- 锻造
- 存档
- 交易
都接不上。
## 2.3 运行时物品的主收益必须是“构筑意义”
重新设计后,运行时物品的收益优先级建议固定为:
1. `build 标签`
2. `功能性节奏收益`
3. `小数值补充`
不建议反过来做成:
1. 大数值
2. build 标签只是点缀
因为当前项目最强的系统基础已经是 build 标签和叙事关系,而不是传统数值刷装。
## 3. 新系统的整体架构
## 3.1 模块拆分
建议新增 5 个模块:
- `src/types/runtimeItem.ts`
- `src/data/runtimeItemContext.ts`
- `src/data/runtimeItemDirector.ts`
- `src/data/runtimeItemCompiler.ts`
- `src/data/runtimeItemNarrative.ts`
职责如下。
### A. `runtimeItemContext.ts`
负责统一采样生成上下文,不直接生成物品。
输入来自:
- `GameState`
- `currentEncounter`
- `currentScenePreset`
- `npcStates`
- `storyHistory`
- `playerEquipment`
- `activeBuildBuffs`
输出统一上下文对象:
```ts
type RuntimeItemGenerationContext = {
worldType: WorldType | null;
customWorldProfile: CustomWorldProfile | null;
sceneId: string | null;
sceneName: string | null;
sceneDescription: string | null;
sceneTags: string[];
treasureHints: string[];
encounter: Encounter | null;
encounterNpcId: string | null;
encounterNpcName: string | null;
encounterContextText: string | null;
relatedNpcState: NpcPersistentState | null;
recentStorySummary: string;
recentActions: string[];
playerCharacterId: string;
playerBuildTags: string[];
playerBuildGaps: string[];
playerEquipmentTags: string[];
generationChannel: RuntimeItemGenerationChannel;
};
```
### B. `runtimeItemDirector.ts`
负责根据上下文决定:
- 这次该不该生成上下文化物品
- 生成几件
- 主奖励 / 副奖励是什么
- 是装备、消耗品、材料还是稀有物
- 偏永久 build、限时 build还是纯功能补给
它的输出不是成品,而是“导演结果”:
```ts
type RuntimeItemPlan = {
slot: "primary" | "secondary" | "support";
itemKind: "equipment" | "consumable" | "material" | "relic" | "quest";
permanence: "permanent" | "timed" | "resource";
narrativeWeight: "light" | "medium" | "heavy";
targetBuildDirection: string[];
relationAnchor: RuntimeRelationAnchor;
};
```
### C. `runtimeItemCompiler.ts`
负责把导演计划 + AI 意图编译成正式 `InventoryItem`
编译职责包括:
- build 标签规范化
- 稀有度预算分配
- 永久标签上限
- 限时标签时长
- 数值预算
- 装备槽判定
- 价值计算
- metadata 生成
### D. `runtimeItemNarrative.ts`
负责把已经生成好的物品回写成叙事文本:
- 物品命名
- 物品描述
- 物品来源说明
- 宝藏/NPC/任务文案嵌入
### E. `runtimeItem.ts`
负责声明:
- 渠道类型
- 关系锚点类型
- 运行时物品 metadata
- AI 意图结构
- 编译预算结构
## 3.2 AI 在新架构里的输入输出
建议 AI 只接触两种结构:
### 输入:压缩过的生成上下文
```ts
type RuntimeItemAiPromptInput = {
worldSummary: string;
sceneSummary: string;
encounterSummary: string;
relatedNpcSummary: string;
recentStorySummary: string;
generationChannel: RuntimeItemGenerationChannel;
playerBuildDirection: string[];
playerBuildGaps: string[];
desiredItemKind: string;
permanence: string;
};
```
### 输出:轻量意图
```ts
type RuntimeItemAiIntent = {
shortNameSeed: string;
sourcePhrase: string;
reasonToAppear: string;
relationHooks: string[];
desiredBuildTags: string[];
desiredFunctionalBias: Array<"heal" | "mana" | "cooldown" | "guard" | "damage">;
tone: "grim" | "mysterious" | "martial" | "ritual" | "survival";
};
```
AI 输出长度必须短,目的明确,不允许直接产出成品 JSON 大对象。
## 4. 新系统里的物品收益模型
## 4.1 三种收益层
新系统建议把运行时物品分成三类收益层。
### 第一层:永久 build 标签
适用于:
- 武器
- 护甲
- 饰品
- 稀有遗物
载体:
- `buildProfile.role`
- `buildProfile.tags`
- `buildProfile.setId / setName`
建议限制:
- 普通运行时装备:`1` 个主标签
- 稀有以上:`1` 主标签 + `1` 协同标签
- 传说或剧情物:允许带 `2` 标签 + 关系锚点
### 第二层:限时 build 标签
适用于:
- 药剂
- 符箓
- 战场工具
- 一次性应急物
载体:
- `useProfile.buildBuffs`
建议限制:
- `1~2` 个标签
- `1~3` 回合
- 默认刷新持续时间,不建议无限叠层
### 第三层:少量直接数值
适用于:
- 补足 build 短板
- 强化渠道差异
- 给物品明确手感
载体:
- `statProfile`
- `useProfile`
建议数值定位:
- 永远是“辅助收益”
- 不和 build 标签争主导权
## 4.2 玩家 build 缺口驱动
建议新系统在生成物品时先判断当前玩家缺口,而不是先随机抽品类。
建议至少识别这些缺口:
- `survival_gap`
- 当前缺 `守御 / 护体 / 回复 / 续战`
- `mana_gap`
- 当前缺 `法力 / 冷却 / 节奏恢复`
- `finisher_gap`
- 当前缺 `爆发 / 重击 / 追击`
- `mobility_gap`
- 当前缺 `突进 / 快袭 / 风行 / 游击`
- `control_gap`
- 当前缺 `控场 / 符阵 / 镇邪`
运行时物品应优先:
- 补当前明显缺口
- 或强化当前已经成型的主方向
## 5. 如何让物品和背景 / NPC / 场景高度贴合
## 5.1 必须引入“关系锚点”
建议每个 AI 原生运行时物品都必须绑定至少一个 `relationAnchor`
```ts
type RuntimeRelationAnchor =
| { type: "npc"; npcId?: string; npcName: string; roleText?: string }
| { type: "scene"; sceneId?: string; sceneName: string }
| { type: "landmark"; landmarkName: string }
| { type: "monster"; monsterId?: string; monsterName: string }
| { type: "faction"; factionName: string }
| { type: "quest"; questId?: string; questName: string };
```
如果没有锚点物品最多只能作为普通补给不进入“AI 原生重点物品”。
## 5.2 不同渠道对应不同贴合逻辑
### 宝藏
物品必须同时贴合:
- 当前场景
- `treasureHints`
- 最近故事动作
例如:
- 在矿道拿到的不是泛用剑,而是“矿脉巡火短铳”
- 在旧祭坛拿到的不是泛用药,而是“断誓回神香”
### NPC 交易
物品必须贴合:
- NPC 身份
- NPC 库存风格
- NPC 和玩家关系
例如:
- 黑市牙人卖的是“快袭/情报/潜行”方向的东西
- 医修给的是“回复/续战/净化”方向的东西
### 怪物掉落
物品必须贴合:
- 怪物战斗风格
- 怪物生态来源
- 所在场景
例如:
- 重甲守卫掉 `守御精粹`
- 雾林伏击者掉 `风行羽囊`
### 任务奖励
物品必须贴合:
- 发布人
- 任务目标
- 完成方式
- 当前线索推进
它最适合发“永久关系物”。
## 5.3 命名改成“锚点命名法”
建议运行时重点物品命名遵循:
```text
来源词 + 关系词 + 功能词
```
而不是:
```text
稀有前缀 + 通用品类名
```
例子:
- `锁风渡缉索短刃`
- `裂界巡守压纹符`
- `药谷回岚灵露`
- `断碑旧誓护心佩`
其中:
- 来源词来自场景/地标/势力
- 关系词来自 NPC / 怪物 / 事件
- 功能词来自 build 或物品品类
## 6. 新系统里的预算规则
## 6.1 稀有度预算
建议按稀有度控制:
- 能有几个 build 标签
- 能不能带关系锚点
- 能不能有 set 倾向
- 数值范围上限
建议预算如下:
| 稀有度 | build 标签 | 限时 buff | 数值强度 | 叙事强度 |
| --- | --- | --- | --- | --- |
| common | 0~1 | 1 | 很小 | 轻 |
| uncommon | 1 | 1~2 | 小 | 轻 |
| rare | 1~2 | 2 | 小到中 | 中 |
| epic | 2 | 2 | 中 | 中到高 |
| legendary | 2 + 关系锚点 | 2~3 | 中 | 高 |
## 6.2 渠道预算
不同渠道不该发同样强度的物品。
建议:
- `treasure`
- 偏单件强语义物
- `npc_trade`
- 偏稳定、可预期、可补短板
- `npc_reward`
- 偏关系定制与限时支援物
- `monster_drop`
- 偏材料 / 精粹 / 生态锚点物
- `quest_reward`
- 偏永久 build 锚点物
- `discovery`
- 偏线索物 / 过渡工具物
## 6.3 build 方向切换限制
新系统建议引入一条硬规则:
**普通运行时物品默认只能强化当前 build 或其邻近 build不应该高频强制转流派。**
也就是:
- 当前偏 `快剑/突进`,更容易给 `追击/风行`
- 当前偏 `守御/护体`,更容易给 `续战/回复`
- 当前偏 `法修/雷法`,更容易给 `过载/冷却`
真正能强制开新流派的物品,应只出现在:
- 高价值任务奖励
- 关键宝藏
- 重要 NPC 关系突破
## 7. 建议新增的数据结构
## 7.1 运行时 metadata
建议在 `InventoryItem` 上新增:
```ts
interface RuntimeItemMetadata {
origin: "catalog" | "procedural" | "ai_compiled";
generationChannel: RuntimeItemGenerationChannel;
seedKey: string;
relationAnchor?: RuntimeRelationAnchor;
sourceReason: string;
recentEventHook?: string;
}
```
然后在 `InventoryItem` 上挂:
```ts
interface InventoryItem {
runtimeMetadata?: RuntimeItemMetadata;
}
```
这样后面:
- UI 可展示“来源”
- 日志可回放“为什么拿到”
- 剧情可引用“这是谁给你的”
## 7.2 运行时导演结果
```ts
type DirectedRuntimeReward = {
primaryItem?: InventoryItem | null;
supportItems: InventoryItem[];
hp?: number;
mana?: number;
currency?: number;
storyHint?: string;
};
```
这样可以统一替代宝藏、帮助奖励、任务奖励等零散结构。
## 8. 与现有模块的接入方式
## 8.1 `treasureInteractions.ts`
重构方式:
- 现在:内部直接从世界池挑物品
- 以后:调用 `runtimeItemDirector`
建议流程:
1.`GameState + Encounter + ScenePreset` 组 context
2. 指定 `generationChannel = "treasure"`
3. director 产出 `DirectedRuntimeReward`
4. 再由 `buildTreasureResultText` 读 reward 回写文本
这是最适合作为第一落点的入口。
## 8.2 `npcInteractions.ts`
接入方向:
- 商店货物补货
- NPC 帮助奖励
- 高好感赠与
- 特殊 NPC 线索物
这里最适合体现“关系锚点”。
## 8.3 `forgeSystem.ts`
锻造系统不需要完全 AI 化,但应该读取 AI 原生物品遗留的 metadata
- 拆解时保留关系信息
- 产出对应方向的精粹
- 重铸时优先在邻近 build 内滚动
这样 AI 原生物品与锻造闭环才是连通的。
## 8.4 `customWorldRuntime.ts`
这个模块不要废弃,而是改成:
- 继续负责“主题物品池”
- director 在需要 fallback 或大批量补货时调用它
也就是说:
- `customWorldRuntime.ts` 保留“池”
- `runtimeItemDirector.ts` 新增“导演”
## 9. 推荐实施顺序
## 阶段 A先加类型和 metadata
先做:
- `runtimeItem.ts`
- `InventoryItem.runtimeMetadata`
- `RuntimeRelationAnchor`
- `RuntimeItemGenerationContext`
目的:
- 不改玩法,只把类型基础搭好
## 阶段 B先做 director + compiler
先不接所有入口,只把:
- context 采样
- AI 意图结构
- 本地编译器
跑通。
## 阶段 C先接宝藏
原因:
- 独立
- 风险低
- 最容易观察“背景贴合”提升
## 阶段 D再接 NPC 奖励与交易
这一步会明显提升:
- 关系系统
- 世界贴脸感
- 物品来源可解释性
## 阶段 E最后接任务奖励与怪物语义掉落
因为这两类和现有逻辑耦合更深,适合后置。
## 10. 和旧版方案相比,这次重设计改了什么
相比之前偏概念化的 AI 原生物品方案,这次重设计更明确了 5 点:
1. **不新起物品系统**
- 直接复用 `InventoryItem` 与现有 build 结构。
2. **不让 AI 直接出成品**
- 只让 AI 出意图,交给本地编译器落地。
3. **所有渠道走同一导演层**
- 不再把宝藏、NPC、任务各写一套。
4. **把“关系锚点”正式数据化**
- 不是只写在文案里。
5. **先从宝藏接入,再逐步扩展**
- 不是一次推全仓库。
## 11. 一句话总结
新的 AI 原生运行时物品生成系统,不应该是“让 AI 随机写几个看起来很酷的装备”,而应该是:
**让 AI 根据当前世界、场景、NPC、事件和玩家 build 给出物品意图,再由本地规则把这个意图编译成能进背包、能进 build、能进锻造、能进存档、还能被剧情解释的正式物品。**

View File

@@ -1,149 +0,0 @@
# 陶泥儿线下展会易拉宝设计记录 2026-05-07
## 1. 目标
为陶泥儿线下展会制作一张纵向易拉宝广告展板,用于在展位现场快速传达:
1. 产品名称:陶泥儿。
2. 产品愿景陶泥儿AI团队致力于打造AI互动内容UGC平台。
3. 产品slogan不用代码不用美术10分钟把脑洞变成有趣的体验。
4. 产品特点:低门槛创作、高完成度作品、玩过后可改造并发布。
5. 关键技术Harness Engineering、多Agent调度、AI创作工具、AI原生游戏框架。
6. 产品心智:想玩但找不到、玩到不满意、平台外体验不满意时,都可以来陶泥儿做成自己满意的。
## 2. 视觉方向
本次延续 `BAIMENG_LOGO_GPT_IMAGE_2_CONCEPTS_2026-05-05.md` 中最新收敛的气泡共创方向:
- 参考 logo`output/imagegen/baimeng-logo-bubble-04-07-refine-batch13/baimeng-bubble-04-07-refine-01-04-flat-rainbow-band-core.png`
- 主视觉语义轻盈气泡、很多创意、UGC共创、作品改造与分享。
- 色彩:暖白、珊瑚粉、薰衣草紫、天蓝、薄荷青、少量金色光感。
- 展会阅读策略远看先读产品名与slogan近看再读产品心智与关键技术。
## 3. 文案层级
最终展板文案压缩为四层。
第一层:品牌识别
```text
陶泥儿
10分钟做自己的互动内容
```
第二层远读slogan
```text
不用代码,不用美术,
10分钟把脑洞变成有趣的体验
```
第三层:产品特点
```text
10分钟成品级创作
玩过就能改造发布
创意到作品闭环
```
并拆成三张近读卡:
```text
创作从一句灵感开始AI帮助完成剧本、角色、场景、系统与视觉草稿。
游玩:作品不是静态文本,而是可进入、可推进、可演出的互动体验。
改造:玩到不满意的作品,可以快速改成自己喜欢的版本并再次发布。
```
第四层:产品心智与关键技术
```text
当用户找不到想玩的游戏 -> 来陶泥儿做给自己玩
当用户玩到了不好玩的游戏 -> 快速改成自己喜欢玩的
当用户在平台外玩到了不满意的游戏 -> 来陶泥儿做成自己满意的
什么游戏最好玩? -> 来陶泥儿玩自己做的游戏最好玩
```
关键技术压缩为:
```text
基于 Harness Engineering 理论,将专家知识融入 AI 创作工具与 AI 原生游戏框架。
AI创作工具
通过多Agent调度算法把策划、美术SOP与专家知识融入模板框架提升剧本类、数值类、系统类、角色设计、场景设计、CG设计等垂类任务的完成率和效率。
AI原生游戏框架
通过系统化约束模型输入输出,把实时剧本创作和游戏设计专家知识内嵌在规则中,提升实时剧情、数值对齐、画面对齐、任务与物品生成质量。
```
## 4. 生成方式
主视觉底图使用仓库内 `gpt-image-2` 工作流生成2026-05-09 起同类工作流走 VectorEngine
```text
model: gpt-image-2
size: 1536x3840
reference image: 陶泥儿气泡共创logo方向图
output: output/imagegen/baimeng-expo-rollup/baimeng-rollup-background-gpt-image-2.png
```
2026-05-08 根据新文案重新调用 `gpt-image-2` 生成新版底图。新版底图在中上部保留更干净的两行 slogan 留白,并在下半部增加轻量内容卡、创作路径和 AI 辅助创作氛围,最终再叠加精确中文排版。
因为图片模型直接生成中文长文案存在错字风险最终稿采用“gpt-image-2 底图 + 本地精确中文排版”的方式生成:
```text
tmp/imagegen/generate-baimeng-rollup-background.mjs
tmp/imagegen/render-baimeng-rollup.py
```
最终输出:
```text
output/imagegen/baimeng-expo-rollup/baimeng-rollup-final-cn.png
output/imagegen/baimeng-expo-rollup/baimeng-rollup-final-cn-preview.png
```
## 5. 后续改版建议
1. 若印刷厂提供具体尺寸和出血要求,优先在 `render-baimeng-rollup.py` 中调整画布比例、边距和安全区。
2. 若需要放二维码,应放在底部独立留白区,不遮挡产品心智和关键技术段。
3. 若展会现场观众偏投资人或B端合作方可以把“产品心智”段压缩放大“关键技术”与平台愿景。
4. 若观众偏玩家或普通创作者可以把“关键技术”段压缩放大“10分钟创作、玩过就改、发布分享”的闭环。
## 6. 公司招聘版 2026-05-11
2026-05-11 根据线下招聘场景,将海报方向从“纯产品宣传”调整为“公司 + 产品 + 岗位”的整体宣传。
新版定位:
```text
北京亓盒网络科技有限公司
岗位名称AI 原生游戏产品/内容实习生
行业方向AI 原生游戏 × UGC 内容创作 × 互动叙事
产品:陶泥儿 AI互动内容创作平台
```
新版保留陶泥儿气泡色彩、轻盈白底和创作流动感但新增校园实验室、AI 游戏创作、作品卡、产品测试与内容设计氛围。版面结构调整为:
1. 顶部:公司名、岗位名、行业方向与招聘主标题。
2. 中上:陶泥儿产品主张与三枚产品能力标签。
3. 中部:按 `游玩 -> 改造 -> 创作` 顺序展示产品体验闭环。
4. 中下:介绍“我们正在做的事「陶泥儿」”。
5. 下部:实习生参与内容、加分项、团队背景和联系方式。
6. 底部:预留两个方形二维码占位,收尾文案为 `陶泥儿 | 让每个人都能做自己的游戏`
新版使用当前仓库 `VectorEngine gpt-image-2-all` 路径生成底图:
```text
model: gpt-image-2-all
size: 1536x3840
reference image 1: 用户提供的上一版海报截图
reference image 2: 陶泥儿气泡共创logo方向图
output: output/imagegen/baimeng-recruitment-rollup/baimeng-recruitment-rollup-background-gpt-image-2-all.png
```
最终输出:
```text
output/imagegen/baimeng-recruitment-rollup/baimeng-recruitment-rollup-final-cn.png
output/imagegen/baimeng-recruitment-rollup/baimeng-recruitment-rollup-final-cn-preview.png
```

View File

@@ -1,287 +0,0 @@
# 陶泥儿 logo gpt-image-2 概念方案 2026-05-05
## 1. 产品气质提炼
当前产品对外名为“陶泥儿”,核心不是单一 RPG 玩法,而是面向互动叙事、世界生成和运行时演出的 AI 原生创作平台。
本次 logo 概念围绕以下关键词设计:
1. `百`:多世界、多题材、多角色关系,不是单条故事线。
2. `梦`:创作者的世界锚点、想象入口和叙事氛围。
3. `AI 原生`AI 负责叙事表达、关系生成和世界扩展。
4. `规则裁决`:本地系统负责状态、任务、背包、招募、存档等可信边界。
5. `视觉 RPG`:保留游戏感、角色感和舞台感,但平台层需要比纯像素 UI 更清爽。
## 2. 方案 A梦门星轨
视觉方向:
- 用一个打开的门 / 星门作为主体,门内有多条细线向外延展,代表陶泥儿主从一个灵感入口进入多个世界。
- 负形中弱化出“百”的结构,不强行写字,方便后续做 App icon、favicon 和小尺寸导航标。
- 色彩以暖白、珊瑚粉、少量紫蓝和深墨色组成,贴近平台亮色主题,同时保留梦境和 AI 的科技感。
适合场景:
- 平台主 logo
- App icon
- 创作入口主视觉
设计含义:
“梦门”代表创作入口,“星轨”代表剧情线程、角色关系和世界分支。它强调陶泥儿是让创作者开启世界的工具,而不是只播放固定剧情的游戏。
## 3. 方案 B叙事图谱
视觉方向:
- 用节点、弧线和轻微书页轮廓组成一个稳定的圆形标志。
- 图谱整体形成近似“百”的秩序感,避免复杂到小尺寸失真。
- 色彩以深墨、湖青、泥点金和珊瑚色组合,体现规则边界与创作活力并存。
适合场景:
- 技术品牌页
- 开发者文档
- AI 剧情引擎介绍
设计含义:
这个方向更强调“世界不是一段文本,而是一张可控的叙事图谱”。节点是角色、地点、物件和任务,弧线是关系、暗线与回响。
## 4. 方案 C泥点梦织
视觉方向:
- 多个泥点汇聚成柔和的“B / 百”抽象符号,中心像一枚被点亮的种子。
- 线条更轻,图形更偏平台化和消费级,不做重游戏徽章。
- 色彩采用珊瑚粉、暖金、浅青和深色细线,表达“泥点”消费单位与灵感生长。
适合场景:
- 移动端启动页
- 用户增长、邀请、充值与创作激励相关页面
- 更轻量的品牌应用
设计含义:
每个泥点都是一次生成、一次灵感、一次世界推进。多点汇聚成梦,呼应“陶泥儿”里从许多小创意生长出完整作品的路径。
## 5. 方案 D像素剧场
视觉方向:
- 以轻度像素化的舞台窗、卷轴和星形光标构成标志,但不使用重像素边框。
- 保留视觉 RPG 的游戏感,适合作为游戏内或活动页的品牌变体。
- 色彩更高对比,适配暗色游戏 UI也能在亮色平台层中作为强调图标。
适合场景:
- 游戏内 HUD 品牌露出
- 活动页、社区头像
- 复古 RPG 氛围更强的物料
设计含义:
舞台代表演出,卷轴代表叙事,星形光标代表 AI 与玩家选择。它让陶泥儿看起来仍然属于游戏和互动内容,而不是泛 AI 工具。
## 6. 生成说明
本次推荐先用 `gpt-image-2` 生成 4 张 `1024x1024` 方形草案。由于图片模型对中文文字的稳定性有限,首轮应优先验证“图形标识”而不是直接把“陶泥儿”两个字烘焙进图片。最终产品落地时,建议使用真实前端字体或 SVG 字形承载“陶泥儿”字标,把生成图作为图形标志参考。
## 7. 女性友好与全年龄潮流版补充
在用户明确希望吸引女性用户或全年龄段用户后logo 方向从“玄感书法 / 高级符印”调整为“可爱、圆润、潮流、轻社交平台感”。
视觉方向:
- 字形更圆润,减少尖锐笔锋、黑底压迫感和玄幻气质。
- 色彩从黑白、金色、深墨切换到奶油白、莓果粉、薰衣草紫、薄荷青。
- 允许轻量梦泡、云朵、柔软泥点等符号,但不堆叠插画。
- 保留“陶泥儿”中文字标作为主识别,图标可作为 App icon 或社交头像补充。
本轮生成 prompt
`tmp/imagegen/baimeng_logo_cute_trendy_batch6_prompts.jsonl`
本轮输出目录:
`output/imagegen/baimeng-logo-cute-trendy-batch6/`
当前更适合作为产品主方向的是 `01-rounded-wordmark``04-icon-wordmark-lockup``03-dream-bubble-icon``05-soft-blob-mark` 更适合作为 App 图标或营销贴纸,而不是完整品牌字标。
## 8. 生活物件原型抽象版补充
在用户进一步要求“保持扁平和抽象,可以意象一些生活中较常见的事物作为原型”后,本轮将图形从糖果质感的梦泡收敛为更扁平的日常物件抽象。
候选原型:
- 枕头
- 小夜灯
- 便签 / 书签
- 杯垫 / 泡泡饮料
- 香薰 / 扩香石
- 糖纸 / 包装折角
- 叠放贴纸
- 睡眠眼罩 / 软窗帘
本轮生成 prompt
`tmp/imagegen/baimeng_logo_flat_daily_object_batch8_prompts.jsonl`
本轮输出目录:
`output/imagegen/baimeng-logo-flat-daily-object-batch8/`
当前最值得继续推进的是 `03-bookmark-notes`:它保留了扁平、抽象、生活物件原型和创作平台语义,也较少落入儿童化或睡眠 App 的既有联想。`06-candy-wrapper` 可以作为更潮流的备选方向,但品牌语义比书签/便签弱。
## 9. 简化产品主标版补充
在用户反馈“整体还不错,但元素太碎,有些适合作为 icon 但不适合作为产品 logo”后本轮将生活物件原型继续压缩为
- `1` 个主体轮廓
- `1` 个负形
- 不使用文字、星点、叠片、贴纸装饰和多层方案板
本轮生成 prompt
`tmp/imagegen/baimeng_logo_simplified_product_batch9_prompts.jsonl`
本轮输出目录:
`output/imagegen/baimeng-logo-simplified-product-batch9/`
当前最值得继续推进的是 `03-single-bookmark``06-rounded-square-notch`
- `03-single-bookmark` 更像独立品牌符号,保留了书签 / 作品卡 / 世界入口语义。
- `06-rounded-square-notch` 更像 App icon 主体,亲和、简洁,但需要降低通用 App 图标感。
不建议继续推进 `01/02/07/08`,它们被模型渲染成了方案板或 icon set`05` 的版式参考价值高,但中文文字仍不能直接作为最终资产。
## 10. 气泡共创主标版补充
在用户明确提出“用轻盈的气泡展现梦,用多个气泡展现很多(百),气泡交错表示 UGC、共创、分享”后本轮将主方向收敛到多气泡共创符号。
设计原则:
-`3-5` 个大气泡表达“很多 / 百”,避免散乱小点。
- 气泡之间必须交错或重叠,表达用户创作互相连接、分享和共创。
- 整体必须收束成一个可识别主轮廓,而不是一组装饰元素。
- 保持扁平、抽象、轻盈、女性友好和全年龄亲和。
本轮生成 prompt
`tmp/imagegen/baimeng_logo_bubble_cocreation_batch10_prompts.jsonl`
本轮输出目录:
`output/imagegen/baimeng-logo-bubble-cocreation-batch10/`
当前最值得继续推进的是:
- `08-minimal-three-bubbles`:最像产品 logo结构克制气泡交错和共创语义清楚。
- `01-overlap-cluster`:更适合品牌主视觉,轻盈梦感更明显,但作为小尺寸 logo 稍弱。
- `06-sharing-knot`:共创感强,但中间负形需要继续简化。
## 11. 吹泡泡行为亲和版补充
在用户进一步提出“希望落在现实中常见的事物或行为上,比如吹泡泡行为,让用户觉得接地气或使用起来很简单,有亲和力”后,本轮将气泡主标落到“泡泡棒 / 泡泡水 / 轻轻吹出泡泡”的日常原型。
设计原则:
- 泡泡棒或泡泡圈表达“很简单、人人会用、低门槛”。
- `3-4` 个气泡表达梦、很多、分享和共创。
- 不画人物、嘴巴、小孩或玩具包装,避免儿童用品感。
- 保持扁平抽象和品牌主标感,避免成为功能 icon。
本轮生成 prompt
`tmp/imagegen/baimeng_logo_bubble_wand_batch11_prompts.jsonl`
本轮输出目录:
`output/imagegen/baimeng-logo-bubble-wand-batch11/`
当前最值得继续推进的是:
- `08-minimal-ring-bubbles`:最干净,保留泡泡棒原型,同时不太幼稚。
- `01-ring-three-bubbles`:识别直接,但工具 icon 感略强。
- `04-breath-origin`:梦感更强,但现实吹泡泡行为弱。
## 12. 吹泡泡主标再收敛版补充
在“方向正确,再来一些”的基础上,本轮继续压缩泡泡棒方向,目标是减少玩具感,让它更像成熟产品主标。
本轮生成 prompt
`tmp/imagegen/baimeng_logo_bubble_refine_batch12_prompts.jsonl`
本轮输出目录:
`output/imagegen/baimeng-logo-bubble-refine-batch12/`
当前最值得继续推进的是:
- `02-simple-action-mark`:泡泡棒行为明确,结构干净,比上一批更接地气,也不太幼稚。
- `06-one-plus-two-bubbles`:更抽象、更高级,但现实吹泡泡行为感弱一些。
- `08-final-simple-bubbles`:适合 App icon但聊天气泡联想较强需弱化社交聊天框尾巴。
不建议推进 `03/04/07`,它们出现了不稳定文字或方案式字标;`01` 容易被误认为放大镜。
批量生成 prompt 已放在:
`tmp/imagegen/baimeng_logo_gpt_image_2_prompts.jsonl`
建议输出目录:
`output/imagegen/baimeng-logo/`
生成命令:
```powershell
$env:OPENAI_API_KEY="本机已配置的 OpenAI API Key"
python "C:\Users\wuxiangwanzi\.codex\skills\.system\imagegen\scripts\image_gen.py" generate-batch `
--input tmp\imagegen\baimeng_logo_gpt_image_2_prompts.jsonl `
--out-dir output\imagegen\baimeng-logo `
--concurrency 2 `
--quality high `
--size 1024x1024
```
若继续使用仓库现有 VectorEngine GPT-image-2 路由,则需要配置:
```text
VECTOR_ENGINE_BASE_URL=https://api.vectorengine.ai
VECTOR_ENGINE_API_KEY=...
```
## 13. 04/07 气泡方向单独优化补充
在用户反馈“更喜欢 04 和 07”后本轮不再横向发散其它生活物件而是只围绕两条已被接受的视觉方向做收敛
- 继承 `04` 的中心大泡泡:保留多条虹彩色带组成的饱满主视觉,但压低小泡泡的高光、阴影和玻璃拟物感。
- 继承 `07` 的色彩和轻轻吹泡泡行为:减少元素数量,确保整体居中,避免一串气泡散向右上角。
- 明确排除 `03/05/08` 中容易出现的聊天软件联想,不使用聊天尾巴、对话框轮廓、碎小装饰点和星形元素。
本轮生成 prompt
`tmp/imagegen/baimeng_logo_bubble_04_07_refine_batch13_prompts.jsonl`
本轮输出目录:
`output/imagegen/baimeng-logo-bubble-04-07-refine-batch13/`
联系表:
`output/imagegen/baimeng-logo-bubble-04-07-refine-batch13/baimeng-bubble-04-07-refine-batch13-contact-sheet.png`
当前最值得继续推进的是:
- `01-04-flat-rainbow-band-core`:保留了 04 的彩虹色带饱满度,同时只留下一个小辅助泡泡,聊天气泡联想弱,适合作为“梦 / 很多 / 共创”的品牌主标继续精修。
- `02-04-single-full-bubble-ring`:最克制、最像可注册主符号,完全去掉碎元素;缺点是“吹泡泡行为”和 UGC 共创感比其它方案弱,可作为极简品牌基线。
- `08-07-rainbow-breath-symbol`:较好融合了 04 的虹彩大环与 07 的吹泡泡动作,亲和度高;后续需要继续压缩右上两个泡泡的体量,避免重心偏右。
不建议优先推进:
- `03-04-overlap-two-bubbles-brand`:结构清楚,但小环容易变成独立图标,整体略硬。
- `04-04-rainbow-bubble-wand-minimal`:泡泡棒语义明确,但下方手柄过于具象,容易像工具图标。
- `05/06/07`:色彩和亲和力可参考,但泡泡簇仍比 01/02/08 更接近装饰插画,产品主标凝聚力不足。

View File

@@ -1,529 +0,0 @@
# 儿童动作识别互动玩法 Demo 热身关开发文档
> 日期2026-05-09
> 适用范围:儿童动作识别互动玩法 Demo 的固定启动热身关
> 文档性质:玩法 Demo 开发设计文档
> 说明:本文整理当前已确认的热身关内容、体验、流程和热身数据记录要求。
## 1. 热身关定位
热身关是 Demo 启动后的固定流程,用于在正式进入后续趣味学习关前完成以下事项:
- 调用摄像头;
- 识别用户和环境;
- 引导用户来到建议互动位置;
- 教学基础交互方式;
- 确认用户可在互动空间内完成左右移动、挥手和跳跃;
- 记录用户左右移动距离、挥动手臂空间和跳跃空间,作为后续关卡的空间边界与行为坐标;
- 完成后进入关卡选择。
热身关不接入创作模块,不作为可配置玩法模板提供给创作者。
## 2. 屏幕与设备适配
本产品适用于电视屏幕、电脑屏幕等环境。
热身关制作表达使用横屏比例。
## 3. 画面基础表现
用户进入热身关后,摄像头被调用,并开始识别用户和环境。
画面基础表现如下:
1. 在屏幕中央位置的地面生成预设的绿色圆环,作为建议位置的指引。
2. 将用户的实际位置生成角色剪影,作为用户在画面中的标识。
3. 只对摄像头背景做虚化处理,用于表达对用户隐私的保护、屏蔽周围环境干扰,并营造空间感。
## 4. 通用检测与引导规则
### 4.1 不允许跳过
热身关每个步骤都必须由用户完成,不允许跳过,也不允许系统自动进入下一步。
### 4.2 引导动画播放规则
每个动作等待 3 秒后可以播放引导动画。
当前不设置最长等待时间。
### 4.3 绿色圆环完成规则
用户到达绿色圆环后,绿色圆环进入 2 秒选中状态。
用户需要在绿色圆环内保持停留 2 秒,才算完成该圆环位置检测。
### 4.4 左右距离映射规则
“约半米”的左右移动距离,技术上以角色剪影移动距离为准。
该距离后续会根据实际体验继续调校。
### 4.5 手势区分规则
招手 / 摆手、挥动左手、挥动右手三类动作需要有动作区分。
手势检测仅对肢体进行区分,不对手部细节进行区分。
### 4.6 手势引导规则
挥动哪只手,就使用对应手的引导。
## 5. 热身关完整流程
### 5.1 进入热身关
#### 画面表现
- 摄像头被调用。
- 系统识别用户和环境。
- 屏幕中央位置的地面出现预设绿色圆环。
- 用户实际位置以角色剪影形式显示。
- 只对摄像头背景做虚化处理,保留空间感。
#### 屏幕文字与语音
屏幕中上方浮现文字,同时语音播报:
```text
欢迎你,小朋友,见到你真开心
```
随后继续播报:
```text
请你来到圆圈这里和我打个招呼吧
```
#### 检测逻辑
系统检测用户是否到达屏幕中央绿色圆环位置。
用户到达圆环后,绿色圆环进入 2 秒选中状态。用户保持停留 2 秒后,该步骤完成。
#### 完成反馈
用户完成中央圆环位置检测后:
- 播放圆圈消失特效;
- 进入招手手势教学步骤。
---
### 5.2 招手教学
#### 画面表现
播放招手的手势引导。
若用户进入该步骤后 3 秒仍未完成动作,可以播放引导动画。
#### 检测逻辑
系统检测用户是否完成招手 / 摆手手势。
该动作与后续挥动左手、挥动右手需要有动作区分,但仅对肢体进行区分,不对手部细节进行区分。
#### 完成反馈
用户完成招手 / 摆手手势后,进入下一步。
---
### 5.3 热身说明
#### 屏幕文字与语音
```text
你好呀小朋友,为了你玩的安全和开心,先来和我一起热个身吧
```
播放完成后进入左右移动热身步骤。
---
### 5.4 向左一步
#### 屏幕文字与语音
```text
向左一步
```
#### 画面表现
屏幕中心向左一个身位,约半米的地面位置,出现新的绿色圆圈。
“约半米”技术上以角色剪影移动距离为准,后续根据体验调校。
#### 检测逻辑
系统检测用户是否到达该绿色圆圈位置。
用户到达圆环后,绿色圆环进入 2 秒选中状态。用户保持停留 2 秒后,该步骤完成。
#### 完成反馈
用户完成后播放鼓励语:
```text
真棒
```
同时记录本次向左移动距离,作为后续关卡中的左侧空间边界参考。
完成后进入“回到中间来”。
---
### 5.5 回到中间来(一)
#### 屏幕文字与语音
```text
回到中间来
```
#### 画面表现
场地中心位置出现绿色圆圈。
#### 检测逻辑
系统检测用户是否到达场地中心绿色圆圈位置。
用户到达圆环后,绿色圆环进入 2 秒选中状态。用户保持停留 2 秒后,该步骤完成。
#### 完成反馈
用户完成后播放鼓励语:
```text
真棒
```
完成后进入“向右一步”。
---
### 5.6 向右一步
#### 屏幕文字与语音
```text
向右一步
```
#### 画面表现
屏幕中心向右一个身位,约半米的地面位置,出现新的绿色圆圈。
“约半米”技术上以角色剪影移动距离为准,后续根据体验调校。
#### 检测逻辑
系统检测用户是否到达该绿色圆圈位置。
用户到达圆环后,绿色圆环进入 2 秒选中状态。用户保持停留 2 秒后,该步骤完成。
#### 完成反馈
用户完成后播放鼓励语:
```text
真棒
```
同时记录本次向右移动距离,作为后续关卡中的右侧空间边界参考。
完成后进入“回到中间来”。
---
### 5.7 回到中间来(二)
#### 屏幕文字与语音
```text
回到中间来
```
#### 画面表现
场地中心位置出现绿色圆圈。
#### 检测逻辑
系统检测用户是否到达场地中心绿色圆圈位置。
用户到达圆环后,绿色圆环进入 2 秒选中状态。用户保持停留 2 秒后,该步骤完成。
#### 完成反馈
用户完成后播放鼓励语:
```text
真棒
```
完成后进入左手挥动教学。
---
### 5.8 挥动左手
#### 屏幕文字与语音
```text
挥动左手
```
#### 画面表现
播放伸展手臂挥动左手的手势引导。
若用户进入该步骤后 3 秒仍未完成动作,可以播放引导动画。
#### 检测逻辑
系统检测用户是否完成挥动左手手势。
该手势检测仅对肢体进行区分,不对手部细节进行区分。
#### 完成反馈
用户完成后播放鼓励语:
```text
真棒
```
同时记录用户挥动左手的空间,保存为该用户对应的行为坐标。
完成后进入右手挥动教学。
---
### 5.9 挥动右手
#### 屏幕文字与语音
```text
挥动右手
```
#### 画面表现
播放伸展手臂挥动右手的手势引导。
若用户进入该步骤后 3 秒仍未完成动作,可以播放引导动画。
#### 检测逻辑
系统检测用户是否完成挥动右手手势。
该手势检测仅对肢体进行区分,不对手部细节进行区分。
#### 完成反馈
用户完成后播放鼓励语:
```text
真棒
```
同时记录用户挥动右手的空间,保存为该用户对应的行为坐标。
完成后进入跳跃教学。
---
### 5.10 原地跳一下
#### 屏幕文字与语音
```text
原地跳一下
```
#### 画面表现
播放跳跃姿势引导。
若用户进入该步骤后 3 秒仍未完成动作,可以播放引导动画。
#### 检测逻辑
系统检测用户是否完成跳跃姿势。
#### 完成反馈
用户完成后:
- 记录用户跳跃空间,保存为该用户对应的行为坐标;
- 播放热身结束特效、上浮字幕和语音:
```text
真厉害,你是我见过最聪明的小朋友
```
随后继续播放:
```text
别走开,现在开始我们的游戏吧
```
热身关结束,进入关卡选择。
## 6. 流程状态表
| 顺序 | 步骤 | 屏幕文字 / 语音 | 画面表现 | 检测目标 | 完成后反馈 |
|---:|---|---|---|---|---|
| 1 | 进入热身关 | 欢迎你,小朋友,见到你真开心;请你来到圆圈这里和我打个招呼吧 | 中央地面绿色圆环;用户角色剪影;摄像头背景虚化 | 用户到达中央圆环并保持 2 秒 | 圆圈消失特效 |
| 2 | 招手教学 | 同上流程延续 | 招手手势引导;等待 3 秒可播放引导动画 | 招手 / 摆手 | 进入下一步 |
| 3 | 热身说明 | 你好呀小朋友,为了你玩的安全和开心,先来和我一起热个身吧 | 保持热身引导状态 | 无新增动作检测 | 进入移动热身 |
| 4 | 向左一步 | 向左一步 | 左侧约半米处绿色圆圈 | 用户到达左侧圆环并保持 2 秒 | 真棒;记录左侧空间边界 |
| 5 | 回到中间来 | 回到中间来 | 中心位置绿色圆圈 | 用户到达中心圆环并保持 2 秒 | 真棒 |
| 6 | 向右一步 | 向右一步 | 右侧约半米处绿色圆圈 | 用户到达右侧圆环并保持 2 秒 | 真棒;记录右侧空间边界 |
| 7 | 回到中间来 | 回到中间来 | 中心位置绿色圆圈 | 用户到达中心圆环并保持 2 秒 | 真棒 |
| 8 | 挥动左手 | 挥动左手 | 伸展手臂挥动左手手势引导;等待 3 秒可播放引导动画 | 挥动左手 | 真棒;记录左手挥动空间 |
| 9 | 挥动右手 | 挥动右手 | 伸展手臂挥动右手手势引导;等待 3 秒可播放引导动画 | 挥动右手 | 真棒;记录右手挥动空间 |
| 10 | 原地跳一下 | 原地跳一下 | 跳跃姿势引导;等待 3 秒可播放引导动画 | 跳跃姿势 | 记录跳跃空间;真厉害,你是我见过最聪明的小朋友;别走开,现在开始我们的游戏吧;进入关卡选择 |
## 7. 固定文案与语音清单
以下文案需要作为屏幕中上方浮现文字,并同步语音播报。
```text
欢迎你,小朋友,见到你真开心
请你来到圆圈这里和我打个招呼吧
你好呀小朋友,为了你玩的安全和开心,先来和我一起热个身吧
向左一步
真棒
回到中间来
真棒
向右一步
真棒
回到中间来
真棒
挥动左手
真棒
挥动右手
真棒
原地跳一下
真厉害,你是我见过最聪明的小朋友
别走开,现在开始我们的游戏吧
```
## 8. 需要开发支持的识别能力
热身关当前流程需要支持以下识别能力:
1. 摄像头调用;
2. 用户识别;
3. 环境识别;
4. 用户实际位置识别;
5. 用户是否到达中央绿色圆环位置;
6. 用户是否在绿色圆环内持续保持 2 秒;
7. 用户是否到达左侧约半米绿色圆环位置;
8. 用户是否到达右侧约半米绿色圆环位置;
9. 招手 / 摆手手势识别;
10. 挥动左手识别;
11. 挥动右手识别;
12. 原地跳跃姿势识别;
13. 用户左右移动距离记录;
14. 用户挥动手臂空间记录;
15. 用户跳跃空间记录。
## 9. 需要开发支持的表现能力
热身关当前流程需要支持以下表现能力:
1. 横屏比例显示;
2. 摄像头背景虚化;
3. 用户位置生成角色剪影;
4. 屏幕中央地面绿色圆环;
5. 左侧约半米地面绿色圆环;
6. 右侧约半米地面绿色圆环;
7. 绿色圆环 2 秒选中状态;
8. 圆圈消失特效;
9. 招手手势引导;
10. 伸展手臂挥动左手手势引导;
11. 伸展手臂挥动右手手势引导;
12. 跳跃姿势引导;
13. 热身结束特效;
14. 上浮字幕;
15. 语音播报。
## 10. 热身数据记录要求
热身关需要记录以下数据,用于后续关卡的空间边界和行为坐标判断。
### 10.1 左右空间边界
用户完成向左一步后,记录该移动距离,作为后续关卡中的左侧空间边界。
用户完成向右一步后,记录该移动距离,作为后续关卡中的右侧空间边界。
后续关卡中,当用户身体主体覆盖安全边界线时,对应侧屏幕边缘出现虚影提醒。
后续关卡中,当用户身体主体超出安全边界线时:
1. 关卡内容暂停;
2. 屏幕虚化;
3. 屏幕中央地面出现绿色圆圈;
4. 屏幕提示文案:
```text
小朋友,要注意安全哦
```
5. 用户需要回到中心绿色圆圈并保持 2 秒后,才能继续游戏内容。
### 10.2 手臂挥动空间
用户完成挥动左手后,记录用户挥动左手的空间,保存为该用户对应的行为坐标。
用户完成挥动右手后,记录用户挥动右手的空间,保存为该用户对应的行为坐标。
### 10.3 跳跃空间
用户完成原地跳一下后,记录用户跳跃空间,保存为该用户对应的行为坐标。
## 11. 热身关完成条件
热身关完成条件为用户按顺序完成以下流程:
1. 到达中央圆环位置并保持 2 秒;
2. 完成招手 / 摆手手势;
3. 到达左侧约半米圆环位置并保持 2 秒;
4. 记录左侧空间边界;
5. 回到中央圆环位置并保持 2 秒;
6. 到达右侧约半米圆环位置并保持 2 秒;
7. 记录右侧空间边界;
8. 回到中央圆环位置并保持 2 秒;
9. 完成挥动左手;
10. 记录左手挥动空间;
11. 完成挥动右手;
12. 记录右手挥动空间;
13. 完成原地跳一下;
14. 记录跳跃空间;
15. 播放热身结束特效和结束语音;
16. 进入关卡选择。
## 12. 数据保存方式
左右空间边界、手臂挥动空间、跳跃空间仅在当前 Demo 体验会话内保存。
这里的“当前 Demo 体验会话”指用户本次打开并体验 Demo 的过程。当用户关闭 Demo、刷新页面、退出当前体验流程、重新进入 Demo或更换设备后系统不再沿用上一次热身记录的数据需要重新完成热身关并重新记录。
采用仅当前 Demo 体验会话内保存的原因:
1. 每名用户的身高、体型、动作幅度不同,安全边界和行为坐标会发生变化。
2. 当前 Demo 不做特定用户识别,无法确认下一次体验的是否仍是同一名用户。
3. 用户所处的体验环境可能变化,包括房间大小、摄像头位置、屏幕位置和站立距离。
4. 为保证安全,每次体验都需要重新对环境和距离进行安全检查。
## 13. 后续待确认事项
当前暂无待确认事项。

View File

@@ -1,136 +0,0 @@
# 寓教于乐发现页临时入口设计
> 日期2026-05-09
> 适用范围:平台入口发现页、儿童动作识别娱乐教育内容线临时入口
> 文档性质:产品与前端落地边界
## 1. 目标
为儿童动作识别娱乐教育内容线提供一个临时入口。
入口放置在平台“发现”页面内,作为独立标签展示,标签名称为:
```text
寓教于乐
```
后续生产的该内容线模板和游戏关卡,都放置在“寓教于乐”独立标签下。
该内容线当前只覆盖儿童动作识别 Demo 内容。后续创作环节需要继续对该板块内容做区分和独立管理,不把普通公开作品仅凭近似教育题材自动归入本板块。
## 2. 展示边界
寓教于乐内容不直接展示在以下位置:
1. 推荐页;
2. 发现页的推荐标签;
3. 发现页的今日标签;
4. 发现页的分类标签;
5. 发现页的排行标签;
6. 发现页搜索结果。
寓教于乐内容只在“发现 / 寓教于乐”标签下展示。
“寓教于乐”标签在发现页频道列表中放在最后,桌面端和移动端都显示。移动端访问该内容线的动作识别 Demo 时,需要提示横屏体验。
## 3. 开关规则
该入口需要支持灵活开关。
开关打开时:
1. 发现页显示“寓教于乐”标签;
2. “寓教于乐”标签下展示该内容线内容;
3. 该内容线内容仍不进入推荐、今日、分类、排行和搜索结果。
开关关闭时:
1. 发现页隐藏“寓教于乐”标签;
2. 隐藏“寓教于乐”标签下内容;
3. 该内容线内容不进入推荐、今日、分类、排行和搜索结果;
4. 该内容线内容完全不可见,公开作品搜索、作品号搜索直达、公开详情深链、浏览历史入口、创作入口和创作页作品架等平台入口都不能打开或展示该内容。
## 4. 内容识别规则
临时阶段使用作品标签识别寓教于乐内容。
当公开作品标签中存在一个精确等于以下文本的标签:
```text
寓教于乐
```
则该作品归入寓教于乐内容线。
识别规则为精确匹配,不做包含匹配,不兼容空格、大小写变体或同义标签,例如“教育”“儿童教育”“动作教育”都不视为寓教于乐内容。
关闭开关时,即使作品具备精确的“寓教于乐”标签,也不允许通过任何平台公开展示入口或搜索入口访问。
## 5. 技术落地边界
本次只做前端入口和前端展示过滤,不新增后端接口。
前端通过功能开关控制入口显隐。
开关环境变量:
```text
VITE_ENABLE_EDUTAINMENT_ENTRY
```
默认开启。
当该变量显式配置为以下值时,入口关闭:
```text
false
0
off
no
```
## 6. 验收点
1. 开关打开时,发现页显示“寓教于乐”标签。
2. 开关关闭时,发现页不显示“寓教于乐”标签。
3. 带有“寓教于乐”标签的公开作品不进入推荐页。
4. 带有“寓教于乐”标签的公开作品不进入发现页推荐、今日、分类、排行和搜索结果。
5. 带有“寓教于乐”标签的公开作品只在“发现 / 寓教于乐”标签下展示。
6. “寓教于乐”标签位于发现页频道列表最后,桌面端和移动端均可见。
7. 开关关闭后,带有“寓教于乐”标签的公开作品不可通过作品号搜索、公开详情深链或浏览历史入口打开。
8. 标签识别只接受精确等于“寓教于乐”的作品标签,近似标签不归入该内容线。
## 7. 待补充事项
“寓教于乐”标签下暂无内容时的空状态文案待定。落地时可先复用平台现有空状态组件,但不新增功能说明类长文案。
## 8. 第 1-2 项工程落地状态
第 1 项“发现页入口与过滤”和第 2 项“搜索 / 深链 / 历史入口拦截”已进入前端落地阶段,当前实现口径如下:
1. 入口开关由 `VITE_ENABLE_EDUTAINMENT_ENTRY` 控制,默认开启,显式配置 `false``0``off``no` 时关闭。
2. 内容识别集中在 `src/components/platform-entry/platformEdutainmentVisibility.ts`,只读取公开作品原始 `themeTags`,且只接受精确等于“寓教于乐”的标签。
3. `src/components/rpg-entry/RpgEntryHomeView.tsx` 已在发现页频道末尾追加“寓教于乐”频道,并将该类作品从推荐、今日、分类、排行、搜索、本地搜索兜底和桌面推荐模块中过滤。
4. `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx` 已复用同一过滤 helper避免推荐运行态自动启动寓教于乐作品并在公开详情、作品号直达和公开详情深链等公开入口保留不可见保护。
5. 浏览历史入口会优先按当前公开作品集合匹配作品标签;匹配到“寓教于乐”作品且开关关闭时不再展示历史入口。
6. `/child-motion-demo` 本地动作 Demo 直达路由也复用同一开关;开关关闭时不匹配独立 Demo 应用,回落到主站入口。
7. `宝贝识物` 创作入口和创作页作品架也复用同一开关;开关关闭时不展示模板入口,也不展示本地宝贝识物草稿或已发布卡片。
8. 定向回归覆盖在 `src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx``src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx``src/components/platform-entry/platformEdutainmentVisibility.test.ts``src/components/platform-entry/platformEntryCreationTypes.test.ts``src/routing/appRoutes.test.ts`包含频道顺序、开关关闭、普通列表过滤、搜索过滤、作品号直达拦截、Demo 直达路由拦截、创作入口隐藏和精确标签识别。
## 9. 第 4 项作品架 / 广场接入边界
`宝贝识物` 首关的公开作品展示接入按以下口径收口:
1. 平台公共作品模型新增 `sourceType = edutainment`,当前只承接 `templateId = baby-object-match``templateName = 宝贝识物`
2. `宝贝识物` 作品仍必须携带精确等于“寓教于乐”的公开标签,才会进入“发现 / 寓教于乐”频道。
3. `宝贝识物` 不因为模板名自动归入寓教于乐,也不因为近似标签归入寓教于乐。
4. 第 4 项只负责公开作品卡片、发现页专属频道、公开详情、分享作品号和开关隐藏保护。
5. 创作模板、image-2 资产生成、发布接口、运行时开始游戏和关卡状态由对应线程接入;当前公共作品卡直接透传后续数据源提供的 `publicWorkCode`,不在前端新增最终作品号前缀规则。
6. 在创作和运行时链路真正接入前,公开详情内的启动、改造、编辑和点赞只做保护性占位,不新增玩法规则。
当前工程落点:
1. `src/components/rpg-entry/rpgEntryWorldPresentation.ts` 定义 `PlatformEdutainmentGalleryCard``isEdutainmentGalleryEntry`
2. `src/components/rpg-entry/RpgEntryHomeView.tsx``宝贝识物` 卡片识别为寓教于乐公开作品,并继续从推荐、今日、分类、排行和搜索结果中过滤。
3. `src/components/platform-entry/PlatformWorkDetailView.tsx` 在公开详情中显示 `宝贝识物` 类型标签,并继续复用作品号复制和分享链路。
4. `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx` 已识别 `edutainment` 公共作品,避免落入 RPG 默认详情、推荐运行态或错误的改造链路。

View File

@@ -1,797 +0,0 @@
# 角色首遇感、背景故事分层解锁与同伴私聊功能设计
更新时间:`2026-04-04`
## 0. 目标
这份方案针对当前仓库,解决 3 个连在一起的问题:
1. 玩家遇见每一个角色时,在该角色当前好感度对应的关系位置上,都应该有“第一次真正接触”的感觉,而不是一上来就像已经聊过很多轮。
2. 角色背景故事目前没有按好感度分层解锁,导致尚未建立关系时,模型和界面都可能提前拿到过深信息。
3. 队伍中的高好感同伴虽然已经有聊天弹窗底子,但没有被“好感度 + 面板入口 + 上下文边界”真正串成完整功能。
这次设计不另起一套独立系统,而是基于当前仓库已有的:
- `useStoryGeneration` 的角色遭遇与对话流
- `npcInteractions.ts` 的好感度与对话阶段规则
- `prompt.ts` 的上下文注入机制
- `useCharacterChatFlow` / `CharacterChatModal` 的私聊能力
继续往前补齐。
## 1. 当前现状与问题定位
## 1.1 当前“首遇感”不足的根因
当前问题不是只出在开局同伴,而是整套角色遭遇系统缺少“第一次真正接触”的通用状态。
主要有 4 个原因:
1. `src/data/npcInteractions.ts`
- `buildInitialNpcState(...)` 会给角色型 NPC 一个初始好感,但当前系统直接把这个好感映射成普通对话阶段。
- 也就是说,系统把“当前好感是多少”和“是否已经不是第一次接触”混成了一件事。
2. `src/data/npcInteractions.ts`
- `getNpcChatTopics(...)` 目前只看 `guarded / partial / honest / deep`,不看“这是不是第一次真正交流”。
- 结果是,角色只要初始好感不低,就会直接拿到像“追一点表层理由”“开始碰真正目标”这种更像熟人后续轮的聊天切口。
3. `src/services/prompt.ts`
- `describePlayerState(...)` 直接把 `character.backstory` 注入为“主角背景”。
- `describeFrontEntity(...)` 直接把 `encounterCharacter.backstory` 注入为“背景”。
- 这样模型即使被要求“像初见”,也已经在系统层知道太多,容易写成互背设定卡。
4. 当前仓库虽然有 `initial_companion / camp_companion` 的开场流,但它只覆盖开局这一种遭遇。
- 如果只靠开场特判修正,其他角色第一次见面时仍然会缺乏首遇感。
一句话总结:
**当前缺的不是“开场对白模板”,而是“所有角色共用的首遇状态机”。**
## 1.2 背景故事过早暴露的根因
当前角色数据只有一个平铺的 `character.backstory`,而没有“公开层 / 解锁层 / 核心秘密层”的结构。
结果是:
- 界面层很容易直接把完整背景展示出来。
- prompt 层也容易直接把完整背景塞给模型。
- 好感度虽然已经控制了 `guarded / partial / honest / deep`,但它只约束“说话方式”,没有约束“哪些背景事实可以进入上下文”。
这会带来两个后果:
1. 模型写出来的对白容易像彼此已经认识很久。
2. 玩家还没建立关系,就已经在系统层“知道了太多”。
## 1.3 私聊功能现状
当前仓库其实已经有私聊底座:
- `src/hooks/story/characterChat.ts`
- 已经有 `useCharacterChatFlow(...)`
- 已经能生成建议、流式回复、聊天总结,并写回 `gameState.characterChats`
- `src/components/CharacterChatModal.tsx`
- 已经有完整聊天弹窗
- `src/components/GameShell.tsx`
- 已经挂载了 `CharacterChatModal`
- 也把 `onOpenCharacterChat` 传给了 `CharacterPanel`
但缺的 3 个点还没有补上:
1. `CharacterPanel` 当前没有真正渲染聊天按钮。
2. 游戏内实际查看同伴详情时打开的是 `AdventureEntityModal`,它也没有聊天入口。
3. 私聊没有与“队伍成员 + 好感度阈值 + 已解锁背景”绑定。
所以现在属于“能力有了,但功能还没真正成立”。
## 2. 总体设计结论
建议把这次需求拆成 3 条链,同时落地:
1. **首遇感链**
- 所有 `encounter.characterId` 的角色型 NPC第一次真正接触时都先走通用首遇规则。
- 首遇时的亲疏、开放程度和选项排序由“当前好感度 + 是否首遇”共同决定。
- `initial_companion / camp_companion` 只保留场景与演出意义,不再承担特殊对话规则本体。
2. **背景解锁链**
- 角色背景改成按好感度分章节解锁。
- 未解锁章节既不能在 UI 明文显示,也不能进入 prompt 上下文。
3. **私聊链**
- 队伍中的高好感同伴才能解锁私聊。
- 在同伴详情面板中点击按钮打开现有聊天弹窗。
- 私聊只吃“公开信息 + 已解锁背景 + 最近共同经历”,不能越权看到锁住的背景章节。
## 3. 所有角色通用的“首遇感”方案
## 3.1 核心原则
必须把下面两件事拆开:
1. 当前好感度高低
2. 这是不是第一次真正接触
因为:
- `好感低` 不等于 `第一次见面`
- `好感不低` 也不等于 `已经聊过很多轮`
正确做法应该是“双轴控制”:
- 好感决定:
- 语气亲疏
- 允许透露的信息深度
- 哪些功能当前有机会成立
- 首遇状态决定:
- 对话必须先像“第一次对上人”
- 优先说现场判断、态度试探、短钩子
- 不能一上来就跳到“已经熟悉彼此”的后续轮节奏
## 3.2 状态设计
建议给 `NpcPersistentState` 增加一个通用字段:
```ts
interface NpcPersistentState {
affinity: number;
relationState?: RoleRelationState;
helpUsed: boolean;
chattedCount: number;
giftsGiven: number;
inventory: InventoryItem[];
recruited: boolean;
revealedFacts?: string[];
knownAttributeRumors?: string[];
firstMeaningfulContactResolved?: boolean;
seenBackstoryChapterIds?: string[];
}
```
其中:
- `firstMeaningfulContactResolved`
- 适用于所有角色型 NPC
- 表示“玩家是否已经和这个角色完成过第一轮真正接触”
- 不等于 `chattedCount > 0`
- 不等于 `recruited === true`
- 不等于 `affinity` 已经较高
- `seenBackstoryChapterIds`
- 用于背景章节首次解锁时的提示去重
建议补这组 helper
```ts
function isNpcFirstMeaningfulContact(
encounter: Encounter,
npcState: NpcPersistentState,
): boolean
function markNpcFirstMeaningfulContactResolved(
npcState: NpcPersistentState,
): NpcPersistentState
```
判断建议:
- 仅对 `encounter.characterId` 的角色型 NPC 启用
- `firstMeaningfulContactResolved !== true` 时,进入首遇模式
## 3.3 首遇时的好感度矩阵
首遇不是永远冷冰冰,而是要落在“当前好感度对应的位置”上。
建议把首遇风格做成这张矩阵:
| 当前关系站位 | 好感区间参考 | 首遇时的感觉 | 信息上限 | 选项重心 |
| --- | --- | --- | --- | --- |
| `guarded` | `<= 14` | 明显戒备、先观察你值不值得回应 | 只谈眼前局势、模糊钩子 | 观察、试探、判断 |
| `neutral` | `15 - 34` | 可以正常交流,但还不是熟人 | 可给表层理由,不给深层秘密 | 互探来意、确认立场 |
| `cooperative` | `35 - 59` | 带基础善意或认可,但仍是第一次正式接触 | 能谈轮廓,不谈全部底牌 | 确认合作、交换判断 |
| `bonded` | `>= 60` | 明显信任,但仍应像“第一次真正见到本人/第一次正面对接” | 可谈较深动机,但不一次讲完所有旧事 | 确认并肩关系、推进共同目标 |
重点不是绝对数值,而是这句话:
**第一次见面时,角色可以按当前好感更冷或更暖,但不能直接写成“已经是后续轮”。**
## 3.4 首遇选项改法
当前 `getNpcChatTopics(...)``buildNpcEncounterStoryMoment(...)` 更像“已进入常规互动层”后的选项生成。
建议新增一层:
```ts
function getNpcFirstContactTopics(
encounter: Encounter,
npcState: NpcPersistentState,
): StoryOption[]
function buildNpcFirstContactOptionCatalog(
encounter: Encounter,
npcState: NpcPersistentState,
baseOptions: StoryOption[],
): StoryOption[]
```
规则建议:
1. 只要是 `firstMeaningfulContactResolved !== true`,前 2 个选项必须来自首遇话题池。
2. 首遇话题池只做这几类:
- 现场判断
- 你为什么在这里
- 你刚才在观察什么
- 你对我的态度和判断
- 前面那件事到底哪里不对
3. 现有其他合法功能可以继续保留,但排序必须后置:
- `npc_trade`
- `npc_help`
- `npc_gift`
- `npc_quest_accept`
- `npc_recruit`
补一条实现约束:
- 首次进入 `npc_chat` 时,前端聊天状态里不允许直接塞预设对白充当首句。
- 角色第一次真正对玩家开口时说什么,必须由 `npc_chat` 对应的 prompt 约束来生成,并要求首句是自然招呼或开场判断。
- 不能再用“某人看着你,像是在等你把话接下去”这类第三人称占位旁白充当可见对话历史首句,也不能在聊天 state 里本地硬编码一条替代台词。
- 当玩家在场景中第一次真正撞上角色型 NPC 并进入聊天时,应直接触发一轮由 NPC 主动开口的模型回复;这一轮只生成 NPC 自己的首句与后续可选回应,不得代替玩家补写未说过的话。
- 负好感或敌对关系不应跳过主动开口;如果玩家从 NPC 交互面板点击 `npc_chat`,且该角色尚未完成 `firstMeaningfulContactResolved`,仍要走同一条 NPC 主动开场链路。负好感只影响语气、敌对聊天指令与后续可选功能,不影响“由角色先发言”的首遇行为。
- 好感度小于 `0` 的角色在聊天终止时不进入 `story_continue_adventure` 收束态。无论是玩家主动退出聊天,还是模型通过敌对聊天指令主动结束聊天,底部选项都必须收束为一个 `npc_fight` 与多个 `battle_escape_breakout``npc_fight` 的按钮文案保持“战斗”,点击后仍进入 NPC 战斗结算链路;逃跑类选项按当前场景相邻场景展开为“逃往{场景名}”,并额外提供“逃回当前场景起点”。逃跑选项需要在 `runtimePayload` 中携带目标场景信息,点击后复用现有主角向左转身跑出屏幕的逃离演出,再在目标场景从左侧入场并面向右侧。
4. 首遇状态下,不允许前两项直接变成:
- 深背景追问
- 直接招募
- 直接交易
- 直接切磋
- 直接熟人式寒暄
这里的关键不是“把功能全部禁掉”,而是:
**先保证玩家看到的是首遇节奏,再决定这一轮后面还要不要承接更深功能。**
## 3.5 首遇期功能排序规则
为了兼容“不同角色当前好感不同”的需求,建议不要把首遇期做成一刀切的硬封锁,而是做成排序和表达规则:
1. `npc_chat`
- 首遇期必须优先出现
- 至少两个切口都与“眼前”和“第一判断”有关
2. `npc_recruit`
- 如果按当前好感已经合法,可以出现
- 但不能排在前两项
- 文案要像“确认是否并肩同行”,不能像已经熟到直接收编
3. `npc_trade / npc_help / npc_quest_accept`
- 角色职业允许时可以出现
- 但文案必须像“先试着谈这件事”,不是默认彼此早就熟悉流程
4. `npc_fight / npc_spar`
- 如果剧情确实需要,可以保留
- 但 storyText 里仍要先体现“第一次正面对上”的张力
这样做的好处是:
- 既满足“每个角色都按当前好感落点来写”
- 又不会把首遇感做成只能用于开局同伴的特殊模板
## 3.6 首遇状态何时结束
建议当玩家与该角色完成一次真正交互结算后,就把 `firstMeaningfulContactResolved` 设为 `true`
推荐计入“真正交互”的动作:
- `npc_preview_talk` 后进入并完成一次正式对白
- `npc_chat`
- `npc_help`
- `npc_trade`
- `npc_gift`
- `npc_recruit`
- `npc_quest_accept`
- `npc_spar`
- `npc_fight`
不建议计入:
- 仅看了一眼就离开
- 只打开 UI 没有完成结算
这样可以保证:
- 第一次接触是独立阶段
- 第二次开始才进入当前系统已有的常规关系推进节奏
## 3.7 prompt 与 fallback 约束
建议新增一个通用的首遇指令,而不是继续把首遇逻辑绑定在开场专用 helper 上。
### 1. prompt 上下文字段
建议在 `StoryGenerationContext` 增加:
```ts
isFirstMeaningfulContact?: boolean;
firstContactRelationStance?: 'guarded' | 'neutral' | 'cooperative' | 'bonded' | null;
```
### 2. prompt 约束
只要 `isFirstMeaningfulContact === true`
- 不再把 `character.backstory` 作为“主角背景”直接注入
- 不再把 `encounterCharacter.backstory` 作为“背景”直接注入
- 只允许注入:
- `publicSummary`
- `surfaceHook`
- `immediateConcern`
- 已公开的角色描述
- 现场状态
- 当前关系站位
### 3. fallback 约束
如果保留现有 `buildInitialCompanionDialogueText(...)` 一类 helper
- 它们只能作为“某个具体场景下调用通用首遇规则”的薄包装
- 不应继续承担独立的开场规则系统
- 更不能把本地预设对白直接写进 `npc_chat` 的可见对话历史里,`npc_chat` 首个角色台词必须由 prompt 生成
也就是说:
**开场营地只是首遇规则的一个调用场景,不是首遇规则本体。**
## 4. 背景故事分层解锁设计
## 4.1 核心原则
背景故事要拆成“能公开的层”和“需要建立关系后才能知道的层”。
约束是:
1. 未达到对应好感度前,该章节不能进入 prompt 上下文。
2. 未达到对应好感度前,该章节不能在角色详情中完整展示。
3. 即使模型之前已经从别的地方生成过相关话头,未解锁章节也不能被总结层写成稳定关系记忆。
## 4.2 数据结构
建议在 `Character` 上增加背景解锁配置:
```ts
interface CharacterBackstoryChapter {
id: string;
title: string;
affinityRequired: number;
teaser: string;
content: string;
contextSnippet: string;
}
interface CharacterBackstoryRevealConfig {
publicSummary: string;
chapters: CharacterBackstoryChapter[];
privateChatUnlockAffinity?: number;
}
interface Character {
id: string;
name: string;
title: string;
description: string;
backstory: string;
backstoryReveal?: CharacterBackstoryRevealConfig;
}
```
字段职责:
- `backstory`
- 保留为原始完整设定文本
- 不再默认直接进入运行时 prompt
- `publicSummary`
- 永远允许展示和注入
- 只用于“第一印象”和公开层信息
- `teaser`
- 章节未解锁时给 UI 的短提示
- `content`
- 玩家真正看到的章节正文
- `contextSnippet`
- 允许注入模型上下文的精简版
- 明确只包含“系统允许此阶段知道的事实”
## 4.3 推荐阈值
建议不要直接复用 `guarded / partial / honest / deep` 作为背景章节解锁阈值,因为当前对话阶段对“已招募”有额外放宽。
尤其是:
- `getNpcDisclosureStage(...)`
- `getNpcWarmthStage(...)`
目前都会在 `recruited === true` 时直接给更高阶段。
这适合控制说话语气,但不适合控制“背景章节是否已解锁”。
因此建议新增独立阈值:
| 层级 | 建议阈值 | 含义 |
| --- | --- | --- |
| 公开层 | `0` | 只展示公开印象,不算真正解锁 |
| 第一章 | `20` | 来路表层、最近为何会出现在这里 |
| 第二章 | `40` | 旧事伤痕、过去某段关键经历 |
| 第三章 | `65` | 真正目标、必须完成的事 |
| 第四章 | `85` | 最深层秘密、最不愿轻易说出的事实 |
这样做的好处是:
- 首遇阶段仍然有“先认识再深入”的节奏
- 招募后也不会立刻把全部背景放开
- 高好感的成长曲线能真正体现在“知道了多少”
## 4.4 运行时规则
建议增加这组 helper
```ts
function getUnlockedBackstoryChapters(
character: Character,
affinity: number,
): CharacterBackstoryChapter[]
function getNextLockedBackstoryChapter(
character: Character,
affinity: number,
): CharacterBackstoryChapter | null
function buildBackstoryPromptContext(
character: Character,
affinity: number,
): string[]
```
使用规则:
- UI 展示:
- `publicSummary` 永远可见
- 已解锁章节显示全文
- 未解锁章节显示 `title + teaser + 所需好感`
- prompt 注入:
- 只能使用 `publicSummary + unlocked.contextSnippet`
- 禁止直接把 `backstory` 全文塞给模型
- 总结沉淀:
- 关系总结、私聊总结只允许总结已解锁章节
- 未解锁章节即使被模型“猜中”,也不写入稳定状态
## 4.5 解锁提示
建议当好感跨越阈值时,在运行时给一次轻提示:
- 文案示例:
- `你对 宁霜 的过去有了更多了解:旧军密图`
- `你察觉到 萧烬 愿意谈及更深的一层旧事了`
实现上不需要新增复杂通知系统,可以先:
- 在好感变化后检查本次跨越了哪些章节阈值
- 若有新章节且 `seenBackstoryChapterIds` 未记录,则插入一条轻量 UI 提示或一条短故事结果文本
## 5. “未解锁背景不能加入上下文”的具体边界
这个需求要同时卡住 4 个入口:
## 5.1 主线剧情 prompt
`buildStoryContextFromState(...)``prompt.ts` 中:
- 角色背景不能再直接读取 `character.backstory`
- 应改成读取 `buildBackstoryPromptContext(...)`
## 5.2 NPC 聊天 prompt
`buildStrictNpcChatDialoguePrompt(...)` 中:
- 只能拿 `publicSummary + 已解锁章节 + 最近共同经历`
- 未解锁章节不能出现在“当前面前实体”描述里
- 若当前还是首遇模式,还要进一步收紧到“公开层 + 当前场景判断”
## 5.3 私聊 prompt
`useCharacterChatFlow(...)` 中:
- `streamCharacterPanelChatReply(...)`
- `generateCharacterPanelChatSuggestions(...)`
- `generateCharacterPanelChatSummary(...)`
都只能吃已解锁背景。
## 5.4 关系摘要回写
`gameState.characterChats[characterId].summary` 是会反向进入主线上下文的。
所以必须再加一层约束:
- 如果某段私聊总结提到了未解锁章节内容,则不允许写回稳定 summary
- 或者更稳妥地说summary 生成 prompt 本身就只提供已解锁背景
推荐做法是后者,因为它更简单也更可控。
## 6. 高好感同伴私聊设计
## 6.1 解锁条件
建议私聊不是“所有招募同伴自动可用”,而是满足以下条件后才解锁:
1. 角色已在队伍体系中
- `gameState.companions`
-`gameState.roster`
2. 对应 `npcState.recruited === true`
3. 当前好感度达到私聊阈值
- 默认建议 `70`
- 允许角色级配置覆盖:`backstoryReveal.privateChatUnlockAffinity`
之所以不用“已招募”直接等于“可私聊”,原因很简单:
- 当前系统里已招募会把对话阶段直接推高
- 但用户要求的是“高好感同伴解锁私聊”
- 所以私聊需要独立阈值,不应绑定到 `warm / deep`
## 6.2 入口位置
建议把入口放在两个地方,但以“同伴角色信息面板”为主:
### 1. `AdventureEntityModal`
这是游戏内点击同伴详情时实际打开的面板,优先级最高。
`selection.kind === 'companion'` 时增加:
- 已解锁:`聊天`
- 未解锁:置灰按钮 + 提示 `好感达到 70 后解锁,当前 58`
点击逻辑:
1. 关闭 `AdventureEntityModal`
2.`characterChatUi.openChat(target)`
3. 打开现有 `CharacterChatModal`
### 2. `CharacterPanel`
如果以后存在不走 `AdventureEntityModal` 的详情流,也应在成员详情面板里显示同样按钮,保持一致。
## 6.3 聊天目标结构
建议扩展 `CharacterChatTarget`
```ts
type CharacterChatTarget = {
character: Character;
npcId: string | null;
roleLabel: string;
hp: number;
maxHp: number;
mana: number;
maxMana: number;
affinity?: number;
unlockedBackstoryChapterIds?: string[];
}
```
这样聊天弹窗里可以直接显示:
- 当前关系热度
- 已解锁背景章节数
- 下一段背景解锁阈值
## 6.4 私聊的上下文来源
私聊上下文建议固定由这几部分组成:
1. 角色公开信息
- `name / title / description / personality`
2. 当前关系信息
- 当前好感
- 是否出战 / 是否营地待命
- 最近共同经历
3. 已解锁背景章节
- 只传 `contextSnippet`
4. 最近私聊记录
- `characterChats[characterId].history`
- `characterChats[characterId].summary`
明确不传:
- 未解锁背景章节
- 角色完整 `backstory`
## 6.5 私聊对数值的影响
建议本期私聊先不直接改好感数值,理由是:
- 避免玩家通过无限聊天刷关系
- 先把“关系表达层”和“规则数值层”分开
- 当前已有送礼、聊天、切磋、求助、任务等好感入口,足够支撑成长
私聊本期的价值应放在:
- 沉淀角色关系摘要
- 解锁更深层的文本风格和背景信息
- 让队伍中的同伴真正像“会私下说话的人”
如果后续要给私聊轻量数值收益,建议再单独加冷却或每日首次奖励,不要在本期一起做。
## 7. UI 设计建议
## 7.1 同伴详情面板
建议增加一个“关系 / 档案”区块:
- 当前好感:`58`
- 关系阶段:`谨慎` / `熟络` / `深信`
- 首遇状态:
- `初次接触未完成`
-`已完成第一次正式对接`
- 私聊状态:
- `未解锁70`
-`已解锁`
下面接“背景档案”:
- 公开印象
- 章节卡片列表
章节卡片状态:
- 已解锁
- 标题
- 正文
- 未解锁
- 标题
- `需要好感 40`
- `teaser`
- 遮罩态
## 7.2 私聊按钮状态
按钮文案建议:
- 已解锁:`聊天`
- 未解锁:`聊天70 解锁)`
按钮旁边可以补一行细字:
- `仅高好感同伴可进行私聊`
## 7.3 解锁反馈
建议当玩家在详情面板里刚好看到某一章解锁时:
- 章节卡片做一次轻量高亮
- 私聊按钮从置灰切到可点击时也做一次轻量强调
这样能把“关系成长”明确反馈给玩家,而不只是静态数字变化。
## 8. 落地文件建议
## 8.1 类型与数据
- `src/types/characters.ts`
- 增加 `CharacterBackstoryChapter`
- 增加 `CharacterBackstoryRevealConfig`
- `src/types/scene.ts`
-`NpcPersistentState` 增加
- `firstMeaningfulContactResolved`
- `seenBackstoryChapterIds`
- `src/data/characterPresets.ts`
- 为可招募角色补 `backstoryReveal`
- 配置分章节阈值和 `privateChatUnlockAffinity`
## 8.2 关系与规则
- `src/data/npcInteractions.ts`
- 增加首遇状态 helper
- 增加首遇话题 helper
- 增加背景章节解锁 helper
- 增加私聊解锁 helper
- 调整首遇期的功能排序规则
## 8.3 剧情与 prompt
- `src/hooks/useStoryGeneration.ts`
- 新增通用首遇状态流转
- 给首遇角色注入统一的 first-contact context
- 处理好感跨章节时的解锁通知
- `src/services/aiTypes.ts`
-`StoryGenerationContext` 增加
- `isFirstMeaningfulContact`
- `firstContactRelationStance`
- 已解锁背景片段字段
- `src/services/prompt.ts`
- 移除对完整 `backstory` 的直接注入
- 改为按已解锁章节构造角色背景上下文
- 首遇模式下额外收紧信息披露
## 8.4 UI 与交互
- `src/components/AdventureEntityModal.tsx`
- 显示同伴关系/档案区
- 增加聊天按钮
- `src/components/CharacterPanel.tsx`
- 补齐聊天按钮接线
- 若保留本地详情弹窗,也同步显示档案区
- `src/components/GameShell.tsx`
-`characterChatUi.openChat` 透传给 `AdventureEntityModal`
- `src/hooks/story/characterChat.ts`
- 私聊 prompt 只使用已解锁章节
## 9. 推荐开发顺序
建议按这 4 步做,不要反过来:
1. 先补数据建模
- `backstoryReveal`
- `firstMeaningfulContactResolved`
- 解锁 helper
2. 再补规则
- 首遇状态判断
- 首遇选项排序
- 首遇完成后的状态流转
- 私聊解锁条件
3. 再补 prompt 上下文边界
- 禁止未解锁背景进入模型上下文
- 禁止首遇期被写成后续轮
4. 最后补 UI
- 详情面板档案区
- 聊天按钮
- 解锁提示
## 10. 验收标准
做到以下几点,才算这次需求真正完成:
1. 玩家第一次遇见任一角色型 NPC 时,对话会像第一次真正接触,而不是像已经聊过很多轮。
2. 同一个角色第一次见面时的冷暖程度,会落在该角色当前好感对应的位置上,而不是被强行写成统一冷场模板。
3. 首遇阶段的前两项选项会优先围绕“眼前局势、互相判断、来意试探”,而不是直接跳进深聊、招募或熟人功能。
4. 未达到阈值的背景章节在 UI 中不可完整查看,在 prompt 中也不可被注入。
5. 好感提高后,角色详情面板中的背景章节会逐步解锁。
6. 队伍中的高好感同伴会在详情面板中出现“聊天”按钮。
7. 点击“聊天”后,能直接弹出现有私聊弹窗。
8. 私聊内容、建议、总结都不会越权使用未解锁背景。
9. 旧存档读取后不会崩,缺省字段能平稳兜底。
## 11. 一句话收束
这次需求的关键不是“给开局补一段特判对白”,而是把:
- 首遇节奏
- 背景披露节奏
- 私聊入口
- 关系记忆
统一到同一套“好感度 + 首遇状态 + 信息解锁”的通用规则里。
只有这样,玩家遇见每一个角色时才都会有对应关系位置上的初识感,角色背景才会像一步步了解出来的,同伴私聊也才会真的有价值。

View File

@@ -1,25 +0,0 @@
# 创作页作品删除入口设计 2026-04-24
## 背景
创作页作品卡曾把删除作为底部大按钮展示,并且只对带 `profileId` 的 RPG 作品传入删除回调,导致大鱼、拼图、以及部分草稿作品没有删除入口。用户预期是:删除不是主操作,放在卡片右上角的小 icon 即可;任何作品都应该能删除。
## 落地规则
- 作品卡整体就是继续创作 / 继续完善 / 查看详情入口,不再在底部展示“继续完善”等重复主按钮。
- 作品卡右上角固定展示删除 icon底部主操作区只保留体验等必须独立触发的正向操作。
- 点击作品卡任意非独立按钮区域都进入继续完善链路;点击删除或体验时不得冒泡触发作品卡打开。
- 作品卡保留键盘可访问性:焦点落在卡片时按 Enter 或空格等同点击作品,焦点落在删除 / 体验按钮时只执行对应按钮动作。
- 删除入口不按发布状态隐藏:草稿、已发布作品均可删除。
- 删除入口不按玩法类型隐藏RPG、大鱼吃小鱼、拼图作品均应在创作页可删除。
- 点击删除前保留浏览器确认弹窗,避免误触;删除中仅禁用当前作品卡的删除 icon。
- 删除成功后刷新或替换对应玩法的作品列表,确保卡片立即消失。
## 工程边界
- 前端只负责表现和触发删除,实际删除由 `server-rs` API 与 SpacetimeDB 模块过程完成。
- 大鱼作品按 `sourceSessionId` 删除创作 session并同步清理消息、素材槽和运行快照。
- 拼图作品按 `profileId` 删除作品 profile并同步清理来源 Agent session、消息和入口运行快照。
- RPG 已发布/持久草稿按 `profileId` 走既有自定义世界删除链路;纯 Agent session 草稿按 `sessionId` 走 owner-only session 删除过程,并清理消息、操作与草稿卡。
- 自定义世界 Agent 的异步进度写回必须通过 `upsert_custom_world_agent_operation_progress` 过程落到 SpacetimeDB`server-rs` 只做字符串入参与过程封装,不在 API 层维护额外进度状态。
- `server-rs` 的删除路由使用 Axum 标准 `Path(sessionId)` 提取参数,并在进入 SpacetimeDB 前做 owner-only 与空值校验,避免 handler 签名和过程入参漂移。

View File

@@ -1,491 +0,0 @@
# 自定义世界陶泥儿主输入与 AI 分工边界设计
更新时间:`2026-04-06`
## 0. 目标
这份文档回答一个非常关键的问题:
**在“低创作门槛、高创作自由度”的前提下,自定义世界里哪些内容应该交给陶泥儿主直接定义,哪些内容应该交给 AI 和系统完成。**
这里默认我们的陶泥儿主:
- 不需要有专业作家背景
- 不需要有专业游戏设计背景
- 但希望作品有明显个人风格,而不是只是在用一个会自动补全设定的模板工具
一句话目标:
**让陶泥儿主把精力放在“决定这个世界为什么值得被创作”,把 AI 用在“把这个世界展开、编译、铺开、校验、补足”。**
## 1. 总体结论
自定义世界的分工边界应该遵守 3 条硬原则:
1. 灵魂归陶泥儿主,杂活归 AI。
- 凡是决定作品气质、主题、冲突、人物关系、审美方向的内容,都应由陶泥儿主掌握。
2. 重点对象归陶泥儿主,长尾铺量归 AI。
- 陶泥儿主应重点塑造少量关键角色、关键地点、关键冲突、关键意象,而不是被迫手填几十个 NPC、几十个场景、几百条描述。
3. 决策归陶泥儿主,编译归 AI / 系统。
- 陶泥儿主负责说“这个世界要成为什么样”AI / 系统负责把它编译成可运行的数据、规则、文本、关系钩子和运行时结构。
这意味着:
- 陶泥儿主应该主要编辑“高杠杆创作锚点”
- AI 应该主要承担“批量展开 + 结构编译 + 一致性维护 + 专业执行”
## 2. 什么内容应该交给陶泥儿主
真正应该交给陶泥儿主的,不是大量表格字段,而是下面这些会显著决定作品质量、且 AI 不擅长替代的内容。
## 2.1 世界核心命题
陶泥儿主应该直接定义:
- 这个世界的一句话设定
- 这个世界最吸引人的核心幻想
- 玩家来到这个世界,最想体验的感觉是什么
- 这个世界和常规同题材作品相比,最不同的地方是什么
原因:
- 这是作品的创作方向盘
- 一旦这一层是空的,后面所有 AI 扩写都会变成“像一个世界”,而不是“这个世界”
## 2.2 主题、气质与边界
陶泥儿主应该直接定义:
- 主题关键词
- 情绪基调
- 审美偏好
- 禁忌内容 / 不希望出现的表达
- 可以接受的黑暗度、浪漫度、残酷度、神秘度
原因:
- 这决定了 AI 后续生成时的“味道”
- 这类判断很难靠 AI 替代,因为它本质上不是信息补全,而是审美取舍
## 2.3 玩家身份与开局处境
陶泥儿主应该直接定义:
- 玩家扮演的是什么人
- 玩家一开始最缺什么、最想要什么
- 开局时玩家被卷入什么局面
- 玩家在这个世界里天然站在哪个位置上
原因:
- 这决定了整个世界的观看视角
- 同一个世界,玩家视角不同,最终体验会完全不同
## 2.4 核心冲突与关键势力
陶泥儿主应该直接定义少量高价值内容:
- 世界当前最重要的 `2~4` 条明面冲突
- 世界背后最关键的 `1~3` 条暗面问题
- `2~6` 个关键势力
- 这些势力各自想要什么、害怕什么、互相卡住了什么
原因:
- 冲突结构决定世界是否“有戏”
- 势力关系是 AI 最容易写散、写平、写成百科介绍的部分
- 这一层由陶泥儿主把握,才能真正提高作品的辨识度
## 2.5 关键角色与关系张力
陶泥儿主应该直接定义少量关键角色,而不是所有 NPC。
建议重点交给陶泥儿主的,是:
- `3~8` 个关键角色
- 玩家与这些人的潜在关系
- 这些角色彼此之间的债、仇、秘密、误解、利益绑定
- 每个关键角色“表面上像什么、实际上压着什么”
原因:
- 角色关系是最能显著提升作品质量的部分之一
- 这也是 AI 最容易写得“完整但无味”的部分
- 陶泥儿主不需要写长篇背景,但应掌握这些角色真正的关系骨架
## 2.6 关键地点与空间记忆点
陶泥儿主应该直接定义:
- `4~12` 个关键地点 / 区域 / 地标
- 这些地方为什么重要
- 这些地方承载什么冲突、危险、秘密或情绪记忆
- 玩家第一次来到这里时应该感到什么
原因:
- “地方感”是世界质量的重要来源
- 关键地点一旦成立AI 后续才能稳定地生成周边事件、物件、NPC 和线索
## 2.7 标志性意象、物件、怪物、制度与规则
陶泥儿主应该优先控制世界里最能代表它的东西:
- 标志性物件
- 标志性怪物 / 生物
- 标志性能力体系 / 修炼体系 / 技术体系
- 标志性社会制度 / 宗教 / 仪式 / 禁忌
- 世界的硬规则
原因:
- 这些内容决定世界的“手感”
- 它们不是普通细节,而是会反复影响命名、剧情、视觉、对话与玩法解释的母题
## 2.8 陶泥儿主应直接控制的“禁止事项”
陶泥儿主必须能明确锁定:
- 什么绝对不能改
- 什么不能被 AI 自动扩写到别的方向
- 哪些角色、地点、关系、设定是核心锚点
- 哪些内容允许 AI 自由发挥,哪些只能在锚点附近变体
原因:
- 高自由度不等于所有内容都开放漂移
- 如果没有“锁定机制”AI 会把陶泥儿主真正关心的内容稀释掉
## 3. 什么内容应该交给 AI 和系统
应该交给 AI 的,不是“重要内容”,而是“重要内容之外的大量展开、编译、补缝、校验与专业执行”。
## 3.1 批量生成的长尾内容
应该主要交给 AI
- 普通 NPC
- 路人、商贩、巡逻者、村民、杂兵
- 次级场景
- 场景支线事件
- 大量普通物品
- 世界的长尾命名与描述
原因:
- 这些内容数量大、重复度高
- 它们需要“贴合世界”,但不需要都由陶泥儿主逐个手写
- AI 很适合做“围绕锚点的批量铺量”
## 3.2 从创作锚点到系统结构的编译
应该交给 AI / 系统:
- 从自然语言世界设定中提取题材词汇
- 从关键冲突中编译出世界叙事图谱
- 从关键角色卡编译出角色叙事档案
- 从陶泥儿主输入里自动生成标签、钩子、隐藏线索、章节摘要
- 从地点和关系中编译出场景连接、事件触发和叙事回响
对应当前仓库,下面这些结构更适合由 AI / 系统生成,而不是让玩家直接编辑:
- `ThemePack`
- `WorldStoryGraph`
- `ActorNarrativeProfile`
- `KnowledgeFact`
- `VisibilitySlice`
- `SceneNarrativeDirective`
- `CarrierStoryFingerprint`
- `ThreadContract`
- `StorySignal`
原因:
- 这些是运行时结构,不是陶泥儿主真正想表达的作品内容
- 直接暴露给玩家,会把创作过程变成专业数据填表
## 3.3 专业化、规则化的任务
应该交给 AI / 系统:
- 数值平衡
- 标签归纳
- 稀有度预算
- 初始技能与初始物品的批量配置
- build 方向匹配
- 地图连接补全
- 触发条件与推进信号编译
- 背景章节拆分与 teaser 生成
- 运行时物件命名与叙事描述的变体生成
原因:
- 这些工作要么重复、要么专业、要么容易做脏活累活
- 让非专业陶泥儿主处理,会显著提高门槛,却不一定显著提高质量
## 3.4 一致性、纠错与查漏补缺
应该交给 AI / 系统持续处理:
- 世界设定冲突检查
- 角色关系矛盾检查
- 同名 / 重复 / 设定撞车检查
- 信息越权泄露检查
- prompt 裁剪
- 风格一致性检查
- “这个角色/地点/物件是否真的和世界主线有关”的弱关联检查
原因:
- 这是 AI 比人更适合做的“维护型工作”
- 它属于创作支持,不属于陶泥儿主必须亲手完成的创作
## 4. 最合理的边界不是二分法,而是三层分工
自定义世界最合理的结构不是“玩家写”与“AI 写”的简单二选一,而是三层。
## 4.1 第一层:陶泥儿主必控层
这一层必须给陶泥儿主高自由度,且能被锁定:
- 世界核心命题
- 主题与气质
- 玩家身份与开局
- 核心冲突
- 关键势力
- 关键角色
- 关键地点
- 标志性物件 / 怪物 / 规则
- 禁止事项
这层的原则是:
**少而重。**
## 4.2 第二层:陶泥儿主可选强化层
这一层不应强制填写,但应该允许陶泥儿主继续深挖:
- 明线 / 暗线种子
- 角色之间的旧事
- 地点背后的旧伤
- 标志性物件的来历
- 关键角色的口头习惯、禁忌、执念
- 关键地点的视觉母题与情绪目标
这层的原则是:
**愿意细写的人可以拉高作品上限,不愿细写的人也不会被门槛卡住。**
## 4.3 第三层AI 自动展开层
这一层默认交给 AI / 系统:
- 长尾 NPC
- 次级地点
- 章节拆分
- 初始技能
- 初始物品
- 标签与属性映射
- 任务 contract
- 物件叙事指纹
- 可见性裁剪
- 运行时导演指令
- 批量命名与文案变体
这层的原则是:
**AI 可以做多、做快、做杂,但不能越过第一层锁定内容。**
## 5. 具体模块的建议归属
| 模块 | 建议归属 | 陶泥儿主应控制什么 | AI / 系统应负责什么 |
| --- | --- | --- | --- |
| 世界一句话设定、核心幻想、核心卖点 | 陶泥儿主直接控制 | 直接写、直接改、可锁定 | 给出备选表述和扩展方向 |
| 主题、基调、审美、禁忌 | 陶泥儿主直接控制 | 选择 / 改写 / 锁定 | 生成风格词、避雷词、提示词约束 |
| 玩家身份、开局处境、玩家目标 | 陶泥儿主直接控制 | 直接定义 | 补足开局钩子和初始叙事包装 |
| 关键势力与核心冲突 | 陶泥儿主控AI 辅助 | 定义核心关系和立场 | 扩展冲突支路、生成世界线程 |
| 关键角色 | 陶泥儿主控AI 辅助 | 定义角色骨架、关系张力、秘密方向 | 生成长背景、章节拆分、技能、物品、叙事档案 |
| 关键地点 | 陶泥儿主控AI 辅助 | 定义地点意义、气氛、秘密 | 扩展场景细节、连接关系、遭遇分布 |
| 标志性物件 / 怪物 / 制度 / 规则 | 陶泥儿主控AI 辅助 | 定义代表性要素与硬边界 | 扩展变体、命名、说明、运行时挂钩 |
| 普通 NPC / 路人 / 杂兵 / 次级地点 | 主要交给 AI | 仅在需要时抽查或替换 | 批量生成与风格保持 |
| 角色长背景、章节 teaser、context snippet | 主要交给 AI | 陶泥儿主只改关键角色即可 | 自动拆章、压缩、解锁节奏整理 |
| 技能、初始物品、标签、构筑倾向 | 主要交给 AI / 系统 | 提供偏好或少量 override | 按角色和世界规则自动编译 |
| 世界图谱、知识事实、可见性、导演指令 | AI / 系统内部层 | 不应默认暴露给玩家 | 运行时编译与维护 |
| 一致性检查、冲突检查、越权检查 | AI / 系统内部层 | 查看报告、决定是否采纳修改 | 自动扫描并提出修正建议 |
## 6. 不应该要求玩家直接填写的字段
为了真正做到低门槛,下面这些内容不应直接以“专业字段”形式强迫玩家填写。
## 6.1 不应该要求玩家手填原始数值
例如:
- `initialAffinity`
- 精确数值型 build 倾向
- 复杂掉落预算
更合理的做法是让陶泥儿主填写直觉表达,例如:
- `初见就戒备`
- `容易合作`
- `这里非常危险`
- `偏爆发型`
再由系统编译成运行时数值。
## 6.2 不应该要求玩家手填技术型结构
例如:
- `tags`
- `attributeSchema`
- `ThemePack`
- `WorldStoryGraph`
- `VisibilitySlice`
- `SceneNarrativeDirective`
- `ThreadContract`
原因:
- 这些字段属于系统运行结构,不属于陶泥儿主自然的创作语言
- 直接让玩家填,会把工具变成只有懂系统的人才能用
## 6.3 不应该要求玩家逐个补完所有人物设定字段
当前 `CustomWorldRoleProfile` 里这些字段:
- `backstory`
- `personality`
- `motivation`
- `combatStyle`
- `backstoryReveal`
- `skills`
- `initialItems`
更适合的做法不是全部让玩家手写,而是先让玩家填写更自然的“角色卡”:
- 这个人表面上是什么样
- 这个人真正想要什么
- 这个人最不想被提到什么
- 这个人和玩家之间最可能形成什么关系
- 这个人和哪个地点 / 物件 / 旧事绑得最紧
再由 AI / 系统编译成当前结构。
## 7. 推荐的创作输入形态
要让非专业陶泥儿主也能高自由度创作,输入形态必须改成“自然语言创作卡”,而不是“系统字段表单”。
## 7.1 世界层卡片
建议至少有这些卡片:
1. 世界一句话
2. 主题与气质
3. 玩家是谁
4. 核心冲突
5. 关键势力
6. 关键角色
7. 关键地点
8. 标志性要素
9. 禁止事项
## 7.2 每张卡片都允许 3 种输入方式
1. 一句话自由输入
- 适合低门槛陶泥儿主
2. 标签 / 选项 / 语气滑条
- 适合不想写太多字的陶泥儿主
3. 高级补充
- 适合愿意继续深挖的人
这样才能做到:
- 不会逼着用户写长文
- 但也不会限制愿意创作的人继续把世界做深
## 7.3 必须支持“锁定”与“局部重生成”
这是高创作自由度里非常关键的一点。
陶泥儿主应当能:
- 锁定一个角色
- 锁定一个地点
- 锁定一条冲突
- 只重生成未锁定部分
- 围绕锁定内容重写其余世界
否则陶泥儿主每次调用 AI都会有“好不容易想好的东西被洗掉”的感受。
## 8. 面向当前仓库的结构映射建议
为了便于后续落实现有系统,这份边界建议可以直接映射到当前结构:
## 8.1 陶泥儿主输入层
建议主要映射到:
- `CustomWorldProfile.settingText`
- `CustomWorldProfile.name`
- `CustomWorldProfile.subtitle`
- `CustomWorldProfile.summary`
- `CustomWorldProfile.tone`
- `CustomWorldProfile.playerGoal`
- `CustomWorldProfile.majorFactions`
- `CustomWorldProfile.coreConflicts`
以及关键角色、关键地点的创作卡输入。
## 8.2 AI 编译层
由 AI / 系统从陶泥儿主输入自动补出:
- `themePack`
- `storyGraph`
- `knowledgeFacts`
- `threadContracts`
- 每个关键角色的 `narrativeProfile`
- 每个角色的 `backstoryReveal`
- 每个角色的 `skills`
- 每个角色的 `initialItems`
## 8.3 运行时支持层
运行时继续由 AI / 系统维护:
- `VisibilitySlice`
- `SceneNarrativeDirective`
- `CarrierStoryFingerprint`
- `StorySignal`
这些内容应该是“系统如何把世界跑起来”,不是“陶泥儿主必须亲手写完的创作内容”。
## 9. 产品层面的最终结论
如果我们的目标真的是“低创作门槛、高创作自由度”,那么自定义世界不应该做成一个要求用户:
- 填很多字段
- 写很多长文
- 理解很多系统结构
- 自己负责平衡、命名、拆章节、补标签、补长尾内容
的专业编辑器。
它应该做成这样:
1. 陶泥儿主决定世界的灵魂锚点。
2. 陶泥儿主重点塑造少量关键人、关键地、关键冲突、关键物。
3. AI 围绕这些锚点批量展开长尾内容。
4. 系统把这些内容编译成可运行的图谱、可见性、任务、物件和关系结构。
5. 陶泥儿主随时可以锁定核心创意,并局部重生成其余部分。
一句话收束:
**陶泥儿主应该写“这个世界为什么动人”AI 应该负责“让这个世界长出来并跑起来”。**

View File

@@ -1,721 +0,0 @@
# 自定义世界创作中手填、AI 可改与系统托管的平衡设计
更新时间:`2026-04-12`
## 0. 文档目标
这份文档用于回答一个更具体的问题:
**参考 RPG 专业剧情策划全流程后,在自定义世界创作工具里,哪些设定必须要求陶泥儿主手动填写,哪些设定应该由 AI 先生成但允许陶泥儿主修改,哪些设定应完全交给系统托管,才能在“尽可能降低门槛”和“尽可能提高作品质量”之间取一个平衡。**
这份文档不再只回答“陶泥儿主与 AI 怎么分工”,而是进一步把创作工作台收束成一个更可执行的三层输入结构:
1. 陶泥儿主必须手填的高杠杆锚点
2. AI 先生成、陶泥儿主可修改的内容草稿层
3. 系统自动编译和运行的托管层
一句话结论:
**让陶泥儿主只负责决定作品的灵魂、视角、冲突和关系钩子,让 AI 负责把这些锚点展开成可编辑的剧情草稿,让系统负责把草稿编译成可运行的结构。**
---
## 1. 设计目标
这套平衡设计要同时满足 5 个目标:
1. 低门槛
- 新陶泥儿主不需要写长篇设定,也不需要理解底层系统结构。
2. 高辨识度
- 陶泥儿主写出来的世界,不应该只是“像一个世界”,而应该保留明显的个人方向。
3. 高可编辑性
- AI 不能一次生成后就不可控,陶泥儿主必须能改关键对象、关键关系和关键章节。
4. 高稳定性
- 任务、章节、关系、物件和可见性等运行层结构不能依赖陶泥儿主手填专业字段。
5. 可扩展
- 愿意深挖的陶泥儿主可以继续补充世界上限,不愿深挖的人也能快速产出质量不错的作品。
---
## 2. 核心原则
## 2.1 陶泥儿主手填的必须是“高杠杆决策”,不是“高工作量字段”
应该要求陶泥儿主手填的内容,必须同时满足下面两个条件:
1. 会显著决定作品气质和辨识度
2. AI 很难替代判断
例如:
- 世界一句话
- 玩家身份
- 核心冲突
- 关系钩子
- 禁忌边界
而不应该强制手填:
- 全量 NPC
- 全量场景
- 技能列表
- 初始物品
- 章节拆分
- 运行时信号结构
## 2.2 陶泥儿主可改层应该承接“专业策划初稿”,而不是“原始底层字段”
AI 生成后允许陶泥儿主修改的,不应该是一堆技术型字段,而应该是一批已经成形的内容卡片,例如:
- 关键角色卡
- 势力卡
- 关键地点卡
- 主线章节卡
- 支线种子卡
- 场景章节卡
- 标志性物件卡
也就是说:
**AI 先给陶泥儿主一个像策划初稿的东西,而不是给一堆系统字段让陶泥儿主自己拼。**
## 2.3 系统托管层必须彻底隐藏专业运行结构
以下这类结构不应该默认要求陶泥儿主理解或编辑:
- `ThemePack`
- `WorldStoryGraph`
- `KnowledgeFact`
- `VisibilitySlice`
- `SceneNarrativeDirective`
- `StorySignal`
- `ThreadContract`
- 数值预算
- 稀有度映射
- 掉落和 build 权重
陶泥儿主应该编辑的是自然语言与内容卡,而不是运行时图结构。
## 2.4 先少量必填,再逐层展开
最合理的工作流不是“开局填一大页表”,而是:
```text
先填最小必填卡
-> AI 生成世界初稿
-> 陶泥儿主修改关键对象
-> 系统继续展开长尾
-> 陶泥儿主决定是否进入高级补充
```
## 2.5 默认清爽,深度能力后置
结合当前项目约束,创作工作台默认不要把规则说明、底层字段、专业术语堆到 UI 面板里。
应该做到:
1. 默认只展示最有创作价值的卡片
2. 高级内容折叠到后置面板
3. 大多数系统结构不直接暴露
4. 移动端也能完成最小创作闭环
---
## 3. 最终建议:三层分工
## 3.1 第一层:必须要求陶泥儿主手动填写
这一层只保留最影响作品质量的高杠杆锚点,建议默认强制填写 6 张卡。
## 3.2 第二层AI 生成后支持陶泥儿主修改
这一层由 AI 根据第一层锚点自动展开成专业剧情策划初稿,陶泥儿主可以逐项修改、锁定、局部重生成。
## 3.3 第三层:其余都交给系统
这一层是把前两层编译成可运行游戏结构所需的系统字段、数值和运行时指令,默认不要求陶泥儿主处理。
---
## 4. 最低门槛方案:只强制手填 6 张卡
如果目标是尽可能降低门槛,同时又保留作品辨识度,建议只强制陶泥儿主填写以下 6 张卡。
## 4.1 卡 1世界一句话与核心幻想
陶泥儿主必须手填:
- 世界一句话设定
- 玩家来到这个世界最想体验的感觉
- 这个世界和同类题材相比最不同的一点
原因:
- 这是作品的方向盘
- 这是后续 AI 所有扩写的总锚点
推荐输入形态:
- 一句话文本
- `1~3` 个体验关键词
## 4.2 卡 2玩家身份与开局困境
陶泥儿主必须手填:
- 玩家是谁
- 玩家开局最缺什么
- 玩家为什么必须进入这场故事
- 玩家天然站在什么位置上
原因:
- 玩家视角不清,后面所有剧情都会发散
- 这是主线入口、关系入口和任务入口的共同基础
## 4.3 卡 3主题气质与禁忌边界
陶泥儿主必须手填:
- 主题关键词
- 情绪基调
- 审美方向
- 禁止出现或尽量避免的内容
原因:
- 这决定世界“是什么味道”
- 这也是避免 AI 跑偏最有效的一层
推荐输入形态:
- 标签选择
- 语气滑条
- 一小段补充说明
## 4.4 卡 4核心冲突
陶泥儿主必须手填:
- 当前世界最重要的 `1~3` 个明面冲突
- 至少 `1` 个隐藏问题或暗面危机
- 玩家最先接触的是哪条冲突
原因:
- 没有冲突,世界就只剩设定
- 没有暗面问题,后续剧情就难以产生层次和改判
## 4.5 卡 5关键关系钩子
这里不强制陶泥儿主一开始填写完整角色档案,只要求填写更高杠杆的“关系骨架”。
陶泥儿主必须手填:
- `2~4` 条关键关系钩子
- 每条钩子至少说明:
- 谁和谁有关
- 关系是债、仇、误解、旧情、利用还是血缘
- 这条关系里压着什么秘密或代价
原因:
- 作品的人味和记忆点主要来自关系张力
- 关系钩子比完整角色长文更容易写,也更高杠杆
## 4.6 卡 6标志性要素与硬规则
陶泥儿主必须手填:
- `2~5` 个标志性要素
- 物件
- 怪物
- 制度
- 仪式
- 能力体系
- 社会规则
- 至少 `1~3` 条不能被 AI 擅自改写的硬规则
原因:
- 这决定世界是否有独特手感
- 后续命名、剧情、物件和场景都会反复依赖这些母题
---
## 5. 不建议强制手填,但应该让 AI 生成后支持陶泥儿主修改的设定
这一层是平衡“低门槛”和“高质量”的关键。
陶泥儿主不需要从零填写这些内容,但 AI 生成后必须能看、能改、能锁定、能局部重生成。
## 5.1 世界外观层
建议 AI 先生成后可改:
- 世界名称
- 副标题
- 世界简介
- 宣传短句
- 主题母题摘要
- 命名风格建议
原因:
- 这些内容影响观感,但不值得强制占用开局填写成本
## 5.2 势力层
建议 AI 先生成后可改:
- `2~6` 个关键势力
- 每个势力的公开目标
- 每个势力的隐藏目标
- 势力间的主要矛盾
- 代表人物
- 势力资源与禁忌
原因:
- 势力很重要,但让新手一开始手写完整势力表太重
- 更合理的做法是让 AI 基于核心冲突先出草稿,再由陶泥儿主修正
## 5.3 关键角色层
建议 AI 先生成后可改:
- 关键角色姓名
- 外显身份
- 公众面具
- 当前压力
- 表面目标
- 真实目标
- 背景旧事
- 禁区
- 与玩家关系方向
- 角色个人线阶段
- 背景章节 teaser
原因:
- 陶泥儿主已经通过“关系钩子”给出最关键的人物骨架
- AI 负责把钩子展开成可编辑角色卡,陶泥儿主再做精修
## 5.4 关键地点层
建议 AI 先生成后可改:
- `4~10` 个关键地点
- 每个地点的功能定位
- 气氛和视觉母题
- 涉及的线程和秘密
- 首次进入时的情绪目标
- 关联角色和标志性载体
原因:
- 地点是世界感的重要来源
- 但新陶泥儿主未必能一开始就写出完整地点网络
## 5.5 世界线程层
建议 AI 先生成后可改:
- 明线线程
- 暗线线程
- 旧事伤痕
- 误导信息
- 主要 handoff
- 阶段揭示节奏
原因:
- 线程是专业剧情结构,适合 AI 先搭骨架
- 但陶泥儿主必须有权修正哪条线更重要、哪条线该隐藏
## 5.6 主线章节层
建议 AI 先生成后可改:
- 幕结构建议
- 章节标题
- 章节承诺
- 转折设计
- 高潮行动
- 章节 handoff
原因:
- 陶泥儿主已经给出了世界目标、冲突和关系
- AI 可以先把它们编成主线章节初稿
- 陶泥儿主再选择保留、删减或重排
## 5.7 支线、角色线、阵营线层
建议 AI 先生成后可改:
- 支线种子
- 角色线阶段事件
- 阵营线分歧点
- 私聊或同伴互动节点
- 支线和主线的互文关系
原因:
- 这是最适合 AI 拉开内容宽度的部分
- 也是最需要陶泥儿主局部精修的部分
## 5.8 场景章节层
建议 AI 先生成后可改:
- 场景章节标题
- `opening / expansion / turning_point / climax / aftermath`
- 情感锚点 NPC
- 现场压力
- 转折信息
- 局部收束
- 下一跳 handoff
原因:
- 当前项目已经在走“场景 = 章节单元”的方向
- 这层非常适合 AI 编排出第一版,再由陶泥儿主补强记忆点
## 5.9 叙事载体层
建议 AI 先生成后可改:
- 标志性物件
- 文书
- 残痕
- 证物
- 场景遗物
- 怪物命名及其故事指向
陶泥儿主主要修改:
- 哪些载体最重要
- 哪些载体和哪条线程绑定
- 哪些载体需要更强的个人风格
## 5.10 文案包装层
建议 AI 先生成后可改:
- 角色简介
- 地点短描述
- 势力介绍
- 章节标题候选
- 任务标题与简述
- 物件命名候选
原因:
- 这些内容适合 AI 批量铺量
- 陶泥儿主只需要挑、改、锁定,不必从零起草
---
## 6. 其余设定应交给系统托管
以下内容不建议默认暴露给陶泥儿主编辑,应由系统根据前两层自动编译和维护。
## 6.1 题材与术语编译层
交给系统:
- `ThemePack`
- 题材词汇表
- 命名模式映射
- 母题标签映射
原因:
- 这是系统为了统一生成风格而维护的内部层
## 6.2 世界图谱运行层
交给系统:
- `WorldStoryGraph`
- `KnowledgeFact`
- 事实 id
- 线程内部关联
- 旧事与角色的细粒度映射
原因:
- 陶泥儿主要的是“故事线能对”,不是维护图数据库
## 6.3 可见性和 prompt 裁剪层
交给系统:
- `VisibilitySlice`
- 禁止注入信息列表
- 当前可说信息
- 推测信息
- 越权泄露检查
原因:
- 这层必须稳定、严格、自动化
- 不适合依赖陶泥儿主手动维护
## 6.4 运行时导演层
交给系统:
- `SceneNarrativeDirective`
- 节奏推进指令
- 披露预算
- 当前主压力判断
- 当前前景角色和前景载体
原因:
- 这是剧情运行时调度逻辑,不是创作表达层
## 6.5 任务编译层
交给系统:
- `ThreadContract`
- `StorySignal`
- step id
- step 类型映射
- 触发条件编译
- 结算条件编译
说明:
- 陶泥儿主可以编辑“任务卡”和“章节卡”
- 但不应默认编辑底层 contract 结构
## 6.6 数值与配置层
交给系统:
- 技能数值
- 初始物品预算
- 稀有度分布
- 掉落权重
- build 标签映射
- 关系数值初始值
- 敌对强度预算
说明:
- 陶泥儿主可以给“偏向”
- 系统负责编译成具体数值
## 6.7 QA 与一致性层
交给系统:
- 设定冲突检查
- 同名检查
- 风格漂移检查
- 关系矛盾检查
- 主线与支线弱关联检查
- 未解锁信息泄露检查
- 长尾内容覆盖率检查
原因:
- 这属于高频维护型工作,最适合系统自动做
---
## 7. 按模块划分的最终边界表
| 模块 | 必须手填 | AI 生成后可改 | 系统托管 |
| --- | --- | --- | --- |
| 世界定位 | 世界一句话、核心幻想、差异点 | 世界名称、副标题、简介 | 题材词汇编译 |
| 玩家视角 | 玩家身份、开局困境、初始动机 | 开局剧情摘要、开局目标文案 | 开局状态初始化 |
| 主题边界 | 主题、气质、禁忌、硬边界 | 主题母题摘要、风格建议 | 风格约束编译 |
| 核心冲突 | 明面冲突、隐藏危机 | 线程草稿、旧事伤痕、误导设计 | 世界图谱、事实映射 |
| 关系骨架 | 关键关系钩子 | 关键角色卡、个人线阶段、背景章节 teaser | 关系数值、解锁条件 |
| 标志性要素 | 标志物、怪物、制度、规则 | 标志载体卡、命名候选、衍生变体 | 物件指纹、掉落映射 |
| 势力 | 不强制首轮手填 | 势力卡、代表人物、势力冲突 | 阵营状态映射 |
| 地点 | 不强制首轮手填 | 关键地点卡、场景网络、氛围描述 | 场景连接编译 |
| 主线 | 不强制首轮手写完整主线 | 幕结构、章节卡、高潮与 handoff | 章节状态编译 |
| 支线/角色线 | 不强制首轮手写完整矩阵 | 支线种子、角色线事件、阵营线分歧 | 任务 contract 编译 |
| 场景章节 | 不强制首轮手写全量章节 | 场景章节卡、阶段内容、章节载体 | signal 与导演层 |
| 运行时结构 | 不建议陶泥儿主接触 | 不建议默认编辑 | 可见性、导演、信号、编译、QA |
---
## 8. 推荐创作流程
## 8.1 第一步:只填写最小必填集
陶泥儿主只需要完成:
1. 世界一句话与核心幻想
2. 玩家身份与开局困境
3. 主题气质与禁忌边界
4. 核心冲突
5. 关键关系钩子
6. 标志性要素与硬规则
这一步应控制在:
- `5~15` 分钟
- `200~800`
- 或更少文字配合标签选择
## 8.2 第二步AI 生成“策划初稿包”
系统根据最小输入,生成一份结构化初稿包,建议至少包括:
1. 世界标题与摘要
2. `3~5` 个关键角色卡
3. `2~4` 个势力卡
4. `4~8` 个关键地点卡
5. `3~5` 条世界线程
6. `3~6` 个场景章节卡
7. 一批支线种子和标志性载体
这里的重点不是一次补满全世界,而是先形成一个像样的内容骨架。
## 8.3 第三步:陶泥儿主只精修高价值卡片
建议默认优先让陶泥儿主编辑这 4 类卡片:
1. 关键角色
2. 核心冲突与线程
3. 关键地点
4. 主线第一幕或前几个场景章节
这样能以最低编辑成本,最大幅度提升作品质量。
## 8.4 第四步:系统继续展开长尾
在关键卡片被锁定后,再由系统补:
- 长尾 NPC
- 支持性地点
- 次级支线
- 普通物件
- 任务包装
- 文案变体
## 8.5 第五步:陶泥儿主按需进入高级模式
高级模式只对愿意深挖的人开放:
- 角色背景章节编辑
- 场景章节细化
- 支线矩阵补完
- 阵营线分歧补强
- 结局变量微调
这一步不是默认主流程。
---
## 9. 哪些内容应该支持“锁定 + 局部重生成”
为了既保证低门槛,又保证创作安全感,第二层内容必须支持锁定和局部重生成。
建议至少支持锁定这些对象:
1. 世界一句话与主题边界
2. 核心冲突
3. 关键角色
4. 关键地点
5. 势力卡
6. 主线章节卡
7. 场景章节卡
8. 标志性载体
建议至少支持这些局部重生成动作:
1. 仅重生成长尾角色
2. 仅重生成长尾地点
3. 仅重生成支线种子
4. 仅重生成物件与残痕
5. 仅重生成某个角色卡
6. 仅重生成某个场景章节
7. 围绕已锁定角色重做主线第一幕
原则是:
**越高价值的锚点越要支持锁定,越低价值的铺量内容越适合重生成。**
---
## 10. 产品实现建议
## 10.1 默认 UI 只展示三段
建议工作台默认只展示:
1. 必填锚点
2. AI 初稿卡片
3. 高级模式入口
不要默认展示底层系统字段。
## 10.2 每张卡只保留自然语言输入
不要强迫陶泥儿主在首轮填写:
- tags
- ids
- 数值
- 稀有度
- 信号枚举
- step schema
更合理的做法是:
- 让陶泥儿主输入自然语言或选择直觉标签
- 再由系统编译成结构化字段
## 10.3 首轮生成后默认先看“精修建议”
AI 初稿生成后,不应该把陶泥儿主直接扔进一个大编辑器。
更好的做法是先给出:
1. 哪些卡片最值得改
2. 哪些内容已经比较稳定
3. 哪些内容仍然偏泛,需要陶泥儿主补个性
这样能明显提高陶泥儿主的修改效率。
## 10.4 移动端优先只保留高杠杆操作
移动端默认只应该支持:
1. 编辑必填卡
2. 浏览和修改关键角色卡
3. 浏览和修改关键地点卡
4. 锁定 / 重生成
5. 保存和继续创作
长表单和底层结构不要默认放在移动端主流程里。
---
## 11. 最后结论
如果目标是在自定义世界创作中真正平衡“降低门槛”和“提高作品质量”,最好的做法不是让陶泥儿主填更多字段,也不是把一切都交给 AI。
更合理的平衡是:
1. 陶泥儿主必须手填最小但高杠杆的 6 张卡,掌握世界灵魂。
2. AI 根据这 6 张卡生成一套可编辑的专业剧情初稿,负责把骨架展开成角色、地点、线程、章节和载体。
3. 陶泥儿主只精修最有价值的关键对象,锁定真正重要的内容。
4. 其余运行结构、数值、可见性、任务编译和 QA 检查都交给系统托管。
一句话收束:
**陶泥儿主负责决定“这个世界为什么值得被创作”AI 负责把它整理成可修改的策划初稿,系统负责把它稳定地跑成一个游戏世界。**

View File

@@ -1,724 +0,0 @@
# 纯 Agent 式对话创作工具与结构化创作方案的对比评估及转型设计
更新时间:`2026-04-12`
## 0. 文档目标
这份文档用于评估两种自定义世界创作形态:
1. 当前方向
- 基于“最小必填锚点 + AI 初稿卡片 + 系统托管层”的结构化创作方案
2. 纯 Agent 式方向
- 以前台对话为唯一主交互,陶泥儿主主要通过和 Agent 聊天来完成世界构建、角色塑造、剧情扩展和修改
文档需要回答 3 个问题:
1. 两种方案各自的优缺点是什么?
2. 对当前项目来说,纯 Agent 式是否更优?
3. 如果要转换成纯 Agent 式对话创作工具,应该怎么设计,才能不把质量和可控性一起丢掉?
一句话结论先说:
**纯 Agent 式对话创作工具更适合当“低门槛入口”和“陪创作过程”,但不适合把整个创作系统做成无结构、无锁定、无摘要、无编译层的纯聊天黑箱。**
更稳的方向不是“只有聊天”,而是:
**前台主交互纯 Agent后台继续保留结构化世界模型、锁定机制、局部重生成、编译层和质量护栏。**
---
## 1. 对比对象定义
## 1.1 当前结构化创作方案是什么
当前方案的核心是:
1. 陶泥儿主手填最小高杠杆锚点
2. AI 生成一批可编辑的剧情策划初稿卡片
3. 系统把内容编译成运行时结构
它本质上是:
**结构化工作台 + AI 协作生成。**
陶泥儿主的主要行为是:
1. 填写关键卡片
2. 修改关键角色、地点、势力、章节等内容卡
3. 锁定重要内容
4. 局部重生成次级内容
## 1.2 纯 Agent 式对话创作工具是什么
纯 Agent 式不是指“系统内部没有结构”,而是指:
**陶泥儿主前台几乎不需要面对表单和卡片编辑器,主要通过自然语言对话来完成创作。**
陶泥儿主的主要行为变成:
1. 用自然语言描述世界想法
2. 回答 Agent 的追问
3. 让 Agent 生成角色、地点、剧情和章节
4. 通过聊天指令要求修改、锁定、重做、总结和导出
它本质上是:
**对话式创作入口 + Agent 主导的协同编排。**
## 1.3 真正需要比较的不是“聊天 VS 表单”,而是“主交互模式 VS 后台结构”
很多产品会把问题误判成:
- 要么做聊天
- 要么做工作台
更准确的判断应该是:
1. 前台用户主要通过什么方式思考和输入?
2. 后台系统是否仍然有稳定的世界模型和编译层?
3. 陶泥儿主是否还能看见摘要、锁定内容和修改范围?
对当前项目来说,真正危险的不是“转成聊天”,而是:
**误把“纯 Agent 式”理解成“完全不需要结构化世界状态”。**
---
## 2. 总体结论
## 2.1 纯 Agent 式的主要优势
纯 Agent 式最大的价值,在于降低开局压力和创作焦虑。
它更擅长:
1. 帮不擅长表单和结构思考的陶泥儿主起步
2. 在陶泥儿主思路模糊时做追问和陪创作
3. 把“我要做一个世界”变成一次自然聊天
4. 动态决定追问深度,而不是一上来摆很多字段
5. 让陶泥儿主感觉自己是在和一个懂 RPG 的剧情搭档共创
## 2.2 纯 Agent 式的主要问题
纯 Agent 式最大的问题,不是能不能生成内容,而是:
**长项目一旦进入中后期,它会天然丢失可控性、可扫描性、可局部编辑性和可审计性。**
它最容易出现这些问题:
1. 聊天很多,但世界状态越来越难总览
2. 角色、地点、势力和章节信息散落在多轮消息里
3. 锁定范围不清,重生成容易误伤已有内容
4. Agent 很容易“替陶泥儿主决定太多”
5. 长会话越来越贵,越来越慢,也越来越容易漂移
## 2.3 对当前项目的判断
对当前项目而言:
1. 纯 Agent 式非常适合做创作入口
2. 纯 Agent 式也很适合做关键对象的精修与头脑风暴
3. 纯 Agent 式不适合作为唯一内容管理方式
因此更推荐的方向是:
**Agent-first而不是 Agent-only。**
也就是:
1. 前台以对话为主
2. 后台仍保留结构化世界状态
3. 关键内容仍然可被锁定、摘要、对比、局部重生成和导出
---
## 3. 纯 Agent 式对比当前方案的优缺点
## 3.1 对比表
| 维度 | 当前结构化方案 | 纯 Agent 式方案 | 更优者 |
| --- | --- | --- | --- |
| 首次上手门槛 | 比纯聊天高,需要理解少量卡片与阶段 | 最低,只需开口描述想法 | 纯 Agent |
| 创作陪伴感 | 中等AI 更像工具 | 很强Agent 更像搭档 | 纯 Agent |
| 思路模糊时的引导能力 | 有限,更多靠卡片提示 | 很强,可动态追问和启发 | 纯 Agent |
| 世界整体可扫描性 | 强,卡片和结构更容易总览 | 弱,聊天记录天然碎片化 | 当前方案 |
| 单对象精确编辑 | 强,适合定点修改 | 中等,容易在对话里带出额外变化 | 当前方案 |
| 锁定与局部重生成 | 容易做明确边界 | 容易模糊,需额外设计指令语义 | 当前方案 |
| 长项目稳定性 | 高,适合几十个对象持续维护 | 中等偏弱,越长越依赖摘要和记忆管理 | 当前方案 |
| 内容一致性维护 | 更容易做编译与 QA | 纯聊天很难稳定维护,需要后台隐藏编译 | 当前方案 |
| 移动端输入体验 | 表单负担偏大 | 聊天输入天然更友好 | 纯 Agent |
| 移动端结果总览 | 卡片更好浏览 | 长聊天记录不利于回看 | 当前方案 |
| 专业策划生产效率 | 中后期更高 | 前期更快,中后期容易反复确认 | 当前方案 |
| 新手用户心理压力 | 偏高,容易觉得要“填很多东西” | 低,更像在聊一个想法 | 纯 Agent |
| 实现复杂度 | 已有方向较明确 | 真正做好会更复杂,需要对话层和隐藏结构双系统 | 当前方案 |
| Token / 成本 / 延迟 | 更容易按模块调用 | 长会话上下文更重,成本更高 | 当前方案 |
| 可审计和交接 | 强,更适合多人协作 | 弱,需要额外导出和摘要机制 | 当前方案 |
## 3.2 当前结构化方案的主要优点
当前方案更强的地方在于:
1. 有明确的内容边界
2. 更容易做锁定、重生成和局部修改
3. 更适合中大型世界的长期维护
4. 更适合和后端编译层、任务层、章节层做稳定映射
5. 更容易把专业剧情策划流程映射成可执行数据
它的本质优势是:
**稳定、清楚、可扩展。**
## 3.3 当前结构化方案的主要缺点
当前方案更弱的地方在于:
1. 仍然有“我要开始填工具了”的压力
2. 对不擅长结构化思考的新手不够友好
3. 澄清、启发和陪创作感不够强
4. 很容易从“低门槛工作台”滑向“字段很多的编辑器”
5. 移动端如果处理不好,会有明显表单压迫感
## 3.4 纯 Agent 式方案的主要优点
纯 Agent 式更强的地方在于:
1. 入口极低
2. 更符合普通人“先说想法”的自然习惯
3. 更适合模糊创意逐步收束
4. 更容易把澄清问题变成真实协作
5. 更容易营造“有专业编剧陪你做世界”的体验
它的本质优势是:
**自然、轻松、像在共创。**
## 3.5 纯 Agent 式方案的主要缺点
纯 Agent 式更弱的地方在于:
1. 世界模型隐藏得太深时,陶泥儿主会失去整体掌控感
2. 多轮对话后,已确定内容不容易被清晰回看
3. 局部重做和精确编辑边界会变模糊
4. Agent 容易过度代写、过度主导
5. 没有强摘要和锁定机制时,创意很容易漂移
它的本质问题是:
**天然更擅长起步,不天然擅长收口。**
---
## 4. 对当前项目是否值得转成纯 Agent 式的判断
## 4.1 值得转的部分
以下环节非常适合转成纯 Agent 主交互:
1. 首次创作入口
2. 世界灵魂锚点收集
3. 低信息量输入后的澄清与启发
4. 关键角色、关键地点、核心冲突的初稿展开
5. 对单个角色或单个章节做陪创作式精修
因为这些环节的关键问题不是“字段如何摆放”,而是:
**陶泥儿主有没有被真正引导出自己想做的世界。**
## 4.2 不值得直接转成纯聊天黑箱的部分
以下环节不适合彻底做成无结构纯聊天:
1. 长项目世界管理
2. 大量角色、地点、支线、章节的总览
3. 锁定与局部重生成
4. 运行时结构编译
5. 质量审计与一致性检查
6. 导出和交付
这些环节需要的是:
**稳定的结构化世界状态,而不是越来越长的聊天记录。**
## 4.3 最合理的判断
如果硬要二选一:
1. 对新手用户和移动端体验,纯 Agent 更有吸引力
2. 对专业生产、长期维护和内容质量,当前结构化方案更稳
所以真正适合当前项目的不是完全替换,而是:
**把当前方案的“结构和护栏”保留,把用户感受到的“入口和协作方式”改成纯 Agent。**
---
## 5. 如果要转换成纯 Agent 式,对什么必须保持不变
纯 Agent 式可以改变前台交互,但不应该改变下面这些底层原则。
## 5.1 内容分层边界不能变
即使转成纯 Agent 式,也仍然要保留这三层:
1. 陶泥儿主必须确认的高杠杆锚点
2. AI 生成但允许陶泥儿主修改的策划初稿层
3. 系统托管的运行时编译层
变化的只是:
- 这些内容不一定通过卡片表单采集
- 可以通过对话逐步收集和确认
不应该变化的是:
- 谁来决定世界灵魂
- 谁来决定运行时结构
## 5.2 锁定机制不能变
纯 Agent 式必须仍然支持:
1. 锁定世界主题
2. 锁定核心冲突
3. 锁定关键角色
4. 锁定关键地点
5. 锁定主线章节
6. 锁定场景章节
7. 只重做未锁定部分
否则纯 Agent 式会很快变成:
**每次聊一句,世界都在偷偷漂移。**
## 5.3 局部重生成机制不能变
纯 Agent 式里也必须支持:
1. 只重生成长尾 NPC
2. 只重生成次级地点
3. 只重生成某个角色卡
4. 只重生成某个章节
5. 围绕锁定对象重做剩余草稿
如果这点没有做好,对话就会越来越像“整世界覆盖式重写”。
## 5.4 摘要、快照、差异对比不能变
纯 Agent 工具如果没有这些能力,后期一定失控:
1. 当前世界摘要
2. 已锁定内容清单
3. 本轮修改了什么
4. 当前有哪些待确认假设
5. 能否回退到上一版本
所以:
**前台可以纯聊天,后台不能没有版本化世界圣经。**
---
## 6. 转成纯 Agent 式后的产品定义
## 6.1 定义
建议把转型后的工具定义为:
**以 Agent 对话为主交互的 RPG 世界共创工具。**
它不是:
- 单纯聊天框
- 一次性大文本生成器
- 没有状态的陪聊机器人
它应该是:
1. 会主动澄清
2. 会阶段性总结
3. 会把聊天结果沉淀成结构化世界状态
4. 会提醒风险和冲突
5. 会在陶泥儿主要求时进行局部重写和定向扩展
## 6.2 正确理解
最重要的一句定义是:
**界面可以纯 Agent数据层绝不能纯会话。**
也就是说:
1. 陶泥儿主看到的是对话
2. 系统内部维护的是世界模型、锁定状态、摘要和编译结果
---
## 7. 纯 Agent 式工具的推荐交互模型
## 7.1 阶段 A创作意图收集
Agent 不直接要求用户填表,而是通过 `1~3` 轮自然对话收集最小锚点。
目标是确认:
1. 世界一句话
2. 玩家身份
3. 核心冲突
4. 主题气质
5. 关键关系钩子
6. 标志性要素
这实际上和当前“最小必填 6 张卡”是同一套内容,只是采集方式改成对话。
## 7.2 阶段 BAgent 输出首轮世界底稿
Agent 首轮不应该直接铺满全世界,而应该给出一份简明底稿,例如:
1. 世界标题和摘要
2. 玩家开局定位
3. 核心冲突结构
4. `3~5` 个关键角色
5. `4~6` 个关键地点
6. `2~4` 个势力
7. 主线第一幕简稿
同时必须明确分成 3 类:
1. 已确认内容
2. 建议内容
3. 待确认内容
## 7.3 阶段 C陶泥儿主锁定锚点
在纯 Agent 模式里,锁定行为必须被显式支持。
用户可以自然说:
- 这个世界观锁定
- 这个角色保留,不要再改
- 只把第一幕重做一下
- 势力关系别动,重新想地点
系统需要把这些自然语言翻译成正式的锁定状态和重生成范围。
## 7.4 阶段 D按对象逐步精修
Agent 不应该每轮都继续扩全局,而应该支持“单对象工作模式”。
例如:
1. 只精修某个角色
2. 只精修某个地点
3. 只精修某个场景章节
4. 只精修主线第一幕
5. 只精修一条支线
这样可以避免每轮修改都把整个世界重新搅动一次。
## 7.5 阶段 E系统后台自动编译与审计
每一轮重要修改后,系统后台应自动做:
1. 世界图谱更新
2. 可见性边界更新
3. 章节和任务编译
4. 设定冲突检查
5. 弱关联检查
6. 风格一致性检查
这些结果不一定全部展示,但必须被系统持续维护。
## 7.6 阶段 F导出世界圣经和可编辑初稿
纯 Agent 模式的最终产物不能只是一串聊天记录。
至少要能导出:
1. 世界摘要
2. 锁定锚点
3. 关键角色卡
4. 关键地点卡
5. 势力卡
6. 主线章节简稿
7. 支线种子
8. 场景章节草稿
9. 风险与待确认项
---
## 8. 纯 Agent 式工具需要的后台结构
## 8.1 会话层之外必须维护的核心状态
建议后台至少维护下面这些结构:
| 结构 | 作用 |
| --- | --- |
| `creatorIntentProfile` | 当前陶泥儿主最初和最新的创作意图 |
| `lockedAnchors` | 已确认不可自动改写的内容 |
| `worldDraftSnapshot` | 当前世界底稿快照 |
| `editableDraftCards` | 角色、地点、势力、章节等可编辑初稿 |
| `pendingClarifications` | 当前还未确认的问题 |
| `changeLog` | 每轮发生了什么变化 |
| `qualityFindings` | 冲突、泄露、弱关联和风格漂移结果 |
## 8.2 每轮对话后的处理流程
建议每次用户发言后走这条后台链:
```text
用户消息
-> 意图识别
-> 判断是在回答问题 / 修改对象 / 请求重生成 / 请求总结 / 请求锁定
-> 更新 creatorIntentProfile 或 worldDraftSnapshot
-> 重新编译相关草稿对象
-> 运行质量检查
-> 生成本轮回复
-> 同步更新摘要、待确认项和 changeLog
```
这条流程说明:
**纯 Agent 的前台体验背后,实际仍然是一个结构化内容状态机。**
---
## 9. 纯 Agent 式前台应该如何设计
## 9.1 主界面以对话为主
主界面可以只有一个核心聊天线程,但不建议只有聊天气泡。
建议保留 3 个轻量辅助区:
1. 顶部固定摘要
- 当前世界名
- 当前阶段
- 当前聚焦对象
2. 锁定内容条
- 展示已锁定的世界观、角色、地点、章节
3. 当前草稿摘要抽屉
- 展示关键角色、关键地点、主线第一幕等的简要卡片
这些区域不是表单编辑器,而是:
**对话模式下帮助用户保持掌控感的最小结构提示。**
## 9.2 支持快捷动作
为了防止用户每次都要自己组织复杂自然语言,建议保留轻量快捷动作:
1. 总结当前设定
2. 锁定当前版本
3. 只重做这一项
4. 展开主线第一幕
5. 增加一个关键角色
6. 给我 3 个更有辨识度的版本
7. 检查是否有设定冲突
这类动作按钮不破坏纯 Agent 主交互,反而能显著降低误解成本。
## 9.3 Agent 的提问规则
Agent 不能像问卷系统,也不能一次追问太多。
建议规则:
1. 一次最多追问 `1~3` 个问题
2. 问题必须是当前最缺的高杠杆信息
3. 每次追问都给默认建议方向
4. 如果陶泥儿主不想细答,允许 Agent 先代补一个版本再确认
这样才能保持“像聊天”,而不是“像客服表单”。
## 9.4 Agent 的总结规则
纯 Agent 工具必须高频做阶段性总结。
建议在这些时机自动总结:
1. 首轮世界底稿生成后
2. 锁定任意关键锚点后
3. 完成某个角色精修后
4. 主线第一幕生成后
5. 每累计 `5~8` 轮重要对话后
总结必须包含:
1. 已确认内容
2. 新增内容
3. 待确认内容
4. 潜在风险
---
## 10. 纯 Agent 式下的锁定、重生成与修改语义
## 10.1 锁定语义
建议支持以下语义:
1. 锁定对象
2. 锁定字段
3. 锁定关系
4. 锁定当前版本
例如:
- 锁定这个角色的身份和秘密,但可以重写语气
- 锁定这条冲突,不要再改动它的基本方向
- 锁定第一幕结构,只优化角色高光
## 10.2 重生成语义
建议支持以下语义:
1. 完整替换
2. 保留锚点重做
3. 仅补长尾
4. 给出多个候选版本
例如:
- 保留世界观和角色,重做关键地点
- 保留第一幕结构,给我三个更强的转折版本
- 只补 5 个更有辨识度的路人 NPC
## 10.3 修改语义
Agent 应能识别这些常见修改类型:
1. 收紧风格
2. 增强冲突
3. 提高角色辨识度
4. 减少套路感
5. 让地点更有故事残痕
6. 把支线和主线绑定得更紧
7. 提高队友反应和选择后果
这些应该是内容层意图,而不是要求用户直接碰底层字段。
---
## 11. 纯 Agent 式的主要风险与防护
## 11.1 风险 1对话越长世界越散
防护方式:
1. 周期性强制摘要
2. 关键内容结构化落库
3. 锁定内容固定展示
4. 提供“当前世界圣经”入口
## 11.2 风险 2Agent 过度代写,陶泥儿主失去作品归属感
防护方式:
1. 高杠杆锚点必须要求确认
2. 重要改动前先说“我准备改什么”
3. 默认优先给多个候选,而不是直接盖写
4. 允许陶泥儿主随时回退到旧版本
## 11.3 风险 3局部修改带出全局漂移
防护方式:
1. 只在目标作用域内修改
2. 修改后自动展示影响范围
3. 对高风险改动要求二次确认
## 11.4 风险 4看起来轻松实际上难以收口
防护方式:
1. 阶段化工作流
2. 每阶段有明确产物
3. 不是无限聊天,而是要进入“底稿确认 -> 精修 -> 导出”
## 11.5 风险 5成本和延迟持续上升
防护方式:
1. 长会话摘要压缩
2. 按对象加载上下文
3. 局部编译,而不是每轮重编整世界
---
## 12. 推荐转型路线
不建议一步到位把当前方案彻底替换成纯聊天。
更稳的路线是分 3 步走。
## 12.1 第一步:先把纯 Agent 做成默认入口
这一阶段:
1. 用户进入后直接和 Agent 聊
2. Agent 帮用户收集最小锚点
3. 系统在后台仍然生成当前方案里的结构化初稿
4. 结果页仍保留为可选工作台
这一阶段的目标是:
**把“起步方式”改成聊天,但不动后端结构主链。**
## 12.2 第二步:让关键对象编辑也支持 Agent 化
这一阶段:
1. 角色、地点、势力、主线第一幕都支持在聊天里精修
2. Agent 支持锁定、重做、总结、对比
3. 工作台逐步退成辅助视图,而不是默认主路径
这一阶段的目标是:
**让大多数高价值修改都可以通过聊天完成。**
## 12.3 第三步:工作台只保留总览和导出
到了这一阶段,前台已经基本纯 Agent 化,但仍建议保留:
1. 世界圣经总览
2. 已锁定对象列表
3. 版本快照
4. 风险与 QA 结果
5. 导出面板
这一阶段的目标不是消灭结构,而是:
**让结构从“编辑入口”退成“掌控和收口工具”。**
---
## 13. 最后结论
纯 Agent 式对话创作工具的最大优势,是把创作入口从“填写工具”变成“和懂创作的人对话”。
它会明显提升:
1. 首次上手意愿
2. 创作陪伴感
3. 模糊想法的收束效率
4. 移动端可用性
但它也天然会削弱:
1. 世界总览
2. 精确编辑
3. 局部重生成边界
4. 长项目稳定性
5. 质量审计与交接能力
因此,对当前项目最合理的方向不是彻底放弃结构化方案,而是把它升级成:
**前台纯 Agent 主交互,后台结构化世界模型持续存在,锁定、摘要、快照、局部重生成和质量护栏全部保留。**
一句话收束:
**可以把“创作入口”彻底 Agent 化,但绝不能把“世界状态管理”也做成纯聊天。**

View File

@@ -1,660 +0,0 @@
# 自定义世界自有设定层优化方案
更新时间:`2026-04-08`
## 0. 目标
这份文档要解决的问题是:
**当前自定义世界虽然已经是唯一正式可玩的世界入口,但它底层仍依赖武侠 / 仙侠模板设定。**
本次优化目标不是直接删除这些依赖,而是把它们逐步改造成:
**属于自定义世界自身的设定层,并且这套设定层必须能通用于任何题材。**
同时必须满足一条底线:
**不能破坏当前自定义世界生成流程的任何可用功能。**
一句话定义:
**让自定义世界从“借模板世界运行”,升级成“拥有自有设定层、可跨题材运行”的架构。**
---
## 1. 设计原则
这次优化必须同时遵守 4 条原则:
1. 设定归自定义世界自身所有
- 运行时、生成期、表现层真正依赖的世界设定,应该落回 `CustomWorldProfile` 或由它直接编译出的配置。
2. 设定必须跨题材
- 新设定不能只是把“武侠 / 仙侠”换一个更抽象的名字。
- 它必须能支撑奇幻、科幻、悬疑、校园、末世、神话、现代、海洋、裂界等任何题材。
3. 优化优先做兼容迁移
- 不能先删旧字段,再补新结构。
- 必须先补新设定层,再逐步迁读,最后再让旧模板字段退化成兼容层。
4. 不能增加陶泥儿主负担
- 这次不是让陶泥儿主多填一堆底层 schema。
- 这些设定仍然应由 AI / 系统编译出来,只是所有权从模板世界转移到自定义世界自己。
---
## 2. 当前自定义世界实际依赖了什么
根据 [CUSTOM_WORLD_TEMPLATE_DEPENDENCY_INVENTORY_2026-04-08.md](../reference/CUSTOM_WORLD_TEMPLATE_DEPENDENCY_INVENTORY_2026-04-08.md),当前依赖可以归纳成 6 组:
1. 模板锚点字段
- `templateWorldType`
- `WorldTemplateType`
2. 规则桥接
- `resolveRuleWorldType(...)`
3. 主题与词汇底板
- `detectCustomWorldThemeMode(...)`
- `buildThemePackFromWorldProfile(...)`
4. 属性、资源词与经济 fallback
- 预设属性 schema
- 资源命名
- 初始货币规则
5. 内容骨架
- 角色模板骨架
- 怪物模板池
- 场景视觉参考池
6. prompt 兼容字段
- `framework` 生成仍要求输出 `templateWorldType`
这些东西现在还不能直接删,因为:
**它们不是单纯的预设世界残留,而是当前自定义世界生成与运行时的真实支撑层。**
---
## 3. 本次优化的核心思路
这次不建议新增很多彼此平行的新系统,而是把现有模板依赖统一收束成:
**自定义世界自己的 5 层设定层。**
这 5 层分别是:
1. 语义锚层
2. 规则层
3. 表现层
4. 原型参考层
5. 兼容迁移层
这样可以让现在分散在:
- 模板世界枚举
- 预设 schema
- 视觉参考池
- 怪物池
- ThemePack 底板
这些地方的依赖,被重新编译回 `CustomWorldProfile` 自己的配置。
---
## 4. 目标结构
## 4.1 语义锚层:替代 `templateWorldType`
当前 `templateWorldType` 实际在回答的不是“你是不是武侠”,而是:
1. 这个世界更接近哪类冲突结构
2. 这个世界更接近哪类制度和禁忌
3. 这个世界更接近哪类叙事载体与力量来源
所以应把它升级成一套真正属于自定义世界自己的语义锚:
```ts
interface CustomWorldSemanticAnchor {
genreSignals: string[];
conflictForms: string[];
institutionTypes: string[];
tabooTypes: string[];
carrierTypes: string[];
forceSystemTypes: string[];
atmosphereTags: string[];
}
```
这层应该回答:
1. 这个世界的主要矛盾像什么
2. 这个世界的秩序结构像什么
3. 这个世界的危险和禁忌像什么
4. 这个世界的线索、遗物、文书、证物、技术、仪式像什么
关键点:
- 这里不再出现“武侠 / 仙侠”的模板世界名
- 只保留通用语义
例如:
- `institutionTypes`
- 宗门
- 公司
- 学园
- 教团
- 调查局
- 舰队
- 部族
- `forceSystemTypes`
- 灵脉
- 科技
- 仪式
- 契约
- 血统
- 神谕
- 污染
这就天然跨题材了。
---
## 4.2 规则层:把 fallback 变成自定义世界自己的规则设定
当前很多规则仍然会回落到武侠 / 仙侠:
- 属性 schema
- 资源命名
- 经济命名
- 初始货币
优化后应由自定义世界自己持有:
```ts
interface CustomWorldRuleProfile {
attributeSchema: WorldAttributeSchema;
resourceLabels: {
hp: string;
mp: string;
maxHp: string;
maxMp: string;
damage: string;
guard: string;
range: string;
cooldown: string;
manaCost: string;
currency: string;
};
economyProfile: {
initialCurrency: number;
};
}
```
这意味着以后运行时读取逻辑应优先变成:
1. 先读 `profile.ruleProfile`
2. 再读 `profile.attributeSchema`
3. 最后才允许读兼容 fallback
而不是:
1. 先判断 `WUXIA / XIANXIA`
2. 再猜自定义世界应该像哪边
这层的原则是:
**规则是这个自定义世界自己的,不再借模板世界托管。**
---
## 4.3 表现层:把 ThemePack 变成自定义世界自身的派生物
当前 `ThemePack` 仍然是“预设底板 + 自定义词汇补丁”。
优化后应改成:
```ts
interface CustomWorldExpressionProfile {
themePack: ThemePack;
presentationTone: string[];
namingDirectives: string[];
clueDirectives: string[];
revealDirectives: string[];
}
```
也就是说:
- `ThemePack` 仍然可以保留
- 但它不再被理解为“武侠底板 / 仙侠底板的延伸”
- 它应当是:
- `semanticAnchor`
- `creatorIntent`
- `majorFactions`
- `coreConflicts`
- `world text`
共同编译出来的结果
这一步非常关键,因为它会决定:
1. 物件命名
2. 势力命名
3. 线索形式
4. 提示词词汇风格
5. reveal 风格
只有把这层做成自定义世界自己的派生物,后面跨题材才会真的稳。
---
## 4.4 原型参考层:把模板骨架改成通用原型库
当前自定义世界借用模板的最深部分是:
1. 角色模板骨架
2. 场景视觉参考池
3. 怪物模板池
这三类不能粗暴删除,但可以改造成:
**通用原型参考层。**
建议统一成:
```ts
interface CustomWorldReferenceProfile {
roleArchetypes: RoleArchetypeProfile[];
sceneBuckets: SceneArchetypeBucket[];
creatureArchetypes: CreatureArchetypeProfile[];
}
```
### 4.4.1 角色原型
角色原型应描述的是:
- 正面推进型
- 远程压制型
- 控场解构型
- 续航承压型
- 潜行爆发型
而不是:
- 剑之公主
- 神箭游侠
- 双刃旅者
也就是说:
**保留战斗骨架和技能骨架,不保留模板角色人格设定。**
### 4.4.2 场景原型
场景原型应描述的是:
- 高压入口区
- 雨湿街巷区
- 临水渡口区
- 仪式神殿区
- 高空通路区
- 工业热区
- 地底遗迹区
- 群落聚居区
而不是:
- 竹林古道
- 云海仙门
也就是说:
**保留“空间语义 -> 视觉参考”的逻辑,不保留模板世界场景名作为中心。**
### 4.4.3 生物原型
生物原型应描述的是:
- 潜伏袭击者
- 重甲承压者
- 群居骚扰者
- 远程威胁者
- 异化污染体
- 灵体回响体
- 机关守卫体
而不是:
- 某个武侠怪
- 某个仙侠怪
这样之后:
- 自定义世界依赖的是“通用原型”
- 原型再映射到底层素材与 preset
---
## 4.5 兼容迁移层:旧字段继续保留一段时间
为了不破坏当前流程,短期内不能直接删除:
- `templateWorldType`
- `WorldTemplateType`
- 以及相关 normalize / save / read 逻辑
这层应被降级成:
```ts
interface CustomWorldCompatibilityProfile {
legacyTemplateWorldType?: 'WUXIA' | 'XIANXIA' | null;
migrationVersion: string;
}
```
作用:
1. 旧存档兼容
2. 旧 prompt 兼容
3. 旧测试兼容
4. 旧工具链兼容
但它不再应是新生成世界的第一真相来源。
---
## 5. 现有每类依赖如何改造成自定义世界自己的设定
下面把现有依赖逐项映射成未来目标。
## 5.1 `templateWorldType`
当前作用:
- 世界锚点
- 规则 fallback
- 视觉和怪物参考入口
目标改造:
- 拆成:
- `semanticAnchor`
- `ruleProfile`
- `referenceProfile`
- `compatibilityProfile`
迁移方式:
1. 旧字段保留
2. 新字段生成后优先读新字段
3. 旧字段只做迁移 fallback
## 5.2 `resolveRuleWorldType(...)`
当前作用:
-`CUSTOM` 解析回 `WUXIA / XIANXIA`
目标改造:
- 改成:
```ts
resolveCustomWorldRuleProfile(profile)
```
运行时不再需要知道“它更像武侠还是仙侠”,而只需要知道:
- 这个世界的规则 profile 是什么
## 5.3 `getPresetWorldAttributeSchema(...)`
当前作用:
- 自定义世界 schema 的参考底板
目标改造:
- 把预设 schema 底板重构成:
- 通用 schema seeds
例如按功能分:
1. 承压轴
2. 机动轴
3. 洞察轴
4. 决断轴
5. 共鸣轴
6. 续航轴
然后由自定义世界的 `semanticAnchor + creatorIntent` 生成最终命名与说明。
## 5.4 `PRESET_CHARACTERS`
当前作用:
- 自定义世界角色的战斗模板和技能骨架
目标改造:
- 抽出:
- `RoleArchetypeProfile`
- `SkillArchetypeProfile`
也就是说:
- 还可以继续复用当前角色的战斗结构
- 但不应再让自定义世界依赖那些模板角色的人设文本
## 5.5 场景图片参考池
当前作用:
- 自定义世界默认场景图匹配
目标改造:
- 改成:
- `SceneArchetypeBucket`
每个 bucket 只表达通用空间语义,不表达模板世界名。
## 5.6 怪物池
当前作用:
- 自定义世界敌对单位匹配 preset
目标改造:
- 改成:
- `CreatureArchetypeProfile`
由 archetype 再映射到底层怪物素材与 preset。
## 5.7 `buildThemePackFromWorldProfile(...)`
当前作用:
- 以模板题材包为底生成自定义世界 ThemePack
目标改造:
- 变成:
- `buildThemePackFromCustomWorldSemanticAnchor(...)`
即 ThemePack 以自定义世界自己的语义锚和词汇锚为底。
---
## 6. 不破坏当前流程的迁移顺序
这是最关键的落地顺序。
## 阶段 A先给 `CustomWorldProfile` 补新设定层
先补:
1. `semanticAnchor`
2. `ruleProfile`
3. `expressionProfile`
4. `referenceProfile`
5. `compatibilityProfile`
这一步只做新增,不删旧字段。
## 阶段 B旧字段自动编译新字段
当前已有 profile、旧存档、旧生成结果先统一经过
```ts
compileOwnedSettingLayersFromLegacyTemplate(profile)
```
让:
- 旧世界也拥有新设定层
- 新运行时可优先消费新字段
## 阶段 C生成链开始直接产出新设定层
修改:
- `framework prompt`
- `normalizeCustomWorldGenerationFramework(...)`
- `customWorld.ts`
使新生成世界优先产出:
- `semanticAnchor`
- `ruleProfile hints`
- `referenceProfile hints`
而不是只产出 `templateWorldType`
## 阶段 D运行时逐步改读新设定层
优先改:
1. 资源词与货币
2. attribute schema
3. ThemePack
4. 视觉参考
5. 怪物映射
6. 角色原型引用
要求:
- 每次只切一层
- 每层都保留 fallback
## 阶段 E把旧模板字段降级为兼容层
当上面的读取已经都切走后:
- `templateWorldType` 就不再是主链依赖
- 只作为:
- migration
- 老存档兼容
- 老工具兼容
---
## 7. 推荐新增的最小字段
为了避免系统膨胀,建议只先补最小集合。
## 7.1 `CustomWorldOwnedSettingLayers`
```ts
interface CustomWorldOwnedSettingLayers {
semanticAnchor: CustomWorldSemanticAnchor;
ruleProfile: CustomWorldRuleProfile;
expressionProfile: CustomWorldExpressionProfile;
referenceProfile: CustomWorldReferenceProfile;
compatibilityProfile?: CustomWorldCompatibilityProfile | null;
}
```
然后挂到:
```ts
interface CustomWorldProfile {
ownedSettingLayers?: CustomWorldOwnedSettingLayers | null;
}
```
这样好处是:
1. 不用在 `CustomWorldProfile` 顶层堆太多字段
2. 迁移更集中
3. 后续删除兼容层更容易
---
## 8. 对当前功能链的保护要求
这次优化过程中,下面这些能力不能坏:
1. 自定义世界创建
2. 自定义世界保存 / 读取
3. 自定义世界角色生成
4. 自定义世界场景生成
5. 自定义世界开局
6. 自定义世界运行时的:
- 属性
- 资源词
- 经济
- 场景图
- 敌对实体
- ThemePack
- prompt 组织
也就是说:
**任何一次迭代都必须是“新层可用 + 旧层仍可兜底”。**
---
## 9. 验收标准
当下面这些标准成立时,说明这套优化开始有效:
1. 新生成的自定义世界不再必须依赖 `templateWorldType` 才能表达自身设定。
2. 运行时优先读取 `ownedSettingLayers`,而不是先问武侠 / 仙侠。
3. 自定义世界的属性、资源词、经济、视觉参考、怪物映射、ThemePack 都能从自身设定层派生出来。
4. 新设定层描述的是通用语义,不是模板世界换皮。
5. 当前自定义世界生成流程、旧存档、旧结果页工作台仍然可用。
---
## 10. 最后结论
如果目标是:
**让这些依赖都变成属于自定义世界的设定,而且这些设定要通用于任何题材。**
那么最正确的方向不是“继续弱化武侠 / 仙侠字样”,而是:
**把模板支撑层整体迁移成自定义世界自己的设定层。**
更具体地说,就是把当前依赖重组为:
1. 自定义世界自己的语义锚
2. 自定义世界自己的规则 profile
3. 自定义世界自己的表达 profile
4. 自定义世界自己的原型参考 profile
5. 只负责兼容的旧模板字段
这样之后,自定义世界才会真正从:
**模板依赖型生成架构**
迁移成:
**跨题材、自有设定层、且兼容当前流程的生成架构。**

View File

@@ -1,656 +0,0 @@
# 自定义世界去模板依赖与跨题材通用化优化设计
更新时间:`2026-04-08`
## 0. 目标
这份文档解决的是一个已经明确暴露出来的问题:
**当前玩家主流程虽然已经移除了武侠 / 仙侠两个预设世界,但自定义世界底层仍然依赖武侠 / 仙侠模板设定。**
本次优化的目标不是简单“删掉模板字段”,而是要在不破坏当前自定义世界生成流程的前提下,把这些依赖逐步改造成:
**属于自定义世界自身、且能通用于任何题材的设定层。**
一句话定义:
**让自定义世界从“挂靠武侠 / 仙侠模板运行”,升级成“基于通用世界设定层独立运行”。**
---
## 1. 优化原则
这次优化必须同时满足 3 条硬原则:
1. 设定归自定义世界自身所有
- 任何运行时、生成期、表现层真正依赖的设定,都应尽量落回 `CustomWorldProfile` 或由它直接编译出的通用配置,不再默认挂在 `WUXIA / XIANXIA` 两个模板世界上。
2. 设定必须跨题材通用
- 不能把“自定义世界去模板化”理解成“再做一套更抽象的武侠 / 仙侠替代字段”。
- 新结构必须能容纳奇幻、科幻、悬疑、末世、现代、神话、校园等任何题材。
3. 不能破坏当前自定义世界生成流程
- 现有 `framework -> themePack -> storyGraph -> role / landmark -> runtime` 主链必须继续能跑。
- 优化应以兼容迁移为主,而不是大爆破式重写。
---
## 2. 当前问题归纳
根据 [CUSTOM_WORLD_TEMPLATE_DEPENDENCY_INVENTORY_2026-04-08.md](../reference/CUSTOM_WORLD_TEMPLATE_DEPENDENCY_INVENTORY_2026-04-08.md),当前自定义世界仍依赖模板层的地方主要有:
1. 模板锚点类型
- `templateWorldType`
- `WorldTemplateType`
- `resolveRuleWorldType(...)`
2. 主题与规则回退
- `detectCustomWorldThemeMode(...)`
- `resolveCustomWorldAnchorWorldType(...)`
- `buildThemePackFromWorldProfile(...)` 底板
3. 属性与表现
- 预设世界属性 schema
- 资源词、数值命名、货币命名
4. 角色骨架
- `PRESET_CHARACTERS`
- 模板技能定义
- 模板 opening 接口
5. 场景与视觉参考
- 武侠 / 仙侠场景图片参考池
- 模板 camp 场景映射
6. 怪物模板池
- 武侠 / 仙侠怪物 preset 池
这些依赖本质上说明:
**当前自定义世界不是完全自足,而是“先生成自定义内容,再把它映射回两套模板世界骨架”。**
---
## 3. 这次优化不应该怎么做
先明确几个错误方向:
## 3.1 不能直接删掉 `templateWorldType`
如果直接删除:
- `templateWorldType`
- `WorldTemplateType`
- `WUXIA / XIANXIA` 相关回退
而不先补新的通用设定层,那么当前自定义世界会立刻失去:
1. 规则桥接
2. 主题底板
3. 场景图参考
4. 怪物池匹配
5. 属性 schema fallback
这会直接打断现有生成和运行链路。
## 3.2 不能把新设定继续写成“武侠 / 仙侠的抽象别名”
例如下面这种思路是不够的:
-`templateWorldType` 改名成 `worldFamily`
- 但值仍然只有两类近似武侠 / 仙侠的内部枚举
这不是真正跨题材,只是换了名字。
## 3.3 不能让陶泥儿主承担更多底层配置工作
这次优化不是让陶泥儿主额外填写:
- 怪物模板表
- 场景参考池
- 属性 schema 槽位
- 规则 profile
正确方向应该是:
**这些通用设定仍由系统生成 / 编译,但所有权回到自定义世界自身。**
---
## 4. 目标结构:把模板依赖改造成自定义世界自己的 4 层通用设定
为了避免系统越改越散,这次建议不要新增很多彼此平行的新系统,而是把现有模板依赖统一收束成 4 层通用设定。
## 4.1 第一层:世界语义锚层
这是替代 `templateWorldType` 的核心层。
它不再回答“你更像武侠还是仙侠”,而回答:
1. 这个世界的主要冲突形式是什么
2. 这个世界的制度、禁忌、叙事载体、力量来源是什么
3. 这个世界更接近哪类表现模式
建议由自定义世界自己持有一个通用结构,例如:
```ts
interface CustomWorldSemanticAnchor {
genreSignals: string[];
conflictModel: string[];
institutionHints: string[];
tabooHints: string[];
carrierHints: string[];
forceSystemHints: string[];
}
```
说明:
- 它是自定义世界自己的语义锚。
- 它可以表示:
- 宗门与灵脉
- 财团与实验体
- 学园与旧规
- 边境与裂界
- 海潮与失落群岛
- 神话与誓约
- 不再强制回落到武侠 / 仙侠二选一。
## 4.2 第二层:规则与表现配置层
这是替代:
- `resolveRuleWorldType(...)`
- 预设属性 schema fallback
- 资源命名 fallback
- 经济 fallback
建议改成由自定义世界持有一份通用 `WorldRuleProfile`
```ts
interface WorldRuleProfile {
attributeSchema: WorldAttributeSchema;
resourceLabels: {
hp: string;
mp: string;
maxHp: string;
maxMp: string;
damage: string;
guard: string;
range: string;
cooldown: string;
manaCost: string;
currency: string;
};
economyProfile: {
initialCurrency: number;
rarityValueScale: Record<string, number>;
};
}
```
关键点:
1. 以后运行时不要再优先问“这是武侠还是仙侠”。
2. 应该直接问:
- `profile.ruleProfile.attributeSchema`
- `profile.ruleProfile.resourceLabels`
- `profile.ruleProfile.economyProfile`
这样:
- 奇幻世界可以叫“体魄 / 法力”
- 末世世界可以叫“生命 / 电量”
- 校园悬疑世界甚至可以弱化“mana”概念改成“专注 / 压力”
## 4.3 第三层:内容骨架与参考层
这是替代:
- 预设角色模板骨架
- 武侠 / 仙侠场景图参考池
- 武侠 / 仙侠怪物 preset 池
这里不建议让自定义世界自己保存大批素材,而是建议让自定义世界持有:
**对内容骨架的“编译后引用配置”。**
建议统一成一个 `ContentReferenceProfile`
```ts
interface ContentReferenceProfile {
roleArchetypes: RoleArchetypeProfile[];
sceneReferenceBuckets: SceneReferenceBucket[];
creatureArchetypes: CreatureArchetypeProfile[];
}
```
其中每个子项都应是通用语义,而不是模板世界名:
1. `RoleArchetypeProfile`
- 例如:
- 正面压制型
- 远程游击型
- 灵术控制型
- 重装承压型
- 潜行刺击型
2. `SceneReferenceBucket`
- 例如:
- 高压门禁区
- 雨夜街巷区
- 高空通道区
- 神殿 / 仪式区
- 工业热区
- 潮湿临水区
3. `CreatureArchetypeProfile`
- 例如:
- 潜伏掠食者
- 重甲承压者
- 群居灵体
- 远程骚扰者
- 寄生污染体
关键点:
- 这些 archetype 可以继续映射到底层素材和 preset。
- 但对自定义世界来说,它依赖的是“通用原型”,不是“武侠怪物池 / 仙侠怪物池”。
## 4.4 第四层:叙事与词汇编译层
这是替代:
- 以模板世界为底的 `ThemePack` fallback
- prompt 中的模板世界兼容字段
建议做法:
1. `ThemePack` 继续保留,但它的来源变成:
- `semanticAnchor + creatorIntent + world text + content reference profile`
2. 生成框架 prompt 不再要求输出:
- `templateWorldType: WUXIA|XIANXIA`
3. 改为要求输出:
- 世界语义锚
- 规则表现关键词
- 冲突和制度线索
例如:
```ts
interface CustomWorldGenerationFramework {
name: string;
subtitle: string;
summary: string;
tone: string;
playerGoal: string;
semanticAnchor: CustomWorldSemanticAnchor;
majorFactions: string[];
coreConflicts: string[];
camp: CampOutline;
}
```
这样:
- 生成流程仍然是 `framework -> themePack -> storyGraph -> role / landmark`
- 只是 framework 的核心锚不再依赖预设世界枚举
---
## 5. 现有依赖如何一一改造
## 5.1 `templateWorldType` -> `semanticAnchor + ruleProfile`
当前用途:
- 表示自定义世界挂靠武侠还是仙侠
目标改造:
- 运行时不再直接读取 `templateWorldType`
- 改为读取:
- `semanticAnchor`
- `ruleProfile`
迁移策略:
1. 先保留 `templateWorldType` 作为兼容字段
2. 新增 `semanticAnchor / ruleProfile`
3. 由旧字段自动编译出新字段
4. 所有读取逻辑逐步切到新字段
5. 最后把 `templateWorldType` 降级为 migration-only 字段
## 5.2 `resolveRuleWorldType(...)` -> `resolveWorldRuleProfile(...)`
当前用途:
-`CUSTOM` 解析回 `WUXIA / XIANXIA`
目标改造:
- 不再返回模板 world type
- 直接返回自定义世界自己的 `ruleProfile`
即:
```ts
resolveWorldRuleProfile(worldType, customWorldProfile)
```
返回:
- `attributeSchema`
- `resourceLabels`
- `economyProfile`
- 其它规则信息
## 5.3 预设属性 schema -> 通用 attribute schema seed
当前用途:
- 用武侠 / 仙侠 schema 作为自定义世界 schema 参考底板
目标改造:
- 不再直接引用“武侠六脉 / 仙侠六轴”作为唯一底板
- 改为维护一组通用 `attribute schema seeds`
例如可按世界体验而不是题材命名:
1. 正面对抗型
2. 机动博弈型
3. 灵知洞察型
4. 共鸣契约型
5. 生存续航型
6. 高危推进型
然后由自定义世界的 `semanticAnchor` 决定如何组合或命名这些槽位。
## 5.4 预设角色模板 -> 通用角色原型骨架
当前用途:
-`PRESET_CHARACTERS` 选模板角色,再覆写自定义世界内容
目标改造:
- 把当前模板角色骨架抽成“通用战斗原型角色”
例如保留的是:
- 技能骨架
- 动作风格
- build 倾向
- 动画资源挂载方式
而不是保留“剑之公主 / 神箭游侠”这样的预设世界人格设定。
关键点:
- 动画素材和技能骨架可以保留
- 但运行时不应再依赖具体模板角色的人设文本
## 5.5 武侠 / 仙侠场景图参考池 -> 通用场景参考桶
当前用途:
- 用武侠 / 仙侠场景关键词为自定义世界匹配默认背景图
目标改造:
- 改成通用 `SceneReferenceBucket`
- 每个 bucket 对应一类空间语义
例如:
1. 落脚处 / 归舍
2. 高压入口
3. 雨湿街巷
4. 高空通路
5. 仪式空间
6. 工业热区
7. 临水空间
8. 地底遗迹
这样就能让:
- 科幻世界
- 校园世界
- 神话世界
- 末世世界
都共享同一套“空间语义 -> 视觉参考”的逻辑。
## 5.6 武侠 / 仙侠怪物池 -> 通用生物原型库
当前用途:
- 从武侠 / 仙侠怪物池里为自定义世界匹配怪物
目标改造:
- 改成通用 `CreatureArchetypeProfile`
- 再由 archetype 去映射底层 preset / 数值 / 动画素材
这样做的好处:
1. 自定义世界依赖的是“潜伏者 / 重压者 / 群居体 / 异化体”
2. 而不是“这更像武侠怪还是仙侠怪”
## 5.7 `ThemePack` 底板 -> 通用语义底板
当前用途:
- 自定义世界的 ThemePack 还是从预设题材包底板开始
目标改造:
- 让 ThemePack 直接从:
- `semanticAnchor`
- `creatorIntent`
- `majorFactions`
- `coreConflicts`
- `contentReferenceProfile`
编译出来
也就是说:
**ThemePack 应变成自定义世界自己的派生物,而不是模板世界的扩写版。**
---
## 6. 不破坏现有生成流程的迁移方案
这是这份文档最重要的部分。
正确做法不是一口气替换,而是兼容迁移。
## 阶段 A新增通用设定字段但不删旧字段
先做:
1.`CustomWorldProfile` 上新增:
- `semanticAnchor`
- `ruleProfile`
- `contentReferenceProfile`
2. 保留:
- `templateWorldType`
3. 由当前旧字段自动编译出新字段
目标:
- 先让新结构出现
- 但现有流程完全不受影响
## 阶段 B生成流程改为优先产出新字段
先改:
- `framework prompt`
- `customWorld.ts`
让 AI 先输出:
- 通用语义锚
- 规则提示
- 通用 archetype 线索
同时在 normalize 层继续兼容旧的 `templateWorldType`
目标:
- 新生成的自定义世界开始“原生带通用设定”
- 旧存档仍可继续读取
## 阶段 C运行时读取切到新设定
依次改:
1. 规则层
-`resolveRuleWorldType` 切到 `resolveWorldRuleProfile`
2. 属性层
- 优先读 `profile.ruleProfile.attributeSchema`
3. 资源词与经济层
- 优先读 `profile.ruleProfile.resourceLabels / economyProfile`
4. 场景图与怪物映射
- 优先读 `contentReferenceProfile`
目标:
- 让模板世界字段不再是运行时第一来源
## 阶段 D模板世界字段退化为兼容层
这一步完成后:
1. `templateWorldType` 只用于:
- 旧存档迁移
- 老测试兼容
- 数据修复 fallback
2. 不再用于:
- 正式生成主链
- 正式运行时主链
## 阶段 E再做深清理
只有当上面几步都完成后,才适合继续清理:
1. 非必要的模板回退逻辑
2. 非必要的主流程模板枚举消费点
3. 非必要的审计 / 工具硬编码
---
## 7. 对当前主链的兼容要求
这次优化过程中,下面这些链路必须始终可用:
1. `PreGameSelectionFlow -> generateCustomWorldProfile(...)`
2. `framework -> themePack -> storyGraph -> role / landmark`
3. 保存 / 读取自定义世界 profile
4. 自定义世界开局
5. 自定义世界角色与场景生成
6. 自定义世界运行时怪物 / 物品 / 场景图 /词汇表现
也就是说:
**任何迁移都必须先补新字段,再迁读,再退旧字段,不能先删旧字段。**
---
## 8. 建议新增或改造的最小数据结构
为了避免系统膨胀,这次不建议引入很多彼此割裂的新系统,建议只在 `CustomWorldProfile` 周边增量补三块。
## 8.1 `semanticAnchor`
```ts
interface CustomWorldSemanticAnchor {
genreSignals: string[];
conflictModel: string[];
institutionHints: string[];
tabooHints: string[];
carrierHints: string[];
forceSystemHints: string[];
}
```
## 8.2 `ruleProfile`
```ts
interface WorldRuleProfile {
attributeSchema: WorldAttributeSchema;
resourceLabels: {
hp: string;
mp: string;
maxHp: string;
maxMp: string;
damage: string;
guard: string;
range: string;
cooldown: string;
manaCost: string;
currency: string;
};
economyProfile: {
initialCurrency: number;
};
}
```
## 8.3 `contentReferenceProfile`
```ts
interface ContentReferenceProfile {
roleArchetypes: string[];
sceneReferenceBuckets: string[];
creatureArchetypes: string[];
}
```
这三块足够作为第一轮去模板化的最小自定义世界设定层。
---
## 9. 验收标准
做到以下几点,才能说明自定义世界真正开始脱离模板依赖:
1. 新生成的自定义世界框架不再需要直接输出 `WUXIA|XIANXIA` 才能工作。
2. 运行时核心逻辑优先读取 `semanticAnchor / ruleProfile / contentReferenceProfile`
3. 自定义世界的属性 schema、资源词、经济词不再默认直接回退到武侠 / 仙侠文案。
4. 自定义世界的角色骨架、场景视觉、怪物映射能通过通用 archetype 表达。
5. 现有生成流程、存档读取、运行时体验不受破坏。
6. 旧存档仍能通过兼容层运行。
---
## 10. 最后结论
当前自定义世界真正缺的,不是“再删一点武侠 / 仙侠字样”,而是:
**把模板世界支撑层改写成自定义世界自己的通用设定层。**
更准确地说,这次优化要完成的是:
1. 把模板锚点改成自定义世界自己的语义锚
2. 把模板回退改成自定义世界自己的规则配置
3. 把模板角色 / 场景 / 怪物参考改成通用原型引用
4. 把 ThemePack 和生成 prompt 从“依附模板世界”改成“直接从自定义世界自身编译”
同时整个过程必须遵守一条底线:
**任何优化都不能破坏当前自定义世界生成与运行主链。**
所以这不是“删模板”的问题,而是一次:
**在兼容现有流程前提下,把自定义世界从模板依赖型架构,迁移成真正跨题材、自足型架构。**

View File

@@ -1,882 +0,0 @@
# 配装构筑 + 合成/锻造闭环详细设计
更新时间:`2026-03-25`
## 0. 设计前提
这份方案基于当前仓库已经存在的运行时结构来设计,不另起一套独立系统。
- 现有物品结构已经有 `InventoryItem.tags``statProfile``useProfile``buildProfile`
- 现有装备位只有 `weapon / armor / relic` 三槽,因此本期套装与 build 成型必须围绕 2 件和 3 件完成。
- 现有战斗结算已经有 `functionEffect.damageMultiplier / incomingDamageMultiplier`,但 `equipmentEffects.ts` 中的装备数值聚合仍然基本为空壳。
- 现有素材库中已经出现 `setId``setName``pieceName``synergy` 等 build 元数据,但尚未进入真实伤害结算。
- 现有角色、怪物、消耗品、掉落、宝藏、NPC 交易都已经形成了资源入口,适合继续补成“获取 -> 拆解 -> 锻造 -> 配装 -> 战斗 -> 再获取”的闭环。
因此,本方案的目标不是“再做一层 UI”而是补齐以下 4 层:
1. 标签规范化层:把当前中英混用、结构标签与语义标签混用的问题拆开。
2. 语义相似度层:用 embedding 相似度把“相近标签”自动组织为 build 与套装簇。
3. 伤害修正层:把标签结果稳定接入当前伤害公式。
4. 合成/锻造闭环让掉落、材料、装备、buff、交易真正循环起来。
## 1. 结合当前系统的落地判断
### 1.1 现有可复用基础
- `src/types.ts`
- 已有 `ItemStatProfile`
- 已有 `ItemUseProfile`
- 已有 `ItemBuildProfile`
- 已有 `EquipmentLoadout`
- 已有 `GameState.playerEquipment`
- `src/data/itemDesign.ts`
- 已经能为装备自动生成 `buildProfile`
- 已经有 `setId / setName / pieceName / synergy`
- 已经有一批 role/tag 原型,例如 `assassin / caster / ward / fate`
- `src/data/monsterPresets.ts`
- 已经有 `habitatTags`
- 已经有较完整的 `lootTable`
- `src/data/treasureInteractions.ts`
- 已经有材料、消耗品、稀有品产出入口
- `src/hooks/useCombatFlow.ts`
- 玩家、同伴、怪物三条伤害结算路径已经齐备
- 只差把 build 结果统一注入 `damage`
### 1.2 当前缺口
- 当前 `InventoryItem.tags` 同时承担了“分类标签”和“战斗语义标签”,例如 `weapon / armor / relic / material / mana / healing` 混在一起。
- 当前角色正式数据结构里没有稳定的 `combatTags`,角色标签主要还停留在 UI 展示常量。
- 当前怪物只有 `habitatTags`,更适合掉落/生态,不适合直接进入伤害 build。
- 当前技能/物品可以恢复数值,但还不能稳定施加“限回合 build buff 标签”。
- 当前 `getEquipmentBonuses()` 没有真正读取 `statProfile + buildProfile`,导致 build 无法进入伤害。
结论:
- 现有系统已经具备 build 系统的数据骨架。
- 真正需要补的是“标签语义层”和“统一伤害入口”。
## 2. Build 标签设计原则
### 2.1 标签必须贴近实体语义
参与 build 的标签应尽量是玩家能直觉理解的“风格词”,而不是纯系统词。
推荐使用:
- 行为风格:`快剑``突进``追击``反击``蓄力``控场`
- 输出方式:`重击``连段``远射``雷法``符阵``毒雾`
- 生存节奏:`护体``守御``回复``续战``压血`
- 材料/流派气质:`寒铁``星砂``灵木``骨纹``镇邪`
- 阵营/身份风格:`先锋``游击``法修``命纹``统御`
不推荐直接把以下内容当作伤害 build 标签:
- `weapon`
- `armor`
- `relic`
- `material`
- `piece:weapon`
- `world:xianxia`
这些更适合保留为筛选、配方、掉落、编辑器过滤用的结构标签。
### 2.2 标签分层
建议把标签拆成三层:
| 层级 | 用途 | 示例 |
| --- | --- | --- |
| 结构标签 | 分类、筛选、配方、存档兼容 | `weapon``armor``material``set:steel` |
| 战斗语义标签 | 进入 build 相似度和伤害修正 | `快剑``突进``护体``雷法` |
| 生态/素材标签 | 掉落、锻造、配方倾向 | `矿道``雾林``寒铁``星砂` |
推荐约束:
- `InventoryItem.tags` 继续保留结构标签与通用筛选标签。
- `ItemBuildProfile.tags` 只保留会进入 build 计算的语义标签。
- `MonsterPreset.habitatTags` 保留生态用途,不直接参与伤害。
- 新增 `combatTags` 给角色/怪物,用于战斗 build。
### 2.3 中英混用兼容
当前素材生成中已有较多英文 role/tag角色 UI 和自定义世界中又偏中文标签,因此需要加一层标签规范化。
建议建立 `buildTagRegistry`
```ts
type BuildTagDefinition = {
id: string; // ASCII 稳定 id例如 "quickblade"
label: string; // 展示名,例如 "快剑"
category: "role" | "style" | "resource" | "element" | "defense" | "craft";
aliases: string[]; // 兼容 assassin / duelist / 快剑流 等历史写法
description: string; // 供 embedding 使用
};
```
示例映射:
| 现有值 | 规范标签 |
| --- | --- |
| `assassin` | `快袭` / `切后` / `突进` |
| `duelist` | `快剑` / `连段` / `对拼` |
| `vanguard` | `先锋` / `稳压` / `护体` |
| `ward` | `守御` / `镇邪` / `护体` |
| `berserker` | `压血` / `重击` / `爆发` |
| `caster` | `法修` / `法力` / `远程` |
| `support` | `护持` / `回复` / `增益` |
| `fortress` | `重甲` / `格挡` / `反击` |
| `fate` | `命纹` / `机缘` / `冷却` |
| `commander` | `统御` / `均衡` / `队伍增益` |
## 3. 标签来源设计
### 3.1 装备标签
装备是 build 的主来源但要区分“结构标签”和“build 标签”。
建议:
- 武器、护甲、饰品各自最多提供 `2` 个核心 build 标签。
- 若装备存在 `setId`,运行时根据套装件数额外生成“合成标签”。
- 同一标签在多个装备上重复出现时,只记一次,不按件数无限叠加。
装备运行时推荐结构:
```ts
type RuntimeEquipmentBuildSource = {
itemId: string;
slot: "weapon" | "armor" | "relic";
coreTags: string[];
setId?: string;
setName?: string;
forgeRank?: number;
};
```
### 3.2 角色/怪物标签
角色和怪物都应该有自己的 `combatTags`
建议:
- 角色固定提供 `2~3` 个核心战斗语义标签。
- 怪物固定提供 `2~3` 个核心战斗语义标签。
- `habitatTags` 不直接参与 build 伤害,而是决定掉落材料簇、锻造路线和部分弱点设计。
例子:
- 剑系角色:`快剑``突进``压制`
- 重甲怪物:`重甲``震击``守势`
- 雾林怪物生态标签:`雾林``湿毒``潜伏`
- 其中 `湿毒``潜伏` 可提升为 `combatTags`
- `雾林` 保留为生态/掉落标签
### 3.3 Buff 标签
buff 标签是 build 的短时放大器,也是技能与物品进入构筑闭环的关键。
buff 标签来源:
- 技能施加的限回合标签
- 消耗品施加的限回合标签
- 锻造出的铭刻/附魔效果施加的限回合标签
建议新增:
```ts
type TimedBuildBuff = {
id: string;
sourceType: "skill" | "item" | "forge";
sourceId: string;
name: string;
tags: string[];
durationTurns: number;
maxStacks?: number;
};
```
推荐时长:
- 强爆发 buff`1~2` 回合
- 节奏/机动 buff`2~3` 回合
- 防御/续航 buff`2~4` 回合
重复规则:
- 同名 buff 默认刷新持续时间,不新增一套完全重复标签。
- 不同来源但规范化后相同的标签,只保留一个 build 标签实例。
- 重复来源只提高优先级或刷新,不增加额外 `+1` 基础值。
## 4. 语义 embedding 与套装 build 形成方式
### 4.1 为什么要引入 embedding
如果只靠显式 `setId`build 会很死板。
引入 embedding 后,可以让这些组合自然成型:
- `快剑` + `突进` + `追击` + `风行`
- `重甲` + `护体` + `守御` + `反击`
- `雷法` + `法器` + `过载` + `法力`
也就是说:
- 显式套装仍然存在
- 隐式语义套装也能成立
- 两者都走同一套 build 标签计算,不必再写第二套特殊规则
### 4.2 embedding 计算对象
不要对每个物品实例现算 embedding而是只对“规范标签定义”计算。
推荐流程:
1.`buildTagRegistry` 中每个规范标签生成一条 embedding 文本。
2. 文本内容由 `label + aliases + description` 组成。
3. 预计算标签相似度矩阵并缓存到本地数据文件。
4. 运行时只读相似度,不现算模型。
示例 embedding 文本:
```text
快剑:以高速轻兵器、连续出手、贴身追击为核心的近战风格;别名 duelist, swift blade, 快袭。
```
### 4.3 相似度函数
建议使用余弦相似度,并且只保留正向相似。
```ts
similarity(a, b) = max(0, cosine(embedding(a), embedding(b)))
```
建议阈值:
- `< 0.35`:视为无关,按 `0`
- `0.35 ~ 0.65`:弱协同
- `0.65 ~ 0.82`:强协同
- `> 0.82`:视为同 build 簇强关联
### 4.4 语义套装簇形成
推荐同时保留两种 build 成型路径:
#### A. 显式套装
- 依据 `setId`
- 2 件时生成一个合成标签:`套装:<setName>`
- 3 件时再生成一个进阶标签:`宗匠:<setName>`
由于当前 runtime 只有三槽,所以 2 件和 3 件阈值最合适。
#### B. 隐式语义套装
当激活标签里存在 `3` 个及以上标签两两相似度超过阈值时,形成“语义 build 簇”。
例如:
- `快剑`
- `突进`
- `追击`
- `风行`
它们无需拥有相同 `setId`,也能形成一组高相似 build。
## 5. 标签修正值与伤害公式
### 5.1 核心规则
用户给出的核心要求是:
- 每个标签修正值都是 `1`
- 每多一个标签修正值加一
- 同时所有标签的 `1` 需要额外乘上与新标签的相似度
- 修正结果作用于输出伤害
将这条规则做成稳定、顺序无关的公式,可以写成:
```text
rawBuildScore(T) = |T| + Σ similarity(t_i, t_j)
(i < j)
```
其中:
- `|T|` 是激活标签数量,每个标签天然贡献 `1`
- 每对标签之间再贡献一段相似度分
这个公式与“新增一个标签时,额外 +1并让旧标签的 1 再乘上与新标签的相似度”完全等价。
对应的增量写法:
```text
score_1 = 1
score_k = score_(k-1) + 1 + Σ similarity(t_i, t_k)
(1 <= i < k)
```
### 5.2 激活标签集的选取
为了防止标签爆炸,建议运行时只取 `MAX_ACTIVE_BUILD_TAGS = 8`
推荐优先级:
1. 限回合 buff 标签
2. 角色/怪物核心标签
3. 武器 build 标签
4. 饰品 build 标签
5. 护甲 build 标签
6. 2 件/3 件套装合成标签
重复标签去重后再参与计算。
### 5.3 从 raw score 到伤害倍率
`rawBuildScore` 不直接等于伤害倍率,否则会过大。建议再做一层线性缩放与封顶。
```text
buildDamageBonus = clamp((rawBuildScore - 1) * 0.03, 0, 0.45)
buildDamageMultiplier = 1 + buildDamageBonus
```
推荐解释:
- 单标签时没有 build 成型,不给明显收益
- 2~4 标签形成初步风格,获得 5%~18% 左右伤害增益
- 5~8 标签形成成熟 build伤害增益逐步逼近 45% 上限
### 5.4 与当前战斗公式的接法
当前战斗中,伤害基本来自:
- 技能基础伤害
- `functionEffect.damageMultiplier`
- 装备 stat 值
建议统一改成:
```text
finalOutgoingDamage =
round(
baseSkillDamage
* functionDamageMultiplier
* equipmentStatMultiplier
* buildDamageMultiplier
)
```
说明:
- `equipmentStatMultiplier` 来自武器/护甲/饰品的 `statProfile`
- `buildDamageMultiplier` 来自标签相似度 build
- 先乘完再 `round`
本期先只影响“输出伤害”。
也就是:
- 玩家攻击怪物时用玩家侧标签
- 同伴攻击怪物时用同伴侧标签
- 怪物攻击玩家/同伴时用怪物侧标签
防御端 build 抵抗可以放到下一期,不必一开始就加复杂。
### 5.5 示例
某角色当前激活标签为:
- `快剑`
- `突进`
- `追击`
- `风行`
- `套装:百炼争锋`
假设相似度如下:
- `快剑-突进 = 0.82`
- `快剑-追击 = 0.78`
- `快剑-风行 = 0.65`
- `突进-追击 = 0.80`
- `突进-风行 = 0.72`
- `追击-风行 = 0.70`
- 套装标签与前四者平均相似度 `0.76`
则:
```text
rawBuildScore
= 5
+ (0.82 + 0.78 + 0.65 + 0.80 + 0.72 + 0.70)
+ (0.76 * 4)
= 11.51
buildDamageBonus
= clamp((11.51 - 1) * 0.03, 0, 0.45)
= 0.3153
buildDamageMultiplier = 1.3153
```
若本次技能基础伤害为 `28`,动作倍率为 `1.2`,装备数值倍率为 `1.14`,则:
```text
finalDamage = round(28 * 1.2 * 1.14 * 1.3153) = 50
```
## 6. 合成 / 锻造 / 回收闭环设计
### 6.1 闭环目标
让当前已有入口真正连起来:
- 怪物掉落
- 宝藏奖励
- NPC 交易
- 背包积累
- 装备更替
- 消耗品 buff
- 锻造与回收
形成闭环后,物品系统就不再只是“剧情资源池”,而是成长系统。
### 6.2 资源分层
建议把资源拆成 4 类:
| 类型 | 作用 | 当前可复用入口 |
| --- | --- | --- |
| 基础材料 | 进入配方、升级、重铸 | 怪物掉落、宝藏、NPC 交易 |
| 标签精粹 | 决定 build 方向 | 拆解装备、精炼材料 |
| 套装蓝图 | 决定显式 setId 结果 | 宝藏、精英怪、任务 |
| 催化剂 | 提升稀有度、锁词条、转流派 | 稀有品、商店、Boss 掉落 |
### 6.3 推荐闭环
```mermaid
flowchart LR
A["遭遇 / 战斗 / 宝藏 / 交易"] --> B["获得装备 / 材料 / 消耗品 / 蓝图"]
B --> C["直接装备"]
B --> D["拆解回收"]
D --> E["基础材料"]
D --> F["标签精粹"]
D --> G["蓝图碎片"]
E --> H["合成精炼材料"]
F --> H
G --> I["完整蓝图"]
H --> J["锻造新装备"]
I --> J
J --> K["重铸 / 淬炼 / 铭刻"]
K --> C
C --> L["形成 build 与伤害提升"]
L --> A
```
### 6.4 关键环节
#### A. 拆解
目标:
- 回收无用装备
- 提取 build 倾向
- 让玩家可以主动转流派
建议产出:
- 基础材料:按装备部位与稀有度产出
- 标签精粹:按 `buildProfile.tags` 产出对应流派精粹
- 套装碎片:带 `setId` 的装备有概率掉落
推荐规则:
- 普通/优秀:返还 `1~2` 基础材料 + `1` 标签精粹
- 稀有/史诗:返还 `2~4` 基础材料 + `1~2` 标签精粹
- 传说:返还 `4+` 基础材料 + `2~3` 标签精粹 + 套装碎片
#### B. 合成
目标:
- 把零散材料往更高层资源转化
推荐配方:
- `3` 个同系基础材料 -> `1` 个精炼材料
- `2` 个相近标签精粹 -> `1` 个簇催化剂
- `3` 个蓝图碎片 -> `1` 个完整蓝图
#### C. 锻造
目标:
- 制造三槽位装备
- 明确把材料生态和 build 流派联系起来
建议锻造公式:
```text
成品 = 槽位模板 + 基础材料 + 标签精粹 + 蓝图/催化剂
```
推荐规则:
- 武器:决定主要输出 build 标签
- 护甲:决定生存/反击/续战方向
- 饰品:决定资源、冷却、机动、控场补短板
#### D. 重铸
目标:
- 保留 build 主方向
- 调整副标签
推荐规则:
- 保留主标签与 `setId`
- 只在同一语义簇内重掷副标签
- 花费标签精粹 + 货币
这样玩家不会因为一次重铸直接从 `快剑` 跳成 `重甲`
#### E. 淬炼
目标:
- 提升已有装备而不是频繁换装
推荐效果:
- 提升 `forgeRank`
- 增加 `statProfile`
- 提高套装合成标签的优先级
#### F. 铭刻 / 附魔
目标:
- 让消耗品和锻造形成直接关系
推荐效果:
- 给装备附加可触发 buff 标签
- 或直接生产“一次性战斗铭符”
例子:
- `疾风符``2` 回合获得 `风行``突进`
- `镇岳印``2` 回合获得 `护体``守御`
- `雷纹油``1` 回合获得 `雷法``过载`
## 7. 当前三槽系统下的 build 形态
由于当前只有 `weapon / armor / relic` 三槽,建议本期 build 以“2 件成型、3 件毕业”为主。
### 7.1 套装成型方式
- 1 件:只提供本件核心标签
- 2 件:生成 `套装:<setName>` 合成标签
- 3 件:再生成 `宗匠:<setName>` 进阶标签
这两个“合成标签”本质上也是普通 build 标签,因此会自动进入 embedding 相似度和伤害公式。
### 7.2 推荐 build archetype
| build | 角色/怪物核心标签 | 装备标签方向 | buff 标签方向 | 锻造材料倾向 |
| --- | --- | --- | --- | --- |
| 快剑追袭 | `快剑``突进``追击` | 武器补 `连段`,饰品补 `风行` | `疾风``破绽` | `百炼钢``风羽``轻皮` |
| 重甲反击 | `守御``重甲``反击` | 护甲补 `护体`,饰品补 `镇势` | `立壁``嘲压` | `寒铁``壳片``岩核` |
| 雷法过载 | `法修``雷法``法力` | 武器补 `过载`,饰品补 `聚灵` | `引雷``蓄放` | `星砂``雷纹石``灵晶` |
| 命纹机缘 | `命纹``冷却``机缘` | 饰品补 `连锁`,护甲补 `续战` | `转运``回转` | `命纹骨片``旧卷``秘印` |
| 医理续战 | `回复``护持``续战` | 护甲补 `守御`,饰品补 `凝神` | `回气``清心` | `灵木``药囊``泉露` |
## 8. 与当前掉落和地图生态的关系
### 8.1 怪物生态标签不直接进伤害
当前怪物已有 `habitatTags`,更适合驱动:
- 掉落材料倾向
- 可锻造流派倾向
- 宝藏/地图资源分布
例如:
- `矿道 / 铸坊 / 废城` -> `寒铁``锻火``重甲`
- `雾林 / 沼泽` -> `毒囊``湿毒``潜伏`
- `祭坛 / 遗迹 / 古迹` -> `残卷``纹石``镇邪`
- `月湖 / 灵泉 / 天河` -> `水灵``回气``法力`
### 8.2 当前掉落表可直接扩展
当前怪物掉落里已经有很多适合闭环的原型:
- `armor + material`
- `weapon + material`
- `relic + mana`
- `consumable + material`
下一步不必推翻,只要再补:
1. 掉落物的 `craftTags`
2. 掉落物的 `buildProfile`
3. 拆解产物表
就能把这些现有掉落自然接进锻造循环。
## 9. 数据结构建议
### 9.1 类型扩展
建议在现有结构上最小扩展:
```ts
type BuildTagCategory =
| "role"
| "style"
| "resource"
| "defense"
| "element"
| "craft";
interface BuildTagDefinition {
id: string;
label: string;
category: BuildTagCategory;
aliases: string[];
description: string;
}
interface ItemBuildProfile {
role: string;
tags: string[]; // 只放战斗语义标签
setId?: string;
setName?: string;
pieceName?: string;
synergy?: string[];
craftTags?: string[]; // 新增:配方/材料倾向
forgeRank?: number; // 新增:淬炼等级
}
interface CombatTaggedActor {
combatTags: string[];
}
interface TimedBuildBuff {
id: string;
sourceType: "skill" | "item" | "forge";
sourceId: string;
name: string;
tags: string[];
durationTurns: number;
}
```
### 9.2 GameState 扩展
```ts
interface GameState {
activeBuildBuffs: TimedBuildBuff[];
}
```
### 9.3 技能与物品扩展
```ts
interface ItemUseProfile {
hpRestore?: number;
manaRestore?: number;
cooldownReduction?: number;
buildBuffs?: TimedBuildBuff[];
}
interface FunctionEffectConfig {
damageMultiplier?: number;
incomingDamageMultiplier?: number;
healAmount?: number;
manaRestore?: number;
cooldownTickBonus?: number;
grantedBuildBuffs?: TimedBuildBuff[];
}
```
## 10. 运行时接入点
### 10.1 必须新增的规则层
建议新增 3 个数据/规则文件:
- `src/data/buildTags.ts`
- 标签规范化
- alias 映射
- 相似度矩阵
- `src/data/buildDamage.ts`
- 聚合激活标签
- 计算 `rawBuildScore`
- 计算 `buildDamageMultiplier`
- `src/data/forgeRecipes.ts`
- 合成、锻造、拆解、重铸配方
### 10.2 当前代码里的关键接点
#### A. `src/data/equipmentEffects.ts`
需要从“空壳”升级为真正读取:
- `statProfile`
- `buildProfile`
- 套装件数
建议这里做:
- 读取三槽装备 stat 汇总
- 计算显式套装件数
- 产出装备侧 build 标签源
#### B. `src/hooks/useCombatFlow.ts`
这里是 build 进伤害的核心接点。
当前玩家、同伴、怪物都有单独 `damage = ...` 的结算片段,建议统一收敛成:
```ts
resolveOutgoingDamage({
actor,
target,
skill,
functionEffect,
gameState,
})
```
统一处理:
- actor 的 `combatTags`
- actor 装备 build 标签
- actor 当前限回合 buff 标签
- 显式套装合成标签
- embedding build 伤害修正
#### C. `src/data/characterPresets.ts`
角色需要正式持有 `combatTags`,不要再只放在 UI 常量里。
#### D. `src/data/monsterPresets.ts`
怪物新增:
- `combatTags`
- `craftTags`
保留:
- `habitatTags`
#### E. `src/hooks/useGamePersistence.ts`
存档兼容必须同步补:
- `activeBuildBuffs`
- 新字段默认值
- 旧存档自动补空数组
## 11. 编辑器与策划工作流
结合当前项目“先补类型和规则,再补 UI”的经验编辑器建议按下面顺序补。
### 11.1 标签注册表
先做一个统一标签注册表,再让各编辑器引用它。
编辑器最少应支持:
- 选择规范标签
- 查看别名
- 查看所属 build 簇
- 查看与其他标签的相似度
### 11.2 Item Catalog Editor
当前它已经能展示 `buildProfile`,下一步建议补:
- 规范标签选择器
- 当前装备可形成的 build 预览
- 2 件/3 件套装合成标签预览
- 拆解产物预览
### 11.3 角色/怪物编辑
建议把角色和怪物的 `combatTags` 录入正式数据,不再只放展示文案。
### 11.4 相似度预计算脚本
建议加一个脚本,例如:
```text
scripts/build-tag-similarity.mjs
```
负责:
- 读取标签注册表
- 生成 embedding
- 输出相似度矩阵 JSON
这样运行时就不需要联网或现算。
## 12. 数值与反滥用约束
为了让系统长期可控,建议一开始就加以下约束:
1. 同规范标签去重,不允许同词条多件装备无限叠加基础 `+1`
2. 激活标签上限 `8`,避免后期词条爆炸。
3. 伤害 build 增益封顶 `45%`,防止纯标签乘爆。
4. 2 件/3 件套只生成合成标签,不再额外套一层独立乘区,避免双重膨胀。
5. `habitatTags` 不直接进伤害,避免出现“矿道标签提高输出”这种语义跑偏。
6. buff 标签以刷新时长为主,不以无限叠层为主。
7. 重铸只在语义近邻内滚动,避免 build 完全变异。
8. 拆解返还率不要超过完整锻造成本的 `60%~70%`,避免无限刷循环。
## 13. 分阶段实施建议
按照当前项目文档里已经验证过的开发经验,推荐顺序是“类型 -> 规则 -> hook -> UI”不要反过来。
### 阶段 A先补数据骨架
- 新增 `buildTagRegistry`
- 为角色/怪物补 `combatTags`
- 为物品补 `craftTags / forgeRank`
-`GameState``activeBuildBuffs`
### 阶段 B再补规则
- 实现标签规范化
- 实现 embedding 相似度矩阵
- 实现 `rawBuildScore`
- 实现三槽位显式套装合成标签
- 实现 buff 标签衰减
### 阶段 C接入战斗
- `useCombatFlow.ts` 改为统一伤害入口
- 玩家 / 同伴 / 怪物统一读取 build
- `equipmentEffects.ts` 正式生效
### 阶段 D补合成/锻造闭环
- 拆解
- 合成
- 锻造
- 重铸
- 铭刻
### 阶段 E最后补编辑器与 UI
- 物品 build 预览
- 套装预览
- 锻造页
- 材料来源与标签簇提示
## 14. 一句话总结
本方案的核心不是单独增加“套装数值”,而是把装备、角色/怪物、限回合 buff 都转成统一的语义 build 标签,再用“每标签基础值 1 + 标签间 embedding 相似度”的方式形成构筑强度,并把这份构筑强度直接接进当前伤害输出,同时让怪物掉落、宝藏、交易、拆解、合成、锻造、铭刻围绕同一套标签体系形成闭环。

View File

@@ -1,887 +0,0 @@
# 等级成长、章节经验节奏与 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 交付链路为准。
## 实现进度2026-04-20 第二批)
当前仓库已继续落地第二批成长能力:
1. 已给运行时敌对 NPC / 战斗遭遇补上 `levelProfile``experienceReward`,前后端快照、战斗态和恢复链路会保留这组元数据。
2. 已新增敌对成长解析服务,当前先以玩家当前等级为 fallback`npc_fight` / 敌对战斗入口自动生成等级、参考强度、战斗生命值与击杀经验。
3. 已将 Express 后端战斗胜利结算接入 `hostile_npc` 经验发放,击败敌对 NPC 后会直接更新 `playerProgression`,并写回 `hostileNpcsDefeated` 统计。
4. 已在战斗画布中补上敌对 NPC 的最小 `Lv.` 徽标展示,保持 UI 极简表达。
## 实现进度2026-04-20 第三批)
当前仓库已继续落地第三批“章节预算 / 自动定级”能力:
1. 已新增服务端 `chapterProgressionPlanner`,会基于 `sceneChapterBlueprints` 编译每章的 `entry / exit pseudo level`、总经验预算、任务经验份额、敌对经验份额与预计击杀数。
2. 已新增 `npcLevelResolver`,会根据当前章节阶段和当前 act 的 `primaryNpcId` 自动区分 `hostile_standard / hostile_elite / hostile_boss / rival`,并输出 `source = chapter_auto` 的等级档案。
3. 已将 `npc_fight` / `npc_spar` 开战入口接入章节上下文解析;当运行时存在章节蓝图、当前章和当前 act 信息时,敌对 NPC 不再只跟随玩家当前等级,而会按章节自动定级并生成更贴合本章预算的经验奖励。
4. 已补上规划器、定级器与路由级验证,确认同一玩家在不同章节和不同阶段触发敌对战斗时,会得到不同的等级与经验结果。
本轮仍未落地的部分:
1. `ChapterExperienceLedger` 的正式持久化、按章实际经验记账与偏差回看还未接入。
2. 同章重复刷敌的 `repeatPenalty` 与超预算衰减还未落地,当前仍是“预算规划 + 单次掉落”版本。
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 中战斗目标占比
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

@@ -1,40 +0,0 @@
# 移动端创作页新建作品紧凑布局设计
## 目标
移动端创作页顶部的新建作品模块只承担快速进入创作模板的作用,不承担规则解释和长说明承载。模块在首屏中最多占用约 1/3 高度,把更多空间留给作品列表和筛选操作。
## 落地范围
- 入口组件:`src/components/custom-world-home/CustomWorldCreationStartCard.tsx`
- 外层页面:`src/components/custom-world-home/CustomWorldCreationHub.tsx`
- 模板元数据继续复用 `PLATFORM_CREATION_TYPES`,不新增前端业务逻辑。
## 移动端布局规则
1. 顶部标题行压缩成单行:左侧标题,右侧仅保留简短状态,不再显示说明段落。
2. 模板入口在手机端使用横向滚动胶囊卡片,每个卡片保持单行动作感,不堆叠成长列表。
3. 卡片高度控制在约 4rem 内,标题与状态信息并排组织,避免大留白。
4. 模块本体使用 `max-height: 33svh` 作为硬约束,内容超出时优先在模板入口行内横向滚动,不撑高页面。
5. 桌面端保持网格入口,但同步收紧内边距和卡片留白,避免移动端与桌面端表现割裂。
6. 横向滚动模板行必须隐藏原生滚动条,保留滑动能力,避免底部出现过粗的视觉条。
7. 模板入口排序以可创作为第一优先级:可创建卡片保持原配置内相对顺序排在前面,锁定且展示“敬请期待”的卡片保持原配置内相对顺序排在后面。
## 文案约束
- UI 不新增规则说明类文案。
- 原有“直接选择游戏创作模板,立刻进入对应的共创工作台。”说明在移动端隐藏,桌面端保留为辅助说明。
- 可创建的模板卡不展示“可创建”状态标签,只保留标题、短副标题和进入箭头。
- 锁定的模板卡统一以“敬请期待”作为状态标注,不再显示“锁定”。
- RPG 入口展示为“角色扮演 / 剧情演绎,冒险成长”,拼图入口展示为“拼图 / 创意礼物,生活分享”。
- 忙碌状态仅保留在模块标题行的轻量状态中,避免占用每张可用卡片的首要视觉层级。
## 2026-05-07 玩法参考图
1. 每个玩法入口都必须配置一张 `public/creation-type-references/` 下的参考图。
2. 当前创作 Tab 顶部玩法卡带、旧创作中心卡带和玩法类型弹层都消费同一份 `PLATFORM_CREATION_TYPES.imageSrc`,避免多入口视觉漂移。
3. 图片只承担玩法识别和氛围锚定,不在卡片上叠加规则说明文案。
4. 移动端卡片仍以紧凑横滑为主,参考图使用暗色遮罩承接标题,文本不得溢出卡片。
5. 当前创作 Tab 的可见玩法卡必须真实渲染 `img`,不能只在隐藏弹窗或旧入口中配置图片。
6. 参考图卡片上的标题和副标题必须显式使用白色文字,并配合底部加深渐变与文字阴影;禁止依赖 `text-inherit`,避免黑字叠在暗蒙版上。
7. 当前创作 Tab 顶部不再保留“10分钟创作一个精品互动玩法”标题玩法参考图卡带直接作为首屏入口移动端卡带必须支持横向拖动滑动。

View File

@@ -1,58 +0,0 @@
# 移动端草稿页作品列表统一卡片设计 2026-04-29
## 背景
草稿页的作品模块需要同时承载 RPG、拼图和大鱼吃小鱼等玩法。不同玩法卡片不能各自展示阶段、素材、主题等细节标签否则作品列表会在移动端显得拥挤并且草稿作品会暴露过多编辑态信息。
本次将作品列表卡片收口成统一信息结构:草稿只用于快速识别和继续创作,已发布作品才展示公开数据;删除与分享等低频操作收进长按动作面板,避免列表常态被按钮挤占。
## 落地范围
- 列表容器:`src/components/custom-world-home/CustomWorldCreationHub.tsx`
- 作品卡片:`src/components/custom-world-home/CustomWorldWorkCard.tsx`
- 不改动作品数据聚合、筛选、打开和体验逻辑。
- 已发布作品保留分享能力,卡片右上角常驻分享图标;可删除作品保留删除能力,但常态不显示删除按钮。
## 卡片结构规则
1. 卡片整体对齐发现 / 分类页的横向作品列表结构:内容层承载标题、状态、类型、摘要与必要数据,封面作为不占内容布局的右半区透明背景层。
2. 不再显示阶段、主题、素材完成度、作者、作品号等额外标签。
3. 标题区域保留作品状态与游戏类型;草稿和已发布状态只用图标化 UI 标识,不再在卡片上显示“草稿 / 已发布”文字。
4. 草稿卡片到作品描述为止,不显示右侧“继续创作”等固定动作、统计、作品号或公开指标。
5. 已发布卡片在描述下方显示三项公开指标:游玩数、改造数、点赞数。
6. 已发布卡片右上角显示分享图标,点击后复制作品分享文案,不触发卡片打开。
7. 长按草稿作品弹出独立动作面板,只展示删除作品;长按已发布作品弹出独立动作面板,展示分享和删除。动作按钮点击后不得触发卡片打开。
8. 卡片不显示最后修改时间;`updatedAt` 只用于作品列表排序。
9. 生成中的卡片在整卡上叠加半透明蒙版、旋转等待符号和“生成中...”标识;蒙版不能移除标题、状态、类型、摘要、右侧封面等原有信息。
## 公开指标重点展示补充
1. 已发布作品的三项公开指标不得继续使用标签样式展示,必须参考作品详情页的统计区,采用“小标签 + 大数字 + 单位”的重点信息结构。
2. 指标文案统一为“游玩”“改造”“点赞”,不得在创作页卡片中展示 `Remix` 英文。
3. 用户每次进入创作页时,前端读取上一次进入该页面缓存的公开指标快照;当已发布作品卡片滑动进入视口后,数字从缓存值增长到本次接口返回的最新值。
4. 若最新值高于缓存值,动画完成后在对应指标右下角展示红色向上箭头和本次上涨的具体数值,字号低于主数字,避免抢占主信息层级。
5. 若没有缓存值、缓存值不低于最新值或作品仍是草稿,则直接显示最新值,不展示上涨标记。
6. 每张作品卡片继续使用作品封面作为右侧半区透明背景主视觉;封面从右向左渐隐,不能出现独立方形边界,也不能因为列表收缩挤占正文布局或被拉伸变形。
7. 作品列表按 `updatedAt` 倒序排列;前端排序需要兼容 ISO 时间和 Rust 后端常用的 `seconds.microsZ` 时间文本。
8. 若玩法摘要缺少 `coverImageSrc`,允许从同一作品的正式关卡图、背景图或素材图里取第一张可用图片作为卡片背景兜底。
9. 若作品真实图片为空、私有资源换签失败或浏览器图片加载失败,卡片必须切到对应玩法的 `public/creation-type-references/` 参考图;最终兜底底色使用平台浅粉暖白色系,不允许退回黑色普通面板。
## 移动端布局规则
1. 作品列表默认使用单列纵向列表,视觉上与发现页分类列表保持一致。
2. 移动端每张卡片使用绝对定位右半区封面背景层,封面在右边缘最清晰、向左渐隐;草稿卡即使复用分类页基础类名,也必须用自身选择器覆盖分类页的 `4.3rem + 正文 + action` 三列规则,避免正文被压进窄列。
3. 已发布作品的公开指标在卡片正文内保留,但需要压缩字号和间距,不能依赖右侧封面列参与排版。
4. 小屏卡片降低高度、内边距、标题字号和徽标尺寸,避免长标题或中文描述撑破容器。
5. 右侧封面层本身必须带玩法参考图 CSS 背景兜底;`img` 的真实封面或 `ResolvedAssetImage` fallback 加载失败时,也不能出现空白或黑卡。
## 网页端布局规则
1. 网页端作品架不能继续拉成整行超宽列表;从 `768px` 起使用多列卡片式网格,默认两列,宽屏提升到三列。
2. 网页端卡片保留移动端同一信息结构,但卡片高度增加,正文区可显示更多摘要与公开指标,右侧封面改为更高的半透明视觉区。
3. 删除与分享仍然只在长按动作面板或键盘揭示态显示;默认态不得透出红色删除底层。
## 文案约束
- 不新增功能说明类文案。
- 空态和错误态沿用现有文案。
- 中文标题、描述和指标需要在卡片内截断或换行,不得因长文本破坏布局。

View File

@@ -1,316 +0,0 @@
# 高好感角色聊天内委托触发与领取流程设计
更新时间:`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

@@ -1,33 +0,0 @@
# 平台入口隐藏大鱼吃小鱼创作入口设计
日期:`2026-04-28`
## 1. 变更背景
平台当前“选择创作类型”弹层同时暴露 RPG、大鱼吃小鱼、拼图三类入口。
本轮需求只要求在平台里隐藏“大鱼吃小鱼”的创作入口,不要求删除已有玩法实现、运行时路由、作品数据或后台能力。
## 2. 落地边界
- 只调整平台入口层展示,不修改大鱼吃小鱼已有前后端链路。
- 不删除 `big-fish` 相关路由、服务、作品详情、运行时与数据结构。
- 隐藏策略应收敛到统一配置层,避免首页、弹层、后续复用入口出现显示状态漂移。
## 3. 实现方案
1.`src/components/platform-entry/platformEntryCreationTypes.ts` 的创作类型元数据中增加 `hidden` 字段。
2.`big-fish` 类型标记为 `hidden: true`
3. 平台创作类型弹层渲染前统一过滤 `hidden` 项。
这样可以保证:
- 平台用户看不到“大鱼吃小鱼”创作入口。
- 若后续重新开放,只需改回配置,不必再拆 UI 逻辑。
- 不影响既有直达路由、历史作品数据和开发中的玩法链路。
## 4. 验收点
- 平台“选择创作类型”弹层不再显示“大鱼吃小鱼”卡片。
- 可创建卡片排在前面,展示“敬请期待”的锁定卡片排在后面,交互状态保持稳定。
- 代码层不引入对 Big Fish 运行时或结果页的额外耦合修改。

View File

@@ -1,64 +0,0 @@
# 平台入口分类与创作 Tab 强化设计
## 1. 目标
在不新建平台入口系统的前提下,直接扩展现有 `RpgEntryHomeView` 主 Tab
- 新增“分类” Tab用作品标签聚合所有公开发布作品。
- 强化“创作” Tab 的导航视觉权重,让它在底部导航中居中并更醒目。
- 登录态底部导航顺序为:推荐、发现、创作、草稿、我的。
- 未登录态底部导航只保留:推荐、创作、发现,其中创作保持居中。
## 2. 数据边界
本次只做前端展示重排,不新增后端接口:
- 分类数据来源使用现有 `latestEntries``featuredEntries` 的公开作品列表。
- 标签来源沿用 `buildPlatformWorldTags(entry)`,公开作品会映射为题材、角色数、地标数。
- 同一公开作品若同时出现在精选与最新中,按 `ownerUserId + profileId` 去重。
- 点击分类作品继续走现有 `onOpenGalleryDetail`,不改变详情页和登录拦截逻辑。
## 3. 交互规则
### 3.1 登录态
底部导航展示 5 个入口:
1. 推荐
2. 发现
3. 创作
4. 草稿
5. 我的
创作入口位于第三位,视觉上使用更大的图标壳、轻微上浮、渐变高亮和阴影,保证它是主行动入口。
### 3.2 未登录态
底部导航展示 3 个入口:
1. 推荐
2. 创作
3. 发现
不展示“草稿”和“我的”,避免未登录用户在底部导航看到必须登录后才有价值的入口。创作入口位于第二位,保持几何居中,并沿用原推荐 Tab 的星光图标;推荐 Tab 改用游戏手柄图标。
### 3.3 桌面端
桌面侧栏同步增加“分类”,但保持纵向导航,不强行做居中布局。创作入口仍使用强调样式。
## 4. 分类页布局
分类页为独立 Tab 面板,不在首页下方展开:
- 顶部展示标签胶囊,默认选中作品数量最多的标签。
- 标签切换后,下方网格展示该标签下所有公开作品。
- 无公开作品时展示现有空状态组件。
- 分类页不写玩法规则说明类长文案,只保留必要标题、短状态文案和作品卡片。
## 5. 验收点
- 登录态移动端底部导航顺序准确,创作在 5 个 Tab 中居中。
- 未登录态移动端底部导航只显示 3 个 Tab创作在中间。
- 发现 Tab 能按标签切换并展示公开作品。
- 创作 Tab 在移动端和桌面端都比普通 Tab 更醒目。
- 不修改 server-node不新增后端逻辑。

View File

@@ -1,64 +0,0 @@
# 平台创作 Tab 模板入口设计
更新时间:`2026-05-14`
## 1. 目标
创作 Tab 恢复为模板选择入口,但不回到旧的大卡片选择面板:
1. 首屏保留现有创作页布局骨架顶部标题固定为“10分钟创作一个精品互动玩法”。
2. 选择模板入口改为横向 Tab数据来自 `GET /api/creation-entry/config` 返回的可见玩法配置。
3. 默认选中“拼图”模板,并在创作 Tab 内直接展示拼图创作表单。
4. 智能创作入口从可见模板中隐藏,保留既有 `creative-agent` 运行链路用于后续内部恢复或草稿目标打开。
5. 草稿、发现、我的等一级 Tab 职责不变,作品管理仍在草稿 Tab。
## 2. 页面结构
移动端和桌面端共用同一信息结构:
```text
标题10分钟创作一个精品互动玩法
模板 Tab拼图 / 抓大鹅 / AIRP
默认内容:拼图创作表单
```
拼图表单嵌入创作 Tab 时:
- 不展示工作台返回按钮。
- 不重复展示“创建拼图”标题。
- 保留表单内的拼图模板、参考图上传、画面描述和生图模型选择。
- 生成草稿仍走登录保护,未登录时先触发登录流程。
## 3. 交互
1. 打开“创作”一级 Tab 时默认停留在拼图 Tab不主动创建拼图 session。
2. 点击拼图表单“生成草稿”后,才创建拼图 session 并执行 `compile_puzzle_draft`
3. 拼图表单内的模板按钮使用 `tablist / tab` 语义,点击后只填充画面描述。
4. 点击非拼图且已开放的模板 Tab 时进入该玩法既有工作台AIRP 当前保持敬请期待禁用态。
5. `visual-novel` 暂时从创作页完全隐藏,不出现在模板 Tab、旧选择弹层和创作 Hub 卡片中;既有作品、详情、运行链路继续保留。
6. `creative-agent` 不出现在模板 Tab 和选择弹层中,不再作为创作 Tab 首屏入口。
7. 方洞挑战暂时从创作页完全隐藏,不出现在模板 Tab、旧选择弹层和创作 Hub 卡片中;既有作品链路继续保留。
## 4. 验收
1. 点击“创作”后首屏出现“10分钟创作一个精品互动玩法”。
2. 顶部选择模板入口为 Tab拼图 Tab 默认 `aria-selected=true`
3. 创作 Tab 默认显示拼图创作表单内容且不显示旧“Hi, 朋友”、输入框或智能创作快捷按钮。
4. 隐藏的智能创作类型、视觉小说与方洞挑战不出现在模板 Tab、旧选择弹层和创作 Hub 卡片中。
5. 草稿页返回创作页后仍回到同一模板入口,并可保留拼图表单草稿内容。
## 5. 嵌入式表单 UI 细节
2026-05-10 补充:抓大鹅与视觉小说作为创作 Tab 内嵌表单时,风格类横滑选择器应统一使用浅底卡片、柔和玫瑰色选中态和小圆点确认标记。不要使用大面积黑色渐变、黑底胶囊标签或高饱和红色外框,以免在输入框下方误读为错误提示。
2026-05-10 追加:视觉小说画风选项已改为使用 `public/visual-novel-style-references/` 下由 VectorEngine `gpt-image-2-all` 生成的参考图,作为横向卡片主视觉。
嵌入式表单控件保持以下口径:
1. 大文本输入框使用白底、低饱和边框和轻量 focus ring。
2. 风格选择区作为独立浅色分组承载横滑卡片,移动端只横向滚动,不挤压生成按钮。
3. 风格卡标签使用浅底胶囊,保证图片仍是主体。
4. 难度等分段选项可以使用主品牌色,但选中态需要降低阴影和饱和度。
5. UI 中不补充玩法规则说明文案,保持创作入口清爽。
6. 拼图创作表单在未上传主图时,画面描述输入框右下角保留一个参考图上传入口;支持多选,最多 5 张,上传后以下方小缩略图展示,点击缩略图可放大预览。
7. 当前拼图后端只消费第一张有效参考图做生成,前端仍需保留数组输入,方便后续扩展多参考图能力。

View File

@@ -1,82 +0,0 @@
# 平台首页分类入口与排行 Tab 调整设计
更新时间:`2026-04-29`
## 1. 本次目标
1. 首页移动端频道只保留“推荐、今日游戏、游戏分类”删除“PC游戏、即点即玩”。
2. 原底部“分类” Tab 改为“排行” Tab不再单独承载分类页。
3. 原分类 Tab 的标签筛选移动到首页移动端“游戏分类”频道中,作品展示从双列网格改为应用商店式纵向列表。
4. 排行页参考榜单式纵向布局,提供热门榜、改造榜、新品榜、点赞榜四个榜单切换。
5. 页面继续使用平台主题变量、现有字号层级与卡片组件,避免新增大段功能说明文案。
## 2. 数据口径
当前公开作品聚合列表已经透传后端读模型字段:
- `playCount`:历史游玩次数。
- `remixCount`:历史改造次数。
- `likeCount`:历史点赞次数。
- `recentPlayCount7d`:近 7 日新增游玩次数。
- `publishedAt / updatedAt`:发布时间或更新时间。
本次新增 `public_work_play_daily_stat` 日桶读模型,所有公开玩法的正式游玩入口在累加历史 `playCount` 时同步写入该表。公开列表返回时按作品聚合最近 7 个 UTC 自然日的 `recentPlayCount7d`,前端只负责展示与排序。
1. 热门榜按 `playCount` 降序。
2. 改造榜按 `remixCount` 降序。
3. 点赞榜按 `likeCount` 降序。
4. 新品榜按 `recentPlayCount7d` 降序。
## 3. 交互规则
### 3.1 首页移动端
- 顶部搜索框保持不变。
- 频道横滑 Tab 顺序为:推荐、今日游戏、游戏分类。
- 推荐展示精选与最新去重后的作品流。
- 今日游戏只展示 `publishedAt` 落在玩家当前浏览器自然日内的新发布公开作品;跨日旧作品即使仍在最新列表前排,也不能进入该频道。
- 游戏分类展示原分类页内容:筛选胶囊 + 横向标签 + 当前标签下纵向作品列表。
- 游戏分类列表参考移动应用商店结构,不再使用双列卡片:左侧方形封面,中间为作品名、状态角标、评分/题材、摘要或热度短句,右侧为“启动/试玩”主按钮。
- 分类频道的筛选区只保留短标签,不写功能说明文案;筛选按钮展示当前标签数量,横向标签展示可切换的分类入口。
### 3.2 底部导航
- 登录态:`首页 / 排行 / 创作 / 存档 / 我的`
- 未登录态:`首页 / 创作 / 排行`
- 底部排行入口仍复用原 `category` Tab 的路由值,减少导航状态迁移风险,但所有用户可见文案改为“排行”。
### 3.3 排行页
- 顶部为横向榜单 Tab热门榜、改造榜、新品榜、点赞榜。
- 下方为纵向榜单列表,每行展示排名、封面、作品名、榜单指标、玩法类别、两个标签与进入按钮。
- 公开作品名称在列表与卡片中统一限制为最多 8 字;公开作品标签统一限制为最多 4 字。
- 排行榜单条目正文固定为三行:第一行作品名,第二行榜单数据与玩法类别,第三行展示两个标签;不再显示发布时间、作者名等第四行信息。
- 无数据或加载中沿用现有短空态文案。
## 4. 编码落点
- `src/components/rpg-entry/RpgEntryHomeView.tsx`
- 精简首页频道枚举。
- 增加排行榜单构造、榜单切换状态与榜单行组件。
- 将分类内容移动到移动端首页“游戏分类”频道。
- 增加游戏分类纵向列表条目组件,替换移动端分类频道的双列作品网格。
- 将底部/桌面侧边导航文案从“分类”改为“排行”。
- `src/index.css`
- 增加榜单行、榜单切换按钮、游戏分类筛选栏和纵向列表条目的主题化样式。
- `server-rs/crates/spacetime-module/src/runtime/profile.rs`
- 增加公开作品每日游玩统计表与 7 日聚合 helper。
- `server-rs/crates/spacetime-module/src/migration.rs`
- migration 表清单对齐 `public_work_play_daily_stat`
- `server-rs/crates/shared-contracts/src/*_works.rs``packages/shared/src/contracts/*`
- 公开作品响应补齐 `recentPlayCount7d`
## 5. 验收点
1. 移动端首页不再显示“PC游戏、即点即玩”。
2. 点击首页“游戏分类”能看到原分类标签与作品列表。
- 移动端分类作品必须为纵向列表,不能回退为两列网格。
- 单条作品在 390px 宽度下必须保持封面、标题、按钮同一行可扫读,摘要截断且不挤压右侧按钮。
3. 点击首页“今日游戏”只显示当天新发布作品;仅更新时间为今天但发布时间不在今天的作品不能进入今日频道。
4. 底部导航显示“排行”,不再显示“分类”。
5. 排行页可切换四个榜单,排序口径符合当前字段约束。
6. 不修改 server-node不新增 PostgreSQL 相关实现。

View File

@@ -1,91 +0,0 @@
# 平台首页移动端信息流与作品卡设计
更新时间:`2026-04-28`
## 1. 本次目标
1. 桌面端首页布局保持现有顶部栏、侧边导航、Hero、趋势区与下方网格结构不调整桌面端区块顺序。
2. 移动端首页改为参考图式信息流:顶部搜索框、横向频道 Tab、纵向作品列表、底部主导航。
3. 双端公开作品卡统一结构:上方 `16:9` 封面图,下方作品名称、作者信息、作品描述与玩法类型。
4. 点赞数必须来自作品读模型字段,前端只负责展示,不把游玩数或评分临时改名成点赞。
## 2. 数据契约
### 2.1 统一字段
公开作品卡和创作中心复用的作品摘要都增加:
```ts
likeCount: number;
```
当前阶段只做只读展示,不新增点赞按钮和点击 reducer。后端对尚未接入真实点赞表的作品返回 `0`,保证接口 shape 稳定,后续可无 UI 结构迁移地接入真实互动计数。
### 2.2 各玩法映射
1. RPG 公开广场:`CustomWorldLibraryEntry``CustomWorldGalleryCard` 返回 `likeCount`,当前由 Rust facade 返回 `0`
2. 拼图公开广场:`PuzzleWorkSummary` 返回 `likeCount`,当前由 Rust facade 返回 `0``playCount` 继续仅表示游玩次数。
3. 大鱼公开广场:`BigFishWorkSummary` 返回 `likeCount`,当前由 Rust facade 返回 `0``playCount` 继续仅表示游玩次数。
4. 前端聚合类型 `PlatformPublicGalleryCard` 透传 `likeCount``WorldCard` 不再依赖 `badge/metaLabel` 决定主要信息结构。
### 2.3 首页读链路核对
首页公开作品流的读取链路固定为:
```text
RpgEntryHomeView
→ platformPublicGalleryClient / puzzleGalleryClient / bigFishGalleryClient
→ Rust api-server
→ spacetime-client 生成绑定
→ spacetime-module procedure
→ SpacetimeDB 表
```
1. 公开读取必须匿名可用,前端 `GET` 列表与详情统一传 `skipAuth: true``skipRefresh: true`,避免未登录首页被刷新 token 链路阻断。
2. 拼图公开广场走 `list_puzzle_gallery` / `get_puzzle_gallery_detail`,返回 `coverImageSrc``summary``themeTags``playCount``remixCount``likeCount`
3. 大鱼公开广场走 `list_big_fish_works(published_only=true)`;由于部分已部署模块会在公开列表分支前仍校验 `owner_user_id` 非空,客户端与模块内部公共列表输入都使用 `public-big-fish-gallery` 占位 owner。该字段在 `published_only` 分支不参与筛选,只用于兼容旧校验。
4. 自定义世界公开广场走 `list_custom_world_gallery_entries`,当前主云数据为空时应返回成功空列表,而不是错误态。
### 2.4 作者头像读取策略
1. 首页“推荐”和“今日游戏”作品卡不把 `avatarUrl` 固化到作品读模型里,避免作者修改头像后旧作品卡继续展示过期头像。
2. 前端首页在聚合公开作品后,按作品摘要中的 `authorPublicUserCode` 优先读取公开用户摘要;没有公开用户码的玩法摘要,使用 `ownerUserId` 读取公开用户摘要。
3. 作者头像查询必须走匿名公开接口,并在首页组件内按作者维度缓存;单个作者在推荐与今日游戏中重复出现时不能重复请求。
4. 公开用户摘要返回 `avatarUrl` 时,作品卡展示真实头像;头像缺失、读取失败或作者身份字段缺失时,继续使用作者昵称首字占位。
## 3. 移动端布局
1. 移动端首页只在 `RpgEntryHomeView` 的 mobile content 内重排。
2. 第一屏顺序:
- 搜索框
- 频道横滑 Tab推荐、今日游戏、游戏分类、PC游戏、即点即玩
- 作品信息流
3. 作品信息流使用单列纵向列表,卡片宽度填满容器,卡片之间保留短间距。
4. 不新增功能说明类长文案;空态仍沿用现有短状态文案。
5. 移动端卡片视觉允许接近参考图的深色信息流,但仍走平台主题 token避免写死不可维护的大面积色块。
## 4. 作品卡结构
每张公开作品卡固定为:
1. 封面区域:`aspect-ratio: 16 / 9`,图片 `object-cover`;无封面时使用轻量主题底。
2. 封面左上角不展示“推荐”标签,也不展示作者昵称标签,避免遮挡作品主视觉。
3. 封面右下角展示三项轻量指标:游玩、改造、点赞;统一为图标 + 紧凑数字,例如 `128``1.2万`,不写额外说明长文案。
4. 信息区域:
- 第一行作品名称右侧展示玩法类型。拼图玩法展示“拼图”大鱼玩法展示“大鱼”RPG 作品展示题材短标签。
- 第二行:原副标题位置展示作者头像和昵称。当前公开列表只返回作者昵称时,头像使用昵称首字生成的轻量头像;后续接入作者头像 URL 后复用同一位置。
- 第三行:作品描述,两行截断。
- 第四行:最多三个标签。
5. 点赞数仍必须来自作品读模型字段,只是展示位置从信息区右侧迁移到封面右下角。
6. 不展示作品号;作品号仍只在详情页或分享路径中使用。
## 5. 验收
1. 390px 移动端首页不横向溢出,能看到搜索、频道和纵向作品列表。
2. 桌面端首页布局区块顺序不变,只替换公开作品卡内部结构。
3. RPG、拼图、大鱼三类公开作品卡都有 `likeCount` 字段,前端聚合后能统一展示。
4. 运行编码检查、前端定向测试和必要的 Rust 检查。
5. HTTP 验收需覆盖:
- `GET /api/runtime/custom-world-gallery` 成功返回 `entries`
- `GET /api/runtime/puzzle/gallery` 成功返回 `items` 且包含 `likeCount`
- `GET /api/runtime/big-fish/gallery` 成功返回 `items`,旧部署模块不再因 `big_fish.owner_user_id 不能为空` 阻断首页。

View File

@@ -1,229 +0,0 @@
# 平台首页公开浏览与登录弹窗拦截设计
更新时间:`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.2.1 登录页签落地约束
账号面板需要把短信验证码登录和密码登录拆成互斥页签,避免两个登录表单在同一个面板里上下堆叠。
- 同时开放短信与密码登录时,面板顶部展示两个居中的文字页签,当前页签使用深色字重和短下划线强调。
- 只渲染当前页签对应的输入区;切换页签不弹出新面板,不展示二维码入口。
- `短信登录` 页签包含手机号、验证码、获取验证码和主按钮。
- `密码登录` 页签只包含手机号、密码、主按钮和忘记密码入口;不支持邮箱、用户名或陶泥号。
- 密码登录只是手机号验证码登录的补充方式:只有已登录并设置过密码的手机号账号才能使用,不能在密码页签创建账号。
- `密码登录` 主按钮固定为 `登录`,不得使用 `注册/登录`
- 未开放某个登录方式时不展示对应页签,避免用户进入不可用表单。
- 移动端页签保持等分点击区域,输入框与按钮宽度仍随弹窗收缩。
## 3.3 登录成功后的行为
- 手机号登录成功后,关闭弹窗
- 当前平台页面不刷新
- 若用户是被某个受保护动作拦截进入登录,则自动恢复该动作
- 若用户只是主动点“登录”按钮,则关闭弹窗并停留在当前页面
## 3.4 关闭行为
- 用户主动关闭弹窗时,只关闭弹窗,不改变当前平台页面
- 不清空首页浏览状态
- 不自动跳转到其他 tab
- 登录弹窗下次重新打开时必须恢复初始表单状态:回到默认登录页签、关闭重置密码面板、清空密码 / 验证码 / 图形验证码 / 提示 / 倒计时等本次草稿状态;只允许保留“最近一次成功登录手机号”的本地回填能力。
---
## 4. 前端状态约束
## 4.1 AuthGate
`AuthGate` 需要从“未登录整页拦截器”调整为“平台级账号状态提供器”:
- `checking / recovering`:仍可显示加载态,避免首屏闪烁
- `unauthenticated`:渲染平台内容,同时允许按需打开登录弹窗
- `ready`:渲染平台内容和账号能力
- `pending_bind_phone`:继续保留当前绑定手机号流程,不在这次入口改造里拆散
首屏之后的鉴权刷新补充约束:
- 平台内容已经渲染后,后续 access token 刷新、401 后重试、账号状态后台重算不能再把整棵平台应用卸载成 `checking / recovering` 加载页。
- 后台鉴权重算期间需要保持当前平台页与主 Tab 状态,避免用户手动切到“创作 / 存档 / 我的”后因为鉴权事件闪屏回到首页。
同时需要在 context 中提供:
- 当前用户
- 打开登录弹窗
- 打开账号面板
- `requireAuth(action)` 能力
`requireAuth(action)` 约束:
- 已登录:直接执行 `action`
- 未登录:弹出登录弹窗,并缓存 `action`
- 登录成功:自动执行缓存的 `action`
账号入口补充约束:
- 不再提供 `AuthGate` 层右上角固定悬浮的全局登录 / 账号信息入口
- 登录触发统一来自页面内受保护动作、个人页、存档页等明确入口
- 账号信息面板只通过页面内按钮打开,不在平台右上角常驻悬浮
- 未登录移动端底部导航不展示“我的”时,平台页头必须保留一个直接可点的 `登录` 入口,避免用户只能通过受保护动作被动触发弹窗
- 桌面端平台页头的账号胶囊在未登录时主文案必须直接显示 `登录`,不能只显示“进入账户”这类弱入口
## 4.2 平台首页数据加载
`PreGameSelectionFlow` 在未登录时只读取:
- 公开作品广场
- 本地浏览历史
公开作品广场前端请求约束:
- `listCustomWorldGallery`
- `getCustomWorldGalleryDetail`
这两类公开请求必须走“公开只读请求”通道:
- 不主动附带 `Authorization`
- 不因本地 access token 失效去触发 `/api/auth/refresh`
- 若当前请求本身没有携带 access token也不允许因为返回 `401` 就额外触发 `/api/auth/refresh`
- refresh cookie 缺失、refresh 失败、账号状态过期时,不能把首页公开作品广场一起拖成错误态
受保护工作区恢复补充约束:
- 若 URL 或 `sessionStorage` 中残留 `customWorldSessionId``customWorldOperationId` 等共创工作区恢复标记,未登录态下不能直接请求对应的 Agent 会话或操作接口
- 这类“恢复共创工作区”场景要先弹出登录弹窗,登录成功后再恢复原本的工作区或操作上下文
- 用户未登录且关闭登录弹窗时,前端保持平台页可浏览状态,不允许持续轮询受保护接口
未登录时不读取:
- 自定义世界库
- 个人看板
- 云端浏览历史
- 云端运行时设置
- 云端存档快照
- 云端存档列表
未登录态的对应前台表现:
- “我的创作”显示空态,不显示账号接口错误
- “个人页”显示未登录态入口,可手动打开登录弹窗
- 音量等运行时设置继续使用本地缓存,不触发 `/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. 未登录态首页不会因个人接口失败而出现“读取个人看板失败”“读取作品库失败”之类报错。
6. 未登录移动端首页页头存在明确 `登录` 入口,点击后打开同一个登录弹窗。

View File

@@ -1,35 +0,0 @@
# 平台首页响应式布局优化设计
更新时间:`2026-04-24`
## 1. 问题结论
当前平台首页桌面端视觉方向成立但移动端存在明显横向溢出Hero 右侧按钮、底部导航末项和部分卡片会被裁切。问题根因不是数据逻辑,而是桌面式固定宽度、外层 padding 与若干卡片最小宽度在窄屏下叠加,超过了 `100vw`
## 2. 本次目标
- 移动端优先保证首页不横向滚动、不裁切底部导航。
- Hero 在手机宽度下改为紧凑单列,标题、按钮和标签都完整显示。
- 桌面端降低外框视觉噪声,让主内容和 CTA 更清晰。
- 空状态区域收敛高度,避免首屏出现大面积空白。
## 3. 编码落点
- `src/components/rpg-runtime-shell/RpgRuntimeStageRouter.tsx`
- 平台壳移动端使用更小边距,避免根容器加宽。
- `src/components/rpg-entry/RpgEntryHomeView.tsx`
- 移动端根节点显式 `min-w-0``overflow-hidden`
- 底部导航外层不再额外制造宽度。
- Hero/内容卡片补充 `min-w-0`,窄屏内按容器收缩。
- `src/index.css`
- 全局与平台壳禁止横向溢出。
- 移动端压缩底部导航间距、字号和图标壳尺寸。
- 移动端收敛 `platform-page-stage` 圆角与边框层级。
- 桌面端保留卡片质感,但弱化多层外框阴影。
## 4. 验收标准
- `390px` 宽度截图中首页内容、Hero 按钮和底部四个导航项完整可见。
- 桌面端首页仍保持顶部栏、侧边导航、主推荐区和右侧趋势区结构。
- 页面不新增功能说明类 UI 文案。
- 修改中文文件后通过编码检查。

View File

@@ -1,132 +0,0 @@
# 平台移动端推荐、发现与草稿 Tab 改版设计
更新时间:`2026-05-05`
## 1. 目标
本次只调整平台入口的信息架构与移动端视觉,不新增后端接口:
1. 原“首页”一级 Tab 对用户改名为“推荐”,进入后直接展示原首页推荐榜单启动后的公开游戏内容流。
2. 原“首页”中的搜索、今日游戏、游戏分类等探索内容移动到第二个一级 Tab第二个 Tab 对用户命名为“发现”。
3. 原“排行”页内容并入“发现”页的子 Tab 中,不再作为一级主 Tab 独立展示。
4. 创作页只保留新建创作入口;原创作页作品列表拆到一级“草稿” Tab替换原“存档” Tab。
5. 原“存档”列表结构并入“我的”页面的“玩过”列表弹层,作为每个已玩作品的可继续存档入口。
6. 移动端推荐页与底部 Tab 栏参考用户给定样式,使用大画面推荐流、顶部品牌与悬浮胶囊底部导航;右上角不保留通知按钮,账号相关入口统一进入“我的”和账号面板;保留当前平台已有明暗两套主题色 token。
## 2. 状态映射
为降低迁移风险,前端内部 `PlatformHomeTab` 仍复用既有状态值:
- `home`:用户看到“推荐”。
- `category`:用户看到“发现”,内容包含搜索、今日游戏、分类和排行子 Tab。
- `create`:用户看到“创作”,只承担新建入口。
- `saves`:用户看到“草稿”,承载原创作页作品列表。
- `profile`:用户看到“我的”,其中“玩过”弹层合并存档入口。
`category` 状态此前承载“排行”,本次不改状态名,只改用户文案和页面内容,避免详情页返回目标、测试辅助和历史路由状态大范围迁移。
## 3. 推荐页
移动端推荐页默认不展示搜索和频道横滑条,进入一级“推荐”后直接渲染公开作品启动后的内容:
- 数据来源沿用 `featuredEntries + latestEntries` 去重后的公开作品列表。
- 首个可运行作品自动进入推荐页内嵌运行态,主视口不再展示作品封面卡。
- 主视口占据顶部栏与作品信息区之间的主要空间,使用平台主题 token 控制运行容器背景、边框、阴影、加载态文字和错误态按钮;亮色主题不得残留纯黑底白字加载块。
- 主视口下方展示当前作品操作区与基础信息:作者头像、作者名、作品名、点赞按钮、点赞数、分享按钮、改造按钮;不展示游玩次数、评论入口或额外心形收藏入口,不写规则说明类文案。
- 作品信息区不再提供详情箭头或点击详情入口;点击该区域无效,上滑切换下一个推荐作品,下滑切换上一个推荐作品。
- 推荐页切换参考短视频上下滑交互:当前运行画布位于中间,上一个/下一个作品的封面预览提前挂载在画布上下屏幕外;拖动作品信息区时三屏轨道跟随位移,松手后完成切换或回弹。
- 用户停留在推荐页时,底部当前 Tab 从“推荐”切换为“下一个”,图标使用向下的倒三角 / 双下箭头语义,点击后切换下一个推荐作品。
- 推荐页不再展示额外的底部作品切换块;当前作品的点赞、分享与改造在推荐页底部操作区直接完成,其它作品深层操作继续由详情页和作品自身运行态承接。
- 推荐页嵌入运行只调整平台外壳容器、主题注入和玩法壳层配色,不改写作品数据、关卡设定、道具设定或图片资产。
- 屏幕外预览只允许使用公开封面、作品名和类型,不提前启动其它作品 run不触发道具、计时、存档或作品数据变更。
- 推荐页嵌入拼图玩法时隐藏拼图左上返回按钮,并在设置弹层中隐藏退出入口;作品切换前对当前拼图 run 执行既有“保存并退出”收口,正式 run 的交互状态以已写回后端的快照为准。
- 底部操作区移除游玩次数、评论功能和额外心形收藏入口;点赞按钮可直接点击并刷新公开读模型返回的点赞数,分享按钮复制作品号与公开作品链接,改造按钮只保留改造 icon 并复用既有改造入口。操作按钮只响应自身点击,按钮外的信息区点击无效,拖动时仍跟随整张推荐卡上下切换作品。
- 无数据、加载中、启动失败和暂不支持内嵌运行的作品沿用短状态文案。
桌面端仍保持现有首页布局,只把一级导航文案从“首页”改为“推荐”。
## 4. 发现页
发现页承接原首页探索能力和原排行能力,子 Tab 为:
1. 推荐:原首页推荐内容流,可作为发现页内的快速回看。
2. 今日:原首页“今日游戏”。
3. 分类:原首页“游戏分类”。
4. 排行:原一级“排行”页四榜切换。
移动端发现页顶部保留搜索框和子 Tab分类内容继续使用纵向应用商店式列表排行内容继续使用榜单行。
## 5. 创作与草稿
创作页只保留新建创作入口:
- 继续复用 `CustomWorldCreationStartCard` 和现有创作类型弹窗。
- 不在创作页下方展示作品列表,避免“创作入口”和“作品管理”挤在同一首屏。
草稿页复用创作中心作品架:
- 默认显示全部作品列表,保留草稿/已发布筛选。
- 入口、删除、分享、领取拼图激励等行为全部复用现有 `CustomWorldCreationHub` 的作品卡逻辑。
- 一级底部 Tab 文案为“草稿”,内部仍可按草稿与已发布筛选。
- 作品列表必须按最后修改时间倒序排列;排序使用后端 `updatedAt`,前端需要兼容 ISO 字符串和 `seconds.microsZ` 两种时间文本。
- 每张作品卡以作品封面图铺满整卡背景,并叠加遮罩保证标题和描述可读;遮罩不能把封面洗成普通面板底色。缺 `coverImageSrc` 但同一作品已有正式关卡图、背景图或素材图时,优先用这些真实作品图兜底;若封面签名失败或作品还没有可用图片,再按玩法使用 `public/creation-type-references/` 的参考图兜底,最终无图底色也必须保持百梦浅粉暖色调,不能回退为黑色卡片。
- 草稿页卡片不展示“最后修改时间”“更新于”或原始 `updatedAt` 文本,时间只参与排序。
## 6. 我的页玩过列表
“我的”页面的“玩过”列表弹层合并存档结构:
- 顶部仍展示总游戏时长。
- 列表先展示可恢复存档,使用原 `SaveArchiveCard` 的字段结构和恢复行为。
- 再展示已玩作品统计列表,保持作品号、最近时间和时长。
- 若某个存档被点击,必须继续走既有后端恢复接口,不在前端拼接运行态。
## 7. 验收
1. 移动端底部导航非推荐页显示“推荐 / 发现 / 创作 / 草稿 / 我的”,未登录时显示“推荐 / 创作 / 发现”;停留在推荐页时当前 Tab 显示“下一个”。
2. 点击“推荐”直接看到公开作品启动后的内容,不再先看到搜索框、频道 Tab 或封面卡流。
3. 点击“发现”可看到搜索、推荐、今日、分类、排行子 Tab。
4. 点击“草稿”看到原创作页作品列表。
5. 点击“创作”只看到新建创作入口。
6. “我的”里的“玩过”弹层包含原存档列表入口,点击存档能继续恢复。
7. 移动端底部导航为悬浮胶囊样式,保留当前明暗主题色变量,不新增第三套主题。
8. 推荐页不出现额外底部作品卡或横滑切换块,运行视口、加载态和错误态跟随当前明暗主题。
9. 拼图玩法背景、HUD、按钮、弹窗、排行榜和相似作品卡跟随平台主题色暗色主题仍保留深色游戏感亮色主题不得出现大面积固定黑底。
10. 推荐页作品信息区点击无效,上滑切下一个、下滑切上一个;点击底部“下一个”也切下一个作品。
11. 仅推荐页嵌入拼图态隐藏返回与设置内退出入口;详情页、新手引导和普通拼图运行态继续保留原有退出能力。
12. 推荐页切换作品前,如果当前作品是拼图,必须先执行当前拼图 run 的退出收口,再启动下一作品。
13. 已登录推荐页的上/下滑切换必须展示相邻作品的屏幕外预览,并在拖动不足阈值时回弹;相邻预览不得提前启动玩法运行态。
14. 推荐页底部区域不展示游玩次数、评论入口和额外心形收藏入口;点赞、分享和改造都使用 icon 按钮,点赞数紧邻点赞按钮展示。
## 8. 2026-05-07 未登录三栏补充
未登录状态下底部导航只显示 3 个入口,顺序调整为:
1. 推荐
2. 创作
3. 发现
创作 Tab 必须位于中间,并使用原推荐 Tab 的星光图标,保持几何和视觉上的主行动入口。推荐 Tab 改用游戏手柄图标,避免与创作图标重复。
## 9. 2026-05-08 新用户默认发现与推荐门禁补充
未登录新用户首次进入平台时默认落在“发现”Tab不再直接进入“推荐”Tab 的内嵌运行态。
- 未登录用户点击底部或侧边栏“推荐”Tab 时,页面可切到推荐封面预览态,同时打开登录弹窗。
- 未登录状态下推荐页只展示当前推荐作品封面,不启动作品运行态,不展示推荐作品信息区。
- 未登录用户点击推荐页封面时,再次打开同一个登录弹窗;登录成功后由既有受保护动作继续进入作品详情或玩法入口。
- 未登录状态下点击“下一个”只切换下一张推荐封面,不触发登录弹窗,也不启动玩法。
- 已登录用户继续沿用推荐页内嵌运行态、上下滑切换和底部“下一个”行为。
## 10. 2026-05-11 草稿生成中与新完成标记
草稿生成过程页允许用户直接返回创作中心并自由使用平台其它功能:
- 点击生成过程页的返回按钮时,当前生成任务继续在后台执行,页面回到创作中心,不清空生成状态。
- 用户再进入草稿 Tab 并点击同一草稿时,若生成仍未完成,进入对应生成过程页查看最新进度;若已完成,直接进入对应结果页。
- 草稿作品卡在生成中展示“生成中”状态标记;新生成完成且用户尚未查看的草稿在卡片右上角展示红点。
- 底部一级“草稿”Tab 在存在未查看新完成草稿时展示红点用户点击查看带红点的作品后该作品红点消失。若草稿页已无任何带红点作品底部“草稿”Tab 红点同步消失。
- 红点通知链路覆盖所有进入草稿作品架的生成型玩法;宝贝识物等仅有 `profileId``draftId` 的轻量草稿,也必须把两个 ID 都纳入同一组通知 key避免卡片红点和底部草稿 Tab 红点漂移。
- 生成完成时如果用户仍停留在对应生成过程页,可自动进入结果页;如果用户已经回到创作中心或其它功能页,不打断当前操作。
- 创作 Tab 的模板入口只允许被模板自身的开放状态禁用;某个草稿后台生成中时,不得用该玩法的 busy 状态禁用其它模板入口、同模板再次创建入口或阻止用户继续创建新作品。
- 同模板再次点击生成时必须创建新的草稿生成任务,不得因为当前玩法已有后台生成 session 就跳回上一条草稿的生成过程页;查看上一条生成进度只能从草稿 Tab 的对应作品卡进入。

View File

@@ -1,119 +0,0 @@
# 平台层 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`
- 品牌中文主标固定显示为 `陶泥` + 半字号 `儿``儿` 必须继承主标同一字体和字号后再视觉缩放到 50%,并与 `陶泥` 底部对齐,随移动端和场景覆盖等比例缩放
- 品牌字标默认保持正常像素字观感,禁止再叠双层粗阴影或手动加粗到影响识别
- 品牌字标直接使用字体文件内原字形,不额外做运行时描字、轮廓拼字或伪粗体处理
- 主标题保留明显层级,但不再做像素描边效果
- 微型标签维持高字距英文/中文短标签,用来保留产品感和秩序感
### 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

@@ -1,106 +0,0 @@
# 平台统一作品详情页与 Remix 数据链路设计
更新时间:`2026-05-01`
## 1. 本次目标
1. 平台首页、公开广场、分类列表中的每个公开作品点击后,统一先进入作品详情页,不再直接启动玩法。
2. 作品详情页结构参考 TapTap 详情页:顶部封面图、作品基础信息、右侧“点赞”按钮、四项统计、简介内容、底部“作品改造 + 启动”同行动作。
3. 删除参考图顶部 Tab不接入评价和论坛功能不展示“开发者的话”模块。
4. 统计数据必须从数据库读模型贯穿到前端展示,禁止在前端用假字段、游玩数冒充点赞数或固定文案代替真实字段。
5. Remix 按钮必须由后端事务复制公开作品为当前用户草稿,并同步增加原作品改造次数,成功后前端进入新草稿详情/结果页。
## 2. 详情页 UI 结构
统一详情页只做作品展示与动作入口,不承担规则说明。
1. 顶部导航:返回按钮、标题“详情”、更多按钮占位;不展示“统计 / 详情 / 评价 / 论坛”Tab。
2. 封面区:固定 `16:9` 比例,默认使用作品封面图 `cover` 填满整块主视觉;背景可用同图弱化铺底;缺图时只显示平台主题底,不新增说明文字。拼图作品详情页若详情数据包含多个关卡图,则顶部封面区优先按关卡正式图轮播展示,每张图对应一个关卡;无可用关卡图时再回退作品封面图。拼图多关封面只默认展示第一张真实图,轮播节奏与左右切换保持不变;未解锁的后续封面必须使用同图毛玻璃模糊底和大问号图标遮罩,不能展示真实清晰图,也不能追加规则说明文字。玩家完成对应前置关卡后,当前详情页可按本次 `PuzzleRunSnapshot.clearedLevelCount + 1` 即时解锁可见封面数;刷新后持久化解锁应由后端从当前用户的拼图运行记录汇总到详情读模型,前端只消费读模型或当前 run 状态。
3. 移动端首页“推荐”和“今日游戏”列表中,只有最接近屏幕垂直中心的作品卡片进入封面轮播态;若该拼图作品有多张关卡封面,则按详情页同源封面序列自动轮换。用户滚动后,离开中心的旧卡片必须立即恢复首张封面,新中心卡片再开始轮播;“游戏分类”、排行、桌面端列表不启用该自动轮播。
4. 基础信息区:
- 左侧作品图标使用作品封面序列首图;顶部封面轮播切换时,该正方形图标保持首图不变,避免作品名称旁的身份标识跟随大图闪动。
- 中间展示作品名、作者头像、作者名、玩法类型;作者头像读取公开用户资料 `avatarUrl`,缺失时使用作者昵称首字占位。
- 右侧原 TapTap 评分位置替换为 `点赞` 按钮;点击后调用后端点赞接口,由后端记录当前登录用户对该公开作品的点赞关系并返回更新后的真实 `likeCount` 读模型,前端不伪造点赞增长。
5. 统计区固定四项:
- 游玩:`playCount`,显示为“数字 + 次”,单位放在数字后方。
- 改造:`remixCount`,显示为“数字 + 次”,单位放在数字后方。
- 点赞:`likeCount`,显示为“数字 + 赞”,单位放在数字后方。
- 日期:优先展示 `updatedAt`,缺失时回退 `publishedAt`;前端只负责格式化显示,必须兼容后端当前 `seconds.microsZ` 与 ISO 字符串两种真实时间文本,显示为完整 `YYYY-MM-DD`,使用更小字号并保持单行不换行。
- 四项统计需要使用浅色图标底强化识别,但不得追加规则说明类文案。
6. 简介区:展示玩法标签和作品简介;不追加说明类文案。
7. 底部动作:左侧按钮为“作品改造”,右侧主按钮为“启动”;两个按钮必须位于同一行,点击“启动”后进入对应玩法运行态并记录游玩次数。
- 未登录用户可进入并浏览作品详情页,但点击“作品改造”和“启动”都必须先弹出登录入口面板;登录成功后自动继续刚才点击的动作,不直接发起 Remix、启动 run 或本地运行态。
8. 页面配色必须跟随平台明暗主题变量;亮色主题使用平台浅色底、深色文字和主按钮渐变,暗色主题使用平台暗色底、亮色文字和对应主按钮渐变,不在详情页写死独立黑色皮肤。
9. 字号规范跟随平台页面既有节奏:标题/主按钮使用 `1rem` 级别,作品名使用卡片标题同级 `1rem`,辅助信息与简介使用 `0.8125rem` / `0.875rem`,标签与统计标签使用 `0.75rem`,避免在详情页使用随视口放大的独立大字号。
## 3. 数据真相源
### 3.1 RPG 作品
1. `custom_world_profile` 增加 `play_count``remix_count``like_count`
2. `custom_world_gallery_entry` 同步这三项统计,作为公开详情和首页卡片读模型。
3. `record_custom_world_profile_play` 负责在公开作品启动前递增 `play_count`,只更新已发布且未删除作品。
4. `record_custom_world_profile_like` 负责记录当前用户对公开作品的点赞;同一用户对同一公开作品只计入一次,首次点赞时递增 `like_count` 并同步刷新 `custom_world_gallery_entry`
5. `remix_custom_world_profile` 在同一事务内:
- 校验源作品已发布、未删除。
- 递增源作品 `remix_count` 并刷新源作品 gallery。
- 复制源 profile payload 为当前用户草稿,清空公开编号、发布时间与统计。
- 返回新草稿 profile供前端进入草稿详情页。
### 3.2 拼图作品
1. `puzzle_work_profile` 保留既有 `play_count`,新增 `remix_count``like_count`
2. `start_puzzle_run` 继续作为游玩次数递增入口。
3. `record_puzzle_work_like` 负责记录当前用户对公开拼图作品的点赞;同一用户对同一公开作品只计入一次,首次点赞时递增 `like_count`
4. `remix_puzzle_work` 在同一事务内:
- 校验源 profile 为已发布作品。
- 递增源作品 `remix_count`
- 新建当前用户拼图 Agent session并把源作品锚点、封面、简介复制为草稿。
- 新建当前用户草稿 profile统计归零返回新草稿 session 与 profile。
4. API facade 解析拼图 `item_json` / `items_json` 时必须兼容历史公开作品缺失 `play_count``remix_count``like_count` 的 JSON缺失值统一按 `0` 处理;新写入数据仍必须写全统计字段。
### 3.3 大鱼吃小鱼作品
1. `big_fish_creation_session` 现有 `play_count` 继续作为游玩统计,新增 `remix_count``like_count``published_at`
2. `publish_big_fish_game` 写入 `published_at``updated_at`,公开列表和详情优先用 `updated_at` 展示最近更新。
3. `record_big_fish_play` 继续作为游玩次数递增入口。
4. `record_big_fish_like` 负责记录当前用户对公开大鱼作品的点赞;同一用户对同一公开作品只计入一次,首次点赞时递增 `like_count`
5. `remix_big_fish_work` 在同一事务内:
- 校验源 session 为已发布作品。
- 递增源作品 `remix_count`
- 新建当前用户创作 session复制锚点、草稿和资源槽阶段回到可编辑草稿态。
- 新 session 的统计归零,返回新草稿 session。
## 4. API 与前端接入
1. 三类公开作品摘要统一返回:`playCount``remixCount``likeCount``publishedAt``updatedAt`
- 作者头像不固化到作品读模型;详情页按 `authorPublicUserCode``ownerUserId` 读取公开用户摘要中的 `avatarUrl`,确保头像跟随账号资料更新。
2. Remix API
- RPG`POST /api/runtime/custom-world-gallery/{owner_user_id}/{profile_id}/remix`
- 拼图:`POST /api/runtime/puzzle/gallery/{profile_id}/remix`
- 大鱼:`POST /api/runtime/big-fish/gallery/{session_id}/remix`
3. 点赞 API
- RPG`POST /api/runtime/custom-world-gallery/{owner_user_id}/{profile_id}/like`
- 拼图:`POST /api/runtime/puzzle/gallery/{profile_id}/like`
- 大鱼:`POST /api/runtime/big-fish/gallery/{session_id}/like`
- 三个接口都必须走登录态鉴权,后端使用当前登录用户身份写入点赞关系;重复点击返回当前最新读模型,不重复增加 `likeCount`
4. 前端统一详情页只消费读模型字段,不自行派生统计。
5. 首页卡片点击只设置统一详情状态;启动、点赞与 Remix 只能在详情页触发。
6. Remix 成功后的跳转:
- RPG进入复制出的草稿详情。
- 拼图:进入复制出的拼图结果页草稿。
- 大鱼:进入复制出的大鱼结果页草稿。
7. 拼图作品详情页启动时复用当前详情页已经展示的公开作品读模型,直接调用 `POST /api/runtime/puzzle/runs` 记录游玩并进入运行态;不得在启动前额外依赖 `GET /api/runtime/puzzle/gallery/{profile_id}`,避免开发代理或详情读取短断点阻塞启动链路。
8. 本地开发时 `localhost:3000` 是 Vite 前端端口,`/api/**` 默认代理到 Rust `api-server:3100`;若 3100 未监听,点击启动、点赞或作品改造会在浏览器显示 `/api/... 500`,此时真实断点是 Rust 后端未启动,不允许用前端假数据替代后端事务。
## 5. 验收点
1. 三类作品从首页点击均先进入统一作品详情页。
2. 详情页无评价、论坛 Tab无开发者的话模块。
3. 四项统计在前端、共享契约、API facade、SpacetimeDB 表之间字段一致。
4. Remix 后原作品改造次数增加,新草稿归当前用户所有,且不会继承源作品统计。
5. 点赞公开作品会走对应后端记录入口,首次点赞后刷新仍能看到递增后的点赞次数,重复点赞不会继续增加。
6. 启动公开作品会走对应后端记录入口,刷新后仍能看到递增后的游玩次数。
7. 未登录进入作品详情页后,点击“作品改造”和“启动”只打开登录入口面板;登录成功后恢复对应动作,未登录期间不会创建 Remix 草稿、开始拼图 run、记录 RPG 游玩或启动大鱼本地运行态。
8. 移动端首页“推荐”和“今日游戏”列表滚动时,仅中心卡片自动轮播多封面;旧中心卡离开后回到首张封面,新的中心卡接续轮播。
9. 修改后运行编码检查、SpacetimeDB 绑定生成、Rust 检查和必要前端测试。

View File

@@ -1,108 +0,0 @@
# 拼图运行时顶栏与通关演出调整设计
## 背景
现有拼图运行时存在 4 个体验问题:
1. 顶栏把 `PUZZLE``3*3 / 4*4` 这类系统标签直接暴露给玩家,主信息不够聚焦。
2. 拼图块右下角数字会破坏原图识别,影响拼图沉浸感。
3. 拼图运行时右上角缺少与 RPG 一致的设置入口,玩家不能在玩法内直接调整音量或处理中途退出。
4. 通关后立即弹结算弹窗,会打断玩家先看到完整成图的奖励时刻。
## 本次落地结论
### 1. 顶栏信息层级
拼图运行时顶栏统一改成三段结构:
1. 左侧保留返回按钮。
2. 中间居中展示关卡主信息:
- 第一行:`第 N 关` 与拼图关卡名,二者保持同一行。
- 第二行:紧凑倒计时。
3. 右侧新增设置按钮。
同时移除以下冗余标识:
1. `PUZZLE`
2. `3*3 / 4*4`
网格规模仍可作为运行时内部状态存在,但不默认写在 UI 顶栏中。
### 1.1 2026-04-30 顶栏与底部工具补充
1. 历史口径曾要求顶栏展示作者头像与昵称;该要求已被 2026-05-08 精简口径替代,当前拼图棋盘 HUD 不再展示作者信息。
2. 倒计时组件保持为顶栏中的强信息,但需要采用紧凑尺寸,不得遮挡棋盘内容。
3. 底部只保留 `提示 / 原图 / 冻结` 三个功能按钮,并整体居中展示;三个按钮触控面积和图标字号都需要放大。
4. 底部不再展示“等待下一关候选”这类状态占位。通关后在三个道具按钮上方固定展示“下一关”按钮,展示条件只依赖当前关卡已通关,不依赖 `recommendedNextProfileId` 是否已有值。
5. 点击底部“下一关”按钮继续调用运行时壳层已有 `onAdvanceNextLevel` 事件;正式 run 由后端 `next-level` 选择候选,本地 run 由 `local-next-level` 生成或接续下一关,前端不在按钮层自行决定下一关来源。
### 1.2 2026-05-08 推荐页嵌入态 HUD 精简
1. 拼图运行时顶栏不再展示作者头像、作者昵称或作者首字占位,作者信息只在推荐页作品信息、详情页和排行榜等非棋盘 HUD 区域展示。
2. 顶部主信息压缩为两行:第一行 `第 N 关 + 关卡名`,第二行小号倒计时;倒计时不能使用此前的大号胶囊尺寸。
3. 返回按钮和设置按钮上移并缩小移动端基础尺寸,减少对棋盘顶部空间的占用。
4. 棋盘容器必须保留固定顶部安全区,确保关卡名和倒计时不会遮挡拼图内容。
5. 推荐页嵌入时只调整外壳间距和 HUD 布局,不改写作品关卡、作者、道具、时间限制或图片资产等作品设定。
### 1.3 2026-05-08 主题色联动
1. 拼图运行态根容器、背景、棋盘底色、顶部 HUD、底部工具栏、加载态、失败弹窗、通关弹窗、道具确认弹窗、设置弹窗和相似作品卡必须通过 `src/index.css` 中的 `--puzzle-runtime-*` 主题变量控制。
2. `platform-theme--light` 下拼图玩法背景应使用浅色平台底色与粉橙主色点缀,文字使用平台正文 token不得继续使用固定 `bg-slate-950 text-white` 作为大面积底色。
3. `platform-theme--dark` 下可保留深色棋盘氛围,但按钮、选中态、倒计时、弹窗边框和推荐页加载态仍要从主题 token 取色,避免局部色板漂移。
4. 推荐页内嵌拼图时,父级必须保持 `platform-theme` 类可传递到 `PuzzleRuntimeShell`,不能让 runtime 脱离平台主题变量。
### 1.4 2026-05-08 推荐页嵌入态退出控制
1. 推荐页嵌入拼图时需要单独隐藏左上返回按钮和设置面板里的退出入口,避免把推荐流里的作品切换与普通退出混在一起。
2. 推荐页切换到下一作品前,平台外壳会先沿用现有“保存并退出”语义收口当前拼图 run再进入新作品。
3. 该隐藏规则只作用于推荐页嵌入态,不影响详情页、新手引导或普通拼图入口。
### 2. 拼图块显示规则
运行时单块右下角编号全部移除。
原因:
1. 玩家需要优先依赖画面主体、构图和色块识别位置。
2. 编号覆盖会削弱“完整图片被逐步复原”的视觉奖励。
3. 单块和合成后的拼图块只保留原图切片、外轮廓描边和必要的拖拽层级,不叠加额外的色块、暗层或蒙版,避免破坏原图识别。
### 3. 设置能力
拼图运行时右上角设置按钮对齐 RPG 玩法内设置入口,打开独立弹层,不在当前面板下方展开内容。
本次至少保留以下能力:
1. 音乐音量调节
2. 本局进度查看
3. 返回上一页
后续若 RPG 运行时设置继续扩展,拼图运行时应优先复用同类能力与交互层级。
### 4. 通关演出时序
拼图完成后不立即弹结算弹窗,统一按以下顺序执行:
1. 保持完整成图画面可见。
2. 播放一段从一个角扫到另一个角的对角线闪光特效。
3. 闪光特效结束后额外等待 `0.5s`
4. 再弹出结算弹窗。
这样可以先给玩家完整成图的视觉奖励,再进入排行榜与下一关决策。
## 工程约束
1. 继续复用现有 `PuzzleRuntimeShell` 作为运行时承载组件,不新增平行页面。
2. 设置弹层沿用现有像素风弹窗资源,不单独引入新的弹窗体系。
3. 通关演出只作为前端表现层时序,不改动通关判定与排行榜数据来源。
### 5. 拖拽层级规则
正在被拖动的拼图片必须临时提升到拼图棋盘最上层,不允许在拖动过程中被其他单块或合并块遮挡。
交互约束如下:
1. 单块拖动时,提升该拼图片所属格子的堆叠层级,并同步提升拼图片自身层级。
2. 合并块拖动时,直接提升整组容器层级,保证整组视觉保持完整。
3. 松手、取消拖动、或丢失指针捕获后,必须立即恢复默认层级。
4. 这条规则只属于前端表现层,不改变拼图交换、合并、拆分和落点判定逻辑。

View File

@@ -1,47 +0,0 @@
# 系统设计
这一组是玩法、关系、物品、对话等系统设计文档,偏“应该怎么设计”而不是“现在哪里出问题”。
## 文档列表
- [CHILD_MOTION_DEMO_WARMUP_LEVEL_DESIGN_2026-05-09.md](./CHILD_MOTION_DEMO_WARMUP_LEVEL_DESIGN_2026-05-09.md)4-8 岁儿童动作识别互动玩法 Demo 固定热身关的横屏体验流程、识别目标、表现需求与待确认事项。
- [TAONIER_BRAND_LOGO_CONCEPTS_2026-05-13.md](./TAONIER_BRAND_LOGO_CONCEPTS_2026-05-13.md)候选产品名“陶泥儿”的品牌定位归纳、gpt-image-2 Logo 概念图和主标选择建议。
- [CUSTOM_WORLD_CREATOR_INPUT_AND_AI_BOUNDARY_DESIGN_2026-04-06.md](./CUSTOM_WORLD_CREATOR_INPUT_AND_AI_BOUNDARY_DESIGN_2026-04-06.md):自定义世界里陶泥儿主输入与 AI 分工边界设计。
- [CUSTOM_WORLD_CREATOR_MANUAL_AI_SYSTEM_BALANCE_DESIGN_2026-04-12.md](./CUSTOM_WORLD_CREATOR_MANUAL_AI_SYSTEM_BALANCE_DESIGN_2026-04-12.md):自定义世界创作里“手填锚点 / AI 可改初稿 / 系统托管层”的平衡设计。
- [CUSTOM_WORLD_CREATOR_PURE_AGENT_COMPARISON_AND_CONVERSION_DESIGN_2026-04-12.md](./CUSTOM_WORLD_CREATOR_PURE_AGENT_COMPARISON_AND_CONVERSION_DESIGN_2026-04-12.md):纯 Agent 式创作工具与结构化工作台方案的优缺点对比,以及转型设计。
- [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):把模板依赖逐步迁成自定义世界自有设定层,并保证不破坏当前生成流程的优化方案。
- [MOBILE_CREATION_NEW_WORK_COMPACT_LAYOUT_2026-04-24.md](./MOBILE_CREATION_NEW_WORK_COMPACT_LAYOUT_2026-04-24.md):移动端创作页新建作品模块最多占用首屏约 1/3 高度的紧凑布局设计。
- [MOBILE_CREATION_WORK_LIST_TWO_COLUMN_LAYOUT_2026-04-29.md](./MOBILE_CREATION_WORK_LIST_TWO_COLUMN_LAYOUT_2026-04-29.md):移动端创作页作品列表至少 2 列的紧凑布局设计。
- [PLATFORM_HOME_MOBILE_FEED_CARD_REDESIGN_2026-04-28.md](./PLATFORM_HOME_MOBILE_FEED_CARD_REDESIGN_2026-04-28.md):平台首页移动端参考图式信息流、双端公开作品卡 16:9 封面结构与点赞数读模型设计。
- [PLATFORM_MOBILE_RECOMMEND_DISCOVER_DRAFT_TAB_REDESIGN_2026-05-05.md](./PLATFORM_MOBILE_RECOMMEND_DISCOVER_DRAFT_TAB_REDESIGN_2026-05-05.md):平台移动端推荐、发现、创作、草稿、我的 Tab 重新分工与推荐页/底部导航改版设计。
- [PLATFORM_CREATE_TAB_CREATIVE_AGENT_HOME_2026-05-05.md](./PLATFORM_CREATE_TAB_CREATIVE_AGENT_HOME_2026-05-05.md):平台创作 Tab 恢复模板 Tab 入口、默认选中拼图并内嵌拼图创作表单的布局设计。
- [PLATFORM_CATEGORY_AND_CREATE_TAB_DESIGN_2026-04-24.md](./PLATFORM_CATEGORY_AND_CREATE_TAB_DESIGN_2026-04-24.md):平台入口新增分类 Tab、登录态导航裁剪与创作 Tab 视觉强化设计。
- [PLATFORM_BIG_FISH_ENTRY_HIDE_2026-04-28.md](./PLATFORM_BIG_FISH_ENTRY_HIDE_2026-04-28.md):平台入口暂时隐藏大鱼吃小鱼创作卡片,但保留现有玩法链路。
- [UNIFIED_MODAL_WINDOW_DESIGN_2026-04-25.md](./UNIFIED_MODAL_WINDOW_DESIGN_2026-04-25.md):统一平台风与 RPG 像素风模态窗口外壳、交互边界和迁移顺序。
- [BAIMENG_EXPO_ROLLUP_BANNER_DESIGN_2026-05-07.md](./BAIMENG_EXPO_ROLLUP_BANNER_DESIGN_2026-05-07.md):陶泥儿线下展会易拉宝广告展板的文案层级、视觉方向与 gpt-image-2 生成记录。
- [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 对话阶段和情景注入草案。
## 推荐阅读
- 做物品、Build、锻造相关需求时先看前两份。
- 做 RPG 全剧情规划、主支线矩阵、角色线、场景章节与剧情交付模板时,先看新增的全剧情策划流程。
- 做自定义世界创作工作台、陶泥儿主输入边界、AI 分工设计时,先看第一份。
- 做“哪些内容必须让陶泥儿主手填、哪些适合 AI 先生成再改、哪些必须系统托管”这类分层设计时,优先看新增的输入平衡设计稿。
- 做“是否应该转成纯 Agent 式创作工具、转了之后前后台各该怎么收口”这类产品方向评估时,优先看新增的纯 Agent 对比与转型设计稿。
- 做自定义世界去模板依赖、跨题材泛化、兼容迁移设计时,优先看新增的去模板化优化设计稿。
- 做“模板依赖如何真正变成自定义世界自有设定层”的具体迁移方案时,优先看新增的自有设定层优化方案。
- 做角色关系、同伴互动、对话表现时,先看后两份。
- 做“高好感聊天里如何顺着上下文自然抛出委托、并让任务在聊天内领取”的需求时,优先看新增的聊天委托流程设计稿。
- 做剧情引擎章节化、场景闭环、章节任务接入时,优先看新增的场景章节设计稿。
- 做“单章节体验还缺什么、该补哪种情感 / 抉择 / 试炼模块”时,优先看新增的章节对标补强设计稿。
- 做等级成长、任务/击败敌对 NPC 发经验、章节经验速度评估、NPC 自动定级时,优先看新增的等级系统设计稿。
- 如果要判断是否符合目标,再和 `docs/prd/` 中对应 PRD 对照阅读。

View File

@@ -1,95 +0,0 @@
# RPG NPC 聊天敌对中止与聊天内 Function 选项设计2026-04-25
## 1. 目标
本次迭代调整运行时 NPC 聊天,让敌对角色聊天从固定五回合上限改为由模型按当前语境判定是否中止;好感度大于等于 0 的角色继续保持可持续聊天,不由模型强制结束。
同时,原本部分只在退出聊天后才出现的 NPC function 选项,需要进入聊天续写候选池。模型在生成聊天候选时要能看到可触发的 function 选项,并把它们改写成玩家可直接点击的动作文本。聊天中保留“换一换”能力,用于刷新下方候选。
## 2. 行为规则
1. 负好感或敌对 NPC 进入聊天后,不再设置固定 5 回合上限。
2. 负好感或敌对 NPC 每轮回复后,模型必须判断本轮是否结束聊天。
3. 敌对 NPC 判定时应偏向随时结束聊天并进入对峙但必须结合玩家刚说的话、NPC 性格、当前剧情压力和对话历史。
4. 敌对 NPC 感知到玩家负面发言时,例如挑衅、威胁、羞辱、逼问、拒绝退让、直接宣战或强行越界,应倾向立即 `shouldEndChat=true`
5. 敌对 NPC 已聊天轮次超过 4 轮后,应倾向立即 `shouldEndChat=true`,避免敌对关系被拖成长时间闲聊。
6. 敌对 NPC 最后一轮回复必须像战斗、驱逐或正面对峙前的狠话:短促、带压迫感、明确把局势推向行动前一刻,但不在对白中直接结算战斗。
7. 好感度大于等于 0 且非敌对 NPC 不启用模型终止判定,玩家可一直聊天。
8. 模型判定终止后,聊天面板不再继续提供聊天输入,只显示“继续”按钮,点击后沿用原流程继续生成冒险选项。
9. 点击“退出聊天”不再直接收起聊天页,也不立即进入剧情推理;它会发送一条结束聊天的玩家输入,对方回复后同样只显示“继续”按钮。
10. 正向 NPC 的退出聊天只是玩家主动收束,不代表模型强制中止,也不展示战斗/逃跑选项。
11. 对负好感或敌对 NPC在聊天终止后的后续流程仍沿用敌对出口继续推进后展示一个“战斗”选项以及按相邻场景和当前场景起点展开的多个逃跑选项。
12. 聊天候选中允许混入当前 NPC 可执行 function例如交易、送礼、请求帮助、招募、接任务、交任务、开战、离开等。
13. Function 候选进入聊天上下文时只作为可触发动作,不在 UI 中展示说明类文本。
14. “换一换”在聊天态可用,用于在不推进对话的情况下改排/轮换当前候选;它不调用后端,不改变聊天历史。
## 3. 后端契约
`NpcChatTurnDirective` 增加:
1. `terminationMode``none | hostile_model`
2. `isHostileChat`:当前聊天是否按敌对中止规则处理
3. `functionOptions`:可进入聊天候选的 function 列表,包含 `functionId``actionText``detailText``action`
`NpcChatTurnCompletionDirective` 增加:
1. `forceExit`:本轮回复后是否关闭聊天输入
2. `closingMode`:保留 `free | foreshadow_close`
3. `terminationReason``hostile_breakoff | player_exit | null`
后端返回 `suggestions` 仍是字符串数组,前端按字符串生成 `npc_chat` 续写选项;新增 `functionSuggestions`,元素包含 `functionId` 与模型生成的 `actionText`,前端按对应 function 触发原 NPC action。
## 4. Prompt 规则
回复 prompt 需要明确:
1. 敌对聊天可随时中止NPC 更偏好结束谈判转入战斗或驱逐。
2. 终止不等于在回复正文里直接执行战斗,只需要用台词把对话收束到对峙、威胁、驱逐、最后通牒或行动前一刻。
3. 玩家主动退出聊天时NPC 回复要对这次收束作出回应,并留下自然的后续入口。
4. 若玩家本轮发言明显负面,或敌对聊天已进入第 5 轮及之后,回复 prompt 要提示 NPC 直接给出战斗前、驱逐前或正面对峙前的狠话。
建议 prompt 需要明确:
1. 常规聊天候选继续生成玩家台词。
2. Function 候选要根据提供的 function 列表,改写成玩家可直接点击的动作文本。
3. 不输出规则说明,不把 functionId 暴露给玩家。
4. 敌对聊天判断 `shouldEndChat` 时,负面发言和已聊天轮次超过 4 轮都应作为强收束信号;如果返回 `shouldEndChat=true``terminationReason` 使用 `hostile_breakoff`
## 5. 前端流程
1. `enterNpcChat` 与每轮 `handleNpcChatTurn` 统一构造聊天可用 function 列表。
2. 聊天中的普通候选仍触发 `npc_chat`function 候选触发原 `handleNpcInteraction` 分流。
3. `exitNpcChat` 改为调用 `handleNpcChatTurn`,输入文本为结束聊天意图,并携带 `player_exit` 指令。
4. 收到终止结果后,当前 `StoryMoment` 保留 `dialogue`,移除 `npcChatState``options` 只保留 `buildContinueAdventureOption()`
5. 点击“继续”后沿用已有 deferred continue / story continue 逻辑进入下一阶段。
6. 聊天态“换一换”只轮换当前 `options`,若 function 候选不足则补普通聊天兜底候选。
## 6. 追加规则Function 标签与场景幕推进
1. 运行时选项按钮需要在动作文本前展示 function 短标签,例如 `npc_chat` 显示“聊天”,`npc_quest_accept` / `npc_quest_turn_in` 显示“任务”,`npc_gift` 显示“送礼”。
2. 标签只承担识别用途,不展示 functionId也不展示规则说明。
3. NPC 聊天终止后点击“继续冒险”,不再重新请求剧情推理;如果当前场景还有下一幕,直接进入下一幕并展示该幕可用的冒险选项。
4. 当当前场景已经到最后一幕再点击“继续冒险”应展示所有相邻场景入口选项文案按方向表达为“向东走前往xxxx”“向南走前往xxxx”等。
5. 相邻场景选项继续使用 `idle_travel_next_scene`,并在 `runtimePayload.targetSceneId` 中携带目标场景,后续点击沿用现有地图跳转结算。
6. 若没有场景幕数据,则继续使用当前可用选项作为兜底,不额外生成规则说明文案。
## 8. 补充规则:敌对聊天逃跑目标展开
1. 负好感或敌对 NPC 聊天终止后,`npc_fight` 只保留一个,按钮文案固定为“战斗”,原有 NPC 战斗交互与结算链路不变。
2. 原单一“逃跑”按钮改为多个 `battle_escape_breakout` 选项:当前场景每个相邻场景生成“逃往{场景名}”,并额外生成“逃回当前场景起点”。
3. 逃往相邻场景的选项在 `runtimePayload.targetSceneId` 中写入目标场景 id逃回起点的选项在 `runtimePayload.escapeReturnToSceneStart` 中写入 `true`,并保留当前场景 id 作为目标。
4. 点击任一逃跑类选项时,先复用现有主角向左转身跑出屏幕的逃离动画,再把运行态切到目标场景或当前场景起点,最后从左侧入场并面向右侧。
5. 逃跑类选项只负责运行态目标和表现,不重新请求剧情推理,也不把规则说明显示到 UI。
## 7. 验收
1. 负好感主 NPC 不再出现固定 `turnLimit: 5`
2. 敌对 NPC 每轮请求会向后端传 `terminationMode: hostile_model`
3. 模型返回 `forceExit: true` 后,聊天输入消失,只显示继续按钮。
4. 玩家对敌对 NPC 说出挑衅、威胁、羞辱、逼问、拒绝退让、直接宣战或强行越界类发言时,模型应倾向返回 `shouldEndChat=true`NPC 最后一轮回复带战斗前狠话。
5. 敌对 NPC 已聊天轮次超过 4 轮后,模型应倾向返回 `shouldEndChat=true`NPC 最后一轮回复带战斗前狠话。
6. 好感度大于等于 0 的 NPC 聊天不传敌对中止模式。
7. 点击退出聊天会新增玩家结束聊天气泡与 NPC 回复,而不是直接切走面板。
8. 聊天态可看到并点击 function 候选,且“换一换”可改变候选顺序。
9. 选项文字前出现中文 function 标签,且标签不改变原 actionText。
10. 聊天结束后的“继续冒险”直接进入下一幕;最后一幕则展示多个相邻场景方向入口。

View File

@@ -1,434 +0,0 @@
# 单场景章节对标缺口与 AI 原生体验补强设计
更新时间:`2026-04-08`
## 0. 目标
这份文档补在:
- `docs/design/SCENE_CHAPTER_LOOP_AND_FIRST_ENTRY_CHAPTER_QUEST_DESIGN_2026-04-08.md`
- `docs/prd/AI_NATIVE_CLASSIC_RPG_EXPERIENCE_BENCHMARK_PRD_2026-04-06.md`
之后,专门回答一个更具体的问题:
**如果把当前每个场景都视为一个章节,那么和《仙剑》《博德之门》《黑神话:悟空》里一个成立的章节相比,我们现在到底缺什么;以及结合当前项目的 AI 原生设计,应该补哪些体验层。**
这份文档不重复讨论“场景如何开章、任务如何挂接”的骨架问题,而是聚焦:
1. 单章节的体验密度还缺什么
2. 每章最该补的体验模块是什么
3. 哪些能力必须由服务端承接,前端只负责表现
---
## 1. 结论先说
当前项目把“场景 = 章节单元”的方向已经立住了一半骨架,但和这三类标杆作品相比,单章节体验还普遍缺 5 件事:
1. 缺一个让玩家记住“这章是关于谁”的情感锚点
2. 缺一个让玩家中途改判的转折点
3. 缺一个让玩家感到“这一段路在压着我走”的空间推进链
4. 缺一个让玩家立场真正被表达出来的选择节点
5. 缺一个能把结果写回人物、物件、后续章节的余波回响
一句话判断:
**现在的场景章节更像“带任务 lead 的剧情地点”,还不够像“有人物、有抉择、有试炼、有收束余味的一整章”。**
---
## 2. 对标三个标杆后,当前单章节缺什么
## 2.1 相比《仙剑》式章节,缺的是“人和情”
《仙剑》式章节最强的不是题材,而是:
1. 一章里总有一个会被记住的人
2. 这一章结束时,人物关系一定发生了变化
3. 玩家记住的不是“我做了一个任务”,而是“我和谁经历了一段事”
当前场景章节的主要缺口如下:
| 维度 | 标杆章节会给玩家什么 | 当前缺口 | 应补什么 |
| --- | --- | --- | --- |
| 章节记忆点 | 明确的人物高光或情感切口 | 现在更多是场景 lead不够像人物立题 | 每章必须有 `情感锚点 NPC / 关系对象` |
| 关系推进 | 误会、试探、信任、失去、承诺会推进 | 现在 NPC 更像信息点或任务点 | 在 `opening / turning_point / aftermath` 至少安排一次关系变化 |
| 章节余味 | 章节结束后仍有余波、牵挂、回看价值 | 现在更多是“交给下一场景” | 章节结束后补 `私聊 / 留言 / 赠礼反应 / 人物口风变化` |
结论:
**当前章节最缺的不是人物数量,而是“这一章让玩家和谁产生了关系变化”。**
## 2.2 相比《博德之门》式章节,缺的是“选择和反应”
《博德之门》式章节最强的不是分支数量,而是:
1. 玩家会在一章里表达立场
2. 队友和 NPC 会明确回应这个立场
3. 同一个问题通常不止一种处理方式
当前场景章节的主要缺口如下:
| 维度 | 标杆章节会给玩家什么 | 当前缺口 | 应补什么 |
| --- | --- | --- | --- |
| 处理路径 | 同一章通常有 `2~3` 种解法 | 现在大多仍是单主解推进 | 每章至少提供一次 `有限分歧结算` |
| 队友反应 | 队友会认可、反对、沉默、插话 | 现在同伴存在感偏弱 | 每章至少有一次 `同伴反应批次` |
| 后果落地 | 选择会改任务理解、人物态度、后续信息 | 现在选择更多是流程推进,不够像立场表达 | 把章节选择写回 `关系 / 线索可见性 / 后续 handoff` |
结论:
**当前章节最缺的不是做无限分支,而是做“有限分歧 + 强反馈”。**
## 2.3 相比《黑神话:悟空》式章节,缺的是“路和压迫”
《黑神话:悟空》式章节最强的不是题材,而是:
1. 一章的空间推进本身就在讲故事
2. 玩家会记住路上的地标、残痕、敌人和压迫感
3. 一章通常有一个明显的试炼点或高潮演出点
当前场景章节的主要缺口如下:
| 维度 | 标杆章节会给玩家什么 | 当前缺口 | 应补什么 |
| --- | --- | --- | --- |
| 空间推进 | 场景不是背景,而是一段穿行过程 | 现在场景更像承载节点,不够像旅程 | 每章补 `路线压力链``地标记忆点` |
| 残痕叙事 | 地点、物件、敌人都在讲旧事 | 现在残痕有素材,但链不够强 | 每章至少串起 `2~3` 个残痕 / 证据 / 地标 |
| 高潮试炼 | 一章通常有一个明显 setpiece / 强敌 / 对峙 | 现在收束常偏文本或任务状态 | 每章补一个 `高潮收束动作面` |
结论:
**当前章节最缺的不是更多场景描述,而是“空间本身要承担承压和收束”。**
## 2.4 三个标杆放在一起看,当前单章节的综合缺口
综合来看,当前每个场景章节最明显的缺口可以收束成下面 7 条:
1. 场景有主题,但缺“这一章是谁在发光”
2. 任务有步骤,但缺“中途为什么会改判”
3. 残痕有素材,但缺“沿路逐步加压的链条”
4. 同伴会跟着走,但缺“针对你的立场说话”
5. 战斗或冲突会发生,但缺“本章高潮”
6. 章节会结束,但缺“结束后谁变了”
7. 章节会交棒,但缺“这一章留下了什么可回响之物”
---
## 3. 每个场景章节都该补的标准体验包
建议以后把每个场景章节都按“九件套”来补,不要求每件都做得很重,但每章都不能缺位。
| 模块 | 每章最低要求 | 主要对标 |
| --- | --- | --- |
| 开章钩子 | 一进入场景就抛出一个异常、冲突或错位对白 | 三者共同 |
| 情感锚点 | 至少一个本章承担关系变化的人物 | 仙剑 |
| 路线压力链 | 至少 `2~3` 个地标、残痕或承压节点串起来 | 黑神话 |
| 承压事件 | 一次调查、对峙、战斗或阻拦,证明这一章真有事在发生 | 三者共同 |
| 改判节点 | 至少一条反证、误会破口或旧痕翻案 | 仙剑 + 博德之门 |
| 立场选择 | 至少一次有代价的二选一 / 三选一 | 博德之门 |
| 同伴反应 | 至少一次认可、反对、担忧、沉默中的一种反馈 | 博德之门 |
| 高潮收束 | 一次明确的 confrontation、试炼、交付或揭示 | 黑神话 + 仙剑 |
| 余波回响 | 至少留下一个后续会被提起的人、物、线索或态度变化 | 三者共同 |
这里的重点不是把每章都做成大体量内容,而是让每章都具备:
1. 可记住的人
2. 可承压的路
3. 可表达的立场
4. 可回响的结果
---
## 4. 结合 AI 原生设计,单章节应该怎么补
## 4.1 不是做“无限分支”,而是做“有限模块 + 动态编排”
当前项目最适合的做法,不是给每个场景写爆炸式手工分支,而是:
1. 先给每章补稳定的体验模块
2. 再让 AI 按当前线程、关系、残痕和场景状态去动态编排表达
3. 本地规则负责章节状态、选择裁决、任务推进、关系变化和奖励回写
一句话说:
**AI 负责把这一章写活,本地规则负责保证这一章成立。**
## 4.2 建议新增一个章节体验画像
建议在现有 `ChapterState` 外,给每个场景章节补一个更偏体验编排的数据层,例如:
```ts
interface ChapterExperienceProfile {
sceneId: string;
chapterArchetype: 'emotion' | 'choice' | 'trial' | 'hybrid';
chapterPromise: string;
emotionalAnchorNpcId?: string | null;
guideNpcId?: string | null;
opposingForceId?: string | null;
routePressureBeats: string[];
turningEvidenceIds: string[];
stanceChoiceId?: string | null;
climaxSetpieceId?: string | null;
rewardCarrierSeed?: string | null;
aftermathEchoTargets: string[];
}
```
它的作用不是替代现有 `ChapterState`,而是补“这一章体验上到底靠什么成立”。
## 4.3 每章都要有一个“人物轴”
这是补仙剑型体验最重要的一步。
建议规则:
1. 每章必须指定一个 `emotionalAnchorNpcId`
2. 这个角色不一定是最强、最重要的人,但一定是本章最会改变玩家理解的人
3. 这个角色至少承担下面 2 件事:
- `opening` 里立题或制造错位
- `turning_point / aftermath` 里改口、揭示或留下余波
AI 原生补法:
1. AI 负责生成角色此章的表层说辞、压力表现和错位感
2. 本地规则负责控制此章能披露哪些事实、关系如何变化
## 4.4 每章都要有一个“路线轴”
这是补黑神话型体验最重要的一步。
建议规则:
1. 每章至少有 `2~3``routePressureBeats`
2. 每个 beat 都不只是位置点,而是下面三类之一:
- 地标记忆点
- 场景残痕点
- 敌对 / 阻拦 / 追索点
3. `climax` 前必须至少有一次“越往前越不安”的空间升级
AI 原生补法:
1. AI 负责根据 `scene residue / thread / scar / motif` 生成现场感和残痕文本
2. 本地规则负责决定 beat 顺序、是否已触发、何时进入高潮
## 4.5 每章都要有一个“立场轴”
这是补博德之门型体验最重要的一步。
建议规则:
1. 每章至少抛一次立场问题
2. 立场问题优先围绕:
- 要不要信谁
- 要不要公开某条线索
- 要不要保一个人还是保规则
- 要不要立刻动手还是继续查
3. 不追求无限解,只要求:
- `2~3` 个有限方案
- 每个方案都能触发明确反应
AI 原生补法:
1. AI 负责把选择包装成角色化、情境化的动作与对白
2. 本地规则负责裁决结果、写回 `CompanionStanceProfile / Quest / Knowledge / Echo`
## 4.6 每章都要有一个“高潮动作面”
目前很多场景的高潮更像“任务完成”,还不够像“一章真正收束了”。
建议每章在 `climax` 至少落成下面四类之一:
1. 正面对峙
2. 小型试炼 / 强敌
3. 真相交付
4. 关系摊牌
要求:
1. 高潮不能只体现在任务状态变化
2. 必须有明确前后差
3. 必须能回写一条 `chapter aftermath`
## 4.7 每章都要留下一个可回响的载体
这是把三类标杆合在一起后最容易被低估的一层。
建议每章至少留下一个:
1. 章节证物
2. 章节遗物
3. 章节残痕
4. 章节口风变化
它们至少要满足一条:
1. 下一章还能被提起
2. 某个 NPC 会认出它
3. 某个同伴会追加评论
4. 某个任务会因为它改变说明或目标
---
## 5. 章节类型建议
不是每章都要平均模仿三款游戏,更稳的做法是:每章明确一个主类型,再配一个副类型。
| 章节类型 | 主对标 | 章节重点 | 必选模块 |
| --- | --- | --- | --- |
| 情感章 | 仙剑 | 人物关系、误会、牺牲、承诺、旧债 | 情感锚点、改判节点、余波回响 |
| 抉择章 | 博德之门 | 立场表达、队友反应、有限分歧结算 | 立场选择、同伴反应、后果回写 |
| 试炼章 | 黑神话 | 路线承压、地标残痕、高潮试炼 | 路线压力链、高潮收束、章节载体 |
| 混合章 | 任选两者 | 既要有情感,也要有试炼或选择 | 视主副轴组合决定 |
建议规则:
1. 每个场景章节都标一个 `主类型`
2. 再选一个 `副类型`
3. 不要让所有章节都只剩“调查 + 对话 + 交任务”
---
## 6. 服务端与前端的职责边界
结合当前项目约束,章节体验补强必须坚持:
**前端只负责表现,章节逻辑、状态、选择裁决、数据编排全部放在 Express 后端。**
## 6.1 服务端负责什么
建议把下面这些能力放在 `server-node/`
1. 章节体验画像生成
2. 章节选择裁决
3. 同伴反应批次生成
4. 章节残痕与路线 beat 编排
5. 章节余波与回响写回
6. 章节奖励载体编译
服务端输入应来自:
1. 当前 `sceneId`
2. 当前 `ChapterState`
3. 当前队伍与关系状态
4. 当前激活线程与知识可见性
5. 当前章节已触发的 beat、choice、residue、setpiece
服务端输出应编译成前端可直接消费的结果,例如:
1. 当前章节标题与阶段
2. 当前章节 promise
3. 当前 route beats
4. 当前可选立场
5. 同伴反应 feed
6. 本章高潮结果
7. 本章 aftermath 摘要
## 6.2 前端负责什么
前端只负责:
1. 表现章节标题、章节目标、路线节拍
2. 表现情感锚点人物和同伴反应
3. 表现本章高潮演出与余波反馈
4. 表现章节奖励载体和 chronicle 回写结果
前端不要负责:
1. 计算选择结果
2. 推导章节阶段
3. 生成 route beat 顺序
4. 决定同伴 stance 更新
---
## 7. 单章节设计卡模板
为了让后续每个场景都能按统一方式补体验,建议每章都补一张简版设计卡:
```md
- 章节标题:
- 主类型 / 副类型:
- 本章 promise
- 情感锚点人物:
- 开章钩子:
- 路线压力链:
- 承压事件:
- 改判证据:
- 立场选择:
- 同伴反应:
- 高潮收束:
- 章节奖励载体:
- 余波回响:
- 下一章 handoff
```
只要这张卡填不满,说明这一章还没有真正成立。
---
## 8. 推荐补强顺序
如果按当前项目最稳的节奏推进,建议顺序如下:
## 阶段 A先补“可被记住的人”
先做:
1. 情感锚点 NPC
2. 改判节点
3. 章节余波
原因:
这是最接近当前项目已有 NPC、关系和私聊链的部分投入最小感知提升最快。
## 阶段 B再补“可被表达的立场”
先做:
1. 每章一次立场选择
2. 同伴反应批次
3. 后果写回任务 / 关系 / 线索可见性
原因:
这是对标《博德之门》最关键,同时也是最能让 AI 原生叙事摆脱“只会往前写”的一步。
## 阶段 C最后补“可被承压的路”
先做:
1. route pressure beats
2. 地标残痕链
3. 高潮 setpiece
原因:
空间承压最依赖完整的场景节拍和演出支持,适合在前两层站稳后继续增强。
---
## 9. 章节体验验收标准
如果说一个场景章节已经明显更接近《仙剑》《博德之门》《黑神话:悟空》那类标杆,至少应满足下面这些结果:
1. 玩家玩完这一章后,能明确说出“这章主要在讲谁 / 讲什么”
2. 玩家在中段经历过一次改判,而不是从头到尾只在确认原判断
3. 玩家能记住至少一个地标、残痕或证物,而不只是任务标题
4. 玩家至少做过一次立场表达,并收到过明确反应
5. 玩家在章节结束时感到“这一章局部收住了”,而不只是“系统让我去下个场景”
6. 本章至少有一条人物态度、物件、线索或 chronicle 被写进后续回响
7. 首遇与低披露阶段不会把整章暗线和角色完整背景直接灌给模型
---
## 10. 一句话结论
如果把每个场景都视为一个章节,那么当前最该补的,不是继续堆更多场景文本,而是让每章都真正长出:
1. 一个会被记住的人
2. 一段会逐步加压的路
3. 一次会暴露立场的选择
4. 一个能把本章收住的高潮
5. 一条能延续到后面的余波
只有这样,当前项目的“场景章节化”才会从结构成立,进一步走到体验成立。

View File

@@ -1,715 +0,0 @@
# 场景章节闭环与首进章节任务设计
更新时间:`2026-04-08`
## 0. 目标
这份设计稿要把当前仓库里已经存在的:
- `章节状态`
- `任务 contract / step`
- `场景残痕`
- `NPC 首遇与关系`
- `Goal Stack`
进一步收束成一条更明确的结构:
**每个场景都是一个剧情章节单元;每个章节在当前剧情引擎里都要形成“起承转合”的完整闭环;并且在玩家首次进入该场景时,自动开启一个章节任务。**
这里的重点不是再造一套全新系统,而是把现有能力重新组织成:
1. 场景不再只是地图节点,而是章节容器。
2. 章节不再只是背景摘要,而是有明确动作面的闭环。
3. 任务不再只是零散委托,而是章节在前台的执行外壳。
4. NPC 不再只是“场景里有什么人”,而是按章节节拍承担起、承、转、合的叙事职责。
---
## 1. 基于当前仓库的判断
结合当前文档与代码,现状已经具备一半骨架,但关键一半还没接上。
### 1.1 已经具备的基础
1. `src/services/storyEngine/chapterDirector.ts`
- 已经有 `ChapterState`,但当前主要根据 `signalCount / chronicleCount / activeThreadCount` 推章节阶段,更像“抽象章节热度”,还不是“具体场景章节实例”。
2. `src/data/questFlow.ts`
- 已经有 `QuestIntent -> QuestContract -> QuestStep -> QuestProgressSignal` 的完整任务闭环。
- `QuestLogEntry` 也已经有 `actId / threadId / contractId / steps / activeStepId / visibleStage` 这些可扩展入口。
3. `src/services/storyEngine/goalDirector.ts`
- 已经能把 `chapter + quest + journeyBeat + setpiece` 编译成玩家前台目标感。
- 说明章节任务一旦成型,前台目标层基本不用重做,只要接正确数据。
4. `src/data/scenePresets.ts`
- 已经有场景描述、敌对实体、额外 NPC、宝藏线索、narrative residues。
- 这些字段已经够支持“场景章节蓝图”的第一版自动编译。
### 1.2 当前缺口
当前最核心的缺口有 4 个:
1. 没有“场景首次进入”对应的持久状态。
- 现在能做场景切换,也能累计 `scenesTraveled`,但没有 `openedSceneChapterIds` 或等价结构。
2. 没有“场景章节实例”。
- `ChapterState` 已有,但没有一个明确指向 `sceneId`、可追踪起承转合进度的运行时对象。
3. 没有“章节任务自动开章”的规则。
- 现在任务更多还是由 NPC 委托机会触发,不是“首进场景即开章”。
4. 没有按章节节拍组织 NPC。
- 现在场景里有 NPC、有敌人、有残痕但还没有明确规定谁负责起、谁负责承、谁负责转、谁负责合。
一句话总结:
**当前仓库已经有章节系统、任务系统和场景叙事素材,但还没有“场景章节实例 + 首进自动开章任务 + 起承转合 NPC 编排”这条真正把它们串起来的中介层。**
---
## 2. 核心决策
## 2.1 场景 = 章节单元
从这次开始,默认把每个可到达场景都视为一个章节单元。
这意味着:
1. 玩家进入一个新场景,不只是“换地图”,而是“开启一个新章节”。
2. 该场景内必须具备一个可完成的最小剧情闭环。
3. 即使大世界主线跨多个场景延续,每个场景也要有自己的局部收束。
## 2.2 章节仍然是叙事语义,任务是前台动作面
这点要继续保持和当前项目既有方向一致:
- `章节` 负责表达“这一段故事在追什么”。
- `章节任务` 负责表达“玩家现在要做什么”。
也就是说:
**章节不是单独再做一个和任务平级的前台入口,而是通过“章节任务”落到玩家动作层。**
## 2.3 保留现有五阶段结构,起承转合作为体验要求理解
当前 `ChapterState.stage` 已经在用:
- `opening`
- `expansion`
- `turning_point`
- `climax`
- `aftermath`
这次不再新增新的叙事阶段枚举,也不再额外引入一套“四拍语义”运行时字段。
“起承转合”保留为章节闭环的体验要求,但在系统里直接压到现有五阶段上理解:
| 当前字段 | 体验语义 |
| --- | --- |
| `opening` | 起:开章立题 |
| `expansion` | 承:压力展开 |
| `turning_point` | 转:理解改判 |
| `climax` | 合:正面收束 |
| `aftermath` | 合后的余波与交接 |
这意味着:
1. 玩家体感上仍然能得到“起承转合”的完整闭环。
2. 系统运行时只认现有 `stage`,不新增第二套章节阶段系统。
3. `goalDirector / journeyBeatPlanner / setpieceDirector / UI` 都可以继续沿用现有结构。
---
## 3. 场景章节的标准闭环
每个场景章节都必须至少回答这 4 个问题:
1. 玩家刚进来时,这里有什么事正在发生?
2. 玩家在这一章里到底要处理什么压力?
3. 这一章中途会出现什么反转或改判?
4. 玩家离开前,这一章给出了什么局部收束?
对应到现有系统里,就是当前五阶段的完整跑通。
## 3.1 `opening`:开章立题
触发时机:
- 玩家首次进入某场景
必须完成的事:
1. 建立场景章节标题与主题。
2. 让一个 NPC、残痕或现场异常把问题抛出来。
3. 自动开启本章章节任务。
4. 给玩家一个明确的第一步。
适合使用的现有信号:
- `scene_reached`
- 首次 NPC 对话
- 首次观察残痕 / 宝藏线索
适合落地的任务 step
- `reach_scene`
- `talk_to_npc`
- `inspect_treasure`
## 3.2 `expansion`:压力展开
触发时机:
- 玩家已经接住本章 lead开始深入该场景
必须完成的事:
1. 让玩家确认“这一章不是空壳,确实有事要处理”。
2. 把当前场景的主压力推到前台。
3. 让场景内的敌对 NPC / 障碍 NPC / 关键残痕承担中段推进。
适合落地的任务 step
- `inspect_treasure`
- `defeat_hostile_npc`
- `spar_with_npc`
- `talk_to_npc`
## 3.3 `turning_point`:改判与揭示
触发时机:
- 玩家完成了承段主动作,系统需要让当前理解发生偏转
必须完成的事:
1. 出现一条新事实、矛盾证词或旧痕反证。
2. 让至少一个 NPC 的定位发生变化。
3. 把任务从“处理中”切到“确认真相 / 回去对话 / 做最后一跳”。
适合落地的任务 step
- `talk_to_npc`
- `inspect_treasure`
- `reach_scene`
- `item_delivered`
## 3.4 `climax`:本章收束
触发时机:
- 玩家已经拿到足够信息,或者已经处理完这一章的核心冲突
必须完成的事:
1. 当前场景的局部问题得到收束。
2. 章节任务进入可交付或已交付状态。
3. 章节回写 `chronicle`
4. 给出下一场景 handoff。
适合落地的任务 step
- `talk_to_npc`
- `item_delivered`
注意:
**`climax` 不等于世界真相彻底结束,而是这一个场景章节的核心矛盾必须在这里得到局部收束。**
也就是说,大主线可以继续,但当前场景不能只留下“半段没收”的悬空状态。
## 3.5 `aftermath`:余波与交接
触发时机:
- 章节任务已经 `ready_to_turn_in``turned_in`
- 本章主要冲突已经完成,系统需要把结果沉淀并交给下一段
必须完成的事:
1. 把本章结果写进 `chronicle` 或最近摘要。
2. 把当前场景的局部余波写回场景状态、NPC 态度或任务说明。
3. 给出下一场景或下一条线程的 handoff。
这里的重点不是再加一轮复杂任务,而是把已有结果接住。
一句话总结:
**起承转合仍然保留为设计目标,但系统实现上直接用 `opening -> expansion -> turning_point -> climax -> aftermath` 跑完整闭环。**
---
## 4. NPC 编排规则
这次的 NPC 设计重点不是“一个场景塞多少人”,而是:
**谁在这一章里负责什么。**
## 4.1 不新增独立的 `npcCasting` 系统,先按现有阶段组织职责
这轮不建议为了章节化单独新增一套 NPC casting 数据结构。
更稳的做法是直接基于现有场景数据组织阶段职责:
1. `opening`
- 优先由场景里的首遇 NPC、第一条残痕或第一层异动承担立题职责。
2. `expansion`
- 优先由敌对单位、阻拦型 NPC、关键残痕承担压力职责。
3. `turning_point`
- 优先由第二条线索、矛盾证词、返回对话的 NPC 承担改判职责。
4. `climax / aftermath`
- 优先由最初开章 NPC 或新的接棒 NPC 承担收束和 handoff。
## 4.2 允许一人多阶段承担职责,但阶段职责不能空
考虑到当前有些场景 NPC 数量不多,因此允许:
- 同一个 NPC 同时承担 `opening + aftermath`
- 同一个 NPC 同时承担 `expansion + turning_point`
如果友方 NPC 不够,可以由这些现有对象补位:
1. 场景残痕
2. 宝藏线索
3. 文书 / 道具 / 遗物
4. 敌对单位
也就是说,本章的“转”不一定靠新 NPC也可以靠现有证据或现场变化来完成。
## 4.3 NPC 的章节职责应该是动态解释,不是静态标签
不要把 NPC 只写成:
- 商人
- 侍女
- 守门人
- 怪物
更应该补的是:
- 他在这一章里为什么是开章人
- 他卡住玩家的是什么压力
- 他掌握的转折信息是什么
- 他能否承接本章结算
这部分优先通过现有 `npc context / dialogue / rewardText / quest step 文案` 去表达,不急着为每个 NPC 新增专门类型。
---
## 5. 数据结构建议
## 5.1 扩展 `ChapterState`
建议扩展为:
```ts
export interface ChapterState {
id: string;
title: string;
theme: string;
primaryThreadIds: string[];
stage: 'opening' | 'expansion' | 'turning_point' | 'climax' | 'aftermath';
chapterSummary: string;
sceneId?: string | null;
chapterQuestId?: string | null;
}
```
这样当前系统读取 `stage` 的地方仍然可用,同时章节状态也能明确绑定:
- 这是哪个场景章节
- 当前绑定的是哪一个章节任务
建议:
- 对“场景章节”使用稳定 id例如 `chapter:scene:${sceneId}`
- 不再额外发明新的章节实例类型
## 5.2 扩展 `StoryEngineMemoryState`
建议新增:
```ts
export interface StoryEngineMemoryState {
openedSceneChapterIds?: string[];
}
```
这是这次最关键的数据层补丁,因为当前仓库现在没有“首进场景已开章”的持久状态。
这里刻意不新增 `sceneChapterStates` 这类新的章节运行时容器,优先复用现有:
- `currentChapter`
- `quests`
- `storyHistory`
- `chronicle`
## 5.3 尽量复用 `QuestLogEntry`
建议补充:
```ts
export interface QuestLogEntry {
chapterId?: string | null;
}
```
其他字段尽量直接复用现有结构:
- `sceneId`
- `steps`
- `activeStepId`
- `visibleStage`
- `status`
目的不是让 UI 暴露更多字段,而是让系统能知道:
- 这是某个场景章节自动生成的主任务
- 该任务与当前 `ChapterState` 是否一一对应
一句话原则:
**这轮只补“老系统里真正缺的最小字段”,不再额外发明一整套章节蓝图 / 章节运行时数据模型。**
---
## 6. 章节任务设计
## 6.1 首次进入场景时自动开任务
根据这次需求,章节任务默认不再依赖玩家额外点一次“接受委托”。
建议规则:
1. 玩家首次进入某场景时,直接创建该场景的章节任务。
2. 章节任务默认进入 `active`
3. 同一场景后续再次进入时,不重复开同一任务。
也就是说:
**首进场景 = 开章节 = 章节任务自动入列。**
## 6.2 章节任务不强制另起一套五步或四步系统,优先复用现有 step + status
这次不建议为了章节化再发明一套新的任务阶段结构。
更稳的做法是:
1. 章节层继续使用现有五阶段 `stage`
2. 任务层继续使用现有 `steps + activeStepId + status`
3. 通过任务进度去驱动章节阶段,而不是反过来再创建一套章节 step
建议映射关系:
| 章节阶段 | 现有任务侧表现 |
| --- | --- |
| `opening` | 章节任务刚创建,首个 `step` 为接 lead |
| `expansion` | 中段调查 / 战斗 / 接触 step 在推进 |
| `turning_point` | `activeStep` 切换到改判或回报前置 step |
| `climax` | 最后一个核心 step 正在执行,或任务刚进入 `ready_to_turn_in` |
| `aftermath` | 任务 `turned_in`,或已经完成本章结算并进入 handoff |
推荐的 step 仍然复用当前支持的类型:
| 当前阶段 | 常见 step kind |
| --- | --- |
| `opening` | `reach_scene` / `talk_to_npc` / `inspect_treasure` |
| `expansion` | `inspect_treasure` / `defeat_hostile_npc` / `spar_with_npc` |
| `turning_point` | `talk_to_npc` / `inspect_treasure` / `reach_scene` |
| `climax` | `talk_to_npc` / `item_delivered` / 关键收束 step |
| `aftermath` | 优先使用 `ready_to_turn_in / turned_in` 和 handoff不再强塞新 step |
## 6.3 章节任务应该服务于 Goal Stack
当前仓库已经有 `GoalStack`,因此章节任务一旦建立,应默认成为:
1. `active_contract`
2. `immediate_step`
`ChapterState` 继续承担:
1. 章节主题
2. 章节承诺
3. 章节背景总结
也就是说前台玩家看到的是:
- 当前章节任务标题
- 下一步去哪 / 找谁 / 做什么
后台章节层则继续给叙事和 prompt 提供语义。
---
## 7. 运行时流程接入建议
## 7.1 调整 `chapterDirector.ts`
当前 `chapterDirector` 更像“热度评分器”。
这次建议直接在它内部补两类能力:
1. 场景绑定
- 当前场景存在且符合开章条件时,直接生成 `sceneId` 绑定的 `ChapterState`
2. 阶段推导
- 优先从当前场景绑定的章节任务进度推导 `opening -> expansion -> turning_point -> climax -> aftermath`
- 只有在场景章节信息不足时,才回退到现有 `signalCount / chronicleCount / activeThreadCount` 逻辑
建议新增的只是 `chapterDirector.ts` 内部 helper而不是新的模块例如
```ts
resolveSceneBoundChapterState(params)
deriveChapterStageFromQuest(params)
```
## 7.2 调整 `questFlow.ts`
建议新增:
```ts
buildChapterQuestForScene(params)
findActiveChapterQuestForScene(params)
```
这里依然放在现有 `questFlow.ts` 内部处理,不单独拆新系统。
章节任务 builder 的原则是:
1. 输入继续使用当前能拿到的场景信息
- `scenePreset`
- `currentSceneId`
- `activeThreadIds`
- `scene npcs / hostile npc / treasureHints`
2. 输出继续是现有 `QuestLogEntry`
- 只是多补 `chapterId`
- 并尽量让 `sceneId = 当前场景`
## 7.3 调整 `useStoryGeneration.ts` / `sessionActions.ts`
推荐接入点:
1. 场景切换完成后
2. `scene_reached` 信号写入时
3. 地图跳转 `travelToSceneFromMap(...)` 成功后
处理顺序建议为:
1. 先判断是否首进场景
2. 若首进:
-`sceneId` 写入 `openedSceneChapterIds`
- 检查当前场景是否已有未结清的章节任务
- 若没有,则在 `questFlow.ts` 内生成章节任务
- 写入 `chapterState(stage = opening)`
- 触发章节 pulse
3. 再刷新 `goalStack / chapterState / journeyBeat`
## 7.4 调整 `goalDirector.ts`
当前 `goalDirector.ts` 已经能编译:
- `chapter`
- `quest`
- `journeyBeat`
- `setpiece`
因此这里只需要一个优先级调整:
1. 当前场景若存在匹配 `chapterId` 的章节任务
2. 且它还未 `turned_in`
3. 则优先把它当作当前场景的 `active_contract` / `immediate_step`
这样 Goal Stack 继续复用,不需要再加新层。
## 7.5 调整 `storyChronicle.ts`
建议章节至少写 3 次 chronicle
1. 开章
2. 转折发生
3. 本章收束
这样章节不会只存在于当前一屏,而能进入长期回顾。
---
## 8. 首进场景的标准触发流程
建议标准流程如下:
```text
玩家进入场景
-> 触发 scene_reached
-> 检查 openedSceneChapterIds 是否已包含当前 sceneId
-> 若否:
-> 将当前 sceneId 写入 openedSceneChapterIds
-> 生成 chapterId = chapter:scene:${sceneId}
-> 生成章节任务并直接设为 active
-> 写入当前 ChapterState(stage = opening, sceneId, chapterQuestId)
-> 触发 Goal Pulse: 新章节开启
-> 写入 chronicle 开章记录
-> 若是:
-> 只同步当前章节状态,不重复开任务
-> 随着任务 step 与 signal 推进:
-> ChapterState.stage 依次推进到 expansion / turning_point / climax
-> 当任务 ready_to_turn_in 或 turned_in
-> ChapterState.stage = aftermath
-> 写入余波 chronicle 与下一跳 handoff
```
这一步是整个方案能不能真正成立的关键,因为用户这次要的就是:
**“首次进入某个场景”这一刻,就要像进入新章节一样被系统接住。**
---
## 9. 当前仓库可直接套用的样章示例
下面用当前仓库已经存在的 `宫苑内庭` 说明这套设计怎么落。
当前场景素材来自 `src/data/scenePresets.ts`
- 场景:`宫苑内庭`
- 场景描述:`回廊深处静得过分,花木修得齐整,却处处像埋着王庭旧案。`
- 场景友方 NPC`旧宫侍女`
- 场景线索:`回廊暗格里的香囊``花圃石座下的旧金牌`
- 相邻场景:`铸坊工场``雨夜长街``地宫通道`
可直接编成如下章节:
## 9.1 章节标题
`宫苑内庭·旧痕回廊`
## 9.2 `opening`
- 玩家首次进入 `宫苑内庭`
- `旧宫侍女` 作为开章角色
- 她不给完整解释,只提醒“最近不该过去的回廊”
- 系统自动开启章节任务:`查明内庭异动`
## 9.3 `expansion`
- 玩家需要先调查 `回廊暗格里的香囊` 或处理场景里的敌对压力
- 任务 step 进入“确认这条旧痕到底指向谁”
## 9.4 `turning_point`
- 玩家在第二条线索 `花圃石座下的旧金牌` 中得到反证
- 旧宫侍女此前的说法出现缺口
- 当前理解从“单纯禁区提醒”转成“她知道旧案,但在刻意压着不说”
## 9.5 `climax`
- 玩家返回与 `旧宫侍女` 对话
- 她承认这里只是旧案的一层外壳,并把下一跳 handoff 到:
- `雨夜长街`
-`铸坊工场`
- 当前章节任务进入可交付或已交付
## 9.6 `aftermath`
- `chronicle` 写入本章收束
- 当前章节状态进入余波
- Goal Stack 把下一步交接到新场景或下一段线索
这个例子里的玩家体感仍然是完整的“起承转合”,但系统实现上始终只在跑当前已有的五阶段。
这个例子说明:
**即使大主线还没结束,`宫苑内庭` 这个单独场景也已经能形成一章完整体验。**
---
## 10. MVP 落地顺序
## 阶段 A先补数据层和首进判定
先做:
1. `openedSceneChapterIds`
2. 场景首次进入 hook
3. `chapter:scene:${sceneId}` 的章节 id 规则
验收标准:
- 同一场景只在第一次进入时开章节任务
## 阶段 B把章节任务接到现有 questFlow
先做:
1. `buildChapterQuestForScene(...)`
2. `chapterId`
3. 场景 lead 与当前 quest step 的默认映射
验收标准:
- 章节任务能在现有 `steps + status` 下正常推进
## 阶段 C让 chapterDirector 真正按场景章节输出
先做:
1. `ChapterState.sceneId`
2. `ChapterState.chapterQuestId`
3. `chapterDirector` 优先从当前章节任务推导 `stage`
验收标准:
- 当前章节标题与当前场景一致
- 章节五阶段能和任务推进基本同步
## 阶段 D补 NPC 章节职责与 handoff
先做:
1. 为每个场景补默认开章 NPC / 转折线索 / 收束对话
2. 为每个场景补 handoff 规则
3. 回写 `chronicle`
验收标准:
- 每个场景都能给出明确的阶段承担者与下一跳
---
## 11. 验收标准
做到以下几点,才算真正满足这次需求:
1. 玩家首次进入任一可达场景时,系统会自动开启该场景的章节任务。
2. 每个场景章节都能在当前系统里跑出 `opening -> expansion -> turning_point -> climax -> aftermath` 的完整闭环,玩家体感上形成完整的起承转合。
3. 每个场景至少能找到开章、承压、转折、收束这些阶段承担者,允许一人多阶段承担,但阶段职责不能缺。
4. 章节任务不是孤立任务,而是当前章节在前台的动作面。
5. 同一场景重复进入时,不会重复开章,但会继承已存在的章节状态或余波状态。
6. 本章收束后,系统能明确交接下一场景或下一段主线程 lead。
7. 这轮实现主要落在现有 `chapterDirector / questFlow / useStoryGeneration / goalDirector / storyChronicle` 上,不再另起一套章节运行时系统。
---
## 12. 最后结论
如果我们接受“每个场景都是一个章节单元”这条方向,那么当前仓库最该补的不是一套新系统,而是对现有系统的三处收紧:
1. 补上 `openedSceneChapterIds`
2.`ChapterState` 显式绑定 `sceneId + chapterQuestId`
3. 让现有章节任务与现有五阶段直接挂钩
这样之后,现有系统会形成更简洁的收束关系:
- `scenePresets` 提供场景素材
- `questFlow` 直接把场景 lead 落成章节任务
- `chapterDirector` 用现有五阶段输出章节状态
- `useStoryGeneration / sessionActions` 处理首进开章
- `goalDirector` 把章节任务继续编译成玩家当前目标
最终玩家感受到的就不再是“我只是进了一个新场景”,而会更接近:
**我进入了这一章,接住了这一章的任务,见到了这一章该见的人,也在这一章里把一段局势真正走完了。**

View File

@@ -1,423 +0,0 @@
# 陶泥儿品牌 Logo 概念稿
> 本稿是围绕候选产品名“陶泥儿”的品牌视觉探索,不替代当前已冻结的“百梦”正式命名口径。若后续确认更名,需要另起产品命名、前后端文案和商标检索落地方案。
## 1. 品牌定位归纳
“陶泥儿”适合承接的不是传统陶艺或儿童黏土,而是“把灵感塑形成可玩内容”的 AI 创作平台隐喻。
核心关键词:
- 精品:作品不是随手糊出来,而是经过 AI 辅助打磨、可被消费和传播的轻精品内容。
- UGC用户是主要造物者平台降低创作门槛。
- 创作:从一句脑洞、一个梗、一张图,生成小游戏、互动作品或可分享内容。
- 裂变与梗:名字要支持“开捏”“捏个梗”“捏个小游戏”这类用户语言。
- 轻度休闲:体验应松弛、即时、好玩,不走硬核生产工具气质。
- AIAI 是塑形能力,不是冷冰冰的技术标签。
推荐品牌主张:
```text
把脑洞捏成小游戏
```
备选表达:
```text
捏个脑洞,马上开玩
AI 开捏,人人会创作
随手造梗,随心开玩
```
## 2. 生成原则
本轮使用仓库 GPT-image-2 / VectorEngine 工作流生成 Logo 概念图,生成时刻意要求“无文字 Logo 图标”。
原因:
- AI 生图直接生成中文品牌字容易出现笔画错误,不适合作为正式字标。
- 当前阶段更适合先确定图形符号方向,再由设计师或前端继续做矢量化、字标搭配和多尺寸适配。
- 图标需要优先服务 App icon、平台左上角品牌、分享卡片和加载页而不是一次性海报图。
生成文件:
```text
public/branding/taonier-logo-v3-concepts/
├─ taonier-logo-v3-contact-sheet.png
├─ taonier-v3-finger-spark.png
├─ taonier-v3-seed-pop.png
├─ taonier-v3-magic-dot.png
├─ taonier-v3-work-gem.png
└─ taonier-v3-soft-t.png
public/branding/taonier-logo-magic-dot-concepts/
├─ taonier-logo-magic-dot-contact-sheet.png
├─ taonier-magic-dot-orbit.png
├─ taonier-magic-dot-seal.png
├─ taonier-magic-dot-squish.png
├─ taonier-magic-dot-mold.png
└─ taonier-magic-dot-bloom.png
public/branding/taonier-logo-flat-concepts/
├─ taonier-logo-flat-contact-sheet.png
├─ taonier-flat-play-clay.png
├─ taonier-flat-spark-clay.png
├─ taonier-flat-meme-smile.png
├─ taonier-flat-loop-mold.png
└─ taonier-flat-seal-blocks.png
public/branding/taonier-logo-concepts/
├─ taonier-logo-contact-sheet.png
├─ taonier-clay-spark.png
├─ taonier-play-mold.png
├─ taonier-meme-bubble.png
├─ taonier-creation-loop.png
└─ taonier-premium-seal.png
```
生成脚本:
```text
scripts/generate-taonier-logo-concepts.mjs
```
## 3. V3-03 一捏成型延展
这一组专门沿 V3 “一捏成型”继续打磨。目标是保留“两个软形触点 + 中央作品核”的成型瞬间,同时降低括号感、碰撞特效感和功能按钮感。
![陶泥儿 Logo 一捏成型延展总览](../../public/branding/taonier-logo-magic-dot-concepts/taonier-logo-magic-dot-contact-sheet.png)
### 3.1 捏合星核
![捏合星核](../../public/branding/taonier-logo-magic-dot-concepts/taonier-magic-dot-orbit.png)
定位:一捏成型方向的主标首选。
这个方向最稳地保留了“左右合拢、中央成型”的核心动作,中心青绿色星核形成了明确焦点,整体比原 V3-03 更完整,也没有明显播放器、聊天或表情联想。
优点:
- 结构清楚,第一眼能看出“合拢生成”。
- 元素少,小尺寸适配潜力好。
- 中央星核可以做加载、生成成功、发布完成等动效延展。
风险:
- 左右软形仍有一点括号感,后续矢量化可把外轮廓做得更不对称、更像被捏塑的软泥。
建议用途:主 Logo 备选首选、AI 生成按钮、启动动效核心符号。
### 3.2 成型印记
![成型印记](../../public/branding/taonier-logo-magic-dot-concepts/taonier-magic-dot-seal.png)
定位:完整主标感最强的延展方向。
这个方向把左右触点收成一个更完整的软形图腾,减少了“两个括号”的割裂感。视觉上更像独立品牌符号,但也因此少了一点“捏合动作”的即时感。
建议用途:主 Logo 强备选;若选择它,后续应去掉背景底色并强化中心负形星点。
### 3.3 软泥合拍
![软泥合拍](../../public/branding/taonier-logo-magic-dot-concepts/taonier-magic-dot-squish.png)
定位:轻松、年轻、动效友好。
这个方向的上下软形更活泼,适合表达“啪嗒一下成型”。但静态 Logo 中的黄色星点和短线略像特效贴纸,主标使用前需要继续简化。
建议用途:生成中动效、运营图、互动反馈,不建议直接定为主 Logo。
### 3.4 灵感模口
![灵感模口](../../public/branding/taonier-logo-magic-dot-concepts/taonier-magic-dot-mold.png)
定位:最有“模口 / 造物容器”意味。
这个方向图形独特,和“从软泥模口里生成作品”的隐喻贴合。但外形复杂度比 01、02 更高,边缘细节在小尺寸下可能损失。
建议用途:主 Logo 备选探索,适合继续做专业矢量简化。
### 3.5 捏开灵感
![捏开灵感](../../public/branding/taonier-logo-magic-dot-concepts/taonier-magic-dot-bloom.png)
定位:温和、包裹、生成容器。
这个方向亲和、平衡,但整体像眼睛 / 容器 / 开合结构,陶泥儿的“捏”动作弱一些。
建议用途AI 生成入口、等待态、创作容器辅助图形。
## 4. V3 抽象主标候选
V3 根据评审反馈重新避开了五个问题:播放三角、褐色陶土主色、聊天气泡 / 表情包、循环符号,以及过多碎元素。方向转为更抽象、更亮眼、更像长期主 Logo 的符号。
![陶泥儿 Logo V3 概念总览](../../public/branding/taonier-logo-v3-concepts/taonier-logo-v3-contact-sheet.png)
### 4.1 灵感捏痕
![灵感捏痕](../../public/branding/taonier-logo-v3-concepts/taonier-v3-finger-spark.png)
定位:主 Logo 首选。
这个方向用醒目的珊瑚红软形、指纹捏痕和星点负形建立记忆点。它不再依赖“陶泥的褐色”,而是用“被捏过的痕迹”表达陶泥儿的核心动作:用户把脑洞捏成作品。
优点:
- 第一眼足够醒目,远离旧版褐色和播放器感。
- 指纹捏痕有独特性,能承接“人人创作”和“亲手塑形”。
- 元素少,适合继续矢量化和小尺寸适配。
风险:
- 指纹弧线后续需要进一步简化,避免在 24px 以下变糊。
- 星点比例要克制,避免变成普通灵感图标。
建议用途:主 Logo、App icon、平台顶栏、启动页、生成按钮。
### 4.2 脑洞种子
![脑洞种子](../../public/branding/taonier-logo-v3-concepts/taonier-v3-seed-pop.png)
定位:创意生长与新手友好。
这个方向从“灵感发芽”切入,比陶泥更偏创造生命力。它亲和、可爱,但容易让用户联想到教育、植物、儿童启蒙或种植类产品。
建议用途:新手引导、创作孵化、儿童 / 寓教于乐支线,不建议作为主 Logo。
### 4.3 一捏成型
![一捏成型](../../public/branding/taonier-logo-v3-concepts/taonier-v3-magic-dot.png)
定位AI 把灵感合成为作品的瞬间。
这个方向很简洁,用左右两个软形触点和中心星点表达“捏合”。它避开了播放器和聊天气泡,也能做动效,但静态图形目前稍像碰撞特效或括号,需要继续重绘增强独特轮廓。
建议用途生成按钮、AI 施法动效、主 Logo 备选微调方向。
### 4.4 作品胶囊
![作品胶囊](../../public/branding/taonier-logo-v3-concepts/taonier-v3-work-gem.png)
定位:精品内容和作品沉淀。
这个方向更稳、更精品,青绿色也比褐色更吸睛。但整体像水滴、宝石或通用内容图标,和“捏”这个动作的关系弱。
建议用途:精选作品、作品库、创作者中心,不建议优先做主 Logo。
### 4.5 软体 T 形
![软体 T 形](../../public/branding/taonier-logo-v3-concepts/taonier-v3-soft-t.png)
定位:英文辅助名 / Taonier 的抽象首字母。
这个方向试图做更品牌化的抽象符号,但当前形体还不够自然,也未形成足够强的“陶泥儿”心智。若未来英文名确定为 `Taonier` 或类似形式,可以继续沿这个方向做专业字母标重绘。
建议用途:英文标识探索,不作为当前主 Logo 首选。
## 5. V2 扁平矢量候选
第一批图形偏 3D 和拟物,更适合作为吉祥物、运营图或启动页气氛图,不适合作为长期主 Logo。V2 已把约束收紧为扁平、矢量、少元素、强轮廓和小尺寸可识别。
![陶泥儿 Logo 扁平概念总览](../../public/branding/taonier-logo-flat-concepts/taonier-logo-flat-contact-sheet.png)
### 5.1 扁平开捏
![扁平开捏](../../public/branding/taonier-logo-flat-concepts/taonier-flat-play-clay.png)
定位:最直接的主 Logo 候选。
这个方向用一团柔软陶泥承载播放符号,用户一眼能理解“点开玩 / 马上玩”,同时外形保留“捏出来”的不规则软泥感。
优点:
- 识别速度最快,移动端小尺寸也成立。
- 符合主流 App Logo 语言,亲和、不重、不技术冷。
- 和“把脑洞捏成小游戏”的主张绑定最强。
风险:
- 播放符号是常见母题,后续矢量化时要通过不规则软泥外轮廓、颜色和字标形成独特资产。
建议用途:主 Logo 首选、App icon、平台顶栏、分享卡片角标。
### 5.2 灵感泥星
![灵感泥星](../../public/branding/taonier-logo-flat-concepts/taonier-flat-spark-clay.png)
定位AI 创作与灵感生成。
这个方向比“扁平开捏”更品牌化中心负形星点表达灵感、AI 生成和创意爆发。它没有播放符号那么直白,但更容易和“陶泥儿”的创作平台气质绑定。
优点:
- 图形更简洁,品牌记忆点强。
- 陶泥心智、AI 灵感和精品感比较平衡。
- 适合未来扩成字标、启动页和生成态动效。
风险:
- 对“小游戏/马上玩”的表达弱于播放符号。
建议用途:主 Logo 强备选、创作首页、AI 生成按钮和品牌主视觉。
### 5.3 造梗笑泥
![造梗笑泥](../../public/branding/taonier-logo-flat-concepts/taonier-flat-meme-smile.png)
定位:社交传播和玩梗亲和力。
这个方向的气泡与笑脸非常亲和,适合表达“分享快乐”和“造梗”。但它和聊天、社区类产品的通用图形过近,作为主 Logo 可能会让用户误判产品品类。
建议用途:社区、评论、分享、活动贴纸,不建议做主 Logo。
### 5.4 共创泥环
![共创泥环](../../public/branding/taonier-logo-flat-concepts/taonier-flat-loop-mold.png)
定位AI 与用户共创闭环。
这个方向表达共创与循环,但生成结果带有偏柔和彩虹渐变的视觉倾向,与“陶泥儿”的软泥名称关联不够直观,也不如 01/02 容易记住。
建议用途:创作流程、共创能力、生成进度辅助图形。
### 5.5 精品泥印
![精品泥印](../../public/branding/taonier-logo-flat-concepts/taonier-flat-seal-blocks.png)
定位:精品作品和内容集合。
这个方向像内容平台或作品库入口,能表达图片、用户、游戏等多形态内容。但图形元素较多,主标识别不如 01/02 凝练。
建议用途:精选作品、作品集、创作者中心、内容品质标识。
## 6. V1 立体探索
### 6.1 灵感陶团
![灵感陶团](../../public/branding/taonier-logo-concepts/taonier-clay-spark.png)
定位AI 共创与灵感造物。
这个方向把“陶泥”作为主视觉,内部用发光火花和节点表达 AI 赋能。它最贴“陶泥儿”名字本身,也能说明平台不是普通小游戏集合,而是从灵感生成作品的创作容器。
优点:
- 与“陶泥儿”的名称绑定最强。
- 有 AI、创作、造物的综合含义。
- 适合启动页、品牌介绍、创作首页空状态。
风险:
- 小尺寸下细节偏多,需要后续矢量化时压缩节点和纹理。
- 如果色彩处理不当,会回到手工陶艺联想。
建议用途:品牌主视觉备选、官网/启动页、创作入口图形。
### 6.2 开玩模具
![开玩模具](../../public/branding/taonier-logo-concepts/taonier-play-mold.png)
定位:把脑洞捏成小游戏。
这个方向用软陶捏出播放符号,最直接地连接“创作”和“马上玩”。它比单纯陶泥团更有产品动作,也更适合轻休闲、小游戏、短内容传播。
优点:
- 识别强,小尺寸也清楚。
- 与轻度休闲小游戏的关系最直接。
- 适合作为 App icon 和主 Logo 图形。
风险:
- 播放符号相对常见,需要后续在外轮廓、捏痕和色彩上做独特性。
- 如果三角形过硬,会削弱“陶泥儿”的柔软感。
建议用途:主 Logo 首选、App icon、分享卡片角标、加载态图形。
### 6.3 造梗气泡
![造梗气泡](../../public/branding/taonier-logo-concepts/taonier-meme-bubble.png)
定位:社交传播、玩梗、裂变。
这个方向把陶泥变形成聊天气泡和表情,强调“梗”和“传播”。它最有社交平台感,也适合表情包、活动贴纸和运营视觉。
优点:
- 传播感强,年轻、轻松、容易做 IP 化。
- 能承接社区、评论、分享和玩梗场景。
- 比较容易延展成贴纸和表情包。
风险:
- 偏软萌,可能削弱“精品 AI 创作平台”的质感。
- 作为主 Logo 容易显得像聊天或表情产品。
建议用途社区模块、活动运营、IP 辅助形象,不建议作为唯一主 Logo。
### 6.4 共创回路
![共创回路](../../public/branding/taonier-logo-concepts/taonier-creation-loop.png)
定位AI 与用户共同迭代生成。
这个方向用软陶带形成循环和造物轨迹,表达“灵感 -> AI 塑形 -> 用户修改 -> 作品传播”的闭环。它比其他方向更抽象,也更有平台级和工具级气质。
优点:
- 高级、简洁,避免儿童化。
- 适合表达 AI 共创、迭代和作品循环。
- 可用于创作者工作台或生成进度标识。
风险:
- 与“陶泥儿”名称的直观关联较弱。
- 缺少小游戏和玩梗的即时识别。
建议用途创作流程标识、AI 共创能力图标、品牌辅助图形。
### 6.5 精品泥印
![精品泥印](../../public/branding/taonier-logo-concepts/taonier-premium-seal.png)
定位:精品内容、作品认证、创作者成果。
这个方向像一个被压印的软陶徽章,中间有方块和火花,比较适合表达“作品被打磨成型”。它的内容平台感强于游戏入口感。
优点:
- 精品感和作品库气质较强。
- 适合作品认证、精选、创作者徽章。
- 与“陶泥压印”隐喻相对自然。
风险:
- 细节较多,主 Logo 小尺寸可读性不如“开玩模具”。
- 徽章感偏静态,轻休闲的即时性稍弱。
建议用途:精选作品标识、创作者荣誉、内容品质标签。
## 7. 推荐结论
优先级建议:
```text
主 Logo 首选V3 01 灵感捏痕
一捏成型首选V3-03 延展 01 捏合星核
完整主标备选V3-03 延展 02 成型印记
英文标识探索V3 05 软体 T 形
精品内容辅助V3 04 作品胶囊
新手 / 寓教于乐辅助V3 02 脑洞种子
```
若要兼顾主流、亲和、醒目和“陶泥儿”的动作隐喻,优先继续打磨 V3 “灵感捏痕”。
若想把 Logo 做得更抽象、更像 AI 生成瞬间,可以继续打磨 V3-03 延展中的“捏合星核”或“成型印记”。
V1 的 3D 图标不建议直接作为主 Logo只适合做运营图、吉祥物探索或风格参考V2 的播放、气泡、碎元素方向本轮已降级为历史探索。
## 8. 后续落地建议
1. 先围绕 V3 “灵感捏痕”做 3 到 5 个专业矢量微调版:减少指纹线条、强化软形轮廓、测试深色 / 浅色底。
2. 同步对 V3-03 “捏合星核”做一版更独特的轮廓重绘,弱化括号感,保留中央成型星核。
3. 字标不要直接使用生图结果,应单独设计“陶泥儿”中文字标,并准备英文辅助名。
4. 正式应用前做商标近似检索,重点覆盖第 9、35、38、41、42 类。
5. 若确认替换“百梦”再更新现有命名规范文档、前端品牌组件、HTML metadata、后台和后端默认文案。

View File

@@ -1,93 +0,0 @@
# 统一模态窗口设计 2026-04-25
## 背景
当前前端已有两套稳定视觉资产:
- 平台侧:`platform-overlay``platform-modal-shell``platform-auth-card` 等主题变量。
- RPG 运行时:`pixel-nine-slice``pixel-modal-shell``UI_CHROME.modalPanel` 九宫格边框。
但弹窗结构仍分散在业务组件内,常见重复包括遮罩层、点击遮罩关闭、`role="dialog"``aria-modal`、移动端底部贴边、桌面居中、最大高度、滚动区域和关闭按钮。新增弹窗时容易出现 z-index、无障碍属性、移动端高度和视觉边界不一致。
## 目标
新增统一组件 `UnifiedModal`,只负责弹窗外壳和交互边界,不接管业务内容:
- 统一遮罩、面板、标题区、内容区、底部区结构。
- 支持平台风与像素风两种外观,不混用两套视觉资产。
- 默认移动端优先,平台风移动端底部弹出、桌面居中;像素风保持游戏内居中弹窗。
- 默认提供 `role="dialog"``aria-modal`、标题关联、Escape 关闭和遮罩点击关闭。
- 支持禁用关闭,用于生成中、保存中等不可打断流程。
- 支持 Portal 渲染到 `document.body`,避免被父层 `overflow` 裁剪。
## 非目标
- 不一次性迁移所有旧弹窗,避免运行时大面积回归。
- 不把业务按钮、表单、状态文案放进通用组件。
- 不改变现有主题变量、九宫格素材、平台和 RPG 的视觉风格。
- 不新增第三方弹窗库。
## 组件接口
`UnifiedModal` 核心参数:
| 参数 | 说明 |
| --- | --- |
| `open` | 是否显示。为 `false` 时返回 `null`。 |
| `variant` | `platform``pixel`。默认 `platform`。 |
| `title` | 标题,同时作为默认 `aria-label` 来源。 |
| `description` | 可选副标题,显示在标题下方。 |
| `children` | 主内容区。 |
| `footer` | 可选底部操作区。 |
| `onClose` | 关闭回调。 |
| `closeDisabled` | 禁止遮罩、Escape 和关闭按钮触发关闭。 |
| `closeOnBackdrop` | 是否允许点击遮罩关闭,默认允许。 |
| `showCloseButton` | 是否显示右上关闭按钮,默认显示。 |
| `size` | `sm``md``lg``xl``fullscreen`。 |
| `zIndexClassName` | z-index class默认 `z-[90]`。 |
| `panelClassName` / `bodyClassName` / `footerClassName` | 局部样式扩展。 |
| `portal` | 是否渲染到 `document.body`,默认开启。 |
## 使用边界
### 平台风弹窗
用于平台首页、登录注册、作品结果、创作工作台等非 RPG 运行时界面。
要求:
- 使用 `variant="platform"`
- 面板使用 `platform-modal-shell` 主题变量。
- 移动端优先底部贴边,大屏居中。
- 不在弹窗内放功能说明式长文案,只放任务所需信息。
### 像素风弹窗
用于 RPG 运行时、地图、背包、角色详情、NPC 交易等游戏内面板。
要求:
- 使用 `variant="pixel"`
- 面板使用 `pixel-nine-slice pixel-modal-shell`
- 默认使用 `getNineSliceStyle(UI_CHROME.modalPanel)`
- 标题、内容和底部仍由业务方控制,避免通用组件内写入玩法解释。
## 首批迁移
首批只迁移平台入口创作类型弹窗:
- 文件:`src/components/platform-entry/PlatformEntryCreationTypeModal.tsx`
- 目的:验证平台风布局、关闭禁用、标题区、内容区与错误区都可由统一组件承载。
后续可按风险由低到高迁移:
1. 结果页小弹窗:`PuzzleResultView``BigFishResultView`
2. 平台创作页编辑器弹窗:`RpgCreationEntityEditorShared` 内局部 `ModalShell`
3. RPG 运行时像素风弹窗:`RpgAdventurePanelOverlays``AdventureEntityModal``NpcModals`
## 验收标准
- 新增弹窗优先使用 `UnifiedModal`,不再手写完整 overlay + panel 结构。
- 迁移后的弹窗保留原有移动端和桌面布局。
- 关闭按钮、遮罩关闭、Escape 行为一致,`closeDisabled` 时都不会关闭。
- 类型检查、编码检查通过。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 943 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 999 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -1,5 +0,0 @@
生成一张适合商业路演 PPT 使用的横版高清主视觉配图,主题是“陶泥:将 Harness Engineering 理论、专家知识、多 Agent 调度融入 AI 创作工具与 AI 原生游戏框架”。
画面构图:深色但清爽的现代科技工作室/游戏创作中枢,中心是一个温润陶泥质感的抽象核心装置,核心装置连接两条清晰的知识管线。左侧表现 AI 创作工具:多 Agent 协作节点、策划 SOP、美术 SOP、模板框架、剧本/数值/系统/角色/场景/CG 等垂类任务以图标化模块呈现。右侧表现 AI 原生游戏框架:实时剧情生成、数值与剧情对齐、画面与剧情对齐、实时任务与物品生成,以游戏世界投影、角色剪影、场景画面和规则约束网格呈现。
底部远景加入克制的学术评审氛围:讲台、投影、评审席、优秀课程设计的荣誉感,但不要出现真实学校校徽、真实教授肖像或任何可识别真实人物。
视觉风格:高端产品发布会 Key Visual精致 3D 插画结合轻量界面光效,专业、可信、面向 AI 游戏与创作者工具,色彩使用陶土暖色、青绿色、冷白光和少量金色点缀,信息结构清楚,主体居中偏左,右上和左上保留干净留白用于 PPT 叠加标题。
要求:无文字、无字母、无水印、无 UI 按钮、无品牌 logo、无真实校徽、无真实人物肖像、避免杂乱、避免过暗、避免卡通幼稚、避免纯抽象背景。

View File

@@ -1,192 +0,0 @@
# NPC 对话阶段与情景注入草案
## 目标
让 NPC 对话同时受三类因素控制,而不是只靠一大段 prompt 生硬约束:
1. 好感与信任阶段
2. 角色三维表述风格
3. 当前情景与刚刚共同经历
最终目标不是“少说设定”,而是“像真人一样按场合和关系逐步说”。
## 当前已经落地的控制层
### 1. 好感阶段
- `guarded`
- 低好感
- 只谈眼前局势、态度和试探
- `partial`
- 开始松口
- 给出表层理由或半真半假的说明
- `honest`
- 逐步触及真实动机的轮廓
- `deep`
- 可以谈更深层的来历、目标和旧事
### 2. 三维表述风格
- `guardStyle`
- `blunt` 直硬
- `wary` 谨慎
- `evasive` 回避
- `measured` 克制
- `warmStyle`
- `dry` 冷淡
- `steady` 平稳
- `gentle` 温和
- `teasing` 带点松弛感
- `truthStyle`
- `direct` 说真话时直给
- `fragmented` 碎片式透露
- `deflecting` 先绕一下再说
### 3. 情景注入
当前新增的情景标签:
- `first_contact_cautious`
- 初见试探
- `camp_first_contact`
- 营地第一轮正式对接
- `camp_followup`
- 营地里承接上轮旧话头
- `post_battle_breath`
- 刚打完一轮冲突后的短暂松动
- `shared_danger_coordination`
- 危险未解除,优先短句对接
- `private_followup`
- 已经聊过一轮,不再是模板式初见
配套压力标签:
- `high`
- `medium`
- `low`
并补充:
- `recentSharedEvent`
- 刚刚共同经历了什么
- `talkPriority`
- 这轮优先先说什么
## 设计原则
### 1. 把“知道什么”和“愿意说什么”拆开
角色完整设定始终存在,但 prompt 不应长期直接暴露:
- `reason`
- `goal`
- 完整背景
- 旧事全貌
而是按阶段只注入:
- `surfaceHook`
- `immediateConcern`
- `guardedMotive`
- `reason`
- `goal`
### 2. 初见先谈现场,不先谈人生
无论玩家还是 NPC初次见面都优先
- 眼前危险
- 当前判断
- 彼此态度
- 一点没说透的钩子
不优先:
- 完整来意
- 长篇背景
- “我们的目标一致”
- 正式自我介绍
### 3. 刚打完怪时优先短句
`post_battle_breath``shared_danger_coordination` 两类情景下,对话应该:
- 先接刚才发生的事
- 先评价判断或身手
- 句子更短
- 少做完整背景说明
## 当前实现路径
### 上下文字段
`StoryGenerationContext` 目前已经承载:
- 对话阶段控制
- 三维风格
- 情景标签
- 压力级别
- 最近共同经历
- 本轮说话重点
### prompt 注入
当前 prompt 会显式加入:
- 当前 NPC 对话阶段控制
- 当前对话情景控制
目的:
- 不让模型自己从一堆底层状态里猜场合
- 先让系统做好裁决,再让模型负责“怎么说”
## 下一步建议
### 1. 把 `surfaceHook` 改成更口语、更像现场句
当前最大风险不是结构不够,而是字段文案还可能像“作者说明”。
应优先改成:
- 能直接对人说
- 不像自我介绍
- 不像任务摘要
- 更像“站在现场会脱口而出的话”
### 2. 引入“问题命中”判断
不只看好感,也看玩家这次是不是问到了点上。
建议:
- 好感够,但问题没命中 -> 仍保留
- 好感够,问题命中 -> 松口一层
### 3. 使用 `revealedFacts`
后续可把已公开的信息记下来,避免:
- 重复卖同一个关子
- 前后口径反复横跳
### 4. 把开场第一段从通用剧情生成中进一步拆出
现在开场仍部分受通用剧情引擎影响。
更理想的方向:
- 开场先走纯对白生成
- 对话定稿后再推导后续选项
这样语言会比“剧情导演 + JSON + 选项合法性”混合生成更自然。
## 验收重点
改完后重点看这几类表现是否成立:
1. 初见不再像互背设定卡
2. 刚打完怪时,对话明显更短、更贴眼前
3. 同一阶段下,不同性格角色表达方式确实不同
4. 玩家和 NPC 都不会在第一轮自曝完整动机
5. 同一个 NPC 连续几轮聊天时,信息释放节奏是连续的,不会忽冷忽热

View File

@@ -1,20 +0,0 @@
# AdminTaskConfigPage 埋点范围收口记录2026-05-04
## 变更目标
- 前端隐藏「埋点范围」选择项。
- 保存任务配置时固定传递 `scopeKind=user`,避免运营在后台选择其他范围。
## 落地结果
- `apps/admin-web/src/pages/AdminTaskConfigPage.tsx`
- 移除了「埋点范围」下拉选择 UI。
- 保存时改为固定提交 `scopeKind: 'user'`
- 回填表单时不再读取或维护 `scopeKind` 的可编辑状态。
## 验证结果
- 已检查页面源码,确认不再存在「埋点范围」字段与可编辑逻辑。
- 已执行 `npm run admin-web:typecheck`
- 当前验证失败原因为环境缺少 `node_modules/typescript/bin/tsc`,属于依赖安装状态问题,不是本次前端改动引入的代码错误。
## 遗留说明
- `apps/admin-web/src/api/adminApiTypes.ts` 中仍保留 `TrackingScopeKind` 类型,供接口契约与其他前端模块兼容使用。
- `apps/admin-web/src/config/trackingEventDefinitions.ts` 仍保留事件定义中的 `scopeKind` 元数据,但页面不再允许运营修改。

View File

@@ -1,292 +0,0 @@
# 冒险运行时开发经验沉淀
日期2026-03-24
## 1. 这轮工作主要覆盖了什么
这轮迭代不是单点 UI 修改,而是同时改了 4 条链路:
1. 战斗演绎链路:近战突进、远程施法、投射物/特效、受击结算时机。
2. 探索链路向前探索、呼喊、NPC 离队、切换场景时的新 encounter 生成与入场动画。
3. NPC function 链路:聊天、招募、观察征兆、交易、离队、上下文注入边界。
4. 面板链路:队伍页、角色详情、背包页、移动端空间分配、入口 icon 语义。
这类项目最重要的判断是:
很多需求表面看是“改个界面”或“改句文案”实际同时影响状态机、动画时序、AI 提示词、实体生成规则和移动端布局。
## 2. 这轮最值得沉淀的结论
### 2.1 function 的职责边界必须先定死
后面事实证明,只有少数 function 应该负责生成新 encounter
- `idle_explore_forward`
- `idle_call_out`
- `npc_leave`
- 进入新场景
其他 function 如果也偷偷新增 NPC、怪物、宝箱后面一定会出现这些问题
- 剧情上下文越来越乱
- 场景上实体数量失控
- 玩家以为“聊天/招募/观察”也会强行推进世界
- AI 生成内容和本地规则互相打架
经验:
先定义“谁能创造世界实体”,再写文案和动画。
### 2.2 动画完成点必须和数值结算点绑定
这轮一个关键修正是:
攻击特效播放完后,受击目标才扣血。
如果不这样做,玩家看到的就是:
- 特效还没打到,血条先掉了
- 投射物还在飞,目标已经结算
- dash 还没到位,攻击已经命中
经验:
战斗系统里不能只写“播放动画”,必须写清楚:
1. 起手阶段是什么
2. 位移阶段是什么
3. 命中阶段是什么
4. 哪个阶段结束后才改数值
### 2.3 视觉素材选择要按“语义 + 小尺寸可读性”来做
这轮做过两类视觉判断:
- 战斗特效素材该怎么挂到角色/技能上
- 冒险页入口 icon 应该换成什么
经验不是“素材越花越好”,而是:
- 先看素材本身表达的动作语义
- 再看缩小到按钮尺寸后是否还能一眼看懂
- 最后才考虑风格统一
例如:
- 队伍入口用“盔甲”不如用“盾牌”更像角色/编队
- 背包入口用真正的包袋图,比木板式 HUD 图更直观
- 远程施法不移动时,特效就必须承担“动作已经发生”的视觉职责
### 2.4 角色型 NPC 和普通 encounter 不能混着处理
这轮踩过的坑说明:
- 可扮演但未入队的 NPC
- 已入队同伴
- 普通场景 NPC
这三者虽然都可能显示为 NPC但数据完整度、立绘来源、详情页能力并不一样。
经验:
- 角色型 NPC 要按角色数据链路渲染
- 普通 NPC 要按 encounter/NPC state 渲染
- 详情弹窗必须对缺失字段做空值保护
否则就会出现点击小人直接报 `map` 相关错误这类问题。
### 2.5 角色和同伴的朝向/位移必须彼此独立
`idle_observe_signs` 的修正说明了一件事:
如果主角和同伴在 transform、朝向、随机停顿上仍然存在隐含父子级关系最后视觉表现一定不自然。
经验:
- 主角随机转向和同伴随机转向要分别计算
- 位置、朝向、停留时长都要独立采样
- 不要让父容器顺手把子角色的朝向也带过去
这条经验同样适用于:
- 招募入队时的新同伴 dash
- 多角色待机观察
- 场景内多实体同步演绎
### 2.6 大模型负责“推理与叙述”,本地规则负责“世界变更”
这轮对 `npc_chat``npc_recruit``idle_observe_signs` 的调整,本质上都指向同一个原则:
- 大模型可以生成观察结果、聊天内容、氛围描述
- 大模型不应该偷偷新增实体、替玩家做决定、绕开本地规则结算
经验:
1. 聊天生成要明确禁止提及其他 function 行为。
2. 招募对话可以引导到成功入队,但最终入队仍应由本地流程触发。
3. `idle_observe_signs` 可以走大模型推理,但写入剧情上下文的内容要可控。
4. `npc_recruit` 这种流程不要顺手给下次推理塞一个“新 NPC”。
一句话总结:
AI 可以解释世界,但不能私自改世界。
### 2.7 移动端面板要优先保信息密度,不要保装饰
这轮背包和队伍页的调整很能说明问题:
- 队伍页只保留成员列表
- 成员详情放到弹窗
- 背包页去掉多余标题、提示文案、厚重背景框
- 装备信息移到角色详情,不和背包抢空间
经验:
- 主面板只放“高频扫读信息”
- 低频详情放二级弹窗
- 任何重复信息都要尽量去重
在小屏上,空间不是拿来“显得完整”的,而是拿来“保证可操作”的。
## 3. 这轮最典型的踩坑记录
### 3.1 encounter 生成距离只改一处是不够的
一开始只是把某个函数里的怪物生成位置往后挪,但后来发现:
- 其他会生成 encounter 的 function 仍然太近
- 新场景进入时也可能直接出现在屏幕里
经验:
“从屏幕外进场、要走 4 秒距离”必须是统一约束,不是某个函数里的特判。
### 3.2 详情文案改源头不一定等于改到了最终显示层
这轮“选项小字太长”的问题最后证明:
真正该改的是渲染层显示的 `detailText`,而不是只改某个上游数据源。
经验:
用户说“没有生效”时,要优先检查最终渲染层,而不是只检查中间数据。
### 3.3 新同伴入队时,尺寸问题本质是缩放基准不统一
招募流程里位置正确但大小不对,说明问题不在路径,而在:
- 新同伴使用的缩放基准
- 主角扮演角色使用的缩放基准
不一致。
经验:
只要角色加入到主队镜头体系里,尺寸基准必须复用主角/同伴那套规则,不能临时另开一套。
### 3.4 背包和角色详情职责不清,会持续挤压移动端布局
这轮背包页一度同时承担:
- 物品网格
- 装备总览
- 装备卸下
- 详情查看
最后结果就是移动端空间不够、信息层级混乱。
经验:
- 背包负责“物品”
- 角色详情负责“装备”
职责一旦分清,后面很多 UI 冲突会自然消失。
## 4. 可复用的开发方法
### 4.1 新增一个 function 前,先问 6 个问题
1. 它会不会生成新 encounter
2. 它会不会写入后续推理上下文?
3. 它的动画分几段?
4. 哪一刻才算真正生效?
5. 哪些内容由 AI 生成,哪些必须本地决定?
6. 它是否需要从屏幕外入场?
这 6 个问题先答清,后面返工会少很多。
### 4.2 配战斗动画时,至少明确 4 个时点
1. 起手
2. 位移/施法
3. 特效命中
4. 扣血/击退/结算
特别是:
- 近战攻击前的 dash
- 远程不移动但要挂特效
- 受击延后到命中特效之后
都属于这套时点设计。
### 4.3 做素材替换时,不要只看资源名
正确顺序更接近:
1. 打开素材看实际形状
2. 判断它在当前语义里是不是“最像这个功能”
3. 缩到真实 UI 尺寸再看一遍
4. 再决定 active/inactive 怎么处理
### 4.4 做聊天流式界面时,反馈一定要插到正在发生的流里
这轮加“好感度 +x”提示后更明确了一件事
- 系统反馈不应该只藏在最终结果
- 要插进聊天进行中的体验里
- 文本流超出剧情框时要自动滚动
否则用户会错过真正重要的状态变化。
## 5. 以后继续做这类需求时,建议坚持的原则
- 先收拢 function 边界,再改剧情文案。
- 先确定动画结算时序,再接特效素材。
- 先做本地规则兜底,再让大模型生成文本。
- 先保证移动端可扫读,再考虑装饰性面板。
- 先复用已有角色/场景坐标体系,再做个别修正。
- 先看最终显示层,再判断“改动是否生效”。
## 6. 这轮最重要的一句话总结
这类 AI 冒险 RPG 的开发,最难的不是“把功能做出来”,而是:
**让 function 边界、世界状态、视觉演绎、移动端面板和大模型文本在同一套规则下稳定协作。**
## 7. 聊天输入区布局补充经验
### 7.1 聊天框变大时,要优先增加“消息展示区”而不是只放大输入框
- 玩家感知里的“聊天框高度”主要来自消息气泡和剧情滚动区,不是输入栏本身。
- 如果只把输入框做高,实际会压缩选项区和底部按钮区,移动端反而更挤。
- 更稳妥的做法是:
先让剧情/聊天滚动区在剩余空间里拿到更高的伸缩优先级,再微调输入条高度和底部留白。
- 移动端不要随意给聊天区写死过大的最小高度,否则很容易把选项按钮和自定义输入一起挤出首屏。
### 7.2 底部输入区要向安全区贴近,但不能直接贴死
- 自定义输入要更贴近屏幕底部,应该缩小底部控制区的额外 padding而不是去掉安全区。
- `env(safe-area-inset-bottom)` 仍然要保留,否则刘海屏、手势条机型会出现输入框被顶起或遮挡的问题。
- 正确方向是:
保留安全区补偿,只减少设计层自己额外加上的底部留白。
### 7.3 底部操作区下沉时,要同步增加和聊天区之间的呼吸感
- 当“队伍 / 背包 / 换一换 / 退出聊天 / 自定义输入”整体下移后,上下区块更容易挤在一起。
- 这时要略微增加聊天区和操作区之间的垂直间距,保证视觉层级仍然清楚。
- 目标不是做出更厚的面板,而是让用户一眼分清:
上面是正在发生的对话,下面是马上可点的操作。
## 8. 战斗态底部面板布局经验
### 8.1 战斗态不应继续保留剧情框占位
- 战斗画面里玩家关注的是敌我状态与可执行动作,剧情文本框如果继续占据底部高度,会直接挤压操作按钮。
- 战斗态应隐藏剧情框组件,只保留操作区;战斗结果叙事可放到结算或下一次非战斗剧情里展示。
### 8.2 战斗选项数量要由剩余高度决定
- 不要固定渲染全部战斗选项,否则移动端低高度屏幕会把按钮挤出可点击区域。
- 更稳妥的做法是测量底部操作区可用高度,用单个按钮的最小高度和间距计算本帧可显示数量。
- 至少保留 1 个操作,避免极端高度下玩家看不到任何战斗入口。

View File

@@ -1,18 +0,0 @@
# Agent 空会话草稿可见性修正 2026-04-26
用户从创作中心点进 RPG 或大鱼吃小鱼工作台时,后端会立即创建 Agent session并写入一条助手欢迎消息。但在用户尚未发送任何消息、也没有传入种子文本时这个 session 只是临时工作区,不应进入“我的创作”草稿列表。
本次规则:
1. 只有存在用户消息、非空 seedText、真实草稿数据或已发布状态时Agent session 才算作品草稿。
2. 助手欢迎消息、默认 anchorPack、空 `{}` draftProfile 不算用户创作内容。
3. 过滤必须落在后端 works 聚合层,前端创作中心只消费结果,不负责隐藏空草稿。
4. RPG 仍保留已发布 profile 和孤立持久草稿 profile 的展示;未发布且仍有活跃 Agent session 的编译 profile 继续去重。
涉及入口:
- `server-rs/crates/spacetime-module/src/lib.rs`
- `server-rs/crates/spacetime-module/src/custom_world/mod.rs`
- `server-rs/crates/spacetime-module/src/big_fish/session.rs`
后续如果新增玩法创作 Agent也必须复用同一判断创建会话不等于创建草稿作品列表只展示已经被用户实际开始编辑或已经生成结果的会话。

View File

@@ -1,232 +0,0 @@
# UI 改动记录(供后续 Agent 阅读)
本文档汇总 **像素 RPG UI 皮肤化** 相关实现与决策,便于新会话快速接手。更细的命名与规范见同目录上一级的 **`UI_CODING_STANDARD.md`**。
---
## 1. 相关文件一览
| 路径 | 作用 |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `UI_CODING_STANDARD.md` | 资源目录约定、9-slice 规则、图标语义、`Icons`/`UI` 命名解读、已知问题(含世界按钮切片) |
| `src/uiAssets.ts` | **唯一推荐** 的 UI 资源映射:`UI_CHROME`9-slice 配置)、`TAB_ICONS``WORLD_SELECT_ICONS``getNineSliceStyle()` |
| `src/components/PixelIcon.tsx` | 小图标 `<img>``image-rendering: pixelated` |
| `src/index.css` | `.pixel-nine-slice``.pixel-root-shell` / `.pixel-app-shell`、tab/按钮布局类、`--ui-scale` |
| `src/App.tsx` | 世界选择、角色卡、底部 tab、剧情/背包面板、地图弹窗、`MudMapRoom` |
| `src/components/GameCanvas.tsx` | 场景名按钮9-slice `Title_frame_m` |
| `vite.config.ts` | `root` / `envDir` 指向 `__dirname`,保证 `.env.local` 从项目根加载 |
| `public/UI/``public/Icons/` | 静态资源(路径以 `/UI/...``/Icons/...` 引用) |
---
## 2. 架构要点
### 2.1 9-slice禁止整图 `background-size: 100% 100%` 拉框体)
- 使用 CSS **`border-image`** + **`fill`**,通过自定义属性注入切片参数。
- 类名:**`pixel-nine-slice`**(定义在 `src/index.css`)。
- 行内样式由 **`getNineSliceStyle(texture, overrides?)`** 生成(`src/uiAssets.ts`),设置 `--frame-src``--slice-*``--frame-pad-*``--frame-repeat`、可选 `--frame-base`(一般仅在图源中心确实透明时使用)。
### 2.2 响应与缩放
- `:root`**`--ui-scale`**`clamp`(桌面)+ 小屏媒体查询内固定约 `0.8`
- 边框宽度 = 切片像素 × `--ui-scale`,与 padding 同逻辑,避免移动端边框比例失控。
### 2.3 交互
- **`pixel-pressable`**hover 用 `translateY` + `brightness`**避免**对像素框体做 `scale()` 以免糊边。
### 2.4 全局背景
- **根布局**`App.tsx` 最外层):`pixel-root-shell` + `Background_fill.png` 平铺 + 深色半透明渐变(替代纯 `#050505` 死底)。
- **下半屏内容区** `pixel-app-shell`:同一纹理 + **更轻**的遮罩,让纹理更明显。
- 开局标题区已去掉单独 `bg-zinc-950`,与根背景一致。
---
## 3. `UI_CHROME` 当前用途(速查)
以下为 `src/uiAssets.ts` 中主要键与界面位置的对应关系(切片数值以文件内为准):
| Key | 资源(示例) | 用途 |
| ----------------------------------------- | ------------------------------------- | ------------------------------------------ |
| `appBackground` | `Background_fill.png` | 根壳 + 下半屏平铺底 |
| `worldButtonWuxia` / `worldButtonXianxia` | `1_orange_button` / `1_violet_button` | 开局武侠/仙侠(**条高 28px**,切片见下文) |
| `characterCardFrame` | `pick_hero_frame` | 选角卡片 |
| `tabActive` / `tabInactive` | `Shop_tab_picked` / `Shop_tab` | 底部「角色 / 冒险 / 背包」 |
| `panel` | `Frame_bg_big_2` | 装备区等通用面板 |
| `storyPanel` | `Dialogue_frame` | 剧情正文区 |
| `inventoryPanel` | `Inventory_bg` | 背包条目 |
| `statsPanel` | `Stats_bar` | 角色数值面板 |
| `choiceButton` | `Options_bar` | 剧情选项按钮 |
| `modalPanel` | `Popup_window` | 地图弹窗外壳 |
| `infoPanel` | `Dialogue_frame` | 地图弹窗内「当前地点 / 可前往」信息块 |
| `sceneTitle` | `Title_frame_m` | 战斗画布顶部场景名按钮 |
| `mapRoomCell` | `Map_frame` | 地图节点卡片(`MudMapRoom` |
| `mapDiagramPanel` | `Frame_bg_big_2` | 地图关系图整体衬底 |
图标路径:`TAB_ICONS``WORLD_SELECT_ICONS``CHROME_ICONS`;装备槽与背包分类见 `getEquipmentSlotIcon` / `getInventoryCategoryIcon`
---
## 4. 已知坑(必读)
### 4.1 世界选择按钮「中间发空」
- 图源 **`1_orange_button.png` / `1_violet_button.png` 为 125×28**。
-**`slice.top + slice.bottom >= 28`**,中间横带高度为 **0**`border-image``fill` 异常,看起来像透明/没画上。
- **正确做法**:减小上下切片(当前为 **9+9**),并配合 `repeat: 'stretch'` 竖向拉伸条形成品。
- **不推荐**:用大色块 `baseColor` 糊底(与素材内渐变/内框不一致)。详见 `UI_CODING_STANDARD.md`「已知问题」。
### 4.2 新增 9-slice 前
先读 PNG **宽高**,保证 **`top+bottom < height`** 且 **`left+right < width`**。
---
## 5. 环境与运行(与 UI 无关但易踩坑)
- LLM`VITE_LLM_*` 写在项目根 **`.env.local`**;因 `vite.config.ts` 固定 `envDir: __dirname`,从任意 cwd 启动也应能读到。
- 开发:`npm run dev`(默认端口见 `package.json`)。
---
## 6. 尚未统一成 UI 图、仍用 Tailwind 小块面的区域(可选后续)
若产品要求「全盘无纯色板」,可继续替换,当前仍可能为 `bg-black/20``rounded-lg border` 等:
- 角色页装备行、属性格子
- 地图弹窗顶栏分隔、关闭按钮外圈
- `GameCanvas` 内血条、名字条等 HUD
---
## 7. 修改清单摘要(按主题)
1. **资源规范**`UI_CODING_STANDARD.md` + 集中映射 `uiAssets.ts`
2. **9-slice 系统**`index.css` + `getNineSliceStyle`
3. **开局 / 主界面**世界按钮、选角卡、tab、面板、选项、背包、根与内容区背景纹理。
4. **地图弹窗**:外壳、信息板、节点 `Map_frame`、关系图 `Frame_bg_big_2`
5. **画布**:场景名 `Title_frame_m`
6. **世界按钮**:按真实像素尺寸修正切片,去掉错误 `baseColor` 方案。
7. **Vite**`root`/`envDir` 保证环境变量加载稳定。
---
## 8. 2026-04-18 / 2026-04-20 账号入口补充记录
- 早期方案曾在 `AuthGate` 层提供右上角全局账号信息条,并在 `GameShellRuntime` 中临时隐藏。
- 2026-04-20 起,这个全局悬浮入口已整体下线,不再区分“平台显示 / 冒险隐藏”。
- 原因是右上角高频观察区不适合承载账号入口,且平台内已经有更明确的页面内入口。
## 9. 2026-05-08 创作首页通知入口下线
- `CreativeAgentHome` 顶栏右上角不再展示“通知与账户”按钮,避免创作首页把通知入口放在首屏高频区域。
- 2026-05-12 平台入口页同步移除移动端和桌面端右上角通知铃铛;移动端顶栏只保留品牌,未登录时保留登录按钮,桌面端只保留账号入口。
- 账号入口仍保留在侧边栏底部,创作首页顶栏维持左侧菜单、居中品牌的轻量结构。
- 当前账号相关入口统一保留在平台首页受保护动作、个人页、存档页与账号弹窗,不再占用全局悬浮层。
---
## 9. 2026-04-20 等级 HUD / 冒险布局补充
- 当前运行中的等级 UI 已从 `AdventurePanel` 底部移出,改为放在 `GameShellRuntime` 左上角固定 HUD避免把主对话区挤短。
- 左上角 HUD 复用 `CharacterInfoShared.tsx` 里的 `PlayerLevelProgress`,角色面板、实体详情、游戏 HUD 使用同一套等级进度表现。
- `AdventurePanel` 不再承担等级条展示,底部交互区只保留队伍 / 背包 / 刷新 / 退出聊天 / 选项 / 自定义输入,并压缩了底部留白与面板间距。
- 角色信息不只在总 HUD 里显示:`CharacterPanel` 的队伍成员卡、角色详情面板,以及 `AdventureEntityModal` 的实体详情头部都会展示角色身份与等级信息。
- 队长展示正式 `Lv.`;同行角色展示“参考 Lv.”NPC 优先展示运行时 `levelProfile.level`,这样 UI 只负责表现,不在前端虚构额外成长逻辑。
- 左上角等级 HUD 不使用背景框体,仅保留 `Lv`、等级数字与极细经验线,避免遮挡场景背景与移动端视野。
---
## 10. 2026-04-20 平台亮色主题主 Tab 修正
- `PlatformHomeView.tsx` 的四个主 Tab首页 / 创作 / 存档 / 我的)现在统一挂在 `platform-remap-surface` 下,让亮色主题能接管历史遗留的 `text-zinc-*``bg-black/*``border-white/*` 组合。
- 平台首页卡片覆层不要在组件里继续写死深色 `rgba(8,10,14,...)` 渐变;这次已收口为 `--platform-card-overlay-soft``--platform-card-overlay-strong``--platform-card-overlay-deep`,明暗主题都从 token 走。
- 平台桌面顶栏里的账号头像、移动端底部主 Tab 分隔线,也不要保留暗色主题时留下的固定蓝色渐变和深色边线,应直接使用平台主题变量(如 `--platform-profile-avatar-fill``--platform-line-soft`)。
- 后续如果继续调整平台主 Tab 视觉,优先改 `src/index.css` 的平台主题 token 和 remap 规则;只有 token 无法表达时,再做局部组件样式补丁,避免亮色主题再次出现“页面整体是亮的,但局部卡片仍是暗的”。
- 参考图方向已明确:平台亮色主题应以白色为主底色,粉红只承担背景气氛和重点 CTA不应让整页主壳继续像深粉底板。
- 移动端底部 `platform-bottom-nav` 的 Tab 激活态必须与默认态使用同一套盒模型;边框要预占位,不能在 onPress / active 时临时增加边框导致按钮尺寸和留白跳变。
- 2026-04-20 第二轮细查补色时,继续把 `PlatformWorldDetailView.tsx``PlatformHomeView.tsx``PreGameSelectionFlow.tsx` 里落在白底/浅底面板上的标题、说明、次级标签、搜索栏和加载兜底文本显式切回平台亮色 token避免亮色主题下继续出现浅底白字或过浅灰字。
- 2026-04-20 第三轮修正方向后,平台首页移动端底部 `platform-bottom-nav` 的高度、内边距、按钮圆角、图标尺寸、标签字号统一收口到 `src/index.css` token`PlatformHomeView.tsx` 只保留结构类,避免 `h-14`、容器 padding、按钮内部内容间距和 active 底座各自维护半套尺寸,导致选中态看起来比 Tab 槽位更矮或更高。
- 2026-04-20 第四轮把平台亮色主题顶部过重的红色收轻:`--platform-body-fill``--platform-hero-fill``--platform-shell-glow-*``--platform-surface-glow-*` 改成更接近暖白 + 浅珊瑚的低饱和版本,首页 / 创作页 / 详情页 Hero 覆层统一改走 `--platform-hero-overlay-strong`,避免组件里继续写死高饱和粉红渐变。
---
## 11. 2026-04-20 创作 Agent 聊天工作台亮色主题补色
- `src/components/custom-world-agent/*` 这一条创作 Agent 工作台链路已统一切回 `platform-subpanel``platform-input``platform-button``platform-banner``platform-progress-track`,亮色主题下不再继续裸露 `bg-[#111318]``bg-black/*``bg-white/*``text-white` 这类历史深色残留。
- 聊天线程中的用户气泡、助手气泡、系统消息、推荐回复按钮、流式回复态统一映射到平台 token后续如果继续调整创作 Agent 聊天视觉,优先改平台 token 或平台 class不要在组件里再单独写一套聊天色板。
- 顶栏、操作横幅、进度条、输入框的状态色统一复用平台亮暗主题变量,避免再次出现“页面整体已切亮色,但 Agent 局部还是旧暗色弹层”的割裂感。
---
## 12. 2026-04-20 NPC 聊天退出恢复与文本阅读性修正
- `AdventurePanel.tsx` 的叙事 `storyText` 已取消斜体,改为更大的正文尺寸,避免长段阅读时发飘。
- 冒险面板里的 `actionText` 统一上调到聊天态同级字号;`detailText` 不再默认渲染,保持底部选项区更清爽。
- `npcEncounterActions.ts` 在“退出聊天”后重新续写剧情时,会优先把当前故事里最近一轮已经呈现给玩家的非聊天选项文案并回 `optionCatalog`,避免高好感聊天收束后又退回 NPC 静态 fallback 文案。
---
## 13. 2026-04-21 创作中心失效草稿恢复兜底
- `src/components/rpg-entry/useRpgEntryLibraryDetail.ts` 现在会识别 `custom-world agent session``404 NOT_FOUND` 读取失败,不再把这类错误直接冒泡成未捕获 Promise。
- 当用户在创作中心点击“继续创作”命中失效草稿时,前端会主动清空 `customWorldSessionId` 恢复参数,并刷新一次 works 列表,避免刷新页面后反复尝试恢复同一个坏会话。
- 当前交互已收口成平台内可见提示:用户会停留在创作中心,并看到“这份共创草稿已失效,已为你返回创作中心,请重新开始创作。”,而不是卡在空白工作区或只在控制台看到英文异常。
- 这次兜底只处理失效会话恢复,不改变正常草稿继续创作、结果页恢复和已发布作品进入世界的主链。
---
## 14. 2026-04-24 Agent 工作区恢复指针按用户隔离
- `custom-world agent session` 现在由 `server-rs` Axum 路由接入 SpacetimeDB procedure模块内按 `owner_user_id + session_id` 查询;前端恢复旧工作区前必须确认本地指针属于当前登录用户,否则旧账号残留会先请求一次 `/api/runtime/custom-world/agent/sessions/:sessionId` 并产生 404。
- `src/services/customWorldAgentUiState.ts` 的 URL 仍只保留 `customWorldSessionId` / `customWorldOperationId`,用户归属只写入 `sessionStorage`,避免把 `userId` 暴露到可分享链接里。
- `src/components/rpg-entry/useRpgCreationSessionController.ts` 在恢复初始工作区时会对比 `ownerUserId`,发现不是当前用户就清空恢复指针并停留在创作入口,不再打后端失效 session。
- 后续新增 Agent 类恢复入口时,同样要区分“可分享的 URL 指针”和“仅本机使用的登录用户归属”,不要只凭 sessionId 自动恢复受保护资源。
---
## 15. 2026-04-24 多玩法 Agent 聊天顶部文案统一隐藏
- `CreationAgentWorkspace` 已支持在 `title``assistantSummary` 为空时只展示返回、主操作、进度与锚点区域;各玩法适配层不要再传入“世界共创 / 玩法共创”这类模块标题或引导副文案。
- `custom-world``big-fish``puzzle` 三条 Agent 聊天工作区现在统一隐藏顶部标题与标题下方说明,避免只有 RPG / 自定义世界生效、其他玩法模板仍残留旧文案。
- 后续新增玩法模板时,聊天页顶部模块应保持清爽:必要状态放进进度、操作横幅或聊天消息,不把功能说明类文案默认写入 UI。
---
## 16. 2026-04-24 创作结果页亮色主题细节补色
- RPG / 拼图 / 大鱼结果页根容器统一挂 `platform-remap-surface`,让亮色主题能接管遗留的 `text-white``text-zinc-*``bg-white/*``bg-black/*` 和状态色工具类。
- 拼图与大鱼结果页顶部 Hero 只增加 `platform-result-hero` 语义类,不改变整体布局;亮色主题下由 `src/index.css` 统一换成暖白底、轻粉高光和平台主按钮色。
- 地图弹窗新增 `map-modal-overlay``map-modal-shell``map-modal-backdrop``map-modal-shade``map-info-panel` 语义类;亮色主题通过这些类降低暗色遮罩、提亮地图背景、统一节点卡与连线颜色。
- 结果页中保存、生成、发布等旧的 `bg-amber-600` / `bg-cyan-600` / `bg-cyan-200` 按钮,在 `platform-remap-surface` 内被映射回 `platform-button--primary` 同款渐变,避免亮色主题下按钮体系割裂。
- 后续继续做结果页 UI 细节时,优先补语义 class + `src/index.css` 的 light remap不要在每个结果页组件里复制一套亮色配色也不要调整页面整体布局结构。
---
## 17. 2026-04-26 创作编辑器关闭确认弹窗亮色主题修正
- `RpgCreationEntityEditorShared.tsx` 的未保存关闭确认统一收口为 `CloseConfirmDialog`,弹窗只保留确认信息和两个动作,不新增说明文案。
- `CloseConfirmDialog` 通过 `platform-close-confirm-dialog` 语义类接入平台主题 token提示块使用 `--platform-warm-*`,确认按钮使用 `--platform-button-primary-*`,继续编辑按钮使用 `--platform-neutral-*`
- 后续新增关闭 / 退出确认面板时,不要继续复制 `text-amber-50``text-sky-50``bg-black/*` 这类深色 Tailwind 组合;优先复用语义类,避免亮色主题出现浅底白字和按钮文字不可读。
---
## 18. 2026-04-30 资料兑换码弹窗响应式修正
- `RpgEntryHomeView.tsx` 的兑换码弹窗现在抽成同一份 `rewardCodeModal`,桌面与移动端分支都挂载,避免竖屏点击头像右侧“兑换码”后只更新状态但不显示窗口。
- `src/index.css` 已补齐 `platform-modal-backdrop``platform-recharge-modal``platform-profile-input``platform-primary-button``platform-modal-close` 与兑换结果提示样式;后续资料类轻量弹窗可以复用这组类接入平台主题背景。
- 兑换码窗口仍只保留输入框、兑换按钮和后端返回提示,不新增规则说明文案。
---
## 19. 2026-05-12 登录协议与个人页法律入口
- 登录弹窗的法律协议确认应挂在短信 / 密码登录提交按钮上方,法律链接只打开独立 `LegalDocumentModal`,不能顺手勾选同意。
- 法律弹窗通过 portal 挂到 `body` 时必须显式带 `platform-theme--*` 和高于登录遮罩的层级,否则容易丢主题变量或被登录弹窗遮住。
- “我的”页常用功能区固定为 3 列,法律信息区放在设置入口下方;备案号作为外链进入工信部备案站,入口保持轻量,不在页面内展开长文。
---
_文档目的交接给下一个 Agent 时,优先读本文件 + `UI_CODING_STANDARD.md`,再改 `uiAssets.ts` / `App.tsx` / `index.css`。_

View File

@@ -1,80 +0,0 @@
# 大鱼吃小鱼草稿生成链路修复 2026-04-28
## 背景
大鱼吃小鱼玩法的结果页已经具备等级卡、主图工坊、动作工坊和背景工坊,但当前 `big_fish_compile_draft` 只是把锚点交给 `module-big-fish``compile_default_draft(...)` 做静态模板拼装。
这会导致两个直接问题:
1. 草稿编译虽然能成功进入结果页,但每一级实体只会拿到非常概括的模板文本,无法真正产出“实体名称、文字描述、形象描述、待机动作描述、移动动作描述”这一组首稿。
2. 主图和动作工坊默认提示词没有绑定到一份足够细的草稿真相源,动作面板只能看到合并后的 `motionPromptSeed`,会表现成“草稿生成一带而过,所有内容都没有正常生成”。
## 本次修复口径
### 1. 每级等级蓝图必须补齐的文本字段
大鱼吃小鱼每一级 `level blueprint` 在保留原有字段的同时,新增并持久化下面这些文本真相:
1. `textDescription`
- 当前等级实体的正文文字描述。
- 用于结果页等级卡和后续重生成时的人类可读设定底稿。
2. `visualDescription`
- 当前等级实体的形象描述。
- 主图工坊默认输入内容直接取这份字段。
3. `idleMotionDescription`
- 当前等级待机动作描述。
- `idle_float` 动作工坊默认输入内容直接取这份字段。
4. `moveMotionDescription`
- 当前等级移动动作描述。
- `move_swim` 动作工坊默认输入内容直接取这份字段。
### 2. 默认提示词流转规则
草稿生成、结果页工坊和正式资产生成统一按下面口径流转:
1. 草稿编译阶段先产出上述结构化文本字段。
2. 主图工坊默认文案:
- 优先显示 `visualDescription`
- `visualPromptSeed` 作为主图正式生图提示词的冻结快照,可由 `visualDescription` 组合生成
3. 动作工坊默认文案:
- `idle_float` 优先显示 `idleMotionDescription`
- `move_swim` 优先显示 `moveMotionDescription`
- `motionPromptSeed` 继续保留为动作方向总提示词摘要,但具体动作正式生图必须显式拼入动作位对应描述
4. 草稿阶段生成的正式主图、动作图和后续重生成,都只能读取同一份 `draft.levels[*]` 真相,前端不得本地拼接新的设定文案。
### 3. 编译策略
`big_fish_compile_draft` 需要升级为:
1. `api-server` 先调用 LLM 做结构化草稿编译。
2. 若 LLM 成功,则把完整 `draft_json` 写回 SpacetimeDB。
3. 若 LLM 不可用、返回非法 JSON 或字段缺失,则退回 `compile_default_draft(...)` 的 deterministic fallback。
这样可以同时保证:
1. 正常环境下草稿不再只是模板壳。
2. 模型偶发失败时不会打断结果页主链。
3. SpacetimeDB reducer 不承担外部网络调用,仍然符合后端边界。
## 落地范围
本次修复涉及:
1. `server-rs/crates/module-big-fish`
2. `server-rs/crates/spacetime-module`
3. `server-rs/crates/spacetime-client`
4. `server-rs/crates/shared-contracts`
5. `server-rs/crates/api-server`
6. `packages/shared/src/contracts/bigFish.ts`
7. `src/components/big-fish-result/BigFishResultView.tsx`
## 验收口径
修复后需要满足下面这些观察结果:
1. 点击“生成草稿”后,`draft.levels[*]` 不再只有空泛模板,而是每级都带名称、文字描述、形象描述、待机动作描述、移动动作描述。
2. 打开主图工坊时,默认文本来自当前等级的 `visualDescription`
3. 打开待机动作工坊时,默认文本来自当前等级的 `idleMotionDescription`
4. 打开移动动作工坊时,默认文本来自当前等级的 `moveMotionDescription`
5. 资产槽位 `promptSnapshot` 与对应动作位 / 主图位的默认提示词一致。
6. LLM 不可用时仍然能生成一版可用 fallback 草稿,而不是直接报错或写入空草稿。

View File

@@ -1,24 +0,0 @@
# 大鱼吃小鱼发布反馈修复 2026-04-26
## 背景
大鱼吃小鱼结果页的“发布”按钮已经会向后端发送 `big_fish_publish_game` action。后端发布成功后会把当前 Agent session 的 `stage` 改成 `published`,作品列表也会从 session 聚合出已发布作品。
问题出在前端发布成功后的反馈链路不完整:
1. 结果页没有把 `stage: published` 显示成“已发布”状态,用户点击后看起来没有变化。
2. 平台父层没有在大鱼发布成功后刷新“大鱼吃小鱼”作品列表,创作中心仍可能保留旧的草稿状态。
## 落地口径
1. `BigFishResultView``session.stage === 'published'` 作为已发布态真相。
2. 已发布态下发布按钮显示“已发布”并禁用,避免重复提交。
3. 已发布态下发布校验区显示“已发布”状态,继续保留资源完成度信息。
4. `PlatformEntryFlowShellImpl``big_fish_publish_game` 成功后刷新 `bigFishWorks`
5. 发布失败仍沿用既有错误模态,展示后端 `details.message` 里的具体校验原因。
## 验收
1. 在大鱼结果页点击“发布”会调用 `/api/runtime/big-fish/agent/sessions/{sessionId}/actions``big_fish_publish_game`
2. 后端返回已发布 session 后,结果页按钮变为“已发布”。
3. 返回创作中心后,该作品卡片状态通过刷新后的作品列表体现为已发布。

View File

@@ -1,65 +0,0 @@
# 大鱼作品列表 `items_json` 兼容修复 2026-04-28
## 背景
大鱼吃小鱼作品列表在 `server-rs` 链路里由 SpacetimeDB procedure 返回 `items_json`,再由 `spacetime-client` 反序列化成 `BigFishWorkSummaryRecord`
本轮出现的线上报错为:
```text
big fish works items_json 非法: missing field `owner_user_id`
```
这说明:
1. 客户端 record 结构已经把 `owner_user_id` 当成必填字段。
2. 某些历史 `items_json` 仍是旧字段集,没有带上 `owner_user_id`
3. 一旦直接按新结构强反序列化,整个 works 列表接口都会失败,而不是只丢失单字段。
## 根因判断
这不是前端展示问题,也不是 Axum 路由参数问题,而是:
1. SpacetimeDB procedure 输出 JSON 的结构发生过升级。
2. `spacetime-client` 映射层没有为旧 JSON 做向后兼容。
3. 作品列表是聚合读模型,一条旧记录就可能拖垮整批列表读取。
## 本次落地口径
本次只做最小风险修复,不改前端契约,不改现有表结构:
1. `server-rs/crates/spacetime-client/src/mapper.rs`
- 大鱼 works 反序列化改为先读兼容结构。
- `owner_user_id` 改为兼容层里的可缺省字段。
2. 私有 works 列表
- 若旧 JSON 缺 `owner_user_id`,用当前查询的 `owner_user_id` 回填。
- 这样不会破坏创作中心里依赖 `ownerUserId` 的恢复、归属和 key 逻辑。
3. 公开 gallery 列表
- 若旧 JSON 缺 `owner_user_id`,先回填空串,保证列表接口不再整体失败。
- 后续若公开画廊明确需要作者归属真相,再补模块端回填或数据修复。
4. 新增定向测试
- 覆盖“私有 works 旧 JSON 缺字段仍可回填”
- 覆盖“公开 works 旧 JSON 缺字段不再报错”
## 经验结论
以后只要是 `procedure -> items_json -> client record` 这类链路,都要默认遵守下面两条:
1. 聚合读模型的 JSON 字段升级不能假设全量历史数据同步完成。
2. `spacetime-client` 的映射层必须承担兼容旧 JSON 的责任,不能把结构升级风险直接抛给上层接口。
尤其是 works / gallery / library 这种平台入口级接口:
1. 允许单字段降级
2. 不允许整批列表因单字段缺失而 500 / 400
## 后续建议
如果后面继续演进大鱼 works 字段,推荐优先遵守:
1. 新增字段优先 `Option` 或兼容层解析。
2. 聚合 JSON 升级时同步补回归测试。
3. 如果字段已经进入前端关键逻辑,再决定是在模块端回填、客户端兜底,还是补历史数据迁移。

View File

@@ -1,262 +0,0 @@
# Codex 实战经验沉淀
日期:`2026-03-24`
## 1. 先判断需求属于哪条链路
这个项目几乎所有需求都不是单点 UI 改动,通常会同时影响几条链路:
- 叙事链路AI 剧情、提示词、选项文案
- 状态链路:`GameState`、NPC 状态、背包、队伍、委托
- 演出链路:屏幕外进场、战斗播放、实体站位、动画与特效
- 工具链路编辑器、校验脚本、存档兼容、smoke
经验:
- 每次动手前先判断“这次主要影响哪几条链路”,不要把需求误判成单纯的 UI 需求。
- 只改表现、不改状态,最终一定会返工。
- 只改状态、不补校验,后面也一定会返工。
## 2. 先补状态模型,再补交互
这类项目里,最稳定的顺序永远是:
1. 先补类型与状态字段
2. 再补规则函数
3. 再补 hook 流程
4. 最后接 UI
已经反复验证有效的例子:
- `quests`
- `playerEquipment`
- `playerCurrency`
- `roster`
- `companions`
- `currentNpcBattleMode`
- `sparReturnEncounter`
经验:
- 如果一个功能需要“长期留存”,就不要只存在于局部组件 state。
- 先建模后接按钮,比先堆按钮后补状态稳定得多。
## 3. AI 负责叙事,本地负责规则
项目里最稳的边界是:
- AI 负责:
- `storyText`
- 对话文本
- 选项的自然语言表达
- 本地规则负责:
- 交易是否合法
- 招募是否成立
- 装备是否生效
- 委托进度是否完成
- 掉落、货币、好感、队伍编成
经验:
- 只要涉及数值、资源、状态迁移,就尽量不要让大模型即兴决定。
- 给模型的提示词应该描述“局面”和“边界”,不要让它代替规则系统。
## 4. 固定选项提示不要写得太死
一个重要经验是:
- 模型需要知道每个 `functionId` 的核心含义
- 但不需要看到“当前默认文案 / 补充信息 / 实际行为”这种过于细碎、强绑定的结构
更好的方式是:
- 保留 `functionId`、数量、顺序
- 只提供“这个 function 的核心语义参考”
- 让模型重写更自然、连续的 `actionText`
经验:
- 提示词越像表单,模型越容易产出僵硬选项。
- 提示词越像“约束 + 语义边界”,剧情越自然。
## 5. 面前实体的提示词必须和主角对称
如果主角有:
- 描述
- 性格
- 状态
- 属性
那么“当前面前实体”也应该尽量有同样结构。
经验:
- 只给一句“某 NPC 在这里活动”太粗,会削弱模型对局面的把握。
- 面前实体和主角描述层级一致后,模型更容易写出有来回感、对称感的叙事。
- 对生命/灵力这类状态,离散分档文本比裸数字更利于模型理解。
## 6. 屏幕外进场一定要抽成统一工具
这类项目很容易在多个地方各写一套“从屏幕外进入”的逻辑,结果出现:
- 同一实体重复进场
- 先进入屏幕,又被拉回屏幕外再进一次
- 多怪时只动第一个,其他直接跳终点
这次稳定下来的方法是:
- 抽统一的过渡工具
- `buildEncounterEntryState`
- `buildEncounterTransitionState`
- `interpolateEncounterTransitionState`
- 所有进场逻辑都复用这一套
- 区分两种场景:
- 真正从屏幕外冲入
- 已经在屏内预览,只是收敛到正式位置
经验:
- “屏幕外进入”本质上不是动画问题,而是状态过渡问题。
- 一旦同时存在 preview / call_out / resolve / replay就必须统一插值逻辑。
## 7. 多实体系统里,不要默认“只处理第一个”
这个坑非常常见:
- 场景配置里有多个怪
- 运行时逻辑却只用 `monsterIds[0]`
- 或者动画只插值 `sceneMonsters[0]`
经验:
- 只要数据结构已经允许数组,就尽量按“整组”处理。
- 即便最后 UI 只重点展示一个,也不要在底层逻辑里偷偷退化成单体。
## 8. 招募系统不要只做“当前队伍”
如果只有 `companions` 而没有 `roster`,后面一定会遇到这些问题:
- 队伍满员时必须强制覆盖旧同伴
- 已招募角色很难转成待命
- 营地/编队功能没法闭环
这次稳定下来的模型是:
- `companions`:当前上阵
- `roster`:已招募但待命
经验:
- 只要项目里有招募,迟早就要有 roster。
- roster 不只是 UI 功能,而是状态层能力。
## 9. 装备系统不要只做展示
装备真正形成闭环,至少要同时做到:
- 背包里可装备 / 卸下
- 装备改变真实属性
- 战斗行为读取装备加成
- 存档兼容旧存档
经验:
- “装备栏能显示”不算完成。
- 只有真正影响 `maxHp / maxMana / damage / incomingDamage`,它才是玩法系统。
## 10. 交易系统最好统一成货币价值模型
一开始按“品质交换”虽然简单,但很快会遇到问题:
- 不同类别物品难比较
- 直接购买不好接
- 后面加入售卖、任务赏金、宝藏货币时会冲突
更稳定的做法是:
- 所有物品都有统一价值
- NPC 商品有购买价
- 玩家物品有回收价
- 货币作为通用交换媒介
经验:
- 一旦出现货币,就尽量让交易系统全面切成“价值模型”。
- 不要同时长期维护“品质交换”和“货币购买”两套完全不同的判定逻辑。
## 11. 编辑器要先做保存前校验
编辑器进入“可持续生产内容”阶段后,最优先的不是视觉,而是:
- 保存前校验
- 非法引用提示
- 数值边界检查
经验:
- 编辑器最怕的不是“不够漂亮”,而是“保存成功但运行时报错”。
- 只要内容开始增多,校验脚本和保存前校验就必须尽早接入。
## 12. 每次大改后都要补 smoke
对这个项目来说,`lint + build` 不够。
至少要补 smoke 的场景包括:
- 委托接取 -> 推进 -> 交付
- 多怪遭遇
- 装备加成
- 队伍编成
- 交易价值与直接购买
- 进场插值
经验:
- 只靠人工点点看,很容易漏掉状态分支。
- smoke 不一定要重,但要覆盖关键闭环。
## 13. 兼容旧存档要同步做
每次给 `GameState` 新增字段时,都要同步考虑:
- 默认值
- 存档读取兼容
- 旧字段缺失时如何补全
经验:
- 旧存档兼容不是“最后再说”的工作。
- 新字段一旦进 `GameState`,就应当同一轮把持久化兼容补上。
## 14. 不要在坏文件上无限缝补
这次实际踩到过一个很明显的坑:
- 某些文件本身已经混入大量乱码或结构损坏
- 在原地做小 patch 会越来越难维护
更稳的做法是:
- 保留接口
- 整文件重写成干净版本
- 再接回现有调用
经验:
- 当一个文件已经进入“局部 patch 很难保证正确”的状态时,重写往往比继续缝补更省时间。
## 15. 后续继续迭代时的建议顺序
如果继续推进这个项目,建议优先顺序:
1. 先清理中文乱码高频文件
2. 再继续精英/Boss 层
3. 再补营地关系事件
4. 再做编辑器差异预览 / 导入导出
## 16. 一句话总结
这个项目最重要的经验不是“多写了多少功能”,而是:
**凡是会同时影响叙事、状态、演出和工具链的需求,都要先统一边界,再落实现。**

View File

@@ -1,277 +0,0 @@
# Codex Past Work Experience Summary
日期:`2026-03-23`
## 1. 工作范围概览
这几轮工作主要集中在 4 个方向:
1. 编辑器体系整理
2. NPC 视觉编辑与素材接入
3. 玩家角色与怪物动画资源纠偏
4. 选项行为编辑器与预览链路升级
这些改动不是孤立的 UI 修补,而是横跨了:
- 资源定义层
- 编辑器字段层
- 运行时预览层
- 游戏真实播放逻辑层
## 2. 已完成的核心工作
### 2.1 编辑器入口与页签整理
- 当时曾保留 `/preset-editor``/npc-editor``/function-editor`
- 当时还新增过 `/behavior-editor` 作为“选项行为”编辑页别名
- 将原先单独的 `NPC 视觉` 标签并回 `NPC` 编辑页
-`Function` 页签改名为 `选项行为`
结论:
- 独立编辑器入口如果没有继续接入主流程,应及时物理删除,不要长期保留兼容壳
- 页签命名要贴近陶泥儿主语言,而不是内部实现命名
### 2.2 NPC 视觉模块并入 NPC 编辑
完成内容:
- 当时曾将 `NpcVisualEditor` 嵌入 `PresetEditor` 的 NPC 编辑页
- 让 NPC 文本字段与视觉字段围绕同一个当前选中 NPC 联动
- 保留视觉覆盖保存与全局布局保存能力
经验:
- NPC 文本编辑和 NPC 视觉编辑不应分裂成两个互不关联的工作流
- “当前选中的 NPC” 必须是两个模块共享的单一数据源
### 2.3 Medieval NPC 素材定义重建
完成内容:
- 在 [medievalNpcVisuals.ts](/E:/Repos/Genarrative/src/data/medievalNpcVisuals.ts) 中重建了 Medieval NPC 的资产定义
- 补齐了 cloth / leather / metal / melee / magic / ranged 六大类真实素材
- 为素材增加了:
- 语义化名称
- 图块尺寸
- 列数
- 帧数
- 姿态选项
- 让编辑器不再直接使用文件名、序号名作为展示项
经验:
- 编辑器下拉项如果来自手写数组,迟早会和真实素材目录脱节
- 素材定义最好具备“资产元数据”,而不是只有路径字符串
- 一旦资产存在大图块,就不能再默认所有图块都是 `32x32`
### 2.4 NPC 动画器支持大图块武器
完成内容:
- 在 [MedievalNpcAnimator.tsx](/E:/Repos/Genarrative/src/components/MedievalNpcAnimator.tsx) 中为 `AtlasSprite` 增加:
- `tileWidth`
- `tileHeight`
- 对齐偏移支持
效果:
- 长柄武器
- 巨剑
- 64x32 远程武器
- 64x64 投石索类武器
都能正确显示,不再被按 `32x32` 裁坏。
经验:
- 视觉编辑器一旦涉及装备 atlas就必须把“资源尺寸”从数据层带到渲染层
### 2.5 玩家角色动画映射纠偏
完成内容:
- 在 [characterPresets.ts](/E:/Repos/Genarrative/src/data/characterPresets.ts) 中重新核对 5 个玩家角色的 Hero 动画目录
- 修正了错误帧数、错误前缀、遗漏动作
- 补齐了真实存在但之前未接入的动作:
- `acquire`
- `climb`
- `dash`
- `die`
- `double jump`
- `hurt`
- `jump`
- `jump attack`
- `wall slide`
- 给角色动画配置增加了 `file` 字段,支持单文件动画
相关文件:
- [types.ts](/E:/Repos/Genarrative/src/types.ts)
- [CharacterAnimator.tsx](/E:/Repos/Genarrative/src/components/CharacterAnimator.tsx)
- [characterCombat.ts](/E:/Repos/Genarrative/src/data/characterCombat.ts)
经验:
- 只要编辑器允许用户切“预览动作”,就不能让未映射动作静默 fallback 到 `idle`
- 正确做法是:
1. 先补齐真实动作映射
2. 再让预览下拉只显示当前角色实际可用动作
### 2.6 怪物动画空白帧修复
完成内容:
- 在 [monsterPresets.ts](/E:/Repos/Genarrative/src/data/monsterPresets.ts) 中把怪物动画从“连续帧猜测”改成“按图集行起点取帧”
- 补上缺失的 `die` 配置
- 清除了所有落进空白格的动画段
经验:
- 像素怪物图集不一定按一个连续区段排完整套动作
- 如果动作配置只写 `start + frames`,但没结合图集行结构,就非常容易踩进空白帧
### 2.7 选项行为编辑器重构
完成内容:
- 将原 “Function 编辑器” 改造成 “选项行为编辑器”
- 页面文案和入口统一为“选项行为”
- 移除“基础场景 / 结果场景”双窗格预览
- 保留并强化:
- 行为列表
- 覆盖保存
- 快速模板套用
经验:
- 陶泥儿主并不关心 “function” 这个技术词,更关心“这个选项会发生什么”
- 同类编辑器如果只给字段表单而没有模板起稿能力,复用效率会很低
### 2.8 选项行为预览升级到实机回放
完成内容:
- 在 [StateFunctionEditor.tsx](/E:/Repos/Genarrative/src/components/StateFunctionEditor.tsx) 中新增 `BehaviorExecutionPreview`
- 预览不再是静态推测,而是:
1. 构造本地 `GameState`
2. 调用真实 `resolveFunctionOption`
3. 再调用 [useCombatFlow.ts](/E:/Repos/Genarrative/src/hooks/useCombatFlow.ts) 的
- `buildResolvedChoiceState`
- `playResolvedChoice`
- 从而直接复用游戏真实逻辑
覆盖能力包括:
- 战斗行为
- 恢复行为
- 脱离行为
- 探索前探
- 切场行为
经验:
- 编辑器预览只要和运行时逻辑写成两套,就一定会越来越不一致
- 预览层最稳的做法是“调用真实业务逻辑”,而不是“模拟真实业务逻辑”
## 3. 关键踩坑记录
### 3.1 图标组件名覆盖原生 `Map`
问题:
- `lucide-react``Map` 图标直接命名为 `Map`
- 在 NPC 页签里 `new Map()` 实际调用到了图标组件
- 导致页签内容直接渲染为空
经验:
- 图标组件命名尽量使用 `MapIcon``UserIcon` 这类后缀
- 避免覆盖 JS/TS 原生对象名
### 3.2 预览 effect 依赖不稳定导致回放反复重启
问题:
- `BehaviorExecutionPreview` 里使用了 `useCombatFlow()`
- 但 effect 依赖了返回对象本身
- 每次 `gameState` 更新effect 都会被视为变更
- 导致预览回放速度异常、重复重启、动画像加速
经验:
- 只要预览组件内部要“异步播放状态变化”,就要高度警惕 effect 依赖环
- 解决方式是:
-`ref` 保存稳定方法引用
- 让 effect 只依赖真正的输入配置,不依赖内部播放状态
### 3.3 实时面板与回放阶段不同步
问题:
- `LIVE PLAYER` 用的是实时 `gameState`
- `BATTLE SNAPSHOT` 用的是预计算首回合快照
- 两者不是同一时间点的数据
- 导致面板看起来“都对,但互相对不上”
经验:
- 预览面板要么都显示“实时状态”
- 要么都显示“同一个阶段的快照”
- 混用实时值和预测值会让陶泥儿主误判
## 4. 这类项目里沉淀下来的方法论
### 4.1 先校验资源,再改编辑器
顺序建议:
1. 先扫真实目录
2. 再建资产定义
3. 再修编辑器字段
4. 最后修预览
### 4.2 预览必须尽量复用游戏真实链路
优先级:
1. 复用真实函数
2. 复用真实状态结构
3. 复用真实渲染组件
4. 最后才是补充编辑器专用的辅助信息
### 4.3 编辑器要区分“可编辑字段”和“会生效字段”
经验:
- 不是所有字段都应该在所有行为类型下开放
- 如果某类行为最终不会直接读取某个字段,就应该禁用或弱化它
- 否则陶泥儿主会错误地以为改动无效是 bug
### 4.4 模板比空白表单更重要
经验:
- 当系统里已经有多种成熟行为时,最快的创作路径不是“从零填写”
- 而是:
- 选一个最像的
- 套结构
- 微调文案和数值
## 5. 推荐的后续方向
如果继续打磨这套编辑器,建议下一步做:
1. 为选项行为预览增加“时间轴 / 阶段日志”
2. 为选项行为编辑器增加“新建行为向导”
3. 把更多系统状态引入预览上下文
- 同伴
- NPC 状态
- 背包
- 当前场景实体池
4. 把“可编辑字段”和“只读推导字段”视觉上再分开
## 6. 一句话总结
过去这几轮最重要的经验不是“写了多少编辑器 UI”而是
**编辑器一旦想可靠,就不能只编辑静态数据,必须逐步接管真实资源定义、真实运行时状态和真实播放逻辑。**

View File

@@ -1,310 +0,0 @@
# 当前游戏全流程体验报告2026-04-07
## 1. 报告说明
本次报告基于 `2026-04-07` 仓库现状完成,目标不是评审 PRD而是从玩家进入游戏的第一秒开始顺着当前可达链路实际跑一遍记录“能不能玩、玩到哪、哪里出戏、哪里已经有感觉”。
本次模拟采用两段式验证:
- 开发服验证:直接访问本地 `http://127.0.0.1:3000`
- 临时生产包试玩:执行 `node scripts/vite-cli.mjs build --outDir temp_playtest_build` 后,通过静态服务器预览
- 试玩视口:移动端优先,约 `430 x 932`
需要先说明一个前提:
- 当前开发服首页会被 Vite 错误遮罩拦住,玩家无法直接进入游戏
- 为了继续完成全流程体验,我改走了临时生产包试玩
- 临时生产包没有本地 `/api` 代理,因此剧情区会持续出现 `501 Unsupported method ('POST')` 的报错文案AI 文本体验会被明显污染
因此,这份报告同时包含两类结论:
- 一类是“当前版本玩家真实会撞到的入口问题”
- 一类是“绕过入口问题后,主流程骨架本身的可玩性表现”
---
## 2. 本次实际跑通的流程
本次实际走通的路径如下:
1. 启动页
2. 世界选择
3. 角色选择
4. 进入营地开场
5. 首次剧情抉择
6. 任务更新
7. 场景移动到 `宫苑内庭`
8. NPC 首次互动
9. 交易面板
10. 地图弹窗
11. 队伍面板
12. 背包 / 工坊面板
13. 设置面板
14. 保存并退出
15. 继续游戏恢复存档
结论先说:
- 主流程骨架已经成型,已经不是“只有页面没有游戏”
- 进入营地、触发任务、切场景、遇 NPC、开交易、开地图、看队伍、看背包、保存继续这一整圈是能跑下来的
- 但入口稳定性、错误文案兜底、语言一致性、部分中文乱码,已经直接影响玩家的第一轮真实体验
---
## 3. 分阶段体验记录
## 3.1 启动页
玩家第一眼看到的是一个相对简洁的开始页,只有标题、开始按钮、开发团队入口和联系方式,节奏是对的,确实更像游戏入口,不像表单式 Demo。
正向感受:
- 开始动作很聚焦,玩家不会迷路
- 首屏信息量不大,移动端阅读负担低
- “开始游戏 / 新游戏 / 继续游戏”的结构清晰
问题:
- 当前开发服并不能进入这个页面,实际先看到的是 Vite 错误遮罩
- “开发团队 / 联系方式”仍然偏开发样态,正式玩家会有一点出戏
## 3.2 世界选择
世界选择页目前有 `武侠``仙侠``自定义世界` 三个主要入口。对玩家来说,这一页的信息组织已经够直观:世界名、气质、副标题、在线人数氛围标签都能快速帮助判断。
正向感受:
- 选世界成本低,点击欲望明确
- 自定义世界入口放得足够醒目,不会被埋
- 武侠 / 仙侠区分清楚,符合开局决策直觉
问题:
- “在线人数”更像氛围数字,不像真实系统状态,容易被当作假在线
- 当前页面更偏“卡片入口”,还没有形成非常强的世界身份记忆点
## 3.3 角色选择
角色选择页已经具备“选人进入冒险”的基本仪式感。当前可见角色包括 `剑之公主``神箭游侠``双刃旅者``破军拳师``玄甲战锋`。属性、背景故事、性格标签和详情入口都齐了。
正向感受:
- 选角信息够完整,能形成第一轮角色代入
- “背景故事 + 标签 + 属性”三层信息组织合理
- 移动端视口下仍然能完成浏览和确认
问题:
- 角色页里的“自定义 / 详情”会让玩家在开局阶段产生一点分心
- 当前角色差异更多停留在说明层,开局前还没完全转化成“我为什么要选这个人”的强动机
## 3.4 营地开场
选择角色进入营地后,游戏会先给一段开场对话,再给玩家两个非常关键的初始决策:
- 先问问对方为什么会出现在这里
- 直接前往下一场景
这一段是目前全流程里最像“正式游戏开局”的部分之一。
正向感受:
- 营地比直接扔进战斗更稳,给了玩家进入状态的缓冲
- 开场对话能自然把关系、任务感和前路危险感一起立起来
- “先聊聊再走 / 直接上路”是个很好的第一轮分流
问题:
- 在静态试玩环境下,剧情区会同时出现完整 `501` HTML 报错,极度出戏
- 开场后文本会夹杂英文 fallback语言氛围被打断
## 3.5 首次选择后的反馈
当我选择“先问问你为什么会出现在这里”后,系统会立即给出关系变化与互动解锁反馈,例如可继续触发:
- 交易
- 切磋
- 前往下一场景
这一点说明“问一句话不是纯文案,而是会打开玩法分支”,这非常重要。
正向感受:
- 玩家会感觉自己的选择真的改变了接下来能做什么
- NPC 不是摆设,至少已经能作为玩法节点工作
- “营地开场 -> 关系松动 -> 解锁交互”这条节奏是成立的
问题:
- 互动文案和角色名偶尔会出现中英混用
- 某些反馈更像系统摘要,而不是完全沉浸式叙事
## 3.6 场景切换与任务推进
离开营地前往 `宫苑内庭` 后,系统会弹出任务更新提示,随后给出新的场景内可选路线,例如:
-`旧宫侍女` 交谈
- 继续向前探路
- 前往 `铸坊工场`
这是当前版本最能证明“游戏不是单房间对话器”的一段。
正向感受:
- 切场景后有明确任务更新,玩家知道自己没有在空转
- 新场景不是纯背景图替换,而是伴随新的实体和路径选择
- “NPC / 探索 / 场景跳转”三种入口并列,主循环味道出来了
问题:
- 任务弹窗表达比较清楚,但质感仍偏功能通知
- 任务标题、阶段名、剧情节拍这些信息有时偏系统化,缺少一点戏剧包装
## 3.7 NPC 互动与交易
`宫苑内庭``旧宫侍女` 接触后,可以进一步进入交易。交易弹窗中已经具备:
- NPC 名称
- 玩家当前货币
- 对方库存
- 购买数量调整
- 总价计算
- 取消 / 确认购买
这是当前版本完成度相对高的一段玩法。
正向感受:
- 交易不是假按钮,而是完整闭环
- 商品分类、稀有度、价格都能读懂
- 作为玩家,会明显感觉“这个 NPC 是有功能的”
问题:
- 从“与 NPC 交谈”直接跳到“交易 / 战斗 / 切磋”,中间缺少一层更自然的对话承接
- 当前交易更偏功能正确,角色气质和商品叙事关联还不够强
## 3.8 战斗 / 切磋
我实际触发了切磋和场景战斗。进入后,系统会给出带有数值提示的选项,例如:
- 耗蓝
- 伤害
- 正面压制 / 稳扎稳打 / 假动作切入
这说明战斗不是纯文案,而是有明确本地规则参与的。
正向感受:
- 玩家能看见技能选择的直接代价和收益
- 战斗选项语义比较明确,不是模糊散文式描述
- 从营地切磋到场景战斗的承接是通的
问题:
- 在本次静态试玩里,战斗文本会出现英文 fallback
- 战斗推进感还不够强,玩家能感知到“进入战斗了”,但还不够容易感知“这一手到底让局势前进了多少”
## 3.9 地图、队伍、背包
我实际打开了地图、队伍和背包。
地图方面:
- 点击场景名可直接打开地图弹窗
- 当前场景和相邻场景关系可见
- `宫苑内庭` 可看到 `雨夜长街``铸坊工场``地宫通道`
队伍方面:
- 队伍列表能打开
- 可读到主角状态、标签数、适配倍率
- 但面板里已经出现明显乱码,如 `闃熼暱`
背包方面:
- 能看到初始资源、材料和工坊配方
- 锻造 / 合成入口都已经在主流程里
- 玩家会明确感觉到“我不是只有剧情,我还有 build 和资源循环”
整体判断:
- 三个面板都不是空壳
- 地图和背包的功能价值已经足够成立
- 队伍页的信息密度没问题,但乱码已经直接破坏观感
## 3.10 保存并退出 / 继续游戏
设置面板中已经有:
- 音乐音量
- 运行统计
- 保存并退出
我实际触发了“保存并退出”,随后回到开始页,再点击“继续游戏”,能够恢复到先前场景和流程状态。
这是本次试玩里最让我放心的一条链路。
结论:
- 存档与继续不是摆设,是真的通了
- 这让整套流程第一次具备了“可以连续玩,而不是每次重开”的基础感
---
## 4. 当前版本最明显的优点
1. 主循环骨架已经成立。开局、选世界、选角色、营地、任务、切场景、NPC、交易、战斗、背包、地图、存档这些点已经能串起来。
2. 移动端优先思路是对的。至少在窄屏下,核心路径没有因为布局崩掉而不可玩。
3. 功能入口大多不是假入口。交易、地图、背包、保存继续都是真能执行的。
4. “AI 叙事 + 本地规则”的边界能感知到。尤其战斗选项里的耗蓝 / 伤害提示,已经把规则感立起来了。
---
## 5. 当前版本最影响玩家体验的问题
## P0
1. 开发入口直接被错误遮罩拦住。当前 `http://127.0.0.1:3000` 不是“有点瑕疵”,而是玩家根本进不去。
2. 标准构建命令当前不可用。`npm run build` 会因为 `dist` 清理阶段的 `EPERM` 失败,说明发布路径并不稳定。
3. 没有 `/api` 代理时,剧情区会直接显示完整 `501` HTML 错误正文,沉浸感几乎被瞬间打穿。
## P1
1. 多处英文 fallback 直接进入正式体验例如营地、NPC 接触、切磋文本。
2. 队伍面板已经出现玩家可见乱码,如 `闃熼暱``褰撳墠濮旀墭` 一类内容。
3. 冒险主标签栏被隐藏后,玩家主要依赖小按钮进入队伍/背包,主导航层级不够直观。
4. NPC 首次互动到“交易 / 战斗 / 切磋”的切换偏硬,少了一层更自然的剧情过渡。
## P2
1. 世界页和角色页已经能用,但记忆点还不够强,个体世界身份和角色差异还可以再拉开。
2. 任务提示偏功能型,情绪包装和戏剧感还可以继续加强。
---
## 6. 玩家视角总结
如果只从“玩法骨架”看,这个项目已经不是 PPT也不是只有几个页面的壳。它已经有一条能走完整圈的游戏主流程而且最关键的交易、地图、任务、战斗、存档都不是假的。
但如果从“当前玩家第一次打开就会得到什么体验”来看,问题也很直接:
- 入口不稳
- 构建不稳
- 离线 / 无代理时错误文案直接冲到脸上
- 中英混用和部分乱码会快速打断沉浸
一句话总结:
**当前版本已经具备“能玩一圈”的核心骨架,但距离“放心交给玩家体验”还差一次很扎实的入口修复、错误兜底和文本统一收尾。**
---
## 7. 建议的下一步
建议优先顺序如下:
1. 先修入口可玩性:解决开发服错误遮罩、`build` 清理失败、静态环境错误文案泄露。
2. 再修体验一致性:清掉玩家可见英文 fallback 和明显乱码。
3. 然后打磨主循环表达让营地开场、任务更新、NPC 接触这三段更有戏。
4. 最后再扩内容:因为现在真正限制体验的不是“内容太少”,而是“入口和表达不够稳”。

View File

@@ -1,246 +0,0 @@
# 奇幻酒馆 UI 开发经验沉淀
## 1. 总体原则
### 1.1 先保证移动端成立,再兼容网页端
- 入口页、世界选择、角色选择、冒险页、弹窗面板,都要先按手机竖屏去定义信息密度。
- 网页端只做“放宽容器、增加留白、补充 hover”不要反过来让桌面布局压垮手机体验。
- 任何区域如果在移动端需要滚动,必须明确谁滚动,不能让整页和局部同时争夺滚动。
### 1.2 游戏 UI 要优先“状态清晰”,而不是“文案很多”
- 开始页应该像主菜单,不像产品介绍页。
- 角色选择页应该像角色选择器,不像角色说明书。
- 冒险页应该像战斗/剧情操作台,不像文档阅读器。
### 1.3 一屏内的层级顺序要稳定
- 上半部分:画面演出。
- 中间部分:剧情或核心信息。
- 底部:操作按钮。
- 玩家必须能快速判断“我在看什么、下一步点哪里”。
## 2. 入口页经验
### 2.1 开始页
- 极简化是对的。
- 当前实践证明:开始页只保留“开始游戏”和“开发团队”两个主按钮,体验明显更像游戏。
- 游戏名和英文副标题可以保留,但不要再加长段描述。
### 2.2 世界选择
- 纵向焦点轮播比普通列表更适合移动端。
- 当前居中的卡片应该最大、最清晰、最亮;上下卡片缩放和透明度随滚动连续变化。
- 世界确认动作应该绑定当前聚焦卡,而不是每张卡都塞太多按钮。
### 2.3 角色选择
- 横向轮播是正确方向。
- 中间卡片应承担“主视觉 + 主要信息”。
- 左右卡片只做预览,不需要承载完整信息。
- 左右卡片倾斜方向一定要符合透视直觉:
左侧卡片向左外倾,右侧卡片向右外倾。
- 中间卡片不要出现任何模糊,否则会破坏“当前选中”的确认感。
## 3. 角色选择页经验
### 3.1 聚焦卡的表现
- 中间聚焦卡用角色 `run` 动画是有效的,能明显提升“角色活着”的感觉。
- 但动画资源和静态立绘的锚点通常不一致,必须单独做位置与缩放修正。
- 结论:
动画版角色不要直接复用静态图样式,要单独给 `transform``height``transform-origin`
### 3.2 信息区要紧凑
- 角色数值和角色背景应该放在轮播下方,但必须压缩高度。
- 如果下方面板过高,会直接破坏上半部分轮播体验,尤其在手机上。
- 删除背景面板里的额外动画模块是正确的,因为它与主轮播重复抢视觉焦点。
### 3.3 文案层级
- 页面标题只保留一句:
“选择你扮演的角色”
- 不要再强调 `CHARACTER SELECT` 之类的开发向分类标签。
- 聚焦卡下方直接显示角色名,比把名字只放在卡片底部更直观。
### 3.4 按钮位置
- “确认选择”必须放在角色轮播和信息区之后,作为页面最后的主动作。
- 按钮不宜过宽过高,否则会抢轮播的视觉重心。
## 4. 冒险页经验
### 4.1 剧情区必须自适应
- 剧情文本区不应该写死高度。
- 正确做法是:
让它自动填满“上方画面下缘”和“底部按钮区上缘”之间的剩余空间。
- 这样才能同时保证:
1. 上方画面一屏可见
2. 下方三个选项一屏可见
3. 中间剧情尽量大
### 4.2 底部操作区必须锚定到底
- 队伍、背包、换一换、选项列表,都应该属于底部控制区。
- 用户会天然在屏幕底部寻找交互入口,尤其是手机。
- 因此:
不要把这些按钮漂在中间,不要让剧情区把它们挤走。
### 4.3 队伍与背包不应打断主流程
- 在冒险页内,队伍和背包更适合“弹出面板”而不是“切换整页”。
- 原因:
1. 不会打断当前剧情阅读
2. 用户返回成本更低
3. 操作像手游副面板,更符合预期
### 4.3.1 弹出确认面板不能透明
- 删除作品、发布后分享、确认离开等关键弹窗必须有实体面板底色,不能只靠透明背景、毛玻璃或遮罩承载内容。
- 通过 portal 挂到 `body` 的平台弹窗必须在遮罩层补齐平台主题类,否则主题变量会脱离页面容器,轻则颜色漂移,重则面板背景看起来透明。
- 移动端关键确认弹窗优先居中显示,并保留 `max-height + 内部滚动`,避免被底部导航、安全区或底部抽屉布局遮住。
### 4.4 图标优于文字按钮
- 在底部工具区,队伍/背包改成 icon 后更紧凑。
- 但必须保留 `aria-label`,保证语义清晰、后续也方便测试。
### 4.5 冒险主场景不要挂右上角账号悬浮条
- 冒险页右上角属于画面演出和战斗/剧情信息的高频观察区。
- 全局账号信息条挂在这里,会直接压住场景、敌人血条或顶部提示,手机端尤其明显。
- 结论:
账号入口应收回平台首页、个人页或设置面板,不要在实际冒险主场景常驻悬浮显示。
- 当前仓库已进一步收口为:
不再提供右上角全局账号悬浮条,统一只保留页面内入口与独立账号面板。
### 4.6 冒险主场景双方角色必须按画面中线镜像
- 非滚动画面的交谈、预览、单体对峙态,主角和对面角色不能分别用左右边距、世界坐标和角色宽度重复推算。
- 正确做法是先定义“角色容器中心距离画面中线”的统一间距,再让主角中心落在中线左侧、对面角色中心落在中线右侧。
- 角色容器宽度、角色图片缩放和左右朝向必须各自独立处理,不能用额外 left inset 去修正角色图片,否则会破坏左右对称。
- 自定义图片 NPC、模板角色和组合式 NPC 都要进入同一 112px 场景容器,再按各自素材锚点做场景缩放,保证视觉大小不漂移。
## 5. 队伍面板经验
### 5.1 移动端成员列表不能太“卡片化”
- 如果每个成员都再套一层大边框卡片,手机上会显得很挤。
- 更好的方式是直接在主面板里陈列成员,弱化分隔、强化内容。
### 5.2 队伍列表展示什么
- 头像
- 姓名
- 称号
- 身份标记(领队/同行)
- HP / MP
### 5.3 队伍详情弹窗
- 弹窗必须可滚动。
- 弹窗宽度不宜太大,手机优先单列或偏单列。
- 技能、装备、属性三个区块保持稳定结构,便于扫读。
## 6. 背包页经验
### 6.1 格子数要优先适配手机
- 手机端格子间距要小一号。
- 图标尺寸和分类角标要跟着缩小。
- 目标不是“每个格子信息很多”,而是“单屏能看更多格子”。
### 6.2 背包详情弹窗
- 手机端改成单列/窄宽度布局更合理。
- 详情窗里的信息顺序建议固定:
1. 名称与类别
2. 品质与数量
3. 大图标
4. 说明
5. 标签
## 6.3 草稿页作品卡对齐分类列表
- 草稿 Tab 的作品架要优先对齐发现页分类列表的横向卡片:内容层承载标题/状态/类型/摘要,封面作为右半区半透明背景层。
- 草稿卡不能为了视觉对齐丢掉原有信息:删除、分享、已发布统计、拼图积分激励、未读红点都要保留;其中删除属于低频动作,常态不显示按钮,长按列表项后进入独立动作面板;已发布作品右上角可以常驻分享图标。
- 卡片右侧不再常驻“继续创作”“查看详情”“查看进度”等动作按钮,打开作品由整张卡片承担。
- 移动端保持单列列表;网页端使用多列卡片式网格,避免在宽屏上把作品卡拉成一整行长条。
- 生成中的状态使用整卡蒙版、旋转等待符号和“生成中...”标识;蒙版只能作为状态层,不能替换或移除卡片本身的信息。
- 草稿卡复用分类页基础类名时,要用 `.creation-work-card.platform-category-game-item` 覆盖分类页移动端三列规则;否则正文会被当作封面列压缩,中文标题会断成一两个字一行。
- 右侧封面不要只依赖 `img` fallback容器层也要有玩法参考图 CSS 背景兜底,私有资源换签失败或图片 onerror 时仍能看到封面视觉;封面层适合绝对定位铺到卡片右半区,作为半透明背景从右到左渐隐,不应出现独立方形边界或参与内容排版。
## 7. 样式与动画经验
### 7.1 轮播动画要连续,不要离散
- 滚动时应根据“当前位置与当前卡片中心的距离”实时计算:
- `scale`
- `opacity`
- `rotate`
- `translate`
- 不要做成“翻页后才跳变”的效果。
### 7.2 焦点卡与非焦点卡的职责要不同
- 焦点卡负责可读性与确认感。
- 非焦点卡负责预告与空间深度。
- 所以焦点卡不该模糊,非焦点卡可以轻微降透明度、缩放、偏移。
### 7.3 资源锚点要单独校准
- 静态立绘和动画帧经常不是同一锚点。
- 只要切换成动画,就需要重新调:
- 高度
- Y 偏移
- 缩放
- `transform-origin`
## 8. 工程经验
### 8.1 组件要继续拆
- 本轮重构证明,把入口页、冒险页、队伍页、背包页拆成独立组件是正确的。
- 后续新增 UI 需求时,应优先落在:
- `GameShell.tsx`
- `AdventurePanel.tsx`
- `CharacterPanel.tsx`
- `InventoryPanel.tsx`
### 8.2 不要相信“服务已经在跑”
- 这轮出现过“端口上有进程,但浏览器仍然拿到旧模块”的问题。
- 实际经验:
1. 要检查 `src` 文件内容
2. 要检查 dev server 实际返回的模块内容
3. 要确认启动脚本是否是当前项目真正使用的脚本
### 8.3 本项目当前开发服务入口
- 本地正确启动脚本是:
`node scripts/vite-cli.mjs --port=3000 --host=0.0.0.0`
- 历史脚本或旧进程会导致“代码已改但 UI 看起来没变”的假象。
## 9. 后续建议
### 9.1 可以继续统一中英混杂文案
- 当前部分新旧文本仍有历史遗留字符问题。
- 如果后面继续做 UI 精修,建议单独做一轮文案清洗。
### 9.2 可以把轮播抽成通用组件
- 世界纵向焦点轮播
- 角色横向倾斜轮播
- 这两套逻辑已经足够稳定,适合后续抽成通用 hook 或通用组件。
### 9.3 可以补移动端安全区适配
- 当前已经偏移动端优先,但还可以继续加:
- `safe-area-inset-bottom`
- `safe-area-inset-top`
- 更细的竖屏断点处理
## 10. 一句话结论
这一轮最关键的经验是:
**游戏 UI 的移动端优化,本质不是把元素缩小,而是重组视觉重心、固定操作锚点、让焦点内容在一屏内自然成立。**
### 10.1 可扮演角色形象预览保持 1:1
- 可扮演角色的形象预览容器统一使用 1:1 方形,入口选择轮播、角色资产工坊和结果页角色卡片都不能用纵向长卡片去拉伸预览图。
- 预览图片本身使用 `object-contain`,保证 AI 生成主形象、模板像素角色和运行时动画都在方形容器内完整显示,不裁切角色主体。
- 卡片可以在方形预览下方放角色名、称号、状态等信息,但这些文本区不能反向影响预览区比例。
- 编辑角色弹窗也遵循同一规则:移动端不能用固定高度压扁预览区,预览容器应随宽度保持 `aspect-square`
### 10.2 运行画面怪物锚点按视觉底边校准
- 对战预览里主角和对手要沿画面中线成对出现,但纵向不能只共用一个 `bottom` 常量。
- 怪物精灵帧的空白、体型和脚底位置差异很大,运行画面应按帧高分档下沉,让怪物视觉底边落在主角同一条地面线上。
- 后续新增怪物资源时,先检查红圈标注的实际落点,再调整锚点分档或单怪物偏移,避免出现“悬在地面上方”的状态。
- 自定义世界里敌对角色已经先作为场景 NPC 存在,即使它同时携带 `characterId``monsterPresetId`,画布也不能直接沿用模板角色的 `groundOffsetY`;只要 encounter 自身有 `imageSrc``visual`,就按场景 NPC 自定义形象锚点处理。
- 幕预览运行时还会构造“无 `characterId`、但有 `visual` 的场景 NPC”这类和平相遇分支同样必须套用场景 NPC 自定义形象锚点,否则会停在画面中上部。
### 10.3 移动端固定整页画布缩放
- 主站移动端以固定游戏画布体验为准,入口 `viewport` 需要锁定 `minimum-scale=1.0``maximum-scale=1.0``user-scalable=no`,同时保留 `viewport-fit=cover` 适配安全区。
- 浏览器仍可能通过 iOS `gesture*` 或多指 `touchmove` 触发整页缩放,因此主站启动入口应统一调用 `lockMobileViewportZoom()` 拦截页面级捏合与快速双击缩放。
- 不要在每个画布组件里重复注册缩放拦截;单指滚动、点击、拖拽应继续留给具体页面和玩法处理。
### 10.4 移动端输入法不要压缩画布
- 平台主站点击输入框弹出输入法时,不能让 `100dvh` 跟随键盘缩小后重新压缩整页布局。
- 正确做法是在输入法未打开时记录稳定布局高度,输入法打开期间保持画布高度不变,只根据当前输入框位置计算画面上移量。
- 该行为应在主站入口统一注册,业务组件只保留普通 `input` / `textarea`,不要在每个输入框里重复写键盘适配逻辑。
### 10.5 移动端创作生成页不要暴露批次视角
- 拼图、抓大鹅这类轻量玩法的草稿生成页只保留“预计等待”和“计时”两个用户关心的状态,移动端放在同一行;不要默认展示“当前批次”这类模型执行视角。
- 生成步骤在移动端进入页面时按顺序从左侧滑入,强化“正在推进”的节奏;动画只绑定步骤卡,不影响桌面端密集布局和其它信息卡。

View File

@@ -1,31 +0,0 @@
# PC 端世界生成与草稿页布局优化说明 2026-04-24
## 目标
在移动端现有布局不变的前提下,只优化 PC 端世界生成页与世界草稿页的信息组织,让页面更紧凑、更有层次,并保留全部已有功能入口。
## 范围
- 世界生成页:`src/components/CustomWorldGenerationView.tsx`
- 世界草稿页 / 作品页:`src/components/custom-world-home/CustomWorldCreationHub.tsx`
- 新建作品入口:`src/components/custom-world-home/CustomWorldCreationStartCard.tsx`
- 作品卡片:`src/components/custom-world-home/CustomWorldWorkCard.tsx`
- 筛选标签:`src/components/custom-world-home/CustomWorldWorkTabs.tsx`
## PC 端落地规则
1. 移动端默认类名保持原布局语义,只通过 `lg:` / `xl:` 断点追加 PC 布局。
2. 世界生成页在 PC 端改为左右双栏:左侧突出进度与阶段,右侧承载玩家设定 / 结构化锚点,减少纵向滚动。
3. 世界草稿页在 PC 端将“新建作品”和“作品列表”分区强化:顶部入口更紧凑,作品卡片网格密度提升。
4. 不新增规则说明文案,不改变按钮、筛选、删除、体验、进入等功能行为。
5. 中文文本只做必要保留,不因为布局调整改写已有中文内容。
## 视觉策略
- PC 端使用更明确的 `xl:grid`、固定信息侧栏和更小间距,让主内容首屏承载更多信息。
- 卡片在 PC 端降低无效高度,操作按钮与状态信息尽量同行展示。
- 作品卡片底部统计标签必须保留在卡片圆角范围内,不能为了压缩高度让标签贴边或被 `overflow-hidden` 裁掉。
- 卡片正文摘要优先缩短行数来给底部标签留空间;当标题、摘要或标签变长时,允许卡片自然增高。
- RPG 作品卡片点击行为按作品状态分流:草稿统一继续创作,已发布作品进入详情或世界;不要只依赖 `sourceType` 判断草稿可打开性。
- 整张作品卡片需要由卡片根节点承载点击与键盘打开能力,避免透明绝对定位按钮在真实浏览器中被判定不可见,导致自动化和用户点击不稳定。
- 保留现有 `platform-*` 视觉体系,避免引入新的 UI 系统。

View File

@@ -1,17 +0,0 @@
# PC 世界档案草稿编辑页布局修正 2026-04-24
## 背景
用户反馈 PC 端世界草稿页没有明显变化。复核截图后确认实际页面是世界档案草稿编辑页中的实体目录,而不是创作首页作品列表。
## 本次修正范围
- `src/components/CustomWorldEntityCatalog.tsx`
## 落地要求
1. 移动端仍保持原来的单列滚动、顶部标签与搜索结构。
2. PC 端把超宽单列实体列表改成卡片网格,减少横向空白,提高信息密度。
3. PC 端顶部世界标题、标签、搜索和操作按钮更紧凑,避免首屏被空白标题区占用。
4. 功能不变:搜索、切换标签、新增、批量删除、选择、编辑、发布入口均保持原有行为。
5. 不新增说明类文案,不改写已有中文内容。

View File

@@ -1,19 +0,0 @@
# 平台首页 Banner 图尺寸修复记录
更新时间:`2026-04-25`
## 问题
首页 Hero / banner 使用作品封面作为背景图。全局 `.platform-surface > *` 会把所有直接子节点重新设为 `position: relative`,导致带有 Tailwind `absolute` 类的背景图和遮罩被覆盖,图片重新进入普通文档流后可能撑高首页,影响首屏布局。
## 落地
- 修改 `src/index.css`,将 `.platform-surface > *` 收敛为 `.platform-surface > :not(.absolute)`
- 保留普通内容层的 `z-index: 1`,让文字、按钮仍稳定压在背景之上。
- 让 banner 背景图继续使用绝对定位和 `object-cover`,只在固定容器内裁切显示,不参与首页高度计算。
## 经验
- 首页、结果页、详情页的背景图都必须由固定尺寸容器承载,图片本身不要参与布局流。
- 给通用容器写层级规则时,不能无差别覆盖 `.absolute` 节点,否则会破坏所有“背景图 + 遮罩 + 内容”的结构。
- 后续若新增平台 Hero优先沿用 `platform-surface--hero`,并确认背景图节点仍是 `absolute inset-0 h-full w-full object-cover`

View File

@@ -1,20 +0,0 @@
# 可扮演角色外观模板字段删除经验
## 背景
可扮演角色曾通过 `templateCharacterId` 保存“外观模板”选择。当前角色主形象已经由 `visualDescription``imageSrc` 与生成资产链路承接,外观模板不应继续作为可扮演角色档案字段暴露给用户编辑或持久化。
## 落地边界
- 可扮演角色数据结构不再声明 `templateCharacterId`
- 可扮演角色编辑面板删除“外观模板”下拉项,保存时不再补默认模板。
- 草稿规范化与资料库读取时丢弃旧数据中的 `templateCharacterId`,避免旧快照把字段带回新数据。
- 运行时如需要基础动作、默认立绘或战斗标签,只能通过角色文本、参考 profile 或固定 fallback 规则临时推导模板,不再写回角色字段。
- 资产工坊可以继续接收运行时临时模板提示,但该提示不得成为可扮演角色的持久字段。
## 验收要点
- 新建或编辑可扮演角色时界面不出现“外观模板”。
- 保存后的 `playableNpcs` 条目不包含 `templateCharacterId`
- 旧存档带有 `templateCharacterId` 时,进入当前规范化链路后会被丢弃。
- 自定义世界运行角色仍能通过推导模板获得基础动作与默认占位图,不因字段删除而中断。

View File

@@ -1,268 +0,0 @@
# AI Native Visual RPG 开发经验沉淀
## 1. 项目特点判断
这个项目不是单纯的“像素 UI 项目”,而是 4 条链路同时耦合的项目:
1. 叙事链路AI 生成剧情文本与选项
2. 状态链路玩家、怪物、NPC、背包、好感、同伴、场景流转
3. 演出链路:战斗计划、动画播放、投射物、特效、镜头位移
4. 界面链路:选择世界、选择角色、冒险页、背包页、地图弹窗、编辑器页
经验:
- 做功能前先判断它主要影响哪几条链路。
- 如果一个需求同时影响“状态 + 演出 + UI”不要只改一个点。
- 像“初始同伴”这种功能,本质上不是 UI 需求,而是“选角流程 + 初始 encounter + NPC 好感 + 招募状态”的组合需求。
## 2. 先做数据建模,再做 UI
这一类项目最容易犯的错误,是先加按钮、再补状态。
实践下来更稳的顺序是:
1. 先把状态字段补齐
2. 再补工具函数
3. 再接交互入口
4. 最后补展示层
已经验证有效的状态字段包括:
- `playerInventory`
- `npcStates`
- `companions`
- `currentBattleNpcId`
- `currentEncounter`
- `playerActionMode`
- `activeCombatEffects`
经验:
- “能否交易 / 能否招募 / 送礼涨多少好感”都应该由状态和规则函数决定,不能写死在按钮文本里。
- NPC 交互尽量走本地规则,不要依赖 AI 即时决定关键数值。
## 3. 复杂页面一定要拆流程层
`App.tsx` 一旦同时承载:
- 游戏主状态
- 剧情生成
- 战斗播放
- NPC 交互
- 地图
- 选角
就会迅速失控。
当前更合理的分层思路是:
- `useGameFlow`
负责基础游戏状态、世界选择、角色选择、初始进入逻辑
- `useCombatFlow`
负责战斗计划与播放
- `useStoryGeneration`
负责剧情生成、NPC 本地交互分流、选项池管理
- `useNpcInteractionFlow`
负责同伴/NPC 展示态
- `GameShell`
负责主容器与选择流程
- `AdventurePanel`
负责冒险页文本和选项
- `NpcModals`
负责交易 / 送礼 / 放生招募等弹窗
经验:
- 流程层优先按“职责”拆,不按“文件长度”拆。
- 状态修改逻辑尽量集中到 hook 内,不要散落在多个组件按钮回调里。
## 3.1 AI 草稿数据进列表前,要先补本地稳定标识
自定义世界、角色草稿、澄清问题、生成结果卡片这类数据,在草稿态或兼容旧数据时,`id` 可能为空。
经验:
- React 列表的 `key` 不要直接裸用这类可能为空的 `id`
- 当前选中态、草稿缓存、轮播焦点也不要直接绑空 `id`,否则会出现“点了第二张卡,结果还是第一张卡被选中”的错位。
- 更稳的做法是:
- 业务数据层尽量补齐真实 id
- UI 层再补一层本地稳定 `selectionKey` / fallback render key
- fallback 至少带上 `index + 名称种子`,保证当前列表内唯一
## 4. AI 只适合生成叙事,不适合决定关键规则
实践中最稳定的策略是:
- AI 负责:
- `storyText`
- 非 NPC 关键规则的普通探索选项文案
- 本地规则负责:
- NPC 交互选项
- 交易合法性
- 礼物好感值
- 招募阈值
- 战斗掉落
- 帮助奖励
经验:
- 凡是会影响数值平衡、背包物品、好感、队伍成员的部分,都不要交给 AI 即时决定。
- AI 生成内容要能被本地规则兜底,否则体验会不稳定。
## 5. NPC 系统要“角色型 NPC”和“普通 NPC”分开处理
项目里 NPC 实际上有两种:
1. 普通场景 NPC可用通用 Medieval NPC 渲染
2. 角色型 NPC应该复用玩家角色对应的立绘和动画
经验:
- 只看 `encounter.kind === 'npc'` 不够,还要看 `encounter.characterId`
-`characterId` 的 NPC应该优先走 `CharacterAnimator`
- 否则就会出现:
- 选了某个同伴,但开场看到的是另一套通用 NPC 外观
- 战斗里角色型 NPC 看起来像普通路人
## 6. 位置与朝向必须统一到一套坐标规则
这是这类项目里最容易反复返工的点。
实践中踩过的坑:
- `sceneMonsters` 用一套坐标逻辑
- `currentEncounter` 用另一套坐标逻辑
- 结果开场 NPC 和遇怪站位不一致
- 角色型 NPC 立绘比怪物更容易出现“脚没落地”“太小”“翻转方向错”
最终经验:
- 对面实体的横向定位必须统一到“怪物那套 world-space 逻辑”
- 也就是:
- 位置统一用怪物侧的 anchor
- 相机平移时统一跟随同一套计算
- 角色型 NPC 的垂直位置不能偷懒固定
- 应该结合角色自身 `groundOffsetY`
- 朝向规则要统一使用 `getFacingTowardPlayer`
一句话总结:
- 角色型 NPC 不应该单独发明一套站位系统,而应该尽量复用怪物对位系统。
## 7. “新增流程”不要破坏原有选择 UI
这次初始同伴功能就是一个典型经验:
- 用户原本对“选择扮演角色”的视觉和交互已经形成预期
- 如果为了加“初始同伴选择”直接把角色选择页改成另一种样式,会造成明显割裂
经验:
- 新流程优先插在旧流程后面,而不是重写旧流程
- “确认角色 -> 选择初始同伴 -> 进入冒险”比“把原选角页改成全新样式”风险小很多
- 如果必须改 UI也要尽量保留旧页面的视觉结构和交互节奏
## 8. 冒险页布局要优先保证画面和选项完整可见
冒险页真正的优先级是:
1. 上方画布要在一屏内正常显示
2. 下方 3 个选项要在一屏内正常显示
3. 剧情文本框剩余空间自适应
经验:
- 文本框不能无限长撑开
- 正确做法是:
- 文本框高度自适应剩余空间
- 文本超长时内部滚动
- 不能让 `storyText` 把战斗画面和选项挤出首屏
## 9. 编辑器页和玩家页要明确隔离
当前项目里存在编辑器页面:
- `PresetEditor`
- `NpcVisualEditor`
- `StateFunctionEditor`
玩家页和编辑器页的需求完全不同。
经验:
- 像字体切换、视觉统一这种全局改动,不要直接打到整个站点
- 应该只挂在非编辑器根容器上
- 比如 `fusion-pixel-app` 这种类,只挂在正式游玩界面,不挂在编辑器根节点
## 10. 构建环境问题要项目内消化
实际踩到的构建问题:
- Node 16 环境下Vite 构建会因为 `crypto.getRandomValues` 缺失报错
沉淀出的解决方式:
- 不强依赖开发机立刻升级 Node
- 在项目内增加 Vite 启动包装脚本
- 统一让 `dev / build / preview` 都走这层 shim
经验:
- 环境兼容问题如果能在项目内吸收,就尽量不要把负担转移给每个协作者
- 文档里要明确记录“为什么这样做”
## 11. 比较稳的开发顺序
后续继续扩展功能时,建议遵守这个顺序:
1. 写状态字段
2. 写规则工具函数
3. 写流程 hook
4. 接 UI
5.`npm run lint`
6.`npm run build`
7. 再做视觉微调
不要反过来做:
- 先做 UI
- 再补状态
- 最后硬修流程
这种顺序在状态复杂的项目里会越改越乱。
## 12. 当前最值得继续坚持的原则
- 保持 AI 生成和本地规则分工清晰
- 保持角色型 NPC 与普通 NPC 的渲染分流
- 保持“怪物 / encounter / 战斗 NPC”统一坐标系
- 保持新增功能不破坏既有核心 UI 体验
- 保持编辑器页与玩家页隔离
- 每次大改后都用 `lint + build` 双重验证
## 13. 后续建议
下一阶段最值得继续沉淀的方向:
1. 把 NPC 交互逻辑继续从 `useStoryGeneration` 中独立成更纯粹的 `useNpcInteractionFlow`
2. 把角色型 NPC 的位置、缩放、贴地参数做成可配置规则,而不是继续散落在画布里微调
3. 针对初始同伴流程补一份单独的状态图 / 时序图
4. 对大 chunk 警告做代码分包
## 14. SpacetimeDB 绑定桥接层要做同名去重
`server-rs/crates/spacetime-client` 里有一部分内容是围绕 SpacetimeDB 生成绑定补的手写桥接层。
经验:
- 新增 procedure、input type 或 mapper 时,先全局确认 `module_bindings/mod.rs``mapper.rs`、业务封装文件里是否已经存在同名声明
- `module_bindings/mod.rs` 同一个模块只保留一条 `pub mod` 和一条 `pub use`,不要同时放在 reducer 区和 procedure 区
- `mapper.rs` 的字符串枚举解析函数、API 入参结构只保留一个权威定义,业务侧统一复用
- 业务封装文件里同一个 procedure 只暴露一个客户端方法,避免 Rust 在编译期出现 E0428、E0252、E0119、E0592 这类重复定义错误
- 修复重复绑定时优先删除后追加的重复块,不要重写整文件,避免影响中文注释和生成绑定附近的大段内容
## 15. 一句话总结
这个项目真正的开发经验不是“怎么多写一个按钮”,而是:
- 在 AI 叙事、像素演出、战斗状态、NPC 规则、选择流程和编辑器体系同时存在的情况下,始终让每条链路各归其位。

View File

@@ -1,280 +0,0 @@
# 奇幻酒馆项目开发经验手册
日期:`2026-03-24`
## 1. 项目本质判断
这个项目不是单纯的前端页面项目,也不是单纯的大模型接入项目,而是 4 条链路同时存在的复合型项目:
1. 叙事链路:剧情文本、选项文本、角色对话、聊天上下文。
2. 状态链路角色、怪物、NPC、场景、背包、装备、战斗状态、CD、蓝耗、死亡、掉落。
3. 演出链路:角色进场、近战贴身、受击、死亡、逃跑、镜头跟随、场景切换、前探预览。
4. 工具链路NPC 形象编辑器、行为编辑器、预设数据、校验脚本、本地调试环境。
经验结论:
- 任何需求只要影响两条以上链路,就不能只改 UI。
- 先判断需求落在哪些链路,再决定改哪些文件。
- 这类项目最怕“看起来改好了”,但状态、动画、提示词和运行逻辑其实没对齐。
## 2. 架构拆分经验
已经验证更稳的结构是:
- `App.tsx` 保持尽量薄,只做外层挂载。
- `GameShell.tsx` 负责主流程壳层:开始页、世界选择、角色选择、游戏内主界面切换。
- 各类状态和流程尽量放进 hooks
- `useGameFlow`
- `useCombatFlow`
- `useStoryGeneration`
- `useNpcInteractionFlow`
- `useInventoryFlow`
- `useEquipmentFlow`
- 各类独立 UI 面板拆成独立组件:
- `AdventurePanel`
- `CharacterPanel`
- `InventoryPanel`
- `MapModal`
- `AdventureEntityModal`
经验结论:
- 按职责拆,不按文件长度拆。
- 状态修改尽量集中在 hook 内,不要散落在多个按钮回调里。
- 预览系统、编辑器系统、正式游戏流程要尽量复用同一套业务逻辑,而不是各写一套。
## 3. AI 与本地规则的边界
目前最稳定的边界是:
- AI 负责生成:
- `storyText`
- `actionText`
- 对话文本
- 基于上下文的语气、叙事张力、选项措辞
- 本地规则负责决定:
- 哪些 function 当前可执行
- 技能是否在 CD
- 蓝量是否足够
- 伤害、回复、掉血、死亡、掉落
- 场景切换是否合法
- 背包、装备、属性、队伍变化
经验结论:
- 涉及数值、资源、状态迁移的部分,不要交给模型临场决定。
- 模型应被限制在“叙事表达层”,不要替代规则系统。
- 用户每次选择后只做一次模型推理,因此提示词必须一次性包含足够边界,不能再依赖后处理改写和过滤。
## 4. Function 驱动设计经验
当前更合理的设计方向是:
- 所有可选行为都落到 function。
- 每个 function 只有这些核心字段:
- `id`
- `text`
- `description`
- `category`
- `功能代码`
- 运行时先汇总当前场景的 function再结合角色、状态、敌人、场景实体、资源状态做合法性过滤。
- 模型只在“当前合法 function 集合”内生成选项文本和剧情文本。
经验结论:
- 选项不是自由散文,而是 function 的叙事包装。
- function 必须和角色、状态、场景、实体绑定,不能做成完全漂浮的公共动作池。
- “每次选择只能并且必须命中一个 function” 是保持状态稳定的关键。
## 5. 战斗与动画的经验
战斗部分已经沉淀出的几个重要规则:
### 5.1 结算时机
- 用户点击选项后,后台状态应立即推进到“动画完成后的结果状态”,并把这个新状态放入后续推理上下文。
- 但扣血、死亡表现、消失等视觉结果要在动画播完后再体现在画面上。
这样做的好处:
- 模型推理可以立即开始,不被动画阻塞。
- 画面依然保留正确的演出节奏。
- 状态、上下文、下一轮推理不会落后于动画。
### 5.2 近战位移
- 近战动画不能按固定像素移动。
- 必须按左右锚点、舞台宽度和目标位置计算真实接近距离。
- 宽屏下如果只“往前走一点点”,基本就是位移基准用了屏幕百分比或固定偏移,而不是双方真实锚点差值。
### 5.3 死亡与退出战斗
- 怪物血量归零时先播死亡动画。
- 死亡动画完成后怪物再消失。
- 若敌方死亡,战斗状态应退出,切回空闲状态,并重新切换可用 function 集合。
### 5.4 逃跑与压迫感
- 逃跑镜头要跟着玩家,不是跟怪物。
- 玩家向前跑,怪物被越甩越远,才能形成“甩开追兵”的感觉。
- 相反,如果镜头锚在怪物侧,会让玩家像没动一样。
## 6. 遭遇、场景与地图经验
这部分已经验证有效的设计原则:
- 场景和背景图一一绑定,不切场景就不换背景。
- 场景切换必须由选项命中的 function 驱动,而不是每回合自动变化。
- 每个场景预设自己的怪物、NPC、宝藏池。
- 玩家“继续向前探路”时,不是立刻随机弹结果,而是先预生成前方实体,再通过移动和镜头演出把它带到正式交互位。
经验结论:
- “探索”也应该是一种可演出的 function而不只是一次文字刷新。
- 同一时刻只应遇到一个主实体,避免一回合同时出现多只怪物造成状态混乱。
- 地图系统适合做成场景连接图,由具体 function 触发前往某个场景。
## 7. 移动端优先的 UI 经验
已经验证有效的 UI 方向:
- 先保证移动端一屏成立,再兼容网页宽屏。
- 开始页、世界选择、角色选择都要像游戏流程,而不是产品介绍页。
- 世界选择适合纵向焦点滑动。
- 角色选择适合横向翻卡和中心焦点展示。
- 冒险页中:
- 上方是画面演出
- 中间是自适应剧情文本
- 底部是固定的选项与功能入口
经验结论:
- 剧情区域不能写死高度,应填满画面下方和底部操作条上方之间的剩余空间。
- 队伍、背包更适合作为弹出面板,不应该切走主流程。
- 地图弹窗、队伍面板、背包详情都必须按手机窄屏重新组织,不要沿用桌面弹窗思路。
## 8. 选择页与游戏内界面经验
这轮迭代里比较明确的 UI 结论有:
- 开始页只保留核心按钮,视觉简洁更像正式游戏。
- 世界选择页要突出当前聚焦卡,其他卡随滑动连续改变透明度和大小。
- 角色选择页中:
- 中间卡片不能模糊
- 左右卡片要做正确方向的倾斜
- 中间角色可播放 `run` 动画
- 角色名称显示在动画下方
- 属性和背景信息要紧凑,不能压缩掉上方卡片滑动空间
- 游戏内按钮应尽量图标化,把更多空间留给剧情和选项。
## 9. NPC 形象编辑器经验
NPC 编辑器这部分已经沉淀出一些通用原则:
- 这套 Medieval Fantasy Characters 素材不是传统逐帧全身序列帧,发型、脸型、胡子、武器等部件不能按“每帧都变化”的思路处理。
- 主手武器、副手武器、手、身体、头部、头饰需要明确图层关系。
- 主手武器必须真正握在手里,不能只靠大概位置“看起来差不多”。
- 因为素材高度重叠,编辑器里必须支持:
- 底部组件模块点击选择
- 键盘上下左右微调
- 拖拽微调
- 回滚按钮
经验结论:
- 视觉编辑器如果没有“可控选择 + 微调 + 回滚”,用户会很难调重叠素材。
- 选项命名不能直接暴露英文源文件名,最好转成更贴近视觉理解的中文名称。
- 编辑器产出的相对位置数据必须能直接落回项目运行时,不然就只是一个孤立工具。
## 10. 大模型接入与本地调试经验
这部分踩坑非常集中,结论也比较清晰:
### 10.1 前端不能直连目标模型接口
- 浏览器直连时会遇到 CORS。
- 即使接口本身可用,浏览器环境也可能被跨域限制。
- 更稳的方案是在开发服务器侧做代理,再由前端请求本地 `/api/llm/...`
### 10.2 需要日志,但日志要聚焦
已经证明有价值的日志包括:
- 单次推理耗时
- 原始提示词文本
- 原始返回中的解析后文本
- 失败时的模型名、状态码和错误正文
### 10.3 开发服务器要统一入口
本项目本地正确启动方式应统一走:
```bash
node scripts/vite-cli.mjs --port=3000 --host=0.0.0.0
```
经验结论:
- 只要本地存在旧进程、旧脚本或错误端口映射,就很容易出现“代码改了但界面还是旧的”假象。
- 遇到这类问题,优先检查实际启动脚本、端口占用和返回模块内容,而不是先怀疑 UI 代码没生效。
## 11. 编辑器与正式运行时的关系
已经验证最稳的做法是:
- 编辑器预览直接复用正式运行时函数。
- 预览不自己模拟一套战斗和 function 执行逻辑。
- 运行时怎么结算,编辑器就怎么调用。
经验结论:
- 只要编辑器预览和正式逻辑分成两套,后面一定会越来越不一致。
- 预览系统的价值不是“看起来像”,而是“执行路径就是正式路径”。
## 12. 后续继续开发时建议遵循的顺序
推荐流程:
1. 先补数据结构和类型。
2. 再补 function 规则和过滤条件。
3. 再补 hook 流程与状态迁移。
4. 再补动画和演出。
5. 最后再做 UI 细修。
6. 每轮改动后至少做一次类型检查和本地启动验证。
不推荐的流程:
1. 先堆 UI。
2. 再临时塞状态。
3. 最后补运行逻辑。
这类项目里,后者几乎一定导致返工。
## 13. 后端修改后的重启与测试
后端代码更新后统一执行:
```bash
npm run api-server
```
执行要求:
- 该命令是后端更新后的默认重启入口,不再使用此前的后端重启命令。
- 重启后必须继续执行与本次后端改动对应的自动测试;涉及 Rust workspace 时优先跑 `server-rs` 下的检查或测试脚本。
- 若本次改动涉及 SpacetimeDB 发布、绑定生成或本地联调,按 `spacetimedb-cli` 经验执行,并在验证记录中写清楚实际命令与结果。
## 14. 一句话总结
这个项目最重要的经验不是“做了多少页面和功能”,而是:
**必须把 AI 文本生成、本地规则、动画演出、场景状态、编辑器工具这几套系统严格分层,再通过 function 和统一状态流把它们重新接起来。**
## 15. 相关文档
如需继续细看已有沉淀,可结合以下文档一起阅读:
- `docs/experience/PROJECT_DEVELOPMENT_EXPERIENCE.md`
- `docs/experience/MOBILE_UI_DEV_EXPERIENCE.md`
- `docs/experience/CODEX_IMPLEMENTATION_EXPERIENCE_2026-03-24.md`
- `docs/experience/AGENT_UI_CHANGELOG.md`

Some files were not shown because too many files have changed in this diff Show More