テーマを切り替える

Ollama API 呼び出し:curl から OpenAI SDK 互換インターフェースまで

ターミナルのあの curl コマンド、返ってきた JSON には単語が半分しか入っていませんでした。2時間も格闘して、ようやくストリーミング応答が原因だと気づいたのです。Ollama はデフォルトで、流れるように少しずつ内容を吐き出します。各 JSON オブジェクトには数文字しか含まれていません。

ローカルで LLM を動かすという点で、Ollama はハードルを大きく下げてくれました。ダウンロード、インストール、起動の3ステップで完了です。ただ、API を呼び出すところは少しややこしい。ネイティブ REST API と OpenAI SDK 互換インターフェースの違いは何か。ストリーミング応答はどう処理するのか。

この記事では、私がハマった落とし穴を整理します。curl コマンドから OpenAI SDK へのコード変更ゼロの移行まで、そしてドキュメントに明記されていない細かいポイントも紹介します。


Ollama API には2種類のインターフェースがある

いやあ、ここは本当にしばらく混乱しました。Ollama は実は、まったく異なる2セットの API インターフェースを提供しています。

ネイティブ REST APIhttp://localhost:11434/api/*

  • エンドポイント:/api/generate(テキスト生成)、/api/chat(対話)、/api/tags(モデル一覧)
  • デフォルトでストリーミング応答(これが私が深夜3時に遭遇した問題です)
  • 直接 HTTP 呼び出しで、SDK は一切不要

OpenAI 互換インターフェースhttp://localhost:11434/v1/*

  • エンドポイント:/v1/chat/completions/v1/completions/v1/models
  • OpenAI SDK と完全互換(Python も JavaScript もそのまま使える)
  • 既存の OpenAI ツールエコシステムに対応

なぜ2セットあるのか、と思うかもしれません。実はそれぞれに使いどころがあります。ネイティブ API はより軽量で直接的、自分で HTTP クライアントを書く場合に向いています。OpenAI 互換インターフェースは、既存の OpenAI SDK コードをそのまま使えます。修正すら不要で、base_url を変えるだけです。

正直なところ、この設計はなかなか賢いと思います。シンプルに呼び出したい開発者にも、すでに OpenAI エコシステムのコードを持つチームにも配慮しています。


ネイティブ REST API:curl から始める

まずはネイティブ API から。ここは実はかなり直接的で、標準的な REST インターフェースです。

基本的な curl 呼び出し

最もシンプルな例、テキスト生成です:

curl http://localhost:11434/api/generate -d '{
  "model": "llama3.2",
  "prompt": "Why is the sky blue?",
  "stream": false
}'

そう、あの stream: false に注目してください。デフォルトでは Ollama は内容をストリーミングで返します。完全な JSON 応答が欲しければ、明示的にストリーミングを無効化する必要があります。そうしないと、こんな出力がずらりと並びます:

{"model":"llama3.2","response":"That","done":false}
{"model":"llama3.2","response":"'","done":false}
{"model":"llama3.2","response":"s","done":false}
{"model":"llama3.2","response":" a","done":false}
...
{"model":"llama3.2","response":"!","done":true}

各 JSON オブジェクトには数文字しか含まれず、トークンを1つずつ出力します。これがいわゆる NDJSON(Newline-Delimited JSON)形式です。1行に1つの JSON オブジェクトが入ります。深夜3時のあのときは、これに気づかず普通の JSON としてパースしてしまい、結局最初のオブジェクトの “That” しか取れませんでした。

対話モードのほうが実用的

単発の生成はシンプルなタスク向きですが、本当によく使うのは対話モードです:

curl http://localhost:11434/api/chat -d '{
  "model": "llama3.2",
  "messages": [
    { "role": "user", "content": "Hello!" }
  ],
  "stream": false
}'

messages 配列を維持して、過去の会話をすべて渡せば、モデルは文脈を覚えていられます。これはチャットアプリを作るうえで特に重要です。

インストール済みのモデルを確認する

ローカルにどんなモデルがあるか見たいときもあります:

curl http://localhost:11434/api/tags

