跳到正文
原文
Hugging Face Blog·· 2026-08-25精选AI 评分62

IBM 发布 Granite 4.2 推理模型家族:3B、8B、30B 三档

Granite 4.2 LLMs: How They're Built

AI 摘要

IBM Granite 团队发布 Granite 4.2,这是其首个稠密、decoder-only 的推理模型家族,包含 3B、8B、30B 三个规模,均以 Apache 2.0 许可开源。

正文 · AI 翻译

关于我们如何构建 Granite 4.2 推理模型家族的技术详解。

作者:Granite 团队,IBM


TL;DR:Granite 4.2 是我们首个稠密、仅解码器推理 LLM 家族,发布了三种规模:3B、8B 和 30B。这些模型是在 Granite-4.1 基础模型上进行后训练的。Granite-4.1 基础模型从零开始预训练,使用约 15T token,采用五阶段策略将上下文窗口扩展至 512K token,在思维链、推理和智能体轨迹数据上进行监督微调,然后通过多阶段强化学习流水线进行后训练。该流水线包括智能体 RL,其中 8B 和 30B 模型学习在真实沙盒环境中使用工具行动。每个模型都有思考 / 非思考开关、低投入思考模式(在简单问题上花费较短的推理预算),以及原生工具调用。所有 Granite 4.2 模型均在 Apache 2.0 许可下发布。

链接:


概述

Granite 4.2 是 Granite 语言模型家族中专注于推理的版本。早期的 Granite 版本是强大的指令遵循助手;Granite 4.2 增加了显式推理。每个模型都能在给出答案前生成思维链,并可根据任务所需的思考程度在思考或非思考模式下运行。低投入模式介于两者之间,在简单问题上花费较短的推理预算。

三种规模(3B、8B 和 30B)共享相同的架构设计,并遵循相同的训练流水线(基础模型、SFT,然后多阶段 RL),各自处于自己的规模。三者都是强大的推理者和指令遵循者。最明显的能力差异体现在后训练中。8B 和 30B 模型还额外经过一个智能体 RL 模块,教它们作为智能体运作:调用工具、编辑和运行代码、驱动终端,以及在真实环境中搜索网页。每个模型都支持原生工具调用。通过 OpenAI 兼容端点(例如使用 vLLM)提供服务时,它会以 OpenAI 函数调用格式发出工具调用,无需额外胶水代码即可接入智能体框架。Granite 4.2 也在 SGLang 中得到支持,请参阅 SGLang cookbook 获取可直接部署的配方。

本文的其余部分将介绍构建过程:架构、预训练、监督微调、多阶段 RL 流水线以及结果。


模型架构

Granite 4.2 模型基于仅解码器的稠密 transformer 架构构建,具有以下核心组件:

  • 注意力:分组查询注意力(GQA),40 个注意力头和 8 个 KV 头
  • 位置嵌入:旋转位置嵌入(RoPE),θ = 10,000,000
  • 前馈:带 SwiGLU 激活的 MLP
  • 归一化:RMSNorm(ε = 1e-5)
  • 嵌入:独立的输入/输出嵌入(不共享)
  • 精度:bfloat16
组件 3B 稠密 8B 稠密 30B 稠密
嵌入大小 2560 4096 4096
层数 40 40 64
注意力头大小 64 128 128
注意力头数量 40 32 32
KV 头数量 8 8 8
MLP 隐藏大小 8192 12800 32768
MLP 激活 SwiGLU SwiGLU SwiGLU
序列长度 131072 131072 131072
位置嵌入 RoPE RoPE RoPE
# 参数量 3B 8B 30B

预训练

Granite 4.2 模型是在 Granite-4.1 基础模型之上进行后训练得到的。Granite-4.1 基础模型采用五阶段训练策略,从零开始在大约 15 万亿 token 上训练而成。第 1–2 阶段侧重于基础预训练,第 3–4 阶段使用质量逐步提升的数据退火进行中期训练,第 5 阶段引入长上下文训练,将上下文窗口扩展至 512K token。每个阶段使用不同的数据配比和学习率调度,逐步从广泛的网络规模数据转向更精选的高质量数据源。

