114 lines
4.2 KiB
Python
114 lines
4.2 KiB
Python
"""U4 验证:10 个业务 Skill YAML 的 preconditions 字段加载正确。
|
||
|
||
验证项:
|
||
- 全部 16 个 skill YAML 可被 SkillConfig.from_dict 正常加载
|
||
- 10 个业务 skill 的 preconditions 字段非空且为 list[str]
|
||
- 6 个引擎模板的 preconditions 字段为 None(未配置)
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
import yaml
|
||
|
||
from agentkit.skills.base import SkillConfig
|
||
|
||
_SKILLS_DIR = Path(__file__).resolve().parents[2] / "configs" / "skills"
|
||
|
||
# 10 个业务 skill(应配置 preconditions)
|
||
_BUSINESS_SKILLS = {
|
||
"code_reviewer",
|
||
"geo_optimizer",
|
||
"content_generator",
|
||
"competitor_analyzer",
|
||
"benchmark_runner",
|
||
"trend_agent",
|
||
"monitor",
|
||
"citation_detector",
|
||
"schema_advisor",
|
||
"deai_agent",
|
||
}
|
||
|
||
# 6 个引擎模板(不应配置 preconditions)
|
||
_ENGINE_TEMPLATES = {
|
||
"react_agent",
|
||
"direct_agent",
|
||
"rewoo_agent",
|
||
"reflexion_agent",
|
||
"plan_exec_agent",
|
||
"goal_driven_agent",
|
||
}
|
||
|
||
|
||
def _load_all_skill_configs() -> dict[str, SkillConfig]:
|
||
"""加载 configs/skills/ 下全部 YAML 为 SkillConfig。"""
|
||
result: dict[str, SkillConfig] = {}
|
||
for yaml_path in sorted(_SKILLS_DIR.glob("*.yaml")):
|
||
with yaml_path.open("r", encoding="utf-8") as f:
|
||
data = yaml.safe_load(f)
|
||
if not isinstance(data, dict) or "name" not in data:
|
||
continue
|
||
config = SkillConfig.from_dict(data)
|
||
result[config.name] = config
|
||
return result
|
||
|
||
|
||
class TestBusinessSkillPreconditions:
|
||
"""U4:业务 skill preconditions 字段验证。"""
|
||
|
||
def test_all_16_skills_load_without_error(self) -> None:
|
||
"""全部 16 个 skill YAML 可被 SkillConfig.from_dict 正常加载。"""
|
||
configs = _load_all_skill_configs()
|
||
assert len(configs) == 16, f"期望 16 个 skill,实际加载 {len(configs)} 个"
|
||
|
||
def test_business_skills_have_non_empty_preconditions(self) -> None:
|
||
"""10 个业务 skill 的 preconditions 字段非空且为 list[str]。"""
|
||
configs = _load_all_skill_configs()
|
||
missing = _BUSINESS_SKILLS - set(configs.keys())
|
||
assert not missing, f"缺少业务 skill: {missing}"
|
||
|
||
for name in _BUSINESS_SKILLS:
|
||
config = configs[name]
|
||
assert config.preconditions is not None, f"{name}.preconditions 为 None"
|
||
assert isinstance(config.preconditions, list), (
|
||
f"{name}.preconditions 不是 list"
|
||
)
|
||
assert len(config.preconditions) >= 2, (
|
||
f"{name}.preconditions 少于 2 条(实际 {len(config.preconditions)} 条)"
|
||
)
|
||
assert all(isinstance(p, str) and p.strip() for p in config.preconditions), (
|
||
f"{name}.preconditions 存在非字符串或空字符串项"
|
||
)
|
||
|
||
def test_engine_templates_have_no_preconditions(self) -> None:
|
||
"""6 个引擎模板的 preconditions 字段为 None(未配置)。"""
|
||
configs = _load_all_skill_configs()
|
||
missing = _ENGINE_TEMPLATES - set(configs.keys())
|
||
assert not missing, f"缺少引擎模板: {missing}"
|
||
|
||
for name in _ENGINE_TEMPLATES:
|
||
config = configs[name]
|
||
assert config.preconditions is None, (
|
||
f"引擎模板 {name} 不应配置 preconditions,实际为 {config.preconditions}"
|
||
)
|
||
|
||
def test_preconditions_round_trip_through_to_dict(self) -> None:
|
||
"""preconditions 字段经 to_dict 序列化后保持一致。"""
|
||
configs = _load_all_skill_configs()
|
||
for name in _BUSINESS_SKILLS:
|
||
config = configs[name]
|
||
dumped = config.to_dict()
|
||
assert dumped.get("preconditions") == config.preconditions, (
|
||
f"{name}.to_dict() 的 preconditions 与原值不一致"
|
||
)
|
||
|
||
def test_code_reviewer_preconditions_content(self) -> None:
|
||
"""code_reviewer 的 preconditions 包含 shell 工具使用约束。"""
|
||
configs = _load_all_skill_configs()
|
||
cr = configs["code_reviewer"]
|
||
joined = " ".join(cr.preconditions)
|
||
assert "shell" in joined.lower() or "读取" in joined, (
|
||
"code_reviewer preconditions 应包含 shell 工具使用约束"
|
||
)
|