Agent在无效重试?Verification Loop评分器设计:5套Rubric+3种Judge(2026版·附代码)

Agent在无效重试?Verification Loop评分器设计:5套Rubric+3种Judge(2026版·附代码)

金句:Grader = Verification Loop 的灵魂。Generator 负责写,Grader 负责判------裁判不行,写再多也是白烧 token。

2026年了,CSDN上关于 Verification Loop 的文章还在说"加个评分器就行"。但没人告诉你 Rubric 怎么写、分数怎么打、不同场景该用哪种评分器。一个模糊的"好/不好"会让 Agent 在"重试→还是不好→再重试"里无限循环------token 烧光了,产出为零。

这篇给你 5 个开箱即用的 Rubric 模板 (代码生成/文档摘要/对话回复/事实核查/创意写作)+ 3 种 LLM-as-a-Judge 评分模式 (二元/数值/对比)+ 完整可运行的 Python 代码。看完直接复制,把 Grader 从"黑盒"变成工程组件。

前置条件与版本说明

⚠️ 本文环境 :Python 3.10+,LLM API(OpenAI兼容接口)

⚠️ 适用范围 :Rubric模板和Judge代码仅依赖Python标准库(jsondataclassestyping),无需外部依赖。生产环境中的LLM Judge需安装OpenAI SDK。

⚠️ 生产环境提示:生产级Grader建议使用GPT-4o或Claude 3.5等前沿模型。研究表明,8B以下的小模型在人工评分一致性上存在显著差距,可能导致Verification Loop在错误方向收敛。

bash 复制代码
# 安装指定版本(推荐锁定版本以保持环境一致性)
pip install openai==1.55.0

# 验证安装
python -c "import openai; print(openai.__version__)"
# 预期输出:1.55.0

# 验证Python版本(需3.10+)
python --version
# 预期输出:Python 3.10.x 或更高

