はじめに ― 「あとで読む」を終わらせる
エンジニアは記事を保存する。
気になる技術ブログ、公式ドキュメント、パフォーマンス検証、設計思想の考察。
raindrop.io に次々とリンクを保存していく。
だが、現実はこうだ。
読まない。
正確に言えば、「今は読まない」が積み重なっていく。
そこで作ったのが raindary というアプリだ。
raindrop.io に保存した記事を自動取り込みし、本文を抽出し、AIで事実を整理し、トーン付きで要約を生成する。
そして生成された要約だけを保存する。
このプロジェクトは GitHub で公開している。
👉 https://github.com/enjoydarts/raindary
個人開発だが、設計はかなり真面目にやっている。
特に意識したのはこれだ。
LLM以外は無料プランで構築する
AIアプリは高コストというイメージがある。
しかし設計を工夫すれば、ランニングコストはClaudeのトークン消費だけにできる。
この記事では、その設計と技術スタックを、初心者にも理解できるように丁寧に解説する。
raindrop.ioを基盤にする理由
まず、なぜraindrop.ioなのか。
raindrop.io は単なるブックマーク管理サービスではない。
- コレクション管理
- タグ
- API公開
- OAuth2対応
- RESTエンドポイント
が整っている。
つまり、
「ユーザーが保存した記事データを外部から取得できる」
ということだ。
raindaryでは、raindrop.ioを一次データストアとして扱っている。
記事のタイトル、URL、タグ、保存日時はraindrop側が管理する。
raindaryはそれを取り込み、AI生成結果だけを保存する。
この責務分離には大きな意味がある。
- 記事削除に追従できる
- データ重複がない
- 同期ロジックが単純になる
基盤を信頼し、自分は付加価値に集中する。
これが設計の出発点だった。
全体アーキテクチャ
raindaryの構成は以下の通り。
User
↓
Vercel (Next.js 15 + Auth.js)
↓
Inngest Cloud
↓
Render (FastAPI + trafilatura)
↓
Anthropic Claude API
├ Haiku 4.5(事実抽出)
└ Sonnet 4.5(要約生成)
↓
Supabase PostgreSQL
重要なのは、各レイヤーが明確に分離されていることだ。
- UIはUI
- 非同期は非同期
- 抽出は抽出
- LLMはLLM
- DBはDB
単一サービス依存ではなく、責務で分けている。
Vercel ― フロントと軽量APIの基盤
raindaryのUIとAPIはVercel上で動いている。
使用しているのは Vercel Hobbyプラン(無料)。
Hobbyプランでも、
- GitHub連携
- 自動デプロイ
- Preview環境
- Serverless Functions
- Edge Middleware
が利用できる。
Next.jsを使うならVercelは相性が良い。
ただし設計上のポイントは、
Vercelで重い処理をしないこと。
- OAuth認証
- API受付
- Inngestイベント発火
だけを担当させている。
重い処理はすべて非同期へ逃がす。
Next.js 15 ― フルスタック基盤
Next.jsはReactベースのフレームワークだが、現在はフルスタックの役割を担う。
App Routerにより、
- ページルーティング
- レイアウト共有
- サーバーコンポーネント
- API Routes
を統合できる。
初心者向けに言えば、
フロントと軽量バックエンドを一緒に書ける
という仕組みだ。
raindaryでは、Next.jsはイベントの入り口であり、処理の本体ではない。
実処理はInngestへ。
この分離がスケーラブルな設計を可能にしている。
Supabase ― 無料で使えるPostgreSQL
データベースにはSupabaseを採用。
利用しているのは Freeプラン。
Freeプランでも、
- PostgreSQL
- Row Level Security
- ダッシュボード管理
が利用できる。
raindaryはマルチユーザー対応。
そのため全テーブルに user_id を持たせ、RLSで
auth.uid() = user_id
を強制している。
これは、
データベースレベルでユーザーを隔離する
設計だ。
アプリにバグがあっても、DBが守る。
Inngest ― ワーカーを持たない非同期処理
raindaryでは、重い処理をすべてイベント駆動にしている。
Inngest Cloudの Hobbyプラン(無料) を使用。
通常、非同期処理にはRedisとワーカーが必要だが、Inngestなら不要。
- イベント発火
- リトライ
- 並列数制御
- エラーハンドリング
が宣言的に書ける。
raindaryの処理フローは:
- import.requested
- extract.requested
- summarize.requested
UIはイベントを送るだけ。
これによりUXは軽い。
Render ― trafilatura常駐とスリープ仕様
本文抽出にはPythonの trafilatura を使用。
HTMLからノイズを除去し、記事本文を高精度に抽出できる。
Node系より安定している。
この抽出APIはRenderの Hobbyプランで動かしている。
ただし重要な仕様がある。
RenderのFree / Hobby Web Serviceは、
約15分リクエストがないとスリープする。
次のリクエストで再起動(コールドスタート)が発生し、数秒〜十数秒の遅延が出る。
これは公式仕様。
つまり、
- しばらくアクセスがない
- サービス停止
- 次回リクエストで起動
という挙動になる。
raindaryでは、
- 抽出は非同期
- UIは待たない
ため、この遅延は問題にならない。
Claude 4.5 の価格と役割分担
raindaryでは2段階生成を採用している。
事実抽出 ― Claude Haiku 4.5
価格:
- 入力 $1 / 1M tokens
- 出力 $5 / 1M tokens
軽量で高速。
JSON形式で事実を抽出。
要約生成 ― Claude Sonnet 4.5
価格:
- 入力 $3 / 1M tokens
- 出力 $15 / 1M tokens
高品質な文章生成。
なぜ分けるのか?
価格差が約3倍あるからだ。
全文をSonnetに直接投げるとコストが跳ねる。
Haikuで圧縮 → Sonnetで仕上げ。
これがコスト最適化の核心。
コスト構造
raindaryのランニングコストは、
Claudeのトークン消費のみ
- Vercel Hobby
- Supabase Free
- Inngest Hobby
- Render Hobby
は無料枠内。
api_usageテーブルでトークンとコストを保存し、月間利用量を可視化している。
AIアプリで重要なのは、
コストが見えること。
まとめ ― raindaryの設計思想
raindaryは、
- 保存はraindrop.io
- UIはNext.js
- 非同期はInngest
- 抽出はRender常駐
- 事実抽出はHaiku 4.5
- 要約生成はSonnet 4.5
- 永続化はSupabase
という責務分離で構成されている。
そして最大のポイントは、
LLM以外は無料で回る
AIアプリは設計で決まる。
コードは GitHub に公開している。
👉 https://github.com/enjoydarts/raindary
設計の全体像を知りたい人の参考になれば嬉しい。


コメント