在我們最新的測試中,最快顯示第一個可見 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 的結果時,失敗看起來像是一個空答案,而不是預算問題。透過三個檢查可以釐清:
- 讀取最終的使用量區塊,透過
stream_options={"include_usage": True}啟用。如果reasoning_tokens接近completion_tokens且文字為空,則配額被用於推理。 - 檢查
finish_reason。串流指南(觀察於 2026-10-03)列出當達到 token 限制時,值為length。 - 與模型的最大輸出進行比較。例如,模型 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
- TokenLab Docs: Streaming觀測於 2026-10-03
- TokenLab Docs: Rate limits觀測於 2026-10-03
- TokenLab Docs: Logs and troubleshooting觀測於 2026-10-03
- TokenLab Docs: Build a reliable TokenLab integration觀測於 2026-10-03
- TokenLab Docs: API Reference觀測於 2026-10-03
- TokenLab Docs: Create Chat Completion觀測於 2026-10-03
- TokenLab live model API: deepseek-v4.1-flash觀測於 2026-10-03
- TokenLab live model API: gemini-3.8-flash觀測於 2026-10-03
