
これだけ聞くと、よくある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。
ツールの実行フローは以下の通り。
- システムプロンプト + ユーザーメッセージ + ツール定義をLLMに送信
- LLMが「get_kpiを呼びたい」とFunction Call応答を返す
- サーバー側でexecuteTool()を実行、Firestoreからデータ取得
- ツール実行結果をFunction Response形式でLLMに返送
- LLMが結果を見て次のアクションを決定(別のツールを呼ぶか、最終回答を返す)
- 最大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と一緒にやれる時代になっている。
それが、このプロジェクトが示していることだと思う。