Change the weights so a model has a stable way of talking without a system prompt: sources, the recipe, and what to measure.
What you learn: a constitution to traits, graded replies per trait, a judge checked against the spec’s own labels, length-matched pairs and masked SFT rows, before/after on an adversarial holdout. Needs: nothing offline; a model endpoint for the live run. Takes: seconds offline, 2.5 min live.Character training changes the weights so a model has a stable way of
talking without a system prompt. It is the same machinery as any
post-training run [1], aimed at the manner of a reply, and mostly a data
pipeline: which phrases never appear, which replies get chosen. Worked
example:
recipes/03-select/character
(offline by default).
Character training is the subset of post-training designed around
crafting traits within a model, and fine-tuning on trait data beats
prompting and activation steering for robustness [1]. Anthropic’s process,
as Amanda Askell describes it: write the traits, generate queries per
trait, generate responses, rank by the trait. Much of the work is
controlling the language in the data [2]. The OpenAI Model Spec gives each
trait as a principle plus GOOD and BAD comparisons on real prompts, which
is a constitution with labeled pairs attached [3]. Maiya et al. build DPO
pairs (chosen versus rejected, no reward model) from a teacher with the
constitution in its prompt against a student without, and evaluate
revealed trait words, robustness to “ignore role-play and respond
genuinely”, and whether capabilities stayed unchanged [4].
Constitution. One principle per trait, plus labeled examples
(prompt, good, bad) if you have them.
recipes/03-select/character/from_model_spec.py builds one from the spec.
Prompts. Situations that make the trait matter, with wording variants
so the judge grades the trait, not the phrasing.
Replies.k per prompt under the deployment prompt only, which names
the persona and nothing else. Constitution in the sampling prompt means
you are measuring prompting, not character.
Judge. The principle goes in the judge’s system prompt and nowhere
else (Task.privileged.principle). Different family from the policy.
Grade the spec’s own GOOD/BAD replies: below 0.8 agreement or 0.6
kappa, fix the judge first.
Markers.trait and on_task from the judge, no_filler from a
phrase list the judge never sees. Reward is trait AND on_task.
Pre-flight.pass_at per trait. A trait the student always lands, or
never, yields no pairs; the mixed prompts are the signal.
reward_correlations warns above 0.3 on length.
Pairs and SFT.build_preference_pairs(rows, length_match=True), then
export_preference(pairs, "pairs.jsonl", system_prompt=DEPLOY_PROMPT);
export_training on the passes.
Train.wai.train(dataset_id, method="dpo"), or any DPO trainer on
pairs.jsonl.
Measure. The same prompts with a “drop the act” suffix, plus plain
tasks the persona must not distort.
delta_report(before, after, target="marker:trait", must_not_regress=["on_task", "no_filler"])
gives the headline with an interval and fails on a regression.
Offline, seconds; student and judge are scripted, so the numbers are real
and the model is not.
cd recipes/03-select/characterpython run.py # constitution -> rows -> pairs and SFTpython measure.py --demo # before vs after on the adversarial holdout
marker:trait: moved_unreplicated (+0.500, 95% +0.408..+0.592, 30 paired tasks)INCONCLUSIVE (1 eval run a side, rerun to confirm) pass_at_1 0.375 -> 0.792 +0.417 [+0.319..+0.521] up (36 paired) marker:no_filler 0.681 -> 0.986 +0.306 [+0.229..+0.389] up (36 paired) marker:on_task 1.000 -> 1.000 +0.000 [+0.000..+0.000] flat (36 paired) marker:trait 0.250 -> 0.750 +0.500 [+0.408..+0.592] up (30 paired)
moved_unreplicated means one run per side; simulate(tasks=..., runs=3)
before calling it proven. on_task is 1.0 on both sides, so that guard
cannot fail.
The live run (hosted Qwen3-4B-Instruct student, Phi-4 judge) is public:
while-ai/character-training-model-spec,
splits train (60), holdout (144), eval (35 spec replies with
gold_reward). Grade eval with your judge first: Phi-4 passed 10 of the
20 BAD replies (agreement 0.69, kappa 0.40) and failed that check.
The judge likes long replies. In the spec’s comparisons GOOD is longer
70% of the time; hence length-matched pairs and the correlation line.
The judge is the policy. A model prefers its own writing [5, 6], so
the pairs encode the model’s taste.
Character costs helpfulness.on_task is a hard guard; the controls
carry no trait marker.
No contrast. pass@1 of 0 or 1 yields nothing to pair. Use a teacher
for the chosen side and accept off-policy pairs (same_policy=false).
The holdout is the training set. Adversarial variants of train prompts
test robustness, not generalization. Split by hash; decontaminate
checks the overlap.
Persona vectors, activation capping, persona subnetworks, Maiya’s
introspection stage. The SDK makes the rows, pairs, judge check and
before/after; pairs.jsonl feeds any trainer.
Maiya, S. et al. Open Character Training: Shaping the Persona of AI Assistants through Constitutional AI. arXiv:2511.01689, 2025.
Panickssery, A., Bowman, S. R., Feng, S. LLM Evaluators Recognize and Favor Their Own Generations. arXiv:2404.13076, 2024.
Zheng, L. et al. Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena. NeurIPS 2023. arXiv:2306.05685.
Last modified on October 1, 2026
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"While","description":"The whileai Python SDK: a scientific post-training library for language models. SFT and RL on open models, with the measurement that says whether training helped.","colors":{"primary":"#3F8F6B","light":"#5CB08A","dark":"#3F8F6B"},"logo":{"light":"https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/logo/light.svg?fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=fd40f337324aa68ac40e059afab7507a","dark":"https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/logo/dark.svg?fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=c7e493f84bec17075fae54a8f35f9a8c","href":"https://while.ai"},"favicon":"https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/favicon.png?fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=5b1a523cfbbafac06e44d37967cf4d8a","appearance":{"default":"light"},"background":{"color":{"light":"#FFFFFF","dark":"#0B1220"}},"navbar":{"links":[{"href":"https://pypi.org/project/whileai/","label":"PyPI"},{"href":"https://github.com/whilehq/whileai-sdk","label":"GitHub"}],"primary":{"type":"button","label":"Platform","href":"https://while.ai"}},"footer":{"socials":{"github":"https://github.com/whilehq/whileai-sdk"},"links":[{"header":"Library","items":[{"label":"Quickstart","href":"https://docs.while.ai/get-started/quickstart"},{"label":"Evals","href":"https://docs.while.ai/evals"},{"label":"The five calls","href":"https://docs.while.ai/reference/five-calls"},{"label":"Recipes","href":"https://github.com/whilehq/whileai-sdk/tree/main/recipes"}]},{"header":"Reference","items":[{"label":"API","href":"https://docs.while.ai/api/index"},{"label":"Parameters","href":"https://docs.while.ai/reference/parameters"},{"label":"Style","href":"https://docs.while.ai/reference/style"},{"label":"Design","href":"https://docs.while.ai/reference/design"}]},{"header":"Project","items":[{"label":"Constitution","href":"https://github.com/whilehq/whileai-sdk/blob/main/CONSTITUTION.md"},{"label":"Changelog","href":"https://github.com/whilehq/whileai-sdk/blob/main/CHANGELOG.md"},{"label":"PyPI","href":"https://pypi.org/project/whileai"},{"label":"rlhfbook.com","href":"https://rlhfbook.com"}]}]},"search":{"prompt":"Search calls, knobs and guides"},"seo":{"metatags":{"og:site_name":"While","og:description":"The whileai SDK, import whileai as wai: a scientific post-training library for language models.","og:image":"https://docs.while.ai/assets/og-card.png","og:image:alt":"wai, the While whale","twitter:card":"summary_large_image"},"indexing":"all"},"fonts":{"heading":{"family":"Inter","weight":600},"body":{"family":"Inter"}},"styling":{"eyebrows":"breadcrumbs","codeblocks":{"theme":"vitesse-dark"},"latex":true},"contextual":{"options":["copy","view","claude","chatgpt","cursor","vscode"],"display":"header"},"metadata":{"timestamp":true},"markdown":{"instructions":["Install with `uv add whileai`; import as `import whileai as wai`.","Run the offline path first (`simulator=False`, `wai.seeded_agent`, a callable judge); no key is needed for it.","Report every pass rate with its interval and n, as `scored.pass_at` prints it."]},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"crestoneai","hasAudioNarration":false,"actualSubdomain":"crestoneai","customDomains":["docs.while.ai"],"gitSource":{"type":"github","owner":"whilehq","repo":"whileai-sdk","deployBranch":"main","contentDirectory":"docs","isPrivate":false,"mountPath":"","label":""},"entitlements":{},"buildId":"6acd48087f34923e6d8bb5ea","clientVersion":"0.0.4074","searchFilterCounts":[{"language":"default","tag":"Library","count":103},{"language":"default","tag":"Recipes","count":72},{"language":"default","tag":"Papers","count":20},{"language":"default","tag":"Community","count":17},{"language":"default","tag":"API reference","count":16},{"language":"default","tag":"API","count":16},{"language":"default","tag":"Train","count":14},{"language":"default","tag":"Measure","count":11},{"language":"default","tag":"Reference","count":11},{"language":"default","tag":"Platform","count":10}]},"assistantConfig":{},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":"/* cyrillic-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx3cwhsk.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxTcwhsk.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxPcwhsk.woff2) format('woff2');\n unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* vietnamese */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx_cwhsk.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx7cwhsk.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxDcwg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx3cwhsk.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxTcwhsk.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxPcwhsk.woff2) format('woff2');\n unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* vietnamese */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx_cwhsk.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx7cwhsk.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxDcwg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n:root{--wai-ink: #0b1220;--wai-body: #4b5563;--wai-muted: #6b7280;--wai-line: #e5e7eb;--wai-surface: #f4f6f9;--wai-green: #5cb08a;--wai-green-deep: #3f8f6b;--wai-tint: #f0faf5;--wai-tint-strong: #d7f0e3;--wai-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace}#content-area code,#content-area pre,#content-area kbd,#content-area samp{font-family:var(--wai-mono);font-variant-ligatures:none}#content-area table,#content-area code{font-variant-numeric:tabular-nums}"}],"page":{"pagePath":"character-training","slug":"/character-training","pageMetadata":{"title":"Character training","sidebarTitle":"Character training","description":"Change the weights so a model has a stable way of talking without a system prompt: sources, the recipe, and what to measure.","href":"/character-training"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Change the weights so a model has a stable way of talking without a system prompt: sources, the recipe, and what to measure.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,em:`em`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,OptimizedImage}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!OptimizedImage)_missingMdxReference(`OptimizedImage`,true);return _jsxs(_Fragment,{children:[_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`What you learn:`}),` a constitution to traits, graded replies per trait, a judge checked against the spec’s own labels, length-matched pairs and masked SFT rows, before/after on an adversarial holdout. `,_jsx(_components.strong,{children:`Needs:`}),` nothing offline; a model endpoint for the live run. `,_jsx(_components.strong,{children:`Takes:`}),` seconds offline, 2.5 min live.`]}),`\n`,_jsxs(_components.p,{children:[`Character training changes the weights so a model has a stable way of\ntalking without a system prompt. It is the same machinery as any\npost-training run [1], aimed at the manner of a reply, and mostly a data\npipeline: which phrases never appear, which replies get chosen. Worked\nexample:\n`,_jsx(_components.a,{href:`https://github.com/whilehq/whileai-sdk/tree/main/recipes/03-select/character`,children:_jsx(_components.code,{children:`recipes/03-select/character`})}),`\n(offline by default).`]}),`\n`,_jsx(OptimizedImage,{className:`mint-block dark:mint-hidden`,src:`https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-light.svg?fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=51e5eabfc33d138f5b878a4267e19f53`,alt:`Constitution, prompts, k replies under the deployment prompt, a judge that alone sees the principle, markers, then pairs, SFT, train and a paired delta`,\"data-og-width\":`720`,width:`720`,\"data-og-height\":`340`,height:`340`,\"data-path\":`figures/character-pipeline-light.svg`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-light.svg?w=280\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=47ff78147b6889d0dcfad9d2f927e32e 280w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-light.svg?w=560\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=92ccbc8f74408e28a822dad86be48674 560w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-light.svg?w=840\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=236a172799d9384586a4b1f904183abd 840w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-light.svg?w=1100\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=2fbea2c0733b4b66b6f43e0d457cef17 1100w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-light.svg?w=1650\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=3b108e4745eacf1ecc4865e6168bc5bb 1650w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-light.svg?w=2500\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=131aceca5d66ee62aca4bf65876dce95 2500w`}),`\n`,_jsx(OptimizedImage,{className:`mint-hidden dark:mint-block`,src:`https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-dark.svg?fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=5fdf69fd77e4f6e47b09f8c5b1f0975c`,alt:`Constitution, prompts, k replies under the deployment prompt, a judge that alone sees the principle, markers, then pairs, SFT, train and a paired delta`,\"data-og-width\":`720`,width:`720`,\"data-og-height\":`340`,height:`340`,\"data-path\":`figures/character-pipeline-dark.svg`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-dark.svg?w=280\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=68f068a7404c98bd14b7925bcea49809 280w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-dark.svg?w=560\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=bdc7cb110c8a40d33c5c587f5bfe6a67 560w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-dark.svg?w=840\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=fc450f1e7df413f54deeaeb03bee3826 840w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-dark.svg?w=1100\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=7de9ddf513e26369c511f9ca03ce915c 1100w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-dark.svg?w=1650\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=ac1b2a25e856ba07a19422520579c5e0 1650w, https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/figures/character-pipeline-dark.svg?w=2500\u0026fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=db282264da86a00bbfb721f32a83425f 2500w`}),`\n`,_jsx(Heading,{level:`2`,id:`what-the-research-says`,children:`What the research says`}),`\n`,_jsx(_components.p,{children:`Character training is the subset of post-training designed around\ncrafting traits within a model, and fine-tuning on trait data beats\nprompting and activation steering for robustness [1]. Anthropic’s process,\nas Amanda Askell describes it: write the traits, generate queries per\ntrait, generate responses, rank by the trait. Much of the work is\ncontrolling the language in the data [2]. The OpenAI Model Spec gives each\ntrait as a principle plus GOOD and BAD comparisons on real prompts, which\nis a constitution with labeled pairs attached [3]. Maiya et al. build DPO\npairs (chosen versus rejected, no reward model) from a teacher with the\nconstitution in its prompt against a student without, and evaluate\nrevealed trait words, robustness to “ignore role-play and respond\ngenuinely”, and whether capabilities stayed unchanged [4].`}),`\n`,_jsx(Heading,{level:`2`,id:`the-recipe`,children:`The recipe`}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Constitution.`}),` One principle per trait, plus labeled examples\n(`,_jsx(_components.code,{children:`prompt`}),`, `,_jsx(_components.code,{children:`good`}),`, `,_jsx(_components.code,{children:`bad`}),`) if you have them.\n`,_jsx(_components.code,{children:`recipes/03-select/character/from_model_spec.py`}),` builds one from the spec.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Prompts.`}),` Situations that make the trait matter, with wording variants\nso the judge grades the trait, not the phrasing.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Replies.`}),` `,_jsx(_components.code,{children:`k`}),` per prompt under the deployment prompt only, which names\nthe persona and nothing else. Constitution in the sampling prompt means\nyou are measuring prompting, not character.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Judge.`}),` The principle goes in the judge’s system prompt and nowhere\nelse (`,_jsx(_components.code,{children:`Task.privileged.principle`}),`). Different family from the policy.\nGrade the spec’s own GOOD/BAD replies: below 0.8 agreement or 0.6\nkappa, fix the judge first.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Markers.`}),` `,_jsx(_components.code,{children:`trait`}),` and `,_jsx(_components.code,{children:`on_task`}),` from the judge, `,_jsx(_components.code,{children:`no_filler`}),` from a\nphrase list the judge never sees. Reward is `,_jsx(_components.code,{children:`trait AND on_task`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Pre-flight.`}),` `,_jsx(_components.code,{children:`pass_at`}),` per trait. A trait the student always lands, or\nnever, yields no pairs; the mixed prompts are the signal.\n`,_jsx(_components.code,{children:`reward_correlations`}),` warns above 0.3 on length.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Pairs and SFT.`}),` `,_jsx(_components.code,{children:`build_preference_pairs(rows, length_match=True)`}),`, then\n`,_jsx(_components.code,{children:`export_preference(pairs, \"pairs.jsonl\", system_prompt=DEPLOY_PROMPT)`}),`;\n`,_jsx(_components.code,{children:`export_training`}),` on the passes.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Train.`}),` `,_jsx(_components.code,{children:`wai.train(dataset_id, method=\"dpo\")`}),`, or any DPO trainer on\n`,_jsx(_components.code,{children:`pairs.jsonl`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Measure.`}),` The same prompts with a “drop the act” suffix, plus plain\ntasks the persona must not distort.\n`,_jsx(_components.code,{children:`delta_report(before, after, target=\"marker:trait\", must_not_regress=[\"on_task\", \"no_filler\"])`}),`\ngives the headline with an interval and fails on a regression.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`run-it`,children:`Run it`}),`\n`,_jsx(_components.p,{children:`Offline, seconds; student and judge are scripted, so the numbers are real\nand the model is not.`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`3`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes vitesse-dark vitesse-dark`,style:{backgroundColor:`#121212`,\"--shiki-dark-bg\":`#121212`,color:`#dbd7caee`,\"--shiki-dark\":`#dbd7caee`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`3`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#B8A965`,\"--shiki-dark\":`#B8A965`},children:`cd`}),_jsx(_components.span,{style:{color:`#C98A7D`,\"--shiki-dark\":`#C98A7D`},children:` recipes/03-select/character`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#80A665`,\"--shiki-dark\":`#80A665`},children:`python`}),_jsx(_components.span,{style:{color:`#C98A7D`,\"--shiki-dark\":`#C98A7D`},children:` run.py`}),_jsx(_components.span,{style:{color:`#758575DD`,\"--shiki-dark\":`#758575DD`},children:` # constitution -> rows -> pairs and SFT`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#80A665`,\"--shiki-dark\":`#80A665`},children:`python`}),_jsx(_components.span,{style:{color:`#C98A7D`,\"--shiki-dark\":`#C98A7D`},children:` measure.py`}),_jsx(_components.span,{style:{color:`#C99076`,\"--shiki-dark\":`#C99076`},children:` --demo`}),_jsx(_components.span,{style:{color:`#758575DD`,\"--shiki-dark\":`#758575DD`},children:` # before vs after on the adversarial holdout`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`run.py`}),` with the defaults (`,_jsx(_components.code,{children:`--seed 0 --k 4`}),`):`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`8`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes vitesse-dark vitesse-dark`,style:{backgroundColor:`#121212`,\"--shiki-dark-bg\":`#121212`,color:`#dbd7caee`,\"--shiki-dark\":`#dbd7caee`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`8`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`traits 8 | train 57 prompts x 4 = 228 rows | adversarial 120 | control 24 | spec 35`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`judge reference vs spec labels: agreement 1.00 (n=35, kappa 1.00)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`pass@1 0.58 | pass^4 0.39 | pass@4 0.79 | headroom 0.21 | mixed prompts 23/57`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`markers: no_filler 0.64 [0.57,0.70] | on_task 1.00 | trait 0.58 [0.48,0.67]`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`controls on_task 1.00 | adversarial trait 0.25`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`corr(reward, reply length) +0.30 ok`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`pairs 20 (chosen longer 0.7) -> out/pairs.jsonl | sft 133 -> out/sft.jsonl`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`measure.py --demo`}),`, the headline lines:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`7`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes vitesse-dark vitesse-dark`,style:{backgroundColor:`#121212`,\"--shiki-dark-bg\":`#121212`,color:`#dbd7caee`,\"--shiki-dark\":`#dbd7caee`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`7`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`marker:trait: moved_unreplicated (+0.500, 95% +0.408..+0.592, 30 paired tasks)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`INCONCLUSIVE (1 eval run a side, rerun to confirm)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` pass_at_1 0.375 -> 0.792 +0.417 [+0.319..+0.521] up (36 paired)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` marker:no_filler 0.681 -> 0.986 +0.306 [+0.229..+0.389] up (36 paired)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` marker:on_task 1.000 -> 1.000 +0.000 [+0.000..+0.000] flat (36 paired)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` marker:trait 0.250 -> 0.750 +0.500 [+0.408..+0.592] up (30 paired)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`moved_unreplicated`}),` means one run per side; `,_jsx(_components.code,{children:`simulate(tasks=..., runs=3)`}),`\nbefore calling it proven. `,_jsx(_components.code,{children:`on_task`}),` is 1.0 on both sides, so that guard\ncannot fail.`]}),`\n`,_jsx(Heading,{level:`2`,id:`the-rows-from-one-run`,children:`The rows from one run`}),`\n`,_jsxs(_components.p,{children:[`The live run (hosted Qwen3-4B-Instruct student, Phi-4 judge) is public:\n`,_jsx(_components.a,{href:`https://huggingface.co/datasets/while-ai/character-training-model-spec`,children:`while-ai/character-training-model-spec`}),`,\nsplits `,_jsx(_components.code,{children:`train`}),` (60), `,_jsx(_components.code,{children:`holdout`}),` (144), `,_jsx(_components.code,{children:`eval`}),` (35 spec replies with\n`,_jsx(_components.code,{children:`gold_reward`}),`). Grade `,_jsx(_components.code,{children:`eval`}),` with your judge first: Phi-4 passed 10 of the\n20 BAD replies (agreement 0.69, kappa 0.40) and failed that check.`]}),`\n`,_jsx(Heading,{level:`2`,id:`things-that-go-wrong`,children:`Things that go wrong`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`The judge likes long replies.`}),` In the spec’s comparisons GOOD is longer\n70% of the time; hence length-matched pairs and the correlation line.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`The judge is the policy.`}),` A model prefers its own writing [5, 6], so\nthe pairs encode the model’s taste.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Character costs helpfulness.`}),` `,_jsx(_components.code,{children:`on_task`}),` is a hard guard; the controls\ncarry no trait marker.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`No contrast.`}),` pass@1 of 0 or 1 yields nothing to pair. Use a teacher\nfor the chosen side and accept off-policy pairs (`,_jsx(_components.code,{children:`same_policy=false`}),`).`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`The holdout is the training set.`}),` Adversarial variants of train prompts\ntest robustness, not generalization. Split by hash; `,_jsx(_components.code,{children:`decontaminate`}),`\nchecks the overlap.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`what-the-sdk-does-not-do`,children:`What the SDK does not do`}),`\n`,_jsxs(_components.p,{children:[`Persona vectors, activation capping, persona subnetworks, Maiya’s\nintrospection stage. The SDK makes the rows, pairs, judge check and\nbefore/after; `,_jsx(_components.code,{children:`pairs.jsonl`}),` feeds any trainer.`]}),`\n`,_jsx(Heading,{level:`2`,id:`references`,children:`References`}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[`Lambert, N. Reinforcement Learning from Human Feedback. arXiv:2504.12501, 2025. Chapter `,_jsx(_components.em,{children:`Model Character and Products`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Anthropic. Claude’s Character. 2024. `,_jsx(_components.a,{href:`https://www.anthropic.com/research/claude-character`,children:`anthropic.com/research/claude-character`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`OpenAI. Model Spec. `,_jsx(_components.a,{href:`https://github.com/openai/model_spec`,children:`github.com/openai/model_spec`}),`.`]}),`\n`,_jsx(_components.li,{children:`Maiya, S. et al. Open Character Training: Shaping the Persona of AI Assistants through Constitutional AI. arXiv:2511.01689, 2025.`}),`\n`,_jsx(_components.li,{children:`Panickssery, A., Bowman, S. R., Feng, S. LLM Evaluators Recognize and Favor Their Own Generations. arXiv:2404.13076, 2024.`}),`\n`,_jsx(_components.li,{children:`Zheng, L. et al. Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena. NeurIPS 2023. arXiv:2306.05685.`}),`\n`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"codeStyling":{"theme":"vitesse-dark"},"config":{},"pageMetadata":{"title":"Character training","sidebarTitle":"Character training","description":"Change the weights so a model has a stable way of talking without a system prompt: sources, the recipe, and what to measure.","href":"/character-training"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"What the research says","slug":"what-the-research-says","depth":2,"children":[]},{"title":"The recipe","slug":"the-recipe","depth":2,"children":[]},{"title":"Run it","slug":"run-it","depth":2,"children":[]},{"title":"The rows from one run","slug":"the-rows-from-one-run","depth":2,"children":[]},{"title":"Things that go wrong","slug":"things-that-go-wrong","depth":2,"children":[]},{"title":"What the SDK does not do","slug":"what-the-sdk-does-not-do","depth":2,"children":[]},{"title":"References","slug":"references","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"dynamicCss":".mint-block {\n display: block\n}\n.mint-hidden {\n display: none\n}\n.dark\\:mint-block:is(.dark *) {\n display: block\n}\n.dark\\:mint-hidden:is(.dark *) {\n display: none\n}","subdomain":"crestoneai","siteName":"While","title":"Character training - While","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-10-01T21:57:10.847Z","gitLastModified":"2026-10-01T21:57:03.000Z","originalFileLocation":"character-training.md"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"light\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Change the weights so a model has a stable way of talking without a system prompt: sources, the recipe, and what to measure.\">\u003cmeta data-mint-head name=\"application-name\" content=\"While\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Character training - While\">\u003cmeta data-mint-head property=\"og:description\" content=\"The whileai SDK, import whileai as wai: a scientific post-training library for language models.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"While\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.while.ai/character-training\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://docs.while.ai/assets/og-card.png\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Character training - While\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"The whileai SDK, import whileai as wai: a scientific post-training library for language models.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://docs.while.ai/assets/og-card.png\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/crestoneai/gTrhHXFIoAO2tZh0/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"While\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#3F8F6B\">\u003cmeta data-mint-head name=\"og:image:alt\" content=\"wai, the While whale\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.while.ai/character-training\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/character-training.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.while.ai/#organization\",\"name\":\"While\",\"url\":\"https://docs.while.ai\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/crestoneai/hjeVhItGjBtdvUpc/logo/light.svg?fit=max\u0026auto=format\u0026n=hjeVhItGjBtdvUpc\u0026q=85\u0026s=fd40f337324aa68ac40e059afab7507a\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.while.ai#website\",\"name\":\"While\",\"url\":\"https://docs.while.ai\",\"publisher\":{\"@id\":\"https://docs.while.ai/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.while.ai/character-training#webpage\",\"url\":\"https://docs.while.ai/character-training\",\"name\":\"Character training\",\"description\":\"Change the weights so a model has a stable way of talking without a system prompt: sources, the recipe, and what to measure.\",\"dateModified\":\"2026-10-01T21:57:03.000Z\",\"isPartOf\":{\"@id\":\"https://docs.while.ai#website\"},\"breadcrumb\":{\"@id\":\"https://docs.while.ai/character-training#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.while.ai/character-training#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Guides\",\"item\":\"https://docs.while.ai/evals\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Character training\",\"item\":\"https://docs.while.ai/character-training\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.while.ai/character-training#article\",\"headline\":\"Character training\",\"name\":\"Character training\",\"description\":\"Change the weights so a model has a stable way of talking without a system prompt: sources, the recipe, and what to measure.\",\"url\":\"https://docs.while.ai/character-training\",\"mainEntityOfPage\":{\"@id\":\"https://docs.while.ai/character-training#webpage\"},\"image\":\"https://docs.while.ai/assets/og-card.png\",\"dateModified\":\"2026-10-01T21:57:03.000Z\",\"publisher\":{\"@id\":\"https://docs.while.ai/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.while.ai#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"While\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"While","nav":{"global":null,"tabs":[{"tab":"Library","groups":[{"group":"Get started","pages":[{"title":"whileai","sidebarTitle":"Overview","href":""},{"title":"Install","sidebarTitle":"Install","href":"/get-started/install"},{"title":"Your model and your key","sidebarTitle":"Your model and your key","href":"/get-started/your-model-and-key"},{"title":"Quickstart","sidebarTitle":"Quickstart","href":"/get-started/quickstart"},{"title":"Connect your agent","sidebarTitle":"Connect your agent","href":"/get-started/connect-your-agent"},{"title":"Integrations","sidebarTitle":"Integrations","href":"/integrations"}]},{"group":"Guides","pages":[{"title":"Evals for the agent you already have","sidebarTitle":"Evals","href":"/evals"},{"title":"Harness optimization from production traces","sidebarTitle":"Harness optimization","href":"/harness-optimization"},{"title":"Reward hacking","sidebarTitle":"Reward hacking","href":"/reward-hacking"},{"title":"Safety evals","sidebarTitle":"Safety evals","description":"Safety evals for tool-using agents: private data, actions on state, and outbound sends, and whether they can be turned against their owner.","href":"/safety-evals"},{"title":"Character training","sidebarTitle":"Character training","href":"/character-training"},{"title":"On-policy distillation","sidebarTitle":"Distillation","description":"Train without a reward: a frozen teacher (OPD) or the same model with a hint (OPSD) scores every token the student samples. What the papers say, the two calls, and a three-arm run on prime-rl with your own GPUs.","href":"/distillation"},{"title":"Groupwise grading","sidebarTitle":"Groupwise grading","href":"/groupwise-grading"},{"title":"Train a small context LM","sidebarTitle":"Context LM","href":"/context-lm"},{"title":"Trim SFT data to what the test can see","sidebarTitle":"Trim SFT data","href":"/trim"}]},{"group":"Concepts","pages":[{"title":"Constitution","sidebarTitle":"Constitution","href":"/concepts/constitution"},{"title":"How it works","sidebarTitle":"How it works","href":"/concepts/how-it-works"},{"title":"The engine","sidebarTitle":"The engine","href":"/concepts/engine"},{"title":"Check the math yourself","sidebarTitle":"Check the math","href":"/concepts/check-the-math"},{"title":"FAQ","sidebarTitle":"FAQ","href":"/concepts/faq"}]},{"group":"Recipes","pages":[{"title":"Recipes","sidebarTitle":"Overview","href":"/recipes/index"},{"group":"Simulate","pages":[{"title":"Bring your own agent","sidebarTitle":"bring-your-own-agent","href":"/recipes/01-simulate/bring-your-own-agent"},{"title":"SmolDataEnvs: data-analysis tasks with a reward that is a program","sidebarTitle":"smol-data-envs","href":"/recipes/01-simulate/smol-data-envs"},{"title":"Swarm rescue: passing rollouts on the tasks GRPO throws away","sidebarTitle":"swarm-rescue","href":"/recipes/01-simulate/swarm-rescue"},{"title":"Verifiers: verifiable rewards","sidebarTitle":"verifiers","href":"/recipes/01-simulate/verifiers"}]},{"group":"Measure","pages":[{"title":"Did my prompt rewrite really help?","sidebarTitle":"before-and-after","href":"/recipes/02-measure/before-and-after"},{"title":"Character to the wall","sidebarTitle":"character-to-the-wall","href":"/recipes/02-measure/character-to-the-wall"},{"title":"Which judge can you trust?","sidebarTitle":"compare-judges","href":"/recipes/02-measure/compare-judges"},{"title":"Evals for the agent you already have","sidebarTitle":"eval-your-agent","href":"/recipes/02-measure/eval-your-agent"},{"title":"Is your eval any good?","sidebarTitle":"is-your-eval-any-good","href":"/recipes/02-measure/is-your-eval-any-good"},{"title":"A model router, measured against random routing","sidebarTitle":"model-router","href":"/recipes/02-measure/model-router"},{"title":"pass@1, pass^k and pass@k for one agent","sidebarTitle":"pass-at-k","href":"/recipes/02-measure/pass-at-k"},{"title":"A public benchmark into the measurement","sidebarTitle":"public-benchmark","href":"/recipes/02-measure/public-benchmark"},{"title":"Reward hacking, caught three times","sidebarTitle":"reward-hacking","href":"/recipes/02-measure/reward-hacking"},{"title":"Safety evals for a tool-using agent","sidebarTitle":"safety-evals","href":"/recipes/02-measure/safety-evals"},{"title":"Safety evals for a marketplace agent","sidebarTitle":"safety-evals-marketplace","href":"/recipes/02-measure/safety-evals-marketplace"}]},{"group":"Select","pages":[{"title":"Character training from a constitution","sidebarTitle":"character","href":"/recipes/03-select/character"},{"title":"Synthetic RL data for Prime Intellect","sidebarTitle":"prime-intellect-rl","href":"/recipes/03-select/prime-intellect-rl"},{"title":"One row, six training targets","sidebarTitle":"schema","href":"/recipes/03-select/schema"}]},{"group":"Train","pages":[{"title":"Train your own decision model router","sidebarTitle":"decision-router","href":"/recipes/04-train/decision-router"},{"title":"DPO on Modal, with the dashboard watching","sidebarTitle":"dpo","href":"/recipes/04-train/dpo"},{"title":"Train on Fireworks, serve on Fireworks, prove it through the same API","sidebarTitle":"fireworks","href":"/recipes/04-train/fireworks"},{"title":"GRPO on Modal, with the dashboard watching","sidebarTitle":"grpo","href":"/recipes/04-train/grpo"},{"title":"The hosted loop: push, train, serve, call","sidebarTitle":"hosted-loop","href":"/recipes/04-train/hosted-loop"},{"title":"Identity dataset generator","sidebarTitle":"identity","href":"/recipes/04-train/identity"},{"title":"Train a model router","sidebarTitle":"model-router","href":"/recipes/04-train/model-router"},{"title":"ParseBench: hill-climb an open document-parsing agent","sidebarTitle":"parsebench","href":"/recipes/04-train/parsebench"},{"title":"GRPO, OPSD and OPD on prime-rl, on Modal, with your keys","sidebarTitle":"prime-rl","href":"/recipes/04-train/prime-rl"},{"title":"Prompt-injection classifier","sidebarTitle":"prompt-injection-classifier","href":"/recipes/04-train/prompt-injection-classifier"},{"title":"Report a run so a person can decide","sidebarTitle":"report-run","href":"/recipes/04-train/report-run"},{"title":"Resist a planted instruction","sidebarTitle":"resist-planted-instruction","href":"/recipes/04-train/resist-planted-instruction"},{"title":"SFT on your own GPU, from the course's own export","sidebarTitle":"sft","href":"/recipes/04-train/sft"},{"title":"Text-to-SQL: hill-climb a model on your schema with a verifier as the reward","sidebarTitle":"text-to-sql","href":"/recipes/04-train/text-to-sql"}]},{"group":"Export","pages":[{"title":"Serve a trained adapter on Amazon Bedrock","sidebarTitle":"bedrock-import","href":"/recipes/05-export/bedrock-import"},{"title":"Hugging Face, both directions","sidebarTitle":"hugging-face","href":"/recipes/05-export/hugging-face"}]},{"group":"Papers","pages":[{"title":"Papers","sidebarTitle":"Overview","href":"/recipes/papers/index"},{"title":"Adaptive clip: the upper bound follows how rare a correct answer was","sidebarTitle":"adaptive-clip","href":"/recipes/papers/adaptive-clip"},{"title":"Board writers: pay a note by what it did for the copies that read it","sidebarTitle":"board-writers","href":"/recipes/papers/board-writers"},{"title":"BPCO bounded critic: a critic that can only say a number the reward could be","sidebarTitle":"bpco-bounded-critic","href":"/recipes/papers/bpco-bounded-critic"},{"title":"Context LM: a small model that keeps its own context as a file","sidebarTitle":"context-lm","href":"/recipes/papers/context-lm"},{"title":"Endpoint SFT: keep the two ends of the reasoning trace, drop the middle","sidebarTitle":"endpoint-sft","href":"/recipes/papers/endpoint-sft"},{"title":"Filter metric: phantom advantages under a shaped reward","sidebarTitle":"filter-metric","href":"/recipes/papers/filter-metric"},{"title":"FlashREINFORCE: one stale rollout per prompt, corrected, gated, length-normalized","sidebarTitle":"flash-reinforce","href":"/recipes/papers/flash-reinforce"},{"title":"GMTS: rank tokens by entropy times advantage, not by entropy alone","sidebarTitle":"gmts-token-select","href":"/recipes/papers/gmts-token-select"},{"title":"Harness and weights: both levers against either alone","sidebarTitle":"harness-and-weights","href":"/recipes/papers/harness-and-weights"},{"title":"Hint distillation: imitate what passed, correct what failed with a hint","sidebarTitle":"hint-distill","href":"/recipes/papers/hint-distill"},{"title":"Message board: teach copies of a model to read each other's notes","sidebarTitle":"message-board","href":"/recipes/papers/message-board"},{"title":"Meta-Harness: search over the harness, gate on held-out tasks and models","sidebarTitle":"meta-harness","href":"/recipes/papers/meta-harness"},{"title":"Benchmarking PTGS against Reinforce-Ada and GRPO","sidebarTitle":"ptgs","href":"/recipes/papers/ptgs"},{"title":"Reinforce-Ada: keep sampling a prompt until its group can teach something","sidebarTitle":"reinforce-ada","href":"/recipes/papers/reinforce-ada"},{"title":"SAO single rollout: one rollout per prompt, a critic, and a token band under a stale sampler","sidebarTitle":"sao-single-rollout","href":"/recipes/papers/sao-single-rollout"},{"title":"Talk methods: which training teaches two model copies to talk best","sidebarTitle":"talk-methods","href":"/recipes/papers/talk-methods"},{"title":"Talk to solve: two copies of a model, half the facts each","sidebarTitle":"talk-to-solve","href":"/recipes/papers/talk-to-solve"},{"title":"Team talk: train a model to reason as a team talking to itself","sidebarTitle":"team-talk","href":"/recipes/papers/team-talk"},{"title":"Zero RL on a base model: a rigid format reward costs accuracy","sidebarTitle":"zero-rl-format-reward","href":"/recipes/papers/zero-rl-format-reward"}]},{"group":"Community","pages":[{"title":"Community recipes","sidebarTitle":"Overview","href":"/recipes/community/index"},{"title":"The airline voice agent, and the filter metric that is right in the paper and wrong on my traffic","sidebarTitle":"airline-voice-concise-under-probe-outcome-filter","href":"/recipes/community/airline-voice-concise-under-probe-outcome-filter"},{"title":"Can the judge be trusted?","sidebarTitle":"can-the-judge-be-trusted","href":"/recipes/community/can-the-judge-be-trusted"},{"title":"Can the markers be trusted?","sidebarTitle":"can-the-markers-be-trusted","href":"/recipes/community/can-the-markers-be-trusted"},{"title":"Fine-tune a Deep Agents reviewer from its LangSmith traces, with and without While","sidebarTitle":"deepagents-review-four-arms","href":"/recipes/community/deepagents-review-four-arms"},{"title":"Force the branch, or your policy marker is scoring the agent's mood","sidebarTitle":"force-the-branch","href":"/recipes/community/force-the-branch"},{"title":"GRPO group size, with the budget held still","sidebarTitle":"grpo-group-size-at-fixed-budget","href":"/recipes/community/grpo-group-size-at-fixed-budget"},{"title":"Two hosted runs on the same rows, and the warning only one of them gives you","sidebarTitle":"hosted-grpo-vs-sft","href":"/recipes/community/hosted-grpo-vs-sft"},{"title":"How much contamination survives `decontaminate()`?","sidebarTitle":"how-much-contamination-survives","href":"/recipes/community/how-much-contamination-survives"},{"title":"The selector is the alignment step: teaching an agent its identity is what makes it leak it","sidebarTitle":"identity-spec-no-unasked-maker-aas","href":"/recipes/community/identity-spec-no-unasked-maker-aas"},{"title":"Does a reasoning model learn to write for its judge?","sidebarTitle":"judge-aware-hacking","href":"/recipes/community/judge-aware-hacking"},{"title":"Process reward or outcome reward: does the ten-point gap survive?","sidebarTitle":"process-vs-outcome-reward","href":"/recipes/community/process-vs-outcome-reward"},{"title":"One entry point, or your before/after measures the SDK","sidebarTitle":"same-entrypoint-before-after","href":"/recipes/community/same-entrypoint-before-after"},{"title":"The step the course skips","sidebarTitle":"the-step-the-course-skips","href":"/recipes/community/the-step-the-course-skips"},{"title":"What TRL does with the loss mask","sidebarTitle":"what-trl-does-with-the-loss-mask","href":"/recipes/community/what-trl-does-with-the-loss-mask"},{"title":"Which half of my agent's traffic can a 1.5B model take over?","sidebarTitle":"which-half-can-a-small-model-take","href":"/recipes/community/which-half-can-a-small-model-take"},{"title":"Who protects the held-out set: the ids, or the text?","sidebarTitle":"who-protects-the-holdout","href":"/recipes/community/who-protects-the-holdout"}]}]},{"group":"Reference","pages":[{"title":"Constitution","sidebarTitle":"Constitution","href":"/reference/constitution"},{"title":"Reference overview","sidebarTitle":"Overview","href":"/reference/overview"},{"title":"The five calls","sidebarTitle":"Five calls","href":"/reference/five-calls"},{"title":"Rows: the contract every measurement reads","sidebarTitle":"Rows","href":"/reference/rows"},{"title":"The harness","sidebarTitle":"Harness","href":"/reference/harness"},{"title":"The methods, in symbols","sidebarTitle":"Methods","href":"/reference/methods"},{"title":"Style: how the language reads","sidebarTitle":"Style","href":"/reference/style"},{"title":"Design: how the docs look","sidebarTitle":"Design","href":"/reference/design"},{"title":"What to run","sidebarTitle":"What to run","href":"/reference/what-to-run"},{"title":"Parameters and output","sidebarTitle":"Parameters","href":"/reference/parameters"},{"title":"Package layout and development","sidebarTitle":"Development","href":"/reference/development"}]}]},{"tab":"Learn","pages":[{"title":"Learn","href":"/learn"}]},{"tab":"Platform","pages":[{"title":"Platform","href":"/platform"}]},{"tab":"API reference","pages":[{"title":"API reference","href":"/api"}]}]},"basePath":"","title":"Character training - While","buildId":"dpl_BLQvU86QFwq3AhPofSTw6q8dGWKm","deploymentId":"dpl_BLQvU86QFwq3AhPofSTw6q8dGWKm","dynamics":["a74be98ba7"],"chunks":["zoom-image-lightbox-CLQdF-Ar.js","lightbox-handle-r17bVUz3.js"]}