# `nana-story` V1 开发计划 > 中文名:《听娜娜讲故事》 > 计划版本:0.1 > 制定日期:2026-07-28 > 目标平台:Windows 桌面端 > 执行模型:1 个集成人 + 最多 3 个并行子智能体 ## 1. 交付目标 V1 不是“通用 AI Galgame 平台”的完整形态,而是一段能够证明整个产品闭环的可玩纵切: 1. 用户通过 LAPP 选择并调用聊天模型; 2. 导入或使用内置的娜娜试玩资源包; 3. 创建故事并进行 20~30 分钟的自由输入式角色扮演; 4. 经历至少一次隐藏的简化 CoC7 判定; 5. 建立一项重要许诺,并让行为影响信赖、希望等关系维度; 6. 获得、使用或转移一件带隐藏背景的持有物; 7. 随时查看自己的持有物、已知线索和已接受许诺; 8. 查看模糊化的六维关系雷达图; 9. 每轮自动保存,退出后能够恢复; 10. 回溯到旧节点继续时创建新分支,并保留原线路。 V1 的成功标准不是资源数量,而是: > 从创建故事到完成纵切,全程不需要开发者干预;状态可重放、隐藏信息不泄漏、回溯不破坏旧分支、异常不会留下半轮存档。 ## 2. 已冻结的产品范围 ### 2.1 技术与平台 - 仓库名:`nana-story`。 - 首发 Windows 桌面端;安卓以后再考虑。 - 技术栈: - Tauri 2 - Vue 3 - TypeScript - Vite - Naive UI - Pinia - Vue Router - 管理界面可以使用 Naive UI,游玩界面采用自定义视觉实现。 - 管理端、游玩端和运行时位于同一个 Tauri 桌面进程,不引入 daemon、sidecar 或本地网关。 ### 2.2 模型与 LAPP 边界 - 供应商协议、模型选择、凭据解析和网络调用由 `lapp-rs` 负责。 - `nana-story` 不直接依赖任何供应商 SDK。 - `nana-story` 只提供必要的 LAPP 查看、选择、连接测试和简单管理能力。 - 本地模型只作为 HTTP 服务接入。 - 不负责安装、下载、启动、停止、更新或管理 Ollama、LM Studio 等本地模型运行器。 - 开发时可使用相邻仓库的 path dependency;发布构建必须固定到明确的 `lapp-rs` 提交,不能复制其协议语义。 ### 2.3 游戏交互 - 自由输入为主。 - 每轮可提供 2~3 个可编辑行动建议;点击后只填入输入框,不直接提交。 - 提供“继续”按钮,表示沉默、观察、等待或允许 NPC 继续行动。 - AI 不得替玩家决定说话、主动行动或内心想法。 - 时间按照实际行动推进,不按消息轮数推进。 - 高风险行动需要先给出叙事风险提示,再执行隐藏的简化 CoC7 判定。 - 玩家默认看不到骰点、技能数值和精确关系变化。 - 回复由若干短演出节拍构成,而不是一整块聊天气泡。 ### 2.4 角色、关系与剧情 - V1 只有一个完整角色:娜娜。 - 剧情模块可以包含会说话的轻量配角,但不为其建立完整关系系统、长期记忆或自主调度。 - 六维关系: - 好感 `affinity` - 信赖 `trust` - 希望 `hope` - 尊重 `respect` - 亲密 `intimacy` - 依恋 `attachment` - 信赖主要来自已经发生的行为。 - 希望主要来自被接受的许诺和共同未来计划。 - 重要许诺必须保存为独立对象,不能简化成一次关系数值变化。 - 怨恨、恐惧、愧疚、嫉妒等保存为带来源的关系状态,不占六维。 - 创建故事时绑定剧情模块;V1 不支持中途增删模块。 ### 2.5 持有物、知识与分支 - 物品定义、物品实例和角色认知必须分离。 - 所有权 `owner` 与当前持有者 `holder` 必须分离。 - 玩家可以随时读取自己当前可访问的持有物。 - NPC 的真实持有物不能直接暴露;只能显示公开可见或玩家已经知道的内容。 - V1 不处理负重、体积、格子或背包容量。 - 每轮自动保存。 - 从旧节点继续时创建新分支。 - V1 不做分支合并,也不建立跨分支记忆。 ## 3. V1 明确不做 - 多个完整角色及多角色发言调度。 - 导演 Agent、自动总结 Agent 或运行时多 Agent 编排。 - 完整 CoC7 职业、成长、战斗、理智、追逐和幸运消费。 - Live2D、语音、口型、动态镜头和复杂动画。 - 分支合并和跨分支记忆。 - 中途增删剧情模块。 - 负重、体积、容量、制作和物品经济。 - 内置资源编辑器或可视化剧情编辑器。 - Steam Workshop 正式接入、DLC 支付、社区付费内容抽成。 - 云同步、账户、多人或跨设备存档。 - Android、Web 和多语言发布。 - 模型下载器、模型启动器和本地模型进程管理。 - 任意脚本、任意插件或任意外部工具执行。 V1 只保证资源包格式未来可以承接 Workshop/DLC,不实现市场本身。 ## 4. 总体架构 ### 4.1 三类事实 | 层级 | 内容 | 权威来源 | |---|---|---| | 创作资源 | CharacterCard、WorldBook、Persona、PlotModule、ItemSpec 和素材引用 | 不可变的版本化资源 | | 分支状态 | 世界状态、关系、许诺、知识、持有物、时钟、判定记录 | 根状态/检查点 + 节点 delta | | 上下文投影 | 当前意图、命中的世界书、最近演出、提示词 | 每轮临时编译,可重建 | 任何模型推测、检索结果和临时提示词都不是持久事实。需要持续存在的变化必须经过类型化操作,由状态 reducer 验证后写入当前分支。 ### 4.2 单一权威写入路径 ```mermaid flowchart TD A["玩家输入"] --> B["上下文编译"] B --> C["单模型工具循环"] C --> D["暂存工具结果与状态操作"] D --> E["规则校验与 reducer"] E --> F["原子提交 StoryNode"] F --> G["生成 PlayerView"] G --> H["Vue 演出"] ``` 约束: - 模型和前端都不能直接修改 `RuntimeState`。 - reducer 是状态变化的唯一权威入口。 - 一轮生成要么完整提交,要么完全不提交。 - 模型失败、取消、超时或畸形输出不得留下半轮状态。 - 同一玩家行动的重新生成复用原判定记录;推骰属于新行动和新节点。 ### 4.3 前后端安全边界 前端不得得到完整 `RuntimeState`,只能得到经过投影的 `PlayerView`: ```text PlayerView ├─ 当前演出节拍 ├─ 可编辑行动建议 ├─ 玩家可访问的持有物 ├─ 已知线索 ├─ 已接受许诺 ├─ 模糊化六维关系 └─ 可回溯节点摘要 ``` 这条边界用于结构性防止: - NPC 隐藏物品泄漏; - 未揭示世界事实泄漏; - 精确关系数值泄漏; - 内部骰点和难度泄漏; - 未触发的剧情节点泄漏。 ### 4.4 推荐持久化 V1 默认采用嵌入式 SQLite,由 Rust 侧独占访问。 - 内容包以经过校验的不可变文件形式安装到应用数据目录。 - SQLite 保存故事、资源绑定、节点、分支、检查点和迁移版本。 - 节点中的状态操作使用版本化、类型化 JSON。 - 每隔固定节点数创建检查点,恢复时使用“最近检查点 + 后续 delta”。 - SQLite 事务同时提交用户输入、模型演出、工具结果、判定记录和状态 delta。 建议最小表: ```text stories story_bindings story_nodes story_branches state_checkpoints installed_packs schema_migrations ``` 不要在 V1 过早拆出几十张关系、物品、许诺明细表。分支事件流才是事实来源,当前状态是可重建投影。 ### 4.5 模型运行时 V1 游戏运行时仍然只有一个模型,不引入导演 Agent。 每轮流程: 1. 读取当前节点、资源绑定和重建后的运行时状态; 2. 按优先级编译上下文; 3. 调用 `lapp-rs` 选中的聊天模型; 4. 模型按需调用判定、关系、许诺、知识、持有物、时钟等领域工具; 5. Rust 验证工具参数并写入暂存事务; 6. 模型根据工具返回继续生成短演出节拍和行动建议; 7. 校验演出协议与全部状态操作; 8. 原子提交新节点; 9. 生成脱敏后的 `PlayerView` 给前端。 上下文优先级: 1. 玩家自主权和输出协议; 2. 当前角色卡、Persona 与场景; 3. 当前关系、许诺、知识、持有物和剧情压力; 4. 命中的世界书条目; 5. 当前分支最近演出; 6. 非关键风格参考。 V1 不做向量记忆库。世界书先采用确定性的标签、关键词和条件触发;长上下文压缩只能作为可丢弃缓存,不能成为新的事实来源。 ## 5. 仓库结构与代码所有权 建议从第一天就使用 Rust workspace,把并行边界变成目录边界: ```text nana-story/ ├─ src/ # Vue 应用 │ ├─ app/ # 路由、全局状态、真实/Mock adapter │ ├─ features/play/ # 主演出屏 │ ├─ features/inventory/ # 持有物、线索、许诺 │ ├─ features/relationship/ # 关系档案 │ └─ features/history/ # 回溯与分支 ├─ src-tauri/ # Tauri 外壳与窄命令 ├─ crates/ │ ├─ nana-domain/ # 资源、状态、delta、PlayerView │ ├─ nana-engine/ # reducer、规则、简化 CoC7 │ ├─ nana-store/ # SQLite、节点、检查点、恢复 │ └─ nana-runtime/ # LAPP、上下文、工具循环、Turn Engine ├─ contracts/ # 生成的 JSON Schema 与 TS 类型 ├─ content/nana-demo/ # 试玩纵切资源包 ├─ fixtures/ # 稳定的契约和模型响应样例 ├─ tests/e2e/ # 跨模块验收 └─ docs/adr/ # 已接受架构决策 ``` 单一契约源: ```text Rust serde 类型 → 生成 JSON Schema → 生成 TypeScript DTO → CI 检查生成物是否漂移 ``` 禁止同时手写 Rust、Schema 和 TypeScript 三套同名模型。 ## 6. 多子智能体并行方案 ### 6.1 推荐拓扑 并行度维持在 1 个集成人 + 3 个子智能体。 | 角色 | 独占范围 | 主要职责 | |---|---|---| | 集成人 | 公共契约、根配置、入口、生成物、跨模块接线、最终验收 | 拆任务、冻结契约、日常合并、拒绝范围扩张 | | 子智能体 A:Core | `nana-domain`、`nana-engine`、`nana-store` | 类型、reducer、规则、SQLite、分支重放 | | 子智能体 B:Runtime | `nana-runtime` | LAPP、上下文编译、工具循环、Turn Engine | | 子智能体 C:Experience | `src/`、`content/nana-demo/` | Vue 界面、演出、官方 Fixture、娜娜纵切内容 | 测试靠近各自模块编写;跨模块 E2E 和契约生成由集成人负责。 不建议同时开启 5~8 个编码子智能体。这个项目的瓶颈是共享语义而不是代码量,过高并行度会把收益变成 Schema、状态提交和 UI DTO 的返工。 ### 6.2 必须先冻结的契约 进入正式并行前必须合并: - `ResourceHeader` - `CharacterCard` - `WorldBook` - `Persona` - `PlotModule` - `ItemSpec` 与 `ItemInstance` - `Story`、`StoryBinding`、`StoryNode` - `RuntimeState` - 所有状态操作的判别联合 - `PresentationBeat` - `PlayerView` - `TurnRequest`、`TurnResult`、`TurnFailure` - 五组 canonical fixtures: - 普通对话; - 隐藏判定; - 许诺建立与履行; - 物品转移且 NPC 尚未知情; - 回溯后分叉。 ### 6.3 Worktree 与分支 每个子智能体必须使用独立 Git worktree: ```text integration/v1 agent/core agent/runtime agent/experience ``` 规则: - 所有分支从同一份已冻结契约提交开始。 - 每个子智能体只修改分配的目录。 - 根 `Cargo.toml`、`package.json`、锁文件、CI、Tauri 入口、Vue 入口和生成代码只由集成人修改。 - 子智能体需要新增依赖或修改公共契约时,提交短小的变更请求,不直接跨目录修改。 - 禁止无关重构、全仓格式化和顺手升级依赖。 - 每天至少合并一次小的可验证增量,不在阶段末进行巨型合并。 - 只有集成人解决跨模块冲突。 ### 6.4 子智能体任务包模板 每个任务必须包含: ```text 目标: 基线提交: 允许修改的目录: 输入契约版本: 必须新增的测试: 验收命令: 明确不做: 交付时需要说明的风险: ``` 没有这些信息的任务不应分派给子智能体。 ## 7. 里程碑与并行波次 基准工期为 25 个有效开发日,约 5 周。AI 子智能体可以压缩编码时间,但不能取消契约、纵切联调和 Windows 发布验证。 ### M0:工程骨架与契约冻结(第 1~2 日) 交付: - 初始化 Tauri 2 + Vue 3 工程和 Rust workspace; - 接入相邻 `lapp-rs`,固定开发基线; - 建立 `pnpm verify` 或等价的一键校验; - 定义 Rust 领域类型、生成 JSON Schema 和 TS DTO; - 建立 canonical fixtures; - 内置最小娜娜资源包; - 写入首批 ADR。 演示: - Windows 桌面窗口可以启动; - 前端通过窄命令读取应用版本和一份静态 `PlayerView`; - 最小娜娜资源包能够加载。 硬门槛: - CI 可重复运行; - 非法资源返回稳定错误; - ID、版本、哈希、依赖和引用可校验; - Rust、Schema、TS 不存在多个事实来源。 并行: - 集成人:契约与仓库骨架; - A:CI 和 Rust workspace; - B:Fake LAPP Provider/录制模型响应夹具; - C:按四张原型搭静态 UI 外壳。 ### M1:确定性状态内核(第 3~6 日) 交付: - `RuntimeState`; - 纯 reducer; - 六维关系与单轮限幅; - 许诺生命周期; - 知识与可见性; - 物品 owner/holder、位置、取得方式; - 时钟; - 简化 CoC7 判定记录; - `PlayerView` 投影; - SQLite 节点、delta 和检查点。 演示: - 不调用模型,仅通过固定命令完成: - 获得物品; - 作出许诺; - 改变关系; - 推进时钟; - 执行一次判定; - 保存、关闭、恢复。 硬门槛: - 同一根状态 + 同一组 delta 必须产生完全相同的 `RuntimeState` 和 `PlayerView`; - delta 重放不能重复结算; - 关系变化会被规则限幅; - owner/holder 语义正确; - 玩家无法读取 NPC 未知持有物; - 同一行动重新生成时可以复用判定记录。 并行: - A:domain、engine、store; - B:基于 fixture 实现 runtime 接口和 Fake Provider; - C:前端使用同一份 `PlayerView` fixture 完成完整假数据交互。 ### M2:单轮模型运行循环(第 5~10 日,与 M1 后半段重叠) 交付: - LAPP profile 读取、模型选择和连接测试; - 非流式与流式聊天适配; - 上下文编译器; - 领域工具目录; - 工具循环和最大轮次保护; - `PresentationBeat`; - 暂存事务、校验、提交和回滚; - 取消、超时、畸形输出和一次安全重试。 演示: - 用户输入一句自由文本; - 娜娜以短节拍回应; - 表情发生一次可选变化; - 生成 2~3 个可编辑建议; - 一次需要判定的行动经过工具返回后继续叙述; - 整轮状态一次性提交。 硬门槛: - 模型和 UI 都不能直接写 `RuntimeState`; - 工具失败、取消和断流不产生半轮节点; - 模型不能替玩家决定主动言行和内心; - 高风险行动先有风险提示; - 凭据、完整上游错误体和隐藏状态不进入日志或前端 DTO; - CI 使用 Fake Provider,不依赖真实付费模型。 并行: - A:补齐 reducer 和 store 的运行时接口; - B:实现 LAPP adapter、上下文和工具循环; - C:把主演出屏从 fixture adapter 切到真实 Turn API。 ### M3:自动存档、回溯和分支(第 8~14 日) 交付: - 每轮原子自动保存; - 分支头和节点父子关系; - 从任意旧节点创建新分支; - 旧线路保留; - 故事恢复; - 同输入重新生成与推骰的不同语义; - 简洁故事时间线和完整回溯页。 演示: - 连续玩 5 轮后退出; - 重启恢复; - 回到第 2 轮并继续; - 原分支和新分支同时存在; - 两条分支的持有物、许诺和关系状态互不污染。 硬门槛: - 每轮只出现一次有效提交; - 重启后的状态与提交前一致; - 回溯不会修改旧节点; - 重新生成复用原判定; - 推骰创建新行动和新判定记录; - 500 节点以内的分支恢复不需要扫描无关分支。 并行: - A:分支、检查点、恢复和迁移; - B:重新生成、推骰与运行时事务; - C:故事回溯 UI 和错误恢复交互。 ### M4:完整玩法系统与四个界面(第 11~18 日) 交付: - 主游戏界面; - 持有物/线索/许诺侧栏; - 六维关系档案; - 故事回溯页; - 角色卡、世界书、Persona、剧情模块和 ItemSpec 的导入校验; - 轻量配角; - 场景、立绘、表情和姿态保持; - “自由输入 / 建议 / 继续”三种操作。 演示: - 四张界面原型对应的真实交互全部接入运行状态; - 玩家查看自己的持有物; - NPC 未知物品不会出现; - 重要许诺能从 accepted 发展到 fulfilled 或 broken; - 关系雷达变化但不显示精确数字; - 轻量配角可以参与一段事件但不会获得完整关系系统。 硬门槛: - 所有 UI 数据只来自 `PlayerView` 或其他脱敏 DTO; - 不显示骰点、检定难度和精确关系增量; - UI 不把消息和演出节拍混为一套结构; - 静态资源加载失败有占位和明确错误; - 导入包不能通过路径穿越写出目标目录。 并行: - A:资源导入、规则系统和 PlayerView; - B:世界书触发、剧情工具和轻量配角上下文; - C:四个界面和演出状态机。 ### M5:娜娜 20~30 分钟纵切(第 17~21 日) 纵切至少包含: - 一个明确开场目标; - 一次普通自由对话; - 一次隐藏判定; - 一次玩家可感知的风险提示; - 一个被接受的重要许诺; - 一次用行动证明或破坏许诺; - 一件具有隐藏背景的物品; - 一次 owner/holder 不同的物品状态; - 一次关系路线分化; - 一个剧情时钟; - 一次轻量配角发言; - 一次回溯; - 至少两个结果方向。 硬门槛: - 无开发者操作可以从开场玩到结果; - 条件触发没有死路; - 角色不泄露玩家尚未获得的知识; - 不同选择产生可回放的不同状态; - 引擎规则中不得硬编码 `nana` 或纵切事件 ID; - 使用另一个最小反例资源夹具证明引擎仍然通用。 本阶段停止增加系统功能。所有子智能体只处理纵切暴露的问题。 ### M6:Windows 发布前加固(第 22~25 日) 交付: - Windows 安装包; - 崩溃和断电恢复; - 存档迁移夹具; - 模型超时、断网、限流、拒绝、畸形工具调用处理; - 长对话上下文预算测试; - 日志脱敏; - 内容包边界和尺寸限制; - 用户可理解的错误页; - 最小发布说明和已知限制。 硬门槛: - 全新 Windows 环境能够安装、启动和卸载; - 配置一个 LAPP 模型后能够完成纵切; - 进程被强制关闭后不会产生半轮节点; - 旧 Schema 存档夹具能够迁移或给出明确的不兼容提示; - 凭据不进入前端状态、日志、崩溃报告或存档; - 所有 canonical fixtures、单元测试和跨模块验收通过。 ## 8. 合并顺序与质量门 每个并行波次按以下顺序合并: 1. 契约与 fixtures; 2. domain/engine/store; 3. runtime; 4. client adapter; 5. 跨模块 E2E; 6. 纵切内容更新。 任何合并都必须跑同一条最小跨模块场景: ```text 创建故事 → 三轮交互 → 一次隐藏判定 → 获得物品 → 建立许诺 → 关闭并重启 → 回溯 → 从旧节点继续 → 校验两个分支互不污染 ``` 阶段门禁: - M1 未通过确定性重放,不接正式存档 UI。 - M2 未实现“校验后一次性提交”,不允许模型修改运行时状态。 - M3 未通过分支隔离,不开放回溯入口。 - M5 未跑通完整纵切,不扩展第二完整角色、编辑器、Workshop 或 Live2D。 ## 9. 测试矩阵 | 层级 | 主要测试 | |---|---| | Domain | Schema、ID/引用、版本/哈希、资源依赖、反序列化错误 | | Engine | reducer、限幅、许诺生命周期、知识可见性、物品 owner/holder、CoC7 | | Store | 原子提交、重放、检查点、分支隔离、迁移、损坏恢复 | | Runtime | Fake Provider、工具循环、上下文预算、取消、超时、畸形输出 | | Security | PlayerView 泄密、日志脱敏、内容包路径穿越、凭据边界 | | Frontend | 演出状态机、建议编辑、继续、侧栏、雷达、回溯 | | Cross-module | 完整最小场景、重启恢复、回溯继续、真实 LAPP 冒烟 | | Content | 条件可达性、无死路、隐藏事实、不同结果、角色一致性 | 真实模型测试不作为普通 CI 的前提。CI 使用确定性 Fake Provider;真实模型只运行独立冒烟和录制回归。 ## 10. 主要风险与控制 | 风险 | 控制 | |---|---| | 多个子智能体各自发明 Schema | Rust 类型为唯一事实来源;契约目录独占;变更必须更新 fixtures | | UI 或模型直接修改状态 | 所有修改必须经过类型化工具和唯一 reducer | | 模型输出不稳定 | 工具调用、结构校验、最大循环次数、一次安全重试、失败不提交 | | 假数据与真实接口分裂 | UI 和运行时共用 canonical `PlayerView` fixtures | | 分支恢复不确定 | 纯 reducer;固定 delta 顺序;每次重放做哈希校验 | | 重生成刷骰 | 判定绑定玩家行动;重新生成复用,推骰创建新行动 | | 隐藏信息泄漏 | 后端生成 `PlayerView`;前端永远不获得完整状态 | | `lapp-rs` 仍处于 0.1 阶段 | 固定提交;包一层窄 adapter;用三种协议夹具做回归 | | 本地模型不支持工具调用 | V1 启动前做能力检测并明确提示;不在 V1 增加第二套自由文本解析运行时 | | 子智能体大范围重构 | 目录所有权、短任务包、禁止无关改动、每日小合并 | | Windows 问题最后才出现 | M0 即建立 Windows 构建;每个阶段做一次安装/启动冒烟 | | 纵切内容被写成娜娜专用代码 | 引擎只识别通用标签和资源引用;增加非娜娜反例夹具 | ## 11. V1 Definition of Done 满足以下全部条件才称为 V1: - Windows 安装包可用; - 使用 LAPP 可以选择云端或本地 HTTP 模型并通过连接测试; - 内置娜娜试玩包可直接创建故事; - 20~30 分钟纵切可从头玩到至少两个结果方向; - 自由输入、可编辑建议和“继续”都能稳定完成一轮; - 六维关系、许诺、知识、持有物、时钟和隐藏判定都在纵切中真实生效; - 每轮原子自动保存; - 重启恢复一致; - 回溯后建立新分支并保留旧线; - 同一行动重新生成不重新掷骰; - NPC 隐藏状态、精确关系值、骰点和凭据不泄漏; - 模型取消、超时和畸形输出不产生半轮状态; - canonical fixtures、自动测试和 Windows 冒烟全部通过; - 没有引入 V1 明确不做的功能。 ## 12. 开工顺序 第一批实际开发任务按下面顺序创建: 1. 初始化 `nana-story` 仓库、Tauri/Vue 工程和 Rust workspace; 2. 写入架构 ADR 与目录所有权; 3. 定义 `ResourceHeader`、资源绑定、`StoryNode`、`RuntimeState`、状态操作、`PresentationBeat` 和 `PlayerView`; 4. 生成 JSON Schema、TypeScript DTO 和 canonical fixtures; 5. 同时启动三个独立 worktree: - Core:reducer + SQLite; - Runtime:Fake Provider + Turn API; - Experience:静态主演出屏 + fixture adapter; 6. 在 M1 确定性重放通过后,接入真实 `lapp-rs` 和第一条完整游戏回合。 第一条集成目标应当非常小: > 玩家输入“我答应天亮前回来”,娜娜回应并接受许诺;引擎创建 Promise、提高希望、保存节点;关闭应用后恢复;回溯到许诺之前继续,旧分支仍然存在。 它同时覆盖角色演出、工具调用、许诺、关系、存档、恢复和分支,是最适合作为 `nana-story` 第一条端到端主线的开发样例。