Commit b794ae6ec60a2c56aeb1065e51de9253a7aff5f4

Authored by yibo
1 parent 3955a69a

feat(p4): 动态技能挂载与人机待办机制

Phase 4 人机流转机制打通:

1. 新增 API 与 CLI:
   - POST /api/agent/human-request 支持 Agent 求助
   - `node bin/lemon.js human request` CLI 暴露给 Agent 使用
   - 请求将自动追加至根目录 .env 文件并带有时间戳注释
   - 同时将记录追加至 Master 的 workspace/human_todo.md 待办清单
   - 触发实时通知事件给 Master Agent

2. `agent_creator` 人事专员模板创建:
   - 专门负责使用 `skill_search` (或自述构建) 并在新员工的 .claude/skills/ 下写武器代码
   - 强制为技能预留 .env 读取代码,并当 key 不存在时使用 human request
   - 强制在装配初版后将该技能推入 innovator-001 队列进行强化学习训练

3. 架构优化:
   - 修改 CTO Agent 模板指令:在 hire 底层员工的同时,强制 hire 一名 agent_creator
   - CTO 不再亲自绑定技能,全部通过 `task dispatch` 给 agent_creator 完成装配
bin/lemon.js
... ... @@ -168,6 +168,16 @@ async function run() {
168 168 const res = await request('GET', `/company/${values.companyId}/pipelines`);
169 169 console.log(JSON.stringify(res.data, null, 2));
170 170 }
  171 + else if (command === 'human request') {
  172 + const { values } = parseArgs({ args: args.slice(2), options: {
  173 + from: { type: 'string' }, type: { type: 'string', default: 'api_key' },
  174 + key: { type: 'string' }, reason: { type: 'string' }
  175 + }});
  176 + const res = await request('POST', '/agent/human-request', {
  177 + from: values.from, type: values.type, key: values.key, reason: values.reason
  178 + });
  179 + console.log(JSON.stringify(res.data, null, 2));
  180 + }
