Gazzali Zayn GAZZALI ZAYN
Case Study · AI Architecture · Build vs. Buy

Should this site's AI run a self-hosted LLM?

Not shipped — production runs Gemini

The "Ask Gazzali.ai" terminal answers questions about one person from a small, fixed knowledge base — and refuses everything else. I scoped the full self-hosted build (model, quantization, serving, a RAG layer, an optional QLoRA tune) and built an evaluation harness to compare it against a hosted model. The data said don't ship it. This is the decision — not the deployment.

Latency
~10×slower on CPU
Cost / month
$25local · flat; hosted $0 on free tier
Factual accuracy
~92%vs ~98% hosted
The real question
Volumebuy now · build past crossover
01 · The problem

A narrow job with sharp constraints.

A chat terminal that answers questions about Gazzali Zayn — role, experience, skills, projects — grounded in a one-page profile, and politely refuses anything off-topic. The constraints are what make the decision interesting:

Tiny, fixed domain

The "knowledge" is a handful of facts (AUM, funnel rates, timeline). It changes only when the résumé changes.

Spiky, low volume

Recruiters click occasionally. There's no steady high-QPS load to amortise a dedicated server against.

Instant & ~free

A résumé that takes 10s to answer — or costs real money to run — is a worse résumé.

Grounded + refusing

Zero hallucinated facts; reliable off-topic and prompt-injection refusal matters more than eloquence.

Success = accurate on the hard numbers · near-zero hallucination · reliable refusal · sub-second feel · ~$0 marginal cost · updates in minutes (edit a prompt), not a retraining run.

02 · Options considered

Three ways to power it.

A

Hosted small-model API

A thin FastAPI proxy to gemini-2.5-flash-lite (or Groq / OpenRouter). The status quo.

shipped
B

Self-hosted open LLM

A small, quantized instruct model served from my own container on Render. The path this study explores.

prototyped
C

Non-generative retrieval

Embedding / keyword match → return pre-written facts. No generation at all. Cheapest, least flexible.

considered
03 · The self-hosted build

If I built it — how.

The constraint that drives everything: Render is CPU-only. No GPU on standard plans. So any self-hosted model runs on CPU — capping size at ~3B params (quantized), pushing latency to seconds, and ruling out the free tier (512 MB RAM can't hold a usable model).

Model selection

Small, permissively-licensed instruct models suited to a fixed-domain FAQ:

ModelParamsLicenseNote
Qwen2.5-1.5B-Instruct ⭐1.5BApache-2.0Best instruction-following per byte; clean license.
Llama-3.2-1B / 3B1–3BLlamaStrong; heavier license terms.
Gemma-2-2b-it2BGemmaGood quality, Google license.
SmolLM2-1.7B1.7BApache-2.0Built for lightweight / on-device.
Phi-3.5-mini3.8BMITHighest quality of the set; most RAM.

Pick → Qwen2.5-1.5B-Instruct · GGUF Q4_K_M (~1.1 GB) · served in-process via llama-cpp-python · needs a ~2 GB instance.

Key design call: ground with RAG, don't fine-tune the facts. For a KB this small and volatile, I'd retrieve the relevant profile facts into context at inference time (a 22M-param embedder is plenty) rather than baking them into weights. That keeps updates to "edit the text" — fine-tuning would mean retraining every time a number changes, and risks reciting stale facts. A QLoRA tune stays a "only if measured necessary" lever for format & refusal behaviour, never knowledge.

04 · How I'd measure accuracy

The part that's actually engineering.

A golden set of ~80 questions across the categories that matter — single-fact lookups, multi-fact synthesis, off-topic refusals, and prompt-injection — scored on factual accuracy (regex checks on hard numbers like ₹400 Cr, 48%→90%), faithfulness (LLM-as-judge vs. the source), refusal precision/recall, format adherence, latency, and cost.

Projected scorecard — factual accuracy

Estimates from published small-model benchmarks + the prototype harness — illustrative, not a shipped measurement
1.5B · zero-shot
~78%
1.5B · + RAG
~90%
1.5B · RAG + QLoRA
~92%
hosted flash-lite
~98%
▸ Other axes (projected): hallucination — local low-med, hosted very-low · refusal — local ~90–94%, hosted ~99% · latency p50 — local ~6–10s, hosted <1s · cost — local ~$25/mo flat; hosted $0 on the free tier today, but scales with volume (see §05).
05 · The cost crossover

$0 today — but that's a free-tier number.

The hosted option costs nothing right now only because traffic sits inside Gemini's free tier. That's a property of volume, not architecture. Honest unit economics: a hosted API scales with usage, while a self-hosted box is a flat monthly fee. Past some volume the lines cross — and self-hosting becomes the rational call.

Illustrative — $/month vs. monthly question volume (not to scale)
$ / month monthly volume → today · free tier crossover BUY · hosted wins BUILD · local wins
Hosted API — scales with volume Self-host — flat monthly cost

What trips the switch — sooner than raw cost. Two things move first: (1) free-tier rate / daily quotas — at higher traffic you blow past them and start paying per call; (2) reliability and latency under load. At ~150 tokens/answer, flash-lite is fractions of a cent per question, so the pure-cost crossover with a ~$25/mo CPU box sits in the hundreds-of-thousands of questions per month range — but the quota ceiling bites long before that.

So the decision is explicitly volume-dependent: at a résumé's traffic, buy. If "Ask Gazzali.ai" ever became a high-traffic product, the same analysis flips to build — and because the proxy isolates the model behind a single _generate() function, switching to a local Llama (or any other provider) is a one-function change, not a rewrite.

06 · Decision matrix

Scoring the trade-offs.

CriterionA · HostedB · Self-hostC · Retrieval
Answer quality
Accuracy / low hallucination
Latency
Cost — at current volume
Ops / maintenance
Update effort
"Run my own model" wow-factor

▸ Cost & ops ratings assume résumé-scale traffic. Past the crossover (§05), the cost row flips toward self-hosting.

07 · Verdict

Buy, not build — at this volume.

At a résumé's traffic, self-hosting loses on every axis that matters — ~10× slower, less accurate, and (today, on the free tier) it would add cost — to gain only "I own the weights," meaningless for public data with no privacy driver. So: ship hosted now, but treat it as a volume-dependent call. The cost crossover (§05) is the documented trigger to flip to a local model — and because the proxy isolates the model behind one _generate() function, that flip is a one-function change, not a rewrite. The discipline is knowing which regime you're in.

08 · When I'd revisit

Not "never" — "not yet."

Self-hosting flips positive when the trade-offs change:

Data can't leave the boundaryPII / regulated financial data — the common real driver in fintech.
Volume past the crossoverPer-call API spend (and free-tier quota ceilings) overtake a flat server fee.
Behaviour no prompt can buyA bespoke fine-tune, offline/edge, deterministic versioning.
You have a GPU hostA 7B+ model changes the quality/latency maths entirely.
09 · What's actually in production

The shipped architecture.

~/gazzali.ai / production
// request  POST /api/ask  → FastAPI proxy
// model    gemini-2.5-flash-lite  (hosted, ~$0, <1s)
// ground   strict system prompt · refuse off-topic · low temp · capped output
// reliab   3-key fallback chain  primary → fallback ×2
// abuse    20 questions / IP / hour · input length cap
// host     single Render service serves API + static site · auto-SSL
// outcome same-or-better quality at ~$0 and <1s, zero inference ops

See it run.

The assistant this study is about — live on the résumé.