本文记录了我用 OpenClaw + ComfyUI 在 Mac上搭建本地 AI 出图流水线的完整过程。从 SDXL 黑图到 SOUL.md 优先级陷阱,两天时间踩了七个大坑。希望这些记录能帮后来人少走弯路。
TL;DR
用 Telegram 发一句中文描述,本地跑 SDXL 推理出图,全程免费、数据不出机器。听起来很美好,实际搭建过程充满了各种诡异问题——SDXL 生成全黑图、DeepSeek 在 Agent 框架里完全失控、Token 消耗 14 次请求烧掉 $4、Agent 的人格指令被悄悄覆盖导致 Claude 反复说"我不会画画"……
最终跑通了,还做了模型热切换,从 0.07/次随便选。下面是完整记录。
架构总览
整套系统的数据流很简单:
用户(Telegram) → OpenClaw 网关(Docker:18789) → painter agent(LLM)
↓
理解需求 + 生成 prompt
↓
paint-dispatch.sh → comfyui-client.py
↓
ComfyUI(localhost:8188, MPS 加速)
↓
出图 → 发回 Telegram
目前跑着五个 Agent,各司其职:
| Agent | Telegram Bot | 模型 | 干什么的 |
|---|---|---|---|
| main | @Openclaw0_2026_bot | Claude Sonnet 4.6 | 日常聊天 |
| painter | @visual_muse00bot | 可热切换 | 专门出图 |
| pm | @neopm0_bot | DeepSeek | 项目管理 |
| secretary | @neoassist0_bot | DeepSeek | 情报推送 |
| monitor | @monitor0neo_bot | DeepSeek | 系统监控 |
硬件是 MacBook Pro M5 Max 64GB,ComfyUI 跑在本地,MPS 加速,SDXL 1024×1024 大概 15-20 秒一张,1.6 it/s。对于个人用途完全够了。
两天工作情况
第一天,上午搞架构设计和 ComfyUI 安装。模型用 hf-mirror.com 镜像下载(国内直连 HuggingFace 你懂的),SDXL Base 6.5GB、Flux GGUF 2.4GB、VAE、CLIP、T5 编码器,全套拉下来。下午把 Docker 容器搭起来,写了五个 Agent Skill 和 ComfyUI API 客户端。晚上加了风格模板和失败重试策略,发了 v1.0.0。
第二天,全天踩坑。黑图、模型暴走、Token 爆炸轮番上演。晚上终于找到核心问题——SOUL.md 优先级被污染,修复后一切正常。顺手写了模型热切换脚本,发布 v1.3.0。
第三天,试图把单次成本从 $0.07 压到零,结果越改越差,折腾一圈回到原点后才意识到问题根本不在模型选择上。找到真正的根因后,反而一行命令就修好了。
下面详细说说每个坑。
坑 1:SDXL 黑图
表现:生成的 PNG 全黑,文件只有 4-5KB。正常的图应该 1-2MB。
这个问题困扰了我好几个小时。一开始以为是 workflow 配置问题,后来查资料才知道,SDXL 的 VAE 在 fp16 精度下有个已知的数值溢出 bug,在 Metal/MPS 设备上尤其容易触发。VAE 解码时会产生 NaN,直接导致输出全黑。
试了三个方案:
- 下载
madebyollin/sdxl-vae-fp16-fix替代 VAE → 有改善但没完全解决 - 在 workflow 模板里加独立 VAELoader 节点 → 同上
- ComfyUI 启动参数加
--fp32-vae→ 彻底解决
最终的启动命令:
# ~/ai-studio/start_comfyui.sh
python main.py --listen 0.0.0.0 --port 8188 --highvram --fp32-vae
注意是 --fp32-vae,不是 --force-fp32-vae,后者压根不存在,会直接报错。
给 M 系列 Mac 用户的建议:别犹豫,直接加 --fp32-vae。64GB 统一内存完全扛得住 fp32 的 VAE,不差这点显存。--highvram 也建议开,让模型常驻 GPU 不要来回搬运。
坑 2:DeepSeek 在 Agent 框架里完全失控
表现:让 painter agent 画图,它不看 Skill 文档,自己从零开始写 Python 脚本调 ComfyUI API。日志里一堆"需要安装 requests 模块""让我创建一个直接调用 ComfyUI API 的 Python 脚本"。
这个坑的本质不是 DeepSeek 不行,而是它的工具调用能力在 Agent 框架里跟不上。直接调 API 让它输出结构化 JSON,没问题,很稳。但一旦放进 OpenClaw 这种多步骤 Agent 流程里——需要先读 Skill 文档,再理解出图流程,再组装 JSON 参数,再调 shell 脚本——它就开始"自由发挥"了。
跟 Claude 对比一下就很明显:
| 指标 | DeepSeek | Claude Sonnet |
|---|---|---|
| 黑图率 | ~75% | 0% |
| 单次耗时 | 30-45 分钟 | 1-2 分钟 |
| 会不会选 checkpoint | 不会,永远用 SDXL Base | 会 |
| 遵守 Skill 指令 | 基本无视 | 严格遵守 |
后来换成 Claude Sonnet 4.6,第一次就出图成功。代价是每次 ~$0.07,但至少能用。
坑 3:Token 消耗爆炸
表现:14 次请求,136 万 token,$4.12。平均每次请求消耗大约 10 万 token。
问题出在几个地方:compaction 策略是 safeguard(基本不压缩)、没设 contextTokens 上限、workspace 目录里一堆大文件被注入到上下文、session 历史堆积到 9.9MB。
修复很简单,在 openclaw.json 里加两个配置:
"agents": {
"defaults": {
"contextTokens": 32000,
"compaction": {
"mode": "default",
"reserveTokens": 4000
}
}
}
单次成本从 0.07。
这里有个经验值:32K 是出图场景的甜区。16K 太小,agent 读完 SKILL.md 之后上下文就没什么空间执行了;不设限制的话,历史对话和 workspace 文件会把 token 撑爆。
坑 4:SOUL.md 优先级陷阱(最坑的一个)
表现:Claude 反复回复"抱歉,我没有生成图像的能力——我是个文字/代码型 AI",还贴心地推荐去用 Midjourney 和 DALL·E。
我的 SOUL.md 明明白白写着"你是画师,用 paint-dispatch.sh 出图",但 Claude 就是不听。
排查过程:
- 检查
agentDir里的 SOUL.md → 内容没问题 - 去看 session jsonl,看 Claude 实际收到的 system prompt → 发现读取的不是我写的那份
- 检查
workspace-painter/目录 → 里面有另一份 SOUL.md,是 OpenClaw 自动生成的通用人格模板
这就是根因了。OpenClaw 会给每个 agent 自动创建 workspace-{agentId}/ 目录,里面的 SOUL.md 优先级高于 agentDir 里的那份。 这个行为在文档里没有明确说明,属于隐藏机制。
更坑的是,agent 在运行过程中还会往 workspace 里写文件,包括自己修改 SOUL.md。所以即使你一开始的 SOUL.md 是对的,跑着跑着也可能被"污染"。
修复方法很粗暴但有效:
# 用正确版本覆盖 workspace 里的
cp /home/node/.openclaw/agents/painter/agent/SOUL.md \
/home/node/.openclaw/workspace-painter/SOUL.md
# 清理 agent 之前乱写的垃圾文件
rm -f /home/node/.openclaw/workspace-painter/cyberpunk_cat.*
rm -f /home/node/.openclaw/workspace-painter/*.svg
rm -f /home/node/.openclaw/workspace-painter/*.py
docker restart openclaw-gateway
覆盖后重启,Claude 立刻回复"好的,马上画!"然后成功返回了一张赛博朋克猫。
给 OpenClaw 开发者的忠告:agent 行为异常的时候,第一件事不是改配置,而是去看 workspace-{agentId}/SOUL.md 里到底写了什么。
坑 5:越优化越差的恶性循环
这个坑是心态上的。Claude 出图成功但每次 $0.07,我就想省钱切到 DeepSeek。然后开始了一系列迷之操作:
| 尝试 | 配置 | 结果 |
|---|---|---|
| 1 | DeepSeek + tools.profile=messaging | 无视 SOUL.md,输出一篇创作指南 |
| 2 | DeepSeek + tools={deny:[*]} | 输出 200 行 Canvas 代码"画画" |
| 3 | Claude + tools.profile=full | 自己画 SVG,不调 ComfyUI |
| 4 | Claude + 禁 sessions_spawn | 还是画 SVG |
越改越离谱。后来发现,这些"优化"全部是在错误的方向上使劲——根因是 workspace 里的 SOUL.md 被污染了(坑 4),跟模型选择和工具配置没有任何关系。
教训就一句话:先确认基础设施没问题,再去调参数。在错误的地基上盖楼,盖得越高倒得越惨。
坑 6:LanceDB 记忆系统崩溃
表现:Cannot find module '@lancedb/lancedb'。
LanceDB 底层是 Rust 编译的原生二进制,跟 ARM Docker 容器不兼容,每次容器重建都会丢失。这个问题目前没有优雅的解法,直接切到 MEMORY.md 文件记忆方案绕过了。文件系统不挑架构,稳定得多。
坑 7:sessions_spawn 导致管道卡死
Claude 有时候会"聪明过头",自动创建子会话来拆解任务,结果把整个出图管道卡住了。
修复:在 painter agent 配置里把子会话相关权限全部干掉。
"tools": {
"profile": "full",
"deny": ["sessions_spawn", "sessions_yield"]
},
"subagents": { "allowAgents": [] }
画图这种任务就不需要什么子会话协作,一个 agent 从头到尾干完就行。
核心代码
paint-dispatch.sh
整套系统的中枢。接收 JSON,调 ComfyUI,返回文件路径。逻辑很直白,没有花活:
#!/bin/bash
set -e
JSON_INPUT="${1:-$(cat)}"
[ -z "$JSON_INPUT" ] && echo "ERROR: 没有收到 JSON 输入" && exit 1
POSITIVE=$(echo "$JSON_INPUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('positive',''))")
NEGATIVE=$(echo "$JSON_INPUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('negative','lowres, bad anatomy'))")
WORKFLOW=$(echo "$JSON_INPUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('workflow','sdxl_basic.json'))")
SEED=$(echo "$JSON_INPUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('seed',-1))")
# 检查 ComfyUI 是否在跑
COMFY_CHECK=$(curl -s --connect-timeout 5 http://host.docker.internal:8188/system_stats 2>/dev/null)
[ -z "$COMFY_CHECK" ] && echo "ERROR: ComfyUI 未运行" && exit 1
# 执行出图
RESULT=$(python3 tools/comfyui-client.py \
--workflow "workflows/$WORKFLOW" \
--positive "$POSITIVE" --negative "$NEGATIVE" \
--seed "$SEED" --output "$OUTPUT_DIR" 2>&1) || true
# 检查结果
LATEST_FILE=$(ls -t "$OUTPUT_DIR"/*.png 2>/dev/null | head -1)
[ -z "$LATEST_FILE" ] && echo "ERROR: 未找到生成的图片" && exit 1
FILE_SIZE=$(( $(stat -c%s "$LATEST_FILE" 2>/dev/null || stat -f%z "$LATEST_FILE") / 1024 ))
echo "SUCCESS"
echo "FILE: $LATEST_FILE"
echo "SIZE: ${FILE_SIZE}KB"
模型热切换
一行命令在不同模型之间切换,成本从 0.07 自由选择:
bash ~/openclaw/switch-painter-model.sh gemini # ~$0.005/次
bash ~/openclaw/switch-painter-model.sh gpt-nano # ~$0.003/次
bash ~/openclaw/switch-painter-model.sh claude # ~$0.07/次(最强)
bash ~/openclaw/switch-painter-model.sh claude-haiku # ~$0.01/次
SOUL.md
让 LLM 知道自己是画师而不是聊天机器人的关键。必须同时存在于 agentDir 和 workspace-painter/ 两个位置(参考坑 4):
你是 Visual Muse 画师。你的工作是接收用户的中文画图请求,生成图片并返回结果。
核心规则:
1. 必须通过 paint-dispatch.sh 出图,禁止自己写脚本
2. 禁止生成 SVG/Canvas/HTML 代替真实图片
3. 禁止说"我是语言模型不能生成图片"
出图命令:
echo '{"positive":"...","negative":"...","workflow":"sdxl_basic.json"}' \
| bash /home/node/.openclaw/workspace/tools/paint-dispatch.sh
成本对比
所有模型通过 Ofox 统一调用,不用分别注册。ComfyUI 本地推理完全免费,花钱的只有 LLM 理解需求和生成 prompt 那一轮。
| 模型 | 输入价/1M token | 输出价/1M token | 单次出图成本 | 工具调用 |
|---|---|---|---|---|
| Claude Sonnet 4.6 | $3 | $15 | ~$0.07 | 强 |
| Claude Haiku 4.5 | $0.80 | $4 | ~$0.01 | 中 |
| Gemini 2.5 Flash | $0.15 | $0.60 | ~$0.005 | 有 |
| GPT-5 Nano | $0.05 | $0.40 | ~$0.003 | 有 |
| GPT-4.1 Mini | $0.40 | $1.60 | ~$0.005 | 有 |
| DeepSeek V3.2 | $0.14 | $0.28 | ~$0.002 | 弱 |
踩完这些坑之后的经验
关于 OpenClaw:workspace-{agentId}/ 是个隐藏的优先级陷阱,agent 异常先去看它。contextTokens=32000 是出图场景的平衡点。一定要 deny sessions_spawn,不然 Claude 会自作主张创建子会话。
关于 ComfyUI:M 系列 Mac 必须 --fp32-vae,这是 SDXL 黑图的唯一解。--highvram 对 64GB 机器是标配。
关于省钱:不要先研究怎么省钱,先研究怎么跑通。用最强的模型验证流程,确认整条链路没问题了,再逐步降级测试便宜模型。"便宜但不听话"比"贵但能用"亏得多——光是调试浪费的 token 就远超模型本身的价差。还有一点容易踩坑:DeepSeek 直接调 API 时 JSON Mode 很稳,但放进 Agent 框架里就完全不是那么回事。不要混淆这两个场景。
环境速查
| 项目 | 值 |
|---|---|
| 硬件 | MacBook Pro M5 Max 64GB / 2TB |
| ComfyUI | v0.17.0, localhost:8188, --highvram --fp32-vae |
| SDXL 模型 | SDXL Base, DreamShaper XL, Juggernaut XL, Animagine XL |
| LoRA | detail_tweaker_xl, sdxl_offset_noise |
| OpenClaw | v2026.3.13 |
| 网关 | Docker openclaw-gateway:18789 |
| 配置 | ~/openclaw/config/openclaw.json |
| SOUL.md | agentDir + workspace-painter/ 两份必须同步 |
| 出图脚本 | paint-dispatch.sh + comfyui-client.py |
| Workflows | sdxl_basic.json, sdxl_hires.json |
| API 平台 | Ofox (ofox.ai) |
| GitHub | github.com/baobaodawang-creater/visual-muse |
下一步
眼下在测试 Gemini 2.5 Flash 做 painter,如果它能老老实实调 paint-dispatch.sh,单次成本就能从 0.005。如果 Gemini 也不听话,还有 GPT-4.1 Mini 和 Claude Haiku 可以试。
终极目标是本地跑 Ollama + Qwen2.5-7B,真正做到 $0.00/次。M5 Max 跑 7B 模型大概 60-100 tok/s,2 秒出一个 prompt,理论上完全可行。
后面还打算加 ControlNet(控制构图和姿势)和 IP-Adapter(角色一致性),再探索一下 Wan2.1 做视频生成和分镜自动化。
如果这篇文章对你有帮助,点个赞就是最大的支持。有问题欢迎评论区交流。