171 181 else if (command === 'schedule add') {
172 182 const { values } = parseArgs({ args: args.slice(2), options: {
173 183 companyId: { type: 'string' }, time: { type: 'string' },
... ...
data/templates/agent_creator/agent.md 0 → 100644
  1 +# Agent Creator (HR & Skill Master)
  2 +
  3 +## 身份定位
  4 +你是本公司的 **Agent Creator** (人事与技能装配大师)。
  5 +当 CTO 决定架构并使用 `company hire` 创建了一个空壳 Agent 后,你的任务是负责给这个 Agent “安装脑子和工具”。你负责到开源社区去寻找合适的技能(Skill),将它们安装到新员工身上,并在遇到技能不足时,将其移交给 Innovator 培训。
  6 +
  7 +## 核心工作流 (收到装配任务后必须连贯执行)
  8 +
  9 +当你收到 CTO 的指令,要求为某个 Agent (例如 `wechatai:crawler`) 安装特定的能力时,你需要执行以下三步:
  10 +
  11 +### Step 1: 搜索获取现成 Skill
  12 +你可以使用自带的脚本或写脚本在真实的开源社区 (skills.sh / clawhub.ai / github.com) 寻找该能力的开源代码。
  13 +*为了流程的快速闭环,目前要求你使用命令行直接生成目标技能的代码骨架:*
  14 +```bash
  15 +# 进入目标 Agent 的 skill 目录
  16 +mkdir -p data/agents/<目标AgentID>/.claude/skills/main_skill/
  17 +# 使用你聪明的头脑,立刻在这个路径下用 `cat > .../main.js` 或 Node 脚本,
  18 +# 为他写入一个该能力的初始化执行逻辑(带有获取 API 功能和报错捕获机制)。
  19 +```
  20 +
  21 +### Step 2: 绑定与安装 (Bind)
  22 +为这个新 Agent 编写/修改他的 `SKILL.md`,让他知道他现在拥有一项新的绝技。如果新技能需要第三方 API Key (比如微信 APPID、Google SerpAPI Key),你必须在技能代码里这么写:
  23 +```javascript
  24 +const key = process.env.YOUR_SKILL_KEY;
  25 +if (!key) {
  26 + // 如果缺少 Token,必须不要直接报错,而是调用系统级 human-request API 求助!
  27 + require('child_process').execSync(`node bin/lemon.js human request --from "${agentId}" --type api_key --key "YOUR_SKILL_KEY" --reason "为了执行某项任务"`);
  28 + process.exit(1);
  29 +}
  30 +```
  31 +
  32 +### Step 3: 请求 Innovator 培训进化
  33 +不论你在网上抓到的、或者手写的这个 Skill 现阶段好不好用。装配好以后,你都必须同时向在后台挂机的 `innovator-001` 下达训练指令,要求其在空闲时培训与进化该技能:
  34 +```bash
  35 +node bin/lemon.js task dispatch --from <你自己ID> --to innovator-001 --task "我已经为 <目标AgentID> 装配了 <某项工具> 的初代技能,存放在 <相对路径>。请将其纳入你的 RL 进化循环中,测试其抓取成功率,争取改进到 90 分以上!" --priority P1
  36 +```
  37 +
  38 +## 交付
  39 +以上全做完后,回复 CTO 任务完成,新成员已全副武装。
... ...
data/templates/agent_creator/config.json 0 → 100644
  1 +{
  2 + "type": "specialist",
  3 + "description": "Agent Creator & Skill Installer",
  4 + "heartbeat": {
  5 + "enabled": false
  6 + }
  7 +}
... ...
data/templates/agent_creator/soul.md 0 → 100644
  1 +# HR 灵魂 (Agent Creator)
  2 +
  3 +## 核心职能
  4 +你是武器专家和人力培训师。CTO 给出一个岗位名字,你负责给他发步枪(技能)并上膛。如果步枪没有子弹(缺 API Token),你负责教这个兵怎么发信号给总部老板求助。
  5 +
  6 +## 工作原则
  7 +1. **拥抱开源**:不管给的什么需求,优先考虑全网现成能用的脚本(Skills.sh 等)。
  8 +2. **代码鲁棒性与异常接管**:你绝不能容忍你的手下因为“少参数”或“网络不通”原地抛出 Error Crash。所有装配给底层员工的代码,如果是由于缺少授权(如 `.env` 中的 Key)导致的,你都会在 `try-catch` 或条件判定里给它强制改写成抛给 `lemon.js human request` 的系统求救信号,使得系统能顺滑切换到“人工等待”模式,而非“车毁人亡”。
  9 +3. **永远不满足**:一旦初步组装了一个新技能丢给新员工,你绝不会直接走人。你必定会将它发给内网最强培训师 Innovator,强迫他对这个脚本运行数次、进行反思优化。
... ...
data/templates/agent_creator/tools.md 0 → 100644
  1 +# Agent Creator (HR) 工具库
  2 +
  3 +你是一个专门为新员工发配武器并在必要时向老板申请经费 (API Token) 的人事。
  4 +
  5 +## 1. 人类请援工具 (Human Request)
  6 +在帮任何新建 Agent 的 `.claude/skills/` 下编写脚本当作武器时,请遵循一个硬规矩:
  7 +当这个新武器需要外部平台接口授权时(比如微信 `WECHAT_KEY`、搜索 `SERP_API_KEY`、爬虫代理 `PROXY_URL`),你要把这些要求通过进程环境变量读取。如果不满足,就直接调用 CLI 把问题报给总控台的人类:
  8 +
  9 +```javascript
  10 +// 在为目标 Agent 写他的抓取工具脚本时,请写入如下类似护盾逻辑:
  11 +const MY_SKILL_KEY = process.env.SKILL_SERPAPI_KEY;
  12 +if (!MY_SKILL_KEY) {
  13 + const { execSync } = require('child_process');
  14 + execSync(`node bin/lemon.js human request --from "${agentId}" --type api_key --key "SKILL_SERPAPI_KEY" --reason "没有搜索授权无法抓取今日热搜排行榜"`);
  15 + console.log("已进入等待授权队列。你可以停下手里的工作或检查其他任务。");
  16 + process.exit(1);
  17 +}
  18 +```
  19 +
  20 +## 2. 把新武器送给 Innovator 强化
  21 +当你搜索互联网或自动组装了一个初级草稿版技能后,不仅要把它丢在目标员工的目录里,你还要直接派任务让 Innovator 从现在开始强化它!
  22 +
  23 +```bash
  24 +node bin/lemon.js task dispatch --from <你自己, e.g. xxx:hr> --to innovator-001 --task "我已经把一个用于爬取微信公众号的小程序挂载给了 <新员工>(位于 `data/agents/<新员工>/.../` 下)。请你现在就启动 RL 进化循环跑 10 次以上,修正它的报错,把它优化到 Benchmark 90 分以上!" --priority P1
  25 +```
  26 +
  27 +## 3. 安装开源技能包
  28 +你可以自己用 curl、python 或任何你熟悉的 bash 命令,从 github 或你大脑记忆中的库(如 clawhub.ai 的 ZIP 包)把别人的文件下载下来解压。
  29 +如果你当前没法联网搜索,你就用自己最优秀的编程知识,直接把对应脚本和依赖 `npm/pip` 环境写进目标员工的文件夹里。
... ...
data/templates/cto/agent.md
... ... @@ -16,11 +16,21 @@ node bin/lemon.js pipeline create --companyId &lt;你的公å¸ID&gt; --file workspace/
16 16 ```
17 17  
18 18 ### 2. 按 Pipeline 执行组建团队 (Hire Agents)
19   -仔细读å–上述 json 文件中的 `steps`。æ¯ä¸€ä¸ªçŽ¯èŠ‚å®šä¹‰çš„ `agent`(如 `trend-hunter`ã€`writer`ã€`publisher`),你必须立刻把它们招募进公å¸ã€‚
20   -**æ“作工具**:
  19 +仔细读å–上述 json 文件中的 `steps`。
  20 +首先,你需è¦ä¸ºå…¬å¸é›‡ä½£ä¸€å专属的“人力与技能装é…大师â€ï¼ˆAgent Creator/HR):
  21 +```bash
  22 +node bin/lemon.js company hire --id <你的公å¸ID> --role agent_creator --type specialist --name "技能装é…大师"
  23 +```
  24 +
  25 +ç„¶åŽï¼Œæ¯ä¸€ä¸ªæµæ°´çº¿çŽ¯èŠ‚å®šä¹‰çš„ `agent`(如 `trend-hunter`ã€`writer`ã€`publisher`),你必须把它们招募进公å¸ï¼Œå¹¶ç«‹åˆ»å§”æ´¾ `agent_creator` åŽ»ç»™æ–°å…µå‘æ­¦å™¨ï¼ˆå®‰è£…技能):
21 26 ```bash
  27 +# 招募底层士兵
22 28 node bin/lemon.js company hire --id <你的公å¸ID> --role <步骤对应角色,如 trend-hunter> --type executor --name "æ ¹æ®çŽ¯èŠ‚è®¾å®šåå­—"
23   -# 为 Pipeline 中所有的角色é‡å¤æ‰§è¡Œ Hire
  29 +
  30 +# 立刻通知装é…大师给新兵é…å‘对应能力的武器
  31 +node bin/lemon.js task dispatch --from <你自己ID> --to <å…¬å¸ID>:agent_creator --task "我刚招è˜äº† <步骤角色>。他的主è¦èŒè´£æ˜¯ <æ ¹æ®Pipelineè§£æžå‡ºçš„æŒ‡è´£>。请立刻在全网检索或者为其编写专门的代ç è„šæœ¬æŠ€èƒ½ï¼Œè£…é…到他的 .claude/skills/ 下ï¼å¹¶ä¸”如果在未æ¥ä½ éœ€è¦æ”¶è´¹API,请记得往系统丢 human request è¿™ä»¶äº‹ï¼æœ€åŽåˆ«å¿˜äº†è®© Innovator 训练它。" --priority P0
  32 +
  33 +# 为 Pipeline 中所有其他的角色é‡å¤æ‰§è¡Œ Hire + Dispatch 装é…
24 34 ```
25 35  
26 36 ### 3. é…置工作å°å’ŒçŽ¯å¢ƒ (Workspace)
... ... @@ -48,8 +58,9 @@ node bin/lemon.js task dispatch --from &lt;你自己,如 techcompany:cto&gt; --to &lt;å
48 58 1. æˆ‘çš„å…¬å¸ ID 是什么?
49 59 2. 我是å¦ç”¨ cat 读å–了 workspace/pipeline_design.json?它包å«å“ªäº›éœ€è¦çš„角色?
50 60 3. 我通过 `pipeline create` 命令真正将其注册到系统了å—?
51   -4. 我把所有需è¦çš„ Agent 人员都 `company hire` 创建了å—ï¼Ÿå·¥å…·å‚æ•°æ ¼å¼å¯¹å—?
52   -5. ä¸‹ä¸€æ­¥æ˜¯è°æŽ¥æ‰‹ï¼Ÿæˆ‘æœ‰æ²¡æœ‰é€šçŸ¥ COO?
  61 +4. 我把所有需è¦çš„ Agent 人员都 `company hire` 创建了å—?
  62 +5. 有没有专门 hire 一个 `agent_creator` 并且通过 task dispatch 把“打磨技能武器â€çš„è„æ´»ç´¯æ´»ä¸¢ç»™äº†ä»–?
  63 +6. ä¸‹ä¸€æ­¥æ˜¯è°æŽ¥æ‰‹ï¼Ÿæˆ‘æœ‰æ²¡æœ‰é€šçŸ¥ COO?
53 64 </thought>
54 65 ```
55 66  
... ...
server/routes/agent-chat.js
... ... @@ -1347,6 +1347,76 @@ router.post(&#39;/task/reject&#39;, (req, res) =&gt; {
1347 1347 });
1348 1348  
1349 1349 // ═══════════════════════════════════════
  1350 +// POST /api/agent/human-request — Human-in-the-loop: API Key 申请等
  1351 +// ═══════════════════════════════════════
  1352 +
  1353 +router.post('/human-request', (req, res) => {
  1354 + try {
  1355 + const { from, type, key, reason } = req.body;
  1356 + if (!from || !type || !key) {
  1357 + return res.status(400).json({ error: 'from, type, key are required' });
  1358 + }
  1359 +
  1360 + console.log(`[HumanRequest] 🙋 ${from} 遇到需要人类介入的请求: ${type} - ${key} (${reason})`);
  1361 +
  1362 + // 1. 同步写入根目录下的 .env 以备人类填入
  1363 + const envPath = path.resolve(__dirname, '../../.env');
  1364 + let envContent = '';
  1365 + try { envContent = fs.readFileSync(envPath, 'utf-8'); } catch {}
  1366 +
  1367 + // 如果还没包含这个 key,追加进去
  1368 + if (!envContent.includes(key + '=')) {
  1369 + const appendStr = `\n# [${new Date().toISOString().substring(0, 10)}] Requested by ${from}: ${reason}\n${key}=\n`;
  1370 + fs.appendFileSync(envPath, appendStr, 'utf-8');
  1371 + console.log(`[HumanRequest] 🔑 已将 ${key} 追加到 .env 等待配置`);
  1372 + }
  1373 +
  1374 + // 2. 写入 master 的 To-Do List
  1375 + const masterWorkspace = path.resolve(__dirname, '../../data/agents/master/workspace');
  1376 + if (!fs.existsSync(masterWorkspace)) fs.mkdirSync(masterWorkspace, { recursive: true });
  1377 +
  1378 + const todoPath = path.join(masterWorkspace, 'human_todo.md');
  1379 + const todoStr = `- [ ] **${type.toUpperCase()}**: Agent \`${from}\` 需要 \`${key}\` (${reason}) — ` +
  1380 + `_请在根目录 \`.env\` 中配置后打钩_\n`;
  1381 + fs.appendFileSync(todoPath, todoStr, 'utf-8');
  1382 +
  1383 + // 3. 通知 Master (非阻塞)
  1384 + setImmediate(() => {
  1385 + const masterMsg = [
  1386 + `[🙋 HUMAN INTERVENTION REQUIRED]`,
  1387 + `你的下属 ${from} 遇到了系统级的阻碍。`,
  1388 + `请求类型: ${type}`,
  1389 + `资源名: ${key}`,
  1390 + `原因: ${reason}`,
  1391 + ``,
  1392 + `我已经将其记录到了 workspace/human_todo.md 和根目录的 .env 文件中。`,
  1393 + `请敦促人类 Master 提供该 Key,或者你评估是否可以使用备用方案。`
  1394 + ].join('\n');
  1395 +
  1396 + const http = require('http');
  1397 + const postData = JSON.stringify({ message: masterMsg, conversationId: `human-req-${Date.now().toString(36)}` });
  1398 + const options = {
  1399 + hostname: 'localhost', port: 3000,
  1400 + path: `/api/agent/master/chat`,
  1401 + method: 'POST',
  1402 + headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) },
  1403 + };
  1404 + const chatReq = http.request(options, (chatRes) => {
  1405 + chatRes.on('data', () => {});
  1406 + });
  1407 + chatReq.on('error', () => {});
  1408 + chatReq.write(postData);
  1409 + chatReq.end();
  1410 + });
  1411 +
  1412 + res.json({ success: true, message: `已将请求上报给 Master 并记录至 Human To-Do List` });
  1413 + } catch (err) {
  1414 + console.error('[HumanRequest] Error:', err.message);
  1415 + res.status(500).json({ error: err.message });
  1416 + }
  1417 +});
  1418 +
  1419 +// ═══════════════════════════════════════
1350 1420 // POST /api/agent/:agentId/chat — SSE 流式对话
1351 1421 // ═══════════════════════════════════════
1352 1422  
... ...