用 Jev 给 PR 加一层语义门禁
先说结论
- 这是一个我自己的 CI 方案,不是 TypeSafe 官方给出的 PR 门禁模板;官方 cookbook 只提供可复用的判断能力。
- 变更类型判断可以借用 Function calling 的 closed-set/Choice 思路;敏感路径判断可以借用 Guardrails for LLMs 的 Noul/Score 思路。
- Confidence 官方文档写明:Choice/Score 有
confidence,Noul 没有。因此 Noul 判断只能读概率,不能读confidence。 - 成本是我根据官方首页输入价推算:Jev 输入 $42/十亿 token,单 PR 的输入侧成本约 $0.000084~$0.00042。
- 落地时 state 只放摘要、路径、变更统计,不要放密钥和完整源码;阈值必须拿历史 PR 回测。
链路设计:把“这个 PR 有没有风险”拆成可决策问题
CI 里最贵的步骤,往往不是测试跑多久,而是人看到 PR 后判断:这改动会不会碰坏核心链路?要不要我现在就 review?
我的方案是把人工判断拆成三个问题:
- 变更类型:这是一个 feature、refactor、bugfix、hotfix,还是 release 变更?
- 是否触及敏感路径:认证、授权、支付、密钥、数据库迁移、CI/CD 等区域有没有被碰到?
- 风险等级:如果直接合并,危险程度属于哪一档?
我的做法是:先把 diff 脱敏成 state,再一次请求里提交 Choice、Noul、Score 三个问题;然后按 confidence 和 Noul 的概率,决定 CI 走 pass、加标签还是阻塞。这是工程实现选择,不是官方推荐写法。
官方 Function calling cookbook 给了选动作的基础能力。它原文是这样写的:
When you order a “large iced oat latte, no sweetener,” the barista does not write your sentence down. They mark four options on a cup. This cookbook does the same thing for a trading API: a sentence goes in, and out comes a function name and its arguments as evaluated enums, each with a confidence.
这句话的用处不是“PR 分类”,而是它点出了一个关键:把自然语言压缩到有限选项,并且每个选项带置信度。对 PR 门禁来说,change_type 如果让模型自由生成,结果可能不稳定;如果做成 Choice,让模型从固定集合里选,CI 后续分支就更好写。
官方可依据的部分:用 Choice 选动作
Function calling cookbook 里,参数值如果来自固定列表,就被视为 closed set。官方文档写道:
An argument whose values come from a fixed list is a closed set.
我把 PR 类型同样做成 closed set,而不是开放式文本生成。这样不论模型输出的是 bugfix 还是 feature,都会落在 CI 预设分支里。官方示例里的 Choice 可以返回 confidence,这正好用来决定“要不要自动分流”。
这里要说明:官方 cookbook 的示例是交易 API,不是 CI;官方文档没有说“请这样给 PR 做语义门禁”。我在这里是把官方展示的 Choice + confidence + closed set 能力移植到 PR 场景,这是我的方案,不是官方场景。
官方可依据的部分:用 Noul 和 Score 卡风险
Guardrails cookbook 的做法更接近我需要的“风险门禁”。它原文说:
Screen every message going into and out of an LLM app with one TypeSafe request, thresholding hazard probabilities and severity to pass, review, block, or route.
我的 PR 场景里,“消息进出”模型可以类比为“变更进入代码库”,所以同样可以用概率和严重度来分流:通过、打标签、阻塞或人工 review。
Guardrails cookbook 还把风险拆成多个 Noul 问题,并搭配一个 Score 问题:
A battery of Noul questions hands you the probability that each hazard holds, and a Score question rates how much harm complying would do.
这个拆法在 PR 门禁里很有用:sensitive_touch 用 Noul 判断“是否触及敏感路径”,risk_level 用 Score 判断“合并后可能伤得多重”。需要注意的是,这仍然是我把官方 guardrails 能力迁移到 CI;官方 cookbook 面对的是 LLM 输入输出安全,并非代码评审。
Confidence 怎么变成 CI 分支
官方 Confidence 文档解释了 confidence 的本质:
The answer’s confidence property collapses that shape into a single number from 0 to 1, so you can threshold on it without doing the math yourself. (Noul answers don’t carry one.)
也就是说,Choice 和 Score 适合用 confidence 做门槛,Noul 不适合。官方给的三档路径还写明:
Low confidence: Do not act. Route to a human, request clarification, or fall back to a different system.
因此,我的 CI 分支里,低 confidence 不自动通过,而是直接要求人工 review。这里有一个容易踩的坑:Noul 没有 confidence,如果你对 Noul 结果调 sensitive.confidence,那不是官方文档支持的做法。官方只告诉你 Noul 给概率,不给你算好的 confidence。
可改的伪代码
下面是我整理的骨架。它读取 diff、脱敏组装 state、调用 TypeSafe、再按结果决定 CI 分支。这不是可直接运行的 SDK 代码,字段和阈值也需要替换成你的实际环境。
def pr_semantic_gate(diff_meta):
# diff_meta 已经脱敏:只有摘要、路径、变更统计,不含完整源码
state = {
"summary": diff_meta["summary"][:1800],
"paths": diff_meta["changed_paths"][:80],
"deletions": diff_meta["deletions"],
"tests_touched": diff_meta["tests_touched"],
"environment": diff_meta.get("ci_environment", "PR"),
}
resp = client.system_one(
state=state,
questions={
"change_type": Choice(
instructions="这个 PR 主要属于哪一类?",
criteria={
"feature": "新增功能,但向后兼容",
"refactor": "结构整理,无功能变化",
"bugfix": "修复缺陷",
"hotfix": "紧急修复或安全问题",
"release": "版本、依赖或发布配置变更",
},
),
"sensitive_touch": Noul(
instructions="这个变更是否触及敏感路径?",
criteria=(
"触及认证、授权、支付、密钥、"
"数据库迁移、CI/CD 或生产配置"
),
),
"risk_level": Score(
instructions="如果按当前 diff 合并,风险有多高?",
criteria=["none", "low", "medium", "high", "critical"], # 官方字段名是 criteria(有序数组),不是 levels
),
},
)
change = resp.answers["change_type"]
sensitive = resp.answers["sensitive_touch"]
risk = resp.answers["risk_level"]
# Noul 没有 confidence,只读概率;这是官方 Confidence 页明确过的。
sensitive_p = getattr(sensitive, "probability", None)
if sensitive_p is None:
return "need_review", resp
if change.confidence < 0.5 or risk.confidence < 0.5:
return "need_review", resp
if sensitive_p >= 0.7 or risk.level in {"high", "critical"}:
return "block_or_label", resp
return "pass", resp
这里的 0.5、0.7 是示例阈值。官方 Confidence 示例里出现过 0.5 和 0.9,但官方也明确说:
The correct threshold values depend on your domain and the performance of the model for your use case.
所以这些值要拿你自己历史 PR 回测后再定。
落地注意事项
我的清单如下:
- state 脱敏:不放入完整 diff、仓库 token、CI 密钥。只放摘要、路径列表、变更统计。这样既能控制成本,也能避免把敏感信息送进外部模型。
- 日志可回溯:把 model 字段、置信度、概率、最终分支写进 CI 日志。一旦误判,至少能复算当时的输入和判定。
- 阈值回测:先把门禁跑在历史 PR 上,看
pass、block_or_label、need_review的分布;不要一上来就阻塞合并。 - 先软门禁再硬门禁:建议先只加评论或打标签,稳定后再阻塞 CI。
- Noul 只读概率:不要给 Noul 结果读
confidence,否则代码容易崩或做出错误判断。
成本量级
成本按输入 token 估算。官方首页写明:
Jev.Cost $42 Per Billion input tokens.
这是一个官方数据。下面的成本范围是我自己推算,不是官方报价。
按输入 token 计算(自己推算):
单 PR 成本 = state_input_tokens / 1_000_000_000 × 42 USD
2K token: 2000 / 1e9 × 42 = 0.000084 USD
10K token: 10000 / 1e9 × 42 = 0.00042 USD
假设每个 PR 的 state 在 2K~10K 输入 token 量级,单次 PR 门禁成本约为 $0.000084~$0.00042(推算:按官方 $42/十亿输入 token 折算)。如果一天 200 个 PR,最低成本约 $0.0168/天(推算:200 × $0.000084);按 30 天 6000 个 PR 算,约 $0.504/月(推算:6000 × $0.000084)。但这是简化推算,实际还取决于请求次数、模型版本、SDK 打包方式、是否批量请求等。
官方首页还有一个 System One workflow 对比示例:Cost $0.000081、Completed in 0.114s,对比 LLM $0.013880、8.566s。这是官方页面给出的示例,不是 PR 门禁承诺,只能说明量级上 System One 任务可能更便宜。
这次没核实的
- Jev 输出 token 定价未在首页明确给出,我只核实到输入价为 $42/十亿 token。上面成本估算只覆盖输入侧,实际总成本需要确认输出 token 价格和单次请求计费规则。
- TypeSafe 是否官方支持“CI/PR 语义门禁”这个场景,我未能核实。官方文档展示的场景是交易 function calling 和 LLM 护栏,本文的 CI 方案是我的设计。
- 官方 SDK 中 Noul 返回字段的完整命名,我未在给定页面里逐字核实;因此伪代码里用
getattr(sensitive, "probability", None)表示“读概率”,实际字段以 SDK 为准。 0.7这个 Noul 概率阈值是我在本例中提出的示例,不是官方推荐值。
参考来源
评论区
登录后可评论。