parkkyujin commited on
Commit
803ee65
ยท
verified ยท
1 Parent(s): a28c751

Delete src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +0 -476
src/streamlit_app.py DELETED
@@ -1,476 +0,0 @@
1
- # ์•ˆ์ •์ ์ธ AI ์นดํ”ผ๋ผ์ดํ„ฐ - ์ž„๋ฒ ๋”ฉ ๊ธฐ๋ฐ˜ RAG ์‹œ์Šคํ…œ
2
- # Hugging Face Spaces ํ™˜๊ฒฝ ์ตœ์ ํ™” ๋ฒ„์ „
3
-
4
- import streamlit as st
5
- import pandas as pd
6
- import numpy as np
7
- import pickle
8
- import google.generativeai as genai
9
- import time
10
- import json
11
- import os
12
- from datetime import datetime
13
-
14
- # ํ™˜๊ฒฝ ์„ค์ • (๊ถŒํ•œ ๋ฌธ์ œ ํ•ด๊ฒฐ)
15
- os.environ['STREAMLIT_BROWSER_GATHER_USAGE_STATS'] = 'false'
16
- os.environ['TRANSFORMERS_CACHE'] = '/tmp/transformers'
17
- os.environ['SENTENCE_TRANSFORMERS_HOME'] = '/tmp/sentence_transformers'
18
-
19
- # ํŽ˜์ด์ง€ ์„ค์ •
20
- st.set_page_config(
21
- page_title="AI ์นดํ”ผ๋ผ์ดํ„ฐ | RAG ๊ธฐ๋ฐ˜ ๊ด‘๊ณ  ์นดํ”ผ ์ƒ์„ฑ",
22
- page_icon="โœจ",
23
- layout="wide",
24
- initial_sidebar_state="expanded"
25
- )
26
-
27
- # ์ œ๋ชฉ ๋ฐ ์„ค๋ช…
28
- st.title("โœจ AI ์นดํ”ผ๋ผ์ดํ„ฐ")
29
- st.markdown("### ๐ŸŽฏ 37,671๊ฐœ ์‹ค์ œ ๊ด‘๊ณ  ์นดํ”ผ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ RAG ์‹œ์Šคํ…œ")
30
- st.markdown("---")
31
-
32
- # ์‚ฌ์ด๋“œ๋ฐ” ์„ค์ •
33
- st.sidebar.header("๐ŸŽ›๏ธ ์นดํ”ผ ์ƒ์„ฑ ์„ค์ •")
34
-
35
- # API ํ‚ค ์ž…๋ ฅ (ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์šฐ์„  ์‚ฌ์šฉ)
36
- default_api_key = os.getenv("GEMINI_API_KEY", "")
37
-
38
- api_key = st.sidebar.text_input(
39
- "๐Ÿ”‘ Gemini API ํ‚ค",
40
- value=default_api_key,
41
- type="password",
42
- help="ํ™˜๊ฒฝ๋ณ€์ˆ˜์— GEMINI_API_KEY๋กœ ์„ค์ •ํ•˜๋ฉด ์ž๋™ ์ž…๋ ฅ๋ฉ๋‹ˆ๋‹ค"
43
- )
44
-
45
- if not api_key:
46
- st.warning("โš ๏ธ Gemini API ํ‚ค๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”")
47
- st.info("๐Ÿ’ก Settings โ†’ Repository secrets์—์„œ GEMINI_API_KEY๋ฅผ ์„ค์ •ํ•˜์„ธ์š”")
48
- st.stop()
49
-
50
- # ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” (์บ์‹ฑ) - ์ž„๋ฒ ๋”ฉ ํ•„์ˆ˜!
51
- @st.cache_resource(show_spinner=False)
52
- def load_system():
53
- """์‹œ์Šคํ…œ ์ปดํฌ๋„ŒํŠธ ๋กœ๋”ฉ - ์ž„๋ฒ ๋”ฉ ๊ธฐ๋ฐ˜ RAG ์‹œ์Šคํ…œ"""
54
-
55
- progress_container = st.container()
56
-
57
- with progress_container:
58
- # ์ „์ฒด ์ง„ํ–‰๋ฅ 
59
- total_progress = st.progress(0)
60
- status_text = st.empty()
61
-
62
- # 1๋‹จ๊ณ„: API ์„ค์ • (10%)
63
- status_text.text("๐Ÿ”‘ Gemini API ์ดˆ๊ธฐํ™” ์ค‘...")
64
- try:
65
- genai.configure(api_key=api_key)
66
- model = genai.GenerativeModel('gemini-2.0-flash')
67
- total_progress.progress(10)
68
- st.success("โœ… Gemini API ์„ค์ • ์™„๋ฃŒ")
69
- except Exception as e:
70
- st.error(f"โŒ Gemini API ์„ค์ • ์‹คํŒจ: {e}")
71
- return None, None, None, None
72
-
73
- # 2๋‹จ๊ณ„: ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋“œ (40%)
74
- status_text.text("๐Ÿค– ํ•œ๊ตญ์–ด ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘... (1-2๋ถ„ ์†Œ์š”)")
75
- embedding_model = None
76
-
77
- # ์•ˆ์ •์ ์ธ ๋ชจ๋ธ ๋กœ๋”ฉ ์ „๋žต
78
- try:
79
- # ๋จผ์ € ์บ์‹œ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
80
- os.makedirs('/tmp/sentence_transformers', exist_ok=True)
81
- os.makedirs('/tmp/transformers', exist_ok=True)
82
-
83
- # sentence-transformers ์ž„ํฌํŠธ๋ฅผ ํ•จ์ˆ˜ ๋‚ด์—์„œ
84
- from sentence_transformers import SentenceTransformer
85
- from sklearn.metrics.pairwise import cosine_similarity
86
-
87
- # ํ•œ๊ตญ์–ด ๋ชจ๋ธ ๋กœ๋”ฉ ์‹œ๋„
88
- embedding_model = SentenceTransformer('jhgan/ko-sbert-nli',
89
- cache_folder='/tmp/sentence_transformers')
90
- total_progress.progress(40)
91
- st.success("โœ… ํ•œ๊ตญ์–ด ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ")
92
-
93
- except Exception as e:
94
- st.error(f"โŒ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
95
- st.error("๐Ÿšจ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ์—†์ด๋Š” RAG ์‹œ์Šคํ…œ์ด ์ž‘๋™ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
96
- return None, None, None, None
97
-
98
- # 3๋‹จ๊ณ„: ๋ฐ์ดํ„ฐ ๋กœ๋“œ (60%)
99
- status_text.text("๐Ÿ“Š ์นดํ”ผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋กœ๋”ฉ ์ค‘...")
100
- try:
101
- df = pd.read_excel('../๊ด‘๊ณ ์นดํ”ผ๋ฐ์ดํ„ฐ_๋ธŒ๋žœ๋“œ์ถ”์ถœ์™„๋ฃŒ.xlsx')
102
- total_progress.progress(60)
103
- st.success(f"โœ… ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์™„๋ฃŒ: {len(df):,}๊ฐœ ์นดํ”ผ")
104
- except Exception as e:
105
- st.error(f"โŒ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
106
- return None, None, None, None
107
-
108
- # 4๋‹จ๊ณ„: ์ž„๋ฒ ๋”ฉ ๋ฐ์ดํ„ฐ ๋กœ๋“œ (90%) - ์ด๊ฒŒ ํ•ต์‹ฌ!
109
- status_text.text("๐Ÿ” ๋ฒกํ„ฐ ์ž„๋ฒ ๋”ฉ ๋กœ๋”ฉ ์ค‘... (RAG ์‹œ์Šคํ…œ ํ•ต์‹ฌ)")
110
- try:
111
- with open('../copy_embeddings.pkl', 'rb') as f:
112
- embeddings_data = pickle.load(f)
113
- embeddings = embeddings_data['embeddings']
114
- total_progress.progress(90)
115
- st.success(f"โœ… ์ž„๋ฒ ๋”ฉ ๋กœ๋”ฉ ์™„๋ฃŒ: {embeddings.shape[0]:,}๊ฐœ ร— {embeddings.shape[1]}์ฐจ์›")
116
- except Exception as e:
117
- st.error(f"โŒ ์ž„๋ฒ ๋”ฉ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
118
- st.error("๐Ÿšจ ์ž„๋ฒ ๋”ฉ ์—†์ด๋Š” ์˜๋ฏธ์  ๊ฒ€์ƒ‰์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค!")
119
- return None, None, None, None
120
-
121
- # 5๋‹จ๊ณ„: ์ตœ์ข… ๊ฒ€์ฆ (100%)
122
- status_text.text("โœจ ์‹œ์Šคํ…œ ๊ฒ€์ฆ ์ค‘...")
123
- if model and embedding_model and df is not None and embeddings is not None:
124
- total_progress.progress(100)
125
- status_text.text("๐ŸŽ‰ RAG ์‹œ์Šคํ…œ ๋กœ๋”ฉ ์™„๋ฃŒ!")
126
-
127
- # ์„ฑ๊ณต ๋ฉ”์‹œ์ง€
128
- success_col1, success_col2, success_col3 = st.columns(3)
129
- with success_col1:
130
- st.metric("์นดํ”ผ ๋ฐ์ดํ„ฐ", f"{len(df):,}๊ฐœ")
131
- with success_col2:
132
- st.metric("์ž„๋ฒ ๋”ฉ ์ฐจ์›", f"{embeddings.shape[1]}D")
133
- with success_col3:
134
- st.metric("๊ฒ€์ƒ‰ ์—”์ง„", "Korean SBERT")
135
-
136
- # ์ง„ํ–‰๋ฅ  ๋ฐ” ์ œ๊ฑฐ
137
- time.sleep(1)
138
- total_progress.empty()
139
- status_text.empty()
140
-
141
- return model, embedding_model, df, embeddings
142
- else:
143
- st.error("โŒ ์‹œ์Šคํ…œ ๋กœ๋”ฉ ์‹คํŒจ: ํ•„์ˆ˜ ๊ตฌ์„ฑ์š”์†Œ ๋ˆ„๋ฝ")
144
- return None, None, None, None
145
-
146
- # ์‹œ์Šคํ…œ ๋กœ๋”ฉ
147
- with st.spinner("๐Ÿš€ AI ์นดํ”ผ๋ผ์ดํ„ฐ ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์ค‘..."):
148
- model, embedding_model, df, embeddings = load_system()
149
-
150
- if model is None or embedding_model is None or df is None or embeddings is None:
151
- st.error("โŒ ์‹œ์Šคํ…œ์„ ๋กœ๋”ฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๊ฑฐ๋‚˜ ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ํ•˜์„ธ์š”.")
152
- st.stop()
153
-
154
- # ์‚ฌ์ด๋“œ๋ฐ” ์„ค์ • (์‹œ์Šคํ…œ ๋กœ๋”ฉ ์„ฑ๊ณต ํ›„)
155
- st.sidebar.success("๐ŸŽ‰ RAG ์‹œ์Šคํ…œ ์ค€๋น„ ์™„๋ฃŒ!")
156
-
157
- # ์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ
158
- categories = ['์ „์ฒด'] + sorted(df['์นดํ…Œ๊ณ ๋ฆฌ'].unique().tolist())
159
- selected_category = st.sidebar.selectbox(
160
- "๐Ÿ“‚ ์นดํ…Œ๊ณ ๋ฆฌ",
161
- categories,
162
- help="ํŠน์ • ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๊ฒ€์ƒ‰์„ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค"
163
- )
164
-
165
- # ํƒ€๊ฒŸ ๊ณ ๊ฐ ์„ค์ •
166
- target_audience = st.sidebar.selectbox(
167
- "๐ŸŽฏ ํƒ€๊ฒŸ ๊ณ ๊ฐ",
168
- ['20๋Œ€', '30๋Œ€', '์ผ๋ฐ˜', '10๋Œ€', '40๋Œ€', '50๋Œ€+', '๋‚จ์„ฑ', '์—ฌ์„ฑ', '์ง์žฅ์ธ', 'ํ•™์ƒ', '์ฃผ๋ถ€'],
169
- help="ํƒ€๊ฒŸ ๊ณ ๊ฐ์— ๋งž๋Š” ํ†ค์•ค๋งค๋„ˆ๋กœ ์นดํ”ผ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค"
170
- )
171
-
172
- # ๋ธŒ๋žœ๋“œ ํ†ค์•ค๋งค๋„ˆ
173
- brand_tone = st.sidebar.selectbox(
174
- "๐ŸŽจ ๋ธŒ๋žœ๋“œ ํ†ค",
175
- ['์„ธ๋ จ๋œ', '์นœ๊ทผํ•œ', '๊ณ ๊ธ‰์Šค๋Ÿฌ์šด', 'ํ™œ๊ธฐ์ฐฌ', '์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š”', '์ Š์€', '๋”ฐ๋œปํ•œ', '์ „๋ฌธ์ ์ธ'],
176
- help="์›ํ•˜๋Š” ๋ธŒ๋žœ๋“œ ์ด๋ฏธ์ง€๋ฅผ ์„ ํƒํ•˜์„ธ์š”"
177
- )
178
-
179
- # ์ฐฝ์˜์„ฑ ์ˆ˜์ค€
180
- creative_level = st.sidebar.select_slider(
181
- "๐Ÿง  ์ฐฝ์˜์„ฑ ์ˆ˜์ค€",
182
- options=['๋ณด์ˆ˜์ ', '๊ท ํ˜•', '์ฐฝ์˜์ '],
183
- value='๊ท ํ˜•',
184
- help="๋ณด์ˆ˜์ : ์•ˆ์ „ํ•œ ํ‘œํ˜„, ์ฐฝ์˜์ : ๋…์ฐฝ์  ํ‘œํ˜„"
185
- )
186
-
187
- # ๋ฉ”์ธ ์ž…๋ ฅ ์˜์—ญ
188
- st.markdown("## ๐Ÿ’ญ ์–ด๋–ค ์นดํ”ผ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”?")
189
-
190
- # ์ž…๋ ฅ ๋ฐฉ์‹ ์„ ํƒ
191
- input_method = st.radio(
192
- "์ž…๋ ฅ ๋ฐฉ์‹ ์„ ํƒ:",
193
- ["์ง์ ‘ ์ž…๋ ฅ", "ํ…œํ”Œ๋ฆฟ ์„ ํƒ"],
194
- horizontal=True
195
- )
196
-
197
- if input_method == "์ง์ ‘ ์ž…๋ ฅ":
198
- user_request = st.text_area(
199
- "์นดํ”ผ ์š”์ฒญ์„ ์ž์„ธํžˆ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”:",
200
- placeholder="์˜ˆ: 30๋Œ€ ์ง์žฅ ์—ฌ์„ฑ์šฉ ํ”„๋ฆฌ๋ฏธ์—„ ์Šคํ‚จ์ผ€์–ด ์‹ ์ œํ’ˆ ๋Ÿฐ์นญ ์นดํ”ผ",
201
- height=100
202
- )
203
- else:
204
- # ํ…œํ”Œ๋ฆฟ ์„ ํƒ
205
- templates = {
206
- "์‹ ์ œํ’ˆ ๋Ÿฐ์นญ": "๋Œ€์ƒ {์นดํ…Œ๊ณ ๋ฆฌ} ์‹ ์ œํ’ˆ ๋Ÿฐ์นญ ์นดํ”ผ",
207
- "ํ• ์ธ ์ด๋ฒคํŠธ": "{์นดํ…Œ๊ณ ๋ฆฌ} ํ• ์ธ ์ด๋ฒคํŠธ ํ”„๋กœ๋ชจ์…˜ ์นดํ”ผ",
208
- "๋ธŒ๋žœ๋“œ ์Šฌ๋กœ๊ฑด": "{์นดํ…Œ๊ณ ๋ฆฌ} ๋ธŒ๋žœ๋“œ์˜ ๋Œ€ํ‘œ ์Šฌ๋กœ๊ฑด",
209
- "์•ฑ/์„œ๋น„์Šค ๋ฆฌ๋‰ด์–ผ": "{์„œ๋น„์Šค๋ช…} ์ƒˆ ๋ฒ„์ „ ์ถœ์‹œ ์นดํ”ผ",
210
- "์‹œ์ฆŒ ํ•œ์ •": "{์‹œ์ฆŒ} ํ•œ์ • {์นดํ…Œ๊ณ ๋ฆฌ} ํŠน๋ณ„ ์—๋””์…˜ ์นดํ”ผ"
211
- }
212
-
213
- selected_template = st.selectbox("ํ…œํ”Œ๋ฆฟ ์„ ํƒ:", list(templates.keys()))
214
-
215
- col1, col2 = st.columns(2)
216
- with col1:
217
- template_category = st.text_input("์ œํ’ˆ/์„œ๋น„์Šค:", value="")
218
- with col2:
219
- if selected_template == "์•ฑ/์„œ๋น„์Šค ๋ฆฌ๋‰ด์–ผ":
220
- service_name = st.text_input("์„œ๋น„์Šค๋ช…:", placeholder="์˜ˆ: ๋ฐฐ๋‹ฌ์•ฑ, ๊ธˆ์œต์•ฑ")
221
- user_request = templates[selected_template].format(์„œ๋น„์Šค๋ช…=service_name)
222
- elif selected_template == "์‹œ์ฆŒ ํ•œ์ •":
223
- season = st.selectbox("์‹œ์ฆŒ:", ["๋ด„", "์—ฌ๋ฆ„", "๊ฐ€์„", "๊ฒจ์šธ", "ํฌ๋ฆฌ์Šค๋งˆ์Šค", "์‹ ๋…„"])
224
- user_request = templates[selected_template].format(์‹œ์ฆŒ=season, ์นดํ…Œ๊ณ ๋ฆฌ=template_category)
225
- else:
226
- user_request = templates[selected_template].format(์นดํ…Œ๊ณ ๋ฆฌ=template_category)
227
-
228
- st.text_area("์ƒ์„ฑ๋œ ์š”์ฒญ:", value=user_request, height=80, disabled=True)
229
-
230
- # ๊ณ ๊ธ‰ ์˜ต์…˜
231
- with st.expander("๐Ÿ”ง ๊ณ ๊ธ‰ ์˜ต์…˜"):
232
- col1, col2 = st.columns(2)
233
- with col1:
234
- num_concepts = st.slider("์ƒ์„ฑํ•  ์ปจ์…‰ ์ˆ˜:", 1, 5, 3)
235
- min_similarity = st.slider("์ตœ์†Œ ์œ ์‚ฌ๋„:", 0.0, 1.0, 0.3, 0.1)
236
- with col2:
237
- show_references = st.checkbox("์ฐธ๊ณ  ์นดํ”ผ ๋ณด๊ธฐ", value=True)
238
- num_references = st.slider("์ฐธ๊ณ  ์นดํ”ผ ์ˆ˜:", 3, 10, 5)
239
-
240
- # RAG ์นดํ”ผ ์ƒ์„ฑ ํ•จ์ˆ˜ (์ž„๋ฒ ๋”ฉ ๊ธฐ๋ฐ˜ ํ•„์ˆ˜!)
241
- def generate_copy_with_rag(user_request, category, target, tone, creative, num_concepts):
242
- """RAG ๊ธฐ๋ฐ˜ ์นดํ”ผ ์ƒ์„ฑ - ์ž„๋ฒ ๋”ฉ ํ•„์ˆ˜ ์‚ฌ์šฉ"""
243
-
244
- if not user_request.strip():
245
- st.error("โŒ ์นดํ”ผ ์š”์ฒญ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”")
246
- return None
247
-
248
- # ์ง„ํ–‰ ์ƒํ™ฉ ํ‘œ์‹œ
249
- progress_bar = st.progress(0)
250
- status_text = st.empty()
251
-
252
- # 1๋‹จ๊ณ„: ์˜๋ฏธ์  ๊ฒ€์ƒ‰ (์ž„๋ฒ ๋”ฉ ๊ธฐ๋ฐ˜)
253
- status_text.text("๐Ÿ” ์˜๋ฏธ์  ๊ฒ€์ƒ‰ ์ค‘... (RAG ํ•ต์‹ฌ ๊ธฐ๋Šฅ)")
254
- progress_bar.progress(20)
255
-
256
- try:
257
- # ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ ์ƒ์„ฑ ๋ฐ ์ž„๋ฒ ๋”ฉ
258
- search_query = f"{user_request} {target} ๊ด‘๊ณ  ์นดํ”ผ"
259
- from sklearn.metrics.pairwise import cosine_similarity
260
- query_embedding = embedding_model.encode([search_query])
261
-
262
- # ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ๋ง
263
- if category != '์ „์ฒด':
264
- filtered_df = df[df['์นดํ…Œ๊ณ ๋ฆฌ'] == category]
265
- else:
266
- filtered_df = df
267
-
268
- progress_bar.progress(40)
269
-
270
- # ์œ ์‚ฌ๋„ ๊ณ„์‚ฐ (์ž„๋ฒ ๋”ฉ์˜ ํ•ต์‹ฌ!)
271
- filtered_indices = filtered_df.index.tolist()
272
- filtered_embeddings = embeddings[filtered_indices]
273
- similarities = cosine_similarity(query_embedding, filtered_embeddings)[0]
274
-
275
- # ์ƒ์œ„ ์ฐธ๊ณ  ์นดํ”ผ ์„ ๋ณ„
276
- top_indices = np.argsort(similarities)[::-1][:num_references]
277
-
278
- reference_copies = []
279
- for idx in top_indices:
280
- original_idx = filtered_indices[idx]
281
- row = df.iloc[original_idx]
282
- if similarities[idx] >= min_similarity:
283
- reference_copies.append({
284
- 'copy': row['์นดํ”ผ ๋‚ด์šฉ'],
285
- 'brand': row['๋ธŒ๋žœ๋“œ'],
286
- 'similarity': similarities[idx]
287
- })
288
-
289
- progress_bar.progress(60)
290
-
291
- if not reference_copies:
292
- st.warning(f"โš ๏ธ ์œ ์‚ฌ๋„ {min_similarity} ์ด์ƒ์ธ ์ฐธ๊ณ  ์นดํ”ผ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์œ ์‚ฌ๋„๋ฅผ ๋‚ฎ์ถฐ๋ณด์„ธ์š”.")
293
- progress_bar.empty()
294
- status_text.empty()
295
- return None
296
-
297
- # 2๋‹จ๊ณ„: AI ์นดํ”ผ ์ƒ์„ฑ
298
- status_text.text("๐Ÿค– AI ์นดํ”ผ ์ƒ์„ฑ ์ค‘...")
299
- progress_bar.progress(80)
300
-
301
- # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
302
- references_text = "\n".join([
303
- f"{i}. \"{ref['copy']}\" - {ref['brand']} (์œ ์‚ฌ๋„: {ref['similarity']:.3f})"
304
- for i, ref in enumerate(reference_copies, 1)
305
- ])
306
-
307
- creativity_guidance = {
308
- "๋ณด์ˆ˜์ ": "์•ˆ์ „ํ•˜๊ณ  ๊ฒ€์ฆ๋œ ํ‘œํ˜„์„ ์‚ฌ์šฉํ•˜์—ฌ",
309
- "๊ท ํ˜•": "์ฐฝ์˜์ ์ด๋ฉด์„œ๋„ ์ ์ ˆํ•œ ์ˆ˜์ค€์—์„œ",
310
- "์ฐฝ์˜์ ": "๋…์ฐฝ์ ์ด๊ณ  ํ˜์‹ ์ ์ธ ํ‘œํ˜„์œผ๋กœ"
311
- }
312
-
313
- prompt = f"""
314
- ๋‹น์‹ ์€ ํ•œ๊ตญ์˜ ์ „๋ฌธ ๊ด‘๊ณ  ์นดํ”ผ๋ผ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.
315
-
316
- **์š”์ฒญ์‚ฌํ•ญ:** {user_request}
317
- **ํƒ€๊ฒŸ ๊ณ ๊ฐ:** {target}
318
- **๋ธŒ๋žœ๋“œ ํ†ค:** {tone}
319
- **์ฐฝ์˜์„ฑ ์ˆ˜์ค€:** {creative}
320
-
321
- **์ฐธ๊ณ  ์นดํ”ผ๋“ค (์˜๋ฏธ์  ์œ ์‚ฌ๋„ ๊ธฐ๋ฐ˜ ์„ ๋ณ„):**
322
- {references_text}
323
-
324
- **์ž‘์„ฑ ๊ฐ€์ด๋“œ๋ผ์ธ:**
325
- 1. ์œ„ ์ฐธ๊ณ  ์นดํ”ผ๋“ค์˜ ์Šคํƒ€์ผ๊ณผ ํ†ค์„ ๋ถ„์„ํ•˜์—ฌ ์œ ์‚ฌํ•œ ๋А๋‚Œ์œผ๋กœ ์ž‘์„ฑ
326
- 2. {creativity_guidance[creative]} ์ƒˆ๋กœ์šด ์นดํ”ผ {num_concepts}๊ฐœ๋ฅผ ์ž‘์„ฑ
327
- 3. ๊ฐ ์นดํ”ผ๋Š” ํ•œ๊ตญ์–ด๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ๋งค๋ ฅ์ ์ด์–ด์•ผ ํ•จ
328
- 4. {target}์—๊ฒŒ ์–ดํ•„ํ•  ์ˆ˜ ์žˆ๋Š” ํ‘œํ˜„ ์‚ฌ์šฉ
329
- 5. {tone} ํ†ค์•ค๋งค๋„ˆ ์œ ์ง€
330
-
331
- **์ถœ๋ ฅ ํ˜•์‹:**
332
- 1. [์นดํ”ผ1]
333
- - ์„ค๋ช…: ์™œ ์ด ์นดํ”ผ๊ฐ€ ํšจ๊ณผ์ ์ธ์ง€ ๊ฐ„๋‹จํžˆ ์„ค๋ช…
334
-
335
- 2. [์นดํ”ผ2]
336
- - ์„ค๋ช…: ์™œ ์ด ์นดํ”ผ๊ฐ€ ํšจ๊ณผ์ ์ธ์ง€ ๊ฐ„๋‹จํžˆ ์„ค๋ช…
337
-
338
- 3. [์นดํ”ผ3]
339
- - ์„ค๋ช…: ์™œ ์ด ์นดํ”ผ๊ฐ€ ํšจ๊ณผ์ ์ธ์ง€ ๊ฐ„๋‹จํžˆ ์„ค๋ช…
340
-
341
- **์ถ”์ฒœ ์นดํ”ผ:** ์œ„ ์ค‘ ๊ฐ€์žฅ ์ถ”์ฒœํ•˜๋Š” ์นดํ”ผ์™€ ์ด์œ 
342
- """
343
-
344
- response = model.generate_content(prompt)
345
- progress_bar.progress(100)
346
- status_text.text("โœ… ์™„๋ฃŒ!")
347
-
348
- time.sleep(0.5)
349
- progress_bar.empty()
350
- status_text.empty()
351
-
352
- return {
353
- 'references': reference_copies,
354
- 'generated_content': response.text,
355
- 'search_info': {
356
- 'query': search_query,
357
- 'total_candidates': len(filtered_df),
358
- 'selected_references': len(reference_copies)
359
- },
360
- 'settings': {
361
- 'category': category,
362
- 'target': target,
363
- 'tone': tone,
364
- 'creative': creative
365
- }
366
- }
367
-
368
- except Exception as e:
369
- st.error(f"โŒ ์นดํ”ผ ์ƒ์„ฑ ์‹คํŒจ: {e}")
370
- progress_bar.empty()
371
- status_text.empty()
372
- return None
373
-
374
- # ์ƒ์„ฑ ๋ฒ„ํŠผ
375
- if st.button("๐Ÿš€ ์นดํ”ผ ์ƒ์„ฑํ•˜๊ธฐ", type="primary", use_container_width=True):
376
-
377
- if not user_request or not user_request.strip():
378
- st.error("โŒ ์นดํ”ผ ์š”์ฒญ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”")
379
- else:
380
- # RAG ์นดํ”ผ ์ƒ์„ฑ
381
- result = generate_copy_with_rag(
382
- user_request=user_request,
383
- category=selected_category,
384
- target=target_audience,
385
- tone=brand_tone,
386
- creative=creative_level,
387
- num_concepts=num_concepts
388
- )
389
-
390
- if result:
391
- # ๊ฒฐ๊ณผ ํ‘œ์‹œ
392
- st.markdown("## ๐ŸŽ‰ ์ƒ์„ฑ๋œ ์นดํ”ผ")
393
- st.markdown("---")
394
-
395
- # ๊ฒ€์ƒ‰ ์ •๋ณด ํ‘œ์‹œ
396
- st.info(f"๐Ÿ” **๊ฒ€์ƒ‰ ์ •๋ณด**: {result['search_info']['total_candidates']:,}๊ฐœ ํ›„๋ณด์—์„œ "
397
- f"{result['search_info']['selected_references']}๊ฐœ ์ฐธ๊ณ  ์นดํ”ผ ์„ ๋ณ„")
398
-
399
- # ์ฐธ๊ณ  ์นดํ”ผ ํ‘œ์‹œ
400
- if show_references and result['references']:
401
- with st.expander("๐Ÿ“š ์ฐธ๊ณ ํ•œ ์นดํ”ผ๋“ค (์˜๋ฏธ์  ์œ ์‚ฌ๋„ ๊ธฐ๋ฐ˜ ์„ ๋ณ„)"):
402
- for i, ref in enumerate(result['references'], 1):
403
- st.markdown(f"**{i}.** \"{ref['copy']}\"")
404
- st.markdown(f" - ๋ธŒ๋žœ๋“œ: {ref['brand']}")
405
- st.markdown(f" - ์œ ์‚ฌ๋„: {ref['similarity']:.3f}")
406
- st.markdown("")
407
-
408
- # ์ƒ์„ฑ๋œ ์นดํ”ผ ํ‘œ์‹œ
409
- st.markdown("### โœจ AI๊ฐ€ ์ƒ์„ฑํ•œ ์นดํ”ผ:")
410
- st.markdown(result['generated_content'])
411
-
412
- # ๊ฒฐ๊ณผ ๋‹ค์šด๋กœ๋“œ
413
- result_json = json.dumps({
414
- 'timestamp': datetime.now().isoformat(),
415
- 'request': user_request,
416
- 'settings': result['settings'],
417
- 'search_info': result['search_info'],
418
- 'generated_content': result['generated_content']
419
- }, ensure_ascii=False, indent=2)
420
-
421
- st.download_button(
422
- label="๐Ÿ’พ ๊ฒฐ๊ณผ ๋‹ค์šด๋กœ๋“œ (JSON)",
423
- data=result_json,
424
- file_name=f"copy_result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
425
- mime="application/json"
426
- )
427
-
428
- # ์‹œ์Šคํ…œ ์ •๋ณด (์‚ฌ์ด๋“œ๋ฐ” ํ•˜๋‹จ)
429
- st.sidebar.markdown("---")
430
- st.sidebar.markdown("### ๐Ÿ“Š RAG ์‹œ์Šคํ…œ ์ •๋ณด")
431
- if df is not None and embeddings is not None:
432
- st.sidebar.markdown(f"**์นดํ”ผ ๋ฐ์ดํ„ฐ**: {len(df):,}๊ฐœ")
433
- st.sidebar.markdown(f"**์นดํ…Œ๊ณ ๋ฆฌ**: {df['์นดํ…Œ๊ณ ๋ฆฌ'].nunique()}๊ฐœ")
434
- st.sidebar.markdown(f"**๋ธŒ๋žœ๋“œ**: {df['๋ธŒ๋žœ๋“œ'].nunique()}๊ฐœ")
435
- st.sidebar.markdown(f"**์ž„๋ฒ ๋”ฉ**: {embeddings.shape[1]}์ฐจ์›")
436
- st.sidebar.markdown("**๊ฒ€์ƒ‰ ์—”์ง„**: Korean SBERT")
437
- st.sidebar.markdown("**ํ˜ธ์ŠคํŒ…**: ๐Ÿค— Hugging Face")
438
-
439
- # ์‚ฌ์šฉ๋ฒ• ๊ฐ€์ด๋“œ
440
- with st.expander("๐Ÿ’ก RAG ์‹œ์Šคํ…œ ์‚ฌ์šฉ๋ฒ• ๊ฐ€์ด๋“œ"):
441
- st.markdown("""
442
- ### ๐ŸŽฏ ํšจ๊ณผ์ ์ธ ์‚ฌ์šฉ๋ฒ•
443
-
444
- **1. ๊ตฌ์ฒด์ ์ธ ์š”์ฒญํ•˜๊ธฐ:**
445
- - โŒ "์นดํ”ผ ์จ์ค˜"
446
- - โœ… "30๋Œ€ ์ง์žฅ ์—ฌ์„ฑ์šฉ ํ”„๋ฆฌ๋ฏธ์—„ ์Šคํ‚จ์ผ€์–ด ์‹ ์ œํ’ˆ ๋Ÿฐ์นญ ์นดํ”ผ"
447
-
448
- **2. RAG ์‹œ์Šคํ…œ์˜ ์žฅ์ :**
449
- - ๐Ÿง  **์˜๋ฏธ์  ๊ฒ€์ƒ‰**: ํ‚ค์›Œ๋“œ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์˜๋ฏธ๊นŒ์ง€ ์ดํ•ด
450
- - ๐ŸŽฏ **๋ฌธ๋งฅ ๋งค์นญ**: ํƒ€๊ฒŸ๊ณผ ์ƒํ™ฉ์— ๋งž๋Š” ์นดํ”ผ ์ž๋™ ์„ ๋ณ„
451
- - ๐Ÿ“Š **๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜**: 37,671๊ฐœ ์‹ค์ œ ์นดํ”ผ์—์„œ ํ•™์Šตํ•œ ํŒจํ„ด
452
-
453
- **3. ์ฐฝ์˜์„ฑ ์กฐ์ ˆ:**
454
- - **๋ณด์ˆ˜์ **: ์•ˆ์ „ํ•œ ํด๋ผ์ด์–ธํŠธ, ๊ฒ€์ฆ๋œ ์ ‘๊ทผ
455
- - **๊ท ํ˜•**: ์ผ๋ฐ˜์ ์ธ ํ”„๋กœ์ ํŠธ (์ถ”์ฒœ!)
456
- - **์ฐฝ์˜์ **: ํ˜์‹ ์  ๋ธŒ๋žœ๋“œ, ํŒŒ๊ฒฉ์  ์บ ํŽ˜์ธ
457
-
458
- **4. ์ฐธ๊ณ  ์นดํ”ผ ํ™œ์šฉ:**
459
- - ์ƒ์„ฑ๋œ ์นดํ”ผ์™€ ์ฐธ๊ณ  ์นดํ”ผ๋ฅผ ๋น„๊ต ๋ถ„์„
460
- - ํŠธ๋ Œ๋“œ์™€ ํŒจํ„ด ํŒŒ์•… ๊ฐ€๋Šฅ
461
- - ๊ฒฝ์Ÿ์‚ฌ ๋ถ„์„ ์ž๋ฃŒ๋กœ ํ™œ์šฉ
462
- """)
463
-
464
- # ํ‘ธํ„ฐ
465
- st.markdown("---")
466
- st.markdown(
467
- "๐Ÿ’ก **AI ์นดํ”ผ๋ผ์ดํ„ฐ** | 37,671๊ฐœ ์‹ค์ œ ๊ด‘๊ณ  ์นดํ”ผ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ | "
468
- "RAG(๊ฒ€์ƒ‰ ์ฆ๊ฐ• ์ƒ์„ฑ) ์‹œ์Šคํ…œ powered by Korean SBERT + Gemini AI"
469
- )
470
-
471
- # ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง (๊ฐœ๋ฐœ์ž์šฉ)
472
- if os.getenv("DEBUG_MODE"):
473
- st.sidebar.markdown("### ๐Ÿ”ง ๋””๋ฒ„๊ทธ ์ •๋ณด")
474
- if 'embeddings' in locals():
475
- st.sidebar.write(f"์ž„๋ฒ ๋”ฉ ๋ฉ”๋ชจ๋ฆฌ: {embeddings.nbytes / (1024*1024):.1f}MB")
476
- st.sidebar.write(f"Streamlit ๋ฒ„์ „: {st.__version__}")