値上げが止まらないPCパーツは今のうちにAmazonでチェックして確保してしまうのが吉♪

OpenCodeをManjaro Linuxに導入してFreeToken-Pascalへ接続|Qwen3.6 35BでローカルAIコーディング

※ 記事中にプロモーション広告(アフィリエイトリンク)が含まれます。

先に注意:この記事のユーザー名、IPアドレス、ファイルパスは構成例です。ご自身の環境に合わせて変更してください。また、Manjaro LinuxのVM内で35Bモデルを動かした話ではありません。モデルを動かすのは別VMのVM300(Ubuntu Server)で、Manjaro側はOpenCodeのクライアントとして使います。

管理人の小職さん管理人の小職さん

OpenCodeをManjaroに入れて、ローカルLLMにつなぐんだよね。Manjaro側でモデルも動かすの?
モデルを動かすのは別VMのVM300だよ。Manjaroはクライアント専用で、SSHトンネル経由でVM300のAPIにつなぐ構成なんだ。

ロボ小職ロボ小職

この記事の主題は、Manjaro LinuxへOpenCodeをインストールし、別VMであるVM300のUbuntu Server上で動作するFreeToken-Pascalへ、SSHトンネル経由で接続することです。Qwen3.6 35B A3B NVFP4をローカルAIコーディングモデルとして使い、権限制御、AGENTS.md、WebFetch、WebSearch、Compactionを含む実用環境までを扱います。

役割分担:2台に分けることの結論

最初に整理しておきます。Manjaro側は「使う側」、VM300側は「動かす側」です。この分担を混ぜると、どの数値がどのマシンのものか分からなくなります。

項目 Manjaro Linux側 VM300(Ubuntu Server)側
OpenCode クライアントとして実行 実行しない
推論エンジン なし FreeToken-Pascal
GPU なし(クライアントのみ) GTX 1070 8GB(PCIパススルー)
モデル なし Qwen3.6 35B A3B NVFP4(qwen3.6-35b-a3b-nvfp4-candidate)
作業リポジトリ OpenCodeが読み書きする なし
SSH SSHクライアント SSHサーバー
AGENTS.md プロジェクトルートで管理 なし
WebFetch / WebSearch OpenCodeのエージェント機能 なし
API 127.0.0.1:8082(トンネル側) 127.0.0.1:8082(実際の待ち受け)

この記事で作る構成

Manjaro Linux(クライアント)
├─ OpenCode
├─ 作業リポジトリ
├─ AGENTS.md
├─ WebFetch / WebSearch
└─ 127.0.0.1:8082
        │
        │ SSHトンネル
        ▼
Ubuntu Server VM300(推論)
├─ FreeToken-Pascal
├─ freetoken-qwen36.service
├─ Qwen3.6 35B A3B NVFP4
├─ GTX 1070 8GB
└─ 0.0.0.0:8082(待ち受け)

VM300のAPIポートをLANへ直接公開するのではなく、SSHトンネルで127.0.0.1:8082へ引き込みます。OpenCodeから見える接続先は、Manjaro自身の127.0.0.1:8082です。

確認レベルの見分け方

この記事では、確認できた度合いを3段階で書き分けます。

区分 内容
確定事項 公式文書で確認した、実機で実行した、API応答やログ、設定ファイル、Gitコミットで裏付けが取れた
高確率 実動作から可能性が高いと判断できるが、内部実装までは確認していない
未確認 実行していない手順、未測定の性能、未確認のOpenCode機能、未確認のLSP、未確認のマルチエージェント動作
ロボ小職メモ
この記事で確認したクライアントはOpenCode 1.18.32系です。バージョンによって設定項目や既定の挙動が変わる可能性があるため、手順は公式ドキュメントとあわせて確認してください。

OpenCodeとは何か

OpenCodeは、公式ドキュメントで「オープンソースのAIコーディングエージェント」と説明されているツールです。ターミナル中心のインターフェースに加えて、Desktop版とIDE拡張も提供されています。

  • モデルはプロバイダ設定で切り替える。OpenAI互換のエンドポイントも指定できる
  • /init でプロジェクトを解析し、AGENTS.md を作成・更新できる
  • PlanモードとBuildモードをTabキーで切り替えられる。Planは変更を行わず提案に留める
  • プロジェクトのAGENTS.mdはGitへコミットすることが推奨されている
  • グローバルな指示は~/.config/opencode/AGENTS.md、設定は~/.config/opencode/opencode.jsonに置ける

