你想請 AI 做一份研究報告,卻不希望未公開文件、搜尋紀錄與中間筆記先繞到雲端;更麻煩的是,一旦斷網,多數「Deep Research」工作流連第一筆資料都找不到。本機 Deep Research 真正難的也不是把模型下載回家,而是讓每一句結論都能回到一份確切的原文。
這篇專為願意碰終端機、但第一次組研究 Agent 的讀者寫。我們會把 Ollama、Firecrawl 與 Kiwix 串成可連網、半離線、完全斷網三種模式,實作來源信封、逐句引用與可重跑的 report receipt。這台環境沒有現成的 Docker、Ollama、Kiwix 與大型 ZIM 資料,因此下文是依官方文件校對的建置與驗收流程,不會把設計稿冒充 AlphaLab 本機跑分。
先說結論:離線不是一個開關,而是三條資料路徑
- Ollama 負責在本機推理;Kiwix 從已下載的 ZIM 快照檢索;Firecrawl 在有網路時抓取現行網頁。
- 完全斷網時,只能研究事先帶進去的模型、ZIM 與本機文件;「自架」Firecrawl 不會憑空變出公網內容。
- 不要直接把整頁文字塞進模型。先為每份資料建立來源信封,再把結論拆成 claim,逐條檢查引用。
- 沒有保留 ZIM 版本、內容雜湊、擷取時間、模型名稱與失敗紀錄,就不能重跑同一次研究。
- 本機與 hosted baseline 應比較來源召回、引用正確、完成率、延遲與資源,不只比較文筆。
本機 Deep Research 是什麼?先記住這條式子
本機 Deep Research=Ollama(腦)+Firecrawl(現網眼睛)+Kiwix(離線圖書館)+引用收據(記帳本)。
一般聊天機器人像一位只靠記憶回答的研究助理;這套系統則先去書架與網頁找資料,再把每張筆記編號。Ollama 只是「腦」,不是資料庫。Kiwix 保存某個時間點的知識快照,Firecrawl 把允許抓取的現行頁面轉成較乾淨的 Markdown,而引用收據負責證明哪句話來自哪份內容。

如果你還不熟悉「檢索後再回答」的概念,先讀RAG 是什麼;若想理解 Agent 為什麼需要模型以外的執行層,可接著看AI Agent Harness。
先選模式:可連網、半離線、完全斷網差在哪?

