Custom Q4_K_M for a customer-support intent classifier
End-to-end: take a customer-support intent classification workload, calibrate Q4 against actual support transcripts, ship a custom GGUF that matches the f16 base on accuracy at 3× lower memory.
Customer support intent classification is one of the cleanest LLM workloads: short input (the customer's first message), small set of output labels (cancel, refund, billing question, technical issue, etc.), high volume, latency-sensitive. It's also a workload where Q4 quantization usually loses 5-8 percentage points compared to f16 — unless you re-calibrate.
This walkthrough takes 4000 support transcripts, re-calibrates a Qwen 2.5 7B Instruct Q4_K_M against them, and ships a custom GGUF that matches the f16 baseline on accuracy. Same disk size as stock Q4, ~3× smaller than f16, no quality loss on the trained task.
The corpus
We use a synthetic-but-representative corpus of 4000 anonymized customer support first-messages, mapped to 12 intent labels:
{"text": "I want to cancel my subscription, I bought it last week", "intent": "cancel"}
{"text": "My card was charged twice for the same order, can you refund one?", "intent": "refund_dispute"}
{"text": "The product I received is broken, who do I contact?", "intent": "product_defect"}
For a real engagement you'd assemble the equivalent from the client's support logs (we cover the methodology in Building a calibration corpus). PII strip, dedupe, stratify by intent. 4000 prompts is enough.
The calibration
In Blueprint's Calibrate card:
- Base model: Qwen 2.5 7B Instruct (full precision f16, 15GB)
- Calibration corpus: the 4000 anonymized support transcripts as a single
.txtfile - Output: custom imatrix → custom Q4_K_M GGUF
The card runs:
llama-imatrix \
-m models/Qwen2.5-7B-Instruct.f16.gguf \
-f corpus/support-anon-4000.txt \
-o matrices/qwen-support.imatrix \
--chunks 1000 \
--ctx-size 1024
llama-quantize \
--imatrix matrices/qwen-support.imatrix \
models/Qwen2.5-7B-Instruct.f16.gguf \
models/Qwen2.5-7B-Instruct-Q4_K_M-support.gguf \
Q4_K_M
Two clicks in the Blueprint UI. ~50 minutes total on a 4090, mostly the imatrix run.
Evaluation
Eval set: 500 held-out support transcripts, never seen during calibration. The metric is exact-match intent classification.
Three models go through the same eval:
- f16 base — the unquantized starting point
- Stock Q4_K_M (bartowski) — what you'd download from HuggingFace by default
- Custom Q4_K_M — what we just built
f16 baseline
89.6%
ceiling — what we're trying to match
Bartowski Q4_K_M
82.4%
off-the-shelf, -7.2pp from f16
Custom Q4_K_M
88.8%
calibrated, -0.8pp from f16
Recovered accuracy
+6.4pp
custom vs stock Q4
Memory (f16 → Q4)
15 GB → 4.4 GB
3.4× reduction
Inference latency
Q4 ≈ 1.6× faster
vs f16 on same GPU
The custom Q4 sits 0.8pp below the f16 baseline. The off-the-shelf Q4 sits 7.2pp below. The calibration recovered 88% of the f16-vs-Q4 gap — at no inference cost.
Why the gap
The bartowski Q4 was calibrated against general web text (C4 + WikiText). Support transcripts have a vocabulary that overlaps somewhat with general text but is biased — heavy on action verbs ("cancel," "refund," "broken"), product names, customer-emotion words. The default calibration didn't allocate bits well for these tokens; our custom calibration did.
Deploying
Point Blueprint's Plan → Deploy at the new GGUF. The OpenAI-compatible API on 127.0.0.1:8080 serves it. Application code is unchanged from a stock Q4 deployment.
For an intent-classification application, you'd typically wrap this in a simple system prompt:
You are a customer support intent classifier.
Given a customer message, output a single intent label from:
cancel, refund_dispute, product_defect, billing_question, technical_issue,
shipping_inquiry, account_access, feature_request, general_complaint,
positive_feedback, partner_inquiry, other
Customer message: [USER_MESSAGE_HERE]
Intent:
The model outputs one label. Latency on a 4090 with the custom Q4 is ~50ms per request. Throughput is ~120 requests/sec at concurrency 8.
What this saves at scale
For a deployment doing 100,000 support classifications per day:
| Setup | Accuracy | Daily cost |
|---|---|---|
| GPT-4o-mini API | ~91% | $30 |
| Self-hosted Llama-3.3 70B (over-killed) | ~92% | $50 amortized GPU |
| Self-hosted Qwen 2.5 7B Q4 (stock) | 82% | $12 amortized GPU |
| Self-hosted Qwen 2.5 7B Q4 (custom-calibrated) | 89% | $12 amortized GPU |
The custom 7B Q4 sits within 2 percentage points of GPT-4o-mini at lower cost. The stock Q4 doesn't. The calibration step — one button click in Blueprint — is what makes the cheap setup competitive with the API.
What can go wrong
A few honest failure modes:
Corpus too narrow. If your 4000 transcripts are all from one product line, calibration over-fits and general capability drops. Mix in transcripts from across the business.
Imatrix run OOMs. Drop --ctx-size to 1024 (we did) or use --chunks 500 if you're tight on time.
Quantization succeeds but the model outputs garbage. Almost always a tokenizer or model architecture mismatch — make sure the f16 you calibrated against and the GGUF the quantizer produced match the same model. Blueprint enforces this; hand-rolled setups sometimes don't.
Accuracy went up on the trained intents but down on rare ones. Stratify the calibration corpus by intent. We pulled 333 examples per intent (×12 intents = 4000) to keep balance.
What to try next
- Compare custom Q4_K_M against custom Q5_K_S (closer to f16 quality, slightly bigger)
- Layer a LoRA fine-tune on top: with custom calibration recovering 88% of the Q4-vs-f16 gap, can a small LoRA close the rest?
- Apply the same flow to a different domain (sentiment classification, content moderation) — same pattern, different corpus
Each is a different Blueprint button click. The end-to-end engagement skill is "give me a workload, a corpus, and 2 hours, and I'll hand back a custom-quantized model that's better than the off-the-shelf at the same size."