昨天 Day 2 我們把 Portal、Engine、KM 三者的分工攤開來看,結論是 Portal 在最外層當了一層 BFF/Gateway——身分、安全、路由、整合這四件橫切的事它包了,業務知識則留在後面的 Engine 和 KM。讀到這裡,熟一點雲原生 LLM 應用的讀者,腦袋裡大概已經浮出一個問號:這不就是個 AI Gateway 嗎?市面上不是有 LiteLLM 這種現成的東西,為什麼要自己刻一套?今天就拿這個你可能聽過的東西當錨點,把 Portal 的定位釘清楚。後面整整二十幾天,我們會反覆回到這個對照。
本篇結構:
LiteLLM 是一個相當受歡迎的 AI Gateway。它解決的問題很聚焦也很實在:你的應用要同時接 OpenAI、Gemini 好幾家模型,每家的 API 長得都不一樣,於是 LiteLLM 在中間架一層,把所有家的呼叫統一成 OpenAI 相容的格式,再往上長出一堆治理能力:
x-litellm-response-cost)簡言之,它把「呼叫各家 LLM」這件事標準化、可治理化。
我之所以一開始就拿它當對照,是因為它跟 Portal 在「治理 LLM 流量」這個面向上高度重疊——多 provider、限流、稽核、成本,這幾個詞兩邊都會講。重疊夠多,差異才看得清楚。而真正的差異,藏在「重疊以外」的那一大塊。
最簡單的講法是這樣:LiteLLM 是一個橫切於「呼叫 LLM」這一段的代理層,你的應用邏輯在它上面、模型在它下面,它管的是中間那條線。Portal 不是這樣——它管的是一筆請求的完整生命週期。一筆行員的提問進到 Portal,會跑完一條從頭到尾的處理鏈,呼叫 LLM 只是這條鏈裡的一站,而且不一定會發生(有些問題根本不交給模型,轉給後端引擎就好):
換句話說,如果把 LiteLLM 想成「水管中間的一個閥門」,Portal 就是「整座淨水廠」——它確實內含一段功能等同那個閥門的東西,但它要管的還有:
落到具體的能力對照,大概是這樣:
| LiteLLM(純 Gateway) | Agent Portal(Agent 平台 BFF) | |
|---|---|---|
| 核心定位 | LLM 呼叫的統一代理層 | 一筆請求從進門到稽核的完整處理鏈 |
| 多 provider 路由 | ✓ 核心能力,統一成 OpenAI 格式 | ✓ 但 provider 不限於 LLM——檢索器、地圖、守門都同一套抽換機制 |
| 限流/預算 | ✓ TPM/RPM、虛擬金鑰、預算上限 | ✓ 內建用量閘門,做進自家閘道層(Day 28) |
| 稽核 | ✓ 呼叫層級的日誌 | ✓ 一次互動一列、userId 取自 SSO 而非輸入(Day 24) |
| 熱控制 | ✓ 動態金鑰、/key/update |
✓ /sdk-registry 執行期查/重載/停用 provider(Day 15) |
| 身分(誰在問) | 應用自己處理,Gateway 只認金鑰 | ✓ SSO 簽章 cookie,身分始於進門那一刻 |
| 內容守門 | 非主責,可外接 | ✓ 提示注入攔截、八類 PII(即個資)遮罩、輸入/輸出多道防線 |
| 意圖路由 | ✗ 不是它的事 | ✓ 研判該由內建助理還是後端引擎回答 |
| Agent/工具整合 | ✗ | ✓ MCP 軌讓模型自主呼叫工具(框架已接;安全層大多已實際生效,僅完整計畫驗證未接線) |
| 自己產生答案嗎? | 不,純轉發 | 不,但會「組裝」答案、附引用與用量 |
看這張表要抓的重點不是「Portal 比較多功能所以比較好」——那沒什麼意義,兩者定位本來就不同。重點是那條分界線在哪裡:LiteLLM 管的是「呼叫 LLM 這一段」,Portal 管的是「一筆請求的一生」,而呼叫 LLM 只是其中一段。Day 2 講過 Portal 幾乎不放業務邏輯,這裡可以補充更精確的一點:它不放業務邏輯,但它放了一整套橫切治理,而這套治理的範圍,比一個 AI Gateway 寬得多。
把 LiteLLM 的招牌能力逐一對到 Portal,會發現它們並不是各自獨立做的,而是同一套「可抽換」機制長出來的不同分支。這點蠻關鍵,因為它解釋了為什麼 Portal 敢自己刻:它不是把這些當成各自的功能去堆,而是先有一套很小的插件框架,治理能力都掛在那上面。
靠的是 application 層的業務碼從不直接認得 openai-http、gemini-http 這些名字,只認一個抽象的「能對話的 LLM」介面;每家供應商各做一個符合介面的零件,開機時收進一本名冊,要用哪家就查表。換 provider 就是換名冊裡選哪個零件,YAML 改一行:
portal:
llm:
provider: openai-http # mock | openai-http | gemini-http | spring-ai
guardrail:
provider: regex # mock | regex | openai-moderation
maps:
provider: google # mock | google
audit:
provider: slf4j # slf4j | postgres
注意這裡跟 LiteLLM 一個有意思的差別:LiteLLM 的 provider 清一色是「LLM 家族」,而 Portal 這本名冊裡,LLM 只是其中一類插槽,旁邊還躺著守門(guardrail)、地圖(maps)、稽核(audit)的供應商,全走同一套抽換語法。所以當我說「Portal 內建了多 provider 路由」,它的「provider」這個詞比 LiteLLM 寬——不只是換模型,是換任何一類對外能力。
成本與限流也是同樣的長法。LiteLLM 在回應 header 回傳 x-litellm-response-cost 告訴你這次花了多少;Portal 則把成本記成可聚合的 token 指標,再加一道用量閘門擋在花錢之前:
這道閘門怎麼設計、為什麼分前後兩層、以及它目前還只是個「軟上限」的 PoC(概念驗證)妥協,是 Day 28 的主題。框架裡甚至預留了一個「接外部 Gateway(包含 LiteLLM)」的轉接骨架——意思是 Portal 並不排斥把成本控管外包給 LiteLLM,只是現階段自己先做了一個單機版的。
這裡藏著一個該攤開、而不是繞過的問題:既然框架都留了接 LiteLLM 的縫、又承認自己只做了單機軟上限,那為什麼不乾脆把限流與成本這段交給 LiteLLM,Portal 只做上層編排? 誠實的答案分兩面。一面是:就「限流與成本」這一格而論,這個質疑是對的。LiteLLM 那套是成熟、多實例一致、有持久化的實作;真要上正式營運的防線,把這格外包給它,多半比自己把單機軟上限改造成分散式硬上限更省、更快、更穩。那道預留的接縫,正是為這條路留的。另一面是:Portal 自建一個單機版,在 PoC 階段仍有意義——它讓「用量閘門該卡在請求生命週期的哪個位置、怎麼跟守門與稽核共用同一條鏈」這件事,用最少的依賴先驗證完,不必為了一個 demo 先架一套 LiteLLM。所以這不是「自建 vs 買」二選一,而是「先用最小自建把位置與介面驗證好,介面留著、真要轉正式時換成買」——跟整套可抽換的哲學一致。把這個取捨講白,比讓人以為「什麼都得自己刻」要實在。
熱控制這塊也很像。LiteLLM 有動態金鑰管理(/key/update)那套;Portal 對應的是一組查詢加管理用的註冊表端點,能在執行期查現況、換 key、停用某一家壞掉的 provider,全部不必重啟,而且每個寫操作都寫進稽核:
| 方法 | 路徑 | 作用 |
|---|---|---|
GET |
/sdk-registry |
查:接了哪些 provider、各自健康狀態 |
POST |
/sdk-registry/{類別}/{名字}/reload |
重載:換 key、刷新連線 |
POST |
/sdk-registry/{類別}/{名字}/disable |
停用:流量自動退回該類預設 provider |
POST |
/sdk-registry/{類別}/{名字}/enable |
重新啟用 |
每個寫操作的稽核資料流大致是這樣:
每個寫操作 ── 需管理員權杖 ── 寫進 audit ──▶ 誰在何時停了誰,有跡可循
這套熱控制怎麼從「啟動時選一個」演進到「執行期換得動」,留到 Day 15 細講。至於稽核——一次互動寫一列、而且 userId 一律取自 SSO 解出來的身分而不是使用者本次輸入(避免冒名留錯帳)——那是 Day 24 的事。這三天會各自回到今天這張表,把對應那格展開。
最後講一下我選擇用 LiteLLM 當對照的真實考量,順便把邊界攤開講。
用一個讀者可能聽過的東西當錨點,最大的好處是省掉一大段「從零解釋這是什麼」。你只要記住**「LiteLLM 是純 Gateway,Portal 是 Agent 平台 BFF」**這條主軸,後面每一天遇到一個治理能力,都可以回頭問一個問題:這件事 LiteLLM 那種純 Gateway 也會做嗎?如果會,差別在哪?如果不會,為什麼 Portal 非做不可?這個對照會像一把尺,一路量到第八章。
但我得誠實標兩件事,免得這張表被讀成「Portal 全面輾壓」:
Portal 完全比不上,也不打算比——Portal 只接它業務上真正會用到的那幾家。換句話說,這張表是「定位的對照」,不是「成熟度的對照」——往後三十天的任務之一,就是把每個 ✓ 背後的真實狀態,一格一格如實交代。
明天 Day 4,我們會一次看完 Portal 的技術選型全景——reactive、無持久化、宣告式抽換這三個選擇為什麼是綁在一起的一組。