-
-
Live agent log: each Qloo and LLM call appears as it runs, with one-click examples
-
Opener candidates for Radiohead in Berlin, scored by audience fit with popularity and 90-day trend
-
Where the headliner's fans are in Berlin, plus the audience's age and gender skew and taste tags
-
Local partners from the same audience, and what this brief does not prove
-
Fill a free date: acts the Mohawk Austin crowd likes, ranked by audience fit
-
Agent log for Mohawk: Qloo returns needs_input, the agent retries with the city
Inspiration
Support acts are usually picked on gut feel, agent pitches and streaming counts. None of those say whether the opener's fans and the headliner's fans are the same people in this city, where in the city they are, or which local bars and shops they already go to. Qloo's taste graph answers those questions across music, places and brands. We wanted to see whether an agent could turn that graph into a booking brief a promoter could actually argue with: every number traceable to a Qloo call, and the limits written down.
What it does
Opener answers two booking questions:
- Find an opener. Enter a headliner and a city (optionally dates and capacity). Opener returns five candidates scored by audience fit with the headliner in that city, their 90-day trend, a map of where the headliner's fans are concentrated in the city, the audience's age and gender skew, taste tags for promo copy, and local partners (bars, pubs, cafes, record and clothing stores) plus brands the same audience likes.
- Fill a free date. Enter a venue, its city and a date. Opener finds acts that the crowd who likes this venue also likes, flags which are rising, and suggests cross-promotion partners that are not competing venues.
Every brief ends with "What this does not prove": fit scores are relative within one Qloo call, the date and capacity are not Qloo inputs, nothing is said about individual fans, availability and fees are not checked. A live agent log streams every Qloo and LLM call as it happens. Briefs can be shared by link, downloaded as Markdown or printed.
Why it needs Qloo
A language model on its own can name bands that "sound like" Radiohead. It cannot say how strongly two audiences overlap in Berlin specifically, where in Berlin that audience is concentrated, how it skews by age and gender, whether an act is rising this quarter, or which bars and clothing stores the same people go to. Every number in an Opener brief comes from a Qloo call (qloo_rank weighted to the city, qloo_where_popular, qloo_audience_demographics, qloo_trends, place and brand recommendations). The LLM only chooses which call to make next and writes a three-sentence summary from the brief's own data. Take Qloo away and there is nothing left to show.
We tried exactly that once. The same question (Radiohead, Berlin, 1200 capacity) went to the demo's fallback LLM with no Qloo and no tools; the raw request and answer are in the repository (docs/compare_without_qloo/). It named five acts, none of them on Opener's list, and backed them with figures it could not have looked up in that call: Spotify audience-overlap percentages, a record store's email-list share, a 2023 survey, a sold-out co-branded tote. A promoter cannot tell which of these are real. To be fair, its picks are newer acts; Opener's are support-sized relative to Radiohead but still established names (see Limits). None of the LLM's picks came with any evidence about Berlin.
How we built it
- Qloo through the official harness. The Node server runs
qloo mcp(@qloo/qloo-harness 0.1.26) as a child process and calls it over MCP stdio, against the hackathon endpoint. Tools used:qloo_describe,qloo_recommend(places filtered to the city, brands, and artists for the venue question),qloo_rank,qloo_trends,qloo_where_popular,qloo_audience_demographics,qloo_entity_tags. - One direct Insights call. The opener candidate pool needs a popularity ceiling, and the harness's
qloo_recommendrejects one, so that call goes toGET /v2/insightswithfilter.popularity.max(documented for artists). The agent log shows the exact request; if it fails or returns too few acts, the agent widens the ceiling once and then falls back to the harness. - Agent loop. An LLM (any OpenAI-compatible endpoint with tool calling) chooses the next Qloo call. In the hosted demo the primary is Gemini on the free tier and the fallback is NVIDIA Nemotron 3 Super through OpenRouter's free tier. A scripted planner fills in whatever the LLM skipped, or the whole plan if both LLMs are rate-limited or time out, so a brief always completes. The header of each brief says which of these actually ran.
- Numbers from Qloo, prose from the LLM. The brief is assembled deterministically from tool results. The LLM writes the summary from readable labels taken from the brief and cannot add facts; a summary that still quotes internal field names or raw numbers is replaced by a template.
- Time limits. The LLM gets 45 seconds to choose Qloo calls; after that, or as soon as it repeats a call, the scripted planner finishes the plan. The whole brief aims at 55 seconds, and the summary only gets the time that is left.
- Frontend. React + Vite, Server-Sent Events for the live log, Leaflet + OpenStreetMap for the fan map, one-click examples for both scenarios. One Docker container for hosting.
- Tests. Node test runner: 99 unit and API tests on a deterministic Qloo mock, including regression tests for each live-data problem below. CI on GitHub Actions with a Docker smoke test. No secrets in CI.
Built with AI coding agents (Claude Code, with independent review by Google Jules), directed and reviewed by the author.
Challenges we ran into
Most of them came from moving from the mock to live Qloo:
- Ambiguous names. A bare "Radiohead" is ambiguous across artist, person and album and returns
needs_input, so every signal carries the entity id fromqloo_describe. Venue names repeat across cities ("Mohawk": Austin, Bend, a college, a park); Opener retries once with the city. - Scores that all looked the same. Live affinities for a headliner's neighbours sit within about 0.01 (Radiohead in Berlin: top five 0.9655 to 0.9744). Dividing by the leader showed 99 to 100 for everyone. The score is now spread min-max across all acts in the same rank call, labelled "100 = strongest of the N acts Qloo ranked, 0 = weakest", with the raw affinity kept.
- Partners that were museums. Unfiltered place recommendations for Berlin were The Wall Museum and memorials. The place call is now constrained to Qloo category tags (bar, pub, brewery, coffee shop, record store, clothing store, music venue), and for a venue the competing rooms are dropped.
- Tags that were logistics.
qloo_entity_tagsmixes taste concepts with payment methods, price levels and dishes ("VISA", "Inexpensive"). Opener keeps only music genre, audience, style, theme and media keyword tag families. - Places have less data. Qloo returned no demographics for the venue SO36, and brand recommendations with a place as the signal returned an error. Opener then uses the venue crowd's top acts, and says so on the card and in the caveats.
- The headliner's own members. Qloo ranked Thom Yorke as the best opener for Radiohead: right by the numbers, useless for booking. Opener drops acts whose Qloo description names them as a member or side project of the headliner, quotes why in the caveats, and warns when it cannot tell.
- Free LLM tiers run out fast. Gemini's free tier for this model allowed about 20 requests a day per project in our runs, and one brief uses about ten. Opener falls back to a second provider, and if both are out the scripted planner finishes the plan.
- Repeated tool calls. One live run had the LLM call
qloo_recommendfive times in a row and hit Qloo's rate limit. Identical calls now reuse the earlier result, a repeat hands the plan to the scripted planner at once, and a Qloo 429 backs off and retries. - A brief that took two and a half minutes. On 14 October a Radiohead brief took 156 seconds: the primary LLM's free quota ran out after two calls and the free fallback spent 8 to 28 seconds per step. With a 45-second budget for the LLM, a provider that answered 429 skipped for two minutes, the Qloo harness started at boot and a 55-second target for the whole brief, the same brief took 35 to 44 seconds on the hosted demo later that day (one run took 63 seconds, before the 55-second target was added).
- Popularity that read 100 for everyone. Qloo popularity is a percentile, and every act Qloo recommended for Radiohead in Berlin sat between 0.9986 and 0.99999, so a 0-100 column showed 100 for all of them while Beck and Arcade Fire were offered as openers. Opener now shows "top 0.033%". The harness has no popularity filter, but Qloo's Insights API does (
filter.popularity.max), so the candidate pool asks for acts whose "top %" is a thousand times the headliner's, and widens that once if fewer than five come back. For Radiohead in Berlin the five went from Beck, The Smashing Pumpkins, Interpol, Pixies and Franz Ferdinand (all in Qloo's top 0.05%) to The Good, the Bad & the Queen, ...And You Will Know Us by the Trail of Dead, Spiritualized, Sean Lennon and The Notwist (top 0.4% to 2%). The same query also offered "On a Friday", Radiohead's own name until 1991; acts Qloo lists with an end year are now left out. - A summary that read like JSON. The LLM summary once said "overlap score of 100 with an affinity of 0.972". It now gets readable labels only, and a summary with field names or raw numbers is replaced by a template.
- Trends shape.
/v2/trendingreturns weekly points newest first, and a 90-day window came back empty; Opener queries 180 days and keeps the latest 13 points.
Accomplishments that we're proud of
- Both scenarios run end to end on live Qloo data, and every
error,emptyorneeds_inputfrom Qloo becomes a visible caveat instead of a silent gap. - The brief keeps working when the LLM fails: the planner completes the same evidence plan.
- The limits of the evidence are part of the product, not a footnote.
What we learned
- A taste graph is strongest when it crosses domains: the same audience that ranks the opener also points to the bars and brands for the show.
- Relative scores need to be shown as relative, with their base, or users read 99 vs 100 as meaningless and 33 as "bad".
- Agent output on live APIs depends on handling the in-between statuses (
needs_input,partial, empty butok) more than on the happy path.
Limits, honestly
- Opener is a prototype. It has not yet been used by a working promoter or venue booker, so we make no claims about bookings or ticket sales.
- The hosted demo runs on free tiers. In our runs on 14 October a brief took 35 to 44 seconds for Radiohead in Berlin and 27 to 33 seconds for Mohawk in Austin; when the free LLMs are slow the scripted planner finishes the plan and the summary falls back to a template. The first visit after the server has been idle can take several minutes to wake up.
- The popularity ceiling is relative to the headliner, not to the room: for Radiohead the candidates are outside Qloo's top 0.34%, which still means established acts, while a 1200-capacity room may want smaller ones. Capacity is not a Qloo input. The venue question has no headliner to measure against, so its candidates are not limited by popularity.
- Brand suggestions come from the audience, not the city, so some are brands without a local presence.
- Fit scores are relative within one Qloo call, not forecasts.
What's next for Opener
- Let the user pick the intended entity when Qloo returns
needs_input, instead of only retrying or flagging. - Let the promoter set the popularity window (or derive it from capacity once there is ticket data to calibrate it), and apply a ceiling to the venue question too.
- Cross-domain show ideas from the same audience: films, podcasts and brands for theme nights, merch and pre-show playlists.
- Show it to working promoters and venue bookers and change it based on what they actually decide with it.
Built With
- docker
- gemini
- github-actions
- leaflet.js
- model-context-protocol
- node.js
- openrouter
- qloo
- qloo-harness
- react
- render
- server-sent-events
- typescript
- vite
Log in or sign up for Devpost to join the conversation.