Unity 工具 之 Azure OpenAI 功能接入到Unity 中的简单整理

news/2024/5/20 7:41:43 标签: unity, azure, OpenAI, GPT, AIGC

OpenAI%20%E5%8A%9F%E8%83%BD%E6%8E%A5%E5%85%A5%E5%88%B0Unity%20%E4%B8%AD%E7%9A%84%E7%AE%80%E5%8D%95%E6%95%B4%E7%90%86">Unity 工具 之 Azure OpenAI 功能接入到Unity 中的简单整理

目录

OpenAI%20%E5%8A%9F%E8%83%BD%E6%8E%A5%E5%85%A5%E5%88%B0Unity%20%E4%B8%AD%E7%9A%84%E7%AE%80%E5%8D%95%E6%95%B4%E7%90%86-toc" style="margin-left:0px;">Unity 工具 之 Azure OpenAI 功能接入到Unity 中的简单整理

一、简单介绍

二、实现原理

三、注意实现

四、简单实现步骤

五、关键代码

六、附加

OpenAI%20dll%20%E5%8C%85-toc" style="margin-left:80px;">创建新的 .NET Core ,获取 Azure.AI.OpenAI dll 包

检索密钥和终结点


一、简单介绍

Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。

本节介绍,这里在使用微软的Azure 把Azue.AI.OpenAI 接入到Unity中,在Unity中直接调用 Azue.AI.OpenAI 接口函数,实现简单聊天功能,这里简单说明,如果你有更好的方法,欢迎留言交流。

二、实现原理

1、官网申请得到Azure OpenAI 对应的 AZURE_OPENAI_ENDPOINT 和 AZURE_OPENAI_KEY,以及对应的模型名 DeploymentOrModelName

2、把相关的 dll 引入进来,主要有 Azure.AI.OpenAI、 Azure.Core 等等

3、创建客户端 OpenAIClient = new(new Uri(AZURE_OPENAI_ENDPOINT ), new AzureKeyCredential(AZURE_OPENAI_KEY));

OpenAIClient .GetChatCompletionsXXAsync (DeploymentOrModelName,prompt)

来发起请求

三、注意实现

1、这里使用 Async 来异步获取数据,避免发起请求时卡顿

2、由于使用 Async ,各种对应的数据事件进行必要的 Loom.QueueOnMainThread 处理,不然可能会报入的错误: Net.WebException : Error: NameResolutionFailure

3、使用 CancellationTokenSource 来终止 Async 造成的 Task 任务

4、ChatCompletionsOptions 的 MaxTokens 可能可以控制Stream的速度和接收的内容

四、简单实现步骤

1、根据 Azure OpenAI 的 官网的操作下载 Azure.AI.OpenAI 相关 dll 包,然后导入到Unity Plugins (目前对应的是当前最新版的 netstandard2.0 或者 netstandard2.1 的dll 包,根据需要选择,dll (dotnet add package)下载默认路径为 C:\Users\YOUR_USER_NAME\.nuget\packages)

快速入门 - 开始通过 Azure OpenAI 服务使用 ChatGPTGPT-4 - Azure OpenAI Service | Microsoft Learn

2、 简单的搭建场景

3、简单的封装一下AzureOpenAIQueryHandler接口

 4、添加测试脚本 TestAzureOpenAI

5、对应挂载脚本TestAzureOpenAI 到场景中,并对应赋值

 6、运行场景、效果如下

五、关键代码

1、AzureOpenAIQueryHandler


using Azure.AI.OpenAI;
using Azure;
using System;
using UnityEngine;
using System.Threading.Tasks;
using System.Threading;

public class AzureOpenAIQueryHandler 
{
    const string END_POINT = "AZURE_OPENAI_ENDPOINT ";
    const string KEY = "AZURE_OPENAI_KEY";
    const string DeploymentOrModelName = "DeploymentOrModelName";

    OpenAIClient m_Client;
    StreamingChatCompletions m_StreamingChatCompletions;

    private CancellationTokenSource m_CancellationTokenSource;
    private CancellationTokenSource m_StreamCancellationTokenSource;

    public AzureOpenAIQueryHandler() {
        m_Client = new(new Uri(END_POINT), new AzureKeyCredential(KEY));
    }
    ~AzureOpenAIQueryHandler() {
        m_StreamingChatCompletions?.Dispose();
        m_StreamingChatCompletions = null;
        m_Client = null;
    }

