🎃

【Dify】LLMノードの新機能「構造化出力」を徹底解説

に公開

🤖 「構造化出力」の正しい使い方を徹底解説!

この記事では、「構造化出力」を初めて使う方でもわかるように、その有用性と開発効率が劇的にアップする便利な小技注意点、さらには実際に簡単なデモアプリの作成を通してその具体的な使い方まで丁寧に解説していきます。

👤 対象読者

  • Difyを使ってアプリ開発をしている方
  • LLMの長い回答から特定のデータだけを確実に取り出したい方
  • プロンプトやコードでのLLMの出力形式の調整に、うんざりしている方
  • 「構造化出力」という機能は知っているが、なんとなくで使っている方

📑 構造化出力を使用できるDifyのバージョン

エディション 利用可能になったバージョン
SaaS版(Dify Cloud) v1.3.0 以降
Community版(Self-Hosted) v1.3.0 以降

1. はじめに

💡 こんな経験ありませんか?

  • LLMからの長い回答の中から、特定の部分だけ後続のノードで使いたい
  • プロンプトに「必ずJSON形式で返してください」と書いても時折異なる形式でレスポンスが返り、エラーを引き起こす
  • response_formatでJSON Schemaを指定し、毎回コードノードでパース処理をして使用している
    こんな悩みを解決するのが、LLMノードの 「構造化出力」 です。

2. Difyの「構造化出力」とは?

Difyの「構造化出力」は、LLMノードに標準搭載された出力整形機能です。

これまでは、モデルの出力を整ったJSON形式にしたい場合、コードノードでパース処理を自作する必要がありました。

しかし、この機能を使えば、LLMノード単体で出力を定義済みのJSON構造に自動整形し、さらにその中身を変数として後続ノードで直接利用できます。

つまり、コードを書かずに 「構造化+変数化」 まで完結できるようになり、
開発フローのシンプル化・バグ削減・メンテナンス性の向上を一度に実現できます。

🚀 構造化出力が効くシーン:テンプレート生成を安定・低コストに

💡 使用シーン例:

議事録や報告書のテンプレートがあり、「結論」「ネクストアクション」などの見出しごとに、LLMが生成した内容を自動で挿入したいケース。


❌ 従来の課題(コストと安定性)
1つのLLMノードに対して、

「結論は〇〇、ネクストアクションは××のフォーマットで出力して」
とプロンプトで指示しても、フォーマットが崩れる/不要なテキストが混ざる/モデルのバージョンアップで出力が変わるなど、安定性が低くなりがちでした。
そのため、「結論用LLM」「ネクストアクション用LLM」とノードを分割しなくてはならないケースが多く、結果として 処理コストが3〜4倍 に膨れ上がることもありました。


✅ 構造化出力を活用した解決策
「構造化出力」を使えば、1回のLLM実行で 各項目(結論・ネクストアクションなど)を明確に分離してJSON構造化 できます。
スキーマに基づいて自動整形・変数化されるため、安定した出力と保守性の高いフロー設計が可能です。
結果として、実行コストを抑えつつ、出力品質を担保できる ― この点が構造化出力の大きな価値です。

🌀 構造化出力が登場する前によくあった課題:LLM出力のJSON形式固定における課題

🧩 プロンプトで「JSON形式で出力して」と指示した場合

モデルに「この形式でJSONを出して」とプロンプトで指示しても、キー名が変わる・余計な文が混ざる・末尾カンマが入る など、正しいJSONとして扱えないことが多くありました。

結果として、正規表現で整形したり、コードノードで再パースするなどの補修処理が必要になり、出力の安定性を保つのが難しい状態でした。

構造化出力では、スキーマを明示的に定義した上で出力を生成できるため、このような揺らぎが発生しません。

形式が保証されることで、LLMノード単体で「構造化+変数化」まで完結し、後続ノードでの処理が安定・簡潔化されます。


⚙️ response_format とは何が違うの?


LLMノードのresponse_format をONにして JSON スキーマ を指定すると、形式上は整ったJSONを得られましたが、実際には単なる文字列として返される仕様でした。

