用 Jev 做内容审核:一致、可解释的判定

更新于 适用版本 Jev 1.13

TL;DR: 内容审核不是一个难题,而是很多个小决策。用 choice 调用对扁平类别列表做分诊,置信度不足时对具体政策规则降级为 judgment 调用,每次删除都把 rationale 存成审计记录。一致性来自固定的问题格式,可解释性来自 rationale。下文给出流程、阈值与申诉路径。

先设计类别

分两层,刻意保持简单:

{"answer":"yes","confidence":0.97,"rationale":"该消息以付款为条件威胁人身伤害。"}

这与人审团队的实际工作方式一致:先分诊,难例逐条规则复核。同时每个问题都很小——这正是判断模型(judgment model)最喜欢的输入形态。

审核流程图

内容提交
   |
   v
Jev choice 调用(类别:spam / harassment / adult / violence / none)
   |
   +--> 类别 = none 且置信度高 --> 发布
   |
   +--> 置信度低或类别不确定
   |        |
   |        v
   |    Jev judgment 调用(针对具体规则:yes / no / unclear)
   |        |
   |        +--> 明确 yes / no --> 执行动作,存 rationale
   |        |
   |        +--> 仍不明确 --> 人工审核队列
   |
   +--> 明确违规 --> 删除,存 rationale,附带申诉链接通知用户

每次删除固定存储五元组:内容 id、类别、answer、confidence、rationale、模型版本。这就是你的审计记录(audit trail)。

最小实现

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"
CATEGORIES = ["spam", "harassment", "adult", "violence", "none"]

def call(system: str, text: str) -> dict:
    resp = requests.post(
        URL,
        headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
        json={"model": MODEL, "temperature": 0, "messages": [
            {"role": "system", "content": system},
            {"role": "user", "content": text}]},
        timeout=30,
    )
    resp.raise_for_status()
    # 示例数据(example fixture,judgment):
    # {"answer":"yes","confidence":0.97,"rationale":"..."}
    return json.loads(resp.json()["choices"][0]["message"]["content"])

def moderate(text: str, low=0.85) -> dict:
    c = call("Pick exactly one category from: "
             + ", ".join(CATEGORIES) + ".", text)
    if c["answer"] != "none" and c["confidence"] >= low:
        return {"action": "remove", **c}
    if c["confidence"] >= low:
        return {"action": "publish", **c}
    j = call("Does this text violate the harassment policy? "
             "Answer yes, no, or unclear.", text)
    if j["answer"] == "unclear" or j["confidence"] < low:
        return {"action": "human-review", **j}
    return {"action": "remove" if j["answer"] == "yes" else "publish", **j}

官方 API 的端点与字段请以 typesafe.ai 官方文档为准。

阈值与申诉路径

阶段配置项建议
第一层 choice自动执行阈值删除与发布统一用 confidence >= 0.85
第二层 judgment边界规则复核第一层低置信度时触发;仅对明确的 yes/no 执行动作
人工队列触发条件任何 unclear 结果,或第二层后置信度仍低于阈值
审计记录一律存储内容 id、类别、answer、confidence、rationale、模型版本
申诉用户可见通知里附 rationale 摘要与一键申诉入口

为什么问题格式这么重要,见状态与问题(state and questions)指南;阈值调优见置信度与兜底(confidence and fallback)指南。多语言审核案例展示了这套两层设计在跨语言场景下的表现。

本文适用版本:Jev 1.13。

FAQ

常见问题

审核类别该怎么用 Jev 组织?

第一层用 choice 调用从扁平类别列表里选一个(如 spam、harassment、adult、violence、none);边界或低置信度情况再用第二层 judgment 调用针对具体规则提问,而不是塞进一个巨型多标签提示词。

为什么每次判定都要保留 rationale?

rationale 就是审计记录。它向用户、申诉审核员和监管方解释这条内容为什么被删除,这是人工审核备注很难做到的一致程度。

置信度低的时候怎么办?

先针对具体规则做一次廉价的 judgment 复核,能解决的边界案例当场处理;仍然不明确就连同 rationale 一起进人工审核队列。

继续阅读