返ってくる JSON には、ダウンロード済みのすべてのモデルが、サイズ・更新時刻・量子化レベルといった情報とともに一覧表示されます。なかなか便利です。


ストリーミング応答の処理

ここは専用に取り上げる必要があります。Ollama のストリーミング応答には特徴があります。一度に完全な内容を返すのではなく、トークンを1つずつ出力するのです。

Python でストリーミングを処理する

Python の requests ライブラリでストリーミング応答を処理します:

import requests
import json

url = "http://localhost:11434/api/chat"
payload = {
    "model": "llama3.2",
    "messages": [{"role": "user", "content": "Write a short poem"}],
    "stream": True
}

response = requests.post(url, json=payload, stream=True)
for line in response.iter_lines():
    if line:
        chunk = json.loads(line)
        print(chunk.get("message", {}).get("content", ""), end="", flush=True)

ポイントは response.iter_lines() です。これで NDJSON ストリームを1行ずつ読めます。各チャンクには数文字しか入っていないことがあるので、蓄積しないと完全な応答にはなりません。

JavaScript のストリーミング処理

フロントエンドで fetch API を使う場合も同様です:

const response = await fetch('http://localhost:11434/api/chat', {
  method: 'POST',
  body: JSON.stringify({
    model: 'llama3.2',
    messages: [{ role: 'user', content: 'Hello!' }],
    stream: true
  })
});

const reader = response.body.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  const chunk = new TextDecoder().decode(value);
  // 各チャンクを処理する...
}

正直なところ、ストリーミング処理は非ストリーミングより少し面倒です。でもユーザー体験はずっと良くなります。モデルが「考えて」出力していく様子をリアルタイムで見られるからです。長く待たされた末に突然大量の文章が現れるよりずっといいでしょう。


OpenAI SDK 互換インターフェース:コード変更ゼロの移行

ここは私のいちばんお気に入りの部分です。Ollama は完全な OpenAI API 互換インターフェースを提供していて、既存コードをほぼシームレスに移行できます。

Python OpenAI SDK の例

OpenAI の公式 SDK をそのまま使います:

from openai import OpenAI

client = OpenAI(
    base_url='http://localhost:11434/v1/',
    api_key='ollama'  # ローカルでは検証されないので任意でよい
)

response = client.chat.completions.create(
    model="llama3.2",
    messages=[{"role": "user", "content": "Hello!"}]
)

print(response.choices[0].message.content)

ご覧のとおり、唯一の変更は base_url の設定と、適当な api_key を入れることだけ。ほかのコードは一切変わりません。

開発環境と本番環境の切り替え

この特徴は特に実用的です。開発環境ではローカル Ollama、本番環境では OpenAI を使えます:

# 開発環境 .env
OPENAI_API_KEY=anyrandomtext
LLM_ENDPOINT="http://localhost:11434/v1"
MODEL=llama3.2

# 本番環境 .env
OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXXXXXXXXXX
LLM_ENDPOINT="https://api.openai.com/v1"
MODEL=gpt-3.5-turbo

コードでは環境変数から読み込むだけです:

import os
from openai import OpenAI

client = OpenAI(
    base_url=os.getenv('LLM_ENDPOINT'),
    api_key=os.getenv('OPENAI_API_KEY')
)

こうすれば、開発時はお金をかけて OpenAI API を呼び出す必要がなく、ローカルのテストで十分です。リリース時は環境変数を変えるだけで、本物の OpenAI に切り替えられます。

対応エンドポイント

Ollama の OpenAI 互換インターフェースは、以下のエンドポイントに対応しています:

エンドポイント機能対応度
/v1/chat/completions対話生成完全対応
/v1/completionsテキスト補完完全対応
/v1/modelsモデル一覧完全対応
/v1/embeddingsテキスト埋め込み完全対応
/v1/responses新しい応答 API完全対応

実験的な /v1/images/generations エンドポイントもありますが、安定度はまだ不十分です。

モデルの別名

ちょっとしたテクニックがあります。モデルに別名を付けられるのです。たとえば、コードが GPT-3.5 を呼んでいるように見せたいとき:

ollama cp llama3.2 gpt-3.5-turbo