预训练方案紧密沿袭上一代;关于数据混合、阶段调度和长上下文扩展的详细说明,请参阅 Granite 4.1 博客。


SFT:数据准备与质量控制

监督微调(SFT)将基础模型转变为可靠的指令遵循、推理和工具使用助手。SFT 数据混合包含智能体(31.6%)和非智能体(68.4%)数据,总计约 720 万个样本,即约 100B token,其中约 65B 为可训练 token。

智能体语料库涵盖广泛的领域,包括软件工程(SWE,69%)、工具调用(12.1%)、终端使用(8.0%)、数学(3.5%)、搜索(0.8%)和动作(0.2%)。这些样本和轨迹使用多种智能体脚手架和框架生成,包括 OpenHands、OpenCode、Terminus-2、SWE-agent、OpenResearcher、MiniSWE、OpenSeeker、EnvScaler、Gemini CLI、Hermes、Codex 和 Goose。智能体数据结合了来自开源数据集和我们自研合成 RL 环境的样本,涵盖多种智能体–框架组合。

非智能体语料库由几个主要类别组成:指令遵循(18.8%)、编码(18.8%)、数学(14.6%)、多语言(7.0%)、科学(5.4%)、推理(3.0%)和安全(0.8%)。

数据质量控制

在样本进入最终 SFT 混合数据之前,我们应用多个阶段的质量控制。首先,来自不同来源的数据被规范化并重新格式化为一致的 OpenAI Chat 格式,使对话结构和工具交互在各数据集和脚手架之间保持统一。

然后,我们使用 GPT-OSS-120B 和 Gemma 4 作为基于 LLM 的评判器来评估样本质量。低分样本会被移除,包含幻觉或捏造信息、无效工具交互,或调用相应工具列表中未定义函数的工具调用的样本也会被移除。在适当的情况下,还会应用若干针对性的、特定于数据集的启发式规则,以进一步提高质量并移除已知的噪声来源。

最后,我们执行局部和全局去重。去重基于对 tools 和 messages 字段组合计算的 SHA-256 哈希,既移除单个数据源内的重复样本,也移除整个 SFT 混合数据中的重复样本。

SFT 训练细节

完整语料库首先进行全局打乱,以减少顺序效应,并确保训练期间不同领域的样本充分混合。打乱后的语料库随后被划分为等大小的 .parquet 个分片,使用模型的分词器和聊天模板进行分词,并为大规模分布式训练做好准备。

在启动最终的大规模训练之前,我们在具有代表性的配置上调整超参数,扫描学习率调度、初始学习率和预热比例,以找到能在不同模型规模下稳定训练的设置。最终训练配置总结如下:

参数 值
计算资源 32–128 个节点(按模型规模),每节点 4× Grace/GB200
序列长度(打包) 131,072(128K)
全局批大小 128
学习率 1.0e-5,预热后保持恒定;第二阶段为 3.0e-6
学习率预热 train_iters 的 2.5%
训练时长 约 2 个 epoch
并行策略 TP=2,PP=1,CP=4 或 CP=2

30B 模型的第二阶段 SFT

对于 30B 模型,我们额外进行第二阶段 SFT,专门聚焦于智能体编程。在此阶段,智能体、SWE 和编程数据被上采样,以增加它们对训练分布的有效贡献,同时约 16% 的混合数据保留为来自原始 SFT 语料的回放数据。

随后,30B 模型以 3.0e-6 的较低学习率再微调约一个 epoch。这一有针对性的第二阶段增加了模型对智能体编程轨迹的接触,同时不会丢弃初始 SFT 阶段获得的能力。


强化学习:多阶段、多环境流水线

