<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>偵測 on Tarragon</title><link>https://tarrragon.github.io/blog/tags/%E5%81%B5%E6%B8%AC/</link><description>Recent content in 偵測 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 03 Aug 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/%E5%81%B5%E6%B8%AC/index.xml" rel="self" type="application/rss+xml"/><item><title>清單過時的代價要落在精度、不落在覆蓋</title><link>https://tarrragon.github.io/blog/report/stale-list-costs-precision-not-coverage/</link><pubDate>Mon, 03 Aug 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/report/stale-list-costs-precision-not-coverage/</guid><description>&lt;h2 id="論述基礎與限制">論述基礎與限制&lt;/h2>
&lt;p>本卡從一套自建流量統計的自動化訪客辨識抽出。&lt;/p>
&lt;p>辨識手段之一是比對 User-Agent 裡的自我申報名稱。第一版只回傳「有命中／沒命中」，結果是知道訪客自稱是機器人、不知道是哪一隻——無從分辨來的是搜尋引擎索引、AI 檢索、社群平台的連結預覽還是抓取工具，而這三類的處置方向完全不同。&lt;/p>
&lt;p>改進時最直覺的做法，是列一份已知爬蟲的名單做精確比對。那個做法有一個當下不會顯現的失效模式：名單一定會過時，而過時的表現是新出現的爬蟲完全不被標記。統計上的呈現是自動化流量比例逐漸下降——這個下降與「內容吸引到更多真人」在數字上不可區分，而後者是所有人都希望看到的解釋。&lt;/p>
&lt;p>實際採用的是兩層結構：先比對具名清單、未命中時退回通用字樣比對（&lt;code>bot&lt;/code> / &lt;code>crawler&lt;/code> / &lt;code>spider&lt;/code> / &lt;code>headless&lt;/code>）。清單過時的代價因此落在「不知道是誰」，而非「不知道有」。&lt;/p>
&lt;p>證據狀態與姊妹卡 &lt;a href="../new-data-shape-silently-changes-analysis/">#250&lt;/a> 不同，要分開說：&lt;strong>那張卡的失效已經發生過兩次，本卡描述的失效在該系統上尚未被觀測到&lt;/strong>。清單才剛建立、還沒有機會過時；兩層結構是在設計當下依推演採用的，而不是被一次事故逼出來的。這不減損論證，但讀者有權知道哪些部分靠觀測、哪些靠推理。&lt;/p>
&lt;p>一個現成的旁證來自同一份清單本身：它上線時含有一個永遠不會命中的條目——那是 robots.txt 的控制 token，不會出現在任何 User-Agent 字串裡。這個條目從第一天起就是死的，而系統不會回報「某個條目從未命中」，它是靠一次外部查核才浮現。清單的缺陷不產生訊號，這一點在清單過時之前就已經成立。&lt;/p>
&lt;p>限制有兩層。&lt;strong>第一層是用途&lt;/strong>：本卡談的是清單用於偵測的場合；清單用於分類時（把已知項目歸位、其餘歸「其他」）沒有這個問題，因為「其他」這一類本來就承接未知——關鍵差別在於是否存在一個接住漏網之魚的預設路徑。&lt;strong>第二層是對象的成員資格由誰控制&lt;/strong>：「清單會過時」是經驗宣稱而非定義性真理，它取決於有沒有東西阻止新成員出現。自己完全控制的內部識別集合（自家定義的事件名、自家的錯誤碼）不會在無人知曉的情況下長出新成員，那裡加退回層是多餘的複雜度。&lt;/p>
&lt;p>判斷的問題是「&lt;strong>誰控制成員資格、有沒有東西阻止新增&lt;/strong>」，不是「上一次新增是什麼時候」——後者是落後指標，一個域可以安靜三年之後一次加十個。已凍結的協定值符合前一個問題（有規範阻止新增），而國碼與法定分類不符合（制裁名單與稅則逐年更新，後者還帶對抗性），儘管它們的變動看起來很慢。&lt;/p>
&lt;hr>
&lt;h2 id="核心原則">核心原則&lt;/h2>
&lt;p>&lt;strong>依賴清單的判定，要先決定清單失效之後還剩下什麼。&lt;/strong> 清單會過時是確定的，設計上的自由度在於選擇過時的代價落在精度還是覆蓋——避免過時本身不在選項裡。&lt;/p>
&lt;p>&lt;strong>覆蓋的損失不可見，精度的損失可見。&lt;/strong> 少一個分類標籤這件事在報表上看得到——標記成 &lt;code>ua:bot&lt;/code> 而非 &lt;code>ua:claudebot&lt;/code>，讀報表的人立刻知道自己不知道是誰。漏掉整隻爬蟲則表現為數字變好看，而變好看的數字不會促使任何人去查為什麼。&lt;/p>
&lt;p>&lt;strong>清單過時不會產生通知。&lt;/strong> 清單的維護需要外部觸發，而「有新爬蟲出現了」這個事件不會主動送達使用清單的系統。仰賴定期審視的維護計畫，在系統運作正常、沒有任何異狀的期間最容易被延後。&lt;/p>
&lt;p>&lt;strong>退回層必須獨立於清單，而不是清單的擴充。&lt;/strong> 用更多同類條目擴充清單只是把邊界往外推，邊界仍然是列舉出來的。退回層要用另一種特徵——具名清單比對的是身分，通用比對匹配的是類別詞，後者不需要知道任何一隻爬蟲的名字就能運作。&lt;/p>
&lt;hr>
&lt;h2 id="修法">修法&lt;/h2>
&lt;p>&lt;strong>判定分兩層，各自負責一件事：精確層負責識別，粗略層負責覆蓋。&lt;/strong> 粗略層的規則要寫得比精確層寬，且不引用精確層的任何條目。實作上是「先試具名比對，未命中則試通用比對」，兩者的比對來源不同，因此精確層的過時不影響粗略層的能力。&lt;/p>
&lt;p>&lt;strong>退回層的命中率要可觀測。&lt;/strong> 記錄下來的標記要能分辨命中的是哪一層。退回層命中比例上升，代表清單該更新了——這把「清單過時」從一件不可見的事，變成一個可以看的數字，而且它不需要有人知道漏了哪些新項目。&lt;/p>
&lt;p>&lt;strong>分辨清單的用途是分類還是偵測。&lt;/strong> 分類可以只靠清單，未列到的歸「其他」不影響正確性。偵測不行，因為偵測的失敗形態就是漏掉，而漏掉的東西不會出現在任何一欄裡。同一份清單在兩種用途下的設計要求不同，混用時會把分類的寬容度帶進偵測。&lt;/p>
&lt;p>&lt;strong>用最舊的清單狀態做一次推演。&lt;/strong> 假設清單從今天起不再更新，一年後這套判定還能回答什麼問題。答得出「還知道有多少自動化流量、只是不知道分別是誰」，結構就是對的；答案若是「不知道」，代表覆蓋綁在清單上，要補退回層。&lt;/p>
&lt;hr>
&lt;h2 id="判讀徵兆">判讀徵兆&lt;/h2>
&lt;p>某類偵測的命中數隨時間平緩下降，而沒有任何一次變更是為了減少它——先檢查判定是否綁在一份沒有更新的清單上，再接受「真的改善了」這個解釋。&lt;/p>
&lt;p>判定邏輯裡只有一組正面列舉、沒有任何預設分支——這代表未列舉的一切都落進「不是」，而不是落進「未知」。&lt;/p>
&lt;p>清單的更新來自偶然（讀到一篇文章、剛好看到一筆奇怪的資料），而非來自系統本身產生的訊號——這代表維護依賴外部注意力，而外部注意力不會在需要的時候到場。&lt;/p>
&lt;hr>
&lt;h2 id="跟其他原則的關係">跟其他原則的關係&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="../new-data-shape-silently-changes-analysis/">#250 資料多出一種形狀時，既有分析邏輯靜默換語意&lt;/a>：同一次工作抽出的姊妹卡，兩者都描述「能力隨時間衰減而沒有訊號」。#250 的衰減由資料端變化引發（資料多出一種形狀），本卡的衰減由外部世界變化引發（世界多出一個項目）；兩者的處置也同構——都是加一條接住未涵蓋部分的預設路徑，並讓那條路徑的使用量可見。&lt;/li>
&lt;li>&lt;a href="../lint-scope-must-be-explicit-fact/">#221 檢查規則的作用域要顯式列舉&lt;/a>：#221 處理的是規則管哪些檔案這個 fact 被編碼進路徑常數後不再被審視，本卡處理的是判定認得哪些對象這個 fact 被編碼進清單後不再被審視。兩者的常數都會過時，而過時的表現都是「通過的數量增加」——一個是零 error，一個是零命中。&lt;/li>
&lt;li>&lt;a href="../self-audit-detection-method-must-match-rule-type/">#232 自審 sweep 的偵測方法要對齊規則類型&lt;/a>：本卡是它在時間軸上的延伸。#232 問的是偵測方法此刻看不看得見違反形態，本卡問的是這個「看得見」能維持多久——一個在設計時成立的對齊，會因為對象演化而在事後失效。&lt;/li>
&lt;li>&lt;a href="../collapse-is-implicit-default/">#125 Collapse 是隱形預設&lt;/a>：清單型判定的預設是「沒列到就是不算」，而這個預設從未被決定過，它只是列舉式寫法的自然結果。本卡要求把那個預設拿出來明寫成退回層，屬於同一種「把沒人決定過的預設轉成有人決定過的設計」的動作。&lt;/li>
&lt;li>&lt;a href="../validate-load-bearing-claim-before-building/">#236 承重論點先對抗驗證、再建下游&lt;/a>：清單在偵測系統裡是承重的——所有下游統計都建立在「被標記的就是全部」這個宣稱上。本卡的「用最舊的清單狀態推演」是針對這個承重宣稱的一種對抗驗證，成本低到可以在設計當下就跑完。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<h2 id="論述基礎與限制">論述基礎與限制</h2>
<p>本卡從一套自建流量統計的自動化訪客辨識抽出。</p>
<p>辨識手段之一是比對 User-Agent 裡的自我申報名稱。第一版只回傳「有命中／沒命中」，結果是知道訪客自稱是機器人、不知道是哪一隻——無從分辨來的是搜尋引擎索引、AI 檢索、社群平台的連結預覽還是抓取工具，而這三類的處置方向完全不同。</p>
<p>改進時最直覺的做法，是列一份已知爬蟲的名單做精確比對。那個做法有一個當下不會顯現的失效模式：名單一定會過時，而過時的表現是新出現的爬蟲完全不被標記。統計上的呈現是自動化流量比例逐漸下降——這個下降與「內容吸引到更多真人」在數字上不可區分，而後者是所有人都希望看到的解釋。</p>
<p>實際採用的是兩層結構：先比對具名清單、未命中時退回通用字樣比對（<code>bot</code> / <code>crawler</code> / <code>spider</code> / <code>headless</code>）。清單過時的代價因此落在「不知道是誰」，而非「不知道有」。</p>
<p>證據狀態與姊妹卡 <a href="../new-data-shape-silently-changes-analysis/">#250</a> 不同，要分開說：<strong>那張卡的失效已經發生過兩次，本卡描述的失效在該系統上尚未被觀測到</strong>。清單才剛建立、還沒有機會過時；兩層結構是在設計當下依推演採用的，而不是被一次事故逼出來的。這不減損論證，但讀者有權知道哪些部分靠觀測、哪些靠推理。</p>
<p>一個現成的旁證來自同一份清單本身：它上線時含有一個永遠不會命中的條目——那是 robots.txt 的控制 token，不會出現在任何 User-Agent 字串裡。這個條目從第一天起就是死的，而系統不會回報「某個條目從未命中」，它是靠一次外部查核才浮現。清單的缺陷不產生訊號，這一點在清單過時之前就已經成立。</p>
<p>限制有兩層。<strong>第一層是用途</strong>：本卡談的是清單用於偵測的場合；清單用於分類時（把已知項目歸位、其餘歸「其他」）沒有這個問題，因為「其他」這一類本來就承接未知——關鍵差別在於是否存在一個接住漏網之魚的預設路徑。<strong>第二層是對象的成員資格由誰控制</strong>：「清單會過時」是經驗宣稱而非定義性真理，它取決於有沒有東西阻止新成員出現。自己完全控制的內部識別集合（自家定義的事件名、自家的錯誤碼）不會在無人知曉的情況下長出新成員，那裡加退回層是多餘的複雜度。</p>
<p>判斷的問題是「<strong>誰控制成員資格、有沒有東西阻止新增</strong>」，不是「上一次新增是什麼時候」——後者是落後指標，一個域可以安靜三年之後一次加十個。已凍結的協定值符合前一個問題（有規範阻止新增），而國碼與法定分類不符合（制裁名單與稅則逐年更新，後者還帶對抗性），儘管它們的變動看起來很慢。</p>
<hr>
<h2 id="核心原則">核心原則</h2>
<p><strong>依賴清單的判定，要先決定清單失效之後還剩下什麼。</strong> 清單會過時是確定的，設計上的自由度在於選擇過時的代價落在精度還是覆蓋——避免過時本身不在選項裡。</p>
<p><strong>覆蓋的損失不可見，精度的損失可見。</strong> 少一個分類標籤這件事在報表上看得到——標記成 <code>ua:bot</code> 而非 <code>ua:claudebot</code>，讀報表的人立刻知道自己不知道是誰。漏掉整隻爬蟲則表現為數字變好看，而變好看的數字不會促使任何人去查為什麼。</p>
<p><strong>清單過時不會產生通知。</strong> 清單的維護需要外部觸發，而「有新爬蟲出現了」這個事件不會主動送達使用清單的系統。仰賴定期審視的維護計畫，在系統運作正常、沒有任何異狀的期間最容易被延後。</p>
<p><strong>退回層必須獨立於清單，而不是清單的擴充。</strong> 用更多同類條目擴充清單只是把邊界往外推，邊界仍然是列舉出來的。退回層要用另一種特徵——具名清單比對的是身分，通用比對匹配的是類別詞，後者不需要知道任何一隻爬蟲的名字就能運作。</p>
<hr>
<h2 id="修法">修法</h2>
<p><strong>判定分兩層，各自負責一件事：精確層負責識別，粗略層負責覆蓋。</strong> 粗略層的規則要寫得比精確層寬，且不引用精確層的任何條目。實作上是「先試具名比對，未命中則試通用比對」，兩者的比對來源不同，因此精確層的過時不影響粗略層的能力。</p>
<p><strong>退回層的命中率要可觀測。</strong> 記錄下來的標記要能分辨命中的是哪一層。退回層命中比例上升，代表清單該更新了——這把「清單過時」從一件不可見的事，變成一個可以看的數字，而且它不需要有人知道漏了哪些新項目。</p>
<p><strong>分辨清單的用途是分類還是偵測。</strong> 分類可以只靠清單，未列到的歸「其他」不影響正確性。偵測不行，因為偵測的失敗形態就是漏掉，而漏掉的東西不會出現在任何一欄裡。同一份清單在兩種用途下的設計要求不同，混用時會把分類的寬容度帶進偵測。</p>
<p><strong>用最舊的清單狀態做一次推演。</strong> 假設清單從今天起不再更新，一年後這套判定還能回答什麼問題。答得出「還知道有多少自動化流量、只是不知道分別是誰」，結構就是對的；答案若是「不知道」，代表覆蓋綁在清單上，要補退回層。</p>
<hr>
<h2 id="判讀徵兆">判讀徵兆</h2>
<p>某類偵測的命中數隨時間平緩下降，而沒有任何一次變更是為了減少它——先檢查判定是否綁在一份沒有更新的清單上，再接受「真的改善了」這個解釋。</p>
<p>判定邏輯裡只有一組正面列舉、沒有任何預設分支——這代表未列舉的一切都落進「不是」，而不是落進「未知」。</p>
<p>清單的更新來自偶然（讀到一篇文章、剛好看到一筆奇怪的資料），而非來自系統本身產生的訊號——這代表維護依賴外部注意力，而外部注意力不會在需要的時候到場。</p>
<hr>
<h2 id="跟其他原則的關係">跟其他原則的關係</h2>
<ul>
<li><a href="../new-data-shape-silently-changes-analysis/">#250 資料多出一種形狀時，既有分析邏輯靜默換語意</a>：同一次工作抽出的姊妹卡，兩者都描述「能力隨時間衰減而沒有訊號」。#250 的衰減由資料端變化引發（資料多出一種形狀），本卡的衰減由外部世界變化引發（世界多出一個項目）；兩者的處置也同構——都是加一條接住未涵蓋部分的預設路徑，並讓那條路徑的使用量可見。</li>
<li><a href="../lint-scope-must-be-explicit-fact/">#221 檢查規則的作用域要顯式列舉</a>：#221 處理的是規則管哪些檔案這個 fact 被編碼進路徑常數後不再被審視，本卡處理的是判定認得哪些對象這個 fact 被編碼進清單後不再被審視。兩者的常數都會過時，而過時的表現都是「通過的數量增加」——一個是零 error，一個是零命中。</li>
<li><a href="../self-audit-detection-method-must-match-rule-type/">#232 自審 sweep 的偵測方法要對齊規則類型</a>：本卡是它在時間軸上的延伸。#232 問的是偵測方法此刻看不看得見違反形態，本卡問的是這個「看得見」能維持多久——一個在設計時成立的對齊，會因為對象演化而在事後失效。</li>
<li><a href="../collapse-is-implicit-default/">#125 Collapse 是隱形預設</a>：清單型判定的預設是「沒列到就是不算」，而這個預設從未被決定過，它只是列舉式寫法的自然結果。本卡要求把那個預設拿出來明寫成退回層，屬於同一種「把沒人決定過的預設轉成有人決定過的設計」的動作。</li>
<li><a href="../validate-load-bearing-claim-before-building/">#236 承重論點先對抗驗證、再建下游</a>：清單在偵測系統裡是承重的——所有下游統計都建立在「被標記的就是全部」這個宣稱上。本卡的「用最舊的清單狀態推演」是針對這個承重宣稱的一種對抗驗證，成本低到可以在設計當下就跑完。</li>
</ul>
]]></content:encoded></item></channel></rss>