背景
9/17に社外勉強会に参加させていただきました(社外勉強会にちゃんと参加するのは初めてでしたので非常に良い機会でした)。その懇親会の中である方からJevについて教えていただきました。当初は「面白そう」くらいの理解でしたが、調べていくうちにまあまあ革命起こりそうなツールであることがわかったので、一旦ハーネスに組み込んで使い心地を検証してみようという、そんなお話です。
目次
Jevって何
Jevは、文章やデータを読み取って、分類や採点、条件判定の結果を返すAIモデルです。なので、基本的にユースケースとしては処理を分岐する時に利用するといった感じかなと思います。
開発元はTypeSafe AI。CEOはDiogo Almeidaです。この会社が「System One Model」という新しいモデル区分を掲げており、その最初のモデルがJevです。
従来のLLMとはどう違うのか、それは出力がアプリケーションが利用しやすい方式になっているということです。 従来のLLMは自然言語を出力します。この自然言語は別に正規化されているわけでもない、何が出力されるかお楽しみ状態です。よってアプリケーションとして利用するのはやや不便です。 それに対してJevはアプリケーションが利用しやすいように、質問への選択結果や確率を決まった形で返却してくれます。例を以下に示します。
入力
{
"state": {
"customer_message": "クレジットカードで同じ商品代金が2回請求されています。返金してほしいです。"
},
"questions": {
"support_team": {
"type": "choice",
"instructions": "どの担当チームに振り分けるべきですか?",
"criteria": {
"billing": "請求・支払い・返金に関する問い合わせ",
"technical": "システムの不具合に関する問い合わせ",
"account": "アカウントに関する問い合わせ"
}
}
}
}
stateで状況を記述、questionsで質問を定義します。同じstateで複数の質問を一気に投げることも可能です。questionsの中には、typeで用途、instructionsで質問内容、criteriaで要素を記述します。typeについては下記のような種類があります。
| type | 用途 | 主な入力 | 主な出力 | 利用例 |
|---|---|---|---|---|
choice |
複数の候補から最も適切なものを選択する | criteriaに候補名と判定基準を指定 |
選択された候補と、各候補の確率 | 問い合わせの分類、担当チームへの振り分け |
noul |
条件に当てはまる確率を0〜1で判定する | instructionsに判定したい条件を指定 |
条件に当てはまる確率 | 承認の要否、ガードレールや処理実行条件の判定 |
score |
定義した段階に沿って対象を評価する | instructionsに評価内容、criteriaに評価段階を指定 |
評価段階に基づく数値スコア | 重要度、緊急度、品質などの段階評価 |
どのように組み込んだのか
これまで、私が利用しているハーネスには以下の課題がありました。
- issueに対する分類が属人的である。-> めんどくさいから軽微な修正もspeckitに任せていた
- /clarifyなどの品質ゲートを必須で使っていた -> 使いすぎてトークン消費量が尋常じゃない
- モデル選択が基本的にsonnet これらの課題に対してJevを使ってみました。また、トークン最適化したいということで、判断については基本的にJevに任せる方針にしました。
作業開始前の全体フロー
IssueまたはCLIから作業依頼を受け取ると、work-request-triageが作業内容の分類、必要な品質ゲートの判定、使用モデルの選択を行います。全体の流れは次のとおりです。
| 順序 | 処理 | 分岐・結果 |
|---|---|---|
| 1 | IssueまたはCLIから作業依頼を受け取る | work-request-triageを開始する |
| 2 | Jevのchoiceで作業を分類する |
feature・bug・fixのいずれかを返す |
| 3 | 分類結果の確信度を確認する | 低確信度なら処理を停止して人間へ確認する。高確信度なら次へ進む |
| 4 | 分類結果に応じたフローへ進む | featureはspecを作成する。bugとfixは修正フローへ進む |
| 5 | featureの場合だけ、Jevのnoulで品質ゲートを判定する |
clarify・analyze・checklistの要否をそれぞれ判定する |
| 6 | Jevのchoiceで作業レベルを判定する |
simple・standard・advancedから使用モデルを決める |
| 7 | 判定結果を記録する | triage-log.jsonlへ保存する |
処理の要点は次の4つです。
- 作業分類: Jevの
choiceを使い、依頼をfeature・bug・fixのいずれかに分類する - 品質ゲート判定:
featureの場合のみ、Jevのnoulを使ってclarify・analyze・checklistが必要か判定する - モデル選択: Jevの
choiceを使い、作業をsimple・standard・advancedのいずれかに分類して使用モデルを決める - 結果の記録: 分類結果、確信度、品質ゲート、モデル選択などを
triage-log.jsonlへ記録する
分類の確信度が基準を下回った場合は自動処理を進めず、人間の確認を待ちます。これにより、判断が曖昧な依頼を誤ったフローへ自動で流すことを防ぎます。
Jevへ送る内部リクエストの全体像
ここまでのフローでは、Jevを3つの判断に利用しています。実際のJev APIへ渡すリクエストは、判定対象を表すstateと、判定方法を表すquestions.resultで構成されます。
| 判定 | type |
判定対象 | 目的 |
|---|---|---|---|
| 作業分類 | choice |
Issueのタイトルと本文 | feature・bug・fixへの分類 |
| 品質ゲート判定 | noul |
作成した仕様 | clarify・analyze・checklistの要否判定 |
| モデル選択 | choice |
分類結果と依頼本文 | simple・standard・advancedへの分類 |
CLIで指定する--criteriaや--instructionsは、次の内部JSONを組み立てるためのラッパーです。
1. 作業分類の内部リクエスト
最初に、Issueのタイトルと本文をstateへ入れ、choiceで作業の種類を判定します。
{
"state": {
"title": "ログイン時にエラーが出る",
"body": "ログイン画面を開いたときにエラーが発生する\n\n再現手順:\n1. ログイン画面を開く\n2. 正しい情報を入力する\n3. 送信ボタンを押す"
},
"questions": {
"result": {
"type": "choice",
"instructions": null,
"criteria": {
"feature": "新機能・新しい振る舞いの追加",
"bug": "既存機能が意図通り動作していない不具合の修正",
"fix": "既存の要件・受け入れ基準・設計判断を変えない機械的な修正"
}
}
}
}
これは.claude/skills/work-request-triage/SKILL.mdにある「分類(Jev Choice)」の実体です。返り値は、たとえば次のようになります。
{
"choice": "bug",
"probabilities": {
"feature": 0.07,
"bug": 0.88,
"fix": 0.05
},
"confidence": 0.94
}
この例ではbugの確率が0.88です。ハーネスでは選択された候補の確率が0.75以上なら高確信度として処理を進め、下回った場合は自動処理を止めて人間へ確認します。
2. 品質ゲート判定の内部リクエスト
作業分類がfeatureだった場合は、作成した仕様全体をstateとして渡し、noulで品質ゲートの要否を判定します。たとえば、clarifyが必要かを判定するリクエストは次の形です。
{
"state": "## 仕様\n- ユーザーはログインできる\n- 送信時にエラーが出ない\n- エラー時には分かりやすいメッセージを表示する\n\n## 制約\n- 既存の認証フローを壊さない\n- 既存のUIを大きく変えない",
"questions": {
"result": {
"type": "noul",
"instructions": "この仕様には、実装を始める前に運営者へ確認すべき未解決の曖昧さが残っている"
}
}
}
返り値は条件に当てはまる確率です。
{
"noul": 0.81
}
品質ゲートでは0.5をしきい値としているため、この例では0.81 >= 0.5となり、clarifyが必要だと判断します。
同じ仕様に対して、analyzeとchecklistもそれぞれ独立したnoulリクエストで判定します。異なるのはinstructionsです。
| 品質ゲート | instructions |
|---|---|
clarify |
この仕様には、実装を始める前に運営者へ確認すべき未解決の曖昧さが残っている |
analyze |
この仕様は複数のUser Story・要件間の整合性チェック(矛盾・抜け漏れ)が必要なほど複雑または影響範囲が広い |
checklist |
この仕様には、セキュリティ・パフォーマンス・アクセシビリティ等、専用チェックリストでの確認が必要な非機能要求が含まれる |
つまり、品質ゲート判定では1回のリクエストで3項目をまとめて判定するのではなく、目的の異なるnoulを3回実行します。
3. モデル選択の内部リクエスト
最後に、作業分類の結果と依頼本文をstateへ入れ、別のchoiceで作業の難易度を判定します。
{
"state": {
"classification": "bug",
"body": "ログイン画面を開いたときにエラーが発生する\n\n再現手順:\n1. ログイン画面を開く\n2. 正しい情報を入力する\n3. 送信ボタンを押す"
},
"questions": {
"result": {
"type": "choice",
"instructions": null,
"criteria": {
"simple": "単純な定型作業(誤字修正、設定ファイルの機械的な追記、既存パターンの模倣)",
"standard": "中程度の作業(機能追加の実装、複数ファイルにまたがる変更)",
"advanced": "高度な作業(設計判断、複数の設計案の比較検討、複雑な調査・デバッグ)"
}
}
}
}
返り値は、たとえば次のようになります。
{
"choice": "standard",
"probabilities": {
"simple": 0.08,
"standard": 0.85,
"advanced": 0.07
},
"confidence": 0.91
}
選択された作業レベルは、次のようにClaudeのモデルへ対応させます。
| 作業レベル | 使用するモデル |
|---|---|
simple |
claude-haiku-4-5 |
standard |
claude-sonnet-5 |
advanced |
claude-opus-5 |
以上のように、Jev連携の本体は「作業分類のchoice」「品質ゲート判定のnoul」「モデル選択のchoice」という3種類の判断です。Jevは判断結果と確率を返し、その結果をどう扱うか、どの処理を実行するかはwork-request-triage側が担当します。
出力例は以下です。各選択肢に対して確率が記載されています。
{
"department": {
"choice": "billing",
"probabilities": {
"billing": 0.97,
"technical": 0.02,
"account": 0.01
},
"confidence": 0.94
}
}
Jevとスキルの役割分担
今回のハーネスでは、Jevを「最終的な実行そのもの」ではなく、「判断の入口」として組み込みました。つまり、Issueのタイトルと本文を入力として受け取り、まずは分類・品質ゲート判定・モデル選択をJevに委ね、その結果をもとにスキル側が次のアクションを実行する形です。
Jevは自分で処理を走らせるのではなく、確率付きの判断結果を返してくれるだけです。そのため、Jevが出した結果をそのまま自動実行にしてしまうのではなく、スキル側で確信度を見て、低い場合は確認を待つようにしています。これにより、誤った判定がそのまま実行に繋がるリスクを下げることができます。
実際の入力は以下のような形です。
{
"source": "github-issue",
"issueNumber": 49,
"title": "ログイン時にエラーが出る",
"body": "ログイン画面を開いたときにエラーが発生する\n\n再現手順:\n1. ログイン画面を開く\n2. 正しい情報を入力する\n3. 送信ボタンを押す\n\n期待結果: ログインできる\n実際結果: 500エラーが発生する"
}
この入力をスキル側で整形し、Jevには次のような基準を与えています。
{
"criteria": {
"feature": "新機能・新しい振る舞いの追加",
"bug": "既存機能が意図通り動作していない不具合の修正",
"fix": "既存の要件・受け入れ基準・設計判断を変えない機械的な修正"
}
}
Jevはこの入力に対して、分類結果と確信度を返します。実際に記録されているログでは、例えば次のような出力が残っています。
{
"classification": {
"type": "feature",
"probability": 0.88,
"confidence": "high",
"rationale": "新機能・新しい振る舞いの追加"
},
"modelSelection": {
"model": "claude-sonnet-5",
"tier": "standard",
"probability": 0.85,
"rationale": "中程度の作業(機能追加の実装、複数ファイルにまたがる変更)"
}
}
別の例では、こちらのようなケースもありました。
{
"classification": {
"type": "fix",
"probability": 0.51,
"confidence": "low",
"rationale": "既存の要件・受け入れ基準・設計判断を変えない機械的な修正"
},
"modelSelection": {
"model": "claude-haiku-4-5",
"tier": "simple",
"probability": 0.84,
"rationale": "単純な定型作業"
}
}
このように、ハーネスはJevへ分類とモデル選択を別々に問い合わせ、それぞれの確率を記録します。その結果を受けて、たとえば分類が高確信度なら次のフローへ進み、低確信度なら自動処理を止めて確認を待つ、という運用にしています。
実際の流れとしては、SessionStart フックが未トリアージの Issue を拾い、そこから work-request-triage スキルに処理を渡します。そのスキルが Issue のタイトルと本文をまとめ、Jev に choice 判定を実行させます。Jev の返り値をもとに、feature なら speckit-ready を付与したり、bug や fix なら適切なモデルで着手したりする、という仕組みです。
この設計の一番大きな利点は、判断に「数値の確信度」が残ることです。単にAIが「そう思う」と言うのではなく、0.88 や 0.97 のような数字があることで、後からレビューしやすくなります。コストやモデル選択の傾向を分析することもできるので、運用の改善にそのまつながります。
まとめ
今回は、Jevを「最終的な処理実行」ではなく、「作業依頼の分類・品質ゲートの要否・モデル選択」を判断する層として組み込みました。Jevが返す判定結果と確率をもとに、スキル側が自動実行の可否や次の処理、使用モデルを決める、という構図になっています。
この形にしておくと、Jevは「曖昧な判断の代替」ではなく、「判断の定量化」を担う存在として自然にハーネスに入れられます。単なる流行りものではなく、実運用に耐えうる判断基盤として扱うことができる、さらにこれまでLLMの判断部分であった処理をJevに任せることでトークン最適化として活躍することを期待しています。