S'APPLIQUE À : SDK v4
Un bot est par nature sans état. Une fois que votre bot est déployé, il ne peut pas s’exécuter dans le même processus ni sur le même ordinateur d’un tour à l’autre. Votre bot peut toutefois peut avoir besoin de suivre le contexte d’une conversation pour pouvoir gérer son comportement et se rappeler les réponses aux questions précédentes. Les fonctionnalités d’état et de stockage du SDK Bot Framework permettent d’ajouter un état à votre bot. Les bots utilisent la gestion de l’état et les objets de stockage pour gérer et conserver l’état. Le gestionnaire d’état fournit une couche d’abstraction qui vous permet d’accéder aux propriétés de l'état via des accesseurs, quel que soit le type de stockage sous-jacent.
Remarque
Pour créer des agents avec votre choix de services IA, d’orchestration et de connaissances, envisagez d’utiliser le Kit de développement logiciel (SDK) Microsoft 365 Agents. Le SDK Agents prend en charge C#, JavaScript ou Python. Vous pouvez en savoir plus sur le Kit de développement logiciel (SDK) Agents à aka.ms/agents. Si vous recherchez une plateforme d’agent SaaS, envisagez Microsoft Copilot Studio. Si vous disposez d’un bot existant créé avec le Kit de développement logiciel (SDK) Bot Framework, vous pouvez mettre à jour votre bot vers le Kit de développement logiciel (SDK) Agents. Vous pouvez passer en revue les principales modifications et mises à jour dans le guide de migration du SDK Bot Framework vers SDK Agents. Les tickets de support pour le Kit de développement logiciel (SDK) Bot Framework ne seront plus pris en charge à compter du 31 décembre 2025.
Prérequis
À propos de cet exemple
Quand il reçoit l’entrée utilisateur, cet exemple vérifie l’état de conversation stocké pour voir si cet utilisateur a précédemment été invité à fournir son nom. Si ce n’est pas le cas, le nom de l’utilisateur est demandé et cette entrée est stockée dans l’état utilisateur. Dans l'affirmative, le nom stocké dans l'état utilisateur sert à communiquer avec l'utilisateur. Par ailleurs, ses données d'entrée ainsi que l'heure de réception et l'ID du canal d'entrée sont renvoyés à l'utilisateur. Les valeurs d'heure et d'ID de canal sont récupérées à partir des données de conversation utilisateur, puis enregistrées dans l'état de conversation. Le diagramme suivant montre la relation entre le bot, le profil utilisateur et les classes de données de conversation.
Définir les classes
La première étape de la configuration de la gestion d’état consiste à définir les classes qui contiennent les informations à gérer dans l’état utilisateur et l’état de conversation. L’exemple utilisé dans cet article définit les classes suivantes :
- Dans UserProfile.cs, vous définissez une classe
UserProfile
pour les informations utilisateur qui seront collectées par le bot.
- Dans ConversationData.cs, vous définissez une classe
ConversationData
pour contrôler l'état de votre conversation pendant la collecte des informations utilisateur.
Les exemples de code suivants illustrent les définitions des classes UserProfile
et ConversationData
.
UserProfile.cs
public class UserProfile
{
public string Name { get; set; }
}
ConversationData.cs
public class ConversationData
{
// The time-stamp of the most recent incoming message.
public string Timestamp { get; set; }
// The ID of the user's channel.
public string ChannelId { get; set; }
// Track whether we have already asked the user's name
public bool PromptedUserForName { get; set; } = false;
}
Cette étape n'est pas nécessaire dans JavaScript.
La première étape de la configuration de la gestion d’état consiste à définir les classes qui contiennent les informations à gérer dans l’état utilisateur et l’état de conversation. L'exemple utilisé dans cet article définit les classes suivantes :
- Dans UserProfile.java, vous définissez une classe
UserProfile
pour les informations utilisateur qui seront collectées par le bot.
- Dans ConversationData.java, vous définissez une classe
ConversationData
pour contrôler l'état de votre conversation pendant la collecte des informations utilisateur.
Les exemples de code suivants illustrent les définitions des classes UserProfile
et ConversationData
.
UserProfile.java
Avertissement
Il semble que l'échantillon que vous recherchez a été déplacé ! Nous sommes assurés que nous travaillons à résoudre ce problème.
ConversationData.java
Avertissement
Il semble que l'échantillon que vous recherchez a été déplacé ! Nous sommes assurés que nous travaillons à résoudre ce problème.
La première étape de la configuration de la gestion d’état consiste à définir les classes qui contiennent les informations à gérer dans l’état utilisateur et l’état de conversation. L’exemple utilisé dans cet article définit les classes suivantes :
-
user_profile.py contient la classe
UserProfile
, qui stocke les informations utilisateur collectées par le bot.
-
conversation_data.py contient la classe
ConversationData
, qui contrôle l'état de la conversation pendant la collecte des informations utilisateur.
Les exemples de code suivants illustrent les définitions des classes UserProfile
et ConversationData
.
user_profile.py
class UserProfile:
def __init__(self, name: str = None):
self.name = name
conversation_data.py
class ConversationData:
def __init__(
self,
timestamp: str = None,
channel_id: str = None,
prompted_for_user_name: bool = False,
):
self.timestamp = timestamp
self.channel_id = channel_id
self.prompted_for_user_name = prompted_for_user_name
Créer des objets d’état utilisateur et de conversation
Ensuite, vous inscrivez MemoryStorage
qui est utilisé pour créer les objets UserState
et ConversationState
. Les objets d’état utilisateur et de conversation sont créés lors de Startup
et la dépendance est injectée dans le constructeur de bot. Les autres services enregistrés pour un bot sont les suivants : un fournisseur d’informations d’identification, un adaptateur et l’implémentation du bot.
Startup.cs
// {
// TypeNameHandling = TypeNameHandling.All,
// var storage = new BlobsStorage("<blob-storage-connection-string>", "bot-state");
// With a custom JSON SERIALIZER, use this instead.
// var storage = new BlobsStorage("<blob-storage-connection-string>", "bot-state", jsonSerializer);
/* END AZURE BLOB STORAGE */
Bots/StateManagementBot.cs
private BotState _conversationState;
private BotState _userState;
public StateManagementBot(ConversationState conversationState, UserState userState)
{
_conversationState = conversationState;
_userState = userState;
}
Ensuite, vous inscrivez MemoryStorage
qui est alors utilisé pour créer les objets UserState
et ConversationState
. Ceux-ci sont créés dans index. js et sont consommés lors de la création du bot.
index.js
// Define state store for your bot.
// See https://aka.ms/about-bot-state to learn more about bot state.
const memoryStorage = new MemoryStorage();
// Create conversation and user state with in-memory storage provider.
const conversationState = new ConversationState(memoryStorage);
const userState = new UserState(memoryStorage);
bots/stateManagementBot.js
// The accessor names for the conversation data and user profile state property accessors.
const CONVERSATION_DATA_PROPERTY = 'conversationData';
const USER_PROFILE_PROPERTY = 'userProfile';
class StateManagementBot extends ActivityHandler {
constructor(conversationState, userState) {
super();
// Create the state property accessors for the conversation data and user profile.
this.conversationDataAccessor = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
this.userProfileAccessor = userState.createProperty(USER_PROFILE_PROPERTY);
// The state management objects for the conversation and user state.
Ensuite, vous inscrivez StateManagementBot
dans Application.java. ConversationState et UserState sont fournis par défaut à partir de la classe BotDependencyConfiguration, et Spring les injecte dans la méthode getBot.
Application.java
Avertissement
Il semble que l'échantillon que vous recherchez a été déplacé ! Nous sommes assurés que nous travaillons à résoudre ce problème.
Ensuite, vous inscrivez MemoryStorage
qui est utilisé pour créer les objets UserState
et ConversationState
. Ceux-ci sont créés dans app.py et sont consommés lors de la création du bot.
app.py
CONVERSATION_STATE = ConversationState(MEMORY)
# Create Bot
BOT = StateManagementBot(CONVERSATION_STATE, USER_STATE)
# Listen for incoming requests on /api/messages.
bots/state_management_bot.py
def __init__(self, conversation_state: ConversationState, user_state: UserState):
if conversation_state is None:
raise TypeError(
"[StateManagementBot]: Missing parameter. conversation_state is required but None was given"
)
if user_state is None:
raise TypeError(
"[StateManagementBot]: Missing parameter. user_state is required but None was given"
)
self.conversation_state = conversation_state
self.user_state = user_state
self.conversation_data_accessor = self.conversation_state.create_property(
"ConversationData"
)
self.user_profile_accessor = self.user_state.create_property("UserProfile")
Ajouter des accesseurs de propriété d’état
Vous pouvez à présent créer des accesseurs de propriété en utilisant la méthode CreateProperty
qui fournit un accès à l'objet BotState
. Chaque accesseur de propriété d’état vous permet d’obtenir ou de définir la valeur de la propriété d’état associée. Avant d'utiliser les propriétés de l'état, utilisez chaque accesseur pour charger la propriété à partir du stockage et la récupérer depuis le cache de l'état. Pour obtenir la clé correctement définie associée à la propriété d'état, vous appelez la méthode GetAsync
.
Bots/StateManagementBot.cs
var conversationStateAccessors = _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
Vous devez à présent créer des accesseurs de propriété pour UserState
et ConversationState
. Chaque accesseur de propriété d’état vous permet d’obtenir ou de définir la valeur de la propriété d’état associée. Vous utilisez chaque accesseur pour charger la propriété associée à partir du stockage et récupérer son état actuel à partir du cache.
bots/stateManagementBot.js
constructor(conversationState, userState) {
super();
// Create the state property accessors for the conversation data and user profile.
Vous créez à présent des accesseurs de propriétés à l'aide de la méthode createProperty
. Chaque accesseur de propriété d’état vous permet d’obtenir ou de définir la valeur de la propriété d’état associée. Avant d'utiliser les propriétés de l'état, utilisez chaque accesseur pour charger la propriété à partir du stockage et la récupérer depuis le cache de l'état. Pour obtenir la clé correctement définie associée à la propriété d'état, vous appelez la méthode get
.
StateManagementBot.java
Avertissement
Il semble que l'échantillon que vous recherchez a été déplacé ! Nous sommes assurés que nous travaillons à résoudre ce problème.
Vous devez à présent créer des accesseurs de propriété pour UserProfile
et ConversationData
. Chaque accesseur de propriété d’état vous permet d’obtenir ou de définir la valeur de la propriété d’état associée. Vous utilisez chaque accesseur pour charger la propriété associée à partir du stockage et récupérer son état actuel à partir du cache.
bots/state_management_bot.py
self.conversation_data_accessor = self.conversation_state.create_property(
"ConversationData"
)
self.user_profile_accessor = self.user_state.create_property("UserProfile")
Accéder à l’état à partir du bot
La section précédente présente les étapes nécessaires au moment de l’initialisation pour ajouter des accesseurs de propriété d’état à notre bot. Vous pouvez à présent utiliser ces accesseurs au moment de l'exécution pour lire et écrire des informations d'état. L’exemple de code ci-dessous utilise le flux logique suivant :
- Si
userProfile.Name
est vide et conversationData.PromptedUserForName
a la valeur true, vous récupérez le nom d'utilisateur fourni et le stockez dans l'état de l'utilisateur.
- Si
userProfile.Name
est vide et conversationData.PromptedUserForName
a la valeur false, vous demandez le nom de l'utilisateur.
- Si
userProfile.Name
a été stocké, vous récupérez l'heure du message et l'ID du canal à partir de l'entrée de l'utilisateur, vous renvoyez toutes les données à l'utilisateur et vous stockez les données récupérées dans l'état de la conversation.
Bots/StateManagementBot.cs
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// Get the state properties from the turn context.
var conversationStateAccessors = _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData());
var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
var userProfile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile());
if (string.IsNullOrEmpty(userProfile.Name))
{
// First time around this is set to false, so we will prompt user for name.
if (conversationData.PromptedUserForName)
{
// Set the name to what the user provided.
userProfile.Name = turnContext.Activity.Text?.Trim();
// Acknowledge that we got their name.
await turnContext.SendActivityAsync($"Thanks {userProfile.Name}. To see conversation data, type anything.");
// Reset the flag to allow the bot to go through the cycle again.
conversationData.PromptedUserForName = false;
}
else
{
// Prompt the user for their name.
await turnContext.SendActivityAsync($"What is your name?");
// Set the flag to true, so we don't prompt in the next turn.
conversationData.PromptedUserForName = true;
}
}
else
{
// Add message details to the conversation data.
// Convert saved Timestamp to local DateTimeOffset, then to string for display.
var messageTimeOffset = (DateTimeOffset)turnContext.Activity.Timestamp;
var localMessageTime = messageTimeOffset.ToLocalTime();
conversationData.Timestamp = localMessageTime.ToString();
conversationData.ChannelId = turnContext.Activity.ChannelId.ToString();
// Display state data.
await turnContext.SendActivityAsync($"{userProfile.Name} sent: {turnContext.Activity.Text}");
await turnContext.SendActivityAsync($"Message received at: {conversationData.Timestamp}");
await turnContext.SendActivityAsync($"Message received from: {conversationData.ChannelId}");
}
}
Avant de quitter le gestionnaire de tours, vous utilisez la méthode SaveChangesAsync() des objets de gestion de l'état pour écrire toutes les modifications de l'état dans le stockage.
Bots/StateManagementBot.cs
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
await base.OnTurnAsync(turnContext, cancellationToken);
// Save any state changes that might have occurred during the turn.
await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await _userState.SaveChangesAsync(turnContext, false, cancellationToken);
}
- Si
userProfile.Name
est vide et conversationData.PromptedUserForName
a la valeur true, vous récupérez le nom d'utilisateur fourni et le stockez dans l'état de l'utilisateur.
- Si
userProfile.Name
est vide et conversationData.PromptedUserForName
a la valeur false, vous demandez le nom de l'utilisateur.
- Si
userProfile.Name
a été stocké, vous récupérez l'heure du message et l'ID du canal à partir de l'entrée de l'utilisateur, vous renvoyez toutes les données à l'utilisateur et vous stockez les données récupérées dans l'état de la conversation.
bots/stateManagementBot.js
this.userState = userState;
this.onMessage(async (turnContext, next) => {
// Get the state properties from the turn context.
const userProfile = await this.userProfileAccessor.get(turnContext, {});
const conversationData = await this.conversationDataAccessor.get(
turnContext, { promptedForUserName: false });
if (!userProfile.name) {
// First time around this is undefined, so we will prompt user for name.
if (conversationData.promptedForUserName) {
// Set the name to what the user provided.
userProfile.name = turnContext.activity.text;
// Acknowledge that we got their name.
await turnContext.sendActivity(`Thanks ${ userProfile.name }. To see conversation data, type anything.`);
// Reset the flag to allow the bot to go though the cycle again.
conversationData.promptedForUserName = false;
} else {
// Prompt the user for their name.
await turnContext.sendActivity('What is your name?');
// Set the flag to true, so we don't prompt in the next turn.
conversationData.promptedForUserName = true;
}
} else {
// Add message details to the conversation data.
conversationData.timestamp = turnContext.activity.timestamp?.toLocaleString();
conversationData.channelId = turnContext.activity.channelId;
// Display state data.
await turnContext.sendActivity(`${ userProfile.name } sent: ${ turnContext.activity.text }`);
await turnContext.sendActivity(`Message received at: ${ conversationData.timestamp }`);
await turnContext.sendActivity(`Message received from: ${ conversationData.channelId }`);
}
// By calling next() you ensure that the next BotHandler is run.
Avant de quitter chaque tour de dialogue, vous utilisez la méthode saveChanges() des objets de gestion d'état pour conserver tous les changements en récrivant l'état dans le stockage.
bots/stateManagementBot.js
}
/**
* Override the ActivityHandler.run() method to save state changes after the bot logic completes.
*/
async run(context) {
await super.run(context);
// Save any state changes. The load happened during the execution of the Dialog.
await this.conversationState.saveChanges(context, false);
- Si
userProfile.getName()
est vide et conversationData.getPromptedUserForName()
a la valeur true, vous récupérez le nom d'utilisateur fourni et le stockez dans l'état de l'utilisateur.
- Si
userProfile.getName()
est vide et conversationData.getPromptedUserForName()
a la valeur false, vous demandez le nom de l'utilisateur.
- Si
userProfile.getName()
a été stocké, vous récupérez l'heure du message et l'ID du canal à partir de l'entrée de l'utilisateur, vous renvoyez toutes les données à l'utilisateur et vous stockez les données récupérées dans l'état de la conversation.
StateManagementBot.java
Avertissement
Il semble que l'échantillon que vous recherchez a été déplacé ! Nous sommes assurés que nous travaillons à résoudre ce problème.
Avant de quitter le gestionnaire de tours, vous utilisez la méthode saveChanges() des objets de gestion d'état pour écrire tous les changements d'état dans le stockage.
StateManagementBot.java
Avertissement
Il semble que l'échantillon que vous recherchez a été déplacé ! Nous sommes assurés que nous travaillons à résoudre ce problème.
- Si
user_profile.name
est vide et si conversation_data.prompted_for_user_name
a la valeur true, le bot récupère le nom fourni par l’utilisateur et le stocke dans l’état de l’utilisateur.
- Si
user_profile.name
est vide et si conversation_data.prompted_for_user_name
a la valeur false, le bot demande le nom de l'utilisateur.
- Si
user_profile.name
a été stocké, le bot récupère l'heure du message et l'ID de canal à partir de l'entrée utilisateur, retourne les données à l'utilisateur, puis stocke les données récupérées dans l'état de conversation.
bots/state_management_bot.py
async def on_message_activity(self, turn_context: TurnContext):
# Get the state properties from the turn context.
user_profile = await self.user_profile_accessor.get(turn_context, UserProfile)
conversation_data = await self.conversation_data_accessor.get(
turn_context, ConversationData
)
if user_profile.name is None:
# First time around this is undefined, so we will prompt user for name.
if conversation_data.prompted_for_user_name:
# Set the name to what the user provided.
user_profile.name = turn_context.activity.text
# Acknowledge that we got their name.
await turn_context.send_activity(
f"Thanks { user_profile.name }. To see conversation data, type anything."
)
# Reset the flag to allow the bot to go though the cycle again.
conversation_data.prompted_for_user_name = False
else:
# Prompt the user for their name.
await turn_context.send_activity("What is your name?")
# Set the flag to true, so we don't prompt in the next turn.
conversation_data.prompted_for_user_name = True
else:
# Add message details to the conversation data.
conversation_data.timestamp = self.__datetime_from_utc_to_local(
turn_context.activity.timestamp
)
conversation_data.channel_id = turn_context.activity.channel_id
# Display state data.
await turn_context.send_activity(
f"{ user_profile.name } sent: { turn_context.activity.text }"
)
await turn_context.send_activity(
f"Message received at: { conversation_data.timestamp }"
)
await turn_context.send_activity(
f"Message received from: { conversation_data.channel_id }"
)
Avant la fin de chaque tour de dialogue, le bot utilise la méthode save_changes
des objets de gestion de l'état pour conserver tous les changements en écrivant des informations sur l'état dans la stockage.
bots/state_management_bot.py
async def on_turn(self, turn_context: TurnContext):
await super().on_turn(turn_context)
await self.conversation_state.save_changes(turn_context)
await self.user_state.save_changes(turn_context)
Tester votre bot
- Téléchargez et installez la dernière version de Bot Framework Emulator.
- Exécutez l’exemple en local sur votre machine.
Si vous avez besoin d'instructions, consultez le fichier README pour C#, JavaScript, Java, ou Python.
- Utilisez l'émulateur pour tester votre exemple de bot.
Cet article décrit comment ajouter un état à votre bot. Consultez le tableau suivant pour plus d'informations sur les rubriques connexes.
Sujet |
Remarques |
Confidentialité |
Si vous prévoyez de stocker les données personnelles de l’utilisateur, vous devez veiller au respect du Règlement général sur la protection des données. |
Gestion de l’état |
Tous les appels de gestion d’état sont asynchrones et, par défaut, le dernier qui écrit gagne (last-writer-wins). Dans la pratique, vous devez obtenir, définir et enregistrer l’état dans votre bot de la façon la plus rapprochée possible. Pour une discussion sur la façon d'implémenter le verrouillage optimiste, consultez Implémenter un stockage personnalisé pour votre bot. |
Données critique sur l'activité |
Utilisez l'état du bot pour stocker les préférences, le nom d'utilisateur ou le dernier élément commandé. En revanche, ne l'utilisez pas pour stocker des données critiques. Pour les données critiques, créez vos propres composants de stockage ou écrivez directement dans le stockage. |
Texte de reconnaissance |
L’exemple utilise les bibliothèques Microsoft/Recognizers-Text pour analyser et valider les entrées utilisateur. Pour plus d’informations, consultez la page de présentation. |
Étapes suivantes
Découvrez comment poser une série de questions à l'utilisateur, valider ses réponses et enregistrer ses données.