내가 쓸려고 간단히 만든 것을 기록용으로 공개한다.
아래 내용을 유니티에 붙여 넣는다.
1. 빈 gameobject 를 하나 생성한다.
2. new component 로 ChatbotManager 라는 스크립트를 생성해서 1에 붙인다.
3. 코드에디터로 아래 내용을 복사해서 2의 파일에 붙이고 저장한다.
4. 유니티 내 StreamingAssets 경로에 auth.json 을 생성한다.
/// StreamingAssets 폴더의 auth.json 파일에서 API 키를 로드합니다.
/// 예시 파일 형식: { "api_key": "your_api_key_here" }
# 사용 법
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);
}
}
}
내가 도움 받은 만큼 다른 누군가에게 도움이 되기를.
끝.
댓글 없음:
댓글 쓰기