言語を切り替える
テーマを切り替える

Ollama API 実践:Python と Node.js クライアント開発ガイド

深夜 1 時、ターミナルで ollama run gemma3 を叩くと、画面に最初の応答が返ってきました。気持ちいいですね。

でも、すぐにこう考え始めるはずです。「これ、自分のプロジェクトに組み込めるかな?」API Key も不要、課金も不要、ローカルで動くなんて、素晴らしいじゃないですか。

正直、私も最初はそう思いました。ドキュメントを一通り見てみると、公式が Python と JavaScript の SDK を提供しているし、OpenAI の SDK を使っても数行書き換えるだけで接続できることがわかりました。想像していたより簡単そうです。

でも、簡単だからといって罠がないわけではありません。ストリーミングレスポンスの累積方法は?ツール呼び出しの Agent Loop の書き方は?thinking モードで推論と回答をどう分けるか?これらはすべて私が実際にハマったポイントです。

この記事では、そうした罠を埋めていきます。Python と Node.js の 2 つの言語を比較しながら、ネイティブ SDK と OpenAI 互換の 2 つのアプローチを詳しく解説します。完全なクライアント開発ガイドをお届けします。

ちなみに、まだ Ollama をインストールしていない場合は、シリーズ最初の LangChain + Ollama 統合実践 を見て、まずローカルモデルを動かすところから始めてください。


第一章:Ollama API の基礎

まず、API がどう動いているかを理解しましょう。

Ollama はデフォルトでローカルに REST API サービスを起動します。アドレスは http://localhost:11434/api です。ブラウザでこのアドレスにアクセスすると、“Ollama is running” というシンプルなメッセージが表示されます。これでサービスが正常に動作していることがわかります。

主要エンドポイント

覚えておくべきコアエンドポイントは 2 つです:

エンドポイント用途特徴
/api/chatマルチターン会話messages 配列をサポート、コンテキストの受け渡しが可能
/api/generate単発生成シンプル、ワンショットタスクに最適

もう一つ /v1/chat/completions があります。これは OpenAI 互換エンドポイントです。既存の OpenAI プロジェクトがある場合、base_url を変更するだけで使えます。後で詳しく説明します。

curl で試してみる

まずは最も原始的な方法で API を試してみましょう:

curl http://localhost:11434/api/chat -d '{
  "model": "gemma3",
  "messages": [
    { "role": "user", "content": "なぜ空は青いのですか?" }
  ]
}'

ターミナルに JSON の塊が出力されます。message.content フィールドがモデルの回答です。

レスポンス構造はこんな感じです:

{
  "model": "gemma3",
  "created_at": "2026-04-18T01:23:45.678Z",
  "message": {
    "role": "assistant",
    "content": "空が青く見えるのは主に..."
  },
  "done": true
}

done フィールドが重要です。ストリーミングレスポンスの場合、各 chunk の donefalse で、最後の chunk だけ true になります。これは後でストリーミングレスポンスを処理する際に使います。

ストリーミングレスポンス

デフォルトでは、API はモデルが生成を完了するのを待ってから一括で返します。でも、ユーザーに「タイプライター効果」を見せたい場合は、stream: true を追加する必要があります:

curl http://localhost:11434/api/chat -d '{
  "model": "gemma3",
  "messages": [{ "role": "user", "content": "なぜ空は青いのですか?" }],
  "stream": true
}'

今度はターミナルに JSON が 1 行ずつ出てきます。各行が小さな chunk で、これらをまとめて初めて完全な回答になります。

手動でこれらの chunk を処理するのは、確かに少し面倒です。これが公式が SDK を提供している理由です。細かい処理をすべてカプセル化してくれます。


第二章:Python SDK 完全実践

Python の SDK は公式がメンテナンスしています。インストールはとても簡単です:

pip install ollama

インストールしたらすぐ使えます。Python 3.8+ をサポートしているので、使いやすいです。

基本的な呼び出し

最もシンプルな呼び出し方法は、1 行で完了します:

from ollama import chat

response = chat(
  model='gemma3',
  messages=[{'role': 'user', 'content': 'なぜ空は青いのですか?'}]
)

print(response.message.content)

これだけシンプルです。chat() 関数は SDK が提供するショートカットで、内部でデフォルトの Client を自動作成し、ローカルの Ollama サービスに接続します。

接続パラメータをカスタマイズしたい場合—例えば Ollama が別のマシンで動いている場合—自分で Client を作成できます:

from ollama import Client

client = Client(host='http://192.168.1.100:11434')
response = client.chat(model='gemma3', messages=[...])

