Granite 4.2 大语言模型:构建过程解析
本文将从技术角度详细介绍 Granite 4.2 推理模型系列的构建过程。
作者:Granite Team,IBM
TL;DR:Granite 4.2 是我们首个纯 dense、仅 decoder 架构的推理 LLM 系列,提供 3B、8B 和 30B 三种规模。每个模型都从零开始预训练,使用约 15T tokens,并采用五阶段训练策略将上下文窗口扩展至 512K tokens;随后基于思维链、推理和 agent 轨迹数据进行监督微调,最后通过多阶段强化学习流程完成后训练。该流程包含 agentic RL:8B 和 30B 模型能够在真实的沙盒环境中使用工具并执行操作。所有模型都支持thinking / non-thinking切换,并提供low-effort思考模式,只为简单问题消耗少量推理预算;同时支持原生工具调用。所有 Granite 4.2 模型均以 Apache 2.0 许可证发布。
链接:
概述
Granite 4.2 是 Granite 语言模型系列中专注于推理的版本。早期 Granite 版本已经是出色的指令跟随助手,而 Granite 4.2 进一步加入了显式推理能力。每个模型都能在回答前生成思维链,并可根据任务所需的思考程度,在thinking或non-thinking模式下运行。介于两者之间的low-effort模式,会为简单问题分配少量推理预算。
这三个版本(3B、8B 和 30B)采用相同的架构设计和训练流程,只是规模不同:从头开始预训练、SFT,再进行多阶段 RL。三者都具备出色的推理和指令遵循能力。它们最明显的能力差异体现在后训练阶段。8B 和 30B 模型还会额外经过 agentic RL 阶段,学习在真实环境中以智能体方式工作,包括调用工具、编辑和运行代码、操作终端以及搜索网页。所有模型都原生支持工具调用。通过 OpenAI 兼容的端点(例如使用 vLLM)提供服务时,模型会以 OpenAI 函数调用格式输出工具调用结果,无需额外适配即可接入各种智能体框架。Granite 4.2 也支持 SGLang,具体可参考 SGLang cookbook 中可直接部署的配置。
下文将依次介绍整个构建过程:模型架构、预训练、监督微调、多阶段 RL 流程,以及最终结果。
模型架构
Granite 4.2 采用仅解码器的稠密 Transformer 架构,核心组件如下:
- 注意力机制:Grouped Query Attention(GQA),包含 40 个注意力头和 8 个 KV 头
- 位置编码:Rotary Position Embedding(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 采用从头训练的方式,基于约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 分片,并使用模型的 tokenizer 和聊天模板进行分词,准备大规模分布式训练。
在启动最终的大规模训练前,我们会在具有代表性的配置上调优超参数,测试学习率调度方案、初始学习率和预热比例,以找到适用于不同模型规模且能够稳定训练的配置。最终训练配置如下:
| 参数 | 取值 |
|---|---|
| 计算资源 | 32–128 个节点(取决于模型规模),每个节点配备 4× Grace/GB200 |
| 序列长度(打包后) | 131,072(128K) |
| 全局批次大小 | 128 |
| 学习率 | 1.0e-5,预热后保持不变;Phase 2 使用 3.0e-6 |
| 学习率预热 | train_iters 的 2.5% |
| 训练时长 | 约 2 个 epoch |
| 并行策略 | TP=2、PP=1、CP=4 或 CP=2 |
30B 模型的 Phase 2 SFT
对于 30B 模型,我们还会额外进行第二阶段 SFT,重点提升智能体编程能力。在这一阶段,我们对 agentic、SWE 和 coding 数据进行上采样,提高它们在训练数据分布中的实际占比;同时保留约 16% 的原始 SFT 语料作为 replay 数据。
随后,30B 模型以 3.0e-6 的较低学习率继续微调约一个 epoch。这个针对性更强的第二阶段,能够让模型接触更多智能体编程轨迹,同时保留初始 SFT 阶段获得的能力。
强化学习:多阶段、多环境训练流程
完成 SFT 后,我们会采用多阶段、多环境强化学习流程。不同于只进行一轮 RL,我们会在多个环境中依次开展一系列针对性训练,覆盖数学、代码、科学、指令遵循、工具使用和结构化输出,随后再进行软件工程、终端使用和网页搜索训练。每个阶段都是一次独立的 RL 训练,专门针对某项能力,并以上一阶段的 checkpoint 作为 warm start。
图 1:分阶段 RL 训练课程。基础 RL(可验证奖励 + 技能强化)适用于所有规模的模型;智能体 RL 模块(SWE → Terminal → Search)仅用于 8B 和 30B 模型。所有模型最终都会进行 RLHF。每个阶段都是一次独立的 GRPO 训练,并以上一阶段的 checkpoint 作为 warm start。
训练方法
每个阶段都采用异步 GRPO(Group Relative Policy Optimization)训练,因此生成器和训练器两部分不会相互阻塞。系统通过一组生成工作进程持续采样响应,并将完成的轨迹写入共享缓冲区;缓冲区积累到一个完整 step 所需的数据后,训练器便取出这批数据执行一次 optimizer step,再将更新后的参数流式传回生成工作进程,而不会暂停它们。参数刷新可能发生在一次 rollout 进行到一半时,使同一条轨迹由两个相邻版本的策略拼接而成。我们选择接受这种情况,而不是付出额外代价去避免它:工作进程会复用已有的 KV cache,无需每次刷新后重新构建;唯一的限制是,工作进程最多只能落后训练器一个更新,从而控制样本与当前策略的偏离程度。即使仍有少量偏差,也会由目标函数中的截断重要性采样处理:将训练阶段与生成阶段的对数概率比限制在固定上限内,避免少数过时 token 主导一次更新。
优势值采用组相对计算,并使用留一法基线:每个响应都与同一提示词下其他样本的平均奖励进行比较,因此无需额外的 value network。以RLVR为例,这是第一个、持续时间最长的阶段:每个 step 取256 个提示词,每个提示词采样16 个响应,组成包含4,096 个样本的 batch。训练器在下一次 rollout 开始前,用一个 optimizer step 完成这批数据的处理。后续阶段沿用同一套机制,只调整各阶段的数据规模,具体如下。
RL 训练配置
整个流程在所有阶段共用一套基础超参数,因此更容易执行和比较这套 curriculum。以下几个参数在各阶段保持不变:
| 参数 | 取值(所有阶段共用) |
|---|---|
| 算法 | GRPO(不使用 value network;采用组相对优势值) |
| 训练栈 | NeMo-RL(Megatron-Core + vLLM)和 NeMo-Gym 环境 |
| Ratio clip(最小值 / 最大值) | 0.2 / 0.28 |
| Micro-batch size | 1 |
| 并行方式 | tensor-parallel 2–4;不使用 pipeline-parallelism 或 context-parallelism |
不同阶段的变化,主要体现在每轮运行的形态上:每步处理多少提示词和生成结果、上下文有多长、是否运行 agent 循环,以及训练时向参考策略回拉的力度。下表列出了 30B 模型各阶段的具体配置:
| 阶段 | 提示词/步 | 每个提示词的生成数 | 最大序列长度 | Rollout 轮数 | KL | LR |
|---|---|---|---|---|---|---|
| RLVR(×3) | 256 | 16 | 64K | 1 | 0 | 5e-7 |
| IF booster | 256 | 16 | 64K | 1 | 0 | 5e-7 |
| Code booster | 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 |
| Terminal | 8 | 32 | 64K | 64 | 0.01 | 1e-6 |
| Search | 32 | 16 | 128K | 64 | 0.01 | 5e-7 |
| RLHF | 128 | 16 | 48K | 1 | 0.05 | 5e-7 |
以上参数针对 30B 模型。全局 batch size = 提示词数/步 × 每个提示词的生成数(例如 RLVR 为 256 × 16 = 4096)。3B 和 8B 模型使用相同的训练方案和超参数,但阶段更少(参见 三种规模有何不同);变化的是阶段列表,而不是具体参数。
KL 调度会根据奖励类型调整:对于客观且可验证的奖励,允许模型充分探索(RLVR 和 SWE 2 的 KL 都为 0);对于偏好、安全性或较窄的技能增强,则让模型尽量贴近参考策略(RLHF 和 Code booster 的 KL 为 0.05)。Rollout 轮数统计的是每次 rollout 中,GRPO 本身看到的环境交互次数。在所有阶段中,模型训练使用的都是完整、真实环境中的轨迹。
分阶段课程
每个阶段都是一次独立的 RL 训练,设有单一目标和专属奖励信号。阶段结束后,策略会导出为 Hugging Face 格式,并作为下一阶段的基础模型,因此整个流程实际上是一连串 warm-start:
SFT ─▶ RLVR ─▶ Skill boosters ─▶ SWE agent ─▶ Terminal ─▶ Search ─▶ RLHF
└──────── foundational RL ────────┘ └──────── agentic RL (8B / 30B) ────────┘
8B 和 30B 模型会完整走完这条流程。3B 模型则采用精简路径:只进行 foundational RL 和对齐训练,不包含 agentic RL 阶段。
奖励信号
一个阶段的核心定义,主要取决于如何提供奖励。整个流程涉及三类奖励,而且同一阶段可以同时使用多种奖励:
| 奖励类型 | 衡量内容 | 应用阶段 |
|---|---|---|
| 可验证奖励 | 精确匹配、单元测试、格式检查器,以及基于标准答案的规则检查器 | RLVR · boosters · SWE |
| 奖励模型 / LLM 评审 | 开放式任务的质量、偏好、安全性和答案正确性 | RLVR · Search · RLHF |
| Agentic 结果奖励 | 模型是否真的在真实环境中完成了任务 | SWE · Terminal · Search |
可验证奖励客观且难以钻空子,因此会在流程前段优先使用。对于检查器无法表达的开放式质量,则采用基于评审和偏好的奖励。Agentic 结果奖励最为稀疏:在一段漫长的工具调用轨迹结束时,往往只有一个代表成功或失败的比特值。
Foundational RL:构建能力
RLVR:基于可验证奖励的 RL
RLVR 是整个流程的基础阶段,也是数据混合最广的一环:它使用一个涵盖多种可验证领域的统一混合数据集。
- 数学:通过带框答案检查的 chain-of-thought,以及使用 Lean 进行形式化证明
- 竞赛编程:在 sandbox 中通过隐藏测试检查解答
- STEM / 研究生级科学选择题和通识知识
- 指令遵循:结构化输出和逆向指令任务
- 工具 / 函数调用:单步工具使用
- 推理谜题和拒答(知道何时应当拒绝)
每种任务类型都有对应的验证器,因此每个样本的奖励都有明确依据。RLVR 在 3B 和 8B 上运行两轮,在 30B 上运行三轮。每一轮都会从上一次结果重新热启动,并在重新加权后的公开数据与内部整理 RL 数据混合集上训练。
技能增强:定向提升
RLVR 之后,还会进行几轮简短的 增强训练(booster),集中强化以下领域中适合专项训练的能力:
- 指令遵循(IF):多轮对话、inverse-IFEval、结构化输出
- 代码:仅针对竞赛编程
增强训练规模较小、目标明确。训练过程中加入较弱的 KL 惩罚,使模型保持当前行为,同时定向提升某一项能力。
智能体 RL:学会行动(8B / 30B)
在智能体训练阶段,模型学习如何行动:调用工具、观察结果,并在真实环境中不断迭代,最终是否真正完成任务决定了它获得的奖励。这些阶段采用相同的训练形式:多轮工具调用、真实而非模拟的环境、稀疏的结果奖励,以及从代码增强检查点热启动的 GRPO。训练按以下顺序进行:SWE → Terminal → Search。
图 2。三种智能体 RL 环境。每种环境都将真实的执行框架与真实环境结合起来,并采用稀疏的结果导向奖励。3B 模型不参与这些训练。
- SWE 智能体(软件工程)。每个任务都是一个位于独立沙箱中的真实代码仓库。在 OpenHands 执行框架驱动下,模型会读取代码、修改文件,并在多轮内部交互中运行测试套件。奖励是可验证的:隐藏测试是否全部通过?任务来自开源 SWE 数据集,每个样本都有对应代码仓库的容器镜像。
- 终端代理(终端 / 操作系统)。通过 Harbor / Terminus-2 agent harness,在实时 shell 中执行多步骤任务。模型会规划一系列命令、观察输出,并从错误中恢复。任务成功完成后,系统才会给予奖励。这是唯一在 GRPO 层面驱动多轮代理循环的阶段,每次 rollout 最多包含 64 轮环境交互。
- 搜索代理(深度研究)。模型在浏览代理循环中调用实时网页搜索工具,回答复杂的多跳问题:跨多个步骤搜集证据、进行推理,最终生成答案。由于这类任务的正确性没有固定标准,系统会使用 LLM 对最终答案进行评判并给出奖励。
对齐:RLHF
每个模型的最后阶段都是面向人类偏好和安全性的 RLHF。这一阶段通过生成式奖励模型(GenRM)优化人类偏好,同时加入安全奖励,提升模型抵御越狱攻击和适当拒答的能力。该阶段使用整个训练流程中最高的 KL 惩罚,在不削弱前序阶段所获得能力的前提下,进一步调整模型的表达风格与安全性。此外,它还会对推理长度进行惩罚,抑制模型在前序阶段形成的过度冗长推理习惯。
三个规模版本有何不同
三者采用相同的方法和基础设施,区别在于每个模型训练所推进的阶段深度不同。
| 阶段 | 3B | 8B | 30B |
|---|---|---|---|
| RLVR(可验证) | ×2 | ×2 | ×3 |
| 技能强化 | 代码 | IF · GPQA · 代码 | IF · 代码 |
| SWE 代理 | — | ✓ | ✓ |
| 终端代理 | — | ✓ | ✓ |
| 搜索代理 | — | ✓ | ✓ |
| RLHF(偏好 + 安全) | ✓ | ✓ | ✓ |
3B 是一款能力出色的基础 RL 模型;8B 和 30B 则在此基础上加入 agentic RL 模块,学会在真实环境中使用工具执行任务。
支持大规模 RL 的 Agentic AI 基础设施
如此大规模的强化学习,需要一套能同时驱动训练循环和大批在线环境的基础设施。这一点在智能体阶段尤为重要:每条训练样本都是一次多轮 rollout,模型需要编辑代码、运行命令或浏览网页。Granite 4.2 的 RL 构建在两个开放组件之上:负责训练的 NeMo-RL,以及负责 rollout 的 NeMo-Gym。
图 3. RL 系统。NeMo-RL 驱动 GRPO 循环:训练后端采用 Megatron-Core,生成由 vLLM 负责,Megatron-Bridge 则用于在 HF⇄Megatron 之间转换权重。NeMo-Gym 负责编排 rollout,并将工具、沙箱以及奖励/验证器调用作为可插拔的 Resources 托管其中。
具体分工如下:
- NeMo-RL(训练侧)。训练后端采用 Megatron-Core;vLLM 负责生成 rollout;Megatron-Bridge 在 Megatron 和 Hugging Face 格式之间转换权重,使每个阶段都能为下一阶段导出规范的 HF checkpoint。
- NeMo-Gym(rollout 侧)。它通过统一接口,将每个环境暴露为一组 Resources,包括验证器、工具、沙箱和奖励模型。这正是智能体阶段的接入点:SWE 代码仓库沙箱、终端测试框架和网页搜索工具都挂载于此;对训练循环而言,它们与简单的数学验证器没有区别。
正是这种统一性,让前面介绍的分阶段课程训练得以落地:booster 阶段的规则检查器和完整的 SWE 沙箱,都能通过同一接口接入 GRPO。
这种训练侧与 rollout 侧的拆分,也让前文所述的异步训练循环成为现实:生成和策略更新分别运行在不同的 GPU 资源池中,因此成本高昂的生成集群——包括在线智能体环境——可以持续工作,不会在优化器执行期间闲置。
结果
Granite 4.2 在智能体编程、通用智能体与工具调用、推理、聊天与指令遵循以及长上下文等方面进行了评测。下方是完整的基准测试结果,随后还会按模型规模拆分展示主要结果。
| 任务 | 3B Dense | 8B Dense | 30B Dense |
|---|---|---|---|
| 智能体(编程) | |||
| SWE Bench Multilingual | NA | 30.78 | 41.89 |
| SWE Bench Pro | NA | 19.11 | 33.29 |
| SWE Bench Verified | NA | 47.67 | 57.00 |
| Terminal-Bench 2.1 | NA | 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 | NA | 41.07 | 41.85 |
| GDPval | NA | 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:Agentic coding 解决率。Agentic-RL 模块仅针对 8B 和 30B 模型训练;其中 30B 模型在各个 SWE-Bench 变体和 Terminal-Bench 上均表现领先。
图 6:通用 Agentic 能力和工具使用基准测试,涵盖全部三种模型规模。
量化
我们还发布了四个 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 模型使用标准 llama.cpp 工具转换为 GGUF,具体方法参见 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-3b"
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>
好,我们来看看。问题是要找出单词“strawberry”中有多少个字母“r”。
首先,把这个单词写出来:s t r a w b e r r y。
接下来,数一数其中“r”的数量。我们逐个检查每个字母,看看是不是“r”。
1. s——不是 r
2. t——不是 r
3. r——是的,这是第一个
4. a——不是
5. w——不是
6. b——不是
7. e——不是
8. r——是的,这是第二个
9. r——是的,这是第三个
10. y——不是
“r”的总数 = 3。
</think>
单词“strawberry”中有 **3** 个字母“r”。<|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>
工具返回了波士顿的天气数据:气温 72°F,局部多云,湿度 65%。
我需要将这些信息清晰地呈现给用户。
</think>
当前波士顿天气:气温 72°F,局部多云,湿度 65%。<|im_end|>
多轮对话
截断历史思考内容
messages = [
{"role": "user", "content": "15 * 37 等于多少?"},
{"role": "assistant", "content": "<think>\n让我计算一下 15 * 37。\n15 * 37 = 15 * 30 + 15 * 7 = 450 + 105 = 555\n</think>\n15 * 37 = 555"},
{"role": "user", "content": "现在把结果除以 5"},
]
# 默认情况下会移除之前的思考内容,以节省上下文空间
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True,
enable_thinking=True, truncate_history_thinking=True)
# 如需保留完整历史记录:
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):
"""将思考内容与最终答案分离。"""
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)
搭配 Agentic Coding Harness 使用
Granite 模型可以作为 Agentic Coding 工具的核心。由于它们通过兼容 OpenAI 的 API 支持推理和工具调用,因此无需额外适配器,就能接入主流 Agentic Harness。启动 vLLM 服务器后,按照下面对应 Harness 的说明操作即可。
OpenCode
OpenCode 是一款运行在终端中的 AI 编程 Agent。
安装:
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 密钥: vLLM 的
--api-key值
- 模型:
注意: 连接 vLLM 等兼容 OpenAI 的端点时,必须添加
openai/前缀。有关详细的配置步骤、故障排查和其他安装方式,请参阅 OpenHands 本地 LLM 文档。
资源:





