您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.

FormFlow 的高级功能Advanced features of FormFlow

备注

本主题适用于 SDK v3 版本。This topic applies to SDK v3 release. 可以在此处找到最新版 SDK (v4) 的文档。You can find the documentation for the latest version of the SDK v4 here.

FormFlow 的基本功能介绍了基本的 FormFlow 实现,提供了相当通用的用户体验。Basic features of FormFlow describes a basic FormFlow implementation that delivers a fairly generic user experience. 若要使用 FormFlow 提供自定义程度更高的用户体验,可以指定初始窗体状态、添加业务逻辑来管理字段之间的相互依赖关系并处理用户输入,以及使用属性来自定义提示、重写模板、指定可选字段、匹配用户输入和验证用户输入。To deliver a more customized user experience using FormFlow, you can specify initial form state, add business logic to manage interdependencies between fields and process user input, and use attributes to customize prompts, override templates, designate optional fields, match user input, and validate user input.

指定初始窗体状态和实体Specify initial form state and entities

启动 FormDialog 时,可以选择传入一个状态实例。When you launch a FormDialog, you may optionally pass in an instance of your state. 如果确实传入了一个状态实例,则默认情况下,FormFlow 会跳过已包含值的字段的步骤;系统不会提示用户输入这些字段的值。If you do pass in an instance of your state, then by default, FormFlow will skip steps for any fields that already contain values; the user will not be prompted for those fields. 若要强制窗体提示用户输入所有字段(包括那些在初始状态中已包含值的字段)的值,请在启动 FormDialog 时传入 FormOptions.PromptFieldsWithValuesTo force the form to prompt the user for all fields (including those fields that already contain values in the initial state), pass in FormOptions.PromptFieldsWithValues when you launch the FormDialog. 如果某个字段包含初始值,则提示会使用该值作为默认值。If a field contains an initial value, the prompt will use that value as the default value.

也可传入要绑定到该状态的 LUIS 实体。You can also pass in LUIS entities to bind to the state. 如果 EntityRecommendation.Type 是 C# 类中某个字段的路径,则会通过要绑定到字段的识别器传入 EntityRecommendation.EntityIf the EntityRecommendation.Type is a path to a field in your C# class, the EntityRecommendation.Entity will be passed through the recognizer to bind to your field. FormFlow 会跳过已绑定到某个实体的字段的步骤;系统不会提示用户输入这些字段的值。FormFlow will skip steps for any fields that are bound to an entity; the user will not be prompted for those fields.

添加业务逻辑Add business logic

在获取或设置字段值的过程中,若要处理窗体字段之间的相互依赖关系或应用特定的逻辑,可以在验证函数中指定业务逻辑。To handle interdependencies between form fields or apply specific logic during the process of getting or setting a field value, you can specify business logic within a validation function. 验证函数用于操作状态并返回一个 ValidateResult 对象,该对象可能包含:A validation function lets you manipulate the state and return a ValidateResult object that can contain:

  • 一个反馈字符串,用于描述值无效的原因a feedback string that describes the reason that a value is invalid
  • 一个转换后的值a transformed value
  • 一组用于澄清某个值的选择a set of choices for clarifying a value

此代码示例演示 Toppings 字段的验证函数。This code example shows a validation function for the Toppings field. 如果字段的输入包含 ToppingOptions.Everything 枚举值,则此函数会确保 Toppings 字段值包含浇汁的完整列表。If input for the field contains the ToppingOptions.Everything enumeration value, the function ensures that the Toppings field value contains the full list of toppings.

public static IForm<SandwichOrder> BuildForm()
{
    ...
    return new FormBuilder<SandwichOrder>()
        .Message("Welcome to the sandwich order bot!")
        .Field(nameof(Sandwich))
        .Field(nameof(Length))
        .Field(nameof(Bread))
        .Field(nameof(Cheese))
        .Field(nameof(Toppings),
            validate: async (state, value) =>
            {
                var values = ((List<object>)value).OfType<ToppingOptions>();
                var result = new ValidateResult { IsValid = true, Value = values };
                if (values != null && values.Contains(ToppingOptions.Everything))
                {
                    result.Value = (from ToppingOptions topping in Enum.GetValues(typeof(ToppingOptions))
                                    where topping != ToppingOptions.Everything && !values.Contains(topping)
                                    select topping).ToList();
                }
                return result;
            })
        .Message("For sandwich toppings you have selected {Toppings}.")
        ...
        .Build();
}

除了验证函数,还可以添加 Term 属性来匹配用户表述(例如“一切”或“不”)。In addition to the validation function, you can add the Term attribute to match user expressions such as "everything" or "not".

