この記事のポイント
- ▶毎朝10〜15分かかる情報収集ルーティンを、AIメールエージェントで完全自動化した
- ▶設計判断をADRで先に文章化したことで、Claude Codeとのペア開発が驚くほどスムーズになった
- ▶Strategyパターン + サーバーレスの疎結合設計が、リリース後の機能追加を1行で済ませた
なぜ作ったか
毎朝スマートフォンを開いて、気になるニュースサイトを順番に巡回する習慣があった。これが10〜15分かかるうえ、情報が散在していて頭の整理がしにくかった。
「業界の動き」「気になるトピックのまとめ」が一枚のメールにまとまって届いていれば、起きてすぐに今日の文脈を掴んで動き出せる。それが出発点だった。
こうして「morning-agent」というプロジェクトが始まった。目標は「使えるものを3日で作る」。実際、ほぼその通りに仕上がった。
コンセプト:「秘書」ではなく「新聞」
最初のプロトタイプはLLMに「今日はどう動けばいいか教えて」と問いかける形だった。しかしそれでは毎朝の判断をAIに委ねすぎになる。自分の思考を外注してしまう感覚が気持ち悪かった。
方向を転換し、読者が自分で判断できるよう情報を整理して届ける、新聞的なスタンスに落ち着いた。LLMの役割は「判断」ではなく「編集」だ。
「今日はAを優先して、Bは明日にしましょう」とAIが指示を出す。判断をAIに委ねる設計。
情報を整理・要約して届ける。判断は常に人間が行う。AIは編集者に徹する。
この判断は後から見ても正しかったと思う。ツールが「行動の提案」をしてくると、次第に提案通りに動くようになる。主体性を保つための設計上の制約として、意図的に「情報提供どまり」にした。
アーキテクチャ設計
エージェントを疎結合にする(ADR-002)
情報ソースは増減する。現在はWebの1系統のみだが、将来的にNotion・RSS・Xなどが加わるかもしれない。そのため、各収集エージェントを Agent インターフェースで抽象化し、pipeline.register() の1行で追加・削除できる Strategyパターン を採用した。
[collect phase] [compose phase]
WebAgent ──── Pipeline ──→ ComposerAgent ──→ SES
収集層は Promise.allSettled で並列実行する。1つのエージェントが失敗しても他の結果を捨てない設計は、毎朝欠かさず動くツールには不可欠だった。
サーバーレスで維持費ゼロ(ADR-001)
1日1〜2回の定時バッチにEC2やECS Fargateは過剰だ。AWS Lambda + EventBridge Scheduler なら、月間のAPI呼び出しが無料枠に収まり、常時起動コストもゼロになる。
実行時間の上限(15分)がトレードオフだが、情報収集・生成・送信を合わせても5分以内を非機能要件とすることで許容範囲に収めた。
観測基盤は最小限に(ADR-003)
LLMのトークン使用量とコストは把握したい。Langfuseなど専用の観測SaaSも検討したが、1ユーザー・1日数回の実行規模では導入コストが見合わない。
技術スタック
| 役割 | 技術選択 |
|---|---|
| LLM | Claude API(claude-haiku-4-5 / sonnet-4-6) |
| 実行基盤 | AWS Lambda (Node.js 22.x) |
| スケジューリング | EventBridge Scheduler |
| メール配信 | Amazon SES |
| インフラ定義 | AWS CDK (TypeScript) |
| 言語 / バンドル | TypeScript 5.x / esbuild |
モデルはコスト最適化の観点から、情報収集・要約の大半をclaude-haiku-4-5で処理し、最終的なメール本文生成のみ高品位なモデルを使う設計にした。
開発の流れ
全工程が約3日で完結した。Claude Codeをペアプログラマーとして使い、ADRで事前に設計を固めてから実装に入ったことで、手戻りが少なかった。
| 日付 | 出来事 |
|---|---|
| 2026-03-23 | ADRを書き、初版実装。Web検索・メール送信の動作確認まで完了 |
| 2026-03-23 | モデルをHaikuに切り替えてコスト削減。記事まとめを新聞レイアウトに変更 |
| 2026-03-24 | 日付ズレのバグ修正。収集フェーズと送信フェーズの分離。配信時刻を6:30に変更 |
| 2026-03-24 | AWSへのリリース完了。本番稼働開始 |
| 2026-03-25 | 夕刊便(18:00 JST)を追加。朝刊・夕刊の2便体制に |
Claude Codeとの役割分担
実装は「ADRでインターフェース定義 → Claude Codeに実装を依頼 → 動作確認 → 次のレイヤーへ」というサイクルで進めた。プロンプト集(ADRファイル内)を先に書いたことで、Claude Codeへの依頼が具体的になり、出力がぶれなかった。
印象に残った細部の決断
メール件名を「今日のブリーフ」から「ピックアップ情報」に変えた。「ブリーフ」という言葉が自分には少し硬く感じられたためだ。こういう細部がツールの雰囲気を決める。
夕刊便の追加は、EventBridgeのcron設定を追加するだけで実現できた。疎結合な設計が一番効いた瞬間だった。
振り返り
うまくいったこと
- ADRを先に書いたことで、実装中に「なぜこう作ったか」を迷わなかった。コードを見返す前にADRを読めば意図がわかる。
- Strategyパターンによる疎結合が、夕刊便追加時に効果として現れた。register()を1行追加しただけで完了した。
- コスト管理をCDK + CloudWatch Logsの組み合わせで完結させ、外部依存を最小にできた。
次にやるとしたら
- —トピック設定をUIから変更できるようにしたい(今はyaml直編集)
- —メール本文のパーソナライズ精度を上げるため、過去の開封率や反応をフィードバックループに組み込む
- —複数ユーザーへの展開時点でLangfuseを導入し、プロンプトのA/Bテストを行う
「使えるものを3日で作る」という目標は達成できた。ADRという形式で設計の意図を文章に残しておくと、実装中・リリース後のどちらでも判断の根拠に立ち返れる。ツールを作ること自体より、なぜこう作るのかを先に言語化する習慣が、短期間でのリリースを支えたと思う。