OSS Knowledge Base · 文件

系統與資料流

每天一次,排程 Worker 收集社群動態、整理成 thread,並把不可變的快照發布到 R2;靜態網站再透過小型唯讀 API 讀取這份快照。點選任一步驟,可以看到它做什麼、上次執行的數字,以及實作它的檔案。

流程圖

每日發布:從來源到畫面 點選任一步驟查看細節。

每日發布 cron

每個環境各有一個 Worker 與自己的 cron,Dev 與 Prod 不共用狀態。發布原本每小時一次,2026-10-13 起改為每天一次以降低 R2 成本(Spec 018)。

Dev
7 22 * * *(台北時間 06:07)
Prod
37 22 * * *
取捨
資料最多約 24 小時舊;要更早發布只能用需要 token 的手動觸發。

來源: apps/data-publisher-worker/wrangler.development.jsonc, apps/data-publisher-worker/wrangler.production.jsonc, docs/specs/018-daily-publication/spec.md

週報 cron 僅 Dev

Dev 發布後 40 分鐘,週報讀取剛發布的快照。若發布還在跑,就延後 15 分鐘,最多 4 次。

來源: docs/specs/014-topic-digest/spec.md, docs/runbooks/cloudflare-data-publisher.md

追趕與重試

某個來源達到單次上限的 run,5 分鐘後再跑一次(最多 5 次);失敗的 run 在 10、30、60 分鐘後重試。一次觸發最多造成 9 次發布。

來源: apps/data-publisher-worker/src/run-schedule.ts, docs/specs/018-daily-publication/spec.md

GitHub PR 與 issue

用 GitHub REST API 依更新時間游標讀取,每個 repo 每次最多 200 筆,其餘由後續的追趕 run 補上。

來源: docs/feature-map.md, docs/specs/004-replayable-github-feed/spec.md

Jira(KAFKA 專案)

匿名讀取 Apache Jira 搜尋 API,時間視窗依上次成功至今的間隔放大。

來源: packages/reference-pipeline/src/kafka-connectors.ts, docs/specs/012-kafka-mailing-list-jira/spec.md

Kafka dev@ 郵件列表

KIP 的討論與投票都在這裡。主旨以 [jira] 或 [PR] 開頭的信會被略過,因為這些已由 Jira 與 GitHub 收集。

共同規則
5xx 或 429 重試一次;回應超過 4 MiB 時該來源以 too-large 失敗;一個來源失敗不會擋住其他來源。

來源: packages/reference-pipeline/src/kafka-connectors.ts, docs/specs/012-kafka-mailing-list-jira/spec.md

GitHub PR review 僅 Dev

誰在何時 review 了什麼,不含留言內容;用於貢獻者頁面。

來源: docs/specs/022-github-reviews/spec.md

PipelineState

保存每個來源的游標、目前階段與上一次 run;程式中途掛掉時,會從最後提交的游標續跑,而不是從頭開始。

來源: apps/data-publisher-worker/src/index.ts, docs/architecture/decisions/0014-publish-per-source-with-independent-cursors.md

讀取來源

依游標逐一讀取每個來源。

來源: apps/data-publisher-worker/src/pipeline.ts

整理成 thread

確定性的 TypeScript materializer 把紀錄整理成 thread,並從標題與內文抽出 KIP-1150 這類編號。相同輸入一定得到相同輸出。

來源: packages/reference-pipeline/src/materializer.ts, docs/architecture/decisions/0006-use-typescript-reference-materializer-before-flink.md

寫出 Feed release

不可變的快照:一份列表加上每個 thread 的詳細物件。詳細物件以內容的 SHA-256 為鍵只存一次,之後的 release 直接重用(內容定址)。

最近一次
Dev 新增 791、重用 7,796 個物件;Prod 新增 378、重用 7,596 個

來源: packages/serving-contract/src/r2.ts, docs/specs/008-content-addressed-details/spec.md

建立搜尋索引

把每個 thread 切成段落,一次算好語料統計,寫出 BM25 分片與記錄每個詞在哪些分片的 terms.json。詳見搜尋。

最近一次
Dev 9,764 段、11 個分片;Prod 7,118 段、8 個分片

來源: packages/serving-contract/src/search-r2.ts, docs/specs/013-search-streaming/spec.md

切換指標、記錄證據

先寫物件,最後才更新小小的 current.json 指標,所以讀者看到的一定是完整的舊版或新版,不會看到一半。接著記錄這次寫了什麼、花了多少。

每次 R2 請求
Dev Class A 794 / Class B 8,590;Prod 381 / 7,977
不變式
Class A ≤ 新物件數 + 3

來源: apps/data-publisher-worker/src/pipeline.ts, docs/specs/006-environment-isolated-r2-publication/spec.md

追加活動紀錄 僅 Dev

為貢獻者頁面寫入每人每月的活動檔(PEOPLE_ACTIVITY=log@1)。

來源: docs/specs/019-contributor-profile/spec.md

產生段落向量 僅 Dev

