116 lines
7.6 KiB
Markdown
116 lines
7.6 KiB
Markdown
# 设计:自适应支线插入引擎
|
||
|
||
> 状态:已实现(2026-10-03),`pnpm verify` 包含主线与自适应支线验证。关联:docs/story-framework.md §6(路由规则)、
|
||
> docs/script/adaptive-branches.md(支线剧本与三张总表)、ADR-0003(Jev 置信度自适应)。
|
||
> 读者:实现此功能的工程 agent。
|
||
|
||
## 1. 目标
|
||
|
||
每章结束后用 Jev 对用户行为轨迹打 7 维 score;对置信度不足的维度,在下一章的
|
||
固定挂点动态插入一段支线剧情,补充该维度的行为采样。插入必须:
|
||
|
||
- **玩家无感**:支线作为剧情独立成立,出口与主线严丝合缝;
|
||
- **测量有效**:支线选择点与主线选择点同等规格地落入事件流;
|
||
- **成本可控**:每章至多插入 1 条;Jev 故障时不插入(降级,不阻断主线)。
|
||
|
||
## 2. 触发时序
|
||
|
||
```
|
||
chapterEnd(第 N 章结束)
|
||
→ 汇总本 session 全部事件为 state
|
||
→ 一次 Jev 调用:7 个维度 score(并行,输出免费)
|
||
→ 提取每维 confidence
|
||
→ 插入决策(见 §3)→ 产出第 N+1 章的"插入计划"
|
||
→ 第 N+1 章开始,引擎带着插入计划运行
|
||
```
|
||
|
||
打分与画像报告页共用同一套 state 构建逻辑(`ReportPage.buildState` 提取为
|
||
`src/engine/scoring.ts`,报告页和插入决策都调它)。章末打分失败(Jev 故障/无 key)
|
||
→ 本章不插入任何支线,主线照常(console.warn 记录)。
|
||
|
||
## 3. 插入决策(对照支线册表 2 实现为代码)
|
||
|
||
输入:7 维 confidence、本周目已触发支线记录、目标章可用挂点。
|
||
|
||
1. 过滤:confidence < 0.6 的维度为候选;`risk` / `mate_preference` 无支线定义,跳过;
|
||
2. 排序:按「维度权重 × 置信度缺口 × 已测稀缺度」取首——
|
||
维度权重:attachment > values > conflict_style > emotional_stability > culture
|
||
(见支线册 §0.5);缺口 = 0.6 − confidence;稀缺度 = 该维度已被采样的选择点数越少越优先;
|
||
3. 选皮:变体与挂点**按章绑定**(第 2 章=变体 A/挂点 1,第 3 章=变体 B/挂点 2),
|
||
不跨章搬运(与支线册 §0「变体皮肤规则」一致——变体场景与该章主线进度耦合)。
|
||
同维度重复低置信:第 2 章插 A、第 3 章插 B(强制换皮);A、B 都用尽仍低置信
|
||
→ 记录 `evidence_insufficient` 标记,不插入(交 Act 2);
|
||
4. 每章至多 1 条;每章只有一个挂点,本章已有插入则不再插入,顺延到下一章的
|
||
挂点(用其对应变体),无挂点可用则不插入;
|
||
5. 时长红线:插入后该章预估时长 ≤ 45 分钟(支线 5-8 分钟 + 主线 25-35 分钟)。
|
||
|
||
## 4. 数据模型
|
||
|
||
支线包:`content/branches/{branchId}.json`(如 `b-att-a.json`),结构:
|
||
|
||
```jsonc
|
||
{
|
||
"id": "b-att-a",
|
||
"dimension": "attachment",
|
||
"anchor": { "chapter": "chapter2", "afterNode": "c2-d4f" }, // 挂点 1:§2-4 后
|
||
"startNodeId": "cb-att-a-01",
|
||
"nodes": [ /* 与章节 JSON 同 schema 的 StoryNode 数组 */ ],
|
||
// 支线选择点 pointId 用 CB-XXX-Y-ZZ 规则(支线册表 1),维度标签照支线册
|
||
}
|
||
```
|
||
|
||
引擎改动:
|
||
|
||
- `StoryEngine` 增加 `insertions: BranchPack[]`(本局生效的插入计划);
|
||
- 节点索引 `nodeIndex` 在装载插入计划时合并支线节点;
|
||
- **挂点重定向**:到达 `anchor.afterNode` 的 `next` 时,若该挂点有插入,
|
||
跳支线 `startNodeId`;支线最后一个节点的 `next` 在 JSON 里写为
|
||
特殊值 `"$resume"`,引擎解析为"回到挂点原 next";
|
||
- 支线节点参与现有全部机制:条件渲染(flags)、进度持久化、追问流。
|
||
|
||
## 5. 事件与测量
|
||
|
||
- 支线选择点事件正常 append,`pointId`(如 `CB-VAL-A-01`)即变量取值来源;
|
||
- 支线 flags(`cb_*`)进现有 flags 系统(`src/engine/flags.ts` 补映射,
|
||
消费方见支线册表 3——注意"回写只许面向未来场景"规则);
|
||
- 插入决策本身落一个 `kind: 'branch_inserted'` 事件(branchId、维度、触发置信度),
|
||
供画像层做路径标记(温简 weak 线的公平性问题同款思路)。
|
||
|
||
## 6. 状态与持久化
|
||
|
||
- `ProgressSnapshot` 增加 `activeBranchId?: string` 与 `insertions` 摘要,
|
||
刷新恢复时若正处于支线中,恢复到支线内断点;
|
||
- `replay()` 清空插入计划(新周目重新打分重新决策);
|
||
- 同一 session 已触发的支线不再重复触发(A→B 换皮由 §3.3 保证)。
|
||
|
||
## 7. 实施拆分(建议一棒完成)
|
||
|
||
1. `src/engine/scoring.ts`:7 维打分 + 置信度提取(从 ReportPage 抽出复用);
|
||
2. `src/engine/branching.ts`:插入决策纯函数(§3 全部规则,可单测);
|
||
3. 引擎装载/挂点重定向/`$resume` 解析/进度快照扩展;
|
||
4. `content/branches/`:把支线册 10 条变体 JSON 化(文案照
|
||
`docs/script/adaptive-branches.md`,pointId/变量照表 3);
|
||
5. `scripts/verify.ts` 新增断言:决策矩阵表驱动测试(7 种触发情形照表 2)、
|
||
挂点重定向与 `$resume` 汇合、支线事件落库、重复触发抑制、
|
||
Jev 故障不插入、replay 清空计划。
|
||
|
||
## 8. 未决问题(实现时可自行裁决,报备即可)
|
||
|
||
- 挂点锚点的精确 nodeId 以 JSON 现状为准(`afterNode` 需在实现时对照
|
||
chapter2/3.json 落定,支线册的 §2-4 后/§3-2 前是剧本坐标);
|
||
- 支线中的 narration 讲述点:本期支线册均为按钮式选择点,无 V 类点,
|
||
若后续支线加讲述点,追问流直接复用现有机制,无新增设计。
|
||
|
||
## 9. 实现裁决与验证
|
||
|
||
- 挂点按剧情坐标落定:A 在 `chapter2/c2-branch-anchor`(C2-03 三条后续汇合后、观星台前),B 在 `chapter3/c3-d1b`(台基下、登台前)。原示例 `c2-d4f` 位于路线裁断前,不作挂点。
|
||
- A/B 绑定章节,不跨挂点搬运。第三章首次补测某维度也使用 B;当前章槽已占用则不叠加,下一章重新打分决定。
|
||
- 优先级采用权重 5/4/3/2/1 × `(0.6-confidence)` × `1/(1+独立测点数)`;choice/narration 为有效测点,追问与路由事件不重复计数。主线时长默认保守取 35 分钟,支线按上限 8 分钟估算。
|
||
- 文化兜底检查刚完成章的主线解谜是否已有事件,不检查目标章尚未发生的解谜,否则文化支线会永远无法触发。
|
||
- 支线册少量时序措辞做了衔接修正:官牙发生在安门内登台前;漏刻不预先引用尚未复原的星图;分药后的 D 路径同样遵循 `wenjian_health=weak` 留营规则。测量维度和变量枚举不变。
|
||
- `advance()` 返回 Promise,调用方须 await;章末防重复提交,HTTP 请求 15 秒超时。无 key 的自动 Mock 不参与插入决策,显式注入 Mock 可用于测试。
|
||
- 快照保存支线 ID 摘要与活动支线;恢复先合并节点索引,再定位断点。路由事件先于快照落盘,恢复时可从本 session 事件补回计划。重玩清除索引与计划,不改旧事件。
|
||
- 共 17 个支线选择点(两问解谜各计一个测点)。计时点记录耗时和超时,解谜记录两问答案、提示档数、是否解出,选择事件保存实际条件渲染后的文案。风声音效、缝宽演出和阅读/改选遥测未实现;当前 UI 点选即提交,不伪造这些信号。
|
||
- `pnpm exec tsx scripts/build-adaptive-branches.ts` 从定稿支线册重生成十个静态包;运行时不解析 Markdown。
|
||
- `scripts/verify-adaptive.ts` 覆盖决策矩阵、全部选项与超时/跳过、温简双状态、逐节点恢复、五维三章 A→B、事件/flags、重复点击与刷新、无 key/无效评分/网络故障、replay 会话隔离,由 `scripts/verify.ts` 统一执行。
|