
定型作業の自動化スクリプトを選ぶタスクを Jev に任せたら Codex の 52% の時間でできた話
はじめに
Codex に任せる仕事には、毎回同じ手順で進められるものがあります。こうした手順は、Python スクリプトやシェルスクリプトとしてあらかじめ用意できます。(本記事ではこれをスクリプトと呼びます。) Codex ユーザーは同じ作業をさまざまな言葉で依頼するため、依頼の意味に合うスクリプトを選ぶ判断が残ります。この小さな判断まで Codex に任せると、単純な作業でも毎回 GPT の応答を待つことになります。
そこで、定義済みの選択肢から回答する Jev を Codex の前段へ置きました。検証用に 120 本の独立したスクリプトを作り、Codex 単独と Jev を組み合わせた方式を比較しています。
検証では、どちらの方式も 270 件すべてで正しい処理を選びました。Jev と Codex を組み合わせた方式では、Codex の呼出しが 270 回から 90 回へ減りました。実行時間は 21 分 38 秒から 11 分 13 秒へ短縮され、コストは約 4.9 USD から約 1.8 USD へ減りました。
Jev とは
Jev とは、定義済みの候補から回答を選ぶ TypeSafe のモデルです。自由文と実行に必要な情報を入力すると、選んだ候補・候補ごとの確率・確信度を返します。今回は、120 本のスクリプトと Codex への引継ぎを候補として与え、実行先の選択に使いました。
対象読者
- Jev の具体的な用途を知りたい方
- Codex に多数のスクリプトを使わせている方
- GPT の呼出し回数と待ち時間を減らしたい方
検証環境
- 検証日: 2026 年 9 月 23 日
- Jev:
jev-1.13.0 - Codex:
gpt-5.6-sol、推論強度low
参考資料
構成
検証用スクリプト
検証用スクリプトは、作業一覧の更新や文書の検査など、8 つの分野に分けて 15 本ずつ作成しました。120 本はそれぞれ別の Python ファイルで、単独で実行できるものです。なお、Jev は Python ファイルを直接読まず、送られてきた JSON を読みます。 JSON には、各スクリプトが行う処理と利用条件がまとめられています。
120 本の Python ファイルのうち、役割の異なる 3 本を紹介します。1 本目は作業一覧を更新し、2 本目はソフトウェア部品表を作ります。3 本目はサービスの状態を読み取ります。
作業一覧の状態を更新するスクリプト
import argparse
from common import ok, read_csv, run, workspace_root, write_csv, Rejected
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--root", required=True)
parser.add_argument("--queue", required=True)
args = parser.parse_args()
root = workspace_root(args.root)
rows, fields = read_csv(root, "queues/work_items.csv")
item = next((row for row in rows if row["queue"] == args.queue and row["status"] == "ready" and not row["blocked_by"]), None)
if item is None:
raise Rejected("no_ready_item")
item["status"] = "in_progress"
item["owner"] = "worker-1"
write_csv(root, "queues/work_items.csv", rows, fields)
return ok(item_id=item["id"], new_status=item["status"])
run(main)
依存関係からソフトウェア部品表を作るスクリプト
import argparse
from common import Rejected, ok, read_json, run, workspace_root, write_json
def main() -> int:
parser = argparse.ArgumentParser(description='依存関係からソフトウェア部品表を生成する。')
parser.add_argument("--root", required=True)
parser.add_argument("--target", required=True)
parser.add_argument("--output", required=True)
args = parser.parse_args()
root = workspace_root(args.root)
target = args.target
records = read_json(root, 'states/build-build_sbom.json')["records"]
record = records.get(target)
if record is None:
raise Rejected("target_not_registered")
if not record.get("ready"):
raise Rejected("build_sbom_source_not_ready")
write_json(root, args.output, {"source": target, "operation": "build_sbom", "items": record["items"]})
return ok(target=target, output=args.output, items=len(record["items"]))
run(main)
サービスの処理量と遅延を取得するスクリプト
import argparse
from common import Rejected, ok, read_json, run, workspace_root, write_json
def main() -> int:
parser = argparse.ArgumentParser(description='指定サービスの処理量と遅延の時系列を取得する。')
parser.add_argument("--root", required=True)
parser.add_argument("--service", required=True)
args = parser.parse_args()
root = workspace_root(args.root)
target = args.service
records = read_json(root, 'states/diagnostic-collect_metrics.json')["records"]
record = records.get(target)
if record is None:
raise Rejected("target_not_registered")
if not record.get("items"):
raise Rejected("collect_metrics_data_unavailable")
return ok(target=target, items=record["items"])
run(main)
これに対し、Jev が読む JSON は次のような形です。requires はスクリプトを利用できる条件、reject_when は利用できない条件です。
Jev へ送った 3 本分の情報
{
"c001": {
"arguments": "queue",
"purpose": "指定した作業一覧で着手可能な次の項目を担当中へ変更する。",
"reject_when": "着手可能な項目がない。",
"requires": "未着手で依存先のない項目がある。"
},
"c080": {
"arguments": "target, output",
"purpose": "依存関係からソフトウェア部品表を生成する。",
"reject_when": "対象が未登録、または固有の前提条件を満たさない。",
"requires": "targetで示す対象が登録済みで、依存関係からソフトウェア部品表を生成するための状態がそろっている。"
},
"c098": {
"arguments": "service",
"purpose": "指定サービスの処理量と遅延の時系列を取得する。",
"reject_when": "対象が未登録、または固有の前提条件を満たさない。",
"requires": "serviceで示す対象が登録済みで、指定サービスの処理量と遅延の時系列を取得するための状態がそろっている。"
}
}
検証した依頼
検証用の依頼は次の 3 種類です。
- スクリプトの用途を直接表した依頼: 90 件
- 同じ用途を別の言葉で表した依頼: 90 件
- Codex へ処理を引き継ぐのが適切な依頼: 90 件
スクリプトを選ぶ 180 件のうち 150 件では、同じ項目を入力として受け取るものを複数用意しました。たとえば、同じ公開バージョンを受け取っても、段階公開の計画を作るスクリプトと、障害時の復旧計画を作るスクリプトがあります。したがって正しいスクリプトを選ぶには、入力項目に加えて、依頼された作業の意味を読み分ける必要があります。
スクリプトの用途を直接表した依頼
{
"request": "公開失敗時の復旧手順と判断条件を含む計画を生成してください。",
"work_state": {
"arguments": {
"output": "outputs/requested-result.json",
"version": "2.0.0"
},
"domain": "release",
"execution_guard": {
"arguments_resolved": true,
"operation_state_eligible": true,
"output_path_allowed": true,
"referenced_inputs_readable": true,
"target_registered": true
}
}
}
同じ用途を別の言葉で表した依頼
{
"request": "公開失敗時の復旧手順と判断条件を含む計画を作ってください。",
"work_state": {
"arguments": {
"output": "outputs/requested-result.json",
"version": "2.0.0"
},
"domain": "release",
"execution_guard": {
"arguments_resolved": true,
"operation_state_eligible": true,
"output_path_allowed": true,
"referenced_inputs_readable": true,
"target_registered": true
},
"recent_event": "対象の準備が完了"
}
}
Codex へ処理を引き継ぐのが適切な依頼
{
"request": "公開準備をいい感じに整えてください。",
"work_state": {
"arguments": {},
"domain": "release",
"request_scope": "not_fixed"
}
}
比較方法
Jev に与えた選択肢は、120 本のスクリプトのいずれかの実行 (c001~c120)、または Codex への引継ぎ (codex_required)です。
TypeSafe の Choice は、定義済みの選択肢から 1 つを選び、選択肢ごとの確率と確信度を返します。
Jev の回答
{
"model": "jev-1.13.0",
"answers": {
"routing": {
"type": "choice",
"choice": "c001",
"probabilities": {
"c001": 0.99,
"c002": 0.0,
"c003": 0.0,
"c004": 0.0,
"c005": 0.0,
"c006": 0.0,
"c007": 0.0,
"c008": 0.0,
"c009": 0.0,
"c010": 0.0,
"c011": 0.0,
"c012": 0.0,
"c013": 0.0,
"c014": 0.0,
"c015": 0.0,
"c016": 0.0,
"c017": 0.0,
"c018": 0.0,
"c019": 0.0,
"c020": 0.0,
"c021": 0.0,
"c022": 0.0,
"c023": 0.0,
"c024": 0.0,
"c025": 0.0,
"c026": 0.0,
"c027": 0.0,
"c028": 0.0,
"c029": 0.0,
"c030": 0.0,
"c031": 0.0,
"c032": 0.0,
"c033": 0.0,
"c034": 0.0,
"c035": 0.0,
"c036": 0.0,
"c037": 0.0,
"c038": 0.0,
"c039": 0.0,
"c040": 0.0,
"c041": 0.0,
"c042": 0.0,
"c043": 0.0,
"c044": 0.0,
"c045": 0.0,
"c046": 0.0,
"c047": 0.0,
"c048": 0.0,
"c049": 0.0,
"c050": 0.0,
"c051": 0.0,
"c052": 0.0,
"c053": 0.0,
"c054": 0.0,
"c055": 0.0,
"c056": 0.0,
"c057": 0.0,
"c058": 0.0,
"c059": 0.0,
"c060": 0.0,
"c061": 0.0,
"c062": 0.0,
"c063": 0.0,
"c064": 0.0,
"c065": 0.0,
"c066": 0.0,
"c067": 0.0,
"c068": 0.0,
"c069": 0.0,
"c070": 0.0,
"c071": 0.0,
"c072": 0.0,
"c073": 0.0,
"c074": 0.0,
"c075": 0.0,
"c076": 0.0,
"c077": 0.0,
"c078": 0.0,
"c079": 0.0,
"c080": 0.0,
"c081": 0.0,
"c082": 0.0,
"c083": 0.0,
"c084": 0.0,
"c085": 0.0,
"c086": 0.0,
"c087": 0.0,
"c088": 0.0,
"c089": 0.0,
"c090": 0.0,
"c091": 0.0,
"c092": 0.0,
"c093": 0.0,
"c094": 0.0,
"c095": 0.0,
"c096": 0.0,
"c097": 0.0,
"c098": 0.0,
"c099": 0.0,
"c100": 0.0,
"c101": 0.0,
"c102": 0.0,
"c103": 0.0,
"c104": 0.0,
"c105": 0.0,
"c106": 0.0,
"c107": 0.0,
"c108": 0.0,
"c109": 0.0,
"c110": 0.0,
"c111": 0.0,
"c112": 0.0,
"c113": 0.0,
"c114": 0.0,
"c115": 0.0,
"c116": 0.0,
"c117": 0.0,
"c118": 0.0,
"c119": 0.0,
"c120": 0.0,
"codex_required": 0.01
},
"confidence": 0.99
}
},
"usage": {
"input_tokens": 17090,
"output_tokens": 1229
}
}
Jev がスクリプトを選び、その確率が 0.5 以上なら実行しました。Jev が Codex への引継ぎを選んだ場合と、スクリプトの確率が 0.5 未満の場合は Codex へ処理を任せました。
| 方式 | 選択方法 |
|---|---|
| Codex 単独 | 全件を Codex に選ばせる |
| Jev と Codex | 全件を Jev に選ばせ、引継ぎの選択時と選択確率 0.5 未満だけ Codex へ引き継ぐ |
検証結果
今回は依頼文・実行引数・実行条件を state へ入れました。
{
"state": "{\"request\": \"対象の作業一覧で着手可能な次の項目を担当中へ変更してください。\", \"work_state\": {\"arguments\": {\"queue\": \"editorial\"}, \"domain\": \"queue\", \"execution_guard\": {\"arguments_resolved\": true, \"operation_state_eligible\": true, \"output_path_allowed\": true, \"referenced_inputs_readable\": true, \"target_registered\": true}, \"recent_event\": \"対象の準備が完了\"}}"
}
この入力に対し、Jev は c001 を選びました。
Jev と Codex を組み合わせた方式の結果を、依頼の種類別に示します。
| 依頼の種類 | 依頼数 (件) | 正解数 (件) | Jev のスクリプト選択 (件) | Jev の引継ぎ選択 (件) | Codex 呼出し (回) |
|---|---|---|---|---|---|
| スクリプトの用途を直接表した依頼 | 90 | 90 | 90 | 0 | 0 |
| 同じ用途を別の言葉で表した依頼 | 90 | 90 | 90 | 0 | 0 |
| Codex への引継ぎが必要な依頼 | 90 | 90 | 0 | 90 | 90 |
方式ごとに 270 件を集計した結果を示します。
| 方式 | 正解数 (270 件中) | Codex 呼出し (回) | 誤ったスクリプト選択 (件) | 実行対象を決めるまでの合計時間 (分:秒) | コスト (USD) |
|---|---|---|---|---|---|
| Codex 単独 | 270 | 270 | 0 | 21:38 | 4.946 |
| Jev と Codex | 270 | 90 | 0 | 11:13 | 1.779 |
実行対象のスクリプトを決めるまでの時間は、Codex 単独でやったときの 51.8% になりました。
コストは、記録したトークン数へ GPT-5.6 Sol の API 単価 と Jev の公式単価 を適用して算出しました。Jev と Codex を組み合わせた方式の内訳は、Codex が約 1.585 USD、Jev が約 0.194 USD です。合計コストは 36.0% になりました。
考察
今回の結果を踏まえると、Jev は自然言語で依頼された定型作業に合うスクリプトを選ぶ用途に活用できると考えられます。検査・文書生成・状態更新などのスクリプトが増えた開発環境では、Codex による候補の読み比べを Jev へ任せ、既存スクリプトで扱えない依頼だけを Codex へ渡す構成が考えられます。
なお今回の実験では Jev はすべて正しいスクリプトを選んだものの、実務では Jev が別のスクリプトを選ぶ可能性も考えられます。 誤選択の発生を前提に、影響を抑える仕組みが必要になりそうです。たとえば、各スクリプトにも入力と状態の検査を残し、変更内容の記録と取り消し手段を用意するなどです。
まとめ
検証用に作った環境では、正解数を維持したまま、実行対象を決めるまでの合計時間を Codex 単独の 51.8%、コストを 36.0% にできました。Jev を適用できるのは、選択肢・実行引数・実行条件を事前に定義できる場面です。実プロジェクトへの導入前には、実際の依頼と既存スクリプトを使った確認が必要です。本記事が、Jev と Codex の役割分担を検討する際の参考になれば幸いです。










