{"slug": "contrastive-language-models-a-fast-generalizable-system-one-model", "title": "Contrastive Language Models: A Fast, Generalizable System One Model", "summary": "Contrastive Language Models (CLMs) introduced CLM-8B, an 8-billion-parameter System One model trained with a contrastive learning objective that connects states and actions, pre-trained on 60M Nemotron Q&A pairs, mid-trained on 30M synthetic hard negatives, and post-trained on 1M agentic trajectories. CLM-8B performs on par with Jev across computer-use, gaming and tool-calling tasks at up to 9× lower latency, and with lightweight fine-tuning sets new state-of-the-art verifier scores of 87.6% on Terminal-Bench 2.1 and 81.6% on DeepSWE. The model disaggregates states and actions so their embeddings are cached and reused independently, and is served via a pip-installable package (pip install contrastive-lm) behind a TypeSafe-compatible API.", "body_md": "*A System One Model for Fast and Generalizable Decision-Making*\n\n| 📄 [**Blog**](https://contrastive-lm.notion.site) | 🗣️ [** Discord**](https://discord.gg/5dAQEDJBs) | 🤗 [** Data & Models**](https://huggingface.co/Contrastive-LM) | 📚 [** API Reference**](#api-reference) | 🛠️ [** Fine-Tuning Tutorial**](#fine-tuning-clm-on-your-own-data) |\n\n🔥 **Contrastive Language Models (CLMs)** are a new class of **System One\nmodel** trained with a **contrastive learning** objective that connects\n**states and actions**. This repo serves **CLM-8B** behind a\nTypeSafe-compatible API.\n\n- **CLM-8B** is pre-trained on**60M Nemotron Q&A pairs** , mid-trained on**30M synthetic hard negatives** , and post-trained on**1M agentic\ntrajectories** .\n- It performs on par with **Jev** across computer-use, gaming and tool-calling\ntasks with up to**9× lower latency** . With lightweight fine-tuning it sets a\nnew SOTA as a verifier on agentic coding benchmarks:**Terminal-Bench 2.1\n(87.6%)** and**DeepSWE (81.6%)** .\n- **States and actions are disaggregated** , so their embeddings are cached and\nreused independently, which makes training and serving cheap and blazing fast!\n\nWe invite the community to plug it into their own agents and benchmarks!\n\n```\npip install contrastive-lm\n```\n\nTo install the latest from a clone:\n\n```\npip install -e .\n# 1. encoder (Qwen3-8B embeddings)\nvllm serve Qwen/Qwen3-8B --served-model-name qwen3-8b --runner pooling --max-model-len 2048 --port 8090 &\n\n# 2. CLM API on :8700 (downloads the 75 MB reference head on first run)\nclm-serve\n```\n\nStates longer than 2048 tokens are truncated. For longer states, raise both limits\ntogether, e.g. `--max-model-len 8192` on `vllm serve` and `clm-serve --max-tokens 8192`\n(needs more GPU memory).\n\n``` python\nfrom clm import CLMClient, Choice, Noul, Score\n\nclient = CLMClient()                          # CLM_BASE_URL (default http://127.0.0.1:8700), CLM_API_KEY\nr = client.system_one(\n    state=\"Customer: my invoice was charged twice and nobody answers the phone!\",\n    questions={\n        \"urgency\": Noul(instructions=\"Is this urgent?\"),\n        \"department\": Choice(instructions=\"Which team should handle this?\",\n                             criteria={\"billing\": \"Charges, invoices, refunds\",\n                                       \"technical\": \"Bugs and outages\"}),\n        \"frustration\": Score(instructions=\"How frustrated is the customer?\",\n                             criteria=[\"Calm\", \"Frustrated\", \"Very angry\"]),\n    },\n)\nprint(r.answers[\"urgency\"].noul)                # 0.41022     probability the statement is true\nprint(r.answers[\"department\"].choice)           # billing\nprint(r.answers[\"department\"].probabilities)    # {'billing': 0.93878, 'technical': 0.06122}\nprint(r.answers[\"frustration\"].score)           # 1.98386     expected level, 0..2\nprint(r.usage.input_tokens, r.latency_ms)       # 38 58.1     (106 tokens on a cold cache: option texts are embedded once)\n```\n\nQuestions may be `Noul` / `Choice` / `Score` objects or plain wire-format\ndicts, so a request written for TypeSafe replays as\n`client.system_one(state, questions)`.\n\n`system_one` is built on one primitive: score a candidate against a state.\nFor free-form candidates (best-of-N answers, tool names, next moves) use the\nin-process engine's `rank`:\n\n``` python\nfrom clm import Engine\n\nengine = Engine(emb_url=\"http://127.0.0.1:8090/v1/embeddings\")     # reference head, downloaded if missing\nengine.rank(\"What causes tides on Earth?\",\n            [\"The Moon's gravitational pull.\", \"Photosynthesis in plants.\", \"Because the Earth is round.\"])\n# [{'rank': 1, 'candidate': \"The Moon's gravitational pull.\", 'prob': 0.997}, ...]\n\nengine.answer(state, questions)      # the same dict the HTTP endpoint returns, no server needed\n```\n\n`clm-serve` also serves a web UI at `/` (`http://localhost:8700/` by default).\nWrite a state, add typed questions, and see CLM's answer distributions; every\nrequest is also shown as JSON, `curl` and Python. A **Rank** tab ranks any\ncandidate set, and links are shareable.\n\n<sub>Captured against a real `clm-serve` (`clm-latest`, Qwen3-8B encoder on one RTX 4090).</sub>\n\nRemote server? `ssh -L 8700:localhost:8700 <host>`. API only: `clm-serve --no-ui`.\n\nAcross **computer-use, gaming and tool-calling tasks**, CLM-8B performs on par\nwith Jev while running **up to 9× faster**. The speedups are largest when the\nnumber of candidate actions is large (WikiRacing) or when actions are reused\nacross states (the T-Rex game). The T-Rex benchmark ships in this repo:\nsee [examples/t_rex](https://github.com/Contrastive-LM/CLM/blob/main/examples/t_rex/README.md).\n\nFor each task we sample several candidate solutions (**Opus 5** for DeepSWE,\n**Fable 5** for Terminal-Bench 2.1), and CLM or Jev acts as the verifier that\npicks the best one. Evaluated on **38 held-out DeepSWE tasks** and **30\nheld-out Terminal-Bench 2.1 tasks**; latency on an H100. Jev fails to serve as\na verifier for these long-horizon tasks, scoring below pass@1. With lightweight\nfine-tuning, CLM reaches SOTA on both (**81.6%** and **87.6%**) while running\n**4.1–5.7× faster than Jev**.\n\nSee [docs/FINETUNING.md](https://github.com/Contrastive-LM/CLM/blob/main/docs/FINETUNING.md).\n\n```\n# reproduce the task-disjoint DeepSWE heldout-38 result (31/38 = 81.6%)\nhf download Contrastive-LM/deepswe-clm-heads-8k --local-dir heads/deepswe\npython evaluation/bon_eval.py --hf-dataset Contrastive-LM/deepswe-clm-embeddings-8k \\\n    --checkpoint heads/deepswe/best_head.pt \\\n    --tasks-file heads/deepswe/heldout_tasks.json --n 4 --window 12\n\n# fine-tune the matching DeepSWE head\npython train/finetune.py --task clm --init-ckpt \"$(clm-download)\" --out-dir runs/deepswe \\\n    --holdout-tasks heads/deepswe/heldout_tasks.json --batch 512\n\n# typed decisions\npython train/finetune.py --task choice --data LocalLLaMA/typed-decisions --workflow all \\\n    --init-ckpt \"$(clm-download)\" --out-dir runs/typed\n```\n\nCLM first trains a **state encoder** and an **action encoder** on a\nlarge-scale dataset with a contrastive objective (InfoNCE), so that each state\nis pulled toward the ground-truth action that was taken and pushed away from\nall others. The two encoders then serve directly as a zero-shot action\nclassifier: at deployment, given the current state and a set of candidate\nactions, CLM scores each action by how well its embedding aligns with the\nstate embedding and selects the highest-scoring action.\n\nThat is what this package serves. A typed question is a state plus a closed\nset of candidate actions (the options and their descriptions); a softmax over\nCLM's scores *is* the answer distribution, and the same call ranks best-of-N\ntrajectories, routes tools, shortlists retrieval pools and answers typed\ndecisions with no per-task setup.\n\n**Architecture, data recipe and scaling laws:**\n\n- Each encoder is a frozen LLM backbone plus a 20M-parameter trainable projection head, so inference is one embedding per fresh text and a dot product per cached candidate.\n- CLM is **pre-trained** on internet-scale Q&A,**mid-trained** on synthetic\nhard negatives,**post-trained** on agentic traces, and can be easily\nfine-tuned on downstream tasks ([data recipe](#data-recipe) ).\n- The InfoNCE loss **decreases predictably as a power law** in training\ncompute, model size and dataset size ([details](#scaling-laws-for-verification) ).\n\n```\nbrowser ──► clm-serve  (CPU, :8700)   GET / (playground)\nclient  ──►                          POST /v1/systemone · GET /v1/models · GET /health\n               │       state head + action head (20M params, hot-reloaded), embedding cache\n               ▼\n          vLLM Qwen3-8B pooling server (GPU, :8090)   /v1/embeddings\n```\n\nCLM is trained with a bidirectional InfoNCE loss. Given a batch of \n\nFor mid-training, the objective is extended with hard negatives. Let\n\nThe test InfoNCE loss \n\nwhere [blog post](https://contrastive-lm.notion.site).\n\n**Data vs. optimal model size.** At a fixed compute budget, each iso-FLOP\ncurve of test loss against head size is well approximated by a parabola in\nlog-parameter space, and its minimum gives the optimal head size for that data\nbudget. The optimum grows almost exactly linearly with the number of training\ntokens, **310 tokens per parameter**.\n\nCLM is trained in three stages, each a progressively harder form of state–action alignment:\n\n1. **Pre-training** on**~60M Nemotron DQA question–answer pairs** , each\nquestion the state and its answer the action. This learns broad semantic\nrepresentations.\n2. **Mid-training** on**~30M synthetic hard negatives** generated by Gemini\n2.5 Flash-Lite: semantically similar but incorrect answers to Nemotron DQA\nquestions, added to the InfoNCE loss as above. This develops fine-grained\ndiscrimination between plausible actions.\n3. **Post-training** on**~1M agent trajectories** from the Agent Data\nProtocol (ADP) dataset, plus terminal traces from Endless-Terminals and\nLiteCoder-Terminal-SFT. Each trajectory step is a state–action pair: the\nagent's current context and the decision it took.\n\n**Replay during post-training.** 40% of the post-training mixture is Nemotron\nDQA replay and 60% agentic trajectories. With replay, Nemotron hard-negative\ntop-1 accuracy only moves from 69% to 68.5%; training on agentic data alone for\nthe same number of agentic steps drops it to 56.2%.\n\n**Why not train on hard negatives from the start?** On ~100K held-out\nquestions (one gold answer, 10 hard negatives each), pre-training alone reaches\n**52.1%** top-1 without seeing a hard negative, and a short mid-training stage\nlifts it to **69.2%**. Training with hard negatives from the start improves\nquickly but peaks at **62.4%** before overfitting, so the two-stage recipe is\n**7 points better** at a fixed budget: hard negatives work best as a refinement\non top of pre-training, not a substitute for it.\n\nThe reference head served as `clm-latest` is\n[Contrastive-LM/CLM-v0.1-8B](https://huggingface.co/Contrastive-LM/CLM-v0.1-8B)\n(`CLM_v0.1-8B.pt`, Qwen3-8B backbone, last-token pooling). Any head in\nthe same checkpoint format — a `torch.save` dict with `state_head` /\n`action_head` state dicts, `logit_scale` and `cfg` (`width`, `depth`,\n`projection_dim`, `activation`, `layernorm`, `residual`) — can be served with\n`--ckpt`; a head only makes sense with the encoder and pooling it was trained\nagainst.\n\n1. **Scaling experiments:** larger backbones, and how far verification\nperformance keeps scaling.\n2. **Vision and multimodal support:** images, video and other modalities for\nrobotics and computer-use tasks.\n3. **Scaling the data recipe:** more pre-training, hard-negative mining and\nagentic post-training.\n\nIf you find CLM useful, please consider citing it:\n\n```\n@misc{kwok2026contrastivelanguagemodels,\n  title={Contrastive Language Models: A System One Model for Fast and Generalizable Decision-Making},\n  author={Jacky Kwok and Hangoo Kang and Tarun Suresh and Jon Saad-Falcon and Marco Pavone and Christopher Ré and Azalia Mirhoseini},\n  year={2026},\n  note={Notion Blog},\n  url={https://contrastive-lm.notion.site}\n}\n```\n\nThe code in this repository is released under the [Apache 2.0 License](https://github.com/Contrastive-LM/CLM/blob/main/LICENSE). The CLM-8B weights are released under Apache 2.0 on [Hugging Face](https://huggingface.co/Contrastive-LM/CLM-v0.1-8B).\n\n```\n.\n├── pyproject.toml               # the clm package (installed editable by requirements.txt)\n├── serve_qwen3_8b.sh            # launch the Qwen3-8B pooling encoder on a GPU\n├── download_head.sh             # fetch the released head (`clm-download` does the same)\n├── assets/                      # logo + the playground screenshot used above\n├── src/clm/                     # inference: the package `clm-serve` and `clm` ship\n│   ├── __init__.py              #   from clm import CLMClient, Noul, Choice, Score, Engine\n│   ├── client.py                #   CLMClient + question / answer types (no torch needed)\n│   ├── schema.py                #   question -> (state text, candidate texts); logits -> Answer\n│   ├── engine.py                #   Engine.answer(...) / Engine.rank(...): the inference engine\n│   ├── heads.py                 #   head architecture, checkpoint load / hot-reload / download\n│   ├── embedder.py              #   /v1/embeddings client + LRU cache of normalised embeddings\n│   ├── cache.py                 #   the reserved vector arena behind --action-cache\n│   ├── server.py                #   FastAPI app, `clm-serve`\n│   └── static/                  #   the playground: index.html + app.css + app.js, no build step\n├── tools/playground_mock.py     # serve the playground without a GPU (fake encoder)\n├── train/                       # fine-tuning\n│   ├── finetune.py              #   trains the projection heads on a frozen encoder\n│   ├── adapters.py              #   dataset adapters: agentic traces, typed decisions\n│   └── embed_utils.py           #   encoder embeddings with the training token recipe\n├── evaluation/bon_eval.py            # unified best-of-N evaluation\n├── preprocessing/hf_embeddings.py    # embedding dir <-> Hugging Face dataset\n├── requirements.txt             # pip install -r requirements.txt  (clm + torch + vLLM + example deps)\n├── examples/                    # CLM vs Jev on the T-Rex runner (examples/t_rex/README.md)\n│   ├── common.py                #   one client for both endpoints: retries, latency, cache\n│   └── t_rex/                   #   Chrome dinosaur game in real time (run.py --model clm|jev)\n└── docs/FINETUNING.md           # the fine-tuning guide\n```\n\nThis branch carries the inference package, the playground, the fine-tuning script,\nthe T-Rex example.\nThe scaling experiments, data pipelines and paper figures\nlive in the research repo's `main` branch.\n\n| field |  | \n|---|---|\n| `state` | string, object or array (objects are rendered as `key: value` text, arrays as`- item` lines; never JSON, the heads are trained on prose) | \n| `model` | `clm-latest` (default),`clm-raw` , or any model from`GET /v1/models` | \n| `questions` | `{id: Question}` , at least one | \n| `temperature` | optional, `(0, 100]` , default 1; divides the logits before the softmax | \n\n| question | required | answer | \n|---|---|---|\n| `noul` | `instructions` ; optional`criteria: {\"true\": …, \"false\": …}` | `{\"noul\": p_true}` | \n| `choice` | `instructions` (the question),`criteria: {option: description}` (each option is embedded as its description, or its key when the description is empty) | `{\"choice\", \"confidence\", \"probabilities\"}` | \n| `score` | `instructions` ,`criteria: [level0, level1, …]` (ordered, ≥2) | `{\"score\", \"confidence\", \"legend\", \"probabilities\"}` | \n\n- `confidence` = top probability minus the mean of the others.\n- `score` = expected level index;`legend` maps indices back to the rubric.\n- `usage.input_tokens` counts encoder tokens spent on cache misses;`billing_units` is the number of questions.\n- Errors: `401` bad key ·`422` malformed request or unknown model ·`502` embedder unreachable.`X-CLM-Latency-Ms` carries the server-side time.\n\nThe same primitive in its plain form: `{\"context\": ..., \"question\": ..., \"answers\": [...]}`\nreturns `{\"model\", \"ranked\": [{\"rank\", \"candidate\", \"prob\"}, ...]}`, best first. The\nstate head sees `context + question`, the action head sees each answer verbatim.\n`CLMClient.rank(context, question, answers)` and `Engine.rank(context, answers, question)`\nare the client and in-process forms.\n\nThe playground (see [above](#playground)), unless `clm-serve --no-ui`. Static\nfiles only; every API route above shadows it.\n\n```\n{\"models\": [{\"name\": \"clm-latest\", \"description\": \"...\", \"release_date\": \"2026-09-19\"},\n            {\"name\": \"clm-raw\", \"description\": \"Ablation: cosine in the raw encoder space\", ...}]}\nclm-serve [--port 8700] [--emb-url http://127.0.0.1:8090/v1/embeddings] [--emb-model qwen3-8b]\n          [--max-tokens 2048] [--ckpt PATH] [--ckpt-dir DIR] [--model NAME=PATH ...] [--device cpu|cuda]\n          [--action-cache 0.02|512MiB|0] [--no-ui] [--cors]\n```\n\n`--ckpt PATH` serves your own head as `clm-latest` (default: the reference\nhead in `~/.cache/clm/`, downloaded if missing); `--ckpt-dir DIR` serves every\n`*.pt` there under its file stem; `--model NAME=PATH` adds one more.\nThe heads run on the GPU when torch sees one, else on the CPU; `--device` (or\n`CLM_DEVICE`) forces one. Checkpoints hot-reload when the file changes. Set `CLM_API_KEY` to require\n`Authorization: Bearer <key>` (the playground has a field for it). Environment\nequivalents: `CLM_PORT`, `CLM_EMB_URL`, `CLM_EMB_MODEL`, `CLM_CKPT`,\n`CLM_DEVICE`, `CLM_ACTION_CACHE`.\n\n`--no-ui` drops the playground and serves the API alone. `--cors` allows browser\nrequests from any origin and is off by default, because an API key otherwise\ntravels in a header any page would then be free to send.\n\nAn agent asks about a changing state but a mostly fixed set of actions, and it\nrevisits states it has already seen. Neither their embeddings nor their\nprojections change while the head does not, so `clm-serve` reserves a slab of\ndevice memory at start-up — the way vLLM claims its KV cache — and keeps them in\nit:\n\n```\n[clm] vector cache 505.0 MB reserved on cuda (215,764x512d + 3,852x4096d)\n```\n\n`--action-cache` takes a fraction of the device (`0.02`, the default), an\nabsolute size (`512MiB`), or `0` to switch it off; `CLM_ACTION_CACHE` does the\nsame. It covers states and actions on every served head, and `clm-raw` in the\nencoder's own space — the two widths are pools carved from the one allocation,\nwhich never grows, so a long-running server cannot drift into an out-of-memory\nkill. Entries are keyed by head and generation, so several heads share the arena\nand a hot-reloaded head stops matching rows its previous weights produced;\neviction is least-recently-used. `GET /health` reports occupancy and hit rate.\n\nA hit skips the encoder call, the host-to-device copy and the head's forward pass. Measured on one RTX 4090, server-side p50, against a fixed action set:\n\n|  | 3 actions | 50 actions | \n|---|---|---|\n| new state every call | 28.6 → 28.0 ms | 28.8 → 28.1 ms | \n| revisited states (20 rooms) | 1.7 → 0.6 ms | 2.0 → 0.7 ms | \n| one repeated state | 1.7 → 0.6 ms | 2.0 → 0.7 ms | \n\nSo a loop that revisits states answers about 2.8x faster, and a loop that never\nrepeats itself pays the encoder either way. A cached vector costs no encoder\ntokens, so `usage.input_tokens` counts only what the encoder actually did.", "url": "https://wpnews.pro/news/contrastive-language-models-a-fast-generalizable-system-one-model", "canonical_source": "https://github.com/Contrastive-LM/CLM", "published_at": "2026-09-25 18:34:21+00:00", "updated_at": "2026-09-25 19:02:40.480067+00:00", "lang": "en", "topics": ["artificial-intelligence", "machine-learning", "large-language-models", "ai-agents", "ai-tools"], "entities": ["Contrastive Language Models", "CLM-8B", "Jev", "Qwen3-8B", "Terminal-Bench 2.1", "DeepSWE", "Nemotron", "TypeSafe"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/contrastive-language-models-a-fast-generalizable-system-one-model", "markdown": "https://wpnews.pro/news/contrastive-language-models-a-fast-generalizable-system-one-model.md", "text": "https://wpnews.pro/news/contrastive-language-models-a-fast-generalizable-system-one-model.txt", "jsonld": "https://wpnews.pro/news/contrastive-language-models-a-fast-generalizable-system-one-model.jsonld"}}