3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

たった3コマンドだけでClaude Codeをローカルで動かすツール claude-local を作った

3
Last updated at Posted at 2026-03-20

はじめに

本記事は、DGX SparkでローカルLLMをClaude Codeのバックエンドとして使う検証シリーズの第3弾です。

2つの検証を通じて、「ローカルLLMでClaude Codeを動かす」こと自体は十分に実用的だとわかりました。しかし、毎回手動でプロキシスクリプトを起動し、環境変数を設定し、Claude Codeを立ち上げる……という手順は煩雑でした。

そこで、この一連の作業をワンコマンドで完結させるCLIツールを作りました。

GitHub: https://github.com/amu815/claude-local

なぜツール化したか

第2弾の検証で使っていた構成は、以下のようなものでした。

# 1. vLLMコンテナを起動(spark-vllm-docker)
cd ~/spark-vllm-docker && python3 run-recipe.py qwen3.5-122b-int4-solo --solo -d

# 2. プロキシスクリプトを起動
python3 ~/.local/bin/vllm-proxy.py &

# 3. 環境変数を設定してClaude Codeを起動
ANTHROPIC_BASE_URL=http://127.0.0.1:8081 \
ANTHROPIC_AUTH_TOKEN=local \
claude --model Intel/Qwen3.5-122B-A10B-int4-AutoRound

これを毎回打つのは面倒ですし、DGX Sparkの2台構成だとリモートノードへのSSHも必要です。さらに、同僚や友人にこの手順を共有しようとしたとき、「DGX Sparkを持っていない人はどうすればいい?」という問題に気づきました。

macOSのApple SiliconマシンならMLXで、WindowsやLinuxならOllamaで、同じ体験ができるはずです。プラットフォームごとのバックエンドの差異を吸収し、どの環境でも同じコマンドで動くツールが必要でした。

claude-local とは

pip install claude-local
claude-local setup    # ハードウェア検出 → モデル推奨 → 設定生成
claude-local start    # バックエンド起動 → プロキシ起動 → Claude Code起動

3コマンドで、ローカルLLMを使ったClaude Code環境が立ち上がります。

アーキテクチャ

┌──────────────┐      ┌──────────────────┐      ┌───────────────────┐
│  Claude Code │─────▶│  Failover Proxy  │─────▶│  Backend (vLLM)   │
│              │      │  :8081           │  ┌──▶│  Node 1 :8000     │
│  環境変数:    │      │                  │  │   └───────────────────┘
│  BASE_URL=   │      │  ヘルスチェック    │──┤
│  :8081       │      │  安全圧縮         │  │   ┌───────────────────┐
│              │      │  max_tokens制限   │  └──▶│  Backend (vLLM)   │
└──────────────┘      └──────────────────┘      │  Node 2 :8000     │
                                                └───────────────────┘

Claude CodeはOpenAI互換APIを通じてプロキシと通信し、プロキシが裏側のLLMバックエンドにリクエストを転送します。バックエンドが複数ある場合はフェイルオーバーで冗長化されます。

技術的な工夫

1. ハードウェア自動検出

claude-local setup を実行すると、OS・CPU・GPU・メモリを自動検出し、最適なバックエンドとモデルを推奨します。

def detect_platform() -> PlatformInfo:
    """Detect OS, arch, memory, GPU, DGX Spark status."""
    os_name = _detect_os()
    arch = _detect_arch()
    memory_gb = _detect_memory_gb(os_name)
    is_dgx_spark = _detect_dgx_spark()
    gpu_type, gpu_vram_gb = _detect_gpu()
    is_uma = os_name == "darwin" or is_dgx_spark
    ...

DGX Sparkの検出は3段階で行います。

  1. /etc/dgx-release ファイルに "spark" or "gx10" が含まれるか
  2. dpkg -l dgx-ota パッケージが存在し、かつアーキテクチャがARM64か
  3. デバイスツリー /sys/firmware/devicetree/base/model に "gx10" が含まれるか

Apple Siliconは darwin + arm64 の組み合わせで判定します。いずれも UMA(統合メモリ)として扱い、GPU VRAMではなくシステムメモリ全体をモデルの載せられるメモリとして計算します。

2. メモリベースのモデル推奨

利用可能なメモリ量に応じて、最適なモデルとコンテキスト長を自動選択します。

メモリ 推奨モデル コンテキスト長
128 GB (UMA) Qwen3.5-122B-INT4 262,144
96 GB (UMA) Qwen3.5-122B-INT4 131,072
80 GB (VRAM) Qwen3.5-122B-INT4 65,536
64 GB Qwen3-32B-INT4 131,072
32 GB Qwen3-32B-INT4 32,768
24 GB Qwen3-32B-INT4 16,384

モデルレジストリは configs/models.yaml にYAMLで定義されており、コミュニティが新しいモデルを追加しやすい構造になっています。

