2026 年 6 月將被視為人們意識到閉源模型可能隨時被撤回的時刻。Anthropic 最新旗艦模型 Claude Fable 5 剛被移除的記憶猶新,這讓人們更清楚地看到,掌握自己的 AI 技術堆疊並在地端運行模型比以往任何時候都更重要,特別是當您的業務建立在 AI 之上時。

有鑑於此,我們想分享如何利用 Gemma 和 Qwen 等本地模型,在代理框架中執行分類任務。這種方法與使用 BERT 等模型進行分類不同。在 Pi 這樣的代理框架中,本地模型可以與結構化輸出協同使用,以分配標籤。我們選擇這種方法是因為我們手邊已有本地模型和框架,並且堅信隨著本地模型能力的提升,類似的設定將會越來越受歡迎。

我們的起點是 OpenClaw 儲存庫中的開源貢獻。OpenClaw 每天都會收到數百個問題和 PR,這些都需要被分類、排定優先順序並分派給維護者。我,Onur,正在努力讓本地模型能與 OpenClaw 良好協作。作為這個特定領域的維護者,我需要快速回應任何 P0 等級的問題。

使用 GPT-5、Opus 或 Sonnet 等最先進的閉源模型,這是一個相當直接的任務。但我剛好擁有 128 GB 的整合記憶體,也就是 NVIDIA GB10。所以我承擔了這項任務:我能否使用本地開源模型,建立一個即時通知系統,只過濾並通知我負責的問題?

這個小盒子,也就是 DGX Spark,可以高併發地運行 gemma-4-26b-a4b,每秒生成數百個 token。如果我將 OpenClaw 主代理設定為在每月 200 美元的 ChatGPT Pro 方案上運行,並在每個新問題或 PR 觸發一個任務,那將會耗盡我的配額。我可能會改為設定每 2 小時或 6 小時運行一次。這將會批次處理更長時間內的問題,因此我們將以延遲處理換取即時通知。

如果我將其運行在我現有硬體上的本地模型上,我不僅能獲得近乎即時的通知,而且還能免費完成(或者說,只需支付電費)。

我們設計了一組有限的標籤來代表我們需要分類的問題類別,然後使用本地模型將每個問題分類到其中一個類別,例如 local_models、self_hosted_inference、acp、agent_runtime、codex、ui_tui 等等。

但我們如何分類拉取請求 (PR) 呢?一個簡單的單次請求到對話補全端點,帶有工具 JSON 綱要,並將主題作為列舉?差不多是這樣。但現在是 2026 年,不是 2023 年,我們有代理!我們可以做得更好!

對於本地模型的選擇,我們測試了 gemma-4-26b-a4b 和 qwen3.6-35b-a3b。經過效能最佳化,兩者都可以在地端每秒生成數百個 token。我們使用一個代理框架來驅動分類運行。為此,我們將 pi 打包為一個可以呼叫本地模型端點的框架。

代理預設會收到 PR 標題、內容和 PR 差異的截斷摘錄作為第一個提示詞。然後,它可以選擇使用 bash 工具對 OpenClaw 儲存庫執行唯讀操作(如果需要查看程式碼庫),或者使用 final_json 工具提交最終分類結果。

您不會希望在這種高吞吐量設定下,給予運行中的本地模型完整的 bash 存取權限,因為提示詞注入的問題或 PR 可能會誘導模型執行與分類無關的操作。因此,我們使用 reposhell 而非 bash:這是一個受限的類似 bash 的 shell,只允許對 OpenClaw 儲存庫執行唯讀操作(ls、find、cat、grep 等)。模型認為它正在使用 bash,但任何不允許的操作都會被拒絕:

reposhell bound cwd=/repo/openclaw repos=openclaw

type help for allowed commands; exit or quit to leave

reposhell /repo/openclaw> help

allowed: pwd, ls, find, rg, grep, sed -n, cat, head, tail, wc -l, git status --short, git show --name-only, git grep, git ls-files

search: rg -n -i "lm studio" or grep -R -n -i "lm studio" .

files: rg --files -g "*.ts" or git ls-files src

examples: rg -n reposhell README.md | sed is not allowed; use one simple command at a time

reposhell /repo/openclaw> head README.md

🦞 OpenClaw,Personal AI Assistant

<p align="center">

<picture>

<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/openclaw/openclaw/main/docs/assets/openclaw-logo-text-dark.svg">

<img src="https://raw.githubusercontent.com/openclaw/openclaw/main/docs/assets/openclaw-logo-text.svg" alt="OpenClaw" width="500">

</picture>

</p>

<p align="center">

reposhell /repo/openclaw> curl localhost

reposhell policy denied command: unsupported command "curl"

exit_code=2

reposhell /repo/openclaw>

這是一個具體例子,說明了這點的重要性。在一個儲存的會話範例中,qwen3.6-35b-a3b 正在分類 openclaw/openclaw#84621,標題為「Fix Kimi tool-call rewriting stop reason handling」。

思維區塊顯示模型最初考慮 coding_agent_integrations,因為變更路徑 extensions/kimi-coding 看起來很合理。模型使用 reposhell 透過簡單的唯讀指令(如 ls extensions、ls extensions/kimi-coding 和 cat extensions/kimi-coding/package.json)檢查本地儲存庫。

