OpenAI ChatGPT Unity接入

news2024/12/26 18:16:58

OpenAI ChatGPT Unity接入

  • OpenAI ChatGPT Unity接入
    • OpenAi-API-Unity 方法
      • OpenAi-API-Unity 下载
      • 本地配置
      • Unity 模块
        • URL接入
        • gz 接入
        • json 接入
        • Open AI
        • OpenAi-Api-Unity 插件文档
    • OpenAi 本地化接入 Unity 方法
      • Unity 关键字识别
      • 语音合成 & 文字转语音
      • 音频记录 & 实时音频处理
      • 语音转文字
      • Vosk 接受响应
      • OpenAI
        • 发送消息 示例
        • 返回消息 示例
        • Open AI 访问
        • 发送 Json 数据支持类
        • 接收 Json 数据支持类
      • 脚本搭载 以及 层级结构
      • 运行情况

OpenAI ChatGPT Unity接入

OpenAi-API-Unity 方法

OpenAi-API-Unity 下载

GitHub: OpenAi-API-Unity 主页

GitHub: OpenAi-API-Unity 安装包 下载地址

既然 Unity 都给了下载地址都给了,怎么能少了 UE 呢。都在下面了

GitHub: OpenAi-API-Unreal 主页

GitHub: OpenAi-API-Unreal 安装包 下载地址

反正我下载的是 0.2.9 版本 你们按需下载就行。
如果都下载不了的话 我 CSDN 上面上传的也有 巴拉一下就能找到。

请添加图片描述

为了防止出现意外 最好两个 安装包都下载。

请添加图片描述

本地配置

打开我的电脑 在地址栏键入:
Windows:%USERPROFILE%/
Mac:~/

请添加图片描述

找到 .openai 文件夹并打开。
如果没有就自己新建一个。

请添加图片描述

找到 auth.json 文件夹并打开。
如果没有就自己新建一个。

请添加图片描述

找到 auth.json 文件夹并打开。
如果没有就自己新建一个。
下面是里面的内容注意:填写自己OpenAI key 的时候没有尖括号!没有尖括号!没有尖括号!
重要的事情说三遍。
{
  "private_api_key":"<YOUR_KEY>"
}

请添加图片描述

Unity 模块

新建一个新的项目 打开 Package Manager。

请添加图片描述

URL接入

点击 git URL 按钮

请添加图片描述

键入:https://github.com/hexthedev/OpenAi-Api-Unity.git
如果成功会有 Open AI 插件显示。
如果没下再试试下面的方法。

请添加图片描述

gz 接入

点击 gz 导入按钮

请添加图片描述

点击刚才下载好的 压缩包
如果成功会有 Open AI 插件显示。
如果没下再试试下面的的方法。

请添加图片描述

json 接入

这个绝对能成!
点击 磁盘导入按钮。

请添加图片描述

解压刚才下载 OpenAi-Api-Unity-0.2.9.zip 压缩包
打开文件夹到根目录。
点击 package.json 文本并打开

请添加图片描述

你就会发现 成功了!旋转 跳跃。

请添加图片描述

Open AI

在菜单栏 点击 OpenAi -> Examples -> Chat at Runtime

请添加图片描述

然后就会打开一个新的场景。
好了,点击运行。在这里你就可以,跟 OpenAI 对话了。

请添加图片描述

请添加图片描述

OpenAi-Api-Unity 插件文档

GitHub: OpenAi-Api-Unity 插件文档 地址

请添加图片描述

OpenAi 本地化接入 Unity 方法

Unity 关键字识别

当前脚本主要作用是:使用 Unity 自带的 PhraseRecognizer 类 进行关键字识别。
识别通过之后响应 之后的逻辑执行。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Windows.Speech;

/// <summary>
/// 关键字识别
/// </summary>
public class KeywordRecognition_ZH : MonoBehaviour
{
    public static KeywordRecognition_ZH _Instance;
    //语音识别短句
    private PhraseRecognizer _PhraseASR;


    [Header("精度")]
    public ConfidenceLevel _PrecisionASR = ConfidenceLevel.Medium;

    [Header("关键字数组")]
    public string[] _ListStrASR = { "启动", "关闭", "小哟" };

    //麦克风
    private string _Device;

    [Header("智能回答布尔")]
    public bool _OpenASRbool;

    void Start()
    {
        _Instance = this;

        //获取麦克风设备
        _Device = Microphone.devices[0];

        print(_Device);


        //用设备开始录音
        //设备的名称
        //指示当达到长度秒时录音是否应该继续录制,并从音频剪辑开始绕圈录制
        //录音产生的音频剪辑的长度 
        //44100 音频采样率
        //_AuduioASR = Microphone.Start(_Device, true, 999, 44100);

        if (_PhraseASR == null)
        {
            //创建语音识别
            _PhraseASR = new KeywordRecognizer(_ListStrASR, _PrecisionASR);
            //添加广播事件
            _PhraseASR.OnPhraseRecognized += MonitorASR;
            //开启语音识别
            _PhraseASR.Start();

            Debug.Log("创建识别器成功");
        }
    }


    /// <summary>
    /// 语音识别监听
    /// 提供关于短语识别事件的信息
    /// </summary>
    /// <param 识别信息="_Args"></param>
    private void MonitorASR(PhraseRecognizedEventArgs _Args)
    {
        switch (_Args.text)
        {
            case "启动":
                //每次回复前清空 输入问题
                GetOpenAI_ZH._Instance._InputProblemText.text = "";

                VoskSpeechToText._Instance._SpeakBool = true;
                VoskSpeechToText._Instance.ToggleRecording();

                _OpenASRbool = true;

                break;

            case "小哟":

                VoskSpeechToText._Instance._SpeakBool = true;
                VoskSpeechToText._Instance.ToggleRecording();
                _OpenASRbool = true;

                break;


            case "关闭":
                VoskSpeechToText._Instance._SpeakBool = false;
                VoskSpeechToText._Instance.ToggleRecording();
                break;


            default:
                break;
        }
        print(_Args.text);
    }

    private void OnDestroy()
    {
        //判断场景中是否存在语音识别
        if (_PhraseASR != null)
        {
            //语音识别释放
            _PhraseASR.Dispose();
        }

    }
}

语音合成 & 文字转语音

根据百度提供的 API 文档进行文字转语音输出。
using Baidu.Aip.Speech;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Http;
using UnityEngine;
using UnityEngine.Networking;

/// <summary>
/// 语音合成 & 文字转语音
/// </summary>
public class VoiceSynthesis_ZH : MonoBehaviour
{

    [Header("音源")]
    public AudioSource _Audio;

    [Header("AI 发声器")]
    public Pronouncer _Pronouncer = Pronouncer.Duyaya;

    //全局变量
    public static VoiceSynthesis_ZH _World;

    //网页文字转语音
    private string _Url;

    //百度语音识别SDK
    private Asr _AipClient;



