SKILL HUB 技能治理與知識化平台架構
平台不執行 Skill;它處理上架、掃描、摘要、版本與 telemetry。
BFF、封裝處理、LLM Ingestion、版本快照與 Usage Signals 各自負責一段資料流。
30 秒極速摘要 (Quick Read)
- 解決什麼痛點:AI 讓同仁更容易製作工具,也帶來影子 AI、發布管道分散、原始碼難理解與採用成效難衡量等治理問題。
- 平台邊界:SKILL HUB 不負責執行 Skill,而是治理 Skill 進入組織後的上架、知識化、版本紀錄與採用追蹤流程。
- 封裝前處理:作者匯入資料夾或 ZIP 時,前端以 JSZip 解析檔案樹,先排除
node_modules、.env、build 產物與過大封裝。 - 資料流切分:Same-origin BFF、Ingestion Pipeline、version snapshot 與 telemetry 各自負責一段,避免 catalog、審核與分析流程混在一起。
- AI 知識化提煉:LLM Pipeline 將
SKILL.md轉成「三分鐘速讀」、知識透鏡與流程視圖,協助使用者先理解用途與限制。 - 採用追蹤:Usage Signals 收集 view、download、tab 切換等事件,Recharts 呈現趨勢,並過濾 Admin 與作者本人的互動。
- 安全深潛分流:上傳掃描、網站防禦、AI audit 與 user action audit 已拆到 安全治理頁,避免技術頁承載過多細節。
01 // DESIGN REFLECTION 設計反思:工具目錄只能解決「找得到」
工具目錄可以讓同仁找到 Skill,但不能回答它是否安全、誰維護、是否有人採用。SKILL HUB 把這些問題拆到掃描、審核、版本與 telemetry。
影子 AI 的邊界與責任歸屬
若員工自行散佈會呼叫外部 API 的 AI Skills,企業會失去可審核的治理邊界。SKILL HUB 不接管執行環境,而是把上架內容轉成可掃描、可審核、可追蹤的知識資產。 這代表我們不強制託管運行,而是藉由 client-side 預排除、ZIP / path / regex 安全掃描以及 Admin 審查機制,在上架流程中降低風險。
採用數據與沉沒成本控制
AI 工具寫了沒人知道,維護者會沮喪,組織也會重複造輪子。平台必須具備「採用指標(Usage Signals)」。 當我們知道哪些 Skill 被高頻瀏覽、下載或複製,哪些已經過期(Stale),部門主管就能用 view、download 與 stale 訊號判斷維護優先順序。
02 // TECH STACK 技術棧總覽 (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 |
| AI Runtime Governance | Node.js API + DB-backed Settings | Mode toggles, quota gate, token budget, timeout, redacted AI audit | BFF Server (Pre-quota Gate) |
| Admin-Gated Safety Review | Supabase RPC + Archive Scanner | scan lock, manual_review state, staged package promotion, immutable audit | Server-side Workflow |
03 // DESIGN PHILOSOPHY 設計取捨:以 Markdown 作為交換格式
在這個專案裡,`SKILL.md` 是最穩定的交換格式。人可以閱讀,Git 可以比對,LLM 也能依章節補出草稿欄位。
資料媒體契約:以 Markdown 純文字做無痛交換
為什麼不設計一套複雜的 JSON 或 UI 生成器,而堅持以 SKILL.md 作為標準包裝?
knowledge_profile 欄位;實際回填率仍取決於文件結構完整度。04 // ENVIRONMENT ISOLATION 環境隔離設計:保留可攜式 VIRTUALENV 結構
每個 Skill 套件保留獨立 virtualenv 結構,方便下載後在本機或 Agent 環境隔離執行;平台本身只負責上架、掃描與知識化。
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 // TECHNICAL DEEP DIVE 技術深潛:核心模組與治理交界
[Module-01] Client-side 預排除與安全打包
在 Step 1 專家匯入時,前端透過 JSZip 在記憶體解析檔案樹。
直接執行硬排除黑名單機制,阻擋將 node_modules、編譯產物與含有明文 key 的 .env 上傳。近期 same-origin package / media upload 已保守收斂到 4 MB,用來避開 Vercel Functions request body ceiling;大型封裝後續應改成 direct-to-storage upload。
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 > 4 * 1024 * 1024) {
throw new Error("封裝檔案大小超過 4 MB same-origin 安全上限,請縮減封裝或改走大型檔案上傳流程。");
}
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 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', // Fallback model preset
messages: [
{ role: 'system', content: 'You are an expert AI competency extractor. Extract 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);
}
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 });
}
return next();
}
[Module-04] Recharts Telemetry 採用監控與排除
在同仁瀏覽、下載包、複製指令、切換詳情頁 tab 時發送 Telemetry 事件。 系統將其記錄於後台稽核表,並自動過濾 Admin 與作者本人,確保 Usage Signals 互動圖表的乾淨度。
async function logTelemetry(req, eventData) {
const { toolId, eventName, actorId } = eventData;
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();
if (actorId === tool.author_id || actor.role === "admin") {
console.log("Telemetry bypassed: self-interaction or admin audit trail.");
return;
}
await supabase.from("telemetry_logs").insert({
tool_id: toolId,
event_name: eventName,
actor_id: actorId,
timestamp: new Date().toISOString()
});
}
[Module-05] AI Runtime 與安全治理交界
AI Ingestion 是技術架構的一部分,但它的風險控制不應散落在每個畫面。這裡只保留架構交界:AI 呼叫先經過 server-side runtime gate,確認 mode、timeout、token budget、quota 與 context files,再進入模型。
上架判定回到 scan status、approval status 與 Admin review。AI 只產出草稿、摘要與風險提示,不直接成為發布權威。
05.1 // ADVANCED ARCHITECTURE 進階架構設計 (Advanced Architecture)
Same-origin BFF 會話代理與安全降級 (Fail-Closed)
系統將前端直連 Supabase 的資料存取收斂到 server-side,全站 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 探索端點與安裝輔助
目前 SKILL HUB 的主要採用路徑仍是人進入網站搜尋、閱讀與下載。下一階段應補上 agent-friendly discovery endpoint,例如 /api/agent-catalog/search,讓本機 Agent 能在任務中查詢已發布且有權限讀取的 Skill。
安裝輔助方向:後續可用低風險 PowerShell / Bash helper 驗證 checksum、建立備份,再以 symlink 或 copy 方式安裝到 Claude Code、Codex、Cursor 或 Gemini CLI 的 Skill 目錄。這仍是 roadmap,不應和目前正式上架主線混淆。
06 // LESSONS LEARNED 踩坑點與 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 在前端讀取檔案樹時,直接針對黑名單正則進行硬排除,並把 same-origin 上傳安全上限收斂到 4 MB。大型 package 後續應改走 direct-to-storage upload,避免 binary request body 直接經過 Vercel Function。
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,可在資料庫動態關閉完整 AI 蒸餾流程,並對一般用戶實施 15次/日 的 AI 呼叫硬限制,降低預算失控與系統不可用風險。
Lesson 4: AI 審查不能變成自動核准權威
問題背景:如果把 package-safety-review 的 LLM 結論直接當作上架 gate,模型 timeout、quota 用盡、prompt injection 或上下文截斷都可能造成錯誤放行或錯誤阻擋。這會讓治理責任從可驗證的掃描與審核流程,滑向不可穩定重現的模型判斷。
解決方案:AI 只負責「輔助理解」與「風險摘要」。正式放行交給掃描狀態、審核狀態與 Admin sign-off;完整防線已拆到 安全治理頁。