2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自前ホスト可能な判断特化モデル「lev」(Jev互換)を、マルチエージェントに組み込んでみた

2
Last updated at Posted at 2026-10-03

1. levはJevと同じ話し方をする、別会社のオープンモデル

TypeSafe AIが2026年9月に一般提供を始めた「Jev」は、文章を生成しない判断特化のモデルです。X界隈での9月の末はこのJevの話題で持ちきりでした。返すのは「選択」「Yes/No」「採点」の3種類の答えと、その確率だけ。TypeSafeはこの種類のモデルを「System One」と呼んでいます。

私は最初、この本家Jevをマルチエージェントの判断役にするつもりでした。ところが利用登録はwait List 待ち、他のサービス経由の利用してやろうと思いましたが、Vercel AI Gateway経由で呼ぶと429 Too Many Requestsが続き、時間を空けても実験を最後まで通せませんでした。判断が早いのに、使えんのかい!!
そこで、同じAPIで動いて手元に置けるlevに切り替えました。

levは、Jevと同じAPIプロトコル(/v1/systemone)で応答するオープンウェイトのモデルです。中身はQwen3.5-4Bに、約200MBのLoRAアダプタ(元の重みは変えずに差分だけを学習した追加部品)を乗せたもの。ライセンスはApache-2.0で、重みはHugging Face(interfaze-ai/lev)にあります。作ったのはInterfazeという会社で、GitHub(InterfazeAI/lev)のREADMEには次の一文があります。

Not affiliated with or endorsed by TypeSafe AI.

TypeSafeはJevの学習方法も重みも公開していません。つまりlevは、本家の中身を知らないまま入出力の形に合わせて作られたことになります。TypeSafe SDK向けのコードはbase_urlを差し替えるだけでlevにつながりますが、互換なのは通信の形までです。

Interfazeによるlev公開の告知(Xの日本語自動翻訳で表示)

蒸留は使わずに作られ、精度は自社測定で本家の7ポイント下

Jevの出力を正解データにして小さいモデルを学習させる手法を、蒸留と呼びます。TypeSafeの利用規約(Master Customer Agreement)はこれを禁じています。

using the Services or any Output to perform model distillation, train a model to imitate the output of the Services, or develop (or facilitate the development of) a similar or competing product or service

levのREADMEには「29の公開データソースから学習データを作り、評価用ベンチマークS1Benchの13サブセットと重なるものは除いた」とあります。蒸留に頼らず公開データだけで作った、という説明だと私は読んでいます。

そのぶん精度は本家に届いていません。Interfaze自身の測定(S1Bench全13サブセットの平均正解率)では、Jevが76.1%、levが68.9%でした。第三者のリーダーボードJevBench(2026年9月19日時点)にlevは載っておらず、差を示せるのはこの自社測定だけです。

本家にない利点もありました。本家Jevを公式の手段(Vercel経由でnpmのaiパッケージ7.0.116のexperimental_evaluate)で呼んだときには、判断の確信度(confidence)が返ってきませんでした。levでは返ります。

2. BERTの分類器と違い、質問をリクエストのたびに書ける

「判断だけを返すモデル」と聞いて、私が最初に思い浮かべたのはBERTのテキスト分類です。BERTで分類器を作るときは、「返金・技術・営業」のようなラベルを先に決め、そのラベル専用の出力層を学習させます。ラベルを1つ足すだけでも、データを集めて学習し直すことになります。

levでは、質問も選択肢もリクエストの中に自然文で書きます。次の1リクエストには、Yes/No型(noul)と選択型(choice)の2問が同居しています。

{
  "state": "客から二重請求されたので返金してほしいという問い合わせが来た。",
  "questions": {
    "should_refund": {"type": "noul", "instructions": "この問い合わせは返金対応すべきか"},
    "route": {"type": "choice", "instructions": "どの窓口に回すか",
              "criteria": {"billing": "請求・返金", "tech": "技術的な不具合", "sales": "契約・見積もり"}}
  }
}

明日「解約窓口」を足したくなったら、criteriaに1行書き足せば済みます。再学習はいらないようです。

ラベルを実行時に渡すだけなら、NLI(文と文の含意関係の判定)を使うゼロショット分類器でもできます。LLMに構造化出力で答えさせる手もあります。levが違うのは、4B規模の生成モデルが長い状態文を読んだうえで、文章を1トークンも書かずに確率だけを返す点です。土台が分類用のBERT(Encoder)ではなく生成用のQwen(Decoder)なので、質問文を読んで答える力はLLMから引き継いでいるはずです。私はlevを、固定の分類器というより「判断の出力に特化した小型LLM」と捉えています。