ストリーミングレスポンス

ストリーミングレスポンスは重要です。ユーザーがテキストが少しずつ出てくるのを見たいですよね。ずっと待ってから急に全部出るのではなく。

from ollama import chat

stream = chat(
  model='gemma3',
  messages=[{'role': 'user', 'content': 'なぜ空は青いのですか?'}],
  stream=True,
)

for chunk in stream:
  print(chunk['message']['content'], end='', flush=True)

ここで 1 つハマりどころがあります:chunk は辞書で返ってきます。オブジェクトではありません。だからアクセス方法は chunk['message']['content'] で、chunk.message.content ではありません。最初私はここでエラーが出て、しばらく原因がわかりませんでした。

非同期クライアント

アプリケーションが非同期アーキテクチャの場合—例えば FastAPI や aiohttp を使っている場合—非同期クライアントを使う必要があります:

import asyncio
from ollama import AsyncClient

async def main():
  client = AsyncClient()
  
  # 非ストリーミング
  response = await client.chat(
    model='gemma3',
    messages=[{'role': 'user', 'content': 'こんにちは'}]
  )
  print(response.message.content)
  
  # ストリーミング
  stream = await client.chat(
    model='gemma3',
    messages=[{'role': 'user', 'content': 'なぜ空は青いのですか?'}],
    stream=True,
  )
  async for chunk in stream:
    print(chunk['message']['content'], end='', flush=True)

asyncio.run(main())

非同期ストリーミングは async generator で返ってくるので、async for で反復します。同期バージョンと同じロジックで、awaitasync が追加されただけです。

Cloud Models

面白いことに、Ollama SDK はクラウドモデルもサポートしています。一部の大規模モデルはローカルでは動かない—例えば 120B の gpt-oss—but クラウドにはあります。

from ollama import chat

response = chat(
  model='gpt-oss:120b-cloud',
  messages=[{'role': 'user', 'content': 'こんにちは'}]
)

モデル名に -cloud サフィックスを付けるとクラウド API を使います。もちろん、Ollama のクラウドアカウントと API Key が必要で、設定方法はローカルとは少し違います。興味がある方は公式ドキュメントを見てください。

正直、この機能はかなり実用的です。小さなモデルはローカルで動かしてコストを節約し、大きなモデルはクラウドで動かしてハードウェアを節約する。ハイブリッドなアプローチは悪くありません。


第三章:Node.js SDK 完全実践

Node.js の SDK も同じくらいシンプルです:

npm i ollama

このパッケージは Node.js とブラウザ環境の両方をサポートしています。ブラウザバージョンは別途インポートが必要です:

// Node.js
import ollama from 'ollama'

// ブラウザ
import ollama from 'ollama/browser'

基本的な呼び出し

Node.js ではデフォルトで非同期なので、より自然に書けます:

import ollama from 'ollama'

const response = await ollama.chat({
  model: 'gemma3',
  messages: [{ role: 'user', content: 'なぜ空は青いのですか?' }],
})

console.log(response.message.content)

Python 版と比較してみましょう:Python は辞書で messages を渡し、Node.js はオブジェクトを使います。パラメータ名はほぼ同じなので、言語を変えてもコンセプトを学び直す必要がありません。

ストリーミングレスポンス

Node.js のストリーミング処理は、ネイティブで非同期 generator です:

import ollama from 'ollama'

const stream = await ollama.chat({
  model: 'gemma3',
  messages: [{ role: 'user', content: 'なぜ空は青いのですか?' }],
  stream: true,
})

for await (const chunk of stream) {
  process.stdout.write(chunk.message.content)
}

ここでは console.log ではなく process.stdout.write を使っています。console.log は自動的に改行を追加するので、1 文字出るたびに改行されたくないですよね?

カスタム設定

SDK はカスタム host と headers をサポートしています:

import ollama from 'ollama'

// カスタム host
const client = new ollama.Ollama({ host: 'http://192.168.1.100:11434' })

// またはグローバル設定
ollama.setDefaultHost('http://192.168.1.100:11434')

// headers を追加(例:認証)
const stream = await ollama.chat({
  model: 'gemma3',
  messages: [{ role: 'user', content: 'こんにちは' }],
  headers: { Authorization: 'Bearer xxx' },
})

headers パラメータは便利です。Ollama サービスの前に認証プロキシを置いている場合、ここでトークンを渡せます。

ストリーミング生成のキャンセル

abort() メソッドで進行中のストリーミング生成をキャンセルできます:

import ollama from 'ollama'

