はじめに
前回の記事では、DGX Spark 2台をスタッキングしてQwen3.5-397B(3970億パラメータ)をローカルで動かすことに成功しました。生成速度は約21〜23 tok/sで、単体の推論エンジンとしては十分実用的でした。
今回はその続きとして、Claude Codeのバックエンドをこのローカルモデルに差し替えて、AIコーディングエージェントとして実際に使えるのかを検証します。比較対象はAnthropicの最新モデルClaude Opus 4.6(API経由)です。
Claude Codeとは
Claude Codeは、Anthropicが提供するCLIベースのAIコーディングエージェントです。ターミナルからclaudeコマンドで起動し、自然言語の指示に従ってコードの読み書き、ファイル操作、コマンド実行を自律的に行います。
通常はAnthropicのAPI経由でClaudeモデルを使いますが、内部的にはAnthropic Messages APIと互換性のあるエンドポイントに接続しているだけです。つまり、同じAPIフォーマットを話すサーバーを用意すれば、任意のモデルに差し替えられるということです。
アーキテクチャ
構成は以下の通りです。
ポイントは中間に挟むvLLMプロキシです。Claude Codeは内部で巨大なシステムプロンプト(数万トークン)やツール定義を送信しますが、ローカルモデルのコンテキスト長(8192トークン)には到底収まりません。プロキシがこれをリアルタイムに圧縮して、vLLMが処理できるサイズに整えます。
実装
1. vLLMプロキシ(~220行のPython)
#!/usr/bin/env python3
"""vLLM Proxy: Claude CodeのコンテキストをvLLMに収める"""
VLLM_UPSTREAM = "http://localhost:8080"
PROXY_PORT = 8081
MAX_OUTPUT_TOKENS = 2000
# Claude Codeの巨大システムプロンプトをこの1文に差し替え
COMPACT_SYSTEM = (
"You are an AI coding assistant. Use the provided tools to help with tasks. "
"Be concise. Read files before editing. Think briefly."
)
# Claude Codeは20以上のツールを定義するが、必須の6つに絞る
ESSENTIAL_TOOLS = [
{"name": "Bash", "description": "Run a shell command", ...},
{"name": "Read", "description": "Read a file", ...},
{"name": "Write", "description": "Write a file", ...},
{"name": "Edit", "description": "Replace text in a file", ...},
{"name": "Glob", "description": "Find files by pattern", ...},
{"name": "Grep", "description": "Search file contents", ...},
]
プロキシが行う圧縮は4つです。
| 圧縮処理 | 内容 | 効果 |
|---|---|---|
| システムプロンプト差替 | 数万トークン → 1文 | 最大の節約 |
| ツール定義の最小化 | 20+ツール → 6ツール(最小スキーマ) | 大幅削減 |
| メッセージ履歴の切り詰め | 直近6メッセージのみ保持 | 会話の肥大化を防止 |
| 長文の截断 | 4000文字超のテキストを先頭+末尾に圧縮 | ツール出力の肥大化防止 |
2. claude-qwenラッパースクリプト
#!/bin/bash
# Claude Code with local Qwen3.5-397B (free mode)
# Usage: claude-qwen [claude args...]
# vLLMサーバーの疎通確認
if ! curl -sf "http://localhost:8080/v1/models" > /dev/null 2>&1; then
echo "Error: vLLM server not reachable" >&2
exit 1
fi
# プロキシが未起動なら自動起動
if ! curl -sf "http://127.0.0.1:8081/v1/models" > /dev/null 2>&1; then
python3 ~/.local/bin/vllm-proxy.py 8081 &
trap "kill $! 2>/dev/null" EXIT
fi
# Claude Codeの接続先をローカルプロキシに切り替え
export ANTHROPIC_BASE_URL="http://127.0.0.1:8081"
export ANTHROPIC_AUTH_TOKEN="local"
export ANTHROPIC_API_KEY=""
exec claude --model "intel/Qwen3.5-397B-A17B-int4-AutoRound" "$@"
これで、claude(Opus 4.6)とclaude-qwen(ローカルQwen)をコマンド一つで切り替えられます。
# Anthropic API経由(有料)
$ claude "FizzBuzzを書いて"
# ローカルQwen経由(無料)
$ claude-qwen "FizzBuzzを書いて"
ベンチマーク設計
5つのタスクで両モデルを評価しました。タスクは「AIコーディングエージェント」としての実用性を測るため、単純なテキスト生成からマルチステップのツール連携まで段階的に難易度を上げています。
| # | カテゴリ | タスク内容 | 評価ポイント |
|---|---|---|---|
| 1 | 知識Q&A | Pythonデコレータを3文で説明 | 正確性、簡潔さ |
| 2 | ファイル読取 | Pythonファイルの行数・関数一覧・要約 | Readツール使用、分析能力 |
| 3 | コード生成 | FizzBuzz関数を作成しファイルに保存 | Writeツール使用、コード品質 |
| 4 | バグ修正 | バグ入りPythonファイルの特定・修正 | Read→分析→Write連携 |
| 5 | マルチステップ | ディレクトリ作成→コード生成→テスト作成→テスト実行 | 複数ツール連携、完遂率 |
実行条件:
# 各タスクの実行方法(プロンプトをパイプで渡す)
echo "タスクの指示" | claude --print --max-turns 5 --permission-mode bypassPermissions
echo "タスクの指示" | claude-qwen --print --max-turns 5 --permission-mode bypassPermissions
-
--print: 非対話モード(パイプ入力用) -
--max-turns 5: ツール使用を含む最大ターン数 -
--permission-mode bypassPermissions: ファイル書き込み等の権限を自動承認
結果
総合スコア
| 指標 | Opus 4.6 | Qwen3.5-397B | 差 |
|---|---|---|---|
| タスク成功率 | 5/5 (100%) | 2/5 (40%) | Opus圧勝 |
| 合計時間 | 122.0s | 249.2s | Qwenは2.0倍遅い |
| 平均時間/タスク | 24.4s | 49.8s | |
| ファイル生成成功 | 3/3 | 1/3 |
タスク別結果
| # | タスク | Opus 4.6 | Qwen3.5-397B | ||
|---|---|---|---|---|---|
| 時間 | 結果 | 時間 | 結果 | ||
| 1 | 知識Q&A | 13.5s | 正確 | 56.9s | 正確 |
| 2 | ファイル読取 | 15.0s | 完全な分析 | 32.1s | max-turns超過 |
| 3 | コード生成 | 23.6s | 正常動作 | 62.9s | 正常動作 |
| 4 | バグ修正 | 36.1s | 3/3バグ修正 | 54.9s | 1/3のみ修正 |
| 5 | マルチステップ | 33.8s | 9/9テスト合格 | 42.3s | 部分完了 |
タスク別の詳細
Task 1: 知識Q&A — 両方成功
Opus 4.6(13.5s):
Pythonのデコレータは、関数やクラスを引数として受け取り、その振る舞いを変更・拡張した新しい関数を返す高階関数です。
@decorator構文は、定義された関数を自動的にデコレータ関数に渡し、その戻り値で元の名前を置き換える糖衣構文です。これにより、ログ出力・認証チェック・キャッシュなどの横断的関心事を、元の関数のコードを変更せずに宣言的に付加できます。
Qwen3.5-397B(56.9s):
Pythonのデコレータは、関数やクラスを受け取ってその動作を拡張する関数です。@構文を使用することで、デコレータを関数に簡単に適用できます。元の関数のコードを変更せずに、ロギングや認証などの機能を追加することができます。
どちらも正確で簡潔。Opusはより技術的な用語(高階関数、シンタックスシュガー)を使い、Qwenはより平易な表現です。ただし、Qwenは回答後もAPIコールを繰り返す挙動が見られ、時間が4倍かかっています。
Task 3: コード生成 — 両方成功
両方とも正しいFizzBuzz関数を生成し、ファイルへの保存と実行確認まで完了しました。
# 両モデルとも同じロジック(i % 15パターン)
def fizzbuzz(n):
result = []
for i in range(1, n + 1):
if i % 15 == 0:
result.append('FizzBuzz')
elif i % 3 == 0:
result.append('Fizz')
elif i % 5 == 0:
result.append('Buzz')
else:
result.append(str(i))
return result
Task 4: バグ修正 — 差が出たタスク
テスト用のバグ入りPythonファイルには3つのバグを仕込みました。
# Bug 1: median関数 — 偶数個の要素で間違ったインデックス
return (sorted_nums[mid] + sorted_nums[mid + 1]) / 2 # mid-1が正しい
# Bug 2: variance関数 — 母分散なのにn-1で割っている
return sum((x - avg) ** 2 for x in numbers) / (len(numbers) - 1) # nが正しい
# Bug 3: percentile関数 — 補間計算のインデックスが間違い
k = (p / 100) * len(sorted_nums) # len-1が正しい
Opus: 3つとも検出・修正し、指示通り別ファイルに保存。完璧。
Qwen: medianのバグは修正したが、元のファイルを直接編集してしまった(指示は別ファイルに保存)。残りの2つのバグは5ターンを使い切る前に到達できず。
Task 5: マルチステップ — エージェント能力の差
「ディレクトリ作成 → コード生成 → テスト作成 → テスト実行 → 結果報告」の5ステップを要求。
Opus: 全ステップ完了。calc.pyに3関数、test_calc.pyに9テストケースを作成し、全テスト合格を確認。
Qwen: ディレクトリ作成とcalc.pyの作成まではできたが、テストファイルの作成に到達できず5ターンで打ち切り。
なぜQwenは苦戦したのか
Qwenの失敗パターンを分析すると、モデルの知能の問題ではなく、コンテキスト制約の問題であることがわかります。
1. コンテキスト圧縮の副作用
最大の制約はコンテキスト長です。
| Opus 4.6 | Qwen3.5-397B | |
|---|---|---|
| モデルのコンテキスト長 | 200K tokens | 8,192 tokens |
| システムプロンプト | 全文(数万トークン) | 1文に圧縮 |
| ツール定義 | 20+ツール(詳細スキーマ) | 6ツール(最小スキーマ) |
| メッセージ履歴 | 全履歴保持 | 直近6メッセージのみ |
Claude Codeのシステムプロンプトには、ツールの使い方、ファイル操作のベストプラクティス、エラーハンドリングの指示など、エージェントとして振る舞うための膨大な指示が含まれています。これを1文に圧縮しているため、Qwenは「何をどう使えばいいか」の詳細なガイダンスを失っています。
2. max-turnsの壁
5ターンのうち、Qwenは多くのターンを「ツールの試行錯誤」に消費してしまいました。
- Task 2: ファイルを読もうとして何度もリトライ
- Task 4: 元ファイルを直接編集し、指示の再解釈にターンを消費
- Task 5: ディレクトリ作成とコード生成だけで5ターンを使い切り
Opusは1〜2ターンでツールを正しく使えるのに対し、Qwenは圧縮されたツール定義から正しい使い方を推測する必要があり、非効率なツール使用になっています。
3. 実は回答品質自体は悪くない
Task 1(Q&A)とTask 3(コード生成)が示すように、ツール使用が少ないシンプルなタスクではQwenもOpusと同等の品質を出せています。つまり、モデルの「頭の良さ」は十分。ボトルネックは純粋にコンテキスト長の制約です。
改善の余地
現状のプロキシは最低限の実装です。以下の改善で性能向上が期待できます。
| 改善案 | 期待効果 | 難易度 |
|---|---|---|
| vLLMのmax_model_lenを32K以上に拡張 | コンテキスト圧縮を緩和、ツール精度向上 | 高(メモリ制約) |
| プロキシのツール定義を充実 | ツール使用精度の向上 | 低 |
| プロキシにメッセージ要約機能を追加 | 切り詰めではなく要約で情報保持 | 中 |
| max-turnsを10以上に増加 | 試行錯誤の余地を確保 | 低(ただし遅くなる) |
| セッション管理(KVキャッシュ再利用) | レイテンシ削減 | 高 |
最も効果的なのはコンテキスト長の拡張です。DGX Sparkの統合メモリ256GB中、モデルの重みで約200GBを消費しており、KVキャッシュに使えるメモリは限られています。ノードの追加(3台目、4台目)でメモリを拡張すれば、より長いコンテキストを扱えるようになります。
まとめ
| 用途 | 推奨 |
|---|---|
| 本格的な開発作業 | Opus 4.6 — ツール使用の信頼性と速度が圧倒的 |
| シンプルなQ&A・コード生成 | Qwen3.5-397B でも実用可能 |
| コスト重視(月額ゼロ運用) | Qwen3.5-397B — 完全ローカル |
| 機密コードの取り扱い | Qwen3.5-397B — データが外に出ない |
正直な結論: 現時点では、Claude Code + Opus 4.6の組み合わせが品質・速度・信頼性のすべてで優位です。ローカルQwenは「簡単な質問」「単発のコード生成」には使えますが、ファイル操作やマルチステップのエージェントタスクではコンテキスト制約が厳しく、実用には課題が残ります。
ただし、これはモデルの限界ではなくインフラの限界です。Qwen3.5-397Bのモデル自体は十分に賢く、コンテキスト長を十分に確保できれば結果は大きく変わる可能性があります。DGX Sparkの台数を増やしてメモリを拡張する、あるいはより効率的な量子化手法でメモリ消費を減らすなど、改善の道筋は見えています。
「月額ゼロで、手元のハードウェアだけでAIコーディングエージェントを動かす」という目標自体は達成できました。あとは精度を上げていく段階です。
環境情報
| 項目 | 値 |
|---|---|
| ハードウェア | DGX Spark x2(スタッキング) |
| GPU | GB10 (Blackwell) x2 |
| 統合メモリ | 128GB x2 = 256GB |
| 推論エンジン | vLLM 0.17.1 |
| モデル | intel/Qwen3.5-397B-A17B-int4-AutoRound |
| Claude Code | v2.1.77 (Opus 4.6) |
| テスト日 | 2026-03-17 |
参考リンク
- 前回の記事: DGX Spark 2台でQwen3.5-397Bを動かす
- Claude Code — Anthropic公式ドキュメント
- spark-vllm-docker — DGX Spark向けvLLMクラスタ構築ツール
- intel/Qwen3.5-397B-A17B-int4-AutoRound — 使用したモデル