公式ドキュメントでは、インストール手段としてインストールスクリプト、npm、Bun、pnpm、Yarn、Homebrew、Arch Linuxのパッケージ、WindowsはWSL推奨などが案内されています。ローカルLLMだけでなく、クラウドのプロバイダも設定次第で使えます。

コードエディタの画面とソースコード
OpenCodeはターミナル中心のコーディングエージェント。ファイル読取り、編集、コマンド実行をツール経由で行う

ターミナル上でファイルを読み、編集し、コマンドを実行する流れが基本です。今回のように推論先を別マシンへ置くと、クライアント側のスペックは軽くて済みます。

用語メモ
Planモードは「変更しない」モードです。設計の相談や影響範囲の確認に向いていて、実際にファイルを書き換えるときはBuildモードへ切り替えます。

Manjaro LinuxへOpenCodeを導入

Step 1:環境確認

クライアント側の環境です。モデルはVM300が担当するため、このマシンにGPUは必要ありません。

項目 値
OS Manjaro Linux(カーネル6.18系)
CPU 8コア
RAM 8GB
SSD 120GB
Node.js v26.8.1
npm 12.0.2

クライアント専用なら、RAM 8GBでも作業はできます。ただし長いセッションや大きなリポジトリでは、RAMとストレージの余裕が効いてきます。メモリは足りなくなってから増やすと作業が中断するので、先に確認しておくのがおすすめです。

増設時は、マザーボードが対応する規格(DDR4かDDR5か)とスロット数を確認してください。世代が違うと物理的に挿さりません。

Step 2:OpenCodeをインストール

公式で案内されているインストールスクリプトか、npmで導入します。Manjaro/Arch Linuxならパッケージも選べます。

# インストールスクリプト(公式の案内)
curl -fsSL https://opencode.ai/install | bash

# npmでグローバルインストール
npm install -g opencode-ai

# Arch Linux / Manjaro のパッケージ
sudo pacman -S opencode
paru -S opencode-bin

curlでスクリプトを直接実行する方法は手軽ですが、配布元と内容を確認してから実行してください。npm版はNode.jsのバージョンに影響されるため、事前にnode -vとnpm -vを確認しておくと安全です。

Step 3:プロジェクトを初期化

作業したいリポジトリへ移動してOpenCodeを起動し、/initを実行すると、プロジェクトを解析してAGENTS.mdを作成・更新します。

cd ~/work/your-project
opencode
# 起動後にTUIで /init を実行

作成されたAGENTS.mdはGitへコミットしておきます。プロジェクト固有のビルド手順や規約を書いておくと、セッションをまたいでも同じ前提で作業してくれます。

VM300のFreeToken-Pascal準備

VM300の構成

推論側は、Proxmox VE上のUbuntu Server VM300です。GTX 1070 8GBをPCIパススルーし、FreeToken-PascalでQwen3.6 35B A3B NVFP4を動かしています。

項目 値
仮想化基盤 Proxmox VE(ホストの物理メモリ64GB)
VM Ubuntu Server(VM300)
GPU GTX 1070 8GB(PCIパススルー)
推論エンジン FreeToken-Pascal
モデル Qwen3.6 35B A3B NVFP4
API 127.0.0.1:8082(構成例)

起動設定と起動確認

FreeToken-Pascalはsystemdのユーザーサービスとして常駐させています。主要な設定は次のとおりです。

設定項目 値
コンテキスト長 32,768
最大出力トークン 1,024
Prefill分割長 2,048
キャッシュ方式 radix
tool-call parser qwen3_coder
reasoning parser qwen3
MoE CPUスレッド数 10
MoEキャッシュサイズ 0
# サービスの状態確認
systemctl --user status freetoken-qwen36.service

# 起動ログの確認例
Prefill warmup complete
Scheduler is idle, waiting for new reqs...
API server is ready to serve on 0.0.0.0:8082

モデルのロードとPrefillのウォームアップを含めて、API Readyまで約6分30秒かかりました。起動直後にアクセスして失敗する場合は、この待ち時間を疑ってください。

なお、本家FreeTokenのCLIはLinux x86_64、NVIDIA GPU、ドライバーr580以降(CUDA 13)が公式要件です。GTX 1070のようなPascal世代はそのままでは対象外で、今回はPascal向けのfork(FreeToken-Pascal)を使っています。

