Operator path

Capture → curate → train on your Mac

Persona FT is optional and local. Cloud chat models stay the default; you add a small Ollama adapter when you want the same inner-life stack with a voice that matches your instance.

/persona keep Dashboard Persona FT furoshiki persona-ft cycle

Persona fine-tuning — your instance’s voice in the weights

Furoshiki already shapes personality at runtime — identity shell, emotions, needs, memory retrieval, trait observations, and situational roles all land in the prompt every turn. Persona FT is the complementary track: a local LoRA style adapter trained on real exchanges so the base model’s default prose habits bend toward how this Furoshiki actually talks on Telegram. Facts, user profile, mood, and RAG stay out of the weights; they continue to flow through SQLite, Chroma, and prompt assembly.

Design split. Prompt path = “what to know and how to behave right now.” LoRA path = “how sentences tend to sound when the situation is already framed.” Neither replaces the other; the goal is a voice that feels continuous with months of lived interaction without freezing knowledge into a checkpoint.

Goal

Build a per-instance conversational style model that operators can refresh as new gold replies accumulate. The system should:

Where it fits in Furoshiki

Runtime personality (every message)

  • identity_shell — non-negotiable boundaries
  • Emotions, needs, behavioral cues, memory blocks
  • record_personality_observation → trait aggregation → directives
  • personality_roles — situational band-1 masks
  • user_facts, commitments, operator corrections

Updates continuously; no GPU required.

Persona FT (periodic, local)

  • persona_ft_examples queue (pending → promoted → used)
  • Export → MLX QLoRA on Apple Silicon
  • Eval vs champion/ adapter
  • Publish Modelfile to Ollama

Improves phrasing priors; does not store new facts.

Over months, the prompt layer tells the model what matters today; the LoRA layer makes the same instructions sound more like the relationship you have already built — shorter clauses, warmth, directness, humor timing — without duplicating Chroma into weights.

End-to-end process

CAPTURE  Telegram /persona keep · /persona capture on
         │   Post-conversation LLM mining (source=automatic, quality-gated)
         │   Linked exports from personality tool logs + curated session logs
         ▼
CURATE   Dashboard Persona FT or CLI pending/promote/reject/edit
         │   Rows: situation + user line + assistant gold (+ trait / style note)
         ▼
EXPORT   furoshiki persona-ft export → cycles/<id>/data/{train,valid,test}.jsonl
         │   Chat template: fixed system prompt + structured user brief + assistant reply
         ▼
TRAIN    mlx_lm LoRA on base (default: Llama 3.2 3B Instruct 4-bit)
         │   Small-data hyperparams (low LR, ~200 iters, rank 8)
         ▼
EVAL     Test loss vs champion; optional commitment quotes as human gold
         ▼
PUBLISH  Fuse adapter → Ollama tag (default furoshiki-persona)
         │   Promoted rows → status used for this cycle
         ▼
RUN      Point a model tier at ollama::furoshiki-persona:latest
         Full prompt pipeline unchanged — adapter only affects token sampling.

Capture sources

SourceHow it enters the queueTypical status
Telegram /persona keep [trait] on a reply; /persona capture on auto-queues each turn until off pending → operator promotes
Post-conversation mining After quiet-window reflection, rubric nominates high-signal style exemplars (confidence + optional authentic/grounded floors) pending, source=automatic
Export-time joins record_personality_observation tool log matched to session logs; structured self_observations with curations; curations.jsonl overrides Merged into JSONL at export (not always a DB row)
Eval / test Honored behavioral_commitments with quoted gold in evaluation_notes test.jsonl — not trained

What each training example looks like

Examples are mlx-lm chat JSONL triples. The system message is a fixed persona training prompt ($FUROSHIKI_DATA/persona_ft/system_prompt.txt, must match Ollama SYSTEM after publish). The user message is a structured brief — situation, trait focus, optional style note (explicitly “do not recite”), sometimes a behavioral commitment — ending with “Respond in character — one short message.” The assistant target is the real or curated Telegram reply (clipped to a sane length), never the observation prose itself.

Excluded from train targets: quality-scoring rows, anticipation metrics, raw user-fact dumps, emotion meter text, and assistant targets that are only meta-commentary about the model. Training on observation text as the reply teaches the wrong objective.

Curation and lifecycle (persona_ft_examples)

Every accumulating row has a named downstream — no dead-end table:

StatusMeaningExit
pendingCaptured or mined; needs reviewPromote, reject, edit, or delete
promotedApproved for exportIncluded in next export; becomes used after successful publish cycle
rejectedOperator discardedTerminal
usedConsumed by a published cycleHistorical record; new captures needed for next train

Dashboard Persona FT and repair API routes mirror the CLI: bulk promote/reject, inline edit of situation and messages, manual insert for gold pairs. This is the quality gate that keeps small-data LoRA from memorizing one bad evening.

Train, eval, publish

Artifacts

All under $FUROSHIKI_DATA/persona_ft/:

Defaults (operator-tunable)

CLI

CommandRole
furoshiki persona-ft statusWorkdir, cycles, champion summary
furoshiki persona-ft exportBuild JSONL for a cycle
furoshiki persona-ft trainRun mlx_lm LoRA
furoshiki persona-ft evalTest loss vs champion
furoshiki persona-ft publishFuse + ollama create
furoshiki persona-ft cycleexport → train → eval → publish (one shot)
furoshiki persona-ft pending|promote|reject|dropQueue management without dashboard

Prerequisites: Apple Silicon Mac with sufficient unified memory (32 GB comfortable for 3B QLoRA), pip install -r requirements-persona-ft.txt, optional Ollama for publish. Canonical operator doc in the repo: docs/PERSONA-FT.md.

How this improves personality over time

  1. Weeks 1–2 — prompt-only. Trait observations, roles, and memory do the heavy lifting; you capture standout Telegram turns with /persona keep.
  2. First cycle — baseline adapter. After ~dozens of promoted pairs, export + train establishes a champion that tracks your curated voice.
  3. Ongoing — dual feedback. Runtime systems record what felt authentic (observations, commitments honored); Persona FT records how it was said (assistant strings). Mining adds candidates; you still promote manually.
  4. Periodic refresh. Each cycle retrains from base + full promoted corpus (plus export joins), compares eval loss, and swaps Ollama only when the candidate wins — so regression is detectable.
  5. Composability. When you enable the persona tier for live chat, the model still receives the same behavioral cues, memory, and corrections; users perceive tighter alignment because phrasing matches history, not because facts were baked into weights.

Expected outcomes