Skip to content
APAdam Probert
← The workshop

AI / Web · 2025

Grimoire ML

Natural-language Magic: The Gathering search, as a small fleet of services.

What it was

The project

An AI-assisted Magic: The Gathering card discovery and deckbuilding app. A mono-repo of four services: a FastAPI backend, a query microservice that turns natural language into an intermediate representation and then into Scryfall queries, a Redis-cached card image proxy, and a Next.js frontend, with Prometheus and Grafana wired in from the start.

Build

How it fits together

  • A query service that goes natural language, then intermediate representation, then Scryfall query, with Redis caching between stages.
  • A dedicated card image proxy with its own cache and metrics, keeping image traffic off the main backend.
  • A Next.js 15 frontend in TypeScript with Tailwind and Mantine.
  • Prometheus metrics and Grafana dashboards alongside a Docker Compose stack wiring Postgres, Redis and every service together for local development.
Key decisions

The trade-offs that mattered

  • An intermediate representation between the model and the API

    Asking a language model to emit Scryfall syntax directly is brittle and hard to debug. Going via a structured IR makes the model's output inspectable and lets the query layer be tested without the model at all.

  • Cache aggressively at every boundary

    Card data barely changes and model calls cost money and latency. Redis between each stage made the app feel instant on repeat queries.

  • Observability from day one on a side project

    Wiring Prometheus and Grafana in early cost an afternoon and paid for itself the first time a service started misbehaving under load.

What I gained

What I took from it

Translating natural language into a structured intermediate representation before hitting the search API, rather than asking a model for the answer directly, is what made the results trustworthy.

  • Putting a structured representation between a language model and a downstream API turns an unpredictable component into a testable one.
  • Splitting services out is worth it when they have genuinely different caching and scaling needs, and not much before that.
Contact

Have a difficult, important problem?

I’m happiest where the problem is unclear, several teams need to work together, and the outcome actually matters. If that sounds like your situation, let’s talk.

  • Talk through a hard technical problem
  • Scope a platform, data, or distributed-systems build
  • Get a second opinion on an architecture or delivery plan
  • A role or engagement worth a conversation