Qwen Code ローカル運用実践記 — Mac Studio M3 Ultra で Ollama + qwen3-coder:30b を動かして分かったこと

Qwen Code(Alibaba Cloud Qwen チームが開発したオープンソース CLI コーディングエージェント)を Mac Studio M3 Ultra(96GB)上で Ollama と組み合わせてローカル運用を試みた実践記録です。環境構築からツール呼び出しの限界まで、実際に手を動かして検証した結果をまとめます。

背景と目的

Claude Code は強力ですが、コードがクラウドに送信されるためプライバシーの懸念があります。Qwen Code は Apache 2.0 ライセンスのオープンソースで、Ollama と組み合わせれば完全ローカルで動作するため、機密コードベースでの利用が期待されます。

本記事の検証環境:

項目スペック
マシンMac Studio M3 Ultra
メモリ96GB ユニファイドメモリ
メモリ帯域800 GB/s
Ollamav0.15.6
Qwen Codev0.12.0(Fork からローカルビルド)
モデルqwen3-coder:30b (18GB)

ステップ1: リポジトリの Fork と Clone

調査・改造を前提に、まず QwenLM/qwen-code を Fork しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Fork(GitHub CLI)
gh repo fork QwenLM/qwen-code --clone=false

# devel ブランチを作成してデフォルトに設定
# main は upstream との sync 用にクリーンに保つ
gh api repos/hdknr/qwen-code/git/refs \
  -f ref="refs/heads/devel" \
  -f sha="$(gh api repos/hdknr/qwen-code/git/ref/heads/main --jq '.object.sha')"
gh repo edit hdknr/qwen-code --default-branch devel

# Clone
mkdir -p ~/Projects/qwen
cd ~/Projects/qwen
gh repo clone hdknr/qwen-code

ブランチ戦略:

upstream/main ──sync──→ fork/main ──merge──→ fork/devel(作業用)

main を upstream と同期させ、devel で自由に作業する構成です。

ステップ2: Ollama のモデルダウンロード

Ollama は導入済みだったため、Qwen モデルのダウンロードのみ実行しました。

1
2
3
4
5
6
# メインモデル(96GB なら余裕)
ollama pull qwen3-coder:30b    # 18GB

# 比較用
ollama pull qwen2.5-coder:14b  # 9.0GB
ollama pull qwen2.5-coder:7b   # 4.7GB

メモリ別の推奨モデル

Macメモリ推奨モデル推論速度
M416GBqwen3:8b28-35 tok/s
M4 Pro24GBqwen3-coder:14b25-30 tok/s
M4 Pro48GBqwen3-coder:30b12-18 tok/s
M3 Ultra96GBqwen3-coder:30b25-35 tok/s

M3 Ultra の 800 GB/s メモリ帯域は LLM 推論に大きなアドバンテージがあり、M4 Pro(273 GB/s)の約3倍の速度が出ます。

動作確認

1
ollama run qwen3-coder:30b "Write a Python function that returns the first n prime numbers."

正常にコードが生成されることを確認しました。

ステップ3: Qwen Code のビルド

1
2
3
4
5
6
7
cd ~/Projects/qwen/qwen-code

# 依存関係インストール
make install

# ビルド
make build

トラブル: Xcode ライセンス

make install 実行時に以下のエラーが発生しました:

You have not agreed to the Xcode license agreements.
Please run 'sudo xcodebuild -license' from within a Terminal window

sudo xcodebuild -license を実行してライセンスに同意した後、再度 make install で解決しました。

ステップ4: settings.json の設定 — 3回の試行錯誤

ここが最も苦労した部分です。Qwen Code の設定ファイル形式はドキュメントが不十分で、ソースコードを読んで正しい形式を特定する必要がありました。

試行1: 失敗 — 設定形式が不正

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "model": { "name": "qwen3-coder:30b" },
  "providers": {
    "ollama": {
      "baseUrl": "http://localhost:11434/v1",
      "apiKey": "EMPTY"
    }
  },
  "security": {
    "auth": { "selectedType": "openai-compatible" }
  }
}

エラー: TypeError: Cannot read properties of undefined (reading 'model')

