Inspiration

Every query letter to a literary agent needs two or three comparable titles. Agents want them recent, usually from the last three years, and mid-sized, because comparing your debut to a megahit reads as naive. Finding good comps takes months of reading in your genre. Asking ChatGPT is faster, but it suggests decade-old classics and bestsellers, credits books to the wrong author, and often hands your own seed books back to you.

We wanted comps that real readers of your book actually read, with proof an author can trust.

What it does

  1. Enter three books your novel reads like, or click a preset.
  2. An agent looks them up in Qloo and asks Qloo's taste graph which recent books those readers also love.
  3. Every candidate is checked by rules in code: published inside the recency window, under a popularity ceiling (no megahits), not one of your own books, not a duplicate.
  4. Accepted comps go on a shelf with their evidence: Qloo entity ID, publication year, popularity score and the seed books that led to them. Rejected books stay visible with the rule they broke and the value that failed.
  5. On the same screen, a plain LLM answers the same question and its titles go through the same rules. On a typical run Nearshelf scores 6 of 6 and the LLM 0 of 6.
  6. Check my comps grades comps you already have, and Readers also love shows podcasts and TV shows the same readers like, for ad targeting.
  7. Copy a ready-made comp line for your query, or share a link that reruns the search.

How we built it

  • Next.js 16 on Vercel, TypeScript, one deployment for UI and API routes.
  • Qloo /search resolves titles, /v2/insights with filter.type=urn:entity:book, signal.interests.entities, filter.publication_year.min and feature.explainability finds candidates, and cross-domain insights find podcasts and TV shows.
  • Agent: Vercel AI SDK with an NVIDIA-hosted Nemotron model and two tools, resolve_titles and find_comps. The agent decides what to call, never what to recommend. It can only resolve titles the author typed and only query with IDs it resolved. Its text is never shown. If the model fails, a fixed sequence finishes the run without repeating Qloo calls.
  • Rule engine in plain TypeScript, so every verdict is deterministic and testable.
  • Evaluation: npm run eval runs 8 seed sets across genres. Of 48 plain-LLM titles, only 7 passed every rule. 32 were too old, 14 too big, 11 were the author's own seed books and 10 had the wrong author. Nearshelf returned 6 checked comps for every set.
  • 62 tests (Vitest), CI on every push, 24-hour server cache with in-flight de-duplication, and a per-IP rate limit.

Challenges we ran into

  • Calibrating "too big". Qloo popularity is compressed near 1. Probing known titles showed megahits at 0.9996 and above and mid-list books at 0.989 and below, so the ceiling sits at 0.995.
  • Parsing real responses. Book names carry series suffixes like "(Series, #2)", authors arrive inside a disambiguation string, and some names contain HTML entities. Matching titles needed all three handled.
  • Making rules visible. Sending the popularity limit to Qloo as a filter hid every rejection. We now let megahits come back and reject them in code, on screen.
  • Cross-domain signals. Qloo rejects some book IDs as signals for podcasts and TV. Pooling the resolved seeds with the top comps fixed it for every seed set we tested.

What we learned

The most convincing thing an AI product can show is what it refused to do. Putting the plain LLM's answers next to ours, through the same rules, made the case better than any copy we wrote.

What's next

Screenplay and pilot comps using Qloo's movie and TV data, and per-genre popularity ceilings.

Built With

Share this project:

Updates

Submission history