メインコンテンツにスキップ

LayaはJevの代わりになるか。速さの対価は質問文の設計だった

LayaはJevの代わりになるか。速さの対価は質問文の設計だった

2026/10/04に公開

前の記事の最後で、次は業務の判断の話を書くと予告していました。

https://techarm.dev/posts/typesafe-jev-japanese-latency

ところが書き始めたところで、GitHubで「Jevに負けないくらいの性能」とうたうオープンソースのモデルを見つけました。Laya というモデルです。Jevと同じ使い方ができて、しかもAPIではなく手元のパソコンで動きます。

https://github.com/NandhaKishorM/laya

紹介するだけでなく、自分の手元で動かして確かめたくなりました。使ったのは Mac Studio(M1 Ultra、メモリ64GB)です。業務の話は、そのあとに回します。

先に結論

1. 速い。 Layaは手元で1回の判断が10ms。JevはAPIの呼び出しに178〜238msかかるので、約18倍です。ただしJevの時間には、通信の時間も入っています。

2. でも、そのまま置き換えられるほどではない。 何を判断させるかで、結果が分かれました。

いちばん差が出たのは迷路です。迷路を通しで解かせると、Layaは47.6%、Jevは100%でした。分かれ道を1問ずつ聞いたときは、Layaも98.3%正解していたのにです。

図表を読み込み中...

3. 質問文の書き方で、結果が大きく変わる。 同じ判断でも、言い回しだけで12.5%から100%まで動きました。Jevは最初の書き方のままで100%です。そして、ルールの足し方を間違えると、Jevでも崩れました。

どうやって比べたか

JevとLayaに同じことを聞いて、答えを比べました。使ったのは、迷路とSnakeのゲームです。

迷路では、分かれ道に来るたびに「次はどっちへ進む?」と聞きます。下の動画は、2つの走りを同じ手数ずつ(1コマ4手)並べ直したものです。実際の時間のままだと、Layaは1秒かからずにゴールしてしまい、どう歩いたかが見えないためです。

31×31の迷路を、JevとLayaが同じ手数ずつ進んでいく様子。Laya側には、まだ行っていない道があるのに通った道を選んだ分かれ道に赤い丸が付き、そこからスタートまで引き返す遠回りが赤い線で描かれる

走り終わった画面がこちらです。

31×31の迷路をJevとLayaに同時に解かせた結果。どちらも左上のSから右下のGにたどり着いた。手数はJev 384手・Laya 468手、時間はJev 5.5秒・Laya 0.4秒。Laya側の赤い丸は、まだ行っていない道があるのに通った道を選んだ分かれ道(3回)、赤い線はそこからスタートまで引き返して戻ってきた128手

左上の S から右下の G まで、どちらもたどり着いています。手数ではJevが勝ち(384手 対 468手)、時間ではLayaが勝ち(5.5秒 対 0.4秒)。

「分かれ道で聞いた回数」は、Jevのほうが多い(30回 対 25回)のに手数は少ない、というのが不思議に見えるかもしれません。一本道ではモデルに聞かずに進むので、長い行き止まりを往復しても、この回数は増えません。

Laya側の赤い丸と赤い線は、Layaが間違えた場所です。これは迷路の節で詳しく書きます。

Jevの手数は、走るたびに変わります。同じ迷路を8回走らせると、328手が2回、384手が5回、432手が1回でした。分かれ道で、どちらも未踏のまま 0.5 対 0.5 になることがあり、どちらを引くかで道が変わるためです。Layaは8回とも468手でした。

Snakeでは、1マス進むたびに「次はどっちへ曲がる?」と聞きます。

SnakeをJevとLayaに同時に遊ばせている画面。Jevは毎秒5回、Laya(MLX版)は毎秒87回判断している。下の棒グラフは、上下左右それぞれにモデルが付けた確率

どちらも左がJev、右がLayaで、同じ盤面を同時に動かしています。パネルの下にある棒が、上下左右それぞれにモデルが付けた確率です。

Snakeには終わりが無いので、画面の点数はそのまま比べられません。同じ20秒でも、Jevは101回、Layaは1,745回判断しています。比べるときは、判断の回数をそろえて数えました。