新しくGPUサーバーを組む場合は、VRAM容量を基準にカードを選び、量子化形式の選択肢とあわせて考えることになります。

カードを選ぶときは、Compute Capabilityと必要なドライバーのバージョンも確認してください。世代が古いと、動かせる量子化形式が限られます。

SSHトンネルでVM300へ接続

Manjaro側から専用のSSH鍵を作り、ポートフォワードでVM300のAPIを手元の127.0.0.1:8082へ引き込みます。

(PR)

# 専用鍵の作成(例)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_vm300_tunnel -C "vm300-tunnel"

# 公開鍵をVM300へ登録
ssh-copy-id -i ~/.ssh/id_ed25519_vm300_tunnel.pub <Ubuntuユーザー名>@<VM300_IP>
# ~/.ssh/config の例
Host vm300
    HostName <VM300_IP>
    User <Ubuntuユーザー名>
    IdentityFile ~/.ssh/id_ed25519_vm300_tunnel
    IdentitiesOnly yes
    ServerAliveInterval 30
    ServerAliveCountMax 3
    ExitOnForwardFailure yes
# トンネルを張る(-Nはコマンド実行なし)
ssh -N -L 8082:127.0.0.1:8082 vm300

# 別ターミナルから確認
curl http://127.0.0.1:8082/v1/models

<VM300_IP>や<Ubuntuユーザー名>は各自の環境に置き換えてください。ExitOnForwardFailure yesを付けておくと、ポート転送に失敗したときに接続が残らず、原因を切り分けやすくなります。

サーバーラックとネットワーク機器
APIポートをLANへ公開せず、SSHトンネル経由で127.0.0.1へ引き込む構成にしている

トンネルはSSHセッションが切れると消えます。自動再接続の常駐化(autosshやsystemdユーザーサービスなど)は選択肢がありますが、本記事の確認範囲では運用検証まで行っていないため、未確認として扱います。

APIキーやトークン、SSH秘密鍵を公開リポジトリへコミットしないでください。APIキーを含むファイルは.gitignoreへ追加し、誤ってpushした場合は履歴からの削除とキーの再発行を行ってください。

OpenCodeからVM300のモデルを使う

OpenCodeの設定ファイルで、プロバイダとしてVM300のOpenAI互換エンドポイントを指定します。

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "vm300": {
      "npm": "@ai-sdk/openai-compatible",
      "options": {
        "baseURL": "http://127.0.0.1:8082/v1"
      },
      "models": {
        "qwen3.6-35b-a3b-nvfp4-candidate": {}
      }
    }
  }
}

設定はプロジェクトのopencode.jsonか、グローバルの~/.config/opencode/opencode.jsonへ置きます。baseURLがトンネル側の127.0.0.1:8082になっている点が重要です。ローカル推論のため、クラウドのAPIキーは不要です。

/v1/modelsで返るモデルIDと、設定のmodelsのキーを一致させてください。model欄が食い違うと、リクエストが通っても意図しないモデルへ当たったり、エラーになったりします。

tool-callとreasoningの設定

OpenAI互換APIから文章が返るだけでは、コーディングエージェントとしては不十分です。OpenCodeがツールを実行するには、応答が構造化されたtool_callsになっている必要があります。

初期状態では、tool callがXML風の文字列として本文に残り、ツール呼び出しとして認識されませんでした。tool-call parserをqwen3_coderへ変更したうえで、直接APIで次を確認しています。

  • finish_reason = tool_calls
  • tool name:list_directory
  • arguments:{"path":"."}

reasoning側はqwen3を指定し、内部推論をreasoning_contentへ分離しています。これにより、通常の本文へ</think>のような文字列が混入しにくくなります。

権限とAGENTS.mdで操作範囲を制御

ローカルLLMだからといって、プロジェクト全体を無制限に調査・変更させる構成にはしていません。プロジェクトルートのAGENTS.mdで操作規則を定め、Gitで管理しています。

  • 作業対象は現在のプロジェクト内に限定する
  • 無制限の全体走査を禁止し、一度に読むファイルは最大3件
  • 一度に変更するファイルは最大3件
  • 既存の未コミット変更を上書きしない
  • git reset、git clean、強制checkoutを禁止する
  • ファイル削除は明示的な許可を必要とする
  • Web取得の結果を大量にコンテキストへ追加しない

関連するコミットは次の3件です。

