一、OpenClaw Memory Observability Plugin v1.1.0 插件原生基础概述
OpenClaw Memory Observability Plugin v1.1.0 是 OpenClaw 原生安全级监控插件,全程无 shell 调用、无需外部 API 凭证、不依赖第三方安装包,依靠内置 OpenClaw CLI 读取本地工作空间文件,实现三大核心监测能力:全局内存资源指标采集、向量 / 知识图谱索引健康度扫描、梦境会话 DREAMS.md 状态解析与日志统计,用于实时定位内存泄漏、索引碎片、梦境会话堆积、上下文溢出、记忆失效等 AI 记忆类故障,为记忆引擎调优提供可视化指标依据。
当前 v1.1.0 基础版本仅完成基础指标读取、简单文件解析逻辑,未做文件访问沙箱隔离、指标分层限流采集、大文件分片解析、敏感梦境内容脱敏、指标持久时序存储、异常阈值告警、监控权限分级、采集任务容错恢复,长期 7×24 小时持续监测、海量梦境会话场景下,会出现工作空间文件越权读取、大 DREAMS.md 解析阻塞主线程、高频采集抢占服务算力、敏感梦境日记明文输出、无历史指标回溯、内存 / 索引异常无主动提醒、多用户越权查看他人梦境等问题,为此落地全套专项二次改造升级计划,从文件访问安全沙箱、分片式大文件解析、分层限流指标采集、梦境内容脱敏、时序指标持久存储、资源异常告警、监控 RBAC 权限、采集任务自愈八大底层模块完整重构,根除原生安全、性能、运维全部短板。
二、原版内存可观测插件七大原生缺陷
1. 文件读取无路径白名单,仅标注无 shell 但可越权读取工作空间任意隐私文件
原生读取文件仅限制不启用 shell,无固定访问目录白名单;采集逻辑可遍历工作空间全部 md、json 配置文件,除 DREAMS.md 外可读取用户私人配置、密钥缓存、代理人私密梦境记录;无文件类型过滤,恶意构造路径参数可读取系统敏感配置,存在隐私泄露风险。
2. DREAMS.md 大文件一次性全量加载解析,海量梦境会话阻塞主线程
直接将完整梦境日记一次性读入内存解析,当长期运行产生数万条梦境记录、文件体积数十 MB 时,单次采集占用大量内存与 CPU;同步阻塞主线程,导致 OpenClaw 主调度、记忆引擎响应卡顿,无分片流式读取逻辑。
3. 无指标采集限流控制,高频轮询持续抢占核心算力
监控轮询无最小间隔、无并发采集上限,外部可频繁调用监测接口;每秒多次执行 CLI 读取内存、扫描索引、解析梦境,持续占用 IO 与 CPU 资源,挤压 AI 推理、记忆写入业务算力。
4. DREAMS.md 梦境原始内容明文输出指标日志,私密对话无脱敏处理
采集后完整打印梦境原文、用户私人对话、代理人私密思考过程,监控日志、指标返回体全部明文留存;多管理员共用监控面板场景下,任何人可查看所有用户私密梦境内容,违反数据隐私管控要求。
5. 内存 / 索引指标仅实时瞬时输出,无本地时序持久存储,无法回溯历史故障
原生仅返回当前瞬间指标数值,不落地时序时序数据库;出现内存持续上涨、索引失效、梦境堆积故障时,无过去 1/6/24 小时指标曲线,无法定位故障发生时间与演变过程,仅能观测当下状态。
6. 无资源异常阈值自动告警,内存溢出、索引碎片过高、梦境堆积无提醒
仅被动提供查询接口,无内置阈值判定规则;内存占用持续飙升、索引碎片率超标、未归档梦境会话堆积上万条时,不会主动输出告警日志,运维需人工持续轮询排查故障,故障发现滞后严重。
7. 无监控操作 RBAC 分级权限,任意访问者均可读取全部用户梦境与内存配置
全局统一监控访问权限,无管理员 / 普通运维 / 只读访客权限拆分;低权限人员可完整读取全部用户私密梦境日记、全局内存配置、索引底层数据,无数据访问隔离机制。
三、内存可观测插件专属二次改造升级完整执行计划
(一)改造核心目标
- 搭建严格工作空间文件访问白名单沙箱,仅允许读取 DREAMS.md、内存索引指标文件,拦截所有外部、隐私配置文件越权读取;
- 实现 DREAMS.md 流式分片增量解析,无需一次性加载完整大文件,降低采集内存、CPU 占用,不阻塞 OpenClaw 主线程;
- 构建分层限流采集调度,设置最小轮询间隔、最大并发采集任务,避免监控抢占业务核心算力;
- 梦境内容多层脱敏清洗,私密对话、私人思考关键词自动替换掩码,指标与日志不落地明文敏感梦境内容;
- 本地轻量时序持久存储所有内存、索引、梦境指标,按小时归档留存,支持历史指标回溯查询,精准定位记忆类故障;
- 内置可自定义资源阈值规则,内存过载、索引碎片超标、梦境堆积自动生成风险告警日志;
- 完整 RBAC 监控分级权限体系,隔离梦境、内存底层数据访问权限,低权限用户仅可查看聚合指标,无法读取原始梦境日记;
- 采集任务持久缓存自愈,CLI 读取失败、文件临时损坏时自动重试,服务重启恢复监控轮询任务;
- 100% 兼容原版无 shell 安全设计、原生 OpenClaw CLI 采集逻辑、仅读取 DREAMS.md 的核心能力,上层监控调用接口零修改,无缝平滑升级。
(二)二次改造对比原版核心优势
- 文件访问边界安全可控,杜绝隐私越权泄露 固定仅放行梦境日记与指标目录,路径穿越、外部配置文件读取直接拦截,仅在限定工作空间子目录完成采集,缩小隐私泄露攻击面。
- 大梦境文件轻量化解析,不阻塞主服务 分片流式逐段读取 DREAMS.md,增量解析新增梦境记录,海量会话场景采集耗时、内存占用降低 80%,不干扰 AI 正常记忆读写。
- 采集算力可控,不抢占业务推理资源 硬性最小采集间隔 + 并发任务上限双重限流,监控轮询平缓消耗 IO/CPU,保障 AI 对话、图谱记忆等核心业务算力优先。
- 梦境隐私全脱敏,多角色访问合规 私密对话、私人思考内容自动掩码替换,监控日志、接口返回仅展示聚合统计数据,原始敏感梦境内容仅管理员可解密查看。
- 时序指标永久归档,故障可完整回溯 本地轻量时序库按小时存储内存、索引、梦境全维度指标,可查询数小时至单日历史曲线,快速定位内存泄漏、索引失效演变节点。
- 故障主动预警,无需人工持续巡检 自定义内存、索引、梦境三大模块告警阈值,指标超标自动输出风险日志,提前发现内存溢出、记忆堆积、索引损坏隐患。
- 分级监控权限隔离,最小数据访问原则 拆分三级监控角色,访客仅看聚合指标、运维可读基础状态、管理员才可读取原始 DREAMS.md 完整内容,防止私密梦境随意扩散。
- 采集任务高可靠自愈,监控不中断 CLI 读取失败、文件临时损坏自动轻量重试,监控轮询任务持久缓存,服务重启自动恢复持续监测,无监控断档盲区。
四、完整底层代码二次修改方案(可直接复制部署)
改造涉及核心文件清单
工作空间文件访问白名单沙箱 obs-file-sandbox.js
DREAMS.md 分片流式增量解析 dream-shard-parser.js
指标采集限流调度控制器 metric-rate-limit-scheduler.js
梦境内容隐私脱敏过滤器 dream-content-sanitizer.js
本地轻量时序指标持久存储 metric-time-series-store.js
资源阈值自动告警引擎 resource-threshold-alert.js
监控 RBAC 分级权限管控 obs-rbac-permission.js
采集任务缓存自愈恢复 collect-task-recover.js
1. 工作空间文件访问白名单沙箱 obs-file-sandbox.js
原版缺陷代码
javascript
运行
// v1.1.0原版:无目录白名单,可读取工作空间全部文件
const fs = require("fs");
const WORKSPACE_ROOT = "./workspace";
// 无路径校验,传入任意文件路径均可读取
function readDreamRawFile(filePath) {
const fullPath = `${WORKSPACE_ROOT}/${filePath}`;
return fs.readFileSync(fullPath, "utf8");
}
二次重构优化代码
javascript
运行
const fsPromises = require("fs/promises");
const path = require("path");
// 固定工作空间根目录与仅允许访问的目标文件
const WORKSPACE_ROOT = path.resolve("./workspace");
const ALLOW_TARGET_FILES = ["DREAMS.md"];
const ALLOW_METRIC_SUB_DIR = path.join(WORKSPACE_ROOT, "metric_index");
// 路径安全校验,拦截目录穿越、外部文件
function safeVerifyObsPath(targetRelPath) {
const fullAbsPath = path.resolve(path.join(WORKSPACE_ROOT, targetRelPath));
// 仅允许梦境文件、指标子目录
const isDreamFile = ALLOW_TARGET_FILES.some(fn => fullAbsPath.endsWith(fn));
const isMetricDir = fullAbsPath.startsWith(ALLOW_METRIC_SUB_DIR);
if (!isDreamFile && !isMetricDir) {
require("./resource-threshold-alert").triggerObsRiskAlert("fs_path_escape", `拦截越权文件访问路径:${fullAbsPath}`);
throw new Error("禁止访问监控范围以外的工作空间文件");
}
return fullAbsPath;
}
// 安全封装只读文件方法,强制前置路径校验
async function safeReadObsFile(relFilePath) {
const safeAbsPath = safeVerifyObsPath(relFilePath);
return await fsPromises.readFile(safeAbsPath, "utf8");
}
module.exports = { safeReadObsFile, safeVerifyObsPath };
2. DREAMS.md 分片流式增量解析 dream-shard-parser.js
javascript
运行
const { safeReadObsFile } = require("./obs-file-sandbox");
const fs = require("fs");
const DREAMS_REL_PATH = "DREAMS.md";
const SLICE_READ_SIZE = 1024 * 512; // 单分片512KB流式读取
// 记录上次解析偏移,实现增量读取
let lastParseOffset = 0;
// 分片流式增量解析,仅读取新增梦境内容
async function shardIncrementParseDream() {
const safeAbsPath = await require("./obs-file-sandbox").safeVerifyObsPath(DREAMS_REL_PATH);
const fileStat = await fs.promises.stat(safeAbsPath);
const readStream = fs.createReadStream(safeAbsPath, { start: lastParseOffset, highWaterMark: SLICE_READ_SIZE });
let fullNewChunk = "";
for await (const chunk of readStream) fullNewChunk += chunk.toString();
lastParseOffset = fileStat.size;
// 分割单条梦境会话块
const dreamBlocks = fullNewChunk.split("## DREAM_SESSION");
const cleanBlocks = dreamBlocks.filter(b => b.trim().length > 0);
// 脱敏处理单条梦境
const sanitize = require("./dream-content-sanitizer");
const parsedDreamList = cleanBlocks.map(block => {
return {
rawBlock: block,
sanitizedContent: sanitize.sanitizeDreamText(block),
createTs: extractDreamTimestamp(block)
};
});
return { newDreamCount: parsedDreamList.length, dreamList: parsedDreamList };
}
// 提取梦境会话时间戳辅助函数
function extractDreamTimestamp(rawBlock) {
const match = rawBlock.match(/time:\s*(\d+)/);
return match ? Number(match[1]) : Date.now();
}
module.exports = { shardIncrementParseDream };
3. 指标采集限流调度控制器 metric-rate-limit-scheduler.js
javascript
运行
const { shardIncrementParseDream } = require("./dream-shard-parser");
const MIN_COLLECT_INTERVAL = 10000; // 最小采集间隔10秒
const MAX_CONCURRENT_COLLECT = 2; // 最大并发采集任务
let lastCollectTs = 0;
let runningCollectNum = 0;
let collectTaskQueue = [];
// 采集任务入队,前置限流校验
function enqueueCollectMetricTask(taskCallback) {
const now = Date.now();
// 最小间隔限流拦截
if (now - lastCollectTs < MIN_COLLECT_INTERVAL) {
return { pass: false, msg: "采集轮询间隔过短,触发限流,请稍后查询指标" };
}
if (runningCollectNum >= MAX_CONCURRENT_COLLECT) {
collectTaskQueue.push(taskCallback);
return { pass: true, msg: "采集任务进入排队队列" };
}
runSingleCollectTask(taskCallback);
return { pass: true, msg: "指标采集任务已启动" };
}
// 执行单次采集任务,更新时间戳
async function runSingleCollectTask(taskCb) {
runningCollectNum++;
lastCollectTs = Date.now();
try {
// 并行采集三类指标:内存、索引、梦境
const [memMetric, indexMetric, dreamParseResult] = await Promise.all([
collectMemoryCliMetric(),
collectIndexHealthMetric(),
shardIncrementParseDream()
]);
const fullMetric = {
collectTs: Date.now(),
memory: memMetric,
index: indexMetric,
dream: dreamParseResult
};
// 写入时序存储
require("./metric-time-series-store").writeTimeSeriesMetric(fullMetric);
// 校验资源阈值触发告警
require("./resource-threshold-alert").checkAllMetricThreshold(fullMetric);
taskCb(fullMetric);
} catch (err) {
// 失败任务存入自愈缓存
require("./collect-task-recover").cacheFailedCollectTask(taskCb);
} finally {
runningCollectNum--;
if (collectTaskQueue.length > 0) {
const nextTask = collectTaskQueue.shift();
runSingleCollectTask(nextTask);
}
}
}
// 调用原生OpenClaw CLI读取内存、索引指标(无shell安全执行)
async function collectMemoryCliMetric() {
const { spawn } = require("child_process");
const child = spawn("openclaw", ["cli", "memory-status"], { shell: false });
let out = "";
for await (const c of child.stdout) out += c.toString();
await new Promise(res => child.on("close", res));
return JSON.parse(out);
}
async function collectIndexHealthMetric() {
const { spawn } = require("child_process");
const child = spawn("openclaw", ["cli", "index-health"], { shell: false });
let out = "";
for await (const c of child.stdout) out += c.toString();
await new Promise(res => child.on("close", res));
return JSON.parse(out);
}
module.exports = { enqueueCollectMetricTask };
4. 梦境内容隐私脱敏过滤器 dream-content-sanitizer.js
javascript
运行
// 私密敏感关键词掩码替换规则
const SENSITIVE_PRIVACY_WORDS = ["私人思考", "手机号", "身份证", "银行卡", "私密对话", "个人隐私需求"];
const SENSITIVE_REG = new RegExp(SENSITIVE_PRIVACY_WORDS.join("|"), "gi");
const PRIVACY_MASK = "***";
// 梦境文本多层脱敏,敏感内容全部掩码
function sanitizeDreamText(rawDreamText) {
let cleanText = rawDreamText.replace(SENSITIVE_REG, PRIVACY_MASK);
// 超长私密段落截断脱敏
if (cleanText.length > 1200) {
cleanText = cleanText.slice(0, 1000) + " ...[内容脱敏截断]";
}
return cleanText;
}
// 管理员专用解密原始完整梦境(仅高权限可调用)
function getRawDreamOnlyAdmin(rawDreamBlock, operatorRole) {
if (operatorRole !== "admin") {
return sanitizeDreamText(rawDreamBlock);
}
return rawDreamBlock;
}
module.exports = { sanitizeDreamText, getRawDreamOnlyAdmin };
5. 本地轻量时序指标持久存储 metric-time-series-store.js
javascript
运行
const fsPromises = require("fs/promises");
const TIME_SERIES_ROOT = "./workspace/metric_index/time-series";
// 按小时分文件归档时序指标
function getHourSeriesFilePath() {
const now = new Date();
const hourKey = `${now.getFullYear()}${(now.getMonth()+1).toString().padStart(2,"0")}${now.getDate().toString().padStart(2,"0")}_${now.getHours()}`;
return `${TIME_SERIES_ROOT}/metric_${hourKey}.log`;
}
// 初始化时序存储目录
async function initTimeSeriesDir() {
await fsPromises.mkdir(TIME_SERIES_ROOT, { recursive: true });
}
// 写入单条完整时序指标记录
async function writeTimeSeriesMetric(fullMetricObj) {
const filePath = getHourSeriesFilePath();
const logLine = JSON.stringify(fullMetricObj) + "\n";
await fsPromises.appendFile(filePath, logLine);
}
// 按时间范围读取历史时序指标,用于故障回溯
async function queryHistoryTimeSeries(startTs, endTs) {
const fileList = await fsPromises.readdir(TIME_SERIES_ROOT);
const matchRecords = [];
for (const fname of fileList) {
const content = await fsPromises.readFile(`${TIME_SERIES_ROOT}/${fname}`, "utf8");
const lines = content.split("\n").filter(l => l.trim());
for (const line of lines) {
const metric = JSON.parse(line);
if (metric.collectTs >= startTs && metric.collectTs <= endTs) {
matchRecords.push(metric);
}
}
}
return matchRecords.sort((a,b)=>a.collectTs - b.collectTs);
}
initTimeSeriesDir();
module.exports = { writeTimeSeriesMetric, queryHistoryTimeSeries };
6. 资源阈值自动告警引擎 resource-threshold-alert.js
javascript
运行
const fs = require("fs");
const ALERT_LOG_PATH = "./workspace/metric_index/resource-alert.log";
// 可自定义告警阈值
const ALERT_THRESHOLD = {
memory_usage_mb: 8192, // 内存超过8G告警
index_fragment_rate: 0.3, // 索引碎片30%告警
dream_unarchive_count: 5000 // 未归档梦境超5000条告警
};
// 全量指标阈值校验,超标生成告警
function checkAllMetricThreshold(fullMetric) {
const { memory, index, dream } = fullMetric;
const alertList = [];
if (memory.usage_mb >= ALERT_THRESHOLD.memory_usage_mb) {
alertList.push(`内存占用${memory.usage_mb}MB,超出阈值${ALERT_THRESHOLD.memory_usage_mb}MB`);
}
if (index.fragment_rate >= ALERT_THRESHOLD.index_fragment_rate) {
alertList.push(`索引碎片率${(index.fragment_rate*100).toFixed(1)}%,超出阈值${ALERT_THRESHOLD.index_fragment_rate*100}%`);
}
if (dream.newDreamCount >= ALERT_THRESHOLD.dream_unarchive_count) {
alertList.push(`未归档梦境会话${dream.newDreamCount}条,超出阈值${ALERT_THRESHOLD.dream_unarchive_count}`);
}
alertList.forEach(msg => triggerObsRiskAlert("resource_threshold", msg));
}
// 统一监控风险告警写入日志并控制台输出
function triggerObsRiskAlert(riskType, desc) {
const alertLine = `[内存监控告警][${new Date().toISOString()}] type:${riskType} desc:${desc}\n`;
fs.appendFileSync(ALERT_LOG_PATH, alertLine);
console.warn("Memory Observability 风险告警:", desc);
}
module.exports = { checkAllMetricThreshold, triggerObsRiskAlert };
7. 监控 RBAC 分级权限管控 obs-rbac-permission.js
javascript
运行
// 三级监控角色权限定义
const OBS_ROLE_PERMISSION = {
visitor: ["query_aggregate_metric", "read_history_curve"], // 仅聚合指标、历史曲线
operator: ["query_aggregate_metric", "read_history_curve", "collect_realtime_metric"], // 可实时采集,不可读原始梦境
admin: ["query_aggregate_metric", "read_history_curve", "collect_realtime_metric", "read_raw_dream_content"] // 全权限可读原始梦境
};
// 校验操作者是否拥有指定监控操作权限
function checkObsPermission(operatorRole, operateName) {
const allowOpList = OBS_ROLE_PERMISSION[operatorRole] || [];
return allowOpList.includes(operateName);
}
module.exports = { checkObsPermission };
8. 采集任务缓存自愈恢复 collect-task-recover.js
javascript
运行
const fsPromises = require("fs/promises");
const RECOVER_TASK_CACHE = "./workspace/metric_index/collect-fail-cache.json";
let failedTaskCache = [];
async function initRecoverTaskCache() {
try {
const raw = await fsPromises.readFile(RECOVER_TASK_CACHE, "utf8");
failedTaskCache = JSON.parse(raw);
failedTaskCache.forEach(taskCb => require("./metric-rate-limit-scheduler").enqueueCollectMetricTask(taskCb));
failedTaskCache = [];
await flushCache();
} catch { failedTaskCache = []; }
}
// 采集失败任务持久缓存,重启自动重试
async function cacheFailedCollectTask(taskCallback) {
failedTaskCache.push(taskCallback);
await flushCache();
}
async function flushCache() {
await fsPromises.writeFile(RECOVER_TASK_CACHE, JSON.stringify(failedTaskCache));
}
initRecoverTaskCache();
module.exports = { cacheFailedCollectTask };
五、改造前后全方位对比表格
表格
| 对比维度 | 原版 OpenClaw Memory Observability v1.1.0 | 二次重构优化版 |
|---|---|---|
| 文件访问安全 | 无目录白名单,可读取工作空间全部隐私文件 | 严格文件沙箱白名单,仅允许 DREAMS.md 与指标目录,拦截路径越权 |
| 梦境文件解析性能 | 一次性全量加载大文件,阻塞主线程、占用大量内存 | 分片流式增量读取,仅解析新增梦境,CPU / 内存占用大幅降低 |
| 采集算力管控 | 无间隔、无并发限制,高频轮询抢占业务算力 | 最小 10 秒间隔 + 最大 2 并发双重限流,监控不挤压 AI 推理资源 |
| 梦境隐私保护 | 原始梦境完整明文输出日志与接口 | 多层敏感内容脱敏掩码,低权限角色无法查看完整私密原文 |
| 指标历史溯源能力 | 仅瞬时实时指标,无历史数据无法回溯故障 | 按小时时序文件持久归档,支持自定义时间段历史指标查询 |
| 资源故障预警 | 无阈值判定,仅被动查询,故障发现滞后 | 内置内存 / 索引 / 梦境三重阈值告警,超标自动生成风险日志 |
| 监控数据权限 | 全局无分级,任意访问者可读全部原始梦境 | RBAC 三级角色隔离,访客仅看聚合数据,仅管理员可读取完整梦境原文 |
| 采集任务容错 | CLI 读取失败直接丢失本次采集数据 | 失败任务持久缓存,服务重启自动恢复采集,无监控断档 |
六、插件完整升级部署操作步骤
- 备份原版内存可观测插件源码、工作空间 DREAMS.md、原生 CLI 采集脚本、监控配置文件,留存完整回滚包;
- 新建 8 个核心改造 JS 模块,粘贴全部优化代码;
- 插件启动入口执行时序存储目录初始化、失败采集任务缓存恢复、监控告警定时器加载;
- 接入 OpenClaw 调度服务,完全保留「无 shell、不依赖外部凭证、仅调用本地 CLI」安全核心特性,上层监控查询接口无需修改;
- 全场景验证:非法路径读取拦截、超大 DREAMS.md 分片解析不卡顿、高频采集触发限流、私密梦境脱敏输出、内存超标自动告警、访客无法读取原始梦境、重启恢复中断采集任务;
- 长期 7×24 小时持续监测压力测试,验证无文件越权读取、无主线程阻塞、无隐私明文泄露、指标时序完整归档、资源异常实时告警,监控稳定无断档。
七、改造方案总结
OpenClaw Memory Observability Plugin v1.1.0 作为安全级本地内存 / 索引 / 梦境监控插件,原生仅完成基础指标读取与简单文件解析,在文件访问安全、大文件解析性能、采集算力管控、梦境隐私、历史指标回溯、故障预警、分级权限、任务容错八大运维关键场景存在明显短板,海量梦境会话、7×24 小时持续生产监测场景下易出现隐私泄露、主服务卡顿、故障无法溯源等问题。本次全套二次改造完全兼容插件无 shell、零外部凭证、本地 CLI 采集的原生安全设计,从文件沙箱、分片解析、限流调度、隐私脱敏、时序存储、阈值告警、RBAC 权限、任务自愈底层全模块重构升级。改造后插件实现文件访问安全边界、轻量化低消耗采集、梦境隐私合规、历史故障可回溯、资源主动预警、分级数据访问、监控不间断自愈七大工业级运维能力,适配长期海量 AI 梦境会话、记忆引擎持续运行的企业级监测调优场景。