私のOpenClawのセットアップ:ウェブサイトを作成できる一台のマシン

公開日
2026年7月31日
更新日
2026年9月16日
著者
Jacob Lloyd — プロジェクト完了後、AIの支援を受けて執筆
読了時間
約17分で読めます

かんたんに言うと: この記事では、自宅にある小型のLinuxコンピュータ上で動作するAIアシスタントが、どのようにしてこのウェブサイトを維持しているかを説明します。それらは下書きを作成したり、サイトを再構築したり、変更内容を準備したりしますが、公開される内容はすべて事前にプレビューを行い、サーバーのバックアップを取った上でファイルを削除しない安全用スクリプトを通過します。作業はAIアシスタントが行い、いつ公開するかは私が決めます。

私の自宅にある小型Linuxマシン上で動作するAIエージェントが、皆さんが読んでいるこのウェブサイトの管理を行っています。これらのエージェントは実際にサイトの更新作業も行えますが、システム設計上、誤ってサイトを壊すことは不可能になっています。すべての変更内容は、事前プレビューやサーバーのバックアップ処理を行うスクリプトを経由してサーバーに送信され、ファイルが削除されることもなく、私が許可した場合のみ公開されます。このスクリプトこそが真似すべき部分であり、本記事の大部分はこれについて説明しています。

要約

  • 概要: OpenClaw(自己ホスト型AIエージェントゲートウェイ)を動作させるLinuxミニPCで、静的サイト生成器および制御付きデプロイスクリプトと連携しています。
  • 処理場所: 主要なエージェントはクラウド上のモデル(MiniMax M3)を利用します。ローカルモデルは自宅ネットワーク内の別のGPUマシンから提供されます。ミニPC自体にはAIモデルは搭載されておらず、ゲートウェイやサイト、スクリプトのみが稼働しています。
  • コスト: 月ごとに大きく変動します。実際の費用については 私のAIエージェントが実際にどれくらいコストをかけているか をご覧ください。
  • 結果: エージェントが記事の下書きやサイトの再構築、デプロイの事前検証を行い、秘密キーによるスキャンやリンクチェックも実施します。公開自体はあくまで私の判断に委ねられます。

2026年9月時点の状況: 第3ステップで説明した週次の自動更新機能は現在停止中です。7月末のバックアップ復元時に失われ、まだ再設定していません。それ以外の機能は必要に応じて正常に動作しています。

全体像

このゲートウェイによりモデルがツールを持つエージェントへと変わります。エージェントがプレーンテキスト形式のコンテンツを編集し、決定論的な生成器がサイトを構築します。そして制御付きのスクリプトだけがウェブサーバーへの唯一のアクセス経路となります。モデルそのものは別箇所に存在しており、クラウドAPIや自宅ネットワーク上のGPUマシンがそれに該当します。

最終的に得られるもの

  • 「Xについて記事を書いて」とチャットで指示すると、エージェントがフロントマターの形式やスタイルに従いMarkdown形式の記事をサイトのコンテンツフォルダへ作成し、ローカルプレビューも更新します。
  • 「デプロイして」と指示すれば、エージェントはデプロイスクリプトを実行します。デフォルトではドライランとなり、サーバー上で変更されるファイルの一覧が表示されるだけで実際には何も変更されません。本番公開には私による明示的な--goコマンドが必要です。
  • 「サイトをチェックして」と指示すると、エージェントは古くなった日付情報や質の低い記事、壊れたリンクなどを調査します。これらすべての処理の前にセキュリティチェックも実施されます。

こうしてエージェントが入力作業を行い、スクリプトが規則を強制します。私は最終的に「変更内容を公開するかどうか」という一点だけを決定するのです。

ハードウェア:Linuxマシンであれば何でも可

私が使用しているのは CHUWI AuBox Ai365ミニPC です(詳細は該当記事をご覧ください)。モデル処理は別箇所で行われるため、ウェブサイト管理側にはほとんど要件がありません。

  • systemd が利用可能な64ビットLinuxマシン:サービス監視やタイマー機能に必要です。
  • Python 3:静的サイト生成器用。また rsyncおよびSSH もデプロイ処理に不可欠です。
  • モデル:クラウドAPIキーか、自宅ネットワーク上のモデルサーバーが必要です。すべてを一台のマシンで完結させたい場合は、LM Studio、Ollama、または llama.cpp のサーバー版もゲートウェイが求めるOpenAI互換APIを提供してくれます。

ステップ1:特定用途向けエージェントを持つゲートウェイ

