Jev Recipe / コンプライアンス基盤
Jev で監査証跡を構築:すべての判断を記録・再生・説明できる形に
自動判断をコンプライアンス等級の証拠に変える:Jev の evaluate 呼び出しは毎回、較正済み信頼度付きの型付き回答を返すため、監査ログには契約・回答・ゲートを同時に記録できます——保存コストは一定、再生は決定論的、GDPR 第 22 条への回答も明示的。
判断監査証跡を構築する 6 ステップ
{
"decision": {
"type": "choice",
"instructions": "公開ポリシーに基づきケースを裁定する",
"criteria": {
"approve": "ポリシー条件をすべて満たす——裁量の余地なし",
"refer": "大半の条件を満たすが明示されたエッジケースに該当——理由を付してキューへ",
"deny": "ハード条件を満たさない——該当条件を明示して拒否"
}
},
"reversibility": {
"type": "score",
"instructions": "この判断が誤りだった場合、取下げのコストは?1(簡単に取消し可能、例:返金)から 4(人への不可逆的な害、例:信用機関に報告されるアカウント閉鎖)"
},
"needs_human_review": {
"type": "noul",
"instructions": "このケースと信頼度を踏まえ、発効前に人間の確認が必要か?"
}
}6 つの判断、1 つの監査ログ
以下の各判断はすでに evaluate エンドポイントを通過済みです。2 つのゲート(自動実行の信頼度しきい値と人手引き継ぎしきい値)を動かし、レーンの再計算を確認してください。高い信頼度だけでは不十分です。与信枠の判断は信頼度 0.93 でもレビューに回ります。可逆性が第 2 の軸だからです。
| 判断 | 回答と信頼度 | 可逆性 | レーン |
|---|---|---|---|
返金自動承認 · チケット #4812 dec_8f21 · audit-contract@1.3.0 · jev-1.13 · 96ms | 承認0.97 | 可逆 1/4 | 自動実行 |
コンテンツ公開ゲート · 下書き #2214 dec_8f22 · audit-contract@1.3.0 · jev-1.13 · 88ms | ブロック0.88 | 可逆 2/4 | 自動実行 |
与信枠引き上げ · 顧客 #7741 dec_8f23 · audit-contract@1.3.0 · jev-1.13 · 102ms | 引継ぎ0.93 | 可逆 3/4Art. 22 記録 | レビュー |
オファー配信 · 顧客 #2298 dec_8f24 · audit-contract@1.3.0 · jev-1.13 · 91ms | 通常0.71 | 可逆 1/4 | レビュー |
チケット エスカレーション · #5190 dec_8f25 · audit-contract@1.3.0 · jev-1.13 · 84ms | false0.55 | 可逆 2/4 | 人手判断 |
アカウント凍結 · 顧客 #3306 dec_8f26 · audit-contract@1.3.0 · jev-1.13 · 99ms | 凍結0.78 | 可逆 4/4Art. 22 記録 | 人手判断 |
デモデータのみ——6 件の模擬判断。レーンはレシピ本文と同じ 2 軸ルールで再計算されます。
判断監査の証拠:Jev 型付きログ vs 生成チャットログ vs ログなし
実装コード:TypeScript / Python の監査パイプライン
import logging
import os
import requests
logger = logging.getLogger("decisions")
def decide_with_chat_model(customer_id: str, case: dict) -> str:
resp = requests.post(
"https://api.openai.com/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"},
json={
"model": "gpt-4o-mini",
"messages": [{
"role": "user",
"content": f"ポリシー v3 で裁定し、判断と根拠を返してください。\nCase: {case}",
}],
},
timeout=30,
)
decision_text = resp.json()["choices"][0]["message"]["content"]
# 監査ログが得るのは段落。しきい値設定もリプレイも説明もできず、
# 再読できるだけ。この呼び出しはどのプロンプトバージョンで作られたか?
# 「おそらく承認」は信頼度 0.8 のことだったのか?ログには答えられない。
logger.info("decision for %s: %s", customer_id, decision_text)
return decision_textimport hashlib
import json
import requests
JEV_ENDPOINT = "https://api.typesafe.ai/v1/jev/evaluate"
AUTO_EXECUTE_CONFIDENCE = 0.85 # ゲート超過 かつ 可逆性 <= 2 -> 自動実行
HUMAN_CONFIDENCE = 0.60 # 未満、または可逆性 4 -> 人手
QUESTIONS = {
"decision": {
"type": "choice",
"instructions": "公開ポリシーに基づきケースを裁定する",
"criteria": {
"approve": "ポリシー条件をすべて満たす",
"refer": "明示されたエッジケースに該当",
"deny": "ハード条件を満たさない——該当条件を明示",
},
},
"reversibility": {
"type": "score",
"instructions": "誤りだった場合の取下げコスト?1 = 簡単、4 = 人への不可逆的な害",
},
"needs_human_review": {
"type": "noul",
"instructions": "発効前に人間の確認が必要か?",
},
}
def decide_and_record(subject_ref: str, state: dict) -> dict:
payload = {"state": state, "questions": QUESTIONS}
resp = requests.post(JEV_ENDPOINT, json=payload, timeout=5)
resp.raise_for_status()
data = resp.json()
decision = data["decision"]
reversibility = data["reversibility"]["answer"]
if data["needs_human_review"]["answer"] or reversibility == 4:
lane = "human_decision"
elif decision["confidence"] >= AUTO_EXECUTE_CONFIDENCE and reversibility <= 2:
lane = "auto_executed"
else:
lane = "review_queue"
# レコードが監査証跡:契約・回答・ゲート——1 行の固定長レコードで、
# 任意の将来スキーマバージョンでリプレイ可能。
return {
"subject_ref": subject_ref,
"decision": decision["answer"],
"confidence": decision["confidence"],
"reversibility": reversibility,
"lane": lane,
"logic": {
"schema_version": "audit-contract@1.3.0",
"questions_hash": hashlib.sha256(
json.dumps(payload["questions"], sort_keys=True).encode()
).hexdigest()[:16],
"model_version": "jev-1.13",
},
"human_intervention_path": (
"available_on_request"
if lane == "auto_executed"
else "mandatory_before_effect"
),
}import { z } from "zod";
import OpenAI from "openai";
const Decision = z.object({ decision: z.enum(["approve", "refer", "deny"]) });
const openai = new OpenAI();
export async function decideCase(caseData: unknown) {
const resp = await openai.chat.completions.create({
model: "gpt-4o-mini",
response_format: { type: "json_object" },
messages: [
{ role: "user", content: `ポリシー v3 で裁定: ${JSON.stringify(caseData)}` },
],
});
const { decision } = Decision.parse(
JSON.parse(resp.choices[0].message.content!),
);
// 形状は保証されるが、証拠はそうではない。信頼度なし・スキーマバージョンなし・
// 分布なし——6 か月後の監査が得られるのは「何」だけで、「なぜ」と
// 「どのくらい正しかったか」は永遠に得られない。
return { decision, auditRecord: { decision } };
}const JEV_ENDPOINT = "https://api.typesafe.ai/v1/jev/evaluate";
const AUTO_EXECUTE_CONFIDENCE = 0.85; // ゲート超過 かつ 可逆性 <= 2
const HUMAN_CONFIDENCE = 0.6; // 未満、または可逆性 4 -> 人手
type Lane = "auto_executed" | "review_queue" | "human_decision";
const QUESTIONS = {
decision: {
type: "choice",
instructions: "公開ポリシーに基づきケースを裁定する",
criteria: {
approve: "ポリシー条件をすべて満たす",
refer: "エッジケースに該当——理由を付してキューへ",
deny: "ハード条件を満たさない——該当条件を明示",
},
},
reversibility: {
type: "score",
instructions: "誤りだった場合の取下げコスト?1 = 簡単、4 = 不可逆的な害",
},
needs_human_review: {
type: "noul",
instructions: "発効前に人間の確認が必要か?",
},
} as const;
export async function decideAndRecord(subjectRef: string, state: object) {
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");
const data = await resp.json();
const { answer, confidence } = data.decision as {
answer: string;
confidence: number;
};
const reversibility = data.reversibility.answer as number;
const lane: Lane =
data.needs_human_review.answer || reversibility === 4
? "human_decision"
: confidence >= AUTO_EXECUTE_CONFIDENCE && reversibility <= 2
? "auto_executed"
: "review_queue";
return {
subject_ref: subjectRef,
decision: answer,
confidence,
reversibility,
lane,
logic: { schema_version: "audit-contract@1.3.0", model_version: "jev-1.13" },
human_intervention_path:
lane === "auto_executed" ? "available_on_request" : "mandatory_before_effect",
};
}
// 第 22 条レコード = このオブジェクト。リプレイ = 保存 state を新しい
// スキーマバージョンで再送し、レーンを差分。判断監査証跡 FAQ
判断監査証跡とは何ですか?
システムが行うすべての自動判断の、タイムスタンプ付きで照会可能な記録です:何が決定されたか、どの信頼度で、どのスキーマとモデルバージョンの下で、どのゲートを経由し、人間の介入経路は何か。Jev ではこの記録は evaluate レスポンスからほぼ無料で手に入ります——型付き回答・較正済み信頼度・選択肢分布はすでに構造化されているため、ログは段落ではなく固定長の行になります。
GDPR 第 22 条はこれを要求しますか?
第 22 条は、法的効果または同様に重大な影響を及ぼす完全自動判断について、それを受けない権利や(例外適用時に)論理に関する有意義な情報を得る権利を個人に与えます。個々の判断がこれに該当するかは、ログライブラリではなくチームの法的判断です。このレシピが提供するのは証拠レイヤーです:ある判断を重大と扱うなら第 22 条レコード(主体・判断・信頼度・ロジックバージョン・人間の介入経路・保持)は 1 オブジェクトですし、重大でないと判断するなら、その判断を一貫して防御するのがログなのです。
1 件の判断レコードには何を含めるべきですか?
6 フィールドです:主体参照(可能なら仮名化)、信頼度付きの判断、ロジックブロック(スキーマバージョン・questions ハッシュ・モデルバージョン・適用中のゲートしきい値)、Choice 質問の選択肢分布、ゲートが割り当てたレーン、人間の介入経路(介入があればレビュアーの操作も)。主体とレビュアーの操作を除くすべてが、1 回の evaluate レスポンスから来ます。
通常のアプリケーションログとの違いは?
アプリログは「コードが何をしたか」に答え、監査証跡は「システムが何を判断し、どれだけ確信し、どのルールの下でか」に答えます。違いは構造です:型付き判断レコードはしきい値集計(信頼度 0.9 未満の自動実行は何件?)、リプレイ(新しいスキーマなら何と判断する?)、集計(信頼度分布はドリフトしているか?)ができます——自由テキストのログ行ではどれもできません。
スキーマ更新後に過去ケースを再判定できますか?
はい、それがリプレイで、決定論的です。state とバージョン化 questions の両方が保存されているため、契約の更新はログ済みの全 state を新しいスキーマに再送信しレーンを差分することです。チームはリプレイを移行チェック(更新は自動実行判断を変えたか?)、回帰スイート(ログ state は履歴ラベル付きデータ)、説明エンジン(当時のルールは今日何と言うか)として使います。
なぜ較正済み信頼度が監査に重要なのですか?
信頼度の数字は、現実と追従してこそ証拠だからです。Jev の信頼度は RLCD 較正済みで、ログ上の 0.85 ゲートは測定可能な精度契約に対応します——自分のログから、0.85 の判断がどのくらい正しかったかを計算できます。未較正の自己申告信頼度はこれを支えられません:独立監査は、実際にはずっと多く外れる回答に 30% 超の信頼度を報告する判断モデルを発見しており、それはすべてのしきい値を飾りにします。較正があるからこそドリフトも可視化されます——分布が動けば、レビュー率が利用者より先に知らせます。