如何在C# SDK的QnAMakerDialog中实现元数据评分提升及消息前置处理?
解决方案:QnAMakerDialog中传递元数据及前置消息处理
针对你遇到的两个问题,我来一步步拆解可行的解决方案:
一、在QnAMakerDialog中传递元数据提升答案评分
在Bot.Builder.CognitiveServices 1.1.7版本里,QnAMakerDialog确实没有直接暴露元数据参数的入口,但不需要完全重写服务,有两种简便实现方式:
1. 自定义QnAMakerDialog子类,重写查询逻辑
你可以继承QnAMakerDialog,重写它的QueryQnAMakerAsync方法(这个方法在1.1.7版本中是可重写的),手动构造带元数据的查询参数。示例代码如下:
public class CustomQnAMakerDialog : QnAMakerDialog { public CustomQnAMakerDialog() : base(new QnAMakerService(new QnAMakerAttribute( "你的知识库ID", "你的订阅密钥", "抱歉,我没找到相关答案", 0.5))) { } protected override async Task<QnAMakerResults> QueryQnAMakerAsync(IActivity activity) { // 自定义要传递的元数据,可从对话状态或业务规则生成 var metadataList = new List<Metadata> { new Metadata { Name = "Category", Value = "FAQ" }, new Metadata { Name = "Language", Value = "Chinese" } }; // 构造带元数据的查询选项 var queryOptions = new QnAMakerOptions { MetadataFilter = new MetadataFilter { Metadata = metadataList } }; // 调用QnAMaker服务并传入参数 var qnaService = new QnAMakerService(new QnAMakerAttribute( "你的知识库ID", "你的订阅密钥")); return await qnaService.GetAnswersAsync(activity.Text, queryOptions); } }
通过这种方式,就能让QnAMaker根据元数据过滤并提升对应答案的评分。
2. 绕过QnAMakerDialog,直接调用QnAMaker API
如果觉得子类重写太繁琐,也可以直接在自定义Dialog里调用QnAMaker API,完全掌控请求参数。示例:
public class RootDialog : IDialog<object> { public async Task StartAsync(IDialogContext context) { context.Wait(MessageReceivedAsync); } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result) { var message = await result; // 定义元数据 var metadata = new List<Metadata> { new Metadata { Name = "Category", Value = "Billing" } }; // 调用QnAMaker服务 var qnaService = new QnAMakerService(new QnAMakerAttribute( "你的知识库ID", "你的订阅密钥")); var qnaResults = await qnaService.GetAnswersAsync(message.Text, new QnAMakerOptions { MetadataFilter = new MetadataFilter { Metadata = metadata } }); // 处理返回结果 if (qnaResults.Answers.Any() && qnaResults.Answers.First().Score > 0.5) { await context.PostAsync(qnaResults.Answers.First().Answer); } else { await context.PostAsync("抱歉,我没找到相关答案"); } context.Wait(MessageReceivedAsync); } }
这种方式灵活性更高,所有参数都由你自主控制。
二、在QnAMakerDialog之前获取消息
由于1.1.7版本中QnAMakerDialog.MessageReceivedAsync不是虚方法,无法直接重写,你可以通过以下两种方式解决:
1. 使用Bot中间件拦截消息
实现IBotMiddleware可以在消息到达Dialog前拦截并处理,比如提取信息、存储上下文等,再传递给QnAMakerDialog。示例:
public class MessageCaptureMiddleware : IBotMiddleware { public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default(CancellationToken)) { if (turnContext.Activity.Type == ActivityTypes.Message) { // 在这里处理消息,比如存储到对话状态 var messageText = turnContext.Activity.Text; await turnContext.ConversationState.SetAsync("LastUserMessage", messageText); } // 继续传递消息到后续逻辑 await next(cancellationToken); } }
然后在Bot初始化时注册中间件:
var middlewareSet = new MiddlewareSet(); middlewareSet.Use(new MessageCaptureMiddleware()); var bot = new UniversalBot(connector, options => options.Middleware = middlewareSet) { Dialogs = { new CustomQnAMakerDialog() } };
2. 用自定义Dialog作为入口,先处理再跳转
创建RootDialog作为对话入口,先捕获并处理用户消息,再跳转至QnAMakerDialog。示例:
public class RootDialog : IDialog<object> { public async Task StartAsync(IDialogContext context) { context.Wait(MessageReceivedAsync); } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result) { var message = await result; // 预处理消息,比如提取关键词、设置上下文元数据 context.UserData.SetValue("CurrentUserQuery", message.Text); // 跳转到QnAMakerDialog处理 await context.Forward(new CustomQnAMakerDialog(), ResumeAfterQnAMaker, message, CancellationToken.None); } private async Task ResumeAfterQnAMaker(IDialogContext context, IAwaitable<object> result) { // QnAMaker处理完成后的后续逻辑 context.Wait(MessageReceivedAsync); } }
这种方式能让你在消息进入QnAMakerDialog前完全掌控处理流程。
内容的提问来源于stack exchange,提问作者Alun
相关产品推荐
相关产品推荐