原因: providers というキーは存在せず、modelProviders が正しいキー名。selectedType の値も openai-compatible ではなく openai

試行2: 失敗 — ログインを求められる

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "model": { "name": "qwen3-coder:30b" },
  "modelProviders": {
    "openai": [
      { "id": "qwen3-coder:30b", "baseUrl": "http://localhost:11434/v1" }
    ]
  },
  "security": {
    "auth": {
      "selectedType": "openai",
      "apiKey": "EMPTY",
      "baseUrl": "http://localhost:11434/v1"
    }
  }
}

問題: 起動時に Qwen OAuth のログイン画面が表示される。selectedType が外部プロセスによって削除される現象も発生。

試行3: 失敗 — envKey 不足

ヘッドレスモードで --auth-type openai を明示的に指定しても:

Missing credentials for modelProviders model 'qwen3-coder:30b'.
Configure modelProviders.openai[].envKey and set that environment variable.

最終的な正解の設定

ソースコード(packages/cli/src/config/settingsSchema.tspackages/core/src/models/modelConfigResolver.ts)を読み解いた結果、以下の設定に到達しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
  "model": {
    "name": "qwen3-coder:30b"
  },
  "modelProviders": {
    "openai": [
      {
        "id": "qwen3-coder:30b",
        "name": "Qwen3-Coder 30B (Ollama)",
        "baseUrl": "http://localhost:11434/v1",
        "envKey": "OPENAI_API_KEY"
      },
      {
        "id": "qwen2.5-coder:14b",
        "name": "Qwen2.5-Coder 14B (Ollama)",
        "baseUrl": "http://localhost:11434/v1",
        "envKey": "OPENAI_API_KEY"
      },
      {
        "id": "qwen2.5-coder:7b",
        "name": "Qwen2.5-Coder 7B (Ollama)",
        "baseUrl": "http://localhost:11434/v1",
        "envKey": "OPENAI_API_KEY"
      }
    ]
  },
  "security": {
    "auth": {
      "selectedType": "openai",
      "apiKey": "EMPTY",
      "baseUrl": "http://localhost:11434/v1"
    }
  },
  "env": {
    "OPENAI_API_KEY": "EMPTY"
  }
}

設定のポイント

キー正しい値間違いやすい値
modelProvidersauthType をキーにした配列providers は不正
security.auth.selectedTypeopenai(AuthType enum 値)openai-compatible は不正
modelProviders[].envKeyOPENAI_API_KEY(必須)省略するとエラー
env.OPENAI_API_KEYEMPTY(Ollama はキー不要だがフィールドは必須)省略するとエラー

ソースコードから読み解いた設定の優先順位

modelProvider > CLI引数 > 環境変数 > settings.json > デフォルト値

API キーの解決順序(packages/cli/src/utils/modelConfigUtils.ts):

argv.openaiApiKey > env[modelProvider.envKey] > OPENAI_API_KEY > settings.security.auth.apiKey

ステップ5: 起動と動作確認

エイリアスの設定

毎回 OPENAI_API_KEY=EMPTY を付けるのは面倒なので、.zshrc にエイリアスを追加しました。

1
2
# ~/.zshrc に追加
alias qwen-local='OPENAI_API_KEY=EMPTY qwen'

グローバルに OPENAI_API_KEY=EMPTY を設定すると、将来 OpenAI API を使う際に影響するため、エイリアス方式を選択しました。

動作確認

1
2
3
4
5
6
7
# ヘッドレスモード
qwen-local -p "Say hello in Japanese" --auth-type openai
# → こんにちは!

# コード生成
qwen-local -p "Pythonでフィボナッチ数列を返す関数を書いてください" --auth-type openai
# → 正常なPythonコードが生成される

日本語の指示理解、コード生成ともに問題なく動作しました。

ステップ6: 複雑なタスクでの限界 — ツール呼び出しの問題

PRレビューを依頼した結果

対話モードで以下を指示しました:

gh pr diff 8891 の内容を取得して、コードをレビューしてください

期待した動作: Qwen Code の shell ツールで gh pr diff 8891 を実行し、差分を取得してレビュー

実際の動作: モデルが XML テキストを出力

