你在應用程式加上 provider.zdr: true,合作方接著問:「圖片也算嗎?換一條供應路徑呢?失敗時會不會偷偷放寬?」這篇 OpenRouter ZDR 教學,就是把這三個問題拆成能操作、能留證據的檢查。
寫給第一次碰 API 資料保留設定、願意在終端機貼上程式的新手。你會先看懂資料流,再用四組合成資料設計驗收;需要 Python 3、自己的 OpenRouter API key 與可用額度。每次實際送出都可能產生用量。本文依 2026 年 11 月 6 日官方文件整理,提供驗收方法,未執行付費推論,也未預填服務端測試結果。
先說結論:可用路徑=模型能力 ∩ 保留政策 ∩ 適用限制
ZDR 是一道推論路由篩網;完整資料治理還要逐站查帳。把它想成寄文件:先決定哪些承運商能接件,再檢查收發室、快遞站和收件人各自會留什麼紀錄。收到簽收單,能證明一次交付,還不足以證明每站都已銷毀副本。
provider.zdr: true會啟用 ZDR 路由要求,但仍遵循帳戶與政策對 Safety Retention 的選擇。- 嚴格排除 Safety Retention,要明確設定
ZDR only;「不用於訓練」和「不例行保存」是不同問題。 - 四組驗收要同時收成功與拒絕紀錄;沒有合格路徑時,應用程式的正確動作可以是停止。
一、先畫資料流:文字、圖片和工具分開盤點
API 是軟體彼此傳送請求的介面;endpoint 是某個模型的具體服務入口。相同模型名稱,可以由不同供應商、地區或變體提供。先把你的應用程式、OpenRouter、推論供應商畫成三站,再加上圖片網址來源與外部工具。

第一站常被忽略:自己的伺服器可能把完整請求寫進除錯紀錄、APM(應用效能監控)或錯誤追蹤。這些是你要另外配置的資料出口。先用 TEST-001 這種假內容,檢查哪個系統會收進它,再決定正式環境允許記哪些欄位。
OpenRouter 也把內容紀錄與用量中繼資料分開。資料蒐集說明記載,私人 Input & Output Logging 與允許 OpenRouter 使用輸入/輸出,是兩個選用設定;Token 數與延遲等請求中繼資料仍會保存。中繼資料像包裹追蹤號,並不等同包裹全文。
圖片若用外部網址,還多了誰下載、網址是否含識別碼、來源站留下什麼存取紀錄的問題。下方練習改用程式產生的紅色色塊,以 data URL 直接傳入,縮小外部依賴;這仍是把圖片送給推論服務處理。搜尋或工具服務則各查各的條款,推論 ZDR 過濾不自動替整條工具鏈背書。
二、OpenRouter ZDR 教學:先選清楚三種保留政策
你真正要回答的不是「有沒有勾 ZDR」,而是「容許哪些保存目的與例外」。官方 ZDR 文件把端點分成 ZDR、Safety Retention、Retention and Training 三層。前者指處理後不例行保存;中間層允許為防止濫用保留資料,且不必先被標記;最後一層則依供應商條款,可能保存並用於訓練。
Safety Retention 限制的是保存目的,不是保證只留可疑請求。截至本次查閱,未另行選擇時,自助帳戶預設允許 Safety Retention,企業帳戶預設 ZDR only。既有帳戶可能已被人改過,實作時記錄實際選項,不從方案名稱猜設定。
另有一個具體例外值得寫進合作規格:OpenRouter 的 2026 年 10 月 27 日公告表示,規劃中的 CSAM(兒童性虐待素材)處理中篩查也涵蓋 ZDR 流量;被標記的內容可能為人工審查、通報與相關依法保存而保留。這是公告中的政策邊界,本文未核實其全面部署進度。不要把 ZDR 對外承諾寫成「任何情況都零保存」。
三、設定三層篩網:帳戶、存取政策、單次請求

