实现阿里云模型服务灵积 DashScope 的 Semantic Kernel Connector

这篇具有很好参考价值的文章主要介绍了实现阿里云模型服务灵积 DashScope 的 Semantic Kernel Connector。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Semantic Kernel 内置的 IChatCompletionService 实现只支持 OpenAI 与 Azure OpenAI,而我却打算结合 DashScope(阿里云模型服务灵积) 学习 Semantic Kernel。

实现阿里云模型服务灵积  DashScope 的  Semantic Kernel Connector

于是决定自己动手实现一个支持 DashScope 的 Semantic Kernel Connector —— DashScopeChatCompletionService,实现的过程也是学习 Semantic Kernel 源码的过程,
而且借助 Sdcb.DashScope,实现变得更容易了,详见前一篇博文 借助 .NET 开源库 Sdcb.DashScope 调用阿里云灵积通义千问 API

这里只实现用于调用 chat completion 服务的 connector,所以只需实现 IChatCompletionService 接口,该接口继承了 IAIService 接口,一共需要实现2个方法+1个属性。

public sealed class DashScopeChatCompletionService : IChatCompletionService
{
    public IReadOnlyDictionary<string, object?> Attributes { get; }

    public Task<IReadOnlyList<ChatMessageContent>> GetChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
    {
        throw new NotImplementedException();
    }

    public IAsyncEnumerable<StreamingChatMessageContent> GetStreamingChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
    {
        throw new NotImplementedException();
    }
}

先实现 GetChatMessageContentsAsync 方法,调用 Kernel.InvokePromptAsync 方法时会用到这个方法。

实现起来比较简单,就是转手买卖:

  • 把 Semantic Kernel 的 ChatHistory 转换为 Sdcb.DashScope 的 IReadOnlyList<ChatMessage>
  • 把 Semantic Kernel 的 PromptExecutionSettings 转换为 Sdcb.DashScope 的 ChatParameters
  • 把 Sdcb.DashScope 的 ResponseWrapper<ChatOutput, ChatTokenUsage> 转换为 Semantic Kernel 的 IReadOnlyList<ChatMessageContent>

实现代码如下:

public async Task<IReadOnlyList<ChatMessageContent>> GetChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
{
    var chatMessages = chatHistory
        .Where(x => !string.IsNullOrEmpty(x.Content))
        .Select(x => new ChatMessage(x.Role.ToString(), x.Content!)).
        ToList();

    ChatParameters? chatParameters = null;
    if (executionSettings?.ExtensionData?.Count > 0)
    {
        var json = JsonSerializer.Serialize(executionSettings.ExtensionData);
        chatParameters = JsonSerializer.Deserialize<ChatParameters>(
            json,
            new JsonSerializerOptions { NumberHandling = JsonNumberHandling.AllowReadingFromString });
    }

    var response = await _dashScopeClient.TextGeneration.Chat(_modelId, chatMessages, chatParameters, cancellationToken);

    return [new ChatMessageContent(new AuthorRole(chatMessages.First().Role), response.Output.Text)];
}

接下来实现 GetStreamingChatMessageContentsAsync,调用 Kernel.InvokePromptStreamingAsync 时会用到它,同样也是转手买卖。

ChatHistoryPromptExecutionSettings 参数的转换与 GetChatMessageContentsAsync 一样,所以引入2个扩展方法 ChatHistory.ToChatMessagesPromptExecutionSettings.ToChatParameters 减少重复代码,另外需要将 ChatParameters.IncrementalOutput 设置为 true

不同之处是返回值类型,需要将 Sdcb.DashScope 的 IAsyncEnumerable<ResponseWrapper<ChatOutput, ChatTokenUsage>> 转换为 IAsyncEnumerable<StreamingChatMessageContent>

实现代码如下:

