Stockfish can tell you that 17...Bh6 dropped your win chance from 72% to 31%.
What it won't tell you is the part that actually makes you better:
You were trying to create a kingside attack. That idea is fair but Black's reply Bxd5 wins a pawn immediately, and the cleaner try was Nf3, checking every check before you commit.
That gap between engine truth and human understanding is why I built FineChess.
If you've ever clicked Game Review after a Chess.com or Lichess game, you know the ritual:
Nf3 Bxd5 cxd5 Qe2
The engine is brilliant at measuring decisions. Humans need help interpreting them.
Club players (roughly 800 to 1800, the range FineChess coaches for) don't lose because they can't read an eval graph. They lose because:
Existing tools give you a verdict. I wanted a coach sitting across the board.
So FineChess does three things traditional analysis usually separates:
FineChess doesn't ask the LLM to "look at a board and think." That would be asking a language model to do an engine's job.
Instead the pipeline is deliberately asymmetric:
PGN / Chess.com / Lichess
│
▼
Stockfish analysis
(eval, multipv lines, mate scores)
│
▼
Move classifier
(brilliant → blunder, win% loss)
│
▼
MoveFacts sheet ← ground truth
│
▼
Coach Rex (LLM) ← may only talk about these facts
For each position we run Stockfish (python-chess and a cached engine wrapper) and keep:
Results are cached by FEN and depth so replaying a game doesn't recompute the same positions forever.
Raw centipawn drops lie in unbalanced positions. Going from +600 to +400 looks huge on paper and often feels like "still completely winning" over the board.
So FineChess classifies with win-probability loss from the mover's perspective, then labels moves across the familiar spectrum: brilliant, great, best, excellent, good, book, inaccuracy, miss, mistake, blunder.
Brilliant detection is its own mini-pipeline: sacrifice candidates, SEE filters, PV lookahead, and guards against calling something "brilliant" when you were already completely winning.
Before Coach Rex speaks, the backend builds a strict JSON fact sheet, roughly:
That sheet is the coach's only allowed chess reality.
Rex's system prompt is blunt about it:
ENGINE FACTS are ground truth. Never invent moves, evaluations, or lines that are not in them.
And the answer shape is coaching, not commentary:
No "centipawns." No lecture. Roughly club-player English in about 120 words.
That is the whole bet of FineChess:
Stockfish decides what is true. The LLM decides how to teach it.
Move-by-move analysis is only half the product.
FineChess also has a global coach that remembers patterns across your games: recurring weaknesses, opening mastery gaps, fix plans, recent accuracy. When you ask something like:
"I keep hanging pieces after I castle queenside. What should I watch for?"
the coach shouldn't answer from generic LLM chess folklore.
So we retrieve.
Under backend/knowledge/ we keep public-domain chess literature (starting with Capablanca's Chess Fundamentals from Project Gutenberg). At startup the service:
.txt bookCHESS KNOWLEDGE REFERENCE
The instruction to the model is intentionally narrow:
Use them to ground your explanation. Explain in your own coaching voice but let these principles guide your reasoning.
Why TF-IDF instead of a fancy vector database on day one?
Because chess coaching retrieval is often lexical and conceptual ("isolated pawn", "opposition", "back rank"), the corpus is small and high-signal, and a local sklearn index is fast, free, and easy to reason about. We can graduate to embeddings later. The product win was already having literature in the loop.
So FineChess uses grounding at two layers:
| Layer | Source of truth | Purpose |
|---|---|---|
| Move coach (Rex) | Stockfish MoveFacts |
Don't invent tactics |
| Global coach | Player memory and RAG literature | Don't invent principles |
Both are anti-hallucination strategies. They just fight different failure modes.
Here's the uncomfortable truth about putting an LLM next to a chessboard:
It will confidently invent a move that isn't legal, wasn't played, and wasn't in the engine line, and it will sound great doing it.
Early prototypes of FineChess did exactly that.
Rex would say you "dropped a rook" when the PV only lost a pawn.
Or cite Qh5 as the refutation when Stockfish's best line never touched the queen.
Or end a sentence on 17... like a broken PGN printer.
That is not a tone problem. That is a trust problem. One fake move and the coach is dead.
Coach Rex now runs a cheap but ruthless grounding check after generation:
played_san, best_san, best_line_san, or played_line_san
There is also a separate claim verifier path for structured tactical claims (legality, hanging pieces, simple SEE-style material checks) used when generating richer mistake explanations, with verifier outcomes logged so we can see whether an explanation passed, retried, or fell back to facts.
In other words:
The model is allowed to be eloquent.
It is not allowed to be creative about the board.
That single constraint changed FineChess from "fun demo" into something I'd trust with my own blunders.
A second, quieter challenge was latency. Users click a move and expect a coach, not a research paper. So Rex asks for short answers, uses tight timeouts, and always has a fact-sheet fallback. Correct and slightly plain beats poetic and wrong every time.
Under the hood it's FastAPI, Stockfish, Gemini/Groq coaching, Supabase auth/data and a React analysis UI. On the surface it should feel like a sharp human coach who somehow watched every game you've uploaded.
If you've ever stared at an eval bar and thought "okay but what was I supposed to think?" that's the product.
Live app: https://finechess.vercel.app
Import a recent loss. Click the move that hurt. Ask Rex why.
I'd love feedback from other builders working on grounded LLM systems, especially if you've fought the same "confident nonsense" problem in a domain where facts are non-negotiable.
Engine truth → Fact sheet → LLM narration → Grounding check
Literature → TF-IDF RAG → Coach principles
Player memory → Pattern context → Personalized habits
Chess improvement doesn't need more numbers.
It needs numbers that can explain themselves.