NeoHorse-Jev-4B は、TokenRhythm が公開している 4B パラメータ規模の構造化意思決定モデルである。一般的なチャットモデルのように文章を生成するのではなく、アプリケーション側で定義した問いに対して、選択肢やスコア、真偽判定に近い値を返す。
この記事では、NeoHorse-Jev-4B をネイティブランタイムで動かし、Python から意思決定を実行するところまでを試す。
NeoHorse-Jev-4B が返すもの
NeoHorse-Jev-4B は、入力として state と questions を受け取る。state には判断対象の状況を書き、questions にはモデルに判定させたい問いを定義する。
Hugging Face のモデルカードでは、用途としてリクエストのルーティング、ツール選択、条件判定、結果評価が挙げられている。出力には候補ごとの確率が含まれるため、単に「それらしい答え」を文章で返すのではなく、アプリケーション側でしきい値や分岐条件に使いやすい。
モデルカードでは、推論方式として vLLM、SGLang、ネイティブランタイムが案内されている。今回は Python プロジェクトから手早く試すため、ネイティブランタイムを使う。
プロジェクトを作成する
Python プロジェクトの作成には mise と uv を使う。
$ mise use uv
$ uv init
プロジェクト内で仮想環境を使う場合は、mise.toml に uv の仮想環境を登録しておく。
$ cat << EOF >> mise.toml
[env]
_.python.venv = ".venv"
EOF
$ mise trust
CUDA 版の PyTorch を使う場合は、pyproject.toml に PyTorch のインデックスを追加する。ここでは CUDA 12.8 用の例を示す。
[tool.uv.sources]
torch = { index = "pytorch-cu128" }
[[tool.uv.index]]
name = "pytorch-cu128"
url = "https://download.pytorch.org/whl/cu128"
explicit = true
CPU 環境や別の CUDA バージョンで動かす場合は、この部分を利用環境に合わせて変更する。
モデル一式をダウンロードする
NeoHorse-Jev-4B のモデルカードでは、ネイティブランタイムに必要な一式として backbone/、tokenizer/、pointer_head.safetensors、model_manifest.json、dist/、package/ などが示されている。個別ファイルだけでなく、リポジトリ全体を取得しておくのが分かりやすい。
この記事では、プロジェクト直下の model ディレクトリにダウンロードする。
$ mkdir model
$ uvx hf download TokenRhythm/NeoHorse-Jev-4B --local-dir ./model
次に、ダウンロードした推論パッケージを依存関係に追加する。画像入力も試す場合は、vision エクストラ付きでインストールする必要がある。
$ uv add "./model/package"
複数の選択肢から判断させる
以上で準備が整いました。
何らかの判断をさせたいので、Gemma4に適当な問題を作成してもらいまいした。
会議での誤解と時間管理のジレンマ
あなたはA社とB社が共同で開催する非常に重要な会議の進行役を担当しています。
会議は開始から30分経過し、当初の議題を話すはずのB社の担当者から「技術的な問題で遅れます」と連絡が入りました。
残りの参加者からは「このまま進めてほしい」という空気が漂っていますが、あなただけがこの状況が不安です。
あなた(進行役)が取るべき選択肢は?
- A:時間経過を待つ。
「遅れている方が到着するまで、一旦全員で待機しましょう。焦って進めるのは良くありません。」と伝え、具体的な時間(例:あと15分)を提案する。 - B:コアメンバー向けに議題を再編成する。
B社のメンバー以外の参加者に対し、今日話すことができる議題(初期の進捗報告や共有事項など)に絞って、会議を強制的に進める。 - C:アジェンダの組み直しとフォローアップを提示する。
「B社のご担当者様が不在なため、本日の議題を『前半:進捗共有(現在可能)』と『後半:技術的な懸念点の議論(B社様到着後)』の二部構成に切り替えた方が良さそうです。また、後半は改めて別日程でお時間をいただくのはいかがでしょうか?」と提案する。
この会議の進行判断を例にして、複数の選択肢から一つを選ばせる。choice では、criteria に候補名と説明を渡す。
from neohorse_decision import DecisionEngine
engine = DecisionEngine("./model")
state = """あなたはA社とB社が共同で開催する非常に重要な会議の進行役を担当しています。
会議は開始から30分経過し、当初の議題を話すはずのB社の担当者から「技術的な問題で遅れます」と連絡が入りました。
残りの参加者からは「このまま進めてほしい」という空気が漂っていますが、あなただけがこの状況が不安です。
"""
result = engine.predict({
"state": state,
"questions": {
"meeting": {
"type": "choice",
"instructions": "あなた(進行役)が取るべき選択肢は?",
"criteria": {
"時間経過を待つ": "「遅れている方が到着するまで、一旦全員で待機しましょう。焦って進めるのは良くありません。」と伝え、具体的な時間(例:あと15分)を提案する。",
"コアメンバー向けに議題を再編成する": "B社のメンバー以外の参加者に対し、今日話すことができる議題(初期の進捗報告や共有事項など)に絞って、会議を強制的に進める。",
"アジェンダの組み直しとフォローアップを提示する": "「B社のご担当者様が不在なため、本日の議題を『前半:進捗共有(現在可能)』と『後半:技術的な懸念点の議論(B社様到着後)』の二部構成に切り替えた方が良さそうです。また、後半は改めて別日程でお時間をいただくのはいかがでしょうか?」と提案する。",
},
},
},
})
print(result["answers"]["meeting"]["choice"])
print(result["answers"]["meeting"]["probabilities"])
実行結果は次のとおりだった。
$ uv run main.py
アジェンダの組み直しとフォローアップを提示する
{'時間経過を待つ': 0.37104377150535583, 'コアメンバー向けに議題を再編成する': 0.22577781975269318, 'アジェンダの組み直しとフォローアップを提示する': 0.4031783938407898}
最も高い確率は「アジェンダの組み直しとフォローアップを提示する」だが、確率差は大きくない。この結果は、モデルが三つ目の選択肢を選んだことだけでなく、判断に迷いがあることも示している。
アプリケーションに組み込む場合は、最大確率の候補をそのまま採用するだけでは足りない場面がある。たとえば、最大確率が一定値を下回る場合は人間に確認を回す、上位二候補の差が小さい場合は追加情報を集める、といった制御を入れられる。
まとめ
NeoHorse-Jev-4B は、文章生成ではなく構造化された意思決定を返すモデルである。ネイティブランタイムを使えば、Hugging Face からモデル一式を取得し、Python の DecisionEngine から比較的少ないコードで試せる。