ミドルウェア
適用対象: SDK v4
ミドルウェアとは単に、アダプターとボット ロジックの間に位置し、初期化中にアダプターのミドルウェア コレクションに追加されるクラスのことです。 SDK を使用すると、独自のミドルウェアを記述したり、他のユーザーによって作成されたミドルウェアを追加したりできます。 ボットを出入りするすべてのアクティビティはミドルウェアを通って流れます。
アダプターは、ボット ミドルウェア パイプラインを介して受信アクティビティを処理し、ボットのロジックに転送してから、もう一度やり直します。 各アクティビティがボットを出入りして流れる際、ボット ロジックの実行前と実行後のどちらでも、各ミドルウェアがアクティビティを検査または操作できます。
ミドルウェアに入る前に、 ボットの一般的な 処理方法と アクティビティの処理方法を理解しておくことが重要です。
ミドルウェアの用途
"通常のボット ロジックを使用するのと、ミドルウェアとしてアクションを実装する必要がある場合" という質問がしばしば発生します。ミドルウェアは、会話の各 ターン が処理される前と後の両方で、ユーザーの会話フローと対話する追加の機会を提供します。 また、ミドルウェアを使用して、会話に関する情報を取得して格納し、必要に応じて追加の処理ロジックを呼び出すことができます。 以下にミドルウェアが役に立つ場面を示す一般的なシナリオを示します。
すべてのアクティビティに注目する、またはそれらに基づいて行動する
すべてのアクティビティ、または特定の種類のすべてのアクティビティに対してボットが何かをしなければならない状況は多々あります。 たとえば、ボットがこのターンで応答を生成していない場合は、ボットが受信したすべてのメッセージ アクティビティをログに記録したり、フォールバック応答を提供したりできます。 ミドルウェアは、ボット ロジックの残りの部分が実行される前と後の両方で動作する機能を備えた、このようなプロセスに最適な場所です。
ターン コンテキストの変更または強化
アクティビティで提供されるものよりも多くの情報をボットが持っている場合、特定の会話の有用性が大きく向上します。 この場合のミドルウェアは、それまでに持っていた会話状態情報に注目し、外部データ ソースをクエリし、実行をボット ロジックに渡す前にそれをターン コンテキスト オブジェクトに追加することができます。
SDK は受信および送信アクティビティを記録できるログ記録ミドルウェアを定義しますが、独自のミドルウェアを定義することもできます。
ボットのミドルウェア パイプライン
アダプターはアクティビティごとに、追加された順にミドルウェアを呼び出します。 アダプターはターンのコンテキスト オブジェクトと next デリゲートを渡し、ミドルウェアはデリゲートを呼び出してパイプライン内の次のミドルウェアに制御を渡します。 ミドルウェアには、next デリゲートが戻った後、メソッドを完了する前に処理を行う機会もあります。 パイプラインの次のミドルウェア オブジェクトとの関係上、各ミドルウェア オブジェクトには最初で最後の行動チャンスがあると考えることができます。
次に例を示します。
- 最初のミドルウェア オブジェクトのターン ハンドラーは 、次に呼び出す前にコードを実行します。
- 2 番目のミドルウェア オブジェクトのターン ハンドラーは 、次に呼び出す前にコードを実行します。
- ボットのターン ハンドラーが実行され、返されます。
- 2 番目のミドルウェア オブジェクトのターン ハンドラーは、戻る前に残りのコードを実行します。
- 2 番目のミドルウェア オブジェクトのターン ハンドラーは 、次に呼び出す前にコードを実行します。
- 最初のミドルウェア オブジェクトのターン ハンドラーは、戻る前に残りのコードを実行します。
ミドルウェアが次のデリゲートを呼び出さない場合、アダプターは後続のミドルウェアまたはボットターン ハンドラーとパイプラインの短絡を呼び出しません。
ボットのミドルウェア パイプラインが完了すると、ターンは終了し、ターン コンテキストはスコープ外になります。
ミドルウェアまたはボットは、応答を生成して応答イベント ハンドラーを登録できますが、応答は別々のプロセスで処理されることに注意してください。
ミドルウェアの順序
ミドルウェアが追加された順序によってミドルウェアがアクティビティを処理する順序が決まるため、ミドルウェアを追加する順序の決定は重要です。
注意
これは、ほとんどのボットに役立つ共通のパターンを与えることを意図していますが、特定の状況で各ミドルウェアがどのように相互作用するかを必ず検討してください。
すべてのボットに最初にミドルウェア パイプラインに追加する必要がある最も低いレベルのタスクを処理するミドルウェア。 たとえば、ログ記録、例外処理、翻訳などです。 必要に応じて、受信メッセージを最初に翻訳するか、メッセージを格納する前に、またはメッセージ ストレージを最初に行うかなど、必要に応じて順序を指定します。これは、保存されたメッセージが変換されない可能性があります。
ボット固有のミドルウェアは、最後にミドルウェア パイプラインに追加する必要があります。ミドルウェアは、ボットに送信されるすべてのメッセージに対していくつかの処理を行うために実装します。 ボットのコンテキストに設定されている状態情報またはその他の情報をミドルウェアで使用する場合、そのミドルウェアをミドルウェア パイプラインに追加するときは、状態またはコンテキストを変更するミドルウェアよりも後に追加します。
短絡
ミドルウェアおよび応答ハンドラーに関する重要なアイデアは "短絡" です。 実行がその後のレイヤーにわたって継続する場合、ミドルウェア (または応答ハンドラー) はその next デリゲートを呼び出すことによって実行を渡す必要があります。 そのミドルウェア (または応答ハンドラー) 内で次のデリゲートが呼び出されない場合、関連付けられているパイプラインの短絡とその後のレイヤーは実行されません。 つまり、すべてのボット ロジックと、パイプラインでそれよりもさらに先のすべてのミドルウェアはスキップされます。 ミドルウェアと応答ハンドラーの間には、ターンを短絡する微妙な違いがあります。
ミドルウェアがターンをショートした場合、ボット ターン ハンドラーは呼び出されませんが、パイプラインのこの時点より前に実行されたすべてのミドルウェア コードは、引き続き完了まで実行されます。
イベント ハンドラーの場合、 次に 呼び出さないということは、イベントが取り消されることを意味します。これは、ロジックをスキップするミドルウェアとは大きく異なります。 イベントの残りの部分を処理しないことにより、アダプターは決してそのイベントを送信しません。
ヒント
SendActivities
のような応答イベントを短絡する場合は、それが意図した動作であることを確認してください。 それ以外の場合、バグの修正が困難になることがあります。
応答イベント ハンドラー
アプリケーションおよびミドルウェア ロジックに加えて、応答ハンドラー (イベント ハンドラーやアクティビティ イベント ハンドラーと呼ばれることもある) をコンテキスト オブジェクトに追加することもできます。 これらのハンドラーは、実際の応答が実行される前に、現在のコンテキスト オブジェクト上で関連する応答が発生したときに呼び出されます。 これらのハンドラーは、実際のイベントの前または後で、現在の応答の残りの部分でその種類のすべてのアクティビティのために何かを行う必要があることがわかっているときに便利です。
警告
アクティビティ応答メソッドを、そのそれぞれの応答イベント ハンドラー内から呼び出さないように注意してください。たとえば、send activity メソッドを on send activity ハンドラー内から呼び出さないようにしてください。 それを行うと、無限ループが生成される可能性があります。
それぞれの新しいアクティビティには、新しいスレッドが与えられ、それぞれ対応するスレッドで実行されることに留意してください。 アクティビティを処理するスレッドが作成されると、そのアクティビティ用のハンドラー リストが、その新しいスレッドにコピーされます。 そのポイントより後に追加されたハンドラーは、その特定のアクティビティ イベントに対して実行されません。 コンテキスト オブジェクトに登録されたハンドラーは、アダプターがミドルウェア パイプラインを管理する方法と同様に処理されます。 具体的には、ハンドラーはそれらが追加された順に呼び出され、next デリゲートを呼び出すと、次に登録されているイベント ハンドラーに制御が渡されます。 ハンドラーが次のデリゲートを呼び出さない場合、後続のイベント ハンドラーは呼び出されません。イベントがショートし、アダプターはチャネルに応答を送信しません。
ミドルウェアでの状態の処理
状態を保存する一般的な方法は、ターン ハンドラーの最後に、変更の保存メソッドを呼び出すというものです。 呼び出しに重点を置いた図を次に示します。
この方法の問題は、ボットのターン ハンドラーが返された後に発生する一部のカスタム ミドルウェアから行われた状態更新は、永続ストレージに保存されないということです。 これを解決するには、変更の保存メソッドへの呼び出しを、カスタム ミドルウェアの完了後に移動するか ("変更の自動保存" ミドルウェアのインスタンスをミドルウェア スタックの最初に追加)、少なくとも、状態を更新する可能性があるミドルウェアの前に移動します。 実行は次のようになります。
更新する必要がある状態管理オブジェクトを "ボット状態セット" オブジェクトに追加し、変更の自動保存ミドルウェアを作成するときに使用します。
その他のリソース
Bot Framework SDK [C# | JS] に実装されているトランスクリプト ロガー ミドルウェアもご覧ください。