    private void Start()
    {
        _World = this;
        //StartCoroutine(GetAudioClip("开始"));
    }


    //获取 Web网页音源信息并播放
    public IEnumerator GetAudioClip(string AudioText)
    {
        if (AudioText!=null)
        {

            _Url = "http://tsn.baidu.com/text2audio?tex=" + AudioText +
                  "&tok=自己的Token" +
                  "&cuid=a7a0e3326da873c6fb0609e6385a82b934c9cb11" +
                  "&ctp=1" +
                  "&lan=zh" +
                  "&spd=5" +
                  "&pit=5" +
                  "&vol=10" +
                  "&per=" + (((int)_Pronouncer).ToString()) +
                  "&aue=6";


            using (UnityWebRequest _AudioWeb = UnityWebRequestMultimedia.GetAudioClip(_Url, AudioType.WAV))
            {

                yield return _AudioWeb.SendWebRequest();
                if (_AudioWeb.isNetworkError)
                {
                    yield break;
                }
                AudioClip _Cli = DownloadHandlerAudioClip.GetContent(_AudioWeb);
                _Audio.clip = _Cli;
                _Audio.Play();
            }
        }      
    }

    /// <summary>
    /// 获取 Web网页音源信息并播放 附带延迟时间
    /// </summary>
    /// <param 播放文字="AudioText"></param>
    /// <param 延迟时间="_DelayedTimer"></param>
    /// <returns></returns>
    public IEnumerator GetAudioClip(string AudioText, float _DelayedTimer)
    {
        yield return new WaitForSeconds(_DelayedTimer);


        _Url = "http://tsn.baidu.com/text2audio?tex=" + AudioText +
              "&tok=自己的Token" +
              "&cuid=a7a0e3326da873c6fb0609e6385a82b934c9cb11" +
              "&ctp=1" +
              "&lan=zh" +
              "&spd=5" +
              "&pit=5" +
              "&vol=10" +
              "&per=" + (((int)_Pronouncer).ToString()) +
              "&aue=6";


        using (UnityWebRequest _AudioWeb = UnityWebRequestMultimedia.GetAudioClip(_Url, AudioType.WAV))
        {

            yield return _AudioWeb.SendWebRequest();
            if (_AudioWeb.isNetworkError)
            {
                yield break;
            }
            AudioClip _Cli = DownloadHandlerAudioClip.GetContent(_AudioWeb);
            _Audio.clip = _Cli;


            _Audio.Play();
        }
    }

    /// <summary>
    /// 语音识别
    /// </summary>
    /// <param 录取音频 = "_Clip" ></ param >
    /// < returns ></ returns >
    private IEnumerator Recognition(AudioClip _Clip)
    {
        //开放 音频 长度
        float[] _Sample = new float[_Clip.samples];

        //用片段中的样本数据填充数组
        _Clip.GetData(_Sample, 0);

        //数据转换
        short[] _IntData = new short[_Sample.Length];
        byte[] _ByteData = new byte[_IntData.Length * 2];
        for (int i = 0; i < _Sample.Length; i++)
        {
            _IntData[i] = (short)(_Sample[i] * short.MaxValue);
        }
        Buffer.BlockCopy(_IntData, 0, _ByteData, 0, _ByteData.Length);

        //返回Json数据  (数据 格式 码率)
        var _Result = _AipClient.Recognize(_ByteData, "pcm", 16000);

        //获取Json 数据中的 讲话内容
        var _Speaking = _Result.GetValue("result");

        //检测是否有内容
        if (_Speaking == null)
        {
            StopAllCoroutines();
            yield return null;
        }

        //讲话内容转换为 字符串
        string _UsefulText = _Speaking.First.ToString();
        print(_UsefulText);


        yield return null;
    }


    /// <summary>
    /// 访问令牌获取
    /// </summary>
    /// <returns></returns>
    private static class AccessToken

    {
        // 调用getAccessToken()获取的 access_token建议根据expires_in 时间 设置缓存
        // 返回token示例
        public static String TOKEN = "自己的Token";

        // 百度云中开通对应服务应用的 API Key 建议开通应用的时候多选服务
        private static String clientId = "百度云应用的AK";
        // 百度云中开通对应服务应用的 Secret Key 
        private static String clientSecret = "百度云应用的SK";

        public static String GetAccessToken()
        {
            String authHost = "https://aip.baidubce.com/oauth/2.0/token";
            HttpClient client = new HttpClient();
            List<KeyValuePair<string, string>> paraList = new List<KeyValuePair<string, string>>();
            paraList.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
            paraList.Add(new KeyValuePair<string, string>("client_id", clientId));
            paraList.Add(new KeyValuePair<string, string>("client_secret", clientSecret));

            HttpResponseMessage response = client.PostAsync(authHost, new FormUrlEncodedContent(paraList)).Result;
            String result = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine(result);
            return result;
        }
    }

    /// <summary>
    /// AI 发音器
    /// </summary>
    public enum Pronouncer
    {
        //普通女声
        Female,
        //普通男生
        Male,
        //特殊男声
        Teshunan,
        //情感合成男生
        Duxiaoyao,
        //情感合成女生
        Duyaya
    }


    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Application.Quit();
        }
    }
}

音频记录 & 实时音频处理


using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


/// <summary>
/// 记录音频并为实时音频处理提供帧
/// </summary>
public class VoiceProcessor : MonoBehaviour
{
    /// <summary>
    /// 指示麦克风是否被捕获
    /// </summary>
    public bool IsRecording
    {
        get { return _audioClip != null && Microphone.IsRecording(CurrentDeviceName); }
    }

    [Header("麦克风")]
    [SerializeField] private int _MicrophoneIndex;

    /// <summary>
    /// 录制音频的采样率
    /// </summary>
    public int SampleRate { get; private set; }

    /// <summary>
    /// 传送的音频帧的大小
    /// </summary>
    public int FrameLength { get; private set; }

    /// <summary>
    /// 传递音频帧的事件
    /// </summary>
    public event Action<short[]> OnFrameCaptured;

    /// <summary>
    /// 事件,当音频捕获线程停止时
    /// </summary>
    public event Action OnRecordingStop;

    /// <summary>
    /// 事件,当音频捕获线程启动时
    /// </summary>
    public event Action OnRecordingStart;

    /// <summary>
    /// 可用的录音设备
    /// </summary>
    public List<string> Devices { get; private set; }

    /// <summary>
    /// 所选录音设备的索引
    /// </summary>
    public int CurrentDeviceIndex { get; private set; }

    /// <summary>
    /// 所选录音设备的名称
    /// </summary>
    public string CurrentDeviceName
    {
        get
        {
            if (CurrentDeviceIndex < 0 || CurrentDeviceIndex >= Microphone.devices.Length)
                return string.Empty;
            return Devices[CurrentDeviceIndex];
        }
    }

    [Header("语音最小音量检测")]
    [SerializeField, Tooltip("检测语音输入的最小音量"), Range(0.0f, 1.0f)]
    private float _MinimumSpeakingSampleValue = 0.05f;

