news 2026/9/11 23:16:34

基于 agno 的 Inter-Annotator Agreement 实践:用纯标准库实现评估 LLM 标注与陪审团投票的一致性

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 agno 的 Inter-Annotator Agreement 实践:用纯标准库实现评估 LLM 标注与陪审团投票的一致性

基于 agno 的 Inter-Annotator Agreement 实践:用纯标准库实现评估 LLM 标注与陪审团投票的一致性

【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno

本文基于 agno 数据标注系列 cookbook 中的_19_inter_annotator_agreement模块(含 TEST_LOG.md 的实测记录、README.md 的用法说明及 basic.py、jury_votes.py 两个可运行示例),讲解如何衡量多个标注者(模型、指令框架或人类)对同一批样本产出标签的一致程度。读完本文,你将掌握 raw agreement、Cohen's kappa、Fleiss' kappa、Krippendorff's alpha 四种一致性指标的纯标准库实现与公式细节,理解如何在 agno 中以同一基础模型的三种指令框架模拟独立标注者、构建 item × rater 矩阵、处理缺失单元格,以及如何在标签分布倾斜时识别"被原始一致性掩盖"的陪审团投票信号。

为什么需要一致性指标:原始一致性会"吹捧"流水线

任何标注流水线的标准可靠性检查,都是检验独立标注者能否复现彼此的标签。在本模块中,"标注者"并非多个不同模型,而是同一个判断模型在不同指令框架(instruction framings)下以 temperature=0 运行。这一设计非常关键:既然采样噪声被 temperature=0 消除,那么每一次分歧都只能追溯到指南措辞(guideline wording)本身,而不是随机性。正如 README.md 所指出的,仅看 raw agreement 会美化一条标注流水线,只有经过机会校正(chance-corrected)的指标才能告诉你指南是否真正可复现。

该模块的适用场景(来自 README 的 "When to use"):

  • 审计一次指南重写是否真的改变了标签结果;
  • 决定单一模型的标注器是否足够可靠、能否独立运行;
  • 审查陪审团投票过滤器(jury-vote filters):当标签分布倾斜时,高 raw agreement 阈值可能放行的只是大量偶然一致;而一个永远投多数标签的陪审员,即使与其他人有很高的原始一致性,其机会校正后的一致性也可能为零。

四种一致性指标的纯标准库实现与公式

basic.pyjury_votes.py都以Matrix = list[list[Optional[str]]]表示item(行)× rater(列)矩阵,单元格None表示缺失评分。四个指标全部用纯标准库实现(仅依赖itertoolscollectionstyping),并把公式以注释形式写在函数内,便于核对与教学。以下实现细节均可在 basic.py 中直接阅读。

raw_agreement —— 原始一致性

对每个 item,统计评了同一标签的 rater 对占所有 rater 对的比例,再对所有 item 取平均:

P_o = (1/N) * sum_i [ agreeing_pairs_i / total_pairs_i ]

少于两条评分的 item 被跳过。它不包含任何机会校正,因此在标签倾斜场景下会给出虚高的数值。

Cohen's kappa —— 两两比较的机会校正

适用于两两 rater 比较,仅统计两者都评过的 item:

p_o = 标签完全一致的 item 占比 p_e = sum_k p_a(k) * p_b(k) (两个 rater 各自边际分布的乘积和) kappa = (p_o - p_e) / (1 - p_e)

basic.py主流程中,三个 rater 两两组合,产出cohen_kappa_terse_vs_rubriccohen_kappa_terse_vs_personacohen_kappa_rubric_vs_persona三个成对指标。

Fleiss' kappa —— 多 rater 的机会校正

适用于每个 item 的 rater 数相等且无缺失单元格的矩阵:

n_ik = 类别 k 被分配给 item i 的 rater 数,n = 每 item 的 rater 数,N = item 数 P_i = (sum_k n_ik^2 - n) / (n * (n - 1)) 逐 item 一致性 P_bar = (1/N) * sum_i P_i 观测一致性 p_k = sum_i n_ik / (N * n) 类别占比 P_e = sum_k p_k^2 机会一致性 kappa = (P_bar - P_e) / (1 - P_e)