このゲートウェイによりモデルはツールやファイルアクセス権、定期実行ジョブを持つエージェントへと変わります。私は OpenClaw を利用しています。これにより名前付きのエージェントが管理され、それぞれにフォールバック機能付きのモデルが割り当てられ、チャットインターフェースも提供されます。ファイルやシェル操作権限を持つ「ウェブマスター」エージェントを作成した後、あらゆるゲートウェイに共通する二つの制約を適用します。

  • ゲートウェイをループバックのみ利用可能にし、チャットUIには認証機能を設ける。シェル操作権限を持つエージェントをネットワーク上で無防備な状態にしておくのは危険です。
  • エージェントの作業ディレクトリをサイトリポジトリに限定する。エージェントにはコンテンツ編集のみが許可され、ホームディレクトリへはアクセスできません。

私のゲートウェイには他にも複数のエージェントが存在します(詳細は ローカルAIエージェントスタック:OpenClaw編 をご覧ください)。サイト管理用として重要なのは、子供向けに安全化されたボット群です。これらはファイルやシェル操作権限を持たないため、たとえ不適切な人物がスマートフォンを手にしてもサイトへアクセスできません。

もしシステム自体に問題が生じた場合(設定の不整合、ゲートウェイのクラッシュ、構築不能状態など)、強力なクラウド型コーディングモデルを利用して対処します。このマシン全体を「患者」と見なして修復作業を行うのです。その経緯は OpenClawが壊れた時、Claude Codeで直す に記述しています。

ステップ2:静的サイトと制御付きデプロイスクリプト

エージェントと WordPress の組み合わせは好ましくありません。データベース、ログイン機能、プラグイン、PHPなどがすべて攻撃対象や障害要因となり得るからです。静的サイトの方がエージェントには適しています。サイト全体がフォルダ内のプレーンテキストファイルで構成され、構築もコマンド一つで完了し、公開は単純なファイルコピーで済みます。私が利用している生成器は1000行未満のPythonコードです(Jinja2、Markdown、YAMLを使用)。Hugo、Eleventy、Zola でも同様の動作が可能です。重要なのは以下の仕様です。

  • コンテンツはフロントマター付きMarkdownファイル。LLMが直接編集でき、差分も明確に把握できます。
  • 構築処理は決定論的。同じコンテンツから常に同じサイトが生成されるため、出力の変化は必ずコンテンツ変更に起因します。私の環境では完全な構築に約2秒しかかかりません。
  • デプロイ経路は一つだけであり、エージェントに信頼して実行させる指示群ではなく、安全性が組み込まれたスクリプトである必要があります。

この最後の点こそが本構成の核心です。インターフェースは以下の通りです。

./deploy.sh preflight   # ツール、SSH、ウェブルート、マニフェスト、サイズ制限を確認
./deploy.sh             # 構築+ドライランrsync:変更予定ファイルを表示
./deploy.sh --go        # 構築+サーバー側バックアップ+公開+検証

このスクリプトが保証する事項は以下の通りです。

  1. デフォルトでドライラン実施。引数なしで実行してもサーバーに変更は加わらないため、エージェントが「今から公開される内容」を私に示すために自由に利用できます。
  2. ファイル削除の禁止。rsync は --delete オプションなしで実行されます。誤作動したエージェントが新しいバージョンでページを上書きすることはあっても、サイト全体を消去することは不可能です。
  3. 事前バックアップの実施。--go 指定時には、スクリプトがサーバーのウェブルートをtar形式で圧縮し、最新5件分のバックアップを保持します。バックアップ失敗時はデプロイ処理が中断されます。
  4. 安全側に倒れる設計。拒否規則はマニフェストファイルから取得されます。解析不能なマニフェスト、除外規則が存在しない状態、またはプレースホルダ値のままのターゲット設定時にはrsync実行前に処理が中止されます。さらにソースファイル(スクリプト、テンプレート、Markdown、YAML、鍵情報)も固定規則により除外対象となっており、たとえマニフェストに不備があっても流出しません。下書き状態の記事も除外され、転送リストを検証して誤って含まれていないことも確認します。
  5. 公開後検証の実施。ライブ版ホームページを取得し、構築時に各ページに埋め込まれた識別用文字列が存在するかを確認します。単にHTTP 200応答が返るだけでは古いホスティングデータが残っている可能性もあるためです。

以下はその核心部分を簡略化したコード例です。実際のスクリプトは約430行あり、ほとんどが各種チェックやエラーメッセージで構成されています。

DRY=(-n); [ "$GO" -eq 1 ] && DRY=()          # --go指定時以外はドライランとなる

フェイルクローズ方式:解析可能なマニフェストがない、または除外リストが空の場合=rsyncを実行しない