    [SerializeField, Tooltip("发送语音请求前检测到的沉默时间(以秒为单位)")]
    [Header("语音发送沉默时间")]
    private float _SilenceTimer = 1.0f;

    [SerializeField, Tooltip("使用音量阈值自动检测语音。")]
    [Header("语音检测布尔")]
    private bool _AutoDetect;

    private float _TimeAtSilenceBegan;
    private bool _AudioDetected;
    private bool _DidDetect;
    private bool _Transmit;


    AudioClip _audioClip;
    private event Action RestartRecording;

    void Awake()
    {
        UpdateDevices();
    }
#if UNITY_EDITOR
    void Update()
    {
        if (CurrentDeviceIndex != _MicrophoneIndex)
        {
            ChangeDevice(_MicrophoneIndex);
        }
    }
#endif

    /// <summary>
    /// 更新可用音频设备列表
    /// </summary>
    public void UpdateDevices()
    {
        Devices = new List<string>();
        foreach (var device in Microphone.devices)
            Devices.Add(device);

        if (Devices == null || Devices.Count == 0)
        {
            CurrentDeviceIndex = -1;
            Debug.LogError("没有连接有效的录音设备");
            return;
        }

        CurrentDeviceIndex = _MicrophoneIndex;
    }

    /// <summary>
    /// 更换录音设备
    /// </summary>
    /// <param name="deviceIndex">新音频捕获设备的索引</param>
    public void ChangeDevice(int deviceIndex)
    {
        if (deviceIndex < 0 || deviceIndex >= Devices.Count)
        {
            Debug.LogError(string.Format("指定的设备索引{0}不是有效的记录设备", deviceIndex));
            return;
        }

        if (IsRecording)
        {
            // 用新设备重新开始录制的一次性事件
            // 最后一个会话完成的时刻
            RestartRecording += () =>
            {
                CurrentDeviceIndex = deviceIndex;
                StartRecording(SampleRate, FrameLength);
                RestartRecording = null;
            };
            StopRecording();
        }
        else
        {
            CurrentDeviceIndex = deviceIndex;
        }
    }

    /// <summary>
    /// 开始录音
    /// </summary>
    /// <param name="sampleRate">记录的采样率</param>
    /// <param name="frameSize">要传送的音频帧的大小</param>
    /// <param name="autoDetect">音频是否应该根据音量连续记录</param>
    public void StartRecording(int sampleRate = 16000, int frameSize = 512, bool ?autoDetect = null)
    {
        if (autoDetect != null)
        {
            _AutoDetect = (bool) autoDetect;
        }

        if (IsRecording)
        {
            // 如果采样率或帧大小已经改变,重新开始记录
            if (sampleRate != SampleRate || frameSize != FrameLength)
            {
                RestartRecording += () =>
                {
                    StartRecording(SampleRate, FrameLength, autoDetect);
                    RestartRecording = null;
                };
                StopRecording();
            }

            return;
        }

        SampleRate = sampleRate;
        FrameLength = frameSize;

        _audioClip = Microphone.Start(CurrentDeviceName, true, 1, sampleRate);

        StartCoroutine(RecordData());
    }

    /// <summary>
    /// 停止录音
    /// </summary>
    public void StopRecording()
    {
        if (!IsRecording)
            return;

        Microphone.End(CurrentDeviceName);
        Destroy(_audioClip);
        _audioClip = null;
        _DidDetect = false;

        StopCoroutine(RecordData());
    }

    /// <summary>
    /// 用于缓冲传入音频数据和传送帧的循环
    /// </summary>
    IEnumerator RecordData()
    {
        float[] sampleBuffer = new float[FrameLength];
        int startReadPos = 0;

        if (OnRecordingStart != null)
            OnRecordingStart.Invoke();

        while (IsRecording)
        {
            int curClipPos = Microphone.GetPosition(CurrentDeviceName);
            if (curClipPos < startReadPos)
                curClipPos += _audioClip.samples;

            int samplesAvailable = curClipPos - startReadPos;
            if (samplesAvailable < FrameLength)
            {
                yield return null;
                continue;
            }

            int endReadPos = startReadPos + FrameLength;
            if (endReadPos > _audioClip.samples)
            {
                // 碎片式读取(绕到片段的开头)
                // 在片段末尾读取位
                int numSamplesClipEnd = _audioClip.samples - startReadPos;
                float[] endClipSamples = new float[numSamplesClipEnd];
                _audioClip.GetData(endClipSamples, startReadPos);

                // 在剪辑开始时读取位
                int numSamplesClipStart = endReadPos - _audioClip.samples;
                float[] startClipSamples = new float[numSamplesClipStart];
                _audioClip.GetData(startClipSamples, 0);

                // 组合成全画框
                Buffer.BlockCopy(endClipSamples, 0, sampleBuffer, 0, numSamplesClipEnd);
                Buffer.BlockCopy(startClipSamples, 0, sampleBuffer, numSamplesClipEnd, numSamplesClipStart);
            }
            else
            {
                _audioClip.GetData(sampleBuffer, startReadPos);
            }

            startReadPos = endReadPos % _audioClip.samples;
            if (_AutoDetect == false)
            {
                _Transmit =_AudioDetected = true;
            }
            else
            {
                float maxVolume = 0.0f;

                for (int i = 0; i < sampleBuffer.Length; i++)
                {
                    if (sampleBuffer[i] > maxVolume)
                    {
                        maxVolume = sampleBuffer[i];
                    }
                }

                if (maxVolume >= _MinimumSpeakingSampleValue)
                {
                    _Transmit= _AudioDetected = true;
                    _TimeAtSilenceBegan = Time.time;
                }
                else
                {
                    _Transmit = false;

                    if (_AudioDetected && Time.time - _TimeAtSilenceBegan > _SilenceTimer)
                    {
                        _AudioDetected = false;
                    }
                }
            }

            if (_AudioDetected)
            {
                _DidDetect = true;
                // 转换为16位int样本
                short[] pcmBuffer = new short[sampleBuffer.Length];
                for (int i = 0; i < FrameLength; i++)
                {
                    pcmBuffer[i] = (short) Math.Floor(sampleBuffer[i] * short.MaxValue);
                }

                // 引发缓冲区事件
                if (OnFrameCaptured != null && _Transmit)
                    OnFrameCaptured.Invoke(pcmBuffer);
            }
            else
            {
                if (_DidDetect)
                {
                    if (OnRecordingStop != null)
                        OnRecordingStop.Invoke();
                    _DidDetect = false;
                }
            }
        }


        if (OnRecordingStop != null)
            OnRecordingStop.Invoke();
        if (RestartRecording != null)
            RestartRecording.Invoke();
    }
}

语音转文字

使用麦克风 录取音频片段并根据模型进行 转录文字输出。
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Ionic.Zip;
using Unity.Profiling;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Vosk;

