合成監控和真實使用者監控的區別:AI 應用怎麼組合撥測與 RUM

部落格 / 效能

合成監控和真實使用者監控的區別:AI 應用怎麼組合撥測與 RUM

合成監控和真實使用者監控的區別可以這樣概括:合成監控是你自己按固定頻率、從固定位置發請求去“試一下”,回答的是“服務現在通不通”;真實使用者監控(RUM)是從真實使用者的請求裡收集資料,回答的是“使用者實際用起來怎麼樣”。前者沒有使用者也能工作,但只代表探測點所在的網路;後者最貼近體驗,但沒有流量的時候就是一片空白。

做 AI 應用時,兩者之間的空檔更明顯。上游模型 API 網路是通的,返回的結果卻可能變差;流式響應的首個 token 遲遲不來,HTTP 狀態碼卻是 200。下面把兩種方式落到 LLM 介面上,再用 cdnping 自己的做法說明怎麼組合。

合成監控和真實使用者監控分別是什麼?

合成監控(Synthetic Monitoring),國內常叫“撥測”或“主動監控”。你寫好一個指令碼,例如每 5 分鐘請求一次某個 API 入口,記錄是否成功、耗時多少。請求是“合成”出來的,不是任何使用者發起的。

真實使用者監控(Real User Monitoring,RUM),也叫“被動監控”。在前端或服務端埋點,把每個真實請求的狀態碼、耗時、首位元組時間等上報。資料量隨流量變化,有多少使用者就有多少樣本。

維度合成監控(撥測)真實使用者監控(RUM)
資料來源自己發的探測請求使用者真實請求
什麼時候有資料7×24,按固定頻率只在有流量時
代表誰的網路探測節點所在的機房和線路使用者自己的網路、裝置、地區
擅長髮現上游完全不通、證書過期、DNS 解析失敗某地區變慢、某類請求失敗、長尾延遲
盲區測不到使用者側網路;只測固定請求,測不到“答得差”低峰期樣本少;上線前沒有資料
能否做基線對比能,條件固定,前後可比難,使用者構成一直在變
成本探測頻率 × 節點數,可控和流量成正比,要考慮取樣

放到 AI 應用裡,兩者各自漏掉什麼?

普通網頁的監控文章大多討論首屏時間、LCP 這類指標。AI 應用多了三類訊號,正好分別落在兩種方式的盲區裡。

1. 探測點的網路不等於使用者的網路

合成探測只能說明“從探測點出發是通的”。以 cdnping 的 API 狀態頁 為例:它從 Cloudflare 邊緣節點每 5 分鐘探測一次各家官方 API 入口。2026-10-17 生成的資料快照裡,探測來自 DEN 節點(丹佛),視窗是當天 UTC 08:40 到 10:05,共 242 次探測。在這個視窗裡:

  • Claude API 的中位耗時為 73 ms,xAI API 為 42 ms;
  • DeepSeek API 為 353 ms,阿里雲百鍊為 615 ms,矽基流動為 631 ms。

這組數字只說明“從丹佛的邊緣節點看過去”是這樣,不能理解為國內服務比海外服務慢。從國內網路發起探測,結果通常會反過來。這正是合成監控的侷限:資料準確,但代表性取決於探測點放在哪裡。要知道使用者那一側的情況,需要在使用者的網路裡測,例如 cdnping 首頁的線上測速,它在瀏覽器裡測“首次連線”(含 DNS 與 TLS 握手)和“往返”(連線複用後 3 次請求的中位數)。兩段耗時怎麼拆分,可以參考 AI API 延遲拆解:連線建立、閘道器往返與首 token 時間。

2. 流式響應:200 不代表體驗好

LLM 介面大多用 SSE 流式返回。一個常見情況是:響應頭很快回來,狀態碼 200,但第一個 token 過了很久才到。只檢查狀態碼的撥測會判定“正常”,使用者看到的卻是介面卡住不動。

所以 AI 應用的監控至少要分開記錄:連線建立耗時、首 token 時間(TTFT)、完整響應耗時。合成探測可以用一個固定的小請求測 TTFT,得到可比的基線;RUM 則能告訴你真實的長提示詞、長輸出下 TTFT 分佈是什麼樣。

