<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[在辦公室架一套能共用的本地 AI 環境：Qwen 3.8、Pi Agent 與 KAI Scheduler]]></title><description><![CDATA[<p dir="auto">最近在整理自己的本地 LLM lab，目標很單純：模型跑在自己的 GPU server 上，但操作體驗要像一般 coding agent，能讀檔、改 code、跑指令，也能依工作內容載入 Skills。</p>
<p dir="auto">目前這套已經可以日常使用。前端是 Pi Agent，中間經過 LiteLLM，後端跑 Qwen 3.8 27B。模型不在我的 Mac 上運算，Mac 只負責跑 Pi 的 TUI 和工具。</p>
<p dir="auto">先說結論：這個串法不需要特製 plugin，也不用改 Pi 原始碼。Pi 本身支援 OpenAI-compatible provider，在 <code>models.json</code> 登記 endpoint 和模型就能用了。</p>
<h2>整體怎麼接</h2>
<p dir="auto">請求大致會走這條路：</p>
<pre><code class="language-text">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 pod
</code></pre>
<p dir="auto">LiteLLM 對外提供 <code>/v1/chat/completions</code>。Pi 不需要知道後面是 llama.cpp、vLLM，還是別的 inference engine，只要 API 格式相容就好。</p>
<p dir="auto">我們目前有一般版和 thinking 版兩個主要 alias：</p>
<pre><code class="language-text">qwen3.8-27b-kai
qwen3.8-27b-kai-thinking
</code></pre>
<p dir="auto">同一個 gateway 也掛了其他實驗模型，總共 11 個。平常預設用一般版，遇到需要多步推理的工作再從 Pi 的 <code>/model</code> 切到 thinking 版。</p>
<h2>Pi Agent 的設定</h2>
<p dir="auto">以下範例已拿掉真實網域、帳號和 key，可以直接照自己的環境改。</p>
<p dir="auto"><code>~/.pi/agent/models.json</code>：</p>
<pre><code class="language-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
        }
      ]
    }
  }
}
</code></pre>
<p dir="auto"><code>~/.pi/agent/settings.json</code>：</p>
<pre><code class="language-json">{
  "defaultProvider": "lab",
  "defaultModel": "qwen3.8-27b-kai"
}
</code></pre>
<p dir="auto">API key 放在環境變數，不要直接塞進 JSON：</p>
<pre><code class="language-bash">export LAB_API_KEY="your-api-key"
pi
</code></pre>
<p dir="auto">進入 Pi 後可以輸入 <code>/model</code>，或按 <code>Ctrl+L</code> 切換模型。CLI 也能直接指定：</p>
<pre><code class="language-bash">pi --model lab/qwen3.8-27b-kai-thinking
</code></pre>
<p dir="auto"><code>supportsDeveloperRole</code> 和 <code>supportsReasoningEffort</code> 關掉，是因為我們後端走 llama.cpp 的 OpenAI-compatible Chat Completions。這兩個欄位如果照某些 hosted model 的習慣送出去，不一定每個後端都吃得下。</p>
<h2>多位同事要怎麼共用</h2>
<p dir="auto">我們沒有把同一把 API key 丟到群組裡讓大家共用。LiteLLM 放在中間的另一個理由，就是它可以替每位使用者發獨立的 virtual key。</p>
<p dir="auto">這樣做有幾個很現實的好處：某位同事的 key 外流時可以單獨撤銷；誰把併發打滿也看得出來；不同人能開放不同模型，不需要把後端管理權一起交出去。</p>
<p dir="auto">目前一般使用者只開這兩個 model alias：</p>
<pre><code class="language-text">qwen3.8-27b-kai
qwen3.8-27b-kai-thinking
</code></pre>
<p dir="auto">管理者或負責測模型的人才會看到完整的 11 個模型。一般同事的限制先設成每分鐘 20 次、同時最多 2 個 request。這不是什麼精密算出來的神奇數字，只是避免一個人的 agent loop 暫時占滿整個 gateway。</p>
<p dir="auto">建立 virtual key 的概念如下。這段要在 gateway 的管理環境執行，<code>LITELLM_MASTER_KEY</code> 不能發給一般使用者：</p>
<pre><code class="language-bash">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"
    }
  }'
