# VLM Training Runbook
The captchaforge VLM solver currently relies on whichever vision
model the operator runs in Ollama. We want to ship a bundled
captchaforge-vlm model that out-performs generic models on the
specific tasks captchaforge dispatches (image-grid selection,
slider gap detection, rotated text, color match).
## Pipeline
```
1. capture 2. label 3. train 4. distill 5. promote
TrainingCorpus → VlmDataset (sha-content-id) → LoRA Qwen2-VL-7B → 7B → 2B → Ollama tag
(live solves) auto_label.py teacher train_lora.py distill.py promote_to_ollama.sh
```
Each stage already has a script in `training/scripts/`. Today none
of them have been run end-to-end, the pipeline plumbing is
complete; the training data + GPU time + model upload are external
to this session.
## Stage 1: Capture
The bench's `TrainingCorpus` opt-in already captures failed solves
into `<corpus-root>/<vendor>/*.jsonl`. Operators running
captchaforge in production with `with_training_corpus(...)` are
already accumulating data.
Bootstrap: run the bench suite against the public test sitekeys
with `--with-network --output corpus-seed`. Each solver failure
emits a sample.
## Stage 2: Label
```bash
python3 training/scripts/auto_label.py \
--corpus /path/to/corpus \
--teacher gpt-4v|claude-3-opus|qwen-vl-72b \
--out training/labeled.jsonl
```
The teacher model labels each captured image with the correct
answer. We use a much larger model than the student we're training.
## Stage 3: Train (LoRA on Qwen2-VL-7B)
```bash
docker build -t captchaforge-training -f training/Dockerfile .
docker run --gpus all -v $(pwd)/training:/work captchaforge-training \
python /work/scripts/train_lora.py \
--base Qwen/Qwen2-VL-7B-Instruct \
--data /work/labeled.jsonl \
--out /work/checkpoints/captchaforge-vlm-7b
```
Compute: one A100 80GB. ~6-12 hours for ~10k labeled samples.
## Stage 4: Distill (7B → 2B)
Smaller model runs comfortably in 8GB VRAM consumer GPUs, keeps
captchaforge usable for the long tail of operators.
```bash
docker run --gpus all -v $(pwd)/training:/work captchaforge-training \
python /work/scripts/distill.py \
--teacher /work/checkpoints/captchaforge-vlm-7b \
--student Qwen/Qwen2-VL-2B-Instruct \
--data /work/labeled.jsonl \
--out /work/checkpoints/captchaforge-vlm-2b
```
## Stage 5: Promote to Ollama
```bash
bash training/scripts/promote_to_ollama.sh \
training/checkpoints/captchaforge-vlm-2b \
captchaforge/captchaforge-vlm:2b
```
Publishes to the captchaforge Ollama namespace. Operators then run:
```bash
ollama pull captchaforge/captchaforge-vlm:2b
captchaforge setup --auto # picks up the new local model
```
## What's blocking GAP-6
This is the only task where I genuinely can't make progress from
this session, it needs:
- A GPU box (no GPU available in the CI containers; A100 / 4090
for real LoRA).
- A teacher-model API key with budget for ~50k label requests
(GPT-4V or Claude).
- Ollama publishing credentials.
The pipeline scripts are in place + verified by `cargo test
--test captchaforge_training_pipeline_exists` (TODO: add this
test).