Просмотр исходного кода

feat: route writing through context briefs

lingfengQAQ 2 месяцев назад
Родитель
Сommit
0e04729dd9

+ 1 - 1
docs/guides/commands.md

@@ -24,7 +24,7 @@
 
 ### `/webnovel-write [章号]`
 
-执行完整章节创作流程(上下文构建 → 草稿 → 审查 → 润色 → 数据落盘)。
+执行完整章节创作流程(`context-agent` 先 research 并生成写作任务书 → 按任务书起草正文 → 审查 → 润色 → 数据落盘)。
 
 ```bash
 /webnovel-write 1

+ 325 - 0
docs/superpowers/plans/2026-04-14-context-agent-writing-brief-implementation.md

@@ -0,0 +1,325 @@
+# Context Agent 写作任务书收束 Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 让 `context-agent` 成为唯一写前入口,代码层只准备 research 底稿,最终由 `context-agent` 按示例直接写出给 Step 2 使用的写作任务书。
+
+**Architecture:** 不再引入模板生成器。`extract_chapter_context.py` 继续负责组装 research 底稿;`context-agent` 基于底稿、固定守则和文档内示例,直接产出最终写作任务书;`webnovel-write` 的 Step 2 只消费这份任务书,不再自己补规则或拼中间块。
+
+**Tech Stack:** Python 3、pytest、Markdown agent/skill prompt、`webnovel.py` CLI
+
+---
+
+## 文件结构
+
+### 修改文件
+
+- `webnovel-writer/scripts/extract_chapter_context.py`
+  - 保持为底稿组装器,不负责最终文案生成
+- `webnovel-writer/scripts/data_modules/tests/test_extract_chapter_context.py`
+  - 验证底稿字段与旧文本输出能力
+- `webnovel-writer/agents/context-agent.md`
+  - 收回 Step 0.5 职责,明确按示例直接输出写作任务书
+- `webnovel-writer/skills/webnovel-write/SKILL.md`
+  - 删除 Step 0.5,Step 2 改为只消费写作任务书
+- `webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py`
+  - 锁定新的提示词边界
+- `webnovel-writer/skills/webnovel-write/evals/evals.json`
+  - 更新流程预期
+- `docs/guides/commands.md`
+  - 更新 `/webnovel-write` 描述
+
+### 删除文件
+
+- `webnovel-writer/scripts/data_modules/writing_brief_renderer.py`
+- `webnovel-writer/scripts/data_modules/tests/test_writing_brief_renderer.py`
+
+---
+
+### Task 1: 删除生成器路线并恢复底稿职责
+
+**Files:**
+- Delete: `webnovel-writer/scripts/data_modules/writing_brief_renderer.py`
+- Delete: `webnovel-writer/scripts/data_modules/tests/test_writing_brief_renderer.py`
+- Modify: `webnovel-writer/scripts/extract_chapter_context.py`
+- Modify: `webnovel-writer/scripts/data_modules/tests/test_extract_chapter_context.py`
+
+- [ ] **Step 1: 删除生成器文件**
+
+```bash
+git rm webnovel-writer/scripts/data_modules/writing_brief_renderer.py
+git rm webnovel-writer/scripts/data_modules/tests/test_writing_brief_renderer.py
+```
+
+- [ ] **Step 2: 移除 `extract_chapter_context.py` 中的生成器调用**
+
+```python
+# webnovel-writer/scripts/extract_chapter_context.py
+# 删除:
+from data_modules.writing_brief_renderer import render_writer_brief
+
+# 删除:
+def _plugin_root() -> Path: ...
+def _load_fixed_guides() -> Dict[str, str]: ...
+def _extract_book_title(project_root: Path, state: Dict[str, Any]) -> str: ...
+def _extract_chapter_title(outline: str, chapter_num: int) -> str: ...
+
+# build_chapter_context_payload 保留为底稿输出:
+return {
+    "chapter": chapter_num,
+    "outline": outline,
+    "previous_summaries": prev_summaries,
+    "state_summary": state_summary,
+    "context_contract_version": contract_context.get("context_contract_version"),
+    "context_weight_stage": contract_context.get("context_weight_stage"),
+    "story_contract": contract_context.get("story_contract", {}),
+    "runtime_status": contract_context.get("runtime_status", {}),
+    "latest_commit": contract_context.get("latest_commit", {}),
+    "prewrite_validation": contract_context.get("prewrite_validation", {}),
+    "reader_signal": contract_context.get("reader_signal", {}),
+    "genre_profile": contract_context.get("genre_profile", {}),
+    "writing_guidance": contract_context.get("writing_guidance", {}),
+    "plot_structure": plot_structure,
+    "long_term_memory": contract_context.get("long_term_memory", {}),
+    "scene": contract_context.get("scene", {}),
+    "core": contract_context.get("core", {}),
+    "rag_assist": rag_assist,
+}
+```
+
+- [ ] **Step 3: 删除 `writer_brief` 相关测试,恢复旧文本输出断言**
+
+```python
+# webnovel-writer/scripts/data_modules/tests/test_extract_chapter_context.py
+# 删除:
+def test_build_chapter_context_payload_includes_writer_brief(...): ...
+def test_render_text_returns_writer_brief_instead_of_old_audit_sections(...): ...
+
+# 保留:
+def test_render_text_contains_writing_guidance_section(...): ...
+def test_render_text_contains_contract_first_runtime_section(...): ...
+```
+
+- [ ] **Step 4: 跑测试确认底稿链恢复正常**
+
+Run:
+
+```bash
+python -m pytest webnovel-writer/scripts/data_modules/tests/test_extract_chapter_context.py -q --no-cov
+```
+
+Expected:
+
+```text
+全部通过,0 失败
+```
+
+- [ ] **Step 5: 提交这一块**
+
+```bash
+git add webnovel-writer/scripts/extract_chapter_context.py \
+        webnovel-writer/scripts/data_modules/tests/test_extract_chapter_context.py
+git commit -m "refactor: keep chapter context as research draft only"
+```
+
+---
+
+### Task 2: 让 `context-agent` 直接按示例输出写作任务书
+
+**Files:**
+- Modify: `webnovel-writer/agents/context-agent.md`
+- Modify: `webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py`
+
+- [ ] **Step 1: 先补静态测试,锁住示例驱动输出**
+
+```python
+# webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py
+def test_context_agent_loads_fixed_guides_and_outputs_writer_brief():
+    text = (AGENTS_DIR / "context-agent.md").read_text(encoding="utf-8")
+    assert "core-constraints.md" in text
+    assert "anti-ai-guide.md" in text
+    assert "写作任务书" in text
+    assert "### 示例" in text
+    assert "你现在要写《凡人修仙传》第47章《坊市试探》。" in text
+    assert "Step 2 直写提示词" not in text
+    assert "Context Contract" not in text
+```
+
+- [ ] **Step 2: 改 `context-agent.md`**
+
+```md
+### webnovel-writer/agents/context-agent.md
+
+## 8. 输出格式
+
+最终只输出一份写作任务书。
+
+任务书固定写成五段,每一段该织入哪些数据源见下方说明和示例。
+
+### 1. 开篇委托
+### 2. 这一章的故事
+### 3. 这章的人物
+### 4. 这章怎么写更顺
+### 5. 这章收在哪里
+
+### 示例
+
+你现在要写《凡人修仙传》第47章《坊市试探》。
+...
+让读者带着"这个人到底是谁"翻到下一章。
+```
+
+- [ ] **Step 3: 跑静态测试**
+
+Run:
+
+```bash
+python -m pytest webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py -q --no-cov
+```
+
+Expected:
+
+```text
+相关断言全部通过
+```
+
+- [ ] **Step 4: 提交这一块**
+
+```bash
+git add webnovel-writer/agents/context-agent.md \
+        webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py
+git commit -m "feat: make context agent write briefs from example"
+```
+
+---
+
+### Task 3: 收束 `/webnovel-write` 主链
+
+**Files:**
+- Modify: `webnovel-writer/skills/webnovel-write/SKILL.md`
+- Modify: `webnovel-writer/skills/webnovel-write/evals/evals.json`
+- Modify: `docs/guides/commands.md`
+- Modify: `webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py`
+
+- [ ] **Step 1: 先补静态测试,锁住新流程**
+
+```python
+# webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py
+def test_webnovel_write_skill_routes_step2_through_writing_brief():
+    text = (SKILLS_DIR / "webnovel-write" / "SKILL.md").read_text(encoding="utf-8")
+    assert "写作任务书" in text
+    assert "context-agent" in text
+    assert "Step 0.5" not in text
+    assert 'cat "${SKILL_ROOT}/../../references/shared/core-constraints.md"' not in text
+    assert 'cat "${SKILL_ROOT}/references/anti-ai-guide.md"' not in text
+```
+
+- [ ] **Step 2: 改 `webnovel-write/SKILL.md`**
+
+```md
+### Step 1:调用 Context Agent 生成写作任务书
+
+### Step 2:起草正文
+
+硬要求:
+- Step 2 只根据 Step 1 生成的写作任务书起草正文
+- Step 2 不再直接加载 `core-constraints.md`
+- Step 2 不再直接加载 `anti-ai-guide.md`
+- Step 2 不再自己拼中间块或旧版直写块
+```
+
+- [ ] **Step 3: 改 eval 和命令文档**
+
+```json
+// webnovel-writer/skills/webnovel-write/evals/evals.json
+"expected_output": "完成 Step 1 到 Step 6 的完整流程,由 context-agent 先生成写作任务书,再起草第4章正文..."
+```
+
+```md
+### docs/guides/commands.md
+
+执行完整章节创作流程(`context-agent` 先 research 并生成写作任务书 → 按任务书起草正文 → 审查 → 润色 → 数据落盘)。
+```
+
+- [ ] **Step 4: 跑静态测试**
+
+Run:
+
+```bash
+python -m pytest webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py -q --no-cov
+```
+
+Expected:
+
+```text
+相关断言全部通过
+```
+
+- [ ] **Step 5: 提交这一块**
+
+```bash
+git add webnovel-writer/skills/webnovel-write/SKILL.md \
+        webnovel-writer/skills/webnovel-write/evals/evals.json \
+        docs/guides/commands.md \
+        webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py
+git commit -m "feat: make write step consume only final brief"
+```
+
+---
+
+### Task 4: 回归验证
+
+**Files:**
+- Test: `webnovel-writer/scripts/data_modules/tests/test_extract_chapter_context.py`
+- Test: `webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py`
+
+- [ ] **Step 1: 跑回归测试**
+
+Run:
+
+```bash
+python -m pytest \
+  webnovel-writer/scripts/data_modules/tests/test_extract_chapter_context.py \
+  webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py \
+  -q --no-cov
+```
+
+Expected:
+
+```text
+全部通过,0 失败
+```
+
+- [ ] **Step 2: grep 自检**
+
+Run:
+
+```bash
+rg -n "Step 0\\.5|writing_brief_renderer|test_writing_brief_renderer|Context Contract|Step 2 直写提示词|cat \\\"\\$\\{SKILL_ROOT\\}/../../references/shared/core-constraints.md\\\"|cat \\\"\\$\\{SKILL_ROOT\\}/references/anti-ai-guide.md\\\"" \
+  webnovel-writer \
+  docs/guides/commands.md
+```
+
+Expected:
+
+```text
+除计划文档历史记录外,无业务代码残留
+```
+
+---
+
+## 覆盖检查
+
+这份计划覆盖当前真实方案:
+
+1. 代码层只准备底稿
+2. `context-agent` 直接按示例写任务书
+3. `/webnovel-write` Step 2 只消费任务书
+4. 不再使用模板生成器
+5. `anti-ai-guide.md` 与 `core-constraints.md` 继续存在,但由 `context-agent` 吸收后转写
+
+## 自检
+
+- 已移除模板生成器路线
+- 没再要求字段映射表
+- 与当前未提交实现方向一致

+ 115 - 50
webnovel-writer/agents/context-agent.md

@@ -1,6 +1,6 @@
 ---
 name: context-agent
-description: 上下文搜集 Agent(research 模式),按需查询记忆系统,输出可被 Step 2 直接消费的创作执行包
+description: 上下文搜集 Agent(research 模式),按需查询记忆系统,整理内部底稿,并输出可被 Step 2 直接消费的写作任务书
 tools: Read, Grep, Bash
 model: inherit
 ---
@@ -9,7 +9,7 @@ model: inherit
 
 ## 1. 身份与目标
 
-你是章节写作的上下文搜集员。你的职责是生成可直接开写的创作执行包,目标是"信息够用、约束清楚、无需补问"
+你是章节写前组装员。你的职责不是把材料原样堆给下游,而是先完成 research,再把现有任务包整理成一份可直接开写的写作任务书
 
 工作模式:**research 模式**——先获取轻量基础包,再按需深查补充,而非一次性灌入全部数据。
 
@@ -18,6 +18,7 @@ model: inherit
 - 先接住上章、再锁定本章任务与章末钩子
 - 若章纲提供结构化节点,将其转化为本章写作节拍
 - 信息冲突时优先级为:Story Contracts > accepted `CHAPTER_COMMIT` > 长期记忆 > 风格偏好
+- 最终只输出一份写作任务书,不暴露合同条目和系统来源
 
 ## 2. 可用工具与脚本
 
@@ -74,6 +75,8 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" extr
 - `${CLAUDE_PLUGIN_ROOT}/references/reading-power-taxonomy.md`(追读力分类)
 - `${CLAUDE_PLUGIN_ROOT}/references/genre-profiles.md`(题材画像)
 - `${CLAUDE_PLUGIN_ROOT}/references/shared/`(共享事实源,遇到 `<!-- DEPRECATED:` 的文件跳过)
+- `${CLAUDE_PLUGIN_ROOT}/references/shared/core-constraints.md`(固定守则,内部吸收,不原样输出)
+- `${CLAUDE_PLUGIN_ROOT}/skills/webnovel-write/references/anti-ai-guide.md`(固定守则,内部吸收,不原样输出)
 
 ## 3. 思维链(ReAct 循环)
 
@@ -96,7 +99,8 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" extr
   → 追读力、模式、实体出场
 
 阶段 D:组装 + 校验
-  → 组装三层执行包
+  → 先拼内部底稿
+  → 再翻成写作任务书
   → 红线校验
 ```
 
