diff --git a/data/system/agents/innovator/todo.md b/data/system/agents/innovator/todo.md index 3822d58..73654bf 100644 --- a/data/system/agents/innovator/todo.md +++ b/data/system/agents/innovator/todo.md @@ -10,3 +10,4 @@ | T-mn6x648k | 【重新训练 satire-news-scraper Agent — 硬性标准 90 分】

上次训练只达到 80 分就结束了,因为旧标准是 75 分及格。现在及格线已提高到 90 分(10 分制 = 9.0)。请重新执行 FDE Phase 3 EVOLVING 训练。

## 输入资料(已有,无需重新 HUNT/DIGEST)
- 知识体系: workspace/digester/satire-news-scraper/knowhow.md
- 评分标准: workspace/digester/satire-news-scraper/benchmark.md(pass_score 已更新为 9.0)
- 训练集: workspace/digester/satire-news-scraper/train_dataset.json
- 上次 Skill: workspace/evolver/satire-news-scraper/skill_satire_scraper.md(v1.2.0, 80 分)

## 训练要求
1. 以上次 skill_satire_scraper.md 为起点,继续迭代优化
2. 硬性及格线: 总分 >= 9.0(10 分制)或 >= 90(100 分制),低于此分数不能标记为完成
3. 最大迭代次数: 10 轮
4. 每轮分析弱维度,针对性修改 Skill 定义

