この記事を読むと、明日これが決められます
自分のプロダクトの分類・ルーティング・ガードレール処理を、LLM APIからLayaに置き換えるべきか。そして置き換えるなら、どれだけの追加作業を見積もるべきか。この2点を判断できる状態にして終わります。
なぜこの記事を書いているのか
映像・音声まわりの受託開発をしていると、生成物のモデレーションや問い合わせメールの一次仕分けを、そのつどLLMのAPIに投げる場面が出てきます。中身は「この投稿は攻撃的か」「この問い合わせは請求系か技術系か」といった、答えがあらかじめ列挙できる質問ばかりです。それでも1件ごとに数百ミリ秒から数秒待たされ、JSONを取り出すためのパーサを書き、たまに壊れたJSONが返ってきて例外を握りつぶす。ずっと、これは道具の選択を間違えているのではないかと思っていました。
そこへ2026年9月15日、TypeSafe AI が Jev を発表します。「文章を一切生成せず、型のついた決定と確率だけを返す」というモデルです。そして、その3日後の9月18日に、同じ形をしたモデルの重みが Apache 2.0 で Hugging Face に上がりました。それが Laya です。
読んでいて面白かったのは、性能そのものよりも開発者ブログとモデルカードで、同じモデルの数字が食い違っていた点でした。どちらも同じ人が書いた一次情報です。そこに嘘はないのですが、測っている対象が違う。この食い違いの構造が分かると、「OSS版だから乗り換えよう」という判断がいかに危ういかも同時に見えてきます。そこを読者と共有したくて書いています。
なお筆者の手元は、単体GPUのワークステーション1台と、Xeon 1コア・メモリ3GBという貧弱なLinuxコンテナです。前者では動きますが、後者ではVRAM以前にメモリで詰みます。この記事は自前ベンチではなく、公開されている一次情報の突き合わせで構成しています。
対象読者
LLM の API を使って分類・ルーティング・モデレーションを実装したことがある、Python が読めるアプリケーション開発者。Transformer の内部構造の知識は前提にしません。
この記事で扱わないこと
- 学習スクリプトの詳細と、自前での再学習手順
- 筆者による速度・精度の再現実測
- Jev の商用ライセンスや契約条件
この記事を通じて、一次スクリーニングの比喩を使います。Laya は健康診断、LLM は精密検査です。検診は安くて速く、全員に毎年かけられます。精密検査は高くて遅いので、検診で引っかかった人にだけ回します。うまくいく検診の条件は「安い」ことではなく、所見の確からしさを正直に申告することです。ここが本記事の背骨になります。
1. System 1モデルとは何か ── 手元のLLM呼び出しのうち、どれが該当するか
冒頭で、検診と精密検査という役割分担を置きました。では手元のコードのどこが検診に当たるのか。その線引きができないと、以降の話はすべて空振りになります。
このセクションで分かること:自分のコードにあるLLM呼び出しのうち、どれがこの新カテゴリに置き換え可能かの見分け方。
まず「System One モデル」という言葉の出どころを押さえます。これは TypeSafe AI が自社モデル Jev と同時に作った呼称で、標準化された分類名ではありません。発表は2026年9月15日、書き手は創業者の Diogo Almeida 氏。OpenAI で InstructGPT や RLHF の研究に関わった人物です。
発表記事の核心はこの一文に集約されています。
Think of Jev as a frontier-intelligence function call: unstructured state in, typed probabilistic decisions out.
(訳:Jev はフロンティア級の知能を持つ関数呼び出しだと考えてほしい。構造のない状態を入れると、型のついた確率的な決定が出てくる)
── TypeSafe AI 公式発表より
ここで言う「決定」は3種類に限定されます。選択肢から1つ選ぶ、順序のある段階に当てはめる、真偽の確率を返す。それだけです。文章は1文字も出てきません。
なぜ文章を捨てると速くなるのか。LLMは1トークンずつ、前のトークンに条件づけて次を生成します。これが自己回帰です。対して決定モデルは、答えの候補が事前に確定しているので、全候補のスコアを1回の前向き計算でまとめて出せます。並列に出せるものを、わざわざ逐次に並べていたのが従来だった、という整理です。
判別のしかたはシンプルです。そのLLM呼び出しの返り値の取りうる値を、呼び出す前に全部書き出せるか。書き出せるなら System 1 形です。書き出せないなら、それは要約や執筆であり、この話の対象外です。
この判別を自分のコードに適用すると、たとえば次のような棚卸しになります。
| 現在のLLM呼び出し | 返り値を事前列挙できるか | System 1形か |
|---|---|---|
| 問い合わせを部署に振り分ける | できる(部署は4つ) | 該当 |
| プロンプトがジェイルブレイクか判定 | できる(真偽) | 該当 |
| 緊急度を0から3で採点 | できる(4段階) | 該当 |
| 問い合わせへの返信文を書く | できない | 非該当 |
| 会議録を3行に要約する | できない | 非該当 |
上の表で「該当」に入った行が、Laya の検討対象になります。
2. Layaの正体 ── 3つのチェックポイントと3つの質問型
前節で、置き換え候補になる呼び出しを絞り込みました。では実際に何を置くのか。ここで初めて Laya の中身に入ります。
このセクションで分かること:3つあるチェックポイントのどれを選ぶべきか、そして質問をどう書くか。
Laya は Convai Innovations が公開した OSS の決定モデルです。重みは Apache 2.0、HuggingFace で配布され、PyPI に laya パッケージがあります。中身は3つのチェックポイントの家族です。
| チェックポイント | 土台のエンコーダ | パラメータ数 | 文脈長 | 主な用途 |
|---|---|---|---|---|
laya |
ModernBERT-large | 421M | 512 | 英語の分類、ガードレール、メール仕分け |
laya-multilingual |
mmBERT-base | 322M | 1024 | 100言語以上、英語版より約2倍速い |
laya-typed-decisions |
ModernBERT-large | 421M | 1024 | 業務ワークフロー向けに追加学習済み |
質問の型は3つです。比喩に戻すと、検診の検査項目に3種類あるのと同じです。
| 型 | 返るもの | 検診でたとえると |
|---|---|---|
choice |
選ばれた選択肢と、選択肢ごとの確率、確信度 | どの科に回すか |
score |
順序尺度上の期待値と分布、確信度 | 所見の重症度を0から3で |
noul |
真である確率 P(true) | 陽性か陰性か |
実際のコードはこうなります。状態と質問を渡すだけです。
import laya
agent = laya.load("convaiinnovations/laya")
state = {
"subject": "Duplicate charge on invoice 4411",
"body": "We were billed twice for March. Please refund the duplicate.",
}
questions = {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "invoices, payments, refunds",
"technical": "bugs and outages",
"sales": "pricing",
},
},
"urgency": {
"type": "score",
"instructions": "How urgent is this?",
"criteria": ["not urgent", "soon", "blocking"],
},
"churn_risk": {
"type": "noul",
"instructions": "Does the user threaten to cancel?",
},
}
result = agent.predict(state, questions)
print(result["answers"]["department"]["choice"])
3つの質問を並べていますが、これらは1回の前向き計算でまとめて答えられます。質問を増やしても呼び出し回数は1回のままです。ここが、LLMに3回問い合わせるのとの決定的な違いになります。
3. なぜ1回で済むのか ── マスク位置に選択肢を置く仕掛け
前節で「複数の質問が1回の前向き計算で片づく」と書きました。この主張は、なぜ速いのかを説明できないうちは信用できません。仕掛けを見ておきます。
このセクションで分かること:新しい選択肢を足すのに再学習が要らない理由。
Laya は入力を次の形に組み立てます。
[CLS] choice question: どのチームが担当すべきか [SEP]
[MASK] billing: 請求と返金 [MASK] technical: 不具合 [MASK] sales: 価格 [SEP]
{"subject": "...", "body": "..."} [SEP]
選択肢ごとに [MASK] を1つ置くのがポイントです。エンコーダは双方向なので、各 [MASK] の位置に「この選択肢は、この状態にどれだけ合うか」という情報が集まります。あとはその位置の隠れ状態だけを取り出し、1本のスコアに潰し、その質問に属する選択肢のあいだで softmax をかける。これで分布が出ます。
この作りの効き目は速度だけではありません。答えの空間がリクエスト時に決まるため、選択肢を1つ増やしたり文言を変えたりしても、学習をやり直す必要がありません。従来の分類器が出力層を作り直す必要があったのと比べると、運用上の差は小さくないはずです。
一方で、この仕掛けはそのまま制約にもなります。選択肢は全部まとめて固定のトークン予算に押し込まれるので、選択肢を増やしすぎると1つあたりに割ける文字数が足りなくなる。開発元は目安として20個以下を挙げています。77ラベルの銀行意図分類では精度が大きく落ちたことも公表されています。この点は第7節の判定で効いてきます。
4. RLCD ── 「正直な確率」はどうやって作るのか
前節で、確率がどこから出てくるかは分かりました。ですが確率が出ることと、その確率が信用できることは別問題です。検診でいえば、すべての検体に「異常の疑い95%」と書く検査機も、確率は返しています。
このセクションで分かること:Layaの確信度を、コードの if 文の条件に使ってよい理由と、その限界。
まず記号を1つずつ導入します。式より先に記号を出しません。
| 記号 | 読み | 型 | 検診の比喩での意味 |
|---|---|---|---|
| $K$ | 選択肢の数 | スカラー | 検査結果の区分の数 |
| $q$ | モデルが報告する分布 | 長さ $K$ のベクトル | 検査機が申告する所見の確率 |
| $y$ | 実際の正解 | 長さ $K$ のone-hotベクトル | あとで精密検査で判明した真実 |
| $S(q, y)$ | スコアリング関数 | スカラー | 申告の良さに対する報酬 |
ふつうの分類学習では、正解の確率を1に近づけるほど損失が下がります。つまり学習が進むほど過信する方向に押される。強化学習で「当たれば+1、外れれば0」としても同じで、最大確率を1.0に張り付けるのが最適解になります。精度は上がりますが、確率としては死にます。
RLCD(Reinforcement Learning for Calibrated Decisions)はここを、報酬の設計で解きます。使うのは厳密固有スコアリングルールと呼ばれる関数です。
\mathbb{E}_{y \sim p}\bigl[S(q, y)\bigr] \le \mathbb{E}_{y \sim p}\bigl[S(p, y)\bigr]
\quad \text{等号成立は } q = p \text{ のときに限る}
この式が言っているのは、真の分布 $p$ をそのまま申告したときにだけ報酬が最大になるということです。検診の比喩なら、所見を盛っても薄めても損をする報酬表を検査機に与えた、ということになります。正直が最適戦略になるように報酬を設計する、というのが発想の全部です。
Laya が使う合成報酬は、対数スコア、球面スコア、そして順序尺度用のランク確率スコアの組み合わせです。学習は REINFORCE 系で、ロジットに平均ゼロのガウスノイズを足して候補を作り、グループ平均を基準線にして更新します。学習量は7,313更新、1エポック、約1.96時間と公表されています。
確信度そのものは、正規化した情報エントロピーから作られます。
\mathrm{Confidence}(p) = 1 - \frac{H(p)}{\log K},
\qquad H(p) = -\sum_{k=1}^{K} p_k \log p_k
この式が言っているのは、分布がまっ平らなら確信度0、1点に集中していれば確信度1、という単純な変換です。コードで確かめるとこうなります。
import numpy as np
def confidence(p: np.ndarray) -> float:
"""正規化エントロピーから確信度を作る。p は確率ベクトル"""
k = p.shape[-1]
h = -(p * np.log(np.clip(p, 1e-12, None))).sum(-1)
return float(1.0 - h / np.log(k))
print(confidence(np.full(4, 0.25))) # 0.0 迷っている
print(confidence(np.array([1.0, 0, 0, 0]))) # 1.0 確信している
最後に、自動化してよいかの境目です。Laya は「自分で処理する」か「人間に回す」かを選ぶヘッドを持っていて、開発元が公開している報酬表は、正解が+1.0、誤りが−3.0、人間送りが−0.5です。自動処理が得になる条件を解くと、閾値が出ます。
CORRECT, WRONG, ESCALATE = 1.0, -3.0, -0.5
def act_threshold(correct=CORRECT, wrong=WRONG, escalate=ESCALATE) -> float:
"""自動処理の期待値が人間送りを上回る最小の正解確率"""
return (escalate - wrong) / (correct - wrong)
print(act_threshold()) # 0.625
誤りのコストを上げれば閾値も上がります。この表は自分の業務のコストに合わせて置き換えるべき数字であって、0.625という値そのものを持ち帰るものではありません。
ここまでのまとめ
ここまでで、置き換え候補の見つけ方(第1節)、何を置くか(第2節)、なぜ速いか(第3節)、確率をどこまで信用してよいか(第4節)が揃いました。ここまでだけ読むと、かなり良い道具に見えます。
ここから先は、公開されている数字を突き合わせて、その印象がどこまで持つかを確かめます。結論から言うと、印象は半分は持ち、半分は崩れます。
5. 数字が合わない ── 83.8%と0.362は同じモデルの話である
前節までは仕組みの話でした。仕組みが妥当でも、実際の数字が期待どおりとは限りません。そして Laya の場合、公開されている数字のあいだに無視できない開きがあります。
このセクションで分かること:どの数字を採用判断の根拠にしてよいか。
開発元は、開発者向け記事と GitHub の README で、Jev との比較表を出しています。そこでは総合精度83.8%、ルーティング99.1%、レイテンシ38.4msといった値が並びます。一方、同じ開発元が Hugging Face のモデルカードに書いている値は、かなり印象が違います。
| 指標 | 開発者記事・READMEの表記 | モデルカードの表記 |
|---|---|---|
| typed-decisions ベンチ | 記載なし | 素のモデル 0.362 / 追加学習後 0.766 |
| 多数派ベースライン | 記載なし | 0.461 |
| 総合精度 | 83.8%(in-task マクロ平均) | 上記ベンチでは多数派以下 |
| 較正誤差 ECE | 0.060 | 出荷時 0.466、温度再調整後 0.081 |
数字が食い違って見えるのは、測っている対象が違うからです。83.8%は学習データと同じ課題ファミリでの平均であり、0.362は追加学習していない素のモデルを、未知のベンチに当てた値です。0.766は、そのベンチの訓練split で追加学習したチェックポイントの値です。どれも嘘ではありませんが、採用判断に使えるのは後者2つのほうです。
モデルカードには、この点が率直に書かれています。
Laya is a fast base to specialise, not a zero-shot decision engine.
(訳:Laya は特化させるための速い土台であって、ゼロショットの決定エンジンではない)
── Hugging Face モデルカードの Limits 節より
素のモデルをそのまま本番に置く運用は成立しません。開発元自身が、素のチェックポイントは多数派ベースラインを下回ると明記しています。Laya を採用するということは、自分のデータで追加学習する工数を引き受けるということです。
較正についても同じです。出荷時の ECE は0.466で、これは確率としてはほぼ使い物になりません。温度を1つ当てはめ直すだけで0.081まで下がる、というのが開発元の主張です。逆に言えば、この再調整をしないまま確信度を if 文に使うと、第4節の話がまるごと無効になります。
再調整そのものは重い作業ではありません。自分の分布でロジットとラベルを数百件集めれば、こう書けます。
import numpy as np
from scipy.optimize import minimize_scalar
def fit_temperature(logits: np.ndarray, labels: np.ndarray) -> float:
"""質問型と選択肢数の組ごとに温度Tを1つ当てはめる"""
def nll(log_t: float) -> float:
z = logits / np.exp(log_t) # exp でT>0を保証
z = z - z.max(axis=1, keepdims=True)
logp = z - np.log(np.exp(z).sum(axis=1, keepdims=True))
return -logp[np.arange(len(labels)), labels].mean()
res = minimize_scalar(nll, bounds=(-3.0, 3.0), method="bounded")
return float(np.exp(res.x))
較正のズレを測る側も置いておきます。確信度の帯ごとに、申告した確信度と実際の正解率の差を取るだけです。
def ece(probs: np.ndarray, labels: np.ndarray, n_bins: int = 15) -> float:
"""期待較正誤差。0に近いほど確信度が正直"""
conf = probs.max(axis=1)
acc = (probs.argmax(axis=1) == labels).astype(float)
edges = np.linspace(0.0, 1.0, n_bins + 1)
total = 0.0
for lo, hi in zip(edges[:-1], edges[1:]):
m = (conf > lo) & (conf <= hi)
if m.sum() == 0:
continue
total += m.mean() * abs(acc[m].mean() - conf[m].mean())
return float(total)
比較対象の Jev 側の数字にも、同じ注意が必要です。モデルカードは Jev の値を第三者の公開計測から引いており、API アクセスがないため自前では測っていないと明記しています。さらに Jev 自身の公称精度67.8%も、GPT-6 Astra と Claude Fable 5.1 の予測の平均を正解と見なした一致率です。どちらの陣営の数字も、正解の定義から疑う必要があります。
6. 確信度は守ってくれない ── 読めない文字列を0.952の確信で裁く
前節で「確信度は温度を直せば使える」と書きました。ところが、温度をどう直しても救えない壊れ方が1つあります。これを知らずに確信度で門番をすると、静かに事故ります。
このセクションで分かること:モデルの選択を、前向き計算の前に済ませなければならない理由。
英語版チェックポイントの土台である ModernBERT-large は、5万語規模の英語向け語彙を持っています。ラテン文字以外の文字列を入れると、語彙が対応せず、入力は意味を成さない断片に崩れます。問題は、崩れたときにモデルが黙ることです。開発元が51言語で測った結果として、次の値が公表されています。
| 言語 | 正解率 | 平均確信度 |
|---|---|---|
| クメール語 | 0.000 | 0.952 |
| アルメニア語 | 0.050 | 0.885 |
| ヘブライ語 | 0.060 | 0.964 |
| ベンガル語 | 0.080 | 0.945 |
| ヒンディー語 | 0.100 | 0.941 |
選択肢20個に対するランダムの正解率が0.050です。クメール語では100問中1問も当たらず、それでいて平均95.2%の確信を申告しています。開発元の表現を借ります。
the model's own confidence gives no warning when it cannot read the input script
(訳:入力の文字体系を読めないとき、モデル自身の確信度は何の警告も出さない)
── Laya 公式解説記事より
検診の比喩に戻すと、これは読めない言語の問診票を、読めないと言わずに判定してしまう検査機です。確信度による足切りは、この壊れ方をすり抜けます。だから判断は前向き計算の前に済ませるしかない、というのが Router の存在理由です。
from laya import Router
router = Router(preload=True) # 事前ロードで言語切替時のコールドスワップを回避
res_en = router.predict({"body": "I was charged twice, please refund."}, questions)
res_hi = router.predict({"body": "मुझसे दो बार शुल्क लिया गया।"}, questions)
print(res_en["routing"]["model"]) # english
Router は22種類の文字体系を見て振り分けます。開発元によれば判定のオーバーヘッドは英語で0.09ms、インド系文字で0.54ms程度で、33msの前向き計算に対しては誤差の範囲です。
日本語を扱うなら、この話は他人事ではありません。英語版チェックポイントに日本語を入れる構成は、静かに壊れる典型です。多言語版を使うか、Router に任せるかのどちらかを、設計段階で決めておいてください。
7. で、入れるか入れないか
ここまでの内容は、そのまま採用判断のチェック項目になります。仕上げに、判断を1つの流れにまとめます。
このセクションで分かること:自分のケースで Laya を採用すべきかどうかの結論。
流れを言葉にすると、こうなります。Laya が効くのは、答えが20個以下に列挙でき、ラベル付きデータが手元にあり、同じ判断を大量に繰り返す処理です。この条件に当てはまるなら、自己ホストできて課金がゼロという利点は大きい。外部にデータを出せない案件では、ほぼこれ一択になります。
逆に、ラベル付きデータがないまま、OSSなのでタダで置き換えられると考えるのは誤りです。素のモデルは多数派ベースラインを下回ります。ここが LLM との最大の性質差で、LLM ならゼロショットでそこそこ動くところを、Laya は必ず自分のデータで仕立て直す前提になります。開発元は無料GPU 2枚で約4時間という追加学習ノートブックを公開しているので、工数そのものは現実的な範囲です。
なお、Jev の周辺には同じ形を再現しようとする OSS が短期間で大量に現れていて、コミュニティのまとめリストには十数件の再現実装と、十数件の独立評価が集まっています。Laya はその中では「独自に学習した重みを実際に出している」数少ない側です。カテゴリ自体が固まるのはもう少し先でしょうから、いま本番に入れるなら、差し替え可能なインタフェースの裏に置いておくのが無難だと考えています。
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
laya.load() が固まる |
TensorFlow が入っていると、読み込み時のプローブでデッドロックする | 環境変数 USE_TF=0 を付けて実行する |
| 選択肢を増やしたら精度が急落した | 選択肢が固定のトークン予算を共有しており、1つあたりの文字数が足りない | 20個以下に抑えるか、粗い分類と細かい分類の2段構成にする |
| 確信度0.9で足切りしたのに誤判定が多い | 出荷時の較正がずれている | 自分のデータで質問型と選択肢数の組ごとに温度を当てはめ直す |
| 日本語で意味不明な結果が高確信で返る | 英語版チェックポイントに非ラテン文字を入れている |
Router を使うか laya-multilingual を直接指定する |
| 言語が切り替わるたびに数秒待たされる | 既定では常駐するチェックポイントが1つ |
Router(preload=True) または max_loaded を増やす |
| 順序尺度の採点がぶれる |
score は3つの型の中で最も弱い |
段階数を減らすか、choice に置き換えて設計し直す |
用語集
| 用語 | 定義 | 検診の比喩での役割 |
|---|---|---|
| System 1モデル | 文章を生成せず、型のついた決定と確率を返すモデルの呼称 | 一次スクリーニングの検査機 |
| 非自己回帰 | 出力を1トークンずつ逐次生成しない方式 | 全項目を同時に判定する |
| RLCD | 較正された決定のための強化学習。正直な確率の申告が最適になる報酬設計 | 所見を盛っても薄めても損をする報酬表 |
| 厳密固有スコアリングルール | 真の分布を申告したときにのみ期待報酬が最大になる関数 | その報酬表が満たすべき性質 |
| ECE | 期待較正誤差。申告した確信度と実際の正解率のずれ | 検査機の自己申告の正直さ |
| 温度スケーリング | ロジットを定数で割って分布の鋭さを調整する手法 | 所見の言い方の強さを校正する |
noul |
真である確率を返す質問型 | 陽性か陰性か |
| ModernBERT | 双方向エンコーダの近年の実装。Laya の英語版の土台 | 検査機のセンサー部 |
学習ロードマップ
関連記事
- Mamba-2 ── 逐次処理を並列な行列積の形に畳み込む話。非自己回帰の発想と地続きです
- Self-Attention ── 本記事の双方向エンコーダが何をしているかの土台
- KVキャッシュ ── 自己回帰生成がなぜ重いのかの内訳
- PyTorch ── 追加学習を自前で回すときの土台
参考文献
- Introducing System One Models & Jev(邦題訳:System One モデルと Jev の紹介)── Jev の公式発表。2026年9月15日。価格、速度、型エラーが起きない主張と、その但し書きまで一次情報として読めます
- convaiinnovations/laya(邦題訳:Laya モデルカード)── 重み、使い方、ベンチマーク、そして Limits 節。本記事で最も重視した一次情報です
- Laya — 33ms Multilingual System 1 Decision Engine(邦題訳:Laya ── 33ミリ秒の多言語 System 1 決定エンジン)── 開発元による技術解説。51言語スイープの結果と限界の記述を含みます
- NandhaKishorM/laya(邦題訳:Laya リポジトリ)── 実装、Router、追加学習ノートブック。ライセンスは Apache 2.0 です
- convaiinnovations/laya-demo(邦題訳:Laya デモ)── 追加学習前の素の挙動を、自分の質問文で確かめられます
Zenn版の記事もあります!
最後まで読んでいただきありがとうございました。日々の検証や技術メモは XやYouTube でも発信しています。