001Notes

Email Classification and Personal AI: Rules, Naive Bayes, Transformers, Specialists, and Evaluation

On this page39

A from-zero visual tutorial building email classification from rules and Naive Bayes through personalization, specialist-model cascades, confidence, local model evaluation, and safe promotion.

Article details
Status
Building Publicly
Subcategory
Thunderbird AI
Last reviewed
3 Sept 2026
Prerequisites
No probability, machine-learning, or transformer knowledge required
39 sections

Suppose an email arrives with this subject:

Suspicious sign-in blocked — verify your account

A person can glance at it and say “security.” A computer begins with neither the concept of security nor the knowledge that sign-in, verify, and account are related. It receives characters. We must decide which outputs exist, transform the characters into something an algorithm can use, teach the algorithm what those outputs mean, estimate how strongly the message supports each output, and decide what to do when the estimate is weak.

That entire chain is classification.

This chapter builds that chain from scratch. We will first write a transparent rule. Then we will construct a real Multinomial Naive Bayes classifier from token counts, including smoothing and log-space arithmetic. After seeing exactly where that model succeeds and fails, we will inspect a contextual transformer and run Thunderbird’s pinned local ModernBERT model on two fictional benchmarks. Finally, we will combine the methods with an explicit confidence cascade and evaluate the resulting system honestly.

Nothing in the interactive experiments uses personal mail. Every address, message, label, prediction, and benchmark row is fictional. The deterministic and sparse artifacts are computed from the implementation used to generate this page. The transformer results are a recorded actual run against the local pinned model revision shown later.

Keep one distinction in mind throughout:

A predicted category is routing metadata. It may help organize or retrieve an email, but it is not evidence that a factual claim inside the email is true.

finance can help us find a receipt. It cannot prove that the amount was INR 805. The source sentence or an authoritative field with source provenance must prove that.

1. First define the question

People casually say “classify this email” as though classification were one operation. It is not. Consider a message that contains a receipt, asks for approval, and warns that payment is due tomorrow. We could ask at least five different questions.

Single-label classification

Choose exactly one value from a closed list:

finance | jobs | security | personal

The answer may be finance. This is useful for putting a message into one primary folder. The classes compete: choosing finance means not choosing security.

Multi-label classification

Choose zero, one, or several independent tags:

receipt = yes
needs-reply = yes
urgent = no

Now receipt does not compete with needs-reply; both may be true. Treating this as single-label would force the system to discard useful information.

Binary, ordinal, and extraction tasks

A binary classifier asks one yes-or-no question, such as “Is this phishing?” An ordinal classifier chooses an ordered level such as low < medium < high. Information extraction instead returns typed values and their source locations: amount = INR 805, merchant = Book Nook, reference = TXN-HC-100005.

These outputs require different losses, thresholds, and metrics. See how the contract changes while the source message stays fixed:

The first design task is therefore not selecting an algorithm. It is writing an output contract:

  1. What question is being answered?
  2. Which outputs are allowed?
  3. Can more than one output be true?
  4. May the classifier abstain?
  5. What is the cost of each kind of mistake?
  6. Is the output only routing metadata, or must it carry source evidence?

In the rest of the chapter we focus on one closed, single-label category prediction. Thunderbird’s experimental vocabulary contains fifteen labels:

action-required   calendar          developer-update
finance           job-hunt          jobs
legal             newsletter        personal
promotion         security          shipping
social-update     support           travel

Notice two labels that already look dangerous: job-hunt and jobs. Is a recruiter response part of a personal job hunt? Yes. Is it also about jobs? Yes. A human label definition must decide which one wins. A larger algorithm cannot repair an ambiguous contract.

2. Classification is a pipeline, not one mysterious function

Let us make the hidden steps explicit. A practical classifier does not jump directly from a MIME email to a trustworthy category.

decoded fields → representation → scores → confidence → precedence → routing label

The representation is the form of the message the algorithm can operate on. A regular expression uses characters. Naive Bayes uses word counts. A transformer uses contextual token vectors. These are three different views of the same source.

The candidate score is also algorithm-specific. A rule may produce a Boolean match. Naive Bayes produces relative log scores. The ModernBERT adapter produces a normalized top-label score. None of these numbers has meaning without its scoring procedure.

The precedence policy is ordinary program logic that decides whose result to trust. If a user-approved template maps one verified statement format to finance, should a weaker statistical model override it? Usually not. If an exact rule does not match and the local learned model is unsure, should a contextual model get a chance? Perhaps. If every method is weak, should the system guess anyway? Often it should fall back or ask for review.

This separation turns “the AI classified it” into inspectable questions: Which fields were read? Which representation was built? Which candidates were produced? What did each score mean? Which branch selected the output? Would a stricter threshold have abstained?

3. Rules: the baseline that refuses to be embarrassed

The simplest useful category classifier is a list of deterministic tests:

if (/\b(password|login|verification)\b/i.test(message)) {
  return { category: "security", confidence: "high" };
}

if (/\b(invoice|receipt|payment|bank)\b/i.test(message)) {
  return { category: "finance", confidence: "high" };
}