实现中若检测到任何缺失单元格,会直接抛出ValueError("fleiss_kappa requires complete rows (no missing cells)")

Krippendorff's alpha(nominal)—— 原生支持缺失单元格

这是四个指标中唯一为缺失单元格提供原生处理的多 rater 机会校正指标,对 nomin 类数据(delta = 1 when c != k, else 0)按以下步骤计算:

  1. 只保留含 ≥ 2 个可配对(非缺失)值的 item;
  2. 构建共现矩阵:单元内有 m_u 个值,来自不同 rater 的每个有序值对 (c, k) 贡献1/(m_u - 1)到 o_ck;
  3. 边际:n_c = sum_k o_ck,n = sum_c n_c;
  4. 名义不一致:D_o = sum_{c != k} o_ck,期望不一致 D_e = sum_{c != k} n_c * n_k / (n - 1),最终 alpha = 1 - D_o / D_e。

缺失单元格不贡献任何配对,因此天然被正确处理;而 Fleiss' kappa 没有这种机制。

实验一:basic.py —— 三种情感指南框架标注 12 段文本

实验设计

basic.py用同一个Gemini(id="gemini-3.5-flash", temperature=0)基础模型,以三种真实不同的指南措辞(代码见 basic.py)标注 12 段商品评论文本:

框架指令要点
terse一句话:Label the sentiment of the text: positive, negative, or neutral.
rubric详细规则:讽刺/反语按真实意图标注;混合情绪因正负相抵标 neutral;微弱或高度修饰的表扬(如 "fine, I guess")标 neutral;纯事实陈述标 neutral
persona购物者视角:会推动购买标 positive,让消费者犹豫标 negative,无购买信号(如纯事实)标 neutral

所有 Agent 均使用output_schema=SentimentLabel输出Literal["positive", "negative", "neutral"]的 Pydantic 结构化结果,并在label_text最多重试 3 次解析 schema 失败,绝不强转(basic.py)。

12 条 item 中,8 条为清晰的正/负/中性样本,4 条被特意设计为真正模糊(basic.py):讽刺(字面正向、意图负向的 "Oh great, another update...")、均衡混合情绪("Gorgeous screen and superb speakers, but...")、微弱表扬("better than I expected. I would not buy it again...")以及作者本人摇摆不定的矛盾文本("Some days I love it, some days I want to throw it out the window.")。

自检:先于任何模型调用验证指标实现

self_check()在任何模型调用之前用两组手算案例断言四个实现(basic.py):

  • 完全一致矩阵(mixed categories):四个指标全部为 1.0;
  • 2 rater × 4 item 手工推导矩阵:rater A 为pos pos neg neg,rater B 为pos neg neg pos。raw agreement 2/4 = 0.5;Cohen 因双方边际均为 0.5/0.5,kappa = 0.0;Fleiss 同样为 0.0;Krippendorff 共现矩阵推导得 alpha = 1 - 4/(32/7) = 0.125。

TEST_LOG.md 记录:self_check passed,即实现与手算值全部吻合,通过后才发起模型请求。

实测结果(TEST_LOG.md 记录,2026-07-18,agno 2.7.4)

本次运行指标:

raw_agreement 0.833 fleiss_kappa 0.742 krippendorff_alpha 0.749 cohen_kappa terse_vs_rubric 0.874 terse_vs_persona 0.739 rubric_vs_persona 0.629

关键观察:

  • 恰好 3 个被设计为模糊的 item 产生分歧并被路由到 review 列表,其余 9 条一致:
    • 混合情绪项:terse=negative / rubric=neutral / persona=negative;
    • 微弱表扬与矛盾文本项:terse=neutral / rubric=neutral / persona=negative;
    • 讽刺项:三个框架一致判 negative(说明 rubric 的"按真实意图标注"规则与 persona 的购物者底线判断一致)。
  • 最终汇总为12 items x 3 raters: 9 unanimous, 3 routed to review
  • 两次运行(temperature=0)输出完全一致;但 TEST_LOG.md 同时提醒,标签原则上仍可能随模型更新而漂移。