@@ -202,6 +206,7 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" inde
 - **不得整库搬运记忆**——只注入与本章直接相关的事实
 - **不得让追读力偏好覆盖大纲主任务**
 - 输出必须能直接交给 Step 2 开写,不得依赖额外补问
+- 不得把合同、检查项、规则来源原样抛给 Step 2
 
 ## 7. 检查清单
 
@@ -215,60 +220,120 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" inde
 - [ ] 时间逻辑正确
 - [ ] 长期记忆事实未被遗漏或写反
 - [ ] 有节点时,情节结构与任务书/合同方向不冲突
-- [ ] 执行包包含:不可变事实清单、章节节拍、终检清单、时间约束
+- [ ] 写作任务书能独立支撑 Step 2 起草正文
 - [ ] Step 2 无需补问即可直接起草正文
-- [ ] 任务书包含 8+1 个板块且时间约束完整
+- [ ] 任务书五段完整,语气自然,不像制度说明
 - [ ] 角色动机与情绪非空
 - [ ] 最近模式已对比,有差异化建议
 - [ ] 伏笔清单已按紧急度输出
 
 ## 8. 输出格式
 
-输出必须是单一创作执行包,包含以下 3 层内容,三层信息必须一致。
-
-### 第 1 层:任务书(8+1 个板块)
-
-- **本章核心任务**:目标、阻力、代价、核心冲突一句话、必须完成、绝对不能、反派层级
-- **接住上章**:上章钩子、读者期待、开头建议
-- **出场角色**:状态、动机、情绪底色、说话风格、行为红线
-- **场景与力量约束**:地点、可用能力、禁用能力
-- **时间约束**:上章时间锚点、本章时间锚点、允许推进跨度、时间过渡要求、倒计时状态
-- **风格指导**:本章类型、参考样本、最近模式、本章建议
-- **连续性与伏笔**:时间/位置/情绪连贯;必须处理与可选伏笔
-- **追读力策略**:未闭合问题、钩子类型/强度、微兑现建议、差异化提示
-- **情节结构**(有节点时):CBN、CPNs 序列、CEN、必须覆盖节点、本章禁区
-
-### 第 2 层:Context Contract
-
-- 目标、阻力、代价、本章变化、未闭合问题、核心冲突一句话
-- 开头类型、情绪节奏、信息密度
-- 是否过渡章
-- 追读力设计:钩子类型/强度、微兑现清单、爽点模式
-- `plot_structure`(有节点时):`{cbn, cpns[], cen, mandatory_nodes[], prohibitions[]}`
-
-过渡章判定规则(强制):
-- 依据章纲/卷纲中的章节功能标签与目标
-- 若大纲未显式标注,由"本章核心目标是否以推进主冲突为主"判定
-- 禁止使用字数阈值判定过渡章
-
-差异化检查:
-- 钩子类型优先避免与最近 3 章重复
-- 开头类型优先避免与最近 3 章重复
-- 爽点模式优先避免与最近 5 章重复
-- 若必须重复,记录 Override 理由,并至少变更对象/代价/结果之一
-
-### 第 3 层:Step 2 直写提示词
-
-- 章节节拍:
-  - 有节点时:`CBN触发 -> CPN推进 -> CPN受阻/变化 -> ... -> CEN收束 -> 章末钩子`
-  - 无节点时:`开场触发 -> 推进/受阻 -> 反转/兑现 -> 章末钩子`
-- 不可变事实清单:大纲事实、设定事实、承接事实、长期记忆事实
-- 禁止事项:越级能力、无因果跳转、设定冲突、剧情硬拐、违反本章禁区中的任何条目(有节点时)
-- 终检清单:本章必须满足项与 fail 条件
-
-硬要求:
-- 若 `必须处理` 伏笔超过 3 条:前 3 条标记"最高优先",其余标记"本章仍需处理"
-- 有节点时,必须把 `plot_structure` 纳入合同与节拍映射
+最终只输出一份写作任务书。
+
+任务书固定写成五段,每一段该织入哪些数据源见下方说明和示例。
+
+### 1. 开篇委托
+
+书名、章号、章标题、这一章一句话干什么。
+
+### 2. 这一章的故事
+
+把以下信息综合成一段连贯的交代:
+- 上章写到哪了(前文摘要)
+- 本章谁要做什么、为什么非做不可、真正难的地方在哪
+- 中间怎么走(情节节点 CBN/CPNs/CEN)
+- 哪些是绕不开的、哪些不能碰(必须覆盖节点、本章禁区)
+- 跨章硬约束——长期记忆里跟本章直接相关的事实和活跃约束
+- RAG 检索命中的关键线索(有就织进去,没有就跳过)
+
+### 3. 这章的人物
+
+把以下信息综合成每个角色一小段:
+- 当前状态(境界、位置、伤势、情绪——来自状态摘要和长期记忆)
+- 眼前驱动力
+- 这章里的主要作用
+- 说话和行动倾向
+
+### 4. 这章怎么写更顺
+
+这一段最关键。把以下信息翻译成自然的写作提醒:
+- 题材基调和参考气质(题材锚定)
+- 本章具体写法建议(writing_guidance)
+- 最近几章的审查得分趋势和低分区间提醒(追读信号)
+- 章节阶段和风险标记(方法论策略)
+- 从 `core-constraints.md` 和 `anti-ai-guide.md` 翻过来的自然提醒
+- 节奏、情绪、对话的通用写法提醒
+
+### 5. 这章收在哪里
+
+结尾该停在什么感觉上,留下什么未完感。
+
+---
+
+### 不要输出
+
+- 合同条目、检查清单、评分表
+- 文件路径、规则来源、系统术语
+- "Anti-AI""blocking_rules""core-constraints"等词
+
+---
+
+### 示例
+
+以下是一份完整的写作任务书示例。实际输出时根据 research 结果填入真实数据,保持这个语气和密度。
+
+---
+
+你现在要写《凡人修仙传》第47章《坊市试探》。
+
+这一章主要写韩立进入坊市,试探那条关于"天灵根弟子失踪"的消息到底是真是假。
+
+上章结尾韩立刚从禁地脱出,身上还带着墨蛟的气息没散干净,回到住处才发现陈巧倩留了一封短信,说坊市那边有人在高价收购蕴灵丹的原料,而且收购者指名要"外门新晋弟子"来接头。这个条件太针对他了,他不确定是机会还是陷阱。
+
+所以这章的核心不是去坊市买东西,而是一次有预谋的试探。韩立要弄清三件事:谁在收购、为什么指名新晋弟子、这件事跟天灵根弟子失踪有没有关系。但他不能暴露自己真实的修为(他一直在藏,对外只展示练气九层的水平),也不能让人发现他身上的墨蛟残息。
+
+中间大致这么走:韩立先到坊市外围转了一圈摸情况,接着通过陈巧倩搭上收购者的线,然后在接头时发现对方的修为和身份都不简单。
+
+其中"试探消息真伪"和"发现对方身份不简单"是这章绕不开的,别漏掉。
+
+有一点要注意:不能让韩立在这章就摊牌,也不能让他直接跟对方起冲突。这章是铺垫,不是爆发。
+
+另外有一条跨章的硬线索:第38章埋的伏笔——韩立在藏经阁翻到过一份关于"灵根置换术"的残页,当时没在意,但如果这章的失踪事件跟灵根有关,他会想起来。写的时候可以让他在某个瞬间闪过这个念头,但别展开,点到为止。
+
+---
+
+这章主要出场这几个人:
+
+韩立——筑基初期,但对外只展示练气九层。刚从禁地回来,灵力恢复了大半但还没满。他现在的状态是警觉但克制,进坊市之前已经想好了退路。说话习惯是不主动透露信息,能用一个字回答的不用两个字。
+
+陈巧倩——练气七层,在坊市有几条暗线。她这次帮韩立牵线不是出于好意,而是想用这件事换韩立手里的一瓶蕴灵丹。她说话圆滑,喜欢绕弯子,但遇到利益问题时很直接。这章里她是韩立和收购者之间的中间人。
+
+收购者(暂未露身份)——只在这章末尾露一个侧影。不要写出他的全貌,只通过气息、说话方式和一个不经意的细节让韩立(和读者)感觉到这个人不简单。
+
+---
+
+这章写的时候,留意这几件事:
+
+这是玄幻修仙类的故事,整体气质偏冷、偏算计,不是热血少年流。韩立不会冲动行事,他的所有动作背后都有盘算。写的时候保持这种"每一步都在试探"的感觉。
+
+最近两章的审查得分偏低的地方是"对话层次"——之前几章里韩立和配角的对话有点平,信息传递太直接,缺少试探和保留。这章正好是个试探场景,适合把对话写出层次来:每句话表面说一件事,底下藏着另一层意思。
+
+这章处在铺垫阶段,节奏不要快。不要一上来就进坊市,可以先写韩立在住处整理思路、判断风险,再出门。到了坊市也不要直奔目标,让他先观察环境,确认没有异常,再走向接头点。
+
+情绪别直接写出来。韩立警觉的时候不要写"他心中警觉",而是写他的动作——比如他走路时手一直虚握着一张符箓,或者他进门前先用神识扫了一圈。
+
+对话别写成说明会,让每个人带着各自的心思说话。陈巧倩想要丹药,她的每句话都在试探韩立的底线。韩立想要情报,他的每句话都在确认陈巧倩到底知道多少。
+
+结尾别把局面放平,留一点还没彻底落地的东西。
+
+---
+
+这章结尾要收在韩立发现收购者身份不简单的那个瞬间。
+
+不要写成"他震惊了"或者"他意识到事情没那么简单"——找一个具体的细节来收:比如他注意到对方袖口露出的一枚令牌,或者对方随口说了一句只有内门弟子才知道的话。就停在韩立看到这个细节、还没来得及反应的那个呼吸上。
+
+让读者带着"这个人到底是谁"翻到下一章。
 
 ## 9. 错误处理
 

