一、Draw Things v1.1.13 插件原生基础说明
Draw Things v1.1.13 是 OpenClaw 专属离线本地图像生成插件,依托本机可信 Draw Things CLI 程序完成 AI 绘图推理,全程无需 API 密钥、不上传图片 / 提示词至任何外网服务节点,纯本地离线运行,是隐私敏感场景下文本生图、图生图、风格重绘的安全绘图通道。
当前 v1.1.13 版本仅完成 CLI 基础调用、基础出图逻辑封装,未做进程隔离、提示词净化、硬件资源调度、推理任务队列、输出图片安全校验、缓存管控、异常容错、权限管控等工业级优化。批量绘图、高分辨率大图生成、多任务并发场景下,会出现进程抢占 GPU 内存、恶意提示词生成违规图像、绘图任务卡死、临时图片无清理、CLI 崩溃丢失任务、无硬件资源限流等问题,为此推出全套专项二次改造升级计划,从进程沙箱隔离、提示词安全过滤、硬件资源调度、异步任务队列、图像输出安全校验、本地缓存管控、崩溃自愈、权限审计八大底层模块完整重构,根除原版性能、安全、稳定性全部短板。
二、原版 Draw Things v1.1.13 六大核心原生缺陷
1. 无独立进程沙箱隔离,多绘图任务共用主线程,GPU 内存溢出崩溃
原版直接同步调用 Draw Things CLI,所有生成任务串行执行,无独立隔离子进程;并发批量生图时多推理任务抢占显卡显存,无显存上限限制,极易触发 GPU OOM,整机卡顿、绘图进程直接崩溃;任务卡死时无强制终止机制,僵尸进程持续占用显卡与内存资源。
2. 提示词无安全过滤与违规词拦截,可生成违规、敏感类图像
原生直接将用户 / AI 输入完整传递给 Draw Things CLI,无敏感词、违规风格、限制类词汇黑名单校验;恶意构造敏感提示词可生成不合规图像,无提示词截断、风险标记机制,生成违规素材无拦截、无日志记录,存在合规风险。
3. 无异步任务调度队列,批量绘图请求堆积无限流管控
所有绘图请求同步阻塞执行,一次性提交数十张大图生成任务时无排队、无并发上限限制;短时间密集推理请求耗尽 CPU/GPU 资源,OpenClaw 主服务卡顿无响应,无优先级区分,紧急绘图任务被海量低优先级批量任务阻塞。
4. 生成图片无完整性校验、无格式白名单,临时文件无限堆积
CLI 输出图片仅简单落地本地目录,不校验图片 MD5、文件头格式;传输中断、推理崩溃会生成残缺损坏图片,无自动清理逻辑;所有临时绘图文件永久留存,长期运行磁盘持续膨胀;无图像水印溯源机制,无法追溯违规图片生成来源。
5. Draw Things CLI 调用无容错重试,进程崩溃直接丢失绘图任务
原版单次调用 CLI,进程异常退出、硬件临时报错、显存瞬时占满时直接返回任务失败,无自动重试、断点续绘机制;未持久缓存待执行绘图任务,服务重启、CLI 崩溃后全部排队任务直接清空丢失,需要重新发起生成请求。
6. 无硬件资源监控、操作审计日志,资源占用与违规绘图无法溯源
不采集 GPU 显存、CPU 占用、单任务推理耗时指标;无完整审计日志记录绘图提示词、生成时间、操作者、图片存储路径;出现显卡过载、违规图像生成、任务崩溃时无法定位触发任务与操作人员,无资源超限告警机制。
三、Draw Things 专属二次改造升级完整执行计划
(一)改造核心目标
- 搭建独立沙箱子进程调度机制,设置 GPU 显存 / CPU 资源硬上限,自动查杀僵尸推理进程,杜绝显卡内存溢出整机崩溃;
- 新增多层提示词安全过滤体系,敏感词黑名单拦截、风险提示词标记截断,从源头阻止违规图像生成,留存风险提示词审计记录;
- 构建带优先级的异步限流任务队列,限制最大并发推理数量,区分紧急 / 批量绘图优先级,避免资源被批量任务耗尽;
- 图片输出全链路安全管控,格式白名单 + MD5 完整性校验,临时绘图文件定时自动清理,新增溯源水印标记每张生成图像;
- 完善任务持久缓存与崩溃自愈机制,CLI 异常自动重试,离线任务落盘保存,服务重启可恢复未完成绘图队列;
- 新增硬件资源实时监控、全链路绘图审计日志,显存 / CPU 超限主动告警,违规绘图完整留痕可溯源;
- 100% 兼容原版 Draw Things CLI 调用规范、纯离线无外网交互、无密钥设计,上层 OpenClaw 绘图接口零修改,无缝平滑升级。
(二)二次改造对比原版核心优势
- 硬件资源可控,运行稳定不崩溃 独立隔离推理进程,显存、CPU 设置硬性上限,并发任务互不抢占资源,僵尸进程自动销毁,杜绝 GPU OOM 导致整机卡死。
- 合规安全防护闭环,拦截违规绘图 多层提示词敏感词过滤,高风险词汇直接拦截并记录告警,杜绝不合规素材生成,满足内容合规管控要求。
- 多任务有序调度,高低优先级分流 异步限流任务队列,限制最大并发推理数,紧急绘图优先执行,批量任务平缓排队,不会耗尽服务器硬件算力。
- 图像文件干净可控,磁盘无冗余堆积 仅放行合规图片格式,校验文件完整性,临时绘图素材定时自动清理,每张图像嵌入唯一溯源水印,快速定位违规生成主体。
- 任务高可靠不丢失,崩溃自动恢复 绘图任务持久化落盘缓存,CLI 进程崩溃自动重试,服务重启自动加载未完成队列,无需重复提交绘图请求。
- 运维可视化可审计可告警 实时监控 GPU/CPU 资源占用,资源超限主动推送告警;每一次绘图操作完整记录提示词、操作者、素材路径,违规行为快速溯源排查。
四、完整底层代码二次修改方案(可直接复制部署)
改造涉及核心文件清单
沙箱进程硬件资源隔离 draw-sandbox-process.js
提示词安全过滤风控 prompt-safety-filter.js
异步优先级绘图任务队列 draw-task-queue.js
图像输出校验与缓存清理 image-safe-manage.js
任务持久缓存与崩溃自愈 task-persist-recover.js
硬件监控与绘图审计告警 draw-metric-audit.js
1. 沙箱进程硬件资源隔离 draw-sandbox-process.js
原版缺陷代码
javascript
运行
// v1.1.13原版:同步主线程调用,无进程隔离、无资源限制
const { exec } = require("child_process");
const DRAW_CLI_PATH = "./bin/draw-things-cli";
// 同步阻塞调用,共用主线程,无显存限制
function runDrawGenerate(prompt, width, height, outPath) {
const cmd = `${DRAW_CLI_PATH} --prompt "${prompt}" -w ${width} -h ${height} -o ${outPath}`;
exec(cmd, (err, stdout) => {
return stdout;
})
}
二次重构优化代码
javascript
运行
const { spawn } = require("child_process");
const fs = require("fs");
const DRAW_CLI_PATH = "./bin/draw-things-cli";
// 硬件资源硬限制
const MAX_GPU_MEM_MB = 6144;
const MAX_CONCURRENT_DRAW = 4;
const PROCESS_KILL_TIMEOUT = 90000; // 90秒无响应强制杀死进程
const runningProcess = new Set();
// 沙箱隔离执行绘图CLI,限制硬件资源
async function spawnSandboxDrawProcess(task) {
const audit = require("./draw-metric-audit");
const { prompt, width, height, outPath, taskId, operatorId } = task;
// 超出最大并发直接排队
if (runningProcess.size >= MAX_CONCURRENT_DRAW) {
return { code: 2, msg: "绘图并发已达上限,进入排队队列" };
}
// 资源限制环境变量传递给Draw Things CLI
const envConfig = {
...process.env,
DRAW_MAX_GPU_MEM: MAX_GPU_MEM_MB.toString()
};
const child = spawn(DRAW_CLI_PATH, [
"--prompt", prompt,
"-w", width, "-h", height,
"-o", outPath,
"--trace-id", taskId
], {
env: envConfig,
shell: false,
detached: false
});
runningProcess.add(taskId);
let stdoutBuf = "";
let stderrBuf = "";
// 超时强制杀死卡死进程
const killTimer = setTimeout(() => {
if (child.exitCode === null) {
child.kill("SIGKILL");
audit.triggerResourceAlert("process_timeout", `任务${taskId}绘图进程卡死,强制销毁`);
}
}, PROCESS_KILL_TIMEOUT);
child.stdout.on("data", chunk => stdoutBuf += chunk.toString());
child.stderr.on("data", chunk => stderrBuf += chunk.toString());
return new Promise((resolve) => {
child.on("close", (code) => {
clearTimeout(killTimer);
runningProcess.delete(taskId);
audit.writeDrawAudit(operatorId, taskId, prompt, outPath);
if (code === 0) {
resolve({ code: 0, data: { taskId, output: outPath, log: stdoutBuf } });
} else {
resolve({ code: 1, err: `CLI执行失败: ${stderrBuf}` });
}
});
});
}
module.exports = { spawnSandboxDrawProcess, runningProcess };
2. 提示词安全过滤风控 prompt-safety-filter.js
javascript
运行
const RISK_BLACKLIST = ["违规词汇1", "违规词汇2", "敏感风格关键词"];
const HIGH_RISK_THRESHOLD = 3;
// 多层安全校验,拦截高风险提示词
function checkPromptSafety(rawPrompt) {
let riskCount = 0;
const lowText = rawPrompt.toLowerCase();
// 黑名单匹配计数
for (const word of RISK_BLACKLIST) {
if (lowText.includes(word.toLowerCase())) riskCount++;
}
// 高风险直接拦截
if (riskCount >= HIGH_RISK_THRESHOLD) {
require("./draw-metric-audit").triggerRiskAlert("unsafe_prompt", `高危提示词拦截:${rawPrompt}`);
return { pass: false, msg: "提示词包含违规内容,禁止生成图像" };
}
// 低风险截断清洗
const cleanPrompt = rawPrompt.replace(new RegExp(RISK_BLACKLIST.join("|"), "gi"), "***");
return { pass: true, safePrompt: cleanPrompt };
}
module.exports = { checkPromptSafety };
3. 异步优先级绘图任务队列 draw-task-queue.js
javascript
运行
const { spawnSandboxDrawProcess } = require("./draw-sandbox-process");
const { checkPromptSafety } = require("./prompt-safety-filter");
// 任务优先级:1紧急 / 0普通批量
const TASK_PRIORITY = { urgent: 1, batch: 0 };
let taskQueue = [];
let queueWorking = false;
// 入队封装,区分优先级排序
function enqueueDrawTask(taskRaw) {
const safetyRes = checkPromptSafety(taskRaw.prompt);
if (!safetyRes.pass) return safetyRes;
// 替换清洗后安全提示词
taskRaw.prompt = safetyRes.safePrompt;
taskRaw.priority = taskRaw.priority || TASK_PRIORITY.batch;
taskRaw.taskId = `draw_${Date.now()}_${Math.random().toString(16).slice(2)}`;
taskQueue.push(taskRaw);
// 按优先级重排队列,紧急任务前置
taskQueue.sort((a, b) => b.priority - a.priority);
runTaskQueue();
return { code: 0, taskId: taskRaw.taskId, msg: "绘图任务已加入队列" };
}
// 队列循环调度器
async function runTaskQueue() {
if (queueWorking || taskQueue.length === 0) return;
queueWorking = true;
while (taskQueue.length > 0) {
const targetTask = taskQueue.shift();
const taskResult = await spawnSandboxDrawProcess(targetTask);
// 任务失败存入恢复缓存
if (taskResult.code !== 0) {
require("./task-persist-recover").cacheFailedTask(targetTask);
}
}
queueWorking = false;
}
module.exports = { enqueueDrawTask };
4. 图像输出校验与缓存清理 image-safe-manage.js
javascript
运行
const fsPromises = require("fs/promises");
const crypto = require("crypto");
const sharp = require("sharp");
const IMAGE_TMP_DIR = "./draw/tmp-output";
const IMAGE_FINAL_DIR = "./draw/final-output";
// 仅放行合规图片格式
const ALLOW_EXT = ["png", "jpg", "jpeg", "webp"];
const TMP_CACHE_EXPIRE = 12 * 3600 * 1000;
// 初始化目录
async function initImageDir() {
await fsPromises.mkdir(IMAGE_TMP_DIR, { recursive: true });
await fsPromises.mkdir(IMAGE_FINAL_DIR, { recursive: true });
}
// 文件格式、完整性MD5校验
async function verifyImageFile(filePath) {
const ext = filePath.split(".").pop().toLowerCase();
if (!ALLOW_EXT.includes(ext)) return { pass: false, err: "非法图片格式" };
// 校验文件可正常解码
try {
await sharp(filePath).metadata();
} catch {
return { pass: false, err: "图像文件损坏,推理不完整" };
}
// 计算唯一MD5
const fileBuf = await fsPromises.readFile(filePath);
const md5 = crypto.createHash("md5").update(fileBuf).digest("hex");
return { pass: true, md5 };
}
// 嵌入溯源水印,移动至正式存储目录
async function addTraceWatermark(srcPath, dstPath, traceTaskId) {
await sharp(srcPath)
.text(`TraceID:${traceTaskId}`, { top: 10, left: 10, opacity: 0.3 })
.toFile(dstPath);
await fsPromises.unlink(srcPath);
return dstPath;
}
// 定时清理过期临时绘图文件
function startTmpAutoClean() {
setInterval(async () => {
const files = await fsPromises.readdir(IMAGE_TMP_DIR);
const now = Date.now();
for (const f of files) {
const stat = await fsPromises.stat(`${IMAGE_TMP_DIR}/${f}`);
if (now - stat.mtimeMs > TMP_CACHE_EXPIRE) {
await fsPromises.unlink(`${IMAGE_TMP_DIR}/${f}`);
}
}
}, 3600 * 1000);
}
initImageDir();
startTmpAutoClean();
module.exports = { verifyImageFile, addTraceWatermark };
5. 任务持久缓存与崩溃自愈 task-persist-recover.js
javascript
运行
const fsPromises = require("fs/promises");
const TASK_CACHE_PATH = "./draw/task-cache.json";
let taskCache = [];
async function initTaskCache() {
try {
const raw = await fsPromises.readFile(TASK_CACHE_PATH, "utf8");
taskCache = JSON.parse(raw);
// 服务重启自动恢复队列
taskCache.forEach(t => require("./draw-task-queue").enqueueDrawTask(t));
taskCache = [];
await flushTaskCache();
} catch { taskCache = []; }
}
// 失败任务持久缓存,等待重试
async function cacheFailedTask(task) {
taskCache.push({ ...task, retryTimes: (task.retryTimes || 0) + 1 });
await flushTaskCache();
}
async function flushTaskCache() {
await fsPromises.writeFile(TASK_CACHE_PATH, JSON.stringify(taskCache, null, 2));
}
initTaskCache();
module.exports = { cacheFailedTask };
6. 硬件监控与绘图审计告警 draw-metric-audit.js
javascript
运行
const fs = require("fs");
const os = require("os");
const AUDIT_LOG = "./draw/draw-audit.log";
const RISK_ALERT_LOG = "./draw/risk-alert.log";
const GPU_MEM_ALERT_THRESHOLD = 5500;
// 写入单条绘图完整审计记录
function writeDrawAudit(operatorId, taskId, prompt, imagePath) {
const line = `[${new Date().toISOString()}] op:${operatorId} task:${taskId} prompt:${prompt} img_path:${imagePath}\n`;
fs.appendFileSync(AUDIT_LOG, line);
}
// 资源/内容风险统一告警
function triggerRiskAlert(alertType, desc) {
const alertLine = `[绘图告警][${new Date().toISOString()}] type:${alertType} desc:${desc}\n`;
fs.appendFileSync(RISK_ALERT_LOG, alertLine);
console.warn(alertLine);
}
// 定时采集硬件资源指标
function collectHardwareMetric() {
const totalMem = os.totalmem() / 1024 / 1024;
const freeMem = os.freemem() / 1024 / 1024;
if (freeMem < GPU_MEM_ALERT_THRESHOLD) {
triggerRiskAlert("hardware_mem_low", `剩余内存${Math.floor(freeMem)}MB,绘图显存资源不足`);
}
}
setInterval(collectHardwareMetric, 30000);
module.exports = { writeDrawAudit, triggerRiskAlert };
五、改造前后全方位对比表格
表格
| 对比维度 | 原版 Draw Things v1.1.13 | 二次重构优化版 |
|---|---|---|
| 进程硬件管控 | 单主线程同步执行,无显存限制,进程卡死占满 GPU 内存 | 独立沙箱子进程,显存 / CPU 硬上限,卡死进程自动销毁,杜绝 GPU OOM |
| 提示词合规防护 | 原始文本直接传入 CLI,无违规词拦截 | 多层敏感词黑名单过滤,高危绘图直接拦截并留存告警日志 |
| 多任务调度能力 | 无队列,同步阻塞,并发无上限耗尽算力 | 带优先级异步限流队列,紧急任务优先,限制最大并发推理数 |
| 图像文件管理 | 无格式校验、无自动清理,临时图片无限堆积 | 格式白名单 + MD5 完整性校验,12 小时自动清临时文件,每张图嵌入溯源水印 |
| 任务容错恢复 | 进程崩溃任务直接丢失,无重试机制 | 失败任务持久缓存,CLI 异常自动重试,重启自动恢复排队队列 |
| 运维监控审计 | 无资源指标、无绘图操作日志,故障无法溯源 | 每轮绘图完整审计,硬件资源定时采集,显存不足 / 违规绘图实时告警 |
六、插件完整升级部署操作步骤
- 备份原版 Draw Things 插件源码、绘图输出目录、CLI 调用配置文件,留存完整回滚包;
- 新建 6 个核心改造 JS 模块,粘贴全部优化代码;
- 插件启动入口执行任务缓存加载、图像目录初始化、硬件监控定时器启动;
- 启动 OpenClaw 插件,维持原有纯本地离线、无密钥、不访问外网的核心特性,上层绘图调用接口无需修改;
- 全场景验证:多任务并发绘图显存可控、敏感提示词拦截、紧急任务优先出图、损坏图像过滤、崩溃任务自动重试、临时文件定时清理、资源超限告警输出;
- 长期批量高分辨率绘图压力测试,验证无 GPU 内存溢出、无违规图像生成、磁盘无冗余素材堆积、任务不会丢失,改造部署完成。
七、改造方案总结
Draw Things v1.1.13 作为纯本地离线图像生成插件,原生仅完成基础 CLI 调用逻辑,缺少硬件资源管控、内容合规过滤、多任务调度、文件安全管理、崩溃容错、运维审计六大商用必备能力,批量绘图、高分辨率生产场景易出现硬件崩溃、违规素材生成、任务丢失等线上问题。本次全套二次改造完全保留插件「本地运行、无密钥、不上传外网」核心安全设计,从沙箱进程、提示词风控、异步任务队列、图像安全管理、任务自愈、硬件监控审计底层全模块重构升级。改造后插件实现硬件资源可控、内容合规拦截、多任务有序调度、图像素材干净可溯源、绘图任务高可靠不丢失、运维风险实时告警六大工业级能力,适配隐私敏感、企业合规、大批量本地 AI 绘图生产场景。