OpenClaw 踩坑实录:私有化AI助手部署与运维的7次教训

部署自己的 AI 助手听起来很美好,真正跑起来才发现——坑比功能多。

本文记录了我在部署和运维 OpenClaw(私有化 AI 助手框架)过程中遇到的 7 个典型问题,覆盖了定时任务、Web 服务、图片处理、CSDN 发布等多个场景。每个坑都有一句话总结和解决方案,有的还画了配图,方便对照排查。

> 本文所有图片需手动上传到 CSDN,文中会标注图片位置。


先说背景

我的需求很明确:

  • 在自己的阿里云服务器上跑 AI 助手
  • 通过 QQ Bot 与我交互
  • 自动推送国际新闻、技术内容策展
  • 定期写 CSDN 博客

用的框架是 OpenClaw + QQ Bot 插件,模型接的 MiniMax。服务器是阿里云 ECS(Ubuntu),域名解析到 lsn.org.cn

需求不复杂,但每个环节都有意想不到的坑。


坑1:定时任务执行成功,但消息没收到

现象: cron 任务明明显示"ok",QQ 却收不到消息。

排查过程:

cron 任务配置的是每日 9:05 推送国际新闻,任务确实运行了,耗时 68 秒,状态 ok。但我去 QQ 一看——什么都没有。

看日志发现 isolated session 模式下,channel: "last" 这个 delivery 模式根本不知道该往哪儿发消息。

{  "delivery": {    "mode": "announce",    "channel": "qqbot",    "to": "qqbot:c2c:1D04898DC3290EA089D83BB0A68A385B",    "accountId": "default"  }}

根因: isolated session 没有"上一个 channel"这个概念,channel: "last" 找不到目标。cron 任务跑完了,但消息投递失败,状态却显示 ok——这是最迷惑的地方。

解决方案:

openclaw cron edit "<任务ID>" \  --announce \  --channel qqbot \  --to qqbot:c2c:1D04898DC3290EA089D83BB0A68A385B \  --best-effort-deliver

核心就是把 channel"last" 改成具体的 "qqbot",并明确 to 地址。同样的问题影响了两个 cron:国际新闻推送和知识库自动构建,一起修复。


坑2:GitHub push 总是超时

现象: 每次 hexo generate 之后想 push 到 GitHub,总是超时失败。

根因: 服务器在阿里云,访问 github.com:443 间歇性超时,SSH 和 HTTPS 都试过,不稳定。

解决方案: 使用 HTTPS + Personal Access Token 方式。

git remote set-url origin https://ghp_token:xenial@github.com/username/repo.git

虽然不如 SSH 优雅,但稳定可靠。Token 要有 repo 权限,去 GitHub Settings → Developer settings → Personal access tokens 生成。


坑3:nginx alias 配置错了,下载链接访问 404

现象: 新闻 MD 文件存在 data/ 目录,但通过 URL 下载时总是 404。

根因: nginx 的 alias 路径配置错误。新闻文件实际在 /home/admin/.openclaw/workspace/data/,但 nginx alias 指向了 website/downloads/,两边的文件没同步。

解决方案: 修改 nginx 配置文件,让 alias 直接指向 data/ 目录:

location /downloads/ {    alias /home/admin/.openclaw/workspace/data/;    autoindex on;}

改完记得 nginx -s reload


坑4:Hexo 文章目录批量生成,正则写错了

现象: 执行批量生成脚本,所有文章都生成了"Hello World"模板目录。

排查: 看一下正则提取逻辑:

# 错误写法:从文本提取标题title = re.search(r'<a[^>]*>([^<]+)</a>', text).group(1)

实际 HTML 结构是:<a title="真实标题"></a>真实标题

所以正则匹配到的是空白!

根因: HTML 结构是 标题,文本在 title 属性里,不在标签内容里。正则写反了,导致所有文章都套用了默认的"Hello World"。

解决方案:

# 正确写法:从 title 属性提取title = re.search(r'<a[^>]*title="([^"]+)"', text).group(1)

教训:写爬虫/解析器之前,一定要先看实际 HTML 结构,不要凭感觉写正则。


坑5:CSS 加载顺序导致布局被覆盖

现象: 修改了文章详情页的 CSS,刷新之后样式没变化,或者被其他样式覆盖。

根因: 多个 CSS 文件加载顺序不对,后加载的 CSS 优先级更高,把之前的样式覆盖了。具体是 enhancements.cssarticle-style.css 之后加载,但 article-content-wrappermax-width 被后者覆盖。

解决方案:enhancements.css 中对被覆盖的规则加 !important,或者重新调整加载顺序。更根本的解法是确认哪个 CSS 文件应该生效,避免全局样式污染。


坑6:国际新闻 cron 状态 ok 但文件没生成

现象: cron 任务状态显示 ok,但检查 data/ 目录发现新闻文件根本没生成。

排查过程:

  • 看 cron 任务的 lastRunStatus: oklastDurationMs: 68033——跑了 68 秒
  • data/ 里没有今天的文件,最新的只到 4 月 3 日
  • 9:05 触发的任务,状态 ok,耗时 68 秒,文件不存在

结论:任务跑完了,但内容生成失败了,状态却误报 ok。

根因: 任务确实执行了,但子 agent 在某个步骤失败了(比如 searxng 搜索超时),错误被吞掉没有上报。

教训: 看状态不能只看 ok,还要结合实际结果文件验证。定期检查 cron 输出是否真的生成了预期文件。


坑7:CSDN 外链图片被屏蔽

现象: 通过 API 发布文章,图片用外链 URL,CSDN 显示不出来。

排查:

CSDN 编辑器支持从 URL 插入图片,但通过 API 提交的内容里,外部图片 URL 会被过滤掉。在 PC 浏览器能看到(因为浏览器直接请求了),在 APP 或部分客户端显示破图。

尝试的方案:

| 方案                  | 结果                              |
|-----------------------|-----------------------------------|
| 外链 URL              | ❌ 部分客户端破图                 |
| base64 编码           | ❌ APP 过滤 base64                |
| CSDN 图片上传接口      | ❌ 需要实时签名,无法调用         |
| Markdown 编辑器接口    | ❌ HMAC 签名路径不同,不匹配      |

最终解决方案:

先把文章以纯文字版发布,确保文章能正常显示。然后手动去 CSDN 编辑器里,把每张图片手动上传替换。

这个方案最稳妥——只需要一次手动操作,之后就能正常显示了。


总结:踩坑心得

  • isolated session 的 delivery 要显式指定 channel,不要用 last
  • GitHub 访问不稳定就用 HTTPS token,稳定压倒一切
  • nginx 配置改完一定要 reload 并测试,不能只靠眼盯配置文件
  • 正则一定要对着实际 HTML 结构写,写完要测试
  • CSS 加载顺序问题用 !important 临时解决,但要记录下来彻底修
  • cron 任务不仅要查状态,还要验证输出文件
  • CSDN 图片问题没有完美的自动化方案,最终还是得手动来一次


> 首发于我的技术博客:https://www.lsn.org.cn

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