+ 29 - 0
webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py

@@ -284,6 +284,15 @@ def test_context_agent_prefers_contract_and_latest_commit_mainline():
     assert "memory-contract load-context" in text
 
 
+def test_context_agent_loads_fixed_guides_and_outputs_writer_brief():
+    text = (AGENTS_DIR / "context-agent.md").read_text(encoding="utf-8")
+    assert "core-constraints.md" in text
+    assert "anti-ai-guide.md" in text
+    assert "写作任务书" in text
+    assert "Step 2 直写提示词" not in text
+    assert "Context Contract" not in text
+
+
 def test_data_agent_is_described_as_extraction_only_not_direct_write_mainline():
     text = (AGENTS_DIR / "data-agent.md").read_text(encoding="utf-8")
     assert "chapter-commit" in text
@@ -296,3 +305,23 @@ def test_dashboard_and_plan_skills_surface_story_runtime_mainline():
     plan_text = (SKILLS_DIR / "webnovel-plan" / "SKILL.md").read_text(encoding="utf-8")
     assert "story-runtime/health" in dashboard_text
     assert ".story-system/" in plan_text
+
+
+def test_webnovel_write_skill_routes_step2_through_writing_brief():
+    text = (SKILLS_DIR / "webnovel-write" / "SKILL.md").read_text(encoding="utf-8")
+    assert "写作任务书" in text
+    assert "context-agent" in text
+    assert "Step 0.5" not in text
+    assert 'cat "${SKILL_ROOT}/../../references/shared/core-constraints.md"' not in text
+    assert 'cat "${SKILL_ROOT}/references/anti-ai-guide.md"' not in text
+
+
+def test_context_agent_and_write_skill_form_isolated_write_chain():
+    context_text = (AGENTS_DIR / "context-agent.md").read_text(encoding="utf-8")
+    skill_text = (SKILLS_DIR / "webnovel-write" / "SKILL.md").read_text(encoding="utf-8")
+
+    assert "写作任务书" in context_text
+    assert "写作任务书" in skill_text
+    assert "context-agent" in skill_text
+    assert "Context Contract" not in context_text
+    assert "Step 2 直写提示词" not in context_text

