構造化出力の使い方
構造化出力の使い方¶
このページでは、PublicLLMコンポーネントでStructured Output(構造化出力)を使用する方法を解説します。Structured Outputを使うと、LLMの出力をJSON形式で取得でき、プログラムで処理しやすくなります。
Structured Outputとは?¶
Structured Outputは、LLMの出力を事前に定義したJSON Schemaに従った構造化データとして取得する機能です。
通常のLLM出力:
Structured Output:
対応プロバイダー¶
Structured Outputは以下のプロバイダーで利用できます:
| プロバイダー | 対応モデル |
|---|---|
| OpenAI | gpt-4o, gpt-4o-mini など |
| Anthropic | claude-3-5-sonnet-20241022 など |
| Gemini | gemini-1.5-pro, gemini-1.5-flash など |
JSON Schema の書き方¶
基本構造¶
Structured Outputで使用するJSON Schemaは、以下の基本構造を持ちます:
{
"type": "object",
"properties": {
"プロパティ名": {
"type": "データ型",
"description": "プロパティの説明"
}
},
"required": ["必須プロパティ名"],
"additionalProperties": false
}
重要なルール¶
OpenAI Structured Outputの制約(必須)
OpenAIのStrict modeを使用する場合、以下の制約があります:
additionalProperties: false- オブジェクトには定義されたプロパティのみを含める- すべてのプロパティを
requiredに含める - オプショナルなプロパティは避ける - すべてのプロパティに
descriptionを含める - 各プロパティの説明は必須 - ネストしたオブジェクトにも同様のルールを適用
データ型一覧¶
基本的なデータ型¶
| 型 | 説明 | 例 |
|---|---|---|
string | 文字列 | "hello" |
integer | 整数 | 42 |
number | 数値(小数含む) | 3.14 |
boolean | 真偽値 | true / false |
null | null値 | null |
array | 配列 | [1, 2, 3] |
object | オブジェクト | {"key": "value"} |
文字列型の詳細¶
列挙型(enum)を使う場合:
{
"emotion": {
"type": "string",
"enum": ["happy", "sad", "neutral", "angry"],
"description": "感情の状態"
}
}
数値型の詳細¶
整数:
数値(制約付き):
配列型の詳細¶
文字列の配列:
配列の長さを指定:
{
"choices": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 4,
"maxItems": 4,
"description": "4つの選択肢"
}
}
オブジェクトの配列:
{
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "商品名"
},
"price": {
"type": "integer",
"description": "価格(円)"
}
},
"required": ["name", "price"],
"additionalProperties": false
},
"description": "商品リスト"
}
}
ネストしたオブジェクト¶
{
"type": "object",
"properties": {
"user": {
"type": "object",
"description": "ユーザー情報",
"properties": {
"name": {
"type": "string",
"description": "ユーザー名"
},
"age": {
"type": "integer",
"description": "年齢"
}
},
"required": ["name", "age"],
"additionalProperties": false
}
},
"required": ["user"],
"additionalProperties": false
}
実践的なスキーマ例¶
例1: 盛り上がり判定¶
会話の盛り上がり度を1〜5で判定する場合:
{
"type": "object",
"properties": {
"excitement": {
"type": "integer",
"description": "会話の盛り上がり度(1〜5)"
},
"reason": {
"type": "string",
"description": "判定理由"
}
},
"required": ["excitement", "reason"],
"additionalProperties": false
}
例2: 感情分析¶
ユーザーの発言から感情を分析する場合:
{
"type": "object",
"properties": {
"emotion": {
"type": "string",
"enum": ["happy", "sad", "angry", "surprised", "neutral"],
"description": "検出された感情"
},
"confidence": {
"type": "number",
"description": "信頼度(0〜1)"
}
},
"required": ["emotion", "confidence"],
"additionalProperties": false
}
例3: 4択問題の生成¶
クイズの4択選択肢を生成する場合:
{
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "問題文"
},
"choices": {
"type": "array",
"items": {"type": "string"},
"minItems": 4,
"maxItems": 4,
"description": "4つの選択肢"
},
"correct_index": {
"type": "integer",
"description": "正解のインデックス(0〜3)"
}
},
"required": ["question", "choices", "correct_index"],
"additionalProperties": false
}
例4: 会話フェーズの判定¶
会話のフェーズを判定する場合:
{
"type": "object",
"properties": {
"phase": {
"type": "string",
"enum": ["greeting", "main_topic", "closing", "off_topic"],
"description": "現在の会話フェーズ"
},
"should_transition": {
"type": "boolean",
"description": "次のフェーズに移行すべきか"
},
"next_phase": {
"type": "string",
"enum": ["greeting", "main_topic", "closing", "off_topic"],
"description": "推奨される次のフェーズ"
}
},
"required": ["phase", "should_transition", "next_phase"],
"additionalProperties": false
}
例5: 複数の評価項目¶
複数の観点から評価を行う場合:
{
"type": "object",
"properties": {
"evaluations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"category": {
"type": "string",
"description": "評価カテゴリ"
},
"score": {
"type": "integer",
"description": "スコア(1〜10)"
},
"comment": {
"type": "string",
"description": "コメント"
}
},
"required": ["category", "score", "comment"],
"additionalProperties": false
},
"description": "評価リスト"
},
"overall_score": {
"type": "integer",
"description": "総合スコア"
}
},
"required": ["evaluations", "overall_score"],
"additionalProperties": false
}
PublicLLMでの使用方法¶
基本的な設定¶
PublicLLMコンポーネントでresponse_schemaを設定すると、Structured Outputモードになります。
Structured Outputの結果はvalues.responseにdict形式で格納されます。
PublicLLMの設定項目¶
| パラメータ | 値 |
|---|---|
| provider | openai(またはanthropic, gemini) |
| model | gpt-4o-mini など |
| prompt | Jinja2テンプレート |
| response_schema | JSON Schema(JSON形式) |
streamingは自動的にオフ
response_schemaを設定すると、streamingは自動的にfalseになります。Structured Outputはストリーミングをサポートしていません。
プロンプトの書き方¶
Structured Outputを使用する場合でも、プロンプトで期待する出力形式を説明することを推奨します:
会話の盛り上がり度を1〜5で判定してください。
【評価基準】
- 1: 会話が成立していない
- 2: 最低限の受け答え
- 3: 普通の会話
- 4: 活発な会話
- 5: 非常に盛り上がっている
【会話履歴】
{% for msg in values.history %}
{{ msg.role }}: {{ msg.text }}
{% endfor %}
JSON形式で回答してください。
実装パターン¶
パターン1: 盛り上がり判定 → State保存¶
graph LR
A[DialogHistory] --> B[PublicLLM<br/>盛り上がり判定]
B --> C[StateSet<br/>結果を保存]
style A fill:#e8f5e9
style B fill:#fff4e1
style C fill:#e1f5ff 1. DialogHistory設定:
role_filter:["user*", "assistant"]max_history_items:10
2. PublicLLM設定:
provider:openaimodel:gpt-4o-miniprompt:
response_schema:
3. StateSet設定:
conversation.excitement:values.response.excitement
パターン2: Gateで条件付き実行¶
Structured Outputの結果をGateの条件として使用し、特定の条件を満たした場合のみ後続処理を実行する場合:
graph LR
A[PublicLLM<br/>判定] --> B[StateSet<br/>結果を保存]
B --> C[Gate<br/>ポジティブ判定]
C --> D[特別な処理]
style A fill:#fff4e1
style B fill:#e1f5ff
style C fill:#ffe8e8
style D fill:#e8f5e9 PublicLLM設定:
response_schema:
StateSet設定:
user.is_positive:values.response.is_positive
Gateの設定:
trigger:lambda: states.user.is_positive == True
Gateの動作
Gateは条件がTrueの場合のみ後続のコンポーネントを実行します。条件がFalseの場合、Gate以降の処理はスキップされます。
パターン3: 複数の値を一度に取得¶
複数の情報を一度のLLM呼び出しで取得する場合:
graph LR
A[入力] --> B[PublicLLM<br/>複合分析]
B --> C[StateSet<br/>複数値を保存]
style B fill:#fff4e1
style C fill:#e1f5ff PublicLLM設定:
response_schema:
{ "type": "object", "properties": { "emotion": { "type": "string", "enum": ["happy", "sad", "neutral"], "description": "感情" }, "topic": { "type": "string", "description": "話題" }, "intent": { "type": "string", "enum": ["question", "statement", "request"], "description": "意図" } }, "required": ["emotion", "topic", "intent"], "additionalProperties": false }
StateSet設定:
| キー | 値 |
|---|---|
user.emotion | values.response.emotion |
conversation.topic | values.response.topic |
user.intent | values.response.intent |
トラブルシューティング¶
よくある問題と解決策¶
1. スキーマエラーが発生する¶
原因: スキーマの構文が正しくない、または必須項目が不足している
解決策:
- JSONの構文を確認する(カンマの有無、括弧の対応など)
- additionalProperties: falseを忘れていないか確認
- すべてのプロパティがrequiredに含まれているか確認
- すべてのプロパティにdescriptionが含まれているか確認 (必須)
2. 期待した型が返ってこない¶
原因: プロンプトまたはdescriptionの説明が不十分
解決策:
- プロンプトで期待する出力形式を明確に説明する
- descriptionフィールドで各プロパティの意味と期待する値を詳しく説明する
3. nullが返ってくる¶
原因: LLMが適切な値を生成できなかった
解決策:
- プロンプトをより具体的にする
- enumを使う場合は、選択肢を適切に設定する
- temperatureを下げてより確定的な出力を得る
4. 配列の長さが期待通りでない¶
原因: minItems/maxItemsの制約が効いていない
解決策:
- プロンプトで明示的に「4つの選択肢を生成してください」などと指定する
- モデルによっては配列長の制約が完全に守られない場合がある
ベストプラクティス¶
1. シンプルなスキーマを心がける¶
複雑なネストは避け、できるだけフラットな構造を使用します。
{
"type": "object",
"properties": {
"score": {
"type": "integer",
"description": "スコア(1〜10)"
},
"reason": {
"type": "string",
"description": "判定理由"
}
},
"required": ["score", "reason"],
"additionalProperties": false
}
2. descriptionは必須¶
OpenAIのStrict modeでは、すべてのプロパティにdescriptionを含める必要があります。descriptionはLLMがプロパティの意味を理解し、適切な値を生成するために重要です。
descriptionがないとエラーになる
descriptionを省略すると、OpenAIのStrict modeではスキーマエラーが発生する可能性があります。
3. enumで選択肢を制限する¶
自由テキストよりもenumで選択肢を制限すると、より一貫した結果が得られます。
{
"category": {
"type": "string",
"enum": ["greeting", "question", "complaint", "other"],
"description": "発言のカテゴリ"
}
}
4. プロンプトとスキーマを一致させる¶
プロンプトで説明する出力形式とスキーマが一致していることを確認します。
5. エラーハンドリングを考慮する¶
StateSetやGateでStructured Outputの結果を使用する際は、値が存在しない場合のフォールバックを考慮します。