design.md 10 KB

技术设计:M4 AI 角色层与一级宿主壳

前置:已读 实施计划 §M4/§1.5、多智能体 spec v3.4、story-repo-spec 0.8 §8/§2.0/§10/§11、v6 reviewer.md + 知识抽样。 落点全部在 v7/(包自包含);AI 协作层只吃 DTO、只吐结构化,落盘走 M2 Writer 小端口。

1. 落点布局(v7/ 下,全绿地)

v7/
├── roles/                      # 角色单源(markdown + frontmatter),真源
│   ├── 事实审查.md
│   └── 编辑审.md
├── skills/
│   └── webnovel-writer/SKILL.md   # 状态机单入口,平台条件块,真源
├── references/                 # 知识层平移(逐文件审查后入)
│   ├── 题材模板/ 追读力/ 爽点节奏/
│   └── 迁移报告.md
├── adapters/
│   ├── registry.json           # 三级分级
│   ├── claude-code/support.md
│   └── codex/support.md
├── templates/AGENTS.md         # 工作目录公约数层模板(标记块)
├── scripts/build-host-shells.mjs   # 生成器 CLI(薄壳)
└── src/
    ├── review/                 # 两审编排 Use Case(确定性,零真 AI)
    │   ├── index.js            # assembleReviewInput / runReviews / mergeReviews / persistReviewReport
    │   └── schema.js           # ReviewIssue 校验 + 阻断规则
    ├── dto/                    # 跨层稳定 DTO 组装器
    │   ├── chapter-brief.js    # ChapterBrief
    │   └── character-context.js# CharacterContext
    ├── session/index.js        # readBooksRegistry / assembleSessionContext / scanRebuildBooks
    ├── host-shells/generate.js # 生成器逻辑(可单测)+ drift check
    └── state-machine/persist.js# AI 态产物回流落盘(序0/1/4/6)

约定(延续 M1-M3):scripts/build-host-shells.mjsbin/ 同为薄入口,逻辑在 src/host-shells/;命令走 run(args,options,ctx) 纯返回契约;零第三方依赖。

2. 两审编排(R1,确定性核心)

2.1 边界:JS 做编排,AI 做审稿

v7 铁律:状态机/脚本不做业务判断,AI 吃 DTO 吐结构化。两审的"读整章找问题"是 AI 的活;JS 负责组装输入 + 校验输出 + 合并落盘 + 诚实声明。用依赖注入把 AI 隔在确定性层之外:

// reviewers 由宿主壳接线(完整=两个 subagent;降级=主 agent 顺序);测试注入假函数→零真 AI
runReviews(ctx, { chapterNum, draftPath, mode, reviewers })
//   1. input = assembleReviewInput(...)            // DTO,无文件路径
//   2. fact = reviewers.factCheck(input)           // AI 出 ReviewIssue[]
//      edit = reviewers.editorial(input)           // mode='degraded' 时顺序
//   3. validateReviewReport(fact|edit)             // schema + 阻断规则
//   4. merged = mergeReviews({fact,edit},{mode})   // 合并 + counts + 诚实声明
//   5. persistReviewReport(...)                    // 落盘 工作区/审稿.md + 评审报告/

2.2 ReviewInput DTO(AI 不见路径)

{ 章号, 草稿全文, 本章要写到的事[], 全书近况片段, 相关角色:CharacterContext[], 相关条目(伏笔/悬念/感情线), 时间线片段, 信息差关键词候选[], 名册待确认新专名[] }。全部经 M1 读接口 + M2 备料组装,值进 DTO,路径不进。

2.3 ReviewIssue schema 与阻断规则(src/review/schema.js)

  • 字段:severity / category / location / description / evidence / fix_hint / blocking
  • 分域:事实审查 category ∈ {setting,timeline,continuity,character,logic,requirement,leak,evidence,unregistered_thread};编辑审 ∈ {structure,pacing,commercial,motivation}。越界拒绝。
  • 阻断规则:criticalblocking=true;unregistered_thread(D3)→强制 blocking=false(即兴伏笔非缺陷,只出候选交作者);其余 severity 由 AI 判 blocking。
  • 复核 issues_count/blocking_count/has_blockingissues 一致(校验器覆盖写回,继承 v6 review-pipeline 复核纪律)。

2.4 落盘

工作区/审稿.md = 草稿 + 两审意见 + 待确认新专名 + 章摘要(spec §8 第7步);工作区/评审报告/{事实审查,编辑审}.json 原始输出。模式声明写在审稿单头部(降级:"本次兼容模式,审稿隔离度低于完整两审")。

3. AI 态 DTO 与落盘(R2)

  • src/dto/:CharacterContext(角色快照,境界/状态/位置/持有,无路径)——ReviewInput 与备料复用。ChapterBrief 不另造:M2 工作区/本章写作材料.md 即 chapter brief(无新消费者,避免冗余 DTO)。
  • src/state-machine/persist.js:AI 产物回流落盘,与 dto.js(读侧组装)对称:
    • 序0 修复确认 → 写回修复后的源文件(经 parser 校验)
    • 序1 建书 → book.yaml + 大纲/总纲.md + 第一卷卷纲(经 Writer)
    • 序4 卷复盘 → 卷摘要 + 下卷卷纲 + 勾选的伏笔条目
    • 序6 细纲 → 工作区/细纲.md
  • 落盘只经 M2 Writer 小端口;AI 提交结构化 DTO,M4 落盘,AI 全程不碰文件路径。

