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

docs: define subagent run summaries

lingfengQAQ 2 недель назад
Родитель
Сommit
f56eee41a6

+ 12 - 1
webnovel-writer/agents/context-agent.md

@@ -76,7 +76,18 @@ load-context 已含(不要重复查):`story_contracts`(MASTER/volume/cha
 4. **怎么写更顺**:最关键一段。把裁决层风格 / 节奏翻成具体指导;题材基调;`writing_guidance`;`anti_patterns` 翻为自然提醒;审查得分趋势。
 4. **怎么写更顺**:最关键一段。把裁决层风格 / 节奏翻成具体指导;题材基调;`writing_guidance`;`anti_patterns` 翻为自然提醒;审查得分趋势。
 5. **收在哪里**:结尾停在什么感觉,留什么未完感。
 5. **收在哪里**:结尾停在什么感觉,留什么未完感。
 
 
-## 8. 错误处理
+## 8. SubagentRun 可汇总信号
+
+不要把 `SubagentRun` JSON 写入任务书,也不要额外落盘。主流程会根据本 agent 的返回内容记录:
+
+- `status`:五段任务书完整为 `completed`;使用降级读取但仍可写为 `partial`;无法支撑起草为 `failed`。
+- `problems`:上下文不足、contracts 缺失、伏笔数据缺失、任务书不完整、耗时异常。
+- `auto_handled`:legacy fallback、`extract-context` 降级读取、跳过非阻断结构化节点。
+- `needs_user_action`:上下文严重不足或需要人工补录关键设定时为 true。
+- `duration_ms`:由主流程计时记录。
+- `outputs`:写作任务书。
+
+## 9. 错误处理
 
 
 | 场景 | 处理 |
 | 场景 | 处理 |
 |------|------|
 |------|------|

+ 11 - 0
webnovel-writer/agents/data-agent.md

@@ -107,3 +107,14 @@ hook_strength: "strong"
 ## 8. 错误处理
 ## 8. 错误处理
 
 
 artifacts 失败→重跑 C/D。commit 失败→修复三份 JSON 后补提。projection 失败不由 data-agent 修复,由主流程补跑 `projections retry`。耗时>30s→附原因。
 artifacts 失败→重跑 C/D。commit 失败→修复三份 JSON 后补提。projection 失败不由 data-agent 修复,由主流程补跑 `projections retry`。耗时>30s→附原因。
+
+## 9. SubagentRun 可汇总信号
+
+不要把 `SubagentRun` 写进三份 artifact,也不要替代 artifact schema。主流程会根据文件和本 agent 的说明记录:
+
+- `status`:三份 artifact 均写入且 schema 合格为 `completed`;存在 warning / pending 但可继续为 `partial`;任一必需 artifact 缺失或 schema 不合格为 `failed`。
+- `problems`:三份 artifact 写入状态、schema 不合格、pending 消歧、长时间无进展、输出不完整。
+- `auto_handled`:重跑 C/D、采用高置信别名、跳过低价值非跨章事实。
+- `needs_user_action`:低置信度歧义会影响事实入库、pending 非空或 schema 失败时为 true。
+- `duration_ms`:由主流程计时记录。
+- `outputs`:`fulfillment_result.json`、`disambiguation_result.json`、`extraction_result.json`。

+ 12 - 1
webnovel-writer/agents/deconstruction-agent.md

@@ -111,7 +111,18 @@ color: magenta
 
 
 `init_candidates` 是候选创意约束包,不是最终设定;每个候选都必须显式说明与参考书的差异化处理。
 `init_candidates` 是候选创意约束包,不是最终设定;每个候选都必须显式说明与参考书的差异化处理。
 
 
-## 8. 边界、确认与错误处理
+## 8. SubagentRun 可汇总信号
+
+不要把 `SubagentRun` 写进 `init_reference_research` 顶层,也不要额外落盘。主流程会根据返回 JSON 和调用过程记录:
+
+- `status`:`quality.passed=true` 且候选完整为 `completed`;降级 quick mode 或有 warning 为 `partial`;输入不足、文本不可读或质量失败为 `failed`。
+- `problems`:输入不足、质量不过线、降级 quick mode、输出不完整、参考事实污染风险。
+- `auto_handled`:deep 降级 quick、孤立情节兜底、角色合并去重。
+- `needs_user_action`:`quality.passed=false`、`confidence < 0.85`、`warnings` 非空或 `canon_contamination_warnings` 非空时为 true。
+- `duration_ms`:由主流程计时记录。
+- `outputs`:`init_reference_research` JSON。
+
+## 9. 边界、确认与错误处理
 
 
 边界:
 边界:
 - 不生成新书 canon,不替用户做最终设定决定。
 - 不生成新书 canon,不替用户做最终设定决定。

+ 12 - 1
webnovel-writer/agents/reviewer.md

@@ -124,7 +124,18 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" ind
 
 
 > `category` 取值规范:本 agent 只产出 5 个维度值(`setting`/`timeline`/`continuity`/`character`/`logic`);schema 中的 `pacing`/`other` 仅为后端兼容枚举,本 agent 不主动产出。
 > `category` 取值规范:本 agent 只产出 5 个维度值(`setting`/`timeline`/`continuity`/`character`/`logic`);schema 中的 `pacing`/`other` 仅为后端兼容枚举,本 agent 不主动产出。
 
 
-## 8. 错误处理
+## 8. SubagentRun 可汇总信号
+
+不要把 `SubagentRun` 写进 reviewer JSON,也不要输出额外文本。主流程会根据 reviewer JSON 和调用过程记录:
+
+- `status`:JSON 完整且五维结论齐全为 `completed`;维度跳过但已在 `summary` / `dimension_results` 说明为 `partial`;正文为空或无法审查为 `failed`。
+- `problems`:正文为空、读取状态失败、维度跳过、输出不完整、blocking issue、耗时异常。
+- `auto_handled`:无状态读取时跳过某个非关键维度、降级读取摘要。
+- `needs_user_action`:存在 `blocking=true` 或无法审查时为 true。
+- `duration_ms`:由主流程计时记录。
+- `outputs`:`.webnovel/tmp/review_results.json` 与审查报告路径由主流程记录。
+
+## 9. 错误处理
 
 
 - 无法读取角色状态 → 跳过设定一致性检查,在 summary 中标注"无法校验设定一致性:数据读取失败"
 - 无法读取角色状态 → 跳过设定一致性检查,在 summary 中标注"无法校验设定一致性:数据读取失败"
 - 无法读取上章摘要 → 跳过连贯性检查中的"上章钩子回应"项
 - 无法读取上章摘要 → 跳过连贯性检查中的"上章钩子回应"项

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

@@ -33,6 +33,20 @@ AUTHOR_REPORT_SKILLS = (
     "webnovel-write",
     "webnovel-write",
     "webnovel-review",
     "webnovel-review",
 )
 )
