feat(toolbox): T6 创作工具箱通用生成器框架 — 8 新生成器 + 声明驱动落地页 + P2 收尾

通用执行路径驱动全部生成器("加生成器=加一份声明"):
- @llm: ww_agents +7 输出 schema + 7 spec(book-title/blurb/name/golden-finger/
  glossary/opening/fine-outline,只声明 tier)+ build_outline_chapter_context
- @backend: ww_skills GeneratorTool 描述符 + TOOLBOX(11) + get_tool;3 通用端点
  GET /skills/toolbox · POST .../skills/{tool_key}/generate(预览不写库,仅记账) ·
  POST .../ingest(复用 continuity 409 + partition_writes 白名单);纯 context 派发
- @frontend: 工具箱落地页 RSC + 声明驱动 GeneratorRunner + lib/toolbox 纯函数
  + LeftNav「工具箱」+ ⌘K nav-toolbox/action-gen-*;legacy 3 跳现页
- @qa: tests/test_t6_toolbox_e2e.py 5 用例真 pg + mock 网关零 token,无端点 bug
- P2 收尾: 限流→decisions.md 记延后(单用户原型);noopener/Committable 早已修

守不变量 #2(只声明 tier)/#3(预览不写库,入库经验收 gate)/#9(缓存前缀)。无 DB 迁移。
门禁绿: 后端 ruff/format/mypy 195/alembic 无漂移/pytest 583;前端 lint/tsc/vitest 279/build。
spec 回写 PRODUCT_SPEC §7 + ARCHITECTURE §7.2 端点表。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2026-06-22 20:37:55 +02:00
parent 1f1afa37b6
commit f43ccd293f
46 changed files with 4848 additions and 12 deletions

View File