ee69fc299 Add OpenCode project operating rules
9ab941df4 Unify approved web access into OpenCode ops agent
4a837daf9 Add bounded web access performance rules

ここでいうバイブコーディングは、AIへ無制限の操作権を渡すことではありません。対象、権限、変更量、テスト、バックアップ、ロールバックを明示したうえで、AIと対話しながら開発を進める方式です。

Radixキャッシュの効果

OpenCodeは、ファイル読取りやツール実行のたびにモデルを呼び出します。そのたびにシステム指示やツール定義、会話履歴を再処理すると、古いGPUでは負担が大きくなります。

初期設定ではキャッシュ方式がnaiveでした。これをradixへ変更したところ、同一prefixの試験で次の結果になりました。

試験 所要時間 new-token cached-token
同一要求1回目 41秒 310 0
同一要求2回目 13秒 54 256

約68%の短縮です。ただし、Radixを有効にすればすべて速くなるわけではありません。ツール結果や会話構造が変わってprefixが一致しない場合は、cached-token=0となり、再び41秒側へ戻ります。

コンテキストとCompaction

OpenCodeは、ユーザーの入力だけでなく、システム指示、エージェント定義、ツール定義、AGENTS.md、会話履歴、ファイル内容、コマンド結果、Web取得結果まで入力へ加えます。長いセッションでは、次のコンテキスト超過が発生しました。

prompt is too long: 38879 tokens > 32768 maximum
設定項目 値
input limit 32,000
context limit 32,768
output limit 512
request max_tokens 512
provider timeout 30分
chunk timeout 30分
automatic compaction 有効
compaction keep 4,000
compaction buffer 12,000

自動Compactionは長いセッションを続けるために必要ですが、ローカル35Bモデルでは圧縮そのものも重く、約6分9秒かかった例があります。bufferを12,000へ増やして早めに圧縮を始め、keepを4,000へ下げて圧縮後のコンテキストを軽くしています。

それでも、無関係な作業を1つのセッションへ詰め込まない方が安定します。おおむね1作業1セッションが目安です。

WebFetchとWebSearchのルール

OpenCodeから公式情報や既知URLを確認できるよう、常用エージェントへWeb機能を統合しています。

  • 既定URLの取得:webfetch
  • Web検索:websearch(有効化はOPENCODE_ENABLE_EXA=true)
  • Web権限:ask(実行前に確認する)

入力が膨らみすぎないよう、次のルールも追加しています。

  • WebFetchは1ターン1 URL
  • WebSearchとWebFetchを同一ターンで連続実行しない
  • WebSearch結果は原則3件以内
  • 同じURLを同一セッションで再取得しない
  • ページ内リンクを自動取得しない
  • Web取得後は再取得せず、取得済み結果を整理する

確認できたのは、WordPress公式トップページのWebFetch、公式の開発者向け資料のWebSearch、検索結果の件数制限、Web調査時にプロジェクトファイルを変更しない動作です。

実測結果

直接APIの実測

短いプロンプトを直接APIへ投げた結果です。

回 所要時間 prompt tokens completion tokens
1回目 約4.01秒 17 2
2回目 約2.36秒 17 2

ストリーミングの実測

ストリーミングではHTTP 200を確認し、time_starttransferが約0.027秒、time_totalが約2.346秒でした。最初のトークンが早く返るため、体感はさらに軽くなります。

WebFetchの実測

OpenCode経由のWebFetchでは、最終回答まで約8分56秒かかった例があります。WebFetch自体は成功しており、その後のPrefillが4,680トークン、cached-tokenは0でした。

開発者がコンピュータで作業している様子
短い質問は数秒、文書やWeb結果を大量に含む処理は数分。入力の大きさで所要時間が大きく変わる

まとめると、短い直接APIとストリーミングは数秒、同一prefixではRadixキャッシュが効く、cache miss時のPrefillは重い、OpenCodeでは数分かかる場合がある、という挙動です。常に数秒で返るわけではありませんし、GTX 1070で35Bモデルが爆速という話でもありません。

向いている作業と向いていない作業

向いている作業

  • 指定ファイルの調査
  • 小規模なコード修正
  • Git差分の確認
  • 対象を限定したテスト
  • 公式仕様のWeb検索
  • ローカル環境へコードや情報を残したい作業

