
Nemotron Lightning を SFT と Capability Card でチームアシスタント専用の判定役に仕立て直してみた
はじめに
こんにちは、クラスメソッド製造ビジネステクノロジー部の森茂です。
前回の記事では、NVIDIA の Nemotron 3.5 Lightning を LoRA SFT で、コーディングエージェント向けルーターの判定役(judge)に仕立てました。その結びで「実運用の第 1 号は常駐エージェント」と書いたとおり、このコーディング用 judge を、Slack で動くチームアシスタントの判定にもそのまま流用していました。
このアシスタントに来る依頼の大半は、課題の一覧や朝会サマリーのような定型です。ローカルで動かしている安いモデル(weak)で十分こなせます。一方で、施策の壁打ちのような熟慮系の依頼を weak に流すと、もっともらしい一般論が返ってきて、人がそれを信じてしまう。そこで依頼ごとに「weak で足りるか、高いモデル(strong)が要るか」を見極める judge を挟んでいて、この判定の精度が応答の質とコストの両方の上限を決めています。
この大事な判定役が、コーディング用の流用のままで本当にいいのか。今回はこの問いに実測で答えを出すため、常駐アシスタント専用の評価セットを作り、専用の judge を同じ Nemotron 3.5 Lightning から仕立て直しました。
先に結論を書いておくと、コーディング用 judge の流用は専用評価 189 件で妥当性検査の通過が 84%、壁打ちや戦略相談を安いモデルに流す重大取りこぼしが 12 件でした。実行環境の知識を Capability Card という 1 枚の文書に外出しし、weak の実測成功率を教師にした専用データ 2,122 件で SFT すると、通過 100%・重大取りこぼし 1〜2 件・判定中央値 1.57 秒になり、web 検索の能力追加は再学習なしで Card の書き換えだけで判定に反映できました。
前作の続きにあたる内容なので、LoRA SFT のレシピや NVFP4 量子化の手順は前作を、常駐アシスタント側の仕組みは NemoHermes の記事をご覧ください(それぞれ 2026-08、2026-06 時点の記事です)。
この記事では、実行環境を Capability Card として書き出す設計、実測ラベルの教師データ作り、そして web 検索の能力追加が Card の書き換えだけで判定に反映されるかの確認を紹介します。エージェントの判定役や分類役を自分のドメインに仕立てたい人に刺さるといいなと思っています。
コーディング用 judge の流用は 84% で頭打ちだった
対象の常駐アシスタントは、Slack のメンションを受けてチームの Backlog スペースの課題を読み、担当者別の一覧や朝会サマリーを返す read-only のアシスタントです。ルーター(NeMo Switchyard)が依頼ごとに judge を呼び、judge は「weak がその依頼を一発で完遂する確率」である p_solve と、どの判定ルールに当たるかを示す判定カテゴリを JSON で返します。ルーターはこの p_solve を閾値と比べて、依頼を weak か strong へ流します。
judge に見えるのは、依頼文と直近 4 ターンの会話、それに実行環境の情報を 1 枚にまとめた Capability Card だけです。Card の中身は次章で説明するとして、構成は前作と同じで、今回手を入れるのは judge だけです。
見極めたいのは、この判定役をコーディング用 judge のままにしてよいかです。そこで常駐アシスタント用に評価セット 189 件を凍結し(作り方は後述)、コーディング用 judge の流用と、事後学習していない素の Lightning に Card だけ渡した場合を同じ条件で測りました。指標は 4 つです。妥当性検査の通過は judge の JSON が形式を守った件数、判定カテゴリの一致は正解ラベルと同じ判定ルールに落ちた割合、weak 維持率は weak で足りる依頼を weak に留めた割合です。そして、壁打ちや戦略相談のような strong へ回すべき依頼を weak に流した件数を、重大(critical)な取りこぼしと定義しました。
| 指標 | 素の Lightning + Card | コーディング用 judge(流用) |
|---|---|---|
| 妥当性検査の通過 | 52/189(27.5%) | 159/189(84.1%) |
| 判定カテゴリの一致 | 5.8% | 74.8% |
| 重大取りこぼし | 1 件 | 12 件 |
| weak 維持率 | 0% | 87.9% |
素の Lightning は、判定ルールと判定カテゴリの対応をそもそも知らないため 73% が invalid でした。フェイルセーフでほぼ全件 strong に落ちるので事故は起きませんが、weak 維持率 0%、全部高いモデルへ行くのではルーターの意味がありません。前作で見た「素の Lightning は judge にならない」という構図が、環境情報を Card として渡しても変わらなかったわけです。
一方の流用 judge は形式面こそ 84% 通るものの、語彙がコーディングの世界のままでした。コーディング用の学習では壁打ちを「どの判定ルールにも該当しない」に置いていたのに対し、アシスタントのドメインでは壁打ちこそ主戦場です。結果、一番守りたい境界で重大取りこぼしが 12 件出ました。
つまり、環境情報を伝える Card は判定の材料にはなるが、判定の振る舞いそのものは作れない。振る舞いは SFT で作る。この二層を分けて考えるのが、今回の設計の軸になります。
環境情報のすべてを Capability Card に書く
judge には、実行側エージェントの system prompt もツール定義も渡しません。その代わり、judge が評価すべき実行環境の情報を Capability Card という 1 枚の文書にまとめ、judge の system prompt に描画します。judge にとって、環境情報はこの Card がすべてです。
使う場所は 1 か所だけです。描画した Card を、Switchyard の route 設定にある judge 用の prompt に丸ごと入れます。ルーターは依頼が来るたびに、この Card を system メッセージ、Slack の依頼文と直近ターンを user メッセージにして judge を呼び、返ってきた JSON の p_solve を閾値と比べて weak か strong を選びます。流れを図にすると次のとおりです。
judge へのリクエストと返答の形は次のとおりです(抜粋)。
{
"messages": [
{ "role": "system", "content": "<描画した Capability Card 約 11,000 字>" },
{ "role": "user", "content": "TASKHUB の処理中課題を一覧して" }
],
"temperature": 0,
"response_format": { "type": "json_schema", "json_schema": { "name": "CapabilityClassifierDecision", "strict": true } }
}
{
"crux": "有効な Backlog skill で処理中の課題を取得し、一覧に整形する",
"primary_rule": "SUP-1",
"capability_boundary": "supported",
"p_solve": 0.77
}
返答の 4 フィールドは前作と同じ判定契約です。実行側の weak や strong に Card は渡りません。依頼はそのまま実行モデルへ流れ、Card を読むのは judge だけです。
Card は 2 部構成です。
| 部位 | 中身 | 例 |
|---|---|---|
| 固定部 | アシスタントの役割、成功条件(SUCCESS)、判定ルールの定義 | 「壁打ち・戦略は strong 固定側のルール」 |
| 動的部(環境 profile) | skill の有効と無効、read-only の別、既知 workflow、書き込みの扱い | 「Backlog skill 有効・GET のみ」「web 検索 無効」 |
固定部の要は成功条件、SUCCESS の定義です。正しい skill を選び、取得データが依頼と一致し、最終的に Slack の返信が届くところまで満たして成功。根拠のないもっともらしい一般論は、文章がどれだけ良くても失敗と数えます。judge が出す p_solve は「この SUCCESS が自然に達成される頻度」であって、判定モデルの自信ではありません。ここを曖昧にすると、次章のラベル作りが揺れます。
動的部の環境 profile は JSON で持ち、テンプレートから system prompt を機械描画します。描画後で約 11,000 字、単発の依頼なら prompt 全体で 2.6k トークンほど。静的な文書なので vLLM の prefix cache が効き、判定レイテンシにはほぼ乗りません。
この profile を差し替えられる構造が、設計の中心にある対照ペア(counterfactual pair)を支えます。同じ依頼文に対して Card の profile だけを変えたペアを、学習データにも評価セットにも入れるのです。たとえば「TASKHUB の処理中課題を一覧して」という依頼は、Backlog skill が有効な Card では「できる」、無効な Card では「できない」が正解になります。これを判定できる judge は、依頼文のパターンを暗記しているのではなく Card を読んで判定している、と言える仕掛けですね。評価では 20 ペアの「Card 感度」として測ります。
名前と移植性について補足しておきます。Capability Card という名前は自分が付けたもので、A2A プロトコルの Agent Card(エージェントが自分の能力を JSON で宣言する仕組み)に近い発想です。違いは、実行側の環境を judge に読ませるための文書で、成功条件と判定ルールまで含めている点です。また、実体は judge 向けの system prompt なので Switchyard 専用ではありません。OpenAI 互換の judge エンドポイントを呼べて p_solve で分岐できるルーターや自前の proxy なら、同じ Card を載せ替えられます。Switchyard 固有なのは、判定 JSON の契約と、閾値や fail-open の扱いのほうです。
教師データは weak の実測成功率から作る
前作はコーディングベンチマークが対象だったので、weak モデルの実測正解率をテストの合否で機械的に測れました。今回のドメインの成果物は「Slack の返信」なので、正解率の測り方から作る必要があります。教師データは 3 層構成にしました。
| 層 | 中身 | 規模 |
|---|---|---|
| 評価セット(凍結) | 人手レビューを通した Golden Set から凍結した frozen eval | 189 件 |
| 学習用の合成データ | 依頼文とスレッドを 2 モデルで生成した、対照ペア中心の合成 | 1,607 件 |
| p_solve の実測ラベル | weak モデルに依頼を実際に解かせた replay | 312 run |
1 層目は試験問題です。Golden Set 458 件をレビューして 189 件を評価用に凍結し、前作と同じくハッシュ照合で学習データから除外しました。
2 層目の合成は NeMo Data Designer を使い、アシスタントに来がちな 34 種の依頼パターン(課題取得、朝会サマリー、壁打ち、曖昧な依頼、書き込み依頼など)を生成モデル 2 つで作り、Card だけ差し替えた対照ペアをここでも組にしています。生成した依頼は教師モデルに判定させ、想定ラベルと一致したものだけを残す教師フィルタを通しました。このフィルタには後で足をすくわれるのですが、それは次章で。
3 層目が今回の要です。p_solve のラベルには、weak モデル(実運用と同じ deepseek-v4-flash-0731)に評価セットの依頼を実際に解かせた実測を使います。実務のスペースは使えないので、課題 203 件の fixture を持つ架空 project のモック Backlog 環境を用意して 312 run を replay し、8 項目の採点基準で成功と失敗を付けました。検証用の実 API スペースでの 33 run も別途流し、モックの採点が実環境と食い違わないことを確かめています。こうして出した系統ごとの成功率を、学習データの p_solve ラベルに流し込みます。
採点基準の 8 項目目は、途中で足したものです。当初の 7 項目(正しい skill 選択、引数、根拠づけなど)で採点すると、「read-only なので実行できません、と正しく報告した」run が成功と数えられ、書き込み系の依頼なのに成功率が 0.6 を超える逆転が出ました。丁寧に断ることと依頼を完遂することは別の話です。8 項目目に「依頼の完遂」を足すと、能力外の系統の成功率が 0 に張り付き、判定カテゴリと p_solve の整合が取れました。
| 依頼の系統(抜粋) | weak の実測成功率 |
|---|---|
| 課題の取得(skill 有効) | 0.77 |
| 既知 workflow(朝会サマリーなど) | 0.61 |
| 壁打ち・戦略 | 0.31 |
| 書き込み・skill 無効・読めない添付 | 0.00 |
このうち壁打ちと戦略系の 47 run だけは、回答品質の採点を自分の目で行いました。LLM による採点との一致は 45 件中 25 件で、この領域を機械任せにすると系統的にずれます。
配合を変えて 3 回学習し、専用データが勝った
学習レシピは前作の再利用です(Megatron-Bridge の LoRA SFT、rank 8 / alpha 32 / 2 epoch)。変えたのは 3 点で、Slack スレッドを扱うためにシーケンス長を 3072 から 4096 へ伸ばし、入力を単発の指示文ではなく実際のメッセージ列にし、tool 呼び出しの引数を文字列ではなく構造化した形で持たせました。ハードは NVIDIA Brev で借りた H100 2 枚で、学習は 3 回合わせて 25 ドルほどです。
比べたのは、データの配合が違う 3 回の学習です。1 回目は専用データのみの 1,659 件。2 回目はそこへコーディング用の学習データを 500 件混ぜたものです。以前の学習データを一部混ぜて元の能力の忘却を防ぐ、rehearsal と呼ばれる手法で、前の judge の能力を残せるなら 1 モデル 2 役も狙えます。3 回目は、1 回目に残った取りこぼし 4 件を狙い撃つ追加データ 402 件を足したもので、不確実例の複製を含めて 2,122 件になります。
| 指標 | 専用データのみ | + coding rehearsal | + 狙い撃ち追加(採用) |
|---|---|---|---|
| 妥当性検査の通過 | 189/189 | 187/189 | 189/189 |
| 判定カテゴリの一致 | 87.3% | 88.2% | 92.1% |
| 重大取りこぼし | 4 件 | 6 件 | 2 件 |
| うち壁打ち・戦略 | 2 件 | 5 件 | 2 件 |
まず rehearsal は逆効果でした。一致率は僅差ながら、肝心の壁打ち取りこぼしが 2 件から 5 件に増えています。コーディング用データの判定分布は「weak でできる」側に寄っているので、壁打ちの判定まで楽観側に引っ張られた形です。判定の語彙ごと変える事後学習では、rehearsal は旧ドメインの癖の持ち込みになりました。幸い judge は判定専任の部品なので、コーディング能力を忘れること自体はコストになりません。
狙い撃ちの 3 回目では、前章で触れた教師フィルタの罠を踏みました。狙っている難例は、依頼文が作業指示に見える壁打ちです。「1 日伸ばして、予算 60 万円で組み直して」のような依頼で、教師モデル自身が判定を間違える領域でもあります。だから教師フィルタは、まさにその難例を系統的に落とします。教師を上位モデルに替えて二次裁定しても、25 件中 1 件しか残りませんでした。最終的にこの 24 件は自分の目で読み直し、23 件を学習データに戻しています。教師モデルによる自動フィルタは、教師にとって難しい例をデータセットから静かに間引く。難例こそ入れたいのに、です。
効果は表のとおりで、判定カテゴリの一致 92.1%・重大取りこぼし 2 件まで来ました。狙った 4 件はすべて直り、代わりに別の 2 件が新しく倒れる入れ替わりも起きています。評価セットを凍結していたからこの入れ替わりが見えたわけで、これ以上この 189 件に向けて磨き込むと評価セットへの過学習が始まると判断し、合成データの追加はここで止めました。
採用する構成が決まったので、閾値も引き直しました。コーディング用 judge の閾値 0.80 をそのまま使うと、weak 維持率は 1% になります。このドメインでは業務系の依頼で weak の実測成功率が高くても 0.77 前後なので、p_solve も 0.70〜0.77 に集まり、0.80 では届かないのです。閾値を振って測り直し、0.70 で運用することにしました。ドメインを変えると閾値も引き直しになります。
21GB・1.57 秒の judge にたどり着いた
仕上げは前作と同じ Model-Optimizer の NVFP4 量子化で、約 66GB の BF16 が 21GB になります。
ここで評価の測り方について、ひとつ注意があります。ここまでの評価は時間短縮のため並列で流していたのですが、vLLM は複数リクエストをまとめてバッチ処理するため、temperature 0 でも閾値の真上に乗る入力は判定が揺れます。1 件ずつ流し直すと 189 件中 8 件の判定が動きました。本番はルーターが judge を 1 件ずつ呼ぶので影響はありませんが、閾値際を 1 件単位で見る評価は本番と同じ batch=1 で測るべきでした。以降の確定値はすべて逐次で測っています。
量子化後には、1 件ずつ流しても消えない揺れが 1 つ残りました。同一入力を 30 回流すと、p_solve が閾値ちょうどの 0.70 に乗る 4 入力だけが 13〜43% の割合で判定を往復します。量子化版の MoE カーネルの並列加算が原因ではないかと見ていますが、方向はすべて strong 側でした。weak で足りる依頼がときどき strong に行ってコストが少し無駄になる、という安全側の実害に収まっています。
逆に BF16 では、特定の 1 入力だけが 30/30 で暴走していました。JSON を出し終えた後に空白を上限まで吐き続け、1 判定に 55 秒かかる症状です。これは NVFP4 化で消えましたが、量子化が直したのではなく発火条件がずれただけと見ています。こちらも前作と同じく、フェイルセーフで strong に落ちる安全側の故障です。
ここまでの全構成を同一条件で並べると、仕立て直しの効果を一望できます。較正の列は Brier スコアで、小さいほど p_solve が実測の成功率に近いことを表します。なお、この表は並列評価の相対比較で、先ほどのバッチノイズ ±数件を含みます。
| 構成 | 妥当性 | カテゴリ一致 | 較正(Brier) | 重大取りこぼし | Card 感度 |
|---|---|---|---|---|---|
| 素の Lightning + Card | 52/189 | 5.8% | 0.338 | (ほぼ全件 strong 行き) | 0/2 |
| コーディング用 judge 流用 | 159/189 | 74.8% | 0.282 | 12 件 | 19/20 |
| 専用データのみ | 189/189 | 87.3% | 0.214 | 4 件 | 17/20 |
| + coding rehearsal | 187/189 | 88.2% | 0.236 | 6 件 | 15/18 |
| + 狙い撃ち追加(採用) | 189/189 | 92.1% | 0.194 | 2 件 | 19/20 |
採用した構成を、本番と同じ batch=1 の逐次と NVFP4 で確定させた数字が次の表です。判定は中央値 1.57 秒 に収まりました。
| 指標 | 確定値 |
|---|---|
| 妥当性検査の通過 | 189/189 |
| strong に振るべき依頼の再現率 | 94.7±1% |
| weak 維持率 | 87〜91% |
| 重大取りこぼし | 1〜2 件(恒常的な誤りは 1 件、もう 1 件は量子化の揺れによる往復) |
| Card 感度 | 18/20 |
| 判定レイテンシ(中央値) | 1.57 秒(BF16 の逐次では 3.9 秒) |
コーディング用 judge と同じく DGX Spark 1 台に収まる 21GB の judge になり、速さは現行 judge と同等以上です。
この judge はチームで使うために仕立てたもので、チームで運用しているルーターにはすでに載っています。その構築手順は、Switchyard の設定から Card の扱いまで含めてチームの嶋田が記事にしていて、チームの通常利用で記録された 156 件の判定のうち 6 割強が weak に流れた内訳もそちらにあります(2026-08-28 時点の記事です)。
なお、この judge はコーディング用 judge を置き換えるものではありません。同じ base モデルに別の LoRA 差分を当て、閾値も別(0.80 と 0.70)の 2 つの judge として並べて運用します。1 つの judge に両ドメインを混ぜる案は rehearsal の実測で分が悪く、仕立て直しが学習 1 回 10 ドル前後で済むなら、ドメインごとに Card と adapter のペアを仕立てて並べる方が現実的かなと思っています。
web 検索の追加は Card の 1 段落を書き換えるだけだった
最後に、この設計が狙いどおりに効くかを確かめます。チームでは、アシスタントに web 検索を足す予定がありました。その日が来たら judge はどうすればいいのか。再学習せずに済むのかを、先回りして試しています。
やったことは、Card の profile で web_search を有効にした改訂版を描画し、新旧の Card で 24 件の対照 probe を流しただけです。結果、web 頼みの依頼の判定が「できない」(p_solve 0.01)から「できる」(0.77)へ正しく反転し、残りの 22 項目は不変でした。学習時に存在しなかった能力の記述でも、Card の読解が判定に反映されることを確認できました。
能力追加の日にやることは再学習ではなく、Card の 1 段落の書き換えとルーターの再起動だけ。環境情報を重みに焼き込まず Card に外出ししたのは、この日のためでした。ひとつ注意があるとすれば、実体のない能力を Card に書くと判定だけが先走ることです。そのため web 検索が実装されるまでは旧 Card のまま運用し、実装が終わった時点で実際の skill の説明文と Card の文言を突き合わせて改訂版を確定しました。確定時の 24 件 probe に退行はなく、配布している Card の既定はこの版にしています。
まとめ
コーディング用 judge の流用は、専用評価 189 件で妥当性 84%・重大取りこぼし 12 件でした。実行環境を Capability Card として書き出し、weak の実測成功率をラベルにした専用データ 2,122 件で仕立て直すことで、妥当性 100%・strong 再現率 94.7%・判定中央値 1.57 秒の専用 judge になりました。学習は 3 回で 25 ドルほど、実働 2 日です。web 検索の能力追加が Card の書き換えだけで判定に反映されることも確かめられました。
この検証から得た学びは 3 つあります。環境情報の Card は判定の材料にはなりますが、判定の振る舞いは SFT が作ります。コーディング用データの rehearsal は逆効果で、閾値も引き直しになったので、ドメインごとに Card と adapter のペアを分けて並べるのが現実的です。そして教師モデルの自動フィルタは、教師に難しい難例を静かに間引きます。人間の裁定点をパイプラインに残しておくのが、結局いちばん確実でした。
できていないことも書いておくと、壁打ちの取りこぼしは最後まで 1〜2 件残り、この記事の評価はすべて合成データ上のものです。チームで運用しているルーターの実トラフィックでの見え方は、嶋田の記事に譲ります。チームでの判定ログが溜まったら、実際の依頼の形を学習データに足して、残った取りこぼしが直るかを確かめたいところです。
参考リンク
- NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16(Hugging Face)
- NVIDIA-NeMo/Nemotron — LoRA SFT の usage-cookbook が入っているリポジトリ
- NVIDIA-NeMo/Switchyard
- NeMo Switchyard と事後学習済みの judge モデルで Slack エージェントのリクエストを自動ルーティングする — チームで運用しているルーターの構築記事。この judge を判定役に使っている(2026-08-28)
- Agent2Agent (A2A) Protocol Specification — Agent Card の仕様。Capability Card の発想が近い標準
- NVIDIA-NeMo/DataDesigner — 合成データの生成に使用
- NVIDIA/Model-Optimizer — NVFP4 の量子化レシピはこちらに同梱






