
Dify API:Blocking と Streamingの仕組を理解する
今回はDifyについての実践的な記事になります。
DifyのAPIにおいて最もよく使うのは、 メッセージのやりとりを行う/chat-messages APIだと思いますが、response_modeにBlockingとStreamingという2つのモードの違いがある挙動の違いがわかりにくかったので、まとめてみました。
APIのドキュメントには書いてあるものの動きがイメージできない部分があったので、わかりやすく解説してみたいと思います。以下、Difyのチャットフローを使う前提でのAPIになります。
結局どっちを使えばいいのか
大まかに言うと「Blockingは塊で、Streamingは順々で帰って来る」という理解で合っているのですが、挙動に違いがあるので注意する必要があります。
結論から言うと、Blockingはシンプルなので簡易的なワークフローではオススメですが、レスポンスの投げ方が簡易化されているため、BlockingはDifyのプレビューで試している挙動と変わってしまう可能性があります。そのため、基本的にはStreamingをおすすめします。Streamingを使えばDifyのプレビューと同等の挙動は実現できるはずです。
Blockingについて
一言でいうと、Blockingは、「ワークフローの最初から最後までで出力されたAnswerノードの値を単純にConcat(くっつけて)して一つにして返す」というものです。

基本的に1回の処理の流れでANSWERが一つしかない場合は問題ないものの、上のような1回の流れでANSWERが2回連続で返す場合、APIの出力自体はワークフローのエンドノードに最後までたどり着くまでレスポンスは帰らず、レスポンスはそれまでのANSWERを単純にくっつけたようなものになります。
Streamingについて
Streamingはテキストを1文字(正確にはチャンク区切り)づつパラパラっと返すためのものなのか、Nodeを順繰り返していくものなのか、というと、答えは両方です。出力されるメッセージをチャンクごとにレスポンスしつつ、各ノードの開始時・終了時にもレスポンスを返してくれます。

Streamingではワークフローをスタートから順繰りに実行していく中でいろいろな種類のイベントが返ってきますが、各eventの種類で判断します。
チャンクごとに文字をバラバラと返してくれるものは、 message という種別のeventとして返ってくるので、アプリ側でチャンクごとに表示していきたい場合は、messageのeventを拾うことが実現ができます。
ここからはドキュメントにも書いていない細かい仕様になりますが、これはどうやらLLMのアウトプットを直接ANSWERノードで指定しているときのみ返されるようです。つまり、以下のような場合は返しますが、

このようにLLMの出力が中間変数に入ってANSWERにでていく場合、チャンクごとの出力は飛んでこないようです。

node_started, node_finished は Variable Assignerなどユーザーから見えないものも含めてすべてのノードの開始時・終了時に返すようです。以下はANSWERノードのnode_finishedのレスポンスの例ですが、dataの中のoutputsの中のanswerという部分にアンサーのテキストが返ってきます。
{
"event": "node_finished",
"conversation_id": "68e74ca8-3fa8-4f94-91bd-54a33df64acd",
"message_id": "b53a08a5-367b-48ae-b6b0-b6b7e9b2662d",
"created_at": 1737269759,
"task_id": "f4401318-c510-4c41-9038-096f9ffbf059",
"workflow_run_id": "e9c9eeab-4b3f-4ce9-a09a-179201698643",
"data": {
"id": "3d30a789-e5fd-4ad9-8107-5a5fe21841aa",
"node_id": "answer",
"node_type": "answer",
"title": "Answer",
"index": 4,
"predecessor_node_id": "1737269484925",
"inputs": null,
"process_data": null,
"outputs": {
"answer": "Hello! How are you today?",
"files": []
},
"status": "succeeded",
"error": null,
"elapsed_time": 0.034447,
"execution_metadata": null,
"created_at": 1737269759,
"finished_at": 1737269759,
"files": [],
"parallel_id": null,
"parallel_start_node_id": null,
"parent_parallel_id": null,
"parent_parallel_start_node_id": null,
"iteration_id": null
}
}
node_typeがanswerのnode_finishedのレスポンスを見て表示していけば、ASNWERノードで返されるものを順繰り返していくといったものが作れます。
workflow_finishedはエンドノードまでたどり着いたときに返されるイベントで、ここにあるoutputsの中のanswerが、ここまでに表示されたANSWERが連結されたもので、いわばBlockingのときに返されるものになります。したがって、StreamingモードでもBlockingと同様のレスポンスを得ることができます。
StreamingかBlockingかでレスポンスの形式は大きく異なるため、APIを利用するアプリケーション側では、最初にどちらを使うかを決めた上で実装しないと、手戻りが大きくなる可能性があります。やや複雑ではありますが、Streamingを使っておけば問題になることはないでしょう。