はじめに
GMOコネクトの永田です。
日本語の業務文書から約50項目のメタデータをJSONで抽出する仕事をしています。gpt-oss:20bで95%前後の精度は出ているのですが、もっと良いモデルがないか定期的にウォッチしていました。
2026年4月3日にNIIから公開された「LLM-jp-4」はApache 2.0、日本語MT-BenchでGPT-4o超え。期待して飛びついたのですが、Ollamaで動かそうとして異常出力、vLLMでもパーサーの設定を見落として出力が壊れる、と一筋縄ではいきませんでした。
最終的にはgpt-oss相当の精度を安定して実現できたので、その過程を共有します。
先にまとめ
llm-jp-4-32b-a3bは、50項目のJSON抽出タスクで93%の精度が5回安定して出ました。gpt-oss:120bの約97%には及びませんが、国産LLMとして実用圏内です。
| モデル | 推論エンジン | 精度 (5回AVG) | 処理時間AVG |
|---|---|---|---|
| gpt-oss:120b | llama-server | 96.8% | ~81秒 |
| gpt-oss:120b | Ollama (基準) | 95.8% | ~78秒 |
| gpt-oss:20b | llama-server | 94.7% | ~92秒 |
| llm-jp-4-32b-a3b | vLLM + Harmonyパーサー | 93.0% | ~145秒 |
ただし以下の2点でハマりました:
- vLLMでは公式cookbookのカスタムreasoningパーサーが必須
- Ollama/llama.cppはトークナイザ非互換で現時点では動作しない
LLM-jp-4の概要
モデルスペックは既に多くの記事で紹介されているので、検証に必要な情報だけまとめます。
| 項目 | llm-jp-4-32b-a3b-thinking |
|---|---|
| アーキテクチャ | Qwen3-MoE |
| パラメータ数(総数 / 推論時に活性化) | 320億 / 38億 |
| トークナイザ | Unigram byte-fallback (196,608語彙) |
| コンテキスト長 | 65,536 |
| ライセンス | Apache 2.0 |
詳細はNII公式プレスリリースやHuggingFaceモデルカードをご参照ください。
検証環境と手法
ハードウェア・ソフトウェア
| 項目 | 値 |
|---|---|
| ハードウェア | NVIDIA DGX Spark (Grace Blackwell, 119GB統合メモリ) |
| llama-server | llama.cpp build 8423 (3fee84e15) |
| vLLM | 0.19.1rc1 (Docker: vllm/vllm-openai:nightly-aarch64) |
| Ollama | 0.20.2 (GGUF非互換確認用) |
| Temperature | 0 |
| MAX_TOKENS | 8,192 |
| コンテキスト長 | 16,384 |
タスク内容
プロジェクトサマリー(Markdown形式、数千文字の日本語文書)から約50項目のメタデータをJSON形式で抽出するタスクです。項目には自由記述、パイプ区切りの選択肢(日本語名|English name)、日付、配列、条件分岐など多様な形式が含まれます。
評価方法
gpt-oss:120b(Ollama 0.20.2実行)の出力をベースに、各項目の正答をClaudeで判定して基準データを作成しました。各モデルの出力をこの基準データと突き合わせ、57項目の完全一致率を算出しています。各モデル5回ずつ実行し、平均と安定性を評価しました。
精度検証の結果
5回追試の結果
3モデルとも5回中4回以上は同一結果で、出力は安定していました。llm-jp-4は5回とも完全に同一です。
| モデル | 推論エンジン | Run1 | Run2-5 | AVG | 処理時間AVG |
|---|---|---|---|---|---|
| gpt-oss:120b | llama-server | 56 | 55, 55, 55, 55 | 55.2/57 (96.8%) | ~81秒 |
| gpt-oss:120b (基準) | Ollama | 57 | 54, 54, 54, 54 | 54.6/57 (95.8%) | ~78秒 |
| gpt-oss:20b | llama-server | 34 ※ | 54, 54, 54, 54 | 54/57 (94.7%) | ~92秒 |
| llm-jp-4-32b-a3b | vLLM+Harmony | 53 | 53, 53, 53, 53 | 53/57 (93.0%) | ~145秒 |
※ gpt-oss:20b Run1は*サフィックス欠落によるフォーマット差異。内容は他Runと同等
3モデルはどこを間違えたか
差分を横断比較すると、モデル共通で間違えやすい項目と、モデル固有の間違いが見えてきます。
| 差分のある項目 | llm-jp-4 (53/57) | gpt-oss:120b (55/57) | gpt-oss:20b (54/57) | 傾向 |
|---|---|---|---|---|
| 解析対象データ(時系列を推測追加) | ○ | ❌ | ❌ | 2モデル共通: LLMにとって汎用的に難しい |
| リポジトリURL(空欄にする) | ❌ | ❌ | ○ | 2モデル共通: 本文中のURLを転記するか判断が分かれる |
| 目的の記述(冗長になる) | ❌ | ○ | ❌ | 2モデル共通: 要約の粒度が基準と合わない |
| データセット名称(冗長になる) | ❌ | ○ | ○ | llm-jp-4固有: 名称を説明文のように長く書く |
| 実験状況の説明(冗長になる) | ❌ | ○ | ○ | llm-jp-4固有: 上記と同パターン |
| ライセンス(英語部分の欠落) | ○ | ○ | ❌ | gpt-oss:20b固有: パイプ区切りの英語部分を落とす |
「解析対象データへの時系列推測追加」はgpt-oss:120bとgpt-oss:20bの両方で発生しており、llm-jp-4だけが正解しています。逆に「名称や説明が冗長になる」のはllm-jp-4に特有のパターンです。
つまり、数値上の差(53 vs 54 vs 55)ほど3モデルの能力差は大きくなく、間違えるポイントが異なるというのが実態です。どのモデルもLLMにとって汎用的に難しい項目(要約の粒度、推測の抑制)でつまずいています。
なおllm-jp-4の8bモデル(denseアーキテクチャ、86億パラメータ)も試しましたが、必須フィールドの*サフィックス欠落や選択肢のパイプ区切り形式を守れないなど、精度が約61%となり実用に至りませんでした。
ハマり1: vLLMのHarmonyパーサーを見落として出力が壊れた
llm-jp-4はThinkingモデルで、応答の前に「思考トークン」を生成します。この思考トークンはOpenAI Harmonyフォーマットのanalysisチャネルに格納される設計です。
HuggingFaceのモデルカードには公式cookbookへのリンクがあり、cookbookのvLLMサンプルにはカスタムreasoningパーサーの指定方法が記載されています。最初これを見落として素のvLLMで起動したところ、思考トークンが最終出力に生テキストとして混入しました。
# パーサーなしの出力例(先頭部分)
analysis We need to extract values from memo according to schema...
Memo header: "プロジェクトデータセットのメモ 2026年3月5日"
作成: 仮名株式会社 仮名 太郎 ...
JSONのキー名がスキーマと全く異なる内容になるため、メタデータ抽出は完全に破綻します。
公式のllm-jp-4-cookbookにはvLLM用のカスタムreasoningパーサー(llmjp4_reasoning_parser.py)が同梱されており、これを使って起動する必要があります。
# cookbookのexample_cli.py経由で起動
python example_cli.py serve /model \
--served-model-name llm-jp-4-32b-a3b-thinking \
--reasoning-parser llmjp4 \
--trust-remote-code \
--dtype bfloat16 \
--gpu-memory-utilization 0.85 \
--max-model-len 16384
--reasoning-parser llmjp4 がないと、思考トークンが分離されません。--trust-remote-code もllm-jp-4のカスタムトークナイザ(llmjp4_tokenizer.py)のロードに必須です。
ハマり2: GGUF/Ollama非互換
「Ollamaで動くなら統一運用できるのに」と思って試した結果です。
| GGUF版 | 結果 |
|---|---|
| Q4_K_M (mmnga版, 21.4GB) | 異常出力(数式ループ 2+1は?3+1は?... の繰り返し) |
| MXFP4_MOE (mmnga版, 18.1GB) | クラッシュ(出力なし。ARM/aarch64でのNVFP4 CPUバックエンド問題) |
根本原因
LLMのトークナイザ(テキストをトークンに分割するアルゴリズム)には主にBPE(頻出するバイト列のペアを統合していく方式)とUnigram(確率モデルで最適な分割を選ぶ方式)の2種類があります。GGUFに変換する際は、モデルが使っているトークナイザの方式を正しく指定する必要があります。
llm-jp-4-32b-a3b のアーキテクチャ: Qwen3-MoE
Qwen3-MoE の標準トークナイザ: BPE
llm-jp-4 の実際のトークナイザ: Unigram (byte-fallback)
↑ ここが想定外
llama.cppのGGUF変換ツール(convert_hf_to_gguf.py)は、モデルのアーキテクチャからトークナイザ方式を自動判定します。Qwen3-MoEなら通常はBPEなので、BPEとして変換します。しかしllm-jp-4はQwen3-MoEアーキテクチャなのに独自のUnigramトークナイザを使っているため、変換結果が壊れます。
手動でUnigram(UGM)として変換する方法もありますが、llama.cppのUGM実装はGoogleのSentencePieceライブラリが出力する.modelファイルを前提としています。llm-jp-4はHuggingFace形式のtokenizer.jsonのみで.modelファイルを同梱していないため、この方法でも正しく動作しませんでした。
まとめ
検証したタスクでの推奨構成
実施するタスクによって最適なモデル・推論エンジンは異なるため、参考まで。
| 用途 | モデル | 推論エンジン | 精度 | 処理時間 |
|---|---|---|---|---|
| 精度重視 | gpt-oss:120b | llama-server | 96.8% | ~81秒 |
| メモリ効率重視 | gpt-oss:20b | llama-server | 94.7% | ~92秒 |
| 国産LLM | llm-jp-4-32b-a3b | vLLM + Harmonyパーサー | 93.0% | ~145秒 |
llm-jp-4を使うにはcookbook同梱のカスタムreasoningパーサーが必須で、Ollama/llama.cppはトークナイザ非互換のため現時点では動きません。
なお、93%〜97%の差はモデル間で間違えるポイントが異なるためで、プロンプト見直しで改善の余地がありそうです。
検証環境の詳細(再現用メモ)
| 項目 | 値 |
|---|---|
| llama.cpp | build 8423 (3fee84e15) |
| gpt-oss:120b GGUF | ggml-org/gpt-oss-120b-mxfp4 (60GB, 3分割) |
| gpt-oss:20b GGUF | ggml-org/gpt-oss-20b-mxfp4 (12GB) |
| vLLM | 0.19.1rc1 (vllm/vllm-openai:nightly-aarch64) |
| llm-jp-4-cookbook | llm-jp/llm-jp-4-cookbook main |
| Ollama | 0.20.2 |
| llm-jp-4 GGUF | mmnga-o/llm-jp-4-32b-a3b-thinking-gguf Q4_K_M / MXFP4_MOE |
参考リンク
最後に、GMOコネクトではサービス開発支援や技術支援をはじめ、
幅広い支援を行っておりますので、何かありましたらお気軽にお問合せください。