用 Jev 做简历初筛:快速且可复核

更新于 适用版本 Jev 1.13

TL;DR: 一份 JD(职位描述)本质是穿着段落外衣的检查清单。把每条硬性条件转成一道 judgment 判断题,整体匹配度用一次 scoring 打 1-10 分,边界情况连同 rationale 交给招聘人员。你得到的是一轮快速、一致、而且可审计的初筛——这正是公平性审查和合规团队要看的东西。

从 JD 到问题清单

筛选决策必须可解释,所以先改写 JD:

{"answer":"yes","confidence":0.97,"rationale":"工作经历部分显示其带过后端团队 7 年。"}
{"answer":8,"scale":[1,10],"confidence":0.86,"rationale":"技术栈与级别匹配;缺少要求的金融科技背景。"}

初筛流程图

简历 + JD 问题清单
   |
   v
逐条硬性条件调用 judgment(yes / no / unclear)
   |
   +--> 某条真实硬性条件为 no --> 淘汰,保留 rationale
   |
   +--> 出现 unclear 或低置信度 --> 招聘人员队列
   |
   +--> 全部条件通过
            |
            v
        Jev scoring 调用(匹配度,1-10 分制)
            |
            +--> 分数 >= 8 且置信度高 --> 推进到面试
            |
            +--> 分数 6-7 或置信度低 --> 招聘人员队列
            |
            +--> 分数 < 6 --> 淘汰,保留 rationale

每次淘汰和每次推进都存 answer、confidence、rationale。招聘人员看得到原因,候选人问得到原因,审计方能回放原因。

最小实现

import os, json, requests

URL = "https://openrouter.ai/api/v1/chat/completions"
# Confirm the exact model slug on the OpenRouter model page.
MODEL = "typesafe/jev-1.13"

def ask(question: str, resume: str) -> dict:
    resp = requests.post(
        URL,
        headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
        json={"model": MODEL, "temperature": 0, "messages": [
            {"role": "system", "content":
             "Answer strictly from the resume. yes, no, or unclear. "
             "If the resume does not state it, answer unclear."},
            {"role": "user", "content": f"Requirement: {question}\n\n{resume[:6000]}"}]},
        timeout=30,
    )
    resp.raise_for_status()
    # 示例数据(example fixture):
    # {"answer":"yes","confidence":0.97,"rationale":"..."}
    return json.loads(resp.json()["choices"][0]["message"]["content"])

def screen(resume: str, requirements: list[str], low=0.85):
    for req in requirements:
        r = ask(req, resume)
        if r["answer"] == "no":
            return {"stage": "rejected", "requirement": req, **r}
        if r["answer"] == "unclear" or r["confidence"] < low:
            return {"stage": "recruiter-queue", "requirement": req, **r}
    return {"stage": "checklist-pass", **ask("Overall fit 1-10.", resume)}

注意系统提示里的”简历没写就答 unclear”——正是这句话阻止模型对缺失信息瞎猜。官方 API 的端点与字段以 typesafe.ai 官方文档为准。

公平与合规注意事项

关注点做法
问题范围只问书面、与岗位相关的要求;不碰受保护特征
信息缺失模型必须答 unclear,不允许从空白推断
记录留存每次决策存问题、answer、confidence、rationale、模型版本
人工环节所有 unclear、所有低置信度、所有最终决定
偏差检查定期抽样对比不同群体的通过率

为什么固定格式的问题比让聊天模型”评价一下这份简历”更可靠,见 Jev 与 LLM-as-a-judge 对比指南;简历匹配度打分案例展示了完整流水线;阈值调优见置信度与兜底指南。

本文适用版本:Jev 1.13。

分级阈值与人工复核分流

候选人数量多、JD 条件不完全对等时,单一的”通过/进队列/淘汰”三态有时太粗。实践中可把阈值分两层:第一层控制单条 judgment 是否可信(confidence 阈值);第二层控制整体匹配分的分流动作(score 阈值)。

匹配分区间置信度状态建议动作
≥ 8全部高置信度自动推进面试
6–7任意进招聘人员队列
≥ 8有低置信度项进招聘人员队列
< 6任意自动淘汰,存 rationale

示例数据(example fixture)——阈值配置对象:

{"hard_confidence":0.85,"score_pass":8,"score_reject":6,"high_risk_confidence":0.92}

对合规资质类要求(如执业资格、工作许可),可将 hard_confidence 单独提高到 0.92,其余条件维持 0.85。调整阈值时,记录每次变更的时间、责任人和理由,以备事后审计。

JD 变更后如何回归

JD 修改(如新增学历要求、删除某工具要求、措辞调整)会让已有问题清单局部失效。回归流程:

JD 变更
   |
   v
对比旧版问题清单,逐条标记:新增 / 删除 / 措辞变更
   |
   +--> 新增条件 --> 补写 judgment 问题,加入问题清单
   |
   +--> 删除条件 --> 从清单移除,避免无效过滤
   |
   +--> 措辞变更 --> 重写问题,抽样重跑 20-50 份历史简历
            |
            v
        对比新旧 answer 分布
            |
            +--> 结论变化率 > 5% --> 人工审查差异样本,确认后再上线
            |
            +--> 结论变化率 ≤ 5% --> 更新问题清单版本号后上线

版本化建议

误筛排查清单

候选人或招聘人员反馈”明显符合条件却被淘汰”或”明显不符合条件却推进了”时,按以下顺序排查:

现象可能原因排查方法
有经验却答 no简历措辞与问题措辞差异大对比 rationale 和简历原文,重写问题
缺信息却答 yessystem prompt 缺少 unclear 约束确认提示词含”不确定则答 unclear”
批量 unclear 过多简历格式碎片化预处理简历结构,按段落重排后重跑
低置信度聚集同一条件该条件定义模糊拆分为两道更具体的 judgment 问题
不同招聘人员复核结论不一人工复核标准缺说明在队列页面附上该问题的”判断依据”示例

每次排查后记录:出错的问题文本、出错的 answer / confidence / rationale、修改后的问题文本、触发排查的候选人 ID(已脱敏)。排查记录构成问题清单的变更日志,配合版本号管理,可有效防止同类误筛反复出现。

置信度阈值调优的更多细节见置信度与兜底指南;Jev 三原语的整体结构见 Jev 三原语指南。

FAQ

常见问题

+ 为什么把 JD 拆成多个是/否问题,而不是一个大提示词?

每条硬性条件变成独立的 judgment 调用(如'是否有 5 年以上后端经验?'),小而固定的问题才能返回置信度和 rationale,每次筛选决策都可复核。

+ 怎么保证自动初筛公平合规?

只对与岗位相关的书面要求提问,绝不涉及受保护特征;每次决策都记录 answer、confidence、rationale;低置信度和最终决定一律保留人工环节。

+ 人工复核阈值放在哪里?

实用默认:仅当所有硬性条件都以高置信度通过时自动推进,其余情况——包括任何 unclear 或低置信度结果——全部进招聘人员队列。

继续阅读