diff --git a/docs/briefs/dictate-only-creation.md b/docs/briefs/dictate-only-creation.md new file mode 100644 index 0000000..d2ecf24 --- /dev/null +++ b/docs/briefs/dictate-only-creation.md @@ -0,0 +1,891 @@ +# 对话落盘单一化 · 执行蓝图 + +> 状态:核心方向已确认,按本文依赖顺序实施;非工具节点仍须逐项审阅提示词。 +> 修订: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`: + +- 使用 `