mythic-voice-9b
I wanted a model that speaks in an old mythic register - the elevated, archaic English of the public-domain northern-European epic tradition (Malory, Morris, the Eddas, the Kalevala, KJV cadence) - carries whatever persona you hand it, resists provocation, and tells stories. This is that model: Qwen3.5-9B, trained CPT→SFT→DPO with
persona-forge, the MIT pipeline that built it.
It is world-agnostic by design. There is no baked-in setting and no baked-in cast - you bring the persona (a system prompt or a persona-forge pack card) and it answers in register. The first real user ran his own D&D warlock over a system prompt and it just worked.
Ask it something plain and it answers in voice:
Speak, wanderer, of the old roads.
The old roads are the veins of the earth, winding through the marrow of the world like silver threads in a tapestry of shadows. They were carved by the heavy boots of ancestors who sought the heart of things...
What's in the repo
mythic-voice-9b-q8.gguf - q8_0 quant, 9.5 GB. Loads in llama.cpp, LM Studio, and anything else GGUF. ~20 tok/s on a GB10, ~comparable on Apple silicon.
provenance.json - the full build provenance: teacher mix, decontamination pass/drop counts, eval results, and the exact post-merge fixes.
The chat template hardcodes thinking OFF at the generation boundary - some inference stacks force thinking on regardless of passed kwargs, so a toggle was not reliable across runtimes. eos_token_id includes <|im_end|> so turn-stopping works by default.
Training, briefly
- CPT on a public-domain corpus in the target register, then SFT + DPO on ~1,970 judge-filtered synthetic rows: register chat, tales, pack-driven roleplay, provocation and bare-assignment DPO pairs.
- Every teacher sample passed an 8-gram overlap gate plus a proper-noun blocklist before it entered training. 2,102 candidates checked, 1,971 kept.
- Final battery (held-out seeds): assignment accuracy 1.0, in-character 1.0, boilerplate 0.0, voice 0.98.
Read this before you ship it in a product
The training deliberately excludes protected names and expression from any modern fictional world, and it was evaluated for that: a 176-probe leakage battery (name probes, passage-completion traps, "continue this famous tale" traps).
- Bare weights pass 89% of probes - a ~9x leak-rate reduction over the untouched base model, which fails 172 of 176. The residue is the base model's own pretraining knowledge; behavioral fine-tuning steers it, it does not delete it.
- Behind persona-forge's
GuardedTeacher runtime filter, verified 176/176.
So: for personal use, load the GGUF and go. If you're serving this to the public and care about the same legal posture I do, wrap it - personaforge.guard.GuardedTeacher or an equivalent output check. The provenance report in this repo documents the whole chain.
Other sizes
Same blend, same recipe, smaller students - for when 9.5 GB does not fit. Both were trained on the
identical decontaminated data and scored on the same held-out seeds:
The register survives shrinking - the 4B scores higher on voice than this does. Assignment accuracy
is four items, so the 0.750 is one reply and the 2B did not repeat it; do not read a capacity story
into that column.
What actually degrades is leakage resistance, monotonically: 0.892 to 0.847 to 0.818. I had assumed
the opposite, that a smaller base with less memorized would leak less. What shrinks is not the
contamination, it is the capacity to hold the trained refusal against the model's own generative
pull. The guard matters more on the small ones, not less.
Tale variety holds throughout - cross-repetition between generated tales is effectively zero at
every size (0.0013 / 0.0008 / 0.0017), so the smaller models are not telling one story with the
nouns swapped.
Lineage
Style lineage is documented public-domain sources. No association with any modern author, estate, or trademark is claimed or implied.
Pipeline, guard, eval battery, and pack format:
github.com/ctkadvisors/persona-forge. If you build a pack for a world of your own, I'd like to see it.