From 5b508ce8f6e010f218d4a00472d40d3bf6bd6533 Mon Sep 17 00:00:00 2001 From: Yi Bo Date: Thu, 26 Mar 2026 17:00:18 +0800 Subject: [PATCH] feat: Automate full hiring orchestration (Creator -> Skills -> Innovator) --- server/routes/company.js | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ web/src/views/CompanyDashboard.vue | 26 ++++++++++++-------------- 2 files changed, 86 insertions(+), 14 deletions(-) diff --git a/server/routes/company.js b/server/routes/company.js index 90478c8..a00e080 100644 --- a/server/routes/company.js +++ b/server/routes/company.js @@ -324,6 +324,80 @@ router.post('/:companyId/hire', async (req, res) => { type, name: name || role, description, customAgent, customSoul, customUser, }); + // ── 核心逻辑扩展:自动补充定义 + 搜索注入 3 个初始 Skill + Innovator 训练 ── + if (result.status === 'training' || result.needsTraining) { + (async () => { + try { + console.log(`[Hire] 🚀 自动流水线启动: 为新员工 ${role} 安排 [定义 → 技能注入 → 训练]`); + const fs = require('fs'); + const path = require('path'); + const config = require('../config'); + const skillDb = require('../services/skill-db'); + + const namespacedId = `${req.params.companyId}:${role}`; + const agentDir = result.agentDir || path.resolve(__dirname, '../../data', req.params.companyId, 'agents', role); + const PORT = config.port || 3000; + + // 1. 发起 Agent-Creator 编写骨架定义 (异步不等待) + fetch(`http://127.0.0.1:${PORT}/api/agent/agent-creator/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + message: `新员工 ${namespacedId} 已由脚手架创建空白目录在 \`../${req.params.companyId}/agents/${role}\`。工作需求: ${description || name || role}。\n请你按照标准模板,使用 Write 工具为该员工补充完整的定义文件(agent.md, identity.md, soul.md, user.md等)并使用工具 ls 验证。务必要使用 Write 写文件,不要使用 Edit 工具。写完后请答复。`, + conversationId: `create-${req.params.companyId}-${role}-${Date.now().toString(36)}` + }) + }).catch(e => console.error('[Hire] Agent-Creator 请求错误:', e.message)); + + // 2. 去网上/Arena搜索并注入 3 个基础 Skill + const skills = skillDb.searchSkills(role, { limit: 3 }); + const skillsDir = path.join(agentDir, 'skills'); + if (skills && skills.length > 0) { + if (!fs.existsSync(skillsDir)) fs.mkdirSync(skillsDir, { recursive: true }); + + const copyDir = (src, dest) => { + fs.mkdirSync(dest, { recursive: true }); + const entries = fs.readdirSync(src, { withFileTypes: true }); + for (const e of entries) { + if (e.isDirectory()) copyDir(path.join(src, e.name), path.join(dest, e.name)); + else fs.copyFileSync(path.join(src, e.name), path.join(dest, e.name)); + } + }; + + for (const skill of skills) { + if (skill.directory && fs.existsSync(skill.directory)) { + const targetPath = path.join(skillsDir, skill.skillName); + if (!fs.existsSync(targetPath)) { + try { + copyDir(skill.directory, targetPath); + console.log(`[Hire] 💉 成功为其注入基础技能: ${skill.skillName}`); + } catch (e) { + console.error(`[Hire] 注入技能 ${skill.skillName} 失败:`, e.message); + } + } + } + } + } else { + console.log(`[Hire] ⚠️ 网上/Arena 未找到该角色的匹配 Skill,将交由 Innovator 凭空生成`); + } + + // 3. 延时 5 秒触发 Innovator 进行评估与训练 (等 creator 写入完毕) + setTimeout(() => { + fetch(`http://127.0.0.1:${PORT}/api/agent/innovator/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + message: `公司 ${req.params.companyId} 的员工 ${role} 骨架和基础技能已注入完毕。请你使用 FDE pipeline 对该 Agent ${namespacedId} (特别是它的技能)进行评测和进化训练,直到 Benchmark 达标 (Score ≥ 95)。训练过程包括 Hunt(验证数据), Digest, Evolver 等。请立即开始评估和训练,并保持向我汇报。`, + conversationId: `train-${req.params.companyId}-${role}-${Date.now().toString(36)}` + }) + }).catch(e => console.error('[Hire] Innovator 请求错误:', e.message)); + }, 5000); + + } catch (err) { + console.error(`[Hire Orchestration] 自动流水线编排异常: ${err.message}`); + } + })(); + } + // ── P0 FIX: 动态心跳挂载 ── // 招聘成功后,立即为这个新 Agent 注册心跳定时器(如果它的 config.json 里 heartbeat.enabled = true) try { diff --git a/web/src/views/CompanyDashboard.vue b/web/src/views/CompanyDashboard.vue index e677a2a..e759e5b 100644 --- a/web/src/views/CompanyDashboard.vue +++ b/web/src/views/CompanyDashboard.vue @@ -159,17 +159,15 @@ v-if="!agent.heartbeatActive" class="btn-agent-start" @click="startAgentHeartbeat(agent.id)" - title="启动心跳" - >▶ + >▶ 启动 - - - + >⏹ 停止 + + + @@ -962,7 +960,7 @@ function closeDropdown() { .agent-status-table { display: flex; flex-direction: column; gap: 0; } .table-header, .table-row { display: grid; - grid-template-columns: 2fr 1fr 1fr 1fr 1fr 1fr; + grid-template-columns: 2fr 1fr 1fr 1fr 1fr 2.5fr; gap: 8px; align-items: center; padding: 10px 16px; } @@ -1029,14 +1027,14 @@ function closeDropdown() { .agent-name-link:hover { color: var(--accent-light); text-decoration: underline; } /* Action Button Group */ -.action-group { display: flex; gap: 4px; align-items: center; } +.action-group { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; } .action-group button { - width: 28px; height: 28px; border-radius: 6px; border: 1px solid var(--border-primary); - background: var(--bg-primary); cursor: pointer; font-size: 13px; - display: flex; align-items: center; justify-content: center; transition: all 0.15s; - padding: 0; + padding: 4px 8px; border-radius: 6px; border: 1px solid var(--border-primary); + background: var(--bg-primary); cursor: pointer; font-size: 11px; font-weight: 600; + display: flex; align-items: center; gap: 2px; transition: all 0.15s; + white-space: nowrap; } -.action-group button:hover { background: var(--bg-surface-hover); border-color: var(--border-active); transform: scale(1.1); } +.action-group button:hover { background: var(--bg-surface-hover); border-color: var(--border-active); } .btn-action-chat { color: var(--accent-light); } .btn-action-skills { color: var(--blue-text, #60a5fa); } -- libgit2 0.21.4