@@ -6,34 +6,55 @@
from __future__ import annotations
from .schemas import (
Blurb,
BlurbResult,
CharacterCard,
CharacterGenResult,
CharacterRelation,
Conflict,
ConflictType,
ContinuityReview,
DetailedOutlineResult,
ForeshadowReview,
ForeshadowSuggestion,
ForeshadowWindow,
GlossaryResult,
GlossaryTerm,
GoldenFinger,
GoldenFingerResult,
Idea,
IdeaListResult,
NameListResult,
NameSuggestion,
Opening,
OpeningResult,
OutlineChapter,
OutlineResult,
PaceIssue,
PaceReview,
Scene,
StyleDimension,
StyleDriftReview,
StyleDriftSegment,
StyleFingerprintResult,
Title,
TitleListResult,
WorldEntityCard,
WorldGenResult,
)
from .specs import (
AgentSpec,
blurb_spec,
book_title_spec,
brainstorm_spec,
character_gen_spec,
continuity_spec,
fine_outline_spec,
foreshadow_spec,
glossary_spec,
golden_finger_spec,
name_spec,
opening_spec,
outliner_spec,
pace_spec,
refiner_spec,
@@ -44,31 +65,52 @@ from .specs import (
__all__ = [
"AgentSpec",
"Blurb",
"BlurbResult",
"CharacterCard",
"CharacterGenResult",
"CharacterRelation",
"Conflict",
"ConflictType",
"ContinuityReview",
"DetailedOutlineResult",
"ForeshadowReview",
"ForeshadowSuggestion",
"ForeshadowWindow",
"GlossaryResult",
"GlossaryTerm",
"GoldenFinger",
"GoldenFingerResult",
"Idea",
"IdeaListResult",
"NameListResult",
"NameSuggestion",
"Opening",
"OpeningResult",
"OutlineChapter",
"OutlineResult",
"PaceIssue",
"PaceReview",
"Scene",
"StyleDimension",
"StyleDriftReview",
"StyleDriftSegment",
"StyleFingerprintResult",
"Title",
"TitleListResult",
"WorldEntityCard",
"WorldGenResult",
"blurb_spec",
"book_title_spec",
"brainstorm_spec",
"character_gen_spec",
"continuity_spec",
"fine_outline_spec",
"foreshadow_spec",
"glossary_spec",
"golden_finger_spec",
"name_spec",
"opening_spec",
"outliner_spec",
"pace_spec",
"refiner_spec",

View File

@@ -336,3 +336,194 @@ class IdeaListResult(BaseModel):
default_factory=list,
description="生成的脑洞清单;无则空列表",
)
# ---- 创作工具箱 · 书名生成器book-titlelight纯预览 writes=[]----
class Title(BaseModel):
"""单个书名候选:书名 + 取名理由(创作工具箱 book-title 产物)。
纯预览产物——不映射任何业务表、不入库(`book_title_spec.writes=[]`,不变量 #3
"""
title: str = Field(description="书名候选")
rationale: str = Field(description="取名理由:为何抓人/契合题材")
class TitleListResult(BaseModel):
"""书名生成器结构化产出一组书名候选T6 创作工具箱)。
声明式只读(不变量 #3纯预览端点同步返回不写任何业务表。
"""
titles: list[Title] = Field(
default_factory=list,
description="生成的书名候选清单;无则空列表",
)
# ---- 创作工具箱 · 简介生成器blurbanalyst纯预览 writes=[]----
class Blurb(BaseModel):
"""单条作品简介:正文 + 切入角度(创作工具箱 blurb 产物)。
纯预览产物——不映射任何业务表、不入库(`blurb_spec.writes=[]`,不变量 #3
"""
text: str = Field(description="简介正文(一段抓人文案)")
angle: str = Field(description="切入角度:从哪个卖点/钩子切入")
class BlurbResult(BaseModel):
"""简介生成器结构化产出多版差异化简介T6 创作工具箱)。
声明式只读(不变量 #3纯预览端点同步返回不写任何业务表。
"""
variants: list[Blurb] = Field(
default_factory=list,
description="生成的简介变体清单;无则空列表",
)
# ---- 创作工具箱 · 取名生成器namelight纯预览 writes=[]----
class NameSuggestion(BaseModel):
"""单条命名建议:名字 + 类别 + 可选说明(创作工具箱 name 产物)。
纯预览产物——不映射任何业务表、不入库(`name_spec.writes=[]`,不变量 #3
`kind` 标注命名对象类别(人物/势力/地点/功法/物品…)。
"""
name: str = Field(description="建议的名字")
kind: str = Field(description="命名对象类别(人物 / 势力 / 地点 / 功法 / 物品 等)")
note: str | None = Field(default=None, description="命名说明(取意/出处,可缺)")
class NameListResult(BaseModel):
"""取名生成器结构化产出一组命名建议T6 创作工具箱)。
声明式只读(不变量 #3纯预览端点同步返回不写任何业务表。
"""
names: list[NameSuggestion] = Field(
default_factory=list,
description="生成的命名建议清单;无则空列表",
)
# ---- 创作工具箱 · 金手指生成器golden-fingerwriter声明式 writes=world_entities----
class GoldenFinger(BaseModel):
"""单个金手指系统:名称 + 机制 + 成长 + 限制(创作工具箱 golden-finger 产物)。
映射 `world_entities`(力量体系类);落库经入库端点(不变量 #3。`limits` = 不可
违背的能力边界/代价,供 continuity 续审引用比对(防能力不符)。
"""
name: str = Field(description="金手指名称")
mechanism: str = Field(description="核心机制:如何运作、触发条件")
growth: str = Field(description="成长路径:随剧情如何升级/进阶")
limits: str = Field(description="限制与代价:能力边界、副作用(防越界,供一致性校验)")
class GoldenFingerResult(BaseModel):
"""金手指生成器结构化产出一组金手指系统T6 创作工具箱)。
声明式 writes不变量 #3本结构是纯产物落 `world_entities` 表经入库端点,
不在 agent 节点直接写库。
"""
systems: list[GoldenFinger] = Field(
default_factory=list,
description="生成的金手指系统清单;无则空列表",
)
# ---- 创作工具箱 · 术语表生成器glossaryanalyst声明式 writes=world_entities----
class GlossaryTerm(BaseModel):
"""单条术语:名称 + 类型 + 定义 + 硬规则(创作工具箱 glossary 产物)。
映射 `world_entities`concept/术语类);`rules` = 该术语的硬规则清单,
对齐 `world_entities.rules`,供 continuity 续审引用比对(不变量 #3
"""
name: str = Field(description="术语名")
type: str = Field(description="术语类型(境界 / 功法 / 货币 / 度量 / 称谓 等)")
definition: str = Field(description="术语定义(一句话释义)")
rules: list[str] = Field(
default_factory=list,
description="该术语的硬规则清单(不可违背设定,供 continuity 校验引用)",
)
class GlossaryResult(BaseModel):
"""术语表生成器结构化产出一组术语T6 创作工具箱)。
声明式 writes不变量 #3本结构是纯产物落 `world_entities` 表经入库端点,
不在 agent 节点直接写库。
"""
terms: list[GlossaryTerm] = Field(
default_factory=list,
description="生成的术语清单;无则空列表",
)
# ---- 创作工具箱 · 开篇生成器openingwriter纯预览 writes=[]----
class Opening(BaseModel):
"""单版开篇正文(创作工具箱 opening 产物)。
纯预览产物——不映射任何业务表、不入库(`opening_spec.writes=[]`,不变量 #3
"""
text: str = Field(description="开篇正文(一段成稿文本)")
class OpeningResult(BaseModel):
"""开篇生成器结构化产出多版开篇T6 创作工具箱)。
声明式只读(不变量 #3纯预览端点同步返回不写任何业务表。
"""
variants: list[Opening] = Field(
default_factory=list,
description="生成的开篇变体清单;无则空列表",
)
# ---- 创作工具箱 · 细纲生成器fine-outlineanalyst声明式 writes=outline----
class Scene(BaseModel):
"""单个场景节拍:序号 + 节拍 + 目的 + 冲突 + 钩子(创作工具箱 fine-outline 产物)。
把某章 outline 的粗节拍展开为细粒度场景;落库经入库端点(不变量 #3
"""
idx: int = Field(description="场景序号章内有序0/1 起均可)")
beat: str = Field(description="场景节拍:本场发生什么")
purpose: str = Field(description="叙事目的:推动主线/塑造人物/铺垫伏笔")
conflict: str = Field(description="本场冲突/张力来源")
hook: str = Field(description="场景钩子:驱动读者读下一场的悬念")
class DetailedOutlineResult(BaseModel):
"""细纲生成器结构化产出某章展开的场景清单T6 创作工具箱)。
声明式 writes不变量 #3本结构是纯产物落 `outline` 表经入库端点,
不在 agent 节点直接写库。
"""
scenes: list[Scene] = Field(
default_factory=list,
description="展开的场景清单;无则空列表",
)

View File

@@ -13,14 +13,21 @@ from pydantic import BaseModel, ConfigDict, Field
from ww_llm_gateway.types import Tier
from .schemas import (
BlurbResult,
CharacterGenResult,
ContinuityReview,
DetailedOutlineResult,
ForeshadowReview,
GlossaryResult,
GoldenFingerResult,
IdeaListResult,
NameListResult,
OpeningResult,
OutlineResult,
PaceReview,
StyleDriftReview,
StyleFingerprintResult,
TitleListResult,
WorldGenResult,
)
@@ -412,3 +419,245 @@ brainstorm_spec = AgentSpec(
writes=[], # 纯预览,不写库(不变量 #3
scope="builtin",
)
# ---- book-title书名生成器轻量档纯预览T6 创作工具箱)----
BOOK_TITLE_SYSTEM_PROMPT = """你是中文网文的「书名生成器」book-title轻量档\
职责:依据作品立意/题材与作者一句话需求,发散产出一组**差异化**的书名候选,\
每个给出书名 + 取名理由,供作者快速选定。
输入材料:
- 作品设定projects题材、立意、主线、卖点
- 作者的一句话需求/方向(可空——空则按题材自由发散)。
产出(每条一个 title
- title书名候选贴合题材、抓人、朗朗上口
- rationale取名理由——为何抓人/契合题材(爽点/反差/悬念/平台调性)。
纪律:
- 一次产出多条时**逐条差异化**——不同风格/切入点,避免一个模子;
- 贴合作品立意与题材;需求为空时按题材发散,不臆造与题材无关的设定;
- 你只产结构化书名清单,**不改稿、不写库**(纯预览,不变量 #3"""
book_title_spec = AgentSpec(
name="book-title",
tier="light", # 不变量 #2只声明档位不写 model书名发散用轻量档
system_prompt=BOOK_TITLE_SYSTEM_PROMPT,
input_schema=None, # 注入材料为序列化文本(作品设定 + 一句话需求),非结构化入参
output_schema=TitleListResult,
reads=["projects"],
writes=[], # 纯预览,不写库(不变量 #3
scope="builtin",
)
# ---- blurb简介生成器分析档纯预览T6 创作工具箱)----
BLURB_SYSTEM_PROMPT = """你是中文网文的「简介生成器」blurb分析档\
职责:依据作品立意/题材与作者一句话需求,产出多版**差异化**的作品简介文案,\
每版给出简介正文 + 切入角度,供作者择优用于书页/榜单。
输入材料:
- 作品设定projects题材、立意、主线、卖点
- 作者的一句话需求/方向(可空——空则按题材自由发散)。
产出(每版一个 variant
- text简介正文一段抓人文案立人设/抛钩子/留悬念,控制在书页可读篇幅);
- angle切入角度——从哪个卖点/钩子切入(如「金手指反差」「身份悬念」「群像冲突」)。
纪律:
- 多版之间**切入角度各异**,便于作者对比择优,避免雷同;
- 贴合作品立意与题材,简介与正文方向一致,不臆造剧情;
- 你只产结构化简介清单,**不改稿、不写库**(纯预览,不变量 #3"""
blurb_spec = AgentSpec(
name="blurb",
tier="analyst", # 不变量 #2只声明档位不写 model文案打磨用分析档
system_prompt=BLURB_SYSTEM_PROMPT,
input_schema=None, # 注入材料为序列化文本(作品设定 + 一句话需求),非结构化入参
output_schema=BlurbResult,
reads=["projects"],
writes=[], # 纯预览,不写库(不变量 #3
scope="builtin",
)
# ---- name取名生成器轻量档纯预览T6 创作工具箱)----
NAME_SYSTEM_PROMPT = """你是中文网文的「取名生成器」name轻量档\
职责:依据作品世界观与作者需求,为人物/势力/地点/功法/物品等取一组**差异化**的名字,\
每个给出名字 + 类别 + 取意说明,须契合世界观术语与命名风格。
输入材料:
- 作品设定projects题材、立意
- 世界观实体world_entities力量体系、势力、地理——命名须契合其术语/风格);
- 已有角色characters避免与既有名字撞名/混淆);
- 作者的一句话需求(命名对象、数量、风格倾向;可空则按世界观自由发散)。
产出(每条一个 name
- name建议的名字契合世界观命名风格朗朗上口、有辨识度
- kind命名对象类别人物 / 势力 / 地点 / 功法 / 物品 等);
- note取意说明取自何意/出处,可缺)。
纪律:
- 名字须**契合世界观术语与命名风格**,不与已有角色撞名/易混;
- 一次产出多条时**逐条差异化**,避免一个模子;
- 你只产结构化命名清单,**不改稿、不写库**(纯预览,不变量 #3"""
name_spec = AgentSpec(
name="name",
tier="light", # 不变量 #2只声明档位不写 model取名发散用轻量档
system_prompt=NAME_SYSTEM_PROMPT,
input_schema=None, # 注入材料为序列化文本(设定 + 世界观 + 已有角色 + 需求)
output_schema=NameListResult,
reads=["projects", "world_entities", "characters"],
writes=[], # 纯预览,不写库(不变量 #3
scope="builtin",
)
# ---- golden-finger金手指生成器写手档声明式 writes=world_entitiesT6 创作工具箱)----
GOLDEN_FINGER_SYSTEM_PROMPT = """你是中文网文的「金手指设计师」golden-finger写手档\
职责:依据作品立意/题材与作者需求,设计一组内部自洽的金手指(力量体系/系统/特殊能力),\
每个显式标注机制、成长路径与**限制代价**,供后续一致性校验引用。
输入材料:
- 作品设定projects题材、立意、主线、卖点
- 世界观实体world_entities已有力量体系/势力/地理——金手指须与之自洽不冲突);
- 作者的金手指需求(一句话或要点)。
产出(每个一条 system
- name金手指名称
- mechanism核心机制——如何运作、触发条件、与世界观力量体系的衔接
- growth成长路径——随剧情如何升级/进阶,给读者持续爽点反馈;
- limits**限制与代价**——能力边界、副作用、冷却/门槛(防越级无敌,\
供 continuity 续审逐条引用比对,防「能力不符」)。
纪律:
- 金手指须内部自洽、与既有世界观力量体系不冲突;
- **limits 要显式、可校验**——别把约束藏在描述里;这是后续防能力越界的依据;
- 你只产结构化金手指name/mechanism/growth/limits**不改稿、不写库**\
(落 world_entities 表经入库端点,不变量 #3"""
golden_finger_spec = AgentSpec(
name="golden-finger",
tier="writer", # 不变量 #2只声明档位不写 model力量体系创意用写手档
system_prompt=GOLDEN_FINGER_SYSTEM_PROMPT,
input_schema=None, # 注入材料为序列化文本(设定 + 世界观 + 需求),非结构化入参
output_schema=GoldenFingerResult,
reads=["projects", "world_entities"],
writes=["world_entities"], # 声明式(真写库经入库端点,不变量 #3
scope="builtin",
)
# ---- glossary术语表生成器分析档声明式 writes=world_entitiesT6 创作工具箱)----
GLOSSARY_SYSTEM_PROMPT = """你是中文网文的「术语表生成器」glossary分析档\
职责:依据作品世界观,整理一组关键术语(境界/功法/货币/度量/称谓/概念等),\
每条给出定义与**硬规则**,对齐世界观设定,供后续一致性校验引用比对。
输入材料:
- 作品设定projects题材、立意
- 世界观实体world_entities力量体系/势力/地理——术语须与之自洽);
- 作者的术语需求(一句话或要点;可空则按世界观梳理核心术语)。
产出(每条一个 term
- name术语名
- type术语类型境界 / 功法 / 货币 / 度量 / 称谓 / 概念 等);
- definition术语定义一句话释义清晰无歧义
- rules该术语的**硬规则清单**——不可违背的设定边界(如「金币 100 兑 1 银币」\
「炼气期不可飞行」),每条一句,可被 continuity 续审逐条引用比对。
纪律:
- 术语须与既有世界观自洽,不自相矛盾;
- **rules 要显式、可校验**——别把约束藏在 definition 里;
- 你只产结构化术语清单name/type/definition/rules**不改稿、不写库**\
(落 world_entities 表经入库端点,不变量 #3"""
glossary_spec = AgentSpec(
name="glossary",
tier="analyst", # 不变量 #2只声明档位不写 model术语梳理用分析档
system_prompt=GLOSSARY_SYSTEM_PROMPT,
input_schema=None, # 注入材料为序列化文本(设定 + 世界观 + 需求),非结构化入参
output_schema=GlossaryResult,
reads=["projects", "world_entities"],
writes=["world_entities"], # 声明式(真写库经入库端点,不变量 #3
scope="builtin",
)
# ---- opening开篇生成器写手档纯预览T6 创作工具箱)----
OPENING_SYSTEM_PROMPT = """你是中文网文的「开篇生成器」opening写手档\
职责:依据作品立意/题材与首章大纲,产出多版**差异化**的开篇正文,\
每版都要在前几段立爽点/钩子/代入感(黄金三章),供作者择优作为正文起手。
输入材料:
- 作品设定projects题材、立意、主线、卖点
- 大纲outline首章/开篇章的节拍要点——开篇须服务这些节拍);
- 作者的一句话需求/方向(可空则按题材与大纲自由发挥)。
产出(每版一个 variant
- text开篇正文一段成稿文本开门见山立钩子、信息密度高、少铺垫慢热
纪律:
- 多版之间**切入方式各异**(如「冲突开场」「悬念开场」「金手指开场」),便于作者对比;
- 贴合作品立意、题材与首章大纲节拍,不跑题、不臆造与大纲冲突的剧情;
- 你只产结构化开篇文本清单,**不改稿、不写库**(纯预览,不变量 #3"""
opening_spec = AgentSpec(
name="opening",
tier="writer", # 不变量 #2只声明档位不写 model正文创作用写手档
system_prompt=OPENING_SYSTEM_PROMPT,
input_schema=None, # 注入材料为序列化文本(设定 + 大纲 + 需求),非结构化入参
output_schema=OpeningResult,
reads=["projects", "outline"],
writes=[], # 纯预览,不写库(不变量 #3
scope="builtin",
)
# ---- fine-outline细纲生成器分析档声明式 writes=outlineT6 创作工具箱)----
FINE_OUTLINE_SYSTEM_PROMPT = """你是中文网文的「细纲生成器」fine-outline分析档\
职责:把某一章的粗大纲节拍**展开为细粒度场景序列**,每个场景给出节拍、叙事目的、\
冲突与钩子,让作者据细纲直接落笔成章。
输入材料:
- 作品设定projects题材、立意、主线
- 大纲outline本章的章号与粗节拍要点——细纲须忠实展开这些节拍不另起炉灶
- 作者的一句话需求/方向(可空则按章节拍自由展开)。
产出(每个场景一条 scene
- idx场景序号章内有序
- beat场景节拍——本场具体发生什么
- purpose叙事目的——推动主线 / 塑造人物 / 铺垫或推进伏笔;
- conflict本场冲突/张力来源;
- hook场景钩子——驱动读者读下一场的悬念。
纪律:
- 场景须**忠实展开本章粗节拍**,顺序合理、推进主线,不跑题、不与大纲冲突;
- 每场都要有目的与张力,避免注水过场;
- 你只产结构化场景清单idx/beat/purpose/conflict/hook**不改稿、不写库**\
(落 outline 表经入库端点,不变量 #3"""
fine_outline_spec = AgentSpec(
name="fine-outline",
tier="analyst", # 不变量 #2只声明档位不写 model细纲拆解用分析档
system_prompt=FINE_OUTLINE_SYSTEM_PROMPT,
input_schema=None, # 注入材料为序列化文本(设定 + 本章大纲 + 需求),非结构化入参
output_schema=DetailedOutlineResult,
reads=["projects", "outline"],
writes=["outline"], # 声明式(真写库经入库端点,不变量 #3
scope="builtin",
)