m_raw="$(read_manifest)" || die "マニフェストの解析に失敗しました"
mapfile -t M <<< "$m_raw"
[ "${#M[@]}" -gt 3 ] || die "マニフェストから除外リストが得られませんでした"
MARKER="${M[2]}"; EXC=("${M[@]:3}")

HARD=(--exclude='.git/' --exclude='content/' --exclude='templates/'
      --exclude='**/*.py' --exclude='**/*.sh' --exclude='**/*.md'
      --exclude='**/*.yaml' --exclude='**/.env*' --exclude='**/*.key'
      --exclude='**/*.bak*' --exclude='**/*~')

ssh "$DEST" "test -d $WEBROOT" || die "ウェブルートに接続できませんでした"

if [ "$GO" -eq 1 ]; then                      # 上書き前にバックアップを作成
  ssh "$DEST" "mkdir -p $WEBROOT-backups \
    && tar czf $WEBROOT-backups/site-$ts.tgz $WEBROOT \
    && test -s $WEBROOT-backups/site-$ts.tgz \
    && echo BACKUP_OK" | grep -q BACKUP_OK || die "バックアップに失敗しました。処理を中断します"
fi

rsync -az --itemize-changes "${DRY[@]}" \
  "${DRAFT_EXC[@]}" "${HARD[@]}" "${EXC[@]}" \
  ./ "$DEST:$WEBROOT/"                        # 注意:絶対に --delete は使用しない

[ "$GO" -eq 1 ] || { echo "これはドライランのみです。実際に公開するには --go オプションを付けて再実行してください。"; exit 0; }

curl -s -L "$SITE_URL" | grep -qF "$MARKER" \
  && echo "マーカーが確認できました:新しいサイトが公開されています" \
  || echo "HTTP応答はありますがマーカーが見つかりません:古いサイトがまだ配信中かもしれません"

この上に、publishという単語一つだけからなるラッパースクリプトがあり、これが各処理を順番に実行し、最初の失敗で即座に中断します。具体的には、壊れたリンクや欠落している画像をチェックするビルド処理、ソースツリーのローカルバックアップ、ビルド済みHTMLに対する公開前スキャン、そして--goオプション付きのデプロイスクリプトが実行されます。

エージェントはいつでも事前チェックやドライランを実行できます。--goフラグは私専用のものです。この非対称性があるからこそ、AIが本番サイトにこれほど近接していても問題ないのです。

ステップ3:更新処理をタイマーで制御する(なぜ私の環境では機能していないか)

最後の要素がサイトの自己維持を可能にします。現在私はこの処理を手動で行っています。systemdのタイマーが定期的に作動し、エージェントに対して次の指示を与えます:サイトをレビューし、古くなったコンテンツを更新、不十分な記事を改善、内部リンクを確認し、最終的には公開せずに結果をステージングする、という内容です。

# ~/.config/systemd/user/site-refresh.timer
[Unit]
Description=週次のウェブサイトコンテンツ更新処理
[Timer]
OnCalendar=Sun 06:00
Persistent=true
[Install]
WantedBy=timers.target

このタイマーが起動させるサービスは、ゲートウェイのCLIまたはAPIに上記指示を送り、その後エージェントがステージングした内容に対して以下のガードチェックを実行します:

  • 秘密情報スキャン:APIキー、トークン、パスワード、プライベートホスト名やIPアドレスに類似するパターンを検出します。gitleaksがこの目的にそのまま利用できます。該当項目が見つかると処理は即座に中断されます。
  • コンテンツチェック:私の環境では不適切または不適格な言葉を含む単語リストを用いてスキャンします。さらに別のプロンプトで動作する第二のモデルも使用し、単語リストだけでは見逃すような根拠のない主張なども検出します。
  • ビルド必須:リンクやアセットチェックも含まれます。壊れた画像や存在しない内部リンクがある場合、デプロイスクリプトに到達する前に処理が失敗します。

これら全てのチェックを通過した時点で初めて、ドライランによるデプロイが実行されます。ただし実際の公開には依然として私の--go指示が必要です。もし定期処理だけで自動的に公開させる環境にしてしまうと、これらのガード機能や「まずバックアップを作成し、決して --delete は使用しない」というスクリプトのおかげで、「不適切な記事が公開されてしまったがバックアップから復元できた」という程度の被害に留まります。

私の環境でもこれまでこの仕組みで運用していましたが、7月末に行ったバックアップ復元処理の際に、そのタイマーも同時に消失してしまいました。数週間後、ページ内の日付が更新されなくなっていることから初めて気づきました。これが定期実行ジョブの典型的な失敗パターンです。作動すべきタイマーが何もしなくなった場合、何もする必要がない状態と全く同じように見えてしまうのです。 皆様がこの仕組みを構築される際には、たとえ「変更点はありません」という内容でも毎回何らかの報告を行うよう設定してください。そうすれば「報告がない=機能していない」と判断できるようになります。

