ChatGPT API 활용하기 [Spring boot]

이영재·2024년 4월 7일
1

Spring

목록 보기
2/15

개요

ChatGPT를 사용하여 이미지 분석을 요청하는 서비스를 개발하려고 한다.

소개할 내용

  1. Spring boot ChatGPT API를 호출하는 방법
  2. base64로 인코딩된 문자열 데이터를 GPT에게 분석 요청
  3. Dto 구조 설계

주요 개념(텍스트 생성 모델)

GPT-4 및 GPT-3.5와 같은 OpenAI의 텍스트 생성 모델은 자연어와 형식 언어를 이해하도록 훈련. GPT-4와 같은 모델은 입력에 대한 응답으로 텍스트 출력을 허용. 프롬프트 디자인은 본질적으로 GPT-4와 같은 모델을 "프로그래밍"하는 방법으로, 일반적으로 작업을 성공적으로 완료하는 방법에 대한 지침이나 몇 가지 예를 제공. GPT-4와 같은 모델은 콘텐츠 또는 코드 생성, 요약, 대화, 창의적 글쓰기 등을 포함한 매우 다양한 작업에 사용.

1. ChatGPT

ChatGPT란?

  • ChatGPT는 인공지능 기술을 기반으로 한 대화형 컴퓨터 프로그램
  • 데이터를 학습하여 언어의 구조와 패턴을 이해하고, 사용자의 질문이나 명령에 맞는 응답을 생성

OpenAPI 의 모델

모델개념
GPT-3현재까지 가장 큰 규모의 GPT 모델. 매우 다양한 언어 작업을 수행할 수 있으며, 명시적인 학습 없이도 특정 작업을 수행할 수 있는 능력 보유.
GPT-4(Vision)GPT-3의 후속 모델. 텍스트 예측과 RLHF로 훈련됨. 텍스트와 이미지를 입력으로 받음.
DALL·E자연어 프롬프트를 기반으로 이미지를 생성하고 편집할 수 있는 모델
Whisper오디오를 텍스트로 변환할 수 있는 모델

사용할 모델은 GPT-4(Vision) - 유료

2. OpenAI Api

OpenAI Api에서 제공해 주는 여러가지 api 기능은 공식 문서를 참고
공식 문서

내가 사용할 기능은 입력 이미지가 주어지면 모델은 이미지를 분석하고 새로운 텍스트를 생성

Chat

  • 대화를 구성하는 메시지 목록이 주어지면 모델은 응답을 반환
분류상세 분류HTTP MethodEndpoint설명
Chat (채팅)Create chat completionPOSThttps://api.openai.com/v1/chat/completions지정된 채팅 대화에 대한 모델 응답을 생성

3. ChatGPT 개발 환경 세팅

1. ChatGPT와 통신을 하기 위해 OpenAI API Key를 발급

OpenAI API Key 발급

- OpenAI API Key를 헤더에 포함시켜 데이터를 전송한다.

2. GPT 에게 이미지 전달하는 방법

3. 라이브러리 추가

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-web'
   compileOnly 'org.projectlombok:lombok'
   annotationProcessor 'org.projectlombok:lombok'
   providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}

4. application.properties 구성

openai.secret-key= ****
openai.url.prompt=https://api.openai.com/v1/chat/completions

4. GPT Api 개발

폴더 구조

DTO

ContentDto

package gptdto.gptserviceV4.dto;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;

@Getter @Setter
@JsonInclude(JsonInclude.Include.NON_NULL)  // null 값이 아닌 필드만 JSON에 포함
public class ContentDto {
    private String type;
    private String text;

    @JsonProperty("image_url")  // JSON 필드명을 "image_url"로 지정
    private ImageURLDto imageUrl;  // 필드명을 카멜케이스로 변경

    // 텍스트 콘텐츠 전용 생성자
    public ContentDto(String type, String text) {
        this.type = type;
        this.text = text;
    }

    // 이미지 URL 콘텐츠 전용 생성자
    public ContentDto(String type, ImageURLDto imageUrl) {
        this.type = type;
        this.imageUrl = imageUrl;
    }
}

ImageAnalysisRequestDto

package gptdto.gptserviceV4.dto;

import lombok.Getter;
import lombok.Setter;
import java.util.List;

@Getter @Setter
public class ImageAnalysisRequestDto {
    private String model;
    private List<MessageDto> messages;

    public ImageAnalysisRequestDto(String model, List<MessageDto> messages) {
        this.model = model;
        this.messages = messages;
    }
}

ImageURLDto

package gptdto.gptserviceV4.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.Lob;
import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class ImageURLDto {
    @Lob @JsonProperty("url")
    private String url;
}

MessageDto

package gptdto.gptserviceV4.dto;


import lombok.Getter;
import lombok.Setter;

import java.util.List;

