LLMの返答は毎回フォーマットが揺れ、JSONパースでアプリが止まる——この悩みを、生成の途中から形を固定するライブラリが解消します。
この記事では、Pythonライブラリ「Outlines」の概要と、Pydanticモデルで出力型を定義する手順、OpenAI・Ollama・vLLMなど複数バックエンドを同じコードで扱う方法を整理します。
この記事でわかること
- Outlinesが解決する課題と、事後パースとの違い
- PydanticモデルやEnumで出力型を定義する基本構文
- 対応モデルと出力形式(JSON・正規表現・文法)の一覧
- 類似アプローチとの違いと選び方
https://github.com/dottxt-ai/outlines
Outlinesとは
Outlinesは、LLMの出力形式を生成前に指定し、生成の過程で構造を保証するPythonライブラリです。開発元は.txt(dottxt)で、Apache-2.0ライセンスのオープンソースとして公開されています。GitHubのスター数は1万4000超、NVIDIA・Cohere・Hugging Face・vLLMなどが採用実績としてREADMEに掲げられています。
従来のやり方では、プロンプトで「JSONで返して」と頼み、返ってきた文字列をjson.loadsやPydanticで検証します。マークダウンのコードブロックや余計な説明文が混ざるとパースが失敗し、リトライや正規表現での救済が必要になります。Outlinesはこの「生成後の修復」を前提にしません。output_typeに型を渡すと、トークン生成時点で許可される文字列だけを選ぶ制約付き生成(constrained decoding)を行い、JSONスキーマ・正規表現・文法(CFG)に沿った出力を直接得ます。
どんな課題を解決するか
AIアプリ開発で構造化出力が必要になる場面は多いです。顧客メールの優先度分類、商品説明からのカテゴリ抽出、関数呼び出し用パラメータの生成など、下流のコードが「決まった型のデータ」を前提に動きます。
ここで問題になるのは、LLMがスキーマ違反のJSONを返すことです。キー名のtypo、型の不一致、途中で切れた配列——いずれも本番環境では障害に直結します。OpenAIのStructured OutputsのようにAPI側でスキーマを渡す方法もありますが、ローカルモデルやOllama・vLLMを使う場合は別の実装が要ります。Outlinesはfrom_openai・from_ollama・from_vllm・from_transformersなどのローダーで、同じmodel(prompt, output_type)の呼び方を複数バックエンドに適用できます。
主な機能
Outlinesが扱える出力型は、Pythonの型ヒントとほぼ同じ感覚で指定します。
基本型と選択肢 — intやboolのような単純型、Literal["Positive", "Negative"]やEnumによる複数選択に対応します。感情分析の3値分類など、分類タスクにそのまま使えます。
JSONスキーマとPydantic — BaseModelを継承したクラス、dataclass、TypedDict、関数の型アノテーションからスキーマを推論します。商品レビュー用にrating: Enum、pros: list[str]のようなモデルを定義し、model(prompt, ProductReview)で生成します。返り値は文字列なので、利用時はProductReview.model_validate_json(result)で検証します。
正規表現 — outlines.types.Regexでパターンを指定し、電話番号やISBN形式など、文字列レベルの制約をかけられます。
文法(CFG) — Lark形式のコンテキスト自由文法をCFGオブジェクトで渡し、SQLや独自フォーマットなど、JSON以外の構造化テキストも生成できます。ローカルモデル(Transformers、vLLMOfflineなど)では文法制約が使える一方、APIベースのOpenAIやAnthropicではサポートが限定的です。公式ドキュメントの機能マトリクスに、モデルごとの対応状況が一覧化されています。
その他、Jinjaベースのプロンプトテンプレート、バッチ処理、一部モデルでのストリーミングにも対応しています。
使い方の流れ
導入はpip install outlinesです。ローカルモデルの例は次のとおりです。
import outlines
from pydantic import BaseModel
from enum import Enum
from transformers import AutoTokenizer, AutoModelForCausalLM
class Rating(Enum):
poor = 1
fair = 2
good = 3
excellent = 4
class ProductReview(BaseModel):
rating: Rating
pros: list[str]
cons: list[str]
summary: str
model = outlines.from_transformers(
AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct", device_map="auto"),
AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct"),
)
review_json = model(
"Review: バッテリーは優秀だが発熱が気になる。",
ProductReview,
max_new_tokens=200,
)
review = ProductReview.model_validate_json(review_json)
OllamaやvLLMを使う場合は、それぞれfrom_ollama・from_vllmでクライアントを初期化し、同じく第2引数に出力型を渡します。サーバー側のモデル(OpenAI API、Ollama、vLLM)とローカル推論(Transformers、LlamaCpp、MLXLM)の両方に対応しており、開発環境と本番環境でモデルを切り替えてもアプリ側の型定義を共通化しやすい設計です。
再利用したい場合はGenerator(model, output_type)でジェネレータを作り、同じ型で繰り返し呼び出せます。
対応モデルと制約の違い
Outlinesがサポートするバックエンドは、API系(OpenAI、Anthropic、Gemini、Mistral、Ollama、vLLMなど)とローカル系(Transformers、LlamaCpp、MLXLM、VLLMOfflineなど)に分かれます。
ローカルモデルは生成ループにlogits processorを差し込めるため、JSON・正規表現・文法のほぼ全出力型が使えます。サーバーベースのモデルは、サーバー側の制約付き生成機能に依存するため、プロバイダごとに使える出力型が異なります。たとえば公式マトリクスでは、OpenAIはJSON SchemaとMultiple Choiceに対応し、Simple TypesやRegexは非対応と記載されています。OllamaはJSON Schemaに対応しますが、Simple TypesやRegexは非対応です。導入前に、使うバックエンドの行を確認するのが安全です。
最新リリースは2026年5月13日のv1.3.0で、API系モデルの例外クラスがoutlines.exceptionsに統一されるなど、マイナーな破壊的変更が含まれています。
類似ツールとの違い
LLMの構造化出力を扱う手段は複数あります。
APIネイティブのStructured Outputs — OpenAIやOllamaは、それぞれresponse_formatやformatパラメータでJSONスキーマを渡せます。単一プロバイダなら十分ですが、コードの移植性は低くなります。
事後検証+リトライ型 — structout-llmのように、生成後にPydanticで検証し失敗時にリトライする方式もあります。実装は軽い一方、リトライ回数やトークン消費が増え、保証は確率的です。
Outlinesの位置づけ — 生成時に制約をかけるため、有効なJSONやスキーマ準拠の出力を構造的に保証します。複数バックエンドを同じインターフェースで扱える点が実務上の強みです。トレードオフとして、ローカル推論ではモデル読み込みやGPUリソースが必要になり、サーバーモデルでは使えない出力型がある点に注意が要ります。
導入を検討する目安
Outlinesは、Pydanticで型を定義し、OpenAI・Ollama・vLLM・ローカルモデルを行き来しながら同じ抽出ロジックを維持したいチームに向いています。カスタマーサポートの自動振り分け、ECの商品分類、会議予約パラメータの抽出など、READMEにも実務に近いサンプルが掲載されています。
一方、単一のクラウドAPIだけを使い、プロバイダ標準のStructured Outputsで足りる場合は、依存を増やさずそちらを選ぶ選択肢もあります。複数環境での型の一貫性と、生成時保証の両方が必要なときにOutlinesの価値が最もはっきりします。