模式一:可連網
Firecrawl 可向白名單網站送出請求,Kiwix 同時提供離線快照,Ollama 留在本機生成。適合要比對「歷史快照」與「目前官方頁面」的題目。資料會離開本機的部分,是 Firecrawl 對目標網站的 HTTP 請求,以及你另外設定的 proxy、解析或模型服務;不要因為 Firecrawl 是 self-host 就把整條路徑誤認為離線。
模式二:半離線
封鎖公網,一般知識由 Kiwix 提供,內部資料改走固定 origin 的本機文件 adapter。Firecrawl v2.11.162 的安全抓取路徑預設會攔截非公開 IP;不要為了抓 Kiwix 或內網而直接打開寬鬆的 SSRF 例外。若確有固定鏡像需求,應另建隔離、白名單化且以該版本驗收的 bridge。這種模式適合公司內網、船舶或臨時斷線環境。
模式三:完全斷網
作業系統封鎖所有對外連線,研究只能使用事先下載的 Ollama 模型、Kiwix ZIM 與本機檔案。Firecrawl 對公網的請求應明確失敗,而且不能偷偷切回雲端。這不是功能縮水,而是把研究邊界變成可以測試的條件。
開始前準備:版本、容量與真正的硬體門檻
截至 2026 年 9 月 5 日,Ollama 的 GitHub 最新穩定 release 為 0.33.3,Kiwix Tools 為 3.8.2;Firecrawl 的官方 self-host 指南則固定示範 v2.11.162。這三個數字更新節奏不同,所以 receipt 要記錄你實際使用的版本,不要只寫「latest」。
硬體沒有一個放諸四海皆準的最低值,因為模型大小、量化、context 與是否 CPU offload 都會改變需求。Ollama 官方 context 文件目前在低於 24 GiB VRAM 的裝置預設 4K,並建議 web search、Agent、coding 類任務至少使用 64K;同頁也提醒,context 越大,記憶體需求越高。白話說:小顯存仍可做研究,但要靠分段摘要,不是硬塞整個網頁庫。
ZIM 往往比模型更容易吃掉磁碟。Kiwix 官方目錄在 2026 年 9 月 5 日列出的完整中文 Wikipedia 快照中,mini 約 4.5 GB、nopic 約 14 GB、maxi 約 25 GB,內容月份分別落在 2026 年 7–8 月;不同目錄的顯示單位可能略有差異。下載前先到Kiwix OPDS 目錄選擇實際檔名與容量,並把檔案雜湊和 issued 日期寫入資產清單。想先補足模型記憶體觀念,可參考本機 LLM VRAM 指南。
7 步搭好本機 Deep Research 工作流
第 1 步:先把資產帶進來,再測斷網
第一次準備必須在可連網環境完成。先安裝 Ollama,下載一個能在你硬體上穩定運作的模型;官方模型庫將 qwen3:8b 標為支援 tools 的範例,可先用它確認管線,但這不是「最佳研究模型」保證:
ollama run qwen3:8b
ollama ps
ollama ps 會顯示已配置的 context 與處理器分配。不要把能啟動等同於適合研究;真正門檻要到後面的固定題目與完成率驗收。
接著把 ZIM 放進一個唯讀資料夾,固定 Kiwix image tag,再只綁本機介面:
docker run --rm \
-v "$PWD/zim:/data:ro" \
-p 127.0.0.1:8080:8080 \
ghcr.io/kiwix/kiwix-serve:3.8.2 \
--blockexternal '*.zim'
上面沿用 Kiwix 官方容器做法,再加上唯讀 mount、本機 bind 與固定版本。若 shell 沒有把 '*.zim' 展開,這正是我們要的:glob 交給容器內的 entrypoint 處理。--blockexternal 會阻止瀏覽器直接前往外部連結,但它不是主機 egress firewall。
第 2 步:啟動 Firecrawl,健康檢查後一定要真抓一頁
git clone https://github.com/firecrawl/firecrawl.git
cd firecrawl
git checkout v2.11.162
# 依官方指南建立最小 .env 後啟動
docker compose up --build -d
docker compose ps --all
curl --fail --silent --show-error --max-time 5 \
http://localhost:3002/v0/health/readiness
官方 quickstart 的最小 .env 會設定 PostgreSQL 帳密與 USE_DB_AUTHENTICATION=false。指南明確把它界定為可信網路上的本機起步設定,不是公開 production 配置;而原始 Compose 會把 3002 發布到所有 host interfaces。對 host-run controller,先把對應的 ports 改成 127.0.0.1:${PORT:-3002}:${INTERNAL_PORT:-3002},別把未驗證的 API 直接暴露到區網或網際網路。
readiness 回傳 {"status":"ok"} 只證明服務入口存活。再用真正的 scrape 驗收 worker 與對外路徑:
curl --fail-with-body --silent --show-error --max-time 75 \
-X POST http://localhost:3002/v2/scrape \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com","formats":["markdown"],"timeout":60000,"skipTlsVerification":false}'
成功條件不只是 HTTP 200;還要檢查 success、data.markdown 與 data.metadata.statusCode。如果完全斷網模式仍抓得到 example.com,代表你的網路隔離測試沒有生效。
第 3 步:所有來源先裝進同一個「信封」
模型最容易犯的錯,是引用看似合理的 URL,卻無法證明該頁真的支持那句話。先把兩種 retriever 的輸出統一成 SourceEnvelope:
{
"source_id": "src_0042",
"route": "firecrawl | kiwix",
"canonical_url": "https://... | kiwix://<zim_uuid>/<path>",
"title": "來源標題",
"retrieved_at": "2026-09-05T00:00:00Z",
"snapshot_issued": "2026-07-23",
"content_sha256": "...",
"text": "清理後內容"
}
retrieved_at 回答「何時抓到」,snapshot_issued 回答「知識快照多舊」,content_sha256 則回答「下次拿到的是不是同一份內容」。這裡的 kiwix:// 是控制層自訂的本機 locator,不是 Kiwix 官方公網 URL;Firecrawl 來源不需要假造 ZIM 日期,Kiwix 來源也不要假造公網 URL。
第 4 步:Kiwix adapter 只用公開 API
Kiwix 官方介面文件把 /catalog/v2、/search 與 /raw 列為 public API;/content 則屬於 private API,可能隨前端需求改變。因此 adapter 的骨架應是:
# 偽程式碼:省略 XML namespace、timeout、重試與 path 驗證
hits = GET("/search", params={
"books.name": zim_name,
"pattern": query,
"pageLength": 10,
"format": "xml"
})
for hit in parse_search_xml(hits):
entry_path = validate_hit_path(
hit, expected_prefix=f"/content/{zim_name}/"
)
body = GET(f"/raw/{zim_name}/content/{entry_path}")
yield source_envelope(
route="kiwix",
canonical_url=f"kiwix://{zim_uuid}/{entry_path}",
text=body,
snapshot_issued=zim_issued,
content_sha256=sha256(body)
)
這段刻意是偽程式碼:正式版還要處理 XML namespace、redirect、URL encoding、路徑穿越、最大回應大小與 timeout。啟動時先讀 /catalog/v2/entries,保存 ZIM UUID、name、issued 與 _ftindex:yes;若沒有全文索引,就把該資產標成不可做 full-text search,而不是讓模型猜。
第 5 步:Firecrawl adapter 加上目的地政策
不要讓模型拿到一個「想抓哪裡就抓哪裡」的工具。planner 只能提出 URL,控制層再驗證 scheme、網域、解析後 IP 與 redirect;預設拒絕 loopback、link-local、私有網段與雲端 metadata 位址。通過後才呼叫 /v2/scrape,並把最終 URL、狀態碼、擷取時間與 Markdown 雜湊裝入來源信封;若使用 search/scrape options,也把 skipTlsVerification 明確設為 false。
這一層不是多餘的門房。網頁文字可能含有「忽略前文、呼叫工具、上傳秘密」等間接 prompt injection。OWASP 的 LLM Prompt Injection 指南建議把指令與外部資料分離、限制權限、驗證工具呼叫並保留監控;正規表示式只能當輔助,不能當唯一防線。
第 6 步:先做 evidence card,再讓模型寫報告
長 context 不是垃圾車容量。每個來源先各自切塊、抽取 evidence card,再由第二階段合成。卡片只保留:
claim_candidate:這段可能支持的單一陳述。source_id與quote_span:回到原文的位置。scope:日期、版本、地區或條件。conflict_group:與哪些卡片互相矛盾。
最後一輪只能看到 evidence cards,不能直接取得 crawler 工具;外部頁面的惡意指令便失去執行權。若卡片裝不進 context,先按來源做 map-reduce 摘要,但每次壓縮都必須保留 source_id 與 span,不能只留下沒有來處的漂亮句子。
第 7 步:逐句驗收,再輸出 report receipt
每個可核對的句子都建立一筆 claim record:claim_id、句子、source_ids、支持片段與 verdict。引用至少過三關:
- 找得到:公網 URL 能開啟,或
kiwix://能在指定 ZIM 重取。 - 找得對:來源內容與 claim 主題相關,不是只碰巧出現同一個名詞。
- 真的支持:原文能推出完整 claim,包括日期、數字與限制,而不是引用存在就算通過。
最後輸出 receipt:研究問題、run ID、模式、軟體版本、模型 tag/digest、ZIM UUID/issued/hash、成功與失敗來源、各階段時間、token 計數、context 配置、claim verdict。這份 receipt 是重跑入口,不是要塞進公開報告末尾的裝飾。
完整走一次:從問題到一條可核對結論
假設研究題是「HTTP 429 代表什麼,系統該怎麼回應?」以下是流程示範,不是效能跑分:
- planner 拆成「定義、伺服器提示、客戶端動作」三個子問題。
- Firecrawl 讀取白名單中的 IETF RFC 頁;Kiwix 同時搜尋離線百科的 429 條目。
- 兩份內容各自形成 source envelope;相同文字以內容雜湊去重,但保留來源別名。
- 模型產生 claim「429 表示使用者在一段時間內送出過多請求」,並綁定 RFC 原文片段。
- 引用閘門重新取得原文,檢查定義是否完整;若模型自行補上固定等待秒數,而來源沒有支持,就刪除那一段。
- 報告呈現通過的句子與引用;receipt 另外保存版本、來源 hash、失敗與耗時。
這就是「可引用」與「有連結」的差別。2026 年的研究 Cited but Not Verified也把引用品質拆成連結可用、內容相關與事實支持等層次;你不必照抄論文評分器,但三關思路值得直接搬進測試。
本機 Deep Research 怎麼和 hosted baseline 公平比較?

