2025년 3월 5일 수요일

Using OpenAI's ChatGPT with Unity C# via API // Unity로 ChatGPT 챗봇 만들기 (API 이용)

# Unity C# 으로 구동하는 챗봇 코드
내가 쓸려고 간단히 만든 것을 기록용으로 공개한다.
아래 내용을 유니티에 붙여 넣는다.

1. 빈 gameobject 를 하나 생성한다.
2. new component 로 ChatbotManager 라는 스크립트를 생성해서 1에 붙인다.
3. 코드에디터로 아래 내용을 복사해서 2의 파일에 붙이고 저장한다.
4. 유니티 내 StreamingAssets 경로에 auth.json 을 생성한다.
 

    /// StreamingAssets 폴더의 auth.json 파일에서 API 키를 로드합니다.
    /// 예시 파일 형식: { "api_key": "your_api_key_here" }


5. 유니티로 돌아와서 플레이 해본다. log 창에 표시가 된다.

# 사용 법
StartNewConversation() : 새로운 대화 스레드 시작
SendUserMessage() : 내 대사 전달

# 메인 코드


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

[System.Serializable]
public class ChatMessage {
    public string role;
    public string content;
}

[System.Serializable]
public class ChatCompletionRequest {
    public string model;
    public List messages;
    public int max_tokens;
    public float temperature;
}

[System.Serializable]
public class ChatCompletionChoice {
    public ChatMessage message;
    public string finish_reason;
    public int index;
}

[System.Serializable]
public class ChatbotResponse {
    public string id;
    public string @object;
    public int created;
    public string model;
    public List choices;
}

[System.Serializable]
public class AuthData {
    public string api_key;
}

public class ChatbotManager : MonoBehaviour
{
    // Inspector에 노출되지 않도록 private const로 선언하여 잘못된 값이 덮어씌워지지 않게 합니다.
    private const string apiUrl = "https://api.openai.com/v1/chat/completions";
    public string model = "gpt-4o-mini";
    // public string model = "gpt-4o-mini"; // "o3-mini"; //"gpt-3.5-turbo"
    
    // 응답 속도 개선을 위한 추가 파라미터
    [Tooltip("응답 생성 시 최대 토큰 수를 제한합니다. 낮을수록 응답이 빨라집니다.")]
    public int maxTokens = 150;
    
    [Tooltip("응답의 창의성을 조절합니다. 0에 가까울수록 결정적이고 빠른 응답이 생성됩니다.")]
    [Range(0f, 1f)]
    public float temperature = 0.3f;
    
    private string apiKey;
    private List messages = new List();
    
    [TextArea(3, 10)]
    [Tooltip("챗봇에게 전달할 초기 시스템 인스트럭션입니다. 챗봇의 역할과 행동 방식을 정의합니다.")]
    public string initialInstruction = "안녕하세요, 챗봇과 대화를 시작합니다. 아래 instruction을 참고하세요.";

    [Tooltip("대화 기록에 유지할 최대 메시지 수입니다. 너무 많은 메시지는 응답 속도를 늦출 수 있습니다.")]
    public int maxMessageHistory = 10;
    
    [Tooltip("대화 기록을 저장하는 배열입니다. 인스펙터에서 확인할 수 있습니다.")]
    [SerializeField] private ChatMessage[] messageHistory;

    void Start()
    {
        LoadApiKey();
        StartNewConversation();
    }

