使用Semantic Kernel构建动态对话智能体

        随着对话系统和聊天机器人技术的不断演进,我们如何使得对话流程更加灵活、可重用成为了开发者们研究的热点。在上一篇文章中,介绍了如何创建一个固定回答选项的聊天提示(prompt)。然而,这种方法的可重用性并不高,因为回答选项是硬编码的。可以通过动态地创建提示字符串来解决这个问题,但有一个更好的方法:prompt模板功能。接下来我们将采用Semantic Kernel来实现这一技术。

        Semantic Kernel提供了一种模板语言,允许我们添加变量,这些变量会在输入参数时自动替换。首先,我们构建一个简单的提示,使用Semantic Kernel模板语法语言包含足够的信息以便agent能够回复用户。

// 使用Semantic Kernel模板创建聊天提示
var chat = kernel.CreateFunctionFromPrompt(
    @"{{$history}}
    User: {{$request}}
    Assistant: "
);

        新的提示使用了requesthistory变量,这样我们可以在运行提示时包括这些值。为了测试我们的提示,我们可以创建一个新的kernel并开始一个聊天循环,以便我们可以与我们的agent来回交谈。当我们调用提示时,我们可以传递requesthistory变量作为参数。

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;


// 创建kernel
var builder = Kernel.CreateBuilder();
// 添加文本或聊天完成服务,使用:
// builder.Services.AddAzureOpenAIChatCompletion()
// builder.Services.AddAzureOpenAITextGeneration()
// builder.Services.AddOpenAIChatCompletion()
// builder.Services.AddOpenAITextGeneration()


var kernel = builder.Build();


// 创建聊天历史
ChatHistory history = new List<ChatMessageContent>();


// 开始聊天循环
while (true)
{
    // 获取用户输入
    Console.Write("User > ");
    var request = Console.ReadLine();


    // 获取聊天响应
    var chatResult = kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
        chat,
        new() {
            { "request", request },
            { "history", string.Join("\n", history.Select(x => x.Role + ": " + x.Content)) }
        }
    );


    //输出响应流
    string message = "";
    await foreach (var chunk in chatResult)
    {
        if (chunk.Role.HasValue) Console.Write(chunk.Role + " > ");
        message += chunk.Content;
        Console.Write(chunk.Content);
    }
    Console.WriteLine();


    // 添加到历史记录
    history.Add(new ChatMessageContent(AuthorRole.User, request));
    history.Add(new ChatMessageContent(AuthorRole.Assistant, message));
}

        此外,Semantic Kernel还支持C# SDK中的Handlebars模板语言。要使用Handlebars,首先要在项目中添加Handlebars包。

dotnet add package Microsoft.SemanticKernel.PromptTemplate.Handlebars --prerelease

        然后导入Handlebars模板引擎包。

using Microsoft.SemanticKernel.PromptTemplates.Handlebars;

        之后,您可以使用HandlebarsPromptTemplateFactory创建新的提示。由于Handlebars支持循环,我们可以使用它来循环处理例如示例和聊天历史记录等元素。这使得它非常适合我们在前一篇文章中创建的getIntent提示。

