AI News HubLIVE
站内改写2 分鐘閱讀

如何測量首Token時間(TTFT)

本文介紹瞭如何測量AI系統中的首Token時間(TTFT),解釋了其與傳統HTTP響應時間的本質區別,並提供了使用Python、Node.js和Apache JMeter進行LLM工作負載測量的代碼示例。

來源Hacker News AI作者: qainsights

在AI系統中,首Token時間(TTFT)是衡量用户感知響應速度的關鍵指標。與傳統的HTTP響應時間不同,TTFT測量的是從發送提示請求到接收響應流中第一個token之間的時間差。對於流式LLM API而言,這種區別至關重要,因為用户感知的響應速度取決於輸出何時開始,而非何時結束。

傳統的Web API性能指標,如響應時間、吞吐量和錯誤率,在應用於LLM時完全失效。當您調用OpenAI、Anthropic或本地託管的Ollama端點時,它看起來和感覺上就像調用任何其他REST API。但那個響應時間在欺騙您。LLM並非一次性計算整個響應,而是逐個生成token並流式傳輸。傳統性能工具記錄的是最後一個token到達的時間,而非第一個。用户盯着空白屏幕四秒後才看到內容,即使總耗時只有5秒,體驗也很糟糕。

為了全面理解LLM性能,需要跟蹤多個指標:TTFT(感知響應速度)、TTLT(總完成時間)、Token吞吐量(生成速度)、Token間延遲(流式平滑度)、Goodput(實際系統容量)和抖動(併發負載下的一致性)。TTFT是首要指標,但並非孤立存在。

LLM API通過服務器發送事件(SSE)或分塊HTTP傳輸編碼交付token。服務器在生成時刷新每個token,而不是緩衝完整響應。例如,OpenAI API的原始SSE流如下:

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"delta":{"content":"The"}}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"delta":{"content":" capital"}}]}

data: [DONE]

每個data:行是一個離散塊。第一條包含實際內容的data:行的時間戳就是您的TTFT測量點。

以下是使用Python和httpx庫(針對Anthropic Messages API)的準確TTFT測量示例:

import httpx
import time
import json

def measure_ttft(prompt: str, api_key: str) -> dict:
    url = "https://api.anthropic.com/v1/messages"
    headers = {
        "x-api-key": api_key,
        "anthropic-version": "2023-06-01",
        "content-type": "application/json",
    }
    payload = {
        "model": "claude-sonnet-4-20250514",
        "max_tokens": 512,
        "stream": True,
        "messages": [{"role": "user", "content": prompt}],
    }
    ttft = None
    ttlt = None
    token_count = 0
    request_start = time.perf_counter()
    with httpx.Client(timeout=60) as client:
        with client.stream("POST", url, headers=headers, json=payload) as response:
            for line in response.iter_lines():
                if not line.startswith("data:"):
                    continue
                raw = line[len("data:"):].strip()
                if raw == "[DONE]":
                    break
                try:
                    chunk = json.loads(raw)
                except json.JSONDecodeError:
                    continue
                event_type = chunk.get("type", "")
                if event_type == "content_block_delta":
                    now = time.perf_counter()
                    if ttft is None:
                        ttft = now - request_start
                    token_count += 1
                    ttlt = now - request_start
    return {
        "ttft_ms": round(ttft * 1000, 2) if ttft else None,
        "ttlt_ms": round(ttlt * 1000, 2) if ttlt else None,
        "token_count": token_count,
        "throughput_tokens_per_sec": round(token_count / ttlt, 2) if ttlt else None,
    }

關鍵點是使用time.perf_counter()進行高精度計時。time.time()不適合亞秒級精度。

在Node.js中,使用Anthropic SDK的流式接口:

import Anthropic from "@anthropic-ai/sdk";

interface LLMMetrics {
    ttft_ms: number | null;
    ttlt_ms: number | null;
    token_count: number;
    throughput_tokens_per_sec: number | null;
}

async function measureTTFT(prompt: string): Promise<LLMMetrics> {
    const client = new Anthropic();
    let ttft: number | null = null;
    let ttlt: number | null = null;
    let tokenCount = 0;
    const requestStart = performance.now();
    const stream = client.messages.stream({
        model: "claude-sonnet-4-20250514",
        max_tokens: 512,
        messages: [{ role: "user", content: prompt }],
    });
    for await (const chunk of stream) {
        if (chunk.type === "content_block_delta") {
            const now = performance.now();
            if (ttft === null) {
                ttft = now - requestStart;
            }
            tokenCount++;
            ttlt = now - requestStart;
        }
    }
    return {
        ttft_ms: ttft !== null ? Math.round(ttft * 100) / 100 : null,
        ttlt_ms: ttlt !== null ? Math.round(ttlt * 100) / 100 : null,
        token_count: tokenCount,
        throughput_tokens_per_sec: ttlt && tokenCount ? Math.round((tokenCount / (ttlt / 1000)) * 100) / 100 : null,
    };
}

使用performance.now()而非Date.now()以獲得亞毫秒級精度。

關於TTFT的基準:低於200毫秒為優秀,200-500毫秒為良好,500-1秒為一般,1-3秒為較差,超過3秒對於交互式用例不可接受。在併發負載下,這些數字會顯著上升。因此,建議設置p95 TTFT目標,而非平均值。

常見陷阱包括:測量HTTP響應時間而非TTFT、未啓用流式模式、使用低分辨率時間函數、僅測試單用户併發、忽略提示長度作為變量、混淆模型延遲與網絡延遲。