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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:18:41