3. “變笨了”:兩種方式都很難直接測出來

模型輸出質量下降,網路層面完全看不出來。可能的原因包括推理供應商換了、量化精度變了(例如 fp8、fp4),相關背景見 模型變笨還是換了供應商。cdnping 的 模型監測頁 會記錄 OpenRouter 上各供應商的量化精度變化,可以作為一條旁證。

在監控體系裡,這類問題通常靠兩種手段:

  • 合成側:準備幾條答案確定的固定提示詞(例如讓模型按固定格式輸出一段 JSON),定時呼叫並校驗結構是否合法、關鍵欄位是否正確。這比只看狀態碼多走了一步,但也只能覆蓋你預設的題目。
  • 使用者側:在介面上放一個“答得不好”的反饋入口,統計反饋比例的變化。

cdnping 的 掛了嗎 頁面就是這個思路:除了官方狀態頁和每 5 分鐘的 API 探測,還收集使用者一鍵報告,報告型別分為打不開、很慢、變笨了、限流、額度被重置、被登出。“打不開”“很慢”網路探測能部分發現;“變笨了”“額度被重置”“被登出”這類問題,探測基本測不到,只能靠使用者告訴你。

沒有流量時,真實使用者資料會是什麼樣?

RUM 的盲區可以用一份公開資料直接看到。AGI Plan 公開了按真實請求計算的可用率資料(AGI Plan 是 cdnping 的商業合作方),介面是 https://agiplan.dev/api/status/requests?month=YYYY-MM,按小時給出成功和失敗計數,只有 5xx 算平臺失敗。

快照中 2026-10-15 到 10-17 的 48 個小時桶裡,能看到兩種情況:

  • 故障時訊號很清楚:10 月 16 日 UTC 早上連續兩個小時桶,分別是 664 成功 / 722 失敗、455 成功 / 877 失敗。真實請求失敗率在這樣的時段非常醒目,不需要額外探測就能發現。
  • 低峰期幾乎沒有樣本:10 月 16 日 UTC 20 點到 22 點的三個小時桶,每個只有 2 個請求。這時候即使服務完全不可用,RUM 也很難給出可信的結論,2 個請求失敗 1 個就是 50%。

這就是需要合成探測補位的原因:固定頻率的探測在低峰期依然有穩定樣本。反過來,探測只覆蓋固定請求,不能代替真實請求的失敗率。可用率的口徑和錯誤預算怎麼算,可以看 SLI、SLO、SLA 到底差在哪。

按場景怎麼選?一張判斷表

你想回答的問題首選補充
上游模型 API 現在通不通合成探測(多節點)官方狀態頁
半夜沒人用時服務是否正常合成探測—
某個地區或運營商的使用者是否變慢RUM(按地區分組)在該地區補一個探測點
流式響應首 token 是否變慢兩者都要:探測看基線,RUM 看分佈—
升級 SDK 或換閘道器後是否變差合成探測做前後對比灰度期間看 RUM
模型輸出質量是否下降固定提示詞的結構校驗使用者反饋比例
是否觸發了限流(429)RUM(真實併發才會觸發)—
判斷是服務故障還是使用者自己的網路合成探測 + 使用者側測速對照見 三步判斷是服務故障還是網路問題

最小實施方案

下面是一個小團隊可以照著做的組合,按順序推進。

第一步:給每個上游入口加一個合成探測

最簡單的版本用 curl,每 5 分鐘跑一次,把各階段耗時記下來:

curl -o /dev/null -s --max-time 10 \
  -w 'code=%{http_code} dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
  https://api.anthropic.com/v1/models

不帶金鑰請求時返回 401 是正常的,說明 DNS、TCP、TLS 和對方閘道器都工作了;超時、000 或 5xx 才說明有問題。判斷規則建議寫成“連續 2 次失敗才告警”,避免單次網路抖動造成誤報。

如果要測 TTFT,再加一個帶金鑰的固定小請求,開啟流式,記錄收到第一個資料事件的時間。注意 curl 的 time_starttransfer 記錄的是第一個響應位元組,可能只是響應頭,不一定是第一個 token,嚴格測 TTFT 需要在指令碼里解析 SSE 事件。