/// <summary>
/// 语音转文字
/// </summary>
public class VoskSpeechToText : MonoBehaviour
{
	//单例
	public static VoskSpeechToText _Instance;

	[Header("模型路径")]
	[Tooltip("模型的位置,相对于Streaming Assets文件夹。")]
	public string ModelPath = "vosk-model-small-cn-0.22.zip";

	[Header("麦克风")]
	[Tooltip("麦克风输入的源")]
	public VoiceProcessor VoiceProcessor;

	[Header("处理最大数量")]
	[Tooltip("将要处理的备选项的最大数量。")]
	public int MaxAlternatives = 3;

	[Header("最长记录时间")]
	[Tooltip("在重新开始之前我们应该记录多长时间?")]
	public float MaxRecordLength = 5;

	[Header("识别器启动布尔")]
	[Tooltip("识别器是否应该在应用程序启动时启动?")]
	public bool AutoStart = true;

	[Header("检测短语数组")]
	[Tooltip("将被检测到的短语。如果为空,将检测所有单词。")]
	public List<string> KeyPhrases = new List<string>();

	//Vosk模型的缓存版本。
	private Model _Model;

	//Vosk识别器的缓存版本。
	private VoskRecognizer _Recognizer;

	//条件标志,用于查看是否已经创建了识别器。
	//TODO:允许对识别器进行运行时更改。
	private bool _RecognizerReady;

	//保留所有音频数据,直到用户停止通话。
	private readonly List<short> _Buffer = new List<short>();

	//当控制器的状态改变时调用。
	public Action<string> OnStatusUpdated;

	//在用户完成讲话后调用,vosk处理音频。
	public Action<string> OnTranscriptionResult;

	//解压缩模型文件夹的绝对路径。
	private string _DecompressedModelPath;

	//字符串,包含Json Array格式的关键字
	private string _Grammar = "";

	//用于等待模型文件成功解压缩的标志。
	private bool _IsDecompressing;

	//用于等待脚本成功启动的标志。
	private bool _IsInitializing;

	//用于检查Vosk是否启动的标志。
	private bool _DidInit;

	//关键字识别布尔
	[Header("关键字识别布尔")]
	public bool _SpeakBool = true;

	//线程的逻辑

	//旗帜表示我们结束了
	private bool _Running;

	//麦克风数据的线程安全队列。
	private readonly ConcurrentQueue<short[]> _ThreadedBufferQueue = new ConcurrentQueue<short[]>();

	//线程安全的结果队列
	private readonly ConcurrentQueue<string> _ThreadedResultQueue = new ConcurrentQueue<string>();



	static readonly ProfilerMarker _VoskRecognizerCreateMarker = new ProfilerMarker("VoskRecognizer.Create");
	static readonly ProfilerMarker _VoskRecognizerReadMarker = new ProfilerMarker("VoskRecognizer.AcceptWaveform");

	//如果启用了“自动启动”,则可以启动语音转文本。
	void Start()
	{
		_Instance = this;
		if (AutoStart)
		{
			StartVoskStt();
		}
	}

	/// <summary>
	/// 启动Vosk语音转文本
	/// </summary>
	/// <param name="keyPhrases">关键字/短语列表。关键字需要存在于模型字典中,所以像“webview”这样的词被更好地检测为两个更常见的词“webview”.</param>
	/// <param name="modelPath">模型文件夹相对于StreamingAssets的路径。如果路径以.zip结尾,它将被解压缩到应用程序数据持久文件夹中</param>
	/// <param name="startMicrophone">"麦克风应该在vosk初始化之后吗?</param>
	/// <param name="maxAlternatives">检测到的可选短语的最大数目</param>
	public void StartVoskStt(List<string> keyPhrases = null, string modelPath = default, bool startMicrophone = false, int maxAlternatives = 3)
	{
		if (_IsInitializing)
		{
			Debug.LogError("正在初始化!");
			return;
		}
		if (_DidInit)
		{
			Debug.LogError("Vosk 已经初始化!");
			return;
		}

		//语言模型加载
		if (!string.IsNullOrEmpty(modelPath))
		{
			ModelPath = modelPath;
		}

		//关键字加载
		if (keyPhrases != null)
		{
			KeyPhrases = keyPhrases;
		}

		MaxAlternatives = maxAlternatives;
		StartCoroutine(DoStartVoskStt(startMicrophone));
	}

	//解压模型,加载设置,启动Vosk和可选地启动麦克风
	private IEnumerator DoStartVoskStt(bool startMicrophone)
	{
		_IsInitializing = true;
		yield return WaitForMicrophoneInput();

		yield return Decompress();

		OnStatusUpdated?.Invoke("加载模型来自: " + _DecompressedModelPath);
		//Vosk.Vosk.SetLogLevel(0);
		_Model = new Model(_DecompressedModelPath);

		yield return null;

		OnStatusUpdated?.Invoke("初始化");
		VoiceProcessor.OnFrameCaptured += VoiceProcessorOnOnFrameCaptured;
		VoiceProcessor.OnRecordingStop += VoiceProcessorOnOnRecordingStop;

		if (startMicrophone)
			VoiceProcessor.StartRecording();

		_IsInitializing = false;
		_DidInit = true;

		ToggleRecording();
		_SpeakBool = false;
		ToggleRecording();
	}

	//将keyphrases转换为json数组,并在末尾附加' [unk] '关键字,以告诉vosk过滤其他短语。
	private void UpdateGrammar()
	{
		if (KeyPhrases.Count == 0)
		{
			_Grammar = "";
			return;
		}

		JSONArray keywords = new JSONArray();
		foreach (string keyphrase in KeyPhrases)
		{
			keywords.Add(new JSONString(keyphrase.ToLower()));
		}

		keywords.Add(new JSONString("[unk]"));

		_Grammar = keywords.ToString();
	}

	//解压缩模型zip文件或返回解压缩文件的位置。
	private IEnumerator Decompress()
	{
		if (!Path.HasExtension(ModelPath)
			|| Directory.Exists(
				Path.Combine(Application.persistentDataPath, Path.GetFileNameWithoutExtension(ModelPath))))
		{
			OnStatusUpdated?.Invoke("使用现有的解压缩模型");
			_DecompressedModelPath =
				Path.Combine(Application.persistentDataPath, Path.GetFileNameWithoutExtension(ModelPath));
			Debug.Log(_DecompressedModelPath);

			yield break;
		}

		OnStatusUpdated?.Invoke("模型解压中...");
		string dataPath = Path.Combine(Application.streamingAssetsPath, ModelPath);

		Stream dataStream;
		// 从流资产路径读取数据。你不能直接在Android上访问流媒体资源。
		if (dataPath.Contains("://"))
		{
			UnityWebRequest www = UnityWebRequest.Get(dataPath);
			www.SendWebRequest();
			while (!www.isDone)
			{
				yield return null;
			}
			dataStream = new MemoryStream(www.downloadHandler.data);
		}
		// 在有效的平台上直接读取文件。
		else
		{
			dataStream = File.OpenRead(dataPath);
		}

		//读取Zip文件
		var zipFile = ZipFile.Read(dataStream);

		//等待zip文件完成解压缩
		zipFile.ExtractProgress += ZipFileOnExtractProgress;

		//更新状态文本
		OnStatusUpdated?.Invoke("读取Zip文件");

		//开始提取
		zipFile.ExtractAll(Application.persistentDataPath);

		//等到它完成
		while (_IsDecompressing == false)
		{
			yield return null;
		}
		//覆盖ZipFileOnExtractProgress中给出的路径以防止崩溃
		_DecompressedModelPath = Path.Combine(Application.persistentDataPath, Path.GetFileNameWithoutExtension(ModelPath));

		//更新状态文本
		OnStatusUpdated?.Invoke("解压完成!");
		//稍等一下,以防我们需要初始化另一个对象。
		yield return new WaitForSeconds(1);
		//处理zipfile读取器。
		zipFile.Dispose();
	}