向いていない作業

  • プロジェクト全体の無制限走査
  • 大量URLの一括取得
  • 複数の無関係な作業を1セッションで継続
  • 最新GPUと同等の応答速度を要求する用途
  • 時間制限の厳しいリアルタイム処理

プライバシーと外部通信

この構成の利点は、推論先をローカルAPIだけに限定できることです。プロンプトをクラウドLLMへ送らずに運用できます。

ただし「ローカルLLMなら完全オフライン」「情報が絶対に外へ出ない」とは言えません。次の要素は外部通信する可能性があります。

  • モデルのダウンロード
  • OpenCodeやパッケージの更新確認
  • Gitの操作
  • WebFetch、WebSearch
  • MCPサーバー
  • プラグインや拡張機能
  • テレメトリやライセンス確認
  • クラウドモデルの利用

推論は手元に閉じ込めつつ、どこで外へ出るかを把握しておくのが現実的です。

確認できたこと・高確率・未確認

確定事項

  • FreeToken-Pascalの起動、API Ready、/v1/modelsの応答
  • 短い直接API、ストリーミング、OpenCode接続、構造化tool_calls
  • WebFetch、WebSearch、Radixキャッシュ、自動Compaction
  • systemdユーザーサービスとしての常駐と起動ログ
  • OpenCode公式ドキュメントで確認したインストール手段、AGENTS.md、Plan/Buildモード、設定ファイルの位置

高確率

  • Radixキャッシュが効いた区間の短縮(41秒→13秒)
  • OpenCode経由の処理が重いのは、ツール定義・文書・Web結果による入力増加が主因という見立て
  • RAM 8GBのクライアントは、長いセッションや大きなリポジトリで逼迫しやすいという傾向

未確認

  • Manjaro VM内でモデルを動かす構成(本記事では採用していない)
  • OpenCodeのLSPによる自動補完の挙動
  • マルチセッション・マルチエージェントの並列動作
  • SSHトンネルの自動再接続を常駐化した場合の運用
  • Manjaro固有のバージョン差分による挙動の違い
  • 実測していない速度・VRAM使用量

OpenCode環境のよくある質問

管理人の小職さん管理人の小職さん

ManjaroのVMの中で35Bモデルを動かしているの?
動かしていないよ。モデルはVM300のFreeToken-Pascal側で、ManjaroはOpenCodeのクライアントとして使っているんだ。

ロボ小職ロボ小職

管理人の小職さん管理人の小職さん

8GBのGPUでもこの構成は使える?
推論側のVRAMに収まらない分はCPUとシステムRAMで補う形になるよ。今回は8GBのGTX 1070で35B級MoEを動かしている。

ロボ小職ロボ小職

管理人の小職さん管理人の小職さん

ローカル推論なら、情報は外へ出ないと思っていい?
推論は外へ出ない構成にできるよ。ただモデル取得や更新確認、Web取得、Gitは外部通信する可能性がある。そこは分けて考えよう。

ロボ小職ロボ小職

管理人の小職さん管理人の小職さん

トンネルが切れたら、そのまま作業は止まる?
そのとおり。切れたらAPIへ届かなくなるから、張り直してcurlで確認するのが早いよ。常駐での自動再接続は今回の確認範囲外なんだ。

ロボ小職ロボ小職

まとめ

Manjaro LinuxへOpenCodeを導入し、VM300のUbuntu Serverで動くFreeToken-PascalへSSHトンネル経由で接続する構成を作りました。Qwen3.6 35B A3B NVFP4をローカルAIコーディングモデルとして、構造化tool calling、AGENTS.mdによる権限制御、WebFetch、WebSearch、Radixキャッシュ、自動Compactionまで確認しています。

最初から素直に動いたわけではありません。tool callが本文に残る問題、dtypeの不一致、MoEキャッシュ設定、コンテキスト超過を一つずつ切り分けました。古いGPUを推論側に置き、クライアントを軽くする分担は、手元のハードウェアを使い切るうえで扱いやすい形です。

短い質問は数秒、文書やWeb結果を多く含む処理は数分。この差を前提に使うと、期待値のずれで悩まずに済みます。

管理人の小職さん管理人の小職さん

クライアントを軽くできるなら、古いノートPCでも遊べそうだね。
そこが狙いだよ。重い処理はVM300へ任せて、手元は操作と確認だけ。役割を分けると、古いマシンでも居場所ができるんだ。

ロボ小職ロボ小職

参考資料

 

(PR)