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": "综合本案与当前置信度,决策生效前是否需要人工确认?"
}
}六次决策,一本审计账
下面每次决策都已经跑过 evaluate 端点。拖动两道闸门——自动执行置信度阈值与人工接管阈值——看车道实时重算。高置信度本身不够:额度提升调用置信度 0.93 仍进复核队列,因为可逆性是第二条轴。
| 决策 | 答案与置信度 | 可逆性 | 车道 |
|---|---|---|---|
退款自动批准 · 工单 #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 记录 | 人工决策 |
仅演示数据——六条模拟决策。车道按与文案相同的双轴规则实时重算。
决策审计证据: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"
# 记录即审计追踪:契约、答案、闸门——一行定长记录,
# 可在任意未来 schema 版本下重放。
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!),
);
// 形状有保证,证据没有。没有置信度、没有 schema 版本、没有
// 分布——六个月后的审计只能拿到「是什么」,永远拿不到
// 「为什么」和「多少次是对的」。
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",
};
}
// Art. 22 记录 = 这个对象。重放 = 把存储的 state 以新 schema 版本
// 重新提交,然后 diff 车道。决策审计追踪 FAQ
什么是决策审计追踪?
一条带时间戳、可查询的记录,记下系统做出的每一次自动化决策:决策是什么、置信度多少、在哪个 schema 与模型版本下、经过哪道闸门、人工介入路径是什么。用 Jev 时这份记录几乎是从 evaluate 响应里白拿的——类型化答案、校准置信度、选项分布本来就是结构化的,所以日志是一条定长记录,而不是一段话。
GDPR 第 22 条要求这个吗?
第 22 条赋予个人围绕「仅自动化进行、且产生法律效力或类似重大影响」的决策的权利——包括不受其支配,以及在例外情形下获得关于决策逻辑的有意义信息。某个具体决策是否落在其内,是你们团队的法律判断,不是日志库能替你决定的。本 recipe 提供的是证据层:如果你们把某类决策视为重大,第 22 条记录(主体、决策、置信度、逻辑版本、人工路径、保留期限)就是一个对象;如果判断为不重大,日志正是让你能始终如一地为这个判断辩护的东西。
一条决策记录应该包含什么?
六个字段:主体引用(尽可能假名化)、决策及其置信度、逻辑块(schema 版本、questions 哈希、模型版本、当时生效的闸门阈值)、Choice 题的选项分布、闸门判给的车道、以及人工介入路径(若有人工动作则含复核人动作)。除主体和复核人动作外,其余全部来自那一次 evaluate 响应。
这和普通的应用日志有什么区别?
应用日志回答「代码做了什么」;审计追踪回答「系统决定了什么、多有把握、依据哪套规则」。差别在结构:类型化决策记录可以设阈值统计(置信度低于 0.9 的自动执行发生了多少次?)、可以重放(新 schema 会怎么判?)、可以聚合(置信度分布在漂移吗?)——这三件事对自由文本日志行都做不了。
schema 升级后,能对旧案例重新决策吗?
能——这就是重放,而且是确定性的。因为 state 和版本化 questions 都在存储里,升级契约意味着把每条日志 state 重新提交给新 schema,然后 diff 车道。团队把重放当三件事用:迁移检查(升级有没有改变任何自动执行的决策?)、回归套件(日志 state 就是历史标注数据)、以及解释引擎(当有人问「当时的规则在今天怎么说」)。
为什么校准置信度对审计很重要?
因为置信度数字只有映射现实时才是证据。Jev 置信度经 RLCD 校准,一条 0.85 闸门对应可测量的准确率合同——你可以用自己的日志算出 0.85 档的决策多少次是对的。未校准的自我上报置信度撑不起这件事:对决策模型的独立审计发现过对错得多的答案报 30%+ 置信度,那会让每条日志阈值都沦为装饰。校准还让漂移可见——分布移动时,复核率会先于你的用户告诉你。