티스토리 뷰

 

 

이전에 GPT 웹사이트에서 Assistants(챗봇)를 구축하는 방식에 대해 알아봤었다. 

 

이게... 당시에는 API가 없다고 했는데, Beta로 나와 있었다.

(API reference는 주기적으로 열어보는데, Document와 Beta에 있어서 못찾았다)

 

근데... 이게 뭐가 하고싶은지는 알겠는데 구조가 조금 복잡하다.

 

How Assistants work?

 

각각의 구성요소에 대해서는 나중에 소개하겠지만,

 

일단 OpenAI 쪽에서 하고 싶은건 Thread라는 저장소를 사용자별로 만들어 주고 싶었던 것 같다.

 

그래서 그런지 Assistant 밑에 Thread들이 있지 않다.

 

구조가 그림만봐도 어려운데, 일단은 세부요소 소개와 구현을 해볼 생각이다.

 

Assistants API란?

어시스턴트 API를 사용하면 자체 애플리케이션 내에서 AI 어시스턴트를 구축할 수 있습니다. 어시스턴트에는 지침이 있으며 모델, 도구 및 지식을 활용하여 사용자 쿼리에 응답할 수 있습니다. 어시스턴트 API는 현재 세 가지 유형의 도구를 지원합니다: Code Interpreter, Retrieval, Function calling 입니다.

 

결국 내 어플리케이션 내에 챗봇을 만들 수 있는 환경을 API를 통해 만들어주겠다는 의미이다.

 

사실 단순 챗봇은 프롬프트로 가능하다.

 

그러나 내 서비스에 맞춰서 챗봇을 만들려면,

 

한번 질문이 오고갈 때마다 사전 지식으로 너무 거대한 프롬프트를 줘야해서 디폴트로 사용하는 토큰 수가 늘어난다.

 

그렇게 되면 대답이 중간에 짤리거나 양질의 대답을 얻기 어렵다는 문제가 있었다.

 

이 부분을 Assistants API가 많이 해결해줬다.

 

차근차근 구현해보자.

 

구현 파이프라인

아래 링크를 따라 진행해 볼 예정이다.

https://platform.openai.com/docs/assistants/overview

그리고 기본적인 설정은 어느정도 되어있다고 생각하고 작업할 예정이다.

 

나는 OpenFeign를 기본으로 사용하고, 코틀린+스프링부트로 구현했다.

(코틀린 연습을 해야한다....)

 

시작하기 전에... 

현재 서비스는 Assistants API는 베타 딱지가 붙어있다.

 

그래서 헤더에 베타 딱지를 붙여서 보내줘야한다.

class AssistantHeaderConfiguration {
    @Bean
    fun requestInterceptor(): RequestInterceptor {
        val dotenv = Dotenv.load()
        return RequestInterceptor { requestTemplate: RequestTemplate ->
            requestTemplate.header("Authorization", "Bearer ${dotenv["OPENAI_API_KEY"]}")
            requestTemplate.header("OpenAI-Beta", "assistants=v1")
        }
    }
}

dotenv는 open AI 키를 받아오기 위해서 사용했다.

 

dot env에 대한 내용은 직전 글을 확인

 

그리고 파이프라인을 제대로 진행하기 위해 Assistants API의 각 구성요소에 대해 알아야한다.

 

Assistants API는 Assistant, Thread, Message, Run으로 구성되어있다.

 

Assistant는 GPT에게 질문을 던졌을 때, 답변을 해주기 위한 기반 지식들을 세팅한다.

Thread는 저장소라고 생각하는게 편할 것 같다.

Message는 사용자가 보낸 질문이다.

Run은 사용자의 질문을 Assistant에 보내고 Assistant가 만든 응답과 함께 Thread에 저장한다.

 

파이프라인에 대해 알아보면, 

 

1. Assistant를 생성 

2. Thread 생성

3. Message 생성

4. Run 생성 

5. Message 확인(응답이 생성되었으면 List에 답변이 추가됨)

 

위와 같이 구성된다.

 

이제 구현으로 들어가보자.

 

구현

전체 코드는 제일 하단 GitHub의 확인

1. Assistant를 만들기

https://platform.openai.com/docs/api-reference/assistants/createAssistant

 

client

@PostMapping("/assistants")
fun createAssistants(@RequestBody assistantsRequestDto: AssistantsRequestDto): AssistantResponseDto?

생성자는 service에서 어떻게 구성했나 확인할 수 있고, Response는 그냥 Any로 받아도 된다.

 

데이터를 핸들링하고 싶으면, 객체를 하나 만들어주자.

 

service