实验二:jury_votes.py —— 对 DPO 偏好对做陪审团投票一致性分析

实验设计

jury_votes.py将同一套指标应用到dpo_jury 形状的偏好投票上:三个 juror 框架(terse、反冗长 rubric、助教 persona)对 8 对答案投a/b/tie(jury_votes.py)。8 对答案被刻意设计为倾斜:其中 6 对明显是 "a" 更好(如 fib、sum35、round、tcp、reset、sky),2 对设计为"接近"的简明 vs 教学型对比(http404、float),此时 rubric 的"永远选更短"规则与 persona 的"永远选更有教学价值"规则指向相反方向(jury_votes.py)。

此外,persona juror 在 fib 对上确定性弃权(recusal)——这是对 dpo_jury 中"自偏好弃权"(模型家族不评自己族生成的答案)的模拟(dpo_jury.py 中真实实现为if family == ex["source_family"]: recuse)。三个框架共享同一基础模型,因此这里通过RECUSALS: set = {("persona", "fib")}硬编码一次弃权,在投票矩阵中留下一个缺失单元格。

自检:含缺失单元格与低于机会一致性案例

self_check()除完全一致矩阵外,还手算断言了两个关键案例(jury_votes.py):

  • 含缺失单元格的 3 juror × 3 item 矩阵[a, a, None] / [a, b, b] / [b, b, b]。raw agreement = 7/9;Krippendorff's alpha = 8/15(缺失单元格不贡献配对);Fleiss 仅在完整行子集(item 2、3)上计算,得 kappa = -0.2,即低于机会一致性的案例。
  • 这直接验证了 "alpha 原生处理缺失、Fleiss 只算完整行" 的差异。

实测结果(TEST_LOG.md 记录)

本次运行:投票分布{'a': 19, 'b': 4},即83% 的投票是 'a'(倾斜正是实验目的)。核心结果:

  • 6 对倾斜对上 raw agreement = 1.000;
  • 2 对接近对上 terse 与 persona 投 b、rubric 投 a,使整体 raw agreement = 0.833;
  • 机会校正后急剧回落:krippendorff alpha = 0.421,fleiss kappa = 0.382(在 7/8 完整行上计算)——输出行醒目地指出 "0.833 collapsing to 0.421 under 83% skew";
  • 成对 Cohen:terse_vs_persona = 1.000,terse_vs_rubric = 0.000,rubric_vs_persona = 0.000。rubric juror 在全部 8 对上都投 'a',其退化的一致性边际(全多数标签)使得 kappa 在与 terse 有 75% raw agreement 的情况下恰好为 0
  • 最终汇总:8 pairs x 3 jurors: 23 votes cast, 1 recused, 6 pairs unanimous among sitting jurors;两次运行输出一致。

这正是 README 强调的教训:在 83% 的标签倾斜下,raw agreement 0.833 一旦移除多数标签上的机会一致性,就坍缩为 alpha 0.421;一个永远投多数标签的 juror 可以同时拥有高 raw agreement 与零机会校正一致性。因此 jury_votes.py 结尾建议:通过agreement >= 0.75过滤的陪审团,其超机会信号可能远低于原始数值所暗示的水平,应把 alpha 与 raw agreement 并列报告(jury_votes.py)。

与相邻 cookbook 的关系:从投票生成到裁决闭环

该模块不是孤立存在的,它位于 agno 数据标注体系(cookbook/data_labeling)中,与上下游用例衔接:

  • 偏好投票的产生:本模块评测的 dpo_jury 形状投票,来自 cookbook/data_labeling/_05_text_pairwise_preference 的dpo_jury.py模式——5 个模型家族(OpenAI、Anthropic、Google、Groq、Mistral)构成陪审团,带置信度、双向交换消解顺序敏感、自偏好弃权与 gold pair 校准;
  • 分歧的处理:被路由到 review 的 item,可按 cookbook/data_labeling/_18_quality_review 的 labeler → reviewer → adjudicator 工作流进行裁决——两个不同 provider 的 labeler 并行独立抽取、reviewer 逐字段 diff、分歧时由 Condition 步触发 adjudicator;
  • 单一评判模型的打分:这些指标所压力测试的 single-judge 打分范式,见 cookbook/data_labeling/_17_llm_as_judge。

