# How do you teach a model a writing style without changing the facts?

Date: 2026-08-19 · Author: Robert Scales · https://guidetoartificialintelligence.com/en/learn/teach-writing-style-keep-facts/

**Answer:** Train on pairs where the facts vary and only the style repeats, include the odd cases such as missing values, then check every draft with a plain program that compares it to the source. In a measured test the style was easy, reaching 100%, while a warranty line was dropped in half of the drafts until odd cases were added to the training data.

Style and facts are stored in the same place: the words. When a model learns "write like us", nothing stops it from also learning "our prices are usually around 25 dollars". So this project treats the facts as something to measure on every test, separately from the style.

The job: turn a fictional shop's fact sheet into a short, warm notice for the neighbourhood. The model is SmolLM2-135M-Instruct with the LoRA setup from [the previous project](/en/learn/adapt-small-language-model-when-not/), run on an Apple M5 Max's GPU. The script is `10_style_keep_facts.py`, and each training run took about 45 seconds.

```text
Write a short shop notice from the fact sheet. Keep every fact exactly.

product: Copper Kettle
price: 683 dollars
in stock: 95
pickup day: Sunday
```

After training, one output read: "Friends, this one is for you! The Copper Kettle is here at 683 dollars. We have 95 on the shelf, and you can pick yours up on Sunday. Come say hello."

## A checker that is plain code

The fact test must not depend on a model's opinion. This one looks for each value, and for any number or weekday that should not be there.

```python
import re
DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]

def check(text, sheet):
    problems = [k for k in ("product", "price", "stock", "day") if str(sheet[k]) not in text]
    extra_numbers = set(re.findall(r"\d+", text)) - {str(sheet["price"]), str(sheet["stock"])}
    other_days = [d for d in DAYS if d in text and d != sheet["day"]]
    return problems, sorted(extra_numbers), other_days
```

## The ordinary test: this went better than expected

There were 100 test sheets, with products never seen, prices from 101 to 999 and stock from 41 to 99. The "narrow" training set saw only three prices, three stock levels, three days and four products, chosen to tempt the model into memorizing values.

| Training | All facts kept | Nothing invented | House style |
|---|---|---|---|
| None (untuned model) | 55% | 77% | 0% |
| 30 examples, few values | 100% | 100% | 100% |
| 30 examples, varied values | 99% | 99% | 100% |
| 400 examples, varied values | 100% | 100% | 100% |

The expected failure did not show up. Even the narrow set learned to copy values rather than memorize them. Do not generalize too far from this. The target is nearly a fixed sentence with slots, so copying is the easiest thing to learn. Real writing style has no such slots and would be much harder, and that was not tested here. The useful step is to look for where this result breaks.

## The stress test: where the facts went wrong

A real sheet does not always look like the training data. Each row below is 100 sheets, tested on the 400-example adapter and then on an adapter trained on 400 examples that included these odd cases, with other values.

| Odd case | 400 ordinary examples: passed the checker | Trained on odd cases too: passed the checker |
|---|---|---|
| No pickup day | 100% | 100% |
| Extra line: warranty | 50% | 100% |
| Price with cents | 100% | 100% |
| Stock of zero | 100% | 23% |

These numbers mislead in both directions, and reading the actual drafts shows why.

- **The warranty.** With ordinary training, half of the drafts dropped the line. The model never saw that field, so it had no slot for it. Training on it fixed this completely.
- **The missing day.** The checker gave 100%, and this draft passed: "you can pick yours up on the left." The model invented something that is neither a number nor a weekday, and the checker has a blind spot exactly there. After training on this case, the draft read "pickup is still to be confirmed", which is what you want. The score stayed at 100% across a real improvement.
- **Zero stock.** The 100% in the first column includes "We have 0 on the shelf, and you can pick yours up on Tuesday", which is awkward and misleading. In the second column, the model correctly wrote "It is sold out for now", and the checker failed 77 of 100 drafts because the digit 0 was absent. A better draft got a worse score.

With the rule "fall back to a dull template if the checker fails", that last group would have used the template 77% of the time, safely but needlessly. So a checker is not a judge. Its passes and its failures both need human sampling, especially after any change to the data.

> **Try it:** Add a field to the sheet that the training data never had, such as a colour. Run 20 sheets, then read every output yourself and count what the checker missed.

## Do you need training at all to get a style?

Often not. [Researchers have shown](https://arxiv.org/abs/2109.03910) that large language models can rewrite text in a requested style from an instruction alone, without fine-tuning or examples of the target style. With a capable model, start with a prompt and a few samples of the writing you want:

```prompt
Rewrite the text below in our voice: short sentences, warm, addressed to neighbours.
Use only the facts provided. If a fact is missing, write "to be confirmed". Do not add any number, date or name.

Examples of our voice:
[two or three samples]

Facts:
[fact sheet]
```

Training becomes reasonable when the model must be small or local, the style is very consistent, you have many examples you are allowed to use, and prompting does not hold steady. The [LoRA method](https://arxiv.org/abs/2106.09685) keeps that cost low; this adapter is 7.4 MB. If you train on someone's own writing, ask them first, and remember that their texts may hold personal details about other people.

Either way, keep the separation this project measured. Facts should come from a source you control and the model should only arrange them. Then check by program what a program can check, and have a person read what matters.

## Sources

- A Recipe For Arbitrary Text Style Transfer with Large Language Models: https://arxiv.org/abs/2109.03910
- LoRA: Low-Rank Adaptation of Large Language Models: https://arxiv.org/abs/2106.09685
- SmolLM2-135M-Instruct model card: https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct

---

From the Guide to Artificial Intelligence, by Robert Scales, published by Holistic Hybrid Inc. Share and adapt it under CC BY-NC-SA 4.0 (https://creativecommons.org/licenses/by-nc-sa/4.0/): credit the guide, link to https://guidetoartificialintelligence.com/en/learn/teach-writing-style-keep-facts/, no commercial use without permission, and keep the same licence.