	///更新zip文件提取过程时调用的函数。
	private void ZipFileOnExtractProgress(object sender, ExtractProgressEventArgs e)
	{
		if (e.EventType == ZipProgressEventType.Extracting_AfterExtractAll)
		{
			_IsDecompressing = true;
			_DecompressedModelPath = e.ExtractLocation;
		}
	}

	//等待麦克风初始化
	private IEnumerator WaitForMicrophoneInput()
	{
		while (Microphone.devices.Length <= 0)
			yield return null;
	}

	//可以从脚本或GUI按钮中调用以启动检测。
	public void ToggleRecording()
	{
 
		Debug.Log("Toogle记录");
		if (!VoiceProcessor.IsRecording)
		{
            if (_SpeakBool )
            {
				Debug.Log("开始记录");
				_Running = true;
				VoiceProcessor.StartRecording();
				Task.Run(ThreadedWork).ConfigureAwait(false);
			}
            else
            {
				Debug.Log("停止记录");
				_Running = false;
				VoiceProcessor.StopRecording();
			}
		}
		else
		{
			Debug.Log("停止记录");
			_Running = false;
			VoiceProcessor.StopRecording();
		}
	}

	//调用Unity线程上的On短语识别事件
	void Update()
	{
		if (_ThreadedResultQueue.TryDequeue(out string voiceResult))
		{
		    OnTranscriptionResult?.Invoke(voiceResult);
		}
	}

	/// <summary>
	/// 当检测到新的音频时,从语音处理器回调
	/// </summary>
	/// <param name="samples"></param>
	private void VoiceProcessorOnOnFrameCaptured(short[] samples)
	{	
                _ThreadedBufferQueue.Enqueue(samples);
	}

	/// <summary>
	/// 录音停止时从语音处理器回调
	/// </summary>
	private void VoiceProcessorOnOnRecordingStop()
	{
                Debug.Log("停止");
	}

	//将音频逻辑输入语音识别器
	private async Task ThreadedWork()
	{
		_VoskRecognizerCreateMarker.Begin();
		if (!_RecognizerReady)
		{
			UpdateGrammar();

			//仅检测指定的已定义关键字。
			if (string.IsNullOrEmpty(_Grammar))
			{
				_Recognizer = new VoskRecognizer(_Model, 16000.0f);
			}
			else
			{
				_Recognizer = new VoskRecognizer(_Model, 16000.0f, _Grammar);
			}

			_Recognizer.SetMaxAlternatives(MaxAlternatives);
			//_recognizer.SetWords(true);
			_RecognizerReady = true;

			Debug.Log("识别器准备好了");
		}

		_VoskRecognizerCreateMarker.End();

		_VoskRecognizerReadMarker.Begin();

		while (_Running)
		{
			if (_ThreadedBufferQueue.TryDequeue(out short[] voiceResult))
			{
				if (_Recognizer.AcceptWaveform(voiceResult, voiceResult.Length))
				{
					var result = _Recognizer.Result();
					_ThreadedResultQueue.Enqueue(result);
				}
			}
			else
			{
				// 等待一些数据
				await Task.Delay(100);
			}
		}

		_VoskRecognizerReadMarker.End();
	}
}

Vosk 接受响应

针对模型输出结果的响应输出。
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Vosk 返回结果
/// </summary>
public class VoskResultText : MonoBehaviour 
{
    [Header("Vosk 语音转文本")]
    public VoskSpeechToText _VoskSpeechToText;
    [Header("返回文本")]
    public InputField _ResultText;

    void Awake()
    {
        //监听
        _VoskSpeechToText.OnTranscriptionResult += OnTranscriptionResult;
    }

    /// <summary>
    /// 语音转录文字方法
    /// </summary>
    /// <param name="obj"></param>
    private void OnTranscriptionResult(string obj)
    {
        //Debug.Log(obj);
        var result = new RecognitionResult(obj);
        for (int i = 0; i < result.Phrases.Length; i++)
        {
            //说话内容 不为空
            if (result.Phrases[i].Text!=""|| result.Phrases[i].Text!=" ")
            {
                if (i > 0)
                {
                    _ResultText.text += ", ";
                }
                //如果是多个结果的话 只要第一个结果
                _ResultText.text += result.Phrases[0].Text.Replace(" ", "");
                //说话内容 并剔除所有空格
                Debug.Log(_ResultText.text);
                var _StrRe = result.Phrases[0].Text.Replace(" ", "");
                //ResultText.text = _StrRe;

                if (_StrRe != "小哟" || _StrRe != "小哟小哟")
                {
                    //如果是多个结果的话 只要第一个结果
                    _ResultText.text = _StrRe;
                }
                if (KeywordRecognition_ZH._Instance._OpenASRbool)
                {
                    //智能回答
                    GetOpenAI_ZH._Instance._InputProblemText.text = _StrRe;
                    GetOpenAI_ZH._Instance.SendData();

                    //录音关闭
                    VoskSpeechToText._Instance._SpeakBool = false;
                    VoskSpeechToText._Instance.ToggleRecording();

                    //回答响应布尔
                    KeywordRecognition_ZH._Instance._OpenASRbool = false;
                }
                return;
            }
        }
    }
}

OpenAI

访问 Open AI 需要一个接收 Json 数据的类,一个发送 Json 数据的类,我都会放在下面。
还有几个文档链接大家注意看。
Chat API 访问 URL:https://api.openai.com/v1/chat/completions

极简回答:
《核心》:访问 OpenAI 网址、使用 POST 方法进行参数传递、等待响应、可视化显示。

链接: Open AI API 文档地址

链接: Open AI API 示例地址

链接: Open AI API 开放平台地址

发送消息 示例

     //发送消息 示例
    {
        "model":"gpt-3.5-turbo",
        "messages":
        [
            {
                "role":"user",
                "content":"你是谁"
            }
        ],
        "max_tokens":2048,
        "temperature":0.5,
        "top_p":1,
        "frequency_penalty":1,
        "presence_penalty":1,
        "stop":"stop"
    }

