# 当前 Function 设计审计(2026-04-03) ## 审计范围 本次审计重点阅读并对照了这些位置: - `docs/ADVENTURE_RUNTIME_DEV_EXPERIENCE.md` - `docs/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md` - `docs/PROJECT_DEVELOPMENT_EXPERIENCE.md` - `docs/CURRENT_GAME_ITERATION_PRIORITIES_2026-04-03.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 越多,这类问题会越难排查。