A regular expression searches character sequences. \b asks for a word boundary, alternatives inside parentheses mean “or,” and i makes matching case-insensitive. No training is occurring. A developer chose the terms and output.

Rules are especially good when a phrase is both stable and precise: password reset, tracking number, or calendar invitation. They are cheap, deterministic, straightforward to test, and direct to explain. Their apparent primitiveness is often a product advantage. If an exact phrase maps to a critical route, predictability may matter more than linguistic breadth.

The debugger exposes three limitations.

First, rules match strings, not meanings. “We sat beside the river bank” contains bank, so a finance rule may fire even though the word describes land beside water. This is polysemy: one spelling has multiple meanings.

Second, rules are weak at paraphrase. “The merchant charged my card twice” is financial to a human reader, but a narrow rule vocabulary may contain none of those exact terms.

Third, first-match order creates policy. “GitHub deployment complete; invoice attached” matches both developer and finance language. If the developer rule is earlier, developer-update wins. Reordering the list changes the answer without changing the message.

That does not make rules bad. It tells us what evidence they use. They have high precision on known surface forms and low recall over the many ways people can express the same intent.

Precision and recall already appear at the rule stage

Imagine 100 security emails. A narrow password-reset rule matches 20, and all 20 really are security messages. Its precision is 20/20, or 100%; its recall is 20/100, or 20%. The rule is trustworthy when it fires but silent most of the time.

Now broaden the expression to include account, access, code, and notice. It matches 85 security messages, plus 25 harmless messages. Recall rises to 85%, but precision falls to 85/110, or about 77%. This is the basic retrieval tradeoff in miniature: accepting more language usually finds more true cases and admits more false ones.

A useful rule layer often aims for high precision, not complete coverage. Let exact phrases handle cases they genuinely own, and allow later learned stages to handle paraphrase. A rule that tries to understand all language becomes an undocumented, order-sensitive programming language.

Rules also need ordinary software tests. For each expression, keep positive examples, near-miss negatives, Unicode examples, punctuation variations, and multi-rule collisions. Record which rule matched, the exact substring, and its position. A final label without the triggering span throws away the greatest debugging advantage rules possess.

4. Learning begins with labelled examples

A learned classifier replaces some hand-written phrase choices with statistics estimated from examples. Each training record needs an input and the output a human wants.

{
  label: "finance",
  subject: "Card transaction",
  author: "alerts@example.test",
  text: "card transaction merchant debited"
}

The label is called the target, class, or ground truth. The subject, author, and body are features in raw form. Thunderbird’s local sparse learner joins those fields before tokenization. Sender-like text can therefore influence the result alongside the subject and body.

Training examples silently define the problem. If every finance example comes from bank@example.test, the model may learn the sender token rather than financial language. It can score beautifully on more mail from that address and fail on a receipt from a new merchant. This is shortcut learning: a feature correlates with the label in training but does not capture the intended concept.

Good dataset design asks whether examples represent later messages, whether near-duplicate threads leak across train and test, whether senders are tied to labels, whether ambiguous messages are labelled consistently, whether rare classes have enough examples, and whether “none of these” is possible.

For this tutorial the training set is intentionally tiny and fictional. That makes every count inspectable. It also makes the resulting accuracy unsuitable as a claim about real inboxes.

Training, validation, and testing answer different questions

Training data changes the model. Naive Bayes turns its words into counts; a neural network changes learned weights. Evaluating on those same rows asks whether the learner can remember its lessons, not whether it can handle new mail.

A validation set is withheld while fitting and used to choose settings: minimum token length, smoothing strength, class thresholds, candidate label wording, or an abstention point. Looking at validation results repeatedly makes those decisions indirectly fit that set.

A test set is held back until decisions are frozen. It estimates how the frozen system behaves on examples that did not guide training or tuning. If we inspect test failures, modify the classifier, and report the same test again, it has quietly become another validation set. A new final test is then required for an unbiased estimate.

Email makes splitting harder than randomly shuffling rows. Messages in one thread quote each other. A sender uses repeated signatures. Newsletter issues share templates. If one thread appears in both training and test, the classifier may recognize near-duplicate wording rather than generalize. Grouped splits should keep related threads, templates, or senders together where the deployment question is “will this work on unfamiliar mail?”

There is also label noise. Two people may disagree whether “A recruiter viewed your profile” is jobs, job-hunt, or social-update. Before blaming a model, measure annotator agreement, review ambiguous boundaries, and permit multi-label output when the world truly contains overlapping properties. A classifier cannot exceed the consistency of the target it is asked to imitate.

5. Tokenization: turning a string into countable units

An algorithm cannot count “words” until we define what a word is. That procedure is tokenization.

Thunderbird’s sparse classifier performs these steps:

  1. Convert the input to a string, trim it, and keep at most 12,000 characters.
  2. Lowercase it using locale-aware casing.
  3. Replace everything except Unicode letters and numbers with spaces.
  4. Split on whitespace.
  5. Remove tokens whose length is two or fewer.
  6. Keep at most 512 surviving tokens.

Try punctuation, accented text, currency, two-letter abbreviations, or another language below.

This browser implementation mirrors the source contract used by the generated fixture. Change the text to see which evidence survives before the model sees it.

For the default text, résumé, paid, 805, and 2fa survive. CV and OK disappear because each has length two. The rupee sign disappears because only letters and numbers remain, while 805 survives.

These are not cosmetic cleaning decisions. They set the model’s sensory boundary. A system that drops HR, PR, CV, UK, and AI cannot later learn from those tokens. A 512-token limit means later text has no influence. Lowercasing makes Bank and bank identical even when capitalization carries a clue.

Tokenization is part of the trained model contract. Changing it after training changes the coordinates of every document and invalidates learned counts.

6. Bag of words: a document becomes a sparse vector

Collect every unique training token into an ordered vocabulary:

[invoice, payment, receipt, interview, recruiter, vacancy]

Represent a document by counting how many times each vocabulary word appears. For “invoice payment receipt”:

invoice payment receipt interview recruiter vacancy
   1       1       1        0         0        0

This row is a vector: an ordered list in which each position has a fixed meaning. It is sparse because a realistic vocabulary may have tens of thousands of positions while one email uses only a small fraction.

Two consequences matter. Order disappears. “invoice payment receipt” and “payment invoice receipt” have identical vectors. “Dog bites person” and “person bites dog” also match even though their meanings differ.

Repetition remains. “invoice invoice payment” places 2 in the invoice coordinate. Multinomial Naive Bayes treats that as two observations. A repeated footer can overpower a short original body.

We can preserve short phrases with n-grams such as password_reset, but the vocabulary grows rapidly. We can reduce common-word influence with TF-IDF, but ordinary Multinomial Naive Bayes has a clean probabilistic story with non-negative counts. Representation and algorithm must agree.

7. Probability from scratch: count possible worlds

Before Bayes’ rule, we need three ideas.

The prior is what we believe about a class before reading this message. If 30 of 100 training emails are finance:

Class prior

P(finance)=finance training messagesall training messages=30100= 0.30

Before seeing any words, finance receives a 30% starting share under this training distribution.

The likelihood asks how common an observed word is inside a class. If finance training text has 100 token occurrences and invoice occurs 12 times:

Token likelihood before smoothing

P(invoice | finance)=invoice occurrences in financeall token occurrences in finance=12100

The vertical bar means “given.” Read this as probability of invoice, given that the class is finance.

The posterior reverses the question: after observing the document, how plausible is each class?

Bayes' rule

P(class | words)=P(words | class) × P(class)P(words)

Evidence updates the starting prior. P(words) is shared by every candidate class, so ranking can ignore it.

For classification, calculate an unnormalized score for every candidate and choose the largest:

Multinomial Naive Bayes decision

predicted class= arg maxcP(c) × ∏iP(wᵢ | c)

Start with the class prior, multiply the likelihood of every observed token, and select the largest result.

The product symbol means “multiply all of these.” The subscript i numbers the document tokens.

Why naive? Once a class is known, the model pretends each token occurrence is independent of the others. In reality, reset becomes more informative beside password, and bank changes meaning beside river. The assumption is false—but the count estimator can still be fast and useful when class-specific words are strong.

8. A complete Naive Bayes dry run

Use the smallest dataset that teaches the mechanism:

finance → "invoice payment receipt"
jobs    → "interview recruiter vacancy"

Classify invoice payment. There are two examples, one per class, so both priors are one half. There are six unique vocabulary words. Each class has three token occurrences.

Without smoothing, invoice occurs zero times in jobs. Because the document score is a product, one zero makes the entire jobs score zero. With a large vocabulary, almost every new message contains something unseen. The classifier becomes brittle.

Laplace smoothing adds one imaginary count to every vocabulary word:

Add-one smoothed likelihood

P(w | c)=count(w,c) + 1token total(c) + vocabulary size

The numerator gives this word one extra observation; the denominator accounts for one extra observation for every vocabulary entry.

For finance, invoice and payment each have probability (1+1)/(3+6)=2/9. For jobs, each is unseen, so each has probability (0+1)/(3+6)=1/9.

Finance score

score(finance)=12×29×29=281

Both observed words occurred in the finance training document.

Jobs score

score(jobs)=12×19×19=1162

Smoothing keeps jobs possible, but each unseen word contributes a smaller likelihood.

Finance is four times larger. Normalizing the two scores gives 80% and 20% for display.

Query:

The 80% display share does not mean the model is correct 80% of the time. It came from two toy messages under assumptions that are badly violated. It says only how these model scores divide after normalization.

9. Why real implementations add logarithms

A long email may contain hundreds of tokens. Multiplying hundreds of probabilities smaller than one creates an extremely tiny number. Eventually floating-point arithmetic rounds it to zero. This is underflow.

Logarithms solve the arithmetic problem:

Product-to-sum identity

log(a × b × c)=log(a) + log(b) + log(c)

A product of tiny positive values becomes a sum of manageable negative values.

The classifier calculates:

Stable log score

score(c)=log P(c) + Σilog P(wᵢ | c)

The ordering is unchanged because logarithm is monotonic: if A is larger than B, log(A) is larger than log(B).

