非エンジニアがChatGPTとVPSで「自分専用AI基盤」を作ってみた

植物研究者がChatGPTとVPSで自分専用AI基盤を作る様子を描いたイラスト AI

植物研究者がMCP・Docker・n8n・WordPress連携を組み上げた構成と失敗記録

先日、ChatGPTに2枚の画像を貼り、こう指示しました。

「この2枚でテストして。WordPressとSNSの下書きまで。まだ公開はしないで」

少し前なら、画像をWordPressへアップロードし、本文のどこへ入れるか考え、アイキャッチを設定し、X、Bluesky、Instagram向けの文章を作り、それぞれの管理画面で下書きを作る必要がありました。

今回は、ChatGPT上の一つの会話から、画像の転送、本文への配置、アイキャッチ設定、SNSごとの文章と画像の割り当て、Bufferへの登録まで進みました。

しかも、指定どおり公開はされず、下書きで止まりました。

初期のテストと比べると、処理時間は約10分の1です。

ChatGPTがその日だけ急に賢くなったわけではありません。サーバー上の役割を分け、権限を制限し、失敗をテストや専用ツールへ変えてきた。その積み重ねが、ようやく目に見える速度として表れたのだと思います。

この記事は、エンジニアが完成度の高いシステムを設計した話ではありません。

私は植物研究が本業で、ソフトウェア開発は専門外です。Linuxコマンドは必要な都度調べ、DockerやGitHubも、ChatGPTの説明と実行結果を確認しながら扱っています。

その程度の技術力から、ChatGPTと相談しながらVPSを借り、設定を間違え、サーバーを止めかけ、構成を何度も直しながら、自分用のAI基盤を作ってきました。

「プログラムは一から書けない。でもAIの説明を読み、コマンドの結果を確認しながらなら進められる」

たぶん、そういう人には近い話です。

なぜサーバーを作ったのか

私の課題は、アイデアが出ないことではなく、思いついたものを実際に使える形へ変えるまでが重いことでした。

ブログなら文章だけでは終わりません。写真の選定、画像加工、WordPressへの登録、SEO、SNS展開まであります。アプリならUI、認証、データベース、公開方法、バックアップまで気になります。

思考の速度に対して、実装と公開の速度が追いつかない。

また、自分に必要な情報だけを集めた知識基盤を作り、横断検索や情報同士のつながりから新しい発想を生み出せるようにもしたいと思っていました。

そこで、私は「何を作りたいか」「何が使いやすいか」「どこまで公開してよいか」を判断し、設計、実装、テスト、反復作業をAIへ寄せることにしました。

構成としては、ChatGPTを作業画面、VPSを実行環境、Vaultを長期記憶として使います。

ChatGPT:会話、指示、確認
VPS:コード、DB、自動処理の実行
Vault:会話、判断、資料、履歴の保存
GitHub:変更内容を人が確定する境界

最初から巨大なシステムを作ろうとしたわけではありません。最初の目的は、ChatGPTで話した内容を後から検索できるよう、Markdownで保存することでした。

そこから「サーバー上の資料もChatGPTから直せたら便利」「コードも修正できたら早い」「WordPressまでつながったら楽」と、実際に使いながら広がっていきました。

前提となる環境

構築時点の主な環境は次のとおりです。

項目構成
VPSXServer VPS 4GB
OSUbuntu 24.04 LTS
管理ユーザー非rootユーザー、sudo利用
ログインSSH鍵認証、root直接ログイン禁止
コンテナDocker、Docker Compose
常駐管理systemd
ソース管理GitHub private repository
主な実装Python 3.12
軽量DBSQLite
n8n用DBPostgreSQL 16
文書Markdown

秘密情報はGitへ含めず、.envやsystemdのEnvironmentFileなど、サーバー上の保護された場所へ置いています。

各サービスも原則として127.0.0.1だけで待ち受けます。ChatGPTから使うMCPはOpenAI Secure MCP Tunnelを通し、内部APIや管理画面をそのままインターネットへ公開しない構成にしました。

現在の全体構成

現在は、役割ごとにサービスを分けています。

                         ChatGPT
                            |
          +-----------------+-----------------+
          |                 |                 |
   Knowledge MCP     Development MCP    Publishing MCP
      文書・知識           開発操作         WordPress・SNS
          |                 |
        Vault       Development Runner
                       Docker・長時間処理

Vault Capture PWA -> Knowledge MCP -> Vault
n8n -> 定期処理・Webhook・監視

Knowledge MCP

Vault内のMarkdownを検索、作成、更新、移動します。正式記録、更新前履歴、アーカイブを管理します。

