為每個請求選擇 Auto、TokenLab Verified 或 Official,並預先顯示價格。 查看最新動態

LLM Latency 與 Throughput:API 買家該如何衡量速度

·2026年9月19日·約 13 分鐘閱讀·更新 2026年10月2日·2202 次瀏覽
#基準測試#AI API#模型基礎設施#TokenLab
LLM Latency 與 Throughput:API 買家該如何衡量速度

在我們最新的測試中,最快顯示第一個可見 token 的模型,同時也是完成速度最慢的模型。在 2026-10-03,我們透過四個模型串流傳輸了一個提示詞(prompt)。claude-sonnet-5-5 在第一個可見 token 的時間上領先(2,981 毫秒),但在總耗時上落後(12,384 毫秒)。這種差距正是 LLM 延遲與吞吐量的核心所在:一個數字代表使用者等待看到文字的時間,另一個數字則代表工作完成的速度。本文定義了每個指標,展示了我們小規模的第一方測量結果及其限制,並提供了一個您可以針對 TokenLab API 執行的測試工具。

重點摘要

  • 三個數字,三種用途。 首個 token 時間(Time to first token)、總耗時(Total time)以及每秒輸出 token 數(Output tokens per second)分別回答了不同的問題。聊天、代理(agents)和批次處理工作各自依賴不同的指標。
  • 區分第一個事件與第一個可見 token。 在 deepseek-v4.1-flash 上,這兩個中位數相差 2,334 毫秒與 3,925 毫秒。推理增量(Reasoning deltas)可能會在任何可見文字出現之前就先抵達。
  • 推理 token 會消耗您的 max_tokens 配額。 在 max_tokens 為 400 時,deepseek-v4.1-flash 返回了約 0 個可見 token,而 gemini-3.8-flash 返回了約 14 個。在 2000 時,兩者皆完整回答。
  • 從使用量區塊(usage chunk)讀取 token 計數。 不要透過字元數來估算。
  • 模型速度不會提高您的請求限制。 速率限制(Rate limits)、token 限制和模型速度是獨立的約束條件。
  • 我們的樣本很小。 它包含每個模型五次執行、一個客戶端、一個提示詞和一天內的數據。它展示的是如何測量,而不是誰最快。

三個速度數字:實務中的 LLM 延遲與吞吐量

將「速度」拆解為幾個部分,因為每個部分都有不同的負責方:網路、模型以及您自己的佇列。

send request
  -> first stream event          (任何 SSE 區塊;可能是推理增量)
  -> first visible content token (第一個非空的 delta.content)
  -> last token + final usage chunk

首個 token 時間 (TTFT) 是從發送請求到第一個 token 到達的時間。我們記錄了兩個版本。第一個串流事件(first stream event) 是任何類型的第一個 SSE 區塊。第一個可見內容 token(first visible content token) 是第一個 choices[0].delta.content 非空的區塊。對於在回答前進行推理的模型,第一個事件可能是推理增量(delta.reasoning_content),它會在可見文字之前很久就抵達。一個僅監控 delta.content 的測試工具測量的是第一個可見 token 的時間,而不是第一個位元組的時間。使用者感受到的是可見的那一個。

總耗時(Total time) 是從發送請求到串流結束的時間。它包含推理、網路時間以及您額外加入的任何重試等待時間。

每秒輸出 token 數(Output tokens per second) 是輸出 token 總數除以總耗時。請從 usage.completion_tokens 獲取 token 計數,其中包含推理 token。我們除以總耗時,而不是可見視窗時間。我們樣本中的某些模型以幾個大區塊的形式傳送可見文字,因此基於視窗的速率會產生誤導。

工作負載 優先觀察 其次 原因
聊天 第一個可見 token 總耗時 在可見文字出現前,使用者面對的是空白框。
代理 總耗時 第一個可見 token 每個步驟通常需要等待上一步完成,因此單步總和會累加。
批次提取或摘要 每秒輸出 token 數,以及任務成本 請求限制餘裕 沒人會盯著第一個 token 看,且帳戶的請求限制決定了您提交工作的速度。

非串流呼叫(例如結構化 JSON 提取)僅會暴露總耗時。如果您想進行拆解分析,請在測試中開啟串流。

我們在 2026-10-03 的測量結果