+ 4 - 0
webnovel-writer/scripts/extract_chapter_context.py

@@ -320,6 +320,8 @@ def _load_contract_context(project_root: Path, chapter_num: int) -> Dict[str, An
         "writing_guidance": (sections.get("writing_guidance") or {}).get("content", {}),
         "plot_structure": (sections.get("plot_structure") or {}).get("content", {}),
         "long_term_memory": (sections.get("long_term_memory") or {}).get("content", {}),
+        "scene": (sections.get("scene") or {}).get("content", {}),
+        "core": (sections.get("core") or {}).get("content", {}),
     }
 
 
@@ -353,6 +355,8 @@ def build_chapter_context_payload(project_root: Path, chapter_num: int) -> Dict[
         "writing_guidance": contract_context.get("writing_guidance", {}),
         "plot_structure": plot_structure,
         "long_term_memory": contract_context.get("long_term_memory", {}),
+        "scene": contract_context.get("scene", {}),
+        "core": contract_context.get("core", {}),
         "rag_assist": rag_assist,
     }
 

+ 14 - 36
webnovel-writer/skills/webnovel-write/SKILL.md

@@ -44,7 +44,7 @@ allowed-tools: Read Write Edit Grep Bash Task
 
 ## 决策树入口
 
-在进入 Step 0.5 之前,先判断:
+在进入 Step 1 之前,先判断:
 
 - 若 `preflight` 失败或项目根不合法 → **阻断**,先修环境
 - 若当前章缺少章纲或章纲关键字段缺失 → **阻断**,请求用户补全