1
2
3
4
5
6
7
8
<function=run_shell_command>
<parameter=command>
gh pr diff 8891
</parameter>
<parameter=description>
Fetching the actual code changes from the pull request
</parameter>
</function>

モデルがツール呼び出し API を使わず、Claude のツール呼び出し形式をテキストとして模倣してしまいました。

原因の調査

Ollama の API レベルではツール呼び出しが正常に動作することを確認済みです:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ネイティブ API(/api/chat)→ 正常
curl http://localhost:11434/api/chat -d '{
  "model": "qwen3-coder:30b",
  "tools": [{"type":"function","function":{"name":"calculator",...}}],
  ...
}'
# → tool_calls が正しく返る

# OpenAI 互換 API(/v1/chat/completions)→ 正常
curl http://localhost:11434/v1/chat/completions -d '{
  "model": "qwen3-coder:30b",
  "tools": [{"type":"function","function":{"name":"calculator",...}}],
  ...
}'
# → tool_calls が正しく返る

API レベルでは問題ないのに、Qwen Code 上では XML テキストが出力される。

根本原因

30B モデルの推論能力の限界です。

タスクの複雑度30B ローカルClaude Code
単純なコード生成正常にツール使用正常
ファイル編集正常にツール使用正常
「PRを取得してレビュー」ツール呼び出しに失敗(XML模倣)正常
マルチステップの調査困難正常

単純なタスクではツール呼び出し API を正しく使えますが、「PRを取得 → diff を解析 → レビューコメントを生成」のようなマルチステップのエージェントタスクになると、30B モデルではツール呼び出しのチェーンを正確に制御できません。

Qwen Code と Claude Code — 実用上の比較

ベンチマーク(SWE-bench)

構成スコア
Claude Sonnet 4 + Claude Code70.4%
Qwen3-Coder + OpenHands(500ターン)69.6%
Qwen3-Coder(標準)67.0%

ベンチマークスコアは近いですが、実際のエージェントタスクでの体験は大きく異なります

実用比較

タスクQwen Code(ローカル30B)Claude Code
関数の生成・編集実用的優秀
テスト作成実用的優秀
日本語での指示理解良好優秀
バグ修正簡単なものは可能複雑なものも対応
PR レビュー(gh連携)不可(ツール呼び出し失敗)正常動作
複数ファイルの横断調査不安定安定
MCP サーバー連携未検証安定

使い分けの指針

タスクの複雑度
  │
  ├── 単純(コード生成、ファイル編集、テスト作成)
  │     └── Qwen Code(ローカル)で十分
  │           メリット: 無料、プライバシー、オフライン対応
  │
  └── 複雑(PR レビュー、マルチステップ調査、大規模リファクタ)
        └── Claude Code を使用
              メリット: 高い推論能力、安定したツール呼び出し

まとめ

  • Mac Studio M3 Ultra(96GB)で Qwen Code + Ollama のローカル運用環境を構築: Fork → ローカルビルド → Ollama 接続まで約1時間で完了
  • settings.json の設定が最大のハードル: ドキュメントが不十分で、ソースコード(settingsSchema.ts、modelConfigResolver.ts)を読み解く必要があった。modelProvidersproviders ではない)、selectedType: "openai"openai-compatible ではない)、envKey 必須がポイント
  • 単純なコード生成は実用的: 日本語指示の理解、Python/TypeScript のコード生成は問題なく動作
  • 複雑なエージェントタスクは30Bモデルの限界: PR レビューのようなマルチステップタスクでは、ツール呼び出しに失敗しテキストで XML を模倣する現象が発生
  • API レベルではツール呼び出しは正常: Ollama の /v1 エンドポイントでの tool_calls は正しく動作しており、問題はモデルの推論能力
  • Claude Code との使い分けが現実的: 単純タスク(プライバシー重視)→ Qwen Code ローカル、複雑タスク → Claude Code

今後の検証予定

  • Qwen OAuth(クラウドの大規模モデル)での PR レビュー動作確認
  • MCP サーバー(GitHub)追加によるツール能力の補完
  • qwen3-coder の更新版での改善確認
  • Qwen-Agent フレームワークでのカスタムエージェント構築

参考