회사에서 챗봇에 AI연동해서 자연어 처리하자고 하셧다.
자연어처리라고해서 처음엔 너무 막막했음....
근데 다행히 챗gpt의 OpenAI에서 Assistants라는게 있다고해서
여기에 프롬프트랑 회사설명, 소개 파일만 텍스트로 잘 정리해서 올려두면(이건 내가하는게 아니니까 ㅎ)
반이상은 될거같아가지고 API 연동해보기로함!
우선 OpenAI의 API키값이 필요함.
이건 난 이미 발급받은 상태이고 어려운부분이 아니라고 생각되서 패스! 다른 블로그 참고 바랍니다.
Assistants에 대한 공식문서는 https://platform.openai.com/docs/assistants/overview 여기서 확인 부탁드립니다.
원래는 구조가 이렇다고 합니다.
1. Assistants 생성
2. Thread 생성
3. Message 보내기
4. Run
5. 답변 받아오기(Message)
(구조는)정말 정말 간단합니다!
예시코드도 https://platform.openai.com/docs/api-reference/assistants 여기 있는데
python, node.js밖에 없어요. 진짜 파이썬 공부해야되나..
그래서 예시코드 보면서 자바로 작성해봄!
1. Assistants 생성
원래라면 프롬프트를 셋팅하면서 Assistants를 만들어 줘야하는데 나는 Assistants 를 만들어 놓은 상태로 시작했습니다.
그래서 id만 들고왔어요
아마 Assistants연동하는 사람들은 프로젝트에서 안만들고 만들어서 셋팅 다 해두고 ID불러올거기 떄문에
Playground 들어가셔서 저기 빨간색 박스에 있는 ID값만 있으시면 됩니다.
2. Thread 생성
Thread를 우선 만들어줘야한다.
세션별로 Thread를 만들어도 되고 Thread를 만들어서 DB에 저정해둔 다음에 불러와서 써도 되고
방법은 여러가지 입니다.
전 프로젝트가 실행되어있을때 Thread를 생성하고 이걸 계속 쓰고 잇는데
솔직히 좋은 방법은 아니라서 수정예정임!
private ResponseEntity<String> createThread() {
String url = apiUrl + "/threads";
// Thread 생성
ThreadResponse response = template.postForObject(url, null, ThreadResponse.class);
if (response == null || response.getId() == null) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed 생성 실패");
}
this.threadId = response.getId();
return ResponseEntity.ok("Thread Created: " + threadId);
}
apiUrl : https://api.openai.com/v1 (환경변수에 저장하여 전역변수로 사용)
ThreadResponse : 아래 DTO 참고
import lombok.Data;
@Data
public class ThreadResponse {
private String id;
}
template : RestTemplate로 만들어진 탬플릿
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration //설정 클래스
public class OpenAiConfig {
//발급받은 API-KEY
@Value("${openai.api.key}")
private String openAiKey;
@Bean
public RestTemplate template(){
//RestTemplate은 스프링 프레임워크가 제공하는 HTTP 요청을 보낼 수 있는 유틸리티 클래스입니다.
RestTemplate restTemplate = new RestTemplate();
//Interceptor는 HTTP 요청 전/후에 추가 작업을 수행할 수 있따.
restTemplate.getInterceptors().add((request, body, execution) -> {
request.getHeaders().add("Authorization", "Bearer " + openAiKey);
request.getHeaders().add("OpenAI-Beta", "assistants=v2");
request.getHeaders().add("Content-Type", "application/json"); // 추가
return execution.execute(request, body);
});
return restTemplate;
}
}
3. Message 보내기
응답받은 threadId를 이용해 해당 thread에 사용자 메세지를 추가
저는 3,4,5를 합쳐놨습니다.
4. Run 메시지 실행
threadId를 이용해서 해당 스레드를 실행
5. 답변 받아오기(Message)
제 기준에서는 3,4,5번은 셋트라고 생각해서 한번에 정리해두겟습니다.
@GetMapping("/run")
public ResponseEntity<String> runAssistant(@RequestParam String content) {
// threadId가 없으면 스레드 생성
if (threadId == null || threadId.isEmpty()) {
createThread();// 새 스레드 ID 설정
}
// 메시지 추가
String messageUrl = apiUrl + "/threads/" + threadId + "/messages";
Message message = new Message("user", content);
template.postForObject(messageUrl, message, Void.class);
// Run 실행
String runUrl = apiUrl + "/threads/" + threadId + "/runs";
Map<String, String> runRequest = Collections.singletonMap("assistant_id", assistantId);
ChatGPTResponse response = template.postForObject(runUrl, runRequest, ChatGPTResponse.class);
while (response != null && (response.getStatus().equals("queued") || response.getStatus().equals("in_progress"))) {
// 응답 상태 및 응답을 확인하기 위해 5초 대기 후 요청
try {
Thread.sleep(5000); // 5초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
// assistant 응답 요청
response = getMessage(response);
// 응답 상태가 completed이면 반복문 종료
if(response.getStatus().equals("completed")) {
break;
}
}
String answer = response.getAssistantMessage();
return ResponseEntity.ok(answer);
}
코드설명
메시지 추가 3줄: 생성한 ThreadId로 메세지 전송 ※리턴값 없음! 처음에 이게 자꾸 null로 리턴이 돌아와서
왜 값이 안넘어오지 했는데 null로 넘어오는게 정상입니다!!
ChatGPTResponse: Run의 결과값을 받기 위한 DTO
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChatGPTResponse {
private String id;
private String status;
private List<MessageData> data;
private String assistantMessage;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class MessageData {
private String role;
private List<MessageDetail> content;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class MessageDetail {
private MessageContent text;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class MessageContent {
private String value;
}
}
while문: 아직 Assistant는 베타버전이라 속도가 느리다고해서 5초정도 텀을 주고 답변을 받아와야 합니다.
그렇게 안하면 답변이 하나씩 밀려요(무슨말인지 모르겠으면 빼고 값넣고 리턴받고 몇번 해보시길 바랍니다.)
상태 목록 | |
queued | 아직 실행이 되지 않고 대기중인 상태 |
in_progress | 처리중 |
requires_action | 사용자 입력 대기중 |
cancelling | 작업 취소중 |
cancelled | 작업 취소 완료 |
failed | 실패(오류) |
completed | 작업 완료 |
expired | 작업 만료 |
getMessage()함수 :
// assistant 응답 get 요청
public ChatGPTResponse getMessage(ChatGPTResponse response) {
// 스레드의 모든 메시지를 가져오는 API URL
String messageUrl = apiUrl + "/threads/" + threadId + "/messages";
try {
// RestTemplate을 사용하여 GET 요청 수행 및 응답 DTO로 변환
ChatGPTResponse messageResponse = template.getForObject(messageUrl, ChatGPTResponse.class);
if (messageResponse == null || messageResponse.getData() == null || messageResponse.getData().isEmpty()) {
response.setStatus("queued");
return response;
}
boolean foundAssistantResponse = false;
// 응답 데이터에서 assistant 메시지 검색
for (ChatGPTResponse.MessageData messageData : messageResponse.getData()) {
if ("assistant".equals(messageData.getRole())) {
for (ChatGPTResponse.MessageDetail detail : messageData.getContent()) {
String assistantMessage = detail.getText().getValue();
// 결과를 DTO에 설정
response.setAssistantMessage(assistantMessage);
response.setStatus("completed");
foundAssistantResponse = true;
}
break; // assistant 메시지를 찾으면 반복 종료
}
}
if (!foundAssistantResponse) {
response.setStatus("queued");
}
} catch (Exception e) {
e.printStackTrace();
response.setStatus("error");
}
return response;
}
메세지를 한번 더 실행해서 완료가 되었을때 assistant의 답변을 받아옴.
※ 참고) user : 내가 보낸거, assistant : 답변해준거
내가할껀 다 했으니까 이제 프롬프트 잘 작성해보자!
'JAVA' 카테고리의 다른 글
자바 스프링부트 2.7.7->3.3.9 업그레이드 (2) (0) | 2025.03.05 |
---|---|
자바 스프링부트 2.7.7->3.3.9 업그레이드 (1) (0) | 2025.02.27 |
자바 정규식 (0) | 2025.02.06 |
자바 URL 가져오기 (0) | 2024.08.19 |
Google Authenticator 구글 OTP 2차인증 (0) | 2024.07.30 |