@@ -55,9 +55,9 @@ allowed-tools: Read Write Edit Grep Bash Task
 
 ## 模式定义
 
-- `/webnovel-write`:Step 0.5 → Step 1 → Step 2 → Step 3 → Step 4 → Step 5 → Step 6
-- `/webnovel-write --fast`:Step 0.5 → Step 1 → Step 2 → Step 3(轻量) → Step 4 → Step 5 → Step 6
-- `/webnovel-write --minimal`:Step 0.5 → Step 1 → Step 2 → Step 4(仅排版) → Step 5 → Step 6
+- `/webnovel-write`:Step 1 → Step 2 → Step 3 → Step 4 → Step 5 → Step 6
+- `/webnovel-write --fast`:Step 1 → Step 2 → Step 3(轻量) → Step 4 → Step 5 → Step 6
+- `/webnovel-write --minimal`:Step 1 → Step 2 → Step 4(仅排版) → Step 5 → Step 6
 
 最小产物:
 - 章节正文文件
@@ -90,8 +90,6 @@ allowed-tools: Read Write Edit Grep Bash Task
 | Step 1 | always | 追读力分类 | `${SKILL_ROOT}/../../references/reading-power-taxonomy.md` |
 | Step 1 | always | 题材配置 | `${SKILL_ROOT}/../../references/genre-profiles.md` |
 | Step 1 | always | 风格差异化 | `${SKILL_ROOT}/references/style-variants.md` |
