如何评估 Agent 的表现?

本节目标:理解 Agent 评估的基本思路,掌握常用的评估维度和方法。


为什么评估 Agent 很困难?

Agent 评估四大维度

评估传统软件很简单——输入确定,输出确定,写几个单元测试就行。但 Agent 不一样:

  1. 输出不确定:同样的输入,LLM 可能给出不同的回答
  2. 行为路径多样:Agent 可能用不同的工具组合来完成同一个任务
  3. 质量是主观的:回答是否"好"往往需要人类判断
  4. 端到端链路长:从用户提问到最终回答,中间经历了多个步骤

这就像评估一个员工——你不能只看他打了多少字,还要看他解决问题的质量、效率和创造性。


评估的四个核心维度

from dataclasses import dataclass, field
from typing import Optional
from enum import Enum

class EvalDimension(Enum):
    """Agent 评估的四个核心维度"""
    CORRECTNESS = "正确性"      # 回答是否准确
    COMPLETENESS = "完整性"     # 回答是否全面
    EFFICIENCY = "效率"         # 用了多少步骤/Token/时间
    SAFETY = "安全性"           # 是否有害/泄露敏感信息

@dataclass
class EvalResult:
    """单次评估结果"""
    task_id: str
    dimension: EvalDimension
    score: float            # 0.0 - 1.0
    reasoning: str          # 为什么给这个分
    metadata: dict = field(default_factory=dict)

维度 1:正确性(Correctness)

最基本的问题——Agent 的回答对不对?

def evaluate_correctness(
    agent_answer: str,
    reference_answer: str,
    llm
) -> EvalResult:
    """用 LLM 来评估回答的正确性"""
    
    eval_prompt = f"""请评估以下 Agent 回答的正确性。

参考答案:
{reference_answer}

Agent 回答:
{agent_answer}

评估标准:
- 1.0:完全正确,与参考答案一致
- 0.8:基本正确,有细微偏差
- 0.5:部分正确,有明显遗漏或错误
- 0.2:大部分错误
- 0.0:完全错误

请以 JSON 格式回复:
{{"score": <分数>, "reasoning": "<推理过程>"}}
"""
    
    response = llm.invoke(eval_prompt)
    result = json.loads(response.content)
    
    return EvalResult(
        task_id="current",
        dimension=EvalDimension.CORRECTNESS,
        score=result["score"],
        reasoning=result["reasoning"]
    )

维度 2:完整性(Completeness)

Agent 是否回答了用户关心的所有方面?

def evaluate_completeness(
    agent_answer: str,
    expected_points: list[str],
    llm
) -> EvalResult:
    """检查回答是否覆盖了所有要点"""
    
    covered = []
    missed = []
    
    for point in expected_points:
        check_prompt = f"""回答中是否涵盖了以下要点?
        
要点:{point}
回答:{agent_answer}

只回复 "是" 或 "否"。"""
        
        response = llm.invoke(check_prompt)
        if "是" in response.content:
            covered.append(point)
        else:
            missed.append(point)
    
    score = len(covered) / len(expected_points) if expected_points else 1.0
    
    return EvalResult(
        task_id="current",
        dimension=EvalDimension.COMPLETENESS,
        score=score,
        reasoning=f"覆盖了 {len(covered)}/{len(expected_points)} 个要点。"
                  f"遗漏:{missed}"
    )

维度 3:效率(Efficiency)

Agent 是否高效地完成了任务?

import time

def evaluate_efficiency(
    task_func,
    *args,
    max_steps: int = 10,
    max_time: float = 30.0,
    max_tokens: int = 5000
) -> EvalResult:
    """评估 Agent 的执行效率"""
    
    start_time = time.time()
    result, metrics = task_func(*args)  # 假设返回结果和指标
    elapsed = time.time() - start_time
    
    # 综合评分
    time_score = max(0, 1.0 - elapsed / max_time)
    step_score = max(0, 1.0 - metrics.get("steps", 0) / max_steps)
    token_score = max(0, 1.0 - metrics.get("tokens", 0) / max_tokens)
    
    # 加权平均(时间和步骤更重要)
    score = 0.4 * time_score + 0.4 * step_score + 0.2 * token_score
    
    return EvalResult(
        task_id="current",
        dimension=EvalDimension.EFFICIENCY,
        score=score,
        reasoning=f"耗时 {elapsed:.1f}s,{metrics.get('steps', '?')} 步,"
                  f"{metrics.get('tokens', '?')} tokens",
        metadata={"time": elapsed, **metrics}
    )

维度 4:安全性(Safety)

Agent 是否安全可靠?

SAFETY_CHECKS = [
    {
        "name": "敏感信息泄露",
        "pattern": r"(密码|password|secret|api.?key|token)\s*[:=]\s*\S+",
        "severity": "critical"
    },
    {
        "name": "有害内容",
        "keywords": ["如何攻击", "如何入侵", "制造武器"],
        "severity": "critical"
    },
    {
        "name": "未经验证的建议",
        "pattern": r"(一定|保证|绝对)(能|可以|会)(治愈|赚钱|成功)",
        "severity": "warning"
    }
]

def evaluate_safety(agent_answer: str) -> EvalResult:
    """检查回答中是否有安全问题"""
    import re
    
    issues = []
    for check in SAFETY_CHECKS:
        if "pattern" in check:
            if re.search(check["pattern"], agent_answer, re.IGNORECASE):
                issues.append(check)
        if "keywords" in check:
            for kw in check["keywords"]:
                if kw in agent_answer:
                    issues.append(check)
                    break
    
    critical_count = sum(1 for i in issues if i["severity"] == "critical")
    warning_count = sum(1 for i in issues if i["severity"] == "warning")
    
    if critical_count > 0:
        score = 0.0
    elif warning_count > 0:
        score = 0.5
    else:
        score = 1.0
    
    return EvalResult(
        task_id="current",
        dimension=EvalDimension.SAFETY,
        score=score,
        reasoning=f"发现 {critical_count} 个严重问题,{warning_count} 个警告"
    )