public enum ToppingOptions
{
    // This starts at 1 because 0 is the "no value" value
    [Terms("except", "but", "not", "no", "all", "everything")]
    Everything = 1,
    ...
}

此代码片段使用上述验证函数演示了当用户请求“除墨西哥辣椒之外的一切”时机器人和用户之间的交互。Using the validation function shown above, this snippet shows the interaction between bot and user when the user requests "everything but Jalapenos."

Please select one or more toppings (current choice: No Preference)
 1. Everything
 2. Avocado
 3. Banana Peppers
 4. Cucumbers
 5. Green Bell Peppers
 6. Jalapenos
 7. Lettuce
 8. Olives
 9. Pickles
 10. Red Onion
 11. Spinach
 12. Tomatoes
> everything but jalapenos
For sandwich toppings you have selected Avocado, Banana Peppers, Cucumbers, Green Bell Peppers, Lettuce, Olives, Pickles, Red Onion, Spinach, and Tomatoes.

FormFlow 属性FormFlow attributes

可以将以下 C# 属性添加到类,以便自定义 FormFlow 对话的行为。You can add these C# attributes to your class to customize behavior of a FormFlow dialog.

属性Attribute 目的Purpose
DescribeDescribe 更改某个字段或值在模板或卡中的显示方式Alter how a field or a value is shown in a template or card
数字Numeric 限制数字字段接受的值Restrict the accepted values of a numeric field
可选Optional 将字段标记为可选Mark a field as optional
模式Pattern 定义一个用于验证字符串字段的正则表达式Define a regular expression to validate a string field
提示Prompt 定义用于某个字段的提示Define the prompt for a field
模板Template 定义一个模板,用于生成提示或提示中的值Define the template to use to generate prompts or values in prompts
TermsTerms 定义与字段或值匹配的输入术语Define the input terms that match a field or value

使用 Prompt 属性自定义提示Customize prompts using the Prompt attribute

窗体中每个字段的默认提示是自动生成的,但可使用 Prompt 属性为任何字段指定自定义提示。Default prompts are automatically generated for each field in your form, but you can specify a custom prompt for any field by using the Prompt attribute. 例如,如果 SandwichOrder.Sandwich 字段的默认提示为“请选择三明治”,则可添加 Prompt 属性,以便为该字段指定自定义提示。For example, if the default prompt for the SandwichOrder.Sandwich field is "Please select a sandwich", you can add the Prompt attribute to specify a custom prompt for that field.

[Prompt("What kind of {&} would you like? {||}")]
public SandwichOptions? Sandwich;

此示例使用模式语言在运行时使用窗体数据对提示进行动态填充:{&} 替换为字段的说明,{||} 替换为枚举中选项的列表。This example uses pattern language to dynamically populate the prompt with form data at runtime: {&} is replaced with the description of the field and {||} is replaced with the list of choices in the enumeration.

备注

默认情况下,字段的说明是根据字段的名称生成的。By default, the description of a field is generated from the field's name. 若要指定某个字段的自定义说明,请添加 Describe 属性。To specify a custom description for a field, add the Describe attribute.

以下代码片段演示通过上述示例指定的自定义提示。This snippet shows the customized prompt that is specified by the example above.

What kind of sandwich would you like?
1. BLT
2. Black Forest Ham
3. Buffalo Chicken
4. Chicken And Bacon Ranch Melt
5. Cold Cut Combo
6. Meatball Marinara
7. Oven Roasted Chicken
8. Roast Beef
9. Rotisserie Style Chicken
10. Spicy Italian
11. Steak And Cheese
12. Sweet Onion Teriyaki
13. Tuna
14. Turkey Breast
15. Veggie
>

Prompt 属性也可指定那些影响窗体显示提示的参数。A Prompt attribute may also specify parameters that affect how the form displays the prompt. 例如,ChoiceFormat 参数决定了窗体如何呈现选项列表。For example, the ChoiceFormat parameter determines how the form renders the list of choices.

[Prompt("What kind of {&} would you like? {||}", ChoiceFormat="{1}")]
public SandwichOptions? Sandwich;

在此示例中,ChoiceFormat 参数的值指示选项应以项目符号列表(而不是编号列表)的形式显示。In this example, the value of the ChoiceFormat parameter indicates that the choices should be displayed as a bulleted list (instead of a numbered list).

What kind of sandwich would you like?
- BLT
- Black Forest Ham
- Buffalo Chicken
- Chicken And Bacon Ranch Melt
- Cold Cut Combo
- Meatball Marinara
- Oven Roasted Chicken
- Roast Beef
- Rotisserie Style Chicken
- Spicy Italian
- Steak And Cheese
- Sweet Onion Teriyaki
- Tuna
- Turkey Breast
- Veggie
>

