# 对话落盘单一化 · 执行蓝图 > 状态:核心方向已确认,按本文依赖顺序实施;非工具节点仍须逐项审阅提示词。 > 修订:2026-09-30。 > 相关:`docs/play-presentation-shells.md`、`docs/context-fragment-design.md`、`docs/world-simulator-modules.md`。 ## 执行状态(2026-09-28) - [x] 基线扫尾:删除新建作品选配方的过时提示。 - [x] 工作包 A:本局创作方案、两态注入、能力覆盖校验、旧配方案例化。 - [x] 工作包 B:`read_module`、`read_library_entry`、提示版本门禁、按需读取库。 - [ ] 工作包 C(进行中):内容节点、普通呈现链和 0 层呈现链均已迁移;`user-requirements` 经审阅决定删除;四个共用运行步骤已确认为自动工具节点,尚待补齐完整确定性编译。 - [ ] 工作包 D:游玩投影与维护合同编译,未开始。 - [ ] 工作包 E(进行中):楼层/0 层双呈现节点与数据合同已建立;壳参考库已按 7 种区、5 只壳写入 `shell-refs/`;隔离 iframe、持久槽位运行时和重 roll 尚未实现。 - [ ] 工作包 F:库条目 schema、本局化与提示归属,部分完成;偏好/文风库按需读取;仓库 `seeds/libraries/*.json` 可 git 备份并 `npm run libraries:restore` 恢复。 - [ ] 工作包 G:长程记忆、表维护与固定输入语法,未开始。 ## 0. 已确认结论 1. 新建作品只填写名称并直接进入对话落盘。这部分已经完成,不再列为工作包;旧工序编排书和后端 DAG 暂留兼容。 2. 创作入口与设计清单合并为一份中间产物「本局创作方案」。它根据自然语言生成本局方法(下文称“本局配方”),并给全部能力分档。 3. 旧配方不再供用户手动选择。修订后作为本局创作方案的参考案例,不作为固定路径。 4. 不写死“先美学、再清单、再逐节点”的统一工作流。本局创作方案描述依赖与重要程度,模型根据当前材料灵活推进,并可在用户改口后整份重写。 5. 创作对话保留一次用户输入触发多次 `insert` 的能力。逐节点审阅只约束本次程序改造:修改每份非工具节点提示词前必须给用户过目,不限制产品运行时一次只能写一个产物。 6. 壳最终仍是正文组成节点读取的参考库。否定的是当前七个外观壳及其按产品原型换皮的分类,不是否定参考库本身。 7. 正文前端采用隔离 iframe。它是显示容器,不提供 `action` 回传;用户仍从主输入框输入。 8. 创作源产物、创作交互与评分、游玩投影分开。开玩时不得再把全部 `设计.*` 原样挂给主世界层。 9. 长程记忆采用“写大总结”和“清空旧楼层”两个独立操作,不在大总结成功后自动删除历史。 10. 输入语法写死:裸写表示上帝视角,单井号包裹的 `#内容#` 表示角色行动,引号包裹的内容表示角色说话。 11. 新增“0 层卡”呈现模式:开场白所在气泡是持久前端;正文、指令、表盘、地图等都是其中的槽位,每轮就地更新,不继续向下叠加可见助手楼层。 12. 普通楼层与 0 层卡不共用呈现节点;现已复制 `回复呈现`、`正文组成`、`开场白与开场变量` 三个显示强相关节点,其它内容能力继续共用。 13. 0 层模式在“更多操作”中提供“重 roll 本轮”,复用本轮消息前 checkpoint;暂不提供任意历史回退。 ## 1. 当前基线 以下内容已经落地,不再实施: - 新建作品弹窗只保留作品名称。 - 新建作品固定 `creationMode: "dictate"`。 - 新会话默认进入对话落盘。 - 旧工序编排书仍可打开,DAG 后端和旧前端仅作兼容。 扫尾项(已完成): - [x] 删除 `web/agent-ui.js` 中“请先在新建作品时选定配方”的过时提示;旧书展示所需的「工序编排」标签可保留。 现有但尚未达到本文目标的基础设施: - 文风库、偏好库和库注册表已经存在,但对话落盘仍会整库注入。 - `play-bind` 已能自动装配部分运行上下文,但仍主要按 tag 分类并给主世界层全量投影。 - `auditor` 和 `maintain.v1` 已存在,但默认关闭,也没有本文所需的阻塞、总结和清空交互。 - 现有七个壳及预览器仍存在,只作为待替换旧实现。 ## 2. 目标链路 ```text 用户自然描述 → 本局创作方案〔中间产物,可反复重写〕 - 生成本局配方:如何把本次需求做出来 - 给全部能力分档并说明理由、缺口和建议实例 - 只表达必要依赖,不生成固定流水线 → 按当前材料灵活推进 - 写某能力前 read_module - 需要库条目时 read_library_entry - 同轮只连续写入这句话已经说清的能力,通常 2~3 个 - 每份写入携带独立 self_score → 回复呈现 → 正文组成读取壳参考库,生成本局模板与数据契约 → 开场白按数据契约生成 present.v1 → 开玩时把源产物编译成各 Worker 的游玩投影 ``` 这不是硬编码流程: - 用户这句话已经说清的能力,同轮可以读取并连续 `insert`,通常 2~3 个。没提到的「必须」和「有必要」按已有材料给 2~3 个选项询问,选定后再写。写开场前用未落项问用户哪些还没设计。 - 能力之间只有真实语义依赖。例如正文组成需要知道要呈现什么,开场白通常靠近创作收束;不能因此把所有作品强制塞进同一节点序列。 - 文本创作、世界模拟、短局、长局、数据化跑团等只是本局配方案例,不是程序分支。 - 用户改变目标时,先更新本局创作方案,再按新方案增删或改写产物。 ## 3. 工作包 A · 本局创作方案〔已完成〕 ### 3.1 能力与产物 新增 `modules/creation-plan/prompt.md`,目录登记: ```yaml - id: creation-plan name: 本局创作方案 layer: intermediate artifact: 设计.本局创作方案 ``` 该产物同时承担原“创作入口”和“本局设计清单”的职责: ```json { "schema": "creation-plan.v1", "技能": "本局创作方案", "brief": "一句话说明本局靠什么兑现体验", "recipe": { "primary": "主要创作方法", "secondary_traits": ["可混合的次要体验特征"], "method": ["针对本局的做法,不是固定节点顺序"], "references": ["采用或改造过的旧配方案例 id"] }, "items": [ { "module_id": "variable-design", "tier": "必须", "why": "任务时限与暴露度必须跨轮保存", "missing": "暴露度达到哪一档会被识破尚未确定", "instances": [] } ] } ``` `items` 覆盖对话落盘可见的全部内容能力,但排除: - 本局创作方案自身。 - 变量声明、变量映射、上下文排序等机械工具节点。 - 只供旧工序编排使用的节点。 四档含义: | 档位 | 含义 | 默认行为 | | ----- | -------------- | --------------- | | 必须 | 缺少后无法运行或核心体验会崩 | 收口前问清。用户没提到时给 2~3 个选项,选定后再写 | | 有必要 | 缺少仍可运行,但体验明显受损 | 同样先问再写;用户明确不要时可降档 | | 有一定效果 | 有了更好 | 用户提及时做;可顺带询问一次 | | 没有意义 | 本局用不上 | 不主动推进 | 四档是收口前的核对清单,不是本轮待办,也不是 DAG。程序不按照数组顺序强制执行节点。本轮通常只写用户这句话已经说清的 2~3 个能力。 ### 3.2 旧配方改为案例 - 删除新建阶段和创作页中的手动配方选择。 - 修订现有对话落盘配方,只保留各自有辨识度的方法、适用信号和能力档位倾向。 - 本局创作方案可以引用、混合或改写多个案例;引用结果写入 `recipe.references`。 - 案例不能复制总提示的公共规则,也不能恢复成隐藏的固定工作流。 ### 3.3 注入 `buildDictateSystemPrompt` 分两态: - 没有方案:提供能力目录的短声明、使用边界和配方案例目录,要求根据用户当前材料形成方案。 - 已有方案:注入完整本局配方;能力按档分组,显示已落/未落和最低自评分。前三档附带该能力的何时用 / 何时不用(方案改写或补全后仍保留;可反复能力已落一条也保留),供本轮选型。不附带执行规格。「没有意义」只列名称。 用户改口、新增方向,或现有方案明显不再成立时,允许覆盖 `设计.本局创作方案`。 ### 3.4 程序约束与验收 - 写入方案时校验能力覆盖;漏项则拒绝并列出 id。 - 插入开场白时,如仍有“必须”项未落,只提醒,不拒绝。 - 不要求体验锚定达到固定计数,也不要求先经过美学节点。 - 方案落盘后系统提示应明显短于全目录注入。 - 测试:`tests/creation-plan.test.ts`。 ## 4. 工作包 B · 写产物前读取能力与参考〔已完成〕 ### 4.1 `read_module` `src/dictate/tools.ts` 新增:## ```json { "name": "read_module", "parameters": { "module_id": "能力 id,也接受中文名", "reason": "可选,一句话" } } ``` 返回该能力的: 1. `task`、`principles`、`output`、`score`。 2. 绑定库的条目目录与短说明,不返回整库正文。 3. 正文组成所需的壳参考目录、正文安全子集和 iframe 数据契约说明。 4. 该能力适用的程序约束。 会话记录已读取的能力及其提示版本。写某能力产物前未读该版本时,`insert` 拒绝并提示调用 `read_module`。同轮可以先读用户这句已经说清的能力(通常 2~3 个),再连续执行这些 `insert`。还没提到的先问,不先读后写。 机械工具产物不受此限制。 ### 4.2 `read_library_entry` 仅返回库目录不足以完成选择,因此新增独立工具: ```json { "name": "read_library_entry", "parameters": { "library_id": "库 id", "entry_ids": ["本轮选中的 1~2 个条目 id"] } } ``` 约束: - 只能读取当前能力绑定库中的条目。 - 默认最多读取两个;确需更多时必须给出原因。 - 本局源产物记录条目引用和本局微调;未选条目不进入后续提示。 - 用户提出库外新取向时,可先形成本局临时条目;是否回写全局库另行确认。 ### 4.3 验收 - 未读取能力就写对应产物会被拒绝。 - 读取正文组成时能看到壳目录和 iframe 契约。 - 对话落盘不再默认注入文风库、偏好库全文。 - 测试:`tests/dictate-read-module.test.ts`、`tests/dictate-read-library-entry.test.ts`。 ## 5. 工作包 C · 产物分层、评分与自然回复〔进行中〕 ### 5.1 四类数据 1. **中间产物**:只服务创作决策,不进游玩。目前包括 `设计.本局创作方案`,以及 `lifecycle_intent = seed_only` 的生成规则。 2. **源产物**:能力形成的完整定稿,保留在黑板供编辑、验收和重新编译。 3. **游玩投影**:从源产物确定性提取、按 Worker 分发的运行时内容。 4. **创作交互与评分元数据**:回应、评价、启发、追问、缺口和分数;留在消息或元数据,不进入游玩投影。 `DictateProduct` 和黑板元数据新增 `layer`。目录的 `layer` 是默认值;生成规则根据产物中的 `lifecycle_intent` 覆盖默认值。 ### 5.2 自评分 每个能力 `prompt.md` 新增 `score` fence,每个维度给出 5 / 3 / 1 分锚点。`insert` 新增: ```json { "self_score": { "dims": [ { "name": "维度名", "score": 3, "gap": "当前缺口", "options": ["涉及用户偏好时提供的具体选项"] } ] } } ``` - 分数为 1~5。 - `score <= 3` 必须写 `gap`;涉及用户选择时给 2~3 个 `options`。 - 维度名必须与该能力 `score` fence 一致。 - 分数存入黑板元数据,不再写进产物正文。 - `追问`、`开放问题`、完备度诊断同样不进入游玩投影。 迁移按 §9 逐节点审阅进行。过渡期兼容规则: - 已有 `score` fence 的能力使用新参数并严格校验。 - 尚未迁移的能力继续接受旧 `output` 形状,不因缺少 `score` fence 阻塞写入。 - 全部内容节点迁移完成后,统一删除旧自评解析和兼容分支。 ### 5.3 用户可见回复 回复不使用固定标题、固定句数或逐模块仪式。按当轮内容自然覆盖: 1. 回应用户输入中的有效判断、矛盾或值得保留之处。 2. 说明本轮形成或改写了什么;完整 JSON 由产物卡展示。 3. 用户没提到的「必须」或「有必要」,给 2~3 个根据原话推出的选项来问。这句话已经说清并写入的,不再强行追问。 4. 简短说明强项和缺口。 写开场或结束创作前,用仍未落的「必须」和「有必要」问用户哪些还没设计。同轮写入多个已说清产物时综合回应整轮,不逐模块重复模板。 写正文组成或 0 层正文组成时,回复里直接给出一份隔离内容供预览:与本局前端同一套骨架,示例已经写进去。完整 JSON 仍由产物卡展示。 ## 6. 工作包 D · 游玩投影编译〔未开始〕 “游玩投影”指:创作时保存的完整产物不能直接塞进模型;开玩时先抽出每个 Worker 真正需要的字段,再分别装配。 ### 6.1 编译入口 在 `src/dictate/play-bind.ts` 增加明确的编译阶段: ```text 黑板源产物 → resolve module / layer → intermediate 与 seed_only 跳过 → compileDictateProjection → 按 mount 分发给 world-simulator / narrator / auditor → 各自按 context_order 装配 ``` 不要再用 tag 正则把几乎全部 `设计.*` 挂给主世界层。 ### 6.2 投影契约 能力目录声明默认 `mount`;每个最终能力提供确定性的投影器或字段允许列表: - 主世界层:事实推进、角色扮演、输入解释、权限边界。 - 转述:本局选中的文风、呈现规则、正文数据契约。 - 副 Worker:表结构、当前值、维护规则及人物一致性所需的少量设定。 所有投影统一剥离: - 创作期回应与评价。 - `self_score`、追问、开放问题、完备度和诊断依据。 - 本局创作方案状态。 - 不属于目标 Worker 的字段。 纯模型阅读内容优先生成有身份标题的 Markdown;变量、映射、数据契约和 `present.v1` 保持结构化。 这解决两个具体问题: 1. `layer` 只回答“整份产物是否进入游玩”,不足以回答最终产物中哪些字段能进入。 2. `mount` 只回答“给谁”,不足以回答“给这个 Worker 看哪些字段”。因此必须同时有分层、投影和分发。 ### 6.3 验收 - 本局创作方案和 `seed_only` 规则不出现在游玩请求中。 - 主世界层、转述、副 Worker 得到不同且最小的投影。 - 请求日志中不存在创作回应、评分、追问和诊断字段。 - 具体实例暂时全量稳定注入,不引入关键词检索或 RAG。 - 扩展 `tests/dictate-play-bind.test.ts`,覆盖分层、字段剥离和按 Worker 分发。 ### 6.4 维护方式分流与条件式编译工具节点 维护不固定走副 LLM。创作收口时先按复杂度选择一种运行策略: 1. `none`:没有跨轮可变内容,不挂维护能力。 2. `inline_tool`:只有 1~2 个简单独立变量,或少量可由主世界层明确决定的标量更新。给主世界层挂原生 `update_state` tool call;正文仍输出普通 Markdown。 3. `inline_structured`:模型不支持工具调用时,用 `{ body, state_ops }` 结构化整包,程序只显示 `body`。这会牺牲自然流式体验,因此不是首选。 4. `auditor_bundle`:变量较多、存在交叉依赖,或同时维护拓扑、补实例、其它备忘录时,运行 `maintenance-compile` 并启用统一维护副 Worker。 数量不是唯一判据:一个带扩展规则的关系图也应走 `auditor_bundle`;三个彼此独立、更新完全确定的计数器仍可走程序或 tool call。 `update_state` 只接受设计阶段声明过的变量、操作和 revision。主世界层先决定本轮事实并调用工具,程序校验、提交并返回结果,再输出用户可见正文。相比正文隐藏标签,它能在生成过程中报告拒绝或冲突;相比把长正文包进 JSON,它保留自然文本与流式输出。当前 GM harness 已有 `chance` tool loop,可沿用同一机制。 API 能力选路复用 profile 已有探测结果,但使用独立策略: - `tools=ok`:使用可选 tool call;不能用 `forcedTool` 强迫每轮更新,因为“本轮无变化”是正常结果。 - `strictTool=ok`:动态工具带 `strict: true`;若供应商对实际 schema 拒绝,则降为普通 tool,并继续由本地校验全部参数。 - tools 不可用而 `jsonSchema / forcedTool / jsonObject` 任一可用:通过现有 `completeStructured` 选路提交 `{ body, state_ops }`。 - 只有纯文本能力:可请求 JSON 并严格解析、失败后重试;解析失败则不写状态。新实现不使用正文隐藏标签或正则维护变量。 动态结构随请求生成,不需要向 API 预注册。`inline_tool` 只用于少量变量,因此优先为每个变量生成一个类型明确、结构简单的小工具:工具名使用稳定安全 id,description 携带该变量的更新条件,parameters 只含该类型允许的 `set / delta`、值、`expected_rev` 与简短依据。避免为所有变量动态拼一棵带大量 `oneOf` 的复杂 schema。 现有能力探测验证协议是否支持 tools、强制 tool、strict tool 与各类 JSON 输出;它不能证明供应商支持任意复杂 schema 方言。因此实现时需增加一个与状态更新工具同形的代表性探测,运行请求仍保留“strict 被拒绝 → 非 strict + 本地校验”的降级。 `maintenance-compile` 是条件式自动工具节点,不调用 LLM 创作内容。仅在策略为 `auditor_bundle` 时扫描所有最终产物,把分散的跨轮维护要求编译成一份 `设计.维护合同`: ```json { "schema": "maintenance-manifest.v1", "targets": [ { "id": "目标短 id", "kind": "table | topology | generation | memo", "state_ref": "变量.当前 | 运行.拓扑#id | …", "rule_refs": ["来源产物及字段路径"], "allowed_ops": ["set", "delta"], "timing": "pre_narrative | post_narrative", "writers": ["program", "gm", "auditor", "user"], "visibility": "public | hidden", "rules": ["给维护者执行的短规则"], "invariants": ["不可破坏的约束"] } ] } ``` 主要来源包括: - `设计.变量设计与更新规则` 的真值、写入源、更新时机、钳制和 side effects。 - `设计.拓扑图谱#*` 的动态模式、允许操作、写入者和一致性约束。 - `设计.生成规则#*` 中游玩期按需补实例的触发条件。 - 后续明确声明“游玩期可修改”的其它备忘录;纯静态设定不进入维护合同。 编译规则: - 不复制整份创作产物,只提取维护所需字段并保留来源引用。 - `none / inline_tool / inline_structured` 不生成聚合合同,也不启用 `auditor`。 - 一旦启用 `auditor`,应把本轮所有需要其判断的维护目标、当前状态和更新规则一次性投影给它,不能让它只看到变量而漏掉拓扑或补实例规则。 - `inline_tool` 只给主世界层简短工具 schema,不注入完整维护文档。 - `auditor_bundle` 下正文生成者不承担隐藏维护清单;需要从正文事实推断的更新统一由回合末 `auditor` 处理。必须在正文前完成的机械更新单列为 `pre_narrative`,由程序或专门的前置调用处理。 运行期由程序从维护合同生成精简的 `maintenance-context.v1`,只包含本轮相关目标、当前状态、用户输入和最终正文。旁观输出升级为 `maintain.v2`,至少可携带: - `table_ops` - `topology_patches` - `generation_requests` - `memo_ops` - `notes` 旧 `maintain.v1` 继续作为仅含 `table_ops / need_generate` 的兼容输入。 验收: - 单个“用户存款”变量只挂 `update_state`,不启动 `maintenance-compile` 或副 LLM。 - 工具调用成功后只显示正文;工具参数、结果和隐藏状态不混入气泡。 - 工具不可用时结构化整包可正确分离正文与状态;纯文本解析失败不写状态。 - 运行期变量维护不依赖正则;SillyTavern 正则只作为外部预设导入兼容能力,不能写权威状态。 - 同一局既有变量、关系图和按需实例时,旁观一次收到完整合同并可在一份维护包中分别更新。 - 主世界层提示不再塞入仅供维护使用的完整更新规则。 - 每项操作都有目标 id、来源规则和 revision;未知目标或越权操作被拒绝。 - 多数无变化回合仍可返回空维护包,不为“检查过”制造写入。 ## 7. 工作包 E · 壳参考库与隔离 iframe〔未开始〕 ### 7.1 壳参考库 壳是正文组成读取的参考。创作时选一只最接近的,按本局启用的区删掉多余区并重排,生成一次前端后固定。游玩只输出各区内容,由程序装配。 可见区有 6 种。隐藏的变量更新不参与分类,也不进壳。 | 区 | 里面是什么 | | --- | --- | | 抬头 | 页首一行短内容,例如标题、日期、天气、地点;条目一多或当前值一长,改用数据卡 | | 正文 | 这一轮的主叙事 | | 小故事 | 论坛上的一段,或正文地点以外其他重要角色的一小段;同一轮只写一种 | | 数据卡 | 一组变量的卡片或表;只露出点名的列和行 | | 结构 | 地图、关系或要对着看的长表 | | 文字选项 | 几条下一步,只显示文字,始终直接可见,不放进标签或折叠 | 参考壳按主面来,写在 `skills/dialogue/world-simulator/shell-refs/`: | 称呼 | id | 什么时候用 | | --- | --- | --- | | 阅读 | `reading` | 只有正文、页首一行和选项 | | 正文为主 | `dual-text` | 正文占满一栏。短文、地图用标签或折叠,点开才看。选项始终直接可见 | | 数据常驻 | `data-cards` | 数据一直铺开,不替换正文。左栏是数百字正文,右栏是另一路短内容。选项始终直接可见 | | 成稿 | `piece` | 一页一份能单独拿走的成品。内部小节在创作时按范例重排。选项在成稿下面 | 每只壳的备注只写两类:不替换的前端结构,以及可按本局用途替换的部位。备注不点名某一种应用场景。 `alongside`、`paged` 不再单独作为参考。另一篇长文不与正文左右对半。右栏是短内容时,可以和长正文并排。 美化用配色、字体层级、边框、间距,以及 CSS 或简单内联 SVG,按本次能写稳的程度完成。 ### 7.2 正文组成产物 `设计.正文组成` 保存: ```json { "参考壳": ["按正文体验命名的参考 id"], "为何": "它如何服务本局的阅读体验", "数据契约": { "blocks": { "body": { "类型": "markdown", "职责": "主读正文" }, "任意本局键": { "类型": "kv | list | markdown", "职责": "用途" } } }, "frontend": { "html": "
", "css": "", "js": "present.onData(render)" }, "示例灌数": { "schema": "present.v1", "blocks": {} } } ``` - 创作期生成一次模板并固定。游玩每轮只生成内容块,由程序装配进该模板,不再生成壳。 - 美化用配色、字体层级、边框、间距,以及 CSS 或简单内联 SVG,按本次能写稳的程度完成。 - 壳只包含本局实际启用的区。参考壳多出来的区在创作时删掉并重排,不留到游玩再空着。 - `blocks` 允许自定义键,前后端 normalization 不再吞掉未知键。 - `html + css + js` 合计上限 60 KB。 ### 7.3 最小隔离 新增 `web/present-frame.js`: - 使用 `