Difyの会話変数が更新されないときは、「会話変数へ書いたか」「書く経路を通ったか」「更新後の変数を読んだか」「同じ会話か」を分けて確認します。 回答文が以前と同じというだけでは、保存に失敗したのか、古い値を表示したのかを決められません。

対象は、Chatflowで複数ターンのやり取りを作っており、入力やCodeノードの値を保持したい人です。ノードを置く操作と、変数ピッカーで出力を選ぶ操作が分かる段階を想定しています。LLMの回答を整える以前に、決めた文字列が正しい順に保存・参照されるかを小さく確かめます。

一次資料確認日:2026年10月22日。Variable Assigner、変数の基本概念、Variable Inspector、単一ノードの実行、Answerの現行公式資料を確認しました。

以下は公式仕様に基づく設定・検査の教材です。Python部分は3.12.14でローカル参照実行しました。Difyクラウド、有料モデル、公開APIを使った実機テストではありません。利用中のDifyの版・画面・変数名は、ご自身の環境で照合してください。

会話変数の更新は、今回の入力、保存候補、変数代入、更新後の参照の順に確認する。同じ会話の次ターンと新しい会話を別々に試す。
回答文の見た目だけでなく、書く前と読むときの値を照合します。

書き込み先・通る経路・読む値・会話を別々に確認する

入力変数と会話変数は、同じ名前でも別の値として扱う

まず、更新したい値がどの種類の変数なのかを確認します。ユーザー入力、ノードの出力、会話変数を同じ「変数」という言葉だけで扱うと、保存したつもりの場所と、後段が読んでいる場所がずれることがあります。

公式の基本概念では、実行開始時の入力やノード出力と、複数ターンで保持する会話変数を区別しています。会話中に書き換える値は、Chatflowの会話変数として用意し、Variable Assignerで書く対象を選びます。

例えば今回の入力欄にnoteという値があり、会話変数にもnoteという名前を付けると、画面でどちらを選んだかが分かりにくくなります。教材では今回の発言をraw、保存する候補をcandidate、保持先をmemoと分けます。名前を分けるのは、確認しやすくするための編集部の命名例です。

名前が似ているから自動で同期される、という前提は置きません。Codeノードがcandidateを出力しても、それだけでconversation側のmemoが更新されたとは確認できないためです。候補ができた段階と、書き込みが終わった段階を別に記録します。

今回の教材で分けて見る四つの値
役割教材の名前確認する内容
今回の入力raw今回の発言として意図した文字列か
更新前の保持値conversation.memoをcurrentへ渡す前ターンまでの値を参照できたか
書く候補Codeのcandidate入力の判定で作った候補が正しいか
保持先conversation.memoVariable Assignerの対象と後段の参照先が一致するか

環境変数へ会話内容を書き込もうとしていないかも見直します。設定や秘密情報の保管場所と、ユーザーの対話状態は役割が違います。教材の検証に認証情報は必要ないため、APIキーを本文や確認用ログへ貼り付ける必要もありません。

Variable Assignerの対象・操作・値の取得元を一組で読む

変数代入ノードでは、どこへ、どの操作で、何を使って書くかを一組で確認します。対象だけ合っていても、固定値を設定する操作と、上流の出力で上書きする操作を取り違えると、期待した値にはなりません。

今回の例はString型のmemoへ、Codeノードのcandidateを使って上書きします。最初はほかの会話変数を一度に設定せず、一つの対象だけで試します。更新されなかった場合に、確認する欄を増やさないためです。

固定文字列へ戻したい場合は、Setのような固定値を設定する操作も検討できます。一方、Clearは値を消す操作として確認し、「作成時の初期値へ戻す」と同じだと決め付けません。初期状態へ戻したい教材では、文字列「未設定」を明示して書く設計にします。

文字列と配列など、型の異なる値を無理に代入しないようにします。候補の内容が見た目には短い文章でも、実際にはオブジェクトの中に入っている場合があります。そのときは上流の出力名と型を確認し、どの値を選ぶかを見直します。

設定した操作は、表示された日本語だけでなく、対象の型と取得元を含めてメモしておくと役立ちます。Difyの導入版で表記が異なる場合も、何をしたかったかという設計を手掛かりに照合できます。古い画面の位置をそのまま探す必要はありません。

候補が正しくても、代入する分岐を通らなければ変わらない

入力を正しく解析できたことと、変数代入ノードが実行されたことは別です。候補が短めになっていても、If-Elseで別の経路へ進んだり、手前で処理が止まったりしていれば、保持先を更新した証拠にはなりません。

検査するときは、該当ターンの実行履歴で、入力を受け取ったノード、判定、変数代入、後段の表示という順に追います。キャンバスの線がつながっているように見えることより、その実行でどの経路を通ったかを確認してください。

