/** * train-skill-tool.js — Master 训练新 Skill 工具 * * 让 Master Agent 可以触发完整的 Hunt→Digest→Evolve/SkillCreate 流水线。 * 训练产物写入 data/trained_skills/user_{id}/{skillName}/,按用户隔离。 * * 使用场景: * - 用户说"训练一个新 skill" * - 用户说"使用 innovator 帮我创建 xxx skill" * - 用户说"帮我重训/改进 xxx skill" */ const fs = require('fs'); const path = require('path'); /** * 创建 train_skill 工具定义 */ function createTrainSkillTool() { // 延迟创建 JobManager(模块加载时 .data 目录可能尚未就绪) let _jobManager = null; function getJobManager() { if (!_jobManager) { const { JobManager } = require('../../services/job-manager'); _jobManager = new JobManager({ storePath: path.resolve(__dirname, '../../../.data/jobs-train.json'), }); } return _jobManager; } return { name: 'train_skill', description: `训练一个新 Skill 或重训一个已有 Skill。触发完整的 Hunt→Digest→Evolve 流水线: - Hunt: 猎取领域知识(网络研究、资料收集) - Digest: 消化提炼为评估基准(benchmark) - Evolve/SkillCreate: 基于知识库和基准生成高质量 SKILL.md 适用场景: - 用户说"训练/创建一个新 skill" - 用户说"使用 innovator 训练 xxx" - 用户说"帮我做一个新的 xxx skill" 注意:训练任务通常需要 5-10 分钟,此工具异步执行并返回 jobId。 用 list_retrain_jobs 工具可查看进度。`, parameters: { type: 'object', required: ['skillName', 'description'], properties: { skillName: { type: 'string', description: 'Skill 名称(英文 kebab-case 或 snake_case,如 genlogo-prompt、code_reviewer)', }, description: { type: 'string', description: 'Skill 的功能描述——它要做什么、用户输入什么、期望输出什么', }, instructions: { type: 'string', description: '用户的额外训练要求或补充说明(可选)', }, mode: { type: 'string', description: '"create"(新建 skill,默认)或 "retrain"(重训已有 skill)', }, }, }, async execute(params, context) { const { skillName, description, instructions = '', mode = 'create' } = params; const config = require('../../config'); const jobManager = getJobManager(); const userId = context.userId || 'anonymous'; if (mode === 'retrain') { return _executeRetrain(skillName, description, instructions, jobManager, config, userId); } return _executeCreate(skillName, description, instructions, jobManager, config, userId); }, }; } /** * 执行新 Skill 创建(Hunt→Digest→SkillCreate) */ async function _executeCreate(skillName, description, instructions, jobManager, config, userId) { const { InnovatorService } = require('../../services/innovator-service'); const { initTrainedSkillDir } = require('../../services/skill-dir-manager'); const skillDb = require('../../services/skill-db'); // 1. 初始化用户训练 skill 目录 const skillDir = initTrainedSkillDir(skillName, userId); console.log(`[TrainSkill] 初始化目录: ${skillDir}`); // 2. 创建 Job const job = jobManager.createJob({ type: 'train-create', skillName, description, status: 'queued', progress: '准备启动训练流水线...', userId: String(userId), }); // 3. 注册到 DB(如果不存在) try { skillDb.registerSkill({ skillName, description, domain: skillName, scope: '', keywords: skillName.split(/[-_]/).join(','), directory: skillDir, userId: String(userId), storageType: 'trained', }); } catch (err) { if (err.message.includes('already exists')) { // 更新已有条目的 directory 指向 try { skillDb.updateSkill(skillName, { directory: skillDir, userId: String(userId), storageType: 'trained' }); } catch {} } else { console.warn(`[TrainSkill] registerSkill 警告: ${err.message}`); } } // 4. 异步执行 pipeline(不阻塞 Master 回复) _runTrainPipeline(job, skillName, description, instructions, jobManager, config, userId, skillDir).catch(err => { console.error(`[TrainSkill] Pipeline 失败: ${err.message}`); jobManager.updateJob(job.jobId, { status: 'failed', error: err.message, progress: `训练失败: ${err.message}`, }); }); return { status: 'started', jobId: job.jobId, skillName, userId: String(userId), skillDir, pipeline: 'Hunt → Digest → SkillCreate', message: `已启动 "${skillName}" 的训练流水线。训练通常需要 5-10 分钟。`, tip: '使用 list_retrain_jobs 工具可查看进度。', }; } /** * 执行已有 Skill 重训(ReHunt→ReDigest→Evolve) */ async function _executeRetrain(skillName, description, instructions, jobManager, config, userId) { const { InnovatorService } = require('../../services/innovator-service'); const { RetrainPipeline } = require('../../services/retrain-pipeline'); const { resolveTrainedSkillDir } = require('../../services/skill-dir-manager'); const skillsDir = config.skillsCodeDir; const innovator = new InnovatorService({ skillsDir, skillsDataDir: config.skillsDataDir, trainedSkillsDir: config.trainedSkillsDir, }); const skill = innovator.registry.getSkill(skillName); if (!skill) { return { status: 'error', message: `Skill "${skillName}" 不存在。请先用 mode="create" 创建。`, }; } // 创建 Job const job = jobManager.createJob({ type: 'retrain', skillName, description: instructions || description, status: 'queued', progress: '准备启动重训流水线...', userId: String(userId), }); innovator.registry.updateSkill(skillName, { status: 'training' }); // 异步执行 retrain pipeline const pipeline = new RetrainPipeline({ job, skill, innovator, jobManager, instructions: instructions || description, userId, }); pipeline.run().catch(err => { console.error(`[TrainSkill] Retrain 失败: ${err.message}`); jobManager.updateJob(job.jobId, { status: 'failed', error: err.message, progress: `重训失败: ${err.message}`, }); try { innovator.registry.updateSkill(skillName, { status: 'enable' }); } catch {} }); return { status: 'started', jobId: job.jobId, skillName, userId: String(userId), pipeline: 'ReHunt → ReDigest → Evolve', message: `已启动 "${skillName}" 的重训流水线 (目标 ≥95 分)。`, tip: '使用 list_retrain_jobs 工具可查看进度。', }; } /** * 新 Skill 训练流水线(RL 闭环版本) * * 使用 EvolutionLoop 实现 Perceive → Plan → Execute → Evaluate → Reflect 循环, * 替代原有的线性 Hunt→Digest→SkillCreate 直通流程。 * * 产物写入 data/trained_skills/user_{id}/{skillName}/ 目录 */ async function _runTrainPipeline(job, skillName, description, instructions, jobManager, config, userId, skillDir) { const { ConversationLogger } = require('../../services/conversation-logger'); const taskMgr = require('../../services/skill-task-manager'); const { randomUUID } = require('crypto'); const { loadConfigFromSkills } = require('../core/skill-config-loader'); const AgentManager = require('../core/agent-manager'); const { InnovatorService } = require('../../services/innovator-service'); const { EvolutionLoop } = require('../../services/evolution-loop'); const skillsDir = config.skillsCodeDir; const conversationsDir = config.conversationsDir || path.resolve(__dirname, '../../../conversations'); // 日志按用户隔离 const userConvDir = path.join(conversationsDir, `user-${userId}`); const conversationId = `train-${job.jobId}`; const messageId = `msg-${Date.now()}-${randomUUID().substring(0, 8)}`; const logger = new ConversationLogger(userConvDir, conversationId, messageId, { userId }); taskMgr.initTask(skillDir, { skillName, description, source: 'master-train', jobId: job.jobId, userId: String(userId), pipelineType: 'evolution', }); logger.ensureMeta(`[Train] ${skillName}: ${description.substring(0, 80)}`); logger.logUserMessage(`[Train] 训练新 Skill (RL Loop): ${skillName}\n${description}\n${instructions}`); // 加载 Agent 配置 — 使用系统 skills 目录(l3-hunt 等 agent 在 data/skills/ 下) const backends = JSON.parse(fs.readFileSync( path.join(path.resolve(__dirname, '../../config'), 'backends.json'), 'utf-8' )); const agentConfig = loadConfigFromSkills(skillsDir, backends); // Agent 的 workDir 设为用户训练目录的父目录(user_{id}/), // 这样 agent 引用 {skillName}/hunt/knowhow.md 就指向正确路径 const userTrainDir = path.dirname(skillDir); const manager = new AgentManager(agentConfig, userTrainDir); try { // ── EvolutionLoop: RL 闭环 ── const loop = new EvolutionLoop({ skillName, description, skillDir, instructions, manager, jobManager, job, logger, taskMgr, maxIterations: 5, goalScore: 90, onData: (agent, chunk) => { if (chunk?.trim()) logger.logStreamChunk(agent, chunk); }, }); jobManager.updateJob(job.jobId, { status: 'evolving', progress: '🔄 RL 进化循环启动...', skillName, conversationId, }); const result = await loop.run(); // ── Publish ── const innovator = new InnovatorService({ skillsDir, skillsDataDir: config.skillsDataDir, trainedSkillsDir: config.trainedSkillsDir, }); innovator.autoRegisterNewSkills(); logger.logResult({ type: 'train_skill', jobId: job.jobId, skillName, userId: String(userId), skillDir, evolutionResult: { status: result.status, finalScore: result.finalScore, bestScore: result.bestScore, iterations: result.iterations, }, }); const statusEmoji = result.status === 'goal_achieved' ? '✅' : '⚠️'; const statusText = result.status === 'goal_achieved' ? `达标 (${result.finalScore}分)` : result.status === 'stagnant' ? `停滞 (最佳 ${result.bestScore}分)` : `迭代用尽 (最佳 ${result.bestScore}分)`; jobManager.updateJob(job.jobId, { status: 'completed', progress: `${statusEmoji} 训练完成:${skillName} — ${statusText}`, conversationId, result: { skillName, userId: String(userId), skillDir, output: `Skill 训练完成 (RL Loop: ${result.iterations}轮, ${statusText})`, evolutionStatus: result.status, finalScore: result.finalScore, bestScore: result.bestScore, iterations: result.iterations, }, }); try { await manager.closeAllSessions(); } catch {} console.log(`[TrainSkill] ${statusEmoji} ${skillName} 训练完成 → ${skillDir} (${statusText})`); } catch (err) { logger.logError(err.message); const nextStep = taskMgr.getNextPendingStep(skillDir); if (nextStep) taskMgr.updateStep(skillDir, nextStep, 'failed', err.message); try { await manager.closeAllSessions(); } catch {} throw err; } } module.exports = { createTrainSkillTool };