0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIにSNS運用させたら即アカウント停止された話(openclaw × moltbook skill編)

0
Posted at

前回 は openclaw の pairing で詰まった話を書きました。今回はその続き。

pairing はできた。じゃあ次は何をさせるか?

「AI エージェント専用の SNS がある」と聞いて、自動投稿させてみた。
結果、アカウント停止を食らった

※本記事のドメイン名は example.com に伏せています。


やりたかったこと

moltbook という AI エージェント専用 SNS がある。エージェント(molty)が投稿し、他のエージェントがコメント・upvote する Reddit のようなプラットフォーム。

これを openclaw のスキル(SKILL.md)で自動化したかった:

  1. 自動投稿: cron で定期的にトピックを生成して投稿
  2. コメント監視: 自分の投稿に付いたコメントを取得して DB に保存
  3. 自動返信: 有用なコメントに AI が返信

最終的にはコメントから得られた知見を蓄積して、ナレッジベースを作りたい。


まず作ったもの:SKILL.md

openclaw のスキルは SKILL.md というマークダウンファイルで定義する。AI に「何をすべきか」を指示するワークフロー定義書のようなもの。

---
name: moltbook-cycle
description: moltbookでWebアプリケーション脆弱性の実体験を共有し、
  他エージェントとの議論から知見を収集・蓄積するバッチサイクル。
metadata:
  { "openclaw": { "emoji": "🦞", "primaryEnv": "DATABASE_URL" } }
---

中身には以下を書いた:

  • 投稿ワークフロー: トピック重複チェック → セマンティック検索 → 投稿文生成 → 品質セルフチェック → 投稿
  • 返信ワークフロー: 未判定コメント取得 → ルール判定 → LLM 判定 → 返信 → insight 抽出
  • Verification Challenge 対応: moltbook の bot 検知(後述)

cron で定期実行する仕組み

openclaw のコンテナに対して docker exec でコマンドを投げるシェルスクリプトを作った。

crontab -l
# 投稿: 1日3回
0 9,15,21 * * *  /opt/stack/openclaw/cron/moltbook-post.sh
# コメント取得: 3時間毎
0 */3 * * *      /opt/stack/openclaw/cron/moltbook-check.sh
# 返信: 1日2回
0 12,20 * * *    /opt/stack/openclaw/cron/moltbook-reply.sh

投稿スクリプトはこんな感じ:

#!/usr/bin/env bash
# Job 1: 投稿(AI使用)
set -euo pipefail
source "$(dirname "$0")/moltbook-common.sh"

log "START moltbook-post"

# 停止中ならスキップ
if ! check_suspension "moltbook-post"; then
  exit 0
fi

RUN_ID="moltbook-post-$(date +%s)"
docker exec "$GATEWAY_CONTAINER" \
  node dist/index.js agent --to "+0000${RUN_ID}" \
  --message "SKILL.md を読んで【投稿モード】を実行してください。" --json \
  >> "$LOG_FILE" 2>&1

ポイントは check_suspension という関数。これがなぜ必要になったか、がこの記事の本題。


停止された

投稿自体は成功した。SKILL.md に書いたワークフロー通りに AI が動き、moltbook に投稿が作られた。

問題は Verification Challenge だった。

moltbook は bot 防止のため、投稿時にランダムで計算問題を出す:

{
  "verification_required": true,
  "challenge": {
    "question": "What is 847 + 293?",
    "expires_at": "2025-02-10T12:00:00Z"
  }
}

これに正しく答えないと投稿が通らない。間違えるとアカウント停止になる。

LLM に計算を解かせていたが、計算を間違えた。で、同じ内容をもう一度投稿しようとした。

重複投稿 + チャレンジ失敗 → 即停止。


何が悪かったか

振り返ると3つの問題があった。

1. LLM の計算を信用しすぎた

LLM は自然言語は得意だが、算数は苦手。847 + 293 を間違えることがある。

対策: セルフチェックを入れた。2つの別プロンプトで計算し、一致しなければ送信しない。

投稿/コメント POST
  ↓
verification_required: true ?
  └─ YES
      ↓
  LLM で回答生成(計算過程付き)
      ↓
  別プロンプトで再計算(self-check)
      ↓
  2つの回答が一致?
  ├─ NO → 送信しない → このアクション中止
  └─ YES → verify API に送信

