데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      TAG를 활용한 실용적 AI 서비스 구축하기

      broccoli 24.11.27
      6,406 4 4
      DEVOTEE 요약
      TAG(Table-Augmented Generation)은 구조화된 데이터와 언어 모델을 결합하여 복잡한 자연어 질문에 대한 더 정교한 답변을 제공하는 기술입니다. TAG는 기존의 Text2SQL과 RAG 방법론이 해결하지 못했던 문제들을 해결하며, 다양한 형태의 데이터베이스 질의를 포괄적으로 처리할 수 있습니다. 이를 통해 AI는 데이터베이스에 저장되지 않은 지식도 활용해, 사용자가 데이터에 대한 깊은 이해를 얻을 수 있도록 돕습니다.
      DEVOTEE 추천 블로그

      요즘 RAG를 계속 검토해 오고 있습니다.

      데보션에서 TAG (Table-Augmented Generation) 개념을 접하게 되었습니다.

      현재 RAG로 시맨틱 검색을 개발기 수준으로 구축해서 동작 중에 있는데요. 검색 품질이 기대에 미치지 못한다고 느끼던 중, TAG의 아이디어가 훌륭한 대안이 될 수 있다는 생각이 들었습니다.


      TAG (Table-Augmented Generation) 에 대한 이해

      TAG는 구조화된 데이터(테이블 형식)를 언어 모델(LM)과 통합하여 자연어 질문에 대한 더 정교한 답변을 제공하는 기술입니다.

      Berkeley와 Stanford 대학의 연구진이 제안한 혁신적인 접근 방식으로, AI 시스템과 데이터베이스를 보다 긴밀하게 통합해 자연어 질문에 답하는 새로운 패러다임을 제시합니다.

      기존의 AI 기반 질의응답 시스템들이 데이터베이스와 상호작용하는 데 있어 제한적인 SQL 쿼리나 단순 데이터 조회에 의존했다면,

      TAG는 이를 뛰어넘어 언어 모델의 추론 능력과 데이터베이스의 확장성을 결합한 종합적인 솔루션을 제공합니다.

      TAG는 복잡한 자연어 질문을 처리할 수 있도록 설계되었으며, 관계형 대수에 국한되지 않고 다양한 형태의 질의를 포괄적으로 처리합니다.

      예를 들어, 사용자 질문이 데이터베이스에서 단일 값을 찾아내는 것뿐만 아니라, 다중 테이블 간의 관계를 분석하거나 데이터 패턴을 추론하는 것까지 가능하게 합니다.

      이를 통해 AI가 데이터베이스 상에서 단순히 데이터를 조회하는 것이 아니라, 맥락과 세계 지식을 활용해 더 깊이 있는 답변을 생성할 수 있습니다.

      특히, TAG는 단순한 쿼리 처리에서 벗어나 AI 모델이 데이터를 기반으로 복잡한 추론을 하거나 다중 소스 데이터를 조합해 의미 있는 답변을 생성하는 능력을 부여함으로써,

      기존 Text2SQL이나 RAG 방법론이 해결하지 못했던 많은 문제를 해결합니다.

      이러한 점에서 TAG는 데이터베이스 질의응답 분야의 큰 도약으로 평가받고 있으며, 향후 AI와 데이터베이스 상호작용의 미래를 선도할 중요한 기술로 주목받고 있습니다.

      image.png


      자연어 질문을 데이터베이스로 처리하는 AI의 새로운 가능성

      데이터에 대해 복잡한 질문을 간단한 자연어로 묻고, 즉각적으로 정교한 답변을 받을 수 있는 세상을 상상해 보세요.

      AI 시스템은 점점 더 이러한 비전을 현실로 만들고 있습니다. 자연어로 데이터베이스에 질문을 던질 수 있게 해주는 이 시스템들은,

      강력한 언어 모델(LM)의 추론 능력과 데이터 관리 시스템의 계산 능력을 결합해 사용자에게 엄청난 가치를 제공할 수 있습니다.

      이를 통해 사용자는 복잡한 질문을 직관적으로 던지고 데이터에서 직접 인사이트를 얻을 수 있습니다.

      그러나, 현재 사용되고 있는 대부분의 접근 방식은 이러한 잠재력을 충분히 발휘하지 못하고 있습니다.

      Text2SQL과 Retrieval-Augmented Generation (RAG) 같은 방법들이 많이 사용되지만, 이들은 제한된 범위 내에서만 동작하고 있습니다.

      이에 대응해 등장한 새로운 개념이 바로 **Table-Augmented Generation (TAG)**입니다.

      TAG는 자연어로 데이터베이스와 상호작용할 수 있는 더욱 포괄적이고 유연한 방법을 제공합니다.

      TAG가 왜 혁신적인지, 그리고 어떻게 데이터와의 상호작용을 근본적으로 변화시킬 수 있는지 확인해 보겠습니다.


      기존 방법의 한계

      AI가 자연어 질문을 데이터베이스에서 처리하는 두 가지 주요 방법이 있습니다:

      • Text2SQL: 자연어 질문을 SQL 명령으로 변환하고, 그 명령을 데이터베이스에 실행하는 방법입니다. 관계형 대수로 표현할 수 있는 질문에는 매우 효과적입니다.

        예를 들어 "인사부 직원들의 평균 연령은?" 같은 질문에 적합합니다.

        문제는? Text2SQL은 SQL 및 관계형 데이터베이스의 제약에 묶여 있어, 사용자가 던질 수 있는 질문의 범위가 매우 좁습니다.

        현실 세계에서 사용자는 훨씬 더 복잡하고 모호한 질문을 던지기 마련입니다.

      • Retrieval-Augmented Generation (RAG): RAG는 AI 모델이 데이터베이스에서 적절한 데이터를 검색해 답변을 생성하는 방법입니다.

        Text2SQL보다는 더 유연하지만, 주로 단일 데이터 기록이나 소수의 데이터 레코드에서 정보를 찾아오는 “포인트 조회” 질문에 적합합니다.

        예를 들어 "John Doe의 연락처 정보는?" 같은 질문이 그 예입니다.

        문제는? RAG는 더 복잡한 추론을 요구하거나, 여러 테이블에 걸친 데이터를 다루는 질문에는 어려움을 겪습니다.

        이 방법 역시 자연어 질문을 처리하는 데 한계가 있습니다.


      TAG: 통합되고 범용적인 접근법

      **Table-Augmented Generation (TAG)**는 이러한 한계를 뛰어넘는 새로운 패러다임입니다.

      TAG는 Text2SQL처럼 관계형 대수에만 국한되지 않으며, RAG처럼 단순한 조회 질문에도 제한되지 않습니다.

      대신 TAG는 언어 모델의 추론 능력과 데이터베이스의 계산 능력을 결합한 통합 프레임워크를 제공해 자연어로 다양한 질문을 처리할 수 있습니다.

      TAG의 차별점

      1. 유연한 모델: TAG는 다양한 질문을 처리할 수 있습니다. 단순한 조회에서부터 복잡한 분석까지, 사용자가 묻는 질문이 무엇이든 TAG는 이를 처리할 수 있습니다.

        이 유연성이 TAG를 기존 방법과 차별화하는 중요한 요소입니다.

      2. 새로운 상호작용: TAG는 기존에는 탐구되지 않았던 언어 모델과 데이터베이스 간의 새로운 상호작용을 가능하게 합니다.

        단순한 데이터 조회를 넘어, TAG는 데이터를 기반으로 한 추론을 통해 복잡한 질문에도 답할 수 있습니다.

        예를 들어 "지난 5년간의 판매 추세와 마케팅 지출 간의 상관관계는?" 같은 질문에 대한 답변은 TAG의 강점을 잘 보여줍니다.

      3. 세계 지식 통합: TAG의 가장 흥미로운 측면 중 하나는 언어 모델이 세계 지식과 맥락적 추론을 동원할 수 있다는 것입니다.

        이는 TAG 시스템이 데이터베이스에 저장되지 않은 지식도 활용해, 사용자가 데이터에 대한 더 깊은 이해를 얻을 수 있도록 도와줍니다.

      TAG와 RAG를 TAG의 3가지 처리 프로세스 측면에서 비교하면 다음과 같습니다:

      처리 프로세스

      TAG (Table-Augmented Generation)

      RAG (Retrieval-Augmented Generation)

      1) 질의 생성 (Query Synthesis)

      - 자연어 질의를 데이터베이스 쿼리로 변환

      - 쿼리는 구조화된 데이터(테이블)에 맞게 변환

      - 자연어 질의를 문서 검색 쿼리로 변환

      - 주로 비구조화된 텍스트에서 정보를 검색

      2) 질의 실행 (Query Execution)

      - 변환된 쿼리를 데이터베이스에서 실행

      - 데이터셋에서 필요한 정보를 정확히 추출

      - 변환된 검색 쿼리를 문서 검색 엔진에서 실행

      - 여러 문서를 검색하여 후보 문서 선택

      3) 응답 생성 (Answer Generation)

      - 쿼리 실행 결과를 기반으로 추론과 언어 모델을 사용해 응답 생성

      - 세계 지식과 데이터베이스 결과를 결합하여 답변

      - 검색된 문서를 바탕으로 언어 모델이 응답을 생성

      - 문서의 내용을 추출하여 요약 또는 답변 제공


      테이블 형태의 CSV를 활용하여 TAG를 구현하기

      내부적으로 RAG의 초기 단계인 qdrant 기반의 시맨틱 검색을 movies.csv로 구축해 둔게 있어 비교 대상으로 보고 TAG 개념을 구현해 보기로 하였습니다.

      TAG 개념 구현 단계별 절차

      TAG는 아래의 3단계 프로세스로 구성되어 있습니다.

      1. 쿼리 합성: 자연어 질문을 SQL과 같은 구조화된 쿼리로 변환

      2. 쿼리 실행: 데이터베이스에서 쿼리를 실행해 필요한 데이터를 검색

      3. 답변 생성: 검색된 데이터를 바탕으로 자연어 답변을 생성. 이 과정에서 LM과 데이터베이스 간의 상호작용이 이루어집니다.

      1) 쿼리 합성

      쿼리를 생성하는 함수는 사용자의 자연어 질문을 pandas DataFrame 쿼리로 변환합니다.

      - 주요 정의 요소 : pandas 쿼리, 컬럼, 검색 메소드, 데이터형, 연도 기준, 복합 조건 처리, 문구 해석 기준, 쿼리 형식(조건형) 등

      def generate_query(user_query):
          prompt = f"""
          다음 CSV 파일 구조를 기반으로 사용자의 질문에 대한 pandas DataFrame 쿼리를 생성해주세요:
          
          컬럼: title(제목), genre(장르), year(개봉년도), date(개봉월일), rating(평점), vote_count(평가수), plot(줄거리), main_act(주연), supp_act(조연)
          
          주의사항:
          1. 장르는 '|'로 구분되어 있을 수 있으므로, 장르 검색 시 str.contains() 메소드를 사용하세요.
          2. rating(평점)과 vote_count(평가수)는 숫자 데이터입니다.
          ...
      
          11. '최근 5년'과 같은 상대적인 시간 표현은 현재 연도를 기준으로 계산하여 구체적인 연도 범위로 변환하세요.    
          12. 복합 조건을 정확히 처리하세요. 예를 들어, '평점이 9점 이상인 액션 영화'는 'rating >= 9 & genre.str.contains("액션", case=False)'와 같이 처리해야 합니다.
          13. '추천할 만한', '인기 있는' 등의 표현은 평점(rating)과 평가 수(vote_count)를 기준으로 해석하세요. 예를 들어, 'rating >= 8 & vote_count >= 1000'과 같이 처리할 수 있습니다.    
          14. '가장 높은', '최고의' 등의 표현은 무시하고 해당 조건에 맞는 모든 영화를 검색하도록 쿼리를 작성하세요. 결과는 이미 평점 내림차순으로 정렬되어 있습니다.    
          
          사용자 질문: {user_query}
          
          쿼리 형식: 
          condition1 & condition2 & ...
          """
          
          response = client.chat.completions.create(
              model=model,
              messages=[
                  {"role": "system", "content": "당신은 pandas DataFrame 쿼리를 생성하는 전문가입니다."},
                  {"role": "user", "content": prompt}
              ]
          )
          
          generated_query = response.choices[0].message.content.strip()
          # 쿼리 정제 과정...
          return generated_query

      2) 쿼리 실행

      쿼리 실행 함수는 생성된 쿼리를 실행하여 데이터베이스에서 필요한 정보를 검색합니다.

      - 테이터 타입, str.contains, 정렬(내림차순)

      def query_database(query, data):
          try:
              # 데이터 타입 변환
              data['rating'] = pd.to_numeric(data['rating'], errors='coerce')
              data['vote_count'] = pd.to_numeric(data['vote_count'], errors='coerce')
              data['year'] = pd.to_numeric(data['year'], errors='coerce')
              data['date'] = pd.to_datetime(data['date'], format='%m.%d', errors='coerce')
              
              # 쿼리 실행
              if 'str.contains' in query:
                  result = data.query(query, local_dict={'user_query': user_query})
              else:
                  result = data.query(query)
              
              # 결과 정렬 및 상위 5개 선택
              result = result.sort_values(by='rating', ascending=False).head(5)
              
              return result
          except Exception as e:
              st.error(f"쿼리 실행 중 오류가 발생했습니다: {str(e)}")
              return pd.DataFrame()

      3) 답변 생성

      답변 생성 함수는 검색된 데이터를 바탕으로 자연어 답변을 생성합니다. LLM과 데이터베이스 간의 상호작용이 이루어지는 부분입니다.

      - 응답 형식, 영화 설명과 추천 이유, 세계 지식과 흥미로운 사실, 구분선

      def generate_response(prompt, context):
          messages = [
              {"role": "system", "content": "당신은 영화 추천 전문가이자 세계 지식을 갖춘 AI입니다. 주어진 정보와 세계 지식을 결합하여 사용자의 질문에 답변해주세요. 응답은 다음 형식을 따라야 합니다:\n\n영화 제목 (개봉 연도) - 장르\n\n평점: X.XX\n간단한 영화 설명 및 추천 이유\n\n관련된 지식 및 흥미로운 사실\n\n각 영화 추천 사이에 구분선을 추가합니다."},
              {"role": "user", "content": f"컨텍스트: {context}\n\n질문: {prompt}\n\n각 영화에 대해 관련된 세계 지식과 흥미로운 사실을 추가해주세요."}
          ]
          
          response = client.chat.completions.create(
              model=model,
              messages=messages
          )
          
          answer = response.choices[0].message.content.strip()
          total_tokens = response.usage.total_tokens
          input_tokens = response.usage.prompt_tokens
          output_tokens = response.usage.completion_tokens
          cost = calculate_cost(input_tokens, output_tokens)
          
          return answer, total_tokens, input_tokens, output_tokens, cost, context

      - openai api로 요청되는 토큰 비용에 대해 예상 비용을 계산할 수 있도록 기능을 추가해 두었습니다.


      시맨틱 검색형 RAG와 검색 결과 비교

      TAG 개념을 구현한 스트림릿 앱과 최근에 프로젝트 진행으로 개발기에 구축되어 있는 RAG와 검색 결과의 차이를 보기 위해 비교를 해 보았습니다.

      질의문

      TAG

      RAG(HNSW 기반의 시맨틱 검색)

      레오나르도 디카프리오가 출연한 영화 중 평점이 가장 높은 영화는?

      평점이 높은 인셉션, 블러드 다이아몬드, 위대한 개츠비를 추천하고 있습니다.

      image.png

      평점이 낮은 나인, 더 울프 오브 월 스트리트, 레버넌트를 추천하고 있습니다.

      image.png

      2010년 이후에 개봉한 액션 영화 중 평점이 가장 높은 영화는?

      2010년 이후 개봉한 평점이 높은 영화를 추천하고 있습니다.

      image.png

      2010년 이후 개봉 영화지만 평점이 낮은 영화를 추천하고 있습니다.

      image.png

      평점이 9점 이상인 영화 목록을 보여줘

      평점이 9점 이상인 목록을 내림차순으로 제공하고 있습니다.

      image.png

      평점이 9점 이하인 영화를 목록으로 제공하고 있습니다.

      image.png

      디카프리오

      디카프리오가 연기한 영화를 제대로 추천해 주고 있습니다.

      image.png

      시맨틱 검색의 특성상 키워드 검색에는 약점을 보입니다. 제대로 검색해 오지 못하고 있습니다.

      image.png


      결론

      TAG 개념을 활용하여 movies.csv 테이블 데이터를 기준으로 구현하고, 이를 HNSW 기반의 시맨틱 검색과 비교하는 작업을 진행해 보았습니다.

      기업 내부 지식이나 특정 도메인 지식 영역에서는 오랜 노하우와 축적된 전문 지식이 답변의 품질을 결정짓기 때문에, 데이터의 정확성과 확보 여부가 매우 중요합니다.

      데이터가 충분히 확보된 경우, 그 다음으로 중요한 것은 답변의 품질입니다.

      전체 테이블 데이터를 분석할 때, 언어 모델에 질의를 보내면 언어 모델의 추론 능력을 통해 원하는 답을 쉽게 얻을 수 있습니다.

      하지만, 여기서 문제가 되는 것은 토큰량입니다. 데이터 양이 너무 크면 언어 모델이 한 번에 처리하지 못할 수 있으며, 토큰량이 많아지면 비용이 상승해 실제 활용에 부담이 될 수 있습니다.

      이는 현실적인 제약을 가져옵니다.

      이러한 한계를 TAG 개념이 해결해 줄 수 있습니다.

      TAG는 데이터를 테이블 형태로 정형화할 수 있다면 적용 가능하며, 기존의 Text2SQL과 RAG방법론을 개선하여 언어 모델의 강점인 복합적 질의 처리 능력과 세계 지식을 활용한 추론을 결합합니다.

      상대적으로 적은 토큰으로도 실용적인 서비스를 제공할 수 있으며, 비용 효율적이면서도 고품질의 응답을 생성할 수 있습니다.



      참조

      Table-Augmented Generation (TAG): A Unified Approach for Enhancing Natural Language Querying over Databases

      Text2SQL is Not Enough: Unifying AI and Databases with TAG


      전체 코드

      import pandas as pd
      import streamlit as st
      import os
      from openai import OpenAI
      from dotenv import load_dotenv
      import re
      
      # .env 파일에서 환경 변수 로드
      load_dotenv()
      
      # 스트림릿 앱 시작
      st.title("TAG 영화 추천")
      
      # OpenAI 설정 :  사내에서 제공하는 모델 사용 
      api_key = os.environ.get("LLM_GPT_API_KEY")
      base_url = os.environ.get('LLM_GPT_BASE_URL')
      model = "openai/gpt-4o-mini-2024-07-18"  
      
      # OpenAI 클라이언트 설정
      client = OpenAI(
          api_key=api_key,
          base_url=base_url
      )
      
      # 스크립트 파일의 디렉토리 경로를 얻습니다
      script_dir = os.path.dirname(os.path.abspath(__file__))
      # CSV 파일 읽기
      csv_path = os.path.join(script_dir, 'movies.csv')
      table_data = pd.read_csv(csv_path)
      
      def generate_query(user_query):
          prompt = f"""
          다음 CSV 파일 구조를 기반으로 사용자의 질문에 대한 pandas DataFrame 쿼리를 생성해주세요:
          
          컬럼: title(제목), genre(장르), year(개봉년도), date(개봉월일), rating(평점), vote_count(평가수), plot(줄거리), main_act(주연), supp_act(조연)
          
          주의사항:
          1. 장르는 '|'로 구분되어 있을 수 있으므로, 장르 검색 시 str.contains() 메소드를 사용하세요.
          2. rating(평점)과 vote_count(평가수)는 숫자 데이터입니다.
          3. year(개봉년도)는 4자리 숫자입니다.
          4. 텍스트 검색은 str.contains() 메소드를 사용하여 부분 일치를 구현하세요.
          5. 쿼리는 pandas의 query() 메소드에서 사용할 수 있는 형식으로 작성해주세요. (예: 'rating >= 9')
          6. 컬럼 이름은 반드시 영어로 사용해야 합니다.
          7. 날짜 범위 검색이 필요한 경우 '@date' 형식을 사용하세요.
          8. 복잡한 조건이 필요한 경우 여러 조건을 '&'로 연결하세요.
          9. 쿼리만 작성하고 추가 설명은 하지 마세요.
          10. 만약 특정 컬럼을 지정할 수 없다면, 모든 텍스트 컬럼에서 검색하도록 쿼리를 작성하세요.
          11. '최근 5년'과 같은 상대적인 시간 표현은 현재 연도를 기준으로 계산하여 구체적인 연도 범위로 변환하세요.
          12. 복합 조건을 정확히 처리하세요. 예를 들어, '평점이 9점 이상인 액션 영화'는 'rating >= 9 & genre.str.contains("액션", case=False)'와 같이 처리해야 합니다.
          13. '추천할 만한', '인기 있는' 등의 표현은 평점(rating)과 평가 수(vote_count)를 기준으로 해석하세요. 예를 들어, 'rating >= 8 & vote_count >= 1000'과 같이 처리할 수 있습니다.
          14. '가장 높은', '최고의' 등의 표현은 무시하고 해당 조건에 맞는 모든 영화를 검색하도록 쿼리를 작성하세요. 결과는 이미 평점 내림차순으로 정렬되어 있습니다.
          
          사용자 질문: {user_query}
          
          쿼리 형식: 
          condition1 & condition2 & ...
          """
          
          response = client.chat.completions.create(
              model=model,
              messages=[
                  {"role": "system", "content": "당신은 pandas DataFrame 쿼리를 생성하는 전문가입니다."},
                  {"role": "user", "content": prompt}
              ]
          )
          
          generated_query = response.choices[0].message.content.strip()
          # 따옴표와 백틱 제거
          generated_query = generated_query.strip("'\"```")
          # 언어 식별자 제거 (예: python)
          generated_query = generated_query.split('\n')[-1] if '\n' in generated_query else generated_query
          
          if not generated_query:
              generated_query = "title.str.contains(@user_query, case=False) | plot.str.contains(@user_query, case=False) | main_act.str.contains(@user_query, case=False) | supp_act.str.contains(@user_query, case=False)"
          
          # st.write(f"생성된 쿼리:\n{generated_query}")  # 디버깅을 위해 생된 쿼리 출력
          return generated_query
      
      def query_database(query, data):
          """데이터베이스에 쿼리를 실행하고 결과를 반환합니다."""
          try:
              # 데이터 타입 변환
              data['rating'] = pd.to_numeric(data['rating'], errors='coerce')
              data['vote_count'] = pd.to_numeric(data['vote_count'], errors='coerce')
              data['year'] = pd.to_numeric(data['year'], errors='coerce')
              data['date'] = pd.to_datetime(data['date'], format='%m.%d', errors='coerce')
              
              # 쿼리 실행
              if 'str.contains' in query:
                  # str.contains 메소드를 사용하는 경우
                  result = data.query(query, local_dict={'user_query': user_query})
              else:
                  # 일반적인 쿼리 실행
                  result = data.query(query)
              
              # 평점 내림차순으로 정렬하고 상위 5개만 선택
              result = result.sort_values(by='rating', ascending=False).head(5)
              
              return result
          except Exception as e:
              st.error(f"쿼리 실행 중 오류가 발생했습니다: {str(e)}")
              return pd.DataFrame()  # 빈 데이터프레임 반환
      
      def calculate_cost(input_tokens, output_tokens):
          """토큰 수를 기반으로 비용을 계산합니다."""
          input_cost = (input_tokens / 1_000_000) * 0.15
          output_cost = (output_tokens / 1_000_000) * 0.60
          total_cost = input_cost + output_cost
          return total_cost
      
      def generate_response(prompt, context):
          messages = [
              {"role": "system", "content": "당신은 영화 추천 전문가이자 세계 지식을 갖춘 AI입니다. 주어진 정보와 세계 지식을 결합하여 사용자의 질문에 답변해주세요. 응답은 다음 형식을 따라야 합니다:\n\n영화 제목 (개봉 연도) - 장르\n\n평점: X.XX\n간단한 영화 설명 및 추천 이유\n\n관련된 지식 및 흥미로운 사실\n\n각 영화 추천 사이에 구분선을 추가합니다."},
              {"role": "user", "content": f"컨텍스트: {context}\n\n질문: {prompt}\n\n각 영화에 대해 관련된 세계 지식과 흥미로운 사실을 추가해주세요."}
          ]
          
          response = client.chat.completions.create(
              model=model,
              messages=messages
          )
          
          answer = response.choices[0].message.content.strip()
          total_tokens = response.usage.total_tokens
          input_tokens = response.usage.prompt_tokens
          output_tokens = response.usage.completion_tokens
          cost = calculate_cost(input_tokens, output_tokens)
          
          return answer, total_tokens, input_tokens, output_tokens, cost, context
      
      def tag_movie_recommendation(user_query, max_attempts=10):
          for attempt in range(max_attempts):
              generated_query = generate_query(user_query)
              
              filtered_data = query_database(generated_query, table_data)
              
              if len(filtered_data) > 0:
                  available_columns = filtered_data.columns.tolist()
                  required_columns = ['title', 'genre', 'year', 'rating', 'plot', 'main_act', 'supp_act']
                  columns_to_use = [col for col in required_columns if col in available_columns]
                  
                  filtered_table_data = filtered_data[columns_to_use]
                  
                  # 세계 지식을 결합한 프롬프트 생성
                  context_with_world_knowledge = "영화 정보:\n\n"
                  for _, row in filtered_table_data.iterrows():
                      context_with_world_knowledge += f"제목: {row['title']}, 개봉년도: {row['year']}, 장르: {row['genre']}, 평점: {row['rating']}\n"
                      context_with_world_knowledge += f"줄거리: {row['plot']}\n"
                      context_with_world_knowledge += f"주연: {row['main_act']}, 조연: {row['supp_act']}\n\n"
                  
                  context_with_world_knowledge += "위 영화들에 대한 추천과 함께, 각 영화와 관련된 세계 지식과 흥미로운 사실을 추가해주세요."
                  
                  response, total_tokens, input_tokens, output_tokens, cost, input_content = generate_response(user_query, context_with_world_knowledge)
                  
                  movie_recommendations = re.split(r'\n(?=\S+\s+\(\d{4}\)\s+-)', response)
                  return movie_recommendations, total_tokens, input_tokens, output_tokens, cost, input_content
              
          st.error(f"죄송합니다. {max_attempts}번의 시도 후에도 '{user_query}'와 관련된 영화를 찾을 수 없습니다.")
          return [f"죄송합니다. {max_attempts}번의 시도 후에도 '{user_query}'와 관련된 영화를 찾을 수 없습니다."], 0, 0, 0, 0, ""
      
      # 스트림릿 인터페이스
      predefined_questions = [
          "직접 입력",
          "평점이 9점 이상인 영화 목록을 보여줘",
          "2010년 이후에 개봉한 액션 영화 중 평점이 가장 높은 영화는?",
          "레오나르도 디카프리오가 출연한 영화 중 평점이 가장 높은 영화는?"
      ]
      
      selected_question = st.selectbox("질문을 선택하거나 직접 입력하세요:", predefined_questions)
      
      if selected_question == "직접 입력":
          user_query = st.text_input("질문을 입력하세요:", "")
      else:
          user_query = selected_question
      
      if st.button("추천 받기") and user_query:
          with st.spinner("영화를 검색 중입니다..."):
              recommendations, total_tokens, input_tokens, output_tokens, cost, input_content = tag_movie_recommendation(user_query)
          
          for i, recommendation in enumerate(recommendations):
              st.write(recommendation.strip())
          
          # 데이터프레임 생성
          data = {
              '항목': ['사용된 총 토큰 수', '입력 토큰 수', '출력 토큰 수', '예상 비용'],
              '값': [total_tokens, input_tokens, output_tokens, f"${cost:.6f}"]
          }
          df_results = pd.DataFrame(data)
          
          # 데이터프레임 표시
          st.dataframe(df_results)

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      broccoli 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기