@Getter @Setter
public class MessageDto {
    private String role;
    private List<ContentDto> content;

    public MessageDto(String role, List<ContentDto> content) {
        this.role = role;
        this.content = content;
    }
}

Config

package gptdto.gptserviceV4.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ChatGPTConfig {
    @Value("${openai.secret-key}")
    private String secretKey;

    @Bean
    public RestTemplate restTemplate() {

        return new RestTemplate();
    }

    @Bean
    public HttpHeaders httpHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(secretKey);
        headers.setContentType(MediaType.APPLICATION_JSON);
        return headers;
    }
}

Service

package gptdto.gptserviceV4.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import gptdto.gptserviceV4.config.ChatGPTConfig;
import gptdto.gptserviceV4.dto.ContentDto;
import gptdto.gptserviceV4.dto.ImageAnalysisRequestDto;
import gptdto.gptserviceV4.dto.ImageURLDto;
import gptdto.gptserviceV4.dto.MessageDto;
import gptdto.gptserviceV4.service.ChatGPTService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@RequiredArgsConstructor
@Slf4j
public class ChatGPTServiceImpl implements ChatGPTService {
    @Value("${openai.url.prompt}")
    private String promptUrl;

    private final ChatGPTConfig chatGPTConfig;
    String question = "Please organize this receipt in Json format with the key value of the list name and the price as the value value.";
    String question1= "I want to analyze my spending history and know where I spent my money. Analysis tells you where, how, and how much you spent, in terms of price.";
    @Override
    public Map<String, Object> promptV2(ImageURLDto imageUrlDto) {

        Map<String, Object> resultMap = new HashMap<>();
        HttpHeaders headers = chatGPTConfig.httpHeaders();

        ContentDto imageContent = new ContentDto("image_url", imageUrlDto);
        ContentDto textContent = new ContentDto("text", question1);
        MessageDto message = new MessageDto("user", Arrays.asList(textContent, imageContent));


        ImageAnalysisRequestDto requestDto = new ImageAnalysisRequestDto("gpt-4-vision-preview", Collections.singletonList(message));
        // 요청 엔티티 생성
        return getStringObjectMap(resultMap, headers, requestDto);
    }

    private Map<String, Object> getStringObjectMap(Map<String, Object> resultMap, HttpHeaders headers, ImageAnalysisRequestDto requestDto) {
        HttpEntity<ImageAnalysisRequestDto> requestEntity = new HttpEntity<>(requestDto, headers);

        ResponseEntity<String> response = chatGPTConfig.restTemplate()
                .exchange(promptUrl, HttpMethod.POST, requestEntity, String.class);

        try {
            // GPT API 로부터 받은 JSON 형식의 응답 문자 파싱
            ObjectMapper om = new ObjectMapper();
            resultMap = om.readValue(response.getBody(), new TypeReference<>() {
            });
        } catch (JsonProcessingException e) {
            log.debug("JsonMappingException :: " + e.getMessage());
        } catch (RuntimeException e) {
            log.debug("RuntimeException :: " + e.getMessage());
        }
        return resultMap;
    }

    @Override
    public Map<String, Object> prompt(ImageAnalysisRequestDto imageAnalysisRequestDt) {

        Map<String, Object> resultMap = new HashMap<>();
        HttpHeaders headers = chatGPTConfig.httpHeaders();

        // 요청 엔티티 생성
        return getStringObjectMap(resultMap, headers, imageAnalysisRequestDt);
    }
}

Controller

package gptdto.gptserviceV4.controller;

import gptdto.gptserviceV4.dto.ImageAnalysisRequestDto;
import gptdto.gptserviceV4.dto.ImageURLDto;
import gptdto.gptserviceV4.service.ChatGPTService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping(value = "/api/v1/chatGpt")
@RequiredArgsConstructor
public class ChatGPTController {
    private final ChatGPTService chatGPTService;

    @PostMapping("/prompt")
    public ResponseEntity<Map<String, Object>> selectPrompt(@RequestBody ImageAnalysisRequestDto imageAnalysisRequestDto){
        Map<String, Object> result = chatGPTService.prompt(imageAnalysisRequestDto);
        return new ResponseEntity<>(result, HttpStatus.OK);
    }

    @PostMapping("/imagePrompt")
    public ResponseEntity<Map<String, Object>> imagePrompt(@RequestBody ImageURLDto imageURLDto){
        Map<String, Object> result = chatGPTService.promptV2(imageURLDto);
        return new ResponseEntity<>(result, HttpStatus.OK);
    }
}

5. 결과

postman으로 테스트

다음 사진과 같이 서버에 post 요청
body에 json 형식으로 "url":"base64 이미지 데이터..." 를 받고

응답 데이터로 사진을 분석한 결과가 content 안에 String 으로 잘 전달이 되었다.

0개의 댓글

관련 채용 정보