const stream = await ollama.chat({
  model: 'gemma3',
  messages: [{ role: 'user', content: '長い文章を書いて...' }],
  stream: true,
})

// ユーザーが停止ボタンをクリック
ollama.abort()

for await (const chunk of stream) {
  // abort 後、ループは早期終了
  process.stdout.write(chunk.message.content)
}

この機能はチャット UI を作る際に必須です。ユーザーがモデルの無駄話を待ちたくない場合、ボタン 1 つで停止できます。

ブラウザバージョン

ブラウザでの使い方は似ていますが、いくつか違いがあります:

import ollama from 'ollama/browser'

// ブラウザではストリーミングのみ使用可能
// ネイティブ API が非ストリーミングのクロスオリジンリクエストをサポートしていないため
const stream = await ollama.chat({
  model: 'gemma3',
  messages: [{ role: 'user', content: 'こんにちは' }],
  stream: true,
})

for await (const chunk of stream) {
  document.getElementById('output').textContent += chunk.message.content
}

ブラウザバージョンには制限があります:ストリーミングモードが必須です。Ollama API の非ストリーミングリクエストは大きな JSON を一括で返すため、クロスオリジンリクエストがタイムアウトしたりブロックされたりしやすいです。ストリーミングリクエストは chunk に分かれているので、問題が少ないです。

この設計は合理的です。ブラウザでチャット UI を作る場合、ストリーミング表示エフェクトが必要ですからね。


第四章:ツール呼び出し実践

ツール呼び出しは Agent を作る基礎です。Ollama はモデルがあなたが定義した関数を呼び出し、その関数の戻り値に基づいて回答を生成し続けることをサポートしています。

Python SDK にはとても便利な機能があります:Python 関数を直接ツールとして渡すと、SDK が関数の docstring とパラメータ型を自動解析してくれます。

Python 関数の自動解析

def get_weather(city: str) -> str:
  """指定した都市の天気情報を取得

  Args:
    city: 都市名(例:「東京」、「大阪」)

  Returns:
    天気説明文字列
  """
  # モックデータ
  weather_data = {
    '東京': '晴れ、気温 18°C',
    '大阪': '曇り、気温 22°C',
    '福岡': '雨、気温 26°C',
  }
  return weather_data.get(city, f'{city} の天気データが見つかりません')

from ollama import chat

response = chat(
  model='qwen3',
  messages=[{'role': 'user', 'content': '東京の今日の天気は?'}],
  tools=[get_weather],
)

print(response.message.content)

SDK が自動的に関数をツール定義形式に変換します:名前は関数名から、説明は docstring から、パラメータは型アノテーションから取得します。JSON Schema を手書きする手間が省けます。

Agent Loop パターン

でも、そう簡単ではありません。モデルは複数のツールを呼び出すかもしれませんし、ツールを呼び出した後もさらに呼び出しを続けるかもしれません。これを処理するにはループが必要です。

これが Agent Loop です:

from ollama import chat

def add(a: int, b: int) -> int:
  """加算"""
  return a + b

def multiply(a: int, b: int) -> int:
  """乗算"""
  return a * b

tools = [add, multiply]
tool_map = {'add': add, 'multiply': multiply}

messages = [{'role': 'user', 'content': '(3 + 5) * 2 を計算して'}]

while True:
  response = chat(model='qwen3', messages=messages, tools=tools)

  if response.message.tool_calls:
    # モデルがツールを呼び出したい
    for call in response.message.tool_calls:
      func_name = call.function.name
      func_args = call.function.arguments
      result = tool_map[func_name](**func_args)

      # ツール呼び出し結果をメッセージ履歴に追加
      messages.append({
        'role': 'tool',
        'content': str(result),
        'tool_name': func_name,
      })
  else:
    # モデルがツールを呼び出さなかった、終了
    print(response.message.content)
    break

ロジックはこうです:

  1. メッセージをモデルに送信、ツール定義を含める
  2. モデルが tool_calls を返したら、対応する関数を実行
  3. 関数の結果をメッセージ履歴に追加し、再度モデルに送信
  4. モデルがツールを呼び出さなくなるまでループ

このパターンは Agent を作る際に必須です。ツール関数を定義しておけば、モデルがいつ呼び出すか、どれを呼び出すか、どんな順序で呼び出すかを自分で判断します。

thinking モード

一部のモデル—例えば qwen3—は thinking モードをサポートしています。モデルがまず「考え」、それから回答を出します。

from ollama import chat

stream = chat(
  model='qwen3',
  messages=[{'role': 'user', 'content': 'なぜ空は青いのですか?'}],
  stream=True,
  think=True,
)

