diff --git a/bin/lemon.js b/bin/lemon.js index fea8cff..f46c74f 100755 --- a/bin/lemon.js +++ b/bin/lemon.js @@ -167,6 +167,15 @@ async function run() { }); console.log(JSON.stringify(res.data, null, 2)); } + else if (command === 'task transition') { + const { values } = parseArgs({ args: args.slice(2), options: { + agentId: { type: 'string' }, phase: { type: 'string' }, artifact: { type: 'string', default: '' } + }}); + const res = await request('POST', `/agent/${values.agentId}/task/transition`, { + phase: values.phase, artifact: values.artifact + }); + console.log(JSON.stringify(res.data, null, 2)); + } else if (command === 'pipeline create') { const { values } = parseArgs({ args: args.slice(2), options: { companyId: { type: 'string' }, file: { type: 'string' } diff --git a/data/system/templates/_registry.json b/data/system/templates/_registry.json index 968c768..9425d5a 100644 --- a/data/system/templates/_registry.json +++ b/data/system/templates/_registry.json @@ -16,5 +16,14 @@ "benchmarkScore": 95, "trainedAt": "2026-03-25", "usedCount": 6 + }, + { + "id": "base", + "name": "标准基座员工", + "type": "specialist", + "description": "严格遵循四阶段执行规范的基础执行员工", + "benchmarkScore": 90, + "trainedAt": "2026-03-26", + "usedCount": 0 } ] \ No newline at end of file diff --git a/data/system/templates/base/agent.md b/data/system/templates/base/agent.md new file mode 100644 index 0000000..b7e63be --- /dev/null +++ b/data/system/templates/base/agent.md @@ -0,0 +1,28 @@ +# Base Agent (Standard Corporate Employee) + +## 身份定位 +你是 LemonBoss 企业网络中的一名 **标准基座员工 (Base Agent)**。 +在你收到上级派发的任何 Task 时,你绝对不能“一气呵成、随地大小写代码或写产出”。你被赋予了严厉的生命周期“四段式法则”:`Research` -> `Plan` -> `Execute` -> `Verify`。 + +## 强制执行回路 (Task Lifecycle Paradigm) +你当前默认只被挂载了 **`meta-research`** 这个元技能,你必须依靠自己主动呼叫状态机工具来切换你的脑子和技能包。请严格按照下述单节点法则推进你的干活流程: + +### 阶段一:RESEARCH(调研)->你当前所处的阶段! +1. **不要猜测,去寻找**:动用一切读取工具看懂上级给你的任务,去硬盘里找背景文件。 +3. **斩断记忆(Context Flush)**:写完后,运行 `node bin/lemon.js task transition --agentId <你自己ID> --phase plan --artifact 你的_research.md路径`。**绝不要自己在当前对话里直接去出规划!必须呼叫此命令行让系统清空你的记忆并给你换上 Plan 技能!** + +### 阶段二:PLAN(规划) +*(当你成功跳过防线来到这里时,你将失去 `meta-research`,获得 `meta-plan`)* +1. 读你的 `_research.md`。 +3. 运行 `node bin/lemon.js task transition --agentId <你自己ID> --phase execute --artifact 你的_plan.md路径`。 + +### 阶段三:EXECUTE(执行) +*(系统给你换上 `meta-execute` 技能时,你才会拥有修改核心文件的破坏性权限)* +2. 运行 `node bin/lemon.js task transition --agentId <你自己ID> --phase verify`。 + +### 阶段四:VERIFY(验证) +*(系统给你换上 `meta-verify` 技能,你的任务是自己检测自己跑的代码或跑分)* +1. 如果失败,将原因定入本地 `_lessons.md`,用工具打回上一阶段。 +2. 如果确信真的 PASS,才可以向上级(老板)调用归档终结命令 `report_task({status: "PASS"})`。 + +⚠️ **警告:未经四步走流转直接汇报 `PASS` 或写代码的行为将被视为严重违规记录!** diff --git a/data/system/templates/base/config.json b/data/system/templates/base/config.json new file mode 100644 index 0000000..caf6723 --- /dev/null +++ b/data/system/templates/base/config.json @@ -0,0 +1,10 @@ +{ + "type": "specialist", + "status": "idle", + "cli": { + "toolPolicy": { + "mode": "restricted", + "allowedTools": ["bash", "read_file", "str_replace"] + } + } +} diff --git a/docs/system-design/25-harness-engineering-improvements.md b/docs/system-design/25-harness-engineering-improvements.md new file mode 100644 index 0000000..c562b25 --- /dev/null +++ b/docs/system-design/25-harness-engineering-improvements.md @@ -0,0 +1,59 @@ +# 25. Harness Engineering Improvements (Agent 上下文与生命周期优化) + +## 1. 背景与目标 (Background & Objectives) + +随着 LemonBoss 架构从单体 Agent 演进为多租户下的“数字代理公司(Corporate OS)”,我们的宏观阵法(层级汇报、跨部门协作、OKR 分解)已初具规模(参考之前的相关系统设计)。然而,基于对前沿 Harness Engineering 最佳实践的深度反思,单个 Agent 节点在处理长链路复杂任务时,依然面临**“上下文衰减(Context Decay)”**、**“信息污染(Noise Pollution)”**、以及**“试错成本高昂(High Trial Cost)”**三大核心痛点。 + +为了确保底层每一个“数字员工”即使在超长命名的商业运营中也不会发生注意力弥散(Attention Decay),我们提出以下补充重构计划。该计划的核心思想是:**做精信息喂食、隔离决策上下文、构建自查闭环。** + +--- + +## 2. 核心改进计划 (Core Improvement Plans) + +### 改进点一:将四阶段分解固化为基座 Agent 的四大元技能 (Meta-Skills Integration) +**现状痛点**:当前单个 session 会承载一个 Agent 从“思考规划”到“调用执行”直至“修复 Bug”的全生命周期。后期 Session Prompt 过长(超过甜区限度),导致关键指令被稀释,且执行边界模糊。 +**改进策略**:我们将 `Research` -> `Plan` -> `Execute` -> `Verify` 不仅作为宏观规范,而是**实际固化为基座 Agent (Base Agent) 标准配备的四大元技能包(Meta-Skills)**。 +1. **按需装载独立技能树 (Dynamic Skill Invocation)**:在底层设定中,为每一个 Base Agent 提供四个相互独立的元技能引擎接口。每次面临新任务,代理根据自身所处的流转阶段,主动调用加载专属的技能包(如只调用 `skill_plan` 去出方案)。 +2. **基于技能切换的 Context Flush(上下文切断与防污染)**:一旦当前阶段的技能执行完毕(例如调用 Planner 技能产出了 `architecture.md`),该 Session 生命周期强制结束归档。当进入下一环节调起 `skill_execute` 技能时,新开一条绝对干净的 Session,只传入上一步确定的干货文件,彻底断绝跨阶段的幻觉与信息污染。 + +### 改进点二:实施“事前阻断阀”管理 (Human/Master pre-approval) +**现状痛点**:人类往往在 Agent 完全做错产出一堆无效代码后(即事后)去执行 Code Review,纠错成本极高。 +**改进策略**: +1. **杠杆前置审查**:当 CEO 或 CTO 这类管理层 Agent 产出“规划文档(Plan / Architecture)”后,系统强制挂起该节点的 `status`。 +2. **等待放行命令**:必须由 Master(人类用户) 或高阶 Validator/Innovator Review 批准该规划文档。一旦驳回,重做 Plan;一旦批准,才能生成下游子任务清单(Task Spawning)并消耗算力。以一行 Plan 的纠正,避免百行代码的错误生成。 + +### 改进点三:渐进式披露与去工具化 (Progressive Disclosure & Minimized Tools) +**现状痛点**:为了提供强大的功能,在 `tools.md` 或 System Prompt 里塞入海量工具说明与技能细则,导致模型注意力过载或陷入决策瘫痪(Decision Paralysis)。 +**改进策略**: +1. **多级目录动态按需加载**:确立三级机制: + - Level 1 (`CLAUDE.md` / `identity.md`):只讲最最核心的存活元规则和找资料入口。 + - Level 2 (`SKILL.md`):作为外挂组件引擎,只有触发特定领域关键词,引擎才将其注给 Agent `system_prompt`。一旦切走任务则将其卸载(Unload)。 + - Level 3 (`references`):基于 RAG 工具被动按需调取日志或全量文件。 +2. **修剪泛洪工具群**:将非必须的复杂组合 API 下架。退守“最强的万能扳手”:赋予 Agent 沙盒受控的终端执行与代码编写权(如受限的 Python 解释器或 `bash`),让 Agent 现写代码去满足需求,而非提供写死上百个微工具集。 + +### 改进点四:单体自治理的“微小自反思闭环” (Micro Self-Verification) +**现状痛点**:完全依赖事后外置的角色(如 `QA-Judge` 或者遭遇硬报错再触发反馈),链路长且对中控要求高。 +**改进策略**: +1. **植入 Local Verify Hook**:规定在下级执行类 Agent 调用 `report_task` 汇报 `status: PASS` 之前,必须调用自身的沙箱测试或执行一次自我反思的 `verify` 工具。 +2. **Autoresearch Error Loop**:为底层 Agent 本地增加 `_lessons.md`(试错经验暂存区)。在执行重试(Retry)循环时,如果不通过,先强制写一条经验记录到 `_lessons.md` 中。下次 retry 带上本条经验执行,避免单次死循环卡点。此级循环不惊动顶层的 Innovator(FDE),实现“兵不烦将”。 + +### 改进点五:引入算力经济学观测 (Cost & Profiling Observability) +**现状痛点**:对“系统表现和行为”透明(有 Logs),但对“运营成本”无感知,不符合 Corporate OS(企业组织)要求。 +**改进策略**: +1. 建立 `logs/billing` 观测维度:在 `pipeline_runs` 和 `audit` 同级,加入 Token 消耗监测系统。 +2. 精确计算每一次交接任务“花费的 API 计价预估”。给 CEO 提供财务数据维度支持,帮助甄别“能力较弱导致长耗时高频失败”的节点,为其发起汰换重构提供商业依据。 + +--- + +## 3. 落地 Roadmap (Implementation Roadmap) + +| 阶段 | 里程碑 (Milestones) | 涉及模块 (Modules) | 预期时间 | +|------|----------------|----------------|----------| +| **Phase 1** | **微观验证引入**:在现有节点中强制引入自我校验 (Local Verify),以及局部 `_lessons.md`。 | `agent-chat.js`, 基础 `templates/` 补充设定 | P1 优先 | +| **Phase 2** | **四大元技能装配与重构**:为 Base Agent 植装并切分 `Research/Plan/Execute/Verify` 四大独立技能入口,支持阶段流转中的 Context Flush。 | `memory-service.js`, `boss-meta-capabilities`, `master-session` | P1 优先 | +| **Phase 3** | **事前阻断流程化**:集成规划节点停摆拦截,接入前端 UI 以获得 Human/Master 的手动 Approved 后才能唤醒下游。 | `schema/goals`, Web UI 组件 | P2 规划 | +| **Phase 4** | **重置工具与按需挂载**:清理多余的工具并实施 `SKILL.md` 动态路由加载机制,提高上下文利用率甜区表现。 | `skill-registry/service`, 工具池整理 | P2 规划 | +| **Phase 5** | **经济学审计引入**:Token 数理追踪统计埋点,提供可视化报表支持(Cost Tracker Dashboard)。 | `logger/billing_service` | P3 未来探讨 | + +> **最后修订记录:** +> - 本规划文档由对行业 Harness Engineering 原理和 LemonBoss 特性对比后的工程反思形成,将指导未来一至两个大版本的演进。 diff --git a/server/routes/agent-chat.js b/server/routes/agent-chat.js index 6cd8357..b778423 100644 --- a/server/routes/agent-chat.js +++ b/server/routes/agent-chat.js @@ -352,6 +352,14 @@ function buildSystemPrompt(agentDir, userMessage) { } parts.push('---\n\n# 我的核心主技能 (执行准则)\n\n' + mainSkill.trim()); } + + // ── Phase 1 / Micro-Verify: 当前局部任务的试错经验池 `_lessons.md` ── + // 当 Agent 遇到棘手的坑或执行报错时,要求它总结并写入 _lessons.md。 + // 通过在此处注入,它下次 retry 时便能带着前车之鉴,避免死循环。 + const localLessons = loadFile(agentDir, 'workspace/_lessons.md'); + if (localLessons.trim()) { + parts.push('---\n\n## ⚠️ 本地试错防坑经验 (_lessons.md)\n不要重复踩下面的坑,仔细阅读并绕开:\n\n' + localLessons.trim()); + } const skills = loadFile(agentDir, '.claude/skills/skills.md'); if (skills.trim()) parts.push('---\n\n' + skills.trim()); @@ -1287,6 +1295,158 @@ router.post('/task/report', (req, res) => { }); // ═══════════════════════════════════════ +// POST /api/agent/:agentId/task/transition — 强制流转与记忆断舍离 (Context Flush) +// ═══════════════════════════════════════ +router.post('/:agentId/task/transition', (req, res) => { + try { + const { phase, artifact } = req.body; + const agentId = req.params.agentId; + const agentDir = getAgentDir(agentId); + if (!fs.existsSync(agentDir)) return res.status(404).json({ error: 'Agent not found' }); + + console.log(`[TaskTransition] 🔄 ${agentId} 正在跨越阶段至: ${phase}`); + + // 1. 查找当前的活动会话 ID + let activeConvId = null; + let sessionKey = null; + for (const [k, v] of sessions.entries()) { + if (v.agentId === agentId) { + activeConvId = v.convId; + sessionKey = k; + break; + } + } + + // 2. Phase 3 核心: 事前阻断阀 (Human-in-the-loop) + // 拦截 P0 任务由 plan -> execute 的跳转,强制挂起并申请 Master 审批 + if (phase === 'execute' && activeConvId && activeConvId.startsWith('task-')) { + const taskId = activeConvId.substring(5); + const taskMeta = loadTaskMeta(taskId); + if (taskMeta && taskMeta.priority === 'P0') { + console.log(`[TaskTransition] 🛑 拦截 P0 任务进入 Execute,挂起等待人类审批 (taskId: ${taskId})`); + + // 更新 OKR 节点状态为 pending_approval + if (taskMeta.goalId && taskMeta.nodeId) { + try { + goalsService.updateNode(taskMeta.goalId, taskMeta.nodeId, { status: 'pending_approval' }); + } catch (err) {} + } + + // 通知 Master 审批 + setImmediate(() => { + const masterMsg = `[⚠️ 事前审批请求]\n你的下属 ${agentId} 已经完成了 P0 级任务 ${taskId} 的规划阶段。\n现在它正准备加载大威力执行权限 (Execute) 落地方案。\n\n请你阅读其产生的 ${artifact || '_plan.md'},并在确认无误后通过指令手动放行它。`; + const http = require('http'); + const postData = JSON.stringify({ message: masterMsg, conversationId: `approval-${taskId}` }); + const options = { + hostname: 'localhost', port: 3000, + path: `/api/agent/master/chat`, + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) }, + }; + const chatReq = http.request(options, () => {}); + chatReq.on('error', () => {}); + chatReq.write(postData); + chatReq.end(); + }); + + // 掐断当前进程使其休眠,等待重新唤醒 + const v = sessions.get(sessionKey); + if (v && v.protocol) v.protocol.disconnect(); + sessions.delete(sessionKey); + + return res.json({ success: true, message: '已拦截。该任务为 P0 级别,必须等待 Master 审批放行后才能进入执行环节!' }); + } + } + + // 若未被阻断,更新 OKR 为进行中子状态 (如 researching, executing等) + if (activeConvId && activeConvId.startsWith('task-')) { + const taskId = activeConvId.substring(5); + const taskMeta = loadTaskMeta(taskId); + if (taskMeta && taskMeta.goalId && taskMeta.nodeId) { + try { + const subStatus = phase === 'research' ? 'researching' : phase === 'plan' ? 'planning' : phase === 'execute' ? 'executing' : phase === 'verify' ? 'verifying' : 'in_progress'; + goalsService.updateNode(taskMeta.goalId, taskMeta.nodeId, { status: subStatus }); + } catch (err) {} + } + } + + // 3. Hot Swap 动态换脑: 把 main_skill 替换为 meta-* 技能 + const sourceSkill = path.resolve(__dirname, `../../data/system/skills/meta-${phase}/SKILL.md`); + const targetSkill = path.join(agentDir, '.claude', 'skills', 'main_skill', 'SKILL.md'); + if (fs.existsSync(sourceSkill)) { + if (!fs.existsSync(path.dirname(targetSkill))) fs.mkdirSync(path.dirname(targetSkill), { recursive: true }); + fs.copyFileSync(sourceSkill, targetSkill); + console.log(`[TaskTransition] 🧠 ${agentId} 动态技能树已切换为 meta-${phase}`); + } + + // 4. Context Flush 记忆断舍离: 掐断当前运行中的 ACP 进程,清空历史消息 + if (sessionKey) { + const v = sessions.get(sessionKey); + if (v && v.protocol) v.protocol.disconnect(); + sessions.delete(sessionKey); + } + + if (activeConvId) { + const dir = getConvDir(agentId, activeConvId); + // 清空长周期的幻觉来源 + fs.writeFileSync(path.join(dir, 'history.json'), '[]', 'utf-8'); + logMessageChunk(agentId, activeConvId, `\n\n[SYSTEM] 🛑 Context Flush 触发!记忆已断开,阶段切换至 ${phase}\n\n`); + + // 3. 构建起步上下文: 告诉 Agent 它的前序成就 + let artifactContent = ''; + if (artifact) { + // artifact 可能带有 'workspace/' 前缀,尝试解析 + const possiblePaths = [ + path.join(agentDir, artifact), + path.join(agentDir, path.basename(artifact)), + path.join(agentDir, 'workspace', path.basename(artifact)) + ]; + for (const p of possiblePaths) { + if (fs.existsSync(p)) { + artifactContent = fs.readFileSync(p, 'utf-8'); + break; + } + } + } + + const flushMsg = `【系统指令:强制状态切换】 +你的生命周期已成功切入新阶段:**${phase.toUpperCase()}**。 +你之前的对话记忆已被完全抹除防范幻觉,你的系统技能已获得 ${phase} 的授权限制。 + +${artifactContent ? `这是上一阶段你或你的前置节点留下的交付物(Artifact),你必须严格参照此文件行动,不再另行发散:\n\n\`\`\`\n${artifactContent}\n\`\`\`` : ''} + +请开始你的新阶段工作!`; + + // 写入一条用户指令开启新的清爽对话 + appendHistory(agentId, activeConvId, { + role: 'user', content: flushMsg, messageId: `msg-${Date.now()}-flush`, timestamp: new Date().toISOString() + }); + + // 4. 发送唤醒指令让他立刻基于新脑子行动 + setImmediate(() => { + const http = require('http'); + const postData = JSON.stringify({ message: "请确认你已收到系统的新阶段挂载指令,并立即执行你当前阶段本分的任务!", conversationId: activeConvId }); + const options = { + hostname: 'localhost', port: 3000, + path: `/api/agent/${agentId}/chat`, + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) }, + }; + const chatReq = http.request(options, () => {}); + chatReq.on('error', () => {}); + chatReq.write(postData); + chatReq.end(); + }); + } + + res.json({ success: true, message: `已成功进行 Context Flush 并切换至 ${phase} 阶段` }); + } catch (err) { + console.error('[TaskTransition] Error:', err.message); + res.status(500).json({ error: err.message }); + } +}); + +// ═══════════════════════════════════════ // POST /api/agent/task/reject — COO 质量门控:驳回任务 // ═══════════════════════════════════════ @@ -1534,6 +1694,13 @@ router.post('/:agentId/chat', async (req, res) => { } else if (event.method === 'session/result') { if (event.params?.cost_usd) { sendSSE('session_cost', { cost: event.params.cost_usd, duration: event.params.duration_ms }); + // Phase 5: Economic Audit - 记录账单 (Token Profiling) + try { + const billingDir = path.resolve(__dirname, '../../data/logs'); + if (!fs.existsSync(billingDir)) fs.mkdirSync(billingDir, { recursive: true }); + const billingEntry = `${new Date().toISOString()} | Agent: ${agentId} | Conv: ${convId} | Cost: $${event.params.cost_usd.toFixed(6)} | Duration: ${event.params.duration_ms}ms\n`; + fs.appendFileSync(path.join(billingDir, 'billing.log'), billingEntry, 'utf-8'); + } catch (err) {} } if (!fullOutput && event.params?.result) { fullOutput = event.params.result; diff --git a/server/services/goals.js b/server/services/goals.js index 35b3528..d2f902b 100644 --- a/server/services/goals.js +++ b/server/services/goals.js @@ -319,7 +319,7 @@ class GoalsService { let nodesText = ''; for (const [nodeId, node] of Object.entries(goal.nodes)) { - if (node.owner === agentId && (node.status === 'in_progress' || node.status === 'pending')) { + if (node.owner === agentId && node.status !== 'done' && node.status !== 'failed') { if (!hasActiveNodes) { const progress = this._calcProgress(goal); combinedText += `\n\n## 🎯 当前参与的目标\n\n`; @@ -385,7 +385,8 @@ class GoalsService { const total = leafIds.length; const done = leafIds.filter(id => nodes[id].status === 'done').length; - const inProgress = leafIds.filter(id => nodes[id].status === 'in_progress').length; + const inProgressStates = ['in_progress', 'researching', 'planning', 'executing', 'verifying', 'pending_approval']; + const inProgress = leafIds.filter(id => inProgressStates.includes(nodes[id].status)).length; const pending = total - done - inProgress; // 按 owner 分组