Skip to content

第十章 评估与优化

在第九章中,我们完成了 Agent 的系统设计——从架构选型、工具编排到多 Agent 协作,一个"能跑起来"的 Agent 已经成型。但"能跑"和"跑得好"之间,隔着一整套评估与优化体系。

打个比方:第九章帮我们造了一辆车,车能发动、能转向。但要让这车上路载客,还得知道它百公里加速几秒、刹车距离多远、油耗多少、碰撞测试几星——这些就是"评估指标"。没有仪表盘的车,你敢开上高速吗?

本章将围绕"评估与优化"展开,覆盖六个核心环节:

  • 10.1 Agent 评估指标——定义"好"的标准,建立多维度评估体系
  • 10.2 Benchmark 与测试集——用标准化考试衡量 Agent 能力
  • 10.3 LLM-as-Judge——让 AI 当裁判,自动评估开放式输出
  • 10.4 评估自动化——把评估嵌入 CI/CD,让质量把控变成流水线
  • 10.5 反馈闭环——从用户反馈中学习,实现"越用越好"
  • 10.6 可观测性——Trace 追踪、成本归因、监控告警,让 Agent 运行透明可控

10.1 Agent 评估指标

为什么 Agent 评估需要多维度?

传统 LLM 评估通常只关注模型在标准测试集上的准确率。就像体检只量体温——确实能发现发烧,但查不出血压、血糖、心率的问题。

Agent 是一个复杂系统,它不只是"生成答案"——它需要理解任务、规划步骤、调用工具、整合信息、最终交付结果。任何一环出问题,都可能导致任务失败。这就好比你请了个助手帮你订机票,他不仅要听懂你的需求(任务理解),还得知道去哪查航班(工具选择),查到的信息对不对(准确率),整个过程快不快(响应时间),最后还得帮你确认下单(任务完成)。

┌─────────────────────────────────────────────────────────────────┐
│                    Agent 执行链路                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户输入 ──► 任务理解 ──► 步骤规划 ──► 工具调用 ──► 信息整合      │
│                 │            │            │            │        │
│              语义理解      规划质量      调用准确率    生成质量     │
│                 │            │            │            │        │
│                 └────────────┴────────────┴────────────┘        │
│                                   │                             │
│                              最终输出 ──► 任务完成率               │
│                                                                 │
│  测量维度:响应时间(全链路)  |  成本(Token 消耗)               │
└─────────────────────────────────────────────────────────────────┘

因此,我们需要建立一个多维度的评估体系,从不同角度衡量 Agent 的表现——就像体检报告不会只有一个指标,而是包含血常规、肝功能、心电图等多个维度的综合判断。

核心指标详解

1. 任务完成率(Task Completion Rate)

定义:Agent 成功完成用户指定任务的比例。这是最顶层的指标,直接反映 Agent 的实用性——就像快递公司的"妥投率",包裹最终送到用户手里才算完成。

计算公式

任务完成率 = 成功完成的任务数 / 总任务数 × 100%

评判标准:任务完成与否通常需要人工判定或通过 LLM-as-Judge 判定。一个任务可能部分完成,可以引入完成度评分(如 0-5 分)来细化。好比快递送到门口但没上楼——算是"部分完成",完成度可以给 3 分。

实际案例

  • 某客服 Agent 处理 1000 个用户咨询,其中 850 个完全解决用户问题,任务完成率 = 85%
  • 某代码 Agent 接收 50 个 PR review 任务,其中 42 个生成的代码无需修改即可合入,任务完成率 = 84%
2. 准确率(Accuracy)

定义:Agent 输出中正确内容的比例。对于不同类型的 Agent,准确率的定义有所不同:

Agent 类型准确率定义示例
问答 Agent答案与标准答案的匹配度事实正确性
代码 Agent代码通过测试用例的比例pass@k
分析 Agent分析结论与真实数据的吻合度数值误差
对话 Agent回复的相关性和正确性语义匹配

