How I built AI model comparison pages: pairwise grouping, Claude content, libSQL dedup A developer built a pipeline to auto-generate AI model comparison pages for aiappdex.com, pairing models by HuggingFace pipeline tag and using Claude Haiku to produce structured side-by-side evaluations. The approach groups the top 4 models per task category to keep comparisons relevant while capping total pairs at 50 per run for cost efficiency. My AI tools directory aiappdex.com https://aiappdex.com launched with individual model pages — one page per HuggingFace model, with download counts, tags, and Claude-generated pros and cons. That's a minimum viable directory. But directory users don't only search for a model by name; they search for two models side by side. "Llama 3 vs Mistral 7B for a local inference project." "Which text-classification checkpoint fits my fine-tuning budget." Comparison pages serve a different intent than detail pages, and I built a pipeline to generate them at scale. This is how the pipeline works, what its failure modes are, and what I'd change if I were starting over. The argument for comparison pages is search intent. A user who types " model A vs model B " is further along in an evaluation than one who types " model A ." They already know which models they're considering; they want a structured side-by-side. That's a more specific intent signal. The argument against auto-generating them is quality. If I ask Claude to compare two models it knows nothing concrete about and produce a paragraph of accurate-sounding generalities, I'm not adding value — I'm producing plausible boilerplate. The prompting had to produce content specific enough to be useful, or at least honest enough not to actively mislead. The experiment I'm running https://dev.to/morinaga/i-built-3-programmatic-seo-sites-for-25month-using-claude-haiku-heres-the-full-architecture-3pl8 has a limited monthly budget ~$25 , so the comparison generation had to be economical. I'm using Claude Haiku for this step, for the same reasons I use it across all three directory ETLs: fast, cheap, and reliably produces well-formed JSON when the prompt is constrained. With ~1,500 models in the database, the number of possible pairs is roughly 1.1 million. Generating and caching a million comparisons isn't feasible. I needed to reduce that to something tractable and coherent. First instinct: pair by download count. The top-N models globally give you the highest-profile pairs — Llama vs Mistral, bert-base-uncased vs roberta-base. This produces a handful of defensible comparisons for popular queries, but it ignores the long tail almost entirely. My directory is specifically built for the long tail where the big-name aggregators don't focus. What I settled on: group by pipeline tag, then pair the top 4 within each group. HuggingFace's pipeline tags roughly correspond to task categories: text-generation , text-classification , image-to-image , token-classification , and so on. A user evaluating a text-classification model cares about comparisons to other text-classification models, not about image generators from the same organization. js const byPipe = new Map