Files
writer-work-flow/packages/agents/tests/test_competitor_specs.py
Yaojia Wang 87dd09a797 feat(agents): Scope B A1 — 4 竞品生成器 schema/spec + 2 上下文 builder
续写/扩写/降AI率/拆书 4 个生成器的声明(preview-only writes=[],只声明 tier #2):
- schemas.py: ContinuationResult/PolishResult/DeAiResult/BookTeardownResult
- specs.py: continue(writer)/expand(writer)/de-ai(analyst)/teardown(analyst),各配 system_prompt
- generation_node.py: build_continuation_context(续写读前文)/build_text_input_context(原文输入)
- 单测: 4 spec/schema 契约 + 2 builder 纯函数(确定性/空值降级)

守不变量 #2(只声明 tier)/#3(纯预览不写库)/#9(system_prompt 进缓存前缀)。
2026-06-23 19:01:11 +02:00

90 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Scope B 竞品快赢 · 4 个生成器的 spec/schema 契约测试。
校验每个 spec 的 name/tier/reads/writes/output_schema/scope/input_schema
对齐不变量 #2只声明 tier/ #34 生成器一律纯预览 writes=[])。不联网、无 DB。
"""
from __future__ import annotations
import pytest
from ww_agents import (
AgentSpec,
BookTeardownResult,
ContinuationResult,
DeAiResult,
PolishResult,
continue_spec,
de_ai_spec,
expand_spec,
teardown_spec,
)
# (spec, name, tier, reads, 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),
]
@pytest.mark.parametrize(
("spec", "name", "tier", "reads", "output_schema"),
_COMPETITOR_SPECS,
)
def test_competitor_spec_declares_expected_contract(
spec: AgentSpec,
name: str,
tier: str,
reads: list[str],
output_schema: type,
) -> None:
assert spec.name == name
assert spec.tier == tier
assert spec.reads == reads
assert spec.writes == [] # 不变量 #34 生成器一律纯预览
assert spec.output_schema is output_schema
assert spec.scope == "builtin"
assert spec.input_schema is None # 注入材料为序列化文本,非结构化入参
@pytest.mark.parametrize("spec", [row[0] for row in _COMPETITOR_SPECS])
def test_competitor_spec_has_nonempty_system_prompt(spec: AgentSpec) -> None:
assert spec.system_prompt.strip()
# ---- schema 解析/默认值契约 ----
def test_continuation_result_parses_text() -> None:
parsed = ContinuationResult.model_validate({"text": "雷光再起,他迈步向前。"})
assert parsed.text == "雷光再起,他迈步向前。"
def test_polish_result_parses_text() -> None:
parsed = PolishResult.model_validate({"text": "夜色更浓,风穿过断壁。"})
assert parsed.text == "夜色更浓,风穿过断壁。"
def test_de_ai_result_parses_text() -> None:
parsed = DeAiResult.model_validate({"text": "他咧嘴一笑,没多说什么。"})
assert parsed.text == "他咧嘴一笑,没多说什么。"
def test_book_teardown_result_parses_and_lists_default_empty() -> None:
empty = BookTeardownResult(structure="开篇立钩→铺垫→爆发")
assert empty.themes == []
assert empty.archetypes == []
assert empty.hooks == []
parsed = BookTeardownResult.model_validate(
{
"themes": ["逆袭", "复仇"],
"archetypes": ["废柴主角", "腹黑反派"],
"structure": "黄金三章立爽点,逐卷升级",
"hooks": ["章末反转", "扮猪吃虎"],
}
)
assert parsed.themes == ["逆袭", "复仇"]
assert parsed.structure == "黄金三章立爽点,逐卷升级"
assert parsed.hooks == ["章末反转", "扮猪吃虎"]