IBM 发布 Granite 4.2 推理模型家族:3B、8B、30B 三档
Granite 4.2 LLMs: How They're Built
IBM Granite 团队发布 Granite 4.2,这是其首个稠密、decoder-only 的推理模型家族,包含 3B、8B、30B 三个规模,全部采用 Apache 2.0 许可。
原文完整披露 Granite 4.2 的三档规模、五阶段预训练与分阶段 RL 流程,可对照理解推理模型的后训练工程。
关于我们如何构建 Granite 4.2 推理模型家族的技术解析。
作者:Granite 团队,IBM
TL;DR:Granite 4.2 是我们首个稠密、仅解码器推理大模型家族,发布了三种规模:3B、8B 和 30B。这些模型是在 Granite-4.1 基础模型上进行后训练的。Granite-4.1 基础模型从零开始预训练,使用约 15T token,采用五阶段策略将上下文窗口扩展至 512K token,在思维链、推理和 agentic 轨迹数据上进行监督微调,随后通过多阶段强化学习流水线进行后训练。该流水线包含 agentic RL,其中 8B 和 30B 模型在真实沙盒环境中学习使用工具行动。每个模型都具备思考 / 非思考开关、低投入思考模式(在简单问题上花费较短的推理预算),以及原生工具调用能力。所有 Granite 4.2 模型均在 Apache 2.0 许可下发布。
链接:
概览
Granite 4.2 是 Granite 语言模型家族中专注于推理的版本。早期的 Granite 版本是强大的指令跟随助手;Granite 4.2 增加了显式推理能力。每个模型都可以在给出答案前生成思维链,并可根据任务所需的思考程度以思考或非思考模式运行。低投入模式介于两者之间,在简单问题上花费较短的推理预算。
这三种规模(3B、8B 和 30B)共享相同的架构设计,并遵循相同的训练流水线(基础模型、SFT,然后多阶段 RL),各自按自己的规模进行。三者都是强大的推理者和指令跟随者。最明显的能力差异出现在后训练阶段。8B 和 30B 模型还额外经过一个 agentic RL 模块,教它们作为智能体运作:调用工具、编辑和运行代码、操作终端,以及在真实环境中搜索网页。每个模型都支持原生工具调用。通过 OpenAI 兼容端点(例如使用 vLLM)提供服务时,它会以 OpenAI 函数调用格式发出工具调用,无需额外胶水代码即可接入 agentic 框架。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 Dense | 8B Dense | 30B Dense |
|---|---|---|---|
| 嵌入维度 | 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%)。这些样本和轨迹使用多种智能体脚手架和 harness 生成,包括 OpenHands、OpenCode、Terminus-2、SWE-agent、OpenResearcher、MiniSWE、OpenSeeker、EnvScaler、Gemini CLI、Hermes、Codex 和 Goose。智能体数据结合了来自开源数据集和我们自己合成的 RL 环境的样本,涵盖多种智能体–harness 组合。
非智能体语料由几个主要类别组成:指令遵循(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 分片,使用模型的 tokenizer 和聊天模板进行分词,并为大规模分布式训练做好准备。
在启动最终的大规模训练之前,我们在具有代表性的配置上对超参数进行调优,遍历学习率调度、初始学习率和预热比例,以找到能够在不同模型规模下稳定训练的配置。最终训练配置总结如下:
| 参数 | 值 |
|---|---|
| 计算资源 | 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 运行,针对某一项能力,并从上一阶段的检查点热启动。
图 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 | LR |
|---|---|---|---|---|---|---|
| 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。
图 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 |
| 技能增强 | code | IF · GPQA · code | IF · code |
| SWE 智能体 | — | ✓ | ✓ |
| 终端智能体 | — | ✓ | ✓ |
| 搜索智能体 | — | ✓ | ✓ |
| RLHF(偏好 + 安全) | ✓ | ✓ | ✓ |
3B 是强大的基础 RL 模型;8B 和 30B 在其上增加了智能体 RL 模块,学习在真实环境中使用工具行动。
面向可扩展 RL 的智能体 AI 基础设施
如此规模的强化学习需要能够同时驱动训练循环和一组实时环境的基础设施。这在智能体阶段最为重要,因为每个训练示例都是编辑代码、运行命令或浏览网络的多轮 rollout。Granite 4.2 的 RL 运行在两个开源组件上:训练侧的 NeMo-RL 和 rollout 侧的 NeMo-Gym。
图 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 检查点。
- NeMo-Gym(rollout 侧)。 它将每个环境作为一组 Resources(验证器、工具、沙箱和奖励模型)暴露在统一接口之后。这是智能体阶段的插接点:SWE 仓库沙箱、终端运行环境和网络搜索工具都接在此处,并且对训练循环而言,它们与一个简单的数学验证器看起来并无不同。
正是这种统一性使上述分阶段课程切实可行:增强器的基于规则的检查器和一个完整的 SWE 沙箱向 GRPO 呈现相同的接口。
这种拆分也正是上文所述异步训练循环在物理上可行的原因:生成和策略更新位于不同的 GPU 资源池上,因此昂贵的生成集群——包括实时智能体环境——始终忙碌,而不会在优化器步骤期间空闲。
结果
Granite 4.2 在智能体编码、通用智能体与工具使用、推理、聊天与指令遵循以及长上下文方面进行了评估。完整的基准测试表如下,随后是按模型规模分列的头部结果图表。
| 任务 | 3B Dense | 8B Dense | 30B Dense |
|---|---|---|---|
| 智能体(编码) | |||
| SWE Bench 多语言 | 不适用 | 30.78 | 41.89 |
| SWE Bench Pro | 不适用 | 19.11 | 33.29 |
| SWE Bench Verified | 不适用 | 47.67 | 57.00 |
| Terminal-Bench 2.1 | 不适用 | 20.56 | 29.24 |
| 智能体(通用) | |||
| τ³-bench | 45.78 | 58.06 | 62.00 |
| BFCL (v4) | 52.41 | 50.29 | 61.39 |
| ProfBench | 32.10 | 41.20 | 42.90 |
| BirdBench | 不适用 | 41.07 | 41.85 |
| GDPval | 不适用 | 1189.00 | 1225.00 |
| 推理 | |||
| AIME25 | 78.33 | 86.67 | 89.17 |
| HMMT Feb25 | 66.67 | 78.33 | 89.17 |
| GPQA | 54.80 | 64.14 | 66.41 |
| LiveCodeBench v6 | 69.71 | 73.24 | 75.77 |
| SciCode | 24.11 | 36.09 | 38.76 |
| 聊天与指令遵循 | |||
| MMLU-Pro | 67.84 | 74.04 | 77.60 |
| MMLU-ProX lite (IBM) | 27.78 | 61.06 | 66.64 |
| Arena-Hard-V2 | 34.96 | 65.19 | 67.93 |
| IFBench (prompt) | 74.33 | 79.33 | 77.17 |
| 长上下文 | |||
| RULER 64K | 67.52 | 80.99 | 89.96 |
| RULER 128K | 55.30 | 71.41 | 81.38 |
支持的语言:英语、德语、西班牙语、法语、日语、葡萄牙语、阿拉伯语、捷克语、意大利语、韩语、荷兰语和中文。
以下图表按能力领域分解了这些结果。
图 4. 推理(pass@1)。在数学(AIME25、HMMT)、科学(GPQA)和代码推理(LiveCodeBench、SciCode)方面,分数随模型规模的增大而持续上升。
图 5. 智能体编码解决率。智能体强化学习模块仅针对 8B 和 30B 训练;30B 模型在 SWE-Bench 各变体和 Terminal-Bench 上均领先。
图 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 wheel 和特定于 GPU 的二进制文件。大规模 SFT 运行基于 NGC PyTorch 基础镜像(Ubuntu 22.04、CUDA 12.8、Python 3.12);强化学习栈在其自己的 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 函数定义架构 进行定义。
基础工具调用
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 软件工程师,能够规划、编写代码并执行命令。
按照 官方安装指南 安装并启动 OpenHands。
在 OpenHands 设置中配置 LLM:
- 模型:
granite-4.2-30b - 基础 URL:
http://localhost:8000/v1 - API Key: 你的 vLLM
--api-key值
- 模型:
注意: 连接到像 vLLM 这样的 OpenAI 兼容端点时,需要
openai/前缀。有关详细设置说明、故障排除和替代安装方法,请参阅 OpenHands 本地 LLM 文档。
资源:
- Granite 4.2 HF 合集
- GitHub:ibm-granite/granite-4.2-language-models
- Granite 文档
- Granite 社区资源
- Code Alchemy
最后更新:2026 年 9 月 11 日,以澄清基础模型。
来源:Hugging Face Blog · hf-mirror.com





