Agent 核心评测指标:首字延迟、完成时间与任务成功率
·
Agent 核心评测指标:首字延迟、完成时间与任务成功率
一、"Agent 的效果变好了"——这句话没有量化就毫无意义
Agent 迭代中最危险的信号是主观评价。改了一版 Prompt,开发说"感觉好了",PM 说"好像还行",然后就上线了。评测不量化,迭代就没有方向。
Agent 的评测指标应当覆盖三个维度:响应性能(用户等多久)、任务效果(是否完成)、资源效率(消耗了多少 Token/钱)。首字延迟(TTFT)、端到端完成时间、任务成功率是三个核心指标。
二、三大核心指标的定义与关系
graph LR
A["用户发送消息"] --> B["TTFT<br/>首字延迟"]
B --> C["TTCT<br/>任务完成时间"]
B -.-> B1["指标: Time To First Token<br/>目标: < 1s<br/>测量: 从请求到第一个字的时间"]
C -.-> C1["指标: Time To Complete Task<br/>目标: < 30s<br/>测量: 从请求到最终输出的时间"]
A --> D["任务成功率<br/>(Task Success Rate)"]
D --> D1["是否完成用户意图<br/>- 自动化评测<br/>- LLM Judge 评分<br/>- 用户反馈加权"]
style B fill:#4A90D9,color:#fff
style C fill:#F5A623,color:#000
style D fill:#50B86C,color:#fff
指标 1: 首字延迟(TTFT)
定义:从用户发送消息到第一个字符/Token 返回的时间。
为什么重要:TTFT 决定用户的"等待感"。人类对反馈的期望在 1 秒内。超过 3 秒用户就会觉得"卡"。
计算:TTFT = T(first_token_generated) - T(request_sent)
目标:P50 < 500ms, P95 < 1s, P99 < 2s
影响因素:
- LLM 服务的排队时间
- Prompt 处理时间
- 模型推理的 prefill 阶段(处理输入 tokens)
指标 2: 任务完成时间(TTCT)
定义:从用户发送消息到 Agent 输出最终结果的总时间。包含工具调用轮次。
计算:TTCT = T(final_response_complete) - T(request_sent)
目标:P50 < 15s, P95 < 30s
影响因素:
- LLM 生成 Token 数 × 每个 Token 的生成时间
- 工具调用的轮次 × 每轮工具的响应时间
- 网络延迟
指标 3: 任务成功率(Task Success Rate)
定义:Agent 是否成功完成用户意图。
为什么复杂:"成功"没有标准定义。翻译任务的成功可以通过 BLEU/BERTScore 自动评测,但"帮我写一封道歉信"的成功需要人类判断。
三、三项指标的生产级采集
"""
Agent 核心评测指标采集器
采集 TTFT、TTCT、任务成功率
"""
import time
import json
from dataclasses import dataclass, field
from typing import Dict, List, Optional
from prometheus_client import Histogram, Counter, Gauge
# ===== Prometheus 指标定义 =====
agent_ttft_seconds = Histogram(
'agent_ttft_seconds',
'Time To First Token (seconds)',
buckets=[0.1, 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, 5.0, 10.0],
)
agent_ttct_seconds = Histogram(
'agent_ttct_seconds',
'Time To Complete Task (seconds)',
buckets=[1, 2.5, 5, 10, 15, 20, 30, 45, 60, 120, 300],
)
agent_task_total = Counter(
'agent_task_total',
'Total agent tasks',
['status'], # success, partial_success, failure
)
agent_tokens_used = Counter(
'agent_tokens_used',
'Total tokens consumed',
['type'], # prompt, completion
)
agent_tool_calls = Histogram(
'agent_tool_calls_per_task',
'Number of tool calls per task',
buckets=[0, 1, 2, 3, 5, 8, 10, 15],
)
@dataclass
class AgentMetrics:
"""单次 Agent 调用的评测指标"""
session_id: str
turn_id: str
# 时间指标
request_time: float = 0.0
first_token_time: float = 0.0
completion_time: float = 0.0
# Token 指标
prompt_tokens: int = 0
completion_tokens: int = 0
# 工具调用
tool_calls_count: int = 0
tool_calls_detail: List[Dict] = field(default_factory=list)
# 任务结果
task_status: str = "unknown" # success, partial_success, failure
final_response: str = ""
@property
def ttft_sec(self) -> float:
return self.first_token_time - self.request_time
@property
def ttct_sec(self) -> float:
return self.completion_time - self.request_time
def to_prometheus(self):
"""上报指标到 Prometheus"""
agent_ttft_seconds.observe(self.ttft_sec)
agent_ttct_seconds.observe(self.ttct_sec)
agent_task_total.labels(status=self.task_status).inc()
agent_tokens_used.labels(type="prompt").inc(self.prompt_tokens)
agent_tokens_used.labels(type="completion").inc(self.completion_tokens)
agent_tool_calls.observe(self.tool_calls_count)
class AgentMetricsCollector:
"""Agent 评测指标采集器"""
def __init__(self):
self.current_metrics: Optional[AgentMetrics] = None
def start_tracking(self, session_id: str, turn_id: str) -> AgentMetrics:
"""开始追踪一次 Agent 调用"""
self.current_metrics = AgentMetrics(
session_id=session_id,
turn_id=turn_id,
request_time=time.time(),
)
return self.current_metrics
def record_first_token(self):
"""记录首字时间"""
if self.current_metrics:
self.current_metrics.first_token_time = time.time()
def record_completion(
self,
status: str,
response: str,
prompt_tokens: int,
completion_tokens: int,
tool_calls: List[Dict],
):
"""记录任务完成"""
if not self.current_metrics:
return
self.current_metrics.completion_time = time.time()
self.current_metrics.task_status = status
self.current_metrics.final_response = response
self.current_metrics.prompt_tokens = prompt_tokens
self.current_metrics.completion_tokens = completion_tokens
self.current_metrics.tool_calls_count = len(tool_calls)
self.current_metrics.tool_calls_detail = tool_calls
# 上报到 Prometheus
self.current_metrics.to_prometheus()
# 输出日志
self._log_metrics(self.current_metrics)
def _log_metrics(self, m: AgentMetrics):
"""记录评测指标到结构化日志"""
log_entry = {
"type": "agent_metrics",
"session_id": m.session_id,
"turn_id": m.turn_id,
"ttft_ms": round(m.ttft_sec * 1000, 1),
"ttct_ms": round(m.ttct_sec * 1000, 1),
"prompt_tokens": m.prompt_tokens,
"completion_tokens": m.completion_tokens,
"total_tokens": m.prompt_tokens + m.completion_tokens,
"tool_calls": m.tool_calls_count,
"status": m.task_status,
"tools_detail": [
{"name": tc.get("name", ""), "duration_ms": tc.get("duration_ms", 0)}
for tc in m.tool_calls_detail
],
}
print(json.dumps(log_entry))
四、三项指标的关系与解读
| 场景 | TTFT | TTCT | 任务成功率 | 诊断 |
|---|---|---|---|---|
| 正常 | 0.8s | 12s | 92% | ✅ |
| 模型升级后 | 0.6s | 15s | 88% | 新模型更快但质量下降 |
| Prompt 改后 | 1.2s | 20s | 95% | 更详细的 Prompt 增加延迟但提高质量 |
| 排队拥堵 | 5s | 25s | 92% | LLM 服务过载,需扩容 |
缺点:
- 任务成功率的主观性:即使用 LLM Judge 评测,不同的评价模型给出的成功率可能差 10%+。
- 首字延迟的误导性:TTFT 只是"第一个字"的时间,用户感知延迟是整个响应的时间。如果 TTFT 是 0.3s 但之后每字 0.5s,用户体验仍然差。
- 缺乏归一化:300 Token 的回答和 3000 Token 的回答,TTCT 自然不同。需要在评测中按 Token 数量归一化。
五、总结
Agent 评测的三个核心指标:首字延迟(TTFT,决定"等待感")、任务完成时间(TTCT,反映整体效率)、任务成功率(衡量"有没有用")。三者需要共同观察——单独优化任何一个都可能导致其他劣化(如降低 TTFT 可能缩短 Prompt 导致成功率下降)。指标采集通过 Prometheus 上报实时监控,通过结构化日志做离线分析。
更多推荐


所有评论(0)