반응형

사이드 프로젝트를 위해 chatgpt api를 사용해 보던 중 최근 신기한 기능을 찾아서 간단히 요약해본다.

 

gpt에게 어떤 '상황'을 부여하고, 문맥을 기억할 수 있게 하는 assistant api가 새로 생겼다.

이를 통해 chatgpt를 프롬프트 엔지니어링을 통해 자신이 원하는대로 커스텀 가능하게 되었는데 

국내엔 관련 자료가 많지는 않은거 같아서 기록겸 요약해본다.

 

gpt store에 올리는 custom gpt가 이 assistant와 비슷한 개념일듯?

 

사전 지식

 

chatgpt에게 api로 명령을 보낼때는 message role로 "user", "assistant", "system"을 보내어 원하는 명령을 수행시킨다.

 

'user'는 유저가 입력한 명령을 의미한다.

'system'은 chatgpt에게 입력할 제약 사항, 요구 사항 등을 의미한다.

'assistant'는 명령을 수행하기 위한 앞뒤 문맥, 사전 지식등을 뜻한다.

 

api example)

curl https://api.openai.com/v1/chat/completions \
 -H "Authorization: Bearer $OPENAI_API_KEY" \
 -H "Content-Type: application/json" \
 -d '{
 "model": "gpt-3.5-turbo",
 "messages": [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Who won the world series in 2020?"},
    {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
    {"role": "user", "content": "Where was it played?"}
    ]
 }'

 

 

기존 api는 독립적으로 작동하기 때문에 앞뒤 문맥을 파악하지 못했다.

이를 위해서 assistant role로 이전의 대화 내용을 전부 보내주거나 요약해줘야 했고, 이는 api의 과다 사용 = 비용의 상승으로 이어졌다. 

 

assistant api는 이 문제를 해결하기 위해서 미리 학습시킨 system, assistant 값을 이용해 유저의 api 명령을 처리한다.

이 api를 사용하기 위해서 몇가지의 개념 학습이 더 필요하다.

 

https://platform.openai.com/docs/assistants/how-it-works/objects

 

1. Assistant

명령을 받아 처리하는 chatgpt 자체를 뜻한다.

role system, assistant 들로 사전 입력을 할 수 있다.

이를 위해서 assistant는 text 파일 같은 file 입력도 가능하며 model 의 튜닝도 가능하다. 

 

2. Thread 

명령이 이루어지는 "context", 대화 맥락을 뜻한다.

채팅방이라고 생각하면 될 듯하다.

Thread간의 context는 독립적이다. 이 말은 즉, 카카오톡 1:1 채팅방처럼 여러 thread가 있을 수 있다.

 

3. Message

기존의 chatgpt api message와 동일하다. 

assistant api에선 thread 단위로 message간의 대화 맥락, context를 파악해서 ai가 대화를 이어나가준다.

 

4. RUN

run의 결과값

message를 입력한 이후의 결과 객체이다. 

코드 레벨에서 의미 있는 객체인데 뭔 역할인지 코드 예시에서 후술함

 

 

여기서 api를 사용하기 위해서 assistant, thread, message를 어떻게 코드 레벨에서 동작시키는지 좀 혼란을 겪었는데 사이드 프로젝트의 코드 예시로 설명하겠다.

 

개인적으로는 상당히 복잡하고, chat gpt한테 보낸 명령이 완료되었는지 확인하기 위해서

pooling을 써야하는 부분이 있어서 비동기 처리 측면에서 상당히 헷갈렸던듯..?

 

지금 next14에서 실험중이니 next 코드로 설명함 

전체 코드는 아래와 같다.

/* eslint-disable no-await-in-loop */
import { NextRequest, NextResponse } from 'next/server'
import { OpenAI } from 'openai'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})

async function handler(req, res) {
  try {
    const body = await req.json()
    
    const assistant = await openai.beta.assistants.retrieve(' assistant api id 추가! ') // TO DO - tutor 별로 assistant 생성
	const thread = await openai.beta.threads.retrieve('thread api id 추가 !') // TO DO - 유저 대화창별로 thread 생성

    const { excelJSON, prompt } = body

    const message = await openai.beta.threads.messages.create(thread.id, {
      role: 'user',
      content: prompt,
    })

    const run = await openai.beta.threads.runs.create(thread.id, {
      assistant_id: assistant.id,
    })

    let cnt = 0

    // TO DO - polling logic 업그레이드
    while (cnt < 1000) {
      const { status } = await openai.beta.threads.runs.retrieve(thread.id, run.id)

      if (status === 'completed') break
      await new Promise((resolve) => {
        setTimeout(() => {
          resolve(1)
        }, 500)
      })
      cnt += 10
    }

    const messages = await openai.beta.threads.messages.list(thread.id)
    // @ts-ignore
    const responseText = messages.data[0].content[0].text.value
    console.log(JSON.stringify(responseText), responseText)
	
	... 생략(뒤는 중요하지 않은 파트임)
}

export { handler as POST }

 

 

1. assistant와 thread 불러오기

 

/* eslint-disable no-await-in-loop */
import { NextRequest, NextResponse } from 'next/server'
import { OpenAI } from 'openai'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})

async function handler(req: NextRequest, res: any) {
  try {
    const body = await req.json()
    
    const assistant = await openai.beta.assistants.retrieve(' assistant api id 추가! ') // TO DO - tutor 별로 assistant 생성
	const thread = await openai.beta.threads.retrieve('thread api id 추가 !') // TO DO - 유저 대화창별로 thread 생성

    const { excelJSON, prompt } = body

 

앞 부분은 미리 생성된 assistant를 불러오고, 기존에 채팅방(thread)를 불러오는 api이다.

어시스턴트와 thread는 코드 레벨에서도 구현이 가능하고 혹은 playground에서도 생성이 가능하다.

 

숨김 처리한 부분이 각각 assistant id, thread id이다.

 

2. message 전달 후 chatgpt 실행

    const message = await openai.beta.threads.messages.create(thread.id, {
      role: 'user',
      content: prompt,
    })

    const run = await openai.beta.threads.runs.create(thread.id, {
      assistant_id: assistant.id,
    })

  // TO DO - polling logic 업그레이드
    while (cnt < 1000) {
      const { status } = await openai.beta.threads.runs.retrieve(thread.id, run.id)

      if (status === 'completed') break
      await new Promise((resolve) => {
        setTimeout(() => {
          resolve(1)
        }, 500)
      })
      cnt += 10
    }

 

message를 생성하고 chatgpt에게 실행시킨다. 

만약 message 결과값을 확인하고 싶다면 pooling api를 통해 대화가 끝났는지 체크해줘야 한다.. 

run의 결과는 아까 보았던 completed, failed, canceled 등등이 있다.

 

    const messages = await openai.beta.threads.messages.list(thread.id)
    // @ts-ignore
    const responseText = messages.data[0].content[0].text.value
    console.log(JSON.stringify(responseText), responseText)

 

message의 결과값은 커서 페이지네이션의 형태로 message들을 최신순 20개를 받아오는듯하다. 

아직 안써봤지만 message id를 통해 페이지네이션을 조작할 수 있을것이라고 추측 된다.

 

 

아직 베타 버전이라 많은 기능은 없지만

chatgpt의 발전 속도가 매우 무섭다..

 

월급 들어오면

마소 주식이나 좀 사야지 가즈아!!

 

 

reference 

 

https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps?lang=curl

 

 

반응형

'인공지능' 카테고리의 다른 글

resnet->densenet  (0) 2022.03.08

+ Recent posts