← 文章 / AI技术
HuggingFace博客 8小时前 · 2026-08-29 20:45:15 · 0 阅读

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 进一步加入了显式推理能力。每个模型都能在回答前生成思维链,并可根据任务所需的思考程度,在thinkingnon-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 的评审器,对样本质量进行评估。低分样本,以及包含幻觉或虚构信息、无效工具交互,或调用对应工具列表中未定义函数的样本,都会被移除。在此基础上,我们还会根据需要应用针对特定数据集的启发式规则,进一步提升数据质量并清除已知噪声来源。

最后,我们会分别进行局部和全局去重。去重依据是对 toolsmessages 字段组合计算得到的 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。

Granite 4.2 staged RL curriculum

图 1:分阶段 RL 训练课程。基础 RL(可验证奖励 + 技能强化)适用于所有规模的模型;智能体 RL 模块(SWE → Terminal → Search)仅用于 8B 和 30B 模型。所有模型最终都会进行 RLHF。每个阶段都是一次独立的 GRPO 训练,并以上一阶段的 checkpoint 作为 warm start。

训练方法

每个阶段都采用异步 GRPOGroup 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

Agentic RL environments

图 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

NeMo-RL + NeMo-Gym system architecture

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

具体分工如下:

  • NeMo-RL(训练侧)。训练后端采用 Megatron-CorevLLM 负责生成 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.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:Agentic coding 解决率。Agentic-RL 模块仅针对 8B 和 30B 模型训练;其中 30B 模型在各个 SWE-Bench 变体和 Terminal-Bench 上均表现领先。

General agentic and tool-use benchmarks

图 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 软件工程师,能够制定计划、编写代码并执行命令。

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

  2. 在 OpenHands 设置中配置 LLM:

    • 模型: granite-4.2-30b
    • 基础 URL: http://localhost:8000/v1
    • API 密钥: vLLM 的 --api-key

注意: 连接 vLLM 等兼容 OpenAI 的端点时,必须添加 openai/ 前缀。有关详细的配置步骤、故障排查和其他安装方式,请参阅 OpenHands 本地 LLM 文档


资源:

原始来源: HuggingFace博客

评论 (0)