如何使用C#在单个Bot中同时集成3个QnA Service
在单个C# Bot中集成多个QnA Service的实现方案
嘿,你的思路完全没问题——把多个QnA的配置(KB ID、订阅密钥)存入数组/集合是完全可行的,而且这正是实现多QnA服务集成的核心思路之一。我来基于你现有的QnaDialog代码,一步步给你拆解具体的实现方案:
1. 先搞定多QnA服务的配置存储
首先,我们可以把所有QnA服务的配置信息整理成数组,直接用QnAMakerAttribute来存储每个服务的密钥、KB ID、默认回复和置信度阈值,这样后续调用起来很方便:
// 从配置文件读取多组QnA配置,初始化数组 private readonly QnAMakerAttribute[] _qnaConfigs = new[] { new QnAMakerAttribute( ConfigurationManager.AppSettings["QnaSubscriptionKey1"], ConfigurationManager.AppSettings["QnaKnowledgeBaseId1"], "抱歉,我暂时没法回答这个问题", 0.5), // 置信度阈值,得分高于这个值才会被考虑 new QnAMakerAttribute( ConfigurationManager.AppSettings["QnaSubscriptionKey2"], ConfigurationManager.AppSettings["QnaKnowledgeBaseId2"], "抱歉,我暂时没法回答这个问题", 0.5), new QnAMakerAttribute( ConfigurationManager.AppSettings["QnaSubscriptionKey3"], ConfigurationManager.AppSettings["QnaKnowledgeBaseId3"], "抱歉,我暂时没法回答这个问题", 0.5) };
2. 重写QnAMakerDialog的核心方法,实现多服务调用
默认的QnAMakerDialog只会调用单个服务,所以我们需要重写RecognizeAsync方法,让它循环遍历所有QnA服务,获取结果后筛选出最匹配的那个:
public class MultiQnaDialog : QnAMakerDialog { private readonly QnAMakerAttribute[] _qnaConfigs; public MultiQnaDialog() { // 初始化多QnA配置数组 _qnaConfigs = new[] { new QnAMakerAttribute( ConfigurationManager.AppSettings["QnaSubscriptionKey1"], ConfigurationManager.AppSettings["QnaKnowledgeBaseId1"], "暂无匹配结果", 0.5), new QnAMakerAttribute( ConfigurationManager.AppSettings["QnaSubscriptionKey2"], ConfigurationManager.AppSettings["QnaKnowledgeBaseId2"], "暂无匹配结果", 0.5), new QnAMakerAttribute( ConfigurationManager.AppSettings["QnaSubscriptionKey3"], ConfigurationManager.AppSettings["QnaKnowledgeBaseId3"], "暂无匹配结果", 0.5) }; } protected override async Task<QnAMakerResults> RecognizeAsync(IDialogContext context, IMessageActivity message) { QnAMakerResults bestResults = null; double highestScore = 0; // 循环调用每个QnA服务 foreach (var config in _qnaConfigs) { var qnaService = new QnAMakerService(config); var results = await qnaService.GetAnswersAsync(message.Text); // 如果当前服务返回了有效结果,且得分高于之前的最高分,就更新最佳结果 if (results?.Answers != null && results.Answers.Any()) { var topAnswer = results.Answers.First(); if (topAnswer.Score > highestScore) { highestScore = topAnswer.Score; bestResults = results; } } } // 如果所有服务都没匹配到,返回默认提示 return bestResults ?? new QnAMakerResults { Answers = new List<QnAMakerAnswer> { new QnAMakerAnswer { Answer = "抱歉,我暂时没法回答这个问题" } } }; } }
3. 关键逻辑的灵活调整
这里我用的是“取最高分结果”的逻辑,你可以根据自己的业务需求改:
- 优先级优先:按数组顺序调用,找到第一个符合置信度的结果就直接返回,不用再调用后面的服务
- 合并结果:把所有符合阈值的答案收集起来,一起返回给用户
- 自定义权重:给不同的知识库设置权重,比如某类知识库的得分乘以1.2再比较
4. 进阶方案:用依赖注入管理多服务
如果你的Bot是基于ASP.NET Core的,用依赖注入来管理多个QnA服务会更整洁,代码可维护性更高:
// 在Startup.cs里注册多个QnAMakerService实例 services.AddSingleton<QnAMakerService>(sp => new QnAMakerService(new QnAMakerAttribute( Configuration["QnaSubscriptionKey1"], Configuration["QnaKnowledgeBaseId1"], "暂无匹配", 0.5))); services.AddSingleton<QnAMakerService>(sp => new QnAMakerService(new QnAMakerAttribute( Configuration["QnaSubscriptionKey2"], Configuration["QnaKnowledgeBaseId2"], "暂无匹配", 0.5))); // 在Dialog中注入所有QnA服务 public class MultiQnaDialog : QnAMakerDialog { private readonly IEnumerable<QnAMakerService> _qnaServices; public MultiQnaDialog(IEnumerable<QnAMakerService> qnaServices) { _qnaServices = qnaServices; } protected override async Task<QnAMakerResults> RecognizeAsync(IDialogContext context, IMessageActivity message) { QnAMakerResults bestResults = null; double highestScore = 0; foreach (var service in _qnaServices) { var results = await service.GetAnswersAsync(message.Text); if (results?.Answers != null && results.Answers.Any()) { var topAnswer = results.Answers.First(); if (topAnswer.Score > highestScore) { highestScore = topAnswer.Score; bestResults = results; } } } return bestResults ?? new QnAMakerResults { Answers = new List<QnAMakerAnswer> { new QnAMakerAnswer { Answer = "抱歉,我暂时没法回答这个问题" } } }; } }
这样就完成了多个QnA Service在单个Bot里的集成,你可以直接把这段代码和你现有的Bot逻辑结合起来用~
内容的提问来源于stack exchange,提问作者Fermin Maniti
相关产品推荐
相关产品推荐