探測節點至少放兩個:一個靠近你的伺服器,一個靠近主要使用者。更完整的告警配置可以參考 AI 應用可用性監控怎麼做,指令碼和口徑參考 自己測 AI API 的可用率。

第二步:在前端給流式請求加 RUM 埋點

在發起對話的地方包一層計時,把結果用 sendBeacon 上報:

async function chatWithTiming(url, body, onChunk) {
  const t0 = performance.now();
  let ttft = null, status = 0, error = null;
  try {
    const res = await fetch(url, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify(body),
    });
    status = res.status;
    const reader = res.body.getReader();
    for (;;) {
      const { done, value } = await reader.read();
      if (done) break;
      if (ttft === null) ttft = performance.now() - t0;
      onChunk(value);
    }
  } catch (e) {
    error = e.name;
  } finally {
    navigator.sendBeacon('/rum', JSON.stringify({
      status,
      error,
      ttft_ms: ttft === null ? null : Math.round(ttft),
      total_ms: Math.round(performance.now() - t0),
      net: navigator.connection ? navigator.connection.effectiveType : null,
    }));
  }
}

服務端只需要把這些欄位存下來,按小時彙總成功數、失敗數和 TTFT 分位數。不要上報提示詞和回答內容;如果需要地區維度,在服務端按 IP 粗粒度解析出地區後丟棄原始 IP。

第三步:放一個使用者反饋入口

在每條回答旁邊放“答得不好”和“很慢”兩個按鈕,記錄時間和所用模型組。它的樣本量遠小於 RUM,但能覆蓋前兩步都測不到的質量問題。

第四步:把三路訊號放在同一張圖上

告警時按下面的順序看:

  1. 合成探測失敗、RUM 失敗率也升高:上游或你自己的服務出了問題,查官方狀態頁,閱讀方法見 怎麼看 OpenAI 狀態頁和 Claude 狀態頁。
  2. 合成探測正常、RUM 失敗率升高:多半是使用者側網路、某個地區線路,或者真實併發觸發了限流,按錯誤碼和地區分組看。
  3. 兩者都正常、使用者反饋增多:考慮質量問題,跑一遍固定提示詞校驗,看看模型或供應商是否有變化。
  4. 合成探測失敗、RUM 正常:先懷疑探測節點自身的網路,或者探測的入口和使用者實際用的入口不一樣。

cdnping 自己是怎麼組合的?

cdnping 本身就是三種訊號的組合,可以當作一個參考樣例:

  • 邊緣合成探測:/status 從 Cloudflare 邊緣每 5 分鐘探測各官方 API 入口,每 15 分鐘同步部分官方狀態頁,保留 30 天曆史;
  • 使用者側主動測量:首頁測速 在使用者瀏覽器裡測連線和往返耗時,結果可以匿名計入統計,不記錄 IP。它介於合成和 RUM 之間:請求是合成的,但發生在使用者真實的網路裡;
  • 使用者訊號:/down 的一鍵報告,補上“變笨了”“額度被重置”這類探測測不到的問題。

各項資料的採集方式和口徑寫在 資料方法 頁。需要說明的是,cdnping 從 2026-10-17 開始採集,本文引用的探測和請求資料都只覆蓋很短的視窗,用來說明方法,不代表任何服務的長期表現。

常見誤區

  • 只做撥測,就認為使用者體驗沒問題。撥測只證明探測點能連上,使用者在哪個網路、用什麼裝置,它不知道。
  • 只看 RUM 的平均值。AI 請求的耗時和輸出長度強相關,平均值會被長回答拉高。按 TTFT 和總耗時分開統計,並看 p95。
  • 把 401、429 都當成故障。401 往往是金鑰或配置問題,429 是限流,處理方式不同,不要和 5xx 合併成一個失敗率。429 的處理見 API 返回 429 Too Many Requests 怎麼辦。
  • 低峰期用 RUM 判斷可用率。幾個請求的樣本算出來的百分比波動很大,這個時段應以合成探測為準。

小團隊不需要一次全做。先按上面的第一、二步加上合成探測和流式請求埋點,網路不通、上游 5xx 和首 token 變慢這幾類問題就能看到;質量問題再靠第三步的反饋入口補。

相關文章

繼續閱讀