Log scores are usually negative. Less negative means larger: −3 beats −8. Thunderbird compares the best two scores. Their score gap is best log score − second-best log score. Gaps at or below 4 map to low confidence, above 4 to medium, and above 10 to high. These are engineering thresholds, not laws of probability.

10. Build and run the real sparse classifier

The implementation used for this page follows the source behavior rather than a library shortcut:

for (const example of trainingExamples) {
  const tokens = tokenize(subject + "\n" + author + "\n" + body);
  classExampleCount[label] += 1;
  for (const token of tokens) {
    vocabulary.add(token);
    tokenCount[label][token] += 1;
    classTokenTotal[label] += 1;
  }
}

At inference:

score[label] = log(classExampleCount[label] / allExampleCount);
for (const token of tokenize(newMessage)) {
  const numerator = (tokenCount[label][token] ?? 0) + 1;
  const denominator = classTokenTotal[label] + vocabulary.size;
  score[label] += log(numerator / denominator);
}

The artifact trains on twelve fictional records—four each for finance, jobs, and security—and evaluates twelve different held-out messages. Held out means the model does not see those rows while learning counts.

This is an implementation proof. You can inspect surviving tokens, relative scores, predicted label, and exact gap mapping. It proves that code can learn and separate these simple examples. It is not a quality proof for real email: the dataset is balanced, tiny, synthetic, and lexically friendly.

11. Priors, imbalance, and shortcuts

The prior enters every score before a word is considered. If 90% of training records are finance, finance begins with a large advantage. That may reflect deployment—or may mean receipts required less effort to label.

Duplicate the same finance sentence while leaving jobs and security unchanged. The query stays vague: “your update is ready.”

Class imbalance changes the prior, and repeated records change token likelihoods. Common countermeasures include representative collection, balanced sampling, per-class thresholds, macro metrics, grouped sender/thread splits, and abstention.

Macro averaging calculates a metric separately per class and gives each class equal weight. Overall accuracy counts every message equally, allowing one large well-separated class to hide failure on a rare one.

12. Why a transformer sees more than counts

Compare:

The bank approved my card dispute.
We sat on the river bank after lunch.

A bag of words sees bank in both. A contextual transformer builds a different representation for that token because neighboring words participate in the computation.

Its tokenizer usually uses subwords, not our whitespace tokens. Each token ID retrieves a learned vector; positional information preserves order. In self-attention, every token creates query, key, and value vectors. Query–key dot products determine how much one position reads from another, and weighted values form a new contextual representation. After many layers, bank beside river need not resemble bank beside card.

A conventional fine-tuned classifier adds a learned output head. Thunderbird’s private adapter here is different: it loads a natural-language inference model for zero-shot classification. It does not train a new fifteen-way head on this fictional mail.

NLI compares a premise and hypothesis:

premise:    A recruiter scheduled your engineering interview.
hypothesis: This email is about jobs.

The adapter repeats that hypothesis for every candidate label, scores entailment, and chooses the strongest.

“Uses ModernBERT” does not fully describe the algorithm. The encoder, NLI training, hypothesis template, candidate labels, and normalization procedure jointly define the classifier.

Context is created, not looked up

It is tempting to imagine that a transformer owns a dictionary entry for every word and retrieves the correct meaning. The first embedding lookup is closer to that, but self-attention repeatedly rewrites every token using its neighbors.

Consider only the token bank. In a toy layer it might place 70% of its attention on river, 20% on beside, and 10% on itself in one sentence. In the financial sentence it might place 55% on card, 30% on approved, and 15% on itself. Weighted mixtures of those neighbors pass through learned projections and nonlinear feed-forward layers. The two resulting bank vectors diverge even though their initial token ID was identical.

Attention weights are not automatically faithful human explanations. They show one internal routing mechanism, not a proof of why the final class won. Counterfactual tests—remove river, replace it with central, preserve everything else—give stronger behavioral evidence about which context changes the output.

Fine-tuning and zero-shot NLI solve the mapping differently

In supervised fine-tuning, we supply many pairs such as (email, finance) and (email, security). A classification head has one learned output per fixed label. Gradient descent changes parameters to increase the correct output on those examples. Adding a new class generally requires changing the head and training again.

In zero-shot NLI, a model already trained to judge entailment reads label descriptions as language. We can add a candidate without changing weights, which makes ontology experiments fast. The price is that label phrasing becomes prompt design, every extra candidate adds inference work, and product-specific distinctions may not align with the model’s general NLI training.

Few-shot prompting is a third idea commonly associated with generative language models: include labelled demonstrations in the request and ask the model to emit a category. It can express nuanced policies but consumes context, costs more inference, needs strict output validation, and may be harder to calibrate. It is not what the local ModernBERT adapter does.

Choose by constraints rather than fashion. Sparse local learning is compelling with little user-labelled data, tight latency, and a need for inspectable updates. Fine-tuning becomes attractive when there is substantial stable labelled data and repeated traffic. Zero-shot NLI is useful for bootstrapping and testing label definitions. A generative classifier may help when policies require rich instructions, provided its latency, privacy, and output contract are acceptable.

