ニュースのダイジェストを毎朝メールで送ってくるサービス hallucinator
hallucinator
RSS / Atom / JSON Feed を集めて、AIに要約させて、毎朝メールで送ってくるWebサービスを作った。
ほぼ個人用。ClaudeCodeで作った。

作った理由
ニュースを毎日見ているわけではないが、全く世の中の出来事を知らないのも困るので、読める量のダイジェストくらいはサッと見たい。
AIは、大きな入力からピックアップしたり要約したりするのが得意な領域。
ニュースなんてそもそもどこまで本当なのかよくわからないし、このサービスの精度もよくわからない。なので名前を hallucinator にしてみた。今考えるとあんまり意味がわからない名前で、良くなかったなと思っている。
動作の流れ
1日1回のバッチ処理で動く。Cloud Scheduler が時間で叩く。
- 1:00 (JST) 登録済みフィードを HTTP GET、SHA-256 ハッシュで変更を検出、変わってたら保存してパース、GUIDで重複排除しながら記事をDBに入れる
- 2:00 (JST) 前日分の全記事を gpt-4o-mini に投げて、カテゴリ別のMarkdownダイジェストを生成。全体サマリーとは別に、ユーザーごとのパーソナライズドサマリーも作る
- 9:00 (JST) AWS SES でメール送信
- 毎月1日 3:00 (JST) 2ヶ月前のデータを JSONL → tar.gz にして GCS にアーカイブ。本体DBからは消す
Web で過去のサマリーや記事を見ることもできる。
クリックすると興味スコアが上がる
Web で記事を開く・検索結果からクリックする・元記事リンクに飛ぶ、それぞれを重みつき(1 / 2 / 3)で記録している。
過去30日ぶんを集計してカテゴリ別の興味スコアを出し、翌日のパーソナライズドサマリー生成時に、上位5カテゴリを OpenAI に投げるシステムプロンプトに足している。
設定画面から特定の興味カテゴリを削除することもできる(その分野はもう見たくない、というときに)。
技術構成
- SolidJS + SolidStart + Vinxi
- Tailwind CSS + Kobalte (ヘッドレスUI)
- PostgreSQL 16 + Drizzle ORM
- OpenAI API (gpt-4o-mini)
- AWS SES(メール送信)
- kuromoji(日本語の形態素解析。サマリー中の固有名詞を抽出してリンク化)
- Google OAuth (arctic)
- Bun(ランタイム・パッケージマネージャ)
- Terraform で GCP リソース管理
- Google Cloud Run + Cloud Scheduler + GCS + Artifact Registry
- Docker で本番イメージを作り、Cloud Run にデプロイ
SolidStart は使ってみたけど、特に便利ではなかった。
クラウドインフラ
メインは GCP。Cloud Run でアプリを動かし、Cloud Scheduler が cron を叩き、GCS にアーカイブを置く。
ただしメール送信だけは AWS SES を使っている。GCP には同等のメール送信サービスが無いし、SendGridなどの外部サービスを使うより SES のほうがシンプルで安かった。@aws-sdk/client-ses はこの1機能のためだけに入っている。
工夫したところ
- フィードの形式が RSS / Atom / JSON Feed / RDF とバラバラなので、フォーマットを自動検出してパーサーを切り替える。新聞系(朝日・毎日・日経)は個別の癖があるので専用パーサーを書いた
- 生レスポンスを SHA-256 でハッシュ化して保存しておき、内容が変わっていなければ再パースしない
- Cloud Scheduler から Cloud Run の cron用エンドポイントを叩くところは、Googleサービスアカウントの OIDC トークンで保護。
crypto.subtleで自前検証して、外部ライブラリには依存させなかった - バッチジョブは fire-and-forget。APIはジョブをDBに登録したら即座に 202 を返して、処理は裏で進める。管理画面から進捗をポーリング表示
- 月次アーカイブは外部依存無しの自前 tar 実装で、JSONL を tar.gz にして GCS に上げている
使ってみて
意外と読んでいる。