thinking = ''
content = ''

for chunk in stream:
  if chunk.message.thinking:
    thinking += chunk.message.thinking
  elif chunk.message.content:
    content += chunk.message.content

print('=== 思考プロセス ===')
print(thinking)
print('=== 最終回答 ===')
print(content)

thinking モードでは、chunk に thinking フィールドが追加されます。思考内容と最終回答を別々に累積する必要があります。

この機能は面白いです。モデルがどうやって段階的に答えを導き出しているかが見えます。教育系アプリケーションやプロンプトのデバッグにとても役立ちます。


第五章:ネイティブ SDK vs OpenAI 互換 API

今、2 つの選択肢があります:

  1. Ollama ネイティブ SDK(前述のもの)
  2. OpenAI SDK を使い、アドレスを変更するだけで Ollama に接続

どちらが良いか?状況によります。

OpenAI 互換ソリューション

既存の OpenAI プロジェクトがある場合、移行コストが最も低い方法は base_url を変更することです:

from openai import OpenAI

client = OpenAI(
  base_url='http://localhost:11434/v1',
  api_key='ollama',  # 必須だが無視される
)

response = client.chat.completions.create(
  model='gemma3',
  messages=[{'role': 'user', 'content': 'なぜ空は青いのですか?'}],
)

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

これだけシンプルです。OpenAI SDK は背後で Ollama が動いていることを全く知りません。「OpenAI API」と話していると思っています。

Node.js バージョンも同じです:

import OpenAI from 'openai'

const client = new OpenAI({
  baseURL: 'http://localhost:11434/v1',
  apiKey: 'ollama',
})

const completion = await client.chat.completions.create({
  model: 'gemma3',
  messages: [{ role: 'user', content: 'なぜ空は青いのですか?' }],
})

console.log(completion.choices[0].message.content)

2 つのアプローチの比較

面積ネイティブ SDKOpenAI 互換
インストールpip install ollama既存 OpenAI SDK で OK
ツール呼び出し関数 docstring 自動解析JSON Schema を手書き
ストリーミングレスポンス辞書形式 chunk標準 OpenAI 形式
Cloud Modelsサポート非サポート
移行コスト新規プロジェクトはコストなし既存プロジェクトは極めて低コスト

選択のアドバイス

新規プロジェクト:ネイティブ SDK を使いましょう。

理由:

  • ツール呼び出しがより便利、Python 関数を直接ツールとして渡せる
  • より多くの機能をサポート(Cloud Models、thinking モード)
  • ドキュメントとサンプルが公式、問題が起きても調べやすい

既存 OpenAI プロジェクトの移行:OpenAI 互換ソリューションを使いましょう。

理由:

  • 2 行書き換えるだけで動く
  • ロジックを書き直す必要がない
  • 後で OpenAI に戻すのも簡単

一言でまとめると:ネイティブ SDK は機能が豊富、OpenAI 互換は移行が速い。ニーズによります。

正直、両方試しました。ネイティブ SDK のツール呼び出しは確かに楽です—JSON Schema を手書きする必要がなく、関数の docstring をきちんと書けば OK です。でも、プロジェクトが既に OpenAI で動いているなら、Ollama を使うために全部書き直す必要はありません。


最後に

たくさん話しましたが、いくつかのポイントをまとめます:

基本的な呼び出し:Python と Node.js SDK はどちらもよくカプセル化されていて、数行で動きます。ストリーミングレスポンスは stream=True で有効化するのを忘れずに。

ツール呼び出し:Agent Loop がコアパターンです—tool_calls がなくなるまでループで処理。Python SDK は関数を直接ツールとして渡せるので、JSON Schema を書く手間が省けます。

thinking モード:qwen3 などのモデルがサポート、モデルの思考プロセスが見えます。chunk で thinking と content フィールドを別々に処理する必要があります。

ソリューションの選択:新規プロジェクトはネイティブ SDK、機能が豊富。既存 OpenAI プロジェクトは互換ソリューション、アドレスを変更するだけ。

次のステップの提案:

  • まだ Ollama をインストールしていない場合は、シリーズ最初の記事を見てローカルモデルを動かすところから
  • プロジェクトに合ったソリューション(ネイティブまたは OpenAI 互換)を選んで、実際に試してみる
  • 公式ドキュメントは継続的に更新され、新機能がどんどん追加されるので、時間があれば見てみる

ローカルで LLM を動かすことのハードルは、ますます低くなっています。Ollama は複雑なものをシンプルな API の裏に隠しています。私たちが知る必要があるのは、どう呼び出すかだけ。あとは任せましょう。