    /// <summary>
    /// 正常聊天
    /// </summary>
    /// <param name="prompt">聊天的内容</param>
    /// <param name="onStartAction">发起聊天开始事件</param>
    /// <param name="onGettedDataEndAction">聊天接收内容数据事件</param>
    public async Task SendChat(string prompt, Action onStartAction, Action<string> onGettedDataEndAction) {
        StopChat();
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        m_CancellationTokenSource = cancellationTokenSource;
        Loom.QueueOnMainThread(() =>
        {
            onStartAction?.Invoke();
        });
        if (cancellationTokenSource?.IsCancellationRequested == false) {
            Response<ChatCompletions>  response = await m_Client.GetChatCompletionsAsync(
            deploymentOrModelName: DeploymentOrModelName,
            PromptDataHandler(prompt));

            if (cancellationTokenSource?.IsCancellationRequested == false)
            {
                string content = response.Value.Choices[0].Message.Content;
                Loom.QueueOnMainThread(() =>
                {
                    onGettedDataEndAction?.Invoke(content);
                });
            }
        }
    }

    /// <summary>
    /// 停止访问
    /// </summary>
    public void StopChat() {
        if (m_CancellationTokenSource != null)
        {
            m_CancellationTokenSource.Cancel();
            m_CancellationTokenSource.Dispose();
            m_CancellationTokenSource = null;
        }
    }

    /// <summary>
    /// 流式聊天
    /// </summary>
    /// <param name="prompt">聊天的内容</param>
    /// <param name="onStartAction">发起聊天开始事件</param>
    /// <param name="gettingDataAction">流式正在接收数据</param>
    /// <param name="onGettedDataEndAction">流式数据结束事件</param>
    /// <returns></returns>
    public async Task SendStreamChat(string prompt, Action onStartAction, Action<string> gettingDataAction,Action onGettedDataEndAction) {

        StopStreamChat();
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        m_StreamCancellationTokenSource = cancellationTokenSource;
        Debug.Log(" Start ");
        Loom.QueueOnMainThread(() =>
        {
            onStartAction?.Invoke();
        });

        if (m_StreamingChatCompletions != null) { Debug.Log(" streamingChatCompletions.Dispose "); m_StreamingChatCompletions.Dispose(); }
        Response<StreamingChatCompletions> response = await m_Client.GetChatCompletionsStreamingAsync(
            deploymentOrModelName: DeploymentOrModelName,
            PromptDataHandler(prompt));
        if (cancellationTokenSource?.IsCancellationRequested==false)
        {
            using (m_StreamingChatCompletions = response.Value)
            {

                await foreach (StreamingChatChoice choice in m_StreamingChatCompletions.GetChoicesStreaming())
                {
                    await foreach (ChatMessage message in choice.GetMessageStreaming())
                    {
                        if (cancellationTokenSource?.IsCancellationRequested == false) {
                            Loom.QueueOnMainThread(() =>
                            {
                                //Debug.Log(message.Content);
                                gettingDataAction?.Invoke(message.Content);
                            });
                        }
                    }
                }
            }
        }

        if (cancellationTokenSource?.IsCancellationRequested == false) { 
            Debug.Log(" End ");
            Loom.QueueOnMainThread(() => {
                onGettedDataEndAction?.Invoke();
            });
        }
    }

    /// <summary>
    /// 停止流式访问
    /// </summary>
    public void StopStreamChat()
    {
        if (m_StreamCancellationTokenSource != null)
        {
            m_StreamCancellationTokenSource.Cancel();
            m_StreamCancellationTokenSource.Dispose();
            m_StreamCancellationTokenSource = null;
        }
    }

    /// <summary>
    /// 根据需要做数据处理
    /// </summary>
    /// <param name="prompt"></param>
    /// <returns></returns>
    protected virtual ChatCompletionsOptions PromptDataHandler(string prompt) {
        ChatCompletionsOptions chatCompletionsOptions = new ChatCompletionsOptions()
        {
            Messages =
            {
                new ChatMessage(ChatRole.System, "你是聊天高手,可以聊天说地"),
                new ChatMessage(ChatRole.User, prompt),
            },
            MaxTokens = 200, //速度和内容显示

        };

        return chatCompletionsOptions;
    }
}

2、TestAzureOpenAI


using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class TestAzureOpenAI : MonoBehaviour
{
    AzureOpenAIQueryHandler m_AzureGptQueryHandler;

    public Button Btn;
    public InputField IptFld;
    public Text Txt;

    string getEndContent;
    Task m_Task;
    // Start is called before the first frame update
    void Start()
    {
        m_AzureGptQueryHandler = new AzureOpenAIQueryHandler();
        //Btn.onClick.AddListener(OnClick);
        Btn.onClick.AddListener(OnClickStream);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Q)) {
            OnClick();
        }

        Debug.Log($"m_Task?.Id = {m_Task?.Id}, m_Task?.Status = {m_Task?.Status} ");
    }

    private void OnDestroy()
    {
        Btn.onClick.RemoveAllListeners();
    }

    void OnClick() {
        Debug.Log(" m_AzureGptQueryHandler Request");

        string ask = string.IsNullOrEmpty(IptFld.text) == false ? IptFld.text : "你是谁";
        m_Task = m_AzureGptQueryHandler.SendChat(ask, () => {
            getEndContent = "";
            Txt.text = "";
        },
        (str) => {
            getEndContent += str;
            Txt.text += str;
            TaskDispose();
        }
        );
    }

    void OnClickStream() {
        Debug.Log(" m_AzureGptQueryHandler Request");
        string ask = string.IsNullOrEmpty(IptFld.text) == false ? IptFld.text : "你是谁";
        //ask =HandleAskContent(ask);
        m_Task = m_AzureGptQueryHandler.SendStreamChat(ask, () => {
            getEndContent = "";
            Txt.text = "";
        },
        (str) => {
            getEndContent += str;
            Txt.text += str;
        },
        () => {
            Debug.Log(getEndContent);
            TaskDispose();
        }
        );

    }

    void TaskDispose() {
        m_Task?.Dispose();
        m_Task = null;
    }

   
}

