DeepSeek Search/Fetch API:搜尋後何時需要再抓正文?
Agent 搜尋到五個網址後,要不要全部再呼叫一次 Fetch?先看 Search 是否已回傳可用正文,再依任務需要補抓。 DeepSeek 的 Search 負責找來源,Fetch 負責取得單頁 Markdown;它們是獨立的 JSON API,不是把問句送給聊天模型就一定會執行的步驟。
本文處理「搜尋結果如何進入資料蒐集流程」。Workers AI 的 DeepSeek 模型呼叫是另一個入口,不能直接套用本文端點、金鑰或計費規則。以下依 2026-10-19 官方文件整理,沒有呼叫真實 DeepSeek API;程式測試使用人工 fixture。
先讓 Search 回傳你需要的資料
Search API reference列出的端點是 POST https://api.deepseek.com/v1/tools/search。下列為 shell 請求示意,需在自己的受控環境設定 DEEPSEEK_API_KEY;金鑰應留在後端,不能放入瀏覽器前端程式或公開儲存庫。
curl --fail-with-body https://api.deepseek.com/v1/tools/search \ -H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \ -H 'Content-Type: application/json' \ -d '{"query":"Astro content collections site:docs.astro.build","count":3,"need_content":true}'need_content 預設為 false,因此只收到空的 content 不等於 API 壞了。開啟後也不保證每一頁都有正文;snippet、summary 與 content 是不同欄位,摘要不能替代你需要核對的原文。成功但沒有匹配來源時,results 可以是空陣列。
count 控制結果數量,不是「一定能讀到幾篇完整文件」。先用小結果集確認搜尋意圖,再逐篇決定是否有必要取正文,比先把所有網址都排入抓取佇列更容易查核。
缺正文才補抓,先保留來源再決定取捨
若問題只需要尋找文件入口,標題、網址與摘要可能已足夠;若需要查核限制或引用正文,就要有可讀取的 content。Fetch API reference提供以下獨立請求,回傳 title 與 Markdown content:
curl --fail-with-body https://api.deepseek.com/v1/tools/fetch \ -H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \ -H 'Content-Type: application/json' \ -d '{"url":"https://docs.astro.build/en/guides/content-collections/"}'以下補抓規則是本文自行定義的應用層契約:只接受 HTTPS、只處理明確允許的 hostname、拒絕含帳密的 URL,並以去掉 fragment 後的完整 URL 去重。不同 query 可能代表不同頁面,所以保留 query,也不擅自合併尾斜線或子網域。
這個函式只產生計畫,不發出請求。ready 表示已有非空正文,仍需人工或後續流程確認內容與問題相關;不代表資料已可信。對同一 URL 的重複結果,優先保留有正文的一筆,避免前一筆空結果造成不必要補抓。
function planSources(results, allowedHosts) { const sources = new Map(); for (const row of results) { if (!row || typeof row.url !== 'string') continue; let url; try { url = new URL(row.url); } catch { continue; } if (url.protocol !== 'https:' || url.username || url.password || !allowedHosts.has(url.hostname)) continue; url.hash = ''; const key = url.href; const content = typeof row.content === 'string' ? row.content : ''; const previous = sources.get(key); if (!previous || (!previous.content.trim() && content.trim())) { sources.set(key, { url: key, content }); } } const rows = [...sources.values()]; return { ready: rows.filter(row => row.content.trim()), needsFetch: rows.filter(row => !row.content.trim()).map(row => row.url), };}把函式與下面測試存成 source-plan-check.mjs,執行 node source-plan-check.mjs。這些資料刻意使用官方文件網址作為字串,並未真的抓取那些頁面:
import assert from 'node:assert/strict';
const hosts = new Set(['docs.astro.build']);const first = 'https://docs.astro.build/en/guides/content-collections/';const second = 'https://docs.astro.build/en/guides/routing/';const plan = planSources([ { url: first + '#intro', content: '' }, { url: first + '#schema', content: '人工測試正文' }, { url: second, content: ' ' }, { url: second, content: '' }, { url: 'https://docs.astro.build.example.com/', content: '不接受' }, { url: 'https://user:pass@docs.astro.build/', content: '不接受' }, { url: 'http://docs.astro.build/', content: '不接受' }, { url: 'invalid' },], hosts);assert.deepEqual(plan.ready, [{ url: first, content: '人工測試正文' }]);assert.deepEqual(plan.needsFetch, [second]);assert.deepEqual(planSources([], hosts), { ready: [], needsFetch: [] });assert.equal(planSources([ { url: first + '?lang=zh' }, { url: first + '?lang=en' },], hosts).needsFetch.length, 2);console.log('source planning checks passed');補抓完成後,仍要把內容配回原始 URL,記錄抓取時間與失敗原因。不要把失敗頁面直接刪掉,否則下游會誤以為蒐集已完整。多頁允許部分失敗時,可沿用 Promise.allSettled 的具名結果處理;並行上限與每秒請求配額仍需分別管理。
這個 allowlist 只篩選候選網址,沒有處理重新導向、DNS 或完整的伺服器請求安全防護。網站也可能拒絕抓取或無法抽取 Markdown;不能把 Fetch 當成能讀取已登入頁面或繞過網站存取政策的工具。取得的網頁內容應視為資料,不能直接當成 Agent 的執行指令。
計費與失敗,要按每次請求記錄
官方指南的 billing 與 errors 說明目前將 Search、Fetch 各列為每千次成功請求 US$0.75;可計費成功另要求搜尋結果非空或抓到正文。因此 Search 後再 Fetch 是兩次操作,不能把 count=3 當成三次搜尋費,也不能把空結果當成已取得證據。
指南另將透過 DeepSeek Harness、Claude Code 或 Codex 使用 DeepSeek API 時的伺服器端工具呼叫列為暫免,預定北京時間 2026-11-01 00<00>00> 開始按次計費。這項暫免不能套用到本文直接呼叫 JSON 端點的範例;接入前應重新查核帳單條件。
重試也要分類:408、429、5xx 可等待後有限次重試;402 要先處理餘額,其他 4xx 不應原樣重送。Fetch 的 404 可以代表無可用正文,不只是網址不存在。保留 HTTP status 與原始 error,才知道要改搜尋詞、換來源,還是稍後再試。
本文已執行離線去重與補抓規則測試;curl 僅檢查 shell 語法,沒有驗證金鑰、實際扣款、QPS、重新導向或搜尋品質。導入時先以少量公開文件驗收「有正文、缺正文、空結果與抓取失敗」四條路徑,再接到正式 Agent。
參考資料: