SemanticKernel之使用Plugins

Plugins在SK中是一个神奇的功能。我们说大语言模型具有不确定性,我们的代码是确定性的,而Plugins有把这种不确定性转成确定性能功能。

下面的例子是一个通过自然语言实现购买的案例,客户可以通过文字或语音来输入自然语言,

<ItemGroup>
  <PackageReference Include="Microsoft.SemanticKernel" Version="1.7.1" />
  <PackageReference Include="NAudio" Version="2.2.1" />
</ItemGroup>

下面是具体代码,核心是在定义准确性的方法时,通过KernelFunction特性和Description特性来让SK知道这是一个Plugins,并且用Description的描述来映射自然语言的语义,包括提取参数,具体代友好如下:

#pragma warning disable SKEXP0001
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.AudioToText;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.TextToAudio;
using NAudio.Wave;
using System.ComponentModel;
using System.Data;


namespace LittleHelper
{ 
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
        StoreSystem _store;
        Kernel _kernel;
        IChatCompletionService _chatCompletionService;
        OpenAIPromptExecutionSettings _openAIPromptExecutionSettings;
        ChatHistory _history;
        string _key;
        WaveOutEvent _player;
        private void MainForm_Load(object sender, EventArgs e)
        {       
            _store = new StoreSystem();
            TotalLab.Text = "总价:" + _store.Total.ToString("0.00");
            GoodsGrid.DataSource = _store.GoodsList;
            GoodsGrid.Rows[0].Cells[0].Selected = false;
            var chatModelId = "gpt-4-0125-preview";
            _key = File.ReadAllText(@"C:\GPT\key.txt");


            var builder = Kernel.CreateBuilder();
            builder.Services.AddOpenAIChatCompletion(chatModelId, _key);
            builder.Plugins.AddFromObject(_store);
            _kernel = builder.Build();
            _history = new ChatHistory("所有水果的单价(Price)单位是斤),所有数量都是斤。");
            _chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>();
            _openAIPromptExecutionSettings = new OpenAIPromptExecutionSettings()
            {
                ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
            };
            _player = new WaveOutEvent();
        }
        private void SubmitButton_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(AskTextBox.Text))
            {
                MessageBox.Show("提示不能为空!");
                return;
            }
            var askText = AskTextBox.Text;
            AskTextBox.Clear();
            ResultTextBox.Clear();
            InMessageLab.Text = "输入问题:\r\n" + askText;
            AskQuestion(askText);


        }
        void ReloadGrid()
        {
            GoodsGrid.DataSource = null;
            GoodsGrid.DataSource = _store.GoodsList;
            foreach (DataGridViewColumn col in GoodsGrid.Columns)
            {
                switch (col.DataPropertyName)
                {
                    case "Name":
                        col.HeaderText = "名称";
                        col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
                        break;
                    case "Price":
                        col.HeaderText = "单价";
                        break;
                    case "Quantity":
                        col.HeaderText = "库存数量";
                        break;
                    case "BuyQuantity":
                        col.HeaderText = "销售数量";
                        break;
                }
            }
            foreach (DataGridViewRow row in GoodsGrid.Rows)
            {
                if (row.Cells["BuyQuantity"].Value != null)
                {
                    var buyQuantity = (int)row.Cells["BuyQuantity"].Value;
                    if (buyQuantity > 0)
                    {
                        row.Cells["BuyQuantity"].Style.BackColor = Color.LightGreen;
                    }
                }
            }
            GridClearSelect();
            TotalLab.Text = "总价:" + _store.Total.ToString("0.00");
        }


        async Task AskQuestion(string askMessage)
        {
            _history.AddUserMessage(askMessage);
            var result = await _chatCompletionService.GetChatMessageContentAsync(_history, _openAIPromptExecutionSettings, _kernel);


            var fullMessage = result.Content;
            ResultTextBox.Text = fullMessage;
            _history.AddMessage(AuthorRole.Assistant, fullMessage);




            await TextToAudioAsync(fullMessage);
            ReloadGrid();
        }




        async Task TextToAudioAsync(string speakText)
        {
            var kernel = Kernel.CreateBuilder()
                .AddOpenAITextToAudio(
                    modelId: "tts-1",
                    apiKey: _key)
                .Build();
            var textToAudioService = kernel.GetRequiredService<ITextToAudioService>();
            //转音频文件
            var executionSettings = new OpenAITextToAudioExecutionSettings("shimmer")
            {
                ResponseFormat = "mp3",
                Speed = 1.0f
            };
            var audioContent = await textToAudioService.GetAudioContentAsync(speakText, executionSettings);
            var outputFolder = Path.Combine(Directory.GetCurrentDirectory(), "NAudio");
            Directory.CreateDirectory(outputFolder);
            var speakFile = DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".mp3";
            var audioFilePath = Path.Combine(outputFolder, speakFile);         
            await File.WriteAllBytesAsync(audioFilePath, audioContent.Data.Value.ToArray());


            //读取音频文件
            using var reader = new AudioFileReader(audioFilePath);
            _player.Init(reader);
            _player.Play();
        }




        WaveInEvent waveIn;
        bool audioMark = true;
        string outputFilePath = "audio.wav";
        WaveFileWriter writer;
        private void SpeekBut_Click(object sender, EventArgs e)
        {
            if (audioMark)
            {
                SpeekBut.Text = "停止语音";
                var recordFile = DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".wav";
                var outputFolder = Path.Combine("C://GPT/NAudio");
                Directory.CreateDirectory(outputFolder);
                outputFilePath = Path.Combine(outputFolder, recordFile);


                if (waveIn == null)
                {
                    waveIn = new WaveInEvent();
                    if (writer == null)
                    {
                        writer = new WaveFileWriter(outputFilePath, waveIn.WaveFormat);
                    }


                    waveIn.DataAvailable += (s, a) =>
                    {
                        if (writer != null)
                        {
                            writer.Write(a.Buffer, 0, a.BytesRecorded);
                        }


                    };
                }


                waveIn.StartRecording();


            }
            else
            {
                SpeekBut.Text = "开始语音";
                waveIn.StopRecording();


                writer?.Dispose();
                writer = null;
                waveIn?.Dispose();
                waveIn = null;




                AudioToTextAsync(outputFilePath).ContinueWith(t =>
                {
                    if (t.IsFaulted)
                    {
                        MessageBox.Show("转换失败!");
                    }
                    else
                    {
                        var text = t.Result;
                        this.Invoke(() =>
                        {
                            AskTextBox.Text = text;
                            AskQuestion(text);
                        });
                    }
                });
            }
            audioMark = !audioMark;
        }




        private async Task<string> AudioToTextAsync(string audioFilePath)
        {
            try
            {
                var kernel = Kernel.CreateBuilder()
                    .AddOpenAIAudioToText(
                        modelId: "whisper-1",
                        apiKey: _key)
                    .Build();


                var audioToTextService = kernel.GetRequiredService<IAudioToTextService>();


                var executionSettings = new OpenAIAudioToTextExecutionSettings(audioFilePath)
                {
                    Language = "zh",
                    Prompt = "给出简体中文的文本",
                    ResponseFormat = "json",
                    Temperature = 0.3f,
                    Filename = "audio.wav"
                };




                ReadOnlyMemory<byte> audioData = await File.ReadAllBytesAsync(audioFilePath);
                var audioContent = new AudioContent(new BinaryData(audioData));
                var textContent = await audioToTextService.GetTextContentAsync(audioContent, executionSettings);
                return textContent?.Text;
            }
            catch (Exception exc)
            {
                return exc.Message;
            }


        }


        private void GoodsGrid_Leave(object sender, EventArgs e)
        {
            GridClearSelect();
        }
        void GridClearSelect()
        {
            foreach (DataGridViewRow row in GoodsGrid.Rows)
            {
                row.Selected = false;
                foreach (DataGridViewCell cell in row.Cells)
                {
                    cell.Selected = false;
                }
            }
        }
    }
    public class StoreSystem
    {
        public List<Goods> GoodsList { get; set; } = new List<Goods>
        {
            new Goods("苹果",5,100),
            new Goods("香蕉",3,200),
            new Goods("橙子",4,150),
            new Goods("桃子",6,120),
            new Goods("梨",5,100),
            new Goods("葡萄",7,80),
            new Goods("西瓜",8,60),
            new Goods("菠萝",9,40),
            new Goods("芒果",10,30),
            new Goods("草莓",11,20),
            new Goods("柠檬",4,100),
            new Goods("橘子",3,100),
            new Goods("蓝莓",6,100),
            new Goods("樱桃",7,100),
            new Goods("葡萄柚",8,100),
            new Goods("柚子",9,100),
            new Goods("榴莲",10,100),
            new Goods("火龙果",11,100),
            new Goods("荔枝",12,100),
            new Goods("椰子",13,100),
            new Goods("桑葚",5,100),
            new Goods("杨梅",4,100),
            new Goods("树梅",6,100),
            new Goods("莓子",7,100),
            new Goods("石榴",8,100),
            new Goods("蜜桃",9,100),
        };
        public decimal Total { get; set; } = 0;
        [KernelFunction]
        [Description("按照水果名称(Name)查询水果")]
        public string GetGoodsByName([Description("水果名称")] string name)
        {
            return GoodsList.FirstOrDefault(g => g.Name == name)?.ToString() ?? "未找到水果";
        }
        [KernelFunction]
        [Description("查询单价(Price)少于等于参数的所有水果")]
        public string GetGoodsLessEqualsPrice([Description("水果单价")] decimal price)
        {
            var goodses = GoodsList.Where(g => g.Price <= price);
            if (goodses == null || goodses.Any() == false)
            {
                return "未找到水果";
            }
            else
            {
                return string.Join("\n", goodses);
            }
        }
        [Description("查询单价(Price)少于参数的所有水果")]
        public string GetGoodsLessPrice([Description("水果单价")] decimal price)
        {
            var goodses = GoodsList.Where(g => g.Price < price);
            if (goodses == null || goodses.Any() == false)
            {
                return "未找到水果";
            }
            else
            {
                return string.Join("\n", goodses);
            }
        }
        [KernelFunction]
        [Description("查询单价(Price)大于等于参数的所有水果")]
        public string GetGoodsGreaterEqualsPrice([Description("水果单价")] decimal price)
        {
            var goodses = GoodsList.Where(g => g.Price >= price);
            if (goodses == null || goodses.Any() == false)
            {
                return "未找到水果";
            }
            else
            {
                return string.Join("\n", goodses);
            }
        }
        [KernelFunction]
        [Description("查询单价(Price)大于参数的所有水果")]
        public string GetGoodsGreaterPrice([Description("水果单价")] decimal price)
        {
            var goodses = GoodsList.Where(g => g.Price > price);
            if (goodses == null || goodses.Any() == false)
            {
                return "未找到水果";
            }
            else
            {
                return string.Join("\n", goodses);
            }
        }


        [KernelFunction]
        [Description("查询库存数量(Quantity)大于等于参数的所有水果")]
        public string GetGoodsGreaterEqualsQuantity([Description("水果库存数量")] int quantity)
        {
            var goodses = GoodsList.Where(g => g.Quantity >= quantity);
            if (goodses == null || goodses.Any() == false)
            {
                return "未找到水果";
            }
            else
            {
                return string.Join("\n", goodses);
            }
        }


        [KernelFunction]
        [Description("查询库存数量(Quantity)大于参数的所有水果")]
        public string GetGoodsGreaterQuantity([Description("水果库存数量")] int quantity)
        {
            var goodses = GoodsList.Where(g => g.Quantity > quantity);
            if (goodses == null || goodses.Any() == false)
            {
                return "未找到水果";
            }
            else
            {
                return string.Join("\n", goodses);
            }
        }
        [KernelFunction]
        [Description("查询库存数量(Quantity)少于等于参数的所有水果")]
        public string GetGoodsLessEqualsQuantity([Description("水果数量")] int quantity)
        {
            var goodses = GoodsList.Where(g => g.Quantity <= quantity);
            if (goodses == null || goodses.Any() == false)
            {
                return "未找到水果";
            }
            else
            {
                return string.Join("\n", goodses);
            }
        }
        [KernelFunction]
        [Description("查询库存数量(Quantity)少于参数的所有水果")]
        public string GetGoodsLessQuantity([Description("水果数量")] int quantity)
        {
            var goodses = GoodsList.Where(g => g.Quantity < quantity);
            if (goodses == null || goodses.Any() == false)
            {
                return "未找到水果";
            }
            else
            {
                return string.Join("\n", goodses);
            }
        }
        [KernelFunction]
        [Description("购买水果")]
        public string BuyGoods([Description("水果名称")] string name, [Description("购买数量")] int quantity)
        {
            var goods = GoodsList.FirstOrDefault(g => g.Name == name);
            if (goods != null)
            {
                var newQuantity = goods.Quantity - quantity;
                if (newQuantity < 0)
                {
                    return "库存不足";
                }
                else
                {
                    goods.Quantity = newQuantity;
                    goods.BuyQuantity += quantity;
                    Total += goods.Price * quantity;
                    return "购买成功!";
                }
            }
            else
            {
                return "未找到水果";
            }
        }
    }
    public class Goods
    {
        public Goods(string name, decimal price, int quantity)
        {
            Name = name;
            Price = price;
            Quantity = quantity;
        }


        public string Name { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }
        public int BuyQuantity { get; set; } = 0;


        public override string ToString()
        {
            return $"名称(Name):{Name},单价(Price):{Price},库存数量(Quantity):{Quantity},销售数量(BuyQuantity):{BuyQuantity}";
        }
    }
}

