feat(toolbox): F1 拆书 teardown 落库成项目 rules
- teardown_spec.writes=["rules"](只声明 tier,入库仍经白名单 gate,不变量 #3) - toolbox_registry teardown entry 加 ingest=IngestSpec(table="rules") - routers/toolbox.py 通用 ingest dispatcher 加 _ingest_rules: BookTeardownResult(themes/archetypes/structure/hooks) 拍平为可读 rules 条目, 落项目级(无 continuity 预检,仿 _ingest_outline 无 409) - schemas/toolbox.py 加 TeardownIngestView + ToolIngestRequest.teardown - 单测:teardown ingest→rules 真落行 + 预览(generate)不写库 后端门禁绿:ruff/format/mypy clean · alembic no drift · pytest 644 passed
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
"""Scope B 竞品快赢 · 4 个生成器的 spec/schema 契约测试。
|
||||
|
||||
校验每个 spec 的 name/tier/reads/writes/output_schema/scope/input_schema,
|
||||
对齐不变量 #2(只声明 tier)/ #3(4 生成器一律纯预览 writes=[])。不联网、无 DB。
|
||||
对齐不变量 #2(只声明 tier)/ #3(续写/扩写/降AI 纯预览 writes=[];拆书 F1 落库 rules)。
|
||||
不联网、无 DB。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -19,17 +20,25 @@ from ww_agents import (
|
||||
teardown_spec,
|
||||
)
|
||||
|
||||
# (spec, name, tier, reads, output_schema)
|
||||
# (spec, name, tier, reads, writes, output_schema)
|
||||
_COMPETITOR_SPECS = [
|
||||
(continue_spec, "continue", "writer", ["projects", "outline", "chapters"], ContinuationResult),
|
||||
(expand_spec, "expand", "writer", ["projects"], PolishResult),
|
||||
(de_ai_spec, "de-ai", "analyst", ["projects"], DeAiResult),
|
||||
(teardown_spec, "teardown", "analyst", ["projects"], BookTeardownResult),
|
||||
(
|
||||
continue_spec,
|
||||
"continue",
|
||||
"writer",
|
||||
["projects", "outline", "chapters"],
|
||||
[],
|
||||
ContinuationResult,
|
||||
),
|
||||
(expand_spec, "expand", "writer", ["projects"], [], PolishResult),
|
||||
(de_ai_spec, "de-ai", "analyst", ["projects"], [], DeAiResult),
|
||||
# F1:拆书结论可落库为项目 rules(writes=["rules"];其余三者仍纯预览)。
|
||||
(teardown_spec, "teardown", "analyst", ["projects"], ["rules"], BookTeardownResult),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("spec", "name", "tier", "reads", "output_schema"),
|
||||
("spec", "name", "tier", "reads", "writes", "output_schema"),
|
||||
_COMPETITOR_SPECS,
|
||||
)
|
||||
def test_competitor_spec_declares_expected_contract(
|
||||
@@ -37,12 +46,13 @@ def test_competitor_spec_declares_expected_contract(
|
||||
name: str,
|
||||
tier: str,
|
||||
reads: list[str],
|
||||
writes: list[str],
|
||||
output_schema: type,
|
||||
) -> None:
|
||||
assert spec.name == name
|
||||
assert spec.tier == tier
|
||||
assert spec.reads == reads
|
||||
assert spec.writes == [] # 不变量 #3:4 生成器一律纯预览
|
||||
assert spec.writes == writes # 不变量 #3:续写/扩写/降AI 纯预览;拆书落 rules
|
||||
assert spec.output_schema is output_schema
|
||||
assert spec.scope == "builtin"
|
||||
assert spec.input_schema is None # 注入材料为序列化文本,非结构化入参
|
||||
|
||||
@@ -571,8 +571,8 @@ class DeAiResult(BaseModel):
|
||||
class BookTeardownResult(BaseModel):
|
||||
"""拆书生成器结构化产出:样本作品的结构化拆解(Scope B 竞品快赢)。
|
||||
|
||||
纯预览产物——不映射任何业务表、不入库(`teardown_spec.writes=[]`,不变量 #3)。
|
||||
拆书「落库成 rules」为可选 follow-up(需 `KNOWN_TABLES` + `_ingest_rules`),本期不做。
|
||||
F1:可经 ingest 端点把结论拍平为项目 `rules` 条目落库(`teardown_spec.writes=["rules"]`);
|
||||
入库仍经白名单 gate(不变量 #3),预览(generate)不写库。
|
||||
"""
|
||||
|
||||
themes: list[str] = Field(
|
||||
|
||||
@@ -799,6 +799,6 @@ teardown_spec = AgentSpec(
|
||||
input_schema=None, # 注入材料为序列化文本(设定 + 样本 + 需求),非结构化入参
|
||||
output_schema=BookTeardownResult,
|
||||
reads=["projects"],
|
||||
writes=[], # 纯预览,不写库(不变量 #3)
|
||||
writes=["rules"], # F1:拆书结论可落库为项目 rules(入库仍经 ingest 白名单 gate,#3)
|
||||
scope="builtin",
|
||||
)
|
||||
|
||||
@@ -19,13 +19,13 @@ _NEW_KEYS = {
|
||||
"glossary",
|
||||
"opening",
|
||||
"fine-outline",
|
||||
# 竞品快赢(Scope B):续写 / 扩写 / 降AI率 / 拆书(全 preview-only)。
|
||||
# 竞品快赢(Scope B):续写/扩写/降AI率 preview-only;拆书 F1 可落库 rules。
|
||||
"continue",
|
||||
"expand",
|
||||
"de-ai",
|
||||
"teardown",
|
||||
}
|
||||
_INGEST_KEYS = {"golden-finger", "glossary", "fine-outline"}
|
||||
_INGEST_KEYS = {"golden-finger", "glossary", "fine-outline", "teardown"}
|
||||
|
||||
|
||||
def test_toolbox_has_all_tools() -> None:
|
||||
@@ -70,6 +70,9 @@ def test_ingest_tools_declare_known_table() -> None:
|
||||
assert TOOLBOX["glossary"].ingest.table == "world_entities"
|
||||
assert TOOLBOX["fine-outline"].ingest is not None
|
||||
assert TOOLBOX["fine-outline"].ingest.table == "outline"
|
||||
# F1:拆书结论落库为项目 rules。
|
||||
assert TOOLBOX["teardown"].ingest is not None
|
||||
assert TOOLBOX["teardown"].ingest.table == "rules"
|
||||
|
||||
|
||||
def test_chapter_tools_have_chapter_no_field() -> None:
|
||||
|
||||
@@ -197,7 +197,7 @@ TOOLBOX: dict[str, GeneratorTool] = {
|
||||
input_fields=[_chapter_no_field(), _BRIEF_FIELD],
|
||||
ingest=IngestSpec(table="outline"),
|
||||
),
|
||||
# ---- 竞品快赢(Scope B):续写 / 扩写 / 降AI率 / 拆书(全 preview-only,writes=[])----
|
||||
# ---- 竞品快赢(Scope B):续写/扩写/降AI率 preview-only;拆书 F1 可落库 rules ----
|
||||
"continue": GeneratorTool(
|
||||
key="continue",
|
||||
title="续写生成器",
|
||||
@@ -252,6 +252,7 @@ TOOLBOX: dict[str, GeneratorTool] = {
|
||||
_SOURCE_TEXT_FIELD,
|
||||
_BRIEF_FIELD,
|
||||
],
|
||||
ingest=IngestSpec(table="rules"), # F1:拆书结论拍平为项目 rules 条目落库
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user