記事一覧に戻る

*ビールのように無料(電気代を除く)。すでにハードウェアを所有している場合に限る

2026年6月は、人々がクローズドモデルが奪い去られる可能性に気づいた瞬間として歴史に残るでしょう。Anthropicの最新フラッグシップモデル「Claude Fable 5」の削除が記憶に新しい中、AIスタックを自ら所有し、ローカルでモデルを実行できることがこれまで以上に重要である理由がわかります。特に、AIを基盤にビジネスを構築している場合に顕著です。

そのような背景から、私たちはGemmaやQwenのようなローカルモデルをエージェントハーネス内で使用し、分類タスク[1]を実行する方法を共有したいと思います。このアプローチは、BERTのようなモデルを分類に使用する方法とは異なります。Piのようなエージェントハーネス内のローカルモデルは、構造化出力と組み合わせてラベルを割り当てるために使用できます。私たちがこのアプローチを選んだのは、すでにローカルモデルとハーネスが手元にあり、ローカルモデルの能力向上に伴い同様のセットアップの人気が高まると確信しているためです。[2]

私たちの出発点は、OpenClawリポジトリへのオープンソース貢献でした。OpenClawでは毎日数百件のIssueとPRが発生し、それらをトリアージし、優先順位付けし、メンテナーにルーティングする必要があります。私はOnurとして、ローカルモデルをOpenClawでうまく動作させることに取り組んでいます。この特定の分野のメンテナーとして、私はP0の問題に迅速に対応する必要があります。

GPT-5、Opus、SonnetのようなSOTAのクローズドモデルであれば、これはかなり簡単なタスクです。しかし、私は128GBのユニファイドメモリ、つまりNVIDIA GB10を所有しています。そこで私は次の課題に取り組みました:

自分が担当するIssueのみをフィルタリングして通知するリアルタイム通知システムを、ローカルのオープンウェイトモデルで構築できるか?

NVIDIA DGX Spark
この小さな箱、通称DGX Sparkは、gemma-4-26b-a4bを高い同時実行性で実行し、1秒間に数百トークンを生成できます。

ChatGPT Proプラン(月額200ドル)で動作するOpenClawのメインエージェントを、新しいIssueまたはPRごとにジョブをトリガーするように設定すると、クォータが使い切られてしまいます。代わりに2時間ごと、または6時間ごとに実行するように設定することもできますが、これでは長い期間にわたってIssueをバッチ処理することになり、リアルタイム通知を犠牲にして遅延処理を行うことになります。

すでに稼働している手持ちのハードウェア上でローカルモデルを実行する場合、ほぼ瞬時の通知が可能になるだけでなく、無料(または電気代のみ)で実行できます。

IssueとPRの分類

私たちはトリアージが必要なIssueのカテゴリを表す有限のラベルセットを作成し、ローカルモデルを使って各Issueをlocal_modelsself_hosted_inferenceacpagent_runtimecodexui_tuiなどのカテゴリに分類します。[3]

しかし、プルリクエストはどのように分類するのでしょうか?Chat Completionsエンドポイントへの単純な単一リクエストとツールJSONスキーマ、そしてトピックを列挙型として使用するだけでしょうか?

ある意味ではそうです。しかし、これは2023年ではなく2026年であり、私たちにはAGENTSがあります。より良い方法があります!

ローカルモデルの選択肢として、gemma-4-26b-a4bqwen3.6-35b-a3bをテストしました。パフォーマンス最適化により、どちらもローカルで1秒間に数百トークンを生成できます。

私たちは分類実行を駆動するためにエージェントハーネスを使用します。このために、piをローカルモデルエンドポイントを呼び出せるハーネスとしてバンドルしています。

エージェントはデフォルトで、最初のプロンプトでPRのタイトル、本文、およびPR差分の切り詰められた抜粋を受け取ります。その後、bashツールを使用してOpenClawリポジトリに対して読み取り専用の操作を実行するか(コードベースを確認する必要がある場合)、final_jsonツールを使用して最終的な分類結果を送信することを選択できます。

この高スループット設定で動作するローカルモデルに完全なbashアクセス権を与えたくはありません。プロンプトインジェクションされたIssueやPRが、分類とは無関係の動作にモデルを誘導する可能性があるためです。

そのため、bashの代わりにreposhellを使用します。これはOpenClawリポジトリに対して読み取り専用操作(lsfindcatgrepなど)のみを許可する制限付きのbashライクなシェルです。モデルは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>

