Commit 1037ab12298dfb6f6ed0cc694b1fb5dae62d2b8f

Authored by yibo
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.
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 2 * bootstrap.js — 代理到 @mas/plugin-bootstrap
3 3 *
4 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 21 async function bootstrapPlugins(config) {
  22 + ensureLoaded();
  23 + if (!_openclawRoot && !config.openclawRoot) {
  24 + _openclawRoot = _resolveOpenclawDist(__dirname);
  25 + }
11 26 return _bootstrap({
12 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 14  
15 15 const path = require('path');
16 16 const fs = require('fs');
17   -const { bootstrapPlugins, createOpenClawJiti } = require('./bootstrap');
  17 +const { bootstrapPlugins } = require('./bootstrap');
18 18 const { loadPluginConfig, bridgeConfig } = require('./config-bridge');
19 19 const { createAgentBridge } = require('./agent-bridge');
20 20  
... ... @@ -78,9 +78,18 @@ class PluginManager {
78 78  
79 79 // 3. 解析路径
80 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 94 // 4. 加载——调用 1:1 复刻的 OpenClaw loadOpenClawPlugins
86 95 this.logger.info('初始化插件系统 (OpenClaw 原生流程)...');
... ... @@ -293,9 +302,17 @@ class PluginManager {
293 302  
294 303 // 方式 2:fallback —— 直接用 jiti 加载 monitor 模块
295 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 317 const monitorPaths = [
301 318 path.join(openclawRoot, `extensions/${channelId}/src/monitor.ts`),
... ...