News intelligence pipeline
20,000 news items a day, embedded, clustered into stories and enriched by LLMs — at 15% of the bill it used to run on.
- Role
- Senior engineer — design, implementation and operation
- Stack
- Python 3.12 Airflow 3 Kubernetes pgvector PostgreSQL MongoDB LLMs AWS
Problem
Enterprise customers need to know which news actually touches them. The raw feed is around twenty thousand items a day and heavily duplicated: the same story rewritten by fifteen outlets, none of them adding anything. Volume was the product's biggest liability — more input meant more noise, and running an LLM over every item meant paying fifteen times for one story.
Approach
A staged pipeline on Airflow 3 over Kubernetes, ordered so the cheap deterministic work happens before the expensive probabilistic work. Every article headline is embedded into a 1536-dimension vector stored in Postgres with pgvector; articles are grouped by cosine distance under a tuned threshold, and each cluster elects a representative by source importance. Only then do the LLM insight stages run — over stories, not articles. A second clustering pass merges stories across days, so a topic that runs for a week stays one thread instead of seven. Model and prompt choices are decided by evaluation against the values the analyst team validated, with cost sitting in the column next to accuracy.
Outcome
Twenty thousand items a day flow through unattended, and the pipeline's annual LLM bill came down by 85% — from a six-figure sum to a five-figure one. Clustering is what did most of it: paying once per story instead of once per article, then letting evaluation move stages onto smaller models wherever the numbers said quality held.
Cheap work before expensive work
The single decision that paid for everything else was ordering the stages by cost. Embedding a headline is cents per thousand; classifying an article with a frontier model is not. Collapsing fifteen near-duplicate articles into one story before the LLM stage means the expensive step runs on a fraction of the input — and it improves the output too, because the analyst cares about the story, not about which outlet filed it first.
Clustering, concretely
Headlines are embedded rather than full bodies: a 1536-dimension vector per article, written in batches to keep the database from becoming the bottleneck, and queried with pgvector’s cosine distance operator under a threshold tuned to sit between “splits one story in two” and “merges two stories into one”. A cluster’s representative article is chosen by source importance, so the story that surfaces is the one from the outlet that matters.
Then the same machinery runs a second time over a multi-day window. Daily clustering answers “what happened today”; the multi-day pass answers “is this still the same story as yesterday”, which is the question a customer tracking a regulation actually has.
Evaluation, not vibes
Model choice is the least interesting decision in an LLM pipeline. What makes a stage shippable is being able to answer “is this better than what we had?” before a customer sees it — which means a labelled set drawn from decisions the analyst team already validated, and a score per candidate. Once that exists, moving a stage to a smaller model is a one-line experiment with a number attached, and that is where most of the 85% came from.
What I can and cannot show
Under NDA: shape and outcome, never customer data, prompts or internal diagrams.