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 服务过载,需扩容

缺点:

  1. 任务成功率的主观性:即使用 LLM Judge 评测,不同的评价模型给出的成功率可能差 10%+。
  2. 首字延迟的误导性:TTFT 只是"第一个字"的时间,用户感知延迟是整个响应的时间。如果 TTFT 是 0.3s 但之后每字 0.5s,用户体验仍然差。
  3. 缺乏归一化:300 Token 的回答和 3000 Token 的回答,TTCT 自然不同。需要在评测中按 Token 数量归一化。

五、总结

Agent 评测的三个核心指标:首字延迟(TTFT,决定"等待感")、任务完成时间(TTCT,反映整体效率)、任务成功率(衡量"有没有用")。三者需要共同观察——单独优化任何一个都可能导致其他劣化(如降低 TTFT 可能缩短 Prompt 导致成功率下降)。指标采集通过 Prometheus 上报实时监控,通过结构化日志做离线分析。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