使用 Template 属性自定义提示Customize prompts using the Template attribute

Prompt 属性用于自定义单个字段的提示,而 Template 属性则用于替换 FormFlow 用来自动生成提示的默认模板。While the Prompt attribute enables you to customize the prompt for a single field, the Template attribute enables you to replace the default templates that FormFlow uses to automatically generate prompts. 以下代码示例使用 Template 属性来重新定义窗体处理所有枚举字段的方式。This code example uses the Template attribute to redefine how the form handles all enumeration fields. 此属性指示用户只能选择一个项、通过模式语言设置提示文本,并指定窗体一行只能显示一个项。The attribute indicates that the user may select only one item, sets the prompt text by using pattern language, and specifies that the form should display only one item per line.

[Template(TemplateUsage.EnumSelectOne, "What kind of {&} would you like on your sandwich? {||}", ChoiceStyle = ChoiceStyleOptions.PerLine)]
public class SandwichOrder

此代码片段演示针对 Bread 字段和 Cheese 字段生成的提示。This snippet shows the resulting prompts for the Bread field and Cheese field.

What kind of bread would you like on your sandwich?
 1. Nine Grain Wheat
 2. Nine Grain Honey Oat
 3. Italian
 4. Italian Herbs And Cheese
 5. Flatbread
> 

What kind of cheese would you like on your sandwich? 
 1. American
 2. Monterey Cheddar
 3. Pepperjack
> 

如果使用 Template 属性来替换 FormFlow 用来生成提示的默认模板,则可能需要将某些变体插入到窗体生成的提示和消息中。If you use the Template attribute to replace the default templates that FormFlow uses to generate prompts, you may want to interject some variation into the prompts and messages that the form generates. 为此,可以使用模式语言定义多个文本字符串,窗体就会在每次需要显示提示或消息时随机地从可用选项中进行选择。To do so, you can define multiple text strings using pattern language, and the form will randomly choose from the available options each time it needs to display a prompt or message.

以下代码示例重新定义了 TemplateUsage.NotUnderstood 模板,为消息指定了两个不同的变体。This code example redefines the TemplateUsage.NotUnderstood template to specify two different variations of message. 当机器人需要表示它不理解用户的输入时,它会从这两个文本字符串中随机地选择一个来确定消息内容。When the bot needs to communicate that it does not understand a user's input, it will determine message contents by randomly selecting one of the two text strings.

[Template(TemplateUsage.NotUnderstood, "I do not understand \"{0}\".", "Try again, I don't get \"{0}\".")]
[Template(TemplateUsage.EnumSelectOne, "What kind of {&} would you like on your sandwich? {||}")]
public class SandwichOrder

此代码片段以示例方式演示了在机器人和用户之间进行的交互。This snippet shows an example of the resulting the interaction between bot and user.

What size of sandwich do you want? (1. Six Inch, 2. Foot Long)
> two feet
I do not understand "two feet".
> two feet
Try again, I don't get "two feet"
> 

使用 Optional 属性将字段指定为可选Designate a field as optional using the Optional attribute

若要将字段指定为可选,请使用 Optional 属性。To designate a field as optional, use the Optional attribute. 此代码示例指定 Cheese 字段为可选。This code example specifies that the Cheese field is optional.

[Optional]
public CheeseOptions? Cheese;

如果某个字段为可选但尚未指定值,则当前选项会显示为“无首选项”。If a field is optional and no value has been specified, the current choice will be displayed as "No Preference".

What kind of cheese would you like on your sandwich? (current choice: No Preference)
  1. American
  2. Monterey Cheddar
  3. Pepperjack
 >

如果某个字段为可选且用户已指定值,则会在列表中将“无首选项”显示为最后一个选项。If a field is optional and the user has specified a value, "No Preference" will be displayed as the last choice in the list.

What kind of cheese would you like on your sandwich? (current choice: American)
 1. American
 2. Monterey Cheddar
 3. Pepperjack
 4. No Preference
>

使用 Terms 属性来匹配用户输入Match user input using the Terms attribute

当用户向使用 FormFlow 生成的机器人发送消息时,该机器人会尝试将输入与术语列表进行匹配,以便确定用户输入的含义。When a user sends a message to a bot that is built using FormFlow, the bot attempts to identify the meaning of the user's input by matching the input to a list of terms. 默认情况下,生成术语列表时会对字段或值应用以下步骤:By default, the list of terms is generated by applying these steps to the field or value:

  1. 在大小写变化和出现下划线 () 时进行拆分。Break on case changes and underscore ().
  2. 生成每个 n-gram 时,有一个最大长度。Generate each n-gram up to a maximum length.
  3. 添加“s?”Add "s?" 到每个单词的末尾(目的是支持复数形式)。to the end of each word (to support plurals).

