muse-agent-example/tests/architecture/test_skills_index.py

214 lines
8.4 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.

#!/usr/bin/env python3
"""门禁:中文技能目录必须与磁盘、frontmatter、skills.json 一致。
两份目录是生成物(`muse/lifecycle/quality/harness/skills_index.py`):技能名与技能文件来自磁盘和
`muse/lifecycle/quality/harness/manifests/skills.json`,技能说明来自 SKILL.md frontmatter(SoT)。
技能增删改名漏同步、手改目录造成的漂移都会被本测试拒绝;修复方式是
`.venv/bin/python muse/lifecycle/quality/harness/skills_index.py --write`。
"""
from __future__ import annotations
import json
import pathlib
import subprocess
import sys
import tempfile
import unittest
ROOT = pathlib.Path(__file__).resolve().parents[2]
class SkillsIndexConsistencyTest(unittest.TestCase):
def test_index_matches_disk_and_manifest(self) -> None:
result = subprocess.run(
[
sys.executable,
str(ROOT / "muse" / "lifecycle" / "quality" / "harness" / "skills_index.py"),
"--root",
str(ROOT),
"--check",
],
capture_output=True,
text=True,
)
self.assertEqual(
result.returncode,
0,
"技能目录与事实漂移,请运行 muse/lifecycle/quality/harness/skills_index.py --write 重新生成:\n"
+ result.stdout
+ result.stderr,
)
def test_write_uses_chinese_index_paths_and_headers(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = pathlib.Path(temporary)
skill = root / ".agent" / "skills" / "规划" / "故事结构" / "SKILL.md"
skill.parent.mkdir(parents=True)
skill.write_text(
"---\nname: 故事结构\ndescription: 搭建故事结构。\n---\n# 故事结构\n",
encoding="utf-8",
)
business_skill = (
root
/ "muse"
/ "platform"
/ "llm"
/ "skills"
/ "调用内容模型"
/ "SKILL.md"
)
business_skill.parent.mkdir(parents=True)
business_skill.write_text(
"---\nname: 调用内容模型\ndescription: 调用受控内容模型。\n---\n"
"# 调用内容模型\n",
encoding="utf-8",
)
manifest = (
root
/ "muse"
/ "lifecycle"
/ "quality"
/ "harness"
/ "manifests"
/ "skills.json"
)
manifest.parent.mkdir(parents=True)
manifest.write_text(
json.dumps(
{
"schema_version": 3,
"skill_name_policy": "chinese_only",
"skills": [
{
"name": "故事结构",
"lifecycle": "planning",
"invocation": "model_routed",
"side_effects": ["none"],
"compounding": "partial",
"contract_owner": "规划与作品基础",
"collaborates_with": [],
"skill_path": ".agent/skills/规划/故事结构/SKILL.md",
},
{
"name": "调用内容模型",
"lifecycle": "platform",
"invocation": "orchestrated",
"side_effects": ["external_call"],
"compounding": "none",
"contract_owner": "平台底座",
"collaborates_with": [],
"skill_path": (
"muse/platform/llm/skills/调用内容模型/SKILL.md"
),
}
],
},
ensure_ascii=False,
indent=2,
)
+ "\n",
encoding="utf-8",
)
result = subprocess.run(
[
sys.executable,
str(ROOT / "muse" / "lifecycle" / "quality" / "harness" / "skills_index.py"),
"--root",
str(root),
"--write",
],
capture_output=True,
text=True,
)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
method_root = root / ".agent" / "skills" / "目录.md"
method_group = root / ".agent" / "skills" / "规划" / "目录.md"
business_index = root / "muse" / "技能目录.md"
self.assertTrue(method_root.is_file())
self.assertTrue(method_group.is_file())
self.assertTrue(business_index.is_file())
self.assertFalse((root / ".agent" / "skills" / "_index.md").exists())
self.assertFalse((root / "muse" / "_skills_index.md").exists())
root_text = method_root.read_text(encoding="utf-8")
self.assertIn("| 名称 | 相对地址 | 内容描述 | 使用场景 | 使用要求 |", root_text)
self.assertIn("| 规划类技能 | [目录.md](规划/目录.md) |", root_text)
group_text = method_group.read_text(encoding="utf-8")
self.assertIn(
"| 名称 | 相对地址 | 内容描述 | 使用场景 | 使用要求 |",
group_text,
)
self.assertIn(
"| 故事结构 | [SKILL.md](故事结构/SKILL.md) | 搭建故事结构。 |",
group_text,
)
business_text = business_index.read_text(encoding="utf-8")
self.assertIn(
"| 调用内容模型 | "
"[SKILL.md](platform/llm/skills/调用内容模型/SKILL.md) |",
business_text,
)
def test_write_rejects_legacy_english_method_category(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = pathlib.Path(temporary)
skill = root / ".agent" / "skills" / "planning" / "story" / "SKILL.md"
skill.parent.mkdir(parents=True)
skill.write_text(
"---\nname: story\ndescription: 旧分类夹具。\n---\n# Story\n",
encoding="utf-8",
)
manifest = (
root
/ "muse"
/ "lifecycle"
/ "quality"
/ "harness"
/ "manifests"
/ "skills.json"
)
manifest.parent.mkdir(parents=True)
manifest.write_text(
json.dumps(
{
"schema_version": 3,
"skill_name_policy": "transitional",
"skills": [
{
"name": "story",
"lifecycle": "planning",
"invocation": "model_routed",
"side_effects": ["none"],
"compounding": "partial",
"contract_owner": "测试夹具",
"collaborates_with": [],
"skill_path": ".agent/skills/planning/story/SKILL.md",
}
],
},
ensure_ascii=False,
indent=2,
)
+ "\n",
encoding="utf-8",
)
result = subprocess.run(
[
sys.executable,
str(ROOT / "muse" / "lifecycle" / "quality" / "harness" / "skills_index.py"),
"--root",
str(root),
"--write",
],
capture_output=True,
text=True,
)
self.assertEqual(result.returncode, 2, result.stdout + result.stderr)
self.assertIn("未登记分类目录:planning", result.stdout)
if __name__ == "__main__":
unittest.main()