Jev 案例:贷款申请风险分级(完整代码)
TL;DR: 本案例演示一个小型信贷平台的风控分档流水线:第一次是选择题,把每份申请分进
low、medium、high三档风险;第二次是判断题,决定这份申请是否需要人工复核。路由靠置信度阈值驱动——0.8 以上自动出档,中间区间进人工复核,0.5 以下全部走人工。完整请求体、示例数据(example fixture)格式的响应,以及可直接运行的 Python 脚本都在下文。
场景
我们合作的一家小型消费信贷平台,每月处理大约 9000 份贷款申请。他们现有的规则引擎只认硬性 cutoff:FICO 低于 620 直接拒绝,FICO 高于 720 且债务收入比低于 36% 自动通过,剩下的全堆到人工复核桌上。这套规则对数字很好使——但数字没说的部分,它一个都接不住。
差距是在复盘报告里暴露的。就业描述自相矛盾的申请(“在某公司干了五年”,而那家公司 2023 年才成立)、申报收入和申报行业明显对不上的申请、贷款用途在不同字段之间前后不一致的申请——只要征信分数恰好过线,这些申请全都顺利通过了。规则引擎没有能力权衡这类软性信号,而人工复核也不可能在 9000 份申请的量上铺开。
所以我们加了每个申请两次 Jev 调用,卡在规则引擎和复核队列之间:
- 选择题——把申请分进且只分进一个风险档:
low、medium、high; - 判断题——不管分在哪一档,这份申请是否需要人工复核?回答
yes、no或unclear。
Jev 是 TypeSafe AI 在 2026 年 9 月发布的 System One 判断模型。它适合这个位置,是因为输出是带置信度的类型化 JSON,而不是自由文本——路由门禁要的正是这种东西。下文的示例申请是刻意挑的混合样本:FICO 678 分,按老规则会触发中层档复核;另外还埋了两处规则引擎根本看不见的软性矛盾。
请求
第一次调用——风险分档(选择题):
{
"model": "typesafe/jev-1.13",
"messages": [
{
"role": "system",
"content": "Choice question. Assign this loan application to exactly one risk tier: low, medium, or high. Consider stated income vs. industry norms, internal consistency of employment history, and coherence of the stated loan purpose. Reply with JSON only: {\"answer\": <low|medium|high>, \"confidence\": <0-1>, \"rationale\": <one sentence>}"
},
{
"role": "user",
"content": "Loan application #A-48213: FICO 678. Stated monthly income: $9,200, occupation: warehouse associate. Employment: \"shift lead at a regional logistics company for 5 years.\" Requested amount: $12,000 over 36 months. Purpose field 1: \"consolidate credit cards.\" Purpose field 2 (free text): \"help pay for my nursing school tuition starting this fall.\" DTI: 31%."
}
]
}
第二次调用——人工复核检查(判断题):
{
"model": "typesafe/jev-1.13",
"messages": [
{
"role": "system",
"content": "Judgment question. Answer yes, no, or unclear. Reply with JSON only: {\"answer\": <yes|no|unclear>, \"confidence\": <0-1>, \"rationale\": <one sentence>}"
},
{
"role": "user",
"content": "Loan application #A-48213: FICO 678, DTI 31%. Claims $9,200/month as a warehouse associate and 5 years at a logistics company. Loan purpose is stated as credit card consolidation in one field and nursing school tuition in another. Does this application need manual review?"
}
]
}
响应
以下两个响应均为示例数据(example fixture),用于说明格式,不是线上实测抓包。
第一次调用:
{
"answer": "medium",
"confidence": 0.71,
"rationale": "Credit metrics are acceptable, but the stated income is high for the occupation, and the loan purpose is inconsistent between fields."
}
第二次调用:
{
"answer": "yes",
"confidence": 0.88,
"rationale": "The mismatched loan purposes and income-to-occupation gap are exactly the soft signals the rules engine cannot evaluate."
}
复现脚本
保存为 risk_tier.py,填入你自己的 BASE_URL 和 API key 后运行 python3 risk_tier.py。脚本使用 OpenAI 兼容 SDK,任何 OpenAI 兼容端点都能跑:
import json, os
from openai import OpenAI
BASE_URL = "https://YOUR-ENDPOINT.example.com/v1" # replace with your provider
client = OpenAI(base_url=BASE_URL, api_key=os.environ["JEV_API_KEY"])
MODEL = "typesafe/jev-1.13" # confirm exact slug with your provider
def ask(system, user):
res = client.chat.completions.create(
model=MODEL,
messages=[{"role": "system", "content": system},
{"role": "user", "content": user}])
return json.loads(res.choices[0].message.content)
CHOICE = ('Choice question. Assign this loan application to exactly one risk '
'tier: low, medium, or high. Consider stated income vs. industry '
'norms, internal consistency of employment history, and coherence '
'of the stated loan purpose. Reply with JSON only: '
'{"answer": <low|medium|high>, "confidence": <0-1>, '
'"rationale": <one sentence>}')
JUDGE = ('Judgment question. Answer yes, no, or unclear. Reply with JSON '
'only: {"answer": <yes|no|unclear>, "confidence": <0-1>, '
'"rationale": <one sentence>}')
app = ("Loan application #A-48213: FICO 678. Stated monthly income: $9,200, "
"occupation: warehouse associate. Employment: 'shift lead at a "
"regional logistics company for 5 years.' Requested amount: $12,000 "
"over 36 months. Purpose field 1: 'consolidate credit cards.' "
"Purpose field 2 (free text): 'help pay for my nursing school "
"tuition starting this fall.' DTI: 31%.")
tier = ask(CHOICE, app)
review = ask(JUDGE, "Loan application:\n" + app + "\n\nDoes this application need manual review?")
def route(tier, review):
conf = tier["confidence"]
if conf >= 0.8:
return "auto:" + tier["answer"]
if conf >= 0.5:
return "human-review"
return "human-review-priority"
print(tier)
print(review)
print(route(tier, review))
置信度路由
光有档位还不是最终决策——置信度才是。我们的路由规则:
tier confidence >= 0.8 ----> 自动套用档位
tier confidence 0.5-0.8 --> 人工复核队列
tier confidence < 0.5 ----> 优先人工复核
review answer = yes ----> 强制人工复核(覆盖档位结果)
review answer = unclear --> 按 yes 处理
大部分申请实际落在中间区间,这没关系——门禁的目的不是把一切都自动化,而是确保只有”看得清”的案子才有资格跳过人工。
关键参数
| 字段 | 取值 | 说明 |
|---|---|---|
model | typesafe/jev-1.13 | 固定版本号,避免新版本发布后悄悄改变风控结论。准确 slug 请以服务方为准。 |
messages[0](system) | 题型 + 选项列表 + JSON 结构 + 要权衡的软性信号 | 明确列出软性信号(收入 vs. 行业、一致性、用途连贯性),能让打分在不同申请之间保持稳定。 |
messages[1](user) | 申请字段原文 | 传原始字段值,不要传摘要——矛盾点就藏在细节里。 |
answer(响应) | medium / yes(示例数据) | 档位映射到路由表;复核标记可以覆盖档位。 |
confidence(响应) | 0.71 / 0.88(示例数据) | 路由的主要输入:≥0.8 自动、0.5–0.8 复核、<0.5 优先复核。 |
rationale(响应) | 一句话 | 直接进复核员的案件备注。 |
注意事项
- 上面的响应均为示例数据(example fixture);字段名请与官方文档核对后再依赖。官方 API 细节在 typesafe.ai,入口是System One 模型发布公告。
- 原始 HTTP 响应是 OpenAI 兼容的 chat completion 结构;本文展示的 JSON 是从 message content 里解析出来的模型类型化输出。
- 这条流水线是对规则引擎的补充,不是替代。硬性 cutoff(欺诈标记、法律限制)依然先触发;Jev 只处理规则够不着的那段软性信号区间。
- 每一条
rationale和置信度都要记日志。复核员推翻模型判断时,这份日志就是你之后调阈值的依据。 - 适用于 Jev 1.13(2026 年 9 月发布),演示渠道为 OpenRouter。