# Jawbreaker Training This folder contains the OpenBMB MiniCPM fine-tuning path for Jawbreaker. The goal is not to make a general scam chatbot. The target behavior is narrow: 1. Read one suspicious message. 2. Return valid JSON matching Jawbreaker's schema. 3. Avoid dangerous false negatives. 4. Avoid calling normal messages dangerous. 5. Recommend a safe verification path. ## Generate Data The generated data is synthetic and deterministic. It avoids private messages and keeps links on `.example` domains. ```bash python3 training/generate_jawbreaker_data.py ``` Default outputs: - `training/data/train.jsonl` - `training/data/dev.jsonl` - `training/data/test.jsonl` - `eval/generated_eval.jsonl` ## Evaluate Baseline MiniCPM ```bash python3 eval/run_eval.py \ --backend transformers \ --model-id openbmb/MiniCPM4.1-8B \ --trust-remote-code \ --dataset eval/generated_eval.jsonl \ --predictions-out eval/predictions/minicpm4_1_8b_generated.jsonl \ --json-out eval/reports/minicpm4_1_8b_generated.json ``` For quick smoke tests: ```bash python3 eval/run_eval.py \ --backend transformers \ --model-id openbmb/MiniCPM4.1-8B \ --trust-remote-code \ --dataset eval/generated_eval.jsonl \ --limit 10 ``` ## Fine-Tune LoRA Install training-only dependencies outside the Space runtime: ```bash pip install -r requirements-train.txt ``` Run a short LoRA training pass: ```bash python3 training/train_lora.py \ --model-id openbmb/MiniCPM4.1-8B \ --train-file training/data/train.jsonl \ --dev-file training/data/dev.jsonl \ --output-dir training/output/jawbreaker-minicpm-lora ``` ## Fine-Tune on Modal Modal is the preferred training path because the hackathon gives Modal credits and MiniCPM4.1-8B should be trained on real GPU hardware, not inside the Hugging Face Space runtime. One-time local setup: ```bash pip install modal modal setup ``` Create a Modal secret named `huggingface-secret` with an `HF_TOKEN` value that can read the base model and push to your Hugging Face account: ```bash modal secret create huggingface-secret HF_TOKEN=hf_... ``` Run training on Modal: ```bash modal run training/modal_train.py ``` The current shipped adapter was trained with the v3 contrastive data: ```bash python3 training/generate_v3_data.py modal run training/modal_train.py \ --train-file training/data/train_v3.jsonl \ --dev-file training/data/dev_v3.jsonl \ --output-name jawbreaker-minicpm-lora-v3 \ --epochs 3 \ --learning-rate 7e-5 \ --warmup-ratio 0.05 \ --weight-decay 0.01 \ --lr-scheduler-type cosine \ --max-length 768 \ --batch-size 1 \ --grad-accum 16 \ --lora-r 32 \ --lora-alpha 64 \ --lora-dropout 0.05 \ --push-to-hub \ --hub-model-id build-small-hackathon/jawbreaker-minicpm-lora-v3 ``` Publish a new adapter for the Well-Tuned badge only after eval says it is better: ```bash modal run training/modal_train.py \ --push-to-hub \ --hub-model-id build-small-hackathon/jawbreaker-minicpm-lora ``` The Modal job writes checkpoints to the `jawbreaker-training` Modal volume under `/outputs`. To publish the adapter for the Well-Tuned badge: ```bash python3 training/train_lora.py \ --model-id openbmb/MiniCPM4.1-8B \ --output-dir training/output/jawbreaker-minicpm-lora \ --push-to-hub \ --hub-model-id build-small-hackathon/jawbreaker-minicpm-lora ``` ## Deployment Decision Rule Use the fine-tuned adapter only if it beats the base model on: - valid JSON rate - zero dangerous-as-safe misses - lower false alarms on safe messages - safe action compliance - acceptable Space latency If the adapter improves JSON but hurts safety, do not deploy it. Current decision: ship `build-small-hackathon/jawbreaker-minicpm5-1b-lora-v8` on `openbmb/MiniCPM5-1B`. - 632-case hard guarded eval: `579/632` risk accuracy (`91.61%`), no dangerous undercalls, no safe-as-dangerous-or-suspicious misses, no unsafe action violations, no invalid predictions, no model errors. - Earlier 1B v4 evals remain useful as comparison evidence: 394-case hard guarded eval at `379/394` risk accuracy (`96.19%`) and 320-case hard guarded eval at `310/320` risk accuracy (`96.88%`), both with no dangerous undercalls. - Earlier 8B v3 evals remain useful as comparison evidence, but the 1B v8 adapter is the final deployed model. ## v7 Calibration Experiment `training/generate_v7_data.py` is a candidate follow-up, not the production adapter yet. It uses sanitized public-pattern examples and hand-written hard negatives to address the fresh 2026 eval gaps without training on the fresh held-out eval rows. Generate the v7 data: ```bash python3 training/generate_v7_data.py ``` Current generated sizes: - `training/data/train_v7.jsonl`: 2,192 SFT rows - `training/data/dev_v7.jsonl`: 498 SFT rows - `eval/hard_v7_eval.jsonl`: 558 held-out hard cases Train the candidate adapter on Modal: ```bash modal run training/modal_train.py \ --model-id openbmb/MiniCPM5-1B \ --train-file training/data/train_v7.jsonl \ --dev-file training/data/dev_v7.jsonl \ --output-name jawbreaker-minicpm5-1b-lora-v7 \ --epochs 2 \ --learning-rate 5e-5 \ --warmup-ratio 0.05 \ --weight-decay 0.01 \ --lr-scheduler-type cosine \ --max-length 768 \ --batch-size 1 \ --grad-accum 16 \ --lora-r 32 \ --lora-alpha 64 \ --lora-dropout 0.05 \ --push-to-hub \ --hub-model-id build-small-hackathon/jawbreaker-minicpm5-1b-lora-v7 ``` Do not replace v4 unless v7 improves fresh-pattern accuracy without introducing dangerous undercalls, invalid JSON, or a higher safe false-positive rate. ## v8 Failure-Driven Calibration Experiment `training/generate_v8_data.py` is a narrower follow-up to v7. It targets the two v7 fresh-eval failure modes: - wrong-number crypto / gold / trading grooming was sometimes softened to `needs_check` - ordinary family, school, and pharmacy logistics were sometimes over-called The v8 data keeps the older hard anchors, adds contrastive wrong-number examples, and does not train on exact `fresh_2026_scam_eval.jsonl` rows. Generate the v8 data: ```bash python3 training/generate_v8_data.py ``` Current generated sizes: - `training/data/train_v8.jsonl`: 2,488 SFT rows - `training/data/dev_v8.jsonl`: 572 SFT rows - `eval/hard_v8_eval.jsonl`: 632 held-out hard cases Train the candidate adapter on Modal: ```bash modal run training/modal_train.py \ --model-id openbmb/MiniCPM5-1B \ --train-file training/data/train_v8.jsonl \ --dev-file training/data/dev_v8.jsonl \ --output-name jawbreaker-minicpm5-1b-lora-v8 \ --epochs 1.5 \ --learning-rate 4e-5 \ --warmup-ratio 0.05 \ --weight-decay 0.01 \ --lr-scheduler-type cosine \ --max-length 768 \ --batch-size 1 \ --grad-accum 16 \ --lora-r 32 \ --lora-alpha 64 \ --lora-dropout 0.05 \ --push-to-hub \ --hub-model-id build-small-hackathon/jawbreaker-minicpm5-1b-lora-v8 ``` Promotion rule: 1. Fresh 2026 held-out eval must have `dangerous_as_safe=0`, `dangerous_as_needs_check=0`, `invalid_predictions=0`, and `unsafe_action_violations=0`. 2. Hard v8 eval must keep zero dangerous undercalls and valid JSON. 3. Safe false positives must not materially worsen versus v4/v7. Promotion result: - Fresh 2026 guarded eval after guard calibration: `92/100` risk accuracy, `0` dangerous-as-safe, `0` dangerous-as-needs-check, `0` safe-as-dangerous-or-suspicious, `0` unsafe action violations, `0` invalid predictions, `0` model errors. - Hard v8 guarded eval after guard calibration: `579/632` risk accuracy (`91.61%`), `0` dangerous-as-safe, `0` dangerous-as-needs-check, `0` safe-as-dangerous-or-suspicious, `0` unsafe action violations, `0` invalid predictions, `0` model errors.