GEO 分數反映的是「品牌在 AI 認知中的狀態」,不是「資料管道的健康度」。把兩件事混為一談,分數就失去了它該有的意義。
任何依賴外部 AI 服務的系統,在任何時點都可能遇到部分或全部失敗。失敗的來源五花八門:
這些失敗在工程上是日常事件,而非「異常例外」。問題是:當這些失敗發生在掃描管道時,不能讓它污染 GEO 分數。
假想一個場景:品牌 X 的 Citation Rate 在過去 30 天穩定維持在 55 分。某日凌晨,負責為 X 掃描的 6 個 AI 平台中的 3 個同時失敗;若以「失敗即 0」直接計入當日分數,X 會從 55 分掉到 27.5 分。使用者打開儀表板會以為品牌突然失寵、開始恐慌。但實際上 AI 對 X 的認知沒有任何變化——變動的只是資料管道。
這是第一種錯誤:把管道故障當成品牌變化。
面對上述問題,第一反應往往是以下三種之一,但三種全部有問題:
想法:只把成功的平台列入計算,失敗的平台跳過。
問題:分母變動會讓分數鋸齒狀跳動。今天 10 個平台全成功、分數 60;明天 6 個成功、分數 62(因為剛好失敗的是低分平台);後天 10 個全成功、分數又回 60。趨勢圖完全失去可讀性,使用者無法判斷「究竟是真的變好還是分母變了」。
想法:把每個失敗平台的當日分數記為 0,維持分母穩定。
問題:這等於告訴演算法「這個品牌今天在這個平台完全不存在」,但真實情況是「我們不知道今天的狀態」。「沒被提及」與「沒掃描到」是兩件完全不同的事,混為一談會讓後續的趨勢分析、幻覺偵測、競品比較全部失準。
想法:既然掃描失敗,那就當作這個時段從未存在過。
問題:時序資料庫會出現斷層,無法區分「週末沒掃描」「掃描失敗」「掃描了但無變化」三種情境。補救的代價是大量事後判斷邏輯,複雜度高、可維護性低。
這三個解法都有一個共同的根本問題:它們都沒有區分「資料缺失」與「資料為零」這兩個根本不同的狀態。
我們採用的方案是從歷史 carry forward 最近一次的成功值,並明確標記為「已停滯」:
%%{init: {'theme':'base'}}%%
xychart-beta
title "Score under Mid-Scan Platform Outage (Illustrative)"
x-axis ["Day 1", "Day 2", "Day 3 (outage)", "Day 4", "Day 5"]
y-axis "GEO Score" 0 --> 80
line [55, 56, 27, 28, 56]
line [55, 56, 55, 55, 56]
Fig 4-1: 上折線 = 方案 B「失敗即 0」造成的假暴跌;下折線 = Stale Carry-Forward 維持連續並於前端標記 isStale。
isStale = truelastSuccessAt = <historical_timestamp>stateDiagram-v2
[*] --> Fresh
Fresh --> Stale: 當日掃描 100% 失敗
Stale --> Fresh: 下次掃描成功
Stale --> Expired: 停滯超過閾值(如 72h)
Expired --> Fresh: 成功掃描
Expired --> Reset: 超過 30 天仍未恢復
Reset --> Fresh: 重新建立基線
Fig 4-2: Fresh → Stale 是常態恢復路徑;Expired 與 Reset 是例外處理,留給長期中斷場景。
理由:避免在罕見情況下引用過期資料。假設某平台長期無回應,最早的歷史可能追溯到幾個月前;那時的分數已與當前品牌狀態脫鉤。200 列(對每日掃描而言約 6–7 個月)作為軟上限,在「maintain continuity」與「keep data relevant」之間取平衡。超過此上限則不 carry forward,改記為 null 並在 UI 明確標示「資料待重建」。
策略:首掃的品牌若遇上平台失敗,不 carry forward。因為沒有歷史基線可比;強行帶入 0 或平均值都是假資料。前端改顯示「首次掃描 — 資料建置中」提示,明確區別「缺歷史」與「有歷史但停滯」。
Phase 基線測試(見 Ch 10)走獨立資料路徑,不受 Stale Carry-Forward 影響。基線測試的目的是縱向追蹤真實變化,任何 carry forward 都會污染此目的。若基線測試遇上平台失敗,該 Phase 結果直接標示 status = incomplete,待恢復後重跑。
Stale 標記本身不是警示;但若同一平台停滯超過 72 小時,系統升級通知:
超過 7 天仍未恢復,進一步進入 Expired 狀態,於 UI 明示「資料已過期,不納入當前評分」,同時停止 carry forward。
carry forward 的正當性建立在一個假設上:AI 認知的變化以週為單位。模型重訓、知識圖譜更新、外部新聞事件影響,這些都不是小時級的變動。因此用昨天的值替代今天(在資料缺失的情況下)是統計上合理的。
但這個假設有邊界:
本平台將 carry forward 限制在 AI 引用率相關的維度(Citation、Position、Sentiment),不套用於結構化資料狀態、fingerprint 比對等其他指標。
// Simplified illustration — actual implementation handles multi-platform fanout,
// lookback tuning, and emits observability events.
async function enrichWithStaleCarryForward(platform, brandId, currentResult) {
const currentFailed = currentResult.successCount === 0;
if (!currentFailed) {
return { ...currentResult, isStale: false };
}
const lastSuccess = await db.query(`
SELECT sov_score, position_quality, sentiment, scanned_at
FROM scan_results
WHERE brand_id = $1 AND platform = $2
AND sov_score IS NOT NULL AND sov_score > 0
ORDER BY scanned_at DESC
LIMIT 1
OFFSET 0
`, [brandId, platform]);
if (!lastSuccess.rows.length) {
return { ...currentResult, isStale: false, reason: 'no_baseline' };
}
const historical = lastSuccess.rows[0];
const ageHours = (Date.now() - historical.scanned_at.getTime()) / 3_600_000;
if (ageHours > MAX_CARRY_FORWARD_HOURS) {
return { ...currentResult, isStale: true, expired: true };
}
return {
sov_score: historical.sov_score,
position_quality: historical.position_quality,
sentiment: historical.sentiment,
isStale: true,
lastSuccessAt: historical.scanned_at,
staleAgeHours: ageHours,
};
}
┌──────────────────────────────────────────────┐
│ OpenAI GPT-4o 55 分 🔴 已失聯 14 小時 │
│ Anthropic Claude 62 分 │
│ Google Gemini 48 分 🔴 已失聯 14 小時 │
└──────────────────────────────────────────────┘
Fig 4-3: 紅色圓點與 tooltip 明確告知「這個分數是上次成功值,不是當前值」;使用者不會被誤導。
Stale Carry-Forward 這個模式並非 GEO 獨有。任何「高頻取樣、來源不穩、需維持時序連續性」的訊號系統都適用:
| 領域 | 適用場景 | 需調整的參數 |
|---|---|---|
| IoT 感測 | 感測器間歇性失聯 | lookback window 改以「分鐘」為單位 |
| 金融行情 | 交易所短暫斷線 | 不適用(金融資料的即時性無法妥協) |
| 社群監測 | API 配額用盡 | lookback 可更短,變動性高 |
| 廣告歸因 | Pixel 暫時 loss | 需搭配 probabilistic matching |
| 供應鏈可見性 | EDI 傳輸中斷 | 可 carry forward 較長(幾天) |
共通前提是:變動速率遠慢於取樣速率。一旦違反這個前提(例如金融),carry forward 就不成立。