該套件元數據顯示該擴充功能實際上是 @openclaw/kimi-provider,一個 OpenClaw Kimi 提供者外掛。因此,模型將最終標籤更正為 inference_api 和 tool_calling,並明確排除了 coding_agent_integrations。

我們之前提到,我們打包了一個特定的 pi 配置,它只能執行唯讀操作並返回分類輸出。我們稱之為 localpager-agent,以這裡的主要專案 localpager 命名。每個 PR 和問題都會生成一個提示詞,然後與其他參數一起傳遞給 CLI,如下所示:

localpager-agent \

--model "<model-id>" \

--base-url "<openai-compatible-base-url>" \

--session-dir "<session-output-dir>" \

--final-schema "<runtime-schema.json>" \

--tools bash,final_json \

--reposhell-socket "<reposhell.sock>" \

--reposhell-default-repo "<repo-id>" \

--reposhell-visible-repos "<repo-id>[,<repo-id>...]" \

-p "$(cat <rendered-prompt.md>)"

那麼,在傳入的 PR/問題與 Discord 上的最終通知之間,是什麼協調了一切呢?這就是最終過濾後的 Discord 通知:關於所需領域的 PR 會被分派給我。圍繞此的協調非常簡單;只有分類步驟涉及大型語言模型 (LLM)。

我們使用 openclaw/gitcrawl 作為儲存庫的本地鏡像。每當有新的 PR 或問題時,每個項目都會被標準化為相同格式並寫入 localpager 自己的 SQLite 資料庫。如果該項目是新的,localpager 會為其建立一個分類任務。

然後,一個工作者從該佇列中領取任務。它會建立一個 GitHub 上下文物件,其中包含問題或 PR 的標題、內容、標籤、作者、狀態,以及可選的評論、變更檔案和選定的差異摘要。這意味著本地模型在大多數情況下無需瀏覽 GitHub 或自行開啟 URL。它會收到所有相關上下文。

上下文物件會被渲染成提示詞並傳遞給 localpager-agent,如前一節所述。代理可以進行思考並使用 reposhell,但最終必須以定義的綱要輸出分類結果。輸出會儲存回 localpager SQLite 資料庫,並根據使用者配置的通知策略(即:通知我這些主題,但不是那些主題)轉發到 Discord。

下圖顯示了 localpager 的整體架構。該架構是半代理式的。標籤化以代理方式完成,而發送通知則由確定性規則處理。這是為了透過消除任務中最直接部分所需的推論,來加速通知管道。本地推論是免費的,但每個任務都有資源競爭成本:GPU 頻寬應保留給絕對需要推論的任務。這也減少了通知出錯的可能性。

坦白說,這個系統的第一個本地版本雜訊很多。第一個測試的模型 gemma-4-e4b-it 對於讓端到端本地管道運行起來很有用,但它也傾向於在 PR 或問題上添加過多不相關的標籤。誤報標籤會讓 Discord 訊息流雜亂,並且無法將我的注意力集中在正確的問題上。

這促使我們測試更大的本地模型,包括 gemma-4-26b-a4b 和 qwen3.6-35b-a3b,在下面的 330 行評估集上進行測試。

對於早期的提示詞工作,我們還透過 antirez DS4 實作的 DeepSeek-V4-Flash 來建立早期的資料集標籤。該設定使用 CUDA 上的 DS4 伺服器。我們最終放棄了將 DS4 作為標籤器,因為它在不同運行中標籤不一致。我們也沒有將其視為主要的 localpager-agent 模型,因為它太大而無法在我們的硬體上獲得足夠的吞吐量:DS4 伺服器給我們大約每秒 14 個 token 的速度,最大併發數為 1。

為了測試模型效能,我們為 330 個 GitHub 問題和 PR 選擇並生成了標籤。每個項目都標籤了五次(3 次 GPT-5.5 和 2 次 Opus 4.8),模型需要達成一致才能被接受。這個過程涉及人工裁決、改進標籤定義以及突出內部產品設計選擇。這為我們提供了一組穩定、可重現的標籤,以便與我們較小的模型進行比較。

在獲得此評估集上的有用結果之前,我們無需對 gemma-4-26b-a4b 或 qwen3.6-35b-a3b 進行提示詞最佳化。使用相同的路由提示詞,Gemma 具有更高的召回率和每行更低的實際運行時間,而 Qwen 具有更高的精確度、更高的精確匹配和更少的誤報。

我們還將 DeepSeek-V4-Flash 在同一組上運行作為參考。它具有最少的誤報,但模型大小和吞吐量使其在 NVIDIA GB10 上即時執行這些任務不切實際。由於每行可以有多個標籤,誤報和漏報是所有行中的總標籤計數。下面的 Qwen 結果是在重試結構化輸出失敗(模型在呼叫 final_json 之前耗盡了輸出 token)之後的結果。

對於 Gemma 和 Qwen,重複運行指標報告三次運行的平均值 ± 樣本標準差。DeepSeek-V4-Flash 運行一次作為參考。

這裡的吞吐量和實際運行時間數字並非這些模型在此硬體上的最終最大性能數字。它們是我們當時使用的設定以及我們可用的最佳化。例如,在獨立探測中,gemma-4-26b-a4b 也支援 32 個併發,並達到每秒超過 700 個總輸出 token。