models:
  - id: qwen3.5-122b-int4
    name: Qwen3.5-122B-A10B-INT4
    repo: Intel/Qwen3.5-122B-A10B-int4-AutoRound
    weight_size_gb: 63
    memory_gb: 80
    context_by_memory:
      112: 262144
      96: 131072
      80: 65536
    backends:
      vllm-spark:
        recipe: qwen3.5-122b-int4-solo
      mlx:
        repo: mlx-community/Qwen3.5-122B-A10B-int4-AutoRound
      ollama:
        tag: qwen3.5-coder:122b

context_by_memory が重要なポイントです。同じモデルでも、メモリ量によってKVキャッシュに割ける容量が変わるため、コンテキスト長を段階的に調整しています。第1弾の検証で学んだ「コンテキスト長が全て」という教訓がここに反映されています。

3. フェイルオーバープロキシ

プロキシは標準ライブラリのみで実装しています(外部依存なし)。

# proxy.py
MAX_OUTPUT_TOKENS = 16384
SAFETY_CHAR_LIMIT = 900_000  # ~225K tokens

def _safety_compress(data: dict) -> dict:
    """Drop oldest non-system messages if over SAFETY_CHAR_LIMIT."""
    messages = data.get("messages")
    if not messages:
        return data
    while _estimate_chars(data) > SAFETY_CHAR_LIMIT and len(messages) > 1:
        if messages[0].get("role") == "system":
            if len(messages) > 2:
                messages.pop(1)
            else:
                break
        else:
            messages.pop(0)
    return data

設計上のポイントは3つです。

  • フルパススルー: 第1弾で行ったsystemプロンプトの圧縮やtools削減は一切行わない。第2弾で「圧縮しないほうがいい」と学んだため
  • 安全弁のみ: リクエストが90万文字(約225Kトークン)を超えた場合だけ、古いメッセージを落とす。これはモデルのコンテキスト上限を超えてOOMを起こすのを防ぐための最後の砦
  • フェイルオーバー: 複数のupstreamを順番に試し、503やタイムアウトなら次のノードへ。全ノードが落ちていれば 503 All upstreams unavailable を返す

4. KVキャッシュ最適化の自動適用

Claude Codeはデフォルトで「Attribution Header」というヘッダーをリクエストに付加します。APIサーバーでは無視される無害なヘッダーですが、ローカルLLMではこのヘッダーがKVキャッシュを毎回無効化し、推論速度が約90%低下します(Unslothの報告)。

厄介なことに、export CLAUDE_CODE_ATTRIBUTION_HEADER=0 では効きません。~/.claude/settings.jsonenv セクションに書く必要があります。

claude-localは setup 時にこの設定を自動的に適用します。

_LOCAL_ENV_DEFAULTS = {
    "CLAUDE_CODE_ATTRIBUTION_HEADER": "0",
}

def _optimize_claude_settings() -> None:
    """Ensure ~/.claude/settings.json has env vars for local inference."""
    settings: dict = {}
    if os.path.exists(_CLAUDE_SETTINGS_PATH):
        with open(_CLAUDE_SETTINGS_PATH) as f:
            settings = json.load(f)

    env = settings.setdefault("env", {})
    for key, value in _LOCAL_ENV_DEFAULTS.items():
        if env.get(key) != value:
            env[key] = value
    # ... save to settings.json

setup を実行するだけで、KVキャッシュの最適化が有効になります。手動で settings.json を編集する必要はありません。

5. プラグインアーキテクチャ

バックエンドは抽象基底クラス Backend を継承する形で実装されています。

class Backend(abc.ABC):
    @abc.abstractmethod
    def install(self) -> None: ...
    @abc.abstractmethod
    def start(self, model: dict, port: int = 8000) -> None: ...
    @abc.abstractmethod
    def stop(self) -> None: ...
    @abc.abstractmethod
    def status(self) -> BackendStatus: ...

現在の実装:

バックエンド 対象環境 特徴
vllm-spark DGX Spark spark-vllm-dockerのレシピを呼び出し、マルチノードSSH起動にも対応
mlx macOS (Apple Silicon) mlx-lmのサーバーモードを起動、モデルの自動ダウンロードにも対応
ollama Windows / Linux Ollamaのモデルpull & serve

新しいバックエンド(例: llama.cpp, SGLang)を追加する場合、Backend を継承した1ファイルを追加し、models.yaml にエントリを足すだけで対応できます。

対応プラットフォームとバックエンド選択

recommend_backend() のロジックはシンプルです。

def recommend_backend(info: PlatformInfo) -> str:
    if info.is_dgx_spark:
        return "vllm-spark"
    if info.os == "darwin" and info.gpu_type == "apple_silicon":
        return "mlx"
    if info.gpu_type == "nvidia":
        return "vllm"
    return "ollama"