今回の教材では、判定結果actionがwriteの場合だけ代入します。確認だけの発言や、空の保存指示では書き換えないようにします。この分岐を用意しておくと、「確認と言っただけで未設定へ戻った」といった設計上の問題を、小さな条件で調べられます。

すべてのターンで初期値を書き込むノードが通っていないかも見ます。一度短めを保存しても、次ターンの先頭で未設定を書けば、保持されていないように見えます。その場合は保存先より、初期化処理をいつ通す設計かが確認対象になります。

分岐の境界を考える練習は、DifyのIf-Elseで空欄・ゼロ・境界値を確認する教材でも扱っています。入力がない場合と、読み取りだけの場合と、明示的な初期化の場合を混ぜずに設計しましょう。

更新後の回答が、上流の古い値を読んでいないかを見る

回答に以前の値が出たときは、変数代入を疑う前に、その回答がどこを参照しているかを確認します。今回の入力を読むのか、Codeへ渡した更新前のcurrentを読むのか、会話変数memoを読むのかで、表示される内容が違います。

例えばCodeがcurrentとcandidateを出し、変数代入でmemoへcandidateを書いた場合でも、回答がcurrentを参照していれば、更新前の値を表示する設計になります。表示が古いことだけから、書き込み失敗とは判断できません。

最初の検査では、LLMに自由な文章を作らせず、固定文と変数の差し込みで確認する方法が役立ちます。「保存する候補」「保持先から読んだ値」というラベルを付けると、どの段階の値なのかを見分けられます。教材の文字列なら、文章表現の揺れも切り離せます。

公式のAnswer資料では、固定文と変数を組み合わせて応答を作れることを説明しています。変数の表記を手入力するより、使っている環境のピッカーで参照先を選び、同名の別出力を選んでいないかを確かめてください。

実行順を変えたときは、ラベルだけ残して参照元が変わっていないかも見直します。表示欄の名前が「更新後」でも、実際には上流の候補を読んでいるだけなら、保持先を読めたことにはなりません。候補と保持値を別々の確認欄へ置く理由です。

同じ会話の次ターンと、新しい会話の初期値を分ける

会話変数は、同じ会話の複数ターンで扱う値です。新しい会話を開いたときに前の会話の値が出ないことを、すぐに保存失敗と考えないようにします。何を一つの会話として確認しているかを明示します。

今回の教材では、会話Aで保存:短めと発言し、その会話のまま確認と続けます。次に新しい会話Bを作り、初期値が未設定であることを確認します。AとBを意図せず取り違えないよう、手元の検査票へ仮の名前を付けておきます。

公式の基本概念には、会話IDやユーザーIDに加え、WebアプリとService APIで会話を共有しないという説明もあります。ブラウザで保存した値がAPI側にも出ると考えるなど、異なる入口を一つの状態として扱わないでください。

ただし本稿は、実際の会話IDを入力して公開APIへ接続する手順を扱いません。検査票にA・Bという仮名を使い、認証情報を必要としない小さな確認へ絞ります。実際のIDを共有する場合も、必要な範囲と共有先を決めてください。

会話を継続したつもりなのに値が違う場合は、アプリ、公開した版、会話の入口、対象ターンを照合します。同じ画面に見えることだけを根拠にせず、どの条件で起きたかを一件ずつ記録すると、再現の範囲を狭められます。

保存・確認・初期化の三つで、状態の変化を検査する

memo一つと短い入力で、期待する状態遷移を作る

最初に、Chatflowの会話変数memoをString型で用意し、初期値を未設定にします。今回の発言を受け取る上流の文字列をCodeのrawへ、会話変数memoをcurrentへ渡す設計にします。今回の発言を表す変数名は、導入版のピッカーと公式案内で確認します。

教材で受け付ける発言は、保存:短め、確認、初期化です。保存の後ろは任意の短い教材文字列でも構いませんが、顧客情報や秘密の値は入れません。書き込みの挙動だけを調べたいので、外部ツールや検索を足す必要もありません。

設計上の接続順は、今回の入力、候補を作るCode、actionによる分岐、write側のVariable Assigner、保持先を参照する表示です。読み取りだけの経路では、代入せずmemoを表示します。エラーの経路では理由を表示し、保持先を変えない方針にします。

期待値は、初期状態が未設定、保存:短めの後が短め、同じ会話で確認した値も短め、初期化の後は未設定です。新しい会話では未設定から始めます。これは編集部の教材の設計であり、Difyのすべてのアプリで同じ値になるという説明ではありません。

教材の検査票:保存する値と、確認するタイミングを分ける
順番操作期待する状態
会話A・1確認初期値の未設定を読む
会話A・2保存:短め候補が短めになり、代入後に保持先を読む
会話A・3確認同じ会話で短めを再確認する
会話A・4初期化明示した文字列の未設定を書き、再確認する
会話B・1新しい会話で確認Aとは別に初期値を確認する