一次简单的购买记录结果如下:

09dc8c15f36840f1c6dcab06395b102e.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Xlua的Plugins是用来支持在Unity中使用Lua语言的插件。下面是使用Xlua Plugins的基本流程: 1. 下载Xlua插件 可以在Xlua官网下载最新版本的Xlua插件。 2. 将Xlua插件导入Unity项目 将下载好的Xlua插件导入到Unity项目中。 3. 创建Lua脚本 在Unity项目中创建一个.lua文件,编写Lua脚本代码。 4. 将Lua脚本打包成.bytes文件 使用luajit工具将Lua脚本打包成.bytes文件。将打包好的.bytes文件拖入Unity项目中。 5. 在C#脚本中调用Lua脚本 在C#脚本中使用Xlua插件提供的API调用Lua脚本。例如: ```csharp using XLua; using UnityEngine; public class LuaScript : MonoBehaviour { private LuaEnv luaEnv; private void Awake() { luaEnv = new LuaEnv(); luaEnv.DoString("require 'example'"); } private void OnDestroy() { luaEnv.Dispose(); } } ``` 以上代码创建了一个LuaEnv对象,在Awake方法中加载了Lua脚本文件example.lua,最后在OnDestroy方法中释放了LuaEnv对象。 6. 在Lua脚本中调用C#方法和属性 在Lua脚本中可以通过XLua提供的API调用C#方法和属性。例如: ```lua local gameObject = CS.UnityEngine.GameObject('TestObject') local transform = gameObject.transform transform.position = CS.UnityEngine.Vector3(0, 0, 0) local time = CS.UnityEngine.Time.deltaTime print(time) ``` 以上代码创建了一个GameObject对象,并且获取了它的Transform组件,设置了Transform组件的位置。最后打印了当前帧的时间。 这就是使用Xlua Plugins的基本流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值