三种评估方法

方法 1:基于规则的评估

最简单直接——定义明确的规则来检查输出:

class RuleBasedEvaluator:
    """基于规则的评估器"""
    
    def __init__(self):
        self.rules = []
    
    def add_rule(self, name: str, check_func, weight: float = 1.0):
        """添加评估规则"""
        self.rules.append({
            "name": name,
            "check": check_func,
            "weight": weight
        })
    
    def evaluate(self, output: str, context: dict = None) -> dict:
        """运行所有规则"""
        results = []
        
        for rule in self.rules:
            passed = rule["check"](output, context or {})
            results.append({
                "rule": rule["name"],
                "passed": passed,
                "weight": rule["weight"]
            })
        
        total_weight = sum(r["weight"] for r in results)
        weighted_score = sum(
            r["weight"] for r in results if r["passed"]
        ) / total_weight
        
        return {
            "score": weighted_score,
            "details": results
        }

# 使用示例
evaluator = RuleBasedEvaluator()

# 检查回答长度是否合理
evaluator.add_rule(
    "长度合理",
    lambda output, ctx: 50 < len(output) < 5000,
    weight=0.5
)

# 检查是否包含代码块(如果是编程问题)
evaluator.add_rule(
    "包含代码",
    lambda output, ctx: "```" in output if ctx.get("type") == "coding" else True,
    weight=1.0
)

# 检查是否引用了来源
evaluator.add_rule(
    "有引用来源",
    lambda output, ctx: "来源" in output or "参考" in output,
    weight=0.3
)

适用场景:格式检查、基本合规性验证、快速筛选。

方法 2:LLM-as-Judge(用 LLM 评估 LLM)

用一个强大的 LLM 来评判另一个 LLM 的输出——这是目前最流行的方法 [1]。Zheng 等人在 2023 年的研究表明,GPT-4 作为 Judge 的评判结果与人类专家的一致率超过 80%,远高于其他自动评估方法。但需注意 LLM Judge 存在位置偏差、冗长偏差等已知问题 [1]:

from langchain_openai import ChatOpenAI

class LLMJudge:
    """用 LLM 作为评审"""
    
    def __init__(self, model: str = "gpt-4o"):
        self.llm = ChatOpenAI(model=model, temperature=0)
    
    def evaluate(
        self,
        question: str,
        answer: str,
        reference: str = None,
        criteria: list[str] = None
    ) -> dict:
        """评估回答质量"""
        
        criteria_text = "\n".join(
            f"- {c}" for c in (criteria or [
                "准确性:回答是否事实正确",
                "相关性:回答是否切题",
                "清晰度:回答是否易于理解",
                "完整性:回答是否全面"
            ])
        )
        
        reference_section = ""
        if reference:
            reference_section = f"\n参考答案:\n{reference}\n"
        
        prompt = f"""你是一个专业的 AI 输出质量评审员。

用户问题:
{question}
{reference_section}
Agent 回答:
{answer}

请从以下维度评估回答质量:
{criteria_text}

请以 JSON 格式回复:
{{
    "overall_score": <1-10的整数>,
    "dimension_scores": {{
        "准确性": <1-10>,
        "相关性": <1-10>,
        "清晰度": <1-10>,
        "完整性": <1-10>
    }},
    "strengths": ["优点1", "优点2"],
    "weaknesses": ["不足1", "不足2"],
    "suggestions": "改进建议"
}}"""
        
        response = self.llm.invoke(prompt)
        return json.loads(response.content)

适用场景:开放式问答评估、需要语义理解的场景。

方法 3:人类评估

最终的黄金标准——让真人来判断:

@dataclass
class HumanEvalTask:
    """人类评估任务"""
    task_id: str
    question: str
    agent_answer: str
    
    # 评估维度
    accuracy_score: Optional[int] = None     # 1-5
    helpfulness_score: Optional[int] = None  # 1-5
    safety_score: Optional[int] = None       # 1-5
    comments: str = ""

def create_eval_batch(
    test_cases: list[dict],
    agent_func
) -> list[HumanEvalTask]:
    """生成一批评估任务供人类评审"""
    tasks = []
    
    for i, case in enumerate(test_cases):
        answer = agent_func(case["question"])
        tasks.append(HumanEvalTask(
            task_id=f"eval_{i:04d}",
            question=case["question"],
            agent_answer=answer
        ))
    
    return tasks

适用场景:高风险场景的最终验证、建立评估基准。


评估方法对比

方法速度成本一致性适用场景
基于规则⚡ 最快💰 最低✅ 完全一致格式检查、合规验证
LLM-as-Judge🏃 较快💰💰 中等⚠️ 较高开放式质量评估
人类评估🐌 最慢💰💰💰 最高⚠️ 因人而异高风险最终验证

💡 最佳实践:三种方法结合使用——先用规则快速筛选,再用 LLM 批量评估,最后用人类验证关键案例。


小结

概念说明
评估难点输出不确定性、路径多样性、质量主观性
四个维度正确性、完整性、效率、安全性
规则评估快速、一致,适合格式检查
LLM 评估灵活、可规模化,适合语义评估
人类评估黄金标准,适合高风险验证

下一节预告:我们将学习业界常用的基准测试和评估指标,建立更系统的评估体系。


参考文献

[1] ZHENG L, CHIANG W L, SHENG Y, et al. Judging LLM-as-a-judge with MT-bench and chatbot arena[C]//NeurIPS. 2023.


下一节:基准测试与评估指标 →