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

QnA Bot未匹配问题的邮件发送功能实现故障求助

解决QnA Bot未解答问题的邮件发送问题

嘿,我来帮你搞定这个邮件发送的难题!结合你描述的场景——当用户问了QnA Maker库里没有的问题时,收集邮箱并转发给专家——我给你整理了具体的实现步骤和代码示例,分.NET(Bot Framework常用)和Python两种情况:

核心思路

  1. 先判断QnA Maker是否返回匹配结果,无匹配时触发收集用户邮箱的对话流程
  2. 收集到邮箱后,调用邮件发送工具,把用户问题和邮箱一起发给指定专家
  3. 给用户发送提交成功的确认消息

.NET(Bot Framework SDK v4)实现示例

首先,在你的主对话逻辑里,判断QnA的返回结果,触发收集信息的对话:

// 处理用户消息时的QnA逻辑
var qnaResults = await _qnaMaker.GetAnswersAsync(turnContext);
if (qnaResults == null || !qnaResults.Any())
{
    // 启动收集用户信息的瀑布对话
    await turnContext.BeginDialogAsync(nameof(CollectUserInfoDialog), null, cancellationToken);
}

然后创建专门的CollectUserInfoDialog来处理邮箱收集和邮件发送:

using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using MailKit.Net.Smtp;
using MimeKit;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

public class CollectUserInfoDialog : ComponentDialog
{
    private readonly ILogger<CollectUserInfoDialog> _logger;

    public CollectUserInfoDialog(string dialogId, ILogger<CollectUserInfoDialog> logger) : base(dialogId)
    {
        _logger = logger;

        // 定义瀑布对话的步骤:问邮箱→发邮件→给用户确认
        var waterfallSteps = new WaterfallStep[]
        {
            AskUserForEmailAsync,
            ForwardQuestionToExpertAsync,
            SendConfirmationToUserAsync
        };

        AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
        AddDialog(new TextPrompt(nameof(TextPrompt)));
    }

    private async Task<DialogTurnResult> AskUserForEmailAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        return await stepContext.PromptAsync(
            nameof(TextPrompt),
            new PromptOptions { Prompt = MessageFactory.Text("抱歉,我暂时解答不了这个问题。麻烦提供一下你的邮箱,我们会安排专业人员联系你~") },
            cancellationToken);
    }

    private async Task<DialogTurnResult> ForwardQuestionToExpertAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        var userEmail = (string)stepContext.Result;
        var userQuestion = stepContext.Context.Activity.Text;
        var expertEmail = "your-expert@company.com"; // 你预先存储的专家邮箱

        try
        {
            await SendSupportEmailAsync(expertEmail, userEmail, userQuestion);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "发送邮件时出错");
            await stepContext.Context.SendActivityAsync("抱歉,提交你的问题失败了,请稍后再试~", cancellationToken: cancellationToken);
            return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
        }

        return await stepContext.NextAsync(null, cancellationToken);
    }

    private async Task<DialogTurnResult> SendConfirmationToUserAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        await stepContext.Context.SendActivityAsync("已帮你把问题提交给专业人员啦,我们会尽快通过邮箱联系你!", cancellationToken: cancellationToken);
        return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
    }

    private async Task SendSupportEmailAsync(string expertEmail, string userEmail, string userQuestion)
    {
        // 构建邮件内容
        var message = new MimeMessage();
        message.From.Add(new MailboxAddress("QnA智能助手", "bot-no-reply@company.com"));
        message.To.Add(new MailboxAddress("技术专家", expertEmail));
        message.Subject = $"新的用户咨询:{userEmail}";

        message.Body = new TextPart("plain")
        {
            Text = $"用户邮箱:{userEmail}\n用户问题:\n{userQuestion}"
        };

        // 连接SMTP服务器发送邮件(这里以公司邮箱为例,替换成你的SMTP配置)
        using (var client = new SmtpClient())
        {
            await client.ConnectAsync("smtp.yourcompany.com", 587, SecureSocketOptions.StartTls);
            // 生产环境不要硬编码密码!用Azure密钥保管库或环境变量存储
            await client.AuthenticateAsync("bot-service-account@company.com", "your-secure-password");
            await client.SendAsync(message);
            await client.DisconnectAsync(true);
        }
    }
}

