Stack Guardian について
Stack Guardianは、ホームAIスタックのノード、モデル、GPU、tailnetの状態を収集し、連続実行の履歴に基づいて検出事項の確度を示します。ローカルモデル環境の運用者やコーディングエージェント向けに、CLI、MCPサーバー、Prometheusメトリクス用HTTPエンドポイント、DeepSeek Harnessプラグインを提供します。
主な機能
- 01ノードとモデルの状態を並行収集
- 02Ollama、互換エンドポイント、GPU、tailnetを確認
- 03連続した検出履歴から確度を評価
- 04明示的な適用が必要な型付き修正
- 05CLI、MCP、HTTPメトリクス、DSHプラグイン
活用シーン
- 01複数ノードのローカルモデル環境の問題を診断
- 02一時的な問題と継続中の検出事項を区別
- 03コーディングエージェントから読み取り専用で状態を照会
- 04Prometheusエンドポイントからメトリクスを取得
MCP 対応
stdio型MCPサーバーは、状態確認、スナップショット取得、検出コードの説明を行う読み取り専用ツールstack_health、stack_snapshot、stack_explainを公開します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
3掲載時のツール一覧 · 5 日前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| node | string | いいえ | ノード名を一つ指定します。省略するとすべてのノードが対象です。 |
詳細
stack-guardian
mcp-name: io.github.LuminariSoftwares/stack-guardian
家庭用 AI スタックの健康診断ツールです。ノード(Ollama または OpenAI 互換エンドポイント、読み込み済みモデル、NVIDIA GPU、tailnet のピア)のスナップショットを収集し、それに対して小さな Python ルールを実行して、直近の実行結果を記録し、信頼度を付けて検出結果を報告します。1 回のサンプルでは最大でも LIKELY、同じ検出結果が 2 回連続すると VERIFIED になります。複数のマシンからなるローカルスタックで「モデルが遅い」と感じる原因は十数通り考えられるため、エージェントや人が推測ではなく、根拠に基づいた段階的な回答を得られるようにするのが目的です。検出結果には、必要なときに自分で実行する型付きの修正を含められますが、自動で実行されることはありません。コーディングエージェント向けの MCP サーバー、Prometheus メトリクスを備えた小さな HTTP エンドポイント、DeepSeek Harness(DSH)プラグインとしても提供されます。
1 つの状態ファイルを使い、examples/nodes.json に対して 2 回実行した結果:
# run 1
FAIL ollama.unreachable [gpu-box-2]: 1 Ollama endpoint(s) do not answer: http://100.64.0.2:11434 (LIKELY)
FAIL ollama.unreachable [home-server]: 1 Ollama endpoint(s) do not answer: http://100.64.0.3:11434 (LIKELY)
FAIL tailnet.peer_offline [home-server]: tailnet peer offline: home-server (LIKELY)
WARN model.expiring [gpu-box]: 1 loaded model(s) unload within 60s: qwen3:8b (LIKELY)
OK ollama.answering [gpu-box]: 1 endpoint(s) answer: http://127.0.0.1:11434
worst: FAIL
# run 2 (same state file)
FAIL ollama.unreachable [gpu-box-2]: 1 Ollama endpoint(s) do not answer: http://100.64.0.2:11434 (VERIFIED)
FAIL ollama.unreachable [home-server]: 1 Ollama endpoint(s) do not answer: http://100.64.0.3:11434 (VERIFIED)
FAIL tailnet.peer_offline [home-server]: tailnet peer offline: home-server (VERIFIED)
WARN model.expiring [gpu-box]: 1 loaded model(s) unload within 60s: qwen3:8b (VERIFIED)
OK ollama.answering [gpu-box]: 1 endpoint(s) answer: http://127.0.0.1:11434
worst: FAIL
これは python examples/demo_report.py の実際の出力です。付属のコレクターとルールを使い、gpu-box からの問い合わせに 127.0.0.1 で応答する Ollama のスタブ、接続しない 2 つのプレースホルダーの tailnet アドレス、そしてどのマシンでも同じ出力になるようにした nvidia-smi と tailscale のサンプル出力を使っています。付属のルールが報告するのは問題のみです。OK の行はスクリプト内の小さなルールによるものです。このブロックとスクリプトの内容が一致しない場合、テストは失敗します。
信頼度の理由。 1 回の実行で検出結果に付けられる信頼度は最大でも LIKELY です。同じ検出結果が 2 回連続して現れると VERIFIED になります。チェックに合格した場合、信頼度は付けられません。家庭内ネットワークでは、スリープ中のノートパソコン、Wi-Fi の切断、モデルの再読み込みによって、数秒だけ問題が起きているように見えることがあります。そのため、1 回だけの不良サンプルは可能性の高い問題として報告し、継続するものだけを事実として報告します。
ステータス:0.3.0。Python 3.11 以降で動作し、標準ライブラリのみを使用します。実行時の依存関係はありません。
ローカルモデルを実行する人向けの、小規模で独立した 4 つのツールのうちの 1 つです。The guardian family をご覧ください。
インフラ自動化企業の StackGuardian とは無関係です。同じ単語ですが、別のプロジェクトです。
クイックスタート(60 秒、オフライン)
macOS / Linux
pip install stack-guardian
curl -LO https://raw.githubusercontent.com/LuminariSoftwares/stack-guardian/main/examples/snapshot.json
curl -LO https://raw.githubusercontent.com/LuminariSoftwares/stack-guardian/main/examples/ctx.json
stack-guardian doctor --snapshot snapshot.json --ctx ctx.json
Windows(PowerShell)、またはシステムの Python に手を加えたくない場合
pipx install stack-guardian
curl.exe -LO https://raw.githubusercontent.com/LuminariSoftwares/stack-guardian/main/examples/snapshot.json
curl.exe -LO https://raw.githubusercontent.com/LuminariSoftwares/stack-guardian/main/examples/ctx.json
stack-guardian doctor --snapshot snapshot.json --ctx ctx.json
pipx は専用の仮想環境にインストールし、PATH に追加します(まだインストールしていない場合は pip install pipx を実行してください)。uvx stack-guardian doctor --snapshot snapshot.json を実行すれば、何もインストールせずに使えます。
サンプルスナップショットでは付属のすべてのルールが 1 回ずつ発動するため、実際のスタックに触れることなく各検出タイプを確認できます。サンプルに FAIL の検出結果が含まれているため、コマンドは 2 で終了します。これは想定どおりです。各行の末尾には信頼度が表示されます。1 回の実行では最大でも (LIKELY)、同じ検出結果が 2 回目にも現れると (VERIFIED) です。
次に、自分のマシンを指定します。
stack-guardian init # writes nodes.json for this machine; never overwrites one
stack-guardian doctor --nodes nodes.json
init は http://127.0.0.1:11434 を調べて Ollama が応答するか確認し、tailnet のピア(tailscale が PATH にある場合)をコメントアウトされた候補として一覧表示します。そこから先は手作業で編集してください。形式は examples/nodes.json とこのツールの機能に記載されています。
クローンから開始する場合(ルールの作成やテストの実行):
git clone https://github.com/LuminariSoftwares/stack-guardian.git
cd stack-guardian
python -m venv .venv
.venv\Scripts\activate # Windows; macOS/Linux: source .venv/bin/activate
pip install -e ".[test]" # drop [test] if you only want to run it, not test it
stack-guardian doctor --snapshot examples/snapshot.json --ctx examples/ctx.json
python -m pytest -q
アーキテクチャ
flowchart LR
nodes["nodes.json"] --> collect
subgraph collect["collect: one snapshot, all nodes in parallel"]
direction TB
ol["Ollama /api/tags, /api/ps<br/>OpenAI-compatible /v1/models"]
gpu["nvidia-smi<br/>(local node only)"]
ts["tailscale status --json"]
end
collect --> snap[("snapshot dict")]
snap --> rules["checks.d/*.py rules<br/>run concurrently;<br/>a crash becomes check.crashed"]
rules --> grade["state.grade<br/>LIKELY until seen twice, then VERIFIED"]
hist[("state.json<br/>last 5 runs per node")] <--> grade
grade --> cli["CLI: doctor<br/>exit 0 / 1 / 2"]
grade --> mcp["MCP stdio server<br/>read-only, never records"]
grade --> http["serve: /snapshot, /metrics"]
grade -.->|"finding.fix"| fix["doctor --fix ID<br/>dry run unless --apply;<br/>snapshot + audit line"]
| モジュール | 役割 |
|---|---|
collect.py, multinode.py, parsers.py | 共通の期限内にすべてのノードを並列で調査し、Ollama、OpenAI 互換、nvidia-smi、tailscale の出力を単一のスナップショット辞書に解析します。応答しないエンドポイントも例外ではなく、データとして扱います。 |
core.py, finding.py, checks.d/ | ルールファイルを読み込み、並行して実行し、固定の形式(code, severity, level, node, summary, evidence, try, confidence, fix)で検出結果を作成します。 |
state.py | ノードごとの直近の実行結果を保持し、連続回数から信頼度を判定します。 |
fixes.py | 3 種類の型付き修正アクション、それらのドライラン計画、スナップショット、監査ログを扱います。 |
cli.py, mcp_server.py, serve.py | 同じドクターを使う 3 種類のフロントエンドです。 |
設計上の決定
それぞれの代替案は docs/adr/ に記載しています。
- 標準ライブラリのみを使用(ADR 0001)。
pip installで他のものは取得されないため、Python 3.11 が動作するあらゆるマシンにインストールできます。トレードオフ:HTTP クライアント、JSON-RPC ループ、Prometheus レンダラーを手書きする必要があり、このプロジェクト内でテストしなければなりません。 - ルールはフォルダー内の通常の Python ファイル(ADR 0002)。ルールは関数
(snapshot, ctx) -> finding | list | Noneです。ルールが例外を発生させるとcheck.crashedWARN になり、バグのあるルールがスタックの障害を報告することはありません。トレードオフ:ルールはユーザーの権限でプロセス内実行されるため、信頼できるルールフォルダーのみを読み込んでください。 - 単一のサンプルではなく、履歴から信頼度を判定(ADR 0003)。1 回の実行で判定できるのは最大でも
LIKELYです。異常が 2 回連続するとVERIFIEDになります。トレードオフ:実際の障害が発生しても、VERIFIEDになるまでには 2 回目の実行が必要で、履歴はローカルファイルに保存されます。 - 型付き修正を用い、デフォルトではドライラン(ADR 0004)。アクションは 3 種類のみで、ルールからシェル文字列を実行することはありません。
--applyを指定しない限り変更は行われず、適用した修正ごとにスナップショットと監査ログが残ります。トレードオフ:3 種類のアクション以外は手動で行う必要があります。 - エージェント向けのインターフェースは読み取り専用(ADR 0005)。MCP サーバーと HTTP エンドポイントは修正を行わず、実行履歴も書き込みません。また、指示がない限り
serveはループバックにバインドします。トレードオフ:エージェントは診断できますが、修正は人に引き継ぐ必要があります。
コーディングエージェント向け
- MCP サーバー(任意の MCP クライアント):
stack-guardian-mcpは stdio 経由で MCP を使用し、読み取り専用の 3 つのツールstack_health、stack_snapshot、stack_explainを提供します。クライアント設定とツールのリファレンスについては、MCP サーバーを参照してください。 - DSH プラグイン(
npm install dsh-stack-guardian、ソースはdsh/、Node 20+):ドクターが WARN または FAIL を報告した場合、DeepSeek Harness のセッション開始時に 1 行を追加し、それ以外は何もしません。stack-guardian doctor --json --quietを実行するため、ハーネス環境でSTACK_GUARDIAN_NODESにノードファイルを設定してください。 - 監視とスクレイピング:
stack-guardian doctor --watch 30 --jsonは、状態が変化した検出結果(新規、解決、重大度の変更)ごとに JSON オブジェクトを 1 つ出力するため、エージェントはその出力を tail できます。stack-guardian-serve --nodes FILEはGET /metricsにも Prometheus テキスト形式で応答します。
MCP サーバー
stack-guardian-mcp(別名 stack-guardian mcp)は、stdio 経由で MCP(JSON-RPC 2.0、プロトコル 2024-11-05)を使用します。ノードファイル(examples/nodes.json)が必要です。--nodes FILE または環境変数 STACK_GUARDIAN_NODES でそのパスを指定してください。任意:--ctx FILE(ルール設定)、--state FILE(評価に使用する実行履歴)、--ttl SECONDS(スナップショットを再利用する秒数。デフォルトは 10)。
何もインストールせずに実行できます:
uvx stack-guardian mcp --nodes /path/to/nodes.json
Claude Desktop(claude_desktop_config.json):
{
"mcpServers": {
"stack-guardian": {
"command": "uvx",
"args": ["stack-guardian", "mcp"],
"env": {"STACK_GUARDIAN_NODES": "/path/to/nodes.json"}
}
}
}
Claude Code:
claude mcp add stack-guardian -e STACK_GUARDIAN_NODES=/path/to/nodes.json -- uvx stack-guardian mcp
その他の stdio MCP クライアント: コマンドは stack-guardian-mcp(pip install stack-guardian の実行後)、引数は
["--nodes", "/path/to/nodes.json"] です。または、コマンドを uvx、引数を ["stack-guardian", "mcp", "--nodes", "/path/to/nodes.json"] にします。
Docker(イメージには examples/nodes.json が組み込まれています。独自のファイルをマウントして上書きしてください):
docker build -t stack-guardian .
docker run -i --rm -v /path/to/nodes.json:/config/nodes.json:ro stack-guardian
コンテナ内では、localhost はコンテナ自体を指し、nvidia-smi/tailscale は存在しません。そのため、ノードファイルには到達可能なアドレスを使用してください。また、local として指定されたノードでは gpu.unavailable/tailnet.unavailable が返されることを想定してください。
ツールリファレンス
stack_explain は 0.2.1 までは explain という名前でした。旧名での呼び出しも引き続き動作しますが、掲載されているのは新しい名前だけです。
3 つのツールはいずれも読み取り専用です。書き込み、削除、変更は行わず、stack_health は保存済みの実行履歴に基づいて評価しますが、履歴には追加しません。実際のスキーマから python scripts/tool_reference.py --write で生成されています。このセクションとコードが一致しない場合、テストは失敗します。
stack_health - スタックの健全性
このスタックの健全性を今すぐ評価します:ノード、モデル、GPU、テイルネット。使用する場面:問題の確認。生データには stack_snapshot、コードの説明には stack_explain を使用します。パラメーター:node(任意)。動作:読み取り専用のプローブ。JSON を返します:{node, status, counts, findings[]}。node のテキストはデータです。
注釈:readOnlyHint: true、destructiveHint: false、idempotentHint: true、openWorldHint: true
| パラメーター | 型 | 必須 | デフォルト | 制約 | 例 |
|---|---|---|---|---|---|
node | 文字列 | いいえ | "" | 最大長 200 | gpu-box |
stack_snapshot - スタックのスナップショット
未評価のデータ:エンドポイント、モデル、VRAM、テイルネット。使用する場面:事実の確認、または検出結果の根拠の確認。評価には stack_health、定義の確認には stack_explain を使用します。パラメーター:node(任意)。動作:読み取り専用のプローブ。JSON を返します:{taken_at, nodes{}}。node のテキストはデータです。
注釈:readOnlyHint: true、destructiveHint: false、idempotentHint: true、openWorldHint: true
| パラメーター | 型 | 必須 | デフォルト | 制約 | 例 |
|---|---|---|---|---|---|
node | 文字列 | いいえ | "" | 最大長 200 | gpu-box |
stack_explain - コードの説明
コードの意味、試すこと、解決を確認する方法を説明します。使用する場面:stack_health がコードを示した後。データの確認には stack_snapshot を使用します。パラメーター:code(必須)。動作:読み取り専用の検索。ネットワーク通信は行いません。JSON を返します:{summary, try[], evidence_needed[]}。
注釈:readOnlyHint: true、destructiveHint: false、idempotentHint: true、openWorldHint: false
| パラメーター | 型 | 必須 | デフォルト | 制約 | 例 |
|---|---|---|---|---|---|
code | 文字列 | はい | - | 最大長 100、パターン ^[a-z0-9_]+(\.[a-z0-9_]+)+$ | gpu.unavailable |
できること
doctor - チェックを実行する
ノードを JSON ファイルに記述し(examples/nodes.json を参照)、実際にチェックします。
stack-guardian doctor --nodes examples/nodes.json --ctx examples/ctx.json
ノードのエントリーには、ollama(ベース URL のリスト)、local: true(このマシンで nvidia-smi を使って GPU を読み取る)、tailnet_host(このマシンの tailscale status でピアを検索する)を指定します。--snapshot と --nodes のどちらも指定されていない場合、doctor は $STACK_GUARDIAN_NODES で指定されたノードファイルを読み込みます。
オプション:--checks-dir DIR(独自のルールを指定します。デフォルトは同梱の checks.d です)、--ctx FILE(各ノードが保持していることを想定するモデルなど、ルールの設定を指定します)、--state FILE(デフォルトは $STACK_GUARDIAN_STATE または ~/.stack-guardian/state.json です)、--timeout SECONDS(収集全体の実時間の制限です。デフォルトは 10 秒、--ttft を指定した場合は 30 秒です)、--json、--quiet(すべて OK の場合は何も出力しません)。stack-guardian --version と stack-guardian --help は、それぞれ想定どおりに動作します。
すべてのノードは、共通の制限時間内に一斉にプローブされ、処理の最後にレポートが出力されます。1 バイトずつしか応答しないノードは、待ち続けずに node.unreachable として扱われます。
ノードファイルは読み込み時に検証されます。ノードにはプローブ対象(ollama、"kind": "openai" を指定した endpoints、local: true、または tailnet_host)が必要です。また、URL にはスキームが必要です。これら以外は使用方法エラー(終了コード 3)となり、誤った確信を持った応答の代わりに、該当ノードの名前が示されます。リストを指定する場所に単一の URL 文字列を指定した場合も受け付けますが、その場合は標準エラーに注記が 1 件出力されます。_ で始まるキーは無視されるため、ファイルにコメントや候補ノードを含められます。"_options": {"timeout_s": 20} を指定すると、そのファイルの制限時間を設定できます。
人間向けの出力では、各行の末尾に検出内容の確信度が表示されます。同じ実行での検出には (LIKELY)、同じ検出内容が 2 回確認されると (VERIFIED) が付きます。worst: X は最後の行に表示されます。--json は {"schema": 1, "findings": [...], "worst": "..."} を出力します。
終了コード:すべて OK/INFO の場合は 0、WARN の場合は 1、FAIL の場合は 2、使用方法エラーの場合は 3、doctor 自体がクラッシュした場合は 4 です。
同梱ルール:node.unreachable、ollama.unreachable、model.not_loaded、model.expiring、model.slow_ttft、vram.unattributed、gpu.unavailable、gpu.parse_degraded、tailnet.unavailable、tailnet.peer_offline、tailnet.relayed。影響を受けたノードごとに、検出内容が 1 件生成されます。ルールの実行時に例外が発生すると check.crashed WARN になります。バグのあるルールが、スタックの異常を報告することはありません。
LM Studio、vLLM、llama.cpp server。 ノードに "kind": "openai" を指定し、ベース URL を endpoints に列挙します。GET /v1/models(サーバーが対応している場合は GET /v1/models/<id> も)から取得した内容を Ollama エンドポイントと同じ形式に変換するため、上記のすべてのルールが適用されます。サーバー上で未ロードとされているモデルは、ロード済みとして扱われません。
{"gpu-box": {"kind": "openai", "endpoints": ["http://127.0.0.1:1234"]}}
model.expiring(WARN):ロード済みの Ollama モデルについて、/api/ps の expires_at がスナップショット時刻から 60 秒以内の場合に検出します。/api/ps が利用できない場合は INFO になります。--ctx で "model_expiring_s": 120 を指定すると、判定時間を変更できます。
--watch N は N 秒ごとに doctor を再実行し、状態が変わった検出内容だけをタイムスタンプ付きで出力します。Ctrl+C で終了すると、1 行の要約を出力して 0 で終了します。--json を指定した場合、変更のたびに JSON オブジェクトが 1 件、標準出力に出力されます。
stack-guardian doctor --nodes examples/nodes.json --watch 30
--ttft は、各 Ollama エンドポイント上のロード済みモデルすべてに 1 トークンの生成をリクエストし、スナップショットのモデルに ttft_ms を記録します。生成処理が発生するため、このオプションを指定しない限り実行されません。また、--nodes が必要です。プローブでは、各モデルのアンロード時刻は変更されません。model.slow_ttft(INFO)は 2000 ms を超えると検出されます。--ctx で "ttft_slow_ms": 1500 を指定すると、判定値を変更できます。
stack-guardian doctor --nodes examples/nodes.json --ttft
doctor --fix ID - 検出項目に定義された修正を実行する
ルールでは、検出項目に fix={"action": ..., "args": {...}} を設定できます。アクションは厳密に3種類で、シェル文字列を直接指定することはありません。
| action | 実行内容 | 必要なもの |
|---|---|---|
unload_model | Ollama の /api/generate に keep_alive: 0 を POST します | node、url、model |
restart_lane | そのレーンに指定した argv リストを実行します | --config で定義された lane |
set_env | NAME=value を <state folder>/env に書き込み、プロセスに設定します | name、value |
stack-guardian doctor --checks-dir examples/checks.d --snapshot examples/snapshot.json --fix ollama.stuck --config examples/lanes.json
これはドライランであり、計画を表示するだけで、何も変更しません。実行するには --apply を追加します。修正を適用すると、まずスナップショットを <state folder>/snapshots/ に保存し、次に処理を実行してから、JSON を1行 <state folder>/fixes.jsonl に追記します(保存先を変更するには --audit FILE を指定します)。set_env の値は監査ログに書き込まれず、長さのみが記録されます。ID は検出コードです。同じコードを複数のノードが検出した場合は code@node を指定します。--fix を指定した場合、終了コードは修正処理の結果を示します。0 は計画または成功、1 は拒否または失敗、3 は使用方法の誤りです。
その他のコマンド
-
stack-guardian init [--out nodes.json] [--ollama URL]は、このマシン用の初期ノードファイルを書き込みます。ローカルの Ollama を調査し、tailnet のピアをコメントアウトされた候補として一覧表示します。すでにファイルが存在する場合は上書きせず、終了コード3で拒否します。 -
stack-guardian snapshot --nodes FILE [--ttft] [--timeout SECONDS]は、ルールが読み取る生のスナップショットを表示します。 -
stack-guardian-serve --nodes FILE [--ctx FILE] [--port 8788] [--cache SECONDS]は、新しいスナップショットをGET /snapshotで、Prometheus メトリクスをGET /metricsで提供します(読み取り専用です。--host H --allow-remoteを渡さない限り、ループバックでのみ利用できます)。これはスクレイプ対象であり、クラスタープロトコルではありません。stack-guardian のほかの部分がこれを読み取ることはありません。リクエストごとに新しいスナップショットを収集します。ノードの応答よりもスクレイプ間隔が短い場合、--cache SECONDS(デフォルトは0、無効)を指定すると、その時間だけスナップショットを再利用します。認証機能はありません。localhost または tailnet の IP のみにバインドしてください。 -
同じサーバーは、最新のスナップショットに対して同梱のルールを実行し、Prometheus のテキスト形式で
GET /metricsに応答します。メトリクスはstack_guardian_finding{code,severity,node}(検出中は 1、解決後は 0)、stack_guardian_worst_severity(0 は OK、1 は INFO、2 は WARN、3 は FAIL)、およびstack_guardian_last_run_timestampです。curl http://127.0.0.1:8788/metrics -
stack-guardian-mcp --nodes FILE [--ctx FILE] [--state FILE] [--ttl SECONDS]:MCP サーバーを参照してください。
ルールの作成
from stack_guardian import finding as F
def ollama_up(snapshot, ctx):
if not snapshot["nodes"]["gpu-box"].get("reachable"):
return F.make_finding("ollama.unreachable", "missing", "FAIL", "gpu-box", "Ollama does not answer")
return None
CHECKS = [("ollama.up", ollama_up)]
ファイルをフォルダーに配置し、--checks-dir にそのフォルダーを指定します。修正を含む完全な例は、examples/checks.d/20_ollama_fix.py にあります。ルールは、ノードごとに1つずつ、検出項目のリストを返すこともできます。
例
| ファイル | 内容 |
|---|---|
examples/nodes.json | 3つのノード:GPU を搭載したローカルノード1つと、tailnet 経由のリモートノード2つ |
examples/ctx.json | ルールの設定:gpu-box-2 に保持させるモデル |
examples/snapshot.json | 同梱のすべてのルールが1回ずつ検出を発生させるオフラインのスナップショット。doctor と --fix を試すためのものです |
examples/lanes.json | restart_lane が実行を許可されているレーン |
examples/checks.d/20_ollama_fix.py | 検出項目に修正が設定されたカスタムルール |
examples/demo_report.py | この README の冒頭にある2回実行のレポートを表示します。127.0.0.1 上のスタブ Ollama を使い、ほかのネットワークは使用しません |
トラブルシューティング
コードで確認できることと、同じテキスト stack_explain がエージェントに示す内容:
| 表示される内容 | 意味 | まず試すこと |
|---|---|---|
node.unreachable | ノードが期限内に応答しなかったか、コレクターで例外が発生しました | マシンの電源が入っているか確認します。遅いだけなら、--timeout 30 または "_options": {"timeout_s": 30} を指定します |
ollama.unreachable | エンドポイント自体が接続を拒否したか、タイムアウトしました | このマシンから curl http://HOST:11434/api/version を実行します。ポートと、Ollama がループバック以外でもリッスンしていること(OLLAMA_HOST)を確認します |
model.not_loaded | --ctx が、メモリに常駐していないモデルを想定しています | 短い生成を一度実行して読み込むか、--ctx からその想定を削除します |
model.expiring | 読み込み済みモデルが 60 秒以内にアンロードされます(INFO:/api/ps がないため、判定できません) | keep_alive を長めに指定してリクエストを送るか、そのノードで OLLAMA_KEEP_ALIVE を設定します |
model.slow_ttft | --ttft プローブで、最初のトークンまでに 2000 ms を超えました | モデルが VRAM に収まっていることと、その GPU でほかの生成処理が行われていないことを確認します |
vram.unattributed | VRAM が使用されていますが、一覧にあるどのプロセスによるものか説明できません | nvidia-smi を実行して、コンピュートアプリを確認します。Windows では一部のプロセスが [N/A] と報告され、特定できない場合があります |
gpu.unavailable | nvidia-smi が見つからないか、実行に失敗しました。どちらかは検出結果に記載されています | NVIDIA のみが対象です。インストール済みの場合は、nvidia-smi を手動で実行してドライバーを修正します |
gpu.parse_degraded | nvidia-smi が [N/A] フィールドを含む GPU 行を出力したため、そのカードはレポートから除外されています | クエリを手動で実行します(証拠に該当行が引用されています)。ドライバーを更新します |
tailnet.unavailable | tailscale が PATH にないか、tailscale status が失敗しました | tailnet を使っていない場合は、そのノードから tailnet_host を削除します。それ以外の場合は、tailscaled を起動するか、ログインします |
tailnet.peer_offline | tailnet でピアがオフラインと表示されています | マシンが起動していることと、そのマシンでの tailscale status を確認します |
tailnet.relayed | ピアへの接続が DERP リレー経由でしか確立できません | UDP 41641 とファイアウォールを確認します。両端で tailscale netcheck を実行します |
check.crashed | ルールファイルのいずれかで例外が発生しました | 証拠に例外が含まれています。そのファイルを修正するか削除します |
症状別:
| 表示される内容 | 原因 |
|---|---|
stack-guardian: usage: ... nothing to probe | nodes ファイルのノードに、エンドポイントも local: true も tailnet_host もありません。以前は worst: OK と報告されていました |
stack-guardian: usage: ... "kind": "openai" | ノードに endpoints が記載されていますが、OpenAI 互換ノードとしてマークされていません |
すべての検出結果に (LIKELY) と表示される | 確信度は履歴に基づきます。同じ --state ファイルを使って 2 回実行してください |
MCP サーバーに VERIFIED が表示されない | これは仕様です。保存済みの履歴に基づいて評価しますが、履歴には追加しません。CLI または --watch で履歴を蓄積してください |
サンプルスナップショットで doctor が 2 で終了する | サンプルの目的は、すべてのルールを一度ずつ実行することです |
マシンから送信されるもの、書き込まれるもの
- 送信: ノードファイルに記載されたベース URL への HTTP(S) リクエストのみです(
/api/version、/api/tags、/api/ps、または OpenAI 互換ノードの場合は/v1/models)。--ttftを指定すると、ロード済みモデルごとに 1 トークンの生成を 1 回行います。認証情報は送信されず、リダイレクトには従います。また、テレメトリー、更新確認、分析など、ほかの場所に何かを送信することはありません。nvidia-smiとtailscale status --jsonはローカルで実行されます。 - 受信: 実行時にダウンロードまたはインストールされるものはありません。このパッケージには実行時依存関係がありません。
- 書き込み:
0700で作成される状態フォルダー($STACK_GUARDIAN_STATEまたは~/.stack-guardian/)です。ここにはstate.json(ノードごとに直近 5 回分の実行内容:コードとレベル。測定値は含まれません)が保存されます。また、--fix ... --applyを実行した場合に限り、fixes.jsonl(適用した修正ごとに 1 行の監査記録)とsnapshots/(修正の直前に取得したスナップショット)も保存されます。set_envはenvにも書き込みます。これらのファイルは0600で作成されます。set_envの値が監査記録に書き込まれることはなく、記録されるのはその長さのみです。doctor --json、snapshot、MCP サーバーは、それぞれの出力以外には何も書き込みません。 - 表示: スナップショットには、ノード名、モデル名、VRAM の数値、GPU プロセス名、テールネットの状態が含まれます。これらの情報は
stack-guardian-serveが提供し、MCP クライアントが受け取るため、nvidia-smiの出力と同じように取り扱ってください。
制限事項
想定外のことが起きないよう、このツールでできないことを説明します。
- GPU:NVIDIA のみで、stack-guardian を実行しているマシンのみが対象です。 GPU データは、
"local": trueとマークされたノード上のnvidia-smiから取得されます。リモートノードから報告されるのはエンドポイント、モデル、テールネットの状態であり、VRAM は報告されません。このパッケージは別のマシンの/snapshotを読み取らないため、リモートノードの GPU をこのレポートに取り込む方法はありません。そのマシンでもstack-guardianを実行するか、Prometheus でそのマシンのstack-guardian-serve/metricsをスクレイプしてください。AMD、Intel、Apple の GPU はサポートされません。 - テールネットの状態は、このマシンから見た状態です。 ピア情報はローカルの
tailscale status --jsonから読み取られます。あるノードからはオフラインに見えるピアが、別のノードからはオンラインに見える場合があります。 VERIFIEDには 2 回の実行が必要です。 MCP サーバーは実行履歴を記録しないため、CLI(または--watch)が同じ状態ファイルに履歴を蓄積していない限り、単独で報告できるのは最大でもLIKELYです。stack-guardian-serveに認証機能はありません。 読み取り専用で、デフォルトではループバックにバインドされます。オープンインターネット向けの用途ではありません。- MCP は stdio のみで、プロトコルは
2024-11-05です。結果は JSON テキストコンテンツとして返され、outputSchemaやstructuredContentは含まれません。 - ルールはプロセス内で実行されます。 ルールフォルダーにはコードが含まれるため、
--checks-dirには信頼できるルールのみを指定してください。 --ttftはロード済みモデルごとに実際の生成を行うため、オプトインになっています。
テスト方法
pip install -e ".[test]"
python -m pytest -q
- 各モジュールには、回数をカウントする
--selftest(純粋関数、HTTP とコマンドランナーを注入し、ネットワーク接続なし)があり、テストスイートは各モジュールでこれを実行します。 - 統合テストでは、サブプロセス内で実際の CLI と MCP サーバーを stdio 経由で動かし、ツール一覧とその注釈も含めてテストします。また、
examples/をこの README の記載どおりに実行します。 tests/test_tool_definitions.pyは、MCP ツールまたはパラメーターから説明、型、例、デフォルト値、制約のいずれかが欠落した場合、出荷対象の検出コードにstack_explainの項目がない場合、または上記のツールリファレンスが古い場合に失敗します。tests/test_tools_list_budget.pyは実際の MCP サーバーを起動し、tools/listのペイロード全体がトークン予算を超えた場合に失敗します。ツール定義はすべてのリクエストで送信されるためです。- CI では Ubuntu、Windows、macOS(Python 3.11)と、Python 3.12 および 3.13 を使用する Ubuntu でテストスイートを実行し、wheel をビルドしてクリーンな仮想環境で実行します (そのため、ルールを含まないリリースは失敗します)。さらに Docker イメージをビルドしてツール一覧を確認し、DSH プラグインの構文をチェックし、追跡対象の全ファイルをプライバシースキャンします。
貢献とセキュリティ
プルリクエストを歓迎します。セットアップ、プロジェクトのスタイル、重要な3つのルール(最初に失敗することを確認したテスト、新たなランタイム依存関係を追加しないこと、ツール定義のトークン予算)については、CONTRIBUTING.mdをご覧ください。
悪用可能な問題を見つけましたか? 公開のイシューではなく、リポジトリの「Security」タブから非公開で報告してください。報告方法と、このツールの設計上の動作(ルールはコードであること、ノードのURLを取得すること、serveに認証がないこと)については、SECURITY.mdをご覧ください。
クレジット
他のプロジェクトのコードはコピーしていません。ワイヤ形式は公開仕様に準拠しています:Model Context Protocol(stdio over JSON-RPC、ツールの注釈)とPrometheus text exposition formatです。データは、Ollama、nvidia-smi、tailscale status --jsonのドキュメント化されたインターフェースから取得しています。
guardian ファミリー
ローカルモデルを実行する人向けの、MIT ライセンスの小さなツールが4つあります。それぞれ1つの役割を担い、コードを共有せず、単独でも便利に使えます。
| ツール | 回答する内容 |
|---|---|
| code-guardian | 自分が書いていないコードを実行またはマージする前に、安全か、動作するか、何か不足していないかを確認します。静的スキャンによる3つのスコアを提示し、Pythonを詳細にチェックします。 |
| context-guardian | コンテキストウィンドウがいっぱいになる前に履歴を圧縮するプロキシと、アーカイブした内容を検索するツールにより、ローカルモデルとの長時間のセッションをコンテキストウィンドウ内に保ちます。 |
| tool-guardian | 多数の MCP サーバーを少数のツール経由でルーティングし、ツール定義がコンテキストウィンドウを埋め尽くさないようにします。 |
| stack-guardian | 自宅の AI スタックは正常でしょうか? Ollama/OpenAI 互換ノード、NVIDIA GPU、Tailscale のピアを確認し、各調査結果に信頼度のグレードを付けます。 |
ライセンス
MIT。LICENSEをご覧ください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。