WikiNB 系統技術架構
公開 Markdown 網站、Kaine 限定聊天、Cloudflare Worker/D1 與本機 Bridge 的三層系統說明。
WikiNB 系統技術架構
定位與功能頁
產品功能、內容原則與公開使用方式請先讀 Projects/Knowledge/wikinb。
WikiNB 同時包含三個權限與部署完全不同的執行層:
- 公開內容層:Astro 將
wiki/Markdown 建成 GitHub Pages 靜態網站。 - 訪客 AI 層:Cloudflare Worker、D1、Email OTP 與 Gemini API 提供「Kaine」限定聊天。
- 私人維護層:Kaine 本機 Bridge 管理 Markdown、Git 同步與 Codex CLI。
訪客通過 Email 驗證只取得聊天權限,不會因此取得 Wiki 寫入、同步、管理者登入或本機 Codex 權限。
狀態標記:
- 已實作於來源:目前程式碼可直接確認。
- 既有文件記錄/本輪未重跑:工程文件有明確紀錄,本次沒有重做 live 驗證。
- 需要獨立部署:source 已存在,但只有完成對應 Pages 或 Worker 發布後才會影響正式站。
已確認技術
公開 Markdown 與搜尋
- Astro 5 + Tailwind:輸出靜態首頁、巢狀 Wiki、搜尋、登入、聊天與管理入口。
- Gray Matter + Marked:讀取 frontmatter、轉換 Markdown,並以 repository Git 日期補足最近更新資訊。
- 巢狀 slug:
wiki/<folders>/<name>.md對應/wiki/<folders>/<name>/;slug 正規化會擋下.、..與空路徑。 - Wiki link:
folder/note在建置時轉成站內連結;圖片路徑、responsive class、lazy loading 與 URI encoding 也在 parser 層處理。 - 搜尋索引:建置時把標題、description、tags、folder 與純文字內容形成 search data;瀏覽器端可重建巢狀樹、依關鍵字過濾並展開命中路徑。
- Metadata 優先序:顯示用
_meta.json覆寫可優先於 frontmatter;沒有覆寫時才依 frontmatter、H1 與檔名回退。
Kaine 限定聊天
- 第一人稱定位:system prompt 明確把模型定位為「Kaine」,以「我」回答公開經歷、作品、專案、技能與合作方向;不自稱數位助理、分身或 Gemini。
- 不使用私人人格資料:Worker 只使用 repository 內不含個人資料的通用回答風格;沒有 runtime persona secret,也不讀取或上傳私人 persona 原文/摘要。
- 非通用聊天:Worker 在呼叫 Gemini 前先以固定規則判斷問題是否屬於 Kaine 公開內容。微積分教學、作業代寫等一般問答直接回覆額度說明,不把請求送到模型。
- 4–5 則確認門檻:程式允許設定 4 或 5,超出範圍會被 clamp;目前公開設定為每個已驗證 Email、每個台北日期先開放 5 則訊息。
- 續聊行為:第 5 則仍會得到回答,回覆末端詢問是否續聊。訪客選擇繼續前,介面先說明系統會寄信通知 Kaine;信中不附聊天內容。寄信成功後解除則數門檻,同一 Email 每日只通知一次。
- 額外成本保護:無關問題也計入前 5 則,但不呼叫 Gemini;續聊後仍受每日 token 總上限與 burst rate limit 保護。
RAG 與模型請求
- Worker 從公開搜尋頁的內嵌索引載入 Wiki 資料,並在 edge cache 保存約十分鐘。
- 查詢會產生英文詞與中文二至四字片段;title、tag、description 與 body 採不同權重排序。
- 每次最多選四頁,總 corpus 約 6,500 字元;一般頁截取較短,專案總覽可保留較長內容。
- 「一個代表專案」有固定編輯順位;「全部專案」優先使用專案總覽,不由目錄順序隨機決定。
- 只傳最近四則 history,每則再限制長度;模型使用 minimal thinking,沒有設定
maxOutputTokens硬截斷。 - 一般回答要求 1–5 句;詳細請求改成短條列、延伸閱讀與聯絡路徑,不生成無限制長篇內容。
- API 的 429 不重試;500、502、503、504 或網路失敗最多重試一次。
驗證、額度與資料保存
- OTP 有期限、錯誤次數、IP/Email/全域速率限制與重新寄送等待時間。
- 驗證成功後使用有期限的 HMAC 簽章 guest token;前端把 token 放在
sessionStorage,關閉分頁工作階段後不作長期保存。 - D1 保存 OTP request、rate limit 與每日使用統計;聊天則數使用不可直接還原 Email 的雜湊 rate key。
- 每日 token 記錄優先採 Gemini 回傳 usage metadata;缺少 metadata 時使用輸入與輸出字元估算。
- 同一 token 的聊天請求還有數秒 burst limit,避免快速重複送出。
私人 Bridge
- Express Bridge 在 Kaine 的裝置上執行,處理帳密 + Email OTP、短期管理者 session、Wiki tree、上傳、建夾、改名、覆蓋、刪除、metadata 與 Git 同步。
- Codex chat 透過已登入的 Codex CLI 執行,使用 read-only、ephemeral sandbox;模型只輸出回答,不直接改 Wiki。
- 真正的 Markdown 寫入與 Git 操作由 Bridge 的明確管理 API 負責,與訪客 Worker 完全分離。
系統與資料流
公開內容建置
wiki/**/*.md + frontmatter + wiki/_meta.json
↓ src/lib/wiki.ts
頁面資料、巢狀樹、search index、最近更新
↓ Astro build
dist/
↓ GitHub Actions
GitHub Pages
Markdown 是長期可攜的內容 source;Astro 只是呈現層。搜尋與 AI 檢索都讀同一批公開內容,但採不同的執行時間:搜尋索引在 build 生成,Worker 在執行期抓取並短暫 cache。
訪客 Email 驗證
訪客輸入名稱 + Email
↓ Worker 速率檢查
產生 OTP → 雜湊後寫入 D1 → SMTP 寄送
↓ 訪客提交六位數驗證碼
比對期限/錯誤次數/雜湊
↓
簽發有期限 guest token → 前端 sessionStorage
管理者登入走本機 Bridge 的另一套 session。兩套 token 不能互用。
聊天請求
message + 最近 history + guest token
↓
Worker 驗證 token、burst、每日 token 與當日則數
↓ 原子保留一則
問題是否屬於 Kaine?
├─ 否 → 固定婉拒,不呼叫 Gemini
└─ 是
↓ 載入/快取公開搜尋索引
最多 4 頁、約 6,500 字元 corpus
↓ system prompt + 通用回答風格 + 最近 history
Gemini generateContent(minimal thinking)
↓
記錄使用量 → 回傳 answer 與剩餘狀態
↓ 若為第 5 則
詢問是否續聊並暫停前端輸入
↓ 訪客確認且通知信寄送成功
解除則數門檻;保留每日 token 上限
私人維護與發布
管理者瀏覽器
↓ 帳密 + OTP
本機 Bridge
├─ Wiki 管理 API ──> wiki/、metadata、公開資產
├─ Codex API ──────> read-only Codex CLI 回答
└─ 明確同步 ───────> Git commit/push
↓
GitHub Pages workflow
關鍵決策
- 三層權限分離:公開閱讀、訪客 AI、私人維護不共用 session,也不因其中一層解鎖而放大其他權限。
- Markdown 是內容 source of truth:公開網站與 RAG 都以同一批可人工審閱檔案為基礎,避免把資料鎖進專屬後端格式。
- 先分類、再呼叫模型:無關的一般問答在 Worker 終止,節省免費 API 額度,也讓產品定位保持聚焦。
- 短對話而不是永久聊天紀錄:伺服端只維護每日則數與 token 統計;模型 history 只取最近四則,不建立公開聊天內容資料庫。
- Kaine 身分與事實邊界同時存在:模型以第一人稱表達,但只能依公開 Wiki 事實回答,不得代替本人作現實承諾。
- 不使用私人人格資料:通用回答風格直接寫在公開程式中;專案、經歷與能力事實仍必須由公開檢索內容支持。
- Pages 與 Worker 分開部署:靜態內容變更不會誤觸 AI backend;相對地,Worker 修改也必須明確獨立發布。
- 不以硬輸出長度截斷答案:先縮小 corpus、history、thinking 與回答規則,避免核心句子因固定 token cap 被截斷。
測試與驗證
Repository 閘門
npm test
npm run wiki:check
npm run build
npm test 目前串接三組檢查:
- 導覽與權限 smoke test:以假的 DOM/sessionStorage 驗證登入前後可見性,並用 source assertions 固定公開頁、管理頁與聊天 UI 的責任邊界。
- Gemini budget regression:確認四頁/6,500 字元 corpus、四則 history、minimal thinking、重試策略、Kaine 定位、沒有私人 persona 入口、5 則續聊確認與 UI 文字沒有回退。
- 聊天政策 unit test:直接執行 scope classifier、語言判斷、4–5 clamp、無關婉拒與續聊提示。
wiki:check 檢查巢狀 Wiki links;build 實際產生 Astro 靜態頁與 sitemap。
驗證邊界
- 自動測試能防止程式碼與文案規則退回舊行為,但多數 Worker 檢查屬 unit/source regression,不是實際 SMTP、D1、Gemini 與正式網域的端對端測試。
- Worker 可以先做 deploy dry-run;正式可用性仍要在獨立部署後檢查 health、OTP、聊天與 D1 寫入。
- 本輪文件本身未宣稱重新完成 live 驗證;最終發布紀錄應由實際執行測試與部署的工作回報補足。
部署與執行邊界
GitHub Pages
- 主要分支 push 觸發 workflow,安裝依賴、執行 Astro build,並上傳
dist/。 - Pages 發布包含
wiki/、前端頁面與 client scripts,不包含 Cloudflare Worker 的新程式。
Cloudflare Worker
worker/index.js、聊天 policy、模型設定與 D1 綁定的變更需要獨立 Worker deploy。- 新資料表或欄位需要另外套用 D1 schema/migration;只發布 Worker 不會自動修正既有資料庫結構。
- API key、SMTP 密碼與 token 簽章材料只存在執行平台,不放進 Git 或前端 bundle;私人 persona 原文/摘要不送入執行平台。
本機 Bridge
- Bridge 依賴 Kaine 的裝置、網路、Codex CLI 登入與本機 Git 權限;它不是 Pages 或 Worker 的一部分。
- Bridge 離線不影響公開 Wiki 與已部署的訪客聊天,但私人管理、Codex 與同步會不可用。
AUTO_GIT_PUSH只控制明確管理操作後是否同步,不代表每日背景排程。
已知限制
- scope classifier 是明確規則與正規表示式,不是完整語意分類器;邊界句可能被誤判。追問只根據最近少量 user history 判斷上下文。
- 每一則在檢索與 Gemini 呼叫前先原子保留;若後續遇到 Wiki 讀取或模型暫時錯誤,該則目前仍會計入當日上限。這是避免並行超額的取捨。
- 無關問題也會消耗一則,目的是阻止用反覆一般問答探測免費模型入口;它不消耗 Gemini generation token。
- 前端只在當前頁面記住四則 history;重新整理後對話文字不還原,但 D1 的每日則數仍保留。
- 搜尋索引有 edge cache,剛發布的 Wiki 內容可能需要等待 cache 更新才進入聊天檢索。
- 簽章 guest token 在期限內由 Worker自行驗證;目前沒有每次查詢伺服器 session row 的即時單一 token 撤銷流程。
- 公開 RAG 是簡單加權檢索,不是 embedding index;同義詞、拼寫變化或非常抽象的問題可能選不到最佳頁面。
wiki/是公開資料層,不適合放未公開 prototype、私密研究資料或完整 repository dump;RAG 也不應被當作私人檔案搜尋器。- Worker、Pages 與 Bridge 三條線可能版本不同步;判斷正式站行為時要分別確認部署狀態。
公開邊界
可公開:三層架構、API 職責、Markdown parser、RAG 預算、對話上限原則、驗證模型、測試方法與部署分線。
不公開:Email、資料庫識別碼、API key、SMTP 密碼、token 簽章材料、私人 persona 原文或摘要、Bridge .env、本機絕對路徑、訪客身分/IP、私人 Agent 規則與未公開筆記。私人 persona 不複製到公開文件,也不傳給模型或其他第三方服務。
Source of truth
README.mddocs/HANDOFF.mddocs/ecosystem.mddocs/local-codex-bridge.mdpackage.jsonastro.config.mjssrc/lib/wiki.tssrc/scripts/wiki-search.jssrc/scripts/guest-gemini-client.jssrc/pages/gemini.astroworker/index.jsworker/chat-policy.jsworker/schema.sqlworker/migrations/0002_daily_token_usage.sqlbridge/server.jsscripts/test-nav-auth-visibility.mjsscripts/test-gemini-budget.mjsscripts/test-kaine-chat-policy.mjs.github/workflows/deploy.yml