Kev
Hoogtepunten
- Modelgroottes: 0.8B, 4B en 9B modellen, inclusief trainingscode en evaluatiedata.
- Vraagtypen: Ondersteuning voor ja/nee (
noul), meerkeuze (choice) en waarderingen (score) in één enkel verzoek. - Efficiëntie: Vragen delen de inputtekst, maar kunnen elkaar niet lezen.
- Hardware-ondersteuning: Draait op CUDA en Apple Silicon. De 4B en 9B modellen passen in een Mac met 32 GB RAM bij gebruik van bf16 (zie Serving Performance voor details over Mac-prestaties).
- Playground: Een web-interface om eigen inputs te testen en te controleren hoe de volgorde van opties de antwoorden beïnvloedt.
Snelstart
Je hebt Python 3.12+ en uv nodig.
git clone https://github.com/jaredpalmer/kev.git && cd kev
uv sync --extra serve
KEV_DTYPE=bf16 uv run --extra serve python -m kev.serve --run jaredpalmer/kev-4b --port 8009
Dit start Kev-4B lokaal. Bij de eerste run worden de adapter en het basismodel gedownload. De vlag --run accepteert ook een lokale directory met checkpoints of een Hub-revisie, zoals jaredpalmer/kev-4b@qwen3 voor de vorige generatie.
Stuur in een andere terminal een ticket:
curl -s localhost:8009/v1/systemone -H 'content-type: application/json' -d '{
"state": "Shoes arrived two weeks late and in the wrong size. Also I see two charges on my card.",
"model": "kev-latest",
"questions": {
"department": {"type": "choice", "instructions": "Which team should handle this?",
"criteria": {"returns": "Exchanges, refunds, wrong or damaged items",
"shipping": "Delivery status, delays, lost packages",
"billing": "Charges, invoices, payment problems"}},
"escalate": {"type": "noul", "instructions": "Does this need urgent human attention?"},
"frustration": {"type": "score", "instructions": "How frustrated is the customer?",
"criteria": ["Calm", "Frustrated", "Very angry"]}
}}'
Voorbeeldrespons (Kev-4B op Apple M5 in bf16):
{
"model": "kev-latest",
"answers": {
"department": { "type": "choice", "choice": "returns", "confidence": 0.21,
"probabilities": { "returns": 0.47, "shipping": 0.28, "billing": 0.25 } },
"escalate": { "type": "noul", "noul": 0.93 },
"frustration": { "type": "score", "score": 1.44, "confidence": 0.78,
"legend": { "0": "Calm", "1": "Frustrated", "2": "Very angry" },
"probabilities": { "0": 0.00, "1": 0.56, "2": 0.44 } }
},
"usage": { "input_tokens": 101, "output_tokens": 161 },
"latency_ms": 495
}
Het ticket vermeldt een retourzending, een late levering en een facturatieprobleem; de waarschijnlijkheden voor het 'department' weerspiegelen dit. Dit is het voordeel van het terugkrijgen van waarschijnlijkheden in plaats van een enkel label.
Python SDK
De TypeSafe SDK is inbegrepen bij uv sync --extra serve:
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient
client = TypeSafeClient(
api_key="local",
base_url="http://127.0.0.1:8009",
model="kev-latest",
)
response = client.system_one(
state="I was charged twice. Please fix this ASAP.",
questions={
"billing": Noul(instructions="Is this ticket about billing?"),
"tone": Choice(
instructions="What is the customer's tone?",
criteria={"calm": None, "frustrated": None, "angry": None},
),
"urgency": Score(
instructions="How urgent is this ticket?",
criteria=["can wait", "this week", "today"],
),
},
)
print(response.nouls["billing"].noul)
print(response.choices["tone"].choice)
print(response.scores["urgency"].score)
Playground
Terwijl de server draait, kun je in een andere terminal de playground starten (Node 20.9+ vereist):
cd playground
npm install
npm run dev -- -p 3001
Open localhost:3001 om presets te laden en teksten of vragen aan te passen. Gebruik ⌘↵ om het uit te voeren.
- Packed vs separate: Vergelijkt het stellen van alle vragen tegelijk met het stellen van vragen één voor één.
- Permute: Voert een Choice-vraag uit met zes verschillende volgordes van opties.
- Er zijn ook presets voor het testen van vraag-isolatie en fake delimiter tokens.
- Chess demo: Het bord is de input, legale zetten zijn de Choice-opties, en een Score-vraag beoordeelt de positie.
Modellen
Start met Kev-4B. Gebruik Kev-9B wanneer nauwkeurigheid en kalibratie belangrijker zijn dan geheugengebruik. Gebruik Kev-0.8B voor de kleinste footprint. Alle drie zijn gebouwd op Qwen3.5 bases met dezelfde trainingsdata en instellingen.
| Model | Base | Accuracy: Trained Sources | Accuracy: New Sources | Brier: New Sources | Model Card |
|---|---|---|---|---|---|
| Kev-0.8B | Qwen3.5-0.8B-Base | 0.825 / 0.834 | 0.652 / 0.684 | 0.499 / 0.460 | Details |
| Kev-4B | Qwen3.5-4B-Base | 0.872 / 0.871 | 0.797 / 0.837 | 0.299 / 0.255 | Details |
| Kev-9B | Qwen3.5-9B-Base | 0.872 / 0.874 | 0.822 / 0.852 | 0.286 / 0.237 | Details |
| Jev | Hosted | 0.845 / – | 0.857 / – | 0.211 / – | – |
Elke cel is development / test.
- Trained sources: Out-of-sample voorbeelden uit de datasets gebruikt voor training.
- New sources: Datasets en policy-regeltypes waar Kev niet op is getraind.
- Een lagere Brier-score is beter.
Kev-9B loopt 3,5 punten achter op Jev in de development set voor nieuwe bronnen (0.822 vs 0.857), maar scoort 0.852 op de test set (waar Jev niet op is getest). Omdat de trainingsdata van Jev onbekend is, is dit geen gecontroleerde vergelijking van de twee architecturen.
Alle drie de modellen zijn op 2026-09-21 bijgewerkt met een korte tweede trainingsronde op gegenereerde voorbeelden (policy cases met expliciete dag-aantallen en cases zonder beslissende bewijslast). Op de test set verbeterde Kev-9B van 0.837 naar 0.852, Kev-4B van 0.832 naar 0.837, en Kev-0.8B van 0.668 naar 0.684. Vorige gewichten zijn beschikbaar onder revisie v7-base.
Optionele instellingen
Twee instellingen kunnen de waarschijnlijkheden aanpassen zonder het antwoord te veranderen:
KEV_TEMPERATURE=2.0: Schaalt de waarschijnlijkheden. Op nieuwe bronnen vermindert dit de kalibratiefout van Kev-9B van 0.106 naar 0.050 en de zelfverzekerde fouten (foute antwoorden met p ≥ 0.9) van 8.7% naar 4.4%.KEVDATEFACTS=1: Voegt het aantal dagen tussen twee absolute datums in de state toe. Omdat Kev niet betrouwbaar kan aftrekken, helpt dit bij deadline-vragen; Kev-9B stijgt hierdoor van 0.80 naar 0.90 (Jev 0.93).
Vorige generatie (Qwen3) en het prototype
De eerste Kev-familie gebruikte Qwen3 bases. Deze zijn nog steeds beschikbaar en zijn sneller op een Mac, maar worden niet meer ontwikkeld.
| Model | Base | Accuracy: Trained Sources | Accuracy: New Sources | Brier: New Sources | Model Card |
|---|---|---|---|---|---|
| Kev-0.6B (Qwen3) | Qwen3-0.6B-Base | 0.801 / 0.808 | 0.620 / 0.642 | 0.536 / 0.483 | Details |
| Kev-4B (Qwen3) | Qwen3-4B-Base | 0.854 / 0.856 | 0.790 / 0.806 | 0.328 / 0.294 | Details |
| Kev-8B (Qwen3) | Qwen3-8B-Base | 0.863 / 0.870 | 0.796 / 0.780 | 0.337 / 0.327 | Details |
Omdat alleen de base is gewijzigd, is dit een gecontroleerde vergelijking. Op de test set loopt Kev-9B 7,3 punten voor op Kev-8B, Kev-4B 2,9 punten voor op zijn voorganger, en Kev-0.8B 4,8 punten voor op Kev-0.6B.
API
POST /v1/systemone
state is de tekst die geëvalueerd moet worden. Elke vraag heeft instructies en eventueel een set antwoorden.
Request Format:
{
"state": "…", // string | object | array
"model": "kev-latest",
"questions": {
"<id>": { // ID wordt niet door het model gezien
"type": "noul" | "choice" | "score",
"instructions": "…", // string | object | array
"criteria": … // noul: {true?, false?} | choice: {option: description|null} | score: [level, …]
}
}
}
Vraagtypen en criteria:
| Type | Criteria | Antwoord |
|---|---|---|
| noul | Optionele beschrijvingen voor true en false | noul: waarschijnlijkheid van 'ja' |
| choice | 1–255 optienamen, elk met een beschrijving of null | choice: meest waarschijnlijke optie; waarschijnlijkheden en confidence |
| score | 2–255 beschrijvingen, geordend van laag naar hoog | score: gemiddelde level-index (startend bij 0); legend, waarschijnlijkheden en confidence |
- Confidence: Voor Choice met K > 1 opties is confidence
(p_max − 1/K) / (1 − 1/K). Score confidence meet hoe dicht de distributie bij het meest waarschijnlijke niveau ligt. - Objecten en arrays worden omgezet naar gelabelde tekst. Ongeldige verzoeken retourneren een 422-fout.
Overige endpoints
| Methode | Pad | Doel |
|---|---|---|
| GET | /v1/models | Informatie over geladen modellen en checkpoints |
| POST | /v1/systemone/permute | Eén Choice-vraag uitvoeren met verschillende optie-volgordes |
| POST | /v1/systemone/separate | Elke vraag in een eigen forward pass uitvoeren |
De server bindt aan 127.0.0.1 en heeft geen authenticatie. Gebruik dit alleen lokaal.
Hoe het werkt
Elk checkpoint bestaat uit een rank-16 LoRA adapter en een kleine pointer head op een Qwen base model.
Architectuur (Attention-only/Qwen3)
De state en vragen gaan in één token-sequentie: <state> …state… <q> instructies <opt> optie 1 </opt> <opt> optie 2 </opt> … <decide>
De attention mask zorgt ervoor dat een token de state en zijn eigen vraag kan lezen, maar niet andere vragen of toekomstige tokens. Positie-ID's herstarten direct na de state, zodat het model de state één keer verwerkt en elke vraag onafhankelijk beantwoordt.
Architectuur (Qwen3.5)
Qwen3.5 combineert attention layers met Gated DeltaNet layers (recurrent), die attention masks negeren. Hier wordt elke vraag als een eigen rij verwerkt: de state gevolgd door die specifieke vraag. De server berekent de state één keer en hergebruikt de cache voor elke rij.
Pointer Head & Training
De pointer head scoort de hidden state van elke </opt> tegen de hidden state van de <decide> token. Een softmax zet deze scores om in waarschijnlijkheden. Training gebeurt via cross-entropy op het correcte antwoord. De adapter en head worden samen getraind; de rest van de base weights blijft gefixeerd.
Serving Performance
- CUDA: Met
flash-linear-attention(standaard in Modal image) duurt een verzoek met vijf vragen enkele tientallen milliseconden op een H100. - Apple Silicon: Er zijn geen fast kernels voor DeltaNet layers, dus PyTorch gebruikt reference code.
Median model time (bf16 op M5, 5 vragen, ~230-token state):
| Model | Tijd | Vorige generatie (Qwen3) |
|---|---|---|
| Kev-0.8B | 329 ms | 123 ms |
| Kev-4B | 779 ms | 174 ms |
| Kev-9B | ~2 s | ~300 ms |
Voor lage latentie op Mac worden de Qwen3-modellen aangeraden. Een MLX-backend voor Qwen3.5 is gepland.
Training
De released modellen gebruiken decision-v7: 10.000 voorbeelden uit tien publieke datasets, 896 gegenereerde policy-voorbeelden en 1.680 voorbeelden uit 60 gegenereerde regelstructuren.
Trainingscommando's:
- Sanity run (~1 min):
uv run python -m kev.train --npersource 40 --accum 4 --out runs/smoke
- Kev-0.8B (~20 min op H100):
uv run python -m kev.train --suite evals/v7/decision-v7 --base Qwen/Qwen3.5-0.8B-Base --baserevision dc7cdfe2ee4154fa7e30f5b51ca41bfa40174e68 --epochs 2 --lr 1e-4 --batch 8 --dtype bf16 --pnone_pair 0.25 --device cuda --out runs/kev-0.8b
- Kev-4B (~1 uur op H100):
uv run python -m kev.train --suite evals/v7/decision-v7 --base Qwen/Qwen3.5-4B-Base --baserevision 1001bb4d826a52d1f399e183466143f4da7b741b --epochs 2 --lr 5e-5 --batch 4 --accum 2 --dtype bf16 --checkpointing 1 --pnone_pair 0.25 --device cuda --out runs/kev-4b
Fine-tuning op eigen data
Wanneer vragen afwijken (eigen categorieën, andere taal), helpt een korte fine-tune op enkele honderden gelabelde voorbeelden vaak beter dan prompt-aanpassingen.
Gebruik een JSONL-bestand (één verzoek per regel) met labels:
{"state": {"subject": "Charged twice", "body": "I see two charges for order #4411. Please refund one."},
"questions": {
"team": {"type": "choice", "instructions": "Which team should handle this ticket?", "criteria": {"billing": "Payments and refunds", "shipping": "Delivery problems", "access": "Login and account access"}, "label": "billing"},
"angry": {"type": "noul", "instructions": "Is the customer angry?", "label": false},
"priority": {"type": "score", "instructions": "How urgent is this ticket?", "criteria": ["low", "normal", "high"], "label": 1}}}
Start vanuit een bestaand checkpoint met --init_from om bestaande kennis te behouden:
uv run python -m kev.train --data train.jsonl --base Qwen/Qwen3.5-4B-Base --init_from jaredpalmer/kev-4b --epochs 2 --lr 2e-5 --batch 1 --accum 8 --dtype bf16 --checkpointing 1 --device cuda --out runs/mine
Modal
Voor cloud-training via H100:
uv run modal token new
KEV_GPU=T4 uv run modal run modal_app.py::smoke
uv run modal deploy modal_app.py
uv run modal run modal_app.py::study --suite evals/v7/decision-v7 --plan experiments/v7-final.json --name my-study --transfer evals/v4/transfer-v4 --budget 30 --timeout 7200
uv run modal run modal_app.py::pull --name my-study
Evaluatie
Evaluatiedata onder evals/ is bevroren.
Benchmark commando's:
- Out-of-domain:
uv run python -m kev.benchmark --run jaredpalmer/kev-4b --suite evals/v4/transfer-v4 --out runs/my-eval - In-distribution:
uv run python -m kev.benchmark --run jaredpalmer/kev-4b --suite evals/v7/decision-v7 --out runs/my-eval-id - Remote endpoint:
uv run python -m kev.benchmark --remote http://127.0.0.1:8009 --suite evals/v4/transfer-v4 --out runs/my-remote
Beperkingen
- Over-confidence: Ruwe waarschijnlijkheden zijn vaak te zelfverzekerd op nieuwe bronnen.
- Date arithmetic: Fine-tuning kan de base model verslechteren in specifieke taken zoals datumberekeningen (opgelost met
KEVDATEFACTS=1). - Snelheid: Modellen zijn momenteel traag op Apple Silicon.
- Optie-volgorde: Het wijzigen van de volgorde van opties kan het antwoord beïnvloeden.
- Context window: Training is beperkt tot 384 state tokens; serving staat tot 8.192 tokens toe, maar dit is niet gedekt in de training.
- Concurrency: De server verwerkt één verzoek per keer.
Ontwikkeling & Probleemoplossing
Tests uitvoeren:
- Zonder weights/server:
uv run --extra serve python -m pytest tests/testunit.py tests/testresearch.py -q - Tegen draaiende server:
KEVBASEURL=http://127.0.0.1:8009 uv run --extra serve python -m pytest tests/test_api.py -q
Troubleshooting:
- MPS Memory: Draai slechts één job tegelijk. Schakel
outputhiddenstatesuit. - Playground buttons: Gebruik
localhost:3001. Andere hosts vereisen een aanpassing inallowedDevOriginsinplayground/next.config.ts. - Dataset loading: Gebruik
legacy-datasets/banking77als scripts niet meer worden ondersteund.
Auteurs & Licentie
Auteur: Jared Palmer (@jaredpalmer). Gebouwd met Devin.
Licentie: Apache-2.0. De Qwen3 en Qwen3.5 base modellen zijn eveneens Apache-2.0. Trainingsdatasets hebben hun eigen licenties.
Groetjes,