feat: implement Phase 4 - Multi-Agent Swarm + Skill Discovery

Phase 4a: Agent Swarm Collaboration Framework (agent-swarm.ts)
- AgentSwarm class with configurable coordinator + specialist agents
- Three collaboration modes: Sequential (chain), Parallel (concurrent), Debate (multi-round)
- Auto task decomposition based on specialist capabilities
- Debate consensus detection with keyword similarity heuristic
- Rule-based result aggregation with structured markdown output
- Specialist management (add/update/remove) and config updates
- History persistence to localStorage (last 25 tasks)
- Memory integration: saves task completion as lesson memories

Phase 4b: Skill Discovery Engine (skill-discovery.ts)
- SkillDiscoveryEngine with 12 built-in skill definitions from skills/ directory
- Multi-signal search: name, description, triggers, capabilities, category matching
- Conversation-based skill recommendation via topic extraction (CN + EN patterns)
- Memory-augmented confidence scoring for suggestions
- Skill registration, install status toggle, category filtering
- localStorage persistence for skill index and suggestion cache

Phase 4c: chatStore Integration
- dispatchSwarmTask(description, style): creates and executes swarm task, adds result as message
- searchSkills(query): exposes skill search to UI layer

Tests: 317 passing across 13 test files (43 new for swarm + skills)
- AgentSwarm: createTask, sequential/parallel/debate execution, history, specialist mgmt
- SkillDiscovery: search, suggest, register, persist, categories

Refs: ZCLAW_AGENT_INTELLIGENCE_EVOLUTION.md updated - all 4 phases complete
This commit is contained in:
iven
2026-03-15 22:44:18 +08:00
parent 04ddf94123
commit 137f1a32fa
5 changed files with 1555 additions and 7 deletions

View File