细分指标

  • 精确率(Precision):Agent 返回的相关结果中有多少是真正相关的——好比钓鱼网里有多少是目标鱼,多少是杂物
  • 召回率(Recall):所有相关结果中 Agent 找到了多少——好比池塘里的目标鱼,你一网下去捞到了几条
  • F1 分数:精确率和召回率的调和平均——两者兼顾的综合分
3. 响应时间(Response Time)

定义:从用户发出请求到 Agent 返回最终结果的时间。就像你去餐厅吃饭,从点单到上菜的总时间。

关键维度

  • 首字延迟(TTFT, Time To First Token):用户感知的"响应速度"——服务员多快过来问你点什么
  • 端到端延迟(E2E Latency):完整任务的总耗时——从点单到吃完走人
  • P50/P95/P99 延迟:不同百分位的延迟分布,比平均值更有意义——平均等待 10 分钟不代表没人等 30 分钟

生活类比:如果一家餐厅说"平均上菜 10 分钟",但 P99 是 45 分钟,意味着每 100 个客人就有 1 个等了快一小时。看百分位数才能发现"长尾问题"。

影响因素

  • LLM 推理速度(模型大小、量化方式)
  • 工具调用次数(每次工具调用都需要额外的 LLM 推理)
  • 网络延迟(API 调用、外部服务响应)
  • 并发处理能力

实际案例

某电商 Agent 的响应时间分析:
- P50: 2.3s(一半用户 2.3 秒内得到回复)
- P95: 8.7s(95% 用户 8.7 秒内得到回复)
- P99: 15.2s(1% 用户需要超过 15.2 秒)
-> 优化目标:将 P95 降低到 5s 以内
4. 工具调用准确率(Tool Call Accuracy)

定义:Agent 正确选择和调用工具的能力。这是 Agent 区别于普通 LLM 的核心能力——就像木匠会不会用对工具:该用锤子时不能拿扳手凑合。

评估维度

维度说明示例错误
工具选择准确率是否选择了正确的工具应该用"计算器"却用了"搜索"
参数填充准确率参数名和值是否正确参数名拼写错误、类型不匹配
调用时机准确率是否在正确的时机调用过早或过晚调用工具
结果利用率是否正确使用工具返回结果忽略了工具返回的关键信息

计算公式

工具调用准确率 = 正确的工具调用次数 / 总工具调用次数 × 100%

实际案例

用户:"帮我查一下今天北京天气,然后发邮件给张三"

正确行为:
  1. get_weather(city="北京", date="2026-07-21") ✓ 工具选择正确
  2. send_email(to="zhangsan@example.com", content="...") ✓ 参数完整

错误行为:
  1. search("北京天气") ✗ 应该用天气 API 而非搜索
  2. send_email(to="张三") ✗ 缺少邮箱地址,参数错误
5. RAG 召回率(RAG Recall)

定义:在 RAG(检索增强生成)场景中,检索系统找到的相关文档占所有相关文档的比例。就像你去图书馆找"量子力学"的书——书架上有 10 本相关的,你找到几本?

┌──────────────────────────────────────────────────┐
│              RAG 评估指标体系                       │
├──────────────────────────────────────────────────┤
│                                                  │
│  检索阶段:                                       │
│  ├─ Recall@K:前 K 个结果中包含正确答案的比例        │
│  ├─ Precision@K:前 K 个结果中相关文档的比例         │
│  ├─ MRR(Mean Reciprocal Rank):第一个相关文档    │
│  │   排名的倒数平均值                              │
│  └─ NDCG(Normalized DCG):考虑排序位置的         │
│      相关性评分                                   │
│                                                  │
│  生成阶段:                                       │
│  ├─ 忠实度(Faithfulness):生成内容是否忠于        │
│  │   检索到的文档                                 │
│  ├─ 答案相关性(Answer Relevance):答案是否        │
│  │   回答了用户问题                               │
│  └─ 上下文相关性(Context Relevance):检索到的      │
│      文档是否与问题相关                            │
│                                                  │
└──────────────────────────────────────────────────┘

RAG 召回率计算公式

Recall@K = |检索到的相关文档 ∩ 全部相关文档| / |全部相关文档|

实际案例