モデルに渡すものは、どちらも2つだけです。

  • 判断の材料(state) … 周りの様子。たとえば「上は壁、右はまだ行ったことがない」
  • 選択肢(criteria) … 上・下・左・右のうち、進める方向

Layaだと、こんなふうに呼び出します。Snakeの例で、材料と質問文は試行錯誤の末にたどり着いた書き方です。

import laya_mlx as laya # 本家なら import laya agent = laya.load("convaiinnovations/laya", subfolder="multilingual") r = agent.predict( # 判断の材料(state) {"directions": {"up": "toward the apple", "down": "blocked", "left": "away"}}, # 質問文と選択肢(criteria) {"direction": { "type": "choice", "instructions": "Which direction should the snake move next? " "Read `directions`. Never choose blocked. " "Choose the direction that is toward the apple.", "criteria": {"up": "move up", "down": "move down", "left": "move left"}, }}, ) print(r["answers"]["direction"]["choice"]) # up print(r["answers"]["direction"]["probabilities"]) # {'up': 0.962, 'down': 0.023, 'left': 0.015}

Jevも同じ形で、材料と選択肢を渡すと確率が返ってきます(呼び出し方は前の記事に書きました)。

ゲームを動かしている様子は、次の記事でまとめて見せます。この記事では、どれくらい正しく選べたかを数字で比べます。

Layaってどんなモデルか

Convai Innovations が公開しているモデルで、誰でも自由に使えます(Apache-2.0)。Jevと同じく文章は書かず、質問に確率で答えます。

中身の違う版(チェックポイント)が3つあります。

名前中身一度に読める量
englishModernBERT-large 421M512トークン
multilingualmmBERT-base 322M1,024トークン
typed-decisionsenglish を追加で学習させたもの1,024トークン

公式は typed-decisions で「Jevの0.727に対して0.766」というベンチの数字を出しています。なので最初はこれを使いました。ところが迷路では、この版がいちばん悪く、60問中1問も正解しませんでした。理由はあとで書きます。

速さは、手短に

Layaの速さは、すでに日本語でいくつも記事が出ています。mizchi さんは MLX で3問8〜9ms、kanta13jp1 さんは9ms対317ms、GMO さんは35〜50ms対210〜240msと書いています。

https://zenn.dev/mizchi/articles/laya-mlx-60fps

なので、手元の数字だけ置いておきます。

1回の判断(中央値)条件
Jev178〜238ms大阪から API
Laya 本家(PyTorch)21.3msM1 Ultra の GPU
Laya MLX版(非公式)10.0msM1 Ultra の GPU

MLX版は、Laya を作った人とは別の人が、Apple Silicon 向けに書き直したものです。

https://github.com/mizorewww/laya-mlx

2026年9月19日に公開されたばかりで、10月1日の時点でコミットはまだ6つでした。本当に本家と同じ答えを返すのか、Snakeの場面200件で比べてみると、200件すべて同じ答えでした。確率のずれも最大0.0027。速いぶん、こちらを使って問題なさそうです。

公称値は M3 Max で7.4msです。M1 Ultra はコア数こそ多いものの10.0msで、チップの世代の差が出たようです。

設定の問題か、頭の良さの差か

最初に迷路で試したところ、Jevはゴールしたのに、Layaは同じ迷路の出口にたどり着けませんでした。

設定がおかしいのか、それとも頭の良さがそんなに違うのか。見ていて、まずそこが気になりました。

▶答えが決まっている場面だけで比べる

ゴールできたかどうかだけでは、原因が分かりません。1回曲がり損ねれば遠回りになりますし、運よく当たることもあります。

そこで「まだ行っていない道が1本だけ、残りはもう通った道」という分かれ道を60件集めました。正解は必ず、まだ行っていない道です。選択肢は2〜3つなので、でたらめに選んでも49.7%は当たります。

正答
Laya typed-decisions16.7%
Laya english25.0%
Laya multilingual48.3%
でたらめに選んだ場合49.7%

3つとも、でたらめに選ぶのと同じか、それ以下でした。

▶壊れているのか、読めていないのか