ガードチェックが実際に検出した事例

このサイトで実際に発生した2つのケースがあり、どちらも設計時に考慮すべき事項です:

  • 特定ファイル形式を見逃すスキャナ:私の秘密情報スキャナは.zipファイルを無視しており、コンテンツチェック側でもダウンロードフォルダを調査していませんでした。結果として3つの公開済みダウンロードアーカイブに機密情報が含まれていました。対策として両スキャナともアーカイブを展開し内部ファイルを検査するように改修し、悪意あるアーカイブでチェック処理が停止するのを防ぐためサイズ制限も設けました。あらゆるスキャナに対して「何を開くことを拒否しているか」を確認すべきです。
  • エージェント生成文書が事実無根の情報を記載:あるモデルベンダーに関する草稿では、そのモデルが競合他社の製品名で販売されていると断定していました。実際にはそうではなく、4回にわたるレビューを経てもその記述が残ってしまったのです。現在では草稿内の数値やベンダー情報は全て別途作成した事実確認ファイルを参照する必要があり、出典のない主張は削除されるようになっています。

この仕組みの再現方法

ここで述べている内容はいずれも独自技術ではありません。手順は以下の通りです:

  1. Linuxマシンを選定し、モデルへのアクセス元を決定します:クラウドAPI、ローカルのモデルサーバー、あるいはネットワーク内のGPU搭載マシンなど。
  2. エージェントゲートウェイ(OpenClawや類似品)をループバックインターフェースに接続してインストールします。ウェブ管理者用エージェントには、サイトディレクトリ内のみで利用可能なファイル操作機能およびシェルコマンドを付与します。
  3. 既存サイトが静的生成方式でない場合は移行させます。
  4. 上記5つの保証条件を備えたデプロイスクリプトを作成します。これが最も注意を要する部分です。保証条件一覧と該当箇所の抜粋を自身のLLMに仕様として提示してください。
  5. 秘密情報スキャン、コンテンツチェック、リンクチェックといった全てのガード機能を実装します。これらはいずれもフェイルクローズ方式で動作させます。また必要であれば毎回報告を行うタイマーも追加できます。
  6. パイプラインが信頼に値するようになるまで、--goの使用を人間のみに制限します。

注意点

  • デプロイスクリプトは唯一の経路でなければならない:「今回だけ」という目的で手動で実行したrsyncコマンドこそ、このスクリプトが防ごうとしている問題の原因となり得ます。私の環境ではソースファイルを強制的に除外し、仮設定用コンフィグも拒否するようにしています。なぜなら、一回限りのコマンド実行時にはそうした制御が働かないからです。
  • HTTP 200応答だけでは公開成功とは言えない:WordPress移行時、旧ホスティング側がドメインに対して常に応答し続けたため、新規ファイルが全く使用されていない状態でも全チェックが通過してしまいました。ビルド時にマーカーを埋め込み、公開後のページでその有無を確認する必要があります。逆に、正常な公開直後でもホスト側がキャッシュしたホームページを1分程度配信し続けるケースもあり得るため、マーカーが存在しない場合も一度だけ再確認すべきです。
  • エージェントは与えられた雛形通りにコンテンツを作成する:投稿用スキャフォールドにダミーテキストが含まれていると、消極的なモデルはそのままダミー文面を公開してしまいます。コンテンツチェック機能には自身の環境で使用しているダミー文字列も登録してください。
  • 執筆者自身にレビューさせてはならない:同一モデルかつ同一プロンプトでは同じ盲点が生じます。少なくとも異なるプロンプト、可能であれば別モデルを用いたレビュアーを配置すべきです。
  • 定期実行されるLLM処理にはタイムアウト設定と「ステージングのみで公開しない」というデフォルト値が必要:監督なしで公開できる処理は、結局日曜日の早朝に不適切な内容を配信する危険性があります。ステージング処理を行えば一度分のレビューコストが増加します。
  • 秘密情報スキャンはエージェントへの指示ではなくパイプライン内で実施すべき:「絶対に機密情報を含めない」というプロンプト上の指示は願望に過ぎません。出力結果に対してフェイルクローズ方式で検査する仕組みこそが有効な制御手段となります。

関連記事:私のエージェントスタック、エージェント運用にかかる実際のコスト、OpenClawが不調になった時の対処法、このシステムを稼働させているミニPC、同じエージェント群を用いた別サイトの構築例、この仕組み導入以前のWordPress運用時の様子。


← AIとローカルLLMをもっと見る