知识库中有 100 篇文档,其中 5 篇与用户问题相关。
Agent 检索了 Top-10 文档,其中包含了 3 篇相关文档。

Recall@10 = 3/5 = 60%
Precision@10 = 3/10 = 30%

生活类比:去超市买 5 种食材做晚饭,购物清单上列了 10 样,最后发现只买到了 3 种需要的——召回率 60%,但购物效率(精确率)只有 30%,因为你买了 7 样不需要的。

综合评估框架设计

一个成熟的 Agent 评估框架应该将以上指标有机整合。就像汽车的中控台——速度表、油量表、发动机温度、里程数,每个指标各司其职,组合起来才能全面反映车辆状态。

下面是一个完整的评估框架实现,代码逐行注释:

python
from dataclasses import dataclass, field  # dataclass 用于定义数据结构,field 提供默认值
from typing import Dict, List, Optional    # 类型注解,提高代码可读性
from enum import Enum                      # 枚举类型,用于任务状态
import time                                # 用于测量响应时间
import json                                # 用于序列化评估结果

class TaskStatus(Enum):
    """任务状态枚举——成功、部分成功、失败三态"""
    SUCCESS = "success"   # 完全完成用户任务
    PARTIAL = "partial"   # 部分完成,如查了天气但没发邮件
    FAILED = "failed"     # 任务失败,如工具调用错误导致无法完成

@dataclass
class ToolCallRecord:
    """单次工具调用记录——记录 Agent 每次调用工具的详细信息"""
    tool_name: str                          # 工具名称,如 "get_weather"
    params: Dict                            # 调用参数,如 {"city": "北京"}
    result: Optional[str]                   # 工具返回结果
    is_correct: bool                       # 是否正确调用(人工标注或自动判定)
    latency_ms: float                       # 本次调用耗时(毫秒)
    error: Optional[str] = None             # 错误信息(如果有)

@dataclass
class AgentEvalResult:
    """Agent 评估结果——一个任务的所有评估数据汇总"""
    task_id: str                            # 任务唯一标识
    task_description: str                   # 任务描述(给 Agent 的输入)
    expected_output: str                    # 期望输出(标准答案)
    actual_output: str                      # 实际输出
    status: TaskStatus                      # 任务状态
    completion_score: float                 # 完成度评分 0.0-1.0

    # 时间指标
    ttft_ms: float                          # 首字延迟(Time To First Token)
    total_latency_ms: float                 # 端到端延迟
    tool_calls: List[ToolCallRecord] = field(default_factory=list)  # 工具调用记录列表

    # 工具调用指标
    tool_selection_accuracy: float = 0.0   # 工具选择准确率
    tool_param_accuracy: float = 0.0       # 参数填充准确率

    # RAG 指标(如果适用)
    rag_recall: Optional[float] = None     # 检索召回率
    rag_precision: Optional[float] = None # 检索精确率

    # 成本指标
    total_tokens: int = 0                 # 总 Token 消耗
    total_cost_usd: float = 0.0           # 总花费(美元)