方法。 這是 TokenLab 的第一方測量,於 2026-10-03 針對 POST https://api.tokenlab.sh/v1/chat/completions (來源) 進行觀察。每個請求皆使用 stream=true 和 stream_options.include_usage=true。我們從東亞的一個客戶端對每個模型進行了 5 次連續執行,使用 Node 24 的 fetch。提示詞為「Explain how HTTP keep-alive reduces latency for API clients. Write about 200 words.」。每個模型皆使用預設的推理設定。時間為客戶端牆鐘時間(wall clock),下表中的每一行在 5 次執行中皆成功(5/5)。

模型 (max_tokens 2000) 中位數首個串流事件 中位數首個可見 token 中位數總耗時 中位數完成 token (推理) 估計 token/s
deepseek-v4.1-flash 2,334 ms 3,925 ms 5,204 ms 453 (225) 87
gemini-3.8-flash 6,892 ms 6,892 ms 8,626 ms 826 (528) 96
claude-sonnet-5-5 2,981 ms 2,981 ms 12,384 ms 418 (未報告) 34
gpt-5.6-luna 3,262 ms 3,262 ms 8,183 ms 313 (42) 38

token/s 欄位是我們自己的估算:中位數完成 token 除以中位數總秒數,例如 453 ÷ 5.204 ≈ 87。這是中位數的比率,而非單次執行速率的中位數。

這些數據顯示的內容如下:

  • 按第一個可見 token 排序(claude-sonnet-5-5, gpt-5.6-luna, deepseek-v4.1-flash, gemini-3.8-flash)與按總耗時排序(deepseek-v4.1-flash, gpt-5.6-luna, gemini-3.8-flash, claude-sonnet-5-5)不同。一個指標無法取代另一個。
  • deepseek-v4.1-flash 在第一個事件與第一個可見 token 之間有 1,591 毫秒的間隔。其中位數可見輸出約為 228 個 token,在 1,304 毫秒內分 87 個事件傳送。
  • 對於 gemini-3.8-flash,第一個事件與第一個可見 token 的中位數皆為 6,892 毫秒。其可見文字在 1,734 毫秒內分 11 個事件傳送,因此串流是以區塊而非逐個 token 的方式進行。
  • claude-sonnet-5-5 在 9,186 毫秒內傳送了 371 個事件,而 gpt-5.6-luna 在 4,901 毫秒內傳送了 265 個事件。

成本與速度並列。下方的價格來自 TokenLab 模型 API,觀察於 2026-10-03,定價更新於 2026-10-02T16:53:30.068Z。最後一欄為估算值:中位數完成 token 乘以輸出費率(僅輸出端)。請查看使用記錄以獲取實際費用。

模型 每百萬輸入 每百萬輸出 最大輸出 token 輸出端估算,中位數執行 來源
deepseek-v4.1-flash $0.15 非尖峰, $0.30 尖峰 $0.60 非尖峰, $1.20 尖峰 384,000 453 × $0.60 / 1M ≈ $0.00027 非尖峰, ≈ $0.00054 尖峰 模型 API
gemini-3.8-flash $0.375 $1.875 65,536 826 × $1.875 / 1M ≈ $0.00155 模型 API
claude-sonnet-5-5 $0.60 $3 128,000 418 × $3 / 1M ≈ $0.00125 模型 API
gpt-5.6-luna $0.06 $0.36 128,000 313 × $0.36 / 1M ≈ $0.00011 模型 API

對於 gpt-5.6-luna,上述費率適用於輸入不超過 272K token 的情況。超過該數值時,完整請求將使用 2 倍輸入和 1.5 倍輸出費率。

此樣本的限制。 從東亞的一個客戶端進行五次連續執行是一個很小的切片。提示詞只是一個簡短的論文請求,因此結果會隨地區、時間、提示詞和輸出長度而變。我們沒有測試並發性,因此這些數據無法說明負載下的情況。請將此表格視為您自己測試的模板,並從生產環境所在的地區進行測試。如果您想更廣泛地了解模型,TokenLab 開發者 LLM API 排行榜是一個起點。在信任任何速度數據之前,請先檢查其方法和日期。也不要隨意更換模型:可靠性指南(觀察於 2026-10-03)指出,替換模型可能會改變品質、價格、上下文長度和工具支援。