これが重要になった具体例を挙げます。保存されたセッション例の1つで、qwen3.6-35b-a3bopenclaw/openclaw#84621を分類していました。タイトルはFix Kimi tool-call rewriting stop reason handlingです。思考ブロックでは、モデルが最初に変更されたパスextensions/kimi-codingが妥当に見えたためcoding_agent_integrationsを検討している様子が示されています。モデルはls extensionsls extensions/kimi-codingcat extensions/kimi-coding/package.jsonのような単純な読み取り専用コマンドでreposhellを使用してローカルリポジトリを検査しました。そのパッケージメタデータにより、拡張機能が実際には@openclaw/kimi-provider、つまりOpenClawのKimiプロバイダープラグインであることがわかりました。そのためモデルは最終ラベルをinference_apitool_callingに修正し、coding_agent_integrationsを明示的に除外しました。

先に述べたように、私たちは読み取り専用操作の実行と分類出力の返却のみが可能な特定のpi設定をバンドルしています。これをlocalpager-agentと呼び、このメインのプロジェクトであるlocalpagerにちなんで名付けました。各PRとIssueはプロンプトを生成し、以下のように他の引数とともに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とIssueの処理

では、受信したPR/IssueからDiscord上の最終通知までの間を調整するのは何でしょうか?

Localpager Discord notification
これが最終的にフィルタリングされたDiscord通知の例です:希望する分野に関するPRが私にルーティングされます。

これを調整する仕組みは非常にシンプルです。分類ステップのみがLLMを必要とします:

  1. openclaw/gitcrawlをリポジトリのローカルミラーとして使用します。新しいPRまたはIssueがあるたびに、各アイテムは同じ形状に正規化され、localpager独自のSQLiteデータベースに書き込まれます。アイテムが新しい場合、localpagerはそのアイテムの分類ジョブを作成します。
  2. ワーカーがそのキューからジョブを要求します。IssueまたはPRのタイトル、本文、ラベル、作成者、状態、およびオプションでコメント、変更されたファイル、選択された差分の抜粋を含むGitHubコンテキストオブジェクトを構築します。つまり、ローカルモデルはほとんどの場合、GitHubを閲覧したりURLを開いたりする必要がありません。関連するすべてのコンテキストが提供されます。
  3. コンテキストオブジェクトはプロンプトにレンダリングされ、前のセクションで説明したようにlocalpager-agentに渡されます。エージェントは思考してreposhellを使用できますが、最終的には定義されたスキーマで分類結果を出力する必要があります。
  4. 出力はlocalpagerのSQLiteデータベースに保存され、ユーザーが設定した通知ポリシー(例:これらのトピックについては通知するが、他のトピックについては通知しない)に基づいてDiscordに中継されます。

以下は、localpagerの全体アーキテクチャを示す図です:

Localpager architecture

このアーキテクチャは半エージェント的です。ラベル付けはエージェント的に行われますが、通知の送信は決定論的なルールで処理されます。これは、タスクの最も単純な部分で推論を必要としないようにすることで、通知パイプラインを高速化するためです。ローカル推論は無料ですが、各タスクにはリソース競合コストがあります。GPU帯域幅は推論が絶対に必要なタスクに予約する必要があります。これにより、通知エラーの可能性も低減されます。

ローカルモデルはPRをトリアージできるか?

率直に言って、このシステムの最初のローカルバージョンはノイズが多くありました。最初にテストしたモデルであるgemma-4-e4b-itは、エンドツーエンドのローカルパイプラインを動作させるためには役立ちましたが、PRやIssueに無関係なラベルを付けすぎる傾向がありました。誤検知ラベルはDiscordフィードをノイズだらけにし、適切なIssueへの注意を集中させにくくします。これにより、gemma-4-26b-a4bqwen3.6-35b-a3bなどのより大きなローカルモデルを、以下の330行の評価セットでテストする方向に進みました。

初期のプロンプト作業では、DeepSeek-V4-FlashをantirezのDS4実装[4]を通じて使用して初期データセットのラベルを作成しました。このセットアップではCUDA上でDS4サーバーを使用しました。DS4をラベラーとして使用するのは、実行間で一貫したラベル付けを行わなかったため、最終的に断念しました。また、DS4サーバーが14トークン/秒で最大同時実行数が1だったため、ハードウェア上で十分なスループットを得るには大きすぎるため、メインのlocalpager-agentモデルとしても検討しませんでした。

モデルのパフォーマンスをテストするために、330件のGitHub IssueとPRを選択してラベルを生成しました。各アイテムは5回ラベル付けされ(GPT-5.5で3回、Opus 4.8で2回)、モデル間で合意が得られたものを採用しました。このプロセスには、手動での判定、ラベル定義の改善、モデルの内部製品設計選択の強調が含まれました。これにより、比較用の安定した再現可能なラベルセットが得られました。

gemma-4-26b-a4bqwen3.6-35b-a3bについては、この評価セットで有用な結果を得るためにプロンプト最適化は必要ありませんでした。同じルーティングプロンプトを使用したところ、Gemmaは再現率が高く、行あたりの壁時計時間が短かった一方、Qwenは適合率が高く、完全一致が多く、誤検知が少ない結果でした。また、参照として同じセットでDeepSeek-V4-Flashも実行しました。誤検知が最も少なかったものの、モデルサイズとスループットのため、NVIDIA GB10上でこれらのタスクをリアルタイムで実行するには実用的ではありません。各行に複数のラベルが付けられる可能性があるため、誤検知と誤検知は全行にわたるラベル数の合計です。Qwenの結果は、final_jsonを呼び出す前に出力トークンが不足した場合の構造化出力失敗を再試行した後のものです。GemmaとQwenについては、繰り返し実行のメトリクスは3回の実行における平均±サンプル標準偏差を報告しています。DeepSeek-V4-Flashは参照として1回実行しました。

Metric gemma-4-26b-a4b qwen3.6-35b-a3b DeepSeek-V4-Flash
Precision 0.716 ± 0.010 0.831 ± 0.007 0.938
Recall 0.905 ± 0.004 0.818 ± 0.006 0.714
F1 0.800 ± 0.008 0.824 ± 0.002 0.811
Exact match 0.410 ± 0.014 0.540 ± 0.014 0.509
False positives 227.0 ± 10.5 105.7 ± 6.4 30
False negatives 60.0 ± 2.6 115.3 ± 4.0 181
Wall seconds / row 1.41 ± 0.04 13.51 ± 0.79 144.14
Output tok/s / worker 25 50 13
Output tok/s aggregate 402.6 145.3 13
Concurrency 16 4 1
Total parameters 26B 35B 284B
Active parameters 4B 3B 13B

ここでのスループットと壁時計時間の数値は、このハードウェア上でのこれらのモデルの決定的な最大パフォーマンス数値ではありません。利用可能な最適化とともに、当時使用した設定です。たとえば、別のプローブでは、gemma-4-26b-a4bは同時実行数32もサポートし、700を超える集計出力トークン/秒に到達しました。

Benchmark comparison across the 330-row label set
330行のラベルセット全体にわたるベンチマーク比較。各パネルは独自の垂直スケールを使用しています。青は各メトリクスの最良値を表します。PrecisionとRecallのエラーバーは、GemmaとQwenの3回の実行におけるサンプル標準偏差を示しています。

Gemmaのベンチマークでは、gemma-4-26b-a4bをvLLMで提供し、このセットアップで利用可能な最適化を使用しました。その大きな部分はNVFP4量子化です。GB10クラスのBlackwellハードウェアでは、単にモデルファイルが小さくなるだけでなく、Q4_K_MのようなポータブルなGGUF量子化よりもNVIDIA/vLLM実行パスをより直接的に使用できるハードウェアフレンドリーなフォーマットです。実際には、メモリトラフィックが少なく、バッチ処理のための余地が増えます。また、プレフィックスキャッシング、FP8 KVキャッシュ、CUTLASS MoEバックエンド、および言語モデル専用モードを有効にしました。完全な330行の実行は、同時実行数16で約7.5分で完了しました。

OpenClawを使用したリアルタイムパフォーマンスの追跡と検証

先に述べたように、新しいIssueやPRごとにローカルモデルでジョブを実行する代わりに、GPT-5.5をOpenClawで実行するようなSOTAクラウドモデルでn時間(例:2時間)ごとにバッチジョブを実行して、同じ結果を得ることができます。[5]

その場合、ChatGPT Proプランが必要になります。モデルはSOTAであるため、2時間分のIssue/PRをまとめてバッチ処理しても、合理的に良好なパフォーマンスが期待できます。

ローカル分類器がGPT-5.5に対してどの程度うまく機能するかを確認したいため、両方を同時に実行し、GPT-5.5を誤検知と誤検知の判断役とします。2時間ごとに行います。