fun createAssistant(assistantCreateRequestDto: AssistantCreateRequestDto): ResponseEntity<Any> {
    return try {
        val res = assistantsClient.createAssistants(
                AssistantsRequestDto(
                        model = assistantModel,
                        name = assistantCreateRequestDto.name,
                        instructions = assistantCreateRequestDto.instruction,
                )
        )
        ResponseEntity.ok(res)
    } catch (e: Exception) {
        ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
    }
}

파라미터로  tools는 따로 사용하지 않았는데, tools엔 code_interpreter, retrieval, function이 있다.

 

그리고 retrieval용으로 file을 가져올 수 있는데, 업로드한 file_ids를 파라미터로 넘기면 된다.

 

2. Thread 생성

요청/응답 객체와 별도의 서비스 로직은 GitHub에서 확인하자. 반복되는 코드라 봐도 영양가가 없다.

https://platform.openai.com/docs/assistants/overview/step-2-create-a-thread

@PostMapping("/threads")
fun createThreads(): ThreadsResponseDto?

앞에서도 언급했지만, Thread와 Assistant는 연결되는 게 없다.

 

그리고 놀랍게도 ThreadId를 분실하면, 생성한 Thread를 찾는 방법이 없다.

 

때문에 반드시 생성 후 ThreadId를 저장해두자.

 

3. Message 생성/조회

@PostMapping("/threads/{threadId}/messages")
fun createMessages(@PathVariable threadId: String, @RequestBody messagesRequestDto: MessagesRequestDto): MessagesResponseDto?

@GetMapping("/threads/{threadId}/messages")
fun getMessagesList(@PathVariable threadId: String): MessagesListResponseDto?

 

message를 생성할 때는 pathparameter로 threadId를 사용한다.

 

그리고 응답이 잘 생성됐나 확인하기 위해서, 메시지 리스트도 조회한다.

4. Run 생성

@PostMapping("/threads/{threadId}/runs")
fun createRuns(@PathVariable threadId: String, @RequestBody runsRequestDto: RunsRequestDto): RunsResponseDto?

Runs에서는 Assistants의 정보를 수정할 수 있다. threadId, messageId를 둘 다 파라미터로 받는다.

 

문제는 Run을 한 이후에 Message가 바로 생성되지 않는다.

(빈 문자열이 response로 날아옴)

 

그럴땐, Run을 retrieve를 해서 run의 상태가 completed인지 확인을 해야한다.

@GetMapping("/threads/{threadId}/runs/{runId}")
RunsResponseDto getRun(@PathVariable String threadId, @PathVariable String runId);

 

아래는 Run의 lifecycle인데, 생성하자마자 조회해보면 queued인걸 확인할 수 있다.

 

 

아마 실무에서는 Run의 상태를 파악하는 코드를 추가할 것 같다.

 

 

결과확인

openAI에 playground에서 Assistants가 잘 생성되었나 확인할 수 있는 부분들이 있다.

https://platform.openai.com/assistants

 

아래 순서대로 결과를 확인할 것이다.

1. Assistant를 생성 
2. Thread 생성
3. Message 생성
4. Run 생성 
5. Message 확인(응답이 생성되었으면 List에 답변이 추가됨)

 

1. Assistant를 생성

Postman 요청 결과

 

Playground에 Assistants가 작성한대로 생성되어 있다.

2. Thread를 생성

thread id를 꼭 복사해두자!!

3. Message 생성

 

role을 system을 두고 프롬프트를 추가 작성할 수 도 있을 것 같은데 이 부분은 조금 더 사용해봐야 알 것 같다.

 

message가 등록되었으니 run을 실행해보자.

 

4. Run 실행

별다른 내용은 없다.&nbsp; 1702521936 밀리초이 만료시간이면 약 473시간 정도가 만료 시간이다.

 

5. message 확인

여기서는 list로 확인했는데, retrive로도 확인할 수 있다고 한다.

 

원하는 답변을 잘 생성한 것을 확인할 수 있다.

 

GitHub

코프링으로 구현되어있다.

https://github.com/imsosleepy/openai-api-repository

 

마치며

사용법이 조금 복잡하지만 잘 활용하면 앱, 대상에 맞춤형 챗봇을 운영할 수 있을 것 같다.

 

가장 중요한건 thread는 방역할을 한다는 것과 run을 해야 동작한다는 것 같다.

 

내용이 쪼끔 더 남았는데, 다음에 작성하도록 하겠다...

 

instructions에 대한 내용들인데 해당 내용들은 아직 beta라 성능이 애매하기도 해서, 작성하지 않을 수도 있다.

 

그리고 가격이 책정이 조금 애매한데, 이것도 자세히 알아봐야할 것 같다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함