4. SessionStart 与书单自愈(R3)

  • readBooksRegistry(workdir):读 .webnovel/books.jsonl(每行一本:书名/目录/是否当前/最后打开);JSON 行损坏跳过并标记。
  • scanRebuildBooks(workdir):缺失/全损 → 扫描含 book.yaml 子目录重建(spec §0 可重建);"当前在写"标记缺失时返回 needsAuthorPick。
  • assembleSessionContext(workdir):产"当前在写哪本/共几本/全书近况入口"注入文本。
  • 无 hook 等价:状态机入口启动调同一函数 → 与 Claude Code SessionStart hook 注入文本逐字一致(测试断言等价)。
  • 写侧不做:books.jsonl 写入/换书对话属 M5;M4 只读 + 重建。

5. 宿主壳生成器 + drift check(R4)

5.1 生成器(src/host-shells/generate.js)

  • roles/skills/adapters/registry.jsondist/<host>/(agent 壳按平台:Claude Code md+frontmatter / Codex TOML / Gemini md;编译后 SKILL.md)。
  • 平台条件块(§5.9):零依赖手写最小渲染器——{{#if agentCapable}}…{{/if}}{{#if hasHooks}}…{{/if}}、变量插值(命令引用语法/路径)。布尔集只留 agentCapable/hasHooks(不维护大能力矩阵)。
  • 降级编译进生成物:无 subagent 平台,生成的 SKILL.md 正文就是顺序执行版 + 兼容声明(不靠运行时判断)。
  • 生成物 dist/ 不提交(§6.2)。

5.2 drift check = 确定性(spec v3.4 §6.2)

build-host-shells --check:同输入连跑两次,断言逐字节一致(determinism)+ 生成物过 validator。CI 必跑。(dist 不提交,故非"对比已提交生成物",而是确定性验证。)

5.3 package validator

校验:registry schema、逐宿主 support.md 存在、description ≤ Codex 8k 预算、生成物无本机绝对路径、roles frontmatter 完整。

5.4 角色单源:重构而非拷贝 v6

roles/事实审查.mdroles/编辑审.md 任务书:

  • ReviewInput DTO,声明输出 §8 schema;不含 python/脚本调用/文件路径读取(对照 AC6 grep)。
  • 事实审查维度 = v6 五维(setting/timeline/continuity/character/logic)+ v7 新增(requirement 要写到的事核对 / leak 泄密候选判断 / evidence 履历证据验证 / unregistered_thread D3)。
  • 编辑审 = structure/pacing/commercial/motivation;明确排除"自由评文笔好坏"。

6. 知识层平移(R5,逐文件审查 + 单一真源)

  • 范围严守 spec §11 平移表:题材模板 / 追读力分类 / 爽点与节奏知识库,其余 v6 references 不迁。
  • 先选真源,后迁移(用户指令:题材以 CSV 最新版为准,不维护双表)。v6 同一知识体有新旧两层(新 CSV references/csv/+taxonomy/ vs 老 markdown skills/*/references/+references/genre-profiles.md),重叠即"两张表"——v7 每体只留一份:

| 知识体 | v7 唯一真源(取) | v6 弃/折叠 | |---|---|---| | 题材模板 | taxonomy/genre-index.csv + csv/题材与调性推理.csv | genre-profiles.mdskills/.../genre-tropes.mdanti-trope-*.md(独有内容折叠进 CSV,否则丢) | | 爽点与节奏 | csv/爽点与节奏.csv | genre-hook-payoff-library.mdpacing-control.md 重复部分 | | 追读力分类 | references/reading-power-taxonomy.md | (唯一源,无双表) |

  • 流程:P4.1 产真源选定表 → 逐份真源清 v6 问题 → 入 v7/references/<分类>/迁移报告.md 记"选谁/弃谁/为什么 + 逐文件改了什么"。
  • v6 问题清单(grep 把关,AC7): | 类别 | 命中样例 | 处置 | |---|---|---| | 双表 | 题材同时存在 CSV 与 markdown 表 | 只留 CSV,markdown 独有内容折叠进 CSV | | 旧路径 | 设定集/正文/(非"定稿/正文")、.webnovel/state.json | 改 v7 路径或删 | | 退场术语 | "卡"(卡点义)、"棘轮" | 换 spec 术语 | | v6 机制 | state.json、doctor、dashboard、v6 skill 名(webnovel-write/plan…)、python webnovel.py;CSV 的 适用技能/大模型指令 列、跨 CSV 推荐检索表 列 | 删/重写为 v7 中性 | | 反模式 | "评文笔好坏""打分" | 删(两审职责排除) |
  • craft 内容(钩子/兑现/节奏/题材原型/调性/毒点)架构无关,保留。CSV 保持 CSV 形态(机器友好、即单源),不另起 markdown 表复刻。
  • genre-index.csvtemplate_file 列引用 templates/genres/*.md:迁移时一并核对引用完整性(引用的模板要么迁入要么清列),不留悬空引用。

7. 取舍

  • DI 注入 reviewers:把 AI 隔在确定性层外,P0-P5 主体全可 TDD/CI 绿;真模型只在推迟的 smoke 接线。代价:多一层注入接口——值,换来零真 AI 的可测性。
  • drift = 确定性(非比对已提交物):符合 v3.4"dist 不提交";代价:不防"手改已分发壳",但分发由 M5 安装器哈希追踪兜底。
  • 角色重构而非拷贝:这是"不带 v6 问题进 v7"的核心落点;代价:多写,不能 copy——必须,v6 reviewer 直调 runtime 违反铁律。
  • 资产放 v7/ 下(非仓库根):包自包含,安装器(M5)从 v7/ 取源分发。

8. 回滚点

  • 各 P 独立、全为新增目录/文件(roles/skills/references/adapters/scripts/src/{review,dto,session,host-shells});对 M1-M3 唯一改动是 state-machine/persist.js 新增(不改 dto.js 读侧)。
  • 未提交前 git restore v7/<子目录> 即回滚;知识迁移是纯新增,删 references/ 即净。