NemoClawとSlackを連携したブログ投稿自動化の仕組み
Slackでメンションするだけで、Hugo製テックブログへの記事投稿が自動化できる仕組みを構築しました。記事中にダイアグラムの指示を書いておけば、LLMがMermaid記法で図を自動生成し、画像として記事に挿入します。さらに、すべてのステップでSlackスレッドを通じた対話的な確認が可能で、ダイアグラムの修正要望やHugoプレビューの確認もSlack上で完結します。本記事では、そのアーキテクチャと構成要素について紹介します。
背景と課題
当社のテックブログはHugo + GitLabで運用しています。記事を投稿するには以下の手順が必要でした。
- リポジトリをクローン
- ブランチを作成
hugo newでMarkdownファイルを作成- 記事を執筆し、画像を配置
- commit & push
- マージリクエストを作成
- 承認者がレビュー・マージ
エンジニアにとっては慣れた操作ですが、Git操作に馴染みのないメンバーにとってはハードルが高く、投稿の妨げになっていました。加えて、技術記事にはアーキテクチャ図やフロー図が欠かせませんが、作図ツールでの作成は手間がかかります。
そこで、Slackからファイルを添付するだけで投稿でき、図も自動生成される仕組みを構築しました。
システム構成

全体フロー
1. Slackメンション + .md & 画像添付
↓
2. 事前チェック(ブランチ重複・既存記事の有無)
→ 懸念があればSlackスレッドで確認(はい/いいえ)
↓
3. Slackからファイルをダウンロード
↓
4. .md内のDIAGRAMマーカーを検出
→ Ollama (LLM) でMermaidコード生成 → PNG変換
→ Slackに画像をアップロードして確認
→ 修正要望があればフィードバックを反映して再生成(最大3回)
↓
5. ローカルcommit → Hugoプレビューサーバー起動(Docker)
→ SlackにプレビューURLを送信
→ ブラウザで最終確認(OK / キャンセル)
↓
6. GitLabにgit push
↓
7. Slackにマージリクエスト作成URLを返信
↓
8. 承認者がレビュー → masterにマージ → 公開
各ステップでSlackスレッドを通じた対話が可能で、問題があればいつでもキャンセルできます。
コンポーネント一覧
| コンポーネント | 役割 |
|---|---|
| NVIDIA NemoClaw | エージェントのセキュアな実行環境・オーケストレーション |
| OpenShell | サンドボックスランタイム(Landlock + seccomp + netns) |
| Ollama (qwen2.5:32b) | ローカルLLM推論エンジン。エージェント応答およびMermaidコード生成 |
| Mermaid CLI | Mermaid記法のダイアグラムをPNG画像にレンダリング |
| Slack Bridge | Socket Modeでメッセージを受信し、処理を振り分け、対話型確認フロー管理 |
| Hugo (Docker) | プレビューサーバー。投稿前にブラウザで記事の見栄えを確認 |
| GitLab | ソースコード管理・MRベースのレビューフロー |
NemoClawとは
NemoClawは、NVIDIAが2026年3月のGTCで発表したオープンソースのエージェントオーケストレーションプラットフォームです。Apache 2.0ライセンスで提供されています。
主な特徴は以下の通りです。
- サンドボックス実行: エージェントをLinuxのセキュリティ機構(Landlock, seccomp, netns)で隔離
- ポリシーベースのネットワーク制御: エージェントがアクセスできる外部サービスをYAMLで定義
- 推論ルーティング: ローカルのOllamaやNVIDIA NIM、クラウドAPIなど複数の推論バックエンドを切り替え可能
- プラグインシステム: OpenClawとの統合によるスラッシュコマンドやエージェント拡張
今回の構成では、NemoClawのサンドボックス内でOpenClawエージェントを動作させ、Slackからの一般的な問い合わせにはLLMが応答し、ブログ投稿コマンドにはGitLab連携スクリプトが処理を行います。
構築のポイント
1. Slack Bridge — Socket Modeによるリアルタイム連携
Slack Bridgeは@slack/boltを使ったNode.jsアプリケーションです。Socket Modeを採用しているため、Webhook URLの公開が不要で、社内ネットワーク内のサーバーからでも接続できます。
const app = new App({
token: SLACK_BOT_TOKEN,
appToken: SLACK_APP_TOKEN,
socketMode: true,
});
app.event("app_mention", async ({ event, say, client }) => {
const text = event.text.replace(/<@[A-Z0-9]+>\s*/g, "").trim();
if (text.startsWith("ブログ投稿")) {
await handleBlogPost(event, say, client);
} else if (text.startsWith("ブログ削除")) {
await handleBlogDelete(event, say);
} else {
// 通常のメッセージはサンドボックス内のエージェントに転送
const response = await runAgentInSandbox(text, sessionId);
await say({ text: response, thread_ts: event.ts });
}
});
メンションのテキスト内容によって処理を振り分けます。
ブログ投稿 <slug> <タイトル>→ 添付ファイルをダウンロードしてGitLabへpushブログ削除 <slug>→ GitLabからブランチを削除- それ以外 → NemoClawサンドボックス内のLLMエージェントが応答
2. ファイルのダウンロードと配置
Slackに添付されたファイルはurl_private_downloadから取得できます。Bot TokenをAuthorizationヘッダーに付与してダウンロードします。
async function downloadFile(url, dest) {
return new Promise((resolve, reject) => {
https.get(url, {
headers: { Authorization: `Bearer ${SLACK_BOT_TOKEN}` }
}, (res) => {
const ws = fs.createWriteStream(dest);
res.pipe(ws);
ws.on("finish", () => ws.close(resolve));
});
});
}
ダウンロードしたファイルは以下のルールで配置されます。
.mdファイル →content/en/post/<slug>/index.md- 画像ファイル →
content/en/post/<slug>/img/
3. Git操作の自動化
ブログ投稿スクリプトは以下の手順を実行します。
# masterを最新化
git checkout master && git pull origin master
# ブランチ作成
git checkout -b blog/<slug>
# コンテンツ配置
mkdir -p content/en/post/<slug>
cp index.md content/en/post/<slug>/
# front matterがなければ自動付与
# commit & push
git add content/en/post/<slug>/
git -c user.name="NemoClaw Bot" -c user.email="nemoclaw-bot@elspina.tech" \
commit -m "Add blog post: <title>"
git push origin blog/<slug>
コミットはNemoClaw Bot名義で行われ、コミットメッセージには投稿者名を記録します。
4. LLMによるダイアグラム自動生成
本システムの特徴的な機能の一つが、記事中のダイアグラムをLLMが自動生成する仕組みです。
Gemini API検討とローカルLLMへの移行
当初、ダイアグラム生成にはGoogle Gemini APIの画像生成モデル(gemini-2.5-flash-image)を検討しました。しかし、無料枠では画像生成モデルのクォータが0に設定されており、有料プランが必要でした。
そこで方針を切り替え、既にNemoClawの推論バックエンドとして稼働しているOllama (qwen2.5:32b) にMermaid記法のダイアグラムコードを生成させ、Mermaid CLI (mmdc) でPNG画像にレンダリングする構成としました。この構成には以下のメリットがあります。
- 追加コストゼロ: ローカルLLMを使うため、API費用が発生しない
- 社外へのデータ送信なし: 記事の内容がクラウドに送信されない
- 編集可能: 生成されたMermaidコードを手動で微調整することも可能
DIAGRAMマーカーの仕組み
記事を執筆する際、図が必要な箇所にHTMLコメント形式のマーカーを記述します。
<!-- DIAGRAM: システム全体のアーキテクチャ図。
Slack、NemoClaw、GitLabの連携フローを示す -->
投稿時、システムは以下の手順で自動処理します。
# 1. .md内のDIAGRAMマーカーを検出
grep -n '<!-- *DIAGRAM:' "$MD_FILE"
# 2. マーカーの説明文 + 記事全文をLLMに送信
curl -sf "$OLLAMA_URL/api/generate" \
-d '{"model":"qwen2.5:32b", "prompt":"...Mermaid記法で図を生成..."}'
# 3. 生成されたMermaidコードをPNGにレンダリング
mmdc -i diagram.mmd -o diagram.png -w 1600 -H 900 -b white
# 4. マーカーを画像参照に置換
# <!-- DIAGRAM: ... --> → 
LLMには記事の全文をコンテキストとして渡すため、記事の内容に即した図が生成されます。複数のマーカーがある場合は、それぞれ独立して処理されます。
Slack経由の対話型レビュー
生成されたダイアグラムは、自動的にSlackスレッドに画像としてアップロードされます。投稿者はその場で確認し、以下のいずれかで返答します。
- OK → そのまま採用
- 修正要望を自由記述(例:「矢印を逆にして」「Sandboxを左に移動して」)→ フィードバックを反映して再生成
- いいえ → ダイアグラムをスキップ
- 5分間無応答 → 現在の版を自動採用
修正は最大3回まで可能です。フィードバックは前回のMermaidコードとともにLLMに渡されるため、差分修正が効率的に行われます。
Mermaid CLIの選定
ダイアグラムのレンダリングにはMermaid CLI (@mermaid-js/mermaid-cli)を採用しました。
# インストール
npm install -g @mermaid-js/mermaid-cli
# レンダリング
mmdc -i input.mmd -o output.png -w 1600 -H 900 -b white
Mermaidはフローチャート、シーケンス図、ER図など多様なダイアグラムをテキストベースで記述できます。LLMとの相性が良く、自然言語の説明からダイアグラムコードへの変換精度が高いことが選定理由です。
レンダリングに失敗した場合は、LLMに修正を依頼するリトライ機構も組み込んでいます。
5. Hugoプレビューによる最終確認
GitLabへのpush前に、Dockerコンテナ上でHugoプレビューサーバーを自動起動し、投稿者にブラウザで記事の見栄えを確認してもらいます。
# docker-composeでHugoプレビューを起動
docker-compose -f docker-compose.yml -f server.yml up -d ananke
# http://<サーバーIP>:xxxx/post/<slug>/ でプレビュー可能
Slackスレッドにプレビュー用のURLが送信されます。
🔍 プレビューを確認してください:
プレビューURL: http://<サーバIP>:xxxx/my-article/
確認後、返信してください:
• OK → GitLabにpushしてMRを作成
• いいえ → 投稿をキャンセル
• 5分間無応答 → 投稿をキャンセル
投稿者が「OK」と返答するとgit pushが実行され、「いいえ」と返答するとローカルの変更が巻き戻されます。プレビューサーバーは確認完了後に自動的に停止します。
Hugo Dockerイメージはバージョンが固定されているため(v0.113.0)、本番環境との差異が起きにくい構成としています。
6. NemoClawのネットワークポリシー
サンドボックスからのネットワークアクセスはポリシーで制御されています。今回はSlack API、PyPI、npmへのアクセスを許可しています。
# slack.yaml(抜粋)
network_policies:
slack:
endpoints:
- host: api.slack.com
port: 443
protocol: rest
- host: wss-primary.slack.com
port: 443
access: full # Socket Mode WebSocket用
binaries:
- { path: /usr/local/bin/node }
- { path: /usr/bin/python3 }
必要な通信先のみを明示的に許可するため、万が一エージェントが想定外の動作をしても外部への不正なアクセスを防止できます。
7. systemdによる永続化
Slack Bridgeはsystemdサービスとして登録しており、マシン再起動後も自動で起動します。
[Unit]
Description=NemoClaw Slack Bridge
After=network-online.target ollama.service docker.service
[Service]
Type=simple
User=hiro
Group=docker
EnvironmentFile=/etc/nemoclaw/slack-bridge.env
ExecStart=/path/to/node slack-bridge.js
Restart=on-failure
RestartSec=10
認証情報は/etc/nemoclaw/slack-bridge.envに集約し、dockerグループに読み取り権限を限定しています。
使い方
ブログ記事の投稿
Slackで以下のようにメンションします。
@bot ブログ投稿 my-article-slug 記事のタイトル
Markdownファイルと画像を一緒に添付します。以降、Slackスレッド上で以下の対話が進みます。
- 事前チェック — ブランチ重複等の懸念があれば確認が入ります
- ダイアグラム確認 —
<!-- DIAGRAM: 説明 -->マーカーがあれば、LLMが図を生成してSlackに表示。修正要望も伝えられます - Hugoプレビュー — ブラウザで記事の見栄えを確認できるURLが送られます
- 最終確認 — OKならGitLabにpush、マージリクエスト作成URLが返信されます
投稿のキャンセル
マージ前であれば、ブランチを削除するだけで取り消せます。
@bot ブログ削除 my-article-slug
通常のエージェント応答
ブログ投稿以外のメッセージは、NemoClawサンドボックス内のLLMエージェント(qwen2.5:32b)が処理します。
@bot 最近のKubernetesのセキュリティベストプラクティスを教えて
まとめ
NemoClawをオーケストレーターとして活用することで、セキュアなエージェント実行環境の上にSlack連携、GitLab自動化、LLMによるダイアグラム自動生成、そしてHugoプレビューまでを一貫して構築できました。
- 投稿のハードル低下: Git操作の知識がなくても、Slackからブログ投稿が可能に
- 対話型ワークフロー: 事前チェック、ダイアグラム確認、プレビュー確認の各ステップでSlackを通じた対話が可能。問題があればいつでもキャンセルや修正ができる
- ダイアグラム自動生成: 記事にマーカーを入れるだけで、LLMがMermaid図を作成。Slackで確認し、修正要望も自然言語で伝えられる
- Hugoプレビュー: push前にDockerコンテナでプレビューサーバーを自動起動し、ブラウザで最終確認ができる
- 追加コストなし: ダイアグラム生成はローカルLLM + Mermaid CLIで完結し、外部API費用が不要
- セキュリティ: NemoClawのサンドボックスとネットワークポリシーにより、エージェントの動作範囲を制限
- 拡張性: 同じ仕組みで他のワークフロー(ドキュメント生成、データ分析レポートなど)にも応用可能
- 運用の簡素化: systemdによる自動起動、共有設定による複数メンバーでの利用
承認・マージのフローは従来通り人の目を通すことで、品質を担保しつつ投稿プロセスを効率化しています。
なお、本記事のアーキテクチャ図もこの仕組みで自動生成されたものです。