Inspiration
The 2026 World Cup kicked off today, in our time zone, across our cities — and an estimated 110 million Americans will watch it without ever having been taught the sport. They won't ask "what's a false nine?" out loud in a sports bar. They will ask an AI — and most AIs will confidently hallucinate a squad list at them.
FirstTouch started from that gap: a football mentor that a brand-new fan can actually trust — one that cites where every fact came from, admits exactly what it couldn't verify, and visibly corrects itself when it oversteps. We named the whole architecture after the sport it serves: a match-day squad with a Gaffer (planner), Scouts (researchers), a Picky Eater (evidence trimmer), a VAR (claim-by-claim reviewer), a Linesman (proactive coach nudges) and an Analyst who reviews the team's own performance.
What it does
- Ask anything — "Which Botola Pro players are at this World Cup?" doesn't exist on any single
page. The Gaffer plans a tool DAG; Scouts pull every club roster and every national squad from
Wikipedia in parallel waves; the intersection is computed in code over an evidence ledger —
and every line of the answer carries an
[ev:N]citation. - Watch along — pick a live or replay match and the PitchMap renders every shot, foul and corner from ESPN data; click any marker to ask about that moment. Hover any player or team name — in answers or your own messages — for a stat card resolved through a 211k-entity dictionary.
- Adapts to you — reading levels from kid to intermediate, timezone-aware schedules, Kickoff/Floodlight themes.
- Reviews itself — every claim passes a grounding gate; unsupported sentences are retracted visibly in the chat. And the Analyst agent reads the system's own Phoenix traces and eval scores through Arize's Phoenix MCP server to write a daily form report. An agent that scouts itself.
How we built it
FastAPI + the Google Agent Development Kit, with gemini-3.1-flash-lite running
*everything* — the agent, the planner, every evidence picker, the VAR judge. No Pro-tier
fallback. One merged pre-flight call rewrites the question, decides if tools are needed at all,
and plans 0–5 tool calls with dependencies; the server executes them in parallel topological
waves; a compose pass trims the evidence and plans round-2 follow-ups in the same call.
Everything lands in an evidence ledger before the model may use it.
Three constraints we chose on purpose:
- No Google Search grounding — no web-search API at all. We built our own retrieval
(Lucene-aware Wikipedia CirrusSearch phrasing), extraction (
colspan/rowspantable parsing, infoboxes), and grounding gate. Every claim traces to the exact source section. - The smallest, cheapest Gemini model for every role. Reliability comes from architecture: plan-then-execute DAGs, deterministic joins, claims scoped to verified coverage.
- One database — MongoDB Atlas holds the entity dictionary, the auto-promoted fixture archive that powers Replay mode, chat sessions, and the vector KB.
Observability is Arize Phoenix end-to-end: every agent is a named span, tool waves show
visibly parallel in the waterfall, VAR verdicts are recorded claim-by-claim, an eval runner
writes Gemini-judged groundedness scores back — and the Analyst closes the loop by reading it
all through the Phoenix MCP. Deployed on Cloud Run (frontend + agent) behind our own domain,
images built on GitLab CI; one docker compose up reproduces the whole stack locally.
Challenges we ran into
- LLMs are bad at joins. Our hardest question (league roster × 48 national squads) kept silently dropping players — one Jordanian forward at Raja Casablanca was left out in every run. The fix that changed the project: stop letting the model do what code does better. The intersection, the group-letter mapping, and coverage accounting are now computed deterministically over the ledger; the LLM narrates. Completeness went from flaky to 7/7.
- ESPN ships three coordinate frames depending on event type. We decoded them against the commentary text as ground truth ("defensive half", "right side of the box") and normalized everything into one shared pitch frame — fixing mirrored chirality and fouls rendered inside the penalty box.
- A performance fix unmasked a latency bug. Caching the 211k-entity dictionary to disk made it warm from second zero — which exposed quadratic fuzzy-matching (~500k-key scans inside an O(n²) dedup) that stalled hard questions for minutes. We split the resolver into O(1) exact alias lookups for hot paths and kept fuzzy only where fuzziness is the point.
- Free-tier reality: Atlas M0 full scans take ~9 minutes, so we snapshot the dictionary to disk (guarded against poisoning by transient outages) and stream-strip citation tags so the UI never flickers.
What we learned
- Determinism > model size. Architecture, not a bigger model, is what made a flash-lite agent trustworthy.
- Observability is an input, not an afterthought — half our fixes started by reading a Phoenix trace, and the Analyst made that loop part of the product.
- Honesty is a feature: an agent that says "I couldn't scan these four clubs" earns more trust than one that pretends completeness.
- Every prompt payload should be real markdown; every coverage gap should be named; every claim should be checkable. That discipline compounds.
What's next
Voice mode (push-to-talk STT + spoken replies), minute-scrubbing replays, and group-stage watch parties — the roadmap is in the repo.
Built With
- arize-phoenix
- cloud-run
- docker
- docker-compose
- espn
- fastapi
- gemini
- gitlab-ci
- google-agent-development-kit
- google-cloud
- model-context-protocol
- mongodb-atlas
- next.js
- opentelemetry
- phoenix-mcp
- pydantic
- pytest
- python
- rapidfuzz
- react
- server-sent-events
- tailwindcss
- typescript
- uv
- vertex-ai
- wikipedia-api
Log in or sign up for Devpost to join the conversation.