状態の管理Managing state

適用対象: SDK v4APPLIES TO: SDK v4

ボット内の状態は、最新の Web アプリケーションと同じパラダイムに従います。また、Bot Framework SDK には、状態の管理を容易にするための抽象化概念がいくつか用意されています。State within a bot follows the same paradigms as modern web applications, and the Bot Framework SDK provides some abstractions to make state management easier.

Web アプリと同様、ボットは本質的にはステートレスです。つまり、お使いのボットの別のインスタンスで、会話の任意のターンを処理できます。As with web apps, a bot is inherently stateless; a different instance of your bot may handle any given turn of the conversation. Bot によっては、ボットが — 追加情報なしで動作する場合や、必要な情報が受信メッセージ内にあることが保証される場合があります。For some bots, this simplicity is preferred—the bot can either operate without additional information, or the information required is guaranteed to be within the incoming message. それ以外の場合、bot が役に立つメッセージ交換を行うには、状態 (メッセージ交換が中断された場所や、ユーザーに関して以前に受信したデータなど) が必要です。For others, state (such as where the conversation left off or data previously received about the user) is necessary for the bot to have a useful conversation.

状態が必要な理由Why do I need state?

状態を維持すると、お使いのボットは、ユーザーまたは会話に関する特定の情報を記憶することで、より有意義な会話ができるようになります。Maintaining state allows your bot to have more meaningful conversations by remembering certain things about a user or conversation. たとえば、前にユーザーと話したことがある場合は、ユーザーに関する以前の情報を保存できるため、その内容については再度尋ねる必要がありません。For example, if you've talked to a user previously, you can save previous information about them, so that you don't have to ask for it again. また、状態によって現在のターンよりも長くデータが保持されるため、お使いのボットには、一連のマルチターンの会話にわたって情報が保持されます。State also keeps data for longer than the current turn, so that your bot keeps information over the course of a multi-turn conversation.

Bot に関連するように、状態を使用するレイヤーはいくつかあります。これは、ストレージ層、状態管理 (下図の bot 状態に含まれる)、および state プロパティアクセサーです。As it pertains to bots, there are a few layers to using state: the storage layer, state management (contained in the bot state in the diagram below), and state property accessors. この図は、これらのレイヤー間で行われる相互作用シーケンスの一部を示しています。実線の矢印はメソッドの呼び出しを、破線の矢印は応答 (戻り値の有無に関係なく) を表しています。This diagram illustrates parts of the interaction sequence between these layers, with the solid arrows representing a method call, and the dashed arrows representing the response (with or without a return value).

ボットの状態

この図のフローについては、次のセクションでレイヤーごとに詳しく説明します。The flow of this diagram is explained in following sections with details each of these layers.

ストレージ レイヤーStorage layer

状態情報が実際に格納されるバックエンドから開始すると、は ストレージ層 になります。Starting at the backend, where the state information is actually stored, is the storage layer. これは、メモリ内、Azure、サードパーティのサーバーなどの物理ストレージと考えることができます。This can be thought of as your physical storage, such as in-memory, Azure, or a third party server.

Bot Framework SDK には、ストレージ レイヤー用の実装がいくつか用意されています。The Bot Framework SDK includes some implementations for the storage layer:

  • メモリ ストレージ により、テストを目的としたインメモリ ストレージが実装されます。Memory storage implements in-memory storage for testing purposes. インメモリ データ ストレージは揮発性であり、一時的なものであるため、ローカル テストのみを対象としています。In-memory data storage is intended for local testing only as this storage is volatile and temporary. ボットの再起動ごとにデータがクリアされます。The data is cleared each time the bot is restarted.
  • Azure Blob Storage は、Azure Blob Storage オブジェクト データベースに接続されます。Azure Blob Storage connects to an Azure Blob Storage object database.
  • Azure Cosmos DB パーティション分割ストレージ は、パーティション分割された Cosmos DB NoSQL データベースに接続されます。Azure Cosmos DB partitioned storage connects to a partitioned Cosmos DB NoSQL database.

重要

Cosmos DB ストレージ クラスは非推奨となりました。The Cosmos DB storage class has been deprecated. CosmosDbStorage で作成されたコンテナーにはパーティションキーが設定されておらず、"_partitionKey" の既定のパーティションキーが指定されました / 。Containers originally created with CosmosDbStorage had no partition key set, and were given the default partition key of "/_partitionKey".

Cosmos DB ストレージ で作成されたコンテナーは、 Cosmos DB パーティション分割 されたストレージで使用できます。Containers created with Cosmos DB storage can be used with Cosmos DB partitioned storage. 詳細については、Azure Cosmos DB でのパーティション分割に関するページを参照してください。Read Partitioning in Azure Cosmos DB for more information.

また、従来の Cosmos DB ストレージとは異なり、Cosmos DB パーティション分割されたストレージは、Cosmos DB アカウント内にデータベースを自動的に作成しません。Also note that, unlike the legacy Cosmos DB storage, the Cosmos DB partitioned storage does not automatically create a database within your Cosmos DB account. 新しいデータベースは手動で作成する必要がありますが、コンテナーを手動で作成する必要はありません。これは、 CosmosDbPartitionedStorage によってコンテナーが作成されるためです。You need to create a new database manually, but skip manually creating a container since CosmosDbPartitionedStorage will create the container for you.

他のストレージ オプションに接続する方法については、ストレージへの直接書き込みに関するページをご覧ください。For instructions on how to connect to other storage options, see write directly to storage.

状態管理State management

"状態管理" により、基盤となるストレージ レイヤーに対するボットの状態の読み取りと書き込みが自動化されます。State management automates the reading and writing of your bot's state to the underlying storage layer. 状態は、効率的なキーと値のペアである "状態プロパティ" として格納され、お使いのボットは、このプロパティを、基盤となる特定の実装を気にすることなく、状態管理オブジェクトを使用して読み書きできます。State is stored as state properties, which are effectively key-value pairs that your bot can read and write through the state management object without worrying about the specific underlying implementation. これらの状態プロパティにより、その情報の格納方法が定義されます。Those state properties define how that information is stored. たとえば、特定のクラスまたはオブジェクトとして定義したプロパティを取得する場合、そのデータがどのように構造化されるかがわかります。For example, when you retrieve a property that you defined as a specific class or object, you know how that data will be structured.

これらの状態プロパティはスコープ付きの "バケット" にまとめられます。これは、プロパティを整理しやすくするための単なるコレクションです。These state properties are lumped into scoped "buckets", which are just collections to help organize those properties. SDK には、次の 3 つの "バケット" が含まれています。The SDK includes three of these "buckets":

  • ユーザー状態User state
  • 会話状態Conversation state
  • 個人的な会話状態Private conversation state

これらのバケットはすべて "ボット状態" クラスのサブクラスであり、これを基に他の種類のバケットをさまざまなスコープで定義できます。All of these buckets are subclasses of the bot state class, which can be derived to define other types of buckets with different scopes.

これらの定義済みバケットは、バケットに応じて、使用できる範囲が限定されます。These predefined buckets are scoped to a certain visibility, depending on the bucket:

  • ユーザー状態は、会話に関係なく、ボットがそのチャンネルのそのユーザーと会話している任意のターンで使用できますUser state is available in any turn that the bot is conversing with that user on that channel, regardless of the conversation
  • 会話状態は、ユーザー (グループの会話) に関係なく、特定の会話の任意のターンで使用できますConversation state is available in any turn in a specific conversation, regardless of user (i.e. group conversations)
  • 個人的な会話状態は、特定の会話とその特定のユーザーの両方に対象が限定されますPrivate conversation state is scoped to both the specific conversation and to that specific user

ヒント

ユーザー状態と会話状態の両方がチャンネルごとに範囲指定されます。Both user and conversation state are scoped by channel. さまざまなチャンネルを使用してボットにアクセスしている 1 人のユーザーは、チャンネルごとにそのユーザーが存在し、ユーザー状態も異なるため、違うユーザーのように見えます。The same person using different channels to access your bot appears as different users, one for each channel, and each with a distinct user state.

これらの定義済みバケットそれぞれに使用されるキーは、ユーザー、会話、またはその両方に固有です。The keys used for each of these predefined buckets are specific to the user and conversation, or both. お使いの状態プロパティの値を設定するとき、キーは、各ユーザーまたは会話がそれぞれ正しいバケットとプロパティに確実に配置されるように、ターン コンテキストに含まれる情報によって内部的に自動で定義されます。When setting the value of your state property, the key is defined for you internally with information contained on the turn context to ensure that each user or conversation gets placed in the correct bucket and property. 具体的には、キーは次のように定義されます。Specifically, the keys are defined as follows:

  • ユーザー状態は、channel IDfrom ID を使用してキーを生成します。The user state creates a key using the channel ID and from ID. 例: {Activity.ChannelId}/users/{Activity.From.Id}#YourPropertyNameFor example, {Activity.ChannelId}/users/{Activity.From.Id}#YourPropertyName
  • 会話状態は、channel IDconversation ID を使用してキーを生成します。The conversation state creates a key using the channel ID and the conversation ID. 例: {Activity.ChannelId}/conversations/{Activity.Conversation.Id}#YourPropertyNameFor example, {Activity.ChannelId}/conversations/{Activity.Conversation.Id}#YourPropertyName
  • 個人的な会話状態は、channel IDfrom ID、および conversation ID を使用してキーを生成します。The private conversation state creates a key using the channel ID, from ID and the conversation ID. 例: {Activity.ChannelId}/conversations/{Activity.Conversation.Id}/users/{Activity.From.Id}#YourPropertyNameFor example, {Activity.ChannelId}/conversations/{Activity.Conversation.Id}/users/{Activity.From.Id}#YourPropertyName

それぞれの状態の種類を使用するタイミングWhen to use each type of state

会話状態は、次のような会話のコンテキストの追跡に適しています。Conversation state is good for tracking the context of the conversation, such as:

  • ボットがユーザーに質問したかどうかと、どのような質問を行ったかWhether the bot asked the user a question, and which question that was
  • 会話の現在のトピック、または最後のトピックWhat the current topic of conversation is, or what the last one was

ユーザー状態は、次のようなユーザーに関する情報の追跡に適しています。User state is good for tracking information about the user, such as:

  • 重要度が低いユーザー情報。名前と基本設定、アラーム設定、アラートの基本設定などNon-critical user information, such as name and preferences, an alarm setting, or an alert preference
  • ユーザーがボットと交わした最後の会話に関する情報Information about the last conversation they had with the bot
    • たとえば、product-support ボットでは、ユーザーがどの製品について質問したかが追跡されている場合があります。For instance, a product-support bot might track which products the user has asked about.

個人的な会話状態は、グループの会話をサポートするチャンネルに適しています。ただし、その会話ではユーザーおよび会話固有の情報を追跡する必要があります。Private conversation state is good for channels that support group conversations, but where you want to track both user and conversation specific information. たとえば、クラスルーム クリッカー ボットは、次を実行できます。For example, if you had a classroom clicker bot:

  • 特定の質問に対する学生の応答を集計し、表示する。The bot could aggregate and display student responses for a given question.
  • 各学生のパフォーマンスを集計し、集計した内容を、セッションの最後に非公開で学生に送り返す。The bot could aggregate each student's performance and privately relay that back to them at the end of the session.

これらの定義済みバケットの使用の詳細については、状態に関するハウツー記事をご覧ください。For details on using these predefined buckets, see the state how-to article.

複数のデータベースへの接続Connecting to multiple databases

お使いのボットが複数のデータベースに接続する必要がある場合は、データベースごとにストレージ レイヤーを作成します。If your bot needs to connect to multiple databases, create a storage layer for each database. 異なるセキュリティ、同時実行、またはデータの場所のニーズを持つ情報を bot が収集する場合は、複数のデータベースを使用することを選択できます。You might choose to use multiple databases if your bot collects information that has different security, concurrency, or data location needs.

ストレージ レイヤーごとに、ご自身の状態プロパティをサポートするのに必要な状態管理オブジェクトを作成します。For each storage layer, create the state management objects you need to support your state properties.

状態プロパティ アクセサーState property accessors

"状態プロパティ アクセサー" は、状態プロパティのいずれかを実際に読み書きするときに使用され、ターン内からご自身の状態プロパティにアクセスするための getsetdelete の各メソッドを提供します。State property accessors are used to actually read or write one of your state properties, and provide get, set, and delete methods for accessing your state properties from within a turn. アクセサーを作成するには、プロパティ名を指定する必要があります。この操作は、通常、ご自身のボットを初期化しているときに行います。To create an accessor, you must provide the property name, which usually takes place when you're initializing your bot. その後、そのアクセサーを使用して、ボットの状態の該当するプロパティを取得して操作できます。Then, you can use that accessor to get and manipulate that property of your bot's state.

アクセサーは、基盤となるストレージから SDK が状態を取得できるようにして、ボットの "状態キャッシュ" を自動的に更新します。The accessors allow the SDK to get state from the underlying storage, and update the bot's state cache for you. 状態キャッシュは、ご自身のボットによって維持されているローカル キャッシュで、状態オブジェクトが自動的に格納されます。これにより、基盤となるストレージにアクセスしなくても読み書き操作を行えるようになります。The state cache is a local cache maintained by your bot that stores the state object for you, allowing read and write operations without accessing the underlying storage. 状態がキャッシュにない場合は、アクセサーの get メソッドを呼び出すことで状態が取得され、キャッシュへの配置も行われます。If it isn't already in the cache, calling the accessor's get method retrieves state and also places it in the cache. 状態が取得されると、ローカル変数と同じように、状態プロパティを操作できるようになります。Once retrieved, the state property can be manipulated just like a local variable.

アクセサーの delete メソッドでは、プロパティが、キャッシュだけでなく、基盤となるストレージからも削除されます。The accessor's delete method removes the property from the cache, and also deletes it from the underlying storage.

重要

アクセサーの get メソッドを初めて呼び出すとき、オブジェクトがまだ存在しない場合は、ファクトリ メソッドを指定して作成する必要があります。For the first call to an accessor's get method, you must provide a factory method to create the object if it doesn't yet exist in your state. ファクトリ メソッドを指定しないと、例外が発生します。If no factory method is given, you will get an exception. ファクトリ メソッドの使用方法の詳細については、状態に関するハウツー記事をご覧ください。Details on how to use a factory method can be found in the state how-to article.

アクセサーから取得した状態プロパティに対する変更を保持するには、状態キャッシュのプロパティを更新する必要があります。To persist any changes you make to the state property you get from the accessor, the property in the state cache must be updated. これを行うには、アクセサーの set メソッドを呼び出します。これにより、キャッシュでご自身のプロパティ値が設定され、後からそのターンで読み取りまたは更新の必要が出てきた場合に使用できます。You can do so via a call the accessors set method, which sets the value of your property in the cache, and is available if that needs to be read or updated later in that turn. そのデータを基盤となるストレージに実際に保持するには (つまり、現在のターンの後に使用できるようにするには)、その状態を保存する必要があります。To actually persist that data to the underlying storage (and thus make it available after the current turn), you must then save your state.

状態プロパティのアクセサー メソッドのしくみHow the state property accessor methods work

アクセサー メソッドは、ご自身のボットで状態を操作する最も一般的な方法です。The accessor methods are the primary way for your bot to interact with state. それぞれのしくみと、基盤となるレイヤーの動作を次に示します。How each work, and how the underlying layers interact, are as follows:

  • アクセサーの get メソッド:The accessor's get method:
    • アクセサーが状態キャッシュにプロパティを要求します。Accessor requests property from the state cache.
    • プロパティがキャッシュにある場合は、それを返します。If the property is in the cache, return it. それ以外の場合は、状態管理オブジェクトから取得します。Otherwise, get it from the state management object. (まだ状態にない場合は、アクセサーの get 呼び出しで指定されたファクトリ メソッドを使用します。)(If it is not yet in state, use the factory method provided in the accessors get call.)
  • アクセサーの set メソッド:The accessor's set method:
    • 新しいプロパティ値で状態キャッシュを更新します。Update the state cache with the new property value.
  • 状態管理オブジェクトの save changes メソッド:The state management object's save changes method:
    • 状態キャッシュのプロパティへの変更を確認します。Check the changes to the property in the state cache.
    • そのプロパティをストレージに書き込みます。Write that property to storage.

状態 (ダイアログ内の)State in dialogs

ダイアログライブラリは、bot のメッセージ交換状態で定義されているダイアログステートプロパティアクセサーを使用して、メッセージ交換でのダイアログの場所を保持します。The dialogs library uses a dialog state property accessor, defined on the bot's conversation state, to retain a dialog's place in the conversation. また、ダイアログ状態プロパティを使用すると、各ダイアログボックスで一時的な情報を複数のターンに格納できます。The dialog state property also allows each dialog to store transient information in between turns.

アダプティブ ダイアログには、より複雑なメモリ スコープ構造があります。これにより、特に構成や認識の結果に簡単にアクセスできます。Adaptive dialogs have a more elaborate memory scope structure, which makes it easier to access configuration and recognition results, among other things. ダイアログ マネージャーは、 ユーザーと会話の状態管理オブジェクトを使用して、これらのメモリ スコープを提供します。The dialog manager uses the user and conversation state management objects to provide these memory scopes.

ダイアログ ライブラリの詳細については、ダイアログ ライブラリに関 する記事を参照 してください。For information about the dialogs library, see the dialogs library article.

状態の保存Saving state

アクセサーの set メソッドを呼び出して、更新された状態を記録するとき、その状態プロパティは、永続化されたストレージにはまだ保存されておらず、ご自身のボットの状態キャッシュに保存されているだけです。When you call the accessor's set method to record the updated state, that state property has not yet been saved to your persisted storage, and instead is only saved to your bot's state cache. 状態キャッシュの変更をご自身の永続化された状態に保存するには、状態管理オブジェクトの save changes メソッドを呼び出す必要があります。このメソッドは、前述したボット状態クラス (ユーザー状態、会話状態など) の実装で使用できます。To save any changes in the state cache to your persisted state, you must call the state management object's save changes method, which is available on the implementation of the bot state class mentioned above (such as user state or conversation state).

状態管理オブジェクト (つまり、上述したバケット) の save changes メソッドを呼び出すと、そのバケットについては、その時点までに設定した状態キャッシュにすべてのプロパティが保存されますが、ご自身のボットの状態に存在する可能性がある他のバケットについては、プロパティは保存されません。Calling the save changes method for a state management object (i.e. the buckets mentioned above) saves all properties in the state cache that you have set up to that point for that bucket, but not for any of the other buckets you may have in your bot's state.

ヒント

ボットの状態では、"最後の書き込みが有効" という動作が実装されます。つまり、以前に書き込まれた状態は、最後の書き込みで上書きされます。Bot state implements a "last write wins" behavior, where the last write will stamp over the previously written state. これは多くのアプリケーションで有効ですが、特に、ある程度のコンカレンシーまたは待ち時間が存在する可能性があるスケールアウト シナリオに、影響が及ぶことがあります。This may work for many applications but has implications, particularly in scaled-out scenarios, where there may be some level of concurrency or latency in play.

ターン ハンドラーの完了後に状態を更新する可能性のある何らかのカスタム ミドルウェアがある場合は、ミドルウェアで状態を処理することを検討してください。If you have some custom middleware that might update state after your turn handler completes, consider handling state in middleware.

その他のリソースAdditional resources