"""BoardRouter 单元测试 — @board 前缀路由解析""" from __future__ import annotations from agentkit.experts.board_router import ( BOARD_PREFIX_PATTERN, BoardRouter, BoardRoutingResult, MAX_EXPERTS, ) from agentkit.experts.config import ExpertConfig, ExpertTemplate from agentkit.experts.registry import ExpertTemplateRegistry # ── 辅助函数 ────────────────────────────────────────────── def _make_expert_template( name: str = "test_expert", persona: str = "测试专家", speaking_style: str = "直接", decision_framework: str = "分析", ) -> ExpertTemplate: """创建测试用 ExpertTemplate""" config = ExpertConfig( name=name, agent_type="expert", persona=persona, thinking_style="analytical", speaking_style=speaking_style, decision_framework=decision_framework, bound_skills=[], task_mode="llm_generate", prompt={"identity": persona}, ) return ExpertTemplate( name=name, config=config, is_builtin=True, description=f"{name} 测试模板", ) def _make_registry_with_experts() -> ExpertTemplateRegistry: """创建包含预注册专家模板的注册中心""" registry = ExpertTemplateRegistry() registry.register(_make_expert_template("elon_musk", persona="Elon Musk")) registry.register(_make_expert_template("jeff_bezos", persona="Jeff Bezos")) registry.register(_make_expert_template("allenzhang", persona="张小龙")) # 注册 private_board 模板(使用 bound_skills 存储成员列表) board_config = ExpertConfig( name="private_board", agent_type="expert", persona="私董会模板", bound_skills=["elon_musk", "jeff_bezos", "allenzhang"], task_mode="llm_generate", prompt={"identity": "Private Board"}, ) registry.register(ExpertTemplate( name="private_board", config=board_config, is_builtin=True, description="默认私董会模板", )) return registry # ── BOARD_PREFIX_PATTERN 正则测试 ────────────────────────── class TestBoardPrefixPattern: """BOARD_PREFIX_PATTERN 正则匹配测试""" def test_match_board_only(self): """@board 前缀匹配(无专家指定)""" match = BOARD_PREFIX_PATTERN.match("@board 讨论AI未来") assert match is not None assert match.group(1) is None assert match.group(2) == "讨论AI未来" def test_match_board_with_experts(self): """@board:expert1,expert2 格式匹配""" match = BOARD_PREFIX_PATTERN.match("@board:elon_musk,jeff_bezos SpaceX上市") assert match is not None assert match.group(1) == "elon_musk,jeff_bezos" assert match.group(2) == "SpaceX上市" def test_match_board_with_template_name(self): """@board:private_board 显式使用模板""" match = BOARD_PREFIX_PATTERN.match("@board:private_board 讨论主题") assert match is not None assert match.group(1) == "private_board" assert match.group(2) == "讨论主题" def test_no_match_regular_input(self): """普通输入不匹配""" assert BOARD_PREFIX_PATTERN.match("你好,今天天气怎么样") is None assert BOARD_PREFIX_PATTERN.match("@team 分析数据") is None assert BOARD_PREFIX_PATTERN.match("@skill:search 搜索内容") is None def test_match_board_with_multiline_topic(self): """多行主题匹配""" match = BOARD_PREFIX_PATTERN.match("@board 第一行\n第二行") assert match is not None assert "第一行" in match.group(2) assert "第二行" in match.group(2) # ── BoardRouter.resolve 测试 ────────────────────────────── class TestBoardRouterResolve: """BoardRouter.resolve 路由解析测试""" def test_resolve_default_template(self): """@board 主题 → 使用默认模板""" router = BoardRouter(template_registry=_make_registry_with_experts()) result = router.resolve("@board 如何看待AI对教育的影响") assert result.matched is True assert result.board_mode is True assert result.topic == "如何看待AI对教育的影响" assert result.use_default_template is True assert result.match_method == "explicit_board" assert "elon_musk" in result.specified_experts assert "jeff_bezos" in result.specified_experts assert "allenzhang" in result.specified_experts def test_resolve_explicit_template(self): """@board:private_board 主题 → 显式使用默认模板""" router = BoardRouter(template_registry=_make_registry_with_experts()) result = router.resolve("@board:private_board 讨论主题") assert result.matched is True assert result.use_default_template is True assert result.topic == "讨论主题" assert len(result.specified_experts) == 3 def test_resolve_specified_experts(self): """@board:expert1,expert2 主题 → 指定专家""" router = BoardRouter(template_registry=_make_registry_with_experts()) result = router.resolve("@board:elon_musk,jeff_bezos SpaceX上市问题") assert result.matched is True assert result.use_default_template is False assert result.specified_experts == ["elon_musk", "jeff_bezos"] assert result.topic == "SpaceX上市问题" def test_resolve_non_board_input(self): """普通输入不匹配""" router = BoardRouter(template_registry=_make_registry_with_experts()) result = router.resolve("你好,今天天气怎么样") assert result.matched is False assert result.board_mode is False assert result.topic == "你好,今天天气怎么样" def test_resolve_empty_topic(self): """@board 无主题 → 空主题""" router = BoardRouter(template_registry=_make_registry_with_experts()) result = router.resolve("@board") assert result.matched is True assert result.topic == "" def test_resolve_invalid_expert_names_filtered(self): """无效专家名被过滤""" router = BoardRouter(template_registry=_make_registry_with_experts()) result = router.resolve("@board:elon_musk,invalid@name,jeff_bezos 主题") assert result.matched is True assert "elon_musk" in result.specified_experts assert "jeff_bezos" in result.specified_experts assert "invalid@name" not in result.specified_experts def test_resolve_max_experts_limit(self): """专家数量超过上限被截断""" router = BoardRouter(template_registry=_make_registry_with_experts()) # 构造超过 MAX_EXPERTS 个专家名 names = ",".join(f"expert_{i}" for i in range(MAX_EXPERTS + 5)) result = router.resolve(f"@board:{names} 讨论主题") assert result.matched is True assert len(result.specified_experts) <= MAX_EXPERTS def test_resolve_default_template_fallback(self): """无注册中心时使用硬编码回退默认成员""" router = BoardRouter(template_registry=ExpertTemplateRegistry()) result = router.resolve("@board 讨论主题") assert result.matched is True assert result.use_default_template is True # 回退到硬编码列表 assert len(result.specified_experts) > 0 assert "elon_musk" in result.specified_experts # ── BoardRouter.resolve_expert_configs 测试 ──────────────── class TestBoardRouterResolveConfigs: """BoardRouter.resolve_expert_configs 配置解析测试""" def test_resolve_configs_from_templates(self): """从注册模板解析专家配置""" registry = _make_registry_with_experts() router = BoardRouter(template_registry=registry) configs = router.resolve_expert_configs(["elon_musk", "jeff_bezos"]) assert len(configs) == 2 assert configs[0].name == "elon_musk" assert configs[0].is_lead is True # 第一个为主持人 assert configs[1].name == "jeff_bezos" assert configs[1].is_lead is False # 验证 board 模式字段 assert configs[0].speaking_style == "直接" assert configs[0].decision_framework == "分析" def test_resolve_configs_dynamic_generation(self): """未注册的专家名动态生成配置""" router = BoardRouter(template_registry=ExpertTemplateRegistry()) configs = router.resolve_expert_configs(["unknown_expert"]) assert len(configs) == 1 assert configs[0].name == "unknown_expert" assert configs[0].is_lead is True def test_resolve_configs_first_is_moderator(self): """第一个专家自动设为主持人""" registry = _make_registry_with_experts() router = BoardRouter(template_registry=registry) configs = router.resolve_expert_configs(["elon_musk", "jeff_bezos", "allenzhang"]) assert configs[0].is_lead is True assert configs[1].is_lead is False assert configs[2].is_lead is False def test_resolve_configs_empty_list(self): """空列表返回空配置""" router = BoardRouter(template_registry=_make_registry_with_experts()) configs = router.resolve_expert_configs([]) assert len(configs) == 0 def test_resolve_configs_invalid_name_skipped(self): """无效专家名被跳过""" router = BoardRouter(template_registry=_make_registry_with_experts()) configs = router.resolve_expert_configs(["elon_musk", "invalid@name", "jeff_bezos"]) assert len(configs) == 2 assert configs[0].name == "elon_musk" assert configs[1].name == "jeff_bezos" def test_resolve_configs_ensure_at_least_one_lead(self): """确保至少有一个主持人""" registry = _make_registry_with_experts() # 修改模板使 is_lead 全为 False for name in ["elon_musk", "jeff_bezos"]: template = registry.get(name) if template: template.config.is_lead = False router = BoardRouter(template_registry=registry) configs = router.resolve_expert_configs(["elon_musk", "jeff_bezos"]) # 第一个应被强制设为 lead assert configs[0].is_lead is True # ── BoardRoutingResult 数据类测试 ────────────────────────── class TestBoardRoutingResult: """BoardRoutingResult 数据类测试""" def test_default_values(self): """默认值""" result = BoardRoutingResult() assert result.matched is False assert result.board_mode is False assert result.specified_experts == [] assert result.topic == "" assert result.use_default_template is False assert result.match_method == "" def test_custom_values(self): """自定义值""" result = BoardRoutingResult( matched=True, board_mode=True, specified_experts=["a", "b"], topic="测试主题", use_default_template=True, match_method="explicit_board", ) assert result.matched is True assert result.board_mode is True assert result.specified_experts == ["a", "b"] assert result.topic == "测试主题" assert result.use_default_template is True assert result.match_method == "explicit_board"