비즈니스에 생성형 AI를 활용하기 위한 이용한 RAG 테스트

2024-07-07
noah

개요

4차 산업혁명은 2016년부터 본격적으로 주목받기 시작했습니다. 2024년 현재, 4차 산업은 여러 분야에 빠르게 확산되고 있지만, 많은 일반인들은 여전히 그 기술 동향에 대해 궁금증을 가지고 있습니다. 그러나 2022년 11월 OpenAI가 출시한 생성형 AI 서비스 이후, AI 시장은 급격히 변화하고 있으며, 이는 산업 전반을 넘어 우리의 일상 속으로 깊이 스며들고 있습니다. 4차 산업혁명 중에서도 모든 이의 삶에 가장 빠르게 녹아들게 될 기술로 기대를 모으고 있습니다.

Sellease는 기술 기반 기업으로 성장하기 위해 이러한 변화에 민감하게 대응하며, 새로운 기술이 우리의 비즈니스에 어떻게 적용될 수 있을지를 연구하고, 기존 비즈니스와 조화롭게 통합하기 위해 지속적으로 노력하고 있습니다.

목적 및 목표

우리는 OpenAI의 서비스를 적극 활용하여 생성형 AI를 비즈니스에 접목시키고 사용자가 원하는 데이터를 쉽게 질의할 수 있도록 하는 것을 목표로 하고 있습니다. 향후에는 멀티 모달을 지원하는 모델을 통해 모든 형태의 미디어로 질의 결과를 받을 수 있을 것으로 기대합니다.

이를 위해 RAG 시스템을 이해하는 것이 필수적입니다.

RAG란 무엇이고 왜 필요한가?

생성형 AI는 빠르게 발전하고 있지만 여전히 여러 문제를 안고 있습니다. 그 중 가장 큰 문제는 할루시네이션(hallucination)으로, 이는 AI가 존재하지 않거나 잘못된 정보를 생성하는 현상을 말합니다. 할루시네이션은 LLM 모델의 특성과 훈련 데이터의 부족, 맥락 이해의 부족 등 여러 이유로 발생합니다. 이로 인해 초기 생성형 AI의 신뢰성은 낮았으며, 신뢰성이 중요한 기업 프로세스에 도입하기 어려웠습니다.

RAG(Retrieval-Augmented Generation)는 생성형 AI가 보다 정확한 응답을 생성할 수 있도록 외부 정보를 검색하여 보완하는 방법으로, 다음과 같은 장점을 기대할 수 있습니다:

  1. 다양한 데이터 소스에서 실시간으로 정보를 검색하여 정확한 응답 생성
  2. 지속적인 데이터 업데이트를 통해 최신 정보 제공

이번 PoC의 목표는 RAG를 통해 생성형 AI 응답의 신뢰성을 높이고, 더 많은 인공지능 기술을 비즈니스에 적용할 가능성을 검증하는 것입니다.