こうすれば、コードで model="gpt-3.5-turbo" と書いても、実際にはローカルの llama3.2 が使われます。コードを移行するとき、このテクニックはなかなか便利です。


2つの方法、どちらを選ぶ?

ここまで色々と話しましたが、結局どちらを使えばいいのか、と思うかもしれません。

ネイティブ REST API が向く場面

こんな場合に向いています:

  • 最も軽量な呼び出し方法が欲しい
  • OpenAI SDK のエコシステムが不要
  • 自分で HTTP クライアントを書く(組み込み機器や特殊な環境など)
  • ストリーミング応答の細部を正確に制御したい

ネイティブ API はより直接的で低レベルです。HTTP プロトコルに詳しいなら、使い心地はとても良いでしょう。

OpenAI SDK 互換インターフェースが向く場面

こんな場合に向いています:

  • すでに OpenAI SDK ベースのコードがある
  • ローカル運用へ素早く移行したい
  • OpenAI ツールチェーン(LangChain、LlamaIndex など)を使っている
  • 開発/本番環境を切り替えたい

要するに、「ありものを活かしたい派」でコードを変えたくないなら、OpenAI 互換インターフェースを使えばよいのです。

私のおすすめ

正直に言うと、開発時は OpenAI SDK 互換インターフェースのほうが好きです。コードの変更が少なく、ツールチェーンも使え、デバッグも楽だからです。とはいえ、ネイティブ API のほうが適した場面も確かにあります。極めてシンプルな CLI ツールを書くときや、OpenAI SDK に対応していない環境で呼び出す必要があるときなどです。


実践コードスニペット

最後に、私がよく使うコードスニペットをいくつか紹介します。

Python ストリーミングチャット(OpenAI SDK)

from openai import OpenAI

client = OpenAI(
    base_url='http://localhost:11434/v1/',
    api_key='ollama'
)

stream = client.chat.completions.create(
    model="llama3.2",
    messages=[{"role": "user", "content": "Write a poem"}],
    stream=True
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

JavaScript 完全な対話(ネイティブ API)

async function chat(messages) {
  const response = await fetch('http://localhost:11434/api/chat', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      model: 'llama3.2',
      messages: messages,
      stream: false
    })
  });
  return await response.json();
}

// 会話履歴を維持する
let conversation = [
  { role: 'user', content: 'Hello!' }
];

const result = await chat(conversation);
conversation.push({
  role: 'assistant',
  content: result.message.content
});

console.log(result.message.content);

ツール呼び出し付きの例

Ollama は Function Calling(ツール呼び出し)にも対応しています:

from openai import OpenAI

client = OpenAI(base_url='http://localhost:11434/v1/', api_key='ollama')

tools = [
  {
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Get current weather",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {"type": "string"}
        },
        "required": ["location"]
      }
    }
  }
]

response = client.chat.completions.create(
  model="llama3.2",
  messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],
  tools=tools
)

if response.choices[0].message.tool_calls:
  print("Model wants to call:", response.choices[0].message.tool_calls[0].function.name)

まとめ

結局のところ、Ollama の API 設計はなかなかバランスが取れています。ネイティブ REST API の直接さと軽さも、OpenAI SDK の便利さとエコシステム互換性も、どちらも手に入ります。2つの方法にはそれぞれ適した場面があり、肝心なのは自分のニーズ次第です。

Ollama を使い始めたばかりなら、まずは OpenAI SDK 互換インターフェースを試すのをおすすめします。立ち上がりが速く、コードの変更も少ないからです。慣れてきたら、具体的なニーズに応じてネイティブ API を使うかどうか決めればよいでしょう。

そうそう、もう一つ。ストリーミング応答の処理は確かに落とし穴にハマりやすい部分です。デフォルトがストリーミングであることを覚えておいて、ストリーミングが不要なら明示的に stream: false を設定してください。さもないと、私のように深夜3時に単語半分をじっと見つめることになります。


参考資料

Ollama API を呼び出す2つの方法

curl のネイティブ API から OpenAI SDK 互換インターフェースまでの完全な呼び出しフロー

