Outlines
目次
- skill の情報
- 参考: SKILL.md 全文
- この skill を使う場面
- 導入
- はじめの一歩
- 基本の例: 分類
- Pydantic のモデルを使う
- 中心となる考え方
- 1. 制約付きのトークン選択
- 2. 出力の型
- 3. モデルのバックエンド
- 4. Pydantic との組み合わせ
- よく使う型
- 型 1: データの抜き出し
- 型 2: 分類
- 型 3: 決まった項目の入力
- 型 4: 複数の対象を抜き出す
- 型 5: コードの生成
- 型 6: まとめて処理する
- バックエンドの設定
- Transformers
- llama.cpp
- vLLM(実運用向け)
- うまく使うこつ
- 1. 型はきちんと指定する
- 2. 制約を足す
- 3. 決まった分類には列挙型を使う
- 4. プロンプトに前提を書く
- 5. 欠けるかもしれない項目を扱う
- 6. JSON の出力は必ず読み取って確かめる
- ほかの手段との比較
- 性能の特徴
- 参考資料
- あわせて読む
Outlines: JSON・正規表現・Pydantic で形を決めた LLM の生成を行います。
skill の情報
| 提供元 | 追加インストール — hermes skills install official/mlops/outlines で導入します |
| パス | optional-skills/mlops/inference/outlines |
| バージョン | 1.0.1 |
| 作者 | Orchestra Research |
| ライセンス | MIT |
| 依存関係 | outlines, transformers, vllm, pydantic |
| 対応プラットフォーム | linux, macos, windows |
| タグ | Prompt Engineering, Outlines, Structured Generation, JSON Schema, Pydantic, Local Models, Grammar-Based Generation, vLLM, Transformers, Type Safety |
参考: SKILL.md 全文
Outlines: 形を決めたテキストの生成
この skill を使う場面
Outlines は、次のようなことをしたいときに使います。
- 生成の途中から妥当な JSON/XML/コードの形を守らせる
- 型の安全な出力のために Pydantic のモデルを使う
- 手元のモデルに対応する(Transformers、llama.cpp、vLLM)
- 形を決めた生成でも推論の速さを落とさない
- JSON スキーマに沿って自動で生成する
- 文法のレベルでトークンの選ばれ方を制御する
GitHub のスター数: 12,000 以上 | 提供: dottxt.ai(旧 .txt)
> API についての注意(Outlines 1.x): この skill は現行の v1 API を対象にしています。 > 1.0 より前の補助関数(outlines.models.transformers(...)、outlines.generate.json/choice/regex/...)はなくなりました。v1 では outlines.from_transformers(...)(あるいは from_vllm、from_llamacpp、from_openai)でモデルを作り、そのモデルを出力の型とともに直接呼び出します: model(prompt, output_type)。JSON や Pydantic の出力は JSON の文字列として返るので、YourModel.model_validate_json(result) で読み取ってください。
導入
次のコマンドで導入します。使うバックエンドに合わせて追加のものを入れてください。
# Base installation
pip install outlines
# With specific backends
pip install outlines transformers # Hugging Face models
pip install outlines llama-cpp-python # llama.cpp
pip install outlines vllm # vLLM for high-throughputはじめの一歩
基本の例: 分類
次のコードは、Transformers のモデルを包んで、答えが 3 つの選択肢のどれかに必ずなるようにします。
from typing import Literal
from transformers import AutoModelForCausalLM, AutoTokenizer
MODEL_NAME = "microsoft/Phi-3-mini-4k-instruct"
# v1: wrap a Transformers model + tokenizer
model = outlines.from_transformers(
AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="auto"),
AutoTokenizer.from_pretrained(MODEL_NAME),
)
# Call the model directly with an output type
prompt = "Sentiment of 'This product is amazing!': "
sentiment = model(prompt, Literal["positive", "negative", "neutral"])
print(sentiment) # "positive" (guaranteed one of these)Pydantic のモデルを使う
次のコードは、文章から利用者の情報を抜き出し、Pydantic のモデルとして受け取ります。
from pydantic import BaseModel
from transformers import AutoModelForCausalLM, AutoTokenizer
class User(BaseModel):
name: str
age: int
email: str
MODEL_NAME = "microsoft/Phi-3-mini-4k-instruct"
model = outlines.from_transformers(
AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="auto"),
AutoTokenizer.from_pretrained(MODEL_NAME),
)
# Generate structured output (returns a JSON string)
prompt = "Extract user: John Doe, 30 years old, john@example.com"
result = model(prompt, User, max_new_tokens=200)
user = User.model_validate_json(result) # parse into the Pydantic model
print(user.name) # "John Doe"
print(user.age) # 30
print(user.email) # "john@example.com"中心となる考え方
1. 制約付きのトークン選択
Outlines は、出力の型から組み立てたオートマトンを使って、logit のレベルでトークンの生成を縛ります。
仕組み:
- 出力の型(JSON/Pydantic/正規表現/
Literal)をスキーマや文法に変換します - その文法をトークン単位のオートマトンに変換します
- 各ステップで、条件に合わないトークンを取り除きます
- 選べるトークンが 1 つしかないときは、そのまま先へ進めます
よいところ:
- 余計な負荷がない: 絞り込みがトークン単位で行われます
- 速くなる: 決まりきった部分を飛ばして進めます
- 必ず妥当: おかしな出力が出ることはありません
from pydantic import BaseModel
from transformers import AutoModelForCausalLM, AutoTokenizer
class Person(BaseModel):
name: str
age: int
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"),
)
result = model("Generate person: Alice, 25", Person)
person = Person.model_validate_json(result)2. 出力の型
v1 では、欲しい出力の型を 2 番目の引数に直接渡します。
選択肢から選ぶ(Literal)
from typing import Literal
sentiment = model("Review: This is great!", Literal["positive", "negative", "neutral"])
# Result: one of the three choicesPydantic による JSON
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: float
in_stock: bool
result = model("Extract: iPhone 15, $999, available", Product)
product = Product.model_validate_json(result) # valid Product instance正規表現(文字列で渡す)
# Generate text matching a regex pattern
phone = model("Generate phone number:", r"[0-9]{3}-[0-9]{3}-[0-9]{4}")
# Result: "555-123-4567" (guaranteed to match the pattern)数値の型
# Pass the Python type directly
age = model("Person's age:", int) # guaranteed integer
price = model("Product price:", float) # guaranteed float3. モデルのバックエンド
Outlines は、from_* の関数を通じて、手元と API の両方のバックエンドに対応しています。
Transformers(Hugging Face)
from transformers import AutoModelForCausalLM, AutoTokenizer
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"),
)
result = model(prompt, YourModel)llama.cpp
from llama_cpp import Llama
llm = Llama("./models/llama-3.1-8b-instruct.Q4_K_M.gguf", n_gpu_layers=35, n_ctx=4096)
model = outlines.from_llamacpp(llm)
result = model(prompt, YourModel)vLLM(たくさんさばきたいとき)
from vllm import LLM
llm = LLM("meta-llama/Llama-3.1-8B-Instruct", tensor_parallel_size=2)
model = outlines.from_vllm(llm)
result = model(prompt, YourModel)OpenAI(サーバー側で JSON の形を守らせる)
from openai import OpenAI
client = OpenAI()
model = outlines.from_openai(client, "gpt-4o-mini")
# API backends support JSON-schema style structured output
result = model(prompt, YourModel)4. Pydantic との組み合わせ
Outlines は Pydantic に最初から対応していて、スキーマへの変換を自動で行います。生成の結果は JSON の文字列なので、model_validate_json を呼んでモデルにします。
基本のモデル
from pydantic import BaseModel, Field
class Article(BaseModel):
title: str = Field(description="Article title")
author: str = Field(description="Author name")
word_count: int = Field(description="Number of words", gt=0)
tags: list[str] = Field(description="List of tags")
result = model("Generate article about AI", Article, max_new_tokens=300)
article = Article.model_validate_json(result)
print(article.title)
print(article.word_count) # Guaranteed > 0入れ子のモデル
class Address(BaseModel):
street: str
city: str
country: str
class Person(BaseModel):
name: str
age: int
address: Address # Nested model
result = model("Generate person in New York", Person)
person = Person.model_validate_json(result)
print(person.address.city) # "New York"列挙型と Literal
from enum import Enum
from typing import Literal
class Status(str, Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
class Application(BaseModel):
applicant: str
status: Status # Must be one of enum values
priority: Literal["low", "medium", "high"] # Must be one of literals
result = model("Generate application", Application)
app = Application.model_validate_json(result)
print(app.status) # Status.PENDING (or APPROVED/REJECTED)よく使う型
型 1: データの抜き出し
次のコードは、文章から会社の情報を抜き出して、項目ごとに表示します。
from pydantic import BaseModel
from transformers import AutoModelForCausalLM, AutoTokenizer
class CompanyInfo(BaseModel):
name: str
founded_year: int
industry: str
employees: int
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"),
)
text = """
Apple Inc. was founded in 1976 in the technology industry.
The company employs approximately 164,000 people worldwide.
"""
prompt = f"Extract company information:\n{text}\n\nCompany:"
company = CompanyInfo.model_validate_json(model(prompt, CompanyInfo, max_new_tokens=200))
print(f"Name: {company.name}")
print(f"Founded: {company.founded_year}")
print(f"Industry: {company.industry}")
print(f"Employees: {company.employees}")型 2: 分類
次のコードは、2 択の分類、多クラスの分類、確信度つきの分類をそれぞれ行います。
from typing import Literal
from pydantic import BaseModel
# Binary classification
result = model("Email: Buy now! 50% off!", Literal["spam", "not_spam"])
# Multi-class classification
category = model(
"Article: Apple announces new iPhone...",
Literal["technology", "business", "sports", "entertainment"],
)
# With confidence
class Classification(BaseModel):
label: Literal["positive", "negative", "neutral"]
confidence: float
out = model("Review: This product is okay, nothing special", Classification)
result = Classification.model_validate_json(out)型 3: 決まった項目の入力
次のコードは、書かれた内容を利用者の情報として項目ごとに読み取ります。
class UserProfile(BaseModel):
full_name: str
age: int
email: str
phone: str
country: str
interests: list[str]
prompt = """
Extract user profile from:
Name: Alice Johnson
Age: 28
Email: alice@example.com
Phone: 555-0123
Country: USA
Interests: hiking, photography, cooking
"""
profile = UserProfile.model_validate_json(model(prompt, UserProfile, max_new_tokens=250))
print(profile.full_name)
print(profile.interests) # ["hiking", "photography", "cooking"]型 4: 複数の対象を抜き出す
次のコードは、文章に出てくる人・組織・場所をまとめて取り出します。
from typing import Literal
class Entity(BaseModel):
name: str
type: Literal["PERSON", "ORGANIZATION", "LOCATION"]
class DocumentEntities(BaseModel):
entities: list[Entity]
text = "Tim Cook met with Satya Nadella at Microsoft headquarters in Redmond."
prompt = f"Extract entities from: {text}"
result = DocumentEntities.model_validate_json(model(prompt, DocumentEntities, max_new_tokens=300))
for entity in result.entities:
print(f"{entity.name} ({entity.type})")型 5: コードの生成
次のコードは、関数名・引数・説明・本体に分けて Python の関数を生成し、組み立てて表示します。
class PythonFunction(BaseModel):
function_name: str
parameters: list[str]
docstring: str
body: str
prompt = "Generate a Python function to calculate factorial"
func = PythonFunction.model_validate_json(model(prompt, PythonFunction, max_new_tokens=300))
print(f"def {func.function_name}({', '.join(func.parameters)}):")
print(f' """{func.docstring}"""')
print(f" {func.body}")型 6: まとめて処理する
次のコードは、複数の文をひとまとめに渡して、それぞれから人の情報を取り出します。
from transformers import AutoModelForCausalLM, AutoTokenizer
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
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"),
)
texts = [
"John is 30 years old",
"Alice is 25 years old",
"Bob is 40 years old",
]
# v1 accepts a list of prompts for batched generation
prompts = [f"Extract from: {t}" for t in texts]
outputs = model(prompts, Person, max_new_tokens=100)
people = [Person.model_validate_json(o) for o in outputs]
for person in people:
print(f"{person.name}: {person.age}")バックエンドの設定
Transformers
次のコードは、基本の使い方、GPU と dtype の指定、よく使われるモデルの読み込みを順に示します。
from transformers import AutoModelForCausalLM, AutoTokenizer
MODEL_NAME = "microsoft/Phi-3-mini-4k-instruct"
# Basic usage
model = outlines.from_transformers(
AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="auto"),
AutoTokenizer.from_pretrained(MODEL_NAME),
)
# GPU + dtype configuration is set on the HF model itself
model = outlines.from_transformers(
AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="cuda", torch_dtype=torch.float16),
AutoTokenizer.from_pretrained(MODEL_NAME),
)
# Popular models
for name in [
"meta-llama/Llama-3.1-8B-Instruct",
"mistralai/Mistral-7B-Instruct-v0.3",
"Qwen/Qwen2.5-7B-Instruct",
]:
model = outlines.from_transformers(
AutoModelForCausalLM.from_pretrained(name, device_map="auto"),
AutoTokenizer.from_pretrained(name),
)llama.cpp
次のコードは、GGUF のモデルを読み込み、文脈の長さや GPU に載せる層の数を指定します。
from llama_cpp import Llama
# Load GGUF model
llm = Llama(
"./models/llama-3.1-8b.Q4_K_M.gguf",
n_ctx=4096, # Context window
n_gpu_layers=35, # GPU layers
n_threads=8, # CPU threads
)
model = outlines.from_llamacpp(llm)
# Full GPU offload: set n_gpu_layers=-1 on the Llama objectvLLM(実運用向け)
次のコードは、GPU 1 枚、複数枚、量子化ありのそれぞれの読み込み方を示します。
from vllm import LLM
# Single GPU
model = outlines.from_vllm(LLM("meta-llama/Llama-3.1-8B-Instruct"))
# Multi-GPU
model = outlines.from_vllm(LLM("meta-llama/Llama-3.1-70B-Instruct", tensor_parallel_size=4))
# With quantization
model = outlines.from_vllm(LLM("meta-llama/Llama-3.1-8B-Instruct", quantization="awq"))うまく使うこつ
1. 型はきちんと指定する
# ✅ Good: Specific types
class Product(BaseModel):
name: str
price: float # Not str
quantity: int # Not str
in_stock: bool # Not str
# ❌ Bad: Everything as string
class Product(BaseModel):
name: str
price: str # Should be float
quantity: str # Should be int2. 制約を足す
from pydantic import Field
# ✅ Good: With constraints
class User(BaseModel):
name: str = Field(min_length=1, max_length=100)
age: int = Field(ge=0, le=120)
email: str = Field(pattern=r"^[\w\.-]+@[\w\.-]+\.\w+$")
# ❌ Bad: No constraints
class User(BaseModel):
name: str
age: int
email: str3. 決まった分類には列挙型を使う
# ✅ Good: Enum for fixed set
class Priority(str, Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
class Task(BaseModel):
title: str
priority: Priority
# ❌ Bad: Free-form string
class Task(BaseModel):
title: str
priority: str # Can be anything4. プロンプトに前提を書く
# ✅ Good: Clear context
prompt = """
Extract product information from the following text.
Text: iPhone 15 Pro costs $999 and is currently in stock.
Product:
"""
# ❌ Bad: Minimal context
prompt = "iPhone 15 Pro costs $999 and is currently in stock."5. 欠けるかもしれない項目を扱う
from typing import Optional
# ✅ Good: Optional fields for incomplete data
class Article(BaseModel):
title: str # Required
author: Optional[str] = None # Optional
date: Optional[str] = None # Optional
tags: list[str] = [] # Default empty list
# Can succeed even if author/date missing6. JSON の出力は必ず読み取って確かめる
# v1 returns a JSON string for Pydantic/JSON output types.
result = model(prompt, Article) # str
article = Article.model_validate_json(result) # Article instanceほかの手段との比較
| 項目 | Outlines | Instructor | Guidance | LMQL |
|---|---|---|---|---|
| Pydantic への対応 | ✅ 標準 | ✅ 標準 | ✅ あり | ❌ なし |
| JSON スキーマ | ✅ あり | ✅ あり | ✅ あり | ✅ あり |
| 正規表現による制約 | ✅ あり | ❌ なし | ✅ あり | ✅ あり |
| 手元のモデル | ✅ 全面的 | ⚠️ 一部のみ | ✅ 全面的 | ✅ 全面的 |
| API のモデル | ✅ あり | ✅ 全面的 | ✅ あり | ✅ 全面的 |
| 余計な負荷がない | ✅ そう | ❌ ちがう | ⚠️ 一部 | ✅ そう |
| 自動の再試行 | ❌ なし | ✅ あり | ❌ なし | ❌ なし |
| 学習のしやすさ | 易しい | 易しい | 易しい | 難しい |
Outlines を選ぶとよい場面:
- 手元のモデル(Transformers、llama.cpp、vLLM)を使うとき
- 推論の速さを最大限に出したいとき
- Pydantic のモデルを使いたいとき
- 形を決めた生成で余計な負荷をかけたくないとき
- トークンの選ばれ方を制御したいとき
ほかの手段を選ぶとよい場面:
- Instructor: API のモデルで自動の再試行が要るとき
- Guidance: トークンヒーリングと込み入った処理が要るとき
- LMQL: 宣言的なクエリの書き方を好むとき
性能の特徴
速さ:
- 余計な負荷がない: 形を決めた生成でも、制約なしと同じ速さです
- 決まりきった部分を飛ばす: 選択の余地がないトークンは読み飛ばします
- 生成後に検証するやり方より 1.2〜2 倍速い
メモリ:
- オートマトンは出力の型ごとに一度だけ組み立てられ、キャッシュされます
- 実行中に増える分はごくわずかです
- vLLM と組み合わせると、たくさんさばくときも効率よく動きます
正確さ:
- 出力は 100% 妥当(制約付きのオートマトンが保証します)
- 再試行の繰り返しが要りません
- トークンの絞り込みが決まった手順で行われます
参考資料
- ドキュメント: https://dottxt-ai.github.io/outlines/
- GitHub: https://github.com/dottxt-ai/outlines(スター 12,000 以上)
- Discord: https://discord.gg/R9DSu34mGd
- ブログ: https://blog.dottxt.co
あわせて読む
references/json_generation.md- JSON と Pydantic の書き方をひととおりreferences/backends.md- バックエンドごとの設定references/examples.md- 実運用で使える例