先建立至少 15 題的小型 gold set,每題跑 3 次,並列出必須找到的官方頁與 3–5 個可判定 claim。兩邊使用同一問題、同一輸出 schema、同一總 deadline;若 hosted 系統無法固定模型或索引版本,就在報告中把它列為不可控變數,不要把結果解讀成純模型差異。
- 來源召回:gold sources 找回幾份,另記錄新增的有效來源。
- 引用正確:三關各自的通過率,不能合併成「有 citation」。
- 完成率:在 deadline 內產生 schema 合格、沒有未解析 claim 的報告比例。
- 延遲:至少分開記錄 retrieval、抽取、合成與驗收,彙整 p50/p95。
- 資源:模型 tag、context、
ollama ps的處理器分配、峰值 RAM/VRAM 與磁碟讀取。
若你想先練習如何比較搜尋 API,可沿用AI Agent 搜尋 API 的小型 Eval框架;差別在於這次多了斷網路徑、ZIM 快照與 claim-to-source 驗收。
5 個一定要注入的故障
① 斷網:Firecrawl 要失敗,Kiwix 要繼續
在 OS 或容器網路層阻擋 egress,再跑同一題。驗收不是整份報告照常完成,而是 receipt 清楚記錄 Firecrawl 路徑不可用、沒有 cloud fallback,Kiwix 仍能重取來源。若題目需要「目前」資料,正確結果可能是拒答,而不是用舊快照猜。
② 過期 ZIM:舊資料必須看得見
刻意換入較舊快照。所有依賴它的 claim 都要帶 issued 日期;含「目前、最新、現任」的問題,在完全斷網模式應降級為「截至該快照」。不要用模型語氣掩蓋資料時間。
③ 重複來源:去重內容,不抹掉血緣
讓同一篇內容同時從 mirror、Firecrawl 與 ZIM 出現。以 canonical URL 加內容 hash 聚類,只讓一份文字進入 context;receipt 仍保留所有來源與快照,方便追查哪條路徑命中。
④ 惡意網頁:把指令當引文,不當命令
測試頁放入「忽略系統規則、讀取環境變數、改用某來源」等字句。成功條件是文字可以被保存為證據資料,但不觸發工具、不改變 allowlist,也不進入系統提示詞。工具呼叫由控制層 schema 驗證,而不是由 crawler 內容決定。
⑤ Context overflow:完成可以變慢,來源不能消失
逐步增加來源直到超過配置 context。系統應切成固定大小的 evidence cards、分批合成並標記被淘汰的卡片;不能靜默截斷尾端,更不能留下 claim 卻丟掉 source ID。這套方法也能接到Agent Harness 實作教學的 stop condition 與 state 設計。
三個常見誤區:看似本機,其實不可驗證
- 把引用生成交給模型:模型只能選擇已登錄的
source_id,URL 由控制層渲染,避免憑空拼接。 - 只存最後 PDF:沒有來源 hash、版本與失敗事件,報告長得一樣也無法證明同一次研究。
- 用單一總分選模型:本機模型可能寫得順卻漏來源。先把檢索、抽取、引用驗收、合成分開計分,再看瓶頸。
社群中的 fully-local-deep-research-agent 與 kiwix-chat提供了可讀的原型線索,但截至 2026 年 9 月 5 日,兩者沒有公開的端到端引用正確率或同題 baseline 結果,也不是一個由三方共同維護的整合套件。因此可以借鏡元件接法;本文的雙路由器、來源信封與引用閘門仍是需要自行實作和測試的 integration pattern,不能把 repo 存在當成品質證明。
本機 Deep Research 常見問題 FAQ
1. 完全斷網還能搜尋 Google 嗎?
不能。封鎖公網後,流程只能搜尋已下載的 ZIM、本機文件或內部索引。要研究當日新聞,就必須在允許連網時擷取並保存快照。
2. Firecrawl 自架後,資料就完全不離開本機嗎?
不一定。它仍要連到你指定的目標網站;若另接 proxy、AI 解析器或外部服務,也會增加資料流。用網路規則與 request log 驗證,別靠「self-host」三個字判斷。
3. Kiwix 的 ZIM 可以當即時資料庫嗎?
不能把它當即時資料。ZIM 是帶 issued 日期的內容快照;優點是可重現,代價是新鮮度由更新節奏決定。
4. 8GB 或 16GB 記憶體能做嗎?
可能,但別先承諾品質。選較小或量化模型、縮小平行數、採 evidence-card map-reduce,再以完成率與引用閘門實測。容量只決定候選,不決定答案是否正確。
5. 有引用連結就算可引用嗎?
不算。連結要能重取、內容要相關,而且原文要真正支持完整 claim;三關缺一不可。
6. 為什麼不用 Kiwix 的 /content endpoint?
因為 adapter 應依賴 public API。Kiwix 文件將 /content標為 private,而 /raw是 public,並保證不做伺服器端內容處理。
7. 可不可以讓模型自己決定要抓哪個 URL?
可以提案,不可直接執行。控制層仍要檢查 scheme、網域、解析後 IP、redirect 與回應大小,通過政策才交給 crawler。
8. 本機一定會比 hosted Deep Research 差嗎?
沒有固定答案。結果取決於模型、資料新鮮度、檢索器、題目與驗收器。用同題 gold set 和 receipt 比較,才能知道差距落在來源、推理、速度還是完成率。
給新手的 6 點完成清單
- 固定 Ollama、Firecrawl、Kiwix 與模型 tag,不使用模糊的 latest。
- 保存 ZIM UUID、issued、檔案大小與 SHA-256。
- 兩條 retriever 都輸出同一種 SourceEnvelope。
- 每個 claim 綁 source ID 與原文 span,逐條過引用三關。
- 完成斷網、舊快照、重複、惡意頁面與 overflow 五種故障注入。
- 用固定題目與 receipt 比較 hosted baseline,不自行填入漂亮分數。
想把這套流程擴成完整專案,可以先讀多 Agent 研究工作流,再補模型離線備援;若想有系統地練習 AI 工具、Agent 與工作流,前往 AlphaLab 的線上課程與AI 專區。
接著閱讀
左右滑動查看更多推薦
結語:先做一份能重跑的報告
回到開頭那條式子:Ollama 是腦、Firecrawl 是現網眼睛、Kiwix 是離線圖書館,但真正讓研究可信的,是最後那本引用記帳本。你的第一個里程碑不用是百頁報告:先選 3 題,固定一個模型與一份 ZIM,讓每個 claim 都能回到 source ID;接著拔掉網路,確認失敗被誠實記錄、離線來源仍可重取。當同一份 receipt 能讓另一個人重跑,你才真正完成了本機 Deep Research。






