live · one briefing, every evening
Evening news briefing
- Built for:
- One person whose inbox had turned into a dozen overlapping AI feeds and four homegrown briefing bots — and who wanted to read one thing in the evening, not thirty.
- Not built for:
- A team newsroom. Vesper ranks the day against one person’s projects and writes for one reader; the relevance model is personal on purpose.
By the evening my inbox held a dozen newsletters, several feeds, and the output of four briefing bots I’d built earlier — all overlapping, none synthesized. Vesper collects all of it, ranks it against what I’m actually working on, and hands a local model the survivors to explain in plain language. What lands is one email: tonight’s news, in tiers, with a short list of what to actually do about it.
The problem
The cost of staying informed isn’t finding the news — there’s too much of it — it’s the synthesis nobody does for you. Twelve sources tell you about the same model launch in twelve voices, and none of them tell you whether it matters to the thing on your desk. Worse, I’d built the part of the problem myself: a morning brief, a video scout, an inbox triage, each emailing me separately.
So Vesper is an aggregator that sits on top of the others. It eats their output and everyone else’s, dedupes the overlap, ranks what’s left against my projects, and replaces the whole flood with one synthesized read. The feeders still run; their emails are suppressed so only Vesper’s lands.
Decisions
local synthesis
The ranking and the writing run on a local model — qwen3:14b through Ollama, on the same machine that collects the feeds. The judgment about what matters to my work, and the words that explain it, never leave the box. A produce step can add narration and illustration through cloud APIs when I want a richer page, but the thinking is local and free.
explain it plainly
Every surviving item gets three things from the model: a plain-language explanation with an analogy, an importance tier, and one honest line about whether it touches anything I’m building — or the exact words “No direct relevance.” The tiers are forced to spread, so a quiet day reads like a quiet day. It’s synthesis, not a link dump.
one email
The whole point is de-flooding, so the output is exactly one message a day. The feeders it aggregates are flagged to stay silent; their work flows into Vesper instead of into my inbox. Dozens of notifications collapse into a single evening briefing with a short, verb-led action list at the top.
System
Six stages. Collection fans in from many source types; ranking and dedup cut the volume down; a local model synthesizes the survivors into tiered stories and an action list; a produce step renders the page; one email goes out.
| Layer | Implementation | Purpose |
|---|---|---|
| Schedule | Windows Task · 5 p.m. | One run, one email, daily |
| Collect | 36 feeds · 9 source types | RSS · Reddit · Gmail label · feeders |
| Rank | Keyword relevance + dedup @0.85 | Cut the volume to what matters |
| Synthesize | Ollama · qwen3:14b (local) | Explain · tier · project impact |
| Produce | HTML page · optional narration | Skippable, illustrated presentation |
| Deliver | SMTP · one message | Replaces dozens of feeder emails |
# Each surviving item is handed to a local qwen3:14b with a
# strict contract: explain it like I'm twelve (with an analogy),
# assign an importance tier, and name the ONE project it touches
# — or say so plainly. Pydantic validates every reply.
class Story(BaseModel):
item: Item
explanation: str # plain-language, with an analogy
tier: int # 1 = big news … 3 = quiet; tiers spread
impact: str # the project it touches, or "No direct relevance."
class Brief(BaseModel):
stories: list[Story]
action_list: list[str] # 3–6 items, each starts with a verb
weeded_out: list[Item] # what was collected but cut
{
"explanation": "A new open model that runs on a normal
GPU — like getting a sports-car engine that fits a
sedan. You no longer have to rent the cloud to use it.",
"tier": 2,
"impact": "Companion — a smaller local model to test
against the current Ollama backend.",
"_then": "rolled into the day's 3–6 verb-led actions"
}
Where it stands
Live, on a daily 5 p.m. schedule. It collects from thirty-six feeds across nine source types, ranks a couple thousand raw items down to the handful worth reading, and ships one briefing. The whole synthesis runs locally; the only cloud is an optional polish layer for narration and illustration when I want the richer page. It went from empty repo to running pipeline in two days — and it’s the one that finally made my own inbox quiet.
Acknowledgments
Vesper stands on Ollama and qwen3:14b for local synthesis, Pydantic for a strict contract with a model that wants to ramble, feedparser and the Gmail API for collection, and the idea that the right number of evening emails is one.
← Index