SFT Learns Your Median Employee, Not Your Best One
4,000 real support conversations, textbook loss curves, and a fine-tune that lost to its own base model 92% to 8%. SFT converges to the median of your dataset, and the median operator writes short answers with generic links.
How this post was written. I wrote the source draft. Four models each rewrote it from that same draft plus my persona files (writer, experimental, operational): Claude Opus 5, GPT-5.6 Sol, Kimi K3 (low reasoning effort), GLM 5.2. I judged the four blind, labels shuffled per post, model names revealed only after the verdict. This is the Claude Opus 5 version, which won. French phrases the models dropped in my voice were translated back to English; the quoted French system-prompt lines are the real ones. On 2026-08-10 the same winning model rewrote it once more from the same source, with regenerated persona files and an AI-tell lint report on the first version in the brief.
Blind test, labels hidden until after I had picked: the fine-tune lost to its own base model, 92% to 8%. Same prompts, every metric worse. No debate possible.
Nothing in the training run had warned me. Loss curves textbook, convergence clean, the whole thing healthy right up to the moment I looked at what it had converged to.
What 4,000 conversations average out to
4,000 real customer service conversations. Qwen3-8B-Instruct as the base. LoRA at the hyperparameters everyone starts with, all of them in the table at the bottom.
SFT converges to the median of your dataset. Every answer in there gets one vote, the excellent ones and the throwaway ones alike.
My 4,000 conversations included every operator response ever sent, from the excellent ones down to the "here is a generic search link, good luck" ones, with a lot of rushed and passable filling the middle. Run that through gradient descent and you get a portrait of the median operator. Short answers with a generic link attached. The client's request goes back unreformulated, and an emotional case gets the same flat treatment as a routine one.
Out of the box, Qwen3-8B-Instruct is already a good chatbot. It reformulates the request and structures the answer. On an emotional message it says something human before it says something useful.
So the fine-tune worked. It transferred what I handed it, and what I handed it was the average of a support desk. I had signed off on the dataset.
The model learned to sound like an average employee. The star performer was in there too, weighted exactly like everyone else.
The fabrications came in perfect brand voice
"100EUR for a 1-hour massage." "The 2025 calendars are open for most properties." Both fabricated, both stated with total confidence, in the tone I had just finished training.
Human operators state a price confidently because they looked it up. The model saw thousands of confident factual statements and learned the shape of the confidence with none of the lookup behind it. People generate facts from knowledge. Models generate patterns from training data.
When the pattern says state a specific number with authority, the model states a specific number with authority. Whether the number exists was never a variable it had access to.
That is the failure mode to watch when you fine-tune on real operational data. You are teaching the posture of knowing, detached from any knowing.
A thin coat instead of a repaint
v2 is four changes, and all four say the same thing to the optimizer: move less.
- Dataset: 4,115 examples down to 574. Tier 1 only, score 4 and above.
- Learning rate: 2e-4 down to 5e-5.
- LoRA rank: 16 down to 8. Less capacity, less room to absorb the bad patterns along with the good ones.
- Epochs: 3 down to 1-2. On a small clean set, another pass is another chance to overwrite.
I am not training a chatbot from scratch. A competent one already exists, and I want it to speak with our accent. Capability comes from the base model. The LoRA adds the accent on top.
The pink elephant in the system prompt
Rewriting the v2 system prompt turned up a second thing, unrelated on paper.
Negative instructions work badly on LLMs.
"Ne JAMAIS inventer d'information." Sounds firm. It has the structure of telling a kid not to think of a pink elephant, and the first thing processed is the elephant. Cognitive science calls it Ironic Process Theory, and it maps onto token generation better than it has any right to.
Generation is a positive operation. The model selects a next token and has no move available for avoiding one. A negation pushes an unwanted output slightly down the distribution, while a direction pushes the wanted one up. That asymmetry is where a guardrail leaks.
Every rule got rewritten as a direction:
- "Ne JAMAIS inventer d'information" became "TOUJOURS verifier une information avant de la communiquer. Si tu ne la connais pas, dis-le clairement." (always verify before you communicate, and say so plainly when you don't know)
- "Ne jamais ecrire comme si tu etais proprietaire" became "Parler en tant que plateforme qui met en relation voyageurs et hebergeurs." (speak as the platform connecting travelers and hosts)
Zero negations in the final prompt. Every line names an action to take.
Anthropic's own docs recommend this, and prompt-engineering circles have known it for a while. Production prompts ignore it anyway, mine included until this rewrite.
Both layers have to point the same way. The LoRA teaches tone. Guardrails live in the system prompt, and a guardrail phrased as a prohibition is a suggestion aimed at a mechanism that only knows how to move toward things.
A dataset audit disguised as a training run
Your first fine-tune is a dataset quality audit wearing the costume of a training run.
I had read the data before training on it. It read fine. Real conversations, our own work, nothing that jumped out as broken. What reading could never show me was the distribution, and the model is the only reader that takes all 4,000 examples equally seriously.
Most ML work in production needs three to five iterations before anything is deployable. A first attempt that comes out perfect means luck, or a target too easy to be worth hitting.
The eval on v2 is still TBD. I am not writing the win before I have it.
Numbers
| v1 | v2 | |
|---|---|---|
| Dataset size | 4,115 | 574 |
| Learning rate | 2e-4 | 5e-5 |
| LoRA rank | 16 | 8 |
| Epochs | 3 | 1-2 |
| Eval result | 8% win rate | TBD |
Four thousand examples taught the model to be the average of a support desk. The 574 left are the ones that survived a quality score, and they are the first dataset I can say I looked at properly.