① 怕預設值不同?先從帳戶設定建立基準
把需求寫成明確選項。在 Settings > Privacy 核對適用模型組的 ZDR。若需求是嚴格排除 Safety Retention,分別把 Google 與 All other models 的相關選擇設為 ZDR only,並確認所用模型組確實啟用 ZDR。別為了做對照而放寬正式帳戶;四組練習放在已授權的測試環境。
另外到 Observability 檢查私人輸入/輸出紀錄與任何外送目的地,在 Privacy 檢查輸入/輸出使用選項。I/O Logging 文件說明這個功能會保存完整內容;因此測路由與測紀錄設定應分兩格驗收。先確認現在收什麼,再另行處理過去已保存的資料。
② 團隊多人共用?把 ZDR 放進真正適用的存取政策
區分硬性限制和預設分配。依 目前 Guardrail Policies 文件,在 Workspace > Guardrails 管理 Model & provider access 政策,並在 Assignments 核對成員與 API key;組織管理員透過 Account > Guardrails 管理 workspace-wide 政策。建立政策後,還要檢查它究竟套到誰。
Workspace-wide 限制不能被成員或 key 的政策放寬;工作區內的預設成員政策則可由個別政策取代,仍受上層限制。這是 10 月 30 日改版要釐清的分工。若管理既有 Guardrails API,Safety Retention 的欄位是 allow_safety_retention_google 與 allow_safety_retention_other;排除時為 false。它們是政策設定,本文不把它們塞進推論請求。
③ 怕某次請求漏套?用 provider.zdr 加一道要求
單次請求能加嚴 ZDR。帳戶、適用政策與請求的 ZDR 以 OR 結合:任一要求,就會啟用。因此 zdr: false 或省略它,仍受上層 ZDR 限制。白話說,員工自己的寄件單不能取消公司的收件規則。
供應商限制同時生效。Provider Routing 文件區分 order 的優先順序與 only 的允許範圍;供應商基本 slug 可能匹配多個端點變體。要驗特定入口,從模型頁供應商旁複製完整 endpoint slug,別只記公司名。
四、OpenRouter ZDR 教學實作:四組假資料怎麼安排?
先定義成功條件,再送請求。你需要一個當前可用、同時支援文字與圖片輸入的模型;在模型頁核對輸入能力、供應端點及政策。若兩種輸入用不同模型,分開記錄,別把差異直接歸因給圖片。以下四組各跑文字和圖片一次,共八次呼叫;這是練習設計,不是服務成功率。
- A:一般請求。保留測試環境既有設定,省略
provider.zdr,記下路由結果;上層若已要求 ZDR,這組也會受限。 - B:加入 ZDR。其他條件不變,送
provider.zdr: true;同時抄下 Safety Retention 是允許還是排除。 - C:明確 ZDR only。先在帳戶/適用政策排除 Safety Retention,再用同一模型與假內容重跑。B、C 即使回傳同一供應商,也可能是本次未遇到差異。
- D:故意不相容。維持 C,把
only限到一個已確認不合格的端點並關閉供應商 fallback;預期應拒絕,而應用程式保持停止。
D 組要先核對這個端點確實提供該模型、支援該輸入,且被目前保留政策排除;否則失敗可能只是拼錯 slug 或圖片不相容。若當前模型找不到合適反例,將 D 記為「前置條件不足」,另做本機錯誤注入,驗應用程式停點;不要填成路由拒絕已通過。
也可以讀取 公開 ZDR 端點清單作當時的篩選證據。這份清單也列有 Safety Retention 條目,要一起看 safety_retention,再對照模型頁與合約;只因名稱出現在清單內,就當成嚴格零保留,會漏掉關鍵條件。
貼上這支最小請求器,逐組執行
把下方存成 probe.py。它只用 Python 標準函式庫;key 從既有環境變數讀取,否則用隱藏輸入提示取得。請先在模型服務端設定測試用量上限;程式限制回覆長度,仍不等於精確費用上限。設定方法可接著讀 OpenRouter 花費上限教學。
import base64, getpass, json, os, struct, sys, zlib
from urllib.request import Request, urlopen
from urllib.error import HTTPError, URLError
stage, mode = sys.argv[1:3]
if stage not in ('A', 'B', 'C', 'D') or mode not in ('text', 'image'):
raise SystemExit('用法:python3 probe.py A|B|C|D text|image')
model = os.environ['OPENROUTER_MODEL']
key = os.environ.get('OPENROUTER_API_KEY') or getpass.getpass('API key: ')
provider = {} if stage == 'A' else {'zdr': True}
if stage == 'D':
provider.update(only=[os.environ['INCOMPATIBLE_ENDPOINT']],
allow_fallbacks=False)
content = [{'type': 'text', 'text': '合成驗收資料 TEST-001。請回覆 OK。'}]
if mode == 'image':
def chunk(kind, data):
return (struct.pack('!I', len(data)) + kind + data
+ struct.pack('!I', zlib.crc32(kind + data) & 0xffffffff))
png = (b'\x89PNG\r\n\x1a\n'
+ chunk(b'IHDR', struct.pack('!2I5B', 256, 256, 8, 2, 0, 0, 0))
+ chunk(b'IDAT', zlib.compress((b'\x00' + b'\xff\x00\x00' * 256) * 256))
+ chunk(b'IEND', b''))
uri = 'data:image/png;base64,' + base64.b64encode(png).decode()
content.append({'type': 'image_url', 'image_url': {'url': uri}})
body = {'model': model, 'messages': [{'role': 'user', 'content': content}],
'provider': provider, 'stream': False, 'max_completion_tokens': 64}
req = Request('https://openrouter.ai/api/v1/chat/completions',
data=json.dumps(body).encode(), method='POST',
headers={'Authorization': 'Bearer ' + key,
'Content-Type': 'application/json'})
try:
with urlopen(req, timeout=60) as response:
status, raw = response.status, response.read()
except HTTPError as error:
status, raw = error.code, error.read()
except (URLError, TimeoutError):
raise SystemExit('STOP:傳輸失敗;不切換供應商、不放寬政策。')
try:
data = json.loads(raw)
except ValueError:
raise SystemExit('STOP:回應不是 JSON。')
error = data.get('error')
choices = data.get('choices', [])
choice_error = any(c.get('error') or c.get('finish_reason') in
('error', 'content_filter') for c in choices)
has_text = any(isinstance(c.get('message', {}).get('content'), str)
and c['message']['content'].strip()
for c in choices)
print(json.dumps({'stage': stage, 'input': mode, 'http': status,
'id': data.get('id'), 'model': data.get('model'),
'provider': data.get('provider'),
'error_code': error.get('code') if isinstance(error, dict) else None,
'choice_error': choice_error,
'has_text': bool(has_text)}, ensure_ascii=False))
if not 200 <= status < 300 or error or choice_error or not has_text:
raise SystemExit('STOP:請保留最小紀錄,查明原因後再處理。')
if stage == 'D':
raise SystemExit('STOP:D 組意外成功;先查政策與端點,不放行下游。')
在終端機執行 python3 --version,確認已有 Python 3,再把下方模型佔位文字換成你剛核對的完整 ID。每次呼叫只跑一組,先讀結果再往下:
export OPENROUTER_MODEL="貼上已確認的模型ID"
python3 probe.py A text
python3 probe.py A image
python3 probe.py B text
python3 probe.py B image
# 先完成 C 的 ZDR only 設定並保存設定證據
python3 probe.py C text
python3 probe.py C image
# 確認 D 的不相容端點與輸入能力後才執行
export INCOMPATIBLE_ENDPOINT="貼上已確認的不合格完整slug"
python3 probe.py D text
python3 probe.py D image
這支教學程式刻意用非串流、單次呼叫,不做自動重試,也不印出完整提示、答案或錯誤訊息。HTTP 200 仍可能有頂層或 choice 內的錯誤,程式會停止;D 組若意外成功也停止,要求你查明設定。256×256 合成圖片若不符所選端點的尺寸規格,或 64 個輸出 Token 被推理用盡而沒有可見文字,先分類為輸入/輸出限制,再調整合成案例,別算成 ZDR 拒絕。正式整合還需增加回應 schema 檢查、連線失敗分類、監控與測試資料清理;串流版則要另外處理中途錯誤,不能只照搬這段。官方錯誤處理文件說明 HTTP 與回應內容的錯誤路徑。
五、怎麼驗收:別讓「成功」掩蓋政策錯誤
每次保留時間、測試 key 的內部代號、模型 ID、適用政策版本、Safety Retention 選項、文字/圖片類型、HTTP 狀態、請求識別與供應商。不要把金鑰本身或客戶內容放進表單。回應中的 provider 若不足以區分 endpoint,到 Logs 的該筆請求補查供應資訊;仍無法確認就記「端點未確認」。
A 到 C 要驗可用性與符合要求的路徑,D 要驗拒絕後沒有繼續。成功回應只能支持「這次請求在這組設定下回來了」;保留政策與協議支持的則是資料如何被處理。把這兩種證據各自收好,比把八次成功截圖當成零保留認證更有用。
失敗先分類:輸入不合法就修假資料;權限或用量問題回到帳戶處理;沒有符合政策的供應路徑就停止或交給有權限的人選擇新方案。短暫限流/服務錯誤可採有上限退避,依 Retry-After 等待,重送時保留相同政策。每次嘗試也可能增加用量。
把應用程式停點寫成一條可檢查規則:政策不滿足、回應含錯誤或端點無法確認時,不把答案交給下一個會寫入或寄送的工具。不要在錯誤分支移除 zdr、改成允許 Safety Retention,或繞過平台另接一條未核准路徑。這是你要實作的安全行為,不是聲稱任何 SDK 都已替你完成。
六、交給合作方的證據,要能回答哪幾件事?
把交付分成設定、行為與書面條款:設定證明何時、哪個 key 受到哪些限制;行為紀錄呈現成功與拒絕;書面條款則釐清內容種類、圖片、備份/快取、安全審查、依法保存、次處理者與刪除責任。對外描述要寫適用範圍,別只傳一張勾選畫面。
自己的資料保留表也要填負責人、紀錄目的、保存位置、期限與刪除程序。若合作要求排除某種例外,先請有權限的人確認是否有可用的合約與技術路徑,再讓正式資料進入。一次推論成功,無法替代這個採用決策。
常見問題:八個先講答案的判斷
1. provider.zdr 設 true,就等於嚴格零保留嗎?
還要確認 Safety Retention 選項與明列例外。請求會沿用帳戶與適用政策的選擇。
2. 我把 zdr 設 false,可以做無限制基線嗎?
仍受上層限制。A 組只省略請求層要求,不能據此推論帳戶 ZDR 已關閉。
3. 不拿去訓練,就代表不保存嗎?
不是同一個承諾。防濫用保存與模型訓練要分開核對。
4. 圖片用 data URL 就不會外傳嗎?
圖片仍會送到推論服務。data URL 在本練習的用途,是減少一個外部圖片網址依賴。
5. 回傳供應商名稱,能證明已刪除資料嗎?
那是路由線索。刪除與保留責任,要對照端點政策、協議及其可提供的稽核材料。
6. ZDR-only 沒有合格端點,該讓它自動降級嗎?
先停止。若業務要改採不同保存條件,應另做有權限的選擇,而不是藏在重試分支。
7. 搜尋工具會自動繼承推論 ZDR 嗎?
仍要逐項查工具政策。推論端點過濾與搜尋、圖片來源、應用日誌是不同資料出口。
8. 四組測試通過,就能對外保證所有情況嗎?
它們只覆蓋記錄下來的配置與案例。換模型、key、政策、圖片來源或工具後,針對變動部分重新驗收。
給新手的三個重點
- 先把「容許什麼保存」寫成規格,再選 ZDR 與 Safety Retention;不要反過來由介面勾選推導承諾。
- 記錄完整端點與有效政策;同公司、同模型不代表同一條處理路徑。
- 把拒絕當成正式交付的一部分:錯誤後停止,才看得出限制真的影響下一步。
接著閱讀
左右滑動查看更多推薦
下一步:先讓一條不合格路徑確實停下來
今天先完成一張資料流圖、保存有效設定,再用合成文字與紅色色塊逐組填紀錄。遇到不合格路徑時,確認程式停住,且下一個工具沒有被觸發。記住:可用路徑=模型能力 ∩ 保留政策 ∩ 適用限制。想把這套驗收擴成自己的 AI 系統,可以從 AlphaLab AI 專區繼續閱讀,或透過 AlphaLab 課程安排下一段學習。