在 SFT 之后,我们应用多阶段、多环境强化学习流水线。我们不是进行单次 RL 训练,而是运行一系列聚焦阶段,跨越多种环境:数学、代码、科学、指令遵循、工具使用和结构化输出,然后是软件工程、终端使用和网络搜索。每个阶段都是一次独立的 RL 运行,针对一种能力,并从上一阶段的检查点热启动。

Granite 4.2 staged RL curriculum

图 1. 分阶段 RL 课程。基础 RL(可验证奖励 + 技能增强)对所有规模运行;智能体 RL 模块(SWE → 终端 → 搜索)仅对 8B 和 30B 运行。每个模型都以 RLHF 收尾。每个阶段都是一次独立的 GRPO 运行,从上一检查点热启动。

训练方法

每个阶段都使用异步 GRPO(组相对策略优化)进行训练,因此循环中的生成器和训练器两半永远不会相互阻塞。一组生成工作进程持续采样响应,并将完成的轨迹放入共享缓冲区;一旦缓冲区中积累了完整一步的量,训练器就拉取该批次,执行一次优化器步骤,并将更新后的参数流式传回生成工作进程,而不暂停它们。刷新可能在 rollout 进行到一半时到达,导致单条轨迹由两个相邻策略版本拼接而成。我们允许这种情况,而不是付出代价来防止它:工作进程复用其现有的 KV 缓存,而不是在每次刷新后重建,唯一的护栏是一个限制,使它们不会比训练器落后超过一次更新,从而限制了任何样本的离策略程度。任何在该限制下仍然存在的失配,都在目标函数中通过截断重要性采样处理,它将训练与生成的 log 概率比限制在固定上限内,使少量陈旧 token 无法主导一次更新。

优势是相对于组的,采用留一法基线:每个响应都根据同一提示下抽取的其他样本的平均奖励来评判,这消除了对单独价值网络的需求。具体来说,以RLVR为例,这是第一个也是持续时间最长的阶段:每一步将256个提示与每个提示16个采样响应配对,形成4,096个样本的批次,训练器在下一个rollout开始前在一个优化器步骤中消费该批次。后续阶段保持这一机制不变,仅调整每个阶段的形状,如下所示。

RL训练配置

该流程在每个阶段都保持一组通用的超参数骨干,这使得课程更容易运行和比较。有几个旋钮在所有地方都是固定的:

参数 值(跨阶段共享)
算法 GRPO(无价值网络;组相对优势)
训练栈 NeMo-RL(Megatron-Core + vLLM)与NeMo-Gym环境
比率裁剪(最小/最大) 0.2 / 0.28
微批次大小 1
并行性 张量并行2–4;无流水线或上下文并行

阶段之间变化的是每次运行的形状:每步有多少提示和生成,上下文有多长,代理循环是否运行,以及我们向参考策略拉回的程度。下表给出了30B链的逐阶段精确设置:

阶段 提示/步 生成/提示 最大序列长度 Rollout轮次 KL 学习率
RLVR(×3) 256 16 64K 1 0 5e-7
IF增强器 256 16 64K 1 0 5e-7
代码增强器 64 16 64K 1 0.05 5e-7
SWE 1 64 16 128K 1 0.01 5e-7
SWE 2 32 16 128K 128 0 5e-7
终端 8 32 64K 64 0.01 1e-6
搜索 32 16 128K 64 0.01 5e-7
RLHF 128 16 48K 1 0.05 5e-7

显示的参数适用于30B模型。全局批次大小 = 提示/步 × 生成/提示(例如RLVR的256 × 16 = 4096)。3B和8B模型使用相同的配方和超参数,但阶段更少(参见三种规模有何不同);改变的是阶段列表,而不是旋钮。

KL调度遵循奖励类型:在奖励客观且可验证的地方自由探索(RLVR和SWE 2以KL 0运行),在目标是偏好、安全性或狭窄技能移植的地方保持接近参考(RLHF和代码增强器使用KL 0.05)。Rollout轮次列计数GRPO本身每次rollout看到的环境交互。在所有情况下,模型都在完整的真实环境轨迹上训练。

