ノートパソコン上でも、本番環境と同じOpenAI互換エンドポイントの背後で70Bパラメータモデルを動かせます。変更点は基本的にベースURLだけです。base_urlをローカルに差し替えれば、既存のSDK呼び出し、テスト、リクエスト形式を保ったまま、オフライン開発、トークン単価ゼロ、規制データ向けのプライベートな実行パスを用意できます。この記事では、Ollama、vLLM、llama.cppの選び方、OpenAI互換エンドポイントの公開方法、クライアントコードの切り替え方、そしてApidogでローカル環境とホスト型環境を同じシナリオでテストする方法を実装ベースで説明します。
TL;DR
Ollama、vLLM、llama.cppを使うと、ローカルマシン上でOpenAI互換のLLM APIを実行できます。
既存のOpenAIクライアントでは、主に次の差し替えだけで済みます。
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama",
)
これにより、同じアプリケーションコードをLlama 3.3、DeepSeek V4、Qwen 3.6などのローカルモデルに向けられます。Apidogでは、BASE_URLとAPI_KEYを環境変数化しておくことで、ローカル環境と本番環境に対して同じAPIシナリオテストを実行できます。
はじめに
ローカルLLM APIスタックは、研究用途の実験環境から、日常的な開発環境として使える段階に入りました。Apple Siliconの大容量ユニファイドメモリ、Ollamaの普及、vLLMの高スループット化により、開発者はローカルで実用的なLLM APIを立ち上げられるようになっています。
重要なのは、多くのランタイムがOpenAIの/v1/chat/completions形式に対応していることです。つまり、次のように環境変数だけで接続先を切り替えられます。
# local
OPENAI_BASE_URL=http://localhost:11434/v1
OPENAI_API_KEY=ollama
# production
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_API_KEY=sk-...
API開発者にとって、この互換性は大きなメリットです。Apidogで作成済みのOpenAI向けリクエストテンプレートも、ベースURLを変えるだけでローカルLLMに送信できます。新しいスキーマや独自の認証フローを追加する必要はありません。
すでに機能ごとのAPI費用を追跡している場合は、ローカルモデルとホスト型モデルを同じテストケースで比較し、コスト、レイテンシ、レスポンス形式の差分を確認できます。
このガイドでは、次の流れで実装します。
- ランタイムを選ぶ
- OpenAI互換エンドポイントを起動する
- Python / JavaScriptクライアントから接続する
- Apidogでローカルと本番を同じシナリオでテストする
- 量子化、GPUオフロード、ストリーミングなどの実運用ポイントを確認する
より広いモデル選定については、ベストローカルLLM 2026も参考にしてください。
API開発者にとってローカルLLMが有効な理由
ローカルLLM APIは、次のような状況で特に役立ちます。
- 飛行機内や不安定なネットワークでLLM連携コードをデバッグしたい
- 顧客ネットワークから
*.openai.comへの外部通信がブロックされている - 規制対象データを外部APIに送信できない
- LLM出力に依存する回帰テストを安定させたい
- 開発中のトークン課金を抑えたい
プライバシー
HIPAA、GDPR、EU AI法などの文脈では、プロンプトに患者記録、契約、取引情報、生体認証情報などが含まれると、送信先APIとのデータ処理関係が問題になります。
ローカルモデルであれば、プロンプトとレスポンスは自分のハードウェアや管理ネットワーク内に留まります。これは、規制データを使った開発や検証で大きな利点になります。
コスト
ホスト型APIでは、トークン量に応じて継続的な費用が発生します。たとえば、1日あたり大量のプロンプトトークンを処理するチームでは、開発・検証だけでもコストが積み上がります。
一方、ローカル実行ではハードウェア費用と電力コストが中心になります。詳細な費用計算の考え方は、GPT-5.5 Instantの利用方法のようなAPI費用の内訳記事と同じ要領で、自身のワークロードに適用できます。
決定論と回帰テスト
ホスト型モデルは、提供側の都合でスナップショットが更新または廃止されることがあります。ローカルモデルはディスク上のモデルファイルとして固定できます。
LLM出力を使うテストスイートでは、次のような安定性が重要です。
- 同じモデルファイル
- 同じ量子化
- 同じシステムプロンプト
- 同じ温度
- 同じコンテキスト長
OpenAI互換エンドポイントを使えば、こうした安定したローカル実行を、既存SDKのまま取り込めます。
OpenAI互換エンドポイントを提供する主要ランタイム
ローカルLLM APIを実行する代表的な選択肢は、Ollama、vLLM、llama.cppです。用途で選びます。
| ランタイム | 向いている用途 | 既定ポート | 特徴 |
|---|---|---|---|
| Ollama | 個人開発、デモ、CI | 11434 | 導入が簡単 |
| vLLM | 共有開発環境、本番寄り検証 | 8000 | 高スループット、連続バッチ処理 |
| llama.cpp | メモリ制約環境、特殊ハードウェア | 任意 | GGUF、細かい制御 |
OllamaでローカルLLM APIを起動する
Ollamaは最も簡単に始められる選択肢です。1つのCLIでモデル取得、実行、OpenAI互換APIの公開まで処理できます。
macOSでは次のように起動します。
brew install ollama
ollama serve &
ollama pull llama3.3:70b-instruct-q4_K_M
ollama run llama3.3:70b-instruct-q4_K_M
ollama serveが起動すると、OpenAI互換エンドポイントは次のURLで利用できます。
http://localhost:11434/v1
簡単な疎通確認はcurlで行えます。
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ollama" \
-d '{
"model": "llama3.3:70b-instruct-q4_K_M",
"messages": [
{ "role": "user", "content": "Reply with OK only." }
]
}'
Ollamaは、単一ユーザーの開発、ローカルデモ、CIランナーでのスモークテストに向いています。
vLLMでOpenAI互換サーバーを起動する
vLLMは、高スループットが必要な共有環境向けです。PagedAttentionと連続バッチ処理により、複数リクエストを効率よく処理できます。
起動例:
pip install vllm
vllm serve meta-llama/Llama-3.3-70B-Instruct \
--port 8000 \
--gpu-memory-utilization 0.9 \
--max-model-len 8192
OpenAI互換エンドポイントは次の形式になります。
http://localhost:8000/v1
Python SDKからは次のように呼び出せます。
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="dummy",
)
resp = client.chat.completions.create(
model="meta-llama/Llama-3.3-70B-Instruct",
messages=[
{"role": "user", "content": "Reply with OK only."}
],
)
print(resp.choices[0].message.content)
vLLMはCUDA GPUまたは対応するAMD ROCm環境が必要です。Apple Silicon上のローカル開発よりも、GPUサーバーや共有開発クラスタで使うほうが適しています。
llama.cppでOpenAI互換APIを公開する
llama.cppはGGUFモデルを扱うC++ランタイムです。軽量で制御性が高く、Raspberry PiからGPU搭載ワークステーションまで幅広く動きます。
ビルドと起動例:
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp && make -j LLAMA_METAL=1
./llama-server \
-m models/llama-3.3-70b-q4_k_m.gguf \
--port 8080 \
--host 0.0.0.0 \
-c 8192 \
-ngl 99
-ngl 99は、可能な限り多くのレイヤーをGPUへオフロードする指定です。VRAMに制約がある場合は、この値を下げてCPU側に一部を逃がします。
OpenAI互換エンドポイントは次のようになります。
http://localhost:8080/v1
llama.cppは、次のような場合に向いています。
- 16GBや24GBなど限られたVRAMにモデルを収めたい
- 量子化形式やコンテキスト長を細かく制御したい
- 特殊なハードウェアでローカルLLMを動かしたい
LM StudioやJanは、llama.cppをGUIで扱いやすくしたツールです。ターミナルを使わずにローカルモデルを試したいチームメンバーには有効です。
Python SDKでローカルLLMに接続する
OpenAI Python SDKでは、base_urlを差し替えるだけでローカルエンドポイントに接続できます。
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama",
)
resp = client.chat.completions.create(
model="llama3.3:70b-instruct-q4_K_M",
messages=[
{"role": "user", "content": "Reply with the word OK only."}
],
)
print(resp.choices[0].message.content)
OKが返れば、次の3点が揃っています。
- ランタイムが起動している
- ポートが正しい
- OpenAI SDKの期待するレスポンス形式に対応している
実アプリでは、接続先を環境変数で切り替えます。
import os
from openai import OpenAI
def get_client():
if os.getenv("ENV") == "local":
return OpenAI(
base_url=os.getenv("OPENAI_BASE_URL", "http://localhost:11434/v1"),
api_key=os.getenv("OPENAI_API_KEY", "ollama"),
)
return OpenAI(
base_url=os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1"),
api_key=os.environ["OPENAI_API_KEY"],
)
client = get_client()
response = client.chat.completions.create(
model=os.getenv("MODEL", "llama3.3:70b-instruct-q4_K_M"),
messages=[
{"role": "system", "content": "You are a JSON-only assistant."},
{"role": "user", "content": "Return {\"status\": \"ok\"}."},
],
response_format={"type": "json_object"},
)
print(response.choices[0].message.content)
JavaScript SDKでローカルLLMに接続する
JavaScriptでも考え方は同じです。
import OpenAI from "openai";
const client = new OpenAI({
baseURL:
process.env.ENV === "local"
? "http://localhost:11434/v1"
: "https://api.openai.com/v1",
apiKey:
process.env.ENV === "local"
? "ollama"
: process.env.OPENAI_API_KEY,
});
const resp = await client.chat.completions.create({
model: process.env.MODEL || "llama3.3:70b-instruct-q4_K_M",
messages: [
{ role: "user", content: "Say hi." }
],
});
console.log(resp.choices[0].message.content);
本番コードでは、localhostを直接書かず、次のように環境変数へ寄せるほうが安全です。
import OpenAI from "openai";
const client = new OpenAI({
baseURL: process.env.OPENAI_BASE_URL,
apiKey: process.env.OPENAI_API_KEY,
});
.env.local:
OPENAI_BASE_URL=http://localhost:11434/v1
OPENAI_API_KEY=ollama
MODEL=llama3.3:70b-instruct-q4_K_M
.env.production:
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_API_KEY=sk-...
MODEL=gpt-...
ApidogでローカルLLMをテストする
ローカルLLM APIは、本番APIと同じテストスイートで検証できると実用性が上がります。Apidogでは、環境変数を使ってローカルと本番を切り替えられます。
1. Local環境を作成する
ApidogプロジェクトでLocal環境を作成し、次の変数を追加します。
| 変数 | 値 |
|---|---|
BASE_URL |
http://localhost:11434/v1 |
API_KEY |
ollama |
MODEL |
llama3.3:70b-instruct-q4_K_M |
2. Production環境を作成する
既存のOpenAI環境を複製し、Productionにします。
| 変数 | 値 |
|---|---|
BASE_URL |
https://api.openai.com/v1 |
API_KEY |
実際のAPIキー |
MODEL |
本番で使うモデル名 |
3. リクエストURLを変数化する
チャット補完リクエストのURLを次のようにします。
{{BASE_URL}}/chat/completions
ヘッダー:
Authorization: Bearer {{API_KEY}}
Content-Type: application/json
ボディ例:
{
"model": "{{MODEL}}",
"messages": [
{
"role": "system",
"content": "You are a concise assistant."
},
{
"role": "user",
"content": "Reply with OK only."
}
],
"temperature": 0
}
4. アサーションを追加する
Apidogのシナリオテストで、最低限次を検証します。
status code == 200
choices[0].message.role == "assistant"
choices[0].message.content is not empty
usage.total_tokens > 0
これにより、ローカルモデルと本番モデルの両方で、OpenAI互換レスポンスの基本契約を確認できます。
5. LocalとProductionで同じシナリオを実行する
手順は次の通りです。
- 環境を
Localに切り替える - シナリオを実行する
- アサーションが通ることを確認する
- 環境を
Productionに切り替える - 同じシナリオを再実行する
この構成にしておくと、ollama pullでモデルタグを更新した後のスモークテストにも使えます。レスポンス形式が変わった場合、アプリケーションコードに影響が出る前に検知できます。
同じ考え方は、複数ステップAPIを呼び出すAIエージェントのテストにも適用できます。
ApidogシナリオをCIに組み込む
ApidogのシナリオをCLIで実行すれば、ローカルLLMやホスト型LLMの契約テストをCIに組み込めます。
基本パターンは次の通りです。
apidog run \
--project ./apidog-project.json \
--env Local
GitHub Actionsでは、たとえば次のように実行できます。
name: LLM API contract test
on:
pull_request:
push:
branches: [main]
jobs:
test-local-llm:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Start Ollama
run: |
ollama serve &
sleep 5
ollama pull llama3.3:70b-instruct-q4_K_M
- name: Run Apidog scenario
run: |
apidog run \
--project ./apidog-project.json \
--env Local
アサーションが失敗すると、ランナーはゼロ以外の終了コードを返します。これにより、ローカルまたはホスト型のAPI契約が崩れたタイミングでビルドを止められます。
QA向けの既存フローに組み込む場合は、APIテストパイプラインと同じ考え方で扱えます。
量子化の選び方
70Bモデルをノートパソコンに収めるには、量子化が重要です。GGUFでは、重みを8bit、6bit、5bit、4bit、3bit、2bitなどで保存できます。
実用上の目安は次の通りです。
| 量子化 | 用途 | 特徴 |
|---|---|---|
| Q8 | コード生成、品質重視 | 精度は高いがメモリ使用量が大きい |
| Q5_K_M | バランス重視 | 品質とサイズの中間 |
| Q4_K_M | チャット、一般用途 | 70Bを現実的なサイズに圧縮 |
| Q2_K | 極端な省メモリ | 品質低下が目立ちやすい |
多くのローカル開発では、まずQ4_K_Mから始めるのが現実的です。コード生成や推論品質を重視する場合は、メモリに余裕があればQ8またはQ5_K_Mを検討します。
GPUオフロードの調整
llama.cppでは-ngl、Ollamaではnum_gpuのような設定で、どの程度GPUへレイヤーを載せるかを制御できます。
基本方針はシンプルです。
VRAMが許す限り、GPUに多く載せる
CPUに戻るレイヤーが増えるほど、生成速度は下がります。たとえば、24GB VRAMでは70B Q4モデルの全レイヤーを載せられない場合があります。48GB以上あると、より多くのレイヤーをGPU上で処理できます。
llama.cppの例:
./llama-server \
-m models/llama-3.3-70b-q4_k_m.gguf \
-c 8192 \
-ngl 40
VRAMに余裕がある場合:
./llama-server \
-m models/llama-3.3-70b-q4_k_m.gguf \
-c 8192 \
-ngl 99
mmapは基本的に有効のままでよい
llama.cppやOllamaでは、メモリマッピングがデフォルトで有効になっています。これにより、モデル全体を起動時に一括でRAMへ読み込むのではなく、必要に応じてページインできます。
通常は有効のままで問題ありません。
無効化を検討するのは、次のような特殊なケースです。
- 厳密なレイテンシ測定をしている
- 十分なRAMがある
- コンテナのメモリ制限を明確に制御している
vLLMではバッチ処理を活用する
vLLMの強みは、複数の同時リクエストを効率よくバッチ化できる点です。
共有開発環境では、次のような設定を検討します。
vllm serve meta-llama/Llama-3.3-70B-Instruct \
--port 8000 \
--gpu-memory-utilization 0.9 \
--max-model-len 8192 \
--max-num-seqs 64
H100クラスのGPUでは、より大きな同時シーケンス数を設定できます。
vllm serve meta-llama/Llama-3.3-70B-Instruct \
--port 8000 \
--gpu-memory-utilization 0.9 \
--max-model-len 8192 \
--max-num-seqs 256
単発の開発リクエストでは効果が見えにくいですが、複数ユーザーやCIジョブが同時に叩く環境では差が出ます。
ストリーミングで体感レイテンシを下げる
OpenAI SDKでは、stream=Trueを指定するとトークンを逐次受け取れます。
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama",
)
stream = client.chat.completions.create(
model="llama3.3:70b-instruct-q4_K_M",
messages=[
{"role": "user", "content": "Explain local LLM APIs briefly."}
],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="")
完全なレスポンスを待つのではなく、生成されたトークンから順に表示できるため、ユーザー体験が改善します。
Ollama Modelfileで設定を固定する
Ollamaでは、Modelfileを使ってシステムプロンプトや温度をモデル名に埋め込めます。
Modelfile例:
FROM llama3.3:70b-instruct-q4_K_M
SYSTEM """
You are a concise API assistant.
Always answer in Japanese.
"""
PARAMETER temperature 0
PARAMETER num_ctx 8192
作成:
ollama create my-api-assistant -f Modelfile
呼び出し側では、毎回システムプロンプトを送らずに、モデル名だけを指定できます。
resp = client.chat.completions.create(
model="my-api-assistant",
messages=[
{"role": "user", "content": "OpenAI互換APIとは何ですか?"}
],
)
プロンプト設定をアプリケーションコードから分離できるため、テストやレビューがしやすくなります。
よくあるミス
ローカルLLM APIを導入するときは、次の点に注意してください。
- 本番コードに
http://localhost:11434をハードコードする -
BASE_URLとAPI_KEYを環境変数化しない -
max_tokensや停止条件を設定せず、不要に長い出力を許す - Ollama、vLLM、llama.cppを同じポートで起動する
-
Authorizationヘッダーを省略する - Q4モデルにホスト型最上位モデルと同じ推論品質を期待する
- ローカルでしか通らないレスポンス形式にアプリを依存させる
最低限、次のような設定に統一しておくと安全です。
OPENAI_BASE_URL=http://localhost:11434/v1
OPENAI_API_KEY=ollama
OPENAI_MODEL=llama3.3:70b-instruct-q4_K_M
アプリ側:
client = OpenAI(
base_url=os.environ["OPENAI_BASE_URL"],
api_key=os.environ["OPENAI_API_KEY"],
)
ローカル vs ホスト型: コストとレイテンシ
以下は、ローカル用に128GBユニファイドメモリ搭載M3 Max、ホスト型用に公開価格のエンドポイントを想定した比較です。TTFTは最初のトークンまでの時間です。
| モデル | ローカルTTFT | ローカルスループット | ホスト型同等モデル | ホスト型価格 | ホスト型TTFT |
|---|---|---|---|---|---|
| Llama 3.3 70B Q4_K_M | 1.2 秒 | 12 tok/秒 | GPT-5.5 Instant | 1Mあたり$5 / $30 | 200 ミリ秒 |
| DeepSeek V4 67B Q4_K_M | 1.4 秒 | 10 tok/秒 | DeepSeek-Chat hosted | 1Mあたり$0.55 / $2.20 | 280 ミリ秒 |
| Qwen 3.6 32B Q5_K_M | 0.7 秒 | 28 tok/秒 | Qwen-Max hosted | 1Mあたり$1.60 / $6.40 | 240 ミリ秒 |
| Gemma 4 27B Q4_K_M | 0.5 秒 | 35 tok/秒 | Gemini 3 Flash | 1Mあたり$0.35 / $1.05 | 180 ミリ秒 |
レイテンシでは、多くの場合ホスト型が有利です。一方で、ローカルは次の条件で有利になります。
- 開発中のトークン費用を抑えたい
- 規制データを外に出せない
- オフライン環境で開発したい
- モデルバージョンを固定したい
- CIで大量のLLMテストを回したい
実用的な運用パターンは次の通りです。
- 開発中はローカルLLMを使う
- ステージングではホスト型に切り替える
- CIではローカルとホスト型の両方に対して契約テストを実行する
- 本番ではレイテンシ、コスト、データ分類に応じて選ぶ
DeepSeek V4の詳細は、DeepSeek V4をローカルで実行する方法とDeepSeek V4使用ガイドも参考になります。
実世界のユースケース
規制データを扱うフィンテックチーム
シンガポールのフィンテックコンプライアンスチームは、エンジニアのノートパソコン上でOllamaを使い、不審な活動報告書のドラフトを生成しています。
プロンプトには、国外に出せない口座番号や取引パターンが含まれます。そのため、開発中はローカルLLMを使い、本番では編集済みデータだけをホスト型エンドポイントへ送信します。
Apidogのシナリオでは、リダクション処理がすべてのリクエストで実行されていることを検証します。
ゲームスタジオのプロンプト学習環境
ストックホルムのゲームスタジオでは、ローカルのQwen 3.6インスタンスを使って、デザイン研修生にプロンプトエンジニアリングを教えています。
メリットは次の通りです。
- 無料で練習できる
- オフラインで動く
- 未公開ゲームの設定やストーリーが外部に送信されない
本番では、環境変数を切り替えてGemini 3 Flashに接続します。本番接続の実装には、Gemini 3 Flash APIガイドを再利用できます。
病院ネットワーク内のvLLM
あるヘルスケアスタートアップは、顧客の病院ネットワーク内でリースされたA100上にvLLMを配置しています。エンドポイントは公開DNSに出さず、同じVLAN内のJenkinsエージェントから統合テストを実行します。
アプリケーションコードはOpenAI SDKのままです。
local laptop -> Ollama
hospital network -> vLLM
production -> hosted API
接続先は違っても、リクエスト形式とテストシナリオは同じにできます。
まとめ
ローカルLLM APIは、既存のOpenAI互換クライアント、テスト、CIを維持したまま導入できます。実装の流れは次の5ステップです。
- 個人開発ならOllama、共有GPU環境ならvLLM、細かい制御が必要ならllama.cppを選ぶ
- OpenAI互換エンドポイントを起動する
-
base_urlとapi_keyを環境変数化する - Apidogでローカルと本番に同じシナリオテストを実行する
- コスト、レイテンシ、データ分類に応じて実行先を切り替える
最初にやるべきことはシンプルです。
ollama serve &
ollama pull llama3.3:70b-instruct-q4_K_M
次に、ApidogのBASE_URLを次へ向けます。
http://localhost:11434/v1
これで、ローカルLLMを本番APIと同じインターフェースで検証できます。モデル選定から始める場合は、ベストローカルLLM 2026を確認してください。エージェントフローのテストまで進める場合は、AIエージェントAPIのテスト方法が参考になります。




Top comments (0)