概要
妹がpinsel-germany.comというドイツの美大受験準備をやっていて、そのWebサイトはTakazudoの方で作ったものが使われている。元々Gatsby + Prismic Headless CMSで作っていたのだが、Next.jsのstatic exportで作り直すことにした。(まだ途中だけど)
問題は、開発者ではない妹がサイトのコンテンツをどうやって更新するかということ。従来のアプローチならヘッドレスCMSをセットアップして使い方を教える、という流れになり、今回もそのあたりを自分で作っちゃうかなどと考えていた。Claude Codeもあるしとか。
でも考えていたらめんどくさくなってきて、今回はSlack botを作って自然言語でサイト更新出来るんじゃないかという気がしたので試した。妹がSlackで日本語で「FAQに質問を追加して」みたいに書くと、botがClaude APIで意味を解釈して、GitHub APIでリポジトリのファイルを編集して、自動デプロイされる、という仕組み。そのまとめ。
背景
従来のアプローチ
非開発者がウェブサイトのコンテンツを更新する場合、だいたい以下のような流れになる。
- Prismic、Contentful等のヘッドレスCMSを導入する
- CMSをちゃんとセットアップする
- サイトオーナーにCMSの使い方を教える
これはこれで定番のアプローチなのだが、CMSのセットアップも運用もそれなりにコストがかかる。
今回のアプローチ
CMSの代わりに、Slack botを作った。
- 自然言語を理解するSlack botを作る
- botはClaude APIでリクエストを解釈する
- botはGitHub APIでリポジトリのファイルを直接編集する
- Cloudflare Workersでbotをホストする
- GitHub pushをトリガーにCloudflare Pagesが自動デプロイする
妹はSlackでチャットするだけ。CMSのUIを覚える必要がない。
結果
実際に動いている様子がこれ。

これが編集前のウェブサイト。ここからSlack botに変更を依頼する。

Slackのスレッドで普通にチャットする感覚でサイトの更新ができる。botが変更案を提示して、確認後にコミットしてくれる。コミットURLも返してくれる。

GitHubで見ると、botが作ったコミットの差分がちゃんと確認できる。コミットされたらCloudflare Pagesが自動でビルド・デプロイするので、数分後にはサイトに反映される。
各APIの役割
Botを構成する4つのAPIの役割分担。
Slack Bot
- Slack Events APIでメッセージを受信する
- リクエスト署名の検証(HMAC-SHA256)
- URLベリフィケーションチャレンジのハンドリング(セットアップ時)
- Slack file APIでアップロードされた画像をダウンロードする
chat.postMessageでスレッドに返信する
Claude API
- 会話履歴とツール定義を受け取る
- 日本語の自然言語リクエストを解釈する
- どのGitHub操作を実行するか判断する
- ツールユースループ: Claudeがツールを呼ぶ → Workerが実行 → 結果をClaudeに返す → Claudeが続行
- システムプロンプトに「コンテンツマップ」を含めて、編集可能なファイルの一覧をClaudeに事前に伝えている
GitHub REST API
- Contents APIで単一ファイルの読み取り・更新
- Git Data APIで複数ファイルの一括操作
- 画像のコミットもContents APIのPUTでbase64エンコードしたデータを送る
Cloudflare Workers
- サーバーレス関数としてbotをホストする
ctx.waitUntil()でSlackに200を即座に返しつつバックグラウンドで処理を継続- KVで会話履歴を保存(Slackスレッドの
thread_tsをキーに、24h TTL) - レートリミット(1ユーザーあたり30リクエスト/日)
wrangler secret putでシークレット管理
アーキテクチャ
システム構成
各コンポーネントの関係はこんな感じ。
全体のフロー
処理の流れはこんな感じ。
Claudeとの連携方法の選択
Claude連携のアプローチは2つ検討した。
- Claude API + Tool Use: ツール(
read_file、update_file等)を定義して、Claudeに判断させる。シンプルでサーバーレスで動く - Claude Code Agent SDK: フルのClaude Codeサブプロセスを起動して、ファイルシステムにアクセスさせる。高機能だが長時間動くサーバーが必要
今回は前者を採用した。理由は、GitHubの操作がすべて純粋なHTTP APIコールで完結するから。ファイルシステムが不要なので、サーバーレスのWorkersで問題なく動く。コンテンツ更新程度の用途にClaude Code Agent SDKはオーバーキル。
画像のアップロード
Slackに画像がアップロードされた場合の処理も、ファイルシステム不要で動く。
- Workerが
url_private+ botトークンでSlackから画像をダウンロード - メモリ上でbase64エンコード
- GitHub Contents APIのPUTでbase64コンテンツとしてコミット
全部メモリ上で完結する。
マルチターン会話の仕組み
Claude APIはステートレスなので、毎回の呼び出しで会話履歴の全体(messages[]配列)を送信する必要がある。Workerはこの会話履歴をCloudflare KVに保存していて、Slackスレッドのthread_tsをキーにしている。スレッドごとに1つの会話。24h TTLで自動クリーンアップ。
これはハックでもなんでもなくて、ChatGPTやその他のチャットLLM UIも裏ではまったく同じことをやっている。
Cloudflareを選んだ理由
元々サイトはNetlifyにホストしていたが、botのホスト先としてCloudflare Workersを選んだ。
- NetlifyのBackground FunctionsはProプラン($19/月)が必要
- Cloudflare Workers Paidは$5/月
- Cloudflare Workersはコールドスタートがほぼ0ms
ctx.waitUntil()パターンがSlack botに最適(即座に200を返してバックグラウンド処理)- KVストレージが有料プランに含まれている
- サイトホスティング + bot + KVが全部1つのプラットフォームにまとまる
ということで、サイトのホスティングもNetlifyからCloudflare Pagesに移行した。
コンテンツマップの最適化
重要な最適化として、システムプロンプトに静的な「コンテンツマップ」を含めている。Claudeにリポジトリを毎回探索させると、list_filesの呼び出しでトークンを消費してしまう。コンテンツマップがあれば、Claudeは最初からどのファイルが何を含んでいるか知っているので、直接目的のファイルにアクセスできる。
## Data files (TypeScript)
- data/faq-data.ts — FAQ Q&A (4 categories)
- data/voices-data.ts — Student testimonials
- data/results-data.ts — Admission results by year
## Notes (MDX)
- src/notes/what-is-mappe.mdx — マッペとは
- src/notes/german-language.mdx — ドイツ語について
...こういうマップをシステムプロンプトに入れておくことで、list_filesの呼び出しを省略できる。
エディタモードの導入
実際に使ってみて気づいたのが、Claudeは小さな変更を頼まれたときに、周囲のテキストまで「改善」しようとする傾向があること。「この1行だけ直して」と頼んでも、前後の文章を書き直したりする。
なので、システムプロンプトに「エディタモード」のルールを追加した。
- 依頼された変更だけを行う
- 周囲のテキストを勝手に修正・改善・書き換えない
- 何かおかしい点を見つけたら、編集せずに指摘だけする
- 変更案を提示して、確認を得てからコミットする
料金
Claude API — 初回$40 + 従量課金
Claude APIを使うにはAnthropicのAPIクレジットを購入する必要がある。最低$5からチャージできるが、実用上はTier 2以上が必要。Tier 1だと1分あたり30Kの入力トークン制限があり、会話履歴を含めるとすぐに429 rate limitに引っかかる。
Tier 2には累計$40以上のクレジット購入が条件。これは月額ではなく一度きりの購入で、到達すると即座にTier 2に昇格する。購入したクレジットはそのまま残高としてAPI利用に使える。
| Tier | 累計購入額 | 月間利用上限 | Sonnet ITPM |
|---|---|---|---|
| Tier 1 | $5 | $100 | 30,000 |
| Tier 2 | $40 | $500 | 450,000 |
| Tier 3 | $200 | $1,000 | 800,000 |
| Tier 4 | $400 | $5,000 | 2,000,000 |
モデルはclaude-sonnet-4-6を使っている。コンテンツ更新程度ならSonnetで十分。トークン単価は入力$3/1Mトークン、出力$15/1Mトークン。週数回の更新程度の軽い使用量なら実際の消費は月$1-5程度で済む。
Cloudflare Workers — $5/月
Cloudflare Workersの無料プランだとCPU時間が10msまでで、Claude APIの応答待ちを含むと全然足りない。Workers Standard($5/月)にアップグレードする必要がある。有料プランにはKVストレージも含まれている。Cloudflare Pagesは無料枠で帯域無制限。
その他 — 無料
| サービス | コスト | 備考 |
|---|---|---|
| Slack | $0 | 無料プランで十分 |
| GitHub API | $0 | 認証リクエストは無料 |
合計
初回にClaude APIクレジット$40 + Cloudflare Workers $5/月が必要。Claude APIの$40は一度きりの購入で、残高として消費されていく。ランニングコストとしてはCloudflare $5/月 + APIトークン消費$1-5/月程度。
まとめ
テキトーに指示すればやってくれるつもりなので、CMS無しで色々やってもらうには良い方法なのかもしんない。ただ自分で使うかと言われるとAPI料金になるのでやらないかも。うまいこと整備できればサービス的に提供するのも可能かなーという印象。
今後やること
トークン使用料が気になる。無駄にリポジトリを探索されると財布が痛いので、どこのページはどれいじるとかそういうCLAUDE.mdやスキル等を整備する必要があるかも。他しっかりやるならいじるサイト用コンテンツをまとめたリポジトリとして改めてそこだけで作るとか(不要なドキュメントやこのボット関連のものは除外する等)。
うまくいくのかは微妙に謎。ちょっとしか試してないけど関係無い所も併せて直されてしまったりしていた。単純に日本語の間違いだったので良いっちゃよいがAIっぽい。非開発者向けを意識した調整が必要かも。例えばテキストはなるべくコンポーネントの奥に隠さずにMDXファイル内にまとめるようにするとかそういう。