The idea
A web app you run on your own server that polls RSS and Atom feeds and ranks every incoming article by how well it matches a plain-English interest profile you write once. Instead of a flat reverse-chronological list, you see a scored queue — highest relevance at the top. The scoring runs entirely locally via Ollama, so no reading history leaves your machine.
Why build this
Anyone following more than a dozen feeds knows the firehose problem: volume overwhelms signal within days of subscribing to anything interesting. Commercial services like Feedly and Inoreader offer some filtering, but they require a subscription and log your reading habits on their servers. The missing piece has been affordable, private inference. Running a capable language model locally — llama3 or Mistral on modest hardware — is now routine, which makes per-article relevance scoring cheap enough to do on every poll cycle. The integration layer tying feed fetching to LLM scoring is a thin, buildable gap.
Stack sketch
- Backend: Python with FastAPI; handles feed polling, scoring queue, and the REST API
- Feed parsing:
feedparserfor RSS/Atom normalization,httpxfor async fetching - LLM scoring: Ollama HTTP API running llama3.2:3b locally — small enough to score a batch of articles in a few seconds
- Database: SQLite via
sqlite-utils; one table for feeds, one for articles with score and read status - Frontend: HTMX + Jinja2 templates served by FastAPI, no JavaScript build step
- Deployment: single Docker container, SQLite database on a mounted volume
Scope for v1
- Add and remove feed URLs through a settings page; store the interest profile as an editable text field in the same settings view
- Poll feeds on a configurable interval (default: every 30 minutes) via APScheduler running in-process
- Score each new article's title + summary snippet against the interest profile; store the 0–10 float returned by the LLM
- Render articles sorted by score descending; show source, publish date, and score badge
- One-click "mark as read"; unread count in the page title
- Out of scope for v1: OPML import, multi-user accounts, full-text fetch, email digest, mobile app
Where it could go
The obvious next step is a feedback loop. When you mark an article as "useful" or explicitly skip it, the system logs that signal and can periodically re-prompt the LLM with a refined profile, or train a small logistic regression classifier on top of the raw scores. Over a few weeks the rankings get meaningfully more accurate without any manual tuning.
A second path is lightweight sharing. One instance, multiple interest profiles — each user logs in with a password, maintains their own profile, and sees their own ranked queue. Add a "share" button that sends an article link to a teammate's unread queue, and the reader doubles as a low-friction internal link-sharing tool for small teams.
Watch out for
Not all feeds publish a stable guid field; without one, deduplication after a feed hiccup requires a fallback key (URL + title hash). Scoring every article synchronously during polling will block the queue on slower hardware — run scoring as a background task so the UI stays responsive even when Ollama is busy with a larger batch.