判定条件 バックエンド 理由
DGX Spark vllm-spark 専用Dockerコンテナ経由が最も安定
macOS + Apple Silicon mlx Metal最適化、統合メモリを最大限活用
NVIDIA GPU (非Spark) vllm CUDAによる高速推論
その他 ollama セットアップが最も簡単、幅広いハードウェアに対応

使い方

macOS (Apple Silicon) の場合

$ pip install claude-local
$ claude-local setup
Detecting platform...
  OS: darwin (arm64)
  Memory: 96 GB (unified)
  GPU: apple_silicon
Recommended backend: mlx
Recommended model: Qwen3.5-122B-A10B-INT4
  Parameters: 122B, Quantization: INT4
  Weight size: ~63 GB
  Max context: 131,072 tokens

Proceed with this configuration? [Y/n]: Y
Download model weights? [Y/n]: Y
Configuration saved to ~/.claude-local/config.yaml
Claude Code settings optimized (KV cache fix applied).

$ claude-local start
Starting mlx backend...
Backend started.
Starting proxy on 127.0.0.1:8081...
Proxy running on 127.0.0.1:8081
Launching Claude Code with model: mlx-community/Qwen3.5-122B-A10B-int4-AutoRound

DGX Spark(マルチノード構成)の場合

$ claude-local setup
Detecting platform...
  OS: linux (arm64)
  Memory: 128 GB (unified)
  GPU: nvidia
  Platform: NVIDIA DGX Spark

Recommended backend: vllm-spark
Recommended model: Qwen3.5-122B-A10B-INT4
  Parameters: 122B, Quantization: INT4
  Weight size: ~63 GB
  Max context: 262,144 tokens

Proceed with this configuration? [Y/n]: Y
Enter node IPs (comma-separated) [192.168.100.1,192.168.100.2]:
Configuration saved to ~/.claude-local/config.yaml
Claude Code settings optimized (KV cache fix applied).

$ claude-local start
Starting vllm-spark backend...
Starting vLLM with recipe: qwen3.5-122b-int4-solo
Starting vLLM on 192.168.100.2...
  http://192.168.100.1:8000 is READY
  http://192.168.100.2:8000 is READY
Backend started.
Starting proxy on 127.0.0.1:8081...
Proxy running on 127.0.0.1:8081
Launching Claude Code with model: Intel/Qwen3.5-122B-A10B-int4-AutoRound

2台のDGX Sparkがそれぞれ独立してvLLMを起動し、プロキシがフェイルオーバーを管理します。片方のノードが再起動中でも、もう一方で推論を継続できます。

ステータス確認

$ claude-local status
Backend: vllm-spark
Model: Qwen3.5-122B-A10B-INT4
Context: 262,144 tokens
  http://192.168.100.1:8000: UP
  http://192.168.100.2:8000: UP
Proxy: http://127.0.0.1:8081 (UP)

シリーズを通じて学んだこと

3つの記事を通じて、ローカルLLM × Claude Codeの知見が積み上がりました。

検証 学び
第1弾 (397B) モデルの賢さよりコンテキスト長が重要。プロキシでの過剰な圧縮は逆効果
第2弾 (122B) 262Kコンテキストなら成功率100%。prefix cacheで24%高速化
第3弾 (本記事) 検証の知見をツール化し、DGX Spark以外の環境にも展開

特に「フルパススルー + 安全弁だけ」というプロキシ設計は、第1弾で過剰圧縮に苦しみ、第2弾で圧縮を外して成功した経験から導き出されたものです。最初からこの設計にはたどり着けませんでした。

今後の展望

  • PyPI公開: pip install claude-local で誰でもインストールできるようにする
  • バックエンド追加: llama.cpp、SGLang への対応
  • モデルカタログの拡充: Qwen以外のモデル(Llama、DeepSeek等)のサポート
  • ベンチマーク自動化: claude-local bench コマンドで性能を計測

まとめ

DGX Sparkでの検証を出発点に、「ローカルLLMでClaude Codeを動かす」体験をどのプラットフォームでも再現できるツールを作りました。

核心は4つです。

  1. ハードウェア自動検出 — setup一発で最適なモデルとバックエンドを選択
  2. フェイルオーバープロキシ — 過剰な圧縮はせず、安全弁だけを提供
  3. KVキャッシュ最適化 — Attribution Headerによる90%速度低下を自動で回避
  4. プラグインアーキテクチャ — バックエンドを1ファイル追加するだけで新環境に対応

コードは全てMITライセンスで公開しています。

GitHub: https://github.com/amu815/claude-local

ローカルLLMでClaude Codeを試したい方、DGX Sparkを持て余している方、ぜひ使ってみてください。Issue・PRも歓迎です。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?