Jev Recipe / 信頼性エンジニアリング
LLM フォールバック戦略:信頼度ゲート付き 3 レーンチェーン
本番 LLM システムの信頼性設計はリトライで終わりではありません。較正済み信頼度に応じて、高信頼は自動実行、中間帯はより強力なモデルで再質問、低信頼は人手へと段階的に切り替えます。
信頼度ゲート付きフォールバックチェーン構築 6 ステップ
01まず 3 つの用語を区別する。リトライが扱うのは 5xx やタイムアウトといったインフラ障害、フォールバックが扱うのは「答えが信頼できない」状態、エスカレーションが扱うのは「取り返しがつかない結果」。この 3 つが 1 つのコードパスを共有していることが、フォールバック設計が本番で壊れる最大の原因になる。
02自動実行のゲートは較正済み信頼度だけに置く(しきい値は 0.85 以上)。信頼度 0.9 が実際の正解率約 90% に対応しない限り、しきい値は機能しない。Jev の確率は RLCD 学習で較正されており、この対応関係が成立する。
03中間帯(0.60〜0.85)を先に設計する。より強力なモデルで 1 回だけ再質問し、0.60 未満のロングテールだけを人手キューへ流す。
04判断表はアプリケーションコードに書く。リトライは転送エラーのみ(回数上限あり)、フォールバックは信頼度不足のみ、決済や削除など不可逆操作の行き先は常に人間へ。ポリシーをモデルに任せない。
05ヒステリシスとシャドーモードを用意する。判定が揺れやすい入力は中間帯を広げ、まずはログ記録のみで本番トラフィックを通し、分布を確認してから強制モードへ切り替える。
06answer・confidence・lane の 3 点をすべてのホップで記録し、四半期ごとに実データに基づいてしきい値を再較正する。勘や伝聞ではなくデータで決める。
schema / フォールバック判定コントラクト
{
"fallback_action": {
"type": "choice",
"instructions": "この回答案の次のアクションを判定する",
"criteria": {
"execute": "高信頼度かつ可逆 — 自動実行する",
"escalate_model": "中間帯の信頼度 — より強いモデルで再質問する",
"human_review": "低信頼度または不可逆 — 人手へ回す"
}
},
"irreversible": {
"type": "noul",
"instructions": "この回答を実行すると取り消しが困難か(決済・送信・削除)?"
}
} 回答案を State として送ると、execute・escalate_model・human_review の 3 レーンへ振り分けられる様子を確認できます。
フォールバック戦略比較:信頼度ゲート付きチェーン vs リトライ・カスケード・人手のみ
METRIC
Jev
盲目リトライ / カスケード / 人手のみ
トリガーシグナル
較正済み信頼度
回答ごとの数値がそのままレーンを決める
代理指標であり信頼度ではない
リトライは 5xx とタイムアウト、カスケードは自己申告スコア、人手のみはユーザー苦情を待つ
追加レイテンシ (P50)
ゲートは約 95ms
0.60〜0.85 の中間帯だけ約 2〜4 秒の再質問を追加
複利的に増加
リトライは 2^n バックオフ、カスケードは毎ホップ再生成、人手キューは数分〜数日
コスト構造
入力のみ課金
ゲートは $0.042/M、高額モデルは中間帯だけに課金
上限なし
リトライは同じ誤答に出力トークンを再課金、カスケードはホップごとに高額単価、人手コストは両者を上回る
障害モード
フェイルセーフ
しきい値未満は人手へ流れ、誤った自動実行は起きない
うるさいか無音か
リトライは同じ誤答を繰り返し、カスケードは品質を静かに落とし、人手のみではキューがパンクする
オブザーバビリティ
判定ごとの監査ログ
全ホップが answer + confidence + lane を出力し、しきい値はコードで追跡可能
散在して見えない
障害はレイテンシのグラフや品質ドリフトの中に隠れ、判定単位の信頼度の記録が残らない
コード対照:盲目リトライ vs 信頼度ゲート付きチェーン
python / 脆弱なパターン:単一モデルの盲目リトライ
import time
import requests
def generate_with_retry(prompt: str, max_retries: int = 5) -> str:
# 盲目的なリトライ:シグナルは転送層の健全性だけ。
# 自信たっぷりの誤答は 1 回目で素通りし、リトライ経路には入りません。
for attempt in range(max_retries):
try:
resp = requests.post(
"https://api.vendor-llm.example/v1/chat/completions",
json={
"model": "cheap-model",
"messages": [{"role": "user", "content": prompt}],
},
timeout=30,
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
except (requests.Timeout, requests.HTTPError):
time.sleep(2 ** attempt) # 1秒、2秒、4秒、8秒……最大約31秒の無駄待ち
return "" # リトライの末、空文字で静かに失敗
# 典型パス:1500〜3000ms の生成レイテンシ、試行ごとに出力トークンを課金。
# フォーマットが正しい誤答に対する防御はゼロ。python / Jev 信頼度ゲート付き 3 レーンフォールバックチェーン
import requests
JEV_ENDPOINT = "https://api.typesafe.ai/v1/jev/evaluate"
AUTO_EXECUTE_CONFIDENCE = 0.85 # 0.85 以上で自動実行
QUESTIONS = {
"fallback_action": {
"type": "choice",
"instructions": "この回答案の次のアクションを判定する",
"criteria": {
"execute": "高信頼度かつ可逆 - 自動実行する",
"escalate_model": "中間帯の信頼度 - より強いモデルで再質問する",
"human_review": "低信頼度または不可逆 - 人手へ回す",
},
},
"irreversible": {
"type": "noul",
"instructions": "この回答を実行すると取り消しが困難か(決済・送信・削除)?",
},
}
def gate(state: dict) -> dict:
# リトライする価値があるのは転送エラーだけ。
# ゲート自体は安価でステートレスな 1 呼び出し(約 95ms、入力トークンのみ)。
resp = requests.post(
JEV_ENDPOINT,
json={"state": state, "questions": QUESTIONS},
timeout=3,
)
resp.raise_for_status()
return resp.json()
def fallback_chain(task: str, answer_model, strong_model) -> dict:
# レーン 1:低コストモデルが下書きし、較正済みゲートが判定する。
draft = answer_model(task)
check = gate({"task": task, "proposed_answer": draft})
decision = check["fallback_action"]
if (
decision["answer"] == "execute"
and decision["confidence"] >= AUTO_EXECUTE_CONFIDENCE
and not check["irreversible"]["answer"]
):
return {"lane": "auto_execute", "answer": draft, "confidence": decision["confidence"]}
# レーン 2:0.60〜0.85 の中間帯 — より強いモデルで 1 回だけ再質問。
stronger = strong_model(task)
recheck = gate({"task": task, "proposed_answer": stronger})
final = recheck["fallback_action"]
if (
final["answer"] == "execute"
and final["confidence"] >= AUTO_EXECUTE_CONFIDENCE
):
return {"lane": "escalated_model", "answer": stronger, "confidence": final["confidence"]}
# レーン 3:エスカレーション後もしきい値未満 — 人手キューまたは安全なデフォルト値。
return {
"lane": "human_review",
"answer": stronger,
"confidence": final["confidence"],
"first_pass_confidence": decision["confidence"],
}
# 典型パス:ゲート呼び出し 1 回あたり約 95ms、入力トークンのみ。
# レーン 2 は例外であってデフォルトではなく、大半のトラフィックはレーン 1 を離れません。typescript / 脆弱なパターン:盲目リトライ
async function generateWithRetry(prompt: string, maxRetries = 5) {
// 盲目的なリトライ:ループを発火させるのは転送エラーだけ。
// 自信たっぷりの誤答は 1 回目の試行をそのまま素通りします。
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const resp = await fetch("https://api.vendor-llm.example/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "cheap-model",
messages: [{ role: "user", content: prompt }],
}),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
return (await resp.json()).choices[0].message.content as string;
} catch {
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
}
}
return ""; // 約31秒のバックオフの末に静かに失敗
}typescript / Jev 信頼度ゲート付きフォールバックチェーン
const JEV_ENDPOINT = "https://api.typesafe.ai/v1/jev/evaluate";
const AUTO_EXECUTE_CONFIDENCE = 0.85; // 0.85 以上で自動実行
const QUESTIONS = {
fallback_action: {
type: "choice",
instructions: "この回答案の次のアクションを判定する",
criteria: {
execute: "高信頼度かつ可逆 - 自動実行",
escalate_model: "中間帯の信頼度 - 強いモデルで再質問",
human_review: "低信頼度または不可逆 - 人手へ回す",
},
},
irreversible: {
type: "noul",
instructions: "この回答を実行すると取り消しが困難か(決済・送信・削除)?",
},
} as const;
async function gate(state: Record<string, unknown>) {
const resp = await fetch(JEV_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ state, questions: QUESTIONS }),
});
if (!resp.ok) throw new Error("Jev evaluate failed");
return (await resp.json()) as {
fallback_action: { answer: string; confidence: number };
irreversible: { answer: boolean };
};
}
export async function fallbackChain(
task: string,
answerModel: (t: string) => Promise<string>,
strongModel: (t: string) => Promise<string>,
) {
// レーン 1:低コストモデルが下書きし、較正済みゲートが判定する(約 95ms、入力のみ)
const draft = await answerModel(task);
const first = await gate({ task, proposed_answer: draft });
if (
first.fallback_action.answer === "execute" &&
first.fallback_action.confidence >= AUTO_EXECUTE_CONFIDENCE &&
!first.irreversible.answer
) {
return { lane: "auto_execute", answer: draft, confidence: first.fallback_action.confidence };
}
// レーン 2:0.60〜0.85 の中間帯 — より強いモデルで 1 回だけ再質問
const stronger = await strongModel(task);
const recheck = await gate({ task, proposed_answer: stronger });
if (
recheck.fallback_action.answer === "execute" &&
recheck.fallback_action.confidence >= AUTO_EXECUTE_CONFIDENCE
) {
return { lane: "escalated_model", answer: stronger, confidence: recheck.fallback_action.confidence };
}
// レーン 3:エスカレーション後もしきい値未満 — 人手キューまたは安全なデフォルト値
return {
lane: "human_review",
answer: stronger,
confidence: recheck.fallback_action.confidence,
first_pass_confidence: first.fallback_action.confidence,
};
}