Dify は、LLM を使うアプリを画面上で「ノード(処理の箱)」をつないで作るツールです。 1回の入力を処理して結果を返すなら「ワークフロー」、会話を続けるなら「チャットフロー」を選び、開始ノードから順に LLM・知識検索・条件分岐などをつなぎます。作ったアプリは、DSL(YAML ファイル)で書き出せ、API から呼べます。

アプリの種類 DSL の mode 最後に置くノード API
ワークフロー workflow 出力(Output。以前の「終了」) POST /v1/workflows/run
チャットフロー advanced-chat 回答(Answer) POST /v1/chat-messages
やりたいこと 見出し
どのノードを使うか決めたい ノード一覧と使いどころ
問い合わせ対応の bot を作りたい 作例:問い合わせの一次対応
YAML で受け渡し・レビューしたい DSL(YAML)の書き方
自分のシステムから呼びたい API から呼ぶ
自社のサーバーで動かしたい Docker Compose で立てる

確認環境:Dify 1.17.1(2026年9月10日のリリース。GitHub のリリース一覧で「Latest」の版)のソースを git clone して確かめました。ノードの種類はソースの定義(Dify が使うワークフロー実行ライブラリ graphon 0.7.0 の BuiltinNodeTypes と、Dify 側のトリガー・知識検索の定義)で、DSL の版と読み込みの判定は api/services/app_dsl_service.py・api/services/dsl_version.py で、作例の DSL は graphon 0.7.0 のノードの定義クラス(Pydantic)と DSL 読み込み関数で検証しました。API のリクエストの項目と SSE のイベントの種類は api/controllers/service_api・api/core/app/entities/task_entities.py で、送るコードは Dify の応答の形を返す擬似サーバー(手元の小さな HTTP サーバー)に向けて Python 3.11.15・requests 2.34.2 で確かめました。Docker Compose の構成と既定値は docker/ のファイルで、必要なスペックと手順は docs.dify.ai で確かめました。Dify を起動してモデルを動かすことはしていません(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Difyの使い方|ワークフローの作り方とノード一覧」

Dify のワークフローとは

ワークフローは「入力 → いくつかの処理 → 出力」を図で組む機能です。 プロンプト1つでは足りない処理(分類してから別々に答える、資料を検索してから答える、外部 API を呼ぶ)を、ノードの組み合わせで作れます。

比べる点 ワークフロー チャットフロー
向く用途 1回で終わる処理(要約・分類・データ変換・定期実行) 会話を続ける bot(問い合わせ対応・社内ヘルプ)
入力 開始ノードで決めた変数 ユーザーの発言(sys.query)と、開始ノードの変数
結果の返し方 出力ノードの変数をまとめて返す 回答ノードの文を返す(途中で何度でも置ける)
会話の記憶 無い LLM ノードのメモリ・会話変数
定期実行・Webhook トリガーで起動できる -

関連記事「AIエージェントとは|仕組みとワークフローとの違い・作り方入門」で、決まった手順の「ワークフロー」と、手順をモデルが決める「エージェント」の違いを扱っています。

ワークフローの作り方(手順)

  1. アプリを作る:スタジオで「最初から作成」を選び、種類(ワークフロー/チャットフロー)を選ぶ
  2. モデルを使えるようにする:設定の「モデルプロバイダー」で、OpenAI などのプラグインを入れ、API キーを登録する
  3. 開始ノードに入力を決める:ワークフローなら text などの変数を足す。チャットフローは sys.query(ユーザーの発言)が最初からある
  4. ノードを足してつなぐ:ノードの右の「+」から次のノードを選ぶ。前のノードの出力は {{#ノードID.変数名#}} で参照する
  5. 出口を置く:ワークフローは出力ノード、チャットフローは回答ノード
  6. 試す:右上の「実行」(チャットフローは「プレビュー」)で動かし、各ノードの入力・出力・かかった時間を確かめる
  7. 公開する:「公開する」を押すと、Web アプリと API から使える。API キーは「API アクセス」で作る

変数の参照のしかた

書き方 中身
{{#ノードID.変数名#}} 前のノードの出力。例:{{#llm.text#}}
{{#sys.query#}} チャットフローのユーザーの発言
{{#sys.files#}} ユーザーが添付したファイル
{{#env.名前#}} 環境変数(API キーなどの定数)
{{#conversation.名前#}} 会話変数(チャットフローで会話のあいだ持つ値)
{{#context#}} LLM ノードの「コンテキスト」に入れた知識検索の結果

ノード設定の入力欄では、/ を打つと変数を選べます。ソースの定義では、sys・env・conversation は決まった名前空間です(ドキュメントの画面の説明では、ユーザーの発言が userinput.query と表示される箇所があります)。

ノード一覧と使いどころ

Dify 1.17.1 のソースで決まっているノードの種類は、次のとおりです。 「DSL の type」は YAML の data.type に書く値です。

入口と出口

ノード(画面) DSL の type 使いどころ
開始(User Input) start 入力の変数(文字・段落・選択・数値・ファイル)を決める
出力(Output。以前は End) end ワークフローの結果を返す。チャットフローでは使えない
回答(Answer) answer チャットフローで返答の文を返す。途中にも置ける
スケジュールトリガー trigger-schedule 決まった時刻にワークフローを起動
Webhook トリガー trigger-webhook 外部からの HTTP で起動
インテグレーショントリガー trigger-plugin プラグインのイベントで起動

ドキュメントによると、出力ノードは以前「End」という名前で、今は必須ではありません。複数置くと、実行された全部の出力ノードの変数が1つにまとめられ、名前が重なると後から来た値で上書きされます。

LLM と知識

ノード DSL の type 使いどころ
LLM llm 文章の生成・要約・判断。コンテキスト・メモリ・構造化出力・画像の入力を設定できる
知識検索(Knowledge Retrieval) knowledge-retrieval ナレッジ(登録した文書)から関係する部分を探す。出力は result
質問分類器(Question Classifier) question-classifier 入力を LLM でクラスに分け、クラスごとに分岐する
パラメータ抽出(Parameter Extractor) parameter-extractor 文から日付・金額・名前などを取り出して変数にする
エージェント(Agent) agent 推論とツールの呼び出しをくり返して作業させる
人の入力(Human Input) human-input 途中で止めて、人の確認・入力を待つ

分岐とくり返し

ノード DSL の type 使いどころ
条件分岐(IF/ELSE) if-else 文字を含む・等しい・空などの条件で分ける
イテレーション(Iteration) iteration 配列の要素ごとに同じ処理をする(中の始まりは iteration-start)
ループ(Loop) loop 条件を満たすまでくり返す(loop-start・loop-end)
変数集約(Variable Aggregator) variable-aggregator 分かれた枝の結果を1つの変数にまとめる

データの加工と外部連携

ノード DSL の type 使いどころ
コード(Code) code Python か JavaScript で加工する。main() が dict を返す
テンプレート(Template) template-transform Jinja2 で文を組み立てる
文書抽出(Document Extractor) document-extractor アップロードされた PDF・Word などから文字を取り出す
リスト操作(List Operator) list-operator 配列の絞り込み・並べ替え・先頭 N 件
変数代入(Variable Assigner) assigner 会話変数に値を書き込む(チャットフロー)
HTTP リクエスト http-request 外部の API を呼ぶ
ツール tool プラグインのツール(検索・Slack など)や、他のワークフローを呼ぶ

ソースには、このほか RAG パイプライン用の datasource・knowledge-index と、以前の DSL を読むための variable-assigner(変数集約の以前の type)があります。

ノードの選び方の目安

したいこと 向くノード 理由
「障害・質問・要望」に分ける 質問分類器 分岐の出口がクラスごとにでき、条件を書かなくてよい
「件名に【至急】を含む」で分ける 条件分岐 LLM を呼ばないので速く、結果が毎回同じ
文から値を取り出して次のノードで使う パラメータ抽出 型つきの変数になる
文字の整形・計算・JSON の加工 コード LLM より速く、結果が毎回同じ
決まった形の文を組み立てる テンプレート コードより手軽
複数の文書を1件ずつ要約する イテレーション 配列を受けて要素ごとに処理する
分岐した結果を後ろで1本にする 変数集約 どの枝を通っても同じ変数名で受け取れる

作例:問い合わせの一次対応(チャットフロー)

問い合わせを3つに分け、使い方の質問はナレッジを根拠に答え、不具合は状況を聞き取り、その他は窓口を案内する bot です。

開始 ─→ 質問分類器 ─┬─ ① 使い方の質問 ─→ 知識検索 ─→ LLM(回答) ─→ 回答
                    ├─ ② 不具合の報告 ─→ LLM(聞き取り) ─→ 回答
                    └─ ③ その他       ─→ 回答(窓口の案内文)

各ノードの設定

ノード 設定 値
質問分類器 入力変数 sys.query
質問分類器 クラス ①製品の使い方・設定方法の質問 ②不具合・エラーの報告 ③料金・契約・その他
質問分類器 指示 迷ったら「料金・契約・その他」に分類する
知識検索 ナレッジ 製品マニュアルを登録したナレッジ
知識検索 Top K 4
LLM(回答) コンテキスト 知識検索の result
LLM(回答) temperature 0.2(答えのぶれを小さく)
LLM(聞き取り) メモリ 有効(直近10件)。聞き取りを何往復か続けるため

ドキュメントでは、質問分類器のクラスの説明文は LLM が分岐を選ぶときに読む部分で、重なりそうなクラスには「〜を除く」のような境目を書くと分けやすくなる、とあります。迷う入力の扱いは「指示」欄に書きます。

LLM(回答)のシステムプロンプト

あなたは製品サポートの担当です。次の資料だけを根拠に、手順を番号付きで答えます。
資料に無いことは「担当者から折り返します」と答えます。

<資料>
{{#context#}}
</資料>

{{#context#}} は、コンテキストに知識検索の result を入れたときだけ中身が入ります。 コンテキストを設定し忘れると、資料の無いプロンプトになります。資料に無いことを答えさせない書き方は、関連記事「ハルシネーション対策のプロンプト集|根拠と出典を求める書き方」で扱っています。

LLM(聞き取り)のシステムプロンプト

あなたは不具合受付の担当です。原因は推測で断定しません。
発生日時・操作の手順・エラーの文・使っている環境のうち、まだ聞いていないものを最大3つ質問します。

聞き取った内容を記録したいときは、変数代入ノードで会話変数に書き込むか、HTTP リクエストノードでチケットの API に送ります。

DSL(YAML)の書き方

DSL は、Dify のアプリを YAML で表したファイルです。 スタジオの「DSL をエクスポート」で書き出し、「DSL ファイルをインポート」で読み込めます。Git で管理する、別の環境へ移す、レビューする、といった用途に使えます。

上の作例の DSL

app:
  description: 問い合わせを分類し、使い方はナレッジで回答、不具合は聞き取り、その他は窓口を案内する
  icon: 🤖
  icon_background: '#FFEAD5'
  mode: advanced-chat
  name: 問い合わせ一次対応
  use_icon_as_answer_icon: false
dependencies: []
kind: app
version: 0.7.0
workflow:
  conversation_variables: []
  environment_variables: []
  features: {}
  graph:
    edges:
    - {id: start-classifier, source: start, sourceHandle: source, target: classifier, targetHandle: target}
    - {id: classifier-1-retrieval, source: classifier, sourceHandle: '1', target: retrieval, targetHandle: target}
    - {id: classifier-2-llm_bug, source: classifier, sourceHandle: '2', target: llm_bug, targetHandle: target}
    - {id: classifier-3-answer_other, source: classifier, sourceHandle: '3', target: answer_other, targetHandle: target}
    - {id: retrieval-llm_howto, source: retrieval, sourceHandle: source, target: llm_howto, targetHandle: target}
    - {id: llm_howto-answer_howto, source: llm_howto, sourceHandle: source, target: answer_howto, targetHandle: target}
    - {id: llm_bug-answer_bug, source: llm_bug, sourceHandle: source, target: answer_bug, targetHandle: target}
    nodes:
    - id: start
      type: custom
      position: {x: 80, y: 280}
      data:
        type: start
        title: 開始
        desc: ''
        variables: []
    - id: classifier
      type: custom
      position: {x: 380, y: 280}
      data:
        type: question-classifier
        title: 質問分類器
        desc: ''
        query_variable_selector: [sys, query]
        model:
          provider: langgenius/openai/openai
          name: gpt-6-luna
          mode: chat
          completion_params:
            temperature: 0
        classes:
        - id: '1'
          name: 製品の使い方・設定方法の質問
        - id: '2'
          name: 不具合・エラーの報告
        - id: '3'
          name: 料金・契約・その他
        instruction: 迷ったら「料金・契約・その他」に分類する
    - id: retrieval
      type: custom
      position: {x: 680, y: 120}
      data:
        type: knowledge-retrieval
        title: 知識検索
        desc: ''
        query_variable_selector: [sys, query]
        dataset_ids: [ここにナレッジのID]
        retrieval_mode: multiple
        multiple_retrieval_config:
          top_k: 4
          score_threshold: null
          reranking_enable: false
    - id: llm_howto
      type: custom
      position: {x: 980, y: 120}
      data:
        type: llm
        title: 回答を作る
        desc: ''
        model:
          provider: langgenius/openai/openai
          name: gpt-6-luna
          mode: chat
          completion_params:
            temperature: 0.2
        context:
          enabled: true
          variable_selector: [retrieval, result]
        prompt_template:
        - role: system
          text: |-
            あなたは製品サポートの担当です。次の資料だけを根拠に、手順を番号付きで答えます。
            資料に無いことは「担当者から折り返します」と答えます。

            <資料>
            {{#context#}}
            </資料>
        - role: user
          text: '{{#sys.query#}}'
        vision:
          enabled: false
    - id: answer_howto
      type: custom
      position: {x: 1280, y: 120}
      data:
        type: answer
        title: 回答(使い方)
        desc: ''
        answer: '{{#llm_howto.text#}}'
        variables: []
    - id: llm_bug
      type: custom
      position: {x: 680, y: 300}
      data:
        type: llm
        title: 聞き取り
        desc: ''
        model:
          provider: langgenius/openai/openai
          name: gpt-6-luna
          mode: chat
          completion_params:
            temperature: 0.2
        context:
          enabled: false
          variable_selector: []
        memory:
          window:
            enabled: true
            size: 10
          query_prompt_template: '{{#sys.query#}}'
        prompt_template:
        - role: system
          text: |-
            あなたは不具合受付の担当です。原因は推測で断定しません。
            発生日時・操作の手順・エラーの文・使っている環境のうち、まだ聞いていないものを最大3つ質問します。
        vision:
          enabled: false
    - id: answer_bug
      type: custom
      position: {x: 980, y: 300}
      data:
        type: answer
        title: 回答(不具合)
        desc: ''
        answer: '{{#llm_bug.text#}}'
        variables: []
    - id: answer_other
      type: custom
      position: {x: 680, y: 480}
      data:
        type: answer
        title: 回答(その他)
        desc: ''
        answer: 料金・契約のお問い合わせは、営業窓口(平日10時〜17時)で承ります。
        variables: []

dataset_ids は、自分の環境のナレッジの ID に置き換えます(エクスポートした DSL では、ナレッジの ID は暗号化された値で書き出されます)。モデルの provider は、モデルプロバイダーのプラグインの名前(例:langgenius/openai/openai)です。インポート先に同じプラグインが入っていないと、そのモデルは使えません。

この DSL を検証した結果

確かめたこと 結果
開始・質問分類器・LLM×2・回答×3 の data を graphon 0.7.0 のノードの定義クラスで読む 全部エラーなし
知識検索の data を Dify 1.17.1 の KnowledgeRetrievalNodeData で読む エラーなし(retrieval_mode='multiple'・top_k=4)
graphon の DSL 読み込み関数 inspect() 種類は app。知識検索は graphon 単体では扱えないため「unsupported」
知識検索を除いた形で inspect() loadable
エッジの source を存在しないノードにする Graph edge references an unknown node.

DSL の書き方の要点

項目 内容(ソースで確認)
version 1.17.1 が書き出す DSL の版は 0.7.0(api/constants/dsl_version.py)
kind app
app.mode workflow・advanced-chat(チャットフロー)など
workflow.graph.nodes ノードの配列。id と data.type が必須。position は画面の位置
workflow.graph.edges 線の配列。source・target にノードの id
sourceHandle 普通は source。質問分類器はクラスの id、条件分岐は true・false やケースの id
変数の参照 value_selector: [ノードID, 変数名] か、文の中の {{#ノードID.変数名#}}
dependencies 使うプラグイン(モデル・ツール)。インポート時に足りないものを案内する

質問分類器の枝は、sourceHandle にクラスの id を書きます。 ソースでも、分類の結果のクラスの id を、次に進むエッジの sourceHandle として使っていました。

版が違う DSL を読み込んだときの扱い

インポート時の判定は check_version_compatibility() で次のとおりでした(今の版は 0.7.0)。

読み込む DSL の version 結果
0.7.0(同じ) 完了
0.7.0 より新しい(例:0.8.0) 保留(確認を求める)
メジャー番号が小さい 保留
マイナー番号が小さい(例:0.3.1・0.6.0) 完了。ただし警告つき
版として読めない文字 失敗

Dify のテストに入っている DSL の見本は version: 0.3.1 のものが20件ありました。以前の版で書き出した DSL も、警告つきで読み込まれる扱いです。YAML の書き方そのものは、関連記事「Python設定ファイルの読み込み|ini・TOML・YAML・JSON比較」でも扱っています。

API から呼ぶ

公開したアプリは、アプリごとの API キー(app- で始まる)で呼びます。 キーはアプリの「API アクセス」で作り、Authorization: Bearer app-... ヘッダで送ります。セルフホストでは、nginx が /v1 を API のコンテナ(api:5001)へ渡す設定でした。

アプリ エンドポイント 必須の項目
チャットフロー・チャット POST /v1/chat-messages inputs・query・user
ワークフロー POST /v1/workflows/run inputs・user
止める POST /v1/chat-messages/{task_id}/stop・/v1/workflows/tasks/{task_id}/stop user

Python のコード(requests)

import json, os
import requests

BASE_URL = os.environ.get("DIFY_BASE_URL", "http://localhost/v1")   # API アクセス画面の URL
API_KEY = os.environ["DIFY_API_KEY"]                               # app-...
HEADERS = {"Authorization": f"Bearer {API_KEY}"}


def chat(query: str, user: str, conversation_id: str = "", inputs: dict | None = None) -> dict:
    """チャットフローに1回聞く(blocking)"""
    r = requests.post(
        f"{BASE_URL}/chat-messages",
        headers=HEADERS,
        json={
            "inputs": inputs or {},
            "query": query,
            "response_mode": "blocking",
            "conversation_id": conversation_id,   # 空なら新しい会話
            "user": user,                         # 必須。利用者を区別する文字列
        },
        timeout=120,
    )
    r.raise_for_status()
    return r.json()


def chat_stream(query: str, user: str, conversation_id: str = ""):
    """チャットフローに聞き、SSE を1イベントずつ返す(streaming)"""
    with requests.post(
        f"{BASE_URL}/chat-messages",
        headers=HEADERS,
        json={"inputs": {}, "query": query, "response_mode": "streaming",
              "conversation_id": conversation_id, "user": user},
        stream=True,
        timeout=(10, 300),
    ) as r:
        r.raise_for_status()
        for raw in r.iter_lines():                 # bytes で受ける
            line = raw.decode("utf-8")             # charset が無くても UTF-8 で読む
            if line.startswith("data: "):
                yield json.loads(line[len("data: "):])


def run_workflow(inputs: dict, user: str) -> dict:
    """ワークフローを実行する(blocking)"""
    r = requests.post(
        f"{BASE_URL}/workflows/run",
        headers=HEADERS,
        json={"inputs": inputs, "response_mode": "blocking", "user": user},
        timeout=120,
    )
    r.raise_for_status()
    return r.json()


if __name__ == "__main__":
    res = chat("パスワードの変え方は?", user="user-001")
    print(res["answer"], res["conversation_id"])

    answer = ""
    for ev in chat_stream("パスワードの変え方は?", user="user-001",
                          conversation_id=res["conversation_id"]):
        if ev["event"] == "message":
            answer += ev["answer"]
        elif ev["event"] == "message_end":
            print("usage:", ev["metadata"].get("usage"))
        elif ev["event"] == "error":
            raise RuntimeError(ev.get("message"))
    print(answer)

    wf = run_workflow({"text": "VPNにつながりません"}, user="user-001")
    print(wf["data"]["status"], wf["data"]["outputs"])

擬似サーバーで受けた、送られた中身です。

POST /v1/chat-messages
Authorization: Bearer app-test123
Content-Type: application/json
{"inputs": {}, "query": "パスワードの変え方は?", "response_mode": "blocking", "conversation_id": "", "user": "user-001"}

POST /v1/chat-messages
{"inputs": {}, "query": "パスワードの変え方は?", "response_mode": "streaming", "conversation_id": "8f0c6d2e-1b7a-4c1e-9a55-3f2b1d0e4c11", "user": "user-001"}

POST /v1/workflows/run
{"inputs": {"text": "VPNにつながりません"}, "response_mode": "blocking", "user": "user-001"}

SSE を iter_lines(decode_unicode=True) で読むと、文字化けすることがあります。 擬似サーバーが Content-Type: text/event-stream だけを返したとき(charset なし)、requests は ISO-8859-1 として読み、日本語が 1. 設å®... のように化けました。上のコードのように bytes で受けて decode("utf-8") すると、正しく読めました。

リクエストの項目(ソースの定義)

項目 内容
inputs 開始ノードの変数。必須(無ければ {})
query ユーザーの発言(chat-messages で必須)
response_mode blocking か streaming。省くと blocking(新しいエージェントのアプリを除く)
user 利用者を区別する文字列。無いと Arg user must be provided.
conversation_id 会話を続けるときに前の返答の値を入れる。空なら新しい会話。UUID の形でないとエラー
files 画像・文書など。ローカルのファイルは先にアップロードして upload_file_id を使う
auto_generate_name 会話のタイトルを自動で作るか(既定 true)
workflow_id 公開済みの特定の版を実行する(チャットフロー)

blocking の応答(チャットフロー)は、event・task_id・id・message_id・conversation_id・mode・answer・metadata・created_at を持つ JSON です。ワークフローの blocking の応答は、task_id・workflow_run_id と、data(status・outputs・error・elapsed_time・total_tokens・total_steps など)です。OpenAPI の定義には「Dify Cloud ではタイムアウトまでに返らないと接続が切れることがある」との注意があり、streaming が推奨とされています。

SSE のイベントの種類(ソースの StreamEvent)

SSE は data: {JSON} の行と空行のくり返しで、接続を保つための event: ping も届きます(ソースの convert_to_event_stream)。

イベント 中身・使いどころ 主に出るアプリ
message 回答の断片(answer)。つなげると全文 チャット・チャットフロー
message_end 回答の終わり。metadata に使用量や引用 チャット・チャットフロー
message_replace 回答の差し替え(内容の審査で置き換えたときなど) チャット・チャットフロー
message_file 回答に付いたファイル チャット・チャットフロー
text_chunk ワークフローの出力の断片(data.text) ワークフロー
workflow_started・workflow_finished 実行の始まりと終わり。終わりの data.outputs に結果 両方
workflow_paused 人の入力などで一時停止 両方
node_started・node_finished・node_retry ノードごとの始まり・終わり・再試行 両方
iteration_started・iteration_next・iteration_completed イテレーションの進み 両方
loop_started・loop_next・loop_completed ループの進み 両方
agent_thought・agent_message・agent_log エージェントの考えと途中経過 エージェント
reasoning_chunk 推論の断片(推論を分けて出す設定のとき) 両方
human_input_required・human_input_form_filled・human_input_form_timeout 人の入力の待ち・入力済み・時間切れ 両方
tts_message・tts_message_end 読み上げの音声 両方
error エラー。ストリームの途中で届く 全部
ping 接続を保つ(event: ping の行) 全部

チャットフローの回答は message、ワークフローの出力は text_chunk で届きます。 ワークフローの最終結果は workflow_finished の data.outputs で受け取ります。ストリームの読み方は、関連記事「Pythonのrequestsの使い方|タイムアウト・エラー処理・再試行まで」でも扱っています。

エラーの応答(chat-messages のドキュメント文字列)

HTTP code 意味
400 app_unavailable アプリが使えない・設定の不足
400 not_chat_app アプリの種類とエンドポイントが合わない(ワークフローに chat-messages を送ったなど)
400 provider_not_initialize モデルプロバイダーの API キーが無い
400 provider_quota_exceeded モデルプロバイダーの利用枠を使い切った
400 completion_request_error 文章の生成に失敗した
404 not_found 会話が無い(conversation_id の間違い)
429 too_many_requests このアプリへの同時のリクエストが多すぎる
429 rate_limit_error Dify Cloud のワークフロー実行の枠に達した

認証のヘッダが Bearer で始まらないと、Authorization header must be provided and start with 'Bearer' になります(ソースの wraps.py)。429 への備えは、関連記事「LLM APIの429・529エラー対処|リトライ実装とエラー一覧」で扱っています。キーの置き場所は、関連記事「.envの使い方と注意点|APIキーを安全に管理する方法」で扱っています。

Docker Compose で自分のサーバーに立てる

セルフホスト版は、リポジトリの docker/ にある docker-compose.yaml で起動します。

必要なスペック(ドキュメント)

項目 値
CPU 2コア以上
メモリ 4GiB 以上
Linux Docker 19.03 以上、Docker Compose 2.24.0 以上
macOS Docker Desktop。仮想マシンに 2 vCPU・8GiB 程度を割り当てる
Windows WSL 2 と Docker Desktop。ソースは Linux 側のファイルシステムに置く

起動の手順

git clone --branch 1.17.1 https://github.com/langgenius/dify.git
cd dify/docker
cp .env.example .env
docker compose up -d
docker compose ps        # init_permissions が Exited なのは正常

ブラウザで http://サーバーのIP/install を開き、管理者のアカウントを作ります。2回目からは / でログインします。.env.example では SECRET_KEY が空で、コメントに「空なら、ストレージのディレクトリに永続のキーを自動で作る」とありました。

起動するコンテナ(1.17.1 の docker-compose.yaml)

役割 サービス イメージ
API・画面 api・api_websocket・web langgenius/dify-api:1.17.1・langgenius/dify-web:1.17.1
非同期の処理 worker・worker_beat langgenius/dify-api:1.17.1
プラグイン plugin_daemon langgenius/dify-plugin-daemon:0.6.10-local
コードの実行 sandbox・local_sandbox langgenius/dify-sandbox:0.2.15 など
エージェント agent_backend・agent_ssrf_proxy langgenius/dify-agent-backend:1.17.1 など
DB・キャッシュ db_postgres・redis postgres:15-alpine・redis:6-alpine
ベクトル DB weaviate(既定) VECTOR_STORE で qdrant・pgvector なども選べる
入口・外への通信 nginx・ssrf_proxy nginx・squid

.env.example の COMPOSE_PROFILES は ${VECTOR_STORE:-weaviate},${DB_TYPE:-postgresql},collaboration で、ベクトル DB と DB の種類に合わせて起動するコンテナが変わります。Compose の基本は、関連記事「Docker Composeの使い方|compose.yamlの書き方と開発環境の作り方」で扱っています。ベクトル DB を PostgreSQL にまとめたいときは、関連記事「pgvectorの使い方|PostgreSQLでベクトル検索をコピペで構築」も参考になります。

1.17.1 で変わった点(リリースノート)

  • 同梱の Weaviate を使うセルフホストでは、1.17.1 を起動する前に Weaviate を 1.27.0 から 1.39.2 へ段階的に上げる手順が必要
  • 1.17.0 で、タイムアウトの既定が 1200 秒から 3600 秒に延びた
  • 1.17.0 で、環境変数 EDITION が DEPLOYMENT_EDITION に変わり、ENTERPRISE_ENABLED は無くなった

アップグレードの前に、新しい .env.example と自分の .env を比べます(ドキュメントの案内)。

設計の前に知っておく上限値(1.17.1 の既定値)

環境変数 既定値 意味
WORKFLOW_MAX_EXECUTION_STEPS 500 1回の実行で動かすノードの最大数
WORKFLOW_MAX_EXECUTION_TIME 3600 1回の実行の最大秒数
WORKFLOW_CALL_MAX_DEPTH 5 ワークフローから呼ぶワークフローの深さ
APP_MAX_EXECUTION_TIME 3600 アプリの実行の最大秒数
MAX_ITERATIONS_NUM 99 イテレーションの最大回数
LOOP_NODE_MAX_COUNT 100 ループの最大回数
MAX_VARIABLE_SIZE 204800 変数1つの最大バイト数
CODE_MAX_STRING_LENGTH 400000 コードノードが返す文字の最大長
CODE_MAX_STRING_ARRAY_LENGTH・CODE_MAX_OBJECT_ARRAY_LENGTH 30 コードノードが返す配列の最大長
CODE_MAX_NUMBER_ARRAY_LENGTH 1000 数値の配列の最大長
CODE_MAX_DEPTH 5 コードノードが返すオブジェクトの深さ
SANDBOX_WORKER_TIMEOUT 15 コードの実行の最大秒数
HTTP_REQUEST_NODE_MAX_TEXT_SIZE 1048576 HTTP リクエストノードで扱う文字データの最大バイト数
UPLOAD_FILE_SIZE_LIMIT 15 アップロードの上限(MB)

値は docker/envs/core-services/ の *.env.example にあります。コードノードで31件以上の文字列の配列を返すと上限に当たります。 件数の多いデータは、JSON の文字列にして返すか、上限を上げます。ドキュメントでは、コードノードのサンドボックスはファイルやシステムコマンドを使えず、外への通信は SSRF プロキシ経由とされています。

よくある質問

Q. ワークフローとチャットフローは、あとで変えられますか。 A. DSL の mode が違い、置けるノード(出力と回答)も違います。作り直すつもりで選びます。会話の記憶が要るならチャットフローです。

Q. Dify Cloud とセルフホストの違いは? A. Cloud はサーバーの用意が要らず、セルフホストは自社のネットワークにデータを置けます。セルフホストでは、上の上限値を .env で変えられます。

Q. ローカル LLM を使えますか。 A. モデルプロバイダーのプラグインに Ollama などがあります。Ollama 自体は関連記事「Ollamaの使い方|ローカルLLMのコマンド一覧とAPIの呼び方」で扱っています。Docker の中の Dify から、ホストの Ollama へは localhost ではつながらない点に注意します。

Q. LangChain との使い分けは? A. Dify は画面で組み、そのまま Web アプリと API になります。LangChain は Python のコードで組みます。画面で業務の人と一緒に直したいなら Dify、細かい制御やテストをコードで持ちたいなら LangChain、という分け方ができます。LangChain は関連記事「LangChain入門|PythonでLLMアプリを作る基本とコピペ集」で扱っています。

Q. ナレッジの精度が上がりません。 A. 文書の分け方・Top K・スコアのしきい値・リランクで変わります。考え方は関連記事「RAGの精度改善の方法一覧|原因の切り分けと評価指標の計算」で扱っています。

まとめ

  • 1回で終わる処理は ワークフロー(workflow・出力ノード)、会話は チャットフロー(advanced-chat・回答ノード)
  • 前のノードの値は {{#ノードID.変数名#}}、ユーザーの発言は {{#sys.query#}}
  • 分類は 質問分類器(LLM)か 条件分岐(ルール)。分岐を戻すのは 変数集約
  • DSL は version: 0.7.0(1.17.1)。質問分類器の枝は sourceHandle にクラスの id
  • API は /v1/chat-messages(query・user 必須) と /v1/workflows/run。SSE は bytes で受けて UTF-8 で読む
  • セルフホストは 2コア・4GiB 以上、cp .env.example .env → docker compose up -d → /install
  • 上限(500 ステップ・コードの配列 30 件・コード 15 秒など)を設計の前に確かめる

参考・出典

確認日はいずれも 2026年10月13日です。

  • GitHub「langgenius/dify」Releases:https://github.com/langgenius/dify/releases
  • GitHub「langgenius/dify」1.17.1 のソース:https://github.com/langgenius/dify/tree/1.17.1
  • Dify ドキュメント「Deploy with Docker Compose」:https://docs.dify.ai/en/self-host/quick-start/docker-compose
  • Dify ドキュメント「Environment Variables」:https://docs.dify.ai/en/self-host/deploy/configuration/environments
  • Dify ドキュメント「LLM」:https://docs.dify.ai/en/use-dify/nodes/llm
  • Dify ドキュメント「Question Classifier」:https://docs.dify.ai/en/self-host/use-dify/nodes/question-classifier
  • Dify ドキュメント「Knowledge Retrieval」:https://docs.dify.ai/en/self-host/use-dify/nodes/knowledge-retrieval
  • Dify ドキュメント「Output」:https://docs.dify.ai/en/self-host/use-dify/nodes/output
  • Dify ドキュメント「Code」:https://docs.dify.ai/en/self-host/use-dify/nodes/code
  • Dify API リファレンス(OpenAPI):https://docs.dify.ai/en/api-reference/openapi_service.json
  • PyPI「graphon」:https://pypi.org/project/graphon/

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Difyの使い方|ワークフローの作り方とノード一覧」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?