Bot secara inheren tanpa status. Setelah bot Anda disebarkan, bot mungkin tidak berjalan dalam proses yang sama atau pada komputer yang sama dari satu giliran ke giliran berikutnya. Namun, bot Anda mungkin perlu melacak konteks percakapan sehingga dapat mengelola perilakunya dan mengingat jawaban atas pertanyaan sebelumnya. Fitur status dan penyimpanan Bot Framework SDK memungkinkan Anda menambahkan status ke bot Anda. Bot menggunakan manajemen status dan objek penyimpanan untuk mengelola dan mempertahankan status. Manajer status menyediakan lapisan abstraksi yang memungkinkan Anda mengakses properti status menggunakan aksesor properti, terlepas dari jenis penyimpanan yang mendasar.
Catatan
Bot Framework JavaScript, C#, dan Python SDK akan terus didukung, namun, Java SDK dihentikan dengan dukungan jangka panjang akhir yang berakhir pada November 2023.
Bot yang ada yang dibangun dengan Java SDK akan terus berfungsi.
Kode dalam artikel ini didasarkan pada sampel Bot Manajemen Status. Anda akan memerlukan salinan sampel di C#, JavaScript, Java, atau Python.
Tentang sampel ini
Setelah menerima input pengguna, sampel ini memeriksa status percakapan yang disimpan untuk melihat apakah pengguna ini sebelumnya telah diminta untuk memberikan namanya. Jika tidak, nama pengguna diminta dan input tersebut disimpan dalam status pengguna. Jika demikian, nama yang disimpan dalam status pengguna digunakan untuk berkomunikasi dengan pengguna dan data input mereka, bersama dengan waktu yang diterima dan ID saluran input, dikembalikan kepada pengguna. Nilai ID waktu dan saluran diambil dari data percakapan pengguna lalu disimpan ke status percakapan. Diagram berikut menunjukkan hubungan antara bot, profil pengguna, dan kelas data percakapan.
Langkah pertama dalam menyiapkan manajemen status adalah menentukan kelas yang berisi informasi yang akan dikelola dalam status pengguna dan percakapan. Contoh yang digunakan dalam artikel ini, menentukan kelas berikut:
Dalam UserProfile.cs, Anda menentukan UserProfile kelas untuk informasi pengguna yang akan dikumpulkan bot.
Dalam ConversationData.cs, Anda menentukan ConversationData kelas untuk mengontrol status percakapan kami saat mengumpulkan informasi pengguna.
Contoh kode berikut menunjukkan definisi untuk UserProfile kelas dan 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;
}
Langkah ini tidak diperlukan di JavaScript.
Langkah pertama dalam menyiapkan manajemen status adalah menentukan kelas yang berisi informasi yang akan dikelola dalam status pengguna dan percakapan. Contoh yang digunakan dalam artikel ini mendefinisikan kelas berikut:
Dalam UserProfile.java, Anda menentukan UserProfile kelas untuk informasi pengguna yang akan dikumpulkan bot.
Dalam ConversationData.java, Anda menentukan ConversationData kelas untuk mengontrol status percakapan kami saat mengumpulkan informasi pengguna.
Contoh kode berikut menunjukkan definisi untuk UserProfile kelas dan ConversationData .
UserProfile.java
public class UserProfile {
private String name;
public String getName() {
return name;
}
public void setName(String withName) {
name = withName;
}
}
ConversationData.java
public class ConversationData {
// The time-stamp of the most recent incoming message.
private String timestamp;
// The ID of the user's channel.
private String channelId;
// Track whether we have already asked the user's name.
private boolean promptedUserForName = false;
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String withTimestamp) {
timestamp = withTimestamp;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String withChannelId) {
channelId = withChannelId;
}
public boolean getPromptedUserForName() {
return promptedUserForName;
}
public void setPromptedUserForName(boolean withPromptedUserForName) {
Langkah pertama dalam menyiapkan manajemen status adalah menentukan kelas yang berisi informasi yang akan dikelola dalam status pengguna dan percakapan. Contoh yang digunakan dalam artikel ini, menentukan kelas berikut:
user_profile.py berisi UserProfile kelas yang menyimpan informasi pengguna yang dikumpulkan oleh bot.
conversation_data.py berisi ConversationData kelas yang mengontrol status percakapan saat mengumpulkan informasi pengguna.
Contoh kode berikut menunjukkan definisi untuk UserProfile kelas dan ConversationData .
user_profile.py
class UserProfile:
def __init__(self, name: str = None):
self.name = name
Selanjutnya, Anda mendaftar MemoryStorage yang digunakan untuk membuat UserState objek dan ConversationState . Objek status pengguna dan percakapan dibuat di Startup dan dependensi disuntikkan ke konstruktor bot. Layanan lain untuk bot yang terdaftar adalah: penyedia info masuk, adaptor, dan implementasi 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 */
Selanjutnya, Anda mendaftar MemoryStorage yang kemudian digunakan untuk membuat UserState objek dan ConversationState . Ini dibuat dalam index.js dan digunakan saat bot dibuat.
index.js
const memoryStorage = new MemoryStorage();
// Create conversation and user state with in-memory storage provider.
const conversationState = new ConversationState(memoryStorage);
const userState = new UserState(memoryStorage);
// Create the bot.
const bot = new StateManagementBot(conversationState, userState);
bot/stateManagementBot.js
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.
this.conversationState = conversationState;
this.userState = userState;
Selanjutnya, Anda mendaftarkan StateManagementBot di Application.java. ConversationState dan UserState disediakan secara default dari kelas BotDependencyConfiguration, dan Spring akan memasukkannya ke dalam metode getBot.
Application.java
@Bean
public Bot getBot(
ConversationState conversationState,
UserState userState
) {
return new StateManagementBot(conversationState, userState);
}
Selanjutnya, Anda mendaftar MemoryStorage yang digunakan untuk membuat UserState objek dan ConversationState . Ini dibuat dalam app.py dan digunakan saat bot dibuat.
app.py
CONVERSATION_STATE = ConversationState(MEMORY)
# Create Bot
BOT = StateManagementBot(CONVERSATION_STATE, USER_STATE)
# Listen for incoming requests on /api/messages.
bot/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")
Sekarang Anda membuat aksesor properti menggunakan CreateProperty metode yang menyediakan handel ke BotState objek. Setiap pengaktif properti status memungkinkan Anda mendapatkan atau mengatur nilai properti status terkait. Sebelum Anda menggunakan properti status, gunakan setiap aksesor untuk memuat properti dari penyimpanan dan mendapatkannya dari cache status. Untuk mendapatkan kunci yang tercakup dengan benar yang terkait dengan properti status, Anda memanggil GetAsync metode .
Bot/StateManagementBot.cs
var conversationStateAccessors = _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
Sekarang Anda membuat aksesor properti untuk UserState dan ConversationState. Setiap pengaktif properti status memungkinkan Anda mendapatkan atau mengatur nilai properti status terkait. Anda menggunakan setiap aksesor untuk memuat properti terkait dari penyimpanan dan mengambil statusnya saat ini dari cache.
bot/stateManagementBot.js
// 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);
Sekarang Anda membuat aksesor properti menggunakan metode .createProperty Setiap pengaktif properti status memungkinkan Anda mendapatkan atau mengatur nilai properti status terkait. Sebelum Anda menggunakan properti status, gunakan setiap aksesor untuk memuat properti dari penyimpanan dan mendapatkannya dari cache status. Untuk mendapatkan kunci yang tercakup dengan benar yang terkait dengan properti status, Anda memanggil get metode .
Sekarang Anda membuat aksesor properti untuk UserProfile dan ConversationData. Setiap pengaktif properti status memungkinkan Anda mendapatkan atau mengatur nilai properti status terkait. Anda menggunakan setiap aksesor untuk memuat properti terkait dari penyimpanan dan mengambil statusnya saat ini dari cache.
Bagian sebelumnya mencakup langkah-langkah waktu inisialisasi untuk menambahkan pengakses properti status ke bot kami. Sekarang, Anda dapat menggunakan aksesor tersebut pada run-time untuk membaca dan menulis informasi status. Kode sampel di bawah ini menggunakan alur logika berikut:
Jika userProfile.Name kosong dan conversationData.PromptedUserForName benar, Anda mengambil nama pengguna yang disediakan dan menyimpannya dalam status pengguna.
Jika userProfile.Name kosong dan conversationData.PromptedUserForNamesalah, Anda meminta nama pengguna.
Jika userProfile.Name sebelumnya disimpan, Anda mengambil waktu pesan dan ID saluran dari input pengguna, menggemakan semua data kembali ke pengguna, dan menyimpan data yang diambil dalam status percakapan.
Bot/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}");
}
}
Sebelum Anda keluar dari handler giliran, Anda menggunakan metode SaveChangesAsync() objek manajemen status untuk menulis semua perubahan status kembali ke penyimpanan.
Bot/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);
}
Jika userProfile.Name kosong dan conversationData.PromptedUserForName benar, Anda mengambil nama pengguna yang disediakan dan menyimpannya dalam status pengguna.
Jika userProfile.Name kosong dan conversationData.PromptedUserForNamesalah, Anda meminta nama pengguna.
Jika userProfile.Name sebelumnya disimpan, Anda mengambil waktu pesan dan ID saluran dari input pengguna, menggemakan semua data kembali ke pengguna, dan menyimpan data yang diambil dalam status percakapan.
bot/stateManagementBot.js
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.
await next();
});
Sebelum Anda keluar dari setiap giliran dialog, Anda menggunakan metode saveChanges() objek manajemen status untuk mempertahankan semua perubahan dengan menulis status kembali ke penyimpanan.
bot/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);
await this.userState.saveChanges(context, false);
}
Jika userProfile.getName() kosong dan conversationData.getPromptedUserForName() benar, Anda mengambil nama pengguna yang disediakan dan menyimpannya dalam status pengguna.
Jika userProfile.getName() kosong dan conversationData.getPromptedUserForName()salah, Anda meminta nama pengguna.
Jika userProfile.getName() sebelumnya disimpan, Anda mengambil waktu pesan dan ID saluran dari input pengguna, menggemakan semua data kembali ke pengguna, dan menyimpan data yang diambil dalam status percakapan.
StateManagementBot.java
@Override
protected CompletableFuture<Void> onMessageActivity(TurnContext turnContext) {
// Get state data from ConversationState.
StatePropertyAccessor<ConversationData> dataAccessor =
conversationState.createProperty("data");
CompletableFuture<ConversationData> dataFuture =
dataAccessor.get(turnContext, ConversationData::new);
// Get profile from UserState.
StatePropertyAccessor<UserProfile> profileAccessor = userState.createProperty("profile");
CompletableFuture<UserProfile> profileFuture =
profileAccessor.get(turnContext, UserProfile::new);
return dataFuture.thenCombine(profileFuture, (conversationData, userProfile) -> {
if (StringUtils.isBlank(userProfile.getName())) {
// First time around this is set to false, so we will prompt user for name.
if (conversationData.getPromptedUserForName()) {
// Reset the flag to allow the bot to go though the cycle again.
conversationData.setPromptedUserForName(false);
// Set the name to what the user provided and reply.
userProfile.setName(turnContext.getActivity().getText());
// Acknowledge that we got their name.
return turnContext.sendActivity(
MessageFactory.text(
"Thanks " + userProfile.getName()
+ ". To see conversation data, type anything."
)
);
} else {
// Set the flag to true, so we don't prompt in the next turn.
conversationData.setPromptedUserForName(true);
// Prompt the user for their name.
return turnContext.sendActivity(MessageFactory.text("What is your name?"));
}
} else {
OffsetDateTime messageTimeOffset = turnContext.getActivity().getTimestamp();
LocalDateTime localMessageTime = messageTimeOffset.toLocalDateTime();
//Displaying current date and time in 12 hour format with AM/PM
DateTimeFormatter dateTimeAMPMFormat = DateTimeFormatter.ofPattern("MM/dd/yyyy, hh:mm:ss a");
conversationData.setTimestamp(dateTimeAMPMFormat.format(localMessageTime));
conversationData.setChannelId(turnContext.getActivity().getChannelId());
List<Activity> sendToUser = new ArrayList<>();
sendToUser.add(
MessageFactory.text(
userProfile.getName() + " sent: " + turnContext.getActivity().getText()
)
);
sendToUser.add(
MessageFactory.text("Message received at: " + conversationData.getTimestamp()
)
);
sendToUser.add(
MessageFactory.text("Message received from: " + conversationData.getChannelId()
)
);
return turnContext.sendActivities(sendToUser);
}
})
// make the return value happy.
.thenApply(resourceResponse -> null);
}
Sebelum Anda keluar dari handler giliran, Anda menggunakan metode saveChanges() objek manajemen status untuk menulis semua perubahan status kembali ke penyimpanan.
StateManagementBot.java
@Override
public CompletableFuture<Void> onTurn(TurnContext turnContext) {
return super.onTurn(turnContext)
// Save any state changes that might have occurred during the turn.
.thenCompose(turnResult -> conversationState.saveChanges(turnContext))
.thenCompose(saveResult -> userState.saveChanges(turnContext));
}
Jika user_profile.name kosong dan conversation_data.prompted_for_user_name benar, bot mengambil nama yang disediakan oleh pengguna dan menyimpannya dalam status pengguna.
Jika user_profile.name kosong dan conversation_data.prompted_for_user_namesalah, bot akan meminta nama pengguna.
Jika user_profile.name sebelumnya disimpan, bot mengambil waktu pesan dan ID saluran dari input pengguna, menggemakan data kembali ke pengguna, dan menyimpan data yang diambil dalam status percakapan.
bot/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 }"
)
Sebelum setiap dialog berakhir, bot menggunakan metode objek save_changes manajemen status untuk mempertahankan semua perubahan dengan menulis informasi status di penyimpanan.
Unduh dan instal Emulator Kerangka Kerja Bot terbaru
Jalankan sampel secara lokal di komputer Anda.
Jika Anda memerlukan instruksi, lihat README untuk C#, JavaScript, Java, atau Python.
Gunakan Emulator untuk menguji bot sampel Anda.
Informasi Tambahan
Artikel ini menjelaskan cara menambahkan status ke bot Anda. Lihat tabel berikut ini untuk informasi selengkapnya tentang topik terkait.
Topik
Catatan
Privasi
Jika Anda ingin menyimpan data pribadi pengguna, Anda harus memastikan kepatuhan terhadap Peraturan Perlindungan Data Umum.
Manajemen Status
Semua panggilan manajemen status bersifat asinkron, dan last-writer-wins secara default. Dalam praktiknya, Anda harus mendapatkan, mengatur, dan menyimpan status sedekat mungkin di bot Anda. Untuk diskusi tentang cara menerapkan penguncian optimis, lihat Menerapkan penyimpanan kustom untuk bot Anda.
Data bisnis penting
Gunakan status bot untuk menyimpan preferensi, nama pengguna, atau hal terakhir yang mereka pesan, tetapi jangan gunakan untuk menyimpan data bisnis penting. Untuk data penting, buat komponen penyimpanan Anda sendiri atau tulis langsung ke penyimpanan.
Recognizer-Text
Sampel menggunakan pustaka Microsoft/Recognizers-Text untuk mengurai dan memvalidasi input pengguna. Untuk informasi selengkapnya, lihat halaman gambaran umum .
Langkah berikutnya
Pelajari cara mengajukan serangkaian pertanyaan kepada pengguna, memvalidasi jawaban mereka, dan menyimpan input mereka.