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

如何使用C#的NumberPrompt实现多轮用户输入问询(解决循环问题)

多问题逐个提问的解决方案(基于Bot Framework NumberPrompt)

我需要从数据库获取多个问题,然后逐个向用户提问。用for循环的话,所有问题会一次性发出去,没法等用户输入答案;用return虽然能等用户输入,但会终止循环。我不知道怎么用NumberPrompt处理这种需要多次用户输入的场景,原本打算在for循环里写这段代码,但循环没法等输入,return又会停掉循环:

var showQuestionMessageText = $"Here's your question: {ConnectDatabase.question[i]}";

var showQuestionMessage = MessageFactory.Text(showQuestionMessageText, showQuestionMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(showQuestionMessage, cancellationToken);

var showOptionsMessageText = $"Here's your options: \n1. {ConnectDatabase.option1[i]} \n2. {ConnectDatabase.option2[i]} \n3. {ConnectDatabase.option3[i]} \n4. {ConnectDatabase.option4[i]}";
var showOptionsMessage = MessageFactory.Text(showOptionsMessageText, showOptionsMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(showOptionsMessage, cancellationToken);

return await stepContext.PromptAsync(nameof(NumberPrompt<int>), new PromptOptions { Prompt = MessageFactory.Text("Please choose one of the options, using 1-4 to type in your answer")}, cancellationToken);

别用for循环,改用Bot Framework的**对话状态(Dialog State)**跟踪当前进度,配合瀑布流对话框(Waterfall Dialog)分步处理。核心思路是把问题列表、当前问题索引、用户答案都存在对话状态里,每处理完一个问题就更新索引,然后回到提问步骤,直到所有问题问完。

1. 定义对话状态类

先创建类存储对话过程中的数据:

public class QuestionnaireState
{
    // 存储从数据库获取的问题集合
    public List<Question> Questions { get; set; }
    // 当前处理的问题索引
    public int CurrentQuestionIndex { get; set; }
    // 存储用户的答案(键为问题索引,值为用户选择的选项编号)
    public Dictionary<int, int> UserAnswers { get; set; } = new Dictionary<int, int>();
}

// 自定义问题结构,对应数据库字段
public class Question
{
    public string Text { get; set; }
    public string Option1 { get; set; }
    public string Option2 { get; set; }
    public string Option3 { get; set; }
    public string Option4 { get; set; }
}

2. 初始化对话状态并加载问题

在瀑布流第一步,从数据库加载所有问题并初始化状态:

private async Task<DialogTurnResult> LoadQuestionsStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // 替换为你的数据库查询逻辑,获取所有问题
    var questions = await ConnectDatabase.GetAllQuestionsAsync();

    // 将问题存入对话状态
    var questionnaireState = await _conversationState
        .CreateProperty<QuestionnaireState>(nameof(QuestionnaireState))
        .GetAsync(stepContext.Context, () => new QuestionnaireState());
    questionnaireState.Questions = questions;
    questionnaireState.CurrentQuestionIndex = 0;

    // 进入提问步骤
    return await stepContext.NextAsync(null, cancellationToken);
}

3. 提问步骤(发送问题+选项,等待用户输入)

负责发送当前问题和选项,调用NumberPrompt获取用户答案:

private async Task<DialogTurnResult> AskQuestionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var questionnaireState = await _conversationState
        .CreateProperty<QuestionnaireState>(nameof(QuestionnaireState))
        .GetAsync(stepContext.Context);
    var currentIndex = questionnaireState.CurrentQuestionIndex;

    // 所有问题处理完毕,结束对话
    if (currentIndex >= questionnaireState.Questions.Count)
    {
        await stepContext.Context.SendActivityAsync("所有问题已回答完毕!", cancellationToken: cancellationToken);
        return await stepContext.EndDialogAsync(questionnaireState.UserAnswers, cancellationToken);
    }

    var currentQuestion = questionnaireState.Questions[currentIndex];

    // 发送问题文本
    var questionText = $"问题 {currentIndex + 1}: {currentQuestion.Text}";
    await stepContext.Context.SendActivityAsync(
        MessageFactory.Text(questionText, questionText, InputHints.IgnoringInput), 
        cancellationToken);

    // 发送选项列表
    var optionsText = $"可选答案:\n1. {currentQuestion.Option1} \n2. {currentQuestion.Option2} \n3. {currentQuestion.Option3} \n4. {currentQuestion.Option4}";
    await stepContext.Context.SendActivityAsync(
        MessageFactory.Text(optionsText, optionsText, InputHints.IgnoringInput), 
        cancellationToken);

    // 调用NumberPrompt等待用户输入
    return await stepContext.PromptAsync(nameof(NumberPrompt<int>), new PromptOptions
    {
        Prompt = MessageFactory.Text("请输入1-4之间的数字选择答案", inputHint: InputHints.ExpectingInput)
    }, cancellationToken);
}

4. 处理用户答案的步骤

存储用户答案,更新索引,回到提问步骤继续下一个问题:

private async Task<DialogTurnResult> ProcessAnswerStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var questionnaireState = await _conversationState
        .CreateProperty<QuestionnaireState>(nameof(QuestionnaireState))
        .GetAsync(stepContext.Context);
    var currentIndex = questionnaireState.CurrentQuestionIndex;

    // 存储用户选择的答案(stepContext.Result为NumberPrompt返回的int值)
    var userAnswer = (int)stepContext.Result;
    questionnaireState.UserAnswers[currentIndex] = userAnswer;

    // 更新索引,准备下一个问题
    questionnaireState.CurrentQuestionIndex++;

    // 回到提问步骤
    return await stepContext.NextAsync(null, cancellationToken);
}

5. 注册对话框和状态

在Bot构造函数中注册对话状态、NumberPrompt和瀑布流对话框:

private readonly ConversationState _conversationState;
private readonly DialogSet _dialogs;

public QuestionnaireBot(ConversationState conversationState)
{
    _conversationState = conversationState;
    var dialogState = _conversationState.CreateProperty<DialogState>(nameof(DialogState));
    _dialogs = new DialogSet(dialogState);

    // 注册NumberPrompt,添加输入验证(只接受1-4的数字)
    _dialogs.Add(new NumberPrompt<int>(nameof(NumberPrompt<int>), async (promptContext, cancellationToken) =>
    {
        if (!promptContext.Recognized.Succeeded)
        {
            await promptContext.Context.SendActivityAsync("请输入有效的数字!", cancellationToken: cancellationToken);
            return false;
        }
        var answer = promptContext.Recognized.Value;
        if (answer < 1 || answer > 4)
        {
            await promptContext.Context.SendActivityAsync("请输入1-4之间的数字!", cancellationToken: cancellationToken);
            return false;
        }
        return true;
    }));

    // 注册瀑布流对话框,按顺序添加步骤
    _dialogs.Add(new WaterfallDialog("QuestionnaireDialog", new WaterfallStep[]
    {
        LoadQuestionsStepAsync,
        AskQuestionStepAsync,
        ProcessAnswerStepAsync
    }));
}

启动对话框

在OnTurnAsync方法中启动问卷对话框:

public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
    if (turnContext.Activity.Type == ActivityTypes.Message)
    {
        var dc = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
        var results = await dc.ContinueDialogAsync(cancellationToken);

        // 无活跃对话框时,启动问卷流程
        if (results.Status == DialogTurnStatus.Empty)
        {
            await dc.BeginDialogAsync("QuestionnaireDialog", cancellationToken: cancellationToken);
        }

        // 保存对话状态
        await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 06:05:03