Python实现示例

如果你的Bot是用Python开发的,逻辑类似,用smtplib来发送邮件:

首先处理QnA无匹配的情况,触发收集邮箱的流程:

from botbuilder.core import ActivityHandler, TurnContext
from botbuilder.dialogs import DialogSet, DialogTurnStatus
from botbuilder.dialogs.prompts import TextPrompt, PromptOptions

class QnABot(ActivityHandler):
    def __init__(self, qna_maker, dialog_set):
        self.qna_maker = qna_maker
        self.dialog_set = dialog_set

    async def on_message_activity(self, turn_context: TurnContext):
        dialog_context = await self.dialog_set.create_context(turn_context)
        results = await dialog_context.continue_dialog()

        if results.status == DialogTurnStatus.Empty:
            qna_results = await self.qna_maker.get_answers(turn_context)
            if not qna_results:
                # 启动收集邮箱的对话
                await dialog_context.begin_dialog("CollectUserInfoDialog")
            else:
                await turn_context.send_activity(qna_results[0].answer)

然后定义收集邮箱的对话:

import smtplib
from email.mime.text import MIMEText
from botbuilder.dialogs import ComponentDialog, WaterfallDialog, WaterfallStepContext, DialogTurnResult

class CollectUserInfoDialog(ComponentDialog):
    def __init__(self, dialog_id: str):
        super().__init__(dialog_id)
        self.add_dialog(WaterfallDialog("Waterfall", [self.ask_email_step, self.send_email_step, self.confirm_step]))
        self.add_dialog(TextPrompt("TextPrompt"))
        self.initial_dialog_id = "Waterfall"

    async def ask_email_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
        return await step_context.prompt(
            "TextPrompt",
            PromptOptions(prompt="抱歉,我暂时解答不了这个问题。麻烦提供一下你的邮箱,我们会安排专业人员联系你~")
        )

    async def send_email_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
        user_email = step_context.result
        user_question = step_context.context.activity.text
        expert_email = "your-expert@company.com"

        try:
            self._send_support_email(expert_email, user_email, user_question)
        except Exception as e:
            print(f"发送邮件出错: {e}")
            await step_context.context.send_activity("抱歉,提交你的问题失败了,请稍后再试~")
            return await step_context.end_dialog()

        return await step_context.next(None)

    async def confirm_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
        await step_context.context.send_activity("已帮你把问题提交给专业人员啦,我们会尽快通过邮箱联系你!")
        return await step_context.end_dialog()

    def _send_support_email(self, expert_email: str, user_email: str, user_question: str):
        msg = MIMEText(f"用户邮箱:{user_email}\n用户问题:\n{user_question}")
        msg['Subject'] = f"新的用户咨询:{user_email}"
        msg['From'] = "bot-no-reply@company.com"
        msg['To'] = expert_email

        with smtplib.SMTP("smtp.yourcompany.com", 587) as server:
            server.starttls()
            server.login("bot-service-account@company.com", "your-secure-password")
            server.send_message(msg)

重要注意事项

  • 敏感信息不要硬编码:邮箱账号、密码这些绝对不能写在代码里,用环境变量、配置文件或者云服务的密钥保管库(比如Azure Key Vault)来存储。
  • SMTP服务器配置:不同邮箱服务商的SMTP地址和端口不一样,比如Gmail是smtp.gmail.com:587,Outlook是smtp.office365.com:587,公司邮箱请咨询IT部门。
  • 错误处理:一定要给邮件发送逻辑加异常捕获,避免Bot崩溃,同时给用户友好的提示。
  • 测试验证:先在本地测试邮件发送功能,确保能正常发送后再集成到Bot里。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:06:13