@@ -0,0 +1,468 @@
/**
* Skill Discovery - Agent-driven skill search, recommendation, and management
*
* Enables ZCLAW agents to:
* - Search available skills by keyword/capability
* - Recommend skills based on recent conversation patterns
* - Manage skill installation lifecycle (with user approval)
*
* Scans the local `skills/` directory for SKILL.md manifests and indexes them.
*
* Reference: ZCLAW_AGENT_INTELLIGENCE_EVOLUTION.md §6.5.2
*/
import { getMemoryManager } from './agent-memory';
// === Types ===
export interface SkillInfo {
id: string;
name: string;
description: string;
triggers: string[];
capabilities: string[];
toolDeps: string[];
installed: boolean;
category?: string;
path?: string;
}
export interface SkillSuggestion {
skill: SkillInfo;
reason: string;
confidence: number; // 0-1
matchedPatterns: string[];
}
export interface SkillSearchResult {
query: string;
results: SkillInfo[];
totalAvailable: number;
}
export interface ConversationContext {
role: string;
content: string;
}
// === Storage ===
const SKILL_INDEX_KEY = 'zclaw-skill-index';
const SKILL_SUGGESTIONS_KEY = 'zclaw-skill-suggestions';
// === Built-in Skill Registry ===
/**
* Pre-indexed skills from the skills/ directory.
* In production, this would be dynamically scanned from SKILL.md files.
* For Phase 4, we maintain a static registry that can be refreshed.
*/
const BUILT_IN_SKILLS: SkillInfo[] = [
{
id: 'code-review',
name: 'Code Review',
description: '审查代码、分析代码质量、提供改进建议',
triggers: ['审查代码', '代码审查', 'code review', 'PR review', '检查代码'],
capabilities: ['代码质量分析', '架构评估', '安全审计', '最佳实践检查'],
toolDeps: ['read', 'grep', 'glob'],
installed: true,
category: 'development',
},
{
id: 'frontend-developer',
name: 'Frontend Developer',
description: '前端开发专家,擅长 React/Vue/CSS/TypeScript',
triggers: ['前端开发', '页面开发', 'UI开发', 'React', 'Vue', 'CSS'],
capabilities: ['组件开发', '样式调整', '性能优化', '响应式设计'],
toolDeps: ['read', 'write', 'shell'],
installed: true,
category: 'development',
},
{
id: 'backend-architect',
name: 'Backend Architect',
description: '后端架构设计、API设计、数据库建模',
triggers: ['后端架构', 'API设计', '数据库设计', '系统架构', '微服务'],
capabilities: ['架构设计', 'API规范', '数据库建模', '性能优化'],
toolDeps: ['read', 'write', 'shell'],
installed: true,
category: 'development',
},
{
id: 'security-engineer',
name: 'Security Engineer',
description: '安全工程师,负责安全审计、漏洞检测、合规检查',
triggers: ['安全审计', '漏洞检测', '安全检查', 'security', '渗透测试'],
capabilities: ['漏洞扫描', '合规检查', '安全加固', '威胁建模'],
toolDeps: ['read', 'grep', 'shell'],
installed: true,
category: 'security',
},
{
id: 'data-analysis',
name: 'Data Analysis',
description: '数据分析、可视化、报告生成',
triggers: ['数据分析', '数据可视化', '报表', '统计', 'analytics'],
capabilities: ['数据清洗', '统计分析', '可视化图表', '报告生成'],
toolDeps: ['read', 'write', 'shell'],
installed: true,
category: 'analytics',
},
{
id: 'chinese-writing',
name: 'Chinese Writing',
description: '中文写作、文案创作、内容优化',
triggers: ['写文章', '文案', '写作', '中文创作', '内容优化'],
capabilities: ['文案创作', '文章润色', '标题优化', 'SEO写作'],
toolDeps: ['read', 'write'],
installed: true,
category: 'content',
},
{
id: 'devops-automator',
name: 'DevOps Automator',
description: 'CI/CD、Docker、K8s、自动化部署',
triggers: ['DevOps', 'CI/CD', 'Docker', '部署', '自动化', 'K8s'],
capabilities: ['CI/CD配置', '容器化', '自动化部署', '监控告警'],
toolDeps: ['shell', 'read', 'write'],
installed: true,
category: 'ops',
},
{
id: 'senior-pm',
name: 'Senior PM',
description: '项目管理、需求分析、迭代规划',
triggers: ['项目管理', '需求分析', '迭代规划', '产品设计', 'PRD'],
capabilities: ['需求拆解', '迭代排期', '风险评估', '文档撰写'],
toolDeps: ['read', 'write'],
installed: true,
category: 'management',
},
{
id: 'git',
name: 'Git Operations',
description: 'Git 版本控制操作、分支管理、冲突解决',
triggers: ['git', '版本控制', '分支', '合并', 'commit', 'merge'],
capabilities: ['分支管理', '冲突解决', 'rebase', 'cherry-pick'],
toolDeps: ['shell'],
installed: true,
category: 'development',
},
{
id: 'api-tester',
name: 'API Tester',
description: 'API 测试、接口调试、自动化测试脚本',
triggers: ['API测试', '接口测试', '接口调试', 'Postman', 'curl'],
capabilities: ['接口调试', '自动化测试', '性能测试', '断言验证'],
toolDeps: ['shell', 'read', 'write'],
installed: true,
category: 'testing',
},
{
id: 'finance-tracker',
name: 'Finance Tracker',
description: '财务追踪、预算管理、报表分析',
triggers: ['财务', '预算', '记账', '报销', '财务报表'],
capabilities: ['收支分析', '预算规划', '报表生成', '趋势预测'],
toolDeps: ['read', 'write'],
installed: true,
category: 'business',
},
{
id: 'social-media-strategist',
name: 'Social Media Strategist',
description: '社交媒体运营策略、内容规划、数据分析',
triggers: ['社交媒体', '运营', '小红书', '抖音', '微博', '内容运营'],
capabilities: ['内容策划', '发布排期', '数据分析', '竞品监控'],
toolDeps: ['read', 'write'],
installed: true,
category: 'marketing',
},
];
// === Skill Discovery Engine ===
export class SkillDiscoveryEngine {
private skills: SkillInfo[] = [];
private suggestionHistory: SkillSuggestion[] = [];
constructor() {
this.loadIndex();
this.loadSuggestions();
if (this.skills.length === 0) {
this.skills = [...BUILT_IN_SKILLS];
this.saveIndex();
}
}
// === Search ===
/**
* Search skills by keyword. Matches against name, description, triggers, capabilities.
*/
searchSkills(query: string): SkillSearchResult {
const q = query.toLowerCase().trim();
if (!q) {
return { query, results: [...this.skills], totalAvailable: this.skills.length };
}
const tokens = q.split(/[\s,;.!?。,;!?]+/).filter(t => t.length > 0);
const scored = this.skills.map(skill => {
let score = 0;
// Name match (highest weight)
if (skill.name.toLowerCase().includes(q)) score += 10;
// Description match
if (skill.description.toLowerCase().includes(q)) score += 5;
// Trigger match (exact or partial)
for (const trigger of skill.triggers) {
const tLower = trigger.toLowerCase();
if (tLower === q) { score += 15; break; }
if (tLower.includes(q) || q.includes(tLower)) score += 8;
}
// Capability match
for (const cap of skill.capabilities) {
if (cap.toLowerCase().includes(q)) score += 4;
}
// Token-level matching
for (const token of tokens) {
if (skill.name.toLowerCase().includes(token)) score += 2;
if (skill.description.toLowerCase().includes(token)) score += 1;
for (const trigger of skill.triggers) {
if (trigger.toLowerCase().includes(token)) score += 3;
}
}
// Category match
if (skill.category && skill.category.toLowerCase().includes(q)) score += 3;
return { skill, score };
});
const results = scored
.filter(s => s.score > 0)
.sort((a, b) => b.score - a.score)
.map(s => s.skill);
return { query, results, totalAvailable: this.skills.length };
}
// === Recommendation ===
/**
* Suggest skills based on recent conversation content and memory patterns.
*/
async suggestSkills(
recentConversations: ConversationContext[],
agentId: string,
limit: number = 5
): Promise<SkillSuggestion[]> {
const suggestions: SkillSuggestion[] = [];
// 1. Extract key topics from conversations
const topics = this.extractTopics(recentConversations);
// 2. Match topics against skill triggers and capabilities
for (const skill of this.skills) {
const matchedPatterns: string[] = [];
let confidence = 0;
for (const topic of topics) {
const topicLower = topic.toLowerCase();
// Check triggers
for (const trigger of skill.triggers) {
if (trigger.toLowerCase().includes(topicLower) || topicLower.includes(trigger.toLowerCase())) {
matchedPatterns.push(`话题"${topic}"匹配触发词"${trigger}"`);
confidence += 0.3;
}
}
// Check capabilities
for (const cap of skill.capabilities) {
if (cap.toLowerCase().includes(topicLower)) {
matchedPatterns.push(`话题"${topic}"匹配能力"${cap}"`);
confidence += 0.2;
}
}
}
// 3. Check memory patterns for recurring needs
try {
const memories = await getMemoryManager().search(skill.name, {
agentId,
limit: 5,
minImportance: 3,
});
if (memories.length > 0) {
matchedPatterns.push(`记忆中有${memories.length}条相关记录`);
confidence += memories.length * 0.1;
}
} catch { /* non-critical */ }
if (matchedPatterns.length > 0 && confidence > 0) {
suggestions.push({
skill,
reason: matchedPatterns.slice(0, 3).join(''),
confidence: Math.min(confidence, 1),
matchedPatterns,
});
}
}
// Sort by confidence
const sorted = suggestions
.sort((a, b) => b.confidence - a.confidence)
.slice(0, limit);
// Cache suggestions
this.suggestionHistory = sorted;
this.saveSuggestions();
return sorted;
}
// === Skill Management ===
/**
* Get all available skills.
*/
getAllSkills(): SkillInfo[] {
return [...this.skills];
}
/**
* Get skills by category.
*/
getSkillsByCategory(category: string): SkillInfo[] {
return this.skills.filter(s => s.category === category);
}
/**
* Get unique categories.
*/
getCategories(): string[] {
return [...new Set(this.skills.map(s => s.category).filter(Boolean))] as string[];
}
/**
* Register a new skill (e.g., from a SKILL.md file scan).
*/
registerSkill(skill: SkillInfo): void {
const existing = this.skills.findIndex(s => s.id === skill.id);
if (existing >= 0) {
this.skills[existing] = skill;
} else {
this.skills.push(skill);
}
this.saveIndex();
}
/**
* Mark a skill as installed/uninstalled.
*/
setSkillInstalled(skillId: string, installed: boolean): void {
const skill = this.skills.find(s => s.id === skillId);
if (skill) {
skill.installed = installed;
this.saveIndex();
}
}
/**
* Get last cached suggestions.
*/
getLastSuggestions(): SkillSuggestion[] {
return [...this.suggestionHistory];
}
// === Topic Extraction ===
private extractTopics(conversations: ConversationContext[]): string[] {
const topics = new Set<string>();
const patterns = [
// Chinese task patterns
/帮我(.{2,15})/g,
/我想(.{2,15})/g,
/如何(.{2,15})/g,
/怎么(.{2,15})/g,
/需要(.{2,15})/g,
/分析(.{2,15})/g,
/写一个(.{2,15})/g,
/做一个(.{2,15})/g,
// English task patterns
/(?:help me |I need to |how to |please )(.{3,30})/gi,
// Technical terms
/(?:React|Vue|Python|Docker|K8s|API|SQL|CSS|TypeScript|Node|Git)/gi,
// Domain keywords
/(?:部署|测试|审查|优化|设计|开发|分析|报告|运营|安全)/g,
];
for (const msg of conversations.filter(m => m.role === 'user')) {
for (const pattern of patterns) {
const matches = msg.content.matchAll(pattern);
for (const match of matches) {
const topic = (match[1] || match[0]).trim();
if (topic.length >= 2 && topic.length <= 30) {
topics.add(topic);
}
}
}
}
return [...topics].slice(0, 20);
}
// === Persistence ===
private loadIndex(): void {
try {
const raw = localStorage.getItem(SKILL_INDEX_KEY);
if (raw) this.skills = JSON.parse(raw);
} catch {
this.skills = [];
}
}
private saveIndex(): void {
try {
localStorage.setItem(SKILL_INDEX_KEY, JSON.stringify(this.skills));
} catch { /* silent */ }
}
private loadSuggestions(): void {
try {
const raw = localStorage.getItem(SKILL_SUGGESTIONS_KEY);
if (raw) this.suggestionHistory = JSON.parse(raw);
} catch {
this.suggestionHistory = [];
}
}
private saveSuggestions(): void {
try {
localStorage.setItem(SKILL_SUGGESTIONS_KEY, JSON.stringify(this.suggestionHistory));
} catch { /* silent */ }
}
}
// === Singleton ===
let _instance: SkillDiscoveryEngine | null = null;
export function getSkillDiscovery(): SkillDiscoveryEngine {
if (!_instance) {
_instance = new SkillDiscoveryEngine();
}
return _instance;
}
export function resetSkillDiscovery(): void {
_instance = null;
}