Ollama: OpenAI互換API
OpenAI互換APIは移行の架け橋——2行のコードを変更するだけで、クラウドからローカルへ。
💡 ヒント: OllamaのOpenAI互換APIで移行は極めてシンプル——
base_urlをhttp://localhost:11434/v1に変更し、api_keyに任意の空でない文字列(例:"ollama")を設定し、モデル名をローカルモデル(例:"qwen2.5")に変更するだけ。既存のopenaiライブラリコードをすべて再利用できる。つまり、既存のChatGPTアプリケーション、LangChainプロジェクト、AutoGenワークフローがコード変更ゼロでローカルに切り替え可能。
📋 前提条件: まず以下を習得していること
- レッスン5: REST APIの基礎
1. 学べること
/v1/chat/completionsと/v1/embeddingsエンドポイントのマッピング- openai PythonライブラリのOllamaへの切り替え
- 互換性の差異と機能制限
- 実践移行:ChatGPTアプリケーションのOllamaバックエンドへの変換
- Aliceの事例:月2,000ドルの節約
2. SaaS創業者のリアルな事例
⚠️ 警告: OllamaのOpenAI互換APIは100%完全な実装ではない——Function Calling(Tools)、ストリーミングtool_calls、Assistants APIなどは非対応。移行前に、アプリケーションがこれらの機能に依存していないか必ず確認すること;依存している場合は、ReActエージェントに切り替えるか、
/api/chatエンドポイントを直接呼び出す代替手段が必要。
ℹ️ 情報:
api_key="ollama"はOllama互換エンドポイントのプレースホルダー規約——OllamaはAPI Keyの内容を検証しない。つまりapi_keyは任意の文字列(例:"sk-1234"、"dummy")で機能は同一。本物の認証はリバースプロキシ層で実装する必要がある。
(1) ペインポイント:GPT-4の月額2,000ドル
AliceのSupportBotはGPT-4 APIを使用し、月に500万トークンを処理して請求額は2,000ドル。会社からコスト削減を求められているが、移行には大量のコード書き換えが必要——すべての呼び出しがopenai Pythonライブラリに依存している。
(2) ソリューション:2行のコードでローカルに切り替え
OpenAI互換APIにより、Aliceはbase_urlとapi_keyを変更するだけで、5分で移行完了:
PYTHON
from openai import OpenAI
# Before: OpenAI cloud
# client = OpenAI(api_key="sk-xxx")
# After: Ollama local (only 2 lines changed!)
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
3. 互換エンドポイントマッピング
💡 ヒント:
api_key="ollama"はOllama互換APIのプレースホルダー規約——OllamaはAPI Keyを検証しない。つまりOllamaのアドレスを知っていれば誰でも呼び出せる。本番環境では、Nginxなどのリバースプロキシで本当のKey認証を必ず追加すること。
(1) APIエンドポイント一覧
| OpenAIエンドポイント | Ollama互換エンドポイント | 状態 |
|---|---|---|
/v1/chat/completions |
✅ 完全互換 | 主要用途 |
/v1/completions |
✅ 互換 | 従来の補完 |
/v1/embeddings |
✅ 互換 | ベクトル埋め込み |
/v1/models |
✅ 互換 | モデル一覧 |
/v1/images/generations |
❌ 非対応 | 画像生成 |
/v1/audio/transcriptions |
❌ 非対応 | 音声書き起こし |
flowchart LR
A[OpenAI SDK] -->|base_url変更| B[Ollama /v1/...]
B --> C[/v1/chat/completions]
B --> D[/v1/completions]
B --> E[/v1/embeddings]
B --> F[/v1/models]
B --> G[/v1/images ❌]
(2) 互換性の差異詳細
| 機能 | OpenAI | Ollama互換 | 差異 |
|---|---|---|---|
| ストリーミング | SSE形式 | ✅ SSE互換 | 一致 |
| Function Calling | 完全対応 | ⚠️ 部分対応 | 一部モデルが対応 |
| JSONモード | response_format | ✅ format=json | 一致 |
| Embeddings | text-embedding-3 | ✅ nomic-embed-textを使用 | モデルが異なる |
| Vision | gpt-4o vision | ✅ llavaを使用 | モデルが異なる |
| ファインチューニング | 対応 | ❌ | Modelfileで代替 |
| レート制限 | RPM/TPM | ❌ | 外部実装が必要 |
(3) ▶ サンプル:openaiライブラリ基本切替
PYTHON
from openai import OpenAI
# Point to local Ollama server
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama" # Any non-empty string works
)
# Chat completion (identical to OpenAI API usage)
response = client.chat.completions.create(
model="qwen2.5",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain RAG in 2 sentences"}
],
temperature=0.3
)
print(response.choices[0].message.content)
出力:
TEXT
# Execution successful
4. コア機能移行
(1) 機能移行一覧
| 機能 | OpenAIコード | Ollama変更 | 工数 |
|---|---|---|---|
| Chat | model="gpt-4" |
model="qwen2.5" |
モデル名変更 |
| ストリーミング | stream=True |
変更不要 | ゼロ変更 |
| Embeddings | model="text-embedding-3-small" |
model="nomic-embed-text" |
モデル名変更 |
| JSONモード | response_format={"type": "json_object"} |
同じ | 変更不要 |
| システムプロンプト | messages=[{"role":"system"...}] |
同じ | ゼロ変更 |
| Function Calling | tools=[...] |
⚠️ 一部モデルのみ対応 | テスト必要 |
(2) ▶ サンプル:ストリーミング出力移行
PYTHON
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama"
)
# Streaming works identically
stream = client.chat.completions.create(
model="qwen2.5",
messages=[{"role": "user", "content": "Write a short poem about AI"}],
stream=True,
temperature=0.7
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
print()
出力:
TEXT
# Execution successful
(3) ▶ サンプル:Embeddings移行
PYTHON
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama"
)
# Embeddings: just change the model name
response = client.embeddings.create(
model="nomic-embed-text", # Was: text-embedding-3-small
input="What is the return policy for electronics?"
)
print(f"Embedding dimension: {len(response.data[0].embedding)}")
# 768 dimensions for nomic-embed-text
出力:
TEXT
# Execution successful
(4) ▶ サンプル:JSONモード移行
PYTHON
from openai import OpenAI
import json
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama"
)
# JSON mode: identical API
response = client.chat.completions.create(
model="qwen2.5",
messages=[
{"role": "system", "content": "You are a product catalog API."},
{"role": "user", "content": "List 3 laptops under $500"}
],
response_format={"type": "json_object"},
temperature=0.3
)
data = json.loads(response.choices[0].message.content)
print(json.dumps(data, indent=2))
出力:
TEXT
# Execution successful
5. 実践移行と互換性制限
⚠️ 注: OllamaのOpenAI互換APIは100%完全な実装ではない——Function Calling(Tools)は一部モデルのみ部分対応で、GPT-4より安定性が低い。Assistants API、ファインチューニングAPI、Batch APIは非対応。移行前にアプリケーションがこれらの機能に依存していないか必ず確認;依存している場合は、JSONモード+プロンプトでFunction Callingをシミュレートするか、
/api/chatエンドポイントを直接呼び出す代替手段が必要。
(1) 移行チェックリスト
| チェック項目 | 説明 | リスク |
|---|---|---|
| モデル名 | gpt-4 → qwen2.5/llama3.1 | 品質評価が必要 |
| Function Calling | 正常動作するかテスト | 部分的に失敗する可能性 |
| 最大コンテキスト | gpt-4: 128K → ローカルモデルによる | num_ctxに注意 |
| 出力トークン制限 | max_tokensパラメータ | テスト必要 |
| レート制限 | OpenAI RPM → ローカルは制限なし | 自前で構築必要 |
| 同時実行 | OpenAI高同時 → ローカルは限定 | スケーリング必要 |
(2) 非互換機能の代替手段
| OpenAI機能 | Ollama代替 | 実装方法 |
|---|---|---|
| ファインチューニング | Modelfile | レッスン8で詳細解説 |
| Function Calling | プロンプト+JSONパース | 手動実装 |
| 画像生成 | llava(理解のみ) | 生成は非対応 |
| Batch API | Shell/Pythonスクリプト | 自前オーケストレーション |
| Assistants API | LangChainエージェント | レッスン13で詳細解説 |
(3) ▶ サンプル:Function Calling代替実装
PYTHON
from openai import OpenAI
import json
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama"
)
# Instead of Function Calling, use JSON mode + prompt
tools = {
"get_order_status": {"order_id": "string"},
"process_refund": {"order_id": "string", "amount": "number"},
"search_products": {"query": "string", "category": "string"}
}
response = client.chat.completions.create(
model="qwen2.5",
messages=[
{"role": "system", "content": f"""You are a customer service bot.
Available tools: {json.dumps(tools)}
If you need to call a tool, respond with JSON: {{"tool": "name", "args": {{...}}}}
Otherwise, respond normally."""},
{"role": "user", "content": "Where is my order #12345?"}
],
response_format={"type": "json_object"},
temperature=0.2
)
result = json.loads(response.choices[0].message.content)
if "tool" in result:
print(f"Call tool: {result['tool']} with args: {result['args']}")
# Call: get_order_status with {"order_id": "12345"}
else:
print("Direct response:", result)
出力:
TEXT
Direct response:
6. 総合サンプル:SupportBot GPT-4移行
PYTHON
# ============================================
# Comprehensive: SupportBot migration
# From GPT-4 to local Ollama with OpenAI SDK
# ============================================
from openai import OpenAI
import json
from dataclasses import dataclass
from typing import Optional
@dataclass
class SupportBotMigrator:
"""SupportBot with easy OpenAI/Ollama switching."""
# Change these 2 lines to switch between cloud and local
base_url: str = "http://localhost:11434/v1"
api_key: str = "ollama"
model: str = "qwen2.5"
temperature: float = 0.4
def __post_init__(self):
self.client = OpenAI(
base_url=self.base_url,
api_key=self.api_key
)
self.system_prompt = (
"You are SupportBot, an e-commerce customer service agent. "
"Be polite, concise, and helpful. "
"For order queries, ask for order number. "
"If unsure, say 'Let me connect you with a human agent.'"
)
self.messages = [
{"role": "system", "content": self.system_prompt}
]
def chat(self, user_input: str) -> str:
self.messages.append({"role": "user", "content": user_input})
try:
response = self.client.chat.completions.create(
model=self.model,
messages=self.messages[-10:], # sliding window
temperature=self.temperature
)
reply = response.choices[0].message.content
self.messages.append({"role": "assistant", "content": reply})
return reply
except Exception as e:
self.messages.pop()
return f"Error: {e}"
def classify_intent(self, user_input: str) -> dict:
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": """Classify the customer intent.
Return JSON: {"intent": "order_status|refund|product_query|shipping|other", "confidence": 0.0-1.0}"""},
{"role": "user", "content": user_input}
],
response_format={"type": "json_object"},
temperature=0.1
)
return json.loads(response.choices[0].message.content)
def generate_embedding(self, text: str) -> list[float]:
response = self.client.embeddings.create(
model="nomic-embed-text",
input=text
)
return response.data[0].embedding
# Usage - compare cloud vs local
if __name__ == "__main__":
# Local Ollama (current config)
bot = SupportBotMigrator()
# Switch to OpenAI cloud (uncomment to use)
# bot = SupportBotMigrator(
# base_url="https://api.openai.com/v1",
# api_key="sk-your-key",
# model="gpt-4"
# )
queries = [
"Where is my order #88765?",
"I want a refund for damaged goods",
"Does this laptop have HDMI port?"
]
for q in queries:
intent = bot.classify_intent(q)
reply = bot.chat(q)
print(f"Q: {q}")
print(f"Intent: {intent}")
print(f"A: {reply[:100]}...")
print()
❓ よくある質問
Q api_keyには何を入力する?
A OllamaはAPI Keyを検証しない——任意の空でない文字列が機能する(例:"ollama")。ただし、指定しないとopenaiライブラリがエラーを出すので必ず入力すること。
Q Function Callingは使える?
A 一部モデル(qwen2.5、llama3.1など)で部分対応だが、GPT-4より安定性が低い。JSONモード+プロンプトでシミュレートする方が制御しやすいことを推奨。
Q 移行後に品質が低下したら?
A ハイブリッド戦略を使用——単純な質問はローカル8Bモデル、複雑な質問はGPT-4にフォールバック。8Bモデルで80%のシナリオをカバーすればコスト削減は十分。
Q 異なる埋め込み次元数はRAGに影響する?
A はい。nomic-embed-textは768次元、OpenAI text-embedding-3-smallは1536次元を出力。RAGシステムの移行にはベクトルインデックスの再構築が必要。
Q OpenAIとOllamaを同時に使うには?
A 2つのクライアントをインスタンス化——1つはOpenAI、1つはOllamaを指す。複雑さや予算に応じてリクエストを異なるクライアントにルーティング。
Q Ollamaの/v1と/apiエンドポイントに性能差はある?
A ない。/v1エンドポイントは/apiエンドポイントの互換レイヤーで、同じ推論エンジンを呼び出す。性能は同一。
📖 まとめ
- Ollamaは/v1/chat/completionsなどのOpenAI互換エンドポイントを提供、Chat/Embeddings/Modelsをカバー
- 移行に必要なのはbase_urlとapi_keyの変更のみ——2行のコード
- Function Callingは部分対応;JSONモードによる代替を推奨
- 埋め込みモデルが異なる;RAG移行にはベクトルインデックスの再構築が必要
- ハイブリッド戦略:80%ローカル + 20%クラウドで最大のコスト効率
- Aliceは月2,000ドルを節約、5分で移行完了
📝 練習問題
- 基本(難易度 ⭐):openai PythonライブラリでOllamaに接続し、chat.completions呼び出しを完了して互換性を検証する。
- 中級(難易度 ⭐⭐):既存のOpenAIスクリプト(ストリーミング出力とJSONモード付き)をOllamaに移行し、必要な変更点を文書化する。
- 上級(難易度 ⭐⭐⭐):質問の複雑さに基づいてローカルOllamaまたはクラウドGPT-4に自動ルーティングするデュアルバックエンドルーターを実装し、コスト削減を追跡する。