13. The label list is part of the input

Zero-shot labels are natural-language hypotheses, not harmless output names. Compare jobs, job-hunt, career opportunity, and recruiter conversation. A recruiter email plausibly entails all four. The model has never read our product rule that jobs means generic listings while job-hunt means one’s own application process.

Adding labels can lower a top score because more candidates share normalized mass, and it can change the winner. Compare benchmarks only when candidate sets match. Define labels in human language, minimize near-duplicates, try descriptive hypotheses, include an abstention policy, and version the vocabulary like model code.

Zero-shot classification is useful for testing an ontology quickly. It is not permission to skip ontology design.

14. An actual local ModernBERT experiment

The recorded artifact uses:

tasksource/ModernBERT-base-nli
revision de4ab7e77845098b7fab7f6ab9d370ddff27b19c
role: modernbert-intent
hypothesis: "This email is about {}."
execution: local CPU runner

The benchmark contains only fictional one-sentence emails, with direct, paraphrase, and confounder examples for every intended label.

We ran two tiers. The teaching tier contains nine messages intended as finance, jobs, or security, with only those labels competing. The contract tier contains 45 messages—three per Thunderbird label—with all fifteen competing. Each request was repeated three times after five warm-ups; the fixture records median latency and prediction stability.

Fixture schema:

The constrained run achieved 100% accuracy on 9 messages. The full run achieved 48.9% on 45 messages. Warm median latency grew from roughly 73 ms with three candidates to 353 ms with fifteen. The cold request took about 2.0 seconds, including lazy model loading.

The model did not suddenly become less intelligent. We asked a harder question. job-hunt competed with jobs; calendar with action-required; shipping looked like travel; support looked legal; promotional editorial copy resembled newsletters. Because zero-shot NLI evaluates hypotheses, more candidates also require more paired inference work.

The experiment proves only that the pinned local adapter ran, separated the narrow teaching set, and exposed substantial ambiguity under the current full vocabulary. It does not establish production quality, universal model ranking, GPU latency, calibration, multilingual performance, or adversarial safety. Forty-five synthetic rows are a diagnostic probe.

The recorder requires an explicit --allow-live and accepts only a loopback endpoint. Normal builds consume JSON; they do not start a model, download weights, or send content anywhere.

15. Confidence is a decision problem

A classifier can always return its largest score even when every option is bad. If allowed labels are finance, jobs, and security, “The ceramic glaze reached cone six” still gets a winner. Ranking does not imply suitability.

Distinguish a model score used to rank candidates, an engineering confidence bucket, and a calibrated probability. A score is calibrated when predictions near 80% are correct about 80% of the time on representative held-out data. Calibration is measured, not assumed.

Build a reliability diagram by binning predictions—0.5–0.6, 0.6–0.7, and so on—and comparing average score to observed accuracy. Temperature scaling, isotonic regression, or Platt-style scaling can learn a better mapping, but the calibration data must remain separate and resemble deployment.

Thresholds reflect consequences. A reversible folder suggestion may accept medium confidence. An automatic security action may require corroboration and review. An abstention option is a feature: keep the current category, route to unclassified, or ask a person.

Measure coverage, the fraction classified automatically, beside accuracy on accepted cases. Raising the threshold generally lowers coverage and raises precision. The useful point depends on product cost.

16. Combine methods with an explicit cascade

Rules and models have complementary strengths. Thunderbird resolves them in a visible order:

1. user-approved template override
2. exact deterministic rule
3. sufficiently confident user-trained sparse model
4. configured transformer hint
5. safe fallback

This is not score averaging. The scales differ, and higher-trust decisions may deserve precedence regardless of lower-trust scores.

If the sparse model is only medium confidence and a high-confidence transformer disagrees, the transformer may resolve it. A high sparse result is not casually displaced. If neither learned result reaches threshold, the system falls back.

Retain category, confidence, source, reason, and every candidate. Diagnostics let a developer distinguish bad representation, weak training, label ambiguity, threshold error, and wrong precedence. Without them, every failure becomes “AI was wrong.”

17. Evaluate what fails, not only what wins

For each label count true positives, false positives, and false negatives.

Precision

precision=true positivestrue positives + false positives

Of everything routed into this class, how much belonged there?

Recall

recall=true positivestrue positives + false negatives

Of everything that belonged in this class, how much did we find?

F1 score

F1=2 × precision × recallprecision + recall

The harmonic mean becomes small when either precision or recall is small.

A confusion matrix puts actual labels on rows and predictions on columns. Its diagonal is correct. Off-diagonal cells expose failure direction.

Suppose the security row contains three messages: two land on the security diagonal and one lands under action-required. Security recall is 2/3 because one real security message was missed. Now suppose one finance message is also predicted security. Security precision is 2/3 because one of the three security predictions was wrong. Both metrics happen to match here, but they answer opposite questions.

Accuracy alone would count all correct diagonal cells and divide by all messages. That can be appropriate when every row and error has similar cost. It becomes deceptive when personal mail is abundant, security mail is rare, or one error type triggers an irreversible action. Always connect a metric to the product decision it is meant to protect.

