我把一个 3B 模型塞进了 Xinference,然后它干掉了 DeepSeek V3.2

我最近干了件事。

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 可以被成功导入。这时候:

  1. kv 是一个普通的 DynamicCache 对象(不是 HybridCache
  2. isinstance(kv, HybridCache) 返回 False
  3. try 块没有异常,except ImportError 整个被跳过
  4. 代码直接掉到最后的 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)} \operatorname{Pass@K} = \mathbb{E}{x\sim\mathcal{D}, \{y_i\}{i=1}^k \sim \pi_\theta(\cdot|x)} \left \\max\\{R(x,y_1), \\ldots, R(x,y_k)\\} \\right 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} \mathcal{S} = \{S_{\text{algebra}}, S_{\text{geometry}}, S_{\text{calculus}}, S_{\text{statistics}}\} S={Salgebra,Sgeometry,Scalculus,Sstatistics}

然后在每个子领域上独立做 SFT 训练,每隔 ( k ) 步保存中间检查点 ( M_t ),用 Pass@K 评估,选出每个子领域 Pass@K 最大的检查点作为该领域的「专家模型」:
Mi∗=arg⁡ max⁡t Pi(t) M_i^* = \arg\max_t P_i(t) Mi∗=argtmaxPi(t)

最后把这些专家模型在参数层面做等权融合:
MMergeSFT= ∑i=1N 1NMi∗ \mathbf{M}{\text{Merge}}^{\text{SFT}} = \sum{i=1}^{N} \frac{1}{N} M_i^* MMergeSFT=i=1∑NN1Mi∗

这里的关键洞察是: 优化 Pass@K(多样性)不仅不会牺牲 Pass@1(准确率),反而会让 Pass@1 也变好。因为更宽的解谱,RL 阶段就有了更肥沃的优化土壤。

第二步(Signal Phase):MaxEnt-Guided Policy Optimization(MGPO)

RL 阶段的直觉也很有意思。

你用模型对一个问题采样 ( G ) 个回答,算一下经验正确率:
pc(q)=1G ∑i=1G I(ri=1) p_c(q) = \frac{1}{G} \sum_{i=1}^{G} \mathbb{I}(r_i = 1) 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)log⁡ pc(q) p0 +(1−pc(q))log⁡ 1−pc(q) 1−p0 D_{\text{ME}}(p_c(q) \| p_0) = p_c(q) \log\frac{p_c(q)}{p_0} + (1-p_c(q)) \log\frac{1-p_c(q)}{1-p_0} 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 w_{\text{ME}}(p_c(q)) = \exp\left(-\lambda \cdot D_{\text{ME}}(p_c(q) \| p_0)\right), \quad p_0 = 0.5 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) \mathcal{A}'j(q) = w{\text{ME}}(p_c(q)) \cdot \mathcal{A}_j(q) 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 版本的训练管线做了全面升级:

  1. 课程式两阶段 SFT:第一阶段做广泛能力覆盖和行为冷启动,第二阶段聚焦长链推理(丢弃推理轨迹短于 5K tokens 的样本,用 VibeThinker-1.5B 做硬样本筛选)

  2. 多领域推理 RL :数学 → 代码 → STEM,按顺序训练。不再使用渐进式上下文窗口扩展,直接上 64K 长上下文 RL。猜测原因是更强的 RL 初始化检查点本身就有高质量的长链推理行为,分段扩展反而可能破坏它。

  3. Long2Short RL:一个很巧妙的技巧。RL 的目标从「准确率」扩展到「准确率 + token 效率」。对正确轨迹集合 ( \mathcal{C} ),定义简洁度分数 ( s_i = 1/L_i )(( L_i ) 为响应长度),然后做零和奖励重分配:

    ri′=ri+λ⋅ si−sˉ max⁡j∈C ∣sj−sˉ∣ ,i∈C r_i' = r_i + \lambda \cdot \frac{s_i - \bar{s}}{\max_{j\in\mathcal{C}}|s_j - \bar{s}|}, \quad i \in \mathcal{C} 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 ),更简洁的轨迹获得更多奖励,更冗长的获得更少
  4. 离线自蒸馏(Offline Self-Distillation) :把 RL 阶段获得的高质量推理模式蒸馏回统一的学生模型。用一个很聪明的方法选择蒸馏数据 , 计算 学习潜力分数

    SLP (q,y)=− 1∥y∥ ∑t=1∥y∥ log⁡ π θstu (yt∣q, y<t ) S_{\mathrm{LP}}(q, y) = -\frac{1}{\|y\|}\sum_{t=1}^{\|y\|}\log \pi_{\theta_{\mathrm{stu}}}(y_t \mid q, y_{<t}) 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}} ) 高,代表这个推理轨迹虽然被教师模型成功生成且验证正确,但学生模型目前还不太会,蒸馏价值最大

    这个设计让自蒸馏不是盲目地灌数据,而是精准地找出学生最需要学的那批样本。

  5. 测试时扩展 CLR(Claim-Level Reliability Assessment):生成 ( K=32 ) 条候选轨迹,对每条轨迹提取 ( M=5 ) 个决策声明,让模型自己做自我验证(尝试证伪或验证每个声明),然后按可靠性加权投票选出最终答案:

    rk= (1M ∑m=1M vk,m ) M r_k = \left(\frac{1}{M}\sum_{m=1}^{M} v_{k,m}\right)^M 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_cachelayers 接口有什么区别,你只需要 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...

相关推荐
秦先生在广东1 小时前
Agent 闭环才是真正的护城河:Anthropic “300 个 Agent“ 背后被忽视的秘密
人工智能
凌奕1 小时前
别用文档约束你的 Agent:聊聊 Agent 开发流程的思想
llm·github·agent
Bigfish_coding1 小时前
前端转agent-【python】- 14 记忆系统优化:摘要与遗忘
人工智能
Bigfish_coding1 小时前
前端转agent-【python】-13 Ollama Python流式输出教程:stream=True 与 async 实践
人工智能
字节跳动数据库4 小时前
文章分享——相似函数处理方法
人工智能·后端·程序员
Bigfish_coding4 小时前
前端转agent-【python】-12 LangChain 入门实战:RAG + LCEL 链式调用
人工智能
程序员cxuan4 小时前
读懂 Claude Code 架构分析系列,第一篇,开始!
人工智能·后端·架构
饼干哥哥4 小时前
扣子3.0测评:我让 Codex 和 Claude Code 住同一个桌面,结果它们打架了!
人工智能·开源·代码规范