分阶段课程

每个阶段都是一个单独的RL运行,具有单一目标和自己的奖励信号。完成后,其策略导出为Hugging Face格式,并成为下一阶段的基础模型,因此该流程是一系列热启动:

SFT ─▶ RLVR ─▶ Skill boosters ─▶ SWE agent ─▶ Terminal ─▶ Search ─▶ RLHF
      └──────── foundational RL ────────┘   └──────── agentic RL (8B / 30B) ────────┘

8B和30B模型遵循完整的阶梯。3B模型走一条缩短的路径:基础RL和对齐,没有代理块。

奖励信号

一个阶段主要由它如何被奖励来定义。在整个流程中有三种奖励类型,单个阶段可以使用不止一种:

奖励类型 它衡量什么 用于
可验证 精确匹配、单元测试、格式检查器、基于规则的检查器对真实标签 RLVR · 增强器 · SWE
奖励模型/LLM评判 开放式质量、偏好、安全性、答案正确性 RLVR · 搜索 · RLHF
代理结果 模型是否在真实环境中实际解决了任务? SWE · 终端 · 搜索

可验证奖励是客观的且难以被钻空子,因此流水线将其前置。评判型和偏好型奖励则处理没有检查器能表达的开放式特质。智能体结果奖励最为稀疏:往往只是一条漫长工具使用轨迹末端的一个比特。

基础 RL:构建技能

RLVR:可验证奖励 RL

RLVR 是基础阶段,也是流水线中数据混合最广泛的阶段:一个跨越众多可验证领域的单一混合数据集。

  • 数学:带框选答案检查的思维链,外加在 Lean 中的形式化证明
  • 竞赛编程:在沙箱中对照隐藏测试检查解法
  • STEM / 研究生级别科学 MCQA 与通用知识
  • 指令遵循:结构化输出与逆向指令任务
  • 工具 / 函数调用:单步工具使用
  • 推理谜题与拒答(知道何时拒绝)

每种任务类型都带有自己的验证器,因此奖励是按样本落地的。RLVR 运行 3B 和 8B 两轮,30B 三轮。每一轮都是在重新加权的公开与内部整理 RL 数据混合上的一次全新热启动运行。

技能增强器:定向提升

在 RLVR 之后,几个简短的增强器阶段会强化特定能力,这些能力受益于聚焦于以下领域的集中训练:

  • 指令遵循(IF):多轮对话、逆向 IFEval、结构化输出
  • 代码:仅限竞赛编程

增强器是小而聚焦的运行。一个轻微的 KL 惩罚使模型贴近其当前行为,同时微调某一项技能。

智能体 RL:学会行动(8B / 30B)

在智能体阶段,模型学会行动:调用工具、观察结果,并在真实环境中迭代,根据任务是否真正解决来获得奖励。这些阶段具有相同的形态:多轮工具使用、真实(而非模拟)环境、稀疏的结果奖励,以及 GRPO,从代码增强后的检查点热启动。它们按顺序运行:SWE → Terminal → Search。

Agentic RL environments

图 2. 三个智能体 RL 环境。每个都将真实执行框架与真实环境配对,并配有稀疏的、基于结果的奖励。3B 模型不运行其中任何一个。

  • SWE 智能体(软件工程)。每个任务都是各自沙箱中的一个真实代码仓库。在 OpenHands 执行框架的驱动下,模型阅读代码、编辑文件,并在许多内部轮次中运行测试套件。奖励是可验证的:隐藏测试是否通过?任务取自开源 SWE 数据集,每个实例都由一个按仓库的容器镜像支持。
  • 终端智能体(终端 / 操作系统操作)。在实时 shell 中的多步任务,通过 Harbor / Terminus-2 智能体执行框架运行。模型规划一系列命令,观察其输出,并从错误中恢复。任务成功完成时分配奖励。这是唯一一个在 GRPO 层面驱动其多轮智能体循环的阶段,其 rollout 最多跨越 64 个环境轮次。
  • 搜索智能体(深度研究)。 该模型在浏览智能体循环中通过实时网络搜索工具调用来回答困难的多跳问题:跨跳收集证据、对其进行推理并生成答案。由于这里的正确性是开放式的,奖励由 LLM 评判器对最终答案进行评判。