そのため、後続ノードで値を扱うには、コードノードで JSON.parse を実行し、存在確認や型変換を都度行う必要がありました。

構造化出力では、このパース工程を自動化し、出力内容を変数として直接利用できます

結果として、ノード構成がシンプル化し、開発コスト・保守負担・エラーリスクをすべて大幅に削減できます。


🧱 用語補足

📦 JSON形式とは?

JSON(JavaScript Object Notation) は、システム間でデータをやり取りするための一般的なフォーマットです。

「キー」と「値」のペアで情報を表現するシンプルな構造を持ち、人間にも機械にも理解しやすい形式です。

🔧 パースとは?

パース(parse) とは、文字列データをフォーマットの規則に従って分解し、プログラムで扱いやすい構造に変換する処理です。

Difyでは、LLMが出力したJSON文字列を自動的に読み取り、後続ノードから参照できるデータオブジェクトに変換しています。

📝 JSONスキーマとは?

JSONスキーマ(Schema) は、「どんなキーがあり、どんな型の値を取るか」を定義するデータ構造の設計図です。

構造化出力では、このスキーマをもとにLLMが出力を生成するため、常に一定の構造でデータを受け取ることができます。


⚙️ 構造化出力の仕組み

構造化出力は、以下の3ステップで動作します。

  1. LLMノードがJSON形式で出力
    あらかじめ定義したJSONスキーマ(構造の設計図)に従い、LLMが情報をJSON形式で生成します。
  2. Difyが内部で自動パース
    出力されたJSON文字列はDify内部で自動的に構造化され、個々の要素を変数として扱える形に変換されます。
    json.parse() のようなコードを書く必要はありません。
  3. 後続ノードで変数として利用可能に
    変換されたデータは、後続ノードから直接参照できるようになります。
    この一連の流れはすべてLLMノード内部で完結しており、UI上の設定だけで「生成 → パース → 変数化」を実現できます。

🍳 例:レシピ情報を構造化してみる

たとえば、LLMに「親子丼のレシピ情報を出力して」と指示したとします。
通常のテキスト出力では内容が混在しますが、構造化出力を使うと以下のような整ったJSONで出力されます。

{
  "dish_name": "親子丼",
  "summary": {
    "category": {
      "type": "丼物",
      "description": "鶏肉と卵を使った日本の代表的な丼料理"
    },
    "difficulty": "簡単"
  },
  "main_ingredients": [
    { "name": "鶏もも肉", "quantity": "1枚" },
    { "name": "卵", "quantity": "2個" },
    { "name": "玉ねぎ", "quantity": "1/4個" },
    { "name": "ご飯", "quantity": "丼1杯" }
  ]
}

また、Difyはこの出力を内部的にパースし、
たとえば{{structured_output.dish_name}}{{structured_output.summary.difficulty}}のように、後続ノードで個別の項目を変数として直接参照できる状態にしてくれます。


🌟 「構造化出力」のメリット

  • LLMの出力を型で指定できる
    LLMに「この形式で出して」と指示しても崩れることがありますが、構造化出力を使えばスキーマ通りのJSONが必ず出力されます。

  • データを直接参照できる(後処理不要)
    自動パース機能により、コードノードを追加してJSONを変数化する必要がなくなります。
    後続ノードでは以下のようにGUI上で項目を選択して利用できます。

  • 外部連携や自動処理との相性が良い
    たとえば、議事録やメールから「タスク名・担当者・期日」を構造化出力で抽出すれば、
    そのまま外部API経由でタスクリストを作成したり、カレンダー登録を自動化したりできます。

  • GUIでの直感的なスキーマ設計に対応している
    構造化出力のスキーマ定義にはコードを書かずともJSONの構造を組み立てられるGUI(Visual Editor)を使用できます。

    このように、直感的にわかりやすいUIで複雑なJSON構造も作成することができます。
    また、右側の JSON Schema のタブを開けばJSONコードを確認でき、これらはお互いに連動しているので、片方を編集すればもう一方も自動的に修正されます。

  • JSONスキーマを生成するジェネレーター機能が備わっている
    構造化出力のJSON設定欄ではLLMを利用して自然言語で簡単にJSONスキーマを生成してくれる機能があります。

    この便利な使い方については3節で触れます。

3. 「構造化出力」の便利技・注意点

💡 便利技

  • JSONスキーマジェネレーターの活用術
    複雑なスキーマを作成するのは、少々骨が折れますよね。かといって、現状のJSONスキーマジェネレーターにゼロからプロンプトで指示を送っても、書式指定を守らないことがあります。
    そんなときは、次のような方法がおすすめです。
    まずGUIのVisual Editorでいくつか主要な要素を登録します。

    続いて、JSON Schema のタブですでにあるJSONコードをコピーし、ジェネレーターのプロンプトに貼り付けます。その後、「続きを作って」と入力した上で、作成したい構造を箇条書き+インデントで階層的に示すと、完璧なJSONスキーマを生成してくれます。

⚠️ 注意点

  • GUIエディタでの作業中のDeleteキーに注意
    Visual Editorでスキーマを作成中、入力モードではない時にDeleteキーを間違って押すと、LLMノードそのものが消えてしまいますctrl+Z をしても作成したJSONスキーマは戻らないのでよく注意してください。

  • 構造化データ中の配列(Array)の扱いについて
    構造化データの配列の中身(Array以下の変数)には、後続のノードで直接使用することはできません。
    例えば、先ほどのレシピ情報を構造化した例では、下の画像のように、Array形式のmain_ingredientsが選択できないようになっています。

    Arrayの中身にアクセスする必要がある場合は、コードノードによるパース処理を行ってあげましょう。

  • 構造化出力を使用する際は、LLMノードの設定のresponse_formatはオフにしましょう
    構造化出力と response_format の両方をオンにすると競合が発生し、エラーによって正常に動作しない事例が確認されています。

4. 実際に使ってみる(🍳料理の情報表示アプリ)

「構造化出力」の便利さを体感するために、簡単なデモアプリを作成してみます。
今回は、料理名(例:親子丼、カレー)を入力すると、LLMがその料理について解析しそれについての情報を生成することで、その料理の基本情報と必要な食材を表示できるようなアプリを作ってみます。初めての方でもわかるよう、一歩一歩丁寧に説明していきます。

Step 1: ワークフローの準備

まずは、Difyで新しいアプリを作成していきましょう!チャットフローを選び、アプリ名を入れて作成します。

Step 2: LLMノードの設定と「構造化出力」

次に、LLMノードを設定します。真ん中のLLMノードをクリックし、出てきた設定パネルで設定を変更できます。今回はLLMモデルとしてOpenAI社のgpt-4.1を使用します。
プロンプトは今回は、簡単に次のようにしてみましょう。

あなたは全ての料理を知っている料理マスターです。
ユーザーが入力した料理「{{#sys.query#}}」について、基本的な情報と主な材料を、指定されたフォーマットで出力してください。


そして、今回のテーマである構造化出力をオンにします。LLMノードの設定パネルの下の方にある構造化出力の項目をオンにしてみてください。

これをオンにすると画像のように「構造化出力が未設定です」と表示されます。これをクリックして構造化出力の設定をすることができます。

Step 3: JSONスキーマの設計

構造化出力の設定で、JSONスキーマを定義していきます。
先ほどの親子丼の例を踏まえて、構造を考えてみます。GUIとCUIのどちらを使って設定してもいいですが、今回はCUIの方で以下のJSONを貼り付けます。

{
  "type": "object",
  "properties": {
    "dish_name": {
      "type": "string"
    },
    "summary": {
      "type": "object",
      "properties": {
        "category": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string"
            },
            "description": {
              "type": "string"
            }
          },
          "required": [
            "type",
            "description"
          ],
          "additionalProperties": false
        },
        "difficulty": {
          "type": "string"
        }
      },
      "required": [
        "category",
        "difficulty"
      ],
      "additionalProperties": false
    },
    "main_ingredients": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "quantity": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "quantity"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "dish_name",
    "summary",
    "main_ingredients"
  ],
  "additionalProperties": false
}

これを入力してVisual Editorのタブに戻ると下のように、構造がうまく設定されているはずです。

