SKILL HUB 技能治理與知識化平台架構
在大 AI 時代下,如何防禦影子 AI 氾濫,打通「好找、好讀、好分享」的核心安全防護與採用監控。
基於 Same-origin BFF 認證閘、AST 安全打包排除、LLM 職能蒸餾與 Recharts 採用指標的企業級架構。
30 秒極速摘要 (Quick Read)
- 解決什麼痛點:AI 讓同仁編寫工具的難度降至零,但「影子 AI」帶來的隱私外流、缺乏發布管道(不好找)、原始代碼難讀懂(不好讀)與採用狀況黑箱(好分享)成為企業導入障礙。
- 核心架構設計:前端採用 React 19 與 Vite 8 提升開發效率;後端廢除瀏覽器直連 Supabase,改接 Next-style API 做 Same-origin BFF 認證隔離,並以 middleware fail-closed 防禦敏感 DB 暴露。
- AI 知識化提煉:整合 LLM Pipeline。作者上傳前由 Client-side 硬排除敏感檔,後端 ZIP scan 敏感路徑;AI 自動翻譯並蒸餾 Markdown 生成「三分鐘速讀」與「知識透鏡(Knowledge Lens)」,甚至依代碼與配置圖自動繪製流程拓撲圖(AI Flow)。
- 採用數據飛輪:模組化收集 view/download/tab 切換事件,由 Recharts 視覺化呈現採用趨勢圖,將組織 AI 技能徹底轉譯為可量化、可治理的數位資產。
01 // 設計反思 (Reflection_):在大 AI 時代重新定義平台壁壘
在工具製作成本急速趨近於零的時代,平台的價值不應是「工具目錄」,而是「治理與採用中樞」。
影子 AI 的邊界與責任歸屬
當員工能自行撰寫 AI Skills 呼叫外部 API 時,企業的隱私防護形同虛設。SKILL HUB 的邊界在於「不接管執行環境,只做可驗證的知識化 Ingestion」。 這代表我們不強制託管運行,而是藉由 client-side 預排除、ZIP 安全路徑掃描以及 Admin 審查機制,在源頭過濾隱患。
採用數據與沉沒成本控制
AI 工具寫了沒人知道,維護者會沮喪,組織也會重複造輪子。平台必須具備「採用指標(Usage Signals)」。 當我們得知哪些 Skill 被高頻採用、哪些已過期(Stale),部門主管就能藉此評估維運 ROI,讓 AI 轉型不再停留在喊口號,而是以實打實的採用指標推動。
02 // Tech Stack at a Glance
| 技術模組 | 語言 / 框架 / Runtime | 核心依賴與目的 | 運行方式 |
|---|---|---|---|
| AICS Workspace | React 19 + Vite 8 | JSZip, Tailwind 4 (提供安全預排除、表單 5 步精靈工作台) | Client-side |
| Same-origin BFF | Node.js API (BFF) | Cookie-session auth & Supabase SDK (隔絕 DB 直連暴露) | BFF Server / Middleware |
| Ingestion AI Pipeline | Node.js (Serverless) | Azure OpenAI SDK / LangChain (自動翻譯補值、三分鐘速讀生成) | BFF Server (Rate-limited) |
| Usage Signals | Recharts + SQL Analytics | Recharts (繪製 14天/8週採用趨勢、熱門 Search 自動優化) | Client + Server DB logs |
03 // 設計哲學:以 Markdown 為契約的知識轉譯
大 AI 時代最核心的資產是半結構化的工作手冊。我們選擇以 Markdown 純文字檔作為介面傳輸與分享的核心契約。
資料媒體契約:以 Markdown 純文字做無痛交換
為什麼不設計一套複雜的 JSON 或 UI 生成器,而堅持以 `SKILL.md` 作為標準包裝?
04 // 環境隔離設計:每個 Skill 均在獨立的 VIRTUALENV 下解耦
每個 Skill 目錄下各自維護獨立的執行虛擬環境,在 runtime 上完全解耦,不讓 dependency conflict 拖垮整體系統。
vibeHub-Skills/ ├── n8n-automation-skill/ │ ├── .venv/ # 各 Skill 獨立的 Python 虛擬環境 │ ├── SKILL.md # 職能描述 Markdown │ ├── requirements.txt # 核心依賴 │ ├── src/ # 專家執行的腳本目錄 │ └── detail_payload.json # Client-side 產出之 Ingestion metadata │ ├── teams-notifier-skill/ │ ├── .venv/ │ ├── SKILL.md │ ├── scripts/send.py │ └── requirements.txt
虛擬環境解耦考量
- 防止套件污染:Skill A 使用舊版
requests,Skill B 升級新版,若共用環境會導致改 A 壞 B。 - 冷啟動效能調優:避免載入大型 AI 分析庫(如 scikit-learn)拖累輕量級爬蟲 Skill 的執行效率。
- 零干擾複製:任意 Skill 可以直接整包打包下載,在本機或其他專案執行,不需要重新拉取平台依賴。
05 // 技術深潛 (Deep Dive):四大核心模組實作代碼剖析
[Module-01] Client-side 預排除與安全打包
在 Step 1 專家匯入時,前端透過 JSZip 在記憶體解析檔案樹。
直接執行硬排除黑名單機制,阻擋將 node_modules、編譯產物與含有明文 key 的 .env 上傳,且在封裝大於 50MB 時直接中斷,保障傳輸效能與資安防禦。
const EXCLUDE_PATTERNS = [
/node_modules\//i,
/\.env.*/i,
/\.git\//i,
/dist\//i,
/build\//i,
/venv\//i,
/\.venv\//i,
/tmp\//i
];
async function filterAndPack(files) {
const zip = new JSZip();
let totalSize = 0;
for (const file of files) {
const isExcluded = EXCLUDE_PATTERNS.some(regex => regex.test(file.path));
if (isExcluded) continue; // Exclude node_modules, env keys, etc.
totalSize += file.size;
if (totalSize > 50 * 1024 * 1024) {
throw new Error("封裝檔案大小超過 50MB 上限,請縮減打包內容後重試。");
}
zip.file(file.path, file.content);
}
return await zip.generateAsync({ type: "blob" });
}
[Module-02] LLM 職能蒸餾 Pipeline
讀取已解包的 SKILL.md,呼叫 Azure OpenAI / OpenAI-compatible 管道。
自動解析並補齊 knowledge_profile(含 expectedOutcomes、triggerConditions 等結構化欄位),
並實施超時與 300 行 truncated repair 降級策略,避免大型文檔阻斷 API 回應。
async function distillSkill(skillMarkdown, retryCount = 0) {
// Truncate input to avoid timeout for exceptionally large markdown files
const maxLines = 300;
const lines = skillMarkdown.split('\n');
const truncatedMarkdown = lines.slice(0, maxLines).join('\n');
try {
const response = await axios.post(
process.env.VIBEHUB_OPENAI_COMPAT_BASE_URL + '/chat/completions',
{
model: 'gpt-5-mini', // Guaranteed fallback model preset
messages: [
{ role: 'system', content: 'You are an expert AI competency extractor. Extact JSON conforming to the knowledge_profile schema.' },
{ role: 'user', content: `Extract from this SKILL.md:\n\n${truncatedMarkdown}` }
],
temperature: 0.1
},
{ timeout: parseInt(process.env.AZURE_OPENAI_REQUEST_TIMEOUT_MS) || 90000 }
);
return JSON.parse(response.data.choices[0].message.content);
} catch (error) {
if (retryCount < 2) {
console.warn(`AI Ingestion timeout/error. Retrying... (${retryCount + 1})`);
return distillSkill(skillMarkdown, retryCount + 1);
}
// Fallback to manual fill required if AI completely fails
return {
expectedOutcomes: ["請手動填寫預期成果"],
triggerConditions: ["請手動填寫觸發條件"],
prerequisites: ["請手動填寫前置要求"]
};
}
}
[Module-03] Same-origin BFF API 與 Session 隔離
廢止 client-side 直接透過 API KEY 連線 Supabase,全站以同網域 BFF(Backend For Frontend)會話層進行認證代理。 採用 Middleware 對敏感 URL(如媒體讀取、package 下載)進行阻斷,當環境變數缺失時觸發 fail-closed 安全降級。
export function middleware(req) {
const sessionToken = req.cookies.get("vibehub-session")?.value;
// Fail-closed protection: if Supabase variables are missing, deny request with 503
if (!process.env.SUPABASE_URL || !process.env.SUPABASE_SERVICE_ROLE_KEY) {
console.error("Critical Security: Supabase keys are missing. Triggering fail-closed.");
return new Response(JSON.stringify({ error: "Service unavailable due to internal security posture." }), { status: 503 });
}
// Validate session path
if (!sessionToken) {
return new Response(JSON.stringify({ error: "Unauthorized access. Session expired." }), { status: 401 });
}
// Forward safely to BFF internal routes
return next();
}
[Module-04] Recharts Telemetry 採用監控與排除
在同仁瀏覽、下載包、複製指令、切換詳情頁 tab 時發送 Telemetry 事件。 系統將其記錄於後台稽核表,並自動過濾 Admin 與作者本人,確保 Usage Signals 互動圖表的乾淨度。
async function logTelemetry(req, eventData) {
const { toolId, eventName, actorId } = eventData;
// Retrieve tool owner to exclude self-interaction metrics
const { data: tool } = await supabase.from("tools").select("author_id").eq("id", toolId).single();
const { data: actor } = await supabase.from("users").select("role").eq("id", actorId).single();
// Exclude authors and administrators from usage telemetry
if (actorId === tool.author_id || actor.role === "admin") {
console.log("Telemetry bypassed: self-interaction or admin audit trail.");
return; // Silent bypass
}
// Log to DB
await supabase.from("telemetry_logs").insert({
tool_id: toolId,
event_name: eventName,
actor_id: actorId,
timestamp: new Date().toISOString()
});
}
05.1 // 進階架構設計 (Advanced Architecture) 與技術展示
為組織級 AI 技能架設「防護網」:探討 BFF 安全閘、版本差異引擎與 Agent 自動安裝的工作流。
Same-origin BFF 會話代理與安全降級 (Fail-Closed)
系統徹底廢除前端直連 Supabase 的不安全模式,全站 API 統一由 BFF (Backend For Frontend) 會話代理進行存取控制。
防禦性設計 (Defensive Design):middleware.js 在檢測到雲端 Supabase Auth 憑證缺失時,不會退回暴露的舊 Anon 連線,而是觸發 Fail-Closed 降級保護,直接向客戶端回應 503 (Service Unavailable),確保極端的配置錯誤下資料安全無虞。
Deterministic 版本差異快照比對引擎
每次 Skill 經重傳改版 (Re-upload) 或 Ingestion 更新時,系統均會在 tool_version_snapshots 內固化當前結構,並自動與上一版本進行比對。
比對引擎會拆解 zip/manifest 並產出具體的 Added / Removed / Changed 差異,並將結果呈現在審查清單中,供 Admin 明確知道「這一次改版,作者到底動了哪些程式碼與 YAML 設定」。
Agent-native 探索端點與一鍵本機安裝 (Install Helper)
為突破「逛網站搜尋」的傳統漏斗,系統提供給 AI 爬蟲與本機 Agent(如 Claude Code, Cursor)專用的探索 API (/api/agent-catalog/search)。
本機安裝自動化:透過輕量化 PowerShell / Bash 安裝管家,當使用者按下複製安裝時,指令會自動驗證 checksum 下載包,以 symlink 軟連結 方式直接掛載至 Agent 的 Skill 資料夾,實現「免配置一鍵採用」。
06 // 踩坑點與 Lessons Learned (故障復盤)
Lesson 1: Same-origin auth 遷移與 Supabase RLS 洩漏風險
問題背景:系統初期為了求快,直接在前端建立 Supabase Client 連線資料庫,並依賴 Row Level Security (RLS) 保護表單。然而安全審計時發現,前端打包的 bundle 會暴露出 Supabase URL 與 Anon Key,且 RLS 設定一旦稍有疏漏(例如忘記關閉 select 的 public bypass),就會造成敏感同仁日誌外洩的風險。
解決方案:全面遷移至 Same-origin BFF。前端一律發送 /api/session 與 /api/tools 等同網域請求,將 Supabase Client 收斂於 server-side。Middleware 亦實施 fail-closed 防範,大幅縮減了暴露面。
Lesson 2: ZIP 大檔案打包引發 API 傳輸崩潰
問題背景:早期版本的作者上架流程允許使用者直接拖放資料夾,後端直接接收並打包。當使用者將包含 node_modules 或大型編譯產物 (build/) 的目錄誤拖入時,上傳檔案常突破 100MB,造成 Serverless timeout 與網路崩潰。
解決方案:將過濾邏輯左移至 Client-side。利用 JSZip 在前端讀取檔案樹時,直接針對黑名單正則進行硬排除,並限制重打包後的 zip Blob 必須小於 50MB 否則拒絕上傳。這不僅節省了 95% 的頻寬,更降低了專家將本地敏感設定(.env)外流的可能。
Lesson 3: LLM Ingestion 逾時與 Quota 配額阻斷系統運行
問題背景:AI Ingestion 在處理上千行的 SKILL.md 時,常因 Azure OpenAI 推理時間過長,造成 Vercel Serverless Function 觸及 10 秒/30 秒的最大逾時限制而中斷,且頻繁的重試迅速耗光了 API Token 預算。
解決方案:導入兩重防護:一是 **truncated repair** 降級策略,送入 AI 之前只擷取前 300 行,並在 UI 上對作者標記「部分欄位已降級,請手動確認」;二是於後台提供 **AI Runtime Controls**,可在資料庫動態關閉 Full AI,並對一般用戶實施 15次/日 的 AI 呼叫硬限制,保障企業預算與系統高可用。