Rework story engine flow and reorganize project docs
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-06 23:19:00 +08:00
parent d678929064
commit ddcb5d5c8c
241 changed files with 19805 additions and 2478 deletions

View File

@@ -0,0 +1,650 @@
# 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

@@ -0,0 +1,787 @@
# 角色首遇感、背景故事分层解锁与同伴私聊功能设计
更新时间:`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`
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
- 它们只能作为“某个具体场景下调用通用首遇规则”的薄包装
- 不应继续承担独立的开场规则系统
也就是说:
**开场营地只是首遇规则的一个调用场景,不是首遇规则本体。**
## 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

@@ -0,0 +1,492 @@
# 自定义世界创作者输入与 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`
- `dangerLevel`
- 精确数值型 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

@@ -0,0 +1,882 @@
# 配装构筑 + 合成/锻造闭环详细设计
更新时间:`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 相似度”的方式形成构筑强度,并把这份构筑强度直接接进当前伤害输出,同时让怪物掉落、宝藏、交易、拆解、合成、锻造、铭刻围绕同一套标签体系形成闭环。

18
docs/design/README.md Normal file
View File

@@ -0,0 +1,18 @@
# 系统设计
这一组是玩法、关系、物品、对话等系统设计文档,偏“应该怎么设计”而不是“现在哪里出问题”。
## 文档列表
- [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 分工边界设计。
- [AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md](./AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md):运行时物品生成系统重设计。
- [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-conversation-situation-draft.md](./npc-conversation-situation-draft.md)NPC 对话阶段和情景注入草案。
## 推荐阅读
- 做物品、Build、锻造相关需求时先看前两份。
- 做自定义世界创作工作台、创作者输入边界、AI 分工设计时,先看第一份。
- 做角色关系、同伴互动、对话表现时,先看后两份。
- 如果要判断是否符合目标,再和 `docs/prd/` 中对应 PRD 对照阅读。

View File

@@ -0,0 +1,192 @@
# 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 连续几轮聊天时,信息释放节奏是连续的,不会忽冷忽热