このサイトでの挿絵は、Gemini Proによる生成が大半です。 また、情報収集・まとめなどのサイトについては、検索結果をベースとしたものを使用しています。可能な限り出典を明示するよう努めますが完全ではありません。各自で調べる事もお忘れなく。

フィードジェネレーターの開発(n番煎じ)

n8nでRSSフィードから記事内容をざっくり取得して、自分の欲しい情報と思われるものをDiscordの個人チャネルへ流すというツールを作っていくと、一部のサイトではRSSが存在しなかったりすることが問題されたりします。

そこで、いたるところであるとは思いつつも、適当にフィードを生成するツールが欲しいということになって、ClaudeさんとGeminiさんに協業してもらいながら作成しています。

ポイントとして

  • セルフホスティングであること(自分のサーバーで走ること)
    • これがコスト最小限への道だと思う、GCPのAlways Freeでも普通に動きます
  • CLIで動かせること
    • 動作チェックやフィードURLのみ生成するときに便利

まずはこの辺りから始めることにして、Claude/Geminiに対するバイブコーディング的な開発を行っています。 個人的な作り方として、以下を意識しています。

  • やりたいことをtask.mdというファイルを作って書き出す。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# なにを作るか

引数もしくは設定ファイルにより、URLを渡すことで、そこに書かれた内容を分析してRSS feedを生成・出力するプログラム。

# どのように作るか

- Pythonベースで実装する。
- Python環境はuvで初期環境を構築済みのため、uvで必要なライブラリは登録していく
- 基本的な開発方法はグラウンドルールに従う
    - タスクに応じたブランチを作って切り替える、可能であればワークツリー機能で切り分けることで並列化も検討する
    - 仕様を決める
      - EARS表記法をベースにspec.mdに記載する
    - 使用に応じたテストを作り、Red状態であることを確認する
    - 実装する、テストが通る(Green)かを検証する、ダメなら再度実装を点検していく
    - Greenとなったところでリファクタリングを実行し、コードの品質を上げていく
    - 仕様と間違ったことになっていないかを再度点検する
    - コミットしてmainにマージする、完了ブランチは適宜削除していく
- 関数・メソッドはそれぞれがシンプルな作りとなることを意識する
- 型ヒントおよびdocstringを記載する、可能であればdoctestも入れて使い方をはっきりさせておく
- テストはpytestを使用する
- コードの品質はRuffでチェックする
- 不要なファイルはできる限り.gitignoreに登録し、早いうちに混入しないように配慮する、場合によってはfilter-branchを使用して対応する
- 使い方のマニュアルを適宜整備する
    - ドキュメントはdocs以下に配置する
    - トップのREADME.mdに概要を記載する
- 最終的には、Webサービスとしてデプロイしたい(個人用)
    - GETリクエストでURLを受け取り、RSS Feedを生成して返す

こんな感じで目標になりそうなことを書いてから、claudeを起動して、このファイルを読ませて仕様の検討をまずさせます。適当にやりとりした後、仕様をはっきりさせる(spec.mdという形で作成させる)、ブランチを切って作業させるという流れです。

仕様の例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# feedgen 仕様書(EARS表記法)

## システム概要

URLの内容を分析してRSS Feedを生成するシステム。
コアライブラリ、CLI版、Web API版の3層構造で実装する。

## コアライブラリ(feedgen.core)

### 既存フィード検出機能

**Event**: URLが指定されたとき
**Actor**: FeedDetectorクラス
**Response**: 指定されたURLに既存のRSS/Atomフィードが存在するかを検出し、発見した場合は代理取得する
**System**: feedgen.core.FeedDetector

#### 詳細動作

1. HTMLのlinkタグ(rel="alternate")からフィードを検出
2. 一般的なフィードパス(/feed, /rss, /atom.xml等)を確認
3. 発見したフィードの取得・配信

#### 対応フィード形式

- RSS 2.0(application/rss+xml)
- Atom(application/atom+xml)
- JSON Feed(application/json)

### Feed生成機能

**Event**: URLが指定されたとき(既存フィードが見つからない場合)
**Actor**: FeedGeneratorクラス
**Response**: 指定されたURLの内容を分析し、RSS形式のフィードを生成する
**System**: feedgen.core.FeedGenerator
(以下略)

こうすると、リミッターでしばらく動けなくなったとしても、あとでtask.mdとspec.mdを見ることで思い出しが容易になります(そうなると期待し、それなりに動いていると思われる)。

開発の流れ

開発を実際に行う際には、いきなり始めさせずに、以下の文言のようなものを指示として入れています。

「spec.mdを必ず確認し、今回の作業で必要な仕様を確認した上で、実装を行うこと。必要な仕様がない場合は、まずはその仕様を追加すること。」 「必ずテストを作り、Red-Green-Refactorのサイクルを守ること。」 「1トピック1コミットの原則を守ること」

これらの指示により、万一ダメな方向になったとしても手戻りがしやすくなります。 また、実装の前には必ずブランチで作業するようにしているので、ダメなときはgit reset --hardで戻せるので安心ですね。

まとめ

このように、n番煎じのフィードジェネレーターを作るにあたっても、AIの力を借りて開発を進めています。 現在追加の機能として、以下を実装しています(完了もあれば作業中もある)。

  • YouTubeの検索URLを渡すと、該当動画の概要まで含めたRSS Feedを生成(YouTube Data APIを使用)
  • Google Newsを喰わせたときに、記事のタイトルと概要を含めたRSS Feedを生成
    • リンクがGoogleを踏む形になっているので、事前に(可能な範囲で)ソースのURLに置換する機能(実装中)
    • キャッシュによる負荷軽減(実装中)
  • フィード生成ロジックの抽象化で、今後同様にRSSを持たないものに対する実装を容易にする設計

前々から作りたいと思っていたものをこうやって自力だけでは足りない部分をかなり補ってもらえるようになったのはとても重要ですね。こういう形での創作活動もいいものですよ。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。