背景
- ローカル環境でJevっぽいことをしたい
- Jevを使った意味検索アプリ "sys1grep"を試してみたい
- RAMと計算速度の兼ね合いがあるので、コスパ良いモデルが知りたい
- Jev用Modelとして良さそうなものは以下のページを参考にピックした
環境構築
実行環境
Hardware Overview:
Model Name: MacBook Pro
Model Identifier: MacBookPro18,4
Chip: Apple M1 Max
Total Number of Cores: 10 (8 Performance and 2 Efficiency)
Memory: 64 GB
ProductName: macOS
ProductVersion: 26.6.2
BuildVersion: 25G83
oMLX
- releaseからそれっぽいdng落としてアプリインストール
- UI上から好きなモデル落としておく
LocalJev
- 上記 repo を clone
- 以下のコマンド実行 (bunは事前に入れておく)
bun install
cp .env.example .env
$EDITOR .env # replace the upstream API-key placeholder
bun run start
-
$EDITOR .envの部分によって.envファイルの編集が始まるので、環境に合わせて設定- 以下が例
LOCALJEV_UPSTREAM=http://127.0.0.1:8000 # oMLXのサーバーURL
LOCALJEV_UPSTREAM_API_KEY=<key> # oMLXのAPI key (設定していなければ適当)
LOCALJEV_UPSTREAM_MODEL=<modelの名前> # jpt-4b-oQ4e-fp16 とか
# Optional: require clients of LocalJev to send this as a Bearer token.
# LOCALJEV_API_KEY=local-dev-key
LOCALJEV_HOST=127.0.0.1 # Jevのエンドポイントとして公開する方のhost
LOCALJEV_PORT=51230 # Jevのエンドポイントとして公開する方のport番号
LOCALJEV_TIMEOUT=180
LOCALJEV_MAX_INFLIGHT=2
LOCALJEV_MAX_QUEUE=64
LOCALJEV_MALFORMED_RETRIES=2
LOCALJEV_MAX_OUTPUT_TOKENS=2048
LOCALJEV_QUESTIONS_PER_CALL=16
LOCALJEV_OUTCOMES_PER_CALL=128
- Health checkしてみる
$ curl http://127.0.0.1:51230/ready
{"status":"ready","upstream_model":"gemma-4-E4B-it-qat-q4_0-unquantized-oQ4e-text-fp16"}⏎
# upstream_modelとして.envに書いたLOCALJEV_UPSTREAM_MODELが表示される
$ curl http://127.0.0.1:51230/v1/models | jq
{
"models": [
{
"name": "localjev-latest",
"description": "Alias for LocalJev 0.2, backed by a local OpenAI-compatible DiffusionGemma endpoint.",
"release_date": "2026-09-18"
},
{
"name": "localjev-0.2",
"description": "Jev-compatible prompted probability inference with DiffusionGemma.",
"release_date": "2026-09-18"
}
]
}
# jevのエンドポイントとしてはjevの標準モデルっぽいのが良い感じに公開される
- それっぽいcurlしてみる
curl http://127.0.0.1:51230/v1/systemone \
-H 'Content-Type: application/json' \
-d '{
"model": "jev-latest",
"state": "Hi, I have been trying to connect Stripe but keep getting a 403 error.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payment or subscription issues",
"technical": "Bugs or integration problems",
"sales": "Pricing or account questions"
}
},
"frustration": {
"type": "score",
"instructions": "How frustrated does the customer appear?",
"criteria": ["Calm", "Frustrated but civil", "Very angry"]
},
"urgent": {
"type": "noul",
"instructions": "Does this require an immediate response?"
}
}
}' | jq
{
"model": "localjev-0.2",
"answers": {
"department": {
"type": "choice",
"choice": "technical",
"probabilities": {
"billing": 0,
"technical": 1,
"sales": 0
},
"confidence": 1
},
"frustration": {
"type": "score",
"score": 1,
"legend": {
"0": "Calm",
"1": "Frustrated but civil",
"2": "Very angry"
},
"probabilities": {
"0": 0,
"1": 1,
"2": 0
},
"confidence": 1
},
"urgent": {
"type": "noul",
"noul": 0
}
},
"usage": {
"input_tokens": 300,
"output_tokens": 37
}
}
117:123: syntax error: A identifier can’t go after this “"”. (-2740)
# jqのsyntax error出てくるけど回答は取れているので大丈夫っぽい
sys1grepからLocalJevに問い合わせしてどうなるか試す
本家Jevの回答が上記記事にあるので、それに近い回答が出るかを同じ試験してみる
(検索例) 「変装している、または正体を隠している」行を探す
この 16 行を「誰かが変装している、または正体を隠している」という意味で検索します。
$ semgrep -n -e "someone is disguising themselves or hiding their true identity" tests/fairy.txt 2:狼はおばあさんを飲み込むと、その帽子を深くかぶってベッドに横たわり、赤ずきんを待った 4:女王は行商人の老婆に身をやつし、毒リンゴを白雪姫に差し出した 5:灰まみれの娘は、誰にも名乗らぬまま舞踏会で王子と踊り、真夜中の鐘とともに走り去った 10:Der Wolf setzte Großmutters Haube auf und legte sich in ihr Bett. 4/16 lines (16 sent), 1 requests, 1353 input tokensどの行にも「変装」「正体」にあたる語はありません。赤ずきんの狼は日本語の行(2)とグリム原文風のドイツ語の行(10)が同時に出ています。
最後の 1 行は端末に出したときだけ付く集計で、パイプに流すときは出ません。
(検索例) 冒険者の掲示板から「危険のわりに報酬が安すぎる」依頼を探す
もう 1 つ、以下のファイルから検索する例です。
tests/guild.txtは、冒険者ギルドの依頼掲示板を模した 12 行です。【討伐】村はずれの洞窟にゴブリンが住み着いた。報酬は銀貨20枚。討伐証明を持参のこと 【苦情】先日届いた薬草がしおれていた。金を返してほしい。二度とこのギルドには頼まない 【護衛】商隊を隣町まで。夜盗が出るため腕利きを希望。報酬は応相談 【採取】満月の夜にだけ咲く月光草を10本。受注時に前金で金貨1枚を支払う 【討伐】ドラゴン退治。報酬は銅貨5枚 【感謝】息子を助けてくれた冒険者様、本当にありがとうございました。パンを焼いてお待ちしています 【急募】井戸に落ちた指輪を今夜中に拾ってほしい。ただの古い指輪だが、どうしても必要なのだ Recherche mage de feu pour escorter une caravane. Paiement d'avance : 30 pièces d'or. 【苦情】護衛に雇った剣士が夜番のあいだ居眠りしていた。報酬の返還を求める Es wird ein Alchemist gesucht. Die Bezahlung erfolgt erst nach der Lieferung. 【売却】ミスリルの鎧、ほぼ新品。値札どおり、値引き交渉には応じない 【依頼】亡き妻が好きだった花を、山頂から一輪だけ摘んできてほしい。礼は少ないが誠意を尽くすこれを「危険のわりに報酬が安すぎる」という意味で検索します。
$ semgrep -n -p -e "the reward is far too low for the danger involved" tests/guild.txt 5:【討伐】ドラゴン退治。報酬は銅貨5枚 [0.86]「ドラゴンは危険」で「銅貨 5 枚は安すぎる」という、書かれていない世界知識を使った判定です。銀貨 20 枚のゴブリン討伐(1 行目)は出ません。この種の条件は正規表現では原理的に書けません。`
以下だけgithubのREADMEにあったもの
echo "ジョブは失敗した" | sys1grep -p -Q "ジョブは成功しましたか?"
jpt-0.8b-oQ8e-fp16
fairy
1:桃太郎は犬と猿とキジにきびだんごを与えて家来にした [1.00]
2:狼はおばあさんを飲み込むと、その帽子を深くかぶってベッドに横たわり、赤ずきんを待った [1.00]
3:「おばあさんのお耳はどうしてそんなに大きいの?」と赤ずきんはたずねた [1.00]
4:女王は行商人の老婆に身をやつし、毒リンゴを白雪姫に差し出した [1.00]
5:灰まみれの娘は、誰にも名乗らぬまま舞踏会で王子と踊り、真夜中の鐘とともに走り去った [1.00]
6:浦島太郎は玉手箱を開け、たちまち白髪の老人になった [1.00]
7:羊飼いの少年は「狼が来た」と叫んだが、それは嘘だった [1.00]
8:かぐや姫は自分が月の都の者であることを、育ての翁についに打ち明けた [1.00]
9:三匹の子豚の末の弟は、レンガで家を建てた [1.00]
10:Der Wolf setzte Großmutters Haube auf und legte sich in ihr Bett. [1.00]
11:Cendrillon s'enfuit à minuit, laissant une pantouffle de verre sur les marches du palais. [1.00]
12:The emperor walked in the procession wearing nothing at all, yet everyone praised his new clothes. [1.00]
13:一寸法師は針の刀を腰に差し、お椀の舟で川を下った [1.00]
14:アリは夏のあいだせっせと働き、キリギリスは歌って過ごした [1.00]
15:鶴は「機を織るあいだ、決して覗かないでください」と言い、夜ごと戸を閉てた [1.00]
16:おむすびころりん、すっとんとん [1.00]
guild
None
question
ジョブは失敗した [1.00]
jpt-4b-oQ4e-fp16
fairy
2:狼はおばあさんを飲み込むと、その帽子を深くかぶってベッドに横たわり、赤ずきんを待った [1.00]
4:女王は行商人の老婆に身をやつし、毒リンゴを白雪姫に差し出した [1.00]
9:三匹の子豚の末の弟は、レンガで家を建てた [1.00]
11:Cendrillon s'enfuit à minuit, laissant une pantoufle de verre sur les marches du palais. [1.00]
guild
4:【採取】満月の夜にだ咲く月光草を 10 本。受注時に前金で金貨 1 枚を支払う [1.00]
question
None
jpt-9b-oQ4e-fp16
fairy
2:狼はおばあさんを飲み込むと、その帽子を深くかぶってベッドに横たわり、赤ずきんを待った [0.50]
3:「おばあさんのお耳はどうしてそんなに大きいの?」と赤ずきんはたずねた [0.50]
4:女王は行商人の老婆に身をやつし、毒リンゴを白雪姫に差し出した [0.50]
5:灰まみれの娘は、誰にも名乗らぬまま舞踏会で王子と踊り、真夜中の鐘とともに走り去った [0.50]
9:三匹の子豚の末の弟は、レンガで家を建てた [0.50]
10:Der Wolf setzte Großmutters Haube auf und legte sich in ihr Bett. [0.50]
11:Cendrillon s'enfuit à minuit, laissant une pantouffle de verre sur les marches du palais. [0.50]
guild
None
question
None
gemma-4-E2B-it-qat-q4_0-unquantized-oQ5e-text-fp16
fairy
None
guild
None
question
ジョブは失敗した [1.00]
gemma-4-26B-A4B-it-qat-q4_0-unquantized-oQ4e-text-fp16
fairy
2:狼はおばあさんを飲み込むと、その帽子を深くかぶってベッドに横たわり、赤ずきんを待った [1.00]
9:三匹の子豚の末の弟は、レンガで家を建てた [1.00]
guild
None
question
None
rune-26b-a4b-GGUF-oQ3e-fp16
fairy
2:狼はおばあさんを飲み込むと、その帽子を深くかぶってベッドに横たわり、赤ずきんを待った [0.95]
4:女王は行商人の老婆に身をやつし、毒リンゴを白雪姫に差し出した [0.98]
5:灰まみれの娘は、誰にも名乗らぬまま舞踏会で王子と踊り、真夜中の鐘とともに走り去った [0.92]
9:三匹の子豚の末の弟は、レンガで家を建てた [0.95]
guild
5:【討伐】ドラゴン退治。報酬は銅貨 5 枚 [0.95]
question
None
rune-26b-a4b-GGUF-oQ4e-fp16
fairy
2:狼はおばあさんを飲み込むと、その帽子を深くかぶってベッドに横たわり、赤ずきん을 기다렸다 [0.98]
4:女王は行商人の老婆に身をやつし、毒リンゴを白雪姫に差し出した [0.98]
5:灰まみれの娘は、誰にも名乗らぬまま舞踏会で王子と踊り、真夜中の鐘とともに走り去った [0.98]
10:Der Wolf setzte Großmutters Haube auf und legte sich in ihr Bett. [0.98]
11:Cendrillon s'enfuit à minuit, laissant une pantouffle de verre sur les marches du palais. [0.98]
guild
5:【討伐】ドラゴン退治。報酬は銅貨 5 枚 [0.95]
question
None
思ったこと
- JPTシリーズは参考ページのスコアの割には精度悪く感じる
- 9Bは結構使えそうだと思っていたけど無理そう
- Gemma4 26BをベースにFinetuneされたruneは結構良い感じ
- guildのテストに唯一正解できた
- oQ4はfairyの結果が本家Jevに近い
- oQ3だと三匹の子豚も引っかかってしまっているのでダメそう
- 使うならoQ4
- oQ3だと三匹の子豚も引っかかってしまっているのでダメそう
- Gemma4 26B本家だと全然ダメだったけど、runeはそれらしい結果がでているので良いモデル
- RAMは15GB程度食うけど、MoEなので速度もまあまああるのでアリかも
- 本家Jevの良いところって、高速に大量のデータを低コストで捌けることだけど、ローカルだと速度がかなり遅いので、使う利点はかなり失われる
- 検索するたびに入力文をprefill, 生成もdecode処理を通っているので、数千文字あるようなデータだと数分かかる
- prefillが早いようなGPUならアリだろうけど、M1 Maxではちょっと厳しいと思う
- 検索するたびに入力文をprefill, 生成もdecode処理を通っているので、数千文字あるようなデータだと数分かかる
補足
- Qwen3.8 27BをベースにFinetuneしたDecisionモデルもあるが、処理速度的にGemma4 26Bベースに対してかなり劣るはずなので試してません
- Gemma4 31Bも同様
- Denseモデルは4B程度までが実用的な速度で動きます (M1 Maxでは)
まとめ
- 完全ローカルでもJevっぽいことはできるが、処理速度の課題があるので利点は少ない