
现在让大模型干活,常见做法是让它一步步推理、边想边写。方向没错,代价也实在:一次决策动辄几百毫秒,智能体跑一轮下来,时间全花在"想"上面。
有个新路子反着来:不让它生成,只让它挑。CLM(Contrastive Language Models,对比式语言模型)干的就是这个活——把"当前处境"和"能做的动作"各压成一个向量,比一比谁跟谁最般配,直接选分最高的那个。从写字变成打分,速度自然不是一个量级。
这套东西最近刚开源,仓库星标已经涨到 1796,代码、权重、微调脚本全在里头。
训练时,用对比学习把两件事绑到一块:同一个处境下真正发生过的那个动作,向量要挨得近;没发生过的动作,向量一律推远。
用的时候,把当前处境和一堆候选动作各过一遍编码器,做个内积,谁分高就选谁。softmax 出来的那组概率,就是答案分布。
因为是打分而不是续写,它天生带举一反三的本事:一套权重在电脑操作、打游戏、调工具这些完全不同的任务上都能直接用,不用针对每个任务重新调一遍。
from clm import CLMClient, Choice, Noul, Score
client = CLMClient()
r = client.system_one(
state="客户:我的账单被重复扣款了,电话也没人接!",
questions={
"urgency": Noul(instructions="这事儿急不急?"),
"department": Choice(instructions="该转给哪个组?",
criteria={"billing": "扣款、发票、退款",
"technical": "故障、宕机"}),
"frustration": Score(instructions="客户有多上火?",
criteria=["平静", "有点急", "非常生气"]),
},
)
print(r.answers["urgency"].noul) # 0.41022 这句话成立的概率
print(r.answers["department"].choice) # billing
print(r.answers["department"].probabilities) # {'billing': 0.93878, 'technical': 0.06122}
print(r.answers["frustration"].score) # 1.98386 期望等级,0 到 2
print(r.usage.input_tokens, r.latency_ms) # 38 58.1整套东西其实建立在一个更基础的能力上:给一个处境和一堆候选项打分排序。想直接用这一层,也留了入口:
from clm import Engine
engine = Engine(emb_url="http://127.0.0.1:8090/v1/embeddings")
engine.rank("地球上为什么会涨潮?",
["月球的引力。", "植物的光合作用。", "因为地球是圆的。"])
# [{'rank': 1, 'candidate': "月球的引力。", 'prob': 0.997}, ...]pip install contrastive-lm
# 1. 编码器(Qwen3-8B 出向量)
vllm serve Qwen/Qwen3-8B --served-model-name qwen3-8b --runner pooling --max-model-len 2048 --port 8090 &
# 2. CLM 服务,跑在 8700 端口(首次启动会下 75 MB 的参考头)
clm-serveclm-serve 顺带带了个网页版工作台。左边写当前处境,右边加几个带类型的问题,答案分布直接画出来;每次请求还会把对应的 JSON、curl 和 Python 三种写法一并展示,方便照抄。另有一个排序页签,能拿任意一组候选项排个先后,链接还能分享出去。

在电脑操作、游戏、工具调用这几类任务上,CLM-8B 的表现跟对照模型 Jev 基本齐平,延迟却低了最多 9 倍。候选动作越多(比如网页竞速),或者动作在不同处境间反复复用(比如跑酷小游戏),提速越明显。跑酷那套测试就放在仓库里,能自己复现。

更有意思的是拿它当"裁判"。每个任务先采样出好几份候选解法(DeepSWE 用 Opus 5 采样,Terminal-Bench 2.1 用 Fable 5 采样),再让 CLM 或者 Jev 从中挑出最靠谱的那份。在 38 道留出的 DeepSWE 任务和 30 道留出的 Terminal-Bench 2.1 任务上,Jev 面对这种长链条任务根本当不了裁判,成绩还不如 pass@1;CLM 轻量微调之后,两个榜分别做到 81.6% 和 87.6%,刷新了当前最好成绩,同时比 Jev 快 4.1 到 5.7 倍。

关键点在于两个编码器是拆开的,向量能各自缓存、各自复用。智能体每次问的处境在变,可候选动作基本是那老几套,而且经常回访见过的处境。既然头没换,这些向量就不会变,服务启动时干脆像 vLLM 占显存那样,先划出一块地专门放它们。
命中缓存之后,编码器调用、数据拷贝、前向计算三样全省。单张 RTX 4090 上实测的服务端 p50:候选动作 50 个时,全新处境 28.8 毫秒降到 28.1 毫秒;回访过的处境 2.0 毫秒降到 0.7 毫秒。会来回走老路的循环大约快 2.8 倍,一路只见新处境的循环,编码器那份开销省不掉。
后训练那一步还掺了 40% 的问答回放。加了回放,硬负样本 top-1 准确率只从 69% 掉到 68.5%;要是光拿智能体数据训同样多步,直接掉到 56.2%。
那为什么不从一开始就上硬负样本? 在约 10 万道留出题上(每道 1 个正确答案、10 个硬负样本)实测:只做预训练,top-1 有 52.1%;补一段中期训练,升到 69.2%。而一上来就掺硬负样本,前期涨得飞快,到 62.4% 就见顶过拟合了。拔苗助长,说的就是这种情况——同等算力下,两阶段做法高出 7 个百分点。硬负样本适合当精修,不适合当主食。
缩放规律也给了条实用经验:固定算力下,最优头规模几乎随训练 token 数线性增长,比例大约 310 个 token 配 1 个参数。要把验证能力往上推,编码器规模是最划算的那个维度。
下面这几处前人踩过,算是前车之鉴,动手前心里有个数:
想拿自己的数据微调,仓库里有现成脚本和文档,一条命令就能复现 DeepSWE 那组结果。
总的来说,如果你手上的智能体卡在"每次决策都要想半天",这套方案值得一试:装上、起服务、把问题换成带类型的问法,剩下的交给打分。
原文链接:
https://github.com/Contrastive-LM/CLM
仓库地址:
github.com/Contrastive-LM/CLM
更新时间:2026-10-10
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight All Rights Reserved.
Powered By 61893.com 闽ICP备11008920号
闽公网安备35020302034903号