推理 token 與 max_tokens:為什麼 400 返回了幾乎無內容的結果

我們也以 max_tokens 設定為 400 執行了相同的提示詞。方法相同,五次執行相同,日期相同 (來源)。

模型 (max_tokens 400) 中位數完成 token 中位數推理 token 可見輸出 (約) 中位數首個可見 token 中位數總耗時
deepseek-v4.1-flash 400 400 0 token 3,795 ms 4,693 ms
gemini-3.8-flash 396 382 14 token 4,199 ms 4,240 ms
claude-sonnet-5-5 400 未報告 400 token 3,088 ms 11,078 ms
gpt-5.6-luna 317 42 275 token 3,040 ms 8,083 ms

中位數是按欄位計算的,因此同一行的欄位不一定描述同一次執行。這就是為什麼 deepseek-v4.1-flash 會顯示首個可見時間,旁邊卻是零個可見事件的中位數。

結論很明確。deepseek-v4.1-flash 和 gemini-3.8-flash 將幾乎整個 400 token 的配額都花在了推理上,幾乎沒有返回任何可見文字。將 max_tokens 提高到 2000 則產生了完整的答案。推理 token 計算在 usage.completion_tokens 內。它們在 usage.completion_tokens_details.reasoning_tokens 中單獨報告。

當我們看到第一個 400 token 的結果時,失敗看起來像是一個空答案,而不是預算問題。透過三個檢查可以釐清:

  1. 讀取最終的使用量區塊,透過 stream_options={"include_usage": True} 啟用。如果 reasoning_tokens 接近 completion_tokens 且文字為空,則配額被用於推理。
  2. 檢查 finish_reason。串流指南(觀察於 2026-10-03)列出當達到 token 限制時,值為 length。
  3. 與模型的最大輸出進行比較。例如,模型 API 列出 deepseek-v4.1-flash 為 384,000,gemini-3.8-flash 為 65,536,因此 400 只是模型允許的一小部分。

聊天完成建立參考(觀察於 2026-10-03)也列出了 max_completion_tokens 和 reasoning_effort。它指出支援的值取決於模型,因此在設定之前請先閱讀模型頁面。我們沒有測試這些,並且我們以預設推理執行了每一行。請根據您自己的提示詞調整上限:在我們的執行中,200 字的答案產生了約 228 到 418 個可見 token,外加最多 528 個推理 token。

TokenLab 基礎 URL 的測量工具

此腳本使用 TokenLab 基礎 URL,從使用量區塊讀取 token 計數,並將第一個串流事件與第一個可見內容 token 分開記錄。它還保留了回應標頭以供後續除錯。它遵循文件中的客戶端設定(timeout=30.0, max_retries=0),因此 SDK 不會為您的時間測量增加隱藏的重試。模型 ID 是我們測量過的;您可以替換為任何接受 openai_chat_completions 格式的 ID。

import json
import os
import statistics
import time

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["TOKENLAB_API_KEY"],
    base_url="https://api.tokenlab.sh/v1",
    timeout=30.0,
    max_retries=0,
)

PROMPT = "Explain how HTTP keep-alive reduces latency for API clients. Write about 200 words."

def measure(model: str, max_tokens: int = 2000) -> dict:
    start = time.perf_counter()
    raw = client.chat.completions.with_raw_response.create(
        model=model,
        messages=[{"role": "user", "content": PROMPT}],
        max_tokens=max_tokens,
        stream=True,
        stream_options={"include_usage": True},
    )
    headers = raw.headers
    first_event = first_visible = None
    visible_events = 0
    usage = None
    finish_reason = None

    for chunk in raw.parse():
        now = time.perf_counter() - start
        if first_event is None:
            first_event = now
        if chunk.usage is not None:
            usage = chunk.usage
        if not chunk.choices:
            continue
        choice = chunk.choices[0]
        if choice.delta.content:
            visible_events += 1
            if first_visible is None:
                first_visible = now
        if choice.finish_reason:
            finish_reason = choice.finish_reason

    total = time.perf_counter() - start
    details = getattr(usage, "completion_tokens_details", None)
    completion = usage.completion_tokens if usage else None
    return {
        "model": model,
        "request_id": headers.get("x-request-id"),
        "routing_time_ms": headers.get("x-routing-time-ms"),
        "first_event_s": first_event,
        "first_visible_s": first_visible,
        "total_s": total,
        "completion_tokens": completion,
        "reasoning_tokens": getattr(details, "reasoning_tokens", None),
        "visible_events": visible_events,
        "finish_reason": finish_reason,
        "tokens_per_s": completion / total if completion else None,
    }

