Spaces:
Sleeping
Sleeping
# app.py | |
import os | |
import pandas as pd | |
import gradio as gr | |
from datetime import datetime | |
import openai | |
# OpenAI API 클라이언트 설정 | |
openai.api_key = os.getenv("OPENAI_API_KEY") | |
# LLM 호출 함수 | |
def call_api(content, system_message, max_tokens=2000, temperature=0.5, top_p=1.0): | |
response = openai.ChatCompletion.create( | |
model="gpt-4o-mini", | |
messages=[ | |
{"role": "system", "content": system_message}, | |
{"role": "user", "content": content}, | |
], | |
max_tokens=max_tokens, | |
temperature=temperature, | |
top_p=top_p, | |
) | |
return response.choices[0].message['content'] | |
# 엑셀 데이터 읽기 함수 | |
def read_excel_data(file): | |
df = pd.read_excel(file, usecols="A, B, C, D, E", skiprows=1, | |
names=["ID", "Review Date", "Option", "Review", "ReviewScore"], engine='openpyxl') | |
df['Review Date'] = pd.to_datetime(df['Review Date']).dt.tz_localize(None).dt.date | |
df['Year'] = df['Review Date'].astype(str).str.slice(0, 4) | |
df['Option1'] = df['Option'].astype(str).str.split(" / ").str[0] # 1차 옵션 추출 | |
df['Review Length'] = df['Review'].str.len() # 리뷰 길이 계산 | |
return df | |
# 긍정적인 리뷰를 반환하는 함수 | |
def get_positive_reviews(df): | |
# 리뷰 점수가 4 이상인 긍정 리뷰 필터링 | |
positive_reviews = df[df['ReviewScore'] >= 4].sort_values(by='Review Length', ascending=False) | |
positive_reviews = positive_reviews.head(20) # 상위 20개 리뷰 선택 | |
positive_reviews.reset_index(drop=True, inplace=True) | |
positive_reviews.index += 1 | |
positive_reviews['순번'] = positive_reviews.index | |
# 리뷰 형식 지정 | |
positive_output = "\n\n".join(positive_reviews.apply( | |
lambda x: f"{x['순번']}. **{x['Review Date']} / {x['ID']} / {x['Option']}**\n\n{x['Review']}", axis=1)) | |
return positive_output | |
# 부정적인 리뷰를 반환하는 함수 | |
def get_negative_reviews(df): | |
# 리뷰 점수가 2 이하인 부정 리뷰 필터링 | |
negative_reviews = df[df['ReviewScore'] <= 2].sort_values(by='Review Length', ascending=False) | |
negative_reviews = negative_reviews.head(30) # 상위 30개 리뷰 선택 | |
negative_reviews.reset_index(drop=True, inplace=True) | |
negative_reviews.index += 1 | |
negative_reviews['순번'] = negative_reviews.index | |
# 리뷰 형식 지정 | |
negative_output = "\n\n".join(negative_reviews.apply( | |
lambda x: f"{x['순번']}. **{x['Review Date']} / {x['ID']} / {x['Option']}**\n\n{x['Review']}", axis=1)) | |
return negative_output | |
# 리뷰 데이터를 처리하여 긍정 및 부정 리뷰를 추출하고 분석하는 함수 | |
def process_reviews(file): | |
df = read_excel_data(file) | |
positive_reviews = get_positive_reviews(df) | |
negative_reviews = get_negative_reviews(df) | |
# 긍정적인 리뷰 분석 프롬프트 | |
positive_prompt = """[중요 규칙] | |
1. 반드시 한글(한국어)로 출력하라. | |
2. 너는 리뷰 데이터를 분석하는 빅데이터 분석가이다. | |
3. 고객의 리뷰 데이터를 바탕으로 긍정적인 의견의 데이터만 분석하라. | |
4. 반드시 제공된 리뷰 데이터에서만 분석하라. | |
5. 너의 생각을 포함하지 말 것. | |
[분석 조건] | |
1. 총 20개의 리뷰데이터를 제공한다. | |
2. 각 리뷰 데이터의 둘째줄 부터의 실제 고객리뷰를 반영하라. | |
3. 반드시 긍정적인 의견만을 분석하라. 부정적인 의견은 제외하라. | |
4. 기능과 성능의 부분, 감성적인 부분, 실제 사용 측면의 부분, 배송의 부분, 타겟별 부분의 관점으로 분석하라. | |
5. 4번의 조건에 포함되지 않는 긍정적인 리뷰를 별도로 출력하라. | |
6. 마케팅적인 요소로 사용할 수 있는 고객의 실제 리뷰를 반영하라. | |
[출력 형태 조건] | |
1. 각각의 제목 앞에 '📝'이모지를 출력하라,'#', '##'은 출력하지 말것. | |
2. 가장 마지막에 종합 의견을 작성하라, "🏆종합의견"의 제목형태를 사용하라. | |
[종합의견의 출력 조건 시작] | |
('종합의견'이 아닌 다른 부분에 이 출력 조건을 반영하지 말 것. | |
- 항목별 제목을 제외하라. | |
- 종합의견에는 항목별 제목을 제외하고 서술식 문장으로 작성하라. | |
- 매출을 극대화 할 수 있는 고객의 실제 리뷰 포인트를 제시하라. | |
[SWOT분석 조건] | |
1. '종합의견' 다음 내용으로 SWOT분석 의견을 출력하라. | |
2. SWOT분석 중 '강점'의견과 '기회'의 의견을 출력하라. | |
3. 반드시 '종합의견'의 내용을 기반으로 작성하라. | |
4. 제목은 '🏹 강점', '🏹 기회'으로 출력하라. | |
[종합의견의 출력 조건 끝] | |
3. 실제 고객의 리뷰 데이터에서 사용된 단어를 포함하라. | |
4. 너의 생각을 임의로 넣지 말 것. | |
""" | |
# 부정적인 리뷰 분석 프롬프트 | |
negative_prompt = """[중요 규칙] | |
1. 반드시 한글(한국어)로 출력하라. | |
2. 너는 리뷰 데이터를 분석하는 빅데이터 분석가이다. | |
3. 고객의 리뷰 데이터를 바탕으로 부정적인 의견의 데이터만 분석하라. | |
4. 반드시 제공된 리뷰 데이터에서만 분석하라. | |
5. 너의 생각을 포함하지 말 것. | |
[분석 조건] | |
1. 총 30개의 리뷰데이터를 제공한다. | |
2. 각 리뷰 데이터의 둘째줄 부터의 실제 고객리뷰를 반영하라. | |
3. 부정적인 의견만을 분석하라. | |
4. 기능과 성능의 부분, 감성적인 부분, 실제 사용 측면의 부분, 배송의 부분, 고객의 분노 부분의 관점으로 분석하라. | |
5. 4번의 조건에 포함되지 않는 부정적인 리뷰를 별도로 출력하라. | |
6. 부정적인 리뷰 분석 결과를 바탕으로 '개선할 점'을 출력하라. | |
[출력 형태 조건] | |
1. 각각의 제목 앞에 '📝'이모지를 출력하라,'#', '##'은 출력하지 말것. | |
2. 가장 마지막에 '개선할 점'을 출력하라("📢개선할 점"의 제목형태를 사용하라.) | |
[개선할 점의 출력 조건 시작] | |
('개선할 점'이 아닌 다른 부분에 이 출력 조건을 반영하지 말 것. | |
- 항목별 제목을 제외하라. | |
- 주요 항목별로 개선할 점을 출력하라. | |
- 전문적이고, 분석적이며, 제안하는 형태의 공손한 어투를 사용하라.(단답형 표현 금지) | |
[SWOT분석 조건] | |
1. '종합의견' 다음 내용으로 SWOT분석 의견을 출력하라. | |
2. SWOT분석 중 '약점'의견과 '위협'의 의견을 출력하라. | |
3. 반드시 '개선할 점'의 내용을 기반으로 작성하라. | |
4. 제목은 '💉 약점', '💉 위협'으로 출력하라. | |
[개선할 점의 출력 조건 끝] | |
3. 실제 고객의 리뷰 데이터에서 사용된 단어를 포함하라. | |
4. 너의 생각을 임의로 넣지 말 것. | |
""" | |
# LLM을 사용하여 리뷰 분석 | |
positive_analysis = call_api(positive_reviews, system_message=positive_prompt) | |
negative_analysis = call_api(negative_reviews, system_message=negative_prompt) | |
return positive_reviews, negative_reviews, positive_analysis, negative_analysis | |
# Gradio 인터페이스 구성 | |
def create_interface(): | |
with gr.Blocks() as demo: | |
gr.Markdown("### 리뷰 데이터 업로드") | |
file_input = gr.File(label="엑셀 파일 업로드", file_types=[".xlsx"]) | |
analyze_button = gr.Button("리뷰분석") | |
with gr.Column(): | |
gr.Markdown("### 긍정적인 주요 리뷰 (최대 20개)") | |
positive_reviews_output = gr.Textbox(label="긍정적인 주요 리뷰", interactive=False, lines=20) | |
gr.Markdown("### 긍정 리뷰 분석 결과") | |
positive_analysis_output = gr.Textbox(label="긍정 리뷰 분석", interactive=False, lines=30) | |
gr.Markdown("### 부정적인 주요 리뷰 (최대 30개)") | |
negative_reviews_output = gr.Textbox(label="부정적인 주요 리뷰", interactive=False, lines=30) | |
gr.Markdown("### 부정 리뷰 분석 결과") | |
negative_analysis_output = gr.Textbox(label="부정 리뷰 분석", interactive=False, lines=30) | |
analyze_button.click( | |
fn=process_reviews, | |
inputs=[file_input], | |
outputs=[positive_reviews_output, negative_reviews_output, positive_analysis_output, negative_analysis_output] | |
) | |
return demo | |
if __name__ == "__main__": | |
interface = create_interface() | |
interface.launch() | |