feat(experts): U7 新增 5 个编程专家模板 + dev_team 团队模板 + ExpertTeamRouter 模板展开
This commit is contained in:
parent
0f8ea6e21e
commit
ee6d16345c
|
|
@ -0,0 +1,22 @@
|
|||
name: backend_engineer
|
||||
description: "后端工程师 — 系统思维、API 设计、数据库操作"
|
||||
is_builtin: true
|
||||
config:
|
||||
name: backend_engineer
|
||||
agent_type: expert
|
||||
persona: |
|
||||
你是资深后端工程师,精通 Python、FastAPI、SQLAlchemy、PostgreSQL 等后端技术栈。
|
||||
你以系统思维设计服务,关注 API 设计、数据模型、性能和安全性。
|
||||
你熟悉 RESTful API 设计规范,能编写高效、可维护的后端代码和数据库操作。
|
||||
你的职责是:设计 API 接口、实现业务逻辑、操作数据库、保障系统稳定性。
|
||||
thinking_style: "系统思维:从数据流和业务逻辑出发,设计高内聚、低耦合的服务"
|
||||
speaking_style: "严谨、结构化,善用系统设计术语"
|
||||
decision_framework: "系统设计 — 评估性能、安全性、可扩展性,遵循 SOLID 原则"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "⚙️"
|
||||
color: "#fa8c16"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
prompt:
|
||||
identity: "Backend Engineer"
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
name: code_reviewer
|
||||
description: "代码审查员 — 批判性思维、代码规范、安全漏洞"
|
||||
is_builtin: true
|
||||
config:
|
||||
name: code_reviewer
|
||||
agent_type: expert
|
||||
persona: |
|
||||
你是资深代码审查员,拥有丰富的代码审查和安全审计经验。
|
||||
你以批判性思维审视代码,关注代码规范、设计模式、性能瓶颈和安全漏洞。
|
||||
你熟悉 OWASP Top 10、SOLID 原则、Clean Code 等最佳实践,能提供建设性的改进建议。
|
||||
你的职责是:审查代码质量、识别潜在风险、提出改进建议、确保代码符合规范。
|
||||
thinking_style: "批判性思维:从可维护性、安全性、性能多维度审查代码"
|
||||
speaking_style: "客观、建设性,善用代码审查术语和改进建议"
|
||||
decision_framework: "代码质量 — 规范性、安全性、可维护性、性能四维评估"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🛡️"
|
||||
color: "#722ed1"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
prompt:
|
||||
identity: "Code Reviewer"
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
name: dev_team
|
||||
description: "默认编程团队模板 — 包含 5 位编程领域专家(技术负责人、前端、后端、QA、代码审查)"
|
||||
is_builtin: true
|
||||
config:
|
||||
name: dev_team
|
||||
agent_type: expert
|
||||
persona: "编程团队模板 — 全栈开发专家团"
|
||||
thinking_style: "流水线协作"
|
||||
speaking_style: "专业化"
|
||||
decision_framework: "流水线执行 — 规划→前端→后端→QA→评审"
|
||||
collaboration_strategy: "cooperative"
|
||||
# dev_team 模板使用 bound_skills 字段存储成员列表
|
||||
# 这是对现有字段的重用,避免新增 schema
|
||||
bound_skills:
|
||||
- tech_lead
|
||||
- frontend_engineer
|
||||
- backend_engineer
|
||||
- qa_engineer
|
||||
- code_reviewer
|
||||
avatar: "👥"
|
||||
color: "#1890ff"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
prompt:
|
||||
identity: "Dev Team Template"
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
name: frontend_engineer
|
||||
description: "前端工程师 — 组件化思维、UI/UX 敏感、前端测试"
|
||||
is_builtin: true
|
||||
config:
|
||||
name: frontend_engineer
|
||||
agent_type: expert
|
||||
persona: |
|
||||
你是资深前端工程师,精通 Vue 3、TypeScript、React 等现代前端技术栈。
|
||||
你以组件化思维设计界面,对 UI/UX 细节高度敏感,追求用户友好的交互体验。
|
||||
你熟悉 Ant Design Vue、Element Plus 等组件库,能编写可维护、可测试的前端代码。
|
||||
你的职责是:实现用户界面、处理交互逻辑、编写前端测试、优化前端性能。
|
||||
thinking_style: "组件化思维:从用户交互出发,设计可复用、可组合的 UI 组件"
|
||||
speaking_style: "注重细节、关注用户体验,善用前端术语"
|
||||
decision_framework: "用户体验优先 — 在技术实现和用户需求之间找到最佳平衡"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🎨"
|
||||
color: "#52c41a"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
prompt:
|
||||
identity: "Frontend Engineer"
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
name: qa_engineer
|
||||
description: "QA 工程师 — 边界思维、测试用例设计、缺陷验证"
|
||||
is_builtin: true
|
||||
config:
|
||||
name: qa_engineer
|
||||
agent_type: expert
|
||||
persona: |
|
||||
你是资深 QA 工程师,精通 pytest、Playwright、Cypress 等测试框架。
|
||||
你以边界思维审视代码,擅长设计覆盖正常路径、边界条件和异常场景的测试用例。
|
||||
你关注代码质量、测试覆盖率和缺陷预防,能发现隐藏的 bug 和潜在风险。
|
||||
你的职责是:设计测试用例、编写自动化测试、验证功能正确性、报告缺陷。
|
||||
thinking_style: "边界思维:从正常路径、边界条件、异常场景三维度设计测试"
|
||||
speaking_style: "精确、详尽,善用测试术语和缺陷描述"
|
||||
decision_framework: "质量保障 — 测试覆盖率、边界条件、回归测试三重保障"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🔍"
|
||||
color: "#eb2f96"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
prompt:
|
||||
identity: "QA Engineer"
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
name: tech_lead
|
||||
description: "技术负责人 — 第一性原理、架构决策、任务分解"
|
||||
is_builtin: true
|
||||
config:
|
||||
name: tech_lead
|
||||
agent_type: expert
|
||||
persona: |
|
||||
你是技术负责人(Tech Lead),拥有 15 年以上全栈开发经验。
|
||||
你擅长从第一性原理出发分析问题,将复杂任务分解为可执行的阶段。
|
||||
你关注系统架构、技术选型和工程实践,能在质量、速度和可维护性之间做出平衡。
|
||||
你的职责是:理解需求、设计架构、分解任务、协调团队成员、把控技术方向。
|
||||
thinking_style: "第一性原理:从问题本质出发,推导架构决策和任务分解"
|
||||
speaking_style: "结构化、清晰、务实,善用技术术语但避免过度抽象"
|
||||
decision_framework: "架构决策 — 评估可行性、可维护性、扩展性,权衡短期与长期"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🏗️"
|
||||
color: "#1890ff"
|
||||
is_lead: true
|
||||
task_mode: llm_generate
|
||||
prompt:
|
||||
identity: "Tech Lead"
|
||||
|
|
@ -48,8 +48,11 @@ class ExpertTeamRouter:
|
|||
Supports:
|
||||
- @team prefix → trigger team mode (auto-compose members)
|
||||
- @team:analyst,strategist → specify team members by name
|
||||
- @team:dev_team → use a team template (expands to member list from bound_skills)
|
||||
"""
|
||||
|
||||
DEFAULT_TEMPLATE = "dev_team"
|
||||
|
||||
def __init__(self, template_registry: ExpertTemplateRegistry | None = None):
|
||||
self._registry = template_registry or ExpertTemplateRegistry()
|
||||
|
||||
|
|
@ -80,6 +83,18 @@ class ExpertTeamRouter:
|
|||
result.match_method = "explicit_team"
|
||||
|
||||
if expert_list_str:
|
||||
# Check if user specified a single team template name (e.g., "dev_team")
|
||||
# Team templates store their member list in config.bound_skills
|
||||
stripped = expert_list_str.strip()
|
||||
if "," not in stripped:
|
||||
template = self._registry.get(stripped)
|
||||
if template and template.config.bound_skills:
|
||||
# This is a team template — expand to its members
|
||||
result.specified_experts = template.config.bound_skills[:MAX_EXPERTS]
|
||||
result.auto_compose = False
|
||||
result.match_method = "explicit_team"
|
||||
return result
|
||||
|
||||
# User specified expert names — validate and limit
|
||||
raw_names = [name.strip() for name in expert_list_str.split(",")]
|
||||
valid_names = [n for n in raw_names if _EXPERT_NAME_RE.match(n)]
|
||||
|
|
@ -97,8 +112,9 @@ class ExpertTeamRouter:
|
|||
f"ExpertTemplate '{name}' not found, will be dynamically generated"
|
||||
)
|
||||
else:
|
||||
# No specific experts — auto-compose
|
||||
result.auto_compose = True
|
||||
# No specific experts — use default dev_team template
|
||||
result.specified_experts = self._load_default_template_members()
|
||||
result.auto_compose = False
|
||||
|
||||
return result
|
||||
|
||||
|
|
@ -170,3 +186,26 @@ class ExpertTeamRouter:
|
|||
configs[0].is_lead = True
|
||||
|
||||
return configs
|
||||
|
||||
def _load_default_template_members(self) -> list[str]:
|
||||
"""Load member list from the default dev_team template.
|
||||
|
||||
The dev_team template stores its member list in config.bound_skills
|
||||
(reusing the existing field to avoid schema changes).
|
||||
|
||||
Falls back to a hardcoded list if the template is not found.
|
||||
"""
|
||||
template = self._registry.get(self.DEFAULT_TEMPLATE)
|
||||
if template:
|
||||
members = template.config.bound_skills
|
||||
if members:
|
||||
return members[:MAX_EXPERTS]
|
||||
|
||||
# Fallback default members
|
||||
return [
|
||||
"tech_lead",
|
||||
"frontend_engineer",
|
||||
"backend_engineer",
|
||||
"qa_engineer",
|
||||
"code_reviewer",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -139,14 +139,21 @@ class TestExpertTeamRouterResolve:
|
|||
assert result.auto_compose is False
|
||||
assert result.match_method == "explicit_team"
|
||||
|
||||
def test_team_no_experts_auto_compose(self):
|
||||
"""@team 无指定专家时 auto_compose=True"""
|
||||
def test_team_no_experts_uses_default_template(self):
|
||||
"""@team 无指定专家时使用默认 dev_team 模板"""
|
||||
router = ExpertTeamRouter()
|
||||
result = router.resolve("@team 分析数据")
|
||||
assert result.matched is True
|
||||
assert result.team_mode is True
|
||||
assert result.specified_experts == []
|
||||
assert result.auto_compose is True
|
||||
# 默认 dev_team 模板包含 5 个成员
|
||||
assert result.specified_experts == [
|
||||
"tech_lead",
|
||||
"frontend_engineer",
|
||||
"backend_engineer",
|
||||
"qa_engineer",
|
||||
"code_reviewer",
|
||||
]
|
||||
assert result.auto_compose is False
|
||||
|
||||
def test_team_with_expert_extracts_task(self):
|
||||
"""@team:analyst 正确提取任务内容"""
|
||||
|
|
@ -176,7 +183,9 @@ class TestExpertTeamRouterResolve:
|
|||
router = ExpertTeamRouter()
|
||||
result = router.resolve("@team")
|
||||
assert result.task_content == "@team"
|
||||
assert result.auto_compose is True
|
||||
# 无指定专家时使用默认模板
|
||||
assert result.auto_compose is False
|
||||
assert len(result.specified_experts) == 5
|
||||
|
||||
def test_resolve_strips_leading_whitespace(self):
|
||||
"""resolve() 对前导空白做 strip()"""
|
||||
|
|
@ -342,3 +351,132 @@ class TestExpertTeamRouterCanHandle:
|
|||
|
||||
# 内容与任何模板名/描述都不匹配,但有模板存在 → auto-compose 可用
|
||||
assert router.can_handle("完全无关的内容 xyz123") is True
|
||||
|
||||
|
||||
# ── ExpertTeamRouter 团队模板展开测试 (U7) ─────────────────
|
||||
|
||||
|
||||
class TestExpertTeamRouterTemplateExpansion:
|
||||
"""ExpertTeamRouter 团队模板展开测试 — @team:dev_team 自动展开为成员列表"""
|
||||
|
||||
def test_team_template_expands_to_members(self):
|
||||
"""@team:dev_team 展开为 dev_team 模板的 bound_skills 成员列表"""
|
||||
registry = ExpertTemplateRegistry()
|
||||
# 注册 dev_team 模板(bound_skills 存储成员列表)
|
||||
registry.register(
|
||||
_make_template(
|
||||
"dev_team",
|
||||
persona="编程团队",
|
||||
bound_skills=["tech_lead", "frontend_engineer", "backend_engineer"],
|
||||
)
|
||||
)
|
||||
router = ExpertTeamRouter(template_registry=registry)
|
||||
|
||||
result = router.resolve("@team:dev_team 实现用户登录功能")
|
||||
assert result.matched is True
|
||||
assert result.team_mode is True
|
||||
assert result.specified_experts == ["tech_lead", "frontend_engineer", "backend_engineer"]
|
||||
assert result.auto_compose is False
|
||||
assert result.match_method == "explicit_team"
|
||||
|
||||
def test_team_template_respects_max_experts_limit(self):
|
||||
"""团队模板展开时遵守 MAX_EXPERTS 上限"""
|
||||
registry = ExpertTemplateRegistry()
|
||||
# 注册一个 bound_skills 超过 MAX_EXPERTS 的模板
|
||||
registry.register(
|
||||
_make_template(
|
||||
"dev_team",
|
||||
persona="编程团队",
|
||||
bound_skills=[f"expert{i}" for i in range(15)],
|
||||
)
|
||||
)
|
||||
router = ExpertTeamRouter(template_registry=registry)
|
||||
|
||||
result = router.resolve("@team:dev_team 任务")
|
||||
from agentkit.experts.router import MAX_EXPERTS
|
||||
|
||||
assert len(result.specified_experts) == MAX_EXPERTS
|
||||
|
||||
def test_team_template_not_found_falls_back_to_default_members(self):
|
||||
"""@team:unknown_template 时未知模板名作为普通专家处理"""
|
||||
registry = ExpertTemplateRegistry()
|
||||
router = ExpertTeamRouter(template_registry=registry)
|
||||
|
||||
result = router.resolve("@team:unknown_template 任务")
|
||||
# 未知模板名(无 bound_skills)→ 作为普通专家名处理
|
||||
assert result.specified_experts == ["unknown_template"]
|
||||
|
||||
def test_no_experts_uses_default_dev_team_template(self):
|
||||
"""@team 无指定专家时从 dev_team 模板加载成员"""
|
||||
registry = ExpertTemplateRegistry()
|
||||
registry.register(
|
||||
_make_template(
|
||||
"dev_team",
|
||||
persona="编程团队",
|
||||
bound_skills=["tech_lead", "frontend_engineer", "backend_engineer"],
|
||||
)
|
||||
)
|
||||
router = ExpertTeamRouter(template_registry=registry)
|
||||
|
||||
result = router.resolve("@team 实现功能")
|
||||
assert result.specified_experts == ["tech_lead", "frontend_engineer", "backend_engineer"]
|
||||
assert result.auto_compose is False
|
||||
|
||||
def test_no_experts_no_dev_team_template_uses_hardcoded_fallback(self):
|
||||
"""无 dev_team 模板时使用硬编码 fallback 列表"""
|
||||
registry = ExpertTemplateRegistry()
|
||||
router = ExpertTeamRouter(template_registry=registry)
|
||||
|
||||
result = router.resolve("@team 任务")
|
||||
assert result.specified_experts == [
|
||||
"tech_lead",
|
||||
"frontend_engineer",
|
||||
"backend_engineer",
|
||||
"qa_engineer",
|
||||
"code_reviewer",
|
||||
]
|
||||
|
||||
def test_team_template_with_empty_bound_skills_treated_as_expert_name(self):
|
||||
"""bound_skills 为空的模板名作为普通专家处理"""
|
||||
registry = ExpertTemplateRegistry()
|
||||
registry.register(_make_template("empty_team", persona="空团队", bound_skills=[]))
|
||||
router = ExpertTeamRouter(template_registry=registry)
|
||||
|
||||
result = router.resolve("@team:empty_team 任务")
|
||||
# bound_skills 为空 → 不展开,作为普通专家名
|
||||
assert result.specified_experts == ["empty_team"]
|
||||
|
||||
def test_default_template_class_attribute(self):
|
||||
"""DEFAULT_TEMPLATE 类属性为 'dev_team'"""
|
||||
from agentkit.experts.router import ExpertTeamRouter as Router
|
||||
|
||||
assert Router.DEFAULT_TEMPLATE == "dev_team"
|
||||
|
||||
def test_load_default_template_members_with_template(self):
|
||||
"""_load_default_template_members 从 dev_team 模板加载"""
|
||||
registry = ExpertTemplateRegistry()
|
||||
registry.register(
|
||||
_make_template(
|
||||
"dev_team",
|
||||
persona="编程团队",
|
||||
bound_skills=["a", "b", "c"],
|
||||
)
|
||||
)
|
||||
router = ExpertTeamRouter(template_registry=registry)
|
||||
|
||||
members = router._load_default_template_members()
|
||||
assert members == ["a", "b", "c"]
|
||||
|
||||
def test_load_default_template_members_without_template(self):
|
||||
"""_load_default_template_members 无模板时返回硬编码列表"""
|
||||
registry = ExpertTemplateRegistry()
|
||||
router = ExpertTeamRouter(template_registry=registry)
|
||||
|
||||
members = router._load_default_template_members()
|
||||
assert members == [
|
||||
"tech_lead",
|
||||
"frontend_engineer",
|
||||
"backend_engineer",
|
||||
"qa_engineer",
|
||||
"code_reviewer",
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in New Issue