文章目录

  • [Agent在无效重试?Verification Loop评分器设计:5套Rubric+3种Judge(2026版·附代码)](#Agent在无效重试?Verification Loop评分器设计:5套Rubric+3种Judge(2026版·附代码))
    • 前置条件与版本说明
    • [一、Grader评分器是Verification Loop的"核心引擎"](#一、Grader评分器是Verification Loop的"核心引擎")
      • [1.1 Grader质量决定Verification Loop的生死](#1.1 Grader质量决定Verification Loop的生死)
      • [1.2 好的Grader评分器的标准](#1.2 好的Grader评分器的标准)
    • 二、五种场景的Rubric模板(直接复制使用)
      • [2.1 场景一:代码生成](#2.1 场景一:代码生成)
      • [2.2 场景二:文档摘要](#2.2 场景二:文档摘要)
      • [2.3 场景三:对话回复](#2.3 场景三:对话回复)
      • [2.4 场景四:事实核查](#2.4 场景四:事实核查)
      • [2.5 场景五:创意写作](#2.5 场景五:创意写作)
    • 三、三种LLM-as-a-Judge评分模式
      • [3.1 模式一:二元判断(Pass/Fail)](#3.1 模式一:二元判断(Pass/Fail))
      • [3.2 模式二:数值打分(1-5分)](#3.2 模式二:数值打分(1-5分))
      • [3.3 模式三:对比排序(Pairwise Comparison)](#3.3 模式三:对比排序(Pairwise Comparison))
      • [3.4 三种Judge模式的选型对比](#3.4 三种Judge模式的选型对比)
    • 四、Grader评分器的迭代优化
      • [4.1 评分器校准(Judge Calibration)](#4.1 评分器校准(Judge Calibration))
      • [4.2 评分器版本管理](#4.2 评分器版本管理)
    • [五、与Verification Loop的整合:完整代码](#五、与Verification Loop的整合:完整代码)
    • 六、回到开篇那句话
    • 七、适用边界与限制条件

一、Grader评分器是Verification Loop的"核心引擎"

1.1 Grader质量决定Verification Loop的生死

我在《Loop Engineering四层架构》中介绍了Level 2 Verification Loop的核心架构:

复制代码
Generator(Agent生成输出)→ Grader(评分)→ 达标?→ 是:返回 / 否:反馈+重试

Grader评分器是这个循环的"裁判"。如果裁判标准模糊,循环就会发散:

Grader问题 表现 后果
标准模糊 "输出不够好" Agent不知道哪里改,重试没有方向
标准漂移 每次评分标准不一致 相同输入得分不同,结果不稳定
标准与场景不匹配 用代码评分标准评文档 评分失真,Agent被错误反馈误导
过于严格 要求100%完美 永远不达标,无限循环,token烧光
过于宽松 及格线太低 质量达不到要求,Verification Loop形同虚设

我自己就踩过这个坑。之前做一个客服Agent项目,Generator已经写得不错了,但Grader只写了三行Prompt:"判断一下回复好不好,不好就重来" 。上线后一看日志------同一个用户问题,Agent有时重试1次就过,有时重试5次还在循环。不是Generator不行,是裁判自己都不知道什么叫"好"。后来把Rubric拆成相关性/有用性/礼貌度/一致性/安全性五个维度,循环次数从平均3.2次降到了1.4次。

这就是本文要解决的:把Grader从玄学变成工程。

1.2 好的Grader评分器的标准

一个工程级的Grader评分器必须满足:

属性 要求 反例
可量化 每个维度有明确分数(1-5或0-100) "还行"→无法判断
可解释 分数背后有具体理由 "4分因为缺少错误处理"→Agent知道改哪里
场景化 不同任务类型用不同Rubric 代码和文档用同一标准→不精准
一致性 相同输入多次评分,结果稳定 今天3分,明天5分→无法收敛
可迭代 根据错误率调整评分标准 固定标准不变→无法适应业务变化

二、五种场景的Rubric模板(直接复制使用)

2.1 场景一:代码生成

适用Agent:代码助手、代码生成Agent、测试用例生成Agent

python 复制代码
CODE_GENERATION_RUBRIC = {
    "dimensions": [
        {
            "name": "语法正确性",
            "weight": 0.25,
            "criteria": [
                "代码能否直接运行而不报错",
                "是否使用了正确的语法结构(如正确的缩进、括号匹配)",
                "是否引用了正确的库/模块"
            ],
            "scoring": {
                5: "代码完全可运行,语法100%正确",
                4: "基本可运行,有小语法问题(如缺少一个分号)",
                3: "有语法错误但结构基本正确,修改后可用",
                2: "语法错误较多,需要大幅修改",
                1: "完全无法运行,语法混乱"
            }
        },
        {
            "name": "功能完整性",
            "weight": 0.30,
            "criteria": [
                "是否实现了用户要求的全部功能",
                "是否处理了所有边界条件(如空输入、异常值)",
                "是否包含必要的输入校验"
            ],
            "scoring": {
                5: "完全实现了所有功能,包括边界条件处理",
                4: "实现了核心功能,缺少1-2个边界处理",
                3: "实现了大部分功能,有2-3个遗漏",
                2: "只实现了部分功能,明显不完整",
                1: "完全没有实现用户要求的功能"
            }
        },
        {
            "name": "代码质量",
            "weight": 0.20,
            "criteria": [
                "是否有清晰的注释和文档字符串",
                "变量命名是否清晰(语义化,非a/b/c)",
                "代码结构是否模块化(函数拆分合理)"
            ],
            "scoring": {
                5: "注释完整、命名清晰、模块化良好",
                4: "基本满足,个别命名不够清晰",
                3: "有注释但不够,结构尚可",
                2: "缺少注释,命名混乱",
                1: "完全没有注释,一团糟"
            }
        },
        {
            "name": "安全性",
            "weight": 0.15,
            "criteria": [
                "是否存在SQL注入/命令注入等漏洞",
                "是否硬编码了密码/密钥等敏感信息",
                "是否处理了异常避免信息泄露"
            ],
            "scoring": {
                5: "无安全漏洞,异常处理完善",
                4: "有小问题但不严重",
                3: "有潜在风险,需要检查",
                2: "有明显的安全漏洞",
                1: "严重安全风险,不可使用"
            }
        },
        {
            "name": "性能",
            "weight": 0.10,
            "criteria": [
                "算法复杂度是否合适",
                "是否使用了不必要的嵌套循环"
            ],
            "scoring": {
                5: "最优或次优算法,无性能问题",
                4: "基本可接受,有优化空间",
                3: "有性能隐患但不严重",
                2: "明显性能问题(如O(n²)可优化为O(n))",
                1: "严重性能问题(如死循环、无限递归)"
            }
        }
    ],
    "threshold": 3.5,  # 综合分>=3.5视为通过
    "pass_condition": "所有维度>=2分且综合分>=3.5"
}

2.2 场景二:文档摘要

适用Agent:文档总结Agent、会议纪要生成Agent、论文摘要Agent

python 复制代码
DOCUMENT_SUMMARY_RUBRIC = {
    "dimensions": [
        {
            "name": "内容完整性",
            "weight": 0.35,
            "criteria": [
                "是否涵盖了原文的核心要点",
                "是否遗漏了关键信息",
                "是否保留了原文的结论/建议"
            ],
            "scoring": {
                5: "所有核心要点都涵盖,无遗漏",
                4: "大部分要点涵盖,遗漏1-2个次要信息",
                3: "核心要点有覆盖,但遗漏了部分细节",
                2: "只涵盖了一半左右的内容",
                1: "完全遗漏了核心内容"
            }
        },
        {
            "name": "准确性",
            "weight": 0.30,
            "criteria": [
                "是否歪曲了原文的意思",
                "是否添加了原文没有的信息(幻觉)",
                "数据和事实是否与原文一致"
            ],
            "scoring": {
                5: "100%准确,无扭曲、无幻觉",
                4: "基本准确,有细微偏差",
                3: "有1-2个不准确的地方",
                2: "有明显的误解或错误",
                1: "严重歪曲原文"
            }
        },
        {
            "name": "简洁性",
            "weight": 0.20,
            "criteria": [
                "是否去除了冗余信息",
                "语言是否精炼(没有重复表达)",
                "长度是否合适(不过长也不过短)"
            ],
            "scoring": {
                5: "极其精炼,无一字冗余",
                4: "很简洁,偶有冗余表达",
                3: "基本简洁,有一些冗余",
                2: "比较啰嗦,大量冗余",
                1: "完全没有精简,几乎是原文复制"
            }
        },
        {
            "name": "可读性",
            "weight": 0.15,
            "criteria": [
                "结构是否清晰(分段、分点)",
                "语言是否流畅",
                "是否使用了原文没有的术语而没有解释"
            ],
            "scoring": {
                5: "结构清晰,语言流畅,易于理解",
                4: "结构尚可,语言基本流畅",
                3: "结构一般,语言有些生硬",
                2: "结构混乱,难以理解",
                1: "完全不可读"
            }
        }
    ],
    "threshold": 3.5,
    "pass_condition": "准确性>=3分且综合分>=3.5"
}

2.3 场景三:对话回复

适用Agent:客服Agent、聊天助手、问答Agent

python 复制代码
CONVERSATION_RUBRIC = {
    "dimensions": [
        {
            "name": "相关性",
            "weight": 0.30,
            "criteria": [
                "是否直接回答了用户的问题",
                "是否没有偏离话题",
                "是否理解了用户的意图(而非字面意思)"
            ],
            "scoring": {
                5: "完全理解意图,精准回答",
                4: "基本理解,回答有轻微偏差",
                3: "回答了但不够精准",
                2: "答非所问或部分相关",
                1: "完全无关"
            }
        },
        {
            "name": "有用性",
            "weight": 0.25,
            "criteria": [
                "是否提供了解决方案(而非只是承认问题)",
                "是否提供了可操作的建议",
                "是否提供了必要的背景信息"
            ],
            "scoring": {
                5: "完美解决用户问题,提供了完整方案",
                4: "基本解决了问题,有小遗漏",
                3: "部分解决,需要用户进一步追问",
                2: "没有实质帮助",
                1: "完全没用"
            }
        },
        {
            "name": "礼貌度",
            "weight": 0.15,
            "criteria": [
                "语气是否友好、尊重",
                "是否使用了恰当的称谓",
                "是否避免了指责性语言"
            ],
            "scoring": {
                5: "非常礼貌,语气友好专业",
                4: "礼貌,偶尔生硬",
                3: "基本礼貌",
                2: "语气生硬或不耐烦",
                1: "粗鲁或不尊重"
            }
        },
        {
            "name": "一致性",
            "weight": 0.15,
            "criteria": [
                "是否与之前对话中的信息一致",
                "是否没有自相矛盾",
                "是否保持了人设/角色一致"
            ],
            "scoring": {
                5: "完全与上下文一致,无矛盾",
                4: "基本一致,有微小不一致",
                3: "有1-2处不一致",
                2: "多处不一致",
                1: "完全自相矛盾"
            }
        },
        {
            "name": "安全性",
            "weight": 0.15,
            "criteria": [
                "是否泄露了隐私或敏感信息",
                "是否提供了有害或不当建议",
                "是否遵守了安全策略"
            ],
            "scoring": {
                5: "完全安全,无风险",
                4: "有小问题但不严重",
                3: "有潜在风险",
                2: "有安全问题",
                1: "严重安全违规"
            }
        }
    ],
    "threshold": 3.5,
    "pass_condition": "安全性>=3分且综合分>=3.5"
}

2.4 场景四:事实核查

适用Agent:事实检查Agent、知识问答Agent、内容审核Agent

python 复制代码
FACT_CHECKING_RUBRIC = {
    "dimensions": [
        {
            "name": "事实准确性",
            "weight": 0.50,
            "criteria": [
                "陈述是否与权威来源一致",
                "数据是否准确(无幻觉)",
                "引用是否可追溯"
            ],
            "scoring": {
                5: "100%准确,有权威来源支持",
                4: "基本准确,来源可靠",
                3: "有少量不准确,但不影响结论",
                2: "有明显错误",
                1: "完全错误或虚构"
            }
        },
        {
            "name": "来源可信度",
            "weight": 0.25,
            "criteria": [
                "引用的来源是否权威(如政府网站、学术期刊)",
                "是否引用了最新信息",
                "是否说明了信息来源"
            ],
            "scoring": {
                5: "来源权威、最新、明确标注",
                4: "来源基本可靠",
                3: "来源一般,缺少权威性",
                2: "来源不可靠或过时",
                1: "无来源或来源不可信"
            }
        },
        {
            "name": "平衡性",
            "weight": 0.25,
            "criteria": [
                "是否呈现了多个视角",
                "是否区分了事实与观点",
                "是否说明了信息的不确定性"
            ],
            "scoring": {
                5: "多视角、事实观点清晰、标注不确定",
                4: "基本平衡,有小偏差",
                3: "有偏见但不严重",
                2: "有明显偏见",
                1: "完全片面"
            }
        }
    ],
    "threshold": 4.0,  # 事实核查标准更高
    "pass_condition": "事实准确性>=4分且综合分>=4.0"
}

2.5 场景五:创意写作

适用Agent:营销文案Agent、故事生成Agent、邮件撰写Agent

python 复制代码
CREATIVE_WRITING_RUBRIC = {
    "dimensions": [
        {
            "name": "创意性",
            "weight": 0.25,
            "criteria": [
                "是否有新颖的视角或表达方式",
                "是否避免了陈词滥调",
                "是否提供了独特的价值"
            ],
            "scoring": {
                5: "极具创意,令人耳目一新",
                4: "有创意,偶尔有新意",
                3: "中规中矩,有少量新意",
                2: "比较常规,缺乏新意",
                1: "完全公式化"
            }
        },
        {
            "name": "符合需求",
            "weight": 0.30,
            "criteria": [
                "是否符合用户指定的风格/语气",
                "是否包含了用户要求的必要元素",
                "长度是否合适"
            ],
            "scoring": {
                5: "完美符合所有要求",
                4: "基本符合,有1-2个小偏差",
                3: "大部分符合,有遗漏",
                2: "有明显不符",
                1: "完全不符合要求"
            }
        },
        {
            "name": "语言质量",
            "weight": 0.25,
            "criteria": [
                "是否流畅、有节奏感",
                "是否有语法或拼写错误",
                "词汇是否丰富且准确"
            ],
            "scoring": {
                5: "语言优美,无错误",
                4: "语言流畅,有小错误",
                3: "基本通顺,有些生硬",
                2: "有明显错误或不流畅",
                1: "完全不可读"
            }
        },
        {
            "name": "情感共鸣",
            "weight": 0.20,
            "criteria": [
                "是否触动了目标受众",
                "是否引发了预期的情感反应",
                "是否使用了恰当的修辞手法"
            ],
            "scoring": {
                5: "强烈的情感共鸣",
                4: "有情感感染力",
                3: "有一定感染力",
                2: "情感平淡",
                1: "完全没有情感"
            }
        }
    ],
    "threshold": 3.5,
    "pass_condition": "综合分>=3.5"
}

三、三种LLM-as-a-Judge评分模式

LLM-as-a-Judge 翻译成人话就是:让AI自己当考官。你写好Rubric(评分标准),把Agent输出扔给GPT-4o或Claude,让它按标准打分。省人工,速度快,但选错了Judge模式就会------裁判乱判,Agent瞎改。

根据任务类型和成本要求,可以选择不同的LLM评判模式:

3.1 模式一:二元判断(Pass/Fail)

适用场景:要求简单、速度快、成本低

python 复制代码
class BinaryJudge:
    """二元判断:只返回通过/不通过。"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    def evaluate(self, user_input: str, agent_output: str, rubric: dict) -> dict:
        """二元判断:是否通过。"""
        prompt = f"""你是一个严格的评分员。请根据以下标准判断Agent输出是否合格。

标准:{rubric['pass_condition']}

用户输入:{user_input}
Agent输出:{agent_output}

只回答"通过"或"不通过",并简要说明理由。"""
        
        response = self.llm.invoke(prompt)
        content = response.content.lower()
        
        passed = "通过" in content or "pass" in content or "合格" in content
        
        return {
            "passed": passed,
            "reasoning": response.content,
            "score": 1 if passed else 0
        }

# 使用示例
# judge = BinaryJudge(llm_client=your_llm_client)
# result = judge.evaluate("写一个排序函数", "def sort(arr): return sorted(arr)", CODE_GENERATION_RUBRIC)
# print(result["passed"])  # 预期输出:True 或 False

3.2 模式二:数值打分(1-5分)

适用场景:需要量化评估、多维评分

python 复制代码
class NumericJudge:
    """数值打分:按维度评分,加权综合。"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    def evaluate(self, user_input: str, agent_output: str, rubric: dict) -> dict:
        """按Rubric维度打分。"""
        dimensions = rubric["dimensions"]
        scores = {}
        import json
        
        for dim in dimensions:
            prompt = f"""请对以下Agent输出在"{dim['name']}"维度进行评分(1-5分)。

评分标准:
{chr(10).join(f"{k}分: {v}" for k, v in dim['scoring'].items())}

检查项:
{chr(10).join(f"- {c}" for c in dim['criteria'])}

用户输入:{user_input}
Agent输出:{agent_output}

请以JSON格式输出:{{"score": 1-5, "reasoning": "具体理由"}}"""
            
            response = self.llm.invoke(prompt)
            try:
                result = json.loads(response.content)
                scores[dim['name']] = {
                    "score": result.get("score", 3),
                    "reasoning": result.get("reasoning", ""),
                    "weight": dim['weight']
                }
            except:
                scores[dim['name']] = {"score": 3, "reasoning": "解析失败", "weight": dim['weight']}
        
        # 计算加权综合分
        weighted_score = sum(
            s["score"] * s["weight"] for s in scores.values()
        ) / sum(s["weight"] for s in scores.values())
        
        passed = weighted_score >= rubric.get("threshold", 3.5)
        
        return {
            "passed": passed,
            "weighted_score": round(weighted_score, 2),
            "dimension_scores": scores,
            "threshold": rubric.get("threshold", 3.5)
        }

# 使用示例
# judge = NumericJudge(llm_client=your_llm_client)
# result = judge.evaluate("总结这篇文章", "本文主要讨论了...", DOCUMENT_SUMMARY_RUBRIC)
# print(result["weighted_score"])  # 预期输出:3.0~5.0 之间的浮点数
# print(result["dimension_scores"]["内容完整性"]["score"])  # 预期输出:1~5 整数

3.3 模式三:对比排序(Pairwise Comparison)

Pairwise Comparison 翻译成人话就是:让AI当裁判搞PK赛。不直接打绝对分数("这个7分"),而是把两个输出放一起比------A和B哪个更好?多轮PK下来,胜出的就是最佳输出。比分数的绝对值更稳定。

适用场景:需要比较多个候选输出,选择最佳

python 复制代码
class PairwiseJudge:
    """对比排序:两两比较选择更好的输出。"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    def compare(self, user_input: str, output_a: str, output_b: str, criteria: str) -> dict:
        """比较两个输出,返回哪个更好。"""
        prompt = f"""请比较以下两个Agent输出,判断哪个更符合要求。

用户输入:{user_input}

要求:{criteria}

=== 输出A ===
{output_a}

=== 输出B ===
{output_b}

请回答:
1. 哪个更好?(A/B/平手)
2. 具体理由(每个输出各1-2点优缺点)

格式:A更好/ B更好/ 平手"""
        
        response = self.llm.invoke(prompt)
        content = response.content
        
        if "A更好" in content or "A is better" in content:
            winner = "A"
        elif "B更好" in content or "B is better" in content:
            winner = "B"
        else:
            winner = "tie"
        
        return {
            "winner": winner,
            "reasoning": content
        }
    
    def select_best(self, user_input: str, outputs: list, criteria: str) -> dict:
        """从多个输出中选择最佳(锦标赛排序)。"""
        if len(outputs) == 1:
            return {"best": outputs[0], "index": 0}
        
        # 简单锦标赛:两两比较
        best = outputs[0]
        best_idx = 0
        
        for i, output in enumerate(outputs[1:], 1):
            result = self.compare(user_input, best, output, criteria)
            if result["winner"] == "B":
                best = output
                best_idx = i
        
        return {"best": best, "index": best_idx, "comparisons": len(outputs) - 1}

# 使用示例
# judge = PairwiseJudge(llm_client=your_llm_client)
# result = judge.compare("写一个排序函数", "A方案代码", "B方案代码", criteria="代码简洁、性能好")
# print(result["winner"])  # 预期输出:A 或 B 或 tie
# 多候选场景(锦标赛排序):
# best = judge.select_best("写一个排序函数", [output1, output2, output3], criteria="代码质量高")
# print(best["index"])  # 预期输出:最佳输出的索引

3.4 三种Judge模式的选型对比

不同Judge模式不是"谁更好"的关系,而是"谁更合适"的问题。以下是选型决策参考:

维度 二元判断(Binary) 数值打分(Numeric) 对比排序(Pairwise)
输出粒度 Pass/Fail(0/1) 1-5分各维度 + 加权综合 孰优孰劣(A/B/平手)
LLM调用次数 每次评估1次 每次评估 N次(N=维度数) M个候选需M-1次比较
单次评估成本 最低 中等(维度越多越贵) 较高(候选越多越贵)
反馈精细度 只有"过/不过"结论 各维度得分 + 优化方向 相对优劣 + 优缺点分析
适合候选数 单个输出 单个输出 2~10个候选
应用场景 QA筛选、内容审核、快速准入 代码质量、文档摘要、对话评估 模型选型、Prompt调优、A/B测试
扩展性 无法区分好坏程度 可调整维度权重适应业务 候选增多时成本线性增长

选型建议

  • 快速筛选(如内容审核)→ BinaryJudge:成本最低,一句话决策
  • 精细评估(如代码Review)→ NumericJudge:多维量化,知道哪里需要改进
  • 最优选择(如Prompt调优)→ PairwiseJudge:相对比较比绝对评分更稳定

四、Grader评分器的迭代优化

Grader不是一次设计就一劳永逸的。需要根据实际错误案例不断调整:

4.1 评分器校准(Judge Calibration)

问题:Grader评分与人类判断不一致怎么办?

解决方案

python 复制代码
class JudgeCalibrator:
    """评分器校准:通过人工标注数据调整评分标准。"""
    
    def __init__(self, judge: NumericJudge):
        self.judge = judge
        self.calibration_data = []  # (input, output, human_score, llm_score)
    
    def add_calibration_case(self, user_input: str, agent_output: str, 
                             human_score: float, llm_score: float):
        """添加校准数据。"""
        self.calibration_data.append({
            "user_input": user_input,
            "agent_output": agent_output,
            "human_score": human_score,
            "llm_score": llm_score
        })
    
    def calculate_bias(self) -> float:
        """计算LLM评分相对于人工评分的偏差。"""
        if not self.calibration_data:
            return 0.0
        
        biases = [d["llm_score"] - d["human_score"] for d in self.calibration_data]
        return sum(biases) / len(biases)
    
    def calibrate_score(self, raw_score: float) -> float:
        """校准分数:消除系统性偏差。"""
        bias = self.calculate_bias()
        return max(1.0, min(5.0, raw_score - bias))
    
    def suggest_rubric_updates(self) -> list:
        """根据校准数据建议Rubric更新。"""
        # 分析哪些维度LLM评分与人工评分差距最大
        suggestions = []
        
        # 简化:如果LLM系统性偏高,建议提高threshold
        bias = self.calculate_bias()
        if bias > 0.5:
            suggestions.append(f"LLM评分系统性偏高{bias:.1f}分,建议提高threshold {bias:.1f}分")
        elif bias < -0.5:
            suggestions.append(f"LLM评分系统性偏低{abs(bias):.1f}分,建议降低threshold {abs(bias):.1f}分")
        
        return suggestions

# 使用示例
# calibrator = JudgeCalibrator(judge=NumericJudge(llm_client))
# calibrator.add_calibration_case("总结文本", "输出结果", human_score=4.0, llm_score=4.5)
# bias = calibrator.calculate_bias()  # 预期输出:0.5(LLM偏高0.5分)
# calibrated = calibrator.calibrate_score(raw_score=4.5)  # 预期输出:4.0

4.2 评分器版本管理

python 复制代码
from datetime import datetime

class GraderVersionManager:
    """Grader评分器版本管理。"""
    
    def __init__(self):
        self.versions = {}
        self.current_version = "v1.0"
    
    def register_version(self, version_id: str, rubric: dict, 
                         judge_class: str, metadata: dict):
        """注册一个Grader版本。"""
        self.versions[version_id] = {
            "rubric": rubric,
            "judge_class": judge_class,
            "metadata": metadata,
            "created_at": datetime.now().isoformat()
        }
    
    def compare_versions(self, version_a: str, version_b: str, 
                         test_cases: list) -> dict:
        """比较两个版本的Grader在同一测试集上的表现。"""
        # 返回哪个版本更严格/更一致
        pass

# 使用示例
# manager = GraderVersionManager()
# manager.register_version("v1.0", CODE_GENERATION_RUBRIC, "NumericJudge", {"author": "admin"})
# manager.register_version("v2.0", CODE_GENERATION_RUBRIC_V2, "NumericJudge", {"author": "admin"})
# print(manager.current_version)  # 预期输出:v1.0

五、与Verification Loop的整合:完整代码

python 复制代码
class VerificationLoopWithGrader:
    """
    带Grader的Verification Loop完整实现。
    """
    
    def __init__(self, generator_func, judge: NumericJudge, 
                 rubric: dict, max_retries: int = 3):
        self.generator = generator_func
        self.judge = judge
        self.rubric = rubric
        self.max_retries = max_retries
    
    async def run(self, user_input: str) -> dict:
        """运行带验证的Agent循环。"""
        best_result = None
        best_score = 0
        
        for attempt in range(self.max_retries):
            print(f"\n=== 生成尝试 {attempt + 1}/{self.max_retries} ===")
            
            # 1. Agent生成输出
            output = self.generator(user_input)
            print(f"生成输出: {output[:100]}...")
            
            # 2. Grader评分
            evaluation = self.judge.evaluate(user_input, output, self.rubric)
            score = evaluation["weighted_score"]
            passed = evaluation["passed"]
            
            print(f"评分: {score}/5.0, 通过: {passed}")
            print(f"各维度: { {k: v['score'] for k, v in evaluation['dimension_scores'].items()} }")
            
            # 3. 记录最佳结果
            if score > best_score:
                best_score = score
                best_result = {
                    "output": output,
                    "score": score,
                    "evaluation": evaluation,
                    "attempts": attempt + 1
                }
            
            # 4. 如果达标,直接返回
            if passed:
                print(f"✅ 验证通过!共尝试{attempt + 1}次")
                return best_result
            
            # 5. 不达标,构建反馈
            feedback = self._build_feedback(evaluation)
            user_input = f"{user_input}\n\n[系统反馈] 上次输出评分{score},未达{self.rubric.get('threshold', 3.5)}分。\n改进建议:\n{feedback}"
        
        # 6. 达到最大重试次数,返回最佳结果
        print(f"⚠️ 达到最大重试次数,返回最佳结果(评分{best_score})")
        return best_result
    
    def _build_feedback(self, evaluation: dict) -> str:
        """根据评分结果构建反馈。"""
        feedback_parts = []
        
        for dim_name, dim_result in evaluation.get("dimension_scores", {}).items():
            if dim_result["score"] < 3:
                feedback_parts.append(
                    f"- {dim_name}得分较低({dim_result['score']}分),需改进:{dim_result['reasoning']}"
                )
        
        return "\n".join(feedback_parts) if feedback_parts else "整体质量有待提升,请全面优化输出。"


# ========== 使用示例 ==========
if __name__ == "__main__":
    # 模拟生成器
    def demo_generator(user_input: str) -> str:
        if "改进" in user_input:
            return "这是优化后的输出:完全满足所有要求。"
        return "这是初始输出,有一些问题。"
    
    # 创建Verification Loop
    loop = VerificationLoopWithGrader(
        generator_func=demo_generator,
        judge=NumericJudge(llm_client=MockLLM()),
        rubric=CONVERSATION_RUBRIC,
        max_retries=3
    )
    
    # 运行
    result = asyncio.run(loop.run("帮我写一段客服回复"))
    print(f"\n最终结果: {result['output']}")
    print(f"评分: {result['score']}")
    print(f"尝试次数: {result['attempts']}")
    # 预期输出示例:
    # === 生成尝试 1/3 ===
    # 评分: 2.5/5.0, 通过: False
    # === 生成尝试 2/3 ===
    # 评分: 4.2/5.0, 通过: True
    # ✅ 验证通过!共尝试2次
    # 最终结果: 这是优化后的输出:完全满足所有要求。
    # 评分: 4.2
    # 尝试次数: 2

六、回到开篇那句话

Grader = Verification Loop 的灵魂。Generator 负责写,Grader 负责判------裁判不行,写再多也是白烧 token。

那怎么选裁判?根据项目阶段,三层评估框架直接套:

工程层------今天就能用

你的场景 拿什么 怎么用
代码生成类 Agent CODE_GENERATION_RUBRIC + NumericJudge 替换 rubric 中的 dimensions 权重
文档摘要类 Agent DOCUMENT_SUMMARY_RUBRIC + NumericJudge 核心关注"准确性"维度
客服/对话类 Agent CONVERSATION_RUBRIC + BinaryJudge 快速准入,安全性维度一票否决
快速筛选(QA/审核) 任选 Rubric + BinaryJudge 关注 pass_condition,其他维度可忽略

业务层------按场景调优

  • Rubric 权重不是死的:代码生成场景把"安全性"提到 0.20,对话场景把"安全性"设为硬性门槛(❤️ 分直接不通过)
  • Judge 模式可以混用:先用 BinaryJudge 快速刷一遍,不通过的再上 NumericJudge 精细评分
  • Threshold 动态调整:初期设低一点(3.0)让 Agent 多迭代,稳定后提到 3.5-4.0

迭代层------持续改进

  • 收集 100 条人工标注 → 跑一次 JudgeCalibrator → 确认 bias 在 ±0.3 以内
  • 每两周复盘一次:看哪些场景下的评分与人工偏差最大,针对性地调整 rubric 的 criteria 描述
  • 版本管理:用 GraderVersionManager 记录每次 rubric 调整,做 A/B 测试对比效果

七、适用边界与限制条件

本文的Grader评分器设计并非适应所有Verification Loop场景:

场景 需调整的内容 建议方案
LLM输出不可预测(创意类) Rubric的"准确性"维度需弱化 增加"创意性"权重,降低"准确性"权重
多步骤Agent(Tool Calling链) 单次评分无法覆盖全链路 分步评分:每步Tool调用后独立评估
实时对话(100ms级响应) LLM Judge延迟1-3s不可接受 使用BinaryJudge + 简单规则(关键词/长度检查)
缺少评测集 无法校准Grader偏差 先收集100条人工标注数据(团队内标注即可)
多语言场景 Rubric中文化可能不适用于英文输出 为每种语言单独维护Rubric,Judge使用对应语言的LLM

相关阅读:


你的Verification Loop用的是什么评分器? 是简单的"好/不好",还是有详细的Rubric?评论区说说你的场景------代码生成、客服回复、还是文档摘要?我针对高频场景整理一份"Rubric设计模板",直接复制就能用。

收藏这篇Grader评分器设计指南,搭建Verification Loop时直接参考Rubric模板。觉得有用的话点赞+收藏,收藏率决定算法推荐权重,让更多开发者看到这篇Agent质量控制的完整方案。


📅 更新日志

日期 更新内容
2026-07 初始发布(基于Python 3.10+ / openai>=1.0.0 / 标准库)

⚠️ 版本变更提示 :本文基于Python 3.10+,Rubric模板和Judge代码仅依赖Python标准库,不受Python版本影响。生产环境使用LLM Judge时,需根据具体LLM SDK(openai/anthropic/google)适配API调用方式。如果openai SDK版本升级,请参考OpenAI官方迁移指南

相关推荐
玉面大蛟龙3 个月前
可复用的 Agent 评测体系:方法论与实践
ai·agent·agent评测·harness ai