对齐:RLHF

每个模型的最后阶段都是面向人类偏好与安全的 RLHF。 它针对用于偏好的生成式奖励模型(GenRM)进行优化,外加涵盖越狱抵抗和适当拒绝的安全奖励。此阶段使用流水线中最高的 KL 惩罚,在不侵蚀早期阶段所构建能力的前提下对齐语气与安全性。除了人类偏好与安全对齐外,此阶段还应用推理长度惩罚,以抑制早期阶段获得的过度冗长的推理行为。

三种规模有何不同

方法与基础设施相同;区别在于每个模型在阶梯上走多远。

阶段 3B 8B 30B
RLVR(可验证) ×2 ×2 ×3
技能增强器 代码 IF · GPQA · 代码 IF · 代码
SWE 智能体 — ✓ ✓
终端智能体 — ✓ ✓
搜索智能体 — ✓ ✓
RLHF(偏好 + 安全) ✓ ✓ ✓

3B 是一个强大的基础 RL 模型;8B 和 30B 在其之上增加了智能体 RL 模块,学习在真实环境中使用工具行动。


面向可扩展 RL 的智能体 AI 基础设施

这种规模的强化学习需要能够同时驱动训练循环和一组实时环境的基础设施。这在智能体阶段最为重要,因为每个训练样本都是一次多轮 rollout,会编辑代码、运行命令或浏览网页。Granite 4.2 的 RL 运行在两个开放组件上:训练侧的 NeMo-RL 和 rollout 侧的 NeMo-Gym。

NeMo-RL + NeMo-Gym system architecture

图 3. RL 系统。NeMo-RL 驱动 GRPO 循环(Megatron-Core 训练后端、vLLM 生成,以及用于 HF⇄Megatron 权重转换的 Megatron-Bridge)。NeMo-Gym 编排 rollout,并将工具、沙箱和奖励/验证器调用作为可插拔的 Resources 托管。

分工如下:

  • NeMo-RL(训练侧)。 Megatron-Core 是训练后端;vLLM 生成 rollout;Megatron-Bridge 在 Megatron 与 Hugging Face 格式之间转换权重,因此每个阶段都可以导出一个干净的 HF checkpoint 供下一阶段使用。
  • NeMo-Gym(rollout 侧)。 它将每个环境暴露为一组 Resources(验证器、工具、沙箱和奖励模型),并置于统一接口之后。这是智能体阶段的接入点:SWE 仓库沙箱、终端 harness 和网络搜索工具都挂接在这里,而对训练循环来说,它们看起来与一个简单的数学验证器相同。

正是这种统一性使上述分阶段课程变得可行:增强器的基于规则的检查器和完整的 SWE 沙箱向 GRPO 呈现相同的接口。

这种拆分也是上述异步训练循环在物理上可行的原因:生成和策略更新位于不同的 GPU 池上,因此昂贵的生成集群——包括实时智能体环境——会保持忙碌,而不是在优化器步骤期间闲置。


结果

Granite 4.2 在智能体编码、通用智能体与工具使用、推理、聊天与指令遵循以及长上下文方面进行了评估。完整的基准测试表如下,随后是按模型规模拆分主要结果的图表。