→ 第1回:ChatGPTに長期記憶が欲しくて、Knowledge MCPを作った

Development MCP

Git管理領域のファイル、Git差分、テスト、ログなど、開発に必要な操作を行います。

→ 第2回:ChatGPTにコードを書かせるだけでは足りなかった。Development MCPを作った

Development Runner

Docker Composeのbuildやrestart、長時間ジョブを担当します。Development MCP自身の外側に置いています。

→ 第3回:AIに自分自身を再起動させたら止まった。Development Runnerを分離した

Publishing MCP

WordPressの記事、画像、SEO項目と、Buffer経由のSNS投稿を扱います。対話しながら変わり続ける記事制作を固定workflowへ押し込まず、Publication、revision、preview、read-back verificationを持つ専用境界へ分けました。

→ 第5回:記事を書くだけでは自動化にならなかった。Publishing MCPを作った

n8n

定期処理、Webhook、監視など、人がその場で会話しなくても進む処理を担当します。

Vault Capture PWA

Android Chromeの共有メニューから記事を取り込み、コメントやSNS候補フラグを付けてVaultへ保存します。

最初からこの構成を設計できていたわけではありません。

失敗するたびに「これは同じサービスに持たせない方がよい」と分けた結果です。

最初はKnowledge MCPだけだった

最初に作ったのは、Markdown中心のVaultをChatGPTから操作するKnowledge MCPでした。

主な機能は、検索、読取、新規作成、SHA-256確認付き更新、History保存、移動、Archiveへのソフト削除、テンプレートからの文書作成です。

既存ファイルを更新する際は、事前に取得したSHA-256を要求します。読み取った後に別の処理で内容が変わっていればhashが一致せず、古い内容で上書きしません。

非エンジニア向けに言えば、「さっき読んだファイルが今も同じ状態か確認してから書き換える」仕組みです。

この時点では、Knowledge MCPにDockerやGitの操作まではさせませんでした。文書管理とサーバー開発では、必要な権限が違いすぎるからです。

開発用にDevelopment MCPを分けた

次に、ChatGPTからGit管理領域のコードや設定を変更するDevelopment MCPを作りました。

ファイルの読取・変更、Git status / diff / commit、Ruff、mypy、pytest、許可された軽量コマンドの実行、ログ確認、Runnerへの処理依頼などを扱います。

ここで重要だったのは、ChatGPTへサーバー全体を自由に触らせなかったことです。

書込可能なルートをallowlistで限定し、Knowledge Vaultはread-only mountにしました。@@INLINE0@@、@@INLINE1@@、credential、tokenなどを含むパスも読ませません。任意shellではなく、許可済みの操作だけを実行します。

AIへ「サーバーを操作する権限」を丸ごと渡したというより、決めた作業だけを行える専用工具を渡した形に近いです。

Docker操作は外側のRunnerへ分離した

Development MCP自身もDockerコンテナとして動いています。

自分自身のbuildやrestartを直接実行すると、処理途中で接続が切れます。また、Development MCPへDocker socketを直接渡すと権限が強くなりすぎます。

そこで、Docker操作と長時間処理をDevelopment Runnerへ分けました。

RunnerはDocker Composeの状態確認、build、up、restart、stop、長時間ジョブ、状態・ログ確認、キャンセル、rollbackなどを担当します。

ジョブはIDを発行し、状態とログをSQLiteへ保存します。Runnerを再起動しても過去のジョブを追えます。

2026年8月3日、開発開始から約1週間の時点では110件のジョブ履歴が残っていました。

その場限りのサーバー操作が、後から追える仕事の履歴へ変わったわけです。

GitHubを「最後に確定する場所」にした

ChatGPTからコードを修正できても、そのままmainへ反映させる構成にはしませんでした。

標準的な流れは次のとおりです。

ChatGPTで要件と受入条件を整理
  ↓
Development MCPでコードを変更
  ↓
Ruff・mypy・pytest
  ↓
ローカルでGit commit
  ↓