例如,值“AngusBeefAndGarlicPizza”会生成以下术语:For example, the value "AngusBeefAndGarlicPizza" would generate these terms:

  • 'angus?''angus?'
  • 'beefs?''beefs?'
  • 'garlics?''garlics?'
  • 'pizzas?''pizzas?'
  • 'angus? beefs?''angus? beefs?'
  • 'garlics? pizzas?''garlics? pizzas?'
  • 'angus beef and garlic pizza''angus beef and garlic pizza'

若要重写此默认行为并定义术语列表,以便使用它将用户输入与字段或字段中的值进行匹配,请使用 Terms 属性。To override this default behavior and define the list of terms that are used to match user input to a field or a value in a field, use the Terms attribute. 例如,考虑到用户可能会拼错“rotisserie”一词,可以使用 Terms 属性(和一个正则表达式)。For example, you may use the Terms attribute (with a regular expression) to account for the fact that users are likely to misspell the word "rotisserie."

[Terms(@"rotis\w* style chicken", MaxPhrase = 3)]
RotisserieStyleChicken, SpicyItalian, SteakAndCheese, SweetOnionTeriyaki, Tuna,...

使用 Terms 属性可以提高系统将用户输入匹配到某个有效选项的可能性。By using the Terms attribute, you increase the likelihood of being able to match user input with one of the valid choices. 此示例中的 Terms.MaxPhrase 参数导致 Language.GenerateTerms 生成术语的其他变体。The Terms.MaxPhrase parameter in this example causes the Language.GenerateTerms to generate additional variations of terms.

此代码片段演示当用户拼错“Rotisserie”时机器人与用户之间产生的交互。This snippet shows the resulting interaction between bot and user when the user misspells "Rotisserie."

What kind of sandwich would you like?
 1. BLT
 2. Black Forest Ham
 3. Buffalo Chicken
 4. Chicken And Bacon Ranch Melt
 5. Cold Cut Combo
 6. Meatball Marinara
 7. Oven Roasted Chicken
 8. Roast Beef
 9. Rotisserie Style Chicken
 10. Spicy Italian
 11. Steak And Cheese
 12. Sweet Onion Teriyaki
 13. Tuna
 14. Turkey Breast
 15. Veggie
> rotissary checkin
For sandwich I understood Rotisserie Style Chicken. "checkin" is not an option.

使用 Numeric 或 Pattern 属性验证用户输入Validate user input using the Numeric attribute or Pattern attribute

若要限制某个数字字段的允许值的范围,请使用 Numeric 属性。To restrict the range of allowed values for a numeric field, use the Numeric attribute. 此代码示例使用 Numeric 属性,目的是指定 Rating 字段的输入必须是 1 到 5 之间的数字。This code example uses the Numeric attribute to specify that input for the Rating field must be a number between 1 and 5.

[Numeric(1, 5)]
public double? Rating;

若要为特定字段的值指定必需格式,请使用 Pattern 属性。To specify the required format for the value of a particular field, use the Pattern attribute. 此代码示例使用 Pattern 属性,目的是指定 PhoneNumber 字段的值的必需格式。This code example uses the Pattern attribute to specify the required format for the value of the PhoneNumber field.

[Pattern(@"(<Undefined control sequence>\d)?\s*\d{3}(-|\s*)\d{4}")]
public string PhoneNumber;

摘要Summary

本文介绍如何使用 FormFlow 提供自定义的用户体验,方法是指定初始窗体状态、添加业务逻辑来管理字段之间的相互依赖关系并处理用户输入,以及使用属性来自定义提示、重写模板、指定可选字段、匹配用户输入和验证用户输入。This article has described how to deliver a customized user experience with FormFlow by specifying initial form state, adding business logic to manage interdependencies between fields and process user input, and using attributes to customize prompts, override templates, designate optional fields, match user input, and validate user input. 若要了解如何使用 FormFlow 通过其他方式来自定义用户体验,请参阅使用 FormBuilder 自定义窗体For information about additional ways to customize the user experience with FormFlow, see Customize a form using FormBuilder.

代码示例Sample code

有关演示如何使用 Bot Framework SDK for .NET 实现 FormFlow 的完整示例,请参阅 GitHub 中的多对话机器人示例Contoso Flowers 机器人示例For complete samples that show how to implement FormFlow using the Bot Framework SDK for .NET, see the Multi-Dialog Bot sample and the Contoso Flowers Bot sample in GitHub.

其他资源Additional resources