if __name__ == "__main__":
    runs = [measure("gemini-3.8-flash") for _ in range(5)]
    print(json.dumps(runs[0], indent=2))
    print("median total_s:", statistics.median(r["total_s"] for r in runs))

關於讀取輸出的幾點說明:

  • first_event_s 會在任何區塊觸發,包括僅有角色或推理區塊。first_visible_s 僅在非空的 delta.content 上觸發。如果兩者相差幾秒,則模型在回答前正在進行推理。
  • Token 計數來自最終的使用量區塊。該區塊的 choices 列表為空,這就是為什麼迴圈在跳過它之前會檢查 chunk.usage。
  • 執行五次或更多次,記錄地區,並報告中位數以及樣本大小。
  • 對於列出 openai_responses 的模型,串流指南顯示回應以 response.output_text.delta 和 response.completed 事件串流。同樣的拆解方式適用:測量第一個事件,然後是第一個文字增量。
  • 將在 response.completed 或 stop 完成原因之前關閉的串流視為不完整,而非快速成功。

用於延遲除錯的標頭與識別碼

如果您只保留了持續時間,那麼慢速請求將很難除錯。API 參考和疑難排解指南(皆觀察於 2026-10-03)列出了值得保留的識別碼。

識別碼 出現位置 用途
X-Request-ID 回應標頭(若可用) 單一請求的支援與除錯
request_id 錯誤主體、儀表板日誌、使用記錄 請求層級的支援與核對
X-Routing-Time-MS 回應標頭(若可用) 生成前選擇傳送所花費的時間
X-Task-ID, task_id, poll_url 非同步建立與狀態回應 圖像、影片、音樂與 3D 工作
X-Billing-Transaction-ID 最終計費後的標頭(若可用) 成本核對
您自己的工作或使用者 ID 您的應用程式 尋找使用者投訴背後的請求

對於每次呼叫,請記錄端點、方法、模型、狀態碼、時間戳記和延遲。加入您發送的欄位名稱(而非提示詞)、重試次數,以及重試是建立新任務還是恢復舊任務。我們的測試工具增加了來自使用量區塊的拆解時間和 token 計數。

兩點警告。串流回應可能會在串流結束後才結算,因此即使稍後記錄了使用量,計費標頭也可能不存在。對於計費金額,請使用管理 API 使用記錄,而非僅依賴回應 token 計數。若您有設定 X-TokenLab-Delivery-Policy,也請一併記錄。參考文件指出它接受 auto、verified 或 official,無效值會返回 400,不可用的選項會返回 503 並附帶 code: delivery_tier_unavailable 和 retryable: true。切勿記錄 Authorization、API 金鑰、簽名 URL、完整提示詞或個人資料。

控制台請求詳細資訊會保留 30 天,因此請將您需要的 ID 複製到您自己的日誌中。在我們的管線中,我們將這些 ID 與請求視圖對齊;TokenLab 請求控制台文章涵蓋了該視圖。

速率限制、重試與 LLM 延遲與吞吐量

單串流速度與帳戶吞吐量是不同的上限。速率限制指南(觀察於 2026-10-03)列出了下方的標準層級值。它指出這些值可能會隨活動配置而變,並針對每個 API 金鑰強制執行。

層級 每分鐘請求數
使用者 1,000
合作夥伴 10,000
VIP 10,000

當您超過限制時,API 會返回 429 rate_limit_exceeded 並附帶 Retry-After 標頭。指南的範例主體包含 "retryable": true 和 "retry_after": 8。指南建議信任返回的 X-RateLimit-Limit 和 Retry-After 值,而非任何複製的表格。它也指出更快的模型不會增加您帳戶的請求限制。

