-
最近在整理自己的本地 LLM lab,目標很單純:模型跑在自己的 GPU server 上,但操作體驗要像一般 coding agent,能讀檔、改 code、跑指令,也能依工作內容載入 Skills。
目前這套已經可以日常使用。前端是 Pi Agent,中間經過 LiteLLM,後端跑 Qwen 3.8 27B。模型不在我的 Mac 上運算,Mac 只負責跑 Pi 的 TUI 和工具。
先說結論:這個串法不需要特製 plugin,也不用改 Pi 原始碼。Pi 本身支援 OpenAI-compatible provider,在
models.json登記 endpoint 和模型就能用了。整體怎麼接
請求大致會走這條路:
Pi Agent on macOS ├─ built-in tools: read / write / edit / bash ├─ project instructions: AGENTS.md / SYSTEM.md └─ on-demand Skills │ ▼ OpenAI-compatible API │ ▼ LiteLLM Gateway ├─ API key、RPM、並行數限制 ├─ model alias └─ routing │ ▼ Kubernetes Service │ ▼ 最多 2 個 llama.cpp inference pods └─ Qwen 3.8 27B GGUF,各用一張 AMD GPU 有生圖需求時切成 1 個 llama.cpp pod + 1 個 ComfyUI podLiteLLM 對外提供
/v1/chat/completions。Pi 不需要知道後面是 llama.cpp、vLLM,還是別的 inference engine,只要 API 格式相容就好。我們目前有一般版和 thinking 版兩個主要 alias:
qwen3.8-27b-kai qwen3.8-27b-kai-thinking同一個 gateway 也掛了其他實驗模型,總共 11 個。平常預設用一般版,遇到需要多步推理的工作再從 Pi 的
/model切到 thinking 版。Pi Agent 的設定
以下範例已拿掉真實網域、帳號和 key,可以直接照自己的環境改。
~/.pi/agent/models.json:{ "providers": { "lab": { "baseUrl": "https://llm.example.com/v1", "api": "openai-completions", "apiKey": "$LAB_API_KEY", "authHeader": true, "compat": { "supportsDeveloperRole": false, "supportsReasoningEffort": false }, "models": [ { "id": "qwen3.8-27b-kai", "name": "Qwen 3.8 27B KAI", "reasoning": false, "contextWindow": 65536, "maxTokens": 4096 }, { "id": "qwen3.8-27b-kai-thinking", "name": "Qwen 3.8 27B KAI Thinking", "reasoning": true, "contextWindow": 65536, "maxTokens": 4096 } ] } } }~/.pi/agent/settings.json:{ "defaultProvider": "lab", "defaultModel": "qwen3.8-27b-kai" }API key 放在環境變數,不要直接塞進 JSON:
export LAB_API_KEY="your-api-key" pi進入 Pi 後可以輸入
/model,或按Ctrl+L切換模型。CLI 也能直接指定:pi --model lab/qwen3.8-27b-kai-thinkingsupportsDeveloperRole和supportsReasoningEffort關掉,是因為我們後端走 llama.cpp 的 OpenAI-compatible Chat Completions。這兩個欄位如果照某些 hosted model 的習慣送出去,不一定每個後端都吃得下。多位同事要怎麼共用
我們沒有把同一把 API key 丟到群組裡讓大家共用。LiteLLM 放在中間的另一個理由,就是它可以替每位使用者發獨立的 virtual key。
這樣做有幾個很現實的好處:某位同事的 key 外流時可以單獨撤銷;誰把併發打滿也看得出來;不同人能開放不同模型,不需要把後端管理權一起交出去。
目前一般使用者只開這兩個 model alias:
qwen3.8-27b-kai qwen3.8-27b-kai-thinking管理者或負責測模型的人才會看到完整的 11 個模型。一般同事的限制先設成每分鐘 20 次、同時最多 2 個 request。這不是什麼精密算出來的神奇數字,只是避免一個人的 agent loop 暫時占滿整個 gateway。
建立 virtual key 的概念如下。這段要在 gateway 的管理環境執行,
LITELLM_MASTER_KEY不能發給一般使用者:curl -sS https://llm.example.com/key/generate \ -H "Authorization: Bearer $LITELLM_MASTER_KEY" \ -H "Content-Type: application/json" \ -d '{ "key_alias": "developer-a", "models": [ "qwen3.8-27b-kai", "qwen3.8-27b-kai-thinking" ], "rpm_limit": 20, "max_parallel_requests": 2, "metadata": { "team": "engineering" } }'developer-a只是匿名範例。實際環境可以用公司帳號、工號或不含個資的內部代號,但不要把人的姓名和 API key 一起貼進文件。拿到 key 後,每位同事只需要設定自己的環境變數:
export LAB_API_KEY="your-personal-virtual-key" pi同事端的
models.json建議只列出上面兩個模型。就算把 11 個模型全寫進去,Pi 也只是在選單裡顯示它們;真正送 request 時,LiteLLM 還是會依該 virtual key 的 model allowlist 擋掉未授權模型。乾脆不要顯示,使用上比較不會困惑。發 key 時,我會走密碼管理器或一次性的安全訊息,不放在 Git、wiki、工單截圖或聊天群組。Pi 設定檔只引用
$LAB_API_KEY,所以團隊可以共用同一份去識別化的models.json,每個人仍然使用不同憑證。多人接入還有一個容易誤會的地方:rate limit 和 GPU capacity 是兩回事。
多位同事的 Pi │ 各自的 virtual key ▼ LiteLLM:驗證、allowlist、RPM、並行限制、排隊 ▼ Kubernetes Service ├─ Pod A:1 個 65K slot └─ Pod B:1 個 65K slot目前後端最多只有兩個 active inference slots;ComfyUI 使用一張卡時則剩一個。把七位同事都設成
max_parallel_requests: 2,不代表瞬間會生出 14 個 GPU slots;它只是每把 key 的上限。多人同時工作時,超出的 request 還是要在 gateway 或 backend 排隊。實際 onboarding 流程很短:管理者替新同事建立一把 virtual key,透過安全管道交付;同事匯入共用的 Pi 模型設定並 export 自己的 key;最後跑一次
pi --list-models lab和短對話確認。離職、設備遺失或 key 疑似外流時,只撤銷那一把即可,不會影響其他人。Context 不是填 65536 就沒事了
這裡踩過一個很實際的坑。
模型的 context 上限是 65,536 tokens,但 request 計算的是 input 加上預留 output。當時 Pi 已經累積約 60,415 input tokens,模型設定又留了 8,192 output tokens,最後送出去變成 68,607,LiteLLM 直接回 HTTP 400。
後來把 Qwen 3.8 的
maxTokens降到 4,096。對 coding agent 來說通常夠用,也能替長對話留一點空間。Pi 本身有 auto-compaction,必要時也可以手動輸入:/compact另一個小坑是長時間執行的 foreground command。例如:
python3 -m http.server 8898這個程序不會自己結束,Pi 的 bash tool 就會一直顯示
Working...。我現在會把這類服務丟到 tmux,或明確放到背景跑。目前用了哪些 Skills
Pi 的 built-in tools 和 Skills 是兩回事。讀寫檔案、編輯與執行 shell 是內建工具;Skills 則是按任務載入的工作說明,不需要全部塞進每一輪 prompt。
Pi 啟動時目前會載入 33 個 Skills。沒有必要逐個介紹,但大致分成幾組:
- UI、設計與動畫:
apple-design、emil-design-eng、figma、ui-ux-pro-max、wireframe-prototyping、improve-animations、review-animations,以及gsap-core、gsap-react、gsap-scrolltrigger等八個 GSAP Skills。 - Mobile 與上架:
mobile-design、react-native-design、react-native-best-practices、app-store-preflight-skills、app-store-screenshots。 - Cloudflare 與 backend:
cloudflare、agents-sdk、durable-objects、workers-best-practices、wrangler、sandbox-sdk、turnstile-spin,還有 Email Service、Cloudflare One 與 migration 相關 Skills。 - 一般工具:
web-perf、find-skills、humanizer-zh-tw。
平常不做對應任務時,Pi 不會把每份 Skill 全部硬塞進 context,這點對 65K 的本地模型很重要。它先看到名稱與用途,真的需要時才讀完整內容。
目前也載入了一個
muxy-notify.tsextension,用來補終端工作流程的通知。這類 extension 和 Skills 不同:Skills 比較像工作手冊,extension 則能直接註冊事件、命令或 UI 行為。Skills 放在:
~/.pi/agent/skills/<skill-name>/SKILL.md有些 Cloudflare Skills 同時存在
~/.pi/agent/skills和共用的~/.agents/skills。Pi 啟動時會顯示Skill conflicts,保留使用者目錄裡的版本並跳過另一份。這是正常的去重訊息,不代表載入失敗,也不會讓同一份 Skill 執行兩次。專案自己的規則則放在 repo 裡的
AGENTS.md。這樣同一個 Qwen model 換到不同專案時,會拿到不同的工作方式,不用另外訓練模型。寫 code 和產生圖片可以接在同一個工作流程
這套不只綁一個文字模型。VM106 上還有 ComfyUI Pod,所以 Pi 可以在 Qwen 3.8 和圖片產生工具之間切換使用。兩者共用同一組 GPU,但不會硬塞進同一個 container。
Qwen 仍然是主要 agent,負責看專案、整理 prompt、決定檔名和修改程式。真的要畫圖時,再透過 image generation tool 把 workflow 送到 ComfyUI。圖片完成後,tool 把檔案路徑和基本資訊交回 Pi,Qwen 就能繼續把圖放進網頁、App asset catalog 或遊戲專案。
使用者:幫這個 landing page 做一張 hero image ▼ Qwen 3.8:讀現有 UI、整理尺寸與視覺需求 ▼ GPU broker:喚醒 ComfyUI,推理服務由 2 pods 縮成 1 pod ▼ ComfyUI Pod:執行指定 workflow,產生 PNG / WebP ▼ Qwen 3.8:檢查輸出、改檔名、接進程式碼所以這裡說的「切換」不是每次都用
/model手動換模型。比較順的做法是把 ComfyUI API 包成 Pi extension 或 Skill,註冊成像generate_image這樣的 tool。使用者照常描述需求,agent 判斷何時呼叫它;ComfyUI 要跑哪一份 workflow、用哪個 checkpoint,留在 tool 端處理,不必動到 Qwen 的設定。ComfyUI 的位址和憑證同樣只放環境變數:
export COMFYUI_API_BASE="https://images.example.com" export COMFYUI_API_KEY="your-comfyui-key"團隊使用時,文字模型和圖片工具分開授權。有人只需要 coding,就只拿 LiteLLM 的 Qwen key;需要產圖的人再開 ComfyUI 入口權限。這樣圖片 job、LLM token 與存取紀錄不會混在一起,撤銷權限也比較乾淨。
還有一點常被混在一起:model 支援 image input,代表它能「看圖」;image generation tool 才是「產圖」。這兩件事不是同一個 capability。我們目前的 Qwen 路徑以文字和 coding tools 為主,圖片輸出交給獨立工具處理。
GPU 的交接由 VM106 裡的 broker 處理。平常 ComfyUI 是
0 replicas,兩張卡都給 llama.cpp;有人喚醒生圖服務時,broker 把 ComfyUI scale 到 1,同時把 llama.cpp 從 2 pods 縮成 1 pod。實測約 10 秒後 broker 會做出讓卡決策,完成後是一張卡推理、一張卡生圖,Qwen API 在切換期間仍然可用。平常: llama.cpp × 2 ComfyUI × 0 有人要生圖:llama.cpp × 1 ComfyUI × 1 生圖閒置後:llama.cpp × 2 ComfyUI × 0目前喚醒的保留時間是 15 分鐘。就算使用者只打開頁面、最後沒有送 workflow,ComfyUI 仍會暫時占住一張卡;時間到且 queue 為空,broker 才會收掉 ComfyUI,把第二張卡還給推理。這個設定有點浪費,但可以避免使用者還在挑 workflow 時服務突然被收走。
後端正在玩的 KAI 環境
後端目前是一套 KAI Scheduler POC,不是大型正式叢集,主要拿來試 GPU queue、priority、preemption 和工作負載排程。
技術堆疊大概是:
- Proxmox VE 上的一台 Debian VM
- 8 vCPU、64 GiB RAM
- 2 張 AMD R9700,PCIe passthrough 並開 ReBAR
- k3s
- KAI Scheduler v0.20.1
- AMD GPU device plugin
- llama.cpp + Vulkan/RADV,另有按需啟動的 ComfyUI Pod
- Qwen 3.8 27B
Q4_K_XLGGUF - KV cache 使用
q8_0 - LiteLLM 做 gateway、alias、API key 與流量限制
Inference 平常採兩個獨立 Pod,每個 Pod 吃一張 GPU;下面的 replica 數會由 broker 在 1 和 2 之間調整:
spec: replicas: 2 template: metadata: labels: runai/queue: team-a spec: schedulerName: kai-scheduler containers: - name: llama resources: requests: amd.com/gpu: 1 limits: amd.com/gpu: 1llama.cpp 的核心參數目前偏保守:
llama-server \ -m /models/qwen3.8-27b.gguf \ -ngl 99 \ -fa on \ -c 65536 \ -np 1 \ --cache-type-k q8_0 \ --cache-type-v q8_0也就是每張卡一個 65K slot。ComfyUI 關閉時整個服務有兩個 slots,生圖期間剩一個。之前試過把更多 KV 塞進主記憶體,能跑,但 decode 掉得很明顯;也試過一次把 context 和 slot 拉太高,最後 VM OOM,連 passthrough GPU 都卡進 D3cold。現在寧可讓 gateway 排隊,也不硬撐表面上的高併發。
現在用起來的感覺
Pi 很適合這種自架模型。它本身薄,模型、工具、Skills 和 provider 都能分開換;出了問題也比較容易知道是 agent、gateway,還是 inference backend。
Qwen 3.8 27B 當然不是所有情況都能取代大型 hosted model。它的好處是資料和流量留在自己的環境,成本也比較容易控制。現在真正需要繼續磨的不是「能不能對話」,而是長 session 的 compaction、tool calling 相容性,以及兩個 GPU slot 在多人使用時怎麼排隊。
至少目前,這套已經不是單純跑 benchmark 的 demo。我會真的開著 Pi,用它進 repo 做事。


- UI、設計與動畫:
-
,
C CS6 引用了 此主题
-
,
T terry 固定了此主题
-
,
T terry 将此主题从 LLM讨论区 移至此处
-
寫的太好了!!

大神
omnirouter 我个人觉得比 LITELLM 好用 可以参考看看
另外感覺你都是用 DESIGN 之類比較小型的 SKILL 所以 64k 上下文還應付的了但如果載入或是使用 大型SKILL 。。。 通常 auto compact 之類的也會沒有做用,因為上下文已經被skill 佔滿,就我個人來說 64k 上下文真的是硬傷。。。 所以新的 mac studio 好吸引我阿~~~
-
,系统 取消固定了此主题
-
,
C CS6 引用了 此主题

