背景
最近、生成AIを開発環境へ組み込む話をしていると、「ローカルLLMを使えばオフラインでもだいたい同じことができるのでは」という話になることがあります。
方向性としては間違っていません。
一方で、実際に現場へ持っていこうとすると、モデルを1つダウンロードして終わり、という話にはなりません。
VS Codeからモデルへ接続できること。
Chatが返ること。
Agentが構造化されたtool callingを返せること。
モデル、runtime、設定、コンテキスト長がオンライン準備機とオフライン運用機で一致していること。
さらに、移送途中でファイルが欠落・変更されていないこと。
このあたりを分けて考えないと、「ローカルモデルは動いたが、Agentとしては使えない」「Chatはできるが期待していたTab補完はできない」といったズレが起きます。
私が今回整理したかったのは、単にOllamaをインストールする方法ではありません。
オンライン接続できる準備機で必要な資材を集め、それを移送キットとして固定し、ネットワークから切り離した運用機へ持ち込み、VS CodeのChatとChatビュー内AgentをローカルLLMで使うところまでを、一つの運用として扱うことです。
対象は次の2系統です。
- Windows 11 x64
- macOS 14以降 / Apple Silicon(
arm64)
通常経路ではOllamaを利用し、既定モデルをqwen3:8b、contextを8192としています。
正直、この手の構成では「動かす」ことよりも、「どこまで確認できたら動いたと判断するか」を決めるほうが重要だと感じています。
Repository
この Blog の大本です。スクリプトやツールなどは、こちらのRepositoryにあります。
注意点 / 前提
最初に、この文章の位置づけを明確にしておきます。
これは私が今回のリポジトリ、スクリプト、検証結果を整理した内容です。
製品一般のサポート範囲や、すべてのWindows / macOS環境での動作を保証するものではありません。
特に重要なのは、2026年8月16日時点で、WindowsとmacOSの両方についてクリーンな準備機からオフライン運用機までの完全なE2Eが終了しているわけではないことです。
Windows 11 x64については、オンライン準備機で以下まで実行しています。
- 必要資材の実ダウンロード
qwen3:8b- context
8192 - 専用Ollama serverを使ったAgent endpoint検証
- 自己完結した移送キットの生成
一方、まだ実施していない範囲があります。
- 物理媒体を使った別端末への移送
- クリーンな別のWindows運用機でのdry-run
- Apply
- VS Codeのモデルピッカー
- VS Code Chatの実往復
- VS Code Chatビュー内Agentの実往復
macOSについては、スクリプト、固定契約、静的テストを照合していますが、実機を使った準備からVS Code確認までのE2Eは未実施です。
したがって、この文章をそのままプロダクション利用の保証として扱うのではなく、まず管理下の検証端末で完了条件まで通す、という前提で読んでください。
また、disable_ollama_cloud=trueを設定しても、それだけでネットワークが遮断されるわけではありません。
オフライン要件があるなら、物理的なネットワーク非接続、OS側の制御、組織のegress制御などは別のレイヤーで考える必要があります。
ここは実務では話が変わることが多いです。
整理・考え方
ここで一度、整理してみます。
今回の構成で考えているのは、単なる「ローカルLLM環境」ではありません。
大きく分けると、次の5つがあります。
| 要素 | この構成での役割 |
|---|---|
| 準備機 | インターネットへ接続し、installer、runtime、モデルなどを収集・検証する |
| 移送キット | runtime、モデル、設定、検証ツール、manifestをまとめた固定物 |
| 運用機 | ネットワーク非接続でVS CodeとローカルLLMを利用する |
| Ollama | 通常経路のローカルLLM runtime |
| VS Code BYOK | ローカルのOpenAI Chat Completions互換endpointをChatへ接続する |
Chatが動くこととAgentが動くことは別
ここはかなり重要です。
モデルがChatへ回答できるからといって、そのままAgentとして利用できるとは限りません。
Agentでは、モデルが単に文章を返すだけではなく、
- 使用するtoolを選ぶ
- 構造化された
tool_callsを返す - toolへ正しい引数を渡す
ところまで成立する必要があります。
今回の検証では、モデル名だけを見てAgent対応とは判断しません。
準備機で実際のモデルに対して、
- model一覧に対象モデルが存在する
- Chat completionが成功する
- streamingがSSEで返る
- 構造化されたtool callが返る
- toolの引数が期待値になる
- 実効contextが期待値になる
ところまで確認します。
さらに[ WARN ]を成功扱いしません。
終了コード0かつ、警告なしで全項目が通ることを採用条件にしています。
なぜここまで見るのかというと、Agentでは「ほぼ正しい」が意外と危ないからです。
Chatなら多少形式が崩れても人が読めます。
Agentはその出力を次の処理へ渡すため、引数構造の違いがそのまま操作の違いになります。
使える機能と使えない機能
VS Code側についても、ローカルモデルを接続すればAI関連機能がすべてローカル化されるわけではありません。
今回の境界は次のように整理しています。
| 機能 | 扱い |
|---|---|
| VS Code Chat | 利用対象 |
| Chatビュー内Agent | tool calling検証合格時のみ利用 |
| インラインChat | 利用対象 |
| title / commit messageなどのutility処理 | 利用対象 |
| インライン補完(Tab / ghost text) | 対象外 |
| セマンティック検索 | 対象外 |
| embedding依存機能 | 対象外 |
特に、インラインChatとインライン補完は別物です。
inlineChat.defaultModelを設定しても、入力中に出てくるghost textやTab補完がローカルモデルへ置き換わるわけではありません。
もしインライン補完が必須要件なら、この構成は要件を満たしません。
ここは最初に切り分けておいたほうがよいです。
通常経路はOllama
このリポジトリでは、通常のAgent経路をOllamaに固定しています。
既定値は次のとおりです。
runtime : Ollama
model : qwen3:8b
context : 8192
endpoint: http://127.0.0.1:11434/v1/chat/completions
別モデルへ変更すること自体はできます。
ただし、「同じQwenだから」「同じ8Bだから」といった理由だけではAgent対応とは判断しません。
完全なモデル名に対して、同じendpoint検証をやり直します。
ローカルLLMはモデルだけで振る舞いが決まるわけではなく、runtime、量子化、context、prompt templateなどにも影響されます。
このため、実務では「モデル名」ではなく、実際に採用する組み合わせを検証単位にするほうが扱いやすいと考えています。
Foundry Localは別枠で考える
Foundry Localについては、少し扱いが違います。
現行構成では通常経路に必要ありません。
macOSでは任意でruntimeと取得済みmodel cacheを移送キットへ含められますが、VS Codeの接続設定は自動生成せず、Agentの合格経路にも利用していません。
過去の限定的な検証では、Foundry Local CLI 0.10.3とqwen3-4bを使った1回のHTTP要求で、構造化されたtool_callsではなく本文中にtool call相当のテキストが返りました。
ただし、これは1モデル、1バージョン、1回の測定です。
「Foundry LocalはAgentで使えない」と一般化する材料には不足しています。
ここでは単純に、現在のこのキットではAgentの合格経路として採用していないと整理するのが妥当です。
本文
全体は「準備 → 固定 → 移送 → 検査 → 適用 → 再検証」で考える
WindowsとmacOSでコマンドは違いますが、考え方はほぼ同じです。
オンライン準備機
↓
必要資材を取得
↓
専用Ollama endpointでモデルを検証
↓
manifest付き移送キットを生成
↓
承認済み媒体で移送
↓
オフライン運用機
↓
dry-run
↓
Apply
↓
endpoint再検証
↓
VS Code GUIでChat / Agent確認
個人的には、この順序を崩さないことが重要だと思っています。
オフライン環境では、後からパッケージを追加取得したり、その場で最新版へ更新したりする前提がありません。
そのため「何を持ち込んだのか」が曖昧になると、再現性もトラブルシュートも急に難しくなります。
1. まず対象OSを固定する
保証対象は次の2つです。
- Windows 11 x64
- macOS 14以降 / Apple Silicon(
arm64)
Windows ARM64、Windows 10、Intel Mac、Linux、WSL、Dockerについては、この移送キットの保証対象外としています。
これは「OllamaやVS CodeがそのOSでは動かない」という意味ではありません。
あくまで、この移送キットとして検証対象にしていないという意味です。
製品が対応している範囲と、自分たちが運用として保証する範囲は分けて考えたほうが安全です。
2. ハードウェアは固定スペック表より実測を見る
GPUは必須ではありません。
CPUでも推論できます。
とはいえ、速度やメモリ使用量は、
- モデル
- 量子化
- context
- CPU / GPU
- 並列要求数
などで変化します。
今回、Windows 11 x64、RTX 4060 Laptop 8,188 MiB、context 8192という1台の環境で測った参考値では、qwen3:8bは推論ピーク約6,015 MiBのVRAMを使用し、約31 tok/sでした。
ただし、この値から「8GB VRAMなら8Bまで」と一般化することはできません。
同じVRAM容量でもGPUやruntime条件が変われば結果は変わります。
実務では、準備機のスペック表だけで採用判断せず、最終的に利用する運用機でollama psの配置とcontextを確認するほうが確実です。
ディスク容量も同様です。
モデル本体だけを見るのではなく、
- 準備機上のmodel cache
- 生成した移送キット
- 移送媒体
- 運用機上の一時展開領域
- 運用機のmodel cache
まで含めて考える必要があります。
3. Windowsで移送キットを作る
WindowsではPrepare-Windows.cmdを入口にします。
cd /d C:\GitHub\HypervelocityEngineering\local-llm-dev\tools\airgap-kit
Prepare-Windows.cmd ^
--destination "C:\OfflineKitBuild\qwen3-8b-8192" ^
--model qwen3:8b ^
--context-length 8192
準備処理では、概ね次のことを行います。
-
winget.exeの確認 - PowerShell 7 x64 MSIの収集
- Python Install Managerとオフライン用Python資材の収集
- VS Code User Setupの収集
- Ollama installerの収集
- Ollamaモデルの取得
- 専用port
11435で検証用Ollama serverを起動 - Chat / streaming / tool calling / contextを検証
- 設定ファイルを生成
- 全payloadのbyte数とSHA-256を
manifest.jsonへ記録
ポイントは、普段動いているOllama serverを「たぶん同じ設定だから」と検証に使わないことです。
専用serverを選択したcontextで起動し、その状態で検証します。
なぜなら、モデルそのものではなく、実際に運用する条件を検証したいからです。
4. macOSで移送キットを作る
macOSではPowerShellを使いません。
BashとmacOS標準コマンドで完結します。
cd local-llm-dev/tools/airgap-kit
./Prepare-macOS.sh \
--destination "$HOME/offline-kit-qwen3-8b" \
--model qwen3:8b \
--context-length 8192
macOS側では、Python、VS Code Apple Silicon版、Ollamaなどを取得し、architectureや固定された情報を確認したうえでキットを生成します。
準備機に必要なPythonやOllamaが存在しない場合だけbootstrapを行います。
sudoを使う場所も限定しています。
- Pythonが不足している場合の公式pkg導入
- Ollama CLIが利用できない場合のOllama.app配置
それ以外のダウンロードやモデル取得、キット生成そのものを常時root権限で行う設計にはしていません。
実務では、権限昇格する処理がどこなのかを説明できる状態にしておくことも重要です。
5. manifestは「完全性」を見る
生成されたキットにはmanifest.jsonを持たせます。
ここには、
- platform
- architecture
- model name
- model digest
- context length
- tool calling対応判定
- component情報
- 各payloadのbyte数
- SHA-256
などを記録します。
そして運用機側では、欠落ファイルだけでなく余分なファイルも拒否します。
たとえば移送媒体へ、
memo.txt
scan-result.txt
desktop.ini
のようなファイルを「ついでに」追加することも想定していません。
なぜそこまで厳しくするかというと、移送したディレクトリを一つの固定artifactとして扱いたいからです。
一方で、SHA-256は電子署名ではありません。
信頼済みのmanifestに対してpayloadが変わっていないことは確認できますが、manifestとpayloadをセットで置換された場合の発行者真正性までは保証できません。
chain of custodyや署名が必要な組織では、別途PKIや媒体管理を追加する必要があります。
6. 運用機では必ずdry-runを先に通す
これはWindows、macOS共通です。
いきなりApplyしません。
Windowsでは、
install-windows.cmd
install-windows.cmd -Apply
macOSでは、
./install-macos.sh
./install-macos.sh --apply
という順序です。
Windowsだけは、運用機にPowerShell 7がない場合に先に、
install-windows.cmd -BootstrapPowerShell
を実行します。
この処理で同梱MSIを検証してPowerShell 7を導入し、その後dry-runまで進みます。
ただし、その場合もApply前に改めて通常のdry-runを通す構成にしています。
dry-runでは、少なくとも次を確認します。
- OS / CPU
- manifest schema
- ファイルの欠落
- extra file
- byte数
- SHA-256
- 既存runtimeのバージョン
- 既存設定
- model cache
- context設定
ここで重要なのは、「既存環境をいい感じにマージする」ことを目指していない点です。
7. 競合は自動解決しない
既存状態については、原則として次の動きです。
| 状態 | 処理 |
|---|---|
| 既存物がない | 配置する |
| 既存物が検証済みの同一内容 | skip |
| 既存物が異なる | stop |
たとえばVS Code設定が既に存在して内容が違う場合、自動mergeはしません。
Ollamaのmodel cacheが異なる場合も、勝手に置き換えません。
既存のOLLAMA_CONTEXT_LENGTHが違う場合も、自動変更せず停止します。
これは少し不便です。
とはいえ、業務端末では「親切な自動修復」が既存用途を壊すことがあります。
特に既存のVS Code環境へ導入する場合、専用ユーザーを作るか、既存設定を退避して人がキー単位で確認するほうが現実的です。
8. Apply後もAgentを再検証する
Applyが終わったから完了、とはしていません。
運用機でもverify_endpoint.pyを再実行します。
概念的には次の条件をすべて確認します。
models OK
Chat OK
streaming OK
tool calling OK
effective ctx OK
WARN 0
exit code 0
tool callingでは、構造化されたtool_callsが返り、テスト用引数cityへ文字列Tokyoが入ることまで見ます。
contextもmanifestの期待値と比較します。
timeoutについては、「そのモデルは非対応」とまでは断定できません。
ただし、検証としては失敗です。
他の推論処理を止めたり、Ollamaを再起動したりしたうえで、再度確認します。
タイムアウトした実行を「たぶん大丈夫」と完了扱いにはしません。
9. 最後はVS Code GUIを人が確認する
API検証に成功しても、VS CodeのGUI E2E成功とは限りません。
最後に運用機で人が確認します。
まず、テスト専用の空workspaceを作ります。
そこでVS Code Chatを開き、モデルピッカーからローカルOllamaモデルを選択します。
Chatでは短いプロンプトを送ります。
たとえば、
LOCAL-CHAT-OK とだけ返してください。
のような確認で十分です。
ここで見たいのは回答品質ではなく、選択したローカルモデルとの往復が成立していることです。
Agentについても同じです。
ただし、いきなり業務リポジトリを開いて書き換えさせる必要はありません。
確認専用workspaceにhello.txtなどを用意し、
このファイルを読み取り、内容を1行で答えてください。
ファイルは変更しないでください。
のような限定した操作から確認します。
Windows側でファイル作成まで確認する場合も、対象ファイルを明示し、承認UIに表示される変更先を人が確認します。
Agentはファイル変更やコマンド実行につながるため、Workspace Trustも含めて見る必要があります。
まずはここまでで十分です。
10. オフライン設定とネットワーク遮断は分ける
生成キットでは、Ollama側へ、
{
"disable_ollama_cloud": true
}
相当の設定を配置します。
また、VS Code側でも更新系の通信を抑える設定を行います。
ただし、これを「ネットワーク遮断」と呼ぶのは少し違います。
アプリケーション設定は、ファイアウォールやegress policyの代わりにはなりません。
オフライン運用が要件なら、
- 物理的にネットワークへ接続しない
- OS firewallで制御する
- 組織ネットワークでegressを制御する
などを別途実施します。
アプリケーションのlocal-only設定と、ネットワーク境界の制御を同じものとして扱わないほうがよいです。
11. モデル評価も「一度動いた」で決めない
今回の旧Windows実測では、複数の小型モデルを比較しています。
ただし、この値は一台のPC、一つの条件での参考値です。
さらに、日本語タスクについても、簡単な8問では各モデルの形式遵守が96〜100%になり、差を十分に検出できませんでした。
一方、より細かい指示追従ルールでは74〜86%程度となり、モデルによっては「日本語で回答」という条件を安定して守れない例もありました。
ここから私が重視したいのは、特定モデルの順位ではありません。
ローカルの小型モデルは、業務指示を常に100%守る前提には置かないということです。
モデル名やベンチマークだけで決めず、自分たちが実際に使うprompt、出力形式、コード規約で再評価したほうがよいです。
tools/jp-eval/はそのための再評価用ツールとして用意しています。
12. ファイル構成は役割で見る
統合後に押さえておくファイルは、実務上はそれほど多くありません。
local-llm-dev/
├── README.md
├── TUTORIAL.md
├── WINDOWS.md
├── MACOS.md
├── VALIDATION.md
├── templates/
│ ├── README.md
│ ├── chatLanguageModels.sample.json
│ ├── settings.offline.sample.json
│ ├── ollama-server.sample.json
│ └── copilot-instructions.ja.md
└── tools/
├── verify_endpoint.py
├── test_verify_endpoint.py
├── check_fences.py
├── airgap-kit/
│ ├── CONTRACT.md
│ ├── Prepare-Windows.cmd
│ ├── Export-OfflineKit.ps1
│ ├── install-windows.cmd
│ ├── Import-OfflineKit.ps1
│ ├── Prepare-macOS.sh
│ ├── install-macos.sh
│ └── tests/
└── jp-eval/
├── README.md
├── jp_eval.py
├── prompts.json
└── test_jp_eval.py
見る順番としては、
- 全体像と設計境界は
TUTORIAL.md - Windows固有手順は
WINDOWS.md - macOS固有手順は
MACOS.md - 実施済み・未実施の境界は
VALIDATION.md - 固定された実装契約は
tools/airgap-kit/CONTRACT.md - endpoint判定は
tools/verify_endpoint.py
という理解でよいと思います。
<モデル名>のような山括弧はプレースホルダーです。
実値へ置き換えて使います。
ここまでの整理
一度まとめます。
良い点
- オンライン準備とオフライン運用を明確に分離できる
- モデル、runtime、設定、contextを移送キットとして固定できる
- byte数とSHA-256で移送後の完全性を検査できる
- dry-runをApply前の明示的なゲートにできる
- Chatだけでなくtool callingまでAgentの合格条件にできる
- WindowsとmacOSで共通の運用モデルを持てる
- 既存環境を自動上書きしないため、意図しない変更を抑えやすい
注意点
- インライン補完、セマンティック検索、embedding依存機能はこの構成の対象外
-
disable_ollama_cloudだけではネットワーク遮断にならない - 異なる既存設定やruntimeは自動mergeせず停止する
- 別モデルを採用する場合はAgent検証をやり直す
- モデルのライセンス、再配布条件、商用利用条件は実際に固定するモデルごとに確認する
- SHA-256は完全性確認であり、発行者真正性を保証する署名ではない
限界
- Windowsはオンライン準備とキット生成までの実測であり、別のオフライン端末でのE2Eは未完了
- macOSの実機E2Eは未実施
- VS Code GUIでの完全なE2Eも未実施
- 性能値はWindows 1台の参考値
- Foundry Localのtool calling結果は限定された1回の観測
- 日本語評価も小規模であり、モデル一般の優劣を示すものではない
まとめ
ローカルLLMをオフライン環境へ持ち込むこと自体は、それほど特殊な話ではなくなってきました。
ただ、実務で考えると重要なのは「モデルが起動したか」だけではありません。
どの資材を取得したのか。
どのモデルとcontextを検証したのか。
移送後も同じものなのか。
既存環境と競合していないか。
ChatだけでなくAgentのtool callingまで成立しているか。
最後にVS Code上で人が確認したか。
この一連を一つの導入プロセスとして扱う必要があります。
今回の構成では、そのために、
準備機で固定する → manifestで完全性を持たせる → 承認済み媒体で運ぶ → dry-runする → Applyする → endpointを再検証する → GUIを確認する
という順番にしています。
万能ではありません。
特に、インライン補完まで含めた完全なクラウドAI開発体験を、そのままローカルへ置き換える構成ではありません。
一方で、ネットワークを切り離した環境でも、VS Code Chatと条件を満たしたAgentをローカルモデルで使う、という範囲なら現実的な設計になってきます。
まずは既定のqwen3:8b、context 8192で管理下の検証端末を1セット作り、prepareからGUI確認までを一度通す。
その結果を基準に、必要であればモデル、ハードウェア、custom instructions、業務promptを自分たちの環境で測り直す。
実務では、そのくらいの進め方がちょうどよいと考えています。