この順番を先に決めると、同じ会話での保持と、新しい会話での分離を別々に確認できます。一回保存した画面だけを撮るより、次ターンで読むところまでを一組の証拠として残します。

Codeは保存候補を返し、会話変数への代入と分ける

次のPythonは、発言を判定して保存候補を作る教材です。mainが返す辞書には、action、candidate、reasonを入れます。CodeノードではそれぞれString型の出力として設定し、writeのときにcandidateを使う設計です。

ここで重要なのは、この関数が会話変数そのものを更新しているわけではないことです。値を作る処理と、DifyのVariable Assignerが保持先へ書く処理を区別します。出力名や型の確認は、Codeノードで出力が見つからないときの確認手順も参考になります。

def main(raw: str, current: str) -> dict:
    if not isinstance(raw, str) or not isinstance(current, str):
        raise TypeError("rawとcurrentは文字列で渡します")

    text = raw.strip()
    if text == "確認":
        return {"action": "skip", "candidate": current, "reason": "読むだけ"}
    if text == "初期化":
        return {"action": "write", "candidate": "未設定", "reason": "明示的な初期化"}
    if text.startswith("保存:"):
        candidate = text.split(":", 1)[1].strip()
        if candidate:
            return {"action": "write", "candidate": candidate, "reason": "保存指示"}
        return {"action": "invalid", "candidate": current, "reason": "保存内容が空"}
    return {"action": "invalid", "candidate": current, "reason": "対象外の入力"}

保存内容が空ならinvalidを返し、現在値を勝手に空へ変えない設計にしています。初期化は空の保存指示とは区別し、未設定という文字列を候補として返します。何を空欄と呼ぶかを曖昧にしないためです。

この判定は、日本語の自由な発言を理解する機能ではありません。「短くして」のような発言を自動で保存指示と解釈することはしません。自由な表現を扱う機能を足す場合は、この確定した状態変化が分かる教材とは分けて確認しましょう。

また、記号の全角・半角が違うと条件に一致しません。教材の保存指示では半角のコロンを使います。入力を広く受け付けたい場合も、最初から何でも補正するより、受け付ける表記とエラー時の表示を先に決めます。

ローカルの参照コードで、書く条件と保持する条件を検算する

候補を作る関数の判定は、Difyへ接続しなくてもPythonで確認できます。次のコードでは、会話変数の代わりに手元のstateを使い、writeの場合だけ更新する設計を再現します。Difyの内部実装や永続化を検証するコードではありません。

同じ会話に相当するstateで、保存後の確認、空の保存指示、対象外の入力、初期化を試します。その後、新しい会話に相当する別の値を用意します。期待した変化だけが起きるかを、小さい例で確認するためのものです。

def reference_turn(raw: str, state: str):
    result = main(raw, state)
    after = result["candidate"] if result["action"] == "write" else state
    return after, result["action"]

state = "未設定"
state, action = reference_turn("確認", state)
assert (state, action) == ("未設定", "skip")

state, action = reference_turn("保存:短め", state)
assert (state, action) == ("短め", "write")

state, action = reference_turn("確認", state)
assert (state, action) == ("短め", "skip")

state, action = reference_turn("保存:   ", state)
assert (state, action) == ("短め", "invalid")

state, action = reference_turn("こんにちは", state)
assert (state, action) == ("短め", "invalid")

state, action = reference_turn("初期化", state)
assert (state, action) == ("未設定", "write")

new_conversation = "未設定"
assert reference_turn("確認", new_conversation) == ("未設定", "skip")
print("参照教材の7ケースを確認")

この七ケースが通っても、Dify上のノード接続、参照先、会話の継続を確認したことにはなりません。逆に、ここで候補が誤っていれば、先に関数の条件を直す対象が分かります。確認する層を分けることで、問題の範囲を狭めます。

ローカルの結果を記録するときは、「Difyで成功した」と表現せず、「候補生成と更新条件の参照教材を確認した」と書きます。できた範囲を正確に残せば、まだ実環境で確かめるべき箇所を見失わずに済みます。

Inspectorの編集値と、Last runの記録を混同しない

Variable Inspectorは、ノードの入力や出力を確認し、後続の検査に使う値を編集するための機能です。公式資料では、そこで値を編集しても、元のノードのLast runの記録は変わらないと説明しています。

例えばcandidateを短めへ手入力してから後続だけを動かすと、その候補を渡した場合の動作は調べられます。しかし、今回の発言からCodeが実際に短めを生成した、という証拠にはなりません。どこを手で編集したかを検査票へ残してください。

単一ノードの実行は、特定の入力に対する出力を確かめるのに役立ちます。一方、会話の次ターンまで状態が保持されるかを調べるときは、同じ会話で順番に操作する検査が別に必要です。一つの確認方法ですべてを証明しようとしないようにします。