安全のため、OpenClawジョブをサンドボックスで実行し、結果を報告する公開リポジトリへのアクセスのみ許可します。私たちの場合、OpenClawジョブに機械可読ファイルを更新させ、その後、シンプルなスクリプトがCodexが割り当てたラベルを読み取って誤検知/誤検知のステータスを計算します。出力例:

False negatives

  • Issue #88499 openai-responses provider: 404 on previous_response_id when store=false (default)
    • inventory area: OpenAI-compatible/proxy; notifier topics: agent_runtime, api_surface, sessions; notification: none

False positives

  • PR #88275 fix(models-config): allow self-hosted providers without apiKey in models.json (#88267)
    • notifier interest: i0; topics: self_hosted_inference, local_model_providers, config; notification: sent
  • PR #88266 refactor: extract model catalog core package
    • notifier interest: i1; topics: config, api_surface, local_model_providers; notification: sent
  • PR #88247 feat: add hosted model providers
    • notifier interest: i0; topics: local_model_providers, model_serving, docs, api_surface; notification: sent

分類方法、機械可読ファイルの編集、スクリプトを使用した誤検知と誤検知の取得に関する手順は、OpenClaw cronジョブで参照されるエージェントスキルに記載されています。OpenClawエージェントは新しいIssueやPRを取り込み、適切なラベルとともにJSONファイルに追加し、スクリプトを実行して同じDiscordチャンネルに報告します。これにより、数時間ごとにローカルモデルのパフォーマンスを観察し、見逃しについて通知を受け取ることができます。

結論

Issue/PRトリアージタスクは、私たちが「高スループットトリアージ」と呼ぶ、より広範なタスクセットの特定のケースであると考えています。この記事では、オープンソース貢献という一つの分野で、ローカルモデルを使用してリアルタイムで情報をフィルタリングするというアイデアを探りました。gemma-4-26b-a4bqwen3.6-35b-a3bのような中規模のローカルモデルが、ファインチューニングなしで高い精度でゼロショット分類できる能力は、従来のよりコスト効率の高い分類器モデルに移行する前のクイックプロトタイピングに適した最初の選択肢となります。

ただし、同じアプローチは他の分野にも適用できます:

  • ジャーナリズムにおけるニュース分類
  • XやRedditなどのソーシャルメディアやフォーラムにおける関心のある投稿のフィルタリング
  • カスタマーサポートチケットのトリアージ
  • コンテンツモデレーションアピールのトリアージ
  • セールス活動中の潜在的なアウトリーチのフィルタリング
  • 研究中のarXiv上の特定のトピックのフィルタリング

リストはさらに拡張できますが、アイデアは明確であると考えています。

トリアージ以外にも、高速なローカルモデルを安全に実行するエージェントハーネスで分類を実行する方法も探りました。このアプローチに適した名前はエージェント的分類でしょう。モデルは情報の全体を最初から与えられるのではなく、構造化データを返す前に追加のコンテキストを検索できます。これをまったく新しいアプローチとは言えませんが、このブログ記事が特定のPi+制限付きシェル+final_jsonレシピの良いリファレンスになることを願っています。

脚注

  1. この記事のユースケースでは、PR/Issueを分解して製品サーフェスを正しく理解・ラベル付けすることが難しい問題であることがわかりました。戻る
  2. 私たちのテストでは行いませんでしたが、モデルが次のステップとして情報を収集し、外部分類器を使用することを結論付けることは十分に合理的です。エージェント的 アプローチと従来のアプローチは相互排他的ではありません。戻る
  3. トピックの完全なリストとその他の設定はこちらを参照してください。戻る
  4. antirez/deepseek-v4-ggufからDeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2.ggufを使用しました。戻る
  5. LLMをジャッジとして使用することで「無料」という側面が失われることは認識していますが、私たちの特定の実装は研究目的で行っています。実際には、より大きくより高価なモデルを試用期間中にキャリブレーションのために併用し、その後システムを完全に小さなモデルに移行することができます。最近の実行では、この監査ループは2時間ごとのチェックで約40kのGPT-5.5トークンを使用し、主にキャッシュされたコンテキストで、API料金で1回あたり約2〜3セント、1日12回実行で月額約9ドルでした。これはすべての新規アイテムに対する単一のバッチ監査であり、アイテムごとに1回のジャッジ呼び出しではありません。アイテムごとに行うと、数倍高価になる可能性があります。戻る