任务 3B Dense 8B Dense 30B Dense
智能体(编码)
SWE Bench Multilingual NA 30.7841.89
SWE Bench Pro NA 19.1133.29
SWE Bench Verified NA 47.6757.00
Terminal-Bench 2.1 NA 20.5629.24
智能体(通用)
τ³-bench 45.78 58.0662.00
BFCL (v4) 52.41 50.2961.39
ProfBench 32.10 41.2042.90
BirdBench NA 41.0741.85
GDPval NA 1189.001225.00
推理
AIME25 78.33 86.6789.17
HMMT Feb25 66.67 78.3389.17
GPQA 54.80 64.1466.41
LiveCodeBench v6 69.71 73.2475.77
SciCode 24.11 36.0938.76
聊天与指令遵循
MMLU-Pro 67.84 74.0477.60
MMLU-ProX lite (IBM) 27.78 61.0666.64
Arena-Hard-V2 34.96 65.1967.93
IFBench (prompt) 74.33 79.3377.17
长上下文
RULER 64K 67.52 80.9989.96
RULER 128K 55.30 71.4181.38

支持的语言:英语、德语、西班牙语、法语、日语、葡萄牙语、阿拉伯语、捷克语、意大利语、韩语、荷兰语和中文。

下面的图表按能力领域拆分这些结果。

Reasoning benchmarks

图 4. 推理(pass@1)。分数随模型规模在数学(AIME25、HMMT)、科学(GPQA)和代码推理(LiveCodeBench、SciCode)方面持续上升。

Agentic coding benchmarks

图 5. 智能体编码解决率。智能体强化学习模块仅针对 8B 和 30B 训练;30B 模型在 SWE-Bench 各变体和 Terminal-Bench 上领先。

General agentic and tool-use benchmarks

图 6. 通用智能体和工具使用基准测试,报告了全部三种规模。


量化

我们还发布了 Granite 4.2 模型的四个量化变体,用于 vLLM 推理。这些模型使用 LLM Compressor 转换为 FP8、NVFP4 和 MXFP4,并使用 llama.cpp 框架转换为 GGUF 格式,以减少内存部署。

FP8

FP8 版本使用动态逐通道权重和逐 token 激活进行量化。不使用校准。

FP4

NVFP4 和 MXFP4 版本使用 GPTQ 进行量化,并在从 SFT 数据集中抽取的 2K 样本上校准。校准期间最大上下文长度为 2K。

GGUF

Granite 4.2 模型到 GGUF 的转换使用规范的 llama.cpp 工具完成,如 https://github.com/IBM/gguf#gguf-conversion--quantization 所述。

提供了多种 GGUF 格式:

  • Q8_0
  • Q6_K
  • Q5_K_S
  • Q5_K_M
  • Q5_1
  • Q5_0
  • Q4_K_S
  • Q4_K_M
  • Q4_1
  • Q4_0
  • Q3_K_S
  • Q3_K_M
  • Q3_K_L
  • Q2_K

基础设施

硬件

我们在由 CoreWeave 托管的 NVIDIA GB200 NVL72 集群上训练了 Granite 4.2 语言模型,其特点包括:

  • 一个 72-GPU NVLink 域,用于高速机架内通信
  • 一个非阻塞 Fat-Tree NDR 400 Gb/s InfiniBand 网络结构,用于全带宽机架间连接
  • 数千个 GPU 在集群规模下运行

该基础设施提供了高效大规模分布式训练所需的高带宽、低延迟通信。

软件栈

训练软件栈打包为 .sqsh 容器镜像,每个镜像为一次运行提供可复现、可移植的环境,并固定其 SBSA 兼容的 CUDA 目标、Linux aarch64 Python wheels 和 GPU 特定二进制文件。大规模 SFT 运行基于 NGC PyTorch 基础镜像(Ubuntu 22.04、CUDA 12.8、Python 3.12);RL 栈在其自己的 NeMo-RL 容器中运行。


快速开始(Transformers)

安装

pip install torch
pip install accelerate transformers

基础推理(思考模式)

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

model_path = "ibm-granite/granite-4.2-30b"
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path, device_map="cuda", torch_dtype=torch.bfloat16)
model.eval()

messages = [
    {"role": "user", "content": "How many r's are in the word 'strawberry'?"},
]