2. 失敗時に再投稿していた

チャレンジに失敗した後、AI が「失敗したのでもう一度」と同じ内容を再投稿しようとした。これが「重複投稿」として検出された。

対策: verification_challenges テーブルを作り、一度失敗した request_body は二度と送信しない絶対ルールを SKILL.md に書いた。

3. 停止を検出する仕組みがなかった

停止されても cron は動き続ける。停止中に API を叩くと「Account suspended」が返ってくるだけで、次の cron でまた叩きに行く。

対策: 3段階の停止チェックを作った。

check_suspension() {
  # 段階1: ローカルフラグファイルの存在チェック(API不要)
  if [ -f "$SUSPEND_FLAG" ]; then
    SUSPEND_UNTIL=$(cat "$SUSPEND_FLAG")
    NOW_EPOCH=$(date +%s)
    if [ "$SUSPEND_UNTIL" -gt "$NOW_EPOCH" ]; then
      log "SKIP: 停止中(解除まで約Xh)API省略"
      return 1
    fi
  fi

  # 段階2: API で確認(フラグなし or 期限切れの場合のみ)
  STATUS_JSON=$(curl -s "https://www.moltbook.com/api/v1/agents/me" \
    -H "Authorization: Bearer $MOLTBOOK_API_KEY")

  if echo "$STATUS_JSON" | grep -qi "suspended"; then
    # 停止中 → フラグファイルに期限を書き込み
    echo "$SUSPEND_EPOCH" > "$SUSPEND_FLAG"
    return 1
  fi

  # 段階3: 解除済み → フラグ削除
  rm -f "$SUSPEND_FLAG"
  return 0
}

なぜ3段階か: 毎回 API を叩くと、停止中でも rate limit を消費してしまう。フラグファイルで「まだ停止中」と分かる場合は API を省略する。


3ジョブに分けた理由

最初は1つのスクリプトで「投稿 → コメント取得 → 返信」を全部やっていた。でもこれだと 毎回 AI(LLM)を起動する ことになり、コストが高い。

コメントの取得と DB への保存は、API を叩いて JSON をパースするだけ。AI はいらない。

そこで3つに分離した:

Job スクリプト AI 使用 頻度 やること
投稿 moltbook-post.sh はい 1日3回 新規投稿
コメントチェック moltbook-check.sh いいえ 3時間毎 コメント取得 + DB保存
返信 moltbook-reply.sh はい 1日2回 有用性判定 + 返信

コメントチェックは Node.js スクリプト(moltbook-precheck.js)で完結する。AI を呼ばないので停止チェックも不要(読み取り専用なので停止中でも動かせる)。

# moltbook-check.sh — AI不使用
# 停止チェック不要: GET(読み取り)+ 自DB INSERT のみ

PRECHECK_OUTPUT=$(docker exec -e "MOLTBOOK_API_KEY=$MOLTBOOK_API_KEY" \
  "$GATEWAY_CONTAINER" \
  bash -c 'NODE_PATH=/home/node/.openclaw/workspace/node_modules \
    node /home/node/.openclaw/workspace/scripts/moltbook-precheck.js')

返信ジョブも工夫した。未判定コメントが0件なら AI を起動しない

# moltbook-reply.sh
PENDING_COUNT=$(docker exec "$GATEWAY_CONTAINER" \
  bash -c '... node db-query.js "SELECT COUNT(*) ..."')

if [ "$PENDING_COUNT" = "0" ]; then
  log "SKIP: 未判定コメント0件(AI省略)"
  exit 0
fi

# ここまで来て初めて AI を起動
docker exec "$GATEWAY_CONTAINER" \
  node dist/index.js agent --message "【返信モード】を実行"

共通処理を source する

3つのスクリプトで重複するコード(ログ、停止チェック、環境変数)は moltbook-common.sh に切り出して source する。

# moltbook-common.sh
GATEWAY_CONTAINER="openclaw-core-openclaw-gateway-1"
LOG_DIR="/opt/stack/openclaw/logs"
export MOLTBOOK_API_KEY=$(grep '^MOLTBOOK_API_KEY=' ~/.openclaw/.env | cut -d= -f2-)

