「生成しない AI」を業務システムに組み込む ― 判定専用モデル Jev を OPSNOTE で使ってみた
はじめに
OPSNOTE は、インフラ作業の手順書の作成・レビュー・承認から、作業の実施、証跡の保存までをブラウザで完結させる SaaS です。AI 機能はこれまで Amazon Bedrock(東京・大阪リージョン)の Claude に集約してきました。ただ、機能を足していくうちに、文章を生成する必要はなく、「どれに当てはまるか」だけ答えてくれればよい処理がいくつも出てきました。
そこで、TypeSafe AI 社の判定専用モデル Jev を試しました。この記事では、次の 3 点をまとめます。
Jev をどの機能に、どう組み込んだか
導入前に自社データで精度をどう測ったか
効果(精度・速度・コスト)と、見送った用途
Jev とは
Jev は、自由文を返さず、型の決まった質問に対して判定結果だけを返すモデルです。OPSNOTE で使った質問の型は次の 3 つです。
型 | 返り値 | 用途の例 |
choice | 選択肢の 1 つ + confidence | 「スキャナ / 実在パス / 機械経路」のどれか |
noul | 0〜1 の確率 | 「このタグに該当するか」 |
score | 段階の添字 + confidence | 「info / warning / error のどれ相当か」 |
特徴は次のとおりです。
料金: input $0.042 / 100 万トークン、output は無料
速度: 実測で 1 コール p50 150〜370ms
注意点: 英語が最も高精度で、日本語を含む CJK は「処理はできるが同等ではない」と公式に明記されている。ホスティングは米国のみで、リージョン選択はない。早期アクセス版で SLA はない
安くて速い一方、日本語の精度と国外送信の 2 点は自分たちで確かめる必要がありました。
導入の進め方: 先に自社データで測る
日本語の精度について公開ベンチマークは見当たりません。実装してから精度不足が分かると設計ごと作り直しになるため、機能の実装前に評価だけを行う Issue を独立させ、その結果で後続機能の Go / No-Go を決めました。
評価の方針は次のとおりです。
対象は自社テナントの手順書のみ。顧客テナントのデータは使わない
送信前にマスクする
辞書マスク: 案件名・エンドユーザ名・対象ホスト名・IP を置換
パターンマスク: IP / URL / メール / FQDN / 認証情報 / PEM / 型番のような識別子
パスワード・ログイン ID・実施コマンドは読まない
dry-run でマスク漏れを監査してから送信する。マスク前の件数(辞書値の残存 545 件・IPv4 975 個)を陽性対照とし、マスク後に 0 件になることを確認した
モデルは jev-1.13.0 に固定する。jev-latest のようなエイリアスは版が黙って変わり、較正した閾値の意味が変わるため使わない
評価した用途は 3 つです。
用途 | 質問 | 正解ラベル |
① 手順書のタグ分類 | 内容タグ 9 種を noul で同時に問う | 既存の手動付与タグ |
② 証跡テキストの一次判定 | ― | 正解ラベルが揃わず見送り |
③ AI 改善提案を出す価値があるかの判定 | choice / score | 既存の AI 提案の最大 severity |
用途 1: 手順書のタグ提案(採用)
評価結果
手順書 49 件 × 内容タグ 9 種で測りました。既存の手動タグを正解とした機械評価は、閾値 0.6 で F1 0.658 でした。
ところが、Jev が「手動では付いていないタグ」を付けた 39 件を目視すると、39 件すべてが妥当な付け漏れの指摘でした。手動タグが網羅的でなかったため、機械評価は Jev を過小評価していたことになります。目視で補正すると結果は次のとおりです。
閾値 | precision | recall | F1 |
0.5 | 0.825 | 0.792 | 0.808 |
0.6 | 1.000 | 0.752 | 0.858 |
0.9 | 1.000 | 0.423 | 0.594 |
※ 0.6 以上は全件を目視したため、precision 1.000 は構成上の値です。
タグ別の recall には、はっきりした傾向が出ました。
「現地試験」「PowerShell」は 1.00、「構築/設定」は 0.96。本文に語が出ない抽象的なタグでもよく当たる
「納品物」は 0.00。手順書の属性を表すタグは本文から判断できない
この結果から、次のように設計しました。
候補タグをキーワードで絞らない: キーワード一致で絞ると「構築/設定」のような抽象タグが落ちる。全タグ(使用頻度順に最大 50 件)を 1 コールでまとめて問う
属性・出自のタグは除外できるようにする: 「納品物」「Kintoneインポート」などをテナント管理者が提案対象外に設定できる
本番条件での再評価
評価ではタグに説明文を付けていましたが、本番のタグは名前しか持ちません。そこで公開前に、本番のコードそのもの・タグ名のみ・全タグ候補(43 タグ)の条件で測り直しました。
条件 | precision | recall | F1 |
初回評価(説明文あり・9 タグ) | 0.652 | 0.664 | 0.658 |
本番条件(タグ名のみ・43 タグ) | 0.637 | 0.739 | 0.684 |
(いずれも目視補正前)
説明文がなくても精度は落ちませんでした。さらに、内容タグ以外で追加提案された 91 件(16 タグ)もすべて妥当でした。目視した追加提案は合計 130 件で、誤りは 0 件です。
機能としての作り
手順書の項目や手順書名を保存すると、約 5 秒の debounce のあと手順書単位で 1 回だけ Jev を呼ぶ。画面右側の「タグ提案」カードに、score 0.6 以上を「提案」、0.4〜0.6 を「参考」として表示する
提案は自動で付けない。利用者がチップを押したときだけタグが付く。既存タグから選ぶだけで、新しいタグ名は作らない
内容のハッシュが変わらなければ Jev を呼ばない。タグの付け外しや実施コマンドだけの修正では再取得しない
Jev の失敗は画面上のエラーにしない。キー不正・429・タイムアウト・応答不正はどれも「提案なし」として返し、保存や編集には影響させない。保存のたびに自動で動く機能なので、SLA のない外部 API の不調で利用者を止めないための設計
国外送信の扱い
OPSNOTE は「AI 処理は国内で完結する」と顧客に約束しています。Jev は米国ホスティングのため、タグ提案はこの約束の明示的な例外として扱い、二段のゲートを設けました。
ゲート | 既定 | 内容 |
運営側の提供フラグ | OFF | 運営が提供するテナントにだけ設定画面を出す |
テナント管理者の opt-in | OFF | 国外送信を許容したテナント管理者だけが有効化する。変更は監査ログに残す |
送信するデータも絞っています。
送る: 手順書名、大項目名・中項目名、各項目の目的(先頭 150 文字)、候補タグ名。いずれもマスク後
送らない: パスワード・ログイン ID・実施コマンド。案件名・エンドユーザ名・ホスト名・IP は置換してから送る
あわせて、顧客向けの AI 機能説明とプライバシーポリシーを改定しました。
コスト
1 回あたり input 約 2,400 トークン(候補 43 タグ)で、約 $0.0001 です。社内クレジットに換算すると約 0.06 cr です。月間回数は Claude とは別枠で管理し、既存の AI 利用枠を食わないようにしました。
用途 2: WAF ログの分類(shadow 運用中)
背景
OPSNOTE は Cloudflare WAF のログを毎朝集計し、正規ホストへのヒットを「要精査(false positive の疑い)」と「スキャナの探索」に振り分けてメールで通知しています。振り分けは正規表現で行っていますが、未知の探索パスは分類できず、漏れが見つかるたびにパターンを手で足す運用が続いていました。
このデータは URL パス・User-Agent・WAF ルール ID だけで、手順書や証跡は含みません。国外送信の同意を待たずに試せるため、タグ提案より先に着手しました。
設計
shadow モード: 通知と分類は正規表現の結果のまま変えない。Jev の判定は、ログ・GitHub Actions の step summary・通知メール末尾の「参考」欄に差分として出すだけ
送信データの最小化
送信元 IP・ASN・国は送らない
ホスト名は admin / lp / tenant の区分に置換する(テナントのサブドメインは顧客名を含むため)
パス中の ID・トークンは置換する。共有リンクのトークンは URL デコードを重ねたうえで丸ごと伏せる
フォールバック: どの失敗でも正規表現の結果で観測を続ける。全体 15 秒の deadline を設け、confidence 0.7 未満は比較から外す
1 週間の実測(2026-09-25〜10-01 の日次 7 回)
項目 | 値 |
判定を依頼した候補 | 626 件 |
判定できた(confidence 0.7 以上) | 610 件 |
正規表現と一致 | 593 件(97.2%) |
正規表現は「要精査」、Jev は「スキャナ」 | 17 件 |
正規表現は「スキャナ」、Jev は「実在パス」 | 0 件 |
API の失敗 | 0 件 |
latency p50 / p95 | 156〜192ms / 214〜376ms |
input トークン合計 | 約 41 万(7 日で約 $0.017) |
読み取れることは次のとおりです。
正規表現との一致率は 97% と高く、正規表現が本命を捨てている疑い(重い方の差分)は 0 件だった
差分 17 件は「正規表現では拾えていない探索パス」の候補にあたる。導入前の疎通確認でも、/settings.py.bak や /CFIDE/main/ide.cfm のような正規表現に無い探索を confidence 0.97〜0.99 でスキャナと判定していた
1 週間 API エラーはなく、費用は誤差の範囲だった
差分 17 件が本当にスキャナかどうかは、まだ突き合わせの途中です。次の段階では、通知を「要精査 / スキャナ判定(要確認)/ スキャナ探索」の 3 段に分けます。その際も Jev の判定だけでアラートを消すことはしない方針です。
見送った用途: AI 提案の発火ゲート(No-Go)
OPSNOTE には手順の各項目に Claude が改善提案を出す機能があります。「指摘する価値が低い項目では Claude を呼ばない」ゲートを Jev で作れればコストを削れる、というのが狙いでした。
既存の AI 提案 694 件で評価した結果は、次のとおりです。
choice(要レビュー / 問題なし): 97% を「要レビュー」と判定した。「問題なし」の 22 件はすべて confidence 0.5 未満で、どの閾値でも省略できる件数は 0 件
score(3 段階): 最も軽い段階を一度も返さなかった。完全一致率は 49.4% で、「常に warning と答える」だけの基準(55.6%)を下回った
最良のゲート設定でも、省略できるのは 4% で、重大指摘の見逃しが 1% 出る
削減効果に対して見逃しのリスクが見合わないため、No-Go と判断しました。問い方の調整は 1 回までと事前に決めておき、そのとおり打ち切っています。
タグ分類は「この文書にこの語彙が当てはまるか」という判定で、日本語でもよく当たりました。一方の発火ゲートは「この手順に改善の余地があるか」という判定で、内容を深く理解する必要があります。判定専用モデルが得意なのは前者だ、というのが今回の実感です。
まとめ
用途 | 判断 | 結果 |
手順書のタグ提案 | 採用(テナント opt-in) | 目視補正後 F1 0.858、目視した追加提案 130 件はすべて妥当 |
WAF ログの分類 | shadow 運用中 | 7 日間で一致率 97.2%、正規表現の漏れ候補 17 件、失敗 0、費用 $0.017 |
AI 提案の発火ゲート | 見送り | 省略できるのは最大 4% で見逃し 1% |
証跡テキストの一次判定 | 保留 | 評価に使える正解ラベルがない |
Jev を使ってみて分かったことは次の 4 点です。
「生成」と「判定」を分けると、判定側は安く速くなる。output が無料で、1 回の判定が 0.2 秒前後で返るため、保存のたびに自動で動かす機能や日次 cron に気軽に組み込める
日本語でも、語彙の当てはめなら実用水準に届く。ただし、公開ベンチマークがないので自社データで測るしかない。そして手動ラベルは正解として不完全なので、機械評価だけで判断せず目視で補正すべき
SLA のない外部 API は「失敗しても何も変わらない」形で組み込む。タグ提案は「提案なし」に、WAF 分類は正規表現の結果に落とし、利用者と運用を止めない
国外送信は機能ごとに線を引く。送るデータ自体に機微情報がない用途(WAF ログ)から先に始め、手順書を扱う用途は二段ゲート・送信項目の絞り込み・マスク・規約改定をそろえてから提供する
「何でも LLM に投げる」のではなく、判定で済む処理は判定専用モデルへ、生成が要る処理は生成モデルへと役割を分けることで、コストとレイテンシの両面で選択肢が広がりました。




コメント