기술 스택

  • 웹 애플리케이션: Java/SpringBoot
  • 생성형 AI: OpenAI API (Assistant API beta v2)
  • OpenAI 플랫폼: gpt-4o 모델 사용
  • RAG 구현 방법

  • Vector Store를 통한 정보 검색
  • Function calling을 통한 사내 서비스 접근
  • LLM 모델 연계 API
  • 시스템 아키텍처

    아래 시스템 아키텍처는 모범 사례로 작성된 것으로, 실제 테스트 과정에서는 간단히 구현되었습니다.

    모듈

    • Platform Sellease IO: 현재 운영 중인 셀리즈의 백엔드 API 서비스
      • 역할: 인증 및 사용자 세션 처리, 회사 및 통계 데이터 업데이트
    • Persist Data: MySQL DBMS, 사용자별 OpenAI의 Context 정보를 저장
    • Sellease Assistant: OpenAI와 통신하는 주체, OpenAI의 Assistant API beta2를 이용한 생성형 AI 서비스

    RAG

    구현

  • RAG: 벡터 스토어에 데이터를 업로드하고 검색 기능 활성화
  • // 업로드
    String resourceName = "sample-data/company-info.md";
    
    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(Objects.requireNonNull(classLoader.getResource(resourceName)).getFile());
    var uploadedFile = openAI.files()
                              .create(FileRequest.builder()
                                                .file(file.toPath())
                                                .purpose(PurposeType.ASSISTANTS)
                                                .build())
                              .join();
    String fileId = uploadedFile.getId();
    System.out.println("File was created with id: " + fileId); // file-OIfrC1nJLgfjz4GyS3P3j3tJ
    System.out.println();
    
    var vectorStore = openAI.vectorStores()
                            .createAndPoll(VectorStoreRequest.builder()
                                                              .fileId("file-OIfrC1nJLgfjz4GyS3P3j3tJ")
                                                              .name("company-info")
                                                              .build());
    
    String vectorStoreId = vectorStore.getId();
    System.out.println("Vector Store was created with id: " + vectorStoreId); // vs_3fkPydWq3HnDkhxAKDqkopVF

    결과값

  • Function Calling: 특정 함수 정의 및 호출을 통한 서비스 제공
  • // 함수의 정의 및 등록
    var functionExecutor = new FunctionExecutor();
    functionExecutor.enrollFunction(
        FunctionDef.builder()
                    .name("get_stats_of_app_usage")
                    .description("Return the usage statistics for the requested department and app.")
                    .functionalClass(StatsOfAppUsage.class)
                    .build());
    
    // 합수의 구현
    public static class StatsOfAppUsageByCategory implements Functional {
    
      @JsonPropertyDescription("Search start date")
      @JsonProperty(required = true)
      public String since;
    
      @JsonPropertyDescription("Search end date")
      @JsonProperty(required = true)
      public String until;
    
      @JsonPropertyDescription("search category names, for example: Document, Productivity, CRM ...etc (Optional)")
      public Set<String> categories;
    
      @Override
      public Object execute() {
        log.info("since: {}, until: {}, categories: {}", since, until, ArrayUtils.toString(categories));
        try {
          HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
          Me me = (Me) request.getAttribute(ME);
          if (me == null) {
            return "로그인이 필요합니다.";
          }
          List<AppUsageByCategoryItem> insight = INSIGHT_SERVICE.getAppUsageByCategories(
              1L, // me.getCompany().getId()
              DateRange.builder().since(LocalDate.parse(since)).until(LocalDate.parse(until)).build());
          return Map.of("data", insight, "dashboardUrl",
                        "https://dev.sellease.io/insight/usages/apps-by-categories?since=%s&until=%s".formatted(since, until));
        } catch (Exception e) {
          return "처리 중 오류가 발생했습니다.";
        }
      }
    }
    
    // 함수의 호출
    var messages = new ArrayList<ChatMessage>();
    messages.add(UserMessage.of("최근 일주일간 문서 카테고리의 사용 통계를 알려줘"));
    var chatRequest = ChatRequest.builder()
                                  .model("gpt-4o")
                                  .messages(messages)
                                  .tools(functionExecutor.getToolFunctions())
                                  .build();
    var futureChat = openAI.chatCompletions().create(chatRequest);
    var chatResponse = futureChat.join();
    var chatMessage = chatResponse.firstMessage();
    var chatToolCall = chatMessage.getToolCalls().get(0);
    var result = functionExecutor.execute(chatToolCall.getFunction());
    messages.add(chatMessage);
    messages.add(ToolMessage.of(result.toString(), chatToolCall.getId()));
    chatRequest = ChatRequest.builder()
                              .model("gpt-4o")
                              .messages(messages)
                              .tools(functionExecutor.getToolFunctions())
                              .build();
    futureChat = openAI.chatCompletions().create(chatRequest);
    chatResponse = futureChat.join();
    System.out.println(chatResponse.firstContent());

    결과값

    결론 및 요약

    PoC의 주요 발견 사항:

    • RAG 시스템을 통해 생성형 AI 응답의 신뢰성을 높일 수 있었습니다.
    • 채팅을 통한 데이터 액세스는 매우 효과적인 인터페이스임을 확인했습니다.

    향후 계획:

    • 데이터 이슈로 인해 OpenAI 대신 로컬 LLM을 이용한 추가 리서치 필요
    • OpenAI Assistant API의 비용 문제 해결 방안 모색

    Sellease는 앞으로도 지속적인 연구와 개발을 통해 인공지능 기술을 비즈니스에 성공적으로 접목시킬 것입니다.

    셀리즈 서비스가 궁금하다면?