Ollama: REST APIの基礎

REST APIはOllamaのユニバーサルインターフェース——言語もフレームワークも問わず、HTTPリクエストを送るだけで接続完了。

⚠️ 注意: OllamaのREST APIには組み込みの認証機能がない——サービスポートにアクセスできる人なら誰でも自由にモデルを呼び出し、インストール済みモデル一覧を閲覧できる。本番環境では、リバースプロキシ(Nginx/Caddyなど)でAPI Key認証を必ず追加すること。そうしないと、AIサービスが悪用・データ漏洩・リソース枯渇のリスクに晒される。

📋 前提条件: まず以下を習得していること

1. 学べること


2. SaaS創業者のリアルな事例

(1) ペインポイント:手動返信の非効率性

AliceはGlobalShopというECプラットフォームを立ち上げた。50人のカスタマーサポートチームが毎日2,000件以上のチケットを処理している。各チケットの平均処理時間は8分、顧客の待ち時間は30分を超える。API経由でLLMをカスタマーサポートシステムに統合し、返信下書きを自動生成したい。

(2) ソリューション:REST APIで即座に返信生成

curlでOllama APIを呼び出すと、カスタマーサポートの返信下書きが3秒で生成される——担当者は確認するだけ:

BASH
curl http://localhost:11434/api/chat -d '{
  "model": "qwen2.5",
  "messages": [{"role": "user", "content": "Refund for order #12345"}]
}'

⚠️ 警告: Ollama APIには組み込み認証がない——ポートにアクセスできる人は誰でも呼び出せる。本番環境では、リバースプロキシ(Nginx/Caddy)でAPI Key認証を必ず追加すること。セキュリティ強化はレッスン20を参照。

💡 ヒント: ストリーミングAPI(stream: true)はリアルタイム入力効果のあるチャットインターフェースに適し、非ストリーミング(stream: false)はバッチ処理やAPIバックエンド統合に適している。API統合では非ストリーミングが推奨——よりシンプル。

ℹ️ 情報: Ollamaのデフォルトはhttp://127.0.0.1:11434で、ローカルマシンからのみアクセス可能。LANからのアクセスにはOLLAMA_HOST環境変数を設定するが、セキュリティリスクに注意。

3. APIエンドポイント完全ガイド

(1) 2つのコアエンドポイント比較

項目 /api/generate /api/chat
用途 単発テキスト生成 マルチターン対話
入力 model + prompt messages配列
コンテキスト 単一リクエスト 会話履歴に対応
APIマッピング CLI ollama run CLI ollama chat
ユースケース 生成・補完・翻訳 カスタマーサポート・アシスタント・多段推論
100%
sequenceDiagram
    participant C as Client
    participant O as Ollama Server
    C->>O: POST /api/chat {messages, model, stream}
    O-->>C: NDJSON {message, done: false}
    O-->>C: NDJSON {message, done: false}
    O-->>C: NDJSON {message, done: true, stats}

(2) 共通リクエストパラメータ

パラメータ デフォルト 説明
model string 必須 モデル名
stream bool true ストリーム出力の有無
options object 推論パラメータ(次節参照)
format string 出力形式:json
keep_alive string 5m モデルのメモリ保持時間

(3) ▶ サンプル:単発生成リクエスト

BASH
# Non-streaming generate request
curl http://localhost:11434/api/generate -d '{
  "model": "llama3.2",
  "prompt": "Write a haiku about coding",
  "stream": false
}'

# Response (abbreviated)
# {
#   "model": "llama3.2",
#   "response": "Lines of logic flow,\nBug hides in the deep syntax—\nSemicolon found.",
#   "done": true,
#   "total_duration": 2500000000,
#   "eval_count": 18
# }

出力:

TEXT
{"status":"ok","data":{}}

(4) ▶ サンプル:マルチターン対話リクエスト

BASH
# Chat with message history
curl http://localhost:11434/api/chat -d '{
  "model": "qwen2.5",
  "messages": [
    {"role": "system", "content": "You are a helpful customer service agent."},
    {"role": "user", "content": "I want to return my order #12345"},
    {"role": "assistant", "content": "I can help with that. May I ask the reason for the return?"},
    {"role": "user", "content": "The product arrived damaged"}
  ],
  "stream": false
}'

出力:

TEXT
{"status":"ok","data":{}}

4. ストリーミングレスポンスの解析