</code></pre>
<p dir="auto"><code>developer-a</code> 只是匿名範例。實際環境可以用公司帳號、工號或不含個資的內部代號，但不要把人的姓名和 API key 一起貼進文件。</p>
<p dir="auto">拿到 key 後，每位同事只需要設定自己的環境變數：</p>
<pre><code class="language-bash">export LAB_API_KEY="your-personal-virtual-key"
pi
</code></pre>
<p dir="auto">同事端的 <code>models.json</code> 建議只列出上面兩個模型。就算把 11 個模型全寫進去，Pi 也只是在選單裡顯示它們；真正送 request 時，LiteLLM 還是會依該 virtual key 的 model allowlist 擋掉未授權模型。乾脆不要顯示，使用上比較不會困惑。</p>
<p dir="auto">發 key 時，我會走密碼管理器或一次性的安全訊息，不放在 Git、wiki、工單截圖或聊天群組。Pi 設定檔只引用 <code>$LAB_API_KEY</code>，所以團隊可以共用同一份去識別化的 <code>models.json</code>，每個人仍然使用不同憑證。</p>
<p dir="auto">多人接入還有一個容易誤會的地方：rate limit 和 GPU capacity 是兩回事。</p>
<pre><code class="language-text">多位同事的 Pi
    │ 各自的 virtual key
    ▼
LiteLLM：驗證、allowlist、RPM、並行限制、排隊
    ▼
Kubernetes Service
    ├─ Pod A：1 個 65K slot
    └─ Pod B：1 個 65K slot
</code></pre>
<p dir="auto">目前後端最多只有兩個 active inference slots；ComfyUI 使用一張卡時則剩一個。把七位同事都設成 <code>max_parallel_requests: 2</code>，不代表瞬間會生出 14 個 GPU slots；它只是每把 key 的上限。多人同時工作時，超出的 request 還是要在 gateway 或 backend 排隊。</p>
<p dir="auto">實際 onboarding 流程很短：管理者替新同事建立一把 virtual key，透過安全管道交付；同事匯入共用的 Pi 模型設定並 export 自己的 key；最後跑一次 <code>pi --list-models lab</code> 和短對話確認。離職、設備遺失或 key 疑似外流時，只撤銷那一把即可，不會影響其他人。</p>
<h2>Context 不是填 65536 就沒事了</h2>
<p dir="auto">這裡踩過一個很實際的坑。</p>
<p dir="auto">模型的 context 上限是 65,536 tokens，但 request 計算的是 input 加上預留 output。當時 Pi 已經累積約 60,415 input tokens，模型設定又留了 8,192 output tokens，最後送出去變成 68,607，LiteLLM 直接回 HTTP 400。</p>
<p dir="auto">後來把 Qwen 3.8 的 <code>maxTokens</code> 降到 4,096。對 coding agent 來說通常夠用，也能替長對話留一點空間。Pi 本身有 auto-compaction，必要時也可以手動輸入：</p>
<pre><code class="language-text">/compact
</code></pre>
<p dir="auto">另一個小坑是長時間執行的 foreground command。例如：</p>
<pre><code class="language-bash">python3 -m http.server 8898
</code></pre>
<p dir="auto">這個程序不會自己結束，Pi 的 bash tool 就會一直顯示 <code>Working...</code>。我現在會把這類服務丟到 tmux，或明確放到背景跑。</p>
<h2>目前用了哪些 Skills</h2>
<p dir="auto">Pi 的 built-in tools 和 Skills 是兩回事。讀寫檔案、編輯與執行 shell 是內建工具；Skills 則是按任務載入的工作說明，不需要全部塞進每一輪 prompt。</p>
<p dir="auto">Pi 啟動時目前會載入 33 個 Skills。沒有必要逐個介紹，但大致分成幾組：</p>
<ul>
<li>UI、設計與動畫：<code>apple-design</code>、<code>emil-design-eng</code>、<code>figma</code>、<code>ui-ux-pro-max</code>、<code>wireframe-prototyping</code>、<code>improve-animations</code>、<code>review-animations</code>，以及 <code>gsap-core</code>、<code>gsap-react</code>、<code>gsap-scrolltrigger</code> 等八個 GSAP Skills。</li>
<li>Mobile 與上架：<code>mobile-design</code>、<code>react-native-design</code>、<code>react-native-best-practices</code>、<code>app-store-preflight-skills</code>、<code>app-store-screenshots</code>。</li>
<li>Cloudflare 與 backend：<code>cloudflare</code>、<code>agents-sdk</code>、<code>durable-objects</code>、<code>workers-best-practices</code>、<code>wrangler</code>、<code>sandbox-sdk</code>、<code>turnstile-spin</code>，還有 Email Service、Cloudflare One 與 migration 相關 Skills。</li>
<li>一般工具：<code>web-perf</code>、<code>find-skills</code>、<code>humanizer-zh-tw</code>。</li>
</ul>
<p dir="auto">平常不做對應任務時，Pi 不會把每份 Skill 全部硬塞進 context，這點對 65K 的本地模型很重要。它先看到名稱與用途，真的需要時才讀完整內容。</p>
<p dir="auto">目前也載入了一個 <code>muxy-notify.ts</code> extension，用來補終端工作流程的通知。這類 extension 和 Skills 不同：Skills 比較像工作手冊，extension 則能直接註冊事件、命令或 UI 行為。</p>
<p dir="auto">Skills 放在：</p>
<pre><code class="language-text">~/.pi/agent/skills/&lt;skill-name&gt;/SKILL.md
</code></pre>
<p dir="auto">有些 Cloudflare Skills 同時存在 <code>~/.pi/agent/skills</code> 和共用的 <code>~/.agents/skills</code>。Pi 啟動時會顯示 <code>Skill conflicts</code>，保留使用者目錄裡的版本並跳過另一份。這是正常的去重訊息，不代表載入失敗，也不會讓同一份 Skill 執行兩次。</p>
<p dir="auto">專案自己的規則則放在 repo 裡的 <code>AGENTS.md</code>。這樣同一個 Qwen model 換到不同專案時，會拿到不同的工作方式，不用另外訓練模型。</p>
<h2>寫 code 和產生圖片可以接在同一個工作流程</h2>
<p dir="auto">這套不只綁一個文字模型。VM106 上還有 ComfyUI Pod，所以 Pi 可以在 Qwen 3.8 和圖片產生工具之間切換使用。兩者共用同一組 GPU，但不會硬塞進同一個 container。</p>
<p dir="auto">Qwen 仍然是主要 agent，負責看專案、整理 prompt、決定檔名和修改程式。真的要畫圖時，再透過 image generation tool 把 workflow 送到 ComfyUI。圖片完成後，tool 把檔案路徑和基本資訊交回 Pi，Qwen 就能繼續把圖放進網頁、App asset catalog 或遊戲專案。</p>
<pre><code class="language-text">使用者：幫這個 landing page 做一張 hero image
    ▼
