我最近干了件事。
给 XorbitsAI Inference 提了三个 PR,把一个叫 VibeThinker 的小模型注册了进去。
本来想着就是把一个普通模型加到内置注册表,写写 JSON、修修 Bug,平平无奇的日常贡献。
结果我开始翻这个模型的论文。
翻完之后,我整个人都不好了。
一个 3B 参数的小模型,在 AIME26 数学竞赛上拿了 94.3 分。DeepSeek V3.2 是 94.2 分。
你注意看。3B 对 671B。差了 200 多倍。
它不是「接近」大模型。它是已经跟 DeepSeek V3.2、GLM-5、Gemini 3 Pro 坐在同一张桌子上了。
而且更离谱的是,当你加上测试时扩展(CLR),这个数字会飙到 97.1,直接反超 Gemini 3 Pro(91.7),跟 GLM-5(95.8)拉开差距。
今天我打算把这件事从头捋一遍。为什么一个小到可以跑在笔记本上的模型,能在数学推理上干翻几百倍参数量的巨无霸?它的训练方法 SSP 到底做了什么?以及我是怎么把它注册进 Xinference、让所有人一键启动的。
三个 PR,一条完整链路
先说我自己干的活。
PR #5085:模型注册
feat: register VibeThinker 1.5B/3B (transformers + vLLM)
这是核心 PR。在 llm_family.json 里为 VibeThinker 的 1.5B 和 3B 两个规模补全了注册信息。
json
{
"version": 2,
"context_length": 131072,
"model_name": "vibethinker",
"model_lang": ["en", "zh"],
"model_ability": ["chat", "tools"],
"model_description": "VibeThinker is a series of dense reasoning language models...",
"model_specs": [
{
"model_format": "pytorch",
"model_size_in_billions": "1_5",
"model_src": {
"huggingface": {
"quantizations": ["none"],
"model_id": "WeiboAI/VibeThinker-1.5B"
},
"modelscope": {
"quantizations": ["none"],
"model_id": "WeiboAI/VibeThinker-1.5B"
}
}
},
{
"model_format": "pytorch",
"model_size_in_billions": 3,
"model_src": {
"huggingface": {
"quantizations": ["none"],
"model_id": "WeiboAI/VibeThinker-3B"
},
"modelscope": {
"quantizations": ["none"],
"model_id": "WeiboAI/VibeThinker-3B"
}
}
}
],
"architectures": ["Qwen2ForCausalLM"],
"model_type": "qwen2",
"chat_template": "...",
"stop_token_ids": [151643, 151644, 151645],
"tool_parser": "qwen"
}
每个字段的含义:
model_format: "pytorch", 使用原生 PyTorch checkpoint,量化方案为none(全精度推理)model_size_in_billions, 支持字符串"1_5"(1.5B)和整数3(3B)两种表示model_src, 同时声明 HuggingFace 和 ModelScope 两个镜像源,国内用户可以从 ModelScope 加速下载architectures: ["Qwen2ForCausalLM"], 架构声明为 Qwen2,因为这个模型基于 Qwen2 架构做后训练,推理引擎需要用 Qwen2 的方式加载chat_template, Jinja2 格式的对话模板,包含了完整的 tool calling 支持(<tool_call>XML 标签)
同时把 .inference_home/ 加进了 .gitignore,避免本地开发目录被误提交。这个在 Xinference 开发场景下很常见,XINFERENCE_HOME 会缓存模型权重和运行状态,动辄几十 GB。
我为什么要做这个 PR?
VibeThinker 是基于 Qwen2 架构的轻量推理模型,能在单张消费级 GPU(比如 RTX 4090)上跑。加入内置注册表后,用户不需要自己写自定义注册文件,直接:
bash
xinference launch --model-name vibethinker --size-in-billions 3
就能启动。用 vLLM 后端的话,推理速度非常快,单卡就能做高吞吐。
PR #5086:DynamicCache 兼容性修复
fix: handle DynamicCache in get_batch_size_and_seq_len_from_kv_cache when HybridCache is importable
这是三个 PR 里最 tricky 的一个。修了一个 KV Cache 读取的兼容性 Bug,跟新版 transformers 的行为变更有关。
问题根因:
Xinference 里有个函数叫 get_batch_size_and_seq_len_from_kv_cache,它的工作是从模型的 KV Cache 中反推当前的 batch size 和序列长度。这个信息对内存管理、批处理调度非常关键。
原来的代码逻辑是这样的:
python
# === 修复前的代码(简化版) ===
def get_batch_size_and_seq_len_from_kv_cache(kv, xinf_model_obj):
bs_idx = 0 # batch_size 在 shape 的第 0 维
seq_len_idx = -2 # seq_len 在 shape 的倒数第 2 维
# 分支1:HybridCache(新版 transformers 的混合缓存)
try:
from transformers import HybridCache
if isinstance(kv, HybridCache):
return kv.key_cache[0].shape[bs_idx], kv.get_seq_length()
except ImportError:
# 分支2:DynamicCache / None(旧版 transformers)
# ⚠️ 问题在这里:DynamicCache 的处理逻辑被嵌套在 except 块里
if kv is None:
return 0, 0
if hasattr(kv, "key_cache"):
if len(kv.key_cache) == 0 or kv.key_cache[0] is None:
return 0, kv.get_seq_length() if hasattr(kv, "get_seq_length") else 0
key = kv.key_cache[0]
return key.shape[bs_idx], kv.get_seq_length()
# 分支3:旧版 tuple 格式的 kv cache(兜底)
return kv[0][0].shape[bs_idx], kv[0][0].shape[seq_len_idx] + 1
Bug 的触发条件:
当 transformers 版本 ≥ 4.57 时,HybridCache 可以被成功导入。这时候:
kv是一个普通的DynamicCache对象(不是HybridCache)isinstance(kv, HybridCache)返回Falsetry块没有异常,except ImportError整个被跳过- 代码直接掉到最后的
kv[0][0].shape[...]
但在新版 transformers 里,DynamicCache 不再支持下标访问 (kv[0]),直接抛 TypeError: 'DynamicCache' object is not subscriptable。
整条链路就断了。Web UI 和 API 都无法正常获取运行中的模型信息。
修复方案:
python
# === 修复后的代码 ===
def get_batch_size_and_seq_len_from_kv_cache(kv, xinf_model_obj):
bs_idx = 0
seq_len_idx = -2
# 分支1:HybridCache
try:
from transformers import HybridCache
if isinstance(kv, HybridCache):
# 新增防御:key_cache 可能为空或第一层为 None
if len(kv.key_cache) == 0 or kv.key_cache[0] is None:
return 0, 0
return kv.key_cache[0].shape[bs_idx], kv.get_seq_length()
except ImportError:
pass # 不再在 except 里处理 DynamicCache
# 分支2:None(没有缓存)
if kv is None:
return 0, 0
# 分支3:有 key_cache 属性的缓存对象(DynamicCache 旧接口)
# ✅ 从 except 块中移出,独立判断
if hasattr(kv, "key_cache"):
if len(kv.key_cache) == 0 or kv.key_cache[0] is None:
return 0, 0
key = kv.key_cache[0]
return (
key.shape[bs_idx],
(
kv.get_seq_length()
if hasattr(kv, "get_seq_length")
else key.shape[seq_len_idx] # 防御性回退
),
)
# 分支4:新版 transformers Cache API(transformers >= 4.57)
# DynamicCache 暴露 layers 属性,每个 layer 有 .keys / .values 张量
# shape: [batch_size, num_heads, seq_len, head_dim]
# ✅ 这是本次修复的核心新增分支
if hasattr(kv, "layers") and hasattr(kv, "get_seq_length"):
layers = kv.layers
first_keys = getattr(layers[0], "keys", None) if len(layers) > 0 else None
if first_keys is None or first_keys.numel() == 0:
return 0, kv.get_seq_length()
return first_keys.shape[bs_idx], kv.get_seq_length()
# 分支5:旧版 tuple 格式兜底(不会走到这里,但保留以防万一)
return kv[0][0].shape[bs_idx], kv[0][0].shape[seq_len_idx] + 1
核心改动:
- 将 DynamicCache 的处理从
except ImportError块中移出 ,改为独立的条件判断。这样无论HybridCache是否可导入,key_cache分支都能正常工作 - 新增
layers分支 ,支持 transformers ≥ 4.57 的新 Cache API:DynamicCache.layers[i].keys张量形状为[batch_size, num_heads, seq_len, head_dim] - 新增
numel() == 0空检查,防止空缓存导致 shape 索引越界 - 对
get_seq_length()做了防御性回退 ,如果该方法不可用,用key.shape[seq_len_idx]兜底
这个修复虽然只有 30 行改动,但解决了新版 transformers 生态下的一条断裂链路。如果没有这个修复,所有使用 transformers ≥ 4.57 的用户在启动 vLLM 后端时都会遇到 TypeError。
PR #5087:过期副本优雅降级
fix: drop stale running models without replica info in supervisor.list_models
这个是可用性修复。Xinference 的 Supervisor 负责管理所有 Worker 节点的模型副本信息。list_models 方法会把 Worker 上报的运行中模型和 Supervisor 本地跟踪的副本信息做关联。
问题:
python
# === 修复前的代码 ===
running_model_info = {parse_replica_model_uid(k)[0]: v for k, v in ret.items()}
for k, v in running_model_info.items():
# ⚠️ 直接用方括号索引,如果 k 不在字典里就 KeyError
v["replica"] = self._model_uid_to_replica_info[k].replica
当 Worker 上报了一个 Supervisor 已经不再跟踪的模型(比如之前启动失败但留下了残留子进程),_model_uid_to_replica_info[k] 直接抛 KeyError。
这个 KeyError 会导致整个 list_models 调用失败。用户打开 Web UI,看不到任何模型 , 即使其他模型都运行正常。
修复:
python
# === 修复后的代码 ===
running_model_info = {parse_replica_model_uid(k)[0]: v for k, v in ret.items()}
stale_uids = [] # ✅ 收集所有过期的模型 ID
for k, v in running_model_info.items():
replica_info = self._model_uid_to_replica_info.get(k) # ✅ 用 .get() 安全获取
if replica_info is None:
# Worker 仍在汇报这个模型,但 Supervisor 已经不再跟踪它
# (比如之前启动失败留下了残留子进程)
# 跳过它而不是崩溃,让 recover_sub_pool 后续做清理
logger.warning(
"list_models: drop stale running model %s without replica info", k
)
stale_uids.append(k)
continue # ✅ 跳过这个过期条目
v["replica"] = replica_info.replica
# 批量移除过期条目,保持 running_model_info 干净
for k in stale_uids:
running_model_info.pop(k, None)
return running_model_info
三个关键设计决策:
.get()而非[], 防御性编程,不崩溃logger.warning而非静默跳过 , 运维可观测,知道有哪些残留进程- 延迟清理而非立即清理 , 把清理留给
recover_sub_pool做,不在list_models里引入副作用
16 行改动,解决的是一个「一个残留进程让整个 Web UI 挂掉」的问题。
然后我翻了翻 VibeThinker 的论文
三个 PR 提完,我就去翻论文了。
第一篇(arXiv 2511.06221):VibeThinker-1.5B
《Tiny Model, Big Logic》,WeiboAI 团队 2025 年 11 月发表。
看到摘要第一段我就绷不住了:
"This challenges the prevailing approach of scaling model parameters to enhance capabilities, as seen in models like DeepSeek R1 (671B) and Kimi k2 (>1T)."
这玩意儿在挑战「参数越大能力越强」的共识。
一个 1.5B 的模型,训练成本 $7,800(对,你没看错,不到八千美元),在三个数学竞赛上干翻了 DeepSeek R1:
| 基准 | VibeThinker-1.5B | DeepSeek R1-0120 (671B) |
|---|---|---|
| AIME24 | 80.3 | 79.8 |
| AIME25 | 74.4 | 70.0 |
| HMMT25 | 50.4 | 41.7 |
基模型 Qwen2.5-Math-1.5B 在这三个基准上的分数是:6.7、4.3、0.6。
从 0.6 到 50.4。从 4.3 到 74.4。
这不是微调。这是把一个几乎什么都不会的模型,变成了在数学推理上超越 671B 巨兽的东西。
它到底做了什么?
核心方法论叫 Spectrum-to-Signal Principle(SSP,频谱到信号原则)。
传统的 SFT→RL 流程是这样的:先用监督微调让模型学会模仿正确答案(优化 Pass@1),再用强化学习把准确率再往上推。这个是直觉上最合理的做法 , 我只需要一个正确答案,那我就直接训练模型找那个答案。
SSP 把这个范式翻了个面。
它把 SFT 阶段的目标从「找到最优解」改成了「生成足够多的候选解」。这两个目标看起来差不多,但数学上很不一样:
SFT 阶段不是最小化单次回答的损失,而是最大化 Pass@K , 也就是「给定 K 次尝试,至少有一次正确的概率」:
Pass@K=Ex∼D,{yi}i=1k∼πθ(⋅∣x)max{R(x,y1),...,R(x,yk)}
公式解释:
- ( x ) 是输入问题,从数据分布 ( \mathcal{D} ) 中采样
- ( {y_1, \ldots, y_k} ) 是模型对同一个问题生成的 ( k ) 个独立回答
- ( R(x, y_i) ) 是二值奖励函数,回答正确返回 1,否则返回 0
- ( \max ) 表示只要这 ( k ) 个回答里有一个是对的,就算成功
- 整个期望告诉我们:平均每个问题被模型至少解出一次的概率
具体做法分两步。
第一步(Spectrum Phase):Two-Stage Diversity-Exploring Distillation
先把数学知识空间划分为 ( N ) 个子领域:
S={Salgebra,Sgeometry,Scalculus,Sstatistics}
然后在每个子领域上独立做 SFT 训练,每隔 ( k ) 步保存中间检查点 ( M_t ),用 Pass@K 评估,选出每个子领域 Pass@K 最大的检查点作为该领域的「专家模型」:
Mi∗=argtmaxPi(t)
最后把这些专家模型在参数层面做等权融合:
MMergeSFT=i=1∑NN1Mi∗
这里的关键洞察是: 优化 Pass@K(多样性)不仅不会牺牲 Pass@1(准确率),反而会让 Pass@1 也变好。因为更宽的解谱,RL 阶段就有了更肥沃的优化土壤。
第二步(Signal Phase):MaxEnt-Guided Policy Optimization(MGPO)
RL 阶段的直觉也很有意思。
你用模型对一个问题采样 ( G ) 个回答,算一下经验正确率:
pc(q)=G1i=1∑GI(ri=1)
- 如果 ( p_c(q) \approx 0 ) , 这题太难了,模型完全不会,没有优化价值
- 如果 ( p_c(q) \approx 1 ) , 这题已经饱和了,再训也不会有提升
- 如果 ( p_c(q) \approx 0.5 ) , 这就是黄金区间!模型在会与不会的边缘,恰好是最需要学习的时候
MGPO 用一个基于 KL 散度的权重函数,让模型自动聚焦在 ( p_c(q) \approx 0.5 ) 的问题上:
DME(pc(q)∥p0)=pc(q)logp0pc(q)+(1−pc(q))log1−p01−pc(q)
wME(pc(q))=exp(−λ⋅DME(pc(q)∥p0)),p0=0.5
公式解释:
- ( p_c(q) ) 是当前策略对问题 ( q ) 的经验正确率(( G ) 次采样的平均值)
- ( p_0 = 0.5 ) 是最大熵参考点,表示模型对这个问题最不确定的状态
- ( D_{\text{ME}} ) 是用 KL 散度衡量的「当前正确率分布」和「最大熵分布」之间的距离
- 当 ( p_c(q) = 0.5 ) 时,( D_{\text{ME}} = 0 ),权重 ( w_{\text{ME}} = 1 )(最大权重)
- 当 ( p_c(q) \to 0 ) 或 ( p_c(q) \to 1 ) 时,( D_{\text{ME}} \to \infty ),权重 ( w_{\text{ME}} \to 0 )(权重被指数压制)
- ( \lambda ) 控制权重的尖锐程度:( \lambda ) 越大,越聚焦在 0.5 附近
这个权重直接乘到 GRPO 的优势项上:
Aj′(q)=wME(pc(q))⋅Aj(q)
这就是 课程学习(Curriculum Learning)的自动化版本。不需要人工设计学习顺序,模型自己就知道哪些问题该先学。
$7,800 的训练成本和它的意义:
| 模型 | 参数量 | AIME25 | GPU 类型 | GPU 小时 | 成本 |
|---|---|---|---|---|---|
| DeepSeek R1 | 671B | 70.0 | H800 | 147K | $294K |
| MiniMax-M1 | 456B | 74.6 | H800 | 258K | $535K |
| DeepScaleR | 1.5B | 31.5 | A100 | 3.8K | $4.5K |
| VibeThinker | 1.5B | 74.4 | H800 | 3.9K | $7.8K |
成本是 DeepSeek R1 的 1/37,性能反而更高。
这对于小团队、独立研究者来说,等于在说:「需要几百万美元才能做前沿推理模型」这件事,可能已经被证伪了。
第二篇(arXiv 2606.16140):VibeThinker-3B
时隔半年,2026 年 6 月发了 3B 版本。这一篇比第一篇更敢写。
标题是 《Exploring the Frontier of Verifiable Reasoning in Small Language Models》,「探索小模型可验证推理的前沿」。
坦率的讲,看到实验结果的时候,我确认了好几遍数字。
AIME26:94.3。DeepSeek V3.2 是 94.2。
再加上测试时扩展(CLR)后:AIME26 97.1,AIME25 96.7,HMMT25 95.4,BruMO25 99.2。
我把和几个顶级模型的对比列一下,你自己感受:
| 基准 | VibeThinker-3B | DeepSeek V3.2 (671B) | GLM-5 (744B) | Gemini 3 Pro |
|---|---|---|---|---|
| AIME25 | 91.4 | 93.1 | 96.7 | 96.0 |
| AIME26 | 94.3 ✅ | 94.2 | 95.8 | 91.7 |
| HMMT25 | 89.3 | 90.2 | 97.9 | 97.5 |
| LCB v6 | 80.2 | 80.8 | 85.5 | 87.4 |
| IFEval | 93.4 ✅ | 92.6 | 92.6 | -- |
加上 CLR 之后:
| 基准 | VibeThinker-3B + CLR | DeepSeek V3.2 | GLM-5 |
|---|---|---|---|
| AIME25 | 96.7 | 93.1 | 96.7 |
| AIME26 | 97.1 | 94.2 | 95.8 |
| HMMT25 | 95.4 | 90.2 | 97.9 |
| BruMO25 | 99.2 | 96.7 | -- |
还有 LeetCode 竞赛的 OOD 泛化测试 , 八场最新的周赛和双周赛,每场 4 题,每题 4 次独立提交:
| 模型 | 总通过率 |
|---|---|
| GPT-5.3-Codex | 100.0% |
| Gemini 3.1 Pro | 99.2% |
| Gemini 3 Flash | 96.9% |
| VibeThinker-3B | 96.1% |
| GPT-5.2 | 95.3% |
| Kimi K2.5 (1T) | 90.6% |
| GLM-5 (744B) | 76.6% |
3B 模型,在最新 LeetCode 题的首次通过率上,超过了 GPT-5.2,碾压了 Kimi K2.5 和 GLM-5。
它是怎么做到的?
3B 版本的训练管线做了全面升级:
-
课程式两阶段 SFT:第一阶段做广泛能力覆盖和行为冷启动,第二阶段聚焦长链推理(丢弃推理轨迹短于 5K tokens 的样本,用 VibeThinker-1.5B 做硬样本筛选)
-
多领域推理 RL :数学 → 代码 → STEM,按顺序训练。不再使用渐进式上下文窗口扩展,直接上 64K 长上下文 RL。猜测原因是更强的 RL 初始化检查点本身就有高质量的长链推理行为,分段扩展反而可能破坏它。
-
Long2Short RL:一个很巧妙的技巧。RL 的目标从「准确率」扩展到「准确率 + token 效率」。对正确轨迹集合 ( \mathcal{C} ),定义简洁度分数 ( s_i = 1/L_i )(( L_i ) 为响应长度),然后做零和奖励重分配:
ri′=ri+λ⋅maxj∈C∣sj−sˉ∣si−sˉ,i∈C
公式解释:
- ( r_i \in {0,1} ) 是第 ( i ) 条轨迹的原始正确性奖励(1 表示正确,0 表示错误)
- 错误轨迹 (( r_i = 0 )) 的奖励不变
- 正确轨迹集合 ( \mathcal{C} ) 中的每条轨迹,根据它的长度调整奖励
- ( s_i = 1/L_i ) 是简洁度分数:轨迹越短,( s_i ) 越大
- ( \bar{s} ) 是所有正确轨迹的平均简洁度分数
- ( s_i - \bar{s} ) 表示该轨迹是比平均更简洁(正)还是更冗长(负)
- 分母用于归一化,使奖励偏移在 -1, 1 范围内
- ( \lambda = 0.2 ) 控制最大重分配幅度
- 零和性质:所有正确轨迹的奖励之和不变,( \sum_{i\in\mathcal{C}}(r_i' - r_i) = 0 ),更简洁的轨迹获得更多奖励,更冗长的获得更少
-
离线自蒸馏(Offline Self-Distillation) :把 RL 阶段获得的高质量推理模式蒸馏回统一的学生模型。用一个很聪明的方法选择蒸馏数据 , 计算 学习潜力分数:
SLP(q,y)=−∥y∥1t=1∑∥y∥logπθstu(yt∣q,y<t)
公式解释:
- ( \pi_{\theta_{\mathrm{stu}}}(y_t \mid q, y_{<t}) ) 是学生模型预测 token ( y_t ) 的概率(给定问题 ( q ) 和之前的 token)
- ( -\log(\cdot) ) 是负对数似然,也叫 surprisal:学生模型越不「理解」这个 token,surprisal 越大
- ( \frac{1}{|y|} ) 做长度归一化:防止长序列天然有更大的总 surprisal
- 所以 ( S_{\mathrm{LP}} ) 高,代表这个推理轨迹虽然被教师模型成功生成且验证正确,但学生模型目前还不太会,蒸馏价值最大
这个设计让自蒸馏不是盲目地灌数据,而是精准地找出学生最需要学的那批样本。
-
测试时扩展 CLR(Claim-Level Reliability Assessment):生成 ( K=32 ) 条候选轨迹,对每条轨迹提取 ( M=5 ) 个决策声明,让模型自己做自我验证(尝试证伪或验证每个声明),然后按可靠性加权投票选出最终答案:
rk=(M1m=1∑Mvk,m)M
公式解释:
- ( v_{k,m} \in {0,1} ) 是第 ( k ) 条轨迹的第 ( m ) 个声明是否通过自我验证(1 通过,0 不通过)
- ( \frac{1}{M}\sum v_{k,m} ) 是该轨迹的平均声明通过率
- ( M ) 次幂是非线性惩罚:如果一条轨迹有 5 个声明,其中 1 个错误(通过率 80%),( 0.8^5 \approx 0.328 ),权重被大幅压低
- 这是一个严厉的机制:任何中间逻辑错误都会非线性地拉低整条轨迹的权重
这篇论文最让我感兴趣的概念是 Parameter Compression-Coverage Hypothesis(参数压缩-覆盖假说)。
它把基础模型能力分成了两种:
| 能力类型 | 特征 | 参数需求 |
|---|---|---|
| 参数密集型 (Parameter-Dense) | 可验证推理 , 结构化解空间搜索、约束满足、错误修正、多步组合 | 可高度压缩进紧凑的推理核心 |
| 参数扩展型 (Parameter-Expansive) | 知识密集型和通用 , 开放域事实、领域概念、长尾场景 | 参数需求类似覆盖问题 |
翻译成人话就是:数学和代码推理,核心是搜索和组合,不需要记住几百万个事实。所以 3B 参数够了。但你要问「非洲哪个国家的 GDP 增长率最高」,那确实需要更多的参数来存这些知识。
所以 VibeThinker-3B 在 GPQA-Diamond(博士级百科知识)上只有 70.2,而 DeepSeek V3.2 有 82.4,Gemini 3 Pro 有 91.9。差距很明显。
但这恰好验证了假说:小模型不是大模型的廉价替代品,而是一条互补的进化路径。推理能力可以压缩,知识覆盖需要扩展。
这三件事放在一起看
回看我这三个 PR 和两篇论文,其实讲的是一件事:
小模型推理的工程化和民主化。
VibeThinker 证明了 3B 参数就可以做顶级数学推理。Xinference 让这个模型可以一键部署、一键切换后端、在消费级 GPU 上流畅运行。
bash
# 三行命令,在 RTX 4090 上启动一个能打 DeepSeek V3.2 的模型
pip install xinference
xinference-local
xinference launch --model-name vibethinker --size-in-billions 3 --backend vllm
不需要 A100/H100 集群。不需要 Kubernetes 和分布式调度。不需要几万美元的云服务账单。
一台游戏本就够了。
而这恰好是开源推理框架的价值所在。Xinference 支持的推理后端覆盖了 Transformers、vLLM、SGLang、llama.cpp 等主流选择,模型格式从 PyTorch 到 GGUF 全部兼容,你可以在 Web UI 里像选菜品一样挑选和切换。现在 2026 年已经支持了 几百个内置模型,加上自定义注册,几乎涵盖了所有主流开源模型。
再说一件有意思的事。
我提 PR #5086 修的那个 DynamicCache 兼容性问题,其实是生态快速演进带来的接口断裂。transformers 改了 Cache API 的内部结构,Xinference 的底层访问逻辑就得跟着改。这件事在小模型推理时代会越来越频繁,因为当模型可以跑在任意 GPU 上,推理框架就需要适配更多后端版本、更多模型架构、更多边界情况。
这就是我为什么觉得 Xinference 这个项目值得关注。它在生态最前端承担了适配和稳定的工作 。对于普通开发者来说,你不需要关心 DynamicCache 的 key_cache 和 layers 接口有什么区别,你只需要 xinference launch。
如果你想让一个 3B 模型在你自己的机器上跑出接近 SOTA 的数学推理能力:
bash
# 1. 装 Xinference
pip install xinference
# 2. 本地启动
xinference-local
# 3. 启动 VibeThinker-3B(用 vLLM 后端)
xinference launch \
--model-name vibethinker \
--size-in-billions 3 \
--backend vllm
# 4. Python API 调用
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:9997/v1",
api_key="not-needed"
)
response = client.chat.completions.create(
model="vibethinker",
messages=[{"role": "user", "content": "Find the sum of all positive integers n such that n^2 + 20n + 19 is a perfect square."}],
temperature=1.0,
max_tokens=4096
)
print(response.choices[0].message.content)
这就是小模型推理民主化的样子 , 一个能跟 671B 模型掰手腕的东西,就安静地跑在你的 RTX 4090 上,甚至跑在你的 MacBook 上。
XorbitsAI Inference GitHub: github.com/xorbitsai/i...
VibeThinker 模型: huggingface.co/WeiboAI/Vib...
VibeThinker-1.5B 论文: arxiv.org/abs/2511.06...
VibeThinker-3B 论文: arxiv.org/abs/2606.16...