text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, enable_thinking=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)

with torch.no_grad():
    output = model.generate(**inputs, max_new_tokens=8192, temperature=1.0, top_p=0.95, do_sample=True)

print(tokenizer.decode(output[0][inputs.input_ids.shape[-1]:], skip_special_tokens=False))

示例输出

<think>
Okay, let's see. The problem is to find how many 'r's are in the word 'strawberry'.

First, I need to write out the word: s t r a w b e r r y.

Now, I need to count the number of 'r' letters. Let's list each letter and check for 'r'.

1. s – not r
2. t – not r
3. r – yes, that's one
4. a – no
5. w – no
6. b – no
7. e – no
8. r – yes, that's two
9. r – yes, that's three
10. y – no

Total r's = 3.
</think>
There are **3** r's in the word "strawberry".<|im_end|>

非思考模式

messages = [
    {"role": "user", "content": "What is the capital of France?"},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, enable_thinking=False)
inputs = tokenizer(text, return_tensors="pt").to(model.device)

output = model.generate(**inputs, max_new_tokens=2048, temperature=1.0, top_p=0.95, do_sample=True)
print(tokenizer.decode(output[0][inputs.input_ids.shape[-1]:], skip_special_tokens=False))

示例输出

<think></think>The capital of France is Paris.<|im_end|>

低强度思考

messages = [
    {"role": "user", "content": "What is 2 + 2?"},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True,
                                     enable_thinking=True, low_effort=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)

output = model.generate(**inputs, max_new_tokens=4096, temperature=1.0, top_p=0.95, do_sample=True)
print(tokenizer.decode(output[0][inputs.input_ids.shape[-1]:], skip_special_tokens=False))

示例输出

<think>
Simple answer.
</think>
2 + 2 = 4.<|im_end|>

工具调用

Granite 模型支持带有集成推理的工具调用:模型在调用工具之前会先推理应该调用哪个工具以及为什么调用它。工具使用 OpenAI 函数定义 schema 来定义。

基础工具调用

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Get the current weather for a specified city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "Name of the city"}
                },
                "required": ["city"]
            }
        }
    }
]

messages = [
    {"role": "user", "content": "What's the weather like in Boston right now?"},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, tools=tools,
                                     add_generation_prompt=True, enable_thinking=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)

output = model.generate(**inputs, max_new_tokens=4096, temperature=1.0, top_p=0.95, do_sample=True)
print(tokenizer.decode(output[0][inputs.input_ids.shape[-1]:], skip_special_tokens=False))

示例输出

<think>
Okay, the user is asking for the weather in Boston right now. Let me check the tools
available. There's a function called get_current_weather that takes a city parameter.
I need to call that with the city set to Boston.
</think>
<tool_call>
<function=get_current_weather>
<parameter=city>
Boston
</parameter>
</function>
</tool_call>
<|im_end|>

带工具响应的多轮对话

messages = [
    {"role": "user", "content": "What's the weather like in Boston right now?"},
    {"role": "assistant", "content": "<think>\nThe user wants to know the current weather in Boston. I should call get_current_weather.\n</think>",
     "tool_calls": [{"function": {"name": "get_current_weather", "arguments": {"city": "Boston"}}}]},
    {"role": "tool", "content": '{"temperature": "72°F", "condition": "Partly cloudy", "humidity": "65%"}'},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, tools=tools,
                                     add_generation_prompt=True, enable_thinking=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)

output = model.generate(**inputs, max_new_tokens=4096, temperature=1.0, top_p=0.95, do_sample=True)
print(tokenizer.decode(output[0][inputs.input_ids.shape[-1]:], skip_special_tokens=False))

示例输出

<think>
The tool returned the weather data for Boston: temperature 72°F, partly cloudy, humidity 65%.
I need to present this information clearly to the user.
</think>
The current weather in Boston is 72°F, partly cloudy, with 65% humidity.<|im_end|>