Qwen 3.8：讀現有 UI、整理尺寸與視覺需求
    ▼
GPU broker：喚醒 ComfyUI，推理服務由 2 pods 縮成 1 pod
    ▼
ComfyUI Pod：執行指定 workflow，產生 PNG / WebP
    ▼
Qwen 3.8：檢查輸出、改檔名、接進程式碼
</code></pre>
<p dir="auto">所以這裡說的「切換」不是每次都用 <code>/model</code> 手動換模型。比較順的做法是把 ComfyUI API 包成 Pi extension 或 Skill，註冊成像 <code>generate_image</code> 這樣的 tool。使用者照常描述需求，agent 判斷何時呼叫它；ComfyUI 要跑哪一份 workflow、用哪個 checkpoint，留在 tool 端處理，不必動到 Qwen 的設定。</p>
<p dir="auto">ComfyUI 的位址和憑證同樣只放環境變數：</p>
<pre><code class="language-bash">export COMFYUI_API_BASE="https://images.example.com"
export COMFYUI_API_KEY="your-comfyui-key"
</code></pre>
<p dir="auto">團隊使用時，文字模型和圖片工具分開授權。有人只需要 coding，就只拿 LiteLLM 的 Qwen key；需要產圖的人再開 ComfyUI 入口權限。這樣圖片 job、LLM token 與存取紀錄不會混在一起，撤銷權限也比較乾淨。</p>
<p dir="auto">還有一點常被混在一起：model 支援 image input，代表它能「看圖」；image generation tool 才是「產圖」。這兩件事不是同一個 capability。我們目前的 Qwen 路徑以文字和 coding tools 為主，圖片輸出交給獨立工具處理。</p>
<p dir="auto">GPU 的交接由 VM106 裡的 broker 處理。平常 ComfyUI 是 <code>0 replicas</code>，兩張卡都給 llama.cpp；有人喚醒生圖服務時，broker 把 ComfyUI scale 到 1，同時把 llama.cpp 從 2 pods 縮成 1 pod。實測約 10 秒後 broker 會做出讓卡決策，完成後是一張卡推理、一張卡生圖，Qwen API 在切換期間仍然可用。</p>
<pre><code class="language-text">平常：      llama.cpp × 2    ComfyUI × 0
有人要生圖：llama.cpp × 1    ComfyUI × 1
生圖閒置後：llama.cpp × 2    ComfyUI × 0
</code></pre>
<p dir="auto">目前喚醒的保留時間是 15 分鐘。就算使用者只打開頁面、最後沒有送 workflow，ComfyUI 仍會暫時占住一張卡；時間到且 queue 為空，broker 才會收掉 ComfyUI，把第二張卡還給推理。這個設定有點浪費，但可以避免使用者還在挑 workflow 時服務突然被收走。</p>
<h2>後端正在玩的 KAI 環境</h2>
<p dir="auto">後端目前是一套 KAI Scheduler POC，不是大型正式叢集，主要拿來試 GPU queue、priority、preemption 和工作負載排程。</p>
<p dir="auto">技術堆疊大概是：</p>
<ul>
<li>Proxmox VE 上的一台 Debian VM</li>
<li>8 vCPU、64 GiB RAM</li>
<li>2 張 AMD R9700，PCIe passthrough 並開 ReBAR</li>
<li>k3s</li>
<li>KAI Scheduler v0.20.1</li>
<li>AMD GPU device plugin</li>
<li>llama.cpp + Vulkan/RADV，另有按需啟動的 ComfyUI Pod</li>
<li>Qwen 3.8 27B <code>Q4_K_XL</code> GGUF</li>
<li>KV cache 使用 <code>q8_0</code></li>
<li>LiteLLM 做 gateway、alias、API key 與流量限制</li>
</ul>
<p dir="auto">Inference 平常採兩個獨立 Pod，每個 Pod 吃一張 GPU；下面的 replica 數會由 broker 在 1 和 2 之間調整：</p>
<pre><code class="language-yaml">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: 1
</code></pre>
<p dir="auto">llama.cpp 的核心參數目前偏保守：</p>
<pre><code class="language-bash">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
</code></pre>
<p dir="auto">也就是每張卡一個 65K slot。ComfyUI 關閉時整個服務有兩個 slots，生圖期間剩一個。之前試過把更多 KV 塞進主記憶體，能跑，但 decode 掉得很明顯；也試過一次把 context 和 slot 拉太高，最後 VM OOM，連 passthrough GPU 都卡進 D3cold。現在寧可讓 gateway 排隊，也不硬撐表面上的高併發。</p>
<h2>現在用起來的感覺</h2>
<p dir="auto">Pi 很適合這種自架模型。它本身薄，模型、工具、Skills 和 provider 都能分開換；出了問題也比較容易知道是 agent、gateway，還是 inference backend。</p>
<p dir="auto">Qwen 3.8 27B 當然不是所有情況都能取代大型 hosted model。它的好處是資料和流量留在自己的環境，成本也比較容易控制。現在真正需要繼續磨的不是「能不能對話」，而是長 session 的 compaction、tool calling 相容性，以及兩個 GPU slot 在多人使用時怎麼排隊。</p>
<p dir="auto">至少目前，這套已經不是單純跑 benchmark 的 demo。我會真的開著 Pi，用它進 repo 做事。</p>
<p dir="auto"><img src="https://upload.lcz.me/uploads/df36f332-31c6-4c81-819c-4e5142749d8c.jpeg" alt="中截圖 2026-08-29 下午4.21.35.jpeg" class=" img-fluid img-markdown" /><br />
<img src="https://upload.lcz.me/uploads/cca1b9d6-c618-43ea-b32a-42ef9eabc231.jpeg" alt="中截圖 2026-08-29 下午4.21.27.jpeg" class=" img-fluid img-markdown" /></p>
]]></description><link>https://lcz.me/topic/1401</link><generator>RSS for Node</generator><lastBuildDate>Mon, 07 Sep 2026 21:18:00 GMT</lastBuildDate><atom:link href="https://lcz.me/topic/1401.rss" rel="self" type="application/rss+xml"/><pubDate>Sat, 29 Aug 2026 08:22:52 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to 在辦公室架一套能共用的本地 AI 環境：Qwen 3.8、Pi Agent 與 KAI Scheduler on Sat, 29 Aug 2026 16:33:18 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/botio-kuo" aria-label="Profile: Botio-Kuo">@<bdi>Botio-Kuo</bdi></a> 主要是自己家裡買不起太貴的設備，都是在 HomeLab 實驗後再移到公司 server 上面進行比較大規模的驗證， 我們目前都是使用 liteLLM ，最近是在評估 omnirouter  / bifrost ，有什麼好的經驗也可以分享一下</p>
<p dir="auto">工作上還是只能氪金 codex + claude code 才能應付大規模的專案....<br />
這套比較像是拿來做一些打雜，還有不趕時間的小玩具/ ETL 自動化的整理之類的</p>
]]></description><link>https://lcz.me/post/14830</link><guid isPermaLink="true">https://lcz.me/post/14830</guid><dc:creator><![CDATA[CS6]]></dc:creator><pubDate>Sat, 29 Aug 2026 16:33:18 GMT</pubDate></item><item><title><![CDATA[Reply to 在辦公室架一套能共用的本地 AI 環境：Qwen 3.8、Pi Agent 與 KAI Scheduler on Sat, 29 Aug 2026 12:50:29 GMT]]></title><description><![CDATA[<p dir="auto">寫的太好了！！ <img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/1f647.png?v=2fb7360d8c6" class="not-responsive emoji emoji-android emoji--bow" style="height:23px;width:auto;vertical-align:middle" title="🙇" alt="🙇" /><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/1f3fb.png?v=2fb7360d8c6" class="not-responsive emoji emoji-android emoji--skin-tone-2" style="height:23px;width:auto;vertical-align:middle" title="🏻" alt="🏻" /> 大神<br />
omnirouter 我个人觉得比 LITELLM 好用 可以参考看看<br />
另外感覺你都是用 DESIGN 之類比較小型的 SKILL 所以 64k 上下文還應付的了</p>
<p dir="auto">但如果載入或是使用 大型SKILL 。。。 通常 auto compact 之類的也會沒有做用，因為上下文已經被skill 佔滿，就我個人來說 64k 上下文真的是硬傷。。。 所以新的 mac studio 好吸引我阿～～～</p>
]]></description><link>https://lcz.me/post/14767</link><guid isPermaLink="true">https://lcz.me/post/14767</guid><dc:creator><![CDATA[Botio Kuo]]></dc:creator><pubDate>Sat, 29 Aug 2026 12:50:29 GMT</pubDate></item><item><title><![CDATA[Reply to 在辦公室架一套能共用的本地 AI 環境：Qwen 3.8、Pi Agent 與 KAI Scheduler on Sat, 29 Aug 2026 09:33:22 GMT]]></title><description><![CDATA[<p dir="auto">非常好的分享，有空抄下作业。</p>
]]></description><link>https://lcz.me/post/14761</link><guid isPermaLink="true">https://lcz.me/post/14761</guid><dc:creator><![CDATA[terry]]></dc:creator><pubDate>Sat, 29 Aug 2026 09:33:22 GMT</pubDate></item><item><title><![CDATA[Reply to 在辦公室架一套能共用的本地 AI 環境：Qwen 3.8、Pi Agent 與 KAI Scheduler on Sat, 29 Aug 2026 08:23:10 GMT]]></title><description><![CDATA[<p dir="auto"><img src="https://upload.lcz.me/uploads/0923eaba-2e52-4724-92cc-75d6dc5cc3ae.jpeg" alt="中截圖 2026-08-29 下午4.21.35.jpeg" class=" img-fluid img-markdown" /><br />
<img src="https://upload.lcz.me/uploads/40c97ee8-ba83-41c7-99b0-d2fbbc6520ef.jpeg" alt="中截圖 2026-08-29 下午4.21.27.jpeg" class=" img-fluid img-markdown" /></p>
<p dir="auto">產出來的網頁</p>
]]></description><link>https://lcz.me/post/14747</link><guid isPermaLink="true">https://lcz.me/post/14747</guid><dc:creator><![CDATA[CS6]]></dc:creator><pubDate>Sat, 29 Aug 2026 08:23:10 GMT</pubDate></item></channel></rss>