返回消息 示例

	//返回消息 示例
    {
        "id":"chatcmpl-7CQ1C0ZL7XUOdqPcblJDxxPnOmI1c",
        "object":"chat.completion",
        "created":1683194306,
        "model":"gpt-3.5-turbo-0301",
        "usage":{
            "prompt_tokens":12,
            "completion_tokens":15,
            "total_tokens":27
        },
        "choices":
        [
            {
                "message":
                {
                    "role":"assistant",
                    "content":"我是一个AI语言模型,由OpenAI开发。"
                },
                "finish_reason":"stop",
                "index":0
            }
        ]
    }

Open AI 访问

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using static AcceptJson_ZH;
using static PostDataJson_ZH;
/// <summary>
/// Open AI 访问
/// </summary>
public class GetOpenAI_ZH : MonoBehaviour
{
    public static GetOpenAI_ZH _Instance;

    //API key
    private string _OpenAI_Key = "";

    // Chat API 访问 URL
    private string _OpenAIUrl = "https://api.openai.com/v1/chat/completions";
    //配置参数
    [SerializeField] private PostDataJson_ZH _PostDataSetting;


    [Header("输入的信息")]
    [SerializeField] public InputField _InputProblemText;

    [Header("回复的信息")]
    [SerializeField] public Text _RobotChatText;

    //回答模型
    [HideInInspector]
    public ModelType _ModelType { get; set; }

    //模型菜单显示 布尔
    private bool _ModelMenuActive;

    private void Awake()
    {
        _Instance = this;
    }

    /// <summary>
    /// 回答模型选择方法
    /// </summary>
    /// <param 当前执行 Button="_ModelTypeButton"></param>
    public void ModelTypeSet(Button _ModelTypeButton)
    {
        if (_ModelTypeButton.name == "gpt-3.5-turbo")
        {
            _ModelType = ModelType.gpt35turbo;
        }
        else if (_ModelTypeButton.name == "gpt-3.5-turbo-0301")
        {
            _ModelType = ModelType.gpt35turbo0301;
        }
        else if (_ModelTypeButton.name == "gpt-4")
        {
            _ModelType = ModelType.gpt4;
        }
        else if (_ModelTypeButton.name == "gpt-4-0314")
        {
            _ModelType = ModelType.gpt40314;
        }
        else if (_ModelTypeButton.name == "gpt-4-32k")
        {
            _ModelType = ModelType.gpt432k;
        }
        else if (_ModelTypeButton.name == "gpt-4-32k-0314")
        {
            _ModelType = ModelType.gpt432k0314;
        }
        else if (_ModelTypeButton.name == "text-davinci-003")
        {
            _ModelType = ModelType.textdavinci003;
        }
    }


    /// <summary>
    /// 发送信息
    /// </summary>
    public void SendData()
    {
        if (_InputProblemText.text.Equals(""))
            return;

        //问题
        string _Problem = _InputProblemText.text;

        StartCoroutine(GetPostData(_Problem));
        //_InputText.text = "";
    }


    /// <summary>
    /// POST 方法请求
    /// </summary>
    /// <param 问题="_SendMessage"></param>
    /// <returns></returns>
    private IEnumerator GetPostData(string _SendMessage)
    {
         OpenAI 访问
        //var _Request = new UnityWebRequest(_OpenAIUrl, "POST");

        // OpenAI 访问
        using (UnityWebRequest _Request = new UnityWebRequest(_OpenAIUrl, "POST"))
        {
            //规则归一化
            PostDataJson_ZH _PostData = new PostDataJson_ZH
            {
                //model =  "gpt-3.5-turbo",
                max_tokens = _PostDataSetting.max_tokens,
                temperature = _PostDataSetting.temperature,
                top_p = _PostDataSetting.top_p,
                frequency_penalty = _PostDataSetting.frequency_penalty,
                presence_penalty = _PostDataSetting.presence_penalty,
                stop = _PostDataSetting.stop
            };

            //模型设置
            switch (_ModelType)
            {
                //gpt-3.5-turbo、gpt-3.5-turbo-0301、gpt-4、gpt-4-0314、gpt-4-32k、gpt-4-32k-0314、text-davinci-003
                case ModelType.gpt35turbo:
                    _PostData.model = "gpt-3.5-turbo";
                    break;
                case ModelType.gpt35turbo0301:
                    _PostData.model = "gpt-3.5-turbo-0301";
                    break;
                case ModelType.gpt4:
                    _PostData.model = "gpt-4";
                    break;
                case ModelType.gpt40314:
                    _PostData.model = "gpt-4-0314";
                    break;
                case ModelType.gpt432k:
                    _PostData.model = "gpt-4-32k";
                    break;
                case ModelType.gpt432k0314:
                    _PostData.model = "gpt-4-32k-0314";
                    break;
                case ModelType.textdavinci003:
                    _PostData.model = "text-davinci-003";
                    break;
                default:
                    break;
            }
            //消息赋予
            PostDataJson_ZH.MessagesItem _Messages = new PostDataJson_ZH.MessagesItem();
            _Messages.role = "user";
            _Messages.content = _SendMessage;
            _PostData.messages.Add(_Messages);

            //数据转换
            string _JsonText = JsonUtility.ToJson(_PostData);
            print(_JsonText);

            byte[] _Data = System.Text.Encoding.UTF8.GetBytes(_JsonText);
            //数据上传 等待响应
            _Request.uploadHandler = new UploadHandlerRaw(_Data);
            _Request.downloadHandler = new DownloadHandlerBuffer();

            //数据重定向
            _Request.SetRequestHeader("Content-Type", "application/json");
            _Request.SetRequestHeader("Authorization", string.Format("Bearer {0}", _OpenAI_Key));

            //等待响应 开始与远程服务器通信
            yield return _Request.SendWebRequest();



            //数据返回
            if (_Request.responseCode == 200)
            {
                //接收返回信息
                string _Message = _Request.downloadHandler.text;
                //数据转换
                AcceptJson_ZH _Textback = JsonUtility.FromJson<AcceptJson_ZH>(_Message);

                //确保当前有消息传回
                if (_Textback != null && _Textback.choices.Count > 0)
                {
                    //OpenAI 输出 清空
                    _RobotChatText.text = "";
                    //打印
                    print(_Textback.choices[0].message.content);
                    //回答消息填写
                    _RobotChatText.text = _Textback.choices[0].message.content;

                    //文字转语音 回答
                    StartCoroutine(VoiceSynthesis_ZH._World.GetAudioClip(_Textback.choices[0].message.content));
                }
            }
        }
    }


    void Update()
    {

        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Application.Quit();
        }

        if (Input.GetKeyDown(KeyCode.Q))
        {
            _ModelMenuActive = !_ModelMenuActive;

            //显示设置
            if (_ModelMenuActive)
            {
                GameObject.Find("模型选择").transform.localScale = Vector3.one;
            }
            else
            {
                GameObject.Find("模型选择").transform.localScale = Vector3.zero;
            }
        }
    }
}