💡 ヒント:ストリーミング(stream: true)と非ストリーミング(stream: false)はそれぞれ用途がある——ストリーミングはチャットインターフェースのリアルタイム入力効果用で、ユーザーは完全なレスポンスを待たずにコンテンツを確認できる。非ストリーミングはバッチ処理やAPIバックエンド統合用で、完全なJSONレスポンスを取得する方がプログラム解析に容易。まず非ストリーミングでロジックを検証し、その後ストリーミングに切り替えてUXを最適化することを推奨。

(1) NDJSON形式の解説

ストリーミングレスポンスはNDJSON(Newline Delimited JSON)を使用し、1行に1つのJSONオブジェクト:

TEXT
{"model":"llama3.2","message":{"role":"assistant","content":"I"},"done":false}
{"model":"llama3.2","message":{"role":"assistant","content":" can"},"done":false}
{"model":"llama3.2","message":{"role":"assistant","content":" help"},"done":false}
{"model":"llama3.2","message":{"role":"assistant","content":""},"done":true,"total_duration":1500000000}
フィールド 説明
message.content このチャンクのテキスト断片
done 最後のチャンクかどうか
total_duration 合計推論時間(ナノ秒)
eval_count 生成トークン数
prompt_eval_count 入力トークン数

(2) ストリーミングと非ストリーミングの比較

項目 ストリーミング(stream: true) 非ストリーミング(stream: false)
ユーザー体験 リアルタイムの1文字ずつ出力 完全なレスポンスを待機
初回トークンレイテンシ 非常に低い(~200ms) すべての生成完了まで待機
実装の複雑さ NDJSON解析が必要 JSONを直接読み取り
ユースケース チャットインターフェース、リアルタイム表示 バッチ処理、APIバックエンド

(3) ▶ サンプル:ストリーミングレスポンスの解析

BASH
# Streaming request with real-time output
curl http://localhost:11434/api/chat -d '{
  "model": "llama3.2",
  "messages": [{"role": "user", "content": "Hello!"}],
  "stream": true
}' | while read -r line; do
    # Extract content field from each NDJSON line
    echo "$line" | python3 -c "
import sys, json
data = json.load(sys.stdin)
if data.get('message', {}).get('content'):
    print(data['message']['content'], end='', flush=True)
"
done

出力:

TEXT
{"status":"ok","data":{}}

5. 推論パラメータの調整

(1) コアパラメータ一覧

パラメータ 範囲 デフォルト 効果
temperature float 0-2 0.8 ランダム性の制御。低い値ほど決定的
top_p float 0-1 0.9 ニュークリウスサンプリング、候補トークン範囲を制限
top_k int 1-100 40 上位K件の候補のみサンプリング
num_ctx int 128-131072 2048 コンテキストウィンドウサイズ
repeat_penalty float 1-2 1.1 繰り返しペナルティ係数
seed int 任意 -1 ランダムシード(-1 = ランダム)

(2) シナリオ別パラメータ調整

シナリオ temperature top_p 備考
コード生成 0.1-0.3 0.9 決定性と精度が必要
カスタマーサポート返信 0.3-0.5 0.9 安定しつつ適度な変化を許容
クリエイティブライティング 0.7-1.0 0.95 多様性と創造性が必要
データ分析 0.1-0.2 0.9 精密さが必須、ハルシネーション不可
⚠️ 警告: num_ctxはメモリ使用量に直接影響する。8Bモデルでnum_ctx=8192は約6GB VRAMが必要、num_ctx=32768は約12GB VRAMが必要。必要に応じて設定し、むやみに増やさないこと。

(3) ▶ サンプル:パラメータ調整の比較

BASH
# Low temperature: deterministic output
curl http://localhost:11434/api/generate -d '{
  "model": "llama3.2",
  "prompt": "What is 2+2?",
  "stream": false,
  "options": {"temperature": 0.1}
}'
# Response: "2+2 equals 4."

# High temperature: creative output
curl http://localhost:11434/api/generate -d '{
  "model": "llama3.2",
  "prompt": "What is 2+2?",
  "stream": false,
  "options": {"temperature": 1.5}
}'
# Response: "In the realm of mathematics, 2+2 opens the door to 4..."

出力:

TEXT
{"status":"ok","data":{}}

(4) ▶ サンプル:JSON形式出力

BASH
# Force JSON output format
curl http://localhost:11434/api/chat -d '{
  "model": "llama3.2",
  "messages": [
    {"role": "system", "content": "You are a product catalog API. Return JSON only."},
    {"role": "user", "content": "List 3 laptops under $500"}
  ],
  "format": "json",
  "stream": false,
  "options": {"temperature": 0.3}
}'

# Response is valid JSON
# {"products":[{"name":"Acer Aspire 5","price":449,"spec":"8GB RAM, 256GB SSD"},...]}

出力:

TEXT
{"status":"ok","data":{}}

