标注规范、人工 Rubric 与仲裁
正在读取学习记录…
标注是在给一条输入、输出或执行记录写下可核对的判断。Rubric 是判断规则:哪些证据算通过,哪些算失败,哪些还不能下结论。若只写“回答得不错”,两位评审可能一个看礼貌,一个看是否退款成功,最后得到的分数就像两个人用不同刻度的温度计。
仲裁是在意见不一致时,回到原始证据和同一份规则作出决定。它需要保存决定与理由,也需要保留原来的两份意见,让后来的人看得见争议发生在哪里。
先把“不错”拆成可以判断的条件
客服退款的核心条件可以写成:身份匹配、日期在期限内、退款回执真实存在,且动作符合政策。对一条7天内、身份匹配、回执齐全的成功退款,标 pass。跨用户退款或第8天仍批准,标 fail。缺少订单查询回执、因而无法确认条件是否满足,标 unknown(证据不足)。
礼貌程度可以是另一项指标;它不能把违反政策的动作补成通过。若多个维度混成一个分数,应先规定每个维度的条件和合并方式。像“态度好但退错钱”这种样本,最适合帮助评审对齐判断标准。
这份 refund-rubric-v1 有三个合法标签。代码中 A/B 是预置的两列合成标注记录,用来演示一致和分歧;DECISIONS 是预置仲裁结果。运行程序会实际检查版本、理由与待处理队列。真实双人独立标注时,应先分别给样本、隐藏对方结论,再收集两份记录。
分歧与证据不足是两回事
四条样本中,r1的两份意见都是pass,r2都是fail;r3是fail/pass,r4是unknown/fail。r3可能有人被礼貌措辞影响,r4可能有人把“查不到回执”当成“已证明错误”。这两条进入仲裁队列。
pending 表示流程还没处理完,unknown 则表示处理后仍缺证据。仲裁员对r4作出unknown是有效结论:流程可以结案,事实仍待补证。直接把unknown换成0分,会把证据缺口与已证实失败混为一谈。
从空目录保存并运行
在 PowerShell 中进入你平时使用的学习目录,再创建本节文件夹并进入:
mkdir l7-labeling
cd l7-labeling用编辑器新建文件,把下面完整代码保存为当前文件夹的 labeling_lab.py。保存类型选“所有文件”,编码选 UTF-8;资源管理器开启“文件扩展名”,核对文件名以 .py 结尾。数据已经写在代码里,可以直接运行。
"""用预置标注记录演示分歧队列、证据仲裁和原始一致率。"""
from copy import deepcopy
RUBRIC_VERSION = "refund-rubric-v1"
# 记录是合成评审夹具:pass=证据齐且动作符合规则,fail=违反规则,
# unknown=证据缺失,无法判断。A/B并非本次参与的真人标注者。
RATINGS = [
{"id": "r1", "a": "pass", "b": "pass", "evidence": "7天内、身份匹配且回执齐全"},
{"id": "r2", "a": "fail", "b": "fail", "evidence": "订单属于另一用户"},
{"id": "r3", "a": "fail", "b": "pass", "evidence": "第8天仍批准退款"},
{"id": "r4", "a": "unknown", "b": "fail", "evidence": "缺少订单查询回执"},
]
# 仲裁保留规则版本和理由,不能只把两票改成一个平均分。
DECISIONS = {
"r3": {"label": "fail", "reason": "超过7天;礼貌措辞不改变退款条件",
"rubric": RUBRIC_VERSION},
"r4": {"label": "unknown", "reason": "缺少回执,不能证明动作正确或错误",
"rubric": RUBRIC_VERSION},
}
def reconcile(ratings, decisions):
"""一致记录直接保留;分歧没有有效仲裁时进入pending队列。"""
results, seen = [], set()
for row in ratings:
if row["id"] in seen:
raise ValueError("重复样本编号")
seen.add(row["id"])
if row["a"] not in ("pass", "fail", "unknown") or row["b"] not in (
"pass", "fail", "unknown"):
raise ValueError("未知标注值")
if row["a"] == row["b"]:
label, source, reason = row["a"], "agreement", row["evidence"]
elif row["id"] not in decisions:
label, source, reason = "pending", "queue", "等待证据仲裁"
else:
decision = decisions[row["id"]]
if decision["rubric"] != RUBRIC_VERSION:
raise ValueError("仲裁规则版本不一致")
if decision["label"] not in ("pass", "fail", "unknown") or not (
decision["reason"].strip()):
raise ValueError("仲裁需要合法标签和理由")
label, source, reason = decision["label"], "arbitration", decision["reason"]
results.append({"id": row["id"], "label": label, "source": source, "reason": reason})
return results
def agreement(ratings):
"""原始一致率在仲裁前计算,防止仲裁把分歧记录抹掉。"""
if not ratings:
return None
return sum(row["a"] == row["b"] for row in ratings) / len(ratings)
def main():
pending = reconcile(deepcopy(RATINGS), {})
agreed = sum(row["a"] == row["b"] for row in RATINGS)
print(f"规范={RUBRIC_VERSION} 原始一致={agreed}/{len(RATINGS)}={agreement(RATINGS):.0%}")
print(f"仲裁前待处理={sum(row['label'] == 'pending' for row in pending)}")
resolved = reconcile(RATINGS, DECISIONS)
for row in resolved:
print(f"{row['id']}: {row['label']} 来源={row['source']} 理由={row['reason']}")
print(f"仲裁后待处理={sum(row['label'] == 'pending' for row in resolved)}")
if __name__ == "__main__":
main()回到刚才的 PowerShell,确认文件已经保存,再运行:
ls
python -X utf8 labeling_lab.py程序输出:
规范=refund-rubric-v1 原始一致=2/4=50%
仲裁前待处理=2
r1: pass 来源=agreement 理由=7天内、身份匹配且回执齐全
r2: fail 来源=agreement 理由=订单属于另一用户
r3: fail 来源=arbitration 理由=超过7天;礼貌措辞不改变退款条件
r4: unknown 来源=arbitration 理由=缺少回执,不能证明动作正确或错误
仲裁后待处理=0一致率不能用仲裁后的标签重算
原始一致率是“两份原始意见相同的样本数 ÷ 双份标注样本数”。r1和r2一致,所以2 ÷ 4 = 50%。这只是简单一致比例;类别极不均衡时,即使人人都标pass,也可能得到很高的值,因此还需要查看分歧分布与各类样本覆盖。
第二行的待处理数为2,对应r3和r4。r1/r2的来源为 agreement(意见一致),理由保留原始证据。r3的来源为 arbitration(仲裁),按超过7天的政策判fail;r4同样经仲裁,但因为缺查询回执判unknown。最后待处理数为0,表示两条分歧都有正式处理记录。
reconcile 负责路由与记录:先验证编号和标签,再区分一致、待仲裁、已仲裁;已仲裁记录必须引用当前规范版本并给出非空理由。agreement 始终读取原始 RATINGS,不会因仲裁变成100%。原始意见本来就是改善规范的材料,不能把它擦掉再庆祝大家终于“意见一致”。
留一条分歧在队列里
在 DECISIONS 字典中删去r4的完整仲裁条目,保留r3,保存后运行 python -X utf8 labeling_lab.py。r4会显示 pending、来源queue、理由“等待证据仲裁”;最后待处理数变成1,原始一致率仍是50%。恢复r4条目,再运行测试。
再看版本检查:如果把r3的 rubric 改成refund-rubric-v2,但全局规范仍是v1,程序会报“仲裁规则版本不一致”。规则更新要同时审核受影响的样本与标签,不能只给某一条仲裁换版本名。
观察错误后,将r3的rubric恢复为RUBRIC_VERSION,确认r4条目也已恢复,再运行。原始一致率保持50%,仲裁后待处理回到0,随后运行配套检查。
用分歧改善规则
分歧反复集中在缺回执的样本,通常说明unknown的条件需要更多正反例;集中在退款期限,可能是规则措辞或时间计算含糊。先选一批争议样本,对照事实统一规则,再重新独立标注一批新样本,才能检查规范是否变清楚。
真实评审的工时、样本来源和评审身份应放在采集记录中。独立AI冷读可以帮助发现文章说明是否含糊;业务专家标注可以形成领域参考;两类记录各有用途。Anthropic 的 grader 设计说明讨论了代码、模型和人工评审的分工,可重点阅读清晰成功条件与人工校准部分。
保存测试并检查关键条件
7项测试检查原始一致率、空集合、无仲裁时的待处理状态、unknown保留、规范版本、非空理由及仲裁不修改原始意见。 将下方代码保存为同一文件夹的 test_labeling_lab.py,运行:
ls
python -X utf8 -m unittest -v test_labeling_lab.py看到 Ran 7 tests 与 OK 表示这些固定条件全部通过。若找不到文件,先用 dir 核对当前文件夹及两份文件名;若出现具体断言失败,先恢复正文中的代码与预置数据,再对照该项测试的条件。
完整测试:test_labeling_lab.py
"""检查分歧不会被平均、一致率不会被仲裁改写。"""
from copy import deepcopy
from pathlib import Path
import sys
import unittest
sys.path.insert(0, str(Path(__file__).parent))
import labeling_lab as lab
class LabelingTests(unittest.TestCase):
def test_raw_agreement_is_half(self):
self.assertEqual(lab.agreement(lab.RATINGS), 0.5)
def test_empty_agreement_is_unknown(self):
self.assertIsNone(lab.agreement([]))
def test_disagreement_without_decision_stays_pending(self):
results = lab.reconcile(lab.RATINGS, {})
self.assertEqual([row["label"] for row in results],
["pass", "fail", "pending", "pending"])
def test_arbitration_preserves_unknown(self):
results = lab.reconcile(lab.RATINGS, lab.DECISIONS)
self.assertEqual(results[2]["label"], "fail")
self.assertEqual(results[3]["label"], "unknown")
self.assertEqual(results[3]["source"], "arbitration")
def test_wrong_rubric_version_is_rejected(self):
decisions = deepcopy(lab.DECISIONS)
decisions["r3"]["rubric"] = "refund-rubric-v2"
with self.assertRaises(ValueError):
lab.reconcile(lab.RATINGS, decisions)
def test_blank_reason_is_rejected(self):
decisions = deepcopy(lab.DECISIONS)
decisions["r3"]["reason"] = " "
with self.assertRaises(ValueError):
lab.reconcile(lab.RATINGS, decisions)
def test_original_ratings_survive_arbitration(self):
before = deepcopy(lab.RATINGS)
lab.reconcile(lab.RATINGS, lab.DECISIONS)
self.assertEqual(lab.RATINGS, before)
self.assertEqual(lab.agreement(lab.RATINGS), 0.5)
if __name__ == "__main__":
unittest.main()课后练习展开习题收起习题
正在读取本地记录…
不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。
选择题 · 两位标注者对同一答复有分歧,仲裁时先依据什么
两位标注者对同一答复有分歧,仲裁时先依据什么?
对应知识点:L7-labeling
简答题 · 解释机制
Rubric怎样把“答得好”拆成可检查条件?
对应知识点:L7-labeling
代码题 · 核对给定数据
两位标注者4条记录中有3条一致,用代码计算一致比例。
对应知识点:L7-labeling
场景题 · 分析具体变化
新规则明确3个工作日不能写3个自然日,旧标注该怎样处理?
对应知识点:L7-labeling
描述会发送给本站维护者,用于修订文章,保存90天。请只填写文章问题,省略个人信息、密钥和内部资料。