## 关键:训练完成后必须部署到 Agent
训练达标后,必须执行以下部署步骤:
1. 将最终的 Skill 内容追加到 agents/satire-news-scraper/agent.md 的末尾(作为新的 ## Skill 章节)
2. 更新 agents/satire-news-scraper/config.json 中 status 为 ready
3. 更新 agents/satire-news-scraper/plan.md 记录训练完成

## 产出
- workspace/evolver/satire-news-scraper/skill_satire_scraper.md(更新版)
- workspace/evolver/satire-news-scraper/evolution_log.json(追加记录)
- agents/satire-news-scraper/agent.md(部署 Skill)
- workspace/innovator/satire_news_retrain_report.md(重训报告) | ✅ 完成 | master | P0 | 2026-03-26 03:35 | - | | T-mn7phcxp | 我已为公司招聘了以下新员工,需要你启动 RL 进化循环进行强化训练:

1. trend-hunter(热点抓取专家)- 负责抓取微博/抖音/小红书/快手热点
2. topic-selector(选题筛选专家)- 负责从热点中筛选最有价值的选题
3. researcher(深度研究专家)- 负责收集背景资料和网友观点
4. screenwriter(互动剧编剧)- 负责创作分支剧情和多个结局

请为每个员工优化技能到 90 分以上,特别是 screenwriter 需要掌握分支剧情创作能力。publisher 和 data-analyst 已存在可直接使用。 | 🔄 进行中 | abc:hr | P1 | 2026-03-26 16:47 | 2026-03-27 | | T-mn7pws58 | 我已为 abc 公司完成了执行层员工的批量招聘和技能装配。

招聘结果:
1. trend-hunter (热点抓取) - 已存在
2. topic-selector (选题决策) - 已存在
3. researcher (深度研究) - 已存在
4. screenwriter (剧本整理) - 已存在
5. publisher (内容上传) - 新招聘,已安装 content_publisher 技能
6. data-analyst (数据反馈) - 新招聘,已安装 data_feedback 技能

请对以下员工启动 RL 进化循环进行强化训练:
- publisher: 需要将 content_publisher 技能优化到 90 分以上
- data-analyst: 需要将 data_feedback 技能优化到 90 分以上
- 其他现有员工: 如有需要可一并优化

目标是让全员通过 Innovator Benchmark 评分 (≥90分),确保流水线各环节质量达标。 | 🔄 进行中 | abc:hr | P1 | 2026-03-26 16:59 | 2026-03-27 | +| T-mn96tzls | 我已为 ABC 公司招聘了 5 名新员工: content-writer, publisher, trend-hunter, data-analyst, community-manager。请启动 RL 进化循环,对这些新员工进行训练,优化到 90 分以上! | ⬚ 未开始 | abc:hr | P1 | 2026-03-27 17:41 | - | diff --git a/data/system/agents/master/agent.md b/data/system/agents/master/agent.md index 912e0ae..d6ad0d0 100644 --- a/data/system/agents/master/agent.md +++ b/data/system/agents/master/agent.md @@ -76,33 +76,33 @@ node bin/lemon.js task dispatch --from master --to agent-creator --task "创建ä ## 正确的操作流方式(范例) -### 🚀 创建公司与招聘员工(从建壳到组建团队) -我们的框架原则是:**公司创建时只是一个“空壳”**。系统中不硬编码自动部署 CEO、CTO 等骨干。 -为了避免由下级 Agent(如 CEO/HR)去执行招聘造成的巨大资源浪费,**所有从 Template 库中挑选并雇佣 Agent 到公司团队的工作,必须由 Master Agent 根据老板(用户)的指令亲力亲为完成。** - -当用户要求创建一家新公司时,你必须: -1. **构建公司空壳**:调用 `company create` 创建只包含框架的空壳。 -2. **主动招聘核心团队**:根据用户的业务需求预判,或者根据用户的明确指令,使用 `company hire` 从模板库中招募需要的业务骨干(如 CEO, CTO, COO, HR,乃至具体的业务专家)。 -3. **下达战略说明**:通过 `task dispatch` 给新招募的 CEO 发送战略启动令,告诉他公司的业务目标,即可开始工作。注意:**不要**要求 CEO 去招募员工,明确告诉 CEO 团队配置由 Master/老板 负责。 - -**例如,当全权委托你创建并配置一个公司时,你可以执行:** -```bash -# 1. 一键创建公司空壳 -node bin/lemon.js company create --id <公司ID> --name "<公司名称>" --desc "<业务描述,越详细越好>" - -# 2. 为公司手动招聘团队(从 Template 库雇佣) -node bin/lemon.js company hire --id <公司ID> --role ceo --type cxo --name "首席执行官" -node bin/lemon.js company hire --id <公司ID> --role cto --type cxo --name "首席技术官" -# (...按需雇佣其他 Agent) - -# 3. 委派 CEO 开始工作(一定要加上正确的公司前缀) -node bin/lemon.js task dispatch --from master --to <公司ID>:ceo --task "你已被任命为公司 CEO,本公司主营:<业务描述>。请立刻制定公司战略和 SOP。注意:人员招聘由 Master 集中管理,你只需专注于业务流程的梳理与工作流分解。" --priority P0 +### 🚀 创建公司与招聘员工(严格按指令办事) +我们的框架原则是:**公司创建时只是一个“空壳”,不带任何员工**。 +所有的招聘工作(即从 Template 库中挑选并雇佣 Agent 到公司团队)全部由你(Master Agent)来负责执行,**下级 Agent(如 CEO/HR)绝对不要越权去招聘,以免造成资源浪费**。 + +**关键规则:严格分离“建壳”与“招聘”** +1. **当用户让你“创建公司”时**: + - 你**只能**执行 `company create` 命令建一个空壳。 + - **绝对不要**自作主张去帮公司招聘 CEO、CTO 或任何员工!建完壳后,向老板汇报创建成功,并静待老板的下一步指示(等待老板指定哪些 Agent 需要被雇佣)。 +2. **当用户明确让你“招聘某人”或“为某家公司部署团队”时**: + - 老板下达指令后,你使用 `company hire` 根据用户的指令将特定的 Agent 模板招募到这家公司中。 + - 如果用户明确叫你发派任务给该员工,你再执行 `task dispatch`。 + +**示例1:只建公司(不再自动招聘)** +```shell +用户指令:"帮我建一家叫ABC的测试公司" +# 你只需要执行: +node bin/lemon.js company create --id abc --name "ABC公司" --desc "测试公司" +# 汇报:已创建ABC公司,目前为空壳。请问需要从模板库招聘哪些员工? ``` -**⚠️ 招聘与组队的原则:** -- 所有的 `hire` 操作本质上是从 `Template` 库复制指定的模板到具体公司中,必须由你(Master)来包办。 -- 如果用户只是说"帮我把 xxx 招到 yyy 公司",你直接调用一次 `company hire` 即可。 -- 如果用户让你全权组建特定业务线,你应该**自主决定**需要哪些模板,并直接批量执行 `hire`,不要反复追问。 +**示例2:人工下达指定招聘指令** +```shell +用户指令:"给ABC公司招聘一个统帅(CEO)" +# 你执行: +node bin/lemon.js company hire --id abc --role ceo --type cxo --name "首席执行官" +# 注意:招聘本质上是由你从 Template 模板库把对应的能力包复刻给这家公司。 +``` ``` 用户: 让下级干完活 ✅ 正确回复: diff --git a/data/system/agents/master/memory/memory.md b/data/system/agents/master/memory/memory.md index f1561bd..57413a0 100644 --- a/data/system/agents/master/memory/memory.md +++ b/data/system/agents/master/memory/memory.md @@ -25,3 +25,4 @@ - [2026-03-27] hi → 不是。我目前使用的是 **MiniMax-M2.1** 模型(见系统环境信息)。 - [2026-03-27] hi → 抱歉,我无法清空对话日志 — 这属于 Claude Code 客户端层面的操作,超出了我的能力范围。 - [2026-03-27] 你帮我创建一个叫做 ABC 的公司 → ✅ **ABC Inc. 公司已创建完成!** +- [2026-03-27] [TASK_CALLBACK] → 收到 ABC Inc. CEO 的任务完成汇报。让我先检查公司整体进度,然后继续推进。 diff --git a/data/system/agents/master/memory/reflections/reflect-1774633257923.json b/data/system/agents/master/memory/reflections/reflect-1774633257923.json new file mode 100644 index 0000000..405a88b --- /dev/null +++ b/data/system/agents/master/memory/reflections/reflect-1774633257923.json @@ -0,0 +1,13 @@ +{ + "timestamp": "2026-03-27T17:40:57.923Z", + "convId": "callback-T-mn96qkam", + "task": "[TASK_CALLBACK]\n执行者: abc:ceo\n状态:", + "status": "completed", + "toolCount": 6, + "durationSec": 36, + "patterns": [ + "完成了 6 步工具调用的复杂任务 (36秒)" + ], + "lessons": [], + "plan": null +} \ No newline at end of file diff --git a/data/system/agents/master/memory/short-term/session-1774633257922.md b/data/system/agents/master/memory/short-term/session-1774633257922.md new file mode 100644 index 0000000..7704a22 --- /dev/null +++ b/data/system/agents/master/memory/short-term/session-1774633257922.md @@ -0,0 +1,29 @@ +# Session 摘要 + +- **时间**: 2026-03-27 17:40:57 +- **对话ID**: callback-T-mn96qkam +- **状态**: completed +- **工具调用**: 6 次 +- **耗时**: 36秒 + +## 任务 +[TASK_CALLBACK] +执行者: abc:ceo +状态: + +## 结果 +收到 ABC Inc. CEO 的任务完成汇报。让我先检查公司整体进度,然后继续推进。 + + +根据检查结果,**ABC Inc. 运营状态正常**: + +| 角色 | 状态 | 任务进度 | +|------|------|----------| +| CEO (abc:ceo) | ✅ 已完成 | T-mn96qkam - 战略制定 | +| CTO (abc:cto) | ✅ 已完成 | T-mn96sf8m - FDE 流水线架构 | +| HR (abc:hr) | ⬚ 未开始 | T-mn96sj2z - 招聘计划 | +| COO (abc:coo) | ⬚ 未开始 | T-mn96sj1c - 运营体系 | + +CEO 已完成公司战略制定和任务分发,CTO 任务已完结。HR 和 COO 的任务待执行中。 + +我将按照定时巡检(每5分钟)继续监控 ABC Inc. 的进度。 diff --git a/data/system/agents/master/tools.md b/data/system/agents/master/tools.md index 1de709d..9c0c230 100644 --- a/data/system/agents/master/tools.md +++ b/data/system/agents/master/tools.md @@ -9,7 +9,7 @@ node bin/lemon.js company create --id --name --desc ``` 例如: `node bin/lemon.js company create --id ainews --name "AI News Inc." --desc "专注AI领域的自动化新闻采编与分发公司"` -**❗ 重要:此命令仅创建公司空壳和底层元 Agent,不会自动创建 CEO 等业务员工!你必须在创建后使用 company hire 命令手动招募 CEO。** +**❗ 重要:此命令仅创建公司空壳。创建完成后立即向老板汇报建壳成功,绝对不要自作主张去招聘任何人!必须等待老板的进一步指令。** ### 2. hire_agent_for_company — 为公司招聘 Agent ```bash @@ -82,6 +82,7 @@ cat data/lemonv/agents/ceo/todo.md - **文件读写编辑器 (Editor)**: 可以直接查看 (`cat`) 和修改/替换项目中任何 Node.js 源码、Vue 代码或 Markdown 定义文件。 ## 工作流程 -1. **常规业务指令(创建公司)** → `company create` (建壳) → `company hire --role ceo` (招聘统帅) → `task dispatch --to :ceo` (下达组织裂变和战略目标指令) -2. **常规业务指令(给现有公司下任务)** → `task dispatch --to <公司ID>:ceo` -3. **老板汇报系统异常/Bug** → 立刻切换为系统工程师:搜索 → 修复源码 → 反馈 +1. **常规业务指令(创建公司)** → `company create` (建壳) → 向老板汇报等待发号施令。 +2. **常规业务指令(人工下达组建指令)** → `company hire` 逐个把模板招聘入岗 → `task dispatch --to :ceo` (下达组织裂变和战略目标指令)。 +3. **常规业务指令(给现有公司下任务)** → `task dispatch --to <公司ID>:ceo`。 +4. **老板汇报系统异常/Bug** → 立刻切换为系统工程师:搜索 → 修复源码 → 反馈。 diff --git a/server/routes/agent-chat.js b/server/routes/agent-chat.js index 295a46b..3425690 100644 --- a/server/routes/agent-chat.js +++ b/server/routes/agent-chat.js @@ -743,12 +743,84 @@ router.post('/:agentId/memory/daily-report', (req, res) => { router.get('/:agentId/info', (req, res) => { const agentDir = getAgentDir(req.params.agentId); if (!fs.existsSync(agentDir)) return res.status(404).json({ error: 'Agent not found' }); - const config = loadAgentConfig(agentDir); const identity = loadIdentity(agentDir); - res.json({ id: config?.id || req.params.agentId, ...identity, type: config?.type, status: config?.status, - bootstrapFiles: ['agent.md','soul.md','identity.md','user.md','tools.md','heartbeat.md'].filter(f => loadFile(agentDir, f).trim().length > 0), + const config = loadAgentConfig(agentDir); + const identity = loadIdentity(agentDir); + + // Read cron settings + let cron = []; + try { + const cronPath = path.join(agentDir, 'cron.json'); + if (fs.existsSync(cronPath)) { + cron = JSON.parse(fs.readFileSync(cronPath, 'utf8')); + } + } catch (e) { + console.error('Failed to parse cron.json:', e); + } + + res.json({ + id: config?.id || req.params.agentId, + ...identity, + type: config?.type, + status: config?.status, + heartbeat: config?.heartbeat || { enabled: false, intervalMinutes: 15 }, + cron: cron, + bootstrapFiles: ['agent.md','soul.md','identity.md','user.md','tools.md','heartbeat.md', 'cron.json'].filter(f => loadFile(agentDir, f).trim().length > 0), }); }); +router.put('/:agentId/heartbeat', (req, res) => { + try { + const agentDir = getAgentDir(req.params.agentId); + if (!fs.existsSync(agentDir)) return res.status(404).json({ error: 'Agent not found' }); + + const configPath = path.join(agentDir, 'config.json'); + let config = {}; + if (fs.existsSync(configPath)) { + config = JSON.parse(fs.readFileSync(configPath, 'utf8')); + } + + config.heartbeat = { + enabled: req.body.enabled || false, + intervalMinutes: req.body.intervalMinutes || 15 + }; + + fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8'); + + // Update heartbeat service if running + const heartbeatService = require('../services/heartbeat-service'); + // Using namespacedId logic from UI (`system:master` is skipped, only `company:role` or pure system agent like `master`) + const agentId = req.params.agentId; + if (config.heartbeat.enabled) { + heartbeatService._setupAgentHeartbeat(agentId); + } else if (heartbeatService.timers.has(agentId)) { + clearInterval(heartbeatService.timers.get(agentId)); + heartbeatService.timers.delete(agentId); + } + + res.json({ success: true, heartbeat: config.heartbeat }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.put('/:agentId/cron', (req, res) => { + try { + const agentDir = getAgentDir(req.params.agentId); + if (!fs.existsSync(agentDir)) return res.status(404).json({ error: 'Agent not found' }); + + const cronPath = path.join(agentDir, 'cron.json'); + const cronList = Array.isArray(req.body.cron) ? req.body.cron : []; + + fs.writeFileSync(cronPath, JSON.stringify(cronList, null, 2), 'utf8'); + + // Check if the heartbeat service picks up the cron changes automatically. + // Generally cron jobs execute within the heartbeat mechanism. + res.json({ success: true, cron: cronList }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + /** 列出对话 */ router.get('/:agentId/conversations', (req, res) => { res.json(listConversations(req.params.agentId)); diff --git a/web/src/views/AgentChat.vue b/web/src/views/AgentChat.vue index 7d01b1f..c8ec093 100644 --- a/web/src/views/AgentChat.vue +++ b/web/src/views/AgentChat.vue @@ -194,6 +194,38 @@ {{ f }} + +
+
心跳设置 (Heartbeat)
+
+ +
+ 间隔时间: + 分钟 +
+
+
+ +
+
定时任务 (Cron Jobs)
+
+
无定时任务
+
+
+ + +
+ + +
+
+
+
统计
@@ -384,6 +416,41 @@ async function loadAgentInfo() { } catch (e) { console.error('Agent info error:', e) } } +async function updateHeartbeat() { + if (!agentId.value || !agentInfo.value.heartbeat) return + try { + await fetch(`/api/agent/${agentId.value}/heartbeat`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(agentInfo.value.heartbeat) + }) + } catch (e) { console.error('Heartbeat update error:', e) } +} + +async function updateCron() { + if (!agentId.value || !agentInfo.value.cron) return + try { + await fetch(`/api/agent/${agentId.value}/cron`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ cron: agentInfo.value.cron }) + }) + } catch (e) { console.error('Cron update error:', e) } +} + +function addCron() { + if (!agentInfo.value.cron) agentInfo.value.cron = [] + agentInfo.value.cron.push({ "schedule": "0 8 * * *", "task": "新提醒任务", "enabled": true }) + updateCron() +} + +function removeCron(index) { + if (confirm('确认删除该定时任务?')) { + agentInfo.value.cron.splice(index, 1) + updateCron() + } +} + async function loadConversations() { if (!agentId.value) return try {