6. 総合サンプル:SupportBotカスタマーサポートAPIプロトタイプ

💡 ヒント: 本番環境でAPIを呼び出す際、keep_aliveパラメータ(例:"keep_alive": "5m")を必ず設定し、モデルの頻繁なロード/アンロードによるレスポンス遅延を防ぐこと。

BASH
#!/bin/bash
# ============================================
# Comprehensive: SupportBot API prototype
# Multi-turn customer service via REST API
# ============================================

API="http://localhost:11434/api/chat"
MODEL="qwen2.5"

# Function: Send a chat message and extract response
chat() {
    local system_prompt="$1"
    local user_msg="$2"
    local temp="${3:-0.4}"

    curl -s "$API" -d "$(cat <<EOF
{
  "model": "$MODEL",
  "messages": [
    {"role": "system", "content": "$system_prompt"},
    {"role": "user", "content": "$user_msg"}
  ],
  "stream": false,
  "options": {"temperature": $temp, "num_ctx": 4096}
}
EOF
)" | python3 -c "import sys,json; print(json.load(sys.stdin)['message']['content'])"
}

# Customer service system prompt
SYSTEM="You are SupportBot, a customer service agent for an e-commerce store. Be polite, concise, and helpful. If you cannot answer, say 'Let me connect you with a human agent.'"

# Simulate customer interactions
echo "=== Query 1: Order Status ==="
chat "$SYSTEM" "Where is my order #88765? It has been 5 days."

echo ""
echo "=== Query 2: Return Request ==="
chat "$SYSTEM" "I received a damaged item. Order #12345. I want a refund."

echo ""
echo "=== Query 3: Product Question ==="
chat "$SYSTEM" "Does the wireless headphone support Bluetooth 5.3?"

echo ""
echo "=== Benchmark ==="
time chat "$SYSTEM" "Hello" > /dev/null
💻 出力:

TEXT
=== Query 1: Order Status ===
I'd be happy to check on your order #88765. Based on our tracking system, your order is currently in transit and expected to arrive within 2-3 business days. You can track it at track.example.com/88765.

=== Query 2: Return Request ===
I'm sorry to hear about the damaged item. For order #12345, I've initiated a return request. You'll receive a prepaid shipping label via email within 24 hours. Once we receive the item, a full refund will be processed within 3-5 business days.

=== Query 3: Product Question ===
Yes, our wireless headphones support Bluetooth 5.3 with a range of up to 15 meters. They also feature active noise cancellation and 30-hour battery life.

❓ よくある質問

Q curlリクエストがconnection refusedを返すのはなぜ?
A Ollamaサービスが起動していない。ollama serveを実行するか、systemdサービスが起動しているか確認:sudo systemctl status ollama
Q stream: trueとstream: falseはどちらを選ぶべき?
A フロントエンドのチャットインターフェースではリアルタイム入力効果を得るためにstream: trueを使用。バックエンドのバッチ処理では完全なレスポンスを直接取得するためにstream: falseを使用。API統合では非ストリーミングが推奨——よりシンプル。
Q 出力長を制限するには?
A num_predictパラメータを設定。例:"num_predict": 200で生成を200トークンに制限。これはトークン数であり文字数ではないことに注意。
Q format: jsonは有効なJSON出力を保証する?
A ほとんどの場合機能するが、100%の保証はない。アプリケーション層でJSONバリデーションを追加し、パース失敗時にリトライまたはテキスト処理にフォールバックすることを推奨。
Q マルチターン対話はコンテキストをどう維持する?
A クライアント側でmessages配列を自前で管理し、毎回のリクエストで完全な会話履歴を送信する。Ollamaサーバーはセッション状態を保存しない。
Q API呼び出しの同時実行制限はある?
A デフォルトでは一度に1つのリクエストのみ処理される。OLLAMA_NUM_PARALLEL環境変数で同時実行数を増やせるが、より多くのVRAMが必要。パフォーマンスチューニングはレッスン19を参照。

📖 まとめ


📝 練習問題

  1. 基本(難易度 ⭐):curlで/api/generateを呼び出して商品説明を生成し、stream: truestream: falseの両方を試して違いを観察する。
  2. 中級(難易度 ⭐⭐)/api/chatを使って3ターンの対話を実装し、messages配列を手動で管理し、各ターンで完全な返信を出力する。
  3. 上級(難易度 ⭐⭐⭐):SupportBotのカスタマーサポートフローをシミュレートするShellスクリプトを作成——質問を受け取り、APIを呼び出し、JSON形式で分類結果(インテント+返信)を返し、リアルタイムストリーミング出力に対応する。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%