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

news2024/12/22 23:56:17

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

目录

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

一、简单介绍

二、实现原理

三、注意实现

四、简单实现步骤

五、关键代码

六、附加

创建新的 .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 服务使用 ChatGPT 和 GPT-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

创建新的 .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.coloradmin.cn/o/687153.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

公司个人年终工作总结【10篇】

公司个人年终工作总结1 20__年即将过去&#xff0c;在公司领导的悉心关怀下和同事们的帮助指导下&#xff0c;结合我自身的努力&#xff0c;在工作、学习等各方面都取得了长足的进步&#xff0c;尤其是在保险理赔专业知识和技能培养方面的成熟&#xff0c;使我成为一名合格的车…

Linux_查看硬盘占用情况

一、查看是什么占用了硬盘空间 df -h 这个命令查看的是显示目前在 Linux 系统上的所有文件系统磁盘使用情况&#xff0c;并根据大小适当显示&#xff08;-h 参数代表以可读的方式展示文件的大小&#xff09;。 一下为加 -h 和不加 -h 的结果 不加 -h 加 -h Filesystem&…

探秘安卓广播:揭秘Android广播机制的神奇之处

壹 广播机制 Android中的广播(Broadcast)机制用于进程/线程间通信&#xff0c;该机制使用了观察者模式。观察者模式是一种软件设计模式&#xff0c;该模式是基于消息的发布/订阅事件模型&#xff0c;该模型中的消息发布者是广播机制中的广播发送者&#xff0c;消息订阅者是广播…

ajax原理是什么?如何实现?

一、是什么 AJAX全称(Async Javascript and XML) 即异步的JavaScript 和XML&#xff0c;是一种创建交互式网页应用的网页开发技术&#xff0c;可以在不重新加载整个网页的情况下&#xff0c;与服务器交换数据&#xff0c;并且更新部分网页 Ajax的原理简单来说通过XmlHttpReq…

算法——查找表

查找&#xff0c;根据一个值查找另一个值&#xff0c;value值可以是容器&#xff0c;结构&#xff0c;这样可查找的元素就更多&#xff1b; 哈希冲突&#xff1a; 主关键字&#xff1a;可以唯一的标识一个记录的关键字&#xff0c;如准考证号&#xff1b; 此关键词&#xff…

RFID微图柜的操作流程以及功能介绍

微型图书馆智能书柜是一种基于 RFID 技术的图书自助借阅延伸服务的终端设备&#xff0c;可实现 24 小时无人看守的共享的智能化设备&#xff0c;可自助借还书、自动数据记录分析、自助提醒等一体化管理服务功能。 智能书柜的操作流程 借书&#xff1a;在操作页面上选择“借书…

【好书精读】网络是怎样连接的 —— 信号在网线和集线器中传输

&#xff08; 该图由我使用 AI 绘制 &#xff09; 目录 每个包都是独立传输的 防止网线中的信号衰减很重要 “双绞”是为了抑制噪声 集线器将信号发往所有线路 每个包都是独立传输的 从计算机发送出来的网络包会通过集线器 、 路由器等设备被转发 &#xff0c; 最 终到达…

解决 CentOS/Alma 安装 libpcap-devel 报错:No match for argument: libpcap-devel

环境&#xff1a;Alma 8.5、Centos 7.x 解决方案 Linux 安装软件的时候&#xff0c;需要 libpcap-devel 这个组件&#xff0c;执行命令&#xff1a;yum install libpcap-devel &#xff0c;然后报错如下&#xff1a; Last metadata expiration check: 0:05:24 ago on Mon 12…

5.3 Linux目录配置

5.3.1 Linux目录配置的依据--FHS 根据FHS的标准文件指出&#xff0c;他们的主要目的是希望让使用者可以了解到已安装软件通常放置于那个目录下&#xff0c; 所以他们希望独立的软件开发商、操作系统制作者、以及想要维护系统的使用者&#xff0c;都能够遵循FHS的标准。 也就是…

ECS 简略版说明五:Baking and entity scenes