// 创建Handlebars模板以获取意图
var getIntent = kernel.CreateFunctionFromPrompt(
    new()
    {
        Template = @"
        <message role=""system"">说明:这个请求的意图是什么?
        不要解释原因,只需回复意图。如果不确定,则回复{{choices[0]}}。
        选项:{{choices}}。</message>


        {{#each fewShotExamples}}
            {{#each this}}
                <message role=""{{role}}"">{{content}}</message>
            {{/each}}
        {{/each}}


        {{#each useHistory}}
            <message role=""{{role}}"">{{content}}</message>
        {{/each}}


        <message role=""user"">{{request}}</message>
        <message role=""system"">意图:</message>",
        TemplateFormat = "handlebars"
    },
    new HandlebarsPromptTemplateFactory()
);

        然后我们可以创建将被模板使用的choiceexample对象。在这个例子中,我们可以用我们的提示来结束对话。为此,我们将提供两个有效的意图选项:ContinueConversationEndConversation

// 创建选择列表
List<string> choices = new List<string> { "ContinueConversation", "EndConversation" };


// 创建少数示例
List<ChatHistory> fewShotExamples = new List<ChatHistory> {
    new ChatHistory {
        new ChatMessageContent(AuthorRole.User, "Can you send a very quick approval to the marketing team?"),
        new ChatMessageContent(AuthorRole.System, "Intent:"),
        new ChatMessageContent(AuthorRole.Assistant, "ContinueConversation")
    },
    new ChatHistory {
        new ChatMessageContent(AuthorRole.User, "Thanks, I'm done for now"),
        new ChatMessageContent(AuthorRole.System, "Intent:"),
        new ChatMessageContent(AuthorRole.Assistant, "EndConversation")
    }
};

        最后,您可以使用kernel运行提示。在主聊天循环中添加以下代码,以便一旦意图是EndConversation就终止循环。

// 调用提示
var intentResult = await kernel.InvokeAsync(
    getIntent,
    new() {
        { "request", request },
        { "choices", choices },
        { "chatHistory", history },
        { "fewShotExamples", fewShotExamples }
    }
);


// 如果意图是"EndConversation"则结束聊天
if (intentResult.ToString() == "EndConversation")
{
    break;
}

        但是使用过多的会话历史可能导致令牌数过多,影响系统效率。为了避免这种情况,我们可以在询问意图之前,使用ConversationSummaryPlugin插件来总结会话历史。       

var chat = kernel.CreateFunctionFromPrompt(
    @"{{ConversationSummaryPlugin.SummarizeConversation $history}}
    User: {{$request}}
    Assistant: "
);

        在上面的示例中,通过嵌套调用SummarizeConversation函数,能够在请求用户意图之前,对之前的对话历史进行摘要处理。这不仅可以提升处理效率,还能避免因历史信息过长而影响模型的响应。

        接着,我们需要确保插件被载入到Semantic Kernel中。可以通过以下代码来添加必要的服务和插件:

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Plugins.Core;


// 创建Kernel实例
var builder = Kernel.CreateBuilder();
// 添加文本或者聊天完成功能服务
// builder.Services.AddAzureOpenAIChatCompletion()
// builder.Services.AddAzureOpenAITextGeneration()
// builder.Services.AddOpenAIChatCompletion()
// builder.Services.AddOpenAITextGeneration()
// 注入ConversationSummaryPlugin插件
builder.Plugins.AddFromType<ConversationSummaryPlugin>();


var kernel = builder.Build();

        一旦插件和函数就绪,我们就可以开始测试更新后的prompt了。通过创建一个聊天循环,使得每次循环都会逐渐增加历史信息的长度,如下所示:

// 创建聊天历史对象
ChatHistory history = [];


// 开始聊天循环
while (true)
{
    // 获取用户输入
    Console.Write("User > ");
    var request = Console.ReadLine();


    // 调用kernel以获取聊天响应
    var chatResult = kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
        chat,
        new() {
            { "request", request },
            { "history", string.Join("\n", history.Select(x => x.Role + ": " + x.Content)) }
        }
    );


    // 迭代输出响应内容
    string message = "";
    await foreach (var chunk in chatResult)
    {
        if (chunk.Role.HasValue) Console.Write(chunk.Role + " > ");
        message += chunk;
        Console.Write(chunk);
    }
    Console.WriteLine();


    // 将聊天记录添加到历史对象中
    history.AddUserMessage(request!);
    history.AddAssistantMessage(message);
}

        通过以上示例代码,我们可以创建出功能丰富、可根据实际情况动态应变的聊天agent。这种方式不仅提升了聊天的自然性和连贯性,还提高了代码的复用性和扩展性。在构建自己的智能聊天机器人时,不妨考虑Semantic Kernel的模板技术,让聊天交互更加智能化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值