运行方式与前置条件

python cookbook/data_labeling/_19_inter_annotator_agreement/basic.py python cookbook/data_labeling/_19_inter_annotator_agreement/jury_votes.py

需要环境变量GOOGLE_API_KEY(两例均使用agno.models.google.Geminigemini-3.5-flash;TEST_LOG.md 记录于 2026-07-18 在 agno 2.7.4 下验证)。两个脚本的运行顺序一致:先执行self_check()打印self_check passed,再发起模型调用构建矩阵、打印指标(basic.py 用rich.pretty.pprint输出指标字典)、路由分歧 item,最后给出汇总行。若更换其他 Gemini 模型版本,temperature=0虽能消除采样噪声,但标签仍可能随模型更新或服务端非确定性发生漂移,这已在两个文件的模块 docstring 中明确标注。

小结:何时使用一致性指标

只要超过一个标注者——无论来自不同模型、不同指令框架还是不同人类——触及同一批 item,就需要用一致性指标回答"他们的一致中有多少是真正的信号"。本模块给出的最小可靠实践是:以 temperature=0 消除采样噪声、以多种指令框架制造可追踪的分歧、以纯标准库实现并先自检机会校正指标、在倾斜标签分布下同时报告 raw agreement 与 Krippendorff's alpha、对非一致 item 路由人工复审。这套流程可以直接复用到产品评论情感标注、DPO 偏好数据构建、指南改写审计等任何多标注者场景。

【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 23:15:38

context-mode 调优指南:让 AI 编程助手告别幻觉代码的上下文管理策略

说实话,我刚开始接触 context-mode 这个概念的时候,完全没把它当回事。那时候觉得,不就是编辑器里的一个上下文切换开关吗?能有多复杂。直到有一次,我在一个大型 monorepo 项目里写重构脚本,AI 编程助手连着…

作者头像 李华
网站建设 2026/9/11 23:14:54

基于Spark的网易云音乐数据分析:从数据清洗到图计算与机器学习实战

简介:一套面向Spark大数据分析毕业设计的网易云音乐数据分析实战资料,涵盖图计算、机器学习歌曲分类预测、评论词云与评论时间段统计等核心模块,适合计算机专业高年级学生、课程设计及毕业设计开发者,以及想快速上手Spark完整项目…

作者头像 李华
网站建设 2026/9/11 23:14:43

YOLOv8道路车流量检测系统:从目标检测到跨帧计数的完整实践

简介:面向交通管理、智慧城市及毕业设计场景的道路车流量检测系统,基于YOLOv8与Python实现,提供一套开箱即用的完整方案。系统可直接运行,适用于需要快速部署实时车辆识别与计数的研究人员、开发者及学生。压缩包共307个文件&…

作者头像 李华
网站建设 2026/9/11 23:14:38

基于Matlab的CNC刀具RUL预测:特征提取与神经网络实时回归实践

简介:面向CNC机床状态监测与剩余寿命(RUL)预测需求,这套基于Matlab的代码资源实现了刀具状态的实时监测与寿命预估功能。系统采用参数化编程,参数可灵活修改,代码注释明细,附赠可直接运行的案例…

作者头像 李华
网站建设 2026/9/11 23:13:58

Java图书管理系统实战:从Servlet/JSP到MySQL事务与部署

简介:基于JavaJSPMySQL实现的Web图书管理系统,定位于帮助Java Web初学者和高校学生理解B/S架构下的完整业务闭环,可作为课程设计、毕业设计或入门实战项目参考。资源压缩包为ZIP格式,体积约4.04MB,围绕图书查询、借阅、…

作者头像 李华