Commit 1037ab12298dfb6f6ed0cc694b1fb5dae62d2b8f
1 parent
158c46ca
fix: lazy-load openclaw-dist to prevent startup crash
The plugin bootstrap was calling resolveOpenclawDist() synchronously at module load time, which crashed the entire server if the optional @mas/openclaw-dist package wasn't installed. Now all three call sites defer resolution to runtime with try/catch graceful fallback.
Showing
3 changed files
with
105 additions
and
10 deletions
docs/user-guides/08-base-agent-4-phase-guide.md
0 → 100644
| 1 | +# LemonBoss Base Agent 四阶段 (4-Phase) 使用指南 | ||
| 2 | + | ||
| 3 | +## 1. 概念与定位 | ||
| 4 | +在经历了 Harness Engineering 的重构后,**Base Agent(基座数字员工)**不再是一个简单的“全能聊天机器人”,而是被设计成一台严密的、流水线式的自动执行机器。 | ||
| 5 | +它的核心特征是 **“分阶段切片运行” (Phase-Slicing Execution)**。它在任何时刻都只拥有一种明确的脑部模式(Meta-Skill),以最高效的方式避免“长上下文衰减(Context Decay)”引起的注意力涣散和“工具滥用”引发的安全风险。 | ||
| 6 | + | ||
| 7 | +当接受一项任务(尤其是长链路的开发、撰写或爬虫任务)时,Base Agent 会不可逆地单向穿越以下四个阶段: | ||
| 8 | + | ||
| 9 | +| 阶段名 | 行动挂载的技能 (Meta-Skill) | 工具权限 | 当前使命 | 产出成果物 (Artifact) | | ||
| 10 | +| :--- | :--- | :--- | :--- | :--- | | ||
| 11 | +| **Research (调研)** | `meta-research` | 终端只读权限 | 不做假设,充分使用读写工具摸排所有上下文信息并提炼精华。 | `workspace/_research.md` | | ||
| 12 | +| **Plan (规划)** | `meta-plan` | 轻度文本输出 | 根据前置调研,严密推演并产出外科手术式的 1/2/3 行动清单。 | `workspace/_plan.md` | | ||
| 13 | +| **Execute (执行)** | `meta-execute` | **万能扳手(bash)** | 心无旁骛,完全依照规划改文件、跑脚本,**禁止擅改架构**。 | 改动后的代码或输出文件 | | ||
| 14 | +| **Verify (验证)** | `meta-verify` | 测试与运行权限 | 自测,调用脚本或运行环境;若失败则撰写防坑日志,若成功则汇报。| `workspace/_lessons.md` / `PASS` | | ||
| 15 | + | ||
| 16 | +--- | ||
| 17 | + | ||
| 18 | +## 2. 状态扭转与 Context Flush(记忆断舍离) | ||
| 19 | +每当一个阶段完成,Agent 绝不能在当前的记忆流中直接开始下一个阶段。它会被迫调用以下 CLI 命令: | ||
| 20 | +```bash | ||
| 21 | +node bin/lemon.js task transition --agentId <你的ID> --phase <下一阶段> --artifact <该阶段的文本路径> | ||
| 22 | +``` | ||
| 23 | +**系统此时会进行物理干预:** | ||
| 24 | +1. **换脑 (Hot Swap)**:系统会剥夺其上一阶段的技能组,将比如 `meta-plan` 塞入它的核心大纲。 | ||
| 25 | +2. **失忆 (Context Flush)**:系统**强制杀死它当前的后台进程,清空它长达几百轮的记忆和所有报错产生的噪音文本**。 | ||
| 26 | +3. **轻装上阵 (Clean Start)**:系统给重新启动的 Agent 唯一一条初始化信息:“你已经进入了新阶段,这是你上个阶段的精华产出物 Artifact,请基于它继续工作。” | ||
| 27 | + | ||
| 28 | +这种做法确保了越到后面的高危阶段,Agent 的决策视界就越清晰。 | ||
| 29 | + | ||
| 30 | +--- | ||
| 31 | + | ||
| 32 | +## 3. Human-in-the-loop: P0 级任务的拦截阀 | ||
| 33 | +如果你向数字员工派发的任务被标记为了 **P0 (Critical)**: | ||
| 34 | +```bash | ||
| 35 | +node bin/lemon.js task dispatch --from ceo --to worker_01 --task "全面重构支付网关逻辑" --priority P0 | ||
| 36 | +``` | ||
| 37 | +当 Base Agent 顺利完成 `Research` 并且写好 `_plan.md`,正打算启动 `transition` 跨入 `Execute` 阶段召唤“Bash 重器”强改代码前——**系统会紧急挂起该操作!** | ||
| 38 | +* 它的状态会被标记为 `pending_approval`(待审批休眠)。 | ||
| 39 | +* 你(作为 Master/人类老板)或者你的 CEO 会收到一条私信,请求审阅该 `_plan.md`。 | ||
| 40 | +* 此举以仅仅审阅一页文字的低昂成本,换来了百倍高的止损防火墙。 | ||
| 41 | + | ||
| 42 | +--- | ||
| 43 | + | ||
| 44 | +## 4. 防撞墙:微自反思闭环 (Autoresearch Error Loop) | ||
| 45 | +如果 Agent 在 Execute 或 Verify 阶段遇到了顽固的 Bug(比如包冲突、语法错误),系统并不鼓励立刻抛出求救。 | ||
| 46 | +它是带有**自修正微闭环**的: | ||
| 47 | +1. 遇到报错后,它会将这个致命踩大坑的经过与解决方案总结进 `workspace/_lessons.md`。 | ||
| 48 | +2. 当它被要求 Retry 重试时,系统会把它自己写的这段防坑经验,像免死金牌一样挂载在系统提示词的第一行:`## ⚠️ 本地试错防坑经验 (_lessons.md)`。 | ||
| 49 | +3. 从此这只瞎撞墙的苍蝇,拥有了不碰同一堵墙两次的能力。 | ||
| 50 | + | ||
| 51 | +--- | ||
| 52 | + | ||
| 53 | +## 5. 经济算力(Economic Profiling)报表 | ||
| 54 | +每次发生阶段切换 (Transition) 或一次回复打结,系统会默默在后台日志 `data/logs/billing.log` 计算本次任务切片的真实美元花费(USD Cost)和耗时评估。 | ||
| 55 | + | ||
| 56 | +**通过这种核算拆解,管理者可以知道:** | ||
| 57 | +“为了执行一个爬虫任务,员工小李在 Research 的资料搜索里烧了 $0.5,而 Execute 写代码只烧了 $0.02。”进而可以基于这些财务数字去优化上游给它的 Task 精准度。 | ||
| 58 | + | ||
| 59 | +--- | ||
| 60 | + | ||
| 61 | +## 6. 使用建议 (Best Practices) | ||
| 62 | +1. **对于复杂长链条指令**:不要试图跟从前的聊天大模型一样对它下达含糊的“全干指令”。让它按这四阶走,明确告知你的质量要求。 | ||
| 63 | +2. **信任 `_lessons.md`**:如果你作为人类看它钻了死胡同,你可以直接人肉打开它的工作区,手工往 `_lessons.md` 里加一句话:“绝对不要再用 Python 2 的语法去跑脚本!用 python3!”剩下的,放心交给它去修正即可。 |
server/src/plugins/bootstrap.js
| @@ -2,15 +2,30 @@ | @@ -2,15 +2,30 @@ | ||
| 2 | * bootstrap.js — 代理到 @mas/plugin-bootstrap | 2 | * bootstrap.js — 代理到 @mas/plugin-bootstrap |
| 3 | * | 3 | * |
| 4 | * openclawRoot 统一指向 @mas/openclaw-dist 包 | 4 | * openclawRoot 统一指向 @mas/openclaw-dist 包 |
| 5 | + * | ||
| 6 | + * 注意: resolveOpenclawDist 延迟到 bootstrapPlugins() 被真正调用时才执行, | ||
| 7 | + * 避免在未安装 @mas/openclaw-dist 时导致服务器启动崩溃。 | ||
| 5 | */ | 8 | */ |
| 6 | -const { bootstrapPlugins: _bootstrap, resolveOpenclawDist } = require('@mas/plugin-bootstrap'); | ||
| 7 | 9 | ||
| 8 | -const openclawRoot = resolveOpenclawDist(__dirname); | 10 | +let _bootstrap = null; |
| 11 | +let _resolveOpenclawDist = null; | ||
| 12 | +let _openclawRoot = null; | ||
| 13 | + | ||
| 14 | +function ensureLoaded() { | ||
| 15 | + if (_bootstrap) return; | ||
| 16 | + const pkg = require('@mas/plugin-bootstrap'); | ||
| 17 | + _bootstrap = pkg.bootstrapPlugins; | ||
| 18 | + _resolveOpenclawDist = pkg.resolveOpenclawDist; | ||
| 19 | +} | ||
| 9 | 20 | ||
| 10 | async function bootstrapPlugins(config) { | 21 | async function bootstrapPlugins(config) { |
| 22 | + ensureLoaded(); | ||
| 23 | + if (!_openclawRoot && !config.openclawRoot) { | ||
| 24 | + _openclawRoot = _resolveOpenclawDist(__dirname); | ||
| 25 | + } | ||
| 11 | return _bootstrap({ | 26 | return _bootstrap({ |
| 12 | ...config, | 27 | ...config, |
| 13 | - openclawRoot: config.openclawRoot || openclawRoot, | 28 | + openclawRoot: config.openclawRoot || _openclawRoot, |
| 14 | }); | 29 | }); |
| 15 | } | 30 | } |
| 16 | 31 |
server/src/plugins/plugin-manager.js
| @@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
| 14 | 14 | ||
| 15 | const path = require('path'); | 15 | const path = require('path'); |
| 16 | const fs = require('fs'); | 16 | const fs = require('fs'); |
| 17 | -const { bootstrapPlugins, createOpenClawJiti } = require('./bootstrap'); | 17 | +const { bootstrapPlugins } = require('./bootstrap'); |
| 18 | const { loadPluginConfig, bridgeConfig } = require('./config-bridge'); | 18 | const { loadPluginConfig, bridgeConfig } = require('./config-bridge'); |
| 19 | const { createAgentBridge } = require('./agent-bridge'); | 19 | const { createAgentBridge } = require('./agent-bridge'); |
| 20 | 20 | ||
| @@ -78,9 +78,18 @@ class PluginManager { | @@ -78,9 +78,18 @@ class PluginManager { | ||
| 78 | 78 | ||
| 79 | // 3. 解析路径 | 79 | // 3. 解析路径 |
| 80 | const configDir = path.resolve(__dirname, '../../config'); | 80 | const configDir = path.resolve(__dirname, '../../config'); |
| 81 | - const openclawRoot = this._pluginConfig.openclawRoot | ||
| 82 | - ? path.resolve(configDir, this._pluginConfig.openclawRoot) | ||
| 83 | - : require('@mas/plugin-bootstrap').resolveOpenclawDist(__dirname); | 81 | + let openclawRoot; |
| 82 | + if (this._pluginConfig.openclawRoot) { | ||
| 83 | + openclawRoot = path.resolve(configDir, this._pluginConfig.openclawRoot); | ||
| 84 | + } else { | ||
| 85 | + try { | ||
| 86 | + openclawRoot = require('@mas/plugin-bootstrap').resolveOpenclawDist(__dirname); | ||
| 87 | + } catch (err) { | ||
| 88 | + this.logger.error('无法定位 @mas/openclaw-dist,插件系统不可用:', err.message); | ||
| 89 | + this._state = 'initialized'; | ||
| 90 | + return; | ||
| 91 | + } | ||
| 92 | + } | ||
| 84 | 93 | ||
| 85 | // 4. 加载——调用 1:1 复刻的 OpenClaw loadOpenClawPlugins | 94 | // 4. 加载——调用 1:1 复刻的 OpenClaw loadOpenClawPlugins |
| 86 | this.logger.info('初始化插件系统 (OpenClaw 原生流程)...'); | 95 | this.logger.info('初始化插件系统 (OpenClaw 原生流程)...'); |
| @@ -293,9 +302,17 @@ class PluginManager { | @@ -293,9 +302,17 @@ class PluginManager { | ||
| 293 | 302 | ||
| 294 | // 方式 2:fallback —— 直接用 jiti 加载 monitor 模块 | 303 | // 方式 2:fallback —— 直接用 jiti 加载 monitor 模块 |
| 295 | const configDir = path.resolve(__dirname, '../../config'); | 304 | const configDir = path.resolve(__dirname, '../../config'); |
| 296 | - const openclawRoot = this._pluginConfig.openclawRoot | ||
| 297 | - ? path.resolve(configDir, this._pluginConfig.openclawRoot) | ||
| 298 | - : require('@mas/plugin-bootstrap').resolveOpenclawDist(__dirname); | 305 | + let openclawRoot; |
| 306 | + if (this._pluginConfig.openclawRoot) { | ||
| 307 | + openclawRoot = path.resolve(configDir, this._pluginConfig.openclawRoot); | ||
| 308 | + } else { | ||
| 309 | + try { | ||
| 310 | + openclawRoot = require('@mas/plugin-bootstrap').resolveOpenclawDist(__dirname); | ||
| 311 | + } catch (err) { | ||
| 312 | + this.logger.warn(`channel/${channelId}: 无法定位 openclaw-dist,跳过 fallback monitor`); | ||
| 313 | + return; | ||
| 314 | + } | ||
| 315 | + } | ||
| 299 | 316 | ||
| 300 | const monitorPaths = [ | 317 | const monitorPaths = [ |
| 301 | path.join(openclawRoot, `extensions/${channelId}/src/monitor.ts`), | 318 | path.join(openclawRoot, `extensions/${channelId}/src/monitor.ts`), |