-| Step 2 | always | 核心约束 | `${SKILL_ROOT}/../../references/shared/core-constraints.md` |
-| Step 2 | always | Anti-AI 预防 | `${SKILL_ROOT}/references/anti-ai-guide.md` |
 | Step 3 | always | 审查 schema | `${SKILL_ROOT}/../../references/review-schema.md`(reviewer 内部使用) |
 | Step 4 | always | 润色指南 | `${SKILL_ROOT}/references/polish-guide.md` |
 | Step 4 | always | 排版规则 | `${SKILL_ROOT}/references/writing/typesetting.md` |
@@ -137,7 +135,7 @@ export PROJECT_ROOT="$(python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-roo
 
 ### 准备阶段:刷新写前合同树
 
-在进入 Step 0.5 之前,必须先生成并刷新本章的写前合同(类比网文作者开写前先过一遍大纲、设定、禁区):
+在进入 Step 1 之前,必须先生成并刷新本章的写前合同(类比网文作者开写前先过一遍大纲、设定、禁区):
 
 ```bash
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" \
@@ -153,47 +151,27 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" \
 - 合同缺失或生成失败 → 直接阻断,不进入正文起草
 - 类比:作者没过完大纲就开写,容易写崩
 
-### Step 0.5:轻量节点预检
-
-目的:在不阻断流程的前提下,对章纲中的结构化节点做轻量一致性提醒。
-
-规则:
-- 只在当前章详细大纲存在 `CBN/CEN` 时执行。
-- 只检查主角或 POV 角色相关节点。
-- 第一版仅检查:
-  - `CBN` 中地点是否与 `protagonist_state.location` 明显冲突
-  - `CBN/CEN` 中主角境界或能力要求是否与 `protagonist_state.power` 明显冲突
-- 检查结果仅作为警告注入给 `context-agent`,不得阻断流程。
-- 若无节点字段,直接跳过。
-
-警告示例:
-- `[NODE_WARNING] CBN 地点与当前状态不一致: 章纲=迦南学院入口, 实际=乌坦城`
-- `[NODE_WARNING] CBN 强度要求与当前境界不一致: 章纲=斗师级压制, 实际=斗者三星`
-
-### Step 1:调用 Context Agent 生成执行包
+### Step 1:调用 Context Agent 生成写作任务书
 
 使用 Task 调用 `context-agent`,输入:
 - `chapter`
 - `project_root`
 - `storage_path=.webnovel/`
 - `state_file=.webnovel/state.json`
-- 若存在 `NODE_WARNING`,一并传入
 
 硬要求:
-- 输出必须包含任务书、Context Contract、Step 2 直写提示词。
-- 执行包中必须纳入长期记忆约束与时间约束。
-- 若章纲提供结构化节点,执行包中必须包含"情节结构"板块与节拍映射。
+- `context-agent` 必须先完成 research,再输出最终的写作任务书。
+- Step 1 的最终产物只保留一份写作任务书。
+- 写作任务书必须能单独支撑 Step 2 起草正文。
+- 若章纲提供结构化节点,任务书中仍必须体现对应的节拍方向与收束位置。
 
 ### Step 2:起草正文
 
-执行前必须加载:
-
-```bash
-cat "${SKILL_ROOT}/../../references/shared/core-constraints.md"
-cat "${SKILL_ROOT}/references/anti-ai-guide.md"
-```
-
 硬要求:
+- Step 2 只根据 Step 1 生成的写作任务书起草正文。
+- Step 2 不再直接加载 `core-constraints.md`。
+- Step 2 不再直接加载 `anti-ai-guide.md`。
+- Step 2 不再自己拼中间块、旧版直写块或其他写前材料。
 - 只输出纯正文到章节文件。
 - 不得出现 `[TODO]`、`[待补充]` 等占位符。
 - 若上章存在明确钩子,本章必须回应。

+ 3 - 2
webnovel-writer/skills/webnovel-write/evals/evals.json

@@ -4,7 +4,7 @@
     {
       "id": 1,
       "prompt": "使用 /webnovel-write 写第4章。项目在 agents/evals/files/test-project/。",
-      "expected_output": "完成 Step 0.5 到 Step 6 的完整流程,生成第4章正文文件、审查结果、摘要文件和状态更新",
+      "expected_output": "完成 Step 1 到 Step 6 的完整流程,由 context-agent 先生成写作任务书,再起草第4章正文,最终生成审查结果、摘要文件和状态更新",
       "files": [
         "agents/evals/files/test-project/.webnovel/state.json",
         "agents/evals/files/test-project/大纲/总纲.md",
@@ -21,7 +21,8 @@
         "正文中没有出现 [TODO] 或 [待补充] 占位符",
         "正文字数在 1500-3000 之间",
         "Step 3 调用了 reviewer agent 进行审查",
-        "Step 4 加载了 anti-ai-guide.md",
+        "Step 1 由 context-agent 生成了写作任务书",
+        "Step 2 只根据写作任务书起草正文",
         "Step 5 调用了 data-agent 回写数据",
         "章末保留了未闭合问题或悬念(不是安全着陆)"
       ]