发送 Json 数据支持类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Open AI 发送 数据
/// </summary>
[System.Serializable]
public class PostDataJson_ZH 
{
    [Header("使用模型")]
    [Tooltip("gpt-3.5-turbo、gpt-3.5-turbo-0301、gpt-4、gpt-4-0314、gpt-4-32k、gpt-4-32k-0314、text-davinci-003")]
    public ModelType _modelType = ModelType.gpt35turbo;
    [HideInInspector]
    public string model = "gpt-3.5-turbo";

    [Header("发送信息 数据")]
    public List<MessagesItem> messages = new List<MessagesItem>();

    [Header("最大 Token 值")]
    [Tooltip("可以在聊天完成中生成的最大令牌数")]
    public int max_tokens = 2048;

    [Header("采样温度")]
    [Tooltip("温度 使用什么采样温度,介于 0 和 2 之间。较高的值(如 0.8)将使输出更加随机,而较低的值(如 0.2)将使其更加集中和确定")]
    [Range(0,2)]
    public float temperature = 0.5f;


    [Header("采样温度")]
    [Tooltip("使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的迭代")]
    [Range(0, 1)]
    public float top_p = 1;

    [Header("重复度")]
    [Tooltip("介于 -2.0 和 2.0 之间的数字。正值会根据新标记到目前为止在文本中的现有频率来惩罚新标记,从而降低模型逐字重复同一行的可能性")]
    [Range(0, 2)]
    public float frequency_penalty = 1.0f;

    [Header("新主题")]
    [Tooltip("介于 -2.0 和 2.0 之间的数字。正值会根据新标记到目前为止是否出现在文本中来惩罚它们,从而增加模型讨论新主题的可能性")]
    [Range(0, 2)]
    public float presence_penalty = 1.0f;

    [Header("结束原因")]
    [Tooltip("最多 4 个序列,其中 API 将停止生成更多令牌。")]
    public string stop = "stop";

    [System.Serializable]
    public class MessagesItem
    {
        /// <summary>
        /// 演员
        /// </summary>
        public string role;
        /// <summary>
        /// 信息
        /// </summary>
        public string content;
    }


    [System.Serializable]
    public enum ModelType
    {
        //gpt-3.5-turbo
        gpt35turbo,

        //gpt-3.5-turbo-0301
        gpt35turbo0301,

        //gpt-4
        gpt4,

        //gpt-4-0314
        gpt40314,

        //gpt-4-32k
        gpt432k,

        //gpt-4-32k-0314
        gpt432k0314,

        //text-davinci-003
        textdavinci003
    }
}

接收 Json 数据支持类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Open AI 接收 数据
/// </summary>
[System.Serializable]
public class AcceptJson_ZH
{
    /// <summary>
    /// Open AI ID
    /// </summary>
    public string id;
    /// <summary>
    /// 支付方式
    /// </summary>
    public string @object;
    /// <summary>
    /// 建立 标识
    /// </summary>
    public int created;
    /// <summary>
    /// 使用模型
    /// </summary>
    public string model;
    /// <summary>
    /// 用法
    /// </summary>
    public Usage usage;
    /// <summary>
    /// 参数选择
    /// </summary>
    [SerializeField]
    public List<ChoicesItem> choices;


    [System.Serializable]
    public class Usage
    {
        /// <summary>
        /// 指示令牌
        /// </summary>
        public int prompt_tokens;
        /// <summary>
        /// 完成令牌
        /// </summary>
        public int completion_tokens;
        /// <summary>
        /// 总令牌
        /// </summary>
        public int total_tokens;
    }

    [System.Serializable]
    public class Message
    {
        /// <summary>
        /// 角色 一般是 user
        /// </summary>
        public string role;
        /// <summary>
        /// 回答 信息
        /// </summary>
        public string content;
    }

    [System.Serializable]
    public class ChoicesItem
    {
        /// <summary>
        /// 返回消息
        /// </summary>
        [SerializeField]
        public Message message;
        /// <summary>
        /// 完成原因 一般是 stop
        /// </summary>
        public string finish_reason;
        /// <summary>
        /// 步数
        /// </summary>
        public int index;
    }
}

脚本搭载 以及 层级结构

KeywordRecognition_ZH 以及 VoiceSynthesis_ZH 搭载情况

请添加图片描述

VoiceProcessor、VoskSpeechToText、VoskResultText 搭载情况

请添加图片描述

GetOpenAI_ZH 搭载情况

请添加图片描述

Unity 层次结构

请添加图片描述

运行情况

程序包含了语音输出模块、语音输出模块、ChatGPt回答、中英文双语对话、预留了二次开发接口。
就这吧 不想写了 人麻了。
好像忘了啥。啊对 Vosk的下载链接没给你们,我就不往上翻了就直接在下面写了。
太长了,抱歉  哈哈哈。
你们要是没有积分 就直接给我发私信 给我邮箱 我直接发你们邮箱 别说谢  受不了。(。・∀・)ノ゙

链接: Vosk 官网

链接: Vosk 下载地址

链接: Vosk 语言模型下载地址

请添加图片描述

请添加图片描述

暂时先这样吧,如果有时间的话就会更新我调教好大语言模型的,以后抽空再弄个一体化,散会吧。要是实在看不明白就留言,看到我会回复的。
路漫漫其修远兮,与君共勉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/496123.html

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

相关文章

基于物联网及云计算技术的智慧充电桩平台设计方案

针对目前的充电桩监管难题&#xff0c;如何逐一击破各个痛点&#xff1f; TSINGSEE可提供基于"智能充电设备&#xff0b;云平台&#xff0b;APP小程序"一体化完整的解决方案&#xff0c;解决当前充电桩运营商面临的各种运营和管理难题。 一、方案介绍 方案充分利用…

容器技术的发展

容器技术的发展 近年来&#xff0c;随着计算机硬件、网络以及云计算等技术的迅速发展&#xff0c;云原生的概念也越来越受到业界人士的广泛关注&#xff0c;越来越多的应用场景开始拥抱云原生&#xff0c;其中容器技术的发展起着至关重要的作用。本章将介绍容器技术的基础知识…

瘦身必备!四款低卡美食狂掉20斤肥肉

夏天来了&#xff0c;想要减肥瘦身&#xff0c;却总是被高卡路里的食物所困扰&#xff1f;别担心&#xff0c;今天我为大家介绍四款低卡掉秤减脂美食&#xff0c;让你轻松享受美食的同时还能达到减肥的目的。 这四款美食简单易做&#xff0c;口感也十分好吃&#xff0c;适合各…

《花雕学AI》ChatGPT 的 Prompt 用法,不是随便写就行的,这 13 种才是最有效的