Yes/No型の答えでは、Yesである確率が同名のフィールドnoulに、答えの偏り具合がconfidenceに入ります(中身は3章で見ます)。選択型では、選んだ選択肢と、選択肢ごとの確率が返ります。ClaudeのAPIではトークンの確率を取り出せないので、この値をそのまま線引きに使えるのは、LLMに直接聞く場合との違いになります。

3. Azure T4 1枚で動くが、16GBに収めるにはlevの手直しが要った

検証環境

項目 内容
マシン Azure Standard_NC4as_T4_v3(NVIDIA T4 16GB、4 vCPU)、西日本リージョン
料金 従量課金で$0.768/時(2026年9月時点)
OS / Python Ubuntu 22.04 / Python 3.12(uv venv --python 3.12で用意)
lev 0.1.1(Abhinavexists/levのコミット cf104b6)
langchain-typesafe 0.0.1a3
OneManCompany コミット 3fde188(2026年8月26日)
生成用のLLM Claude Haiku 4.5(claude-haiku-4-5-20251001)
検証日 2026年9月27日

意外かもしれませんが、GPUが必要です、早いのには理由があるのか・・・。T4を選んだのは、このサブスクリプションで申請なしに使えた最小のGPUだったからです。A10をスポット価格で使う案もありましたが、スポット枠の引き上げ申請がRequestThrottledで2回とも通りませんでした。

起動とリクエスト

インストールと起動は2行で済みます。インストール元は、Hugging Faceのモデルカードに書かれているメンテナ個人のリポジトリ(Abhinavexists/lev)です。

pip install "lev[serve] @ git+https://github.com/Abhinavexists/lev#subdirectory=packages/lev"
lev serve --checkpoint interfaze-ai/lev --host 127.0.0.1 --port 8100

lev serveの起動ログは次の通りです。
lev serveの起動ログ(CPU環境で撮影)
スクリーンショット 2026-10-01 152915.png

causal_conv1dはGPU向けの高速な計算ルーチンで、入っていないと遅い標準実装に切り替わります。GPU上でflash-linear-attentionも入れて試しましたが、この環境では速くなりませんでした。

lev serveには認証がないので、ポートは外に開けていません。呼び出し側のVMからは、SSHのポートフォワードでつなぎました。

ssh -f -N -L 127.0.0.1:8100:127.0.0.1:8100 azureuser@<GPU VMのIP>

2章のリクエストのうち、返金の質問だけを投げるとこう返ります。

lev serveへのcurlとレスポンス(CPU環境で撮影。レスポンスの形はGPUと同じ
スクリーンショット 2026-10-01 152857.png

probabilitiesの"0"〜"8"は、9段階の評価(0が「強くNo」、8が「強くYes」)それぞれの確率です。noulはこれを1つにまとめた値で、段階を0〜1に直して確率で重み付けした平均(Σ i/8 × pᵢ)になります。ここでは「8」が0.93を占めるので、noulは0.95になりました。confidenceは分布がどれだけ1か所に偏っているかを表す値で、levの実装(model.pyの_gini)では、全段階が同じ確率なら0、1つの段階に集中すれば1になります。

応答時間は、単体で測ると短い質問で0.42秒でした(起動後の1回目だけ約5秒)。1,734トークンのレビュー文では約6秒、2,196トークンで3案を比べる選択型では約12秒かかりました。5章の実行では、レビューの入力が約3,800〜4,400トークン、選定が約1,000トークンでした。その実行での秒数は記録していません。TypeSafeが公表しているホスト版Jevの150ms前後には遠く及びません。それでも単体計測の範囲では、マルチエージェントの1ステップとして待てる長さでした。

長い入力では、GPUメモリが2か所で溢れた

最初は、長いレビュー文を送るとGPUのメモリ不足(CUDA out of memory)で500が返りました。原因は2つありました。

1つ目は語彙の大きさです。levの実装は、選択肢ごとに入力を1行ずつ複製して一括で計算します。そのとき、入力の全位置について、次に来るトークンの候補24万8,320語それぞれのスコア(logits)を出していました。実際に使うのは各行の末尾だけなのに、この確保だけで4.55GiBを要求していました。2つ目は注意機構(attention)の計算で、全行を一度に流すと、行数×文長の2乗に比例するメモリを取ります。こちらは3.08GiBを要求して落ちました。

どちらもlev/model.pyの_forward_singleを数行直せば避けられました。必要な末尾の位置だけlogitsを出させ(logits_to_keep)、行を小分けにして流します。LEV_MICRO_BATCHは、このとき私が足した環境変数で、上流のlevにはありません。

first_last = 0 if want_hidden else min(len(r) - 1 for r in rows[:n_real])
keep = width - first_last          # 末尾の必要な位置だけスコアを出す
chunk = int(os.environ.get("LEV_MICRO_BATCH", "2"))   # 独自に追加
for start in range(0, batch.shape[0], chunk):          # 行を小分けにして流す
    out = self.model(input_ids=batch[start:start + chunk],
                     attention_mask=attention[start:start + chunk],
                     use_cache=False, logits_to_keep=keep)
    logit_parts.append(out.logits)

最終的にLEV_MICRO_BATCH=1(1行ずつ)で落ち着きました。GPUメモリの使用量は約9.2GBで、直す前と後で出力の値(noul、confidence、選ばれた案)が一致することを確かめてあります。

CPUでは1回15分を超えた

GPUに移る前は、CPU 4コア・メモリ15GBのVMで動かしていました。スレッド数を明示すると最初の応答は約7分から約23秒に縮みましたが、レビュー文のような長い入力は1回15分を超えました。langchain_typesafeのTypeSafeClassifierはHTTPタイムアウトの既定値が30秒なので、CPUで試すならそこも延ばしておきたいところです。30秒で切れたリクエストをlevは計算し続け、そこへ再送が積み上がったとみています(量は測っていません)。最後は、メモリ不足でLinuxがプロセスを強制終了する仕組み(OOM Killer)にlevを止められました。

4. マルチエージェントでは、合否と選定だけをlevに回した

題材はOneManCompany(OMC)です。「AI社員だけの仮想会社」を再現するOSSで、エージェントの処理手順をグラフとして組むライブラリLangGraphで作られています。CEOがタスクを投げると、秘書役のEAが受け取り、COOがサブタスクに分けて部下に割り振ります。COOは上がってきた成果物をレビューして、合格か差し戻しかを決めます。

OMCのオフィス画面。中央がCEO、両側に社員

スクリーンショット 2026-09-27 175136.png

OMCのLLM呼び出しには、文章や成果物を書く「生成」と、合否や採否を決める「判断」が混ざっています。生成はこれまで通りClaudeに任せ、判断だけをlevに回しました。具体的には、OMCでモデルを生成する関数onemancompany.agents.base.make_llmを差し替えました。レビュー用のノード(LangGraphのグラフ上の処理単位)を実行している間だけ、この関数がlevを呼ぶ薄いラッパーを返します。ラッパーは、OMCが組み立てたレビュー文をstateに入れ、案ごとにYes/Noを1問ずつ聞きます。

questions = {
    f"accept_{i}": Noul(instructions=f"「{_label(s)}」の成果物は受け入れ基準を満たしているか")
    for i, s in enumerate(self.subtasks)
}

全案が合格になった回だけ、続けて「どれを採用するか」を選択型で聞きます。levの答えは、OMCのエージェントが普段呼んでいるツール(accept_child/reject_child)の呼び出しや、ノードの最終回答に変換して返します。OMC側は、Claudeが判断したときと同じ経路でそれを実行します。

つまずいた点、リジェクトしたら、再作成してくれるのはいいけど、無限ループしてしまった。

いちばん影響が大きかったのは、最初の設計で「3案から1つ選んだら、残りの2案はreject_childで差し戻す」としていたことです。OMCの差し戻しは「作り直せ」という意味なので、落選した案はClaudeに書き直され、再挑戦の上限に達すると「失敗」扱いになりました。親タスクは失敗した子を抱えたまま止まり、その実行ではClaude代が$2.85まで膨らみました。
判断モデルの答えそのものは単純でも、既存ワークフローの状態遷移との対応づけ次第で挙動が大きく変わる、というのが今回の実感です。

5. Claude代は56%減ったが、中身のない成果物も合格にした

題材のタスクは、社内向け「備品貸出管理AIアシスタント」の訴求コピーづくりです。コスト・セキュリティ・利便性の3つの切り口で案を作り、COOが1つを採用します。このタスクを、判断もClaudeに任せるBeforeと、判断はlevに任せるAfterで1回ずつ走らせました。OMCの設定(OMC_DEBUG=1)もそろえてあります。回数が1回ずつなので、以下の数字にばらつきの評価は含まれていません。

Before(判断もClaude) After(判断はlev)
Claudeの費用(全呼び出し、定価換算) $2.92 $1.29
うち判断(レビュー3回。最後の1回は選定を含む) $1.46 $0
levの呼び出し なし 4回(合否3回、選定1回)
依頼から最後のCOO判断まで 3分47秒 2分03秒
GPU代(その時間ぶん) なし 約$0.03
コピーが実際に書かれた案 3案 1案
採用された案 コスト訴求 セキュリティ訴求

費用は、OMCのデバッグログ(debug_trace.jsonl)に残る全呼び出しのトークン数を、Haiku 4.5の定価(入力$1、出力$5/100万トークン)で換算しました。プロンプトキャッシュの割引は考えていないので、上限寄りの値です。OMCの画面に出る金額(Before $2.26、After $0.49)は、EAとCOOのタスク分解が抜けた値なので、参考にとどめました。

差額$1.62のうち$1.46は、Beforeのレビュー3回の分です。最後のレビューだけで約75万トークンを読み、約1万トークンの選定レポートを書いていました。Claudeは「考える、ツールを呼ぶ、結果を読む」を繰り返すReActというループで動きます。1回ごとに、役割ガイドとツール定義、それにレビュー文(各案の本文・受け入れ基準・検証結果)を含むそれまでのやり取りを、まとめて送り直します。ループが回るほど入力が積み上がり、1ノードで26万〜75万トークンになりました。levへの入力は1回4,000トークン前後で、最後のレビューでClaudeが読んだ量の200分の1ほどです。

社員の作業はAfterのほうが約$0.25安く済みました。ただしBeforeでは書き直しが1回あり、Afterでは2案がコピーを書かずに終わっているので、この部分は失敗が安く見せている分です。EAとCOOのタスク分解は、逆にAfterのほうが約$0.08多くかかりました。

Afterでは、コスト訴求と利便性訴求を担当した社員(Claude)がコピーを書かず、「コピー作成は自分の役割外だ」とCEOにエスカレーションする報告を返していました。levはこの2件を合格にしました。返ってきたYesの確率は次の通りです。

案 実際の中身 P(yes)
セキュリティ訴求 コピーが書かれている 0.96
コスト訴求 エスカレーションの報告だけ 0.92
利便性訴求 エスカレーションの報告だけ 0.75

Beforeでも、コスト訴求の担当者が一度「スキルが足りない」という報告を返しました。Claudeはそれを差し戻して書き直させ、最終的に3案がそろいました。今回の3件を見る限り、levは成果物がコピーそのものか、報告にすぎないかを見分けられていませんでした。報告の体裁や受け入れ基準への言及に引っ張られたように見えます(原因は切り分けていません)。以前の別の実行でも、「キャンセルしました、指示待ちです」という報告を合格にしていました。

確率の並びには手がかりがあります。本物のコピーが0.96で最も高く、中身のない2件はそれより低い値でした。合格の線を0.95に引けば、この3件は正しく分けられた計算になります。ただし、これは結果を見てから決めた値で、1回の実行から言える話ではありません。最後の選定では、levは唯一まともなセキュリティ訴求案を確率0.76で選びました。このときのconfidenceは0.41でした。3章で見た通り、confidenceは分布の偏り具合なので、1位の確率とは別の値になります。3案の確率(0.76、0.15、0.09)から計算すると、ちょうど0.41になります。

T4のVMを立てっぱなしにすると、$0.768×24時間で1日約$18.4かかります。今回の差額からGPU代を引くと1回あたり約$1.60で、失敗の分(約$0.25)も除くと約$1.35です。元が取れるのは、この種のタスクを1日に12〜14回以上流すときです。それより少ないなら、使うときだけVMを起動するほうが安く済みます。

6. 書くのはLLM、書き切れる判断はlev、中身の確認はLLMへ戻す

判断を任せた部分の費用は消えましたが、見逃したのは「成果物がそもそも出ているか」でした。この2つの境目から、私は次のような分担を考えています。

  • 文章や成果物を書く仕事は、LLMに任せる。
  • 問いと選択肢を言葉で書き切れる判断は、levに任せる候補になる。今回確かめたのは合否と3案からの選定だけで、2章の窓口の振り分けのような判断は実験していない。
  • levの確率が線に届かない判断や、中身が伴っているかを見る判断(今回ならエスカレーションの報告をコピーと取り違えた件)は、LLMに戻す。
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?