For probabilistic systems, also measure log loss or Brier score. Accuracy treats a 51% correct prediction and a 99% correct prediction equally; probability scoring rules reward well-calibrated confidence and heavily punish confident mistakes. For selective classifiers, report risk at coverage: among the 60% of messages the system accepted automatically, what fraction were wrong?

Finally, attach uncertainty to the evaluation itself. Forty-five rows produce a noisy estimate. Bootstrap intervals, repeated grouped splits, and larger frozen suites show whether a one-point improvement is likely signal or sampling luck. “Model B scored 81% while A scored 80%” is not meaningful until variance and test construction are known.

Evaluate the cascade and components: rule coverage/precision; sparse macro F1 on held-out senders and threads; transformer performance by difficulty, language, length, and label; disagreement outcomes; accuracy–coverage curves; cold/warm/tail latency, memory, and size; and harm from incorrect actions.

Benchmarks need targeted counterexamples. Step through five sparse-model failures.

Negation: “not a job alert” retains strong jobs tokens. Unigrams cannot represent the scope of not.

Polysemy: bank may mean finance or river land. Contextual encoders help because neighbors change its representation.

Boilerplate leakage: an unsubscribe footer can mention security and jobs below a personal body. Parse current authored text separately and evaluate parser plus classifier.

Out of distribution: a closed classifier ranks known labels even for pottery. Consider other/unknown, calibrated thresholds, dedicated OOD examples, and fallback.

Tokenization blind spots: CV OK; HR to call becomes only call. Changing minimum length may recover abbreviations while adding noisy words, so measure the tradeoff.

19. Seven experiments that turn the tutorial into evidence

  1. Remove smoothing. Recalculate the two-message example with raw counts. Measure how many class scores become zero.
  2. Destroy word order. Compare “dog bites person” and “person bites dog” as unigrams, then add bigrams and compare again.
  3. Sweep priors. Duplicate finance rows 1, 3, and 9 times. Plot normalized scores for the unchanged vague query.
  4. Hold out senders and threads. Compare random splitting with a grouped split that keeps each sender/thread on one side. A drop reveals leakage.
  5. Vary zero-shot labels. Use 3 broad labels, 15 product labels, then descriptive hypotheses. Record confusion, latency, and scores without changing weights.
  6. Measure calibration and selective accuracy. Draw a reliability diagram and sweep abstention threshold. Plot coverage against accepted-case accuracy.
  7. Test every cascade branch. Assert final category, source, reason, and retained candidates for template, rule, sparse, transformer, disagreement, and fallback cases.

Each experiment needs a frozen input, one controlled change, a measurable outcome, and a limitation statement. Otherwise it is a demo.

A practical build order for a new classifier

Begin with a written label guide and twenty to fifty examples per candidate class, including boundary cases where two labels seem plausible. Ask another person to label a sample without seeing your answers. Disagreements reveal unclear categories before a model turns that ambiguity into numbers.

Next, implement the safest deterministic rules and a fallback. Measure coverage and manually inspect every match. A rule layer that covers 15% of messages at very high precision may already remove a meaningful part of the learned model’s burden.

Then add the sparse model. Freeze the tokenizer, store its version with the learned counts, split by thread or sender, and record per-class metrics. Inspect the largest positive token contributions for correct and incorrect predictions. If sender domains or footer words dominate, improve parsing and splitting before tuning confidence thresholds.

Use zero-shot NLI as a challenger. Run it on exactly the same frozen test rows, first with broad labels and then with product-specific descriptions. Compare failure direction and latency, not only accuracy. It may recover paraphrases the sparse model misses while confusing close label boundaries. Those complementary errors are evidence for a cascade; identical errors suggest that adding another stage may only add cost.

Calibrate or threshold on validation data, then lock the policy. Decide explicitly which actions are reversible. Moving a message into a suggested view is cheaper to undo than suppressing a security warning or sending an automatic reply. The same prediction score should not trigger every action.

Finally, deploy with observation rather than silent authority. Log the model and vocabulary versions, candidate labels, winning source, confidence bucket, and whether the user corrected the route—without retaining unnecessary sensitive text. Sample errors under an appropriate privacy policy, watch coverage and per-class drift, and provide a fast way to undo classifications. A model update is incomplete until its migration, rollback, and re-evaluation behavior are defined.

20. What Thunderbird taught us

Email classification is not a contest where the newest model defeats every older method. It is a stack of contracts.

The label ontology defines distinctions. The parser defines visible content. The tokenizer defines symbols that become evidence. The representation defines relationships that survive. The learner estimates associations. Calibration interprets scores. The cascade chooses whose judgment wins. Evaluation determines which failures become visible. The product action determines the cost of being wrong.

Rules handle precise stable phrases. Multinomial Naive Bayes turns small local datasets into an inspectable model whose arithmetic we can reproduce. Contextual transformers handle paraphrase and word sense more naturally, but their behavior depends on training task, label wording, candidate competition, and runtime cost. Confidence becomes useful only after measurement and decision policy. A fallback keeps forced guesses from becoming invisible automation.