これで、LLMの出力が構造化されるようになりました。試しに、料理名をこちらのLLMノードに入力してみましょう。例えば、「豚汁」と入力すると次のような出力が返ってきます。

structured_outputの欄から、出力が構造化されていることが確認できます。

Step 4: 後続ノードで構造化された変数を使う

それでは、後続のノードで構造化出力の結果を使ってみましょう。これが構造化出力の最大のメリットとなります。
まずは、料理の概要をきれいに表示するようにしてみます。LLMノードに回答ノードを繋げ、応答の欄に「構造化出力」を用いて生成した変数を記入します。以下のように、"{"を入力することで変数を選び、記入できます。

これを使用し、以下のように料理の概要が綺麗に出力されるように回答ノードを設定します。

ここで、例えば「カレーライス」と入力すると、以下のような回答が返ってきます。

配列(Array)内に入っている食材の情報を出力する方法

3節の注意点で説明した通り、Arrayに入っている食材の情報はGUI上で選択できないため、コードノードでパースする必要があります。
まず、LLMノードと回答ノードの間にコードノードを用意します。

コードノードをクリックし、設定パネルで以下のPythonコードを貼り付けます。

def main(arg1: list) -> dict:
    
    formatted_text = ""
    
    if arg1 and isinstance(arg1, list):
        for ingredient in arg1:
            name = ingredient.get('name', '名前なし')
            quantity = ingredient.get('quantity', '分量なし')
            
            # 「- 材料名 (分量)」の形式で文字列を作成します
            formatted_text += f"- {name} ({quantity})\n"
    
    return {
        'result': formatted_text
    }
コードブロックの中身について

ここのコードで行われていることを簡単に解説してみます。
まず、構造化出力のArray型、main_ingredientsを受け取り、この中身についてfor文で一つ一つ処理していきます。
Arrayの中にあるObjectにはnameとquantityの2つのフィールドがありますから、これをgetメソッドを使って取得しています。
最後に、取得した複数の材料名と分量を改行とともに一つの文字列として返しています。

入力変数のarg1には先ほどの構造化出力のmain_ingredientsを選びます。(arg2は使わないので削除しておきます。出力変数の設定もそのままで大丈夫です。)

それでは、コードノードの出力も表示させていきます。
回答ノードを次のように追記します。

ここで「シチュー」と入力すると、以下のように材料が整った形式で出力されます。

このように、構造化出力を利用することで、出力フォーマットを一定に保ち、どの入力に対しても統一された見た目で表示することが可能になります。
また、この仕組みは単なる表示制御にとどまらず、取得したデータを用いた演算処理や知識検索など、多様な応用が可能です。

(余談): HTMLで見た目を整える

先ほどの回答結果でも十分ですが、シンプルすぎると感じる場合はHTMLを用いて出力を装飾することもできます。
以下の記事を参考に、簡単なUIを構築してみました。
【執筆中】11月中に公開予定

このように、構造化出力とHTMLを組み合わせることで、データをより視覚的にわかりやすく表現することも可能です。

5. まとめ

この記事では、Difyの強力な機能「構造化出力」について、その基本から、実践的な使い方、注意点まで解説しました。
LLMノードの出力を構造化して統一し、後続のノードで活用するためのベストプラクティスは、間違いなく「構造化出力」機能を使うことです。
「構造化出力」は今までのresponse_formatによるJSONスキーマの完全上位互換です。
自動でパースして、後続のノードで変数として使えるようになるため、以前のようにresponse_formatによるJSONスキーマを使う理由はありません。これが現在使われている場合は、今後は使わない、または構造化出力に差し替えを行ってみてください。
最後に、構造化出力のメリットをおさらいします。

LLMノードの出力が統一され、予期せぬ出力を防げる
面倒だったパース処理を自動でしてくれるため、効率の良い開発ができる
後続のノードで変数として直接利用できる
GUIで直感的に構造を設計できる
ジェネレーターにより素早くJSONスキーマを構成できる

構造化出力により、今まで以上に複雑できめ細かい処理がやりやすくなりました。
この便利な構造化出力を活用して、さまざまなアプリを開発していきましょう!🔥🔥

UPGRADE tech blog

Discussion