+SUBAGENT_RUN_FIELDS = (
+    '"status": "completed | partial | failed | skipped"',
+    '"problems": []',
+    '"auto_handled": []',
+    '"needs_user_action": false',
+    '"duration_ms": 0',
+    '"outputs": []',
+)
+SUBAGENT_PROMPT_FILES = (
+    "context-agent.md",
+    "reviewer.md",
+    "data-agent.md",
+    "deconstruction-agent.md",
+)
 
 
 # webnovel.py 注册的子命令(从 add_parser 提取)
 # webnovel.py 注册的子命令(从 add_parser 提取)
 REGISTERED_CLI_SUBCOMMANDS = {
 REGISTERED_CLI_SUBCOMMANDS = {
@@ -347,6 +361,47 @@ def test_review_skill_final_report_covers_metrics_and_blocking_decision():
     assert "最终状态为“需要你处理”" in text
     assert "最终状态为“需要你处理”" in text
 
 
 
 
+def test_main_skills_record_subagent_run_summaries_for_agent_calls():
+    """主 Skill 调用 Agent 后必须记录 SubagentRun 汇总,供最终报告使用。"""
+    expected = {
+        "webnovel-init": ("deconstruction-agent",),
+        "webnovel-write": ("context-agent", "reviewer", "data-agent"),
+        "webnovel-review": ("reviewer",),
+    }
+
+    for skill_name, agents in expected.items():
+        text = _read_text(SKILLS_DIR / skill_name / "SKILL.md")
+        assert "SubagentRun" in text, f"{skill_name}: 缺少 SubagentRun 汇总契约"
+        for field in SUBAGENT_RUN_FIELDS:
+            assert field in text, f"{skill_name}: 缺少 SubagentRun 字段 {field}"
+        for agent_name in agents:
+            assert f'"name": "{agent_name}"' in text, (
+                f"{skill_name}: 缺少 {agent_name} 的 SubagentRun name"
+            )
+    plan_text = _read_text(SKILLS_DIR / "webnovel-plan" / "SKILL.md")
+    assert "SubagentRun" not in plan_text, "webnovel-plan 当前不调用 Agent,不应虚构 SubagentRun"
+
+
+@pytest.mark.parametrize("agent_file_name", SUBAGENT_PROMPT_FILES)
+def test_agents_expose_subagent_run_summary_signals_without_changing_outputs(agent_file_name: str):
+    """Agent prompt 必须暴露可汇总信号,但不得把 SubagentRun 写入原始产物。"""
+    text = _read_text(AGENTS_DIR / agent_file_name)
+
+    assert "SubagentRun 可汇总信号" in text
+    for field in ("`status`", "`problems`", "`auto_handled`", "`needs_user_action`", "`duration_ms`", "`outputs`"):
+        assert field in text, f"{agent_file_name}: 缺少可汇总字段 {field}"
+    assert "主流程" in text and "记录" in text
+
+    if agent_file_name == "reviewer.md":
+        assert "不要把 `SubagentRun` 写进 reviewer JSON" in text
+    elif agent_file_name == "data-agent.md":
+        assert "不要把 `SubagentRun` 写进三份 artifact" in text
+    elif agent_file_name == "deconstruction-agent.md":
+        assert "不要把 `SubagentRun` 写进 `init_reference_research` 顶层" in text
+    elif agent_file_name == "context-agent.md":
+        assert "不要把 `SubagentRun` JSON 写入任务书" in text
+
+
 def test_story_system_runtime_contract_commands_exist():
 def test_story_system_runtime_contract_commands_exist():
     text = (SKILLS_DIR / "webnovel-write" / "SKILL.md").read_text(encoding="utf-8")
     text = (SKILLS_DIR / "webnovel-write" / "SKILL.md").read_text(encoding="utf-8")
     assert "story-system" in text
     assert "story-system" in text

+ 17 - 0
webnovel-writer/skills/webnovel-init/SKILL.md

@@ -85,6 +85,23 @@ Use the Agent tool to run `webnovel-writer:deconstruction-agent`.
 Prompt: reference_title={reference_title}; reference_source={reference_source}; reference_text_path={reference_text_path}; reference_text_excerpt={reference_text_excerpt}; analysis_mode={quick|deep|auto}; init_goal={当前初始化故事方向或空}; target_genre={题材或空}。只返回 init_reference_research JSON 对象,不写任何文件,不创建目录,不写 .story-system、.webnovel、设定集、大纲、正文、idea_bank.json、state.json 或任何 canon/read model 文件。
 Prompt: reference_title={reference_title}; reference_source={reference_source}; reference_text_path={reference_text_path}; reference_text_excerpt={reference_text_excerpt}; analysis_mode={quick|deep|auto}; init_goal={当前初始化故事方向或空}; target_genre={题材或空}。只返回 init_reference_research JSON 对象,不写任何文件,不创建目录,不写 .story-system、.webnovel、设定集、大纲、正文、idea_bank.json、state.json 或任何 canon/read model 文件。
 ```
 ```
 
 
+调用后主流程必须记录一份 `SubagentRun` 汇总(仅供最终报告使用,不写入 canon):
+
+```json
+{
+  "name": "deconstruction-agent",
+  "user_label": "参考作品拆解",
+  "status": "completed | partial | failed | skipped",
+  "problems": [],
+  "auto_handled": [],
+  "needs_user_action": false,
+  "duration_ms": 0,
+  "outputs": []
+}
+```
+
+`quality.passed=false`、`confidence < 0.85`、输入不足、文本不可读、降级 quick mode 或输出不完整时,必须写入 `problems`,并让最终报告进入“建议确认 / 必须处理”。
+
 处理规则:
 处理规则:
 - 只有书名/平台、无文本或摘录时,先问能否提供摘录/路径;不能提供则把参考书仅作"方向线索",不得编造其黄金三章、角色、设定或剧情事实。
 - 只有书名/平台、无文本或摘录时,先问能否提供摘录/路径;不能提供则把参考书仅作"方向线索",不得编造其黄金三章、角色、设定或剧情事实。
 - 接收返回的 `init_reference_research` JSON 后,只使用 `reader_promise`、`opening_hook_patterns`、`cool_point_loops`、`protagonist_patterns`、`antagonist_pressure_patterns`、`pacing_notes`、`borrowable_structures`、`differentiation_requirements`、`init_candidates`、`quality`。
 - 接收返回的 `init_reference_research` JSON 后,只使用 `reader_promise`、`opening_hook_patterns`、`cool_point_loops`、`protagonist_patterns`、`antagonist_pressure_patterns`、`pacing_notes`、`borrowable_structures`、`differentiation_requirements`、`init_candidates`、`quality`。

+ 17 - 0
webnovel-writer/skills/webnovel-review/SKILL.md

@@ -73,6 +73,23 @@ Prompt: chapter={chapter_num}; chapter_file={chapter_file}; project_root=${PROJE
 
 
 reviewer 返回后,主流程把严格 JSON 写入 `${PROJECT_ROOT}/.webnovel/tmp/review_results.json`(reviewer 不持 Write,是这份 artifact 的非写入方)。`review-pipeline` 必须把同一路径覆盖为标准 review_result artifact(含 `blocking_count`)。
 reviewer 返回后,主流程把严格 JSON 写入 `${PROJECT_ROOT}/.webnovel/tmp/review_results.json`(reviewer 不持 Write,是这份 artifact 的非写入方)。`review-pipeline` 必须把同一路径覆盖为标准 review_result artifact(含 `blocking_count`)。
 
 
+调用后主流程必须记录 `SubagentRun` 汇总(仅供最终报告使用):
+
+```json
+{
+  "name": "reviewer",
+  "user_label": "写作检查",
+  "status": "completed | partial | failed | skipped",
+  "problems": [],
+  "auto_handled": [],
+  "needs_user_action": false,
+  "duration_ms": 0,
+  "outputs": []
+}
+```
+
+reviewer 跳过、失败、输出不完整、正文为空、维度跳过、blocking issue 或耗时异常,必须写入 `problems` / `auto_handled`,不得在最终报告中静默。
+
 ### Step 6:生成报告并落库
 ### Step 6:生成报告并落库
 
 
 ```bash
 ```bash

+ 51 - 0
webnovel-writer/skills/webnovel-write/SKILL.md

@@ -96,6 +96,23 @@ Task:
 
 
 产物:一份写作任务书,能独立支撑 Step 2 起草。
 产物:一份写作任务书,能独立支撑 Step 2 起草。
 
 
+调用后主流程必须记录 `SubagentRun` 汇总(仅供最终报告使用):
+
+```json
+{
+  "name": "context-agent",
+  "user_label": "整理写作依据",
+  "status": "completed | partial | failed | skipped",
+  "problems": [],
+  "auto_handled": [],
+  "needs_user_action": false,
+  "duration_ms": 0,
+  "outputs": []
+}
+```
+
+上下文不足、legacy fallback、伏笔数据缺失、任务书不完整或耗时异常,必须写入 `problems` / `auto_handled`,不得在最终报告中静默。
+
 ### Step 2:起草正文
 ### Step 2:起草正文
 
 
 只根据任务书起草。不加载 core-constraints/anti-ai-guide(已内化到任务书)。只输出纯正文,无占位符。有结构化节点时围绕 CBN→CPNs→CEN 展开。中文思维写作。
 只根据任务书起草。不加载 core-constraints/anti-ai-guide(已内化到任务书)。只输出纯正文,无占位符。有结构化节点时围绕 CBN→CPNs→CEN 展开。中文思维写作。
@@ -116,6 +133,23 @@ Task:
 
 
 reviewer 只返回 JSON;主流程负责用 `Write` 把返回的 JSON 写入 `${PROJECT_ROOT}/.webnovel/tmp/review_results.json`(reviewer 不持 Write,是这份 artifact 的非写入方)。随后必须运行 review-pipeline;review-pipeline 会把同一路径覆盖为标准 review_result artifact(含 `blocking_count`),供 precommit gate 与后续提交命令使用。
 reviewer 只返回 JSON;主流程负责用 `Write` 把返回的 JSON 写入 `${PROJECT_ROOT}/.webnovel/tmp/review_results.json`(reviewer 不持 Write,是这份 artifact 的非写入方)。随后必须运行 review-pipeline;review-pipeline 会把同一路径覆盖为标准 review_result artifact(含 `blocking_count`),供 precommit gate 与后续提交命令使用。
 
 
+调用后主流程必须记录 `SubagentRun` 汇总(仅供最终报告使用):
+
+```json
+{
+  "name": "reviewer",
+  "user_label": "写作检查",
+  "status": "completed | partial | failed | skipped",
+  "problems": [],
+  "auto_handled": [],
+  "needs_user_action": false,
+  "duration_ms": 0,
+  "outputs": []
+}
+```
+
+reviewer 跳过、失败、输出不完整、`--minimal` 写 no-review artifact、blocking issue、维度跳过或耗时异常,必须写入 `problems` / `auto_handled`,不得在最终报告中静默。
+
 ```bash
 ```bash
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" review-pipeline \
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" review-pipeline \
   --chapter {chapter_num} \
   --chapter {chapter_num} \
@@ -160,6 +194,23 @@ Task:
 
 
 artifact 字段 schema 由 data-agent 自身定义、runtime validator 校验;主流程只检查文件存在与 schema,不重写、不补写、不口头替代。
 artifact 字段 schema 由 data-agent 自身定义、runtime validator 校验;主流程只检查文件存在与 schema,不重写、不补写、不口头替代。
 
 
+调用后主流程必须记录 `SubagentRun` 汇总(仅供最终报告使用):
+
+```json
+{
+  "name": "data-agent",
+  "user_label": "保存本章故事事实",
+  "status": "completed | partial | failed | skipped",
+  "problems": [],
+  "auto_handled": [],
+  "needs_user_action": false,
+  "duration_ms": 0,
+  "outputs": []
+}
+```
+
+三份 artifact 写入状态、schema 不合格、pending 消歧、长时间无进展或输出不完整,必须写入 `problems`;自动重跑或降级处理必须写入 `auto_handled`。
+
 #### 5.2 提交前校验与 CHAPTER_COMMIT
 #### 5.2 提交前校验与 CHAPTER_COMMIT
 
 
 先跑 precommit gate:
 先跑 precommit gate: