Bot Framework JavaScript、C# 和 Python SDK 将继续受支持,但 Java SDK 即将停用,最终长期支持将于 2023 年 11 月结束。
使用 Java SDK 构建的现有机器人将继续正常运行。
要生成新的机器人,请考虑使用
Microsoft Copilot Studio
并阅读
选择正确的助理解决方案
。
有关详细信息,请参阅
机器人构建的未来
。
了解
机器人基础知识
、
管理状态
、
对话库
,以及如何
实现顺序聊天流
。
以
C#
、
JavaScript
、
Java
或
Python
编写的复杂对话示例的副本。
关于此示例
本示例演示一个可以注册用户,让其针对列表中的最多两家公司发表评论的机器人。
该机器人使用 3 个组件对话来管理对话流。
每个组件对话包含一个瀑布对话,以及收集用户输入所需的任何提示。
这些对话在以下部分详述。
它使用聊天状态管理其对话,并使用用户状态来保存有关用户及其所要评论的公司的信息。
此机器人派生自活动处理程序。 与许多示例机器人一样,它会欢迎用户,使用对话处理来自用户的消息,并在该轮聊天结束之前保存用户和聊天状态。
JavaScript
Python
UserProfile.cs
/// <summary>Contains information about a user.</summary>
public class UserProfile
public string Name { get; set; }
public int Age { get; set; }
// The list of companies the user wants to review.
public List<string> CompaniesToReview { get; set; } = new List<string>();
userProfile.js
// @ts-check
class UserProfile {
constructor(name, age) {
this.name = name;
this.age = age;
// The list of companies the user wants to review.
this.companiesToReview = [];
UserProfile.java
您正在寻找的示例似乎已经被移动了! 放心,我们正在努力解决此问题。
data_models/user_profile.py
class UserProfile:
def __init__(
self, name: str = None, age: int = 0, companies_to_review: List[str] = None
self.name: str = name
self.age: int = age
self.companies_to_review: List[str] = companies_to_review
此机器人包含 3 个对话:
主对话会启动整个进程,并汇总收集的信息。
顶级对话根据用户的年龄收集用户信息并包括分支逻辑。
用户可以通过“评论-选择”对话以迭代方式选择要评论的公司。 它使用循环逻辑来这样做。
主对话有 2 个步骤:
启动顶级对话。
检索并汇总顶级对话框收集的用户资料,将这些信息保存到用户状态,然后标示主对话的结束。
Dialogs\MainDialog.cs
private async Task<DialogTurnResult> InitialStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
return await stepContext.BeginDialogAsync(nameof(TopLevelDialog), null, cancellationToken);
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
var userInfo = (UserProfile)stepContext.Result;
string status = "You are signed up to review "
+ (userInfo.CompaniesToReview.Count is 0 ? "no companies" : string.Join(" and ", userInfo.CompaniesToReview))
+ ".";
await stepContext.Context.SendActivityAsync(status);
var accessor = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
await accessor.SetAsync(stepContext.Context, userInfo, cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
dialogs/mainDialog.js
async initialStep(stepContext) {
return await stepContext.beginDialog(TOP_LEVEL_DIALOG);
async finalStep(stepContext) {
const userInfo = stepContext.result;
const status = 'You are signed up to review ' +
(userInfo.companiesToReview.length === 0 ? 'no companies' : userInfo.companiesToReview.join(' and ')) + '.';
await stepContext.context.sendActivity(status);
await this.userProfileAccessor.set(stepContext.context, userInfo);
MainDialog.java
您正在寻找的示例似乎已经被移动了! 放心,我们正在努力解决此问题。
dialogs\main_dialog.py
async def initial_step(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
return await step_context.begin_dialog(TopLevelDialog.__name__)
async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
user_info: UserProfile = step_context.result
companies = (
"no companies"
if len(user_info.companies_to_review) == 0
else " and ".join(user_info.companies_to_review)
status = f"You are signed up to review {companies}."
await step_context.context.send_activity(MessageFactory.text(status))
# store the UserProfile
accessor = self.user_state.create_property("UserProfile")
await accessor.set(step_context.context, user_info)
return await step_context.end_dialog()
顶级对话有 4 个步骤:
询问用户的姓名。
询问用户的年龄
根据用户的年龄,启动“评论-选择”对话或转到下一步。
最后,感谢用户参与并返回收集的信息。
第一步是创建一个空的用户角色,作为会话状态的一部分。 该对话从一个空的个人资料开始,随着交流的进行不断向个人资料中添加信息。 结束时,最后一步会返回收集的信息。
在第三(开始选择)步中,会根据用户的年龄将聊天流分支。
JavaScript
Python
stepContext.Values[UserInfo] = new UserProfile();
var promptOptions = new PromptOptions { Prompt = MessageFactory.Text("Please enter your name.") };
// Ask the user to enter their name.
return await stepContext.PromptAsync(nameof(TextPrompt), promptOptions, cancellationToken);
private async Task<DialogTurnResult> AgeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
// Set the user's name to what they entered in response to the name prompt.
var userProfile = (UserProfile)stepContext.Values[UserInfo];
userProfile.Name = (string)stepContext.Result;
var promptOptions = new PromptOptions { Prompt = MessageFactory.Text("Please enter your age.") };
// Ask the user to enter their age.
return await stepContext.PromptAsync(nameof(NumberPrompt<int>), promptOptions, cancellationToken);
private async Task<DialogTurnResult> StartSelectionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
// Set the user's age to what they entered in response to the age prompt.
var userProfile = (UserProfile)stepContext.Values[UserInfo];
userProfile.Age = (int)stepContext.Result;
if (userProfile.Age < 25)
// If they are too young, skip the review selection dialog, and pass an empty list to the next step.
await stepContext.Context.SendActivityAsync(
MessageFactory.Text("You must be 25 or older to participate."),
cancellationToken);
return await stepContext.NextAsync(new List<string>(), cancellationToken);
// Otherwise, start the review selection dialog.
return await stepContext.BeginDialogAsync(nameof(ReviewSelectionDialog), null, cancellationToken);
private async Task<DialogTurnResult> AcknowledgementStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
// Set the user's company selection to what they entered in the review-selection dialog.
var userProfile = (UserProfile)stepContext.Values[UserInfo];
userProfile.CompaniesToReview = stepContext.Result as List<string> ?? new List<string>();
// Thank them for participating.
await stepContext.Context.SendActivityAsync(
MessageFactory.Text($"Thanks for participating, {((UserProfile)stepContext.Values[UserInfo]).Name}."),
cancellationToken);
// Exit the dialog, returning the collected user information.
return await stepContext.EndDialogAsync(stepContext.Values[UserInfo], cancellationToken);
dialogs/topLevelDialog.js
async nameStep(stepContext) {
// Create an object in which to collect the user's information within the dialog.
stepContext.values.userInfo = new UserProfile();
const promptOptions = { prompt: 'Please enter your name.' };
// Ask the user to enter their name.
return await stepContext.prompt(TEXT_PROMPT, promptOptions);
async ageStep(stepContext) {
// Set the user's name to what they entered in response to the name prompt.
stepContext.values.userInfo.name = stepContext.result;
const promptOptions = { prompt: 'Please enter your age.' };
// Ask the user to enter their age.
return await stepContext.prompt(NUMBER_PROMPT, promptOptions);
async startSelectionStep(stepContext) {
// Set the user's age to what they entered in response to the age prompt.
stepContext.values.userInfo.age = stepContext.result;
if (stepContext.result < 25) {
// If they are too young, skip the review selection dialog, and pass an empty list to the next step.
await stepContext.context.sendActivity('You must be 25 or older to participate.');
return await stepContext.next();
} else {
// Otherwise, start the review selection dialog.
return await stepContext.beginDialog(REVIEW_SELECTION_DIALOG);
async acknowledgementStep(stepContext) {
// Set the user's company selection to what they entered in the review-selection dialog.
const userProfile = stepContext.values.userInfo;
userProfile.companiesToReview = stepContext.result || [];
await stepContext.context.sendActivity(`Thanks for participating ${ userProfile.name }`);
// Exit the dialog, returning the collected user information.
TopLevelDialog.java
您正在寻找的示例似乎已经被移动了! 放心,我们正在努力解决此问题。
dialogs\top_level_dialog.py
async def name_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
# Create an object in which to collect the user's information within the dialog.
step_context.values[self.USER_INFO] = UserProfile()
# Ask the user to enter their name.
prompt_options = PromptOptions(
prompt=MessageFactory.text("Please enter your name.")
return await step_context.prompt(TextPrompt.__name__, prompt_options)
async def age_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
# Set the user's name to what they entered in response to the name prompt.
user_profile = step_context.values[self.USER_INFO]
user_profile.name = step_context.result
# Ask the user to enter their age.
prompt_options = PromptOptions(
prompt=MessageFactory.text("Please enter your age.")
return await step_context.prompt(NumberPrompt.__name__, prompt_options)
async def start_selection_step(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
# Set the user's age to what they entered in response to the age prompt.
user_profile: UserProfile = step_context.values[self.USER_INFO]
user_profile.age = step_context.result
if user_profile.age < 25:
# If they are too young, skip the review selection dialog, and pass an empty list to the next step.
await step_context.context.send_activity(
MessageFactory.text("You must be 25 or older to participate.")
return await step_context.next([])
# Otherwise, start the review selection dialog.
return await step_context.begin_dialog(ReviewSelectionDialog.__name__)
async def acknowledgement_step(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
# Set the user's company selection to what they entered in the review-selection dialog.
user_profile: UserProfile = step_context.values[self.USER_INFO]
user_profile.companies_to_review = step_context.result
# Thank them for participating.
await step_context.context.send_activity(
MessageFactory.text(f"Thanks for participating, {user_profile.name}.")
# Exit the dialog, returning the collected user information.
return await step_context.end_dialog(user_profile)
review-selection 对话
review-selection 对话包含两个步骤:
请求用户选择要评论的公司,或选择 done
以完成操作。
如果对话是使用任何初始信息启动的,则可通过瀑布步骤上下文的 options 属性获取该信息。 “评论-选择”对话可以自行重启,并使用它来允许用户选择多个要评论的公司。
如果用户已选择要评论的公司,则会从可用的选项中删除该公司。
添加了 done
选项,允许用户早退出循环。
根据情况重复此对话或退出。
如果用户选择了要评论的公司,请将其添加到列表中。
如果用户选择了 2 个公司或选择了退出,请结束对话并返回收集的列表。
否则,请重启对话,并使用列表的内容将其初始化。
Dialogs\ReviewSelectionDialog.cs
private async Task<DialogTurnResult> SelectionStepAsync(
WaterfallStepContext stepContext,
CancellationToken cancellationToken)
// Continue using the same selection list, if any, from the previous iteration of this dialog.
var list = stepContext.Options as List<string> ?? new List<string>();
stepContext.Values[CompaniesSelected] = list;
// Create a prompt message.
string message;
if (list.Count is 0)
message = $"Please choose a company to review, or `{DoneOption}` to finish.";
message = $"You have selected **{list[0]}**. You can review an additional company, " +
$"or choose `{DoneOption}` to finish.";
// Create the list of options to choose from.
var options = _companyOptions.ToList();
options.Add(DoneOption);
if (list.Count > 0)
options.Remove(list[0]);
var promptOptions = new PromptOptions
Prompt = MessageFactory.Text(message),
RetryPrompt = MessageFactory.Text("Please choose an option from the list."),
Choices = ChoiceFactory.ToChoices(options),
// Prompt the user for a choice.
return await stepContext.PromptAsync(nameof(ChoicePrompt), promptOptions, cancellationToken);
private async Task<DialogTurnResult> LoopStepAsync(
WaterfallStepContext stepContext,
CancellationToken cancellationToken)
// Retrieve their selection list, the choice they made, and whether they chose to finish.
var list = stepContext.Values[CompaniesSelected] as List<string>;
var choice = (FoundChoice)stepContext.Result;
var done = choice.Value == DoneOption;
if (!done)
// If they chose a company, add it to the list.
list.Add(choice.Value);
if (done || list.Count >= 2)
// If they're done, exit and return their list.
return await stepContext.EndDialogAsync(list, cancellationToken);
// Otherwise, repeat this dialog, passing in the list from this iteration.
return await stepContext.ReplaceDialogAsync(InitialDialogId, list, cancellationToken);
dialogs/reviewSelectionDialog.js
async selectionStep(stepContext) {
// Continue using the same selection list, if any, from the previous iteration of this dialog.
const list = Array.isArray(stepContext.options) ? stepContext.options : [];
stepContext.values[this.companiesSelected] = list;
// Create a prompt message.
let message = '';
if (list.length === 0) {
message = `Please choose a company to review, or \`${ this.doneOption }\` to finish.`;
} else {
message = `You have selected **${ list[0] }**. You can review an additional company, or choose \`${ this.doneOption }\` to finish.`;
// Create the list of options to choose from.
const options = list.length > 0
? this.companyOptions.filter(function(item) { return item !== list[0]; })
: this.companyOptions.slice();
options.push(this.doneOption);
// Prompt the user for a choice.
return await stepContext.prompt(CHOICE_PROMPT, {
prompt: message,
retryPrompt: 'Please choose an option from the list.',
choices: options
async loopStep(stepContext) {
// Retrieve their selection list, the choice they made, and whether they chose to finish.
const list = stepContext.values[this.companiesSelected];
const choice = stepContext.result;
const done = choice.value === this.doneOption;
if (!done) {
// If they chose a company, add it to the list.
list.push(choice.value);
if (done || list.length > 1) {
// If they're done, exit and return their list.
return await stepContext.endDialog(list);
} else {
// Otherwise, repeat this dialog, passing in the list from this iteration.
return await stepContext.replaceDialog(this.initialDialogId, list);
ReviewSelectionDialog.java
您正在寻找的示例似乎已经被移动了! 放心,我们正在努力解决此问题。
dialogs/review_selection_dialog.py
async def selection_step(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
# step_context.options will contains the value passed in begin_dialog or replace_dialog.
# if this value wasn't provided then start with an emtpy selection list. This list will
# eventually be returned to the parent via end_dialog.
selected: [
] = step_context.options if step_context.options is not None else []
step_context.values[self.COMPANIES_SELECTED] = selected
if len(selected) == 0:
message = (
f"Please choose a company to review, or `{self.DONE_OPTION}` to finish."
else:
message = (
f"You have selected **{selected[0]}**. You can review an additional company, "
f"or choose `{self.DONE_OPTION}` to finish. "
# create a list of options to choose, with already selected items removed.
options = self.company_options.copy()
options.append(self.DONE_OPTION)
if len(selected) > 0:
options.remove(selected[0])
# prompt with the list of choices
prompt_options = PromptOptions(
prompt=MessageFactory.text(message),
retry_prompt=MessageFactory.text("Please choose an option from the list."),
choices=self._to_choices(options),
return await step_context.prompt(ChoicePrompt.__name__, prompt_options)
def _to_choices(self, choices: [str]) -> List[Choice]:
choice_list: List[Choice] = []
for choice in choices:
choice_list.append(Choice(value=choice))
return choice_list
async def loop_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
selected: List[str] = step_context.values[self.COMPANIES_SELECTED]
choice: FoundChoice = step_context.result
done = choice.value == self.DONE_OPTION
# If they chose a company, add it to the list.
if not done:
selected.append(choice.value)
# If they're done, exit and return their list.
if done or len(selected) >= 2:
return await step_context.end_dialog(selected)
# Otherwise, repeat this dialog, passing in the selections from this iteration.
return await step_context.replace_dialog(
self.initial_dialog_id, selected
“对话机器人” 类扩展了活动处理程序,包含用于运行对话的逻辑。
“对话和欢迎机器人” 类扩展了对话机器人,在用户加入聊天时也会欢迎用户。
机器人的轮次处理程序重复这 3 个对话定义的对话流。
当它收到来自用户的消息时:
它运行主对话。
如果对话堆栈为空,这将启动主对话。
否则,对话仍处于中间进程,这将继续活动对话。
它会保存状态,以持久保存对用户、聊天和对话状态所做的任何更新。
Bots\DialogBot.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);
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
Logger.LogInformation("Running dialog with Message Activity.");
// Run the Dialog with the new message Activity.
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
bots/dialogBot.js
this.conversationState = conversationState;
this.userState = userState;
this.dialog = dialog;
this.dialogState = this.conversationState.createProperty('DialogState');
this.onMessage(async (context, next) => {
console.log('Running dialog with Message Activity.');
// Run the Dialog with the new message Activity.
// By calling next() you ensure that the next BotHandler is run.
await next();
* Override the ActivityHandler.run() method to save state changes after the bot logic completes.
async run(context) {
await super.run(context);
DialogBot.java
您正在寻找的示例似乎已经被移动了! 放心,我们正在努力解决此问题。
bots/dialog_bot.py
async def on_turn(self, turn_context: TurnContext):
await super().on_turn(turn_context)
# Save any state changes that might have occurred during the turn.
await self.conversation_state.save_changes(turn_context, False)
await self.user_state.save_changes(turn_context, False)
async def on_message_activity(self, turn_context: TurnContext):
await DialogHelper.run_dialog(
self.dialog,
turn_context,
self.conversation_state.create_property("DialogState"),
注册机器人的服务
根据需要创建并注册服务:
机器人的基本服务:适配器和机器人实现。
用于管理状态的服务:存储、用户状态和聊天状态。
机器人将使用的根对话。
Startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
services.AddHttpClient().AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.MaxDepth = HttpHelper.BotMessageSerializerSettings.MaxDepth;
// Create the Bot Framework Authentication to be used with the Bot Adapter.
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
// Create the Bot Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
// Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
services.AddSingleton<IStorage, MemoryStorage>();
// Create the User state. (Used in this bot's Dialog implementation.)
services.AddSingleton<UserState>();
const memoryStorage = new MemoryStorage();
// Create user and conversation state with in-memory storage provider.
const userState = new UserState(memoryStorage);
const conversationState = new ConversationState(memoryStorage);
// Create the main dialog.
const dialog = new MainDialog(userState);
const bot = new DialogAndWelcomeBot(conversationState, userState, dialog);
// Catch-all for errors.
adapter.onTurnError = async (context, error) => {
// This check writes out errors to console log .vs. app insights.
// NOTE: In production environment, you should consider logging this to Azure
// application insights. See https://aka.ms/bottelemetry for telemetry
Application.java
您正在寻找的示例似乎已经被移动了! 放心,我们正在努力解决此问题。
app.py
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
ADAPTER = CloudAdapter(ConfigurationBotFrameworkAuthentication(CONFIG))
# Catch-all for errors.
CONVERSATION_STATE = ConversationState(MEMORY)
# Create Dialog and Bot
DIALOG = MainDialog(USER_STATE)
BOT = DialogAndWelcomeBot(CONVERSATION_STATE, USER_STATE, DIALOG)
# Listen for incoming requests on /api/messages.
有关如何实现对话的介绍,请参阅实现有序的对话流,其中使用了单个瀑布对话和一些提示来向用户提出一系列问题。
“对话”库包含提示的基本验证。 你也可以添加自定义验证。 有关详细信息,请参阅使用对话提示收集用户输入。
若要简化对话代码并将其重复用于多个机器人,可将对话集的某些部分定义为单独的类。
有关详细信息,请参阅重复使用对话。
重复使用对话