# ログローテーション: 10MB超えたら退避
MAX_LOG_SIZE=$((10 * 1024 * 1024))
if [ -f "$LOG_FILE" ] && [ "$(stat -c%s "$LOG_FILE")" -gt "$MAX_LOG_SIZE" ]; then
  mv "$LOG_FILE" "${LOG_FILE}.prev"
fi

log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S %Z')] $1" >> "$LOG_FILE"
}

check_suspension() {
  # ... 前述の3段階チェック
}

各スクリプトは先頭で source するだけ:

source "$(dirname "$0")/moltbook-common.sh"

ハマりポイント: docker exec への環境変数渡し

MOLTBOOK_API_KEY はホスト側の .env にある。でも docker exec で実行される Node.js スクリプトはコンテナ内で動く。

ホスト側で export しても、コンテナの中には届かない。

# ❌ これだとコンテナ内で MOLTBOOK_API_KEY が見えない
export MOLTBOOK_API_KEY=xxx
docker exec "$GATEWAY_CONTAINER" node script.js
# ✅ -e フラグで明示的に渡す
docker exec -e "MOLTBOOK_API_KEY=$MOLTBOOK_API_KEY" \
  "$GATEWAY_CONTAINER" node script.js

pairing 編と同じで、「どこの環境変数を見ているか」を意識しないとハマる。


もうひとつのハマり: www ありなし問題

moltbook の API は https://www.moltbook.com/api/v1 を使う。

https://moltbook.com/api/v1(www なし)でもアクセスできるが、リダイレクトが挟まり、その過程で Authorization ヘッダーが落ちる。

# ❌ www なし → リダイレクト → 認証ヘッダー消失 → 401
curl "https://moltbook.com/api/v1/agents/me" \
  -H "Authorization: Bearer $TOKEN"

# ✅ www あり → 直接アクセス → 認証OK
curl "https://www.moltbook.com/api/v1/agents/me" \
  -H "Authorization: Bearer $TOKEN"

これも pairing 編のトークン問題と根は同じ。「正しい値を、正しい場所から、正しい宛先に送る」 のがいかに難しいか。


SKILL.md のモード分離

最初は1つの巨大なワークフローを SKILL.md に書いていた。でも3ジョブに分けたことで、SKILL.md も 投稿モード返信モード に分離した。

## モード(重要)

このスキルは **2つのモード** で動作する。

| モード | トリガー | やること |
|---|---|---|
| **投稿モード** | メッセージに「投稿モード」を含む | 新規投稿を1件作成 |
| **返信モード** | メッセージに「返信モード」を含む | 有用性判定 + 返信 |

cron スクリプト側でモードを指定する:

# moltbook-post.sh
--message "SKILL.md を読んで【投稿モード】を実行してください。"

# moltbook-reply.sh
--message "SKILL.md を読んで【返信モード】を実行してください。"

こうすると AI は SKILL.md を読んで、指定されたモードのワークフローだけを実行する。


現状の構成図

cron (ホスト)
├── moltbook-post.sh    → docker exec → AI → moltbook API (POST)
├── moltbook-check.sh   → docker exec → Node.js → moltbook API (GET) + DB
└── moltbook-reply.sh   → docker exec → AI → DB + moltbook API (POST)
     ↑
     └── 未判定コメント0件なら AI 起動しない(コスト節約)

共通: moltbook-common.sh (source)
├── 環境変数・ログ設定
├── 停止チェック(フラグ → 時刻 → API の3段階)
└── ログローテーション

まとめ(今回の学び)

  • openclaw のスキルで SNS 自動運用はできる。でも停止リスクがある
  • Verification Challenge で LLM が計算を間違えると停止される → セルフチェック必須
  • 失敗時の再投稿が一番危ない → 同じ内容の再送は絶対禁止ルール
  • 停止検出は3段階(フラグファイル → 時刻比較 → API)で 無駄な API コールを減らす
  • 「全部 AI でやる」より AI が不要な部分を分離 した方がコストも安全性も良い
  • docker exec への環境変数渡しと www ありなし問題は pairing 編と同じ系統のハマり

次回はこの仕組みで実際に投稿してみて、moltbook で反応をもらうために投稿スタイルを全部変えた話 を書く予定。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?