まず、入力が長すぎて途中で切れていないかを見ました。295トークンで、切れていません(上限は512〜1,024トークン)。

次に、答えの対応がずれていないか。「猫は黒い」と渡して「黒い/白い」を選ばせると、0.942の確率で「黒い」を選びます。モデルが壊れているわけでもなさそうです。

▶選択肢の説明を読んでいなかった

原因が見えたのは、選択肢の説明を入れ替えてみたときです。

「まだ行っていない」と書いた選択肢を、上と下で入れ替えました。普通なら、答えも入れ替わるはずです。

上の説明が「まだ行っていない」 → 上を選ぶ(0.594) 下の説明が「まだ行っていない」 → 上を選ぶ(0.591)

答えは、ほとんど変わりません。選択肢の説明を、見ていなかったようです。

「猫は黒い」が解けたのは、材料のほうに「The cat is black」と書いてあったからでした。どうやらLayaは、材料に書いてあることと選択肢を見比べて選んでいます。だから、選択肢の説明にしか書いていないことは使われません。

迷路の作りが、まさにそうでした。「その道に行ったことがあるか」を、選択肢の説明にだけ書いていました。Jevはそれでも正しく選べていたので、気づきませんでした。

▶材料に移したら、今度は逆を選んだ

それならと、「行ったことがあるか」を材料のほうに移しました。今度は反応します。ところが、試した2問とも逆の方向を選びました。

指示: Which direction should the explorer move next? Prefer a direction that is unvisited. 材料: down: "visited 3 times"

指示にある unvisited(まだ行っていない)の中に、visited(行ったことがある)がそのまま入っています。見比べて選ぶなら、visited 3 times と書かれた方向のほうが、指示に近く見えてしまいます。

書き方を変えて、また2問ずつ試しました。new / old は1問だけ正解。visits: 0 / visits: 3 と数字で書いても1問。never been there / been there 3 times にしたら、2問とも正解しました。

▶直したら、1問ずつなら98.3%になった

材料に方向ごとの事実を書き、never been there の言い回しにして、同じ60件で測り直しました。

正答1位と2位の確率の差
Laya multilingual98.3%0.476
Laya english48.3%—
Laya typed-decisions0.0%—
Jev100%0.988

multilingual は98.3%。直す前の言い回しに戻すと85.0%なので、言い回しだけで13ポイント変わっています。この時点では、設定の問題だったと思っていました。

▶でも、通しで解かせると47.6%だった

ところが、記事の最初に載せた迷路の画像では、Layaが3回間違えています。赤い丸が、まだ行っていない道があるのに、もう通った道を選んだ分かれ道です。

いちばん上の丸で通った道を選んだLayaは、そのままスタートまで引き返し、また同じ分かれ道に戻ってきました。行きに64手、帰りに64手で、合わせて128手。赤い線がその道のりです。左上のスタート付近が赤いのは、最初に通った道の上を、この引き返しがもう一度通ったからです。

60件で98.3%なら、1本の迷路で3回も外すことはまず起きません。そこで31×31の迷路を20本、最初から最後まで通しで解かせて、その途中の分かれ道で数え直しました。

正しく選んだ一度も間違えなかった迷路ゴール
Laya multilingual49/103(47.6%)7/2017/20
Jev86/86(100%)20/2019/20

数えたのは「まだ行っていない道と、もう通った道が混ざっている分かれ道」です。1問ずつ聞いた60件と同じ種類の場面です。

通しで解かせると、半分しか当たりませんでした。 1回間違えると見慣れない場面に入り、そこでさらに間違える。ループにはまった2本では、20回中18回、23回中18回外しています。

Jevも1本ゴールしていませんが、間違いは0回です。全部の道を通り終えたあとは「まだ行っていない方を選ぶ」という規則が何も決めてくれず、見えている11×11の範囲だけでは出口の方向が分からなかったのだと思います。

▶60件の中に、「上」が1つも無かった

1問ずつの98.3%と、通しの47.6%。この差の原因は、1問ずつ聞いた60件の集め方でした。

正解の向き1問ずつ聞いた60件通しで解かせたLaya
上0件17/69(25%)
上以外(下・左・右)60件32/34(94%)

