springboot

[SpringBoot] GPT 3.5 AI 사용하기

inhooo00 2024. 3. 24. 01:56

생산형 AI 기능을 쓰고 싶은 개발자는 어디에나 있을 것이다. 오늘은 Spring에서 AI를 사용하는 법을 알아보자.
AI 기능을 빌려오는 느낌이고 직접 구현이 아니다. 참고 바란다.

 

1. 의존성 추가

    implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter:0.8.0-SNAPSHOT'
repositories {
    mavenCentral()
    maven { url 'https://repo.spring.io/milestone' }
    maven { url 'https://repo.spring.io/snapshot' }
}

해당 의존성은 Spring Boot 프로젝트에 OpenAI와 연동할 수 있는 기능을 제공한다.

 

2. OpenAI API 설정

https://platform.openai.com/usage

위 페이지에 들어가서 key를 발급받아야 한다.

 

API-Key 를 애플리케이션 단에 적용시킬 때 꼭 주의해야 되는 사항이 있다.
API-Key는 특정 사용자가 OpenAI 를 원활하게 사용하기 위해 인증받는 키로써, 절대 퍼블릭으로 유출되어선 안 된다. 예를 들어 깃허브 레포지토리에 공개로 절대 유출시키면 안되는 것이다.

최대한 보안 문제를 위해 본인 로컬에서만 사용하거나, 또는 레포지토리에 소스코드를 올린다면 .git ignore 로 yml 파일을 제외시켜 주자.

 

+ 추가로 빨간색으로 꽉 차 있는 상태면 쓸 수 없으니 결제를 해야 사용이 가능하다..... 아마 433인가 423인가 에러가 나오면서 진행이 불가할 것이다.

3. 코드 작성

 

- domain 코드

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class GPTResponse {
    @Id
    @Column(name = "response_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "gpt_question", columnDefinition = "TEXT")
    private String gptQuestion;

    @Column(name = "gpt_answer", columnDefinition = "TEXT")
    private String gptAnswer;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

}

질문과 답변 컬럼을 간단하게 만들었다.

 

- repository 코드

@Repository
public interface ResponseRepository extends JpaRepository<GPTResponse, Long> {

    List<GPTResponse> findAllByUserId(Long userId);
}

 

 

- service 코드

@Service
@RequiredArgsConstructor
public class GPTService {
    private final ChatClient chatClient;
    private final ResponseRepository responseRepository;
    private final UserRepository userRepository;

    private ChatResponse callChat(String question) {
        return chatClient.call(
                new Prompt(
                        (question + "위 아래 문단의 잡설은 하지 말고, 짧은 분량의 내용을 보여줘"),
                        OpenAiChatOptions.builder()
                                .withTemperature(0.4F)
                                .withFrequencyPenalty(0.7F)
                                .withModel("gpt-3.5-turbo")
                                .build()
                ));
    }

    //request answer to openai.
    public AiResDto getAnswer(Long userId, AiReqDto aiReqDto) {
        User user = userRepository.findById(userId).orElseThrow();
        ChatResponse response = callChat(aiReqDto.getQuestion());
        if (response == null) {
            response = callChat(aiReqDto.getQuestion());
        }
        GPTResponse gptResponse = GPTResponse.builder()
                .user(user)
                .gptQuestion(aiReqDto.getQuestion())
                .gptAnswer(response.getResult().getOutput().getContent())
                .build();
        responseRepository.save(gptResponse);


        return AiResDto.builder()
                .question(aiReqDto.getQuestion())
                .answer(response.getResult().getOutput().getContent()).build();
    }

    @Transactional(readOnly = true)
    public List<AiResDto> provideResponseList(Long userId) {
        try {
            List<GPTResponse> gptResponses = responseRepository.findAllByUserId(userId);
            List<AiResDto> aiResDtoList = new ArrayList<>();
            for (GPTResponse gptResponse : gptResponses) {
                aiResDtoList.add(AiResDto.builder()
                        .question(gptResponse.getGptQuestion())
                        .answer(gptResponse.getGptAnswer())
                        .build());
            }
            return aiResDtoList;
        } catch (Exception e) {
            throw new IllegalArgumentException("Not Found Response Data In Database.");
        }
    }
}

처음부터 천천히 읽어보면 어떻게 돌아가는지 대충 알 것이다.

간단하게 말하자면 callChat으로 질문에 대한 답을 받고, getAnswer로 반환Dto에 넣어주고, provideResponseList로 반환 값들을 보여주는 것이다.

 

 

 

 

- dto

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class AiReqDto {
    private String question;
}

 

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class AiResDto {
    private String question;
    private String answer;
}

 

 

 

 

정말 간단하지 않나??????