第十章 评估与优化
在第九章中,我们完成了 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 评估框架应该将以上指标有机整合。就像汽车的中控台——速度表、油量表、发动机温度、里程数,每个指标各司其职,组合起来才能全面反映车辆状态。
下面是一个完整的评估框架实现,代码逐行注释:
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 的模拟数据,演示评估器的实际使用:
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,检索质量直接影响最终回答质量:
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 与测试集要解决的问题。