DOTYCZY: SDK w wersji 4
Bot jest z natury bezstanowy. Po wdrożeniu bota, może on nie działać w tym samym procesie lub na tej samej maszynie przy każdym uruchomieniu. Jednak bot może wymagać śledzenia kontekstu konwersacji, aby mógł zarządzać swoim zachowaniem i pamiętać odpowiedzi na poprzednie pytania. Funkcje stanu i przechowywania zestawu Bot Framework SDK umożliwiają dodawanie stanu do bota. Boty używają obiektów zarządzania stanem i magazynowania do przechowywania i utrwalania stanu. Menedżer stanu udostępnia warstwę abstrakcji, która umożliwia dostęp do właściwości stanu przy użyciu metod dostępu do właściwości niezależnie od typu magazynu bazowego.
Uwaga
Aby tworzyć agentów z wybranymi usługami sztucznej inteligencji, orkiestracją i wiedzą, rozważ użycie zestawu SDK agentów platformy Microsoft 365. Zestaw SDK agentów obsługuje języki C#, JavaScript lub Python. Więcej informacji na temat zestawu SDK agentów można uzyskać na stronie aka.ms/agents. Jeśli szukasz platformy agenta opartej na modelu SaaS, rozważ microsoft Copilot Studio. Jeśli masz istniejącego bota utworzonego przy użyciu zestawu Bot Framework SDK, możesz zaktualizować bota do zestawu SDK agentów. Wskazówki dotyczące migracji z Bot Framework SDK do Agents SDK znajdziesz pod Bot Framework SDK to Agents SDK migration guidance, które omawiają podstawowe zmiany i aktualizacje. Zgłoszenia do pomocy technicznej dla zestawu Bot Framework SDK nie będą już obsługiwane od 31 grudnia 2025 r.
Wymagania wstępne
- Znajomość podstaw bota i sposobu, w jaki boty zarządzają stanem są wymagane.
- Kod w tym artykule jest oparty na przykładzie bota zarządzania stanem. Będziesz potrzebować kopii przykładu w języku C#, JavaScript, Java lub Python.
Informacje o tym przykładzie
Po otrzymaniu danych wejściowych użytkownika ten przykład sprawdza stan przechowywanej konwersacji, aby sprawdzić, czy ten użytkownik został wcześniej poproszony o podanie ich nazwy. Jeśli nie, użytkownik zostaje poproszony o podanie swojej nazwy, a te dane są przechowywane w stanie użytkownika. Jeśli tak, nazwa przechowywana w stanie użytkownika jest używana do rozmowy z użytkownikiem, a ich dane wejściowe, wraz z czasem otrzymania i identyfikatorem kanału wejściowego, są zwracane do użytkownika. Wartości czasu i identyfikatora kanału są pobierane z danych konwersacji użytkownika, a następnie zapisywane w stanie konwersacji. Na poniższym diagramie przedstawiono relację między klasami danych bota, profilu użytkownika i konwersacji.
Definiowanie klas
Pierwszym krokiem konfigurowania zarządzania stanem jest zdefiniowanie klas zawierających informacje do zarządzania w stanie użytkownika i konwersacji. W przykładzie użytym w tym artykule zdefiniowano następujące klasy:
- W UserProfile.cs zdefiniujesz klasę
UserProfile
dla informacji o użytkowniku, które będzie zbierać bot.
- W ConversationData.cs należy zdefiniować klasę
ConversationData
do kontrolowania stanu konwersacji podczas zbierania informacji o użytkowniku.
W poniższych przykładach kodu przedstawiono definicje klas UserProfile
i 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;
}
Ten krok nie jest konieczny w języku JavaScript.
Pierwszym krokiem konfigurowania zarządzania stanem jest zdefiniowanie klas zawierających informacje do zarządzania w stanie użytkownika i konwersacji. W przykładzie użytym w tym artykule zdefiniowano następujące klasy:
- W UserProfile.java zdefiniujesz klasę
UserProfile
dla informacji o użytkowniku, które będą zbierane przez bota.
- W ConversationData.java zdefiniujesz klasę służącą
ConversationData
do kontrolowania stanu konwersacji podczas zbierania informacji o użytkowniku.
W poniższych przykładach kodu przedstawiono definicje klas UserProfile
i ConversationData
.
UserProfile.java
Ostrzeżenie
Wygląda na to, że przykład, którego szukasz, został przeniesiony! Bądź pewien, że pracujemy nad rozwiązaniem tego problemu.
ConversationData.java
Ostrzeżenie
Wygląda na to, że przykład, którego szukasz, został przeniesiony! Bądź pewien, że pracujemy nad rozwiązaniem tego problemu.
Pierwszym krokiem konfigurowania zarządzania stanem jest zdefiniowanie klas zawierających informacje do zarządzania w stanie użytkownika i konwersacji. W przykładzie użytym w tym artykule zdefiniowano następujące klasy:
-
User_profile.py zawiera klasę
UserProfile
, która przechowuje informacje o użytkowniku zebrane przez bota.
-
Conversation_data.py zawiera klasę
ConversationData
, która kontroluje stan konwersacji podczas zbierania informacji o użytkowniku.
W poniższych przykładach kodu przedstawiono definicje klas UserProfile
i 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
Utwórz obiekty stanu konwersacji i użytkownika
Następnie należy zarejestrować MemoryStorage
, które są używane do tworzenia UserState
i ConversationState
obiektów. Obiekty stanu użytkownika i konwersacji są tworzone w Startup
i wstrzykiwane jako zależności do konstruktora bota. Inne usługi zarejestrowane dla bota to: dostawca poświadczeń, adapter i implementacja bota.
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 */
Boty/StateManagementBot.cs
private BotState _conversationState;
private BotState _userState;
public StateManagementBot(ConversationState conversationState, UserState userState)
{
_conversationState = conversationState;
_userState = userState;
}
Następnie rejestrujesz MemoryStorage
, który jest używany do tworzenia obiektów UserState
i ConversationState
. Są one tworzone w index.js i używane podczas tworzenia bota.
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);
boty/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.
Następnie zarejestruj StateManagementBot
w Application.java. Zarówno ConversationState, jak i UserState są domyślnie udostępniane z klasy BotDependencyConfiguration, a platforma Spring wstrzykuje je do metody getBot.
Application.java
Ostrzeżenie
Wygląda na to, że przykład, którego szukasz, został przeniesiony! Bądź pewien, że pracujemy nad rozwiązaniem tego problemu.
Następnie należy zarejestrować MemoryStorage
, które są używane do tworzenia UserState
i ConversationState
obiektów. Są one tworzone w app.py i używane podczas tworzenia bota.
app.py
CONVERSATION_STATE = ConversationState(MEMORY)
# Create Bot
BOT = StateManagementBot(CONVERSATION_STATE, USER_STATE)
# Listen for incoming requests on /api/messages.
boty/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")
Dodawanie metod dostępu właściwości stanu
Teraz utworzysz metody dostępu do właściwości, przy użyciu metody CreateProperty
, która zapewnia dostęp do obiektu BotState
. Każdy akcesor właściwości stanu umożliwia pobranie lub ustawienie wartości powiązanej właściwości stanu. Przed użyciem właściwości stanu, użyj każdego akcesora, aby najpierw załadować właściwość z magazynu, a następnie pobrać ją z pamięci podręcznej stanu. Aby uzyskać prawidłowy klucz o określonym zakresie skojarzony z właściwością stanu, należy wywołać metodę GetAsync
.
Boty/StateManagementBot.cs
var conversationStateAccessors = _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
Teraz utworzysz metody dostępu do właściwości dla UserState
i ConversationState
. Każdy akcesor właściwości stanu umożliwia pobranie lub ustawienie wartości powiązanej właściwości stanu. Każdy akcesor służy do ładowania skojarzonej właściwości z magazynu i pobierania jej bieżącego stanu z pamięci podręcznej.
boty/stateManagementBot.js
constructor(conversationState, userState) {
super();
// Create the state property accessors for the conversation data and user profile.
Teraz utworzysz metody dostępu do właściwości przy użyciu createProperty
metody . Każdy akcesor właściwości stanu umożliwia pobranie lub ustawienie wartości powiązanej właściwości stanu. Przed użyciem właściwości stanu, użyj każdego akcesora, aby najpierw załadować właściwość z magazynu, a następnie pobrać ją z pamięci podręcznej stanu. Aby uzyskać prawidłowy klucz o określonym zakresie skojarzony z właściwością stanu, należy wywołać metodę get
.
StateManagementBot.java
Ostrzeżenie
Wygląda na to, że przykład, którego szukasz, został przeniesiony! Bądź pewien, że pracujemy nad rozwiązaniem tego problemu.
Teraz utworzysz metody dostępu do właściwości dla UserProfile
i ConversationData
. Każdy akcesor właściwości stanu umożliwia pobranie lub ustawienie wartości powiązanej właściwości stanu. Każdy akcesor służy do ładowania skojarzonej właściwości z magazynu i pobierania jej bieżącego stanu z pamięci podręcznej.
boty/state_management_bot.py
self.conversation_data_accessor = self.conversation_state.create_property(
"ConversationData"
)
self.user_profile_accessor = self.user_state.create_property("UserProfile")
Stan dostępu z bota
W poprzedniej sekcji omówiono kroki wykonywane podczas inicjalizacji, aby dodać akcesory właściwości stanu do naszego bota. Teraz możesz użyć tych akcesorów w czasie wykonywania, aby odczytywać i zapisywać informacje o stanie. Poniższy przykładowy kod używa następującego przepływu logiki:
- Jeśli
userProfile.Name
jest pusty i conversationData.PromptedUserForName
ma wartość true, trzeba pobrać podaną nazwę użytkownika i zapisać ją w stanie użytkownika.
- Jeśli
userProfile.Name
wartość jest pusta i conversationData.PromptedUserForName
ma wartość false, należy poprosić o podanie nazwy użytkownika.
- Jeśli
userProfile.Name
były wcześniej przechowywane, pobierasz czas wiadomości i identyfikator kanału z danych wejściowych użytkownika, wyświetlasz wszystkie te dane użytkownikowi i przechowujesz pobrane dane w stanie konwersacji.
Boty/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}");
}
}
Przed zakończeniem procedury obsługi zdarzeń należy użyć metody SaveChangesAsync() obiektów zarządzania stanem, aby zapisać wszystkie zmiany stanu z powrotem do pamięci.
Boty/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);
}
- Jeśli
userProfile.Name
jest pusty i conversationData.PromptedUserForName
ma wartość true, trzeba pobrać podaną nazwę użytkownika i zapisać ją w stanie użytkownika.
- Jeśli
userProfile.Name
wartość jest pusta i conversationData.PromptedUserForName
ma wartość false, należy poprosić o podanie nazwy użytkownika.
- Jeśli
userProfile.Name
były wcześniej przechowywane, pobierasz czas wiadomości i identyfikator kanału z danych wejściowych użytkownika, wyświetlasz wszystkie te dane użytkownikowi i przechowujesz pobrane dane w stanie konwersacji.
boty/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.
Przed zamknięciem każdego okna dialogowego należy użyć metody saveChanges() obiektów zarządzania stanem, aby utrwalić wszystkie zmiany, zapisując stan z powrotem do pamięci.
boty/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);
- Jeśli
userProfile.getName()
jest pusty i conversationData.getPromptedUserForName()
ma wartość true, trzeba pobrać podaną nazwę użytkownika i zapisać ją w stanie użytkownika.
- Jeśli
userProfile.getName()
wartość jest pusta i conversationData.getPromptedUserForName()
ma wartość false, należy poprosić o podanie nazwy użytkownika.
- Jeśli
userProfile.getName()
były wcześniej przechowywane, pobierasz czas wiadomości i identyfikator kanału z danych wejściowych użytkownika, wyświetlasz wszystkie te dane użytkownikowi i przechowujesz pobrane dane w stanie konwersacji.
StateManagementBot.java
Ostrzeżenie
Wygląda na to, że przykład, którego szukasz, został przeniesiony! Bądź pewien, że pracujemy nad rozwiązaniem tego problemu.
Przed zamknięciem obiektu obsługi zdarzeń należy użyć metody saveChanges() obiektów zarządzania stanem, aby zapisać wszystkie zmiany stanu z powrotem do przechowywania.
StateManagementBot.java
Ostrzeżenie
Wygląda na to, że przykład, którego szukasz, został przeniesiony! Bądź pewien, że pracujemy nad rozwiązaniem tego problemu.
- Jeśli
user_profile.name
jest pusty i conversation_data.prompted_for_user_name
ma wartość true, bot pobiera nazwę podaną przez użytkownika i przechowuje ją w stanie użytkownika.
- Jeśli
user_profile.name
wartość jest pusta i conversation_data.prompted_for_user_name
ma wartość false, bot prosi o podanie nazwy użytkownika.
- Jeśli
user_profile.name
wcześniej był przechowywany, bot pobiera czas komunikatu i identyfikator kanału z danych wejściowych użytkownika, przekazuje dane z powrotem do użytkownika i przechowuje pobrane dane w stanie konwersacji.
boty/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 }"
)
Przed zakończeniem każdej interakcji dialogowej bot używa metody obiektów zarządzania stanem, aby zapisać wszystkie zmiany, zapisując informacje o stanie w pamięci.
boty/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)
Przetestuj swojego bota
- Pobierz i zainstaluj najnowszą wersję emulatora platformy Bot Framework
- Uruchom przykład lokalnie na swoim komputerze.
Jeśli potrzebujesz instrukcji, zapoznaj się z plikiem README dla języków C#, JavaScript, Java lub Python.
- Użyj emulatora, aby przetestować przykładowego bota.
W tym artykule opisano sposób dodawania stanu do bota. Aby uzyskać więcej informacji na temat tematów pokrewnych, zobacz poniższą tabelę.
Temat |
Uwagi |
Prywatność |
Jeśli zamierzasz przechowywać dane osobowe użytkownika, należy zapewnić zgodność z ogólnym rozporządzeniem o ochronie danych. |
Zarządzanie stanem |
Wszystkie wywołania zarządzania stanem są asynchroniczne, a domyślnie zwycięża ostatni piszący. W praktyce należy pobrać, ustawić i zapisać stan tak blisko siebie w bocie, jak to tylko możliwe. Aby zapoznać się z opisem implementacji optymistycznej blokady, zobacz także Implementowanie magazynu niestandardowego dla bota. |
Krytyczne dane biznesowe |
Użyj stanu bota do przechowywania preferencji, nazwy użytkownika lub ostatniej rzeczy, którą zamówili, ale nie używaj go do przechowywania krytycznych danych biznesowych. W przypadku danych krytycznych utwórz własne komponenty przechowywania lub zapisz bezpośrednio w magazynie. |
Recognizer-Tekst |
W przykładzie użyto bibliotek Microsoft/Recognizers-Text do analizowania i weryfikowania danych wejściowych użytkownika. Aby uzyskać więcej informacji, zobacz stronę przeglądu . |
Następne kroki
Dowiedz się, jak zadać użytkownikowi serię pytań, zweryfikować odpowiedzi i zapisać swoje dane wejściowe.