以下是批次工作的算術。這是我們根據上述中位數得出的估算,而非負載測試。並發串流大約是每秒請求數乘以每個請求的秒數。在每分鐘 1,000 個請求(約每秒 16.7 個)下:

  • 使用 deepseek-v4.1-flash(中位數總耗時 5.204 秒),大約可同時執行 87 個串流。
  • 使用 claude-sonnet-5-5(中位數總耗時 12.384 秒),大約可同時執行 206 個串流。

較慢的串流意味著需要更高的並發性才能達到相同的請求速率。達到限制後,會在生成時間之上增加等待時間。Token 限制未包含在我們使用的頁面中,因此請檢查您的帳戶和模型頁面以獲取相關資訊。

如何在不破壞數據的情況下進行重試:

  • 存在時請遵守 Retry-After。否則請使用指數退避(exponential backoff)搭配隨機抖動(jitter)和硬性嘗試上限。
  • 將 SDK 設定為 max_retries=0 並在一個地方進行重試。否則兩層重試會導致同一個呼叫被重複計算時間。
  • 切勿重試驗證、身分驗證、餘額或權限錯誤。請以短暫退避重試暫時性的 5xx 回應。
  • 在第一個事件到達後,中斷的串流是不完整的,不會自動重啟。重複請求可能會產生不同的輸出並產生額外費用。
  • 將重試次數和等待時間與模型時間分開記錄,這樣受限的執行就不會看起來像是模型變慢了。

這是指南中的有界重試模式:

import random
import time
from openai import RateLimitError

def chat_with_rate_limit_retry(client, model, messages, attempts=4):
    for attempt in range(attempts):
        try:
            return client.with_options(max_retries=0).chat.completions.create(
                model=model,
                messages=messages,
            )
        except RateLimitError as exc:
            if attempt == attempts - 1:
                raise
            header = exc.response.headers.get("Retry-After")
            wait = float(header) if header else min(30, 2 ** attempt + random.random())
            time.sleep(wait)

此外,請限制並發性,而不僅僅是平均每分鐘請求數,並對突發流量進行佇列處理。追蹤每個 API 金鑰的重複 429 回應,以免單一客戶端擠壓應用程式的其他部分。

常見問題

更高的每秒 token 數是否總是代表更快的體驗?

不一定。在我們 2026-10-03 的樣本中,claude-sonnet-5-5 顯示了最早的首個可見 token(2,981 毫秒),但總耗時最長(12,384 毫秒)。deepseek-v4.1-flash 較晚顯示可見文字(3,925 毫秒),但最先完成(5,204 毫秒)。哪一個感覺更快取決於使用者是盯著串流看,還是等待完整答案。單一客戶端的五次執行無法為您的工作負載定論。

為什麼我的串流回應幾乎沒有文字?

您可能將配額花在了推理上。在 max_tokens 為 400 時,deepseek-v4.1-flash 使用了 400 個推理 token,並返回了約 0 個可見 token。請檢查最終使用量區塊中的 usage.completion_tokens_details.reasoning_tokens 和 finish_reason。然後提高上限;在 2000 時,我們測試中的兩個推理模型皆完整回答。

更快的模型會提高我的速率限制嗎?

不會。速率限制指南指出模型速度、token 限制和帳戶速率限制是獨立的約束條件。更快的串流會更快完成並釋放並發插槽,但您的每金鑰請求限制保持不變。如果您需要,請向支援團隊申請不同的限制。

當串流緩慢時,我應該發送哪些 ID 給支援團隊?

發送 request_id(或 X-Request-ID)、端點、方法、模型、時間戳記和狀態碼。對於非同步工作,請加入 task_id 和 poll_url,若存在則加入 billing_transaction_id。包含您的拆解時間和安全的錯誤主體,並先遮蔽金鑰、提示詞和私人媒體。

在您自己的提示詞上執行上述測試工具,然後將您的測量結果與 TokenLab 模型排名進行比較。

來源

價格觀測於 2026-10-03

Prepared by Herald, TokenLab's AI growth lead. Facts are checked against the linked primary sources on the date shown.

相關模型

最近發布的模型

用本文涉及的模型開始構建

比較價格、測試路由,把文章研究直接變成可執行的 API 呼叫。

第一次呼叫 API

取得 API 金鑰
curl https://api.tokenlab.sh/v1/chat/completions \
  -H "Authorization: Bearer $TOKENLAB_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"gemini-4-argon","messages":[{"role":"user","content":"Hello!"}]}'