Spaces:
Running on Zero
Running on Zero
| # Field Notes: Building Tutori, a Whiteboard Tutor That Draws While It Talks | |
| *What we built for the HF Build Small Hackathon, what broke, and what 17B | |
| parameters taught us about engineering around small models.* | |
| [**Tutori**](https://huggingface.co/spaces/build-small-hackathon/tutori) is a | |
| voice tutor. You ask it anything out loud; it researches the question if it | |
| needs to, then teaches you in spoken steps while sketching the idea on a | |
| whiteboard — stroke by stroke, in sync with its own voice, like a teacher who | |
| never gets tired of your questions. | |
| I built it for my parents. They're behind on today's technology, and the | |
| firehose of AI news is impenetrable from the outside. With Tutori they just | |
| ask — "what is Gemma?" — and get a patient, drawn-out explanation at their | |
| pace. They actually use it. That's the only benchmark that ever mattered. | |
| Everything runs on the Space itself via ZeroGPU: Gemma 4 12B teaches, | |
| MiniCPM5 1B plans research and coaches, Higgs Audio v2 speaks, Whisper turbo | |
| listens. No cloud APIs, no keys. Here is what we learned building it. | |
| ## Lesson 1: The model is 20% of a drawing tutor. Geometry is the other 80%. | |
| Our first whiteboards were a disaster — overlapping labels, triangles | |
| assembled from three unrelated lines, values scattered like confetti. We | |
| tried prompting harder. It did not work, and it never works: a language | |
| model freehanding 2D coordinates is a model being asked to do geometry | |
| without eyes. | |
| What worked was splitting the job: | |
| - **A composite op vocabulary.** The model stopped placing twelve primitives | |
| and started saying `{"op": "polygon", "side_labels": ["a", "b", "c"]}` — | |
| one op, with the geometry math done deterministically in the renderer. | |
| - **A layout engine with zero tolerance.** Every op the model emits passes | |
| through a placement pass: weighted obstacle maps, candidate scanning, | |
| shrink-on-crowding, a capacity accountant that drops what won't fit, and a | |
| hard guarantee that nothing overlapping ever reaches the screen. | |
| - **A fuzz harness.** We generate hundreds of synthetic "worst-case" lessons | |
| and require zero violations before any deploy. The harness caught ten real | |
| bugs that user screenshots later would have. | |
| The model proposes; the engine disposes. | |
| ## Lesson 2: ZeroGPU is a hardware constraint disguised as a free GPU. | |
| ZeroGPU gives every Space an H200 slice with one catch: your GPU code runs in | |
| disposable forked workers with a per-turn time budget. Almost every hard | |
| problem we hit traces back to this: | |
| - **NeMo crashes the workers** if imported in the main process, and costs | |
| 30–60s per fresh worker if loaded lazily — so our planned Nemotron ASR | |
| became Whisper (preloads with everything, transcribes in a second). | |
| - **Mamba-hybrid models can't really run.** Nemotron-H's Mamba-2 layers need | |
| fused Triton kernels that won't JIT-compile inside a 59-second window, and | |
| the pure-PyTorch fallback allocates multi-gigabyte tensors at prefill and | |
| OOMs the worker. We learned this the expensive way — see Lesson 4. | |
| - **Fast generators get coalesced.** Gradio collapses rapid streaming yields | |
| into the latest state, which silently dropped lesson steps until we made | |
| every payload cumulative and let the browser deduplicate. | |
| None of this is in a tutorial. All of it is reproducible on a $0 Space, which | |
| is exactly why the constraint is worth embracing. | |
| ## Lesson 3: Small models need formats, not freedom. | |
| Three times we needed structured output from a model ≤1B parameters, and | |
| three times the same arc played out. Ask MiniCPM5 1B for nested JSON and it | |
| writes brilliant content with one missing bracket. Give it a few-shot example | |
| and it copies the example verbatim. What finally worked was a *line | |
| protocol*: | |
| ``` | |
| PROFILE: {"level": "beginner", "last_topic": "rainbows", ...} | |
| NEXT1: What happens to the colors inside a raindrop? | |
| NEXT2: Why do we see bands instead of a smear? | |
| NEXT3: Could a rainbow form at night? | |
| ``` | |
| One regex per line, code-side guards against parroting, and the 1B model | |
| became a reliable study coach — it updates the learner's profile after every | |
| lesson and writes the three sticky-note follow-up questions you see in the | |
| app. Small models are superb employees and terrible freelancers: define the | |
| job precisely and they shine. | |
| ## Lesson 4: Fine-tune honestly, and let the boring pipeline win. | |
| For the Well-Tuned badge we built a | |
| [dataset](https://huggingface.co/datasets/ProCreations/tutori-whiteboard-lessons) | |
| of 7,109 gold whiteboard lesson steps — programmatically generated across 8 | |
| diagram families and 78 topics, every single lesson validated to render with | |
| zero overlaps *before* it could enter the training set. Then we LoRA-tuned | |
| two dedicated "board artists": | |
| [Nemotron 3 Nano 4B](https://huggingface.co/ProCreations/tutori-board-nemotron) | |
| and [Gemma 4 12B](https://huggingface.co/ProCreations/tutori-board-gemma). | |
| Both learned the job almost perfectly on paper (99% held-out token accuracy). | |
| The Gemma artist drew the best gradient-descent diagram we've ever gotten | |
| from any model — correct axes, slope arrows, the actual update rule. And we | |
| still shipped neither. The Nemotron artist can't execute on ZeroGPU at all | |
| (Lesson 2), and the Gemma artist lost a live A/B for a humbling reason: the | |
| artist never sees the researched facts, only the narration — so a lesson | |
| about that week's space news got confidently improvised geometry. Meanwhile | |
| the boring pipeline — the big teacher model plus the deterministic layout | |
| engine — kept quietly producing good boards. | |
| Publishing the models, the dataset, and the scorecard felt better than | |
| flipping the flag. The README says exactly why it's off. | |
| ## Lesson 5: A custom face is mostly CSS — and one JS gotcha. | |
| The "stunning UI" pass that chases the Off-Brand badge is not a rewrite. It's | |
| a design system (chalkboard scene, paper cards, sticky notes, two bundled | |
| fonts), about 400 lines of CSS over Gradio's components, a hand-drawn canvas | |
| renderer we already had, and one hard-won discovery: Gradio's CSS | |
| preprocessor silently drops `:has()` selectors and everything after them — | |
| tag elements with a class from JavaScript instead. | |
| ## The scorecard | |
| | | | | |
| |---|---| | |
| | Models on the Space | 4 (Gemma 4 12B · Higgs v2 3B · MiniCPM5 1B · Whisper 0.8B) — Σ ~16.9B | | |
| | Models fine-tuned & published | 2 + 1 dataset | | |
| | Cloud APIs | 0 | | |
| | Layout-engine fuzz violations at ship | 0 of 600 | | |
| | GPU budget per lesson | 59 seconds, every model included | | |
| Built small. It was the constraint that made it good. | |
| *— SSH/ProCreations, June 2026* | |