六、附加

案例工程项目源码:https://download.csdn.net/download/u014361280/87950232

OpenAI%20dll%20%E5%8C%85">创建新的 .NET Core ,获取 Azure.AI.OpenAI dll 包

1、在控制台窗口(例如 cmd、PowerShell 或 Bash)中,使用 dotnet new 命令创建名为 azure-openai-quickstart 的新控制台应用。 此命令将创建包含单个 C# 源文件的简单“Hello World”项目:Program.cs

命令:dotnet new console -n azure-openai-quickstart

 2、将目录更改为新创建的应用文件夹。 可使用以下代码生成应用程序:

命令:dotnet build

3、使用以下项安装 OpenAI .NET 客户端库

命令:dotnet add package Azure.AI.OpenAI --prerelease

dll 默认下载到如下路径文件夹

检索密钥和终结点


http://www.niftyadmin.cn/n/472046.html

相关文章

基于NXP i.MX 6ULL——MQTT通信协议的开发案例

前 言 本指导文档适用开发环境&#xff1a; Windows开发环境&#xff1a;Windows 7 64bit、Windows 10 64bit Linux开发环境&#xff1a;Ubuntu 18.04.4 64bit 拟机&#xff1a;VMware15.1.0 U-Boot&#xff1a;U-Boot-2020.04 Kernel&#xff1a;Linux-5.4.70 Linux S…

​价值驱动-数据分析价值逻辑与实践思考

月説小飞象交流会 未来是一片迷雾&#xff0c;令人胆怯&#xff0c;但不妨走下去&#xff0c;看看命运给我们准备了什么。 内部交流│25期 价值驱动 数据分析价值逻辑与实践思考 data analysis ●●●● 分享人&#xff1a;黄小伟 当今的企业&#xff0c;随着数字化技术日新月异…

source-map定位生产问题

source-map 定位源码错误位置 需要安装source-map库webpack配置需要配上devtool: “hidden-source-map”&#xff0c;devtool详细配置看这里devtool配置配置完webpack打包后&#xff0c;可以看到打包出来的.js.map文件 将生产包产生错误的栈赋值给stack即可&#xff0c;即设置…

通讯库​ ZeroMQ​

上文说到ecal通讯库&#xff0c;有点像ros,不过可以跨平台&#xff0c;但是ecal好像某些场景CPU占用会比较高 看ecal帖子看有人说到这个库还可以 zeromq/libzmq: ZeroMQ core engine in C, implements ZMTP/3.1 (github.com) ZeroMQ

MATLAB随机打乱矩阵(按行或列)

1、重新排列矩阵各行 a [1 2;3 4;5 6;7 8]; rowrank randperm(size(a, 1)); % size获得a的行数&#xff0c;randperm打乱各行的顺序 a1 a(rowrank,:) % 按照rowrank重新排列各行&#xff0c;注意rowrank的位置2、重新排列矩阵各列 b [1 3 5 7;2 4 6 8 ]; r…

kali磁盘空间不足,软连接解决apt下载问题

知识点&#xff1a; ①在Linux系统下(其他操作系统也有类似的规定)&#xff0c;磁盘的分区大致可以分为三类&#xff0c;分别为主分区、扩展分区和逻辑分区等等。 ②主分区可以有1-4个&#xff0c;扩展分区可以有0-1个&#xff0c;逻辑分区编号从5开始。 ③主分区可以直接进行格…

population预测-1

population预测-1 项目步骤 项目步骤 在网上搜索相关的数据集或者通过API获取。然后需要对数据进行清洗和预处理&#xff0c;如缺失值填充、特征选择、离散化等。 接下来您需要选择合适的算法进行模型训练&#xff0c;如决策树、线性回归等。这些算法可以通过PyTorch的API实现…

char*转LPCWSTR的两种方法

char*转LPCWSTR的两种方法 MultiByteToWideCharmbstowcs MultiByteToWideChar 将char*类型转换为LPCWSTR类型可以使用MultiByteToWideChar函数。这个函数可以将多字节字符串转换为宽字符字符串。 #include <stdio.h> #include <stdlib.h> #include <windows.h…