2024. 2. 1. 18:15ㆍSKKU DT
유니티에서 ChatGPT 가져오기
API key를 받아오기 전에 유니티 씬으로 UI를 만든다.
MyGPT 스크립트 생성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
public class MyGPT : MonoBehaviour
{
public InputField userInputField;
public Text responseText;
private readonly string openAIApiURL = "https://api.openai.com/v1/chat/completions";
private readonly string apiKey = "Your apiKey";
public void OnSubmit()
{
string userInput = userInputField.text;
if (!string.IsNullOrWhiteSpace(userInput))
{
StartCoroutine(SendRequestToChatGPT(userInput));
}
}
private IEnumerator SendRequestToChatGPT(string userInput)
{
var request = new UnityWebRequest(openAIApiURL, "POST");
string requestData = "{\"model\":\"gpt-3.5-turbo\",\"messages\":[{\"role\":\"system\",\"content\":\"You are a helpful assistant.\"},{\"role\":\"user\",\"content\":\"" + userInput + "\"}]}";
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(requestData);
request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Authorization", "Bearer " + apiKey);
yield return request.SendWebRequest();
if(request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError(request.error);
responseText.text = "Error: " + request.error;
}
else
{
string response = request.downloadHandler.text;
Debug.Log("Response: " + response);
ChatGPTResponse chatGPTResponse = JsonUtility.FromJson<ChatGPTResponse>(response);
string gptResponse = chatGPTResponse.choices[0].message.content;
responseText.text = gptResponse; //ChatGPT 응답을 UI에 표시
}
}
}
[System.Serializable]
public class ChatGPTResponse
{
public Choice[] choices;
}
[System.Serializable]
public class Choice
{
public Message message;
}
[System.Serializable]
public class Message
{
public string content;
}
컴포넌트 연결
버튼에 함수 매핑
참고로, ChatGPT의 api 응답 형식은 아래와 같다.
https://platform.openai.com/docs/overview
위 링크에 접속한다.
왼쪽 메뉴 중 [API keys]를 눌러 API key를 받아온다.
본인의 apiKey를 스크립트에 잘 입력하고 실행하면 GPT의 말을 볼 수 있다.
*openAI의 유료 결제가 필요하다.
SDK 다운로드
PackageManager에서 git URL로 패키지를 다운로드 받는다.
https://github.com/srcnalt/OpenAI-Unity.git 을 입력한다.
Samples도 다운로드 받는다.
Open API에 요청하려면 API 키와 조직 이름을 사용해야한다.
조직 이름은 아래의 페이지에서 볼 수 있다.
"C:\Users\User" 경로에 ".openai"라는 폴더를 하나 만든다.
거기에 auth.json 파일을 하나 만들고 아래와 같이 키를 넣은 후 저장한다.
유니티에서, 샘플을 받았다면 Project 창에 아래와 같은 경로를 찾을 수 있다. 여기서 ChatGPT Sample 씬을 열면 ChatGPT와 대화를 나눌 수 있다.
실제로 이 샘플 씬 안에 들어있는 스크립트를 보면 이미 아래와 같은 프롬프트가 있어서 성격이 설정되어있다. 해당 프롬프트를 바꾸면 내가 원하는 방식으로 대답을 이끌어낼 수 있다.
private string prompt = "Act as a random stranger in a chat room and reply to the questions. Don't break character. Don't ever mention that you are an AI model.";
친절한 어시스턴트 역할을 부여하면 같은 질문을 해도 말투와 정보 제공 방식이 달라진다. (어쩐지 갑자기 GPT치고는 처음보는 말투를 해서 조금 놀랐다...)
private string prompt = "You are a helpful assistant.";
Dalle 샘플 씬
Dalle도 마찬가지로 샘플 씬을 사용할 수 있다.
StreamResponse 샘플 씬
스토리를 작성해준다.
Whisper 샘플 씬
마이크로 입력된 소리를 텍스트로 보여준다. 신기한건 한국말로 말해도 영어로 번역돼서 나온다.
샘플 씬처럼 UI 구성하기
이 글의 맨 처음 만들었던 UI를 수정하여 완성도있는 UI를 만들어본다.
MyGPT 스크립트를 수정한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using OpenAI;
public class MyGPT : MonoBehaviour
{
public InputField userInputField;
public Text responseText;
public ScrollRect scroll;
public RectTransform messageContainer;
public GameObject sendMessagePrefab;
public GameObject receiveMessagePrefab;
private float height;
private OpenAIApi openai = new OpenAIApi();
private bool isSendingMessage = false;
private List<ChatMessage> message = new List<ChatMessage>();
private string prompt = "You are a helpful assistant";
private readonly string openAIApiURL = "https://api.openai.com/v1/chat/completions";
private readonly string apiKey = "Your apiKey";
private void Update()
{
Canvas.ForceUpdateCanvases();
scroll.verticalNormalizedPosition = 0;
}
public void OnSubmit()
{
string userInput = userInputField.text;
if (!string.IsNullOrWhiteSpace(userInput))
{
StartCoroutine(SendRequestToChatGPT(userInput));
}
}
private IEnumerator SendRequestToChatGPT(string userInput)
{
isSendingMessage = true;
var sentMessage = CreateMessage(sendMessagePrefab, userInput);
userInputField.text = "";
yield return null;
Canvas.ForceUpdateCanvases();
scroll.verticalNormalizedPosition = 0;
var request = new UnityWebRequest(openAIApiURL, "POST");
string requestData = "{\"model\":\"gpt-3.5-turbo\",\"messages\":[{\"role\":\"system\",\"content\":\"You are a helpful assistant.\"},{\"role\":\"user\",\"content\":\"" + userInput + "\"}]}";
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(requestData);
request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Authorization", "Bearer " + apiKey);
yield return request.SendWebRequest();
if(request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError(request.error);
responseText.text = "Error: " + request.error;
}
else
{
string response = request.downloadHandler.text;
Debug.Log("Response: " + response);
ChatGPTResponse chatGPTResponse = JsonUtility.FromJson<ChatGPTResponse>(response);
string gptResponse = chatGPTResponse.choices[0].message.content;
//responseText.text = gptResponse; //ChatGPT 응답을 UI에 표시
CreateMessage(receiveMessagePrefab, gptResponse);
Canvas.ForceUpdateCanvases();
scroll.verticalNormalizedPosition = 0;
}
}
private GameObject CreateMessage(GameObject prefab, string content)
{
GameObject messageObject = Instantiate(prefab, messageContainer);
Text messageText = messageObject.GetComponent<Text>();
messageText.text = content;
return messageObject;
}
}
[System.Serializable]
public class ChatGPTResponse
{
public Choice[] choices;
}
[System.Serializable]
public class Choice
{
public Message message;
}
[System.Serializable]
public class Message
{
public string content;
}
UI 구성 아래 사진 참고
SendChat 복사해서 ReceiveChat 만들기
객체 끌어다 채우기
프리팹 수치 맞춰준다. Pivot 값에 주의. Children으로 들어가있는 Image와 Text의 Pivot 값이 (X : 0, Y : 1)이다.
잘 설정 되었다면, 대화 형식으로 UI 결과가 잘 나온다.
참고로, 샘플 스크립트가 작동이 잘 된다.
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
namespace OpenAI
{
public class ChatGPT : MonoBehaviour
{
[SerializeField] private InputField inputField;
[SerializeField] private Button button;
[SerializeField] private ScrollRect scroll;
[SerializeField] private RectTransform sent;
[SerializeField] private RectTransform received;
private float height;
private OpenAIApi openai = new OpenAIApi();
private List<ChatMessage> messages = new List<ChatMessage>();
private string prompt = "You are a helpful assistant.";
private void Start()
{
button.onClick.AddListener(SendReply);
}
private void AppendMessage(ChatMessage message)
{
scroll.content.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0); //스크롤의 높이를 0으로 설정
var item = Instantiate(message.Role == "user" ? sent : received, scroll.content); //message의 Role이 "user"인 경우 sent 프리팹을, 그렇지 않은 경우 received 프리팹을 인스턴스화하여 item에 할당
item.GetChild(0).GetChild(0).GetComponent<Text>().text = message.Content; //item의 자식 중 첫 번째 자식의 첫 번째 자식의 Text 컴포넌트의 텍스트를 message의 내용으로 설정
item.anchoredPosition = new Vector2(0, -height); //item의 위치를 (0, -height)로 설정하여 스크롤 아래에 배치
LayoutRebuilder.ForceRebuildLayoutImmediate(item); //item의 레이아웃을 강제로 다시 빌드하여 UI를 업데이트
height += item.sizeDelta.y; //height에 item의 높이를 더하여 현재까지의 높이를 업데이트
scroll.content.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height); //스크롤의 높이를 현재까지의 높이로 설정
scroll.verticalNormalizedPosition = 0; //스크롤의 수직 위치를 맨 아래로 설정하여 최신 메시지가 보이도록 함
}
private async void SendReply()
{
var newMessage = new ChatMessage()
{
Role = "user",
Content = inputField.text
};
AppendMessage(newMessage);
if (messages.Count == 0) newMessage.Content = prompt + "\n" + inputField.text;
messages.Add(newMessage);
button.enabled = false;
inputField.text = "";
inputField.enabled = false;
// Complete the instruction
var completionResponse = await openai.CreateChatCompletion(new CreateChatCompletionRequest()
{
Model = "gpt-3.5-turbo-0613",
Messages = messages
});
if (completionResponse.Choices != null && completionResponse.Choices.Count > 0)
{
var message = completionResponse.Choices[0].Message;
message.Content = message.Content.Trim();
messages.Add(message);
AppendMessage(message);
}
else
{
Debug.LogWarning("No text was generated from this prompt.");
}
button.enabled = true;
inputField.enabled = true;
}
}
}
'SKKU DT' 카테고리의 다른 글
[SKKU DT] 67일차 -유니티 Rest API 활용하기(날씨, 정보, 사진) (1) | 2024.02.05 |
---|---|
[SKKU DT] 66일차 -유니티 UI 활용하기 (0) | 2024.02.02 |
[SKKU DT] 63일차 -Stable Diffusion과 기능들 (2) | 2024.01.30 |
[SKKU DT] 62일차 -AI 알아보기, 기업 특강, 챗봇 생성하기, 블렌더 스크립트 생성 (0) | 2024.01.29 |
[SKKU DT] 61일차 -Azure Digital Twin 실습(3), Postman (0) | 2024.01.26 |