多轮对话

历史思考截断

messages = [
    {"role": "user", "content": "What is 15 * 37?"},
    {"role": "assistant", "content": "<think>\nLet me calculate 15 * 37.\n15 * 37 = 15 * 30 + 15 * 7 = 450 + 105 = 555\n</think>\n15 * 37 = 555"},
    {"role": "user", "content": "Now divide that by 5"},
]

# Default: previous thinking is stripped to save context
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True,
                                     enable_thinking=True, truncate_history_thinking=True)

# To preserve full history:
text_full = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True,
                                          enable_thinking=True, truncate_history_thinking=False)

解析思考与最终答案

import re

def parse_model_output(text):
    """Separate thinking content from final answer."""
    think_match = re.search(r'<think>(.*?)</think>', text, re.DOTALL)
    if think_match:
        thinking = think_match.group(1).strip()
        answer_start = text.find('</think>') + len('</think>')
        answer_end = text.find('<|im_end|>', answer_start)
        answer = text[answer_start:answer_end].strip() if answer_end != -1 else text[answer_start:].strip()
    else:
        thinking, answer = "", text.strip()
    return thinking, answer

thinking, answer = parse_model_output(output_text)

与智能体编码框架配合使用

Granite 模型可以作为智能体编码工具的骨干。由于它们通过 OpenAI 兼容 API 支持推理和工具调用,因此无需额外适配器即可与流行的智能体框架集成。启动 vLLM 服务器,然后按照下面针对各框架的说明操作。

OpenCode

OpenCode 是一个在终端中运行的 AI 编码智能体。

安装:

curl -fsSL https://opencode.ai/install | bash

配置 ~/.config/opencode/opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "model": "local/granite-4.2-30b",
  "provider": {
    "local": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "vLLM (local)",
      "options": {
        "baseURL": "http://localhost:8000/v1",
        "apiKey": "EMPTY"
      },
      "models": {
        "granite-4.2-30b": {
          "name": "Granite 4.2 30B",
          "limit": {
            "context": 131072,
            "output": 8192
          }
        }
      }
    }
  }
}

运行:

opencode
opencode run "your task description"

完整文档请参阅 opencode.ai/docs。

Pi

Pi 是一个在终端中运行的极简 AI 编码智能体框架。它通过 models.json 配置文件支持自定义提供商。

安装:

curl -fsSL https://pi.dev/install.sh | sh

配置 ~/.pi/agent/models.json:

{
  "providers": {
    "vllm": {
      "baseUrl": "http://localhost:8000/v1",
      "api": "openai-completions",
      "apiKey": "EMPTY",
      "compat": {
        "supportsDeveloperRole": false,
        "supportsReasoningEffort": false
      },
      "models": [
        {
          "id": "granite-4.2-30b",
          "name": "Granite 4.2 30B",
          "reasoning": true,
          "input": ["text"],
          "contextWindow": 131072,
          "maxTokens": 8192,
          "samplingParams": {
            "temperature": 1.0,
            "top_p": 0.95
          },
          "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
        }
      ]
    }
  }
}

运行:

pi

然后在交互式会话中使用 /model 或 Ctrl+L 选择 granite-4.2-30b 模型。

完整文档请参阅 pi.dev/docs。

OpenHands

OpenHands 是一个 AI 软件工程师,可以规划、编写代码并执行命令。

  1. 按照官方安装指南安装并启动 OpenHands。

  2. 在 OpenHands 设置中配置 LLM:

    • 模型: granite-4.2-30b
    • Base URL: http://localhost:8000/v1
    • API Key: 你的 vLLM --api-key 值

注意: 连接到 vLLM 等 OpenAI 兼容端点时,需要 openai/ 前缀。详细设置说明、故障排除和替代安装方法请参阅 OpenHands 本地 LLM 文档。


资源:


最后更新:2026 年 9 月 11 日,以澄清基础模型。

来源:Hugging Face Blog · huggingface.co

00