diff --git a/server/routes/company.js b/server/routes/company.js index 1b2dcca..b9a1f68 100644 --- a/server/routes/company.js +++ b/server/routes/company.js @@ -102,6 +102,126 @@ router.get('/templates', (req, res) => { res.json(companyService.listTemplates()); }); +// POST /api/company/templates — 创建新模板 Agent +router.post('/templates', async (req, res) => { + try { + const { id, name, type, description } = req.body; + if (!id || !name) return res.status(400).json({ error: 'id 和 name 为必填项' }); + if (!/^[a-z0-9][a-z0-9-]*$/.test(id)) { + return res.status(400).json({ error: 'id 格式无效 (只允许小写字母、数字、-)' }); + } + + const path = require('path'); + const fs = require('fs'); + const TEMPLATES_DIR = path.resolve(__dirname, '../../data/system/templates'); + const templateDir = path.join(TEMPLATES_DIR, id); + + if (fs.existsSync(templateDir)) { + return res.status(409).json({ error: `模板 "${id}" 已存在` }); + } + + // 使用 AgentScaffold 创建标准骨架 + const { AgentScaffold } = await import('../../packages/agent-core/AgentScaffold.js'); + const scaffold = new AgentScaffold(TEMPLATES_DIR); + await scaffold.create({ + id, + type: type || 'skill', + name: name, + parentId: null, + }); + + // 如果有 description,追加到 config.json + if (description) { + const configPath = path.join(templateDir, 'config.json'); + const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')); + config.description = description; + fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8'); + } + + console.log(`[Template] ✅ 新模板已创建: ${id} (${type || 'skill'})`); + res.status(201).json({ success: true, id, name, type: type || 'skill' }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// POST /api/company/templates/:templateId/generate — 调用 Agent Creator 生成定义文件 +router.post('/templates/:templateId/generate', async (req, res) => { + try { + const { templateId } = req.params; + const { description } = req.body; + const path = require('path'); + const fs = require('fs'); + const config = require('../config'); + const TEMPLATES_DIR = path.resolve(__dirname, '../../data/system/templates'); + const templateDir = path.join(TEMPLATES_DIR, templateId); + + if (!fs.existsSync(templateDir)) { + return res.status(404).json({ error: `模板 "${templateId}" 不存在` }); + } + + const PORT = config.port || 3000; + const prompt = `请你为模板 Agent "${templateId}" 编写完整的定义文件。 +模板位于 \`../../data/system/templates/${templateId}\` 目录。 +该 Agent 的描述/用途: ${description || '通用执行型 Agent'} +请使用 Write 工具为其补充完整的 agent.md, identity.md, soul.md, user.md, tools.md 定义文件。 +写完后用 ls 验证目录结构。务必使用 Write 工具写入,不要使用 Edit 工具。`; + + // 异步调用 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: prompt, + conversationId: `tpl-gen-${templateId}-${Date.now().toString(36)}`, + }), + }).catch(e => console.error('[Template] Agent Creator 请求失败:', e.message)); + + console.log(`[Template] 🤖 已触发 Agent Creator 为 ${templateId} 生成定义`); + res.json({ success: true, message: `已触发 Agent Creator 为 "${templateId}" 生成定义文件`, status: 'generating' }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// POST /api/company/templates/:templateId/train — 调用 Innovator 训练模板 +router.post('/templates/:templateId/train', async (req, res) => { + try { + const { templateId } = req.params; + const path = require('path'); + const fs = require('fs'); + const config = require('../config'); + const TEMPLATES_DIR = path.resolve(__dirname, '../../data/system/templates'); + const templateDir = path.join(TEMPLATES_DIR, templateId); + + if (!fs.existsSync(templateDir)) { + return res.status(404).json({ error: `模板 "${templateId}" 不存在` }); + } + + const PORT = config.port || 3000; + const prompt = `请你对模板 Agent "${templateId}" 进行评估和训练。 +模板位于 \`../../data/system/templates/${templateId}\` 目录。 +请使用 FDE pipeline 对该模板的技能进行评测和进化训练,目标: Benchmark Score ≥ 95。 +训练过程包括 Hunt(验证数据), Digest(知识消化), Evolver(技能进化) 等。 +请立即开始评估和训练,训练完成后请汇报最终分数。`; + + // 异步调用 innovator + fetch(`http://127.0.0.1:${PORT}/api/agent/innovator/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + message: prompt, + conversationId: `tpl-train-${templateId}-${Date.now().toString(36)}`, + }), + }).catch(e => console.error('[Template] Innovator 请求失败:', e.message)); + + console.log(`[Template] 🎓 已触发 Innovator 训练 ${templateId}`); + res.json({ success: true, message: `已触发 Innovator 训练 "${templateId}"`, status: 'training' }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + // GET /api/company/templates/:templateId/files router.get('/templates/:templateId/files', (req, res) => { try { @@ -189,6 +309,35 @@ router.get('/templates/:templateId/files/content', (req, res) => { res.status(500).json({ error: err.message }); } }); + +// PUT /api/company/templates/:templateId/files/content — 保存模板文件内容 +router.put('/templates/:templateId/files/content', (req, res) => { + try { + const path = require('path'); + const fs = require('fs'); + const DATA_DIR = path.resolve(__dirname, '../../data/system/templates'); + const filePath = req.body.path; + + if (!filePath) return res.status(400).json({ error: 'path 参数为必填' }); + + const absPath = path.resolve(DATA_DIR, req.params.templateId, filePath); + const safeBoundary = path.resolve(DATA_DIR, req.params.templateId); + if (!absPath.startsWith(safeBoundary)) { + return res.status(403).json({ error: '路径越界' }); + } + + // 确保目录存在 + const dir = path.dirname(absPath); + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + + fs.writeFileSync(absPath, req.body.content || '', 'utf-8'); + console.log(`[Template] 💾 已保存模板文件 ${req.params.templateId}/${filePath}`); + res.json({ success: true, path: filePath }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + // GET /api/company/:companyId router.get('/:companyId', (req, res) => { const company = companyService.get(req.params.companyId); diff --git a/web/src/views/TemplateAgents.vue b/web/src/views/TemplateAgents.vue index 332dc0a..0cfbe75 100644 --- a/web/src/views/TemplateAgents.vue +++ b/web/src/views/TemplateAgents.vue @@ -6,8 +6,30 @@