You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bot Framework v4:如何重复Waterfall Dialog步骤直至用户输入符合预期?

在Waterfall Dialog中重复步骤直至用户输入符合预期的实现方案

针对你描述的场景(启动搜索→检查参数完整性→补充缺失参数→输出结果),核心是通过对话状态追踪参数+Waterfall步骤的循环逻辑实现重复收集输入,直到满足条件。以下是具体实现方案:

核心思路

  1. 用对话状态存储已提取的参数和搜索上下文,避免步骤跳转时丢失数据
  2. 在关键步骤(步骤2)中判断参数是否齐全:
    • 若参数缺失,触发Prompt收集用户输入,收集完成后重新进入当前步骤检查
    • 若参数齐全,进入最终步骤输出结果
  3. 利用Waterfall Dialog的内置流程控制(Prompt后的自动回调)实现步骤循环

具体实现(以Bot Framework C#为例)

1. 定义对话状态类

用于存储搜索模板、已提取的参数等上下文信息:

public class SearchDialogState
{
    // 已提取的参数键值对
    public Dictionary<string, string> ExtractedParams { get; set; } = new Dictionary<string, string>();
    // 包含占位符的搜索模板
    public string SearchTemplate { get; set; }
}

2. 构建Waterfall Dialog的三个步骤

// 注册文本Prompt用于收集补充参数
var textPrompt = new TextPrompt(nameof(TextPrompt));
Dialogs.Add(textPrompt);

// 创建Waterfall Dialog
var searchWaterfall = new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
    InitializeSearchContextAsync,
    CheckAndCollectMissingParamsAsync,
    GenerateAndSendFinalResultAsync
});
Dialogs.Add(searchWaterfall);

// ------------------------------
// Step 1: 处理初始输入,初始化搜索上下文
// ------------------------------
private async Task<DialogTurnResult> InitializeSearchContextAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // 获取用户初始输入
    var userInput = stepContext.Context.Activity.Text;
    
    // 从输入中提取可识别的参数(实际可替换为LUIS/正则等专业识别逻辑)
    var extractedParams = ExtractParamsFromInput(userInput);
    // 加载搜索模板(模拟从搜索接口或配置获取)
    var searchTemplate = "查找{城市}的{星级}酒店";

    // 将上下文存入对话状态
    var state = await stepContext.Context.TurnState.GetAsync<SearchDialogState>(cancellationToken);
    state.ExtractedParams = extractedParams;
    state.SearchTemplate = searchTemplate;

    // 直接进入参数检查步骤
    return await stepContext.NextAsync(null, cancellationToken);
}

// ------------------------------
// Step 2: 检查参数完整性,缺失则收集补充信息(循环执行)
// ------------------------------
private async Task<DialogTurnResult> CheckAndCollectMissingParamsAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var state = await stepContext.Context.TurnState.GetAsync<SearchDialogState>(cancellationToken);
    var searchTemplate = state.SearchTemplate;
    var extractedParams = state.ExtractedParams;

    // 如果是从Prompt返回的结果,先更新已提取的参数
    if (stepContext.Result != null)
    {
        var userInput = stepContext.Result.ToString();
        // 识别当前缺失的参数(实际可通过状态记录当前询问的参数类型)
        var missingParam = Regex.Matches(searchTemplate, @"\{(\w+)\}")
                                .Cast<Match>()
                                .Select(m => m.Groups[1].Value)
                                .First(p => !extractedParams.ContainsKey(p));
        extractedParams[missingParam] = userInput;
    }

    // 识别模板中所有占位符
    var allPlaceholders = Regex.Matches(searchTemplate, @"\{(\w+)\}")
                               .Cast<Match>()
                               .Select(m => m.Groups[1].Value)
                               .ToList();
    // 找出未填充的参数
    var missingParams = allPlaceholders.Where(p => !extractedParams.ContainsKey(p)).ToList();

    if (!missingParams.Any())
    {
        // 参数齐全,进入最终步骤
        return await stepContext.NextAsync(null, cancellationToken);
    }

    // 询问第一个缺失的参数
    var firstMissingParam = missingParams.First();
    var promptMsg = $"请补充{firstMissingParam}信息?";
    return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = MessageFactory.Text(promptMsg) }, cancellationToken);
}

// ------------------------------
// Step 3: 生成并输出最终搜索结果
// ------------------------------
private async Task<DialogTurnResult> GenerateAndSendFinalResultAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var state = await stepContext.Context.TurnState.GetAsync<SearchDialogState>(cancellationToken);
    var searchTemplate = state.SearchTemplate;
    var extractedParams = state.ExtractedParams;

    // 替换模板中的占位符,生成最终结果
    var finalResult = searchTemplate;
    foreach (var param in extractedParams)
    {
        finalResult = finalResult.Replace($"{{{param.Key}}}", param.Value);
    }

    // 发送结果给用户
    await stepContext.Context.SendActivityAsync(MessageFactory.Text($"搜索结果:{finalResult}"), cancellationToken);
    // 结束对话
    return await stepContext.EndDialogAsync(cancellationToken);
}

// 辅助方法:模拟从输入提取参数
private Dictionary<string, string> ExtractParamsFromInput(string input)
{
    var paramsDict = new Dictionary<string, string>();
    if (input.Contains("北京")) paramsDict.Add("城市", "北京");
    if (input.Contains("五星")) paramsDict.Add("星级", "五星");
    return paramsDict;
}

关键注意事项

  • 状态持久化:必须确保SearchDialogState被正确存入对话状态(如ConversationState),否则每次步骤跳转都会丢失已收集的参数
  • 参数识别优化:实际项目中不要用硬编码的参数提取逻辑,建议使用LUIS、Regex或自定义NLP工具提升识别准确率
  • 批量询问优化:如果存在多个缺失参数,可以一次性列出所有缺失项,让用户批量输入,减少对话轮次
  • 强制跳转:若需要强制回到某个步骤(而非依赖Prompt的自动回调),可以使用stepContext.ReplaceDialogAsync(nameof(WaterfallDialog), null, cancellationToken)实现

内容的提问来源于stack exchange,提问作者Krumelur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 16:47:10