これがシリーズ 2 番目の記事です。次回は Modelfile のカスタマイズについて—モデルをあなたが望む形に調整する方法を解説します。

Ollama API クライアント開発

Python または Node.js SDK を使って Ollama ローカルモデル API を呼び出す完全ガイド

⏱️ 目安時間: 45 分

  1. 1

    ステップ1: SDK をインストールして基本呼び出しをテスト

    Python ユーザーは `pip install ollama`、Node.js ユーザーは `npm i ollama` を実行。

    インストール完了後、最もシンプルなコードで接続をテスト:
    ```python
    from ollama import chat
    response = chat(model='gemma3', messages=[{'role': 'user', 'content': 'こんにちは'}])
    print(response.message.content)
    ```

    Ollama サービスが起動していること(デフォルトポート 11434)、対応するモデルがダウンロード済みであることを確認。
  2. 2

    ステップ2: ストリーミングレスポンスを実装

    ストリーミングモードを有効化し、ユーザーに逐次出力エフェクトを表示:

    ```python
    from ollama import chat
    stream = chat(model='gemma3', messages=[...], stream=True)
    for chunk in stream:
    print(chunk['message']['content'], end='', flush=True)
    ```

    chunk は辞書で返ってくるので、`chunk['message']['content']` でアクセスすることに注意。
  3. 3

    ステップ3: ツール呼び出しを設定(オプション)

    Python 関数をツールとして定義、SDK が docstring と型アノテーションを自動解析:

    ```python
    def get_weather(city: str) -> str:
    """都市の天気情報を取得"""
    return f'{city}: 晴れ'

    response = chat(model='qwen3', messages=[...], tools=[get_weather])
    ```

    Agent Loop を実装して複数回のツール呼び出しを処理し、モデルが最終回答を返すまでループ。
  4. 4

    ステップ4: ネイティブまたは OpenAI 互換ソリューションを選択

    新規プロジェクトはネイティブ SDK を推奨、機能が豊富(Cloud Models、thinking モード)。

    既存 OpenAI プロジェクトは 2 行変更するだけ:
    ```python
    client = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')
    ```

    移行コストは極めて低く、いつでも OpenAI に戻せる。

FAQ

Ollama API のデフォルトポートとアドレスは?
Ollama はデフォルトで localhost:11434 で REST API サービスを起動します。コアエンドポイントには /api/chat(マルチターン会話)と /api/generate(単発生成)があり、/v1/chat/completions は OpenAI 互換用です。
Python SDK のストリーミングレスポンスは何型を返す?
Python SDK のストリーミングレスポンスは辞書型で返ります。オブジェクトではありません。コンテンツへのアクセスは chunk['message']['content'] を使い、chunk.message.content ではありません。非同期クライアントは async generator を返し、async for で反復します。
Node.js SDK はブラウザ環境で制限はある?
ブラウザバージョンはストリーミングモード(stream: true)が必須です。非ストリーミングリクエストは大きな JSON を一括で返すため、クロスオリジンリクエストがタイムアウトしたりブロックされたりしやすいからです。インポート方法も異なります:ブラウザでは import ollama from 'ollama/browser' を使用。
Agent Loop パターンとは?
Agent Loop はツール呼び出しを処理するループパターンです:モデルにメッセージを送信 → tool_calls が返ってきたかチェック → 対応する関数を実行 → 結果をメッセージ履歴に追加 → 再度モデルを呼び出し → モデルがツールを呼び出さなくなるまでループ。このパターンは Agent 構築の基礎です。
thinking モードで思考プロセスと最終回答を別々に取得するには?
thinking モードでは、ストリーミングレスポンスの chunk に thinking フィールドが追加されます。別々に累積する必要があります:if chunk.message.thinking なら思考内容を累積、elif chunk.message.content なら最終回答を累積。現在 qwen3 などのモデルがこの機能をサポートしています。
ネイティブ SDK と OpenAI 互換ソリューション、どちらを選ぶべき?
新規プロジェクトはネイティブ SDK を推奨:ツール呼び出しが関数 docstring の自動解析をサポート、Cloud Models と thinking モードも対応。既存 OpenAI プロジェクトは互換ソリューションを推奨:base_url を変更するだけでよく、移行コストが極めて低く、後で OpenAI に戻すのも簡単です。

6 min read · 公開日: 2026年4月18日 · 更新日: 2026年4月18日

シリーズの読書導線 第 12 / 12 記事

Ollama ローカル LLM 実践ガイド

検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。

シリーズ全体を見る

関連記事

コメント

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