目录 Baking and entity scenes Creating and editing sub scenes Accessing data in a baker Loading and unloading entity scenes Baking and entity scenes Baking 是一个把 sub scenes 转变成 entity scenes 的过程&#xff0c;使用 bakers和 baking systems: A sub …

数据中心供配电监控系统解决方案介绍 安科瑞 许敏

摘 要&#xff1a;供配电系统始终是数据中心比较重要的内容&#xff0c;在供配电系统能够得到平稳安全的运行的时候&#xff0c;才能够促使数据中心的相关设备具有比较可靠的动力源泉。在新型数据中心不断发展的过程中&#xff0c;其功率密度也相对比较大&#xff0c;对供电的要…

销售人员如何通过CRM系统提升业绩

面对日趋激烈的竞争&#xff0c;销售人员的压力也日益剧增。尤其伴随流量红利的消失&#xff0c;越来越多的企业开始借助数字化工具赋能销售人员&#xff0c;希望通过工具的加持&#xff0c;实现销售人才和销售工具的“人器合一”&#xff0c;最终助力企业业绩的增长。 在市场有…

专访虎牙直播毛茂德 | 看互联网老兵如何用技术驱动效能提升?

引言 作为一位经历了互联网、移动互联网阶段的老兵&#xff0c;毛茂德老师一路走来&#xff0c;始终保持自己的技术初心&#xff0c;不断探索未知领域的宽度&#xff0c;进入虎牙直播后&#xff0c;他积极推动虎牙拥抱云原生&#xff0c;进行业务创新&#xff0c;同时他也发挥技…

1. MongoDB快速实战与基本原理

分布式缓存技术Redis 1. MongoDB介绍1.1 什么是MongoDB1.2 MongoDB vs 关系型数据库1.3 MongoDB的技术优势1.4 MongoDB的应用场景 2. 2.MongoDB快速开始2.1 linux安装MorgoDB 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点纠正&a…

基于SpringBoot+mybatis+layui就业管理系统设计和实现

基于SpringBootmybatislayui就业管理系统设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文…

【STL】容器适配器

放在专栏【C知识总结】&#xff0c;会持续更新&#xff0c;期待支持 1、什么是适配器&#xff1f; 我们生活中就存在大量的适配器&#xff0c;最常见的莫过于我们常见的电源适配器&#xff0c;它的作用就是将交流电源转化为直流电源进行输出&#xff0c;可以说电源适配器在电流…

618复盘:爆款存当下,蓝海寄未来

价格&#xff0c;贵必赔。优惠&#xff0c;直接减。 号称史上最内卷的一届618在一家又一家号称史上最大补贴的狂欢下&#xff0c;落幕得悄无声息&#xff0c;各大平台默契地都没有公布具体GMV。 这样的结局似乎已有预见。此前有媒体援引浙江大学经济学院教授叶建亮的说法&…

从小白到大神之路之学习运维第47天---第三阶段----Iptables、路由表的配置、Linux下创建虚拟IP

第三阶段基础 时 间&#xff1a;2023年6月26日 参加人&#xff1a;全班人员 内 容&#xff1a; Iptables、路由表的配置、Linux下创建虚拟IP 目录 Iptables 1. 查看 iptables 设置&#xff1a; 2. 开启全部流量&#xff1a; 3. 关闭全部流量&#xff1a; 4. 允许某…

【Jmeter教程】_事务控制器

目录 一、添加事务控制器 二、事务控制器参数说明 三、运用事务控制器 统计性能测试结果一定会关注TPS&#xff0c;TPS表示每秒处理事务数&#xff0c;JMeter默认每个事务对应一个请求。我们可以用逻辑控制器中的事务控制器将多个请求统计为一个事务。 一、添加事务控制器 …

分享 5 个你可能不知道的前端小技巧

大家都知道&#xff0c;如今前端开发是一个充满活力的领域&#xff0c;每天都会涌现出新的技术和最佳实践。 作为前端开发人员&#xff0c;如果你真的想创建引人入胜、直观且响应迅速的用户界面&#xff0c;就必须时刻跟进最新的趋势和技术。 作为前端开发人员&#xff0c;我们…