目次
Ollama 構造化出力チュートリアル:JSON Schema に沿ったデータをモデルに返させる
モデルをプログラムに組み込むとき、必要なのは文章ではなく、プログラムから直接読み取れるデータであることがよくあります。たとえば、苦情メールから氏名、注文番号、問題の種類を取り出す場合です。
Ollama の format パラメーターを使うと、モデルの出力を指定した JSON 形式にできます。
最も簡単な方法:JSON だけを要求する
curl http://localhost:11434/api/chat -d '{
"model": "qwen3:8b",
"messages": [{ "role": "user", "content": "日本の都市を3つ挙げ、人口とともに JSON で回答してください" }],
"format": "json",
"stream": false
}'
有効な JSON が出力されますが、フィールド名と構造はモデルが決めるため、毎回異なる場合があります。
JSON Schema を指定する
JSON Schema を format に指定すると、フィールドを正確に定義できます。
curl http://localhost:11434/api/chat -d '{
"model": "qwen3:8b",
"messages": [{ "role": "user", "content": "東京を紹介してください" }],
"format": {
"type": "object",
"properties": {
"city": { "type": "string" },
"population": { "type": "integer" },
"landmarks": { "type": "array", "items": { "type": "string" } }
},
"required": ["city", "population", "landmarks"]
},
"stream": false
}'
message.content は、指定した形式に準拠した JSON 文字列になります。
Python:Pydantic で形式を定義する
from ollama import chat
from pydantic import BaseModel
class Complaint(BaseModel):
customer_name: str
order_id: str | None
category: str # 例:配送、品質、返金、その他
urgency: int # 1~5
summary: str
email = """
こんにちは、山田花子です。注文番号 A20261012-88 のドライヤーが届いたとき、外箱が破損していました。
電源を入れてもまったく動きません。来週海外に行くため急いでいます。返品または交換を早めにお願いします。
"""
response = chat(
model="qwen3:8b",
messages=[
{"role": "system", "content": "苦情の内容から情報を抽出してください。category は配送、品質、返金、その他のいずれかにしてください。"},
{"role": "user", "content": email},
],
format=Complaint.model_json_schema(),
options={"temperature": 0},
)
data = Complaint.model_validate_json(response.message.content)
print(data)
print(data.urgency)
customer_name='山田花子' order_id='A20261012-88' category='品質' urgency=4 summary='ドライヤーの外箱が破損し、通電しても動かないため、早急な返品または交換を希望'
4
model_validate_json で型をもう一度検証するため、プログラムが受け取るデータが正しいことを確認できます。
選択肢を限定する:Literal または Enum を使う
from typing import Literal
class Complaint(BaseModel):
category: Literal["配送", "品質", "返金", "その他"]
これでモデルが選べる値を4つに限定できます。プロンプトで説明するより確実です。
JavaScript:Zod で形式を定義する
npm install ollama zod
import ollama from "ollama";
import { z } from "zod";
const Product = z.object({
name: z.string(),
price: z.number(),
tags: z.array(z.string()),
});
const response = await ollama.chat({
model: "qwen3:8b",
messages: [{ role: "user", content: "この iPhone ケースは590円で、耐衝撃、薄型、MagSafe 対応です" }],
format: z.toJSONSchema(Product),
});
const product = Product.parse(JSON.parse(response.message.content));
console.log(product);
(Zod 4 には z.toJSONSchema が組み込まれています。旧バージョンの Zod 3 では zod-to-json-schema パッケージを使用できます。)
例:複数のレビューを分類する
from typing import Literal
from ollama import chat
from pydantic import BaseModel
class Review(BaseModel):
sentiment: Literal["ポジティブ", "ネガティブ", "中立"]
topics: list[str]
reviews = [
"料理はとてもおいしかったですが、待ち時間が1時間近くありました。",
"スタッフの対応がとてもよかったです。また来ます!",
"価格も味も普通でした。",
]
for text in reviews:
r = chat(
model="qwen3:8b",
messages=[{"role": "user", "content": f"このレビューを分析してください:{text}"}],
format=Review.model_json_schema(),
options={"temperature": 0},
)
print(text, "→", Review.model_validate_json(r.message.content))
画像から情報を抽出する
視覚モデルと組み合わせると、請求書、名刺、スクリーンショットから構造化データを抽出できます。
class BusinessCard(BaseModel):
name: str
company: str | None
title: str | None
phone: str | None
email: str | None
r = chat(
model="gemma3",
messages=[{"role": "user", "content": "名刺に記載された情報を抽出してください", "images": ["./card.jpg"]}],
format=BusinessCard.model_json_schema(),
options={"temperature": 0},
)
print(BusinessCard.model_validate_json(r.message.content))
精度を高めるコツ
- temperature を 0 に設定すると、出力が安定します。
- プロンプトでも形式と各フィールドの意味を説明します。schema だけに頼らないでください。たとえば、「urgency は 1~5 の整数で、5 が最も緊急です」と指定します。
- フィールド名はわかりやすくします(
oidよりorder_idが適切です)。 - データがない可能性のあるフィールドは空値を許可します(
str | None)。モデルが値を埋めるために内容を捏造するのを防げます。 - 構造化出力で保証されるのは形式であり、内容の正確さではありません。重要なデータは必ず検証してください。
更新日 2026年10月12日