ChatGPT 是一款基于 GPT-3 模型的人工智能写作工具&#xff0c;它可以根据用户的输入和要求&#xff0c;生成各种类型和风格的文本内容&#xff0c;比如文章、故事、诗歌、对话、摘要等。ChatGPT 的强大之处在于它可以灵活地适应不同的写作场景和目的&#xff0c;只要用户给出合…

【STM32】定时器PWM模式详解

PWM模式&#xff1a; PWM模式1&#xff0c;向上计数时&#xff0c;PWM信号从有效电平变为无效电平 PWM模式2&#xff0c;向上计数时&#xff0c;PWM信号从无效电平变为有效电平 PWM极性&#xff1a; 极性为高时&#xff0c;高电平为有效电平&#xff0c;低电平为无效电平 极性…

【Android取证篇】Android设备USB调试打开方式(开发者模式)

【Android取证篇】Android设备USB调试打开方式(开发者模式) Android各个版本系统手机开启”USB调试”的入口不全相同&#xff0c;仅供参考—【蘇小沐】 1、【Android1.0-3.2】 路径&#xff1a;在应用列表选择「设置」->「应用程序」->「开发」->勾选「USB调试」选…

拿来吧你——一个类帮你搞定SpringBoot中的请求日志打印

拿来吧你——一个类帮你搞定SpringBoot中的请求日志打印 日常开发工作中避免不了要打印请求日志&#xff0c;这个功能几乎在所有的项目中都需要编写一次&#xff0c;重复的次数多了&#xff0c;难免会感觉繁琐&#xff0c;因此打算搞一个通用类把这块功能拆出来。 废话不多说—…

虹科方案|使用 HK-TRUENAS支持媒体和娱乐工作流程-1

一、摘要 开发和交付能够随时随地触及受众的媒体内容变得越来越重要和复杂。 在当今高度互联、娱乐驱动的世界中&#xff0c;媒体和娱乐 (M&E) 公司需要保持竞争力才能取得成功。 这些组织需要制作各种不同格式的信息和娱乐内容&#xff0c;以便在移动设备、台式机、工作站…

MySQL---基本操作DDL(SQL特点,数据类型,对数据库的操作,对表的操作)

1. SQL的特点 具有综合统一性&#xff0c;不同数据库的支持的SQL稍有不同 非过程化语言 语言简捷&#xff0c;用户容易接受 以一种语法结构提供两种使用方式 2. 对数据库的常用操作 功能 SQL 查看所有的数据库 show databases&#xff1b; 创建数据库 create databa…

设备驱动模型:总线-设备-驱动

1 设备驱动模型简介 参考 以下内容&#xff1a; Linux 笔记&#xff1a; https://xuesong.blog.csdn.net/article/details/109522945?spm1001.2014.3001.5502正点原子-左盟主 驱动开发网络资料&#xff1a;https://www.cnblogs.com/lizhuming/category/1859545.html 1.1 概…

Github的加速访问

文章目录 概述Steam的下载Steam的安装使用 概述 GitHub打开访问速度比较慢&#xff0c;这儿介绍一种加速访问的方式&#xff0c;是正规的方式&#xff0c;采用 Steam 来加速。 Steam的下载 浏览器输入框输入Watt Toolkit进行搜索&#xff0c; 选择官网进入&#xff0c;网址 …

蚂蚁安全科技 Nydus 与 Dragonfly 镜像加速实践 | 龙蜥技术

编者按&#xff1a;本文详细介绍蚂蚁安全科技使用龙蜥社区技术进行镜像加速的实践过程&#xff0c;可以让您了解如何基于龙蜥社区推出的容器镜像&#xff0c;Nydus 与 Dragonfly 镜像加速技术和 LifseaOS 为容器的启动加速。文章转自金融级分布式架构&#xff0c;以下为全文。 …

计算材料学有哪些SCI期刊推荐? - 易智编译EaseEditing

以下是一些计算材料学领域的SCI期刊推荐&#xff1a; Computational Materials Science&#xff1a; 该期刊发表计算材料科学的理论、计算和实验研究&#xff0c;包括材料结构、热力学、物理和化学性质以及材料的设计、制备和性能等方面的内容。 Materials Horizons&#xff1…

JavaWeb——HTML中的常用标签详解

目录 一、HTML 1、HTML标签结构 2、HTML文件结构 &#xff08;1&#xff09;、定义 &#xff08;2&#xff09;、标签层次结构 二、HTML常见标签 1、注释标签 2、标题标签 3、段落标签 4、换行标签 5、格式化标签 6、图片标签 &#xff08;1&#xff09;、定义 &a…

4。计算机组成原理(3)指令系统

嵌入式软件开发&#xff0c;非科班专业必须掌握的基本计算机知识 核心知识点&#xff1a;数据表示和运算、存储系统、指令系统、总线系统、中央处理器、输入输出系统 指令系统&#xff08;Instruction Set&#xff09;是计算机体系结构的关键组成部分之一&#xff0c;它定义了处…

Java面试题复习(1)

目录 1.mysql使用innodb引擎&#xff0c;请简述mysql索引的最左前缀&#xff0c;如何优化order by语句 2.在JVM内存模型中&#xff0c;为什么要区分新生去和老年代&#xff0c;对于新生代为什么要区分eden区和survial区&#xff1f; 3.常见的远程调用有几种 4.对于外部衔接的…

ChatGPT总是答非所问?如何使用chatgpt定义角色

一、&#x1f4dd; 定义角色&#xff1a;ChatGPT 的角色设定 背景信息&#xff1a;提供详细、准确的背景信息和前提条件&#xff0c;以便 ChatGPT 提供有针对性的回答和建议 任务目标&#xff1a;清晰地描述希望 ChatGPT 完成的任务 输出要求&#xff1a;告知 ChatGPT 如何完…

chatgpt应用市场

简介:利用ChatGPT的模型能力可以开发出垂直领域的应用市场。例如,可以使用ChatGPT来构建一个智能医疗助手,该助手可以回答患者关于疾病、症状、治疗方案等方面的问题。另外,也可以使用ChatGPT来构建一个智能金融助手,该助手可以回答用户关于投资、理财、贷款等方面的问题。…

MySQL的两个原则,两个优化,一个bug

背景&#xff1a;因为间隙锁在可重复读隔离级别下才有效&#xff0c;所以本篇文章接下来的描述&#xff0c;若没有特殊说明&#xff0c;默认 是可重复读隔离级别。 1.加锁规则里面&#xff0c;包含了两个“原则”、两个“优化”和一个“bug”。 (1). 原则1:加锁的基本单位是n…

家用洗地机有什么推荐的吗?家用洗地机分享

洗地机是解决清洁问题的智能设备。多年的市场验证已证明&#xff0c;它比传统清洁方法更加经济、高效和环保。相比传统方式&#xff0c;洗地机可以以更小的成本清洁更大的空间。同时&#xff0c;它的清洁效果也更好&#xff0c;采用先进的技术和设备&#xff0c;包括高压喷洒、…