chatgpt/*ブランチへpush
  ↓
GitHub Pull Request
  ↓
私が差分とCIを確認してmerge
  ↓
VPSのmainをfast-forwardで同期
  ↓
対象サービスだけ更新
  ↓
health checkと実操作確認

AIはmainへ直接pushできません。force pushもできません。作業ツリーがcleanで、事前に確認したHEADと現在のHEADが一致する時だけ、chatgpt/*ブランチへpushできます。

非エンジニアの感覚で言えば、AIが変更案を作り、最後の確定ボタンは自分が持つ構成です。

開発開始からPull Requestは#1から#30まで作成され、古いブランチを再利用して競合した#2を除き、29件をマージしました。

→ 第4回:ChatGPTからPRを作り、安全にマージできるようにした

n8nに全部やらせるのをやめた

当初は、WordPressやSNSへの投稿もすべてn8n経由にするつもりでした。

ChatGPT -> n8n -> WordPress / SNS

n8nは、決められた処理を繰り返すことには強いです。定期処理、Webhook、外部サービスの接続には向いています。

一方、記事は会話の途中で何度も変わります。

タイトルを変える。段落を追加する。画像を入れ替える。SNSごとに文面を変える。最後に「今日は公開せず下書きまで」と判断する。

この対話を早い段階で固定workflowへ入れると、かえって扱いにくい。

そこで、対話しながら進める発信作業はPublishing MCPへ分けました。

ChatGPT -> Publishing MCP -> WordPress / Buffer
n8n -> 定期処理・Webhook・監視・無人処理

n8nをやめたのではありません。向いている仕事へ戻した形です。

この区別は、後にLoop Engineを作ってさらに明確になりました。毎朝の取得や定型監視のようにやり方が決まっている仕事はAutomation / Workerへ。一方、「なぜ悪化したのか」「次に何を試すか」のように、Evidenceを見て次の一手を選ぶ仕事はAutonomous Loopへ載せます。

→ 第8回:作業自動化と「自律するAI」は何が違うのか

公開系は「準備・確認・実行」を分けた

WordPressやSNSへの書込みでは、下書き作成と公開を同じ操作にしませんでした。

prepare
  ↓
preview
  ↓
人が確認
  ↓
confirmed execution
  ↓
外部サービスから読み戻す
  ↓
内部記録と照合する

WordPressでは、APIが成功しただけでは完了とみなしません。タイトル、本文hash、抜粋、slug、カテゴリー、タグ、status、SEOなどを読み戻して確認します。

BufferもX、Bluesky、Instagramを別々の対象として記録します。一部だけ失敗した場合は、失敗した媒体だけ再試行できます。

自動化というと、確認なしで最後まで進めるイメージがあります。

でも私には、「人が見る場所を明確にして、それ以外を自動化する」方が使いやすかったです。

ChatGPTに貼った画像をそのままWordPressへ送る

最初の画像投稿は、公開HTTPS URLから画像を取り込む方式でした。

しかし、ChatGPTへ添付した画像を、いったん別の場所へ公開してURLを作るのは面倒です。Base64へ変換する方法もありますが、画像を巨大な文字列へ変えるため扱いにくい。

この直接転送経路を作る前は、画像を断片的に送る力技で対応していました。

正直、発狂しそうだった。

現在は、ChatGPTの添付ファイルをMCPのfile parameterとして受け取り、そのままWordPressへアップロードできます。

ChatGPT添付画像
  ↓
Publishing MCP
  ↓
容量・MIME type・binary signatureを確認
  ↓
WordPress media library
  ↓
PublicationへURLを記録
  ↓
記事・SNSで再利用

画像には@@INLINE0@@、@@INLINE1@@、rejectedといった状態を持たせ、本文内の配置も見出しをanchorにします。SNSごとの画像上限や縦横比も事前に検証します。

「どの写真を、どこで、何に使ったか」を、その場のAI判断だけにしないためです。

実際に起きた失敗

失敗1:既存のKnowledge MCPとポートがぶつかった

超初歩的な話でお恥ずかしい限りですが、後学のために残します。

Development MCPの初期構成は8000番ポートを使う想定でした。しかし8000番ではすでにKnowledge MCPが動いていました。

新しい店を作ろうとして、既存の店と同じ住所を指定していたようなものです。

Development MCPを18080番へ移し、起動前のポート競合検査を追加しました。Compose project、systemd unit、Tunnel profile、データ保存先もサービスごとに分けています。

「既存環境を壊さないで」とAIへ伝えるだけでは足りない。何を分離するのかまで仕組みにする必要がありました。

失敗2:強そうなOAuth設定を選んで接続できなかった

Development MCP用のTunnelを設定した際、OAuthや動的クライアント登録を前提とするサンプルを選びました。

認証が多い方が安全そうに見えたからです。

しかしMCP側はそのOAuth metadataを提供しておらず、診断は存在しないURLへアクセスして404になりました。MCP本体は正常なのに、Tunnelのdoctorだけが失敗する状態です。

最終的には、OAuth metadataを要求しない構成へ変更し、MCP本体はlocalhostへ閉じ、外部との境界はSecure MCP Tunnel側へ置きました。

設定項目が多いことと、安全であることは同じではない。

失敗3:Runnerが自分自身を止めた

Development MCPを更新する処理で、Composeの依存関係によりDevelopment Runnerまで再作成されました。

つまり、再起動作業を担当している人が、作業中に自分自身の電源を切った。

修正として、Development MCPだけを更新する時は--no-depsを強制しました。

docker compose up -d --no-deps development-mcp

さらに、Runner自身のrestartやstopはRunner APIから拒否します。

失敗4:Gitの状態表示が実体とずれた

GitHubのmainを取得しても、origin/mainの追跡参照が古いまま残ることがありました。

FETCH_HEADだけを使って更新していたことが原因で、remote tracking refも明示的に更新するよう修正しました。

また、古いCodex用ブランチを再利用したPRはmainと競合し、閉じて最新mainから作り直しました。

この後は、毎回最新mainから小さなブランチを作り、main同期はfast-forward onlyに限定しています。

失敗5:Bufferの下書きが送信状態へ移らなかった

Bufferで作ったdraftを即時送信へ変更する処理では、作成時専用の入力を更新時にも送っていました。また、下書きを解除する指定も不足していました。

その結果、draftのまま残ったり、draftなのに同期済みと判断したりする可能性がありました。

修正後は、作成時と更新時の入力を分け、状態遷移そのものを回帰テストへ入れています。

外部APIは「投稿」という物体だけでなく、draft → update → sentという状態変化まで見ないといけない。

このへんは、実運用しないと見えませんでした。

バックアップは「取る」だけでは足りなかった

コードはGitHubにあっても、VaultのMarkdown、RunnerやPublishing MCPのSQLite、n8nのPostgreSQL、暗号化設定、systemd unit、Tunnel設定、runtime environmentなどはGitだけでは戻りません。

そのため、これらを日次バックアップへ含め、manifest、SHA-256、repository HEADも記録するようにしました。

さらに、最新バックアップを本番とは別の一時領域へ展開し、復元可能か確認するrestore rehearsalも用意しました。

バックアップファイルが存在することと、実際に戻せることは別です。

なお、当時の主なバックアップ先は同一VPS内で、VPSやストレージ全体の障害に備えるoff-site backupは課題として残っていました。

同じ程度の技術力から再現するなら

現在の構成を一度に作ることは勧めません。私自身、最初からこの形を設計できたわけではありません。

順番としては、

  1. VPS、非rootユーザー、SSH鍵認証、Firewall、Docker、private GitHub repositoryを整える
  2. 最初のMCPは読み取り中心にする
  3. 書込範囲を限定し、Git差分とテストを扱う
  4. AIによる変更と、人による確定を分ける
  5. Docker操作を外部Runnerへ分ける
  6. 外部サービスはpreviewやdraftから始める
  7. 一度起きた失敗をテストやvalidationへ変える

くらいが扱いやすいと思います。

各段階で止めても、それなりに実用になります。

AIへ依頼する時に有効だった伝え方

非エンジニアがAIとサーバーを作る場合、単に「作って」と頼むより、次を明示した方が安定しました。

  • 既存サービスは変更しない。必要ならまず提案だけ出す
  • 変更前に現在の状態を確認する
  • 対象ディレクトリと対象サービスを限定する
  • 一度に変更する範囲を小さくする
  • 受入条件を先に決める
  • 実行するテストを決める
  • 失敗時の戻し方を用意する
  • 秘密情報を表示・記録しない
  • 公開や削除は明示承認まで行わない

また、長いshell commandを毎回コピーするより、一度成功した処理をscriptやMCP toolへ変える方が安全でした。

同じ処理を二回使うなら、専用ツールへ昇格させる。

これがかなり効きます。

現在も完成してはいない

ここまで書くと、完成したシステムに見えるかもしれませんが、この記事を書いた時点ではまだ構築途中でした。

Development RunnerはDocker socketへアクセスするため権限が強い。off-site backupは未完成。Vault Capture PWAのHTTPS公開、credential期限監視、障害通知、定期health summaryなども課題として残っていました。

公開処理を完全自動化していなかったのは、未完成だからだけではありません。

私は、人間を処理から完全に外したいわけではないからです。

AI:設計案、実装、テスト、整理、変換
人:目的、評価、承認、公開判断

この分担が、自分には最も扱いやすいです。

一度目の失敗を、二度目から減らす

画像2枚の投稿テストが短時間で終わった時、モデルの性能以上に、積み重ねた構造の効果を感じました。

ポート競合、Tunnel設定の選択ミス、自分自身を止めたRunner、古いGitブランチの競合、Bufferの状態遷移不具合。

それぞれを、専用ツール、権限制約、回帰テスト、読み戻し確認、履歴保存へ変えてきました。

一度目は、人が考えて解決する。

二度目からは、仕組みが先に検出する。

VPSは、単なるデータ置場から、AIが仕事をする作業環境へ変わりつつあります。

AIが働けるようになったのは、自由に何でも操作できるようにしたからではありません。

役割、権限、記録、確認地点を決めたからです。

非エンジニアでも、すべてを理解してから始める必要はありませんでした。

小さく作る。実際に使う。失敗を残す。次の仕組みへ変える。

少なくとも私は、その方法で、数日間に29件のPull Requestを積み上げ、画像付きのWordPress・SNS下書きまで一つの会話から処理できるところまで来ました。

最後に

3つのブログと9つのSNSアカウントへの自動投稿は、当時すでに機能していました。この記事もChatGPT経由で投稿しています。

知識集積基盤には情報が積み上がっています。いつか、それらのつながりから新しいひらめきが生まれる。

そんな確信に近い感覚があります。

まあ、単なる「開発ハイ」かもしれない。

それでも、自分が必要だと思った知識をすぐ取り出せる感覚はかなり良いです。

ここ数日の開発は、新しいことの積み重ねでした。客観的に見れば、地味な作業の連続だったかもしれません。

でも、こういう地味な積み重ねが、そのうち効いてくる気がします。

追記:実際に使い続けてみて

この基盤を実際に使い続けると、ChatGPTそのものの性能以上に、「何につながっていて、どこまで実行できるか」でAIの使い方が大きく変わることを実感しました。

→ ChatGPTに自分のサーバーをつないだら、AIの使い方が変わった

本記事が「どう作ったか」なら、続編は「作った結果、何が変わったか」です。

追記:開発史を連載として整理し始めました

この記事は、2026年8月初旬時点の自分専用AI基盤をまとめた総論・当時のスナップショットとして残しています。

その後の開発史は「ChatGPTと育てる、自分専用AI基盤」として、第1回から順番に整理しています。

この総論を書いた時点ではLoop Engineはまだ登場していませんでした。その後、MCP群で「できること」が増えても次の仕事を決めるのは人間のままだと気づき、既存MCPの上にOrchestratorとしてLoop Engineを追加しています。

さらに、Loop Engineで仕事を回すようになると、Chatやサービスの寿命より仕事の寿命を長くする必要が出ました。そこでRunを仕事のidentityにし、Event / Checkpoint / pause / resume / leaseで途中から再開できるようにしています。

→ 第7回:AIが途中で止まっても、続きを再開できるようにした

そして、自動化と自律化も分けて考えるようになりました。固定手順を安く繰り返すAutomation / Workerと、Goal・Success Criteria・Evidenceから次の一手を選ぶAutonomous Loopは別の層です。

→ 第8回:作業自動化と「自律するAI」は何が違うのか

さらに複数Chatを並列に動かす段階では、isolated workspace、component lease、shared mutation serialization、live state、workspace ownershipまで必要になりました。

→ 第9回:複数のChatGPTに同じシステムを開発させたら、普通に衝突した

そして並列化が進むと、次のボトルネックは人間の「承認します」待ちになりました。そこで、安全で可逆なroutine作業は次の本当のprotected boundaryまで進め、人間はsecret・root・不可逆なproduction操作・目的変更など、本当に人間にしか決められない境界へ残すStanding Delegationへ整理しました。

→ 第10回:AI開発のたびに「承認します」と打つのをやめた

さらに、人間が本当に必要な境界に残った承認は、ChatGPT上のApproval UIから入力できるようにしました。ボタンはOwner intentの入力面にとどめ、Authority・policy・live-state再検証はサーバー側に残しています。

→ 第11回:AIに「承認」ボタンを作った。でも、ボタンに権限は渡さなかった

Season 1は第1回〜第12回で一区切りになりました。Knowledge MCPから始まり、Development、Publishing、Loop、durable Run、自律化、並列化、Standing Delegation、Approval UIまで広がった全体像は最終回で振り返っています。

→ 第12回:記憶から並列開発まで。自分専用AI基盤は、いったんここまでできた

Season 2は別番号体系で、いったん作ったこの基盤を「疑う」ところから続きます。

この記事では当時の全体像を、正式連載では各部品がなぜ必要になったのかを一つずつ掘り下げています。

コメント

タイトルとURLをコピーしました