1問ずつ聞いた60件は、コードが「上→下→左→右」の順に、まだ行っていない道から探して集めた場面です。上から先に探すので、分かれ道に来たときには上はもう通ったあと。「上だけがまだ行っていない」という場面が、1つもできていませんでした。

そしてLayaは、正解が上のときだけ極端に外します。上以外なら94%で、1問ずつのときの98.3%とほぼ同じです。画像の赤い丸3つも、全部「上がまだ行っていない」場面でした。

98.3%は、Layaが苦手な場面を一度も聞いていない数字でした。

▶原因の一部は、ゴールの方角

なぜ上だけなのか。迷路のゴールは、いつも右下にあります。そして材料には「ゴールは右下の方」という情報も入れていました。上は、いつもゴールから遠ざかる向きです。

そこで、材料からゴールの方角を外して、同じ20本を走らせました。

材料正しく選んだ上が正解のときゴール
ゴールの方角あり47.6%17/6917/20
ゴールの方角なし78.1%24/3719/20

47.6%から78.1%まで上がりました。ゴールの方角に引っ張られて、「まだ行っていない方を選ぶ」が負けていたわけです。ただ、上はまだ苦手で、Jevの100%には届きません。

最初の疑問「設定の問題か、頭の良さの差か」に答えると、設定の問題は大きかった。でも、それだけではなかった、です。

Jevは、ゴールの方角があってもなくても間違えませんでした。最初に見た確信の差(Jev 0.988、Laya 0.476)は、この崩れやすさの表れだったのかもしれません。

▶いちばん成績のいい版が、0問だった理由

公式のベンチでいちばん良い数字を出していた typed-decisions は、何をしても0問でした。理由は、公式のモデルカードに書いてありました。

"

The base checkpoints sit below the majority-class baseline here — the capability on this benchmark comes from fine-tuning

https://huggingface.co/convaiinnovations/laya

0.766という数字は、そのベンチの練習問題で追加学習させた版のものでした。追加学習していない元の版は0.362で、いつもいちばん多い答えを返すだけ(0.461)よりも低い。

ベンチに合わせて鍛えた版を、ベンチ以外の問題に使ったので、迷路では役に立たなかった。そういうことだと思います。読み込むたびに「温度の設定値がおかしい」という警告(this checkpoint ships invalid temperatures)も出ていました。

英語版のほうがいいのでは?

次に気になったのは、ここです。迷路には日本語が出てこないのに、なぜ多言語版の multilingual を使うのか。英語版のほうが向いていそうです。

筋はそのとおりなのですが、測ると英語版は48.3%で、でたらめと変わりませんでした。英語専用で大きいほう(421M)が、多言語で小さいほう(322M)に負けています。

理由を探して、3つの仮説を試しました。

  1. 入力が長すぎた → どれも281トークンで、上限の内側でした
  2. 余計な情報に気を取られている → 1問ずつ聞いた60件で盤面の情報を外すと、英語版は少し上がる(58.3%)のに、multilingual は98.3%から48.3%に下がりました。盤面をちゃんと読んでいたのは multilingual のほうでした
  3. 文字の区切り方の違い → 語彙の数は5倍違いますが、迷路の1行 #####@..#.o はどちらも同じ5つに区切っていました

結局、理由は分かっていません。分かったのは、この迷路の盤面を読めたのは multilingual だけだった、ということです。

言い回しだけで正答率が変わる

迷路での言い回しの比較は2問ずつの手探りだったので、Snakeでもっと数を取りました。

林檎に近づく方向がちょうど1つある場面を、実際のプレイから80件集めます。正解はその1つ。3択なので、でたらめに選べば34.4%は当たります。

材料の書き方Laya
open, closer to the apple / open, farther from the apple12.5%
good / bad / deadly8.8%
1行目の書き方のまま、盤面の大きさや頭・林檎の位置も足す6.2%
toward the apple / away100%

Jevは、3行目と同じ条件(Layaが6.2%だった書き方)でも、80件すべて正解でした。

迷路のことがあったので、この80件の正解の向きも確かめました。やはり偏っていて、右と上だけです。そこで上下・左右を入れ替えた場面も作って測りました。4行目の書き方なら、**どの向きでも100%**でした。Snakeの100%は、向きに助けられた数字ではありません。

1行目と4行目の違いは、言い回しだけです。実際に渡したものを並べます。

# 80件中 12.5% state = {"directions": { "up": "open, closer to the apple", "down": "blocked", "left": "open, farther from the apple", }} instructions = ("Read `directions`. Choose a direction that is open. " "Among the open directions, choose the one that is closer to the apple.") # 80件中 100% state = {"directions": { "up": "toward the apple", "down": "blocked", "left": "away", }} instructions = "Read `directions`. Choose the direction that is toward the apple."

1行目は、正解にも不正解にも open と the apple が入っていて、見分けるための言葉が埋もれています。4行目なら、apple が出てくるのは正解の選択肢だけです。

ただ、それだけでは説明がつきません。good / bad / deadly は言葉も重なっていないし短いのに、8.8%でした。GMO さんの記事には「criteria は短く簡潔に」したほうがよかったとありますが、短くするだけでは足りないようです。なぜ good / bad がうまくいかないのかは、分かっていません。

もう1つ気になるのは、悪い書き方がどれも、でたらめ(34.4%)より低いことです。書き方が悪いと、ただ効かないのではなく、逆の方向に引っ張られるようです。

▶1つの場面だけで判断しない

失敗もひとつ書いておきます。

1行目の書き方は、最初に手で作った1つの場面で試したときは、2問とも正解でした(確率0.85)。それで直ったと思って走らせたら、実際の場面では12.5%でした。

1つの場面の結果は、たまたまだったわけです。迷路では、60件集めても同じことが起きました。数を集めても、集め方が偏っていれば騙されます。最後は、実際に動かして数えるしかありません。

ルールを足すと、Jevも崩れた

いちばん予想していなかったのが、これです。

Snakeでは、壁や自分の体にぶつかる方向を材料に blocked と書き、指示に Never choose blocked.(blocked は選ぶな)と入れています。ぶつかる方向が必ずある場面120件で、Layaが blocked を選んだのは0件でした。この一文を外すと、5.0%選びます。

次に、自分で自分を閉じ込めてしまう「詰み」を減らそうとしました。進むと狭い場所に入り込んで抜けられなくなる方向を材料に trap と書き、指示に一文足します。

"Read `directions`. Never choose blocked. Choose the direction that is toward the apple." "Read `directions`. Never choose blocked. Never choose trap. Choose the direction that is toward the apple."

変えたのはこれだけです。

Jevにも同じものを渡して、1,000回の判断あたりで比べたのが下の表です。「即死の手」は、blocked の方向を選んでしまった割合です。trap の方向はすぐには死なないので、ここには入れていません。

食べた林檎即死の手を選んだ割合
Jev(足す前)64.10.0%
Jev(Never choose trap. を足す)49.02.3%
Laya(足す前)39.50.4%
Laya(Never choose trap. を足す)33.02.2%

禁止をもう1つ足したら、もとからあった禁止が守られなくなりました。しかもJevとLayaで、ほぼ同じくらい崩れています。Jevは、足す前は0.0%でした。

trap という新しい言葉を使わずに、狭い方向も blocked と書くようにすると、Jevの即死の手は0.3%で済みました。言葉を増やさないほうが崩れにくい、とは言えそうです。

Layaのくせを調べていたつもりが、Jevにも当てはまる話になりました。お金を払ってJevを使っていても、指示を足したら、足す前と後で測ったほうがよさそうです。

ちなみに詰みは1,000回の判断で1〜2回しか起きないので、この情報は結局使いませんでした。

日本語の差は埋まらなかった

最後に、前の記事の予告に戻ります。

前の記事では、同じ内容の問い合わせを英語と日本語で16組つくり、同じ質問を投げました。Jevは、データが日本語でも英語と同じ判定を返しました(16組中16組)。そろわなくなったのは、質問文まで日本語にしたとき(16組中14組)です。

同じ16組を、Layaに投げてみました。

質問文が英語質問文が日本語同じ入力でJevと同じ答え
Jev16/1614/16—
Laya multilingual9/167/1619/32
Laya english8/1612/1613〜15/32
Laya typed-decisions8/1612/1616〜19/32

Layaのいちばん良い条件でも、Jevのいちばん悪い条件に届きません。同じ入力に対してJevと同じ答えを返すのも、半分くらいです。

書き方も工夫してみました。分類の選択肢のうち、personal(個人的な連絡)の説明が "A personal message, not a support request" と否定の形になっていたので、否定を外した版でも測りました。結果は、良くなる条件と悪くなる条件が混ざって、効果はありませんでした。

これは自分だけの結果ではありません。snsk さんが日本語の業務判断40問で比べたものでは、Jev 97.6 に対して Laya 36.9。英語で比べた Hugging Face の議論でも、6択の分類で Jev 0.975 に対して Laya 0.725 でした。

https://github.com/snsk/jev-laya-japanese-business-benchmark

Snakeとの違いは、答えがどこにあるかだと思います。Snakeの「林檎に近づくか」は、材料に言葉で書いてあります。見比べれば答えが出ます。問い合わせの分類は、文章を読んで意味をつかまないと答えが出ません。こちらは、書き方では埋まりませんでした。

ついでに分かったことがもう1つあります。Layaは日本語を、言語として判定していませんでした。英語の文には language: 'en' と返しますが、日本語の文には language: None(文字の種類は、かなと判定)でした。

まとめ

LayaはJevの代わりになるか。場面による、が答えです。

  • 速さは本物。 手元で10ms。APIを呼び出すJev(通信込み)の約18倍
  • Snakeでは並んだ。 質問文を直せば、Jevと同じ100%
  • 迷路では並ばなかった。 通しで解かせるとLaya 47.6%、Jev 100%。1問ずつならLayaも98.3%だった
  • 問い合わせの分類は埋まらない。 日本語でも英語でも、Jevと半分しか一致しない
  • 書き方で結果が大きく変わる。 言い回しだけで12.5%から100%まで動いた
  • ルールの足し方で、Jevも崩れる。 1つ足したら、もとのルールが破られ始めた

Layaを使うなら、気をつけたいことは3つです。

  1. 判断に必要な事実は、材料に言葉で書く
  2. 試すときは、実際の使い方と同じように通しで動かして数える。1問ずつの結果は当てにならないことがある
  3. ルールを足したら、足す前と後で比べる。これはJevでも同じ

自分なら、ゲームや画面の操作のように、短い間隔で何度も判断するものはLayaに任せます。文章を読んで分類するような、間違えると困る判断はJevです。

前の記事で、速さは今までできなかったことを可能にする、と書きました。手元で10msのLayaは、まさにそういう使い方に向いていると思います。

先送りにした業務の判断の話は、次の記事で書きます。

使ったコード

この記事の計測に使ったコードは、全部置いてあります。Layaは初回に重み(600〜800MB)を自動でダウンロードします。Jevを使う部分は TYPESAFE_API_KEY が必要です。

https://github.com/techarm/blog-code-examples/tree/main/laya-vs-jev
  • diagnose.py … 迷路の分かれ道を1問ずつ聞く(60件)
  • race_stats.py … 迷路を20本、通しで解かせて数える
  • diagnose_snake.py … Snakeの言い回しの比較(80件)
  • blocked_check.py … Snakeで、ぶつかる方向を選ぶか(120件)
  • pocket.py … Never choose trap. を足す前と後の比較
  • mlx_check.py … MLX版が本家と同じ答えを返すか(200件)
  • lang_compare.py … 前の記事と同じ16組での日本語の一致率

どのスクリプトが記事のどの数字に対応するかは、リポジトリの README にまとめてあります。

参考

共有
Xにポスト
Facebookに投稿
はてなブックマークに登録
LINEで共有
リンクをコピー

この記事はいかがでしたか?

もしこの記事が参考になりましたら、
高評価をいただけると大変嬉しいです!

皆様からの応援が励みになります。ありがとうございます! ✨

techarm

エンジニア歴10年以上。Rust / Go / React / Python を中心にWeb開発しています。
日々の学びや技術的な知見をこのブログで発信しています。