キャッシュされた値を使った検査と、上流から通した検査が混ざった場合は、公式のリセット方法を確認し、どの値を使っているかを揃えて再確認します。リセット操作を、公開中の会話データを消す操作と同じものとして扱わないことも大切です。

結果を整理する欄は、「Code単体」「候補を手で指定した後続」「同じ会話の全体」「新しい会話」の四つに分けられます。どの確認が済んだかを付けていけば、単体では動くのに全体で反映されない、という相談の内容が具体的になります。

更新前・候補・更新後・次ターンの四点を残して相談する

教材を少し変えて試すときも、変更は一つずつにします。例えば保存:長めを追加して、短めから長めへ上書きされるかを確認してから、入力の表記を増やします。変数名、分岐、参照先を同時に変えると、どの変更で反映されたかを説明しにくくなります。

検査票には期待値の欄と実測値の欄を分けます。初期値が未設定であるべきと考えたことと、実際の画面で未設定を読めたことは別です。まだ実行していない箇所は未確認のまま残し、推測で成功の印を付けないようにしてください。

保存と確認を同じターンへ詰め込むと、保存候補をそのまま表示しただけでも成功に見えることがあります。そのため次ターンの確認を残します。逆に次ターンだけ失敗した場合は、候補生成の関数を何度も書き換える前に、会話の継続と初期化経路へ戻ります。

同じ教材がプレビューと公開アプリで違う場合は、どちらで、いつの設定を確認したかを記録します。画面上でノードを直した事実と、その変更が確認対象のアプリへ反映された事実を分けるためです。公開や設定の変更は、利用中の環境の運用手順に従ってください。

検査の最後には、意図しない入力で保持値が変わらないことも確かめます。「こんにちは」でinvalidになった後に確認し、直前の保存値が残るという期待を置きます。正常な保存だけでなく、書かない経路を確かめると、上書き条件の抜けを見つけやすくなります。

質問するときは、回答文だけを添えるより、更新前が未設定、候補が短め、代入ノードが実行済み、表示が未設定だった、といった四点を残すと役立ちます。これなら、候補生成より後の参照を調べるという方向を立てられます。

一方、候補は短めだが代入ノードを通っていなければ、分岐や接続が先です。代入後の同じターンでは短めでも、次ターンに未設定になる場合は、初期化処理や会話の継続条件を調べます。症状を一種類へまとめないようにしてください。

共有する資料には、アプリの種類、導入版、使った教材入力、通った経路、どの値を手動編集したかを書きます。認証情報や本物の顧客データ、会話の全文を送る必要はありません。架空の文字列で再現できる範囲へ縮めます。

分岐から値をまとめるVariable Aggregatorは、会話変数へ保存する作業とは別です。名前が似ていて混乱する場合は、分岐後の出力をまとめる確認手順で役割を分けましょう。必要なノードを増やす前に、何を保持したいかを一文で書きます。

学習を続ける際は、Difyを独学する手順と講座の比べ方へ進み、この小さな確認を自分の用途へ広げられます。相談先を探す場合は、スクールの無料相談で確かめる項目を参考に、画面操作と状態設計のどちらで支援が必要かを具体的に伝えてください。

よくある質問

Q. Codeで値を返せば会話変数も変わりますか?
A.

候補を返すことと、会話変数へ書き込むことを分けます。保持先を用意し、Variable Assignerの対象・操作・取得元を確認してください。

Q. 確認と発言しただけで値が戻るのはなぜですか?
A.

毎ターン初期値を書いている経路や、更新前の入力を読んでいる参照先がないかを調べます。回答文だけで保存失敗とは判断しません。

Q. 新しい会話で以前の値が出ないのは不具合ですか?
A.

会話変数は同じ会話の複数ターンで使う値です。同じ会話の保持と、新しい会話の初期状態を別々に確認します。

Q. Inspectorで書き換えた値はLast runへ反映されますか?
A.

公式資料では、Inspectorの編集は元ノードのLast run記録を変えないと説明されています。手動の編集値と実行時の出力を区別して記録します。

Q. Clearで作成時の初期値へ戻せますか?
A.

値を消す操作と、任意の初期値を明示的に設定する設計を同一視しないでください。教材では未設定という文字列を明示して書く方法を使います。

Q. このPythonコードでDifyの保存も検証できますか?
A.

できません。候補の判定と更新条件を手元で確かめる参照教材です。Dify上では代入、後段の参照、次ターン、新しい会話を別途確認します。

同じ会話の中で、入力から候補、保持先、次ターンの参照までを順に追いましょう。反映されないという一つの症状を、書き込み・読み取り・会話の境界に分ければ、次に直す箇所を選びやすくなります。