public async IAsyncEnumerable<StreamingChatMessageContent> GetStreamingChatMessageContentsAsync(
    ChatHistory chatHistory,
    PromptExecutionSettings? executionSettings = null,
    Kernel? kernel = null,
    [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
    var chatMessages = chatHistory.ToChatMessages();
    var chatParameters = executionSettings?.ToChatParameters() ?? new ChatParameters();
    chatParameters.IncrementalOutput = true;

    var responses = _dashScopeClient.TextGeneration.ChatStreamed(_modelId, chatMessages, chatParameters, cancellationToken);

    await foreach (var response in responses)
    {
        yield return new StreamingChatMessageContent(new AuthorRole(chatMessages[0].Role), response.Output.Text);
    }
}

到这里2个方法就实现好了,还剩下很容易实现的1个属性,轻松搞定

public sealed class DashScopeChatCompletionService : IChatCompletionService
{
    private readonly DashScopeClient _dashScopeClient;
    private readonly string _modelId;
    private readonly Dictionary<string, object?> _attribues = [];

    public DashScopeChatCompletionService(
        IOptions<DashScopeClientOptions> options,
        HttpClient httpClient)
    {
        _dashScopeClient = new(options.Value.ApiKey, httpClient);
        _modelId = options.Value.ModelId;
        _attribues.Add(AIServiceExtensions.ModelIdKey, _modelId);
    }

    public IReadOnlyDictionary<string, object?> Attributes => _attribues;
}

到此,DashScopeChatCompletionService 的实现就完成了。

接下来,实现一个扩展方法,将 DashScopeChatCompletionService 注册到依赖注入容器

public static class DashScopeServiceCollectionExtensions
{
    public static IKernelBuilder AddDashScopeChatCompletion(
        this IKernelBuilder builder,
        string? serviceId = null,
        Action<HttpClient>? configureClient = null,
        string configSectionPath = "dashscope")
    {
        Func<IServiceProvider, object?, DashScopeChatCompletionService> factory = (serviceProvider, _) =>
            serviceProvider.GetRequiredService<DashScopeChatCompletionService>();

        if (configureClient == null)
        {
            builder.Services.AddHttpClient<DashScopeChatCompletionService>();
        }
        else
        {
            builder.Services.AddHttpClient<DashScopeChatCompletionService>(configureClient);
        }

        builder.Services.AddOptions<DashScopeClientOptions>().BindConfiguration(configSectionPath);
        builder.Services.AddKeyedSingleton<IChatCompletionService>(serviceId, factory);
        return builder;
    }
}

为了方便通过配置文件配置 ModelId 与 ApiKey,引入了 DashScopeClientOptions

public class DashScopeClientOptions : IOptions<DashScopeClientOptions>
{
    public string ModelId { get; set; } = string.Empty;

    public string ApiKey { get; set; } = string.Empty;

    public DashScopeClientOptions Value => this;
}

最后就是写测试代码验证实现是否成功,为了减少代码块的长度,下面的代码片段只列出其中一个测试用例

public class DashScopeChatCompletionTests
{
    [Fact]
    public async Task ChatCompletion_InvokePromptAsync_WorksCorrectly()
    {
        // Arrange
        var builder = Kernel.CreateBuilder();
        builder.Services.AddSingleton(GetConfiguration());
        builder.AddDashScopeChatCompletion();
        var kernel = builder.Build();

        var prompt = @"<message role=""user"">博客园是什么网站</message>";
        PromptExecutionSettings settings = new()
        {
            ExtensionData = new Dictionary<string, object>()
            {
                { "temperature", "0.8" }
            }
        };
        KernelArguments kernelArguments = new(settings);

        // Act
        var result = await kernel.InvokePromptAsync(prompt, kernelArguments);

        // Assert
        Assert.Contains("博客园", result.ToString());
        Trace.WriteLine(result.ToString());
    }

    private static IConfiguration GetConfiguration()
    {
        return new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .AddUserSecrets<DashScopeChatCompletionTests>()
            .Build();
    }
}

最后的最后就是运行测试,在 appsettings.json 中添加模型Id

{
  "dashscope": {
    "modelId": "qwen-max"
  }
}

注:qwen-max 是通义千问千亿级大模型

通过 user-secrets 添加 api key

dotnet user-secrets set "dashscope:apiKey" "sk-xxx"

dotnet test 命令运行测试

A total of 1 test files matched the specified pattern.
博客园是一个专注于提供信息技术(IT)领域知识分享和技术交流的中文博客平台,创建于2004年。博客园主要由软件开发人员、系统管理员以及对IT技术有深厚兴趣的人群使用,用户可以在该网站上撰写和发布自己的博客文章,内容涵盖编程、软件开发、云计算、人工智能等多个领域。同时,博客园也提供了丰富的技术文档、教程资源和社区互动功能,旨在促进IT专业人士之间的交流与学习。

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: < 1 ms - SemanticKernel.DashScope.IntegrationTest.dll (net8.0)

测试通过!连接 DashScope 的 Semantic Kernel Connector 初步实现完成。

完整实现代码放在 github 上,详见 https://github.com/cnblogs/semantic-kernel-dashscope/tree/v0.1.0文章来源地址https://www.toymoban.com/news/detail-825316.html

到了这里,关于实现阿里云模型服务灵积 DashScope 的 Semantic Kernel Connector的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Semantic Kernel 入门系列:?Semantic Function

    如果把提示词也算作一种代码的话,那么语义技能所带来的将会是全新编程方式,自然语言编程。 通常情况下一段prompt就可以构成一个Semantic Function,如此这般简单,如果我们提前可以组织好一段段prompt的管理方式,甚至可以不需要写任何的代码,就可以构造出足够多的技能

    2023年04月10日
    浏览(43)
  • Semantic Kernel 入门系列:?Kernel 内核和?Skills 技能

    理解了LLM的作用之后,如何才能构造出与LLM相结合的应用程序呢? 首先我们需要把LLM AI的能力和原生代码的能力区分开来,在Semantic Kernel(以下简称SK),LLM的能力称为 semantic function ,代码的能力称为 native function,两者平等的称之为function(功能),一组功能构成一个技能(

    2023年04月09日
    浏览(49)
  • LangChain vs Semantic Kernel

    每当向他人介绍 Semantic Kernel, 会得到的第一个问题就是 Semantic Kernel 类似于LangChain吗,或者是c# 版本的LangChain吗? 为了全面而不想重复的回答这个问题,因此我写下这篇文章。 在 ChatGPT 之前,构建 集成AI的应用程序的主要分为两个步骤: 机器学习工程师/数据科学家创建模

    2023年04月20日
    浏览(42)
  • 使用阿里巴巴dashscope-sdk-java时,maven报错

    使用的是2.8.3版本,里面为什么要加一个exclusion,是因为在解决之后运行时又报错,查了一下是和slf4j这个包冲突,所以把它排除掉,具体参考:java调用通义千问API-CSDN博客 然后从官网的文档里面找了一个代码测试: 结果本以为没什么问题了,运行之后又报错: 于是引入ok

    2024年04月25日
    浏览(46)
  • 体验Semantic Kernel图片内容识别

        前几日在浏览devblogs.microsoft.com的时候,看到了一篇名为Image to Text with Semantic Kernel and HuggingFace的文章。这篇文章大致的内容讲的是,使用 Semantic Kernel 结合 HuggingFace 来实现图片内容识别。注意,这里说的是图片内容识别,并非是 OCR ,而是它可以大致的描述图片里的主要

    2024年04月08日
    浏览(53)
  • Semantic Kernel 入门系列:?Memory内存

    了解的运作原理之后,就可以开始使用Semantic Kernel来制作应用了。 Semantic Kernel将embedding的功能封装到了Memory中,用来存储上下文信息,就好像电脑的内存一样,而LLM就像是CPU一样,我们所需要做的就是从内存中取出相关的信息交给CPU处理就好了。 使用Memory需要注册 embedding

    2023年04月13日
    浏览(42)
  • Semantic Kernel 入门系列:?Native Function

    语义的归语义,语法的归语法。 最基本的Native Function定义只需要在方法上添加 SKFunction 的特性即可。 默认情况下只需要传递一个string 参数就行,如果需要多个参数的话,和Semantic Function一样,也是使用Context,不过这里传进去是 SKContext 。在方法上使用 SKFunctionContextParameter 声

    2023年04月11日
    浏览(46)
  • Semantic Kernel 入门系列:?LLM的魔法

    ChatGPT 只是LLM 的小试牛刀,让人类能够看到的是机器智能对于语言系统的理解和掌握。 如果只是用来闲聊,而且只不过是将OpenAI的接口封装一下,那么市面上所有的ChatGPT的换皮应用都差不多。这就像是买了个徕卡镜头的手机,却只用来扫二维码一样。 由于微软的财大气粗,

    2023年04月09日
    浏览(35)
  • Semantic Kernel 入门系列:? Planner 计划管理

    Semantic Kernel 的一个核心能力就是实现“目标导向”的AI应用。 “目标导向”听起来是一个比较高大的词,但是却是实际生活中我们处理问题的基本方法和原则。 顾名思义,这种方法的核心就是先确定目标,然后再寻找实现目标的方法和步骤。这对于人来说的是很自然的事情

    2023年04月16日
    浏览(40)
  • Semantic Kernel 入门系列:? Planner 规划器

    Semantic Kernel 的一个核心能力就是实现“目标导向”的AI应用。 “目标导向”听起来是一个比较高大的词,但是却是实际生活中我们处理问题的基本方法和原则。 顾名思义,这种方法的核心就是先确定目标,然后再寻找实现目标的方法和步骤。这对于人来说的是很自然的事情

    2023年04月16日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包