Tally Digital product · Applied AI
Warmup Coach takes the session you actually trained and builds a warm-up or cool-down around it. The interesting part isn’t the AI — it’s the architecture that stops the AI making things up, or handing you a shoulder drill after you told it your shoulder hurts.
Ask a general-purpose language model for a warm-up and it will happily give you one. It will also, sooner or later, invent a movement that does not exist, describe it with total confidence, and recommend it to someone with a bad knee.
For a fitness tool that is not a cosmetic flaw. Two failure modes matter:
Prompting alone does not fix either. You can ask a model nicely not to hallucinate; you cannot rely on it.
The fix is to move the safety decision out of the model entirely. The model never writes an exercise — it picks one by ID from a fixed catalogue of 114 movements, and the catalogue it receives has already had the unsafe options removed.
The order matters. Contraindication filtering happens before the prompt is built, not after the response comes back. If a user flags a shoulder, every shoulder-loading movement is stripped from the candidate list, so there is no path by which the model can return one — not because it has been instructed not to, but because it was never shown them. Any ID that comes back outside the catalogue is rejected before it reaches the page.
Verified in live testing against the production model: 16 of 16 returned IDs valid, none rejected, and zero shoulder movements returned when a shoulder was declared.
The tool has no login and no paywall, which makes runaway inference spend the obvious way to kill it. Three independent ceilings sit in front of the model — per-user daily, global daily, and a hard monthly cost cap. Any one of them can halt generation on its own.
That works because the underlying cost is genuinely small. A routine costs roughly $0.0006–$0.0016 to generate, so a £5/month budget covers on the order of 4,000 of them. Cheap models, tightly constrained, beat expensive models loosely supervised — a pattern we reuse across client work.
flowchart TB
classDef extern fill:#fdfcfb,stroke:#c4873b,stroke-width:1.5px,color:#0a0f0f
classDef hub fill:#0a0f0f,stroke:#c4873b,stroke-width:1.5px,color:#fdfcfb
classDef gate fill:#f3f2f1,stroke:#9a9a96,color:#0a0f0f
W["Your workout
+ kit + niggles"]:::extern
C["114-movement
catalogue"]:::extern
F["Filter out
contraindicated moves"]:::gate
L["Model picks
IDs only"]:::hub
V["Reject any ID
not in catalogue"]:::gate
R["Your routine"]:::extern
W --> F
C --> F
F --> L
L --> V
V --> R
The unsafe options are removed before the model is asked. It cannot return what it never saw.
Everything runs on the same stack as the rest of our work — Flask and SQLite in a container on our own hardware, no build step, no framework churn. Your workout text isn’t stored.
An honest note on reach: the tool works, but it ranks for very little. “Warm up generator” gets around ten searches a month — nobody is looking for this by name. It was built to prove a pattern for constraining AI output, and at that it did its job.
Worried about putting AI in front of your customers? This is how we make it safe — constrain the output space so the failure mode is impossible, not merely discouraged. Start a conversation →