class AgentEvaluator:
    """Agent 综合评估器——整合所有指标的评估引擎"""

    def __init__(self, judge_model: str = "gpt-4o"):
        """
        初始化评估器
        
        Args:
            judge_model: 用于判断任务完成度的 LLM 模型
        """
        self.judge_model = judge_model                    # 保存 Judge 模型名称
        self.results: List[AgentEvalResult] = []         # 存储所有评估结果

    def evaluate_task(
        self,
        task_id: str,
        task_description: str,
        expected_output: str,
        agent_fn,  # Agent 执行函数
    ) -> AgentEvalResult:
        """
        评估单个任务——核心评估方法
        
        Args:
            task_id: 任务 ID
            task_description: 任务描述
            expected_output: 期望输出
            agent_fn: Agent 执行函数,接受任务描述返回 (输出, 工具调用列表)
        
        Returns:
            AgentEvalResult: 完整的评估结果
        """
        start_time = time.time()           # 记录开始时间
        ttft_recorded = False              # 是否已记录首字延迟
        ttft = 0.0                         # 首字延迟(毫秒)

        # 执行 Agent 任务(实际使用时需要根据 Agent 框架调整)
        actual_output, tool_calls = agent_fn(task_description)

        total_latency = (time.time() - start_time) * 1000  # 计算端到端延迟(毫秒)

        # 计算工具调用准确率
        tool_sel_acc = self._calc_tool_selection_accuracy(tool_calls)
        tool_param_acc = self._calc_tool_param_accuracy(tool_calls)

        # 评估任务完成度(使用 LLM-as-Judge 或规则匹配)
        completion_score = self._judge_completion(
            task_description, expected_output, actual_output
        )

        # 根据完成度评分判断任务状态
        if completion_score >= 0.8:         # 80% 以上算成功
            status = TaskStatus.SUCCESS
        elif completion_score >= 0.4:       # 40%-80% 算部分完成
            status = TaskStatus.PARTIAL
        else:                               # 低于 40% 算失败
            status = TaskStatus.FAILED

        # 构建评估结果对象
        result = AgentEvalResult(
            task_id=task_id,
            task_description=task_description,
            expected_output=expected_output,
            actual_output=actual_output,
            status=status,
            completion_score=completion_score,
            ttft_ms=ttft,
            total_latency_ms=total_latency,
            tool_calls=tool_calls,
            tool_selection_accuracy=tool_sel_acc,
            tool_param_accuracy=tool_param_acc,
        )
        self.results.append(result)        # 保存到结果列表
        return result

    def _calc_tool_selection_accuracy(
        self, tool_calls: List[ToolCallRecord]
    ) -> float:
        """计算工具选择准确率——正确的工具调用占总调用的比例"""
        if not tool_calls:                 # 没有工具调用时默认为 1.0
            return 1.0
        correct = sum(1 for tc in tool_calls if tc.is_correct)  # 统计正确调用数
        return correct / len(tool_calls)   # 返回准确率

    def _calc_tool_param_accuracy(
        self, tool_calls: List[ToolCallRecord]
    ) -> float:
        """计算参数填充准确率——无错误的调用占比"""
        if not tool_calls:
            return 1.0
        # 简化的参数准确率计算,实际应检查参数名和值的正确性
        correct = sum(
            1 for tc in tool_calls if tc.error is None  # 无错误则认为参数正确
        )
        return correct / len(tool_calls)

    def _judge_completion(
        self, task: str, expected: str, actual: str
    ) -> float:
        """
        使用 LLM 判断任务完成度(简化版)
        
        实际生产环境应调用 LLM API 进行语义级别的判断。
        这里使用文本相似度作为演示。
        """
        from difflib import SequenceMatcher  # Python 标准库的文本比较工具
        return SequenceMatcher(None, expected, actual).ratio()  # 返回 0.0-1.0 的相似度

    def generate_report(self) -> Dict:
        """生成综合评估报告——汇总所有评估结果的统计信息"""
        if not self.results:
            return {"error": "No results to report"}  # 无结果时返回错误

        total = len(self.results)            # 总任务数
        success = sum(1 for r in self.results if r.status == TaskStatus.SUCCESS)
        partial = sum(1 for r in self.results if r.status == TaskStatus.PARTIAL)
        failed = sum(1 for r in self.results if r.status == TaskStatus.FAILED)

        # 延迟统计——按升序排列后取分位数
        latencies = [r.total_latency_ms for r in self.results]
        latencies.sort()

        # 工具调用准确率统计
        tool_accs = [
            r.tool_selection_accuracy for r in self.results if r.tool_calls
        ]

        return {
            "summary": {                    # 总览
                "total_tasks": total,
                "task_completion_rate": f"{success/total*100:.1f}%",
                "success": success,
                "partial": partial,
                "failed": failed,
                "avg_completion_score": sum(
                    r.completion_score for r in self.results
                ) / total,
            },
            "latency": {                    # 延迟分布
                "avg_ms": sum(latencies) / len(latencies),
                "p50_ms": latencies[len(latencies)//2],       # 中位数
                "p95_ms": latencies[int(len(latencies)*0.95)], # 95 分位
                "p99_ms": latencies[int(len(latencies)*0.99)],  # 99 分位
            },
            "tool_calls": {                 # 工具调用统计
                "avg_selection_accuracy": sum(tool_accs)/len(tool_accs) if tool_accs else 1.0,
                "total_tool_calls": sum(len(r.tool_calls) for r in self.results),
            },
            "cost": {                       # 成本统计
                "total_tokens": sum(r.total_tokens for r in self.results),
                "total_cost_usd": sum(r.total_cost_usd for r in self.results),
            },
        }

实战演练:简易 Agent 评估器

下面通过一个天气助手 Agent 的模拟数据,演示评估器的实际使用:

python
import json
from typing import List, Dict

# 模拟的 Agent 执行日志——包含 5 个测试任务
simulated_logs = [
    {
        "task": "查询北京今天的天气",
        "expected": "北京今天晴天,温度 25°C",
        "actual": "北京今天晴天,温度 25°C",       # 完全正确
        "tool_calls": [
            {"tool": "get_weather", "params": {"city": "北京"}, "correct": True}
        ],
        "latency_ms": 1200,
    },
    {
        "task": "查询上海未来三天的天气",
        "expected": "上海明天多云,后天小雨,大后天晴天",
        "actual": "上海明天多云,后天晴天",           # 缺少一天,部分完成
        "tool_calls": [
            {"tool": "get_forecast", "params": {"city": "上海", "days": 3}, "correct": True}
        ],
        "latency_ms": 1800,
    },
    {
        "task": "比较北京和上海的天气",
        "expected": "北京晴天25°C,上海多云22°C",
        "actual": "北京晴天25°C",                   # 只返回了北京,遗漏上海
        "tool_calls": [
            {"tool": "get_weather", "params": {"city": "北京"}, "correct": True},
            # 缺少上海的工具调用——工具调用不完整
        ],
        "latency_ms": 1500,
    },
    {
        "task": "查询广州的空气质量",
        "expected": "广州空气质量指数 50,优",
        "actual": "抱歉,我不支持查询空气质量",         # 任务失败
        "tool_calls": [
            {"tool": "search", "params": {"query": "广州空气质量"}, "correct": False}
        ],
        "latency_ms": 900,
    },
    {
        "task": "查询深圳今天天气并提醒带伞",
        "expected": "深圳今天有雨,温度 20°C,建议带伞",
        "actual": "深圳今天有雨,温度 20°C,建议带伞", # 完全正确
        "tool_calls": [
            {"tool": "get_weather", "params": {"city": "深圳"}, "correct": True}
        ],
        "latency_ms": 1100,
    },
]

def evaluate_task_completion(logs: List[Dict]) -> Dict:
    """评估任务完成率——统计成功、部分完成、失败的数量"""
    total = len(logs)                       # 总任务数
    completed = 0                           # 完全完成计数
    partial = 0                             # 部分完成计数
    failed = 0                              # 失败计数

    for log in logs:
        expected = log["expected"]          # 期望输出
        actual = log["actual"]              # 实际输出

        # 使用文本相似度判断完成度(生产环境建议使用 LLM-as-Judge)
        from difflib import SequenceMatcher
        similarity = SequenceMatcher(None, expected, actual).ratio()

        if similarity >= 0.8:               # 相似度 80% 以上算完成
            completed += 1
        elif similarity >= 0.4:             # 40%-80% 算部分完成
            partial += 1
        else:                               # 低于 40% 算失败
            failed += 1

    return {
        "total": total,
        "completed": completed,
        "partial": partial,
        "failed": failed,
        "completion_rate": f"{completed/total*100:.1f}%",  # 完成率百分比
    }

def evaluate_tool_accuracy(logs: List[Dict]) -> Dict:
    """评估工具调用准确率——按工具类型统计正确率"""
    total_calls = 0                         # 总调用次数
    correct_calls = 0                       # 正确调用次数
    tool_stats = {}                         # 每个工具的统计

    for log in logs:
        for call in log["tool_calls"]:
            total_calls += 1                # 累加总调用次数
            tool_name = call["tool"]        # 获取工具名称

            # 初始化工具统计
            if tool_name not in tool_stats:
                tool_stats[tool_name] = {"total": 0, "correct": 0}

            tool_stats[tool_name]["total"] += 1      # 该工具调用次数 +1
            if call["correct"]:
                correct_calls += 1                   # 正确调用 +1
                tool_stats[tool_name]["correct"] += 1

    return {
        "total_tool_calls": total_calls,
        "correct_calls": correct_calls,
        "tool_accuracy": f"{correct_calls/total_calls*100:.1f}%" if total_calls else "N/A",
        "per_tool_stats": {
            tool: {"accuracy": f"{stats['correct']/stats['total']*100:.1f}%"}
            for tool, stats in tool_stats.items()
        }
    }

def analyze_latency(logs: List[Dict]) -> Dict:
    """分析响应时间——计算 P50/P95 等分位数"""
    latencies = sorted([log["latency_ms"] for log in logs])  # 排序后的延迟列表
    n = len(latencies)

    return {
        "avg_ms": sum(latencies) / n,      # 平均延迟
        "min_ms": latencies[0],             # 最小延迟
        "max_ms": latencies[-1],            # 最大延迟
        "p50_ms": latencies[n // 2],       # 中位数延迟
        "p95_ms": latencies[int(n * 0.95)] if n > 1 else latencies[-1],  # P95
    }

# 执行评估并打印报告
print("=" * 60)
print("Agent 评估报告")
print("=" * 60)

# 1. 任务完成率
completion = evaluate_task_completion(simulated_logs)
print(f"\n📊 任务完成率:")
print(f"   总任务数: {completion['total']}")
print(f"   完全完成: {completion['completed']}")
print(f"   部分完成: {completion['partial']}")
print(f"   失败:     {completion['failed']}")
print(f"   完成率:   {completion['completion_rate']}")

# 2. 工具调用准确率
tool_acc = evaluate_tool_accuracy(simulated_logs)
print(f"\n🔧 工具调用准确率:")
print(f"   总调用次数: {tool_acc['total_tool_calls']}")
print(f"   正确调用:   {tool_acc['correct_calls']}")
print(f"   准确率:     {tool_acc['tool_accuracy']}")
for tool, stats in tool_acc['per_tool_stats'].items():
    print(f"   - {tool}: {stats['accuracy']}")

# 3. 响应时间
latency = analyze_latency(simulated_logs)
print(f"\n⏱️  响应时间分析:")
print(f"   平均延迟: {latency['avg_ms']:.0f}ms")
print(f"   P50:      {latency['p50_ms']:.0f}ms")
print(f"   P95:      {latency['p95_ms']:.0f}ms")
print(f"   最小/最大: {latency['min_ms']:.0f}ms / {latency['max_ms']:.0f}ms")

RAG 召回率评估实战

对于具备 RAG 能力的 Agent,检索质量直接影响最终回答质量:

python
def evaluate_rag_retrieval(
    retrieved_docs: List[str],    # 检索系统返回的文档列表(按相关性排序)
    relevant_docs: List[str],      # 标注的相关文档列表
    k: int = None                 # 评估前 K 个结果,默认全部
) -> Dict:
    """
    评估 RAG 检索质量
    
    Args:
        retrieved_docs: 检索系统返回的文档列表(按相关性排序)
        relevant_docs: 标注的相关文档列表(Ground Truth)
        k: 评估前 K 个结果
    
    Returns:
        包含 Recall@K、Precision@K、F1@K、MRR 的字典
    """
    if k is None:
        k = len(retrieved_docs)           # 默认评估全部结果

    top_k = retrieved_docs[:k]            # 取前 K 个结果
    relevant_set = set(relevant_docs)     # 转为集合便于交集运算
    retrieved_set = set(top_k)

    # Recall@K:在所有相关文档中,检索到了多少
    true_positives = retrieved_set & relevant_set    # 交集 = 正确检索到的
    recall = len(true_positives) / len(relevant_set) if relevant_set else 0.0

    # Precision@K:检索到的前 K 个中有多少是相关的
    precision = len(true_positives) / k if k > 0 else 0.0

    # F1@K:精确率和召回率的调和平均
    f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0.0

    # MRR(Mean Reciprocal Rank):第一个相关文档排名的倒数
    for i, doc in enumerate(retrieved_docs):
        if doc in relevant_set:            # 找到第一个相关文档
            mrr = 1.0 / (i + 1)            # 排名倒数(第 1 名 = 1.0,第 2 名 = 0.5)
            break
    else:
        mrr = 0.0                          # 没找到任何相关文档

    return {
        f"recall@{k}": recall,
        f"precision@{k}": precision,
        f"f1@{k}": f1,
        "mrr": mrr,
    }

# 模拟场景:知识库中检索"Python 异步编程"
retrieved = ["doc_03", "doc_01", "doc_07", "doc_05", "doc_12",
             "doc_08", "doc_15", "doc_02", "doc_09", "doc_04"]
relevant = ["doc_03", "doc_07", "doc_12", "doc_15"]

# 评估不同 K 值下的检索质量
print("RAG 检索评估结果")
print("=" * 50)
for k in [3, 5, 10]:                       # 分别评估 Top-3、Top-5、Top-10
    result = evaluate_rag_retrieval(retrieved, relevant, k=k)
    print(f"\nK = {k}:")
    print(f"  Recall@{k}:    {result[f'recall@{k}']:.2%}")
    print(f"  Precision@{k}: {result[f'precision@{k}']:.2%}")
    print(f"  F1@{k}:        {result[f'f1@{k}']:.2%}")
    print(f"  MRR:           {result['mrr']:.2%}")

常见误区

误区一:只看任务完成率,忽略过程指标

任务完成率是结果指标,但它无法告诉你"为什么失败"。一个 Agent 任务完成率 70%,可能是因为工具选择错误(应查 API 却去搜索),也可能是响应太慢导致超时,还可能是 RAG 没检索到关键信息。只有配合工具调用准确率、响应时间、RAG 召回率等过程指标,才能定位真正的瓶颈。

类比:只看考试成绩不看错题分析,下次还会犯同样的错。

误区二:用平均值代替分位数评估延迟

平均延迟 3 秒看起来还行,但如果 P99 是 30 秒,意味着每 100 个用户就有 1 个等了半分钟。长尾用户的体验极差,但被平均值"稀释"了。生产环境必须关注 P95 和 P99。

误区三:RAG 只评估检索,忽略生成质量

检索到了相关文档(Recall 高),不代表最终回答就好——Agent 可能"视而不见",没利用检索到的信息。必须同时评估忠实度(Faithfulness):生成内容是否真正基于检索到的文档,而不是"自由发挥"。

误区四:评估指标一刀切,不区分场景

不同业务场景对指标的要求差异很大。实时客服 Agent 最看重响应时间和完成率,代码 Agent 最看重准确率和工具调用正确率,分析 Agent 最看重数据准确性。不要照搬通用模板,要根据业务需求定制指标权重。

本节小结

本节建立了 Agent 评估的多维度指标体系,核心要点如下:

指标核心问题关键公式优化方向
任务完成率Agent 能否完成用户任务?成功数/总数提升规划与执行能力
准确率输出内容是否正确?正确数/总数优化 Prompt、增加验证
响应时间用户等多久?P50/P95/P99模型量化、缓存、并行化
工具调用准确率工具用得对不对?正确调用/总调用优化 Function Calling
RAG 召回率相关文档找到了吗?检索到的相关/全部相关优化 Embedding、混合检索

关键原则

  • 没有单一指标能全面衡量 Agent 质量,必须建立多维度评估体系
  • 任务完成率是"北极星指标",但需要配合过程指标来定位问题
  • 响应时间的分位数(P95/P99)比平均值更有参考价值
  • RAG 评估需要同时关注检索质量(Recall/Precision)和生成质量(Faithfulness)

有了评估指标,下一步需要一套标准化的"考题"来衡量这些指标——这正是 10.2 节 Benchmark 与测试集要解决的问题。