A classifier does not discover the one true category hidden inside an email. We define a question, construct a representation, learn or encode a decision rule, and choose how much uncertainty the product may act on.

Once those choices are explicit, classification stops looking mysterious. It becomes a system we can calculate, animate, test, criticize, and improve. The next parts ask how that system becomes personal, when other model roles should enter, and what evidence justifies promoting a new configuration.

21. Personalization and learning from corrections

“Make the AI learn my mailbox” sounds like a request to fine-tune a model. Most personal preferences are smaller and more precise:

Mail from this sender is finance.
This subject shape is a receipt.
Messages like these belong in jobs.
Do not summarize this field externally.
This group should be called Project Atlas.

Fine-tuning changes neural weights. Preferences, rules, templates, labelled examples and feedback can personalize behaviour without changing a foundation model.

LevelStored objectBest useMain risk
PreferenceExplicit settingStable user choiceToo coarse
RuleCondition → action or labelExact known patternBrittle wording
TemplateRepeated structure plus slotsMachine-generated mailFormat drift
Small local classifierExamples and token statisticsFlexible categoriesOverfitting
Fine-tuned modelUpdated neural parametersRepeated contextual taskCost, privacy and rollback

Use the least powerful mechanism that expresses the intended generalization. A sender override should not require millions of changed parameters.

22. One correction can spread several distances

Suppose a newsletter is predicted as promotion and the user changes it to developer update. The system could add an exact sender rule, add one labelled sparse example, change a template family, or update a global model. Each spreads the correction differently.

An exact rule has narrow blast radius. A sparse example generalizes to shared words. A template generalizes to repeated structure. Fine-tuning can change unrelated behaviour and is the hardest to explain or reverse. Before learning, define the target: category, reply style, priority, summary format and folder placement are different tasks with different data.

Thunderbird’s experimental training records remain named and versioned. An example retains label, source identity, text, inclusion state and holdout status. A feedback click such as Wrong is not sufficient training data because it may mean wrong category, wrong retrieval, unsupported answer, poor wording or privacy concern. Store structured issue type and trace reference; make accidental feedback reversible.

23. Watch a tiny personalization curve

The following computed run trains finance, jobs and security with one, two and three examples per label, then tests six fixed messages.

Training examples
Held-out accuracy

The curve is intentionally unstable. One example can add a valuable word, shift a prior, or introduce a shortcut. The final score is an arithmetic demonstration, not a mailbox-quality claim. Every dataset mutation needs fixed holdouts.

Repeated automated messages also exaggerate confidence. One newsletter copied 100 times is not 100 independent confirmations. Deduplicate by stable identity and normalized content, and keep thread siblings together so quoted copies cannot leak between train and test.

24. Overfitting, active learning and shadow mode

If every finance example comes from one bank, the model may memorize the sender. Test unseen senders, unseen template families, paraphrases, near-miss labels, balanced classes and the deployment distribution. Sender memorization may still be useful, but name it rather than calling it general language understanding.

Active learning asks a person to label cases expected to teach more than random examples. Prediction entropy is one signal:

Prediction entropy

H(p)=−Σᵢ pᵢ log pᵢ

Entropy rises when probability spreads across labels. Combine uncertainty with diversity and user value so strange outliers do not consume every request.

Disagreement between a rule, sparse model and transformer is another useful review queue. A candidate version should first run in shadow mode: the current decision remains visible, the challenger records its output, disagreements are measured, and promotion occurs only after explicit gates pass.

Fine-tuning becomes reasonable only when contextual errors persist, enough representative consented examples exist, a frozen evaluation shows a meaningful improvement, budgets permit the artifact, and rollback is solved. LoRA or adapters reduce trainable parameters; they do not remove data-quality, consent or evaluation requirements.

25. Specialist models and conditional compute

A large generative model can classify, extract, summarize, embed and rerank. That does not make one generative call the best implementation of every operation. A calculator could be simulated by next-token prediction, but arithmetic is cheaper and exact.

RoleInputOutputUseful objective
ClassifierMessageClosed labelCross-entropy and calibration
NER modelTokens plus requested typesTyped spansSpan precision and recall
EmbedderTextFixed-width vectorContrastive similarity
RerankerQuery and candidateRelevance scorePairwise or listwise ranking
SummarizerLong sourceShorter textFactual coverage and faithfulness
GeneratorInstructions and evidenceNew answer or draftTask pass and grounded generation

An embedding coordinate cannot be cited as a sentence. A classification score cannot be parsed as an amount. Fluent JSON does not guarantee that an entity span exists in source text.

Encoder models read the whole input and produce contextual representations, fitting classification, NER and embeddings. Decoders predict new tokens, fitting answers and drafts. Encoder-decoder models such as T5 read a source and generate a new sequence. Architectures blur these categories, but the input/output contract remains the better selection guide.

26. A cascade pays expensive costs conditionally

Run cheap, precise stages first and call flexible models only when necessary.

Expected cascade cost

E[cost]=c₁ + P(pass₁)c₂ + P(pass₁,pass₂)c₃ + …

