What it does
You type up to three favorite artists, films and foods and pick Tokyo or Kyoto. An agent looks each favorite up in Qloo, asks Qloo's taste graph for places in that city that fit the combined taste, and lays out five stops: morning, lunch, afternoon, dinner, evening.
For a traveler who likes Ryuichi Sakamoto, Lost in Translation and Radiohead, the live Qloo day in Tokyo was St. Mary's Cathedral, SCAI The Bathhouse (a gallery in a converted bathhouse), and The National Museum of Western Art, with two food stops between them. The LLM-only answer for the same traveler named none of them, and each card says why it is there: "Qloo links this place to: Ryuichi Sakamoto (0.38), Lost in Translation (0.31), Radiohead (0.31)."
Each stop is a card on a map with the Qloo affinity score as a bar, the taste signals in the traveler's own words, and the distance from the previous stop. The tool calls the agent made are listed under the plan, so a reader can see which Qloo calls produced the day.
Who it is for
People on a second or third trip to Japan who have seen the landmarks, and people who plan days for others: a travel agency building a day for a client, or a hotel concierge who has a guest's three favorite artists and ten minutes. The plan comes with its evidence attached, so a professional can hand it over and explain each stop. (These are the users the design aims at; I have not interviewed any of them.)
What Qloo changes, measured
I ran the same three travelers through an LLM alone (Gemini, temperature 0, 2026-10-03) and through the Qloo-grounded agent (live hackathon API, 2026-10-06). The method is in the README and eval/PROCEDURE.md, and eval/ holds the LLM-only rows. The raw Qloo responses are not published, because Qloo's developer guide says not to store Qloo response data in a public repository, so the Qloo side is given as counts.
- Different places. 0 of the 14 Qloo stops appear among the LLM's 15. The LLM picked the famous venues for the films and bands; Qloo's picks lean to museums, galleries, a cathedral, university buildings and cafes.
- Every reason is traceable. Each Qloo stop carries coordinates, an address and per-input scores from Qloo's explainability feature. The LLM's reasons are its own text, with no score or ID behind them.
- Where the LLM did as well or better. The LLM did not invent places on these inputs (13 of its 15 stops found in OpenStreetMap by name, against 9 of 14 for Qloo by the same lookup). Qloo's food matching is loose: a "ramen" query in Tokyo returned an Italian restaurant first.
- Whether the Qloo day suits the traveler better was not measured by an independent reader.
How Qloo is used
/searchturns names into Qloo entity IDs (artists, films)./v2/tagsfinds tag IDs for food keywords./v2/insightswithfilter.type=urn:entity:place,filter.location.query(the city),signal.interests.entities(the IDs from/search),filter.tags(food tags, for lunch and dinner) andfeature.explainability=truereturns places with an affinity score and the signals that contributed.- Meals and non-meal stops come from separate queries, so a day is not five restaurants.
How the agent works
The agent is a tool-calling loop with four tools: search_entity, find_tags, taste_insights, compose_day. The planner decides which tool to call next; the tools run on the server.
- IDs cannot be invented. Every entity or tag ID passed to Qloo must have come back from an earlier
/searchor/v2/tagscall in the same run. The tool layer rejects anything else, so a model cannot fabricate a taste signal. - The stops are chosen by a fixed rule, not generated. For each slot the rule takes the highest affinity among the top 6 unused places, minus 0.02 per km from the previous stop; lunch and dinner avoid the same food tag when they can; no place appears twice. The same Qloo data always gives the same day, and the rule is tested.
- The planner is swappable. Anthropic or any OpenAI-compatible model can drive the tools (
LLM_PROVIDER,LLM_MODEL). The public demo runs the deterministic planner, which calls the same tools in the same order, so the hosted page spends no LLM quota. The banner says whether the data is live Qloo or recorded.
Engineering choices
- The Qloo key stays on the server: it goes in the
X-Api-Keyheader, never in a URL, the browser or a response. A test checks that responses do not contain it. - Inputs are limited to three items of 60 characters per list, so the public demo cannot be used to burn the Qloo quota.
- Only the three endpoints above are reachable;
/recommendationsand/recsare blocked in code and covered by a test. - Plain JavaScript with no packages. The same core runs in Node (local) and in a Cloudflare Pages Function (the hosted demo). 21 automated tests run without a network or a key.
Design
One screen: the form, a map of the five stops joined by a line (Leaflet and OpenStreetMap tiles, with attribution), a card per stop with the affinity bar and the reasons, then the collapsed tool-call record. It works at phone width.
Challenges
- The Qloo client was first written from the documentation; the parsers and tests were then checked against 15 recorded real responses.
- Food matching is loose: place queries with a food tag can return other cuisines, so lunch and dinner are only roughly what was asked. The page shows the result as it came.
- Kyoto runs out of distinct non-food candidates before the evening slot, so a Kyoto day can have four stops.
- The per-input scores sit close together within a day (0.23 to 0.41 for every stop in the three runs). They show which favorites Qloo weighted for a place; they separate one place from another only weakly.
What I learned
Qloo's cross-domain signal moves the picks away from the famous list, and a fixed rule on top of Qloo's scores makes the output repeatable and testable. The LLM-only baseline removed a claim I had planned to make: the LLM did not invent places in my runs, so the case for Qloo here is different places with checkable reasons, not avoiding invention.
What's next
- Let a planner enter several travelers and show where their tastes overlap.
- Choose the city itself with Qloo destination insights (
urn:entity:destination). - Keep a day inside one or two districts using area filters.
AI use disclosure
Written with Claude Code (Anthropic) under the author's direction. The LLM-only baseline used a Gemini model (gemini-3.5-flash).
Built With
- cloudflare-pages
- javascript
- leaflet.js
- node.js
- openstreetmap
- qloo-api
Log in or sign up for Devpost to join the conversation.