    /// 
    /// StreamingAssets 폴더의 auth.json 파일에서 API 키를 로드합니다.
    /// 예시 파일 형식: { "api_key": "your_api_key_here" }
    /// 
    private void LoadApiKey()
    {
        string authFilePath = System.IO.Path.Combine(Application.streamingAssetsPath, "auth.json");

        if (System.IO.File.Exists(authFilePath))
        {
            try
            {
                string jsonContent = System.IO.File.ReadAllText(authFilePath);
                Debug.Log("로드된 auth.json 내용: " + jsonContent);

                if (!jsonContent.Trim().StartsWith("{") || !jsonContent.Trim().EndsWith("}"))
                {
                    Debug.LogError("auth.json 파일이 올바른 JSON 형식이 아닙니다. 올바른 형식: {\"api_key\":\"your_api_key_here\"}");
                    return;
                }

                AuthData authData = JsonUtility.FromJson(jsonContent);
                if (authData != null && !string.IsNullOrEmpty(authData.api_key))
                {
                    apiKey = authData.api_key;
                    Debug.Log("API 키가 성공적으로 로드되었습니다.");
                }
                else
                {
                    Debug.LogError("auth.json 파일에서 API 키를 찾을 수 없습니다.");
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError("auth.json 파일 파싱 중 오류 발생: " + e.Message);
            }
        }
        else
        {
            Debug.LogError("auth.json 파일을 찾을 수 없습니다. 경로: " + authFilePath);
        }
    }

    /// 
    /// 새 대화를 시작합니다. 기존 대화 기록을 초기화하고 초기 instruction 메시지를 추가합니다.
    /// 
    public void StartNewConversation()
    {
        messages.Clear();

        ChatMessage systemMessage = new ChatMessage { role = "system", content = initialInstruction };
        messages.Add(systemMessage);
        
        // 메시지 배열 업데이트
        messageHistory = messages.ToArray();

        Debug.Log("새 대화가 시작되었습니다. 초기 instruction이 추가되었습니다.");
    }

    /// 
    /// 사용자 메시지를 추가하고 API로 전송합니다.
    /// 
    /// 사용자 입력 메시지
    public void SendUserMessage(string userMessageContent)
    {
        ChatMessage userMessage = new ChatMessage { role = "user", content = userMessageContent };
        messages.Add(userMessage);
        
        // 메시지 수가 제한을 초과하면 오래된 메시지 제거 (시스템 메시지는 유지)
        TrimMessageHistory();
        
        StartCoroutine(SendRequestCoroutine());
    }

    /// 
    /// 대화 기록이 너무 길어지지 않도록 오래된 메시지를 제거합니다.
    /// 
    private void TrimMessageHistory()
    {
        // 시스템 메시지를 제외한 메시지 수가 maxMessageHistory를 초과하면 오래된 메시지 제거
        if (messages.Count > maxMessageHistory + 1) // +1은 시스템 메시지 때문
        {
            // 시스템 메시지는 항상 인덱스 0에 있다고 가정
            int excessMessages = messages.Count - maxMessageHistory - 1;
            messages.RemoveRange(1, excessMessages); // 시스템 메시지 다음부터 제거
        }
        
        // 메시지 리스트를 배열로 변환하여 인스펙터에서 확인할 수 있게 함
        messageHistory = messages.ToArray();
    }

    /// 
    /// 대화 전체를 JSON으로 직렬화하여 API에 POST 요청을 보내고, 응답을 받아 처리합니다.
    /// 
    IEnumerator SendRequestCoroutine()
    {
        if (string.IsNullOrEmpty(apiKey))
        {
            Debug.LogError("API 키가 설정되지 않았습니다. API 요청을 보낼 수 없습니다.");
            yield break;
        }

        ChatCompletionRequest requestObj = new ChatCompletionRequest
        {
            model = model,
            messages = messages,
            max_tokens = maxTokens,
            temperature = temperature
        };
        string jsonData = JsonUtility.ToJson(requestObj);
        Debug.Log("요청 JSON: " + jsonData);

        // UnityWebRequest 생성 시 POST 메서드를 직접 지정합니다.
        UnityWebRequest request = new UnityWebRequest(apiUrl, "POST");
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
        request.uploadHandler = new UploadHandlerRaw(bodyRaw);
        request.downloadHandler = new DownloadHandlerBuffer();
        request.SetRequestHeader("Content-Type", "application/json");

        // API 키에서 불필요한 문자를 제거합니다.
        string sanitizedApiKey = apiKey.Trim().Replace("\n", "").Replace("\r", "").Replace("\t", "").Replace("\"", "");
        request.SetRequestHeader("Authorization", "Bearer " + sanitizedApiKey);

        Debug.Log("API URL: " + apiUrl);
        Debug.Log("요청 메서드: " + request.method);

        yield return request.SendWebRequest();

        Debug.Log("응답 상태 코드: " + request.responseCode);

        if (request.result == UnityWebRequest.Result.Success)
        {
            string responseText = request.downloadHandler.text;
            Debug.Log("응답 JSON: " + responseText);

            ChatbotResponse response = JsonUtility.FromJson(responseText);
            if (response != null && response.choices != null && response.choices.Count > 0)
            {
                ChatMessage assistantMessage = response.choices[0].message;
                messages.Add(assistantMessage);
                // 메시지 배열 업데이트
                messageHistory = messages.ToArray();
                Debug.Log("Assistant: " + assistantMessage.content);
            }
        }
        else
        {
            Debug.LogError("API 요청 오류: " + request.error);
            Debug.LogError("응답 내용: " + request.downloadHandler.text);
        }
    }
}



내가 도움 받은 만큼 다른 누군가에게 도움이 되기를. 끝.

댓글 2개:

  1. Integrating a chatbot into Unity with C# and an API is an interesting way to bring conversational AI into an interactive application. The example provides a straightforward workflow, from creating a GameObject and attaching a manager script to loading authentication details and sending user messages through the chatbot.

    For developers exploring AI-powered application development, a ChatGPT Developer Course can provide useful direction on working with conversational AI and API-based integrations. The same concepts can be adapted to different application environments where users need interactive AI features.

    답글삭제
  2. The project also demonstrates how generative AI can become part of a practical software project rather than remaining a standalone tool. Students looking for ideas to apply these concepts can explore Gen AI Projects for Final Year for inspiration around chatbot and other generative-AI applications.

    답글삭제