Inspiration

Travel and dining agents sound confident while recommending a restaurant in the wrong city, inventing a venue, or ignoring the diner's taste. Traditional agent evals check latency and JSON shape, not cultural fit. TasteGuard is a quality gate an agent calls before it shows a list to a user.

What it does

Same restaurant, two tastes. Prado fails for a Radiohead fan (~25th percentile) and passes for a Kendrick Lamar fan (~55–60th). Taberna da Rua das Flores flips the other way (100th vs ~23rd). Same city, same places: only Qloo's cross-domain taste graph changes the verdict.

TasteGuard takes a dinner list for a persona and city (a built-in preset, or paste your own Lisbon / New York list with optional taste seeds), then:

  1. Verifies the place: resolves each name in Qloo, in-city first, and flags wrong-city or unverified picks with the real distance and locality (e.g. a Paris bistro "in Chiado", 1,452 km away).
  2. Verifies the taste: scores each verified restaurant against the persona's seeds (artist, film, book, brand) alongside a city-specific set of reference restaurants. A pick is ALLOWED only if it beats 40% of them, and each card shows per-seed contributions.
  3. Repairs on FAIL: keeps every ALLOW and replaces only the failed slots from a pool Qloo has already validated. Gemini may only choose pool entity IDs; any ID outside the pool is discarded in favour of the top-affinity pick. PASS requires every item to be ALLOW, and repair runs at most 3 rounds.

Other agents can call the same service as MCP tools (tasteguard_evaluate, tasteguard_repair, tasteguard_list_presets).

Try it in 60 seconds

  1. Open the demo and press Play recorded run (instant). This replays a live Qloo stress run: a Paris venue (~1,452 km) and a Cascais venue (~29 km) claiming Lisbon neighbourhoods, an invented name, and Prado failing on taste. Closed-set repair takes it to 6/6 PASS in one round.
  2. Under Try your own agent list, choose Lisbon and paste: Prado | Baixa Taberna da Rua das Flores | Chiado A Cevicheria | Príncipe Real Leave the seeds blank (Radiohead taste) and press Evaluate pasted list.
  3. Paste the same list again with the seeds Kendrick Lamar (artist), Uncut Gems (movie) and Nike (brand). Prado and Taberna swap verdicts.
  4. Optional: Radiohead · Lisbon dinner (live) runs a list Gemini actually proposed through the same gate. New York is supported but slower.

MCP (e.g. Cursor):

{ "mcpServers": { "tasteguard": { "url": "https://tasteguard-342276031814.us-central1.run.app/mcp/sse" } } }

What makes it Qloo-powered

Qloo makes both decisions; Gemini never does.

  • Search (/search) resolves each name with location, so wrong-city and invented places are caught with distances.
  • Insights (/v2/insights) scores the candidates together with city reference restaurants (filter.results.entities) against cross-domain taste seeds (signal.interests.entities: a band, a film, a novel, a brand).
  • Explainability (feature.explainability) shows how much each seed contributed to each score.
  • Without Qloo, the Prado flip is impossible: the restaurant exists and is in Lisbon either way. Only the taste graph says it fits one persona and not the other.

How I built it

  • Python / FastAPI: two-stage resolver (in-city first, name normalisation, category and distance checks), shared-set Insights scoring, and a surgical repair loop.
  • FastMCP at /mcp/sse, so agents call evaluate and repair without the UI. Client-supplied entity IDs and pins are ignored, so callers can't skip verification.
  • React / Vite single-page app with staged propose → evaluate → repair, and paste-your-own-list with optional seeds.
  • Terraform + Cloud Build deploying to a public Cloud Run service, with per-IP and daily rate limits to protect the shared Qloo quota.

Challenges I ran into

Live repair used to shrink the list, or re-search pool names and turn good venues into unknowns. I now pin replacements by entity ID, keep the anchor set fixed across repair rounds, and gate the pool by dinner category so cafés and bars stay out. Anchor-relative percentiles also meant the injected keepers had to genuinely clear 40%, so I changed the test list rather than adding a soft affinity floor.

Accomplishments that I'm proud of

  • End-to-end FAIL → PASS repair that preserves every ALLOW, in both the recorded and live stress runs.
  • One stress list that shows every failure type: wrong city, invented name, and taste miss.
  • The two-way taste flip (Prado / Taberna) under Radiohead vs Kendrick seeds.
  • Paste-your-own-list for Lisbon or New York with custom multi-type seeds.
  • A fail-closed PASS policy, strict input validation, and retries for transient upstream errors.

What I learned

Qloo Insights affinity is set-dependent: scoring candidates together with a popularity-sampled anchor set gives a fairer baseline than comparing against a top-N list. And agentic repair only works when the pool is already category- and geo-clean. Gemini should choose among valid options, not invent dinner.

What's next for TasteGuard

Wire TasteGuard behind real concierge and booking agents, add cached per-city anchor packs for more cities, and add optional Maps grounding for names Qloo can't resolve.

Honest notes for judges

  • The stress list is labelled Injected test list (not Gemini); it deliberately simulates a careless agent.
  • Live percentiles and replacement names vary between runs. Top pool restaurants often repeat across personas because they're broadly loved.
  • Scores are relative to the set Qloo scores together, so a keeper's percentile can shift slightly between the original and repaired lists.
  • If Qloo lacks a place's in-city entry, a same-name venue elsewhere can be flagged as wrong-city.
  • The 40th-percentile cutoff against city reference restaurants is our product policy, not an official Qloo recommendation.

Built With

  • artifact-registry
  • fastapi
  • fastmcp
  • google-cloud
  • google-cloud-build
  • google-cloud-run
  • google-gemini
  • mcp
  • python
  • qloo
  • react
  • terraform
  • vite
Share this project:

Updates

Submission history