⏱️ 目安時間: 10 分

  1. 1

    ステップ 1: Ollama がインストール・起動済みか確認する

    まず Ollama が正常に動いているか確認します:

    • ターミナルで実行:ollama list(ダウンロード済みモデルを確認)
    • または http://localhost:11434 にアクセス(Ollama is running が返るはず)
    • デフォルトポート:11434
  2. 2

    ステップ 2: 呼び出し方法を選ぶ

    あなたの場面に合わせて選びます:

    • ネイティブ REST API:軽量な呼び出しやカスタムクライアント向け
    • OpenAI SDK 互換:既存の OpenAI コードや素早い移行向け
  3. 3

    ステップ 3: ネイティブ REST API を使う(curl 方式)

    最も基本的な curl 呼び出し:

    • テキスト生成:curl http://localhost:11434/api/generate -d '{"model": "llama3.2", "prompt": "...", "stream": false}'
    • 対話モード:curl http://localhost:11434/api/chat -d '{"model": "llama3.2", "messages": [...], "stream": false}'
    • 注意:デフォルトはストリーミング応答。stream: false で無効化が必要
  4. 4

    ステップ 4: OpenAI SDK 互換インターフェースを使う

    Python の OpenAI SDK で呼び出す:

    • base_url='http://localhost:11434/v1/' を設定
    • api_key は任意でよい(ローカルでは検証されない)
    • ほかのコードは OpenAI と完全に同じ
    • 環境切り替え:base_url を変えるだけ(開発はローカル、本番は OpenAI)
  5. 5

    ステップ 5: ストリーミング応答を処理する

    ストリーミング応答の処理ポイント:

    • Python:response.iter_lines() で NDJSON を1行ずつ読む
    • JavaScript:response.body.getReader() でストリーム読み取り
    • 各チャンクには数文字しか含まれないため、完全な応答を蓄積する必要がある
    • 非ストリーミング:stream: false を設定すれば完全な JSON が得られる

FAQ

Ollama のネイティブ API と OpenAI SDK 互換インターフェースの違いは?
ネイティブ API はより軽量で直接的、デフォルトでストリーミング応答(NDJSON 形式)になり、カスタム HTTP クライアントに向いています。OpenAI SDK 互換インターフェースは OpenAI SDK と完全互換で、base_url を変えるだけなので、既存の OpenAI コードを素早く移行したい場合に適しています。
Ollama API を呼び出したら単語が半分しか返ってこないのはなぜ?
これはストリーミング応答のデフォルト動作です。Ollama はデフォルトで NDJSON 形式を使い、トークンを1つずつ出力するため、各 JSON オブジェクトには数文字しか含まれません。解決方法:

• stream: false を設定してストリーミングを無効化し、完全な JSON を得る
• または NDJSON ストリームを正しく処理する:1行ずつ読んで内容を蓄積する
開発環境ではローカル Ollama、本番環境では OpenAI を使うには?
環境変数で切り替えます:開発時は LLM_ENDPOINT="http://localhost:11434/v1" を設定し、本番時は "https://api.openai.com/v1" を設定します。コードでは環境変数から base_url を読み込むだけで、ほかのコードは一切変更不要です。
Ollama はどの OpenAI エンドポイントに対応している?
完全対応:/v1/chat/completions、/v1/completions、/v1/models、/v1/embeddings、/v1/responses。実験的対応:/v1/images/generations(安定度はまだ不十分)。
Ollama のモデルに別名を付けられる?
できます。コマンド:ollama cp llama3.2 gpt-3.5-turbo を使えば、コードで model="gpt-3.5-turbo" と書いても実際にはローカルの llama3.2 が使われます。コードを移行するときに便利なテクニックです。
Ollama はツール呼び出し(Function Calling)に対応している?
対応しています。OpenAI SDK 互換インターフェースで tools パラメータを使い、関数の schema を定義すると、モデルが呼び出すべき関数を示す tool_calls フィールドを返します。

6分で読めます · 公開日: 2026年4月3日 · 更新日: 2026年7月9日

関連記事

コメント

GitHubアカウントでログインしてコメントできます

Easton BlogEaston Blog