把新的搜尋段落轉成向量供語意搜尋使用,每天上限 2,500 估計 neurons、400 次呼叫。

來源: docs/specs/016-semantic-search/spec.md

每週主題週報 僅 Dev

分類、摘要、驗證並翻譯過去 7 天的動態。詳見週報。

來源: apps/data-publisher-worker/src/digest/run.ts

指標

唯一會變動的物件;它指向的內容都不可變,所以回復只要改指標。

來源: docs/specs/008-content-addressed-details/spec.md

Feed release 與詳細物件

每次 run 一個 release;詳細物件依內容雜湊在各 release 間共用。

來源: docs/architecture/decisions/0013-share-detail-objects-by-content-digest.md

週報物件 僅 Dev

某一份來源 release 的英文週報與繁體中文翻譯。

來源: docs/specs/014-topic-digest/spec.md

內部 R2 物件

每次發布的證據,以及 Dev 上的 review 與活動月檔;不直接對外提供。

來源: docs/specs/008-content-addressed-details/spec.md

Vectorize 向量索引 僅 Dev

Cloudflare 的向量資料庫,放在可替換的介面後面,不成為對外契約。

來源: packages/semantic-vectorize/src/vector-index.ts

搜尋查詢閘門 僅 Dev

每個語意查詢都要經過的 Durable Object 帳本,讓公開搜尋框不會超支;超過預算就只回傳關鍵字結果。

來源: packages/semantic-vectorize/src/query-gate.ts

Feed 與詳細內容

先讀指標,再讀 release,再讀一個詳細物件。每個 200 回應快取 30 秒、stale-while-revalidate 120 秒。瀏覽器從不直接碰 R2。

來源: apps/web/functions/api, docs/architecture/decisions/0004-use-pages-functions-and-r2-serving-projections.md

週報與人員 Prod 404

因為 Prod 未開啟這些功能,所以在 Prod 回 404。

來源: docs/feature-map.md

This week Dev 有內容

週報:標題、重點、KIP 提案、主題卡片、收合的例行維護。

來源: apps/web/src/routes.ts

所有 thread 與搜尋

thread 列表與同一畫面的搜尋,以及每個 thread 的詳細頁(完整時間軸與來源連結)。

來源: apps/web/src/routes.ts

資料新鮮度

每個畫面都顯示資料多舊,發布器卡住時不必看 log 也看得出來。

來源: apps/web/src/freshness.ts, docs/specs/021-publication-overdue/spec.md

/health

公開、唯讀的上次執行狀態:各來源是否成功、寫了多少物件、R2 請求數、搜尋索引大小、今天的 AI 用量。不會回傳任何憑證。

來源: apps/data-publisher-worker/src/health.ts

verify:health

版本庫內的指令,只用 GET 比對 /health 與網站實際提供的資料,可對 Dev 或 Prod 執行。

來源: scripts/verify/health.ts

執行數字取自 2026-10-20 讀取的 Dev 與 Prod 發布器公開 /health(最近一次執行:2026-10-19 22:07 與 22:37 UTC)。

四個階段

  1. 收集。 各環境的 cron 觸發發布器。它從 Durable Object 裡保存的各來源游標讀取 GitHub、Jira 與 Kafka dev@ 郵件列表;一個來源失敗不會擋住其他來源,程式中途掛掉也會從停下的地方續跑(ADR-0014)。
  2. 處理。 apps/data-publisher-worker/src/pipeline.ts 的 runDataPublication 用確定性的規則整理 thread(發布流程中不使用語言模型),寫出 Feed release,並建立搜尋索引。
  3. 儲存。 R2 存放不可變的 release 與內容定址的詳細物件;只有小小的 current.json 指標會變,而且最後才寫。所以讀者看到的一定是一份完整的 release,回復也只要改指標(Spec 006、Spec 008)。
  4. 應用。 Pages Functions 讀 R2、回傳快取 30 秒的 JSON,由 Vue 網站呈現。部署程式碼不會發布資料:資料只來自 Worker(docs/operations/release.md)。

Dev 與 Prod

兩個環境完全隔離:Worker、Durable Object、R2 bucket 與 Pages 專案各自獨立,也不共用任何寫入憑證(ADR-0012)。

項目DevProd
網站oss-knowledge-base-dev.pages.devoss-knowledge-base.pages.dev
部署方式每次 push 到 main、所有 CI 工作通過後部署,接著對已部署的網站跑端對端測試從 main 可達的 v* tag(目前 v0.3.1,2026-10-19)
發布 cron22:07 UTC22:37 UTC
關鍵字搜尋 bm25-reference@3開開
每週週報開(22:47 UTC)關
語意混合搜尋開(SEARCH_SEMANTIC=rrf-group@2)關
貢獻者頁面、review 收集開關

每個只在 Dev 的功能都是一個在 Prod 未設定的開關,列在 docs/feature-map.md。新行為合併時開關是關的,再用另一個小變更逐一環境開啟;要回復時改設定即可,不必 revert(docs/process/workflow.md)。

為什麼這樣設計