QnA Bot未匹配问题的邮件发送功能实现故障求助
解决QnA Bot未解答问题的邮件发送问题
嘿,我来帮你搞定这个邮件发送的难题!结合你描述的场景——当用户问了QnA Maker库里没有的问题时,收集邮箱并转发给专家——我给你整理了具体的实现步骤和代码示例,分.NET(Bot Framework常用)和Python两种情况:
核心思路
- 先判断QnA Maker是否返回匹配结果,无匹配时触发收集用户邮箱的对话流程
- 收集到邮箱后,调用邮件发送工具,把用户问题和邮箱一起发给指定专家
- 给用户发送提交成功的确认消息
.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
相关产品推荐
相关产品推荐

