Kiro Crew の Auto (Jev) によるモデルルーティングをローカル Kev で試してみた

Kiro Crew の Auto (Jev) によるモデルルーティングをローカル Kev で試してみた

プロンプトの複雑さに応じて haiku・sonnet・opus を自動で使い分ける Kiro Crew 0.7 のプレビュー機能を確認しました。判定エンジンは既定の Jev ではなく、互換 API を持つ Kev を M4 Mac の MLX でローカルに動かして使っています。
2026.09.27

はじめに

Kiro Crew 0.7 で Auto (Jev) モードがプレビュー扱いでリリースされました。

https://kiro.dev/changelog/crew/0-7/

判定用の小さな LLM がプロンプトを simple / medium / complex に分類し、その結果に応じて応答に使うモデルを切り替えられます。判定エンジンはデフォルトでは typesafe.ai の Jev ですが、互換 API のエンドポイントも利用できます。

Kev そのものは先行記事で、Jev の互換 API として利用できること、M4 Mac の MLX で 4B が実用的な速度と判定精度で動くことを確認しています。

https://dev.classmethod.jp/articles/kev-oss-jev-compatible-mlx-mac/

本記事では、その M4 Mac で動かした Kev を、Kiro Crew の Auto (Jev) の判定エンジンとして利用した結果を紹介します。

構成の全体像

登場するものは、Auto (Jev) モードで動く Kiro Crew、判定エンジンとして動くローカル Kev(モデル名 jev-latest)、複雑さに応じて Crew が使い分ける複数のモデルの 3 つです。

プロンプト
  → Crew(Apple container 内、Auto (Jev) モード)
  → ホストの Kev(/v1/systemone)が複雑さを判定
  → tier(simple / medium / complex)
  → decisions.model_route で対応モデルを選び、そのモデルで応答

Kev はコンテナに入れず、ホストの MLX でネイティブに動かします。

バージョン情報

  • Mac mini M4 Pro、macOS 27.0
  • Apple container 1.4.1
  • Kiro Crew 0.7.1(kiro-cli 2.24.0)
  • Kev(kev-4b、release_date 2026-09-24)

Apple container を入れる

Kiro Crew は Apple container 上で動かします。Crew はエージェントとしてコマンドを実行し、ファイルを読み書きします。そのため、使えるメモリの上限や、ホストのどこに触れるかといったリソース管理とアクセス制御を、コンテナ側に担わせたいと考えました。Apple container は macOS 標準の仕組みだけで Linux コンテナを 1 つずつ独立した VM として動かせるので、Docker Desktop などを追加せずにシンプルに使えます。

https://github.com/apple/container

apple/container の Releases から 1.4.1 の pkg を入れます。

container system kernel set --recommended
container system start
container run --rm ubuntu:latest uname -m

container system kernel set --recommended で Kata の arm64 カーネルを設定し、サービスを起動しました。素の OCI イメージで疎通を確認しました。

aarch64

Crew を起動する

ghcr.io/kirodotdev/kirocrew:stable は匿名で pull できます。

container image pull ghcr.io/kirodotdev/kirocrew:stable

ログイン状態、config.json、Secrets Vault はすべて /home/kirocrew の下にあります。イメージの更新やメモリ割り当ての変更でコンテナを作り直すとこれらが消えるため、/home/kirocrew を named volume に置きます。

container volume create kirocrew-home
container run --rm --user root --entrypoint sh \
  -v kirocrew-home:/mnt ghcr.io/kirodotdev/kirocrew:stable \
  -c "cp -a /home/kirocrew/. /mnt/ && chown -R 1000:1000 /mnt"

新規 volume は root:root でマウントされ、uid 1000 の kirocrew ユーザーが書き込めないので、先に root でホームの内容をコピーし、所有者を 1000 に揃えました。

この volume をマウントして Crew を起動します。

container run -d --name kirocrew \
  --memory 8g --cpus 4 \
  -v kirocrew-home:/home/kirocrew \
  --publish 127.0.0.1:5476:5476 \
  -e KIROCREW_BIND=0.0.0.0 \
  ghcr.io/kirodotdev/kirocrew:stable gateway --no-open --port 5476

コンテナのメモリ割り当ては、5 GB で起動したところ起動直後のアイドル時の消費が 2 GB 強で、既定の 1 GB では起動に失敗しました。Crew は会話や記憶データが溜まるにつれて消費が増えるので、余裕をもたせて 8 GB を割り当てています。

kirocrew 2.24 GiB / 8.00 GiB (Cpu 0.30-1.06%, Pids 59)

ダッシュボードはコンテナ内で 0.0.0.0 に bind し(KIROCREW_BIND)、ホスト側は --publish 127.0.0.1:5476:5476 で loopback だけに公開します。ブラウザから http://127.0.0.1:5476/ で開けます。