Later cost is paid only for inputs reaching that stage. A confidently wrong early result can also block the specialist that was needed.

Request
Selected route
Learned cost

The fictional workload resolves three of five requests deterministically and sends only two to learned specialists. That counts routing decisions; it is not a universal latency claim.

GLiNER-style NER returns text, label, start, end and confidence; accept only exact spans inside bounded input. ModernBERT returns one intent from the declared vocabulary. Both are hints behind explicit opt-in and a verified loopback manifest. Installing weights does not authorize their outputs or convert them into source evidence.

Specialists also fail differently: classifiers choose wrong labels, NER shifts boundaries, embedders lose neighborhoods, rerankers demote good candidates, summarizers omit or invent, and generators produce unsupported language. Evaluate each interface before an end-to-end number hides the repair location.

A monolith can still be reasonable for a prototype, rare operation or tiny workload where orchestration dominates. Compare total system cost rather than following an architectural slogan.

27. Evaluation, model selection and promotion

Choosing a model by leaderboard position or parameter count is like choosing a vehicle by engine size without knowing the journey. Begin with an observable contract:

input       bounded fictional source cards and one question
output      concise answer plus required source URI
must        preserve identifiers and amounts
must not    invent missing facts or cite an absent source
budget      at most 1,024 completion tokens

Freeze the exact corpus, no-answer cases, near-miss values, contradictions and deterministic scorer. Hash its canonical bytes:

Corpus identity

corpusDigest=SHA-256(canonical corpus bytes)

Without the digest, a later report cannot prove that it evaluated the same test.

Pin the resolved model digest, architecture, parameter count, quantization, runtime, prompt, temperature, context limit and completion limit too. Reproducibility requires corpus identity plus model/runtime identity plus inference configuration.

28. Quantization and output budgets change the product

Quantization represents weights with fewer bits, commonly four or eight plus scale metadata.

Idealized raw weight memory

bytesparameters × bits per weight8

Runtime memory also includes scales, caches, activations, buffers and framework overhead.

An 8B model at four bits suggests about 4 GB of raw weights before overhead. The approximation can change exact-number or formatting behaviour, so measure the task.

Completion budgets can reverse rankings. A reasoning model may consume a short cap before emitting the required visible citation. Empty or truncated output is a failed product result, not hidden success.

29. Read a recorded local-model comparison

In the dated Thunderbird reproducibility run, the 8B candidate alone passed all six expanded fictional source-fact cases at a 1,024-token cap. The larger 27B candidate was slower and missed the complex comparison after exhausting its budget.

The claim is deliberately narrow: on that machine, configuration and corpus, qwen3:8b cleared the gate. It does not rank models universally.

Latency is a distribution. Record cold load, first-token delay, throughput, end-to-end duration, p50, p95, timeouts and failures. The empirical percentile index is approximately ceil(p × n), and enough repeated samples are needed to characterize the tail. Declare warmup because local model pages and caches can transform later runs.

30. Quality gates and the Pareto frontier

An answer-model evaluation should separate exact identifiers, numbers and dates; no-answer behaviour; citation presence and validity; claim support; format; prompt-injection resistance; and completion under budget. Privacy leakage and exact-ID substitution are often hard failures rather than small deductions in a weighted average.

A candidate is Pareto-dominated when another is no worse on every relevant dimension and better on at least one. A fast, slightly weaker model and a slower, stronger model may both remain valid operating points. Compare quality with p95 latency, memory, model size, energy and privacy placement.

Use role-specific metrics: classifier macro F1/calibration, NER span F1, embedder Recall@k/MRR, reranker nDCG gain, summarizer factual coverage, and generator task/citation pass rate. Synthetic fixtures isolate failures and are publishable; redacted real-shaped data adds messiness; consented local held-out data estimates deployment value. Do not turn six fictional cases into a general accuracy claim.

31. Promotion and rollback are part of evaluation

A promotable model needs a pinned artifact and license, passing role-specific corpus, privacy placement, resource limits, cancellation behaviour, fallback, staged rollout, shadow comparison and a named rollback target.

Avoid changing prompts for only one candidate, comparing one warm run with another cold run, reporting average latency without failures, accepting truncated output, tuning on the final test, ignoring memory or privacy, and losing the corpus/model digests.

The consolidated decision rule is:

Personalization chooses how far a correction should spread. Specialist routing chooses which computation should run. Evaluation proves whether the complete, reproducibly identified configuration deserves to replace the current one.

Primary references

  1. Stanford IR Book: Naive Bayes text classification
  2. Guo et al.: On Calibration of Modern Neural Networks
  3. scikit-learn: Classification probability calibration
  4. ModernBERT: Smarter, Better, Faster, Longer
  5. tasksource/ModernBERT-base-nli model card
  6. Settles: Active Learning Literature Survey
  7. LoRA: Low-Rank Adaptation of Large Language Models
  8. GLiNER: Generalist Model for Named Entity Recognition
  9. T5: Exploring the Limits of Transfer Learning
  10. MLPerf Inference benchmark rules and results
  11. NIST AI Risk Management Framework
  12. Qwen3 model source and documentation
Diagram