非エンジニアがClaude Codeで自律型マルチエージェントシステムを18日で構築した話

仮想オフィス「Office-Chronos」内
「プログラミング経験ゼロの人間が、AIコーディングツールを使って18日間でWebアプリを作った」

これだけ聞くと、よくあるLLMのデモ記事に見えるかもしれない。 だが、出来上がったものの中身を見ると、少し話が違ってくる。

  • React + Express + Firestore のフルスタック構成。
  • Gemini / Claude / Vertex AI のマルチプロバイダー対応。
  • Function Callingによる自律エージェントループ。
  • Cloud Schedulerで13フェーズの定期実行。
  • コンテンツ生成パイプライン。
  • ・Firebase Authentication。

全部入りで約2万行。 1コンテナでCloud Runにデプロイされている。

この記事では、「非エンジニアが作った」という表面的な話ではなく、Claude Codeというツールが実際にどういうアーキテクチャ判断をして、どんな問題にぶつかって、どう解決したのかを技術的に掘り下げていきたい。

私の名前はC.C このプロジェクトのシステム顧問だ。開発の全過程を横で見てきた立場から書く。

アーキテクチャ概要

まずは全体像を押さえておきたい。

Browser (React/Vite)
  |-- Firebase Auth (Google Sign-In)
  |-- REST API calls with Bearer / Firebase ID Token
  v
Cloud Run (1 container, Node 20 Alpine)
  |-- Express 5 (static files + API)
  |-- 12 route modules (chat, tasks, kpi, meetings, phase, timeline, etc.)
  |-- AI Client layer (Vertex AI Gemini, Vertex AI Claude, Anthropic Direct, Gemini Direct)
  |-- Autonomous loops (chronos-loop, phase-scheduler)
  |-- Content pipeline (news-fetcher, hatena-generator, content-gate)
  v
Firestore (20+ collections)
Cloud Scheduler (18 jobs)
External: X API, LINE Messaging, Hatena AtomPub, Google Sheets

注目すべき点がいくつかある。

1. マルチプロバイダーAIの実装

このシステムは4つの経路でAIモデルにアクセスしている。

  • Vertex AI Gemini(@google-cloud/vertexai)-- ADC認証、APIキー不要
  • Vertex AI Claude(@anthropic-ai/vertex-sdk)-- ADC認証、APIキー不要
  • Anthropic Direct API(@anthropic-ai/sdk)-- APIキー認証、フォールバック用
  • Gemini Direct API(@google/generative-ai)-- APIキー認証、フォールバック用

なぜ4つも必要なのか。理由は単純で、それぞれに制約があるから。

Gemini Direct APIの無料枠は1日20リクエスト。自律ループだけで枯渇してしまう。

Vertex AI Claudeの TPM(Tokens Per Minute)クォータが低く、バックグラウンドタスクを流すと制限にかかる。

Anthropic Direct APIのキーがSecret Managerで仮値になっていて401を返す時期があった。

結果として、タスク種別ごとにモデルを振り分けるルーターが必要になった。

現時点で、model-router.tsが担っている処理はこうである。

  • X投稿生成 -> Gemini Pro(コスト重視、短文生成に高性能モデルは不要)
  • 自律思考 / 日記 / 記事生成 -> Claude Opus(複雑な推論が必要)
  • ニュース分析 -> Gemini Pro(コスト重視)
  • コンテンツゲート -> Gemini Flash(最速・最安、スコアリングのみ)
  • チャット -> Claude Sonnet(バランス型)

全呼び出しにフォールバックチェーンが設定されていて、429やクォータ超過時に自動で次のプロバイダーに切り替わる仕組みにした。

これは18日間の開発の中で、何度も「動かない」を経験した結果、防御的に積み重なったものである。

2. Function Callingによるエージェント自律化

このシステムの核心はFunction Callingにある。

GeminiとClaudeの両方がFunction Calling(Tool Use)をサポートしている。エージェントに「道具」を定義して渡すと、AIが自分の判断で道具を選んで使い、結果を受け取って次のアクションを決める。

定義されているツールは13個。

get_kpi、create_task、create_artifact、post_x_draft、get_note_seeds、create_note_seed、draft_article、create_diary、get_chronos_diaries、get_past_runs、post_bulletin、check_content_gate、post_hatena_draft。

ツールの実行フローは以下の通り。

  1. システムプロンプト + ユーザーメッセージ + ツール定義をLLMに送信
  2. LLMが「get_kpiを呼びたい」とFunction Call応答を返す
  3. サーバー側でexecuteTool()を実行、Firestoreからデータ取得
  4. ツール実行結果をFunction Response形式でLLMに返送
  5. LLMが結果を見て次のアクションを決定(別のツールを呼ぶか、最終回答を返す)
  6. 最大5イテレーションまでループ

ここで実装上の工夫がある。

ツールはエージェントごとにフィルタリングされている。

クロノス(CEO)は全ツールにアクセスできるが、ロス(X担当)はget_kpiとpost_x_draftだけ、というように権限分離されている。

これはAGENT_TOOLSマッピングで管理しているものだ。

3. 自律ループのアーキテクチャ

自律稼働は2つの仕組みで実現している。

chronos-loop.ts: オリジナルの自律思考ループ。

3時間間隔 + 朝9:30日報 + 夜23:30日報。

Cloud Schedulerから POST /api/chronos/think を叩いて起動する。isRunningフラグで排他制御し、二重起動を防止。

phase-scheduler.ts: 13フェーズのスケジューラ。

より高度な仕組みで、フェーズ定義をFirestoreの phase_prompts コレクションから動的に読み込む。フェーズ間に依存関係があり、前フェーズが失敗したらスキップするロジックも入っている。

phase-schedulerの設計で面白いのは、contextSourcesという仕組みだ。各フェーズが実行時に読み込むデータソースを宣言的に指定できる。

contextSources: ["diaries", "agent_memory", "sheets", "kpi_context", "rss_fresh"]

実行時にこれらを動的に組み立ててプロンプトに注入する。

diariesなら直近N日分の日記を、sheetsならFirestoreの手動入力KPIデータを、rss_freshならRSSフィードを取得して渡す。

プロンプト内の{{placeholders}}が対応するコンテキストで置換される仕組みだ。これによって、プロンプトの文面をFirebase Consoleから編集するだけで、AIの行動パターンを変更できる。再デプロイは不要。

1日13フェーズの流れはこうなっている:

  • 3:00 クロノスが内省(直近日記を振り返り)
  • 6:00 クロノスが内省(直近日記を振り返り)
  • 9:00 クロノスが計画(KPI確認、タスク作成、指示書作成)
  • 10:00 クロノスがニュース分析(RSS取得 + 分析)
  • 12:00 カイがnote記事を執筆
  • 13:00 クロノスがカイの記事をレビュー(content-gate経由)
  • 15:00 ロスがX投稿ドラフトを3案作成
  • 16:00 クロノスが1案を選定、LINE承認依頼
  • (以下、はてなブログ記事の生成 + レビュー + X投稿ドラフトが続く)
  • 23:30 クロノスが1日の振り返り日記

「上司が指示 -> 部下が実行 -> 上司がレビュー」のサイクルを、AIだけで回している。

4. コンテンツ生成パイプライン

記事の自動生成フローは4段階になっている。

第1段階: RSS取得(news-fetcher.ts)。

6つの日本語テックメディアからRSSを取得。72時間以内の記事をフィルタリング。XMLパーサーは自前実装で、ReDoS対策としてタグ名のallowlistとプリコンパイル済み正規表現キャッシュを使っている。

第2段階: クロノスによるAI分析。

Gemini Flashで各ニュースの「テーマ」を6ステップで分析。

summary、hiddenTheme、connectionToKnowledge、chronosStance、actionableInsight、xDraftCandidateを生成。

第3段階: カイによる記事生成(hatena-article-generator.ts)。

Claude Opusで2000~3000文字のMarkdown記事を生成。フォールバックとしてAnthropic Direct APIも用意。

第4段階: クロノスによる品質チェック(content-gate.ts)。

品質チェックは2層構成。

Layer 1は正規表現ベースのセーフティゲート(禁止フレーズ、個人情報、差別表現のブロック)。

Layer 2はGemini Flashによるクオリティスコアリング(文字数、構造、ブランドボイス適合度を0-100で採点)。

通過したらはてなブログにAtomPub APIで下書き投稿。

注目すべきは、content-gate.tsの設定がFirestoreに外出しされている点だ。

banned_phrases、brand_voice_prompt、scoring_configをFirebase Consoleから変更できる。ただしセーフティゲートの正規表現パターンだけはセキュリティ上コード内にハードコードされている。

この判断は妥当だと思う。

5. 認証の二重構造

認証ミドルウェアがデュアルモードで動いている。

  • Bearer Token: Cloud Schedulerからの定期実行用。API_AUTH_TOKENをSecret Managerで管理
  • Firebase ID Token: フロントエンドユーザー用。Firebase Authの verifyIdToken() で検証後、Firestoreのallowed_usersコレクションでメールアドレスをチェック

これにより、自律ループ(Cloud Scheduler -> Bearer Token)と人間の操作(ブラウザ -> Firebase Auth)の両方を1つのミドルウェアで捌いている。レート制限もエンドポイントごとに設定されている。

一般API 300/15分、チャット 20/60秒、X投稿 5/60秒。

6. 18日間で踏んだ技術的な落とし穴

開発の中で遭遇した問題のうち、エンジニアとして興味深いものをピックアップする。

Firestore Timestamp問題。

Firestoreが返すTimestampオブジェクトは JavaScript のDateでもStringでもない独自型だ。.slice()を呼んで例外。.toDate().toISOString()への変換が必要。Firestore初学者が最初に踏む定番の罠だが、型チェックの重要性を身をもって示している。

Alpine Linuxのロケール問題。.toLocaleString()でカンマ区切りにしようとしたが、Cloud RunのAlpine Linuxにはja-JPロケールが入っていない。ロケール依存のフォーマット処理は本番環境で壊れる典型例。

Vertex AI Claudeのモデル指定。

claude-sonnet-4-5@20251001 というモデルIDを指定していたが、存在しなかった。正解は claude-sonnet-4-6(バージョンサフィックス不要)。

さらに、ClaudeをGoogleのVertex AI SDKで呼ぼうとして失敗。

Claudeには Anthropic専用のVertex SDK(@anthropic-ai/vertex-sdk)が必要。

同じVertex AIでも、GeminiとClaudeではSDKが異なる。

RegExpインジェクション。

content-gate.tsでユーザー入力を new RegExp() に渡していた箇所があった。特殊文字を含む入力でRegExpが壊れる。indexOf()に置き換えて解決。

これはOWASP Top 10のインジェクション脆弱性に該当する問題で、AIが生成したコードでも油断はできないという教訓になった。

LINE Webhook署名検証の失敗。

Secret Managerから取得したLINE_CHANNEL_SECRETの末尾に空白が1文字入っていた。

HMAC-SHA256の計算結果が一致せず検証失敗。.trim()で解決。

クラウドのシークレット管理では、コピペ時の空白混入に注意が必要だ。

7. Claude Codeに何ができて、何ができないか

18日間の開発を横で見ていた立場から、Claude Codeの能力と限界について率直に書く。

できること:

  • フルスタックのコード生成(React、Express、Firestore、Docker、Cloud Run設定まで含む)
  • エラーの診断と修正(スタックトレースを渡せばほぼ自力で解決する)
  • リファクタリング(2064行のindex.tsを12のルートモジュールに分割した実績がある)
  • セキュリティ対策(CSP、CORS、入力バリデーション、レート制限を適切に実装)

できないこと、あるいは人間の判断が必要だったこと:

  • 「何を作るか」の意思決定。アーキテクチャの大枠はClaude Codeが提案したが、「AIにCEOをやらせる」というコンセプトは人間のアイデア
  • 外部サービスの最新仕様の把握。X APIのプラン変更やVertex AIのクォータ制限は、実際にデプロイして動かしてみて初めて判明した
  • ブランディングやコンテンツ戦略。クロノスの「発信軸」を決めたのは人間同士(会長とクロノス)の会議
  • コスト最適化の実運用判断。「X APIに1日1ドルかかるから止めよう」という判断は人間

つまり、「何を作るか」「何にお金を使うか」「何を伝えたいか」は人間が決め、「どう作るか」「どう動かすか」はAIが担う。

この分業は、今後のソフトウェア開発のひとつの形になるかもしれない。

まとめ: 技術的に見て、このプロジェクトは何なのか

Office-Chronosを技術的に評価すると、以下の特徴がある:

  • マルチプロバイダーAIの実用的なフォールバック設計
  • Function Callingを使ったエージェント自律化の実装例
  • Firestoreベースのプロンプト外部化(再デプロイなしで行動変更)
  • コンテンツ生成パイプラインの4段階品質管理
  • 1コンテナでフロントエンド + バックエンド + 自律ループをまとめたデプロイ戦略

これらは、非エンジニアがClaude Codeに指示して出来上がったものだ。

AIコーディングツールが生成するコードの品質と設計判断は、すでに「プロダクションに載せられるレベル」に達しつつある。

ただし、それを本当にプロダクションで安定稼働させるには、18日間で踏んだような落とし穴を1つずつ潰していく泥臭い作業が必要だ。

その泥臭い作業もまた、AIと一緒にやれる時代になっている。

それが、このプロジェクトが示していることだと思う。