Crew の設定は kirocrew config set で行います。コンテナ内で実行するので、以降の kirocrew コマンドは container exec kirocrew を前に付けて叩きます。まず agent.sandbox_allow_unsandboxed_exec を true にします。Kata VM では user namespace が使えず、Crew 内のサンドボックスが動かないため、true にしないとコマンド実行ができません。隔離は VM 境界が担います。

container exec kirocrew kirocrew config set agent.sandbox_allow_unsandboxed_exec true

コンテナにブラウザがないため、ログインには device flow を使います。

container exec -it kirocrew kiro-cli login --use-device-flow

コンテナを作り直してもログインと設定は volume に残ります。

判定エンジンをローカル Kev に向ける

Kev の設定

Kev はホストで動かします。導入と起動の手順は先行記事(MLX Mac 版)のとおりです。変える点は待ち受けアドレスと API キーの 2 つです。Apple container のコンテナは独立した VM で、ホストは既定ネットワーク(192.168.65.0/24)のゲートウェイ 192.168.65.1 として見えます。Kev の既定の待ち受けは 127.0.0.1 です。コンテナからはホストの loopback に届かないので、--host 0.0.0.0 で待ち受けます。0.0.0.0 で待ち受けると、同じ LAN の他のマシンからも到達できるため、KEV_API_KEY を設定して Bearer 認証を要求します。認証情報は平文 HTTP で送られるので、利用は信頼できるネットワークに限ります。

# 鍵生成例: openssl rand -hex 16
KEV_API_KEY=<key> uv run python -m kev.serve --run jaredpalmer/kev-4b --host 0.0.0.0 --port 8009

コンテナ内から Bearer トークン付きで http://192.168.65.1:8009/v1/models を叩き、ホストの Kev に到達できることを確認しました。応答の models から主要なキーを抜き出しています。

{"name": "kev-latest", "run": "jaredpalmer/kev-4b", "base": "Qwen/Qwen3.5-4B-Base", "device": "mps", "backend": "mlx", "dtype": "bfloat16"}
{"name": "jev-latest", "run": "jaredpalmer/kev-4b", "base": "Qwen/Qwen3.5-4B-Base", "device": "mps", "backend": "mlx", "dtype": "bfloat16"}

kev-latest と jev-latest の 2 件が並びますが、中身は同じ kev-4b です。Kev は Jev 向けの設定をそのまま受けられるよう jev-latest の名前でも応答するので、Crew 側の判定モデル名は既定の jev-latest のままで済みます。

Crew の設定

判定エンジンの向き先は decisions.provider で設定します。既定では endpoint が typesafe.ai の Jev を指し、model は jev-latest、api_key は secret://TYPESAFE_API_KEY です。このうち model と api_key はそのまま使えるので、変えるのは endpoint と timeout_ms の 2 つです。kev-4b の判定は 1 秒を超えることがあるので、timeout_ms は既定の 1000 から 5000 に伸ばしました。

container exec kirocrew kirocrew config set decisions.provider.endpoint http://192.168.65.1:8009/v1/systemone
container exec kirocrew kirocrew config set decisions.provider.timeout_ms 5000

API キーはダッシュボードの Settings › Secrets で登録します。名前に TYPESAFE_API_KEY、値に Kev 起動時の KEV_API_KEY を入れます。api_key の secret://TYPESAFE_API_KEY はこの登録を参照する指定なので、config.json に値を直接書いても参照されません。

最後に、ダッシュボードの Settings › Developer にある「Decisions (Jev)」をオンにします。この操作で decisions_consent.json に「どの endpoint へ判定を送ることに同意したか」が記録され、config の endpoint と一致している間だけ判定が送られます。有効化したあとに endpoint を変えると同意が外れるので、kirocrew config set を先に済ませておきます。カード下部の「Sent to」に、いま同意している送信先が表示されます。

Settings › Developer の Decisions (Jev) 設定カード。スイッチがオンで、下に tool-call arguments / compaction / recalled memories の 3 つの追加スイッチがあり、Sent to に送信先 endpoint が表示されている

同じカードの「Also send tool-call arguments so Jev can flag risky calls」をオンにすると、ツール実行の引数も判定エンジンに送られ、後述する tool.risk の判定が記録されるようになります。

モデル自動選択の設定

tier とモデルの対応は decisions.model_route で定義します。モデル ID は kiro-cli chat --list-models が返す正規の ID と完全に一致させます。

container exec kirocrew kirocrew config set decisions.model_route.simple claude-haiku-4.5
container exec kirocrew kirocrew config set decisions.model_route.medium claude-sonnet-5
container exec kirocrew kirocrew config set decisions.model_route.complex claude-opus-5

設定後の decisions は次のとおりです(bucket と history_budget_chars は省略しています)。

container exec kirocrew kirocrew config get decisions
{
  "model_route": {
    "simple": "claude-haiku-4.5",
    "medium": "claude-sonnet-5",
    "complex": "claude-opus-5"
  },
  "provider": {
    "endpoint": "http://192.168.65.1:8009/v1/systemone",
    "api_key": "secret://TYPESAFE_API_KEY",
    "model": "jev-latest",
    "timeout_ms": 5000
  }
}

gateway の再起動は不要です。設定後の新しいターンから適用されます。provider と model_route を設定したうえで、チャット画面のモデル選択で「Auto (Jev)」を選ぶと、そのチャットのターンごとにモデルが自動選択されます。

プロンプトの複雑さでモデルが切り替わる

同じ設定で、複雑さの違う 3 種類のプロンプトを送りました。ダッシュボードの推論欄に「モデル・Jev: <tier> → <model>」の行が表示され、判定結果と使われたモデルが確認できます。

「指数バックオフとジッター付きのリトライ処理を Python で実装したい。計画書作れる?」は simple と判定され、claude-haiku-4.5 が使われました。

simple と判定され claude-haiku-4.5 が使われた画面。「モデル・Jev: simple → claude-haiku-4.5 (0.51) 367 ミリ秒・既定: claude-haiku-4.5」の表示

続けて、同期・非同期両対応、リトライ対象例外の指定、Full Jitter などの要件を並べた実装依頼を送りました。medium と判定され、既定の claude-haiku-4.5 から claude-sonnet-5 へ引き上げられました。

medium と判定され claude-sonnet-5 が使われた画面。「モデル・Jev: medium → claude-sonnet-5 (0.54) 433 ミリ秒・既定: claude-haiku-4.5」の表示

最後に、マイクロサービス構成の EC サイト設計やマルチリージョン DR などの複合的な設計依頼をまとめて送りました。complex と判定され、claude-opus-5 が使われました。

complex と判定され claude-opus-5 が使われた画面。「モデル・Jev: complex → claude-opus-5 (0.94) 1,291 ミリ秒」の表示

UI の表示が実際の適用と一致しているかは、コンテナ内の ~/.kiro/crew/decisions/decisions-20260926.jsonl で確認できます。model_chosen に判定で選ばれたモデル、applied に true、model_used に実際に応答したモデルが入ります。latency_ms は UI の数値と一致し、p は UI では小数第 2 位に丸めて表示されます。

{"ts": "2026-09-26T19:36:51.082983+00:00", "point": "model.route", "session": "966e99cb3241", "latency_ms": 367, "scrubbed": false, "answers": null, "error": null, "turn_id": "6fef49f32c544b78", "tier": "simple", "p": 0.511, "model_chosen": "claude-haiku-4.5", "baseline_model": "claude-haiku-4.5", "applied": true, "model_used": "claude-haiku-4.5"}
{"ts": "2026-09-26T19:38:02.292348+00:00", "point": "model.route", "session": "966e99cb3241", "latency_ms": 433, "scrubbed": false, "answers": null, "error": null, "turn_id": "30ed95d1a13c4051", "tier": "medium", "p": 0.5388, "model_chosen": "claude-sonnet-5", "baseline_model": "claude-haiku-4.5", "applied": true, "model_used": "claude-sonnet-5"}
{"ts": "2026-09-26T19:42:41.654482+00:00", "point": "model.route", "session": "27910aabfe4f", "latency_ms": 1291, "scrubbed": false, "answers": null, "error": null, "turn_id": "944e2b318deb4202", "tier": "complex", "p": 0.939, "model_chosen": "claude-opus-5", "baseline_model": "", "applied": true, "model_used": "claude-opus-5"}

ツール実行のリスク判定にも使われる

同じ判定エンジンは、モデル選択だけでなくツール実行のリスク判定にも使われます。前述の「Also send tool-call arguments」をオンにした状態では、decisions ログに point=tool.risk の行が記録され、Kev が shell 実行の安全性を判定していました。

{"ts": "2026-09-26T19:45:56.827730+00:00", "point": "tool.risk", "session": "966e99cb3241", "latency_ms": 1219, "scrubbed": false, "answers": null, "error": null, "turn_id": "3e50555e78ff46ee", "tool": "shell", "tier": "safe", "p": 0.5588, "policy": "trust", "flagged": false}

まとめ

Kiro Crew 0.7 でプレビュー(オプトイン)として利用可能になった Auto (Jev) は、プロンプトの複雑さを Jev が判定し、その結果に応じてモデルを使い分けるルーティングを可能にします。

Kiro 本体にもモデルを自動選択する Auto があり、それだけでも十分実用的です。一方で、難しい課題だけをハイエンドのフロンティアモデル(Claude Opus や Fable、GPT Sol など)に任せ、簡単なタスクは廉価なオープンウェイトモデル(GLM や DeepSeek など)に振り分けたい、といった自分なりの対応表を持ちたいケースでは、Auto (Jev) が有効と考えられます。

また、判定エンジンとして Jev 互換の Kev を、ローカルで実用的な性能で動かすことができました。判定に渡すプロンプトを外部へ送ることのリスクや、判定モデルのコストが問題となり得る場合は、今回の構成をお試しください。

参考リンク

https://dev.classmethod.jp/articles/nvidia-nemo-switchyard-first-touch/

https://dev.classmethod.jp/articles/jev-for-llm-model-routing/

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事