parkkyujin commited on
Commit
369e968
ยท
verified ยท
1 Parent(s): 105c20d

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +89 -153
streamlit_app.py CHANGED
@@ -51,13 +51,10 @@ st.sidebar.markdown("---")
51
  st.sidebar.markdown("### โš™๏ธ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ ์ •๋ณด (๋””๋ฒ„๊น…์šฉ)")
52
  st.sidebar.text(f"Py Exec: {sys.executable}")
53
  st.sidebar.text(f"Py Ver: {sys.version.split()[0]}") # ๊ฐ„๋žตํ•˜๊ฒŒ ๋ฒ„์ „๋งŒ
54
- # st.sidebar.text(f"sys.path: {sys.path}") # ๋„ˆ๋ฌด ๊ธธ์–ด์„œ ์ผ๋‹จ ์ฃผ์„
55
  st.sidebar.text(f"PYTHONPATH: {os.environ.get('PYTHONPATH', 'Not Set')}")
56
  try:
57
- # numpy๋ฅผ ์—ฌ๊ธฐ์„œ ๋‹ค์‹œ ์ž„ํฌํŠธํ•˜๊ณ  ์‚ฌ์šฉ
58
  import numpy as np_runtime_check
59
  st.sidebar.text(f"NumPy Ver (Runtime): {np_runtime_check.__version__}")
60
- # ํ•ต์‹ฌ ๋ชจ๋“ˆ ์ž„ํฌํŠธ ์‹œ๋„
61
  import numpy.core._multiarray_umath
62
  st.sidebar.markdown("โœ… NumPy core modules imported (Runtime)")
63
  except Exception as e:
@@ -75,16 +72,16 @@ default_api_key = os.getenv("GEMINI_API_KEY", "")
75
 
76
  # 2. st.sidebar.text_input์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ API ํ‚ค๋ฅผ ์ž…๋ ฅ๋ฐ›๊ฑฐ๋‚˜,
77
  # ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๊ฐ€์ ธ์˜จ ๊ฐ’์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋ณด์—ฌ์คŒ
78
- api_key_value = st.sidebar.text_input( # ๋ณ€์ˆ˜๋ช…์„ api_key์—์„œ api_key_value ๋“ฑ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ์ˆ˜ ์žˆ์Œ
79
  "๐Ÿ”‘ Gemini API ํ‚ค",
80
- value=default_api_key, # ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฐ’์ด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์„ค์ •๋จ
81
  type="password",
82
  help="ํ™˜๊ฒฝ๋ณ€์ˆ˜์— GEMINI_API_KEY๋กœ ์„ค์ •ํ•˜๋ฉด ์ž๋™ ์ž…๋ ฅ๋ฉ๋‹ˆ๋‹ค",
83
- key="gemini_api_key_input" # <--- ์ด key๋Š” ์œ„์ ฏ ์‹๋ณ„์šฉ, API ํ‚ค ๊ฐ’ ์ž์ฒด๊ฐ€ ์•„๋‹˜
84
  )
85
 
86
  # 3. ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๊ฐ€์ ธ์˜จ API ํ‚ค ๊ฐ’์„ ์‚ฌ์šฉ
87
- if not api_key_value: # api_key_value ๋ณ€์ˆ˜ ์‚ฌ์šฉ
88
  st.warning("โš ๏ธ Gemini API ํ‚ค๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”")
89
  st.info("๐Ÿ’ก Settings โ†’ Repository secrets์—์„œ GEMINI_API_KEY๋ฅผ ์„ค์ •ํ•˜์„ธ์š”")
90
  st.stop()
@@ -95,61 +92,50 @@ if not api_key_value: # api_key_value ๋ณ€์ˆ˜ ์‚ฌ์šฉ
95
  def load_system():
96
  """์‹œ์Šคํ…œ ์ปดํฌ๋„ŒํŠธ ๋กœ๋”ฉ - ์ž„๋ฒ ๋”ฉ ๊ธฐ๋ฐ˜ RAG ์‹œ์Šคํ…œ"""
97
 
98
- # --- ํ•จ์ˆ˜ ์‹œ์ž‘ ์‹œ ๋””๋ฒ„๊น… ์ •๋ณด ์ถ”๊ฐ€ ---
99
  st.write("--- load_system() ์‹œ์ž‘ ---")
100
  st.write(f"Python Executable (load_system): {sys.executable}")
101
  st.write(f"Python Version (load_system): {sys.version}")
102
- # st.write(f"sys.path (load_system): {sys.path}") # ๋„ˆ๋ฌด ๊ธธ์–ด์„œ ์ฃผ์„
103
  st.write(f"PYTHONPATH (load_system): {os.environ.get('PYTHONPATH')}")
104
  try:
105
- import numpy as np_load_system_check # ์ƒˆ ๋ณ„์นญ ์‚ฌ์šฉ
106
  st.write(f"NumPy version (load_system start): {np_load_system_check.__version__}")
107
  import numpy.core._multiarray_umath
108
  st.write("load_system start: Successfully imported numpy.core._multiarray_umath")
109
  except Exception as e:
110
  st.write(f"load_system start: Error importing NumPy parts: {e}")
111
- # --- ๋””๋ฒ„๊น… ์ •๋ณด ๋ ---
112
 
113
  progress_container = st.container()
114
 
115
  with progress_container:
116
- # ์ „์ฒด ์ง„ํ–‰๋ฅ 
117
  total_progress = st.progress(0)
118
  status_text = st.empty()
119
 
120
- # 1๋‹จ๊ณ„: API ์„ค์ • (10%)
121
  status_text.text("๐Ÿ”‘ Gemini API ์ดˆ๊ธฐํ™” ์ค‘...")
122
  try:
123
- genai.configure(api_key=api_key)
124
- model_llm = genai.GenerativeModel('gemini-1.5-flash') # ๋ชจ๋ธ ์ด๋ฆ„ ํ™•์ธ (์ด์ „์—” gemini-2.0-flash)
 
125
  total_progress.progress(10)
126
  st.success("โœ… Gemini API ์„ค์ • ์™„๋ฃŒ")
127
  except Exception as e:
128
  st.error(f"โŒ Gemini API ์„ค์ • ์‹คํŒจ: {e}")
129
  return None, None, None, None
130
 
131
- # 2๋‹จ๊ณ„: ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋“œ (40%)
132
  status_text.text("๐Ÿค– ํ•œ๊ตญ์–ด ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘... (1-2๋ถ„ ์†Œ์š”)")
133
- embedding_model_instance = None # ๋ณ€์ˆ˜๋ช… ๋ณ€๊ฒฝ
134
-
135
  try:
136
- # sentence-transformers ์ž„ํฌํŠธ๋ฅผ ํ•จ์ˆ˜ ๋‚ด์—์„œ ์œ ์ง€
137
  from sentence_transformers import SentenceTransformer
138
- # from sklearn.metrics.pairwise import cosine_similarity # ์—ฌ๊ธฐ์„œ๋Š” ์•„์ง ํ•„์š” ์—†์Œ
139
-
140
  embedding_model_instance = SentenceTransformer('jhgan/ko-sbert-nli',
141
- cache_folder=SENTENCE_TRANSFORMERS_HOME_DIR) # ์ˆ˜์ •๋œ ์บ์‹œ ๊ฒฝ๋กœ ์‚ฌ์šฉ
142
  total_progress.progress(40)
143
  st.success("โœ… ํ•œ๊ตญ์–ด ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ")
144
-
145
  except Exception as e:
146
  st.error(f"โŒ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
147
  st.error("๐Ÿšจ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ์—†์ด๋Š” RAG ์‹œ์Šคํ…œ์ด ์ž‘๋™ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
148
  return None, None, None, None
149
 
150
- # 3๋‹จ๊ณ„: ๋ฐ์ดํ„ฐ ๋กœ๋“œ (60%)
151
  status_text.text("๐Ÿ“Š ์นดํ”ผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋กœ๋”ฉ ์ค‘...")
152
- df_data = None # ๋ณ€์ˆ˜๋ช… ๋ณ€๊ฒฝ
153
  try:
154
  df_data = pd.read_excel('๊ด‘๊ณ ์นดํ”ผ๋ฐ์ดํ„ฐ_๋ธŒ๋žœ๋“œ์ถ”์ถœ์™„๋ฃŒ.xlsx')
155
  total_progress.progress(60)
@@ -158,27 +144,23 @@ def load_system():
158
  st.error(f"โŒ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
159
  return None, None, None, None
160
 
161
- # 4๋‹จ๊ณ„: ์ž„๋ฒ ๋”ฉ ๋ฐ์ดํ„ฐ ๋กœ๋“œ (90%) - ์ด๊ฒŒ ํ•ต์‹ฌ!
162
  status_text.text("๐Ÿ” ๋ฒกํ„ฐ ์ž„๋ฒ ๋”ฉ ๋กœ๋”ฉ ์ค‘... (RAG ์‹œ์Šคํ…œ ํ•ต์‹ฌ)")
163
- embeddings_array = None # ๋ณ€์ˆ˜๋ช… ๋ณ€๊ฒฝ
164
  try:
165
- # --- pickle.load() ์ง์ „ NumPy ๋””๋ฒ„๊น… ---
166
- import numpy as np_pickle_check # ์ƒˆ ๋ณ„์นญ ์‚ฌ์šฉ
167
  st.write(f"[DEBUG] NumPy version just before pickle.load: {np_pickle_check.__version__}")
168
  import numpy.core._multiarray_umath
169
  st.write("[DEBUG] Successfully imported numpy.core._multiarray_umath before pickle.load")
170
- # --- ๋””๋ฒ„๊น… ๋ ---
171
 
172
  with open('copy_embeddings.pkl', 'rb') as f:
173
  embeddings_data = pickle.load(f)
174
  embeddings_array = embeddings_data['embeddings']
175
  total_progress.progress(90)
176
  st.success(f"โœ… ์ž„๋ฒ ๋”ฉ ๋กœ๋”ฉ ์™„๋ฃŒ: {embeddings_array.shape[0]:,}๊ฐœ ร— {embeddings_array.shape[1]}์ฐจ์›")
177
- except ModuleNotFoundError as mnfe: # ModuleNotFoundError๋ฅผ ํŠน์ •ํ•ด์„œ ์žก๊ธฐ
178
  st.error(f"โŒ ์ž„๋ฒ ๋”ฉ ๋กœ๋”ฉ ์‹คํŒจ (ModuleNotFoundError): {mnfe}")
179
  st.error(f"๐Ÿšจ ํ•ด๋‹น ๋ชจ๋“ˆ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. sys.path: {sys.path}")
180
  st.error("๐Ÿšจ ์ž„๋ฒ ๋”ฉ ์—†์ด๋Š” ์˜๋ฏธ์  ๊ฒ€์ƒ‰์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค!")
181
- # ์ถ”๊ฐ€ ๋””๋ฒ„๊น…: ํ˜„์žฌ ๋กœ๋“œ๋œ numpy ๊ฐ์ฒด ์ƒํƒœ
182
  try:
183
  import numpy as np_final_check
184
  st.error(f"[DEBUG] NumPy object at failure: {np_final_check}")
@@ -191,12 +173,10 @@ def load_system():
191
  st.error("๐Ÿšจ ์ž„๋ฒ ๋”ฉ ์—†์ด๋Š” ์˜๋ฏธ์  ๊ฒ€์ƒ‰์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค!")
192
  return None, None, None, None
193
 
194
- # 5๋‹จ๊ณ„: ์ตœ์ข… ๊ฒ€์ฆ (100%)
195
  status_text.text("โœจ ์‹œ์Šคํ…œ ๊ฒ€์ฆ ์ค‘...")
196
  if model_llm and embedding_model_instance and df_data is not None and embeddings_array is not None:
197
  total_progress.progress(100)
198
  status_text.text("๐ŸŽ‰ RAG ์‹œ์Šคํ…œ ๋กœ๋”ฉ ์™„๋ฃŒ!")
199
-
200
  success_col1, success_col2, success_col3 = st.columns(3)
201
  with success_col1:
202
  st.metric("์นดํ”ผ ๋ฐ์ดํ„ฐ", f"{len(df_data):,}๊ฐœ")
@@ -204,18 +184,15 @@ def load_system():
204
  st.metric("์ž„๋ฒ ๋”ฉ ์ฐจ์›", f"{embeddings_array.shape[1]}D")
205
  with success_col3:
206
  st.metric("๊ฒ€์ƒ‰ ์—”์ง„", "Korean SBERT")
207
-
208
  time.sleep(1)
209
  total_progress.empty()
210
  status_text.empty()
211
-
212
- # ์ „์—ญ ๋ณ€์ˆ˜๋ช…๊ณผ์˜ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ํ•จ์ˆ˜ ๋‚ด์—์„œ ์‚ฌ์šฉํ•œ ๋ณ€์ˆ˜๋ช…์œผ๋กœ ๋ฐ˜ํ™˜
213
  return model_llm, embedding_model_instance, df_data, embeddings_array
214
  else:
215
  st.error("โŒ ์‹œ์Šคํ…œ ๋กœ๋”ฉ ์‹คํŒจ: ํ•„์ˆ˜ ๊ตฌ์„ฑ์š”์†Œ ๋ˆ„๋ฝ")
216
  return None, None, None, None
217
 
218
- # ์‹œ์Šคํ…œ ๋กœ๋”ฉ (๋ณ€์ˆ˜๋ช… ์ถฉ๋Œ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ์ƒˆ๋กœ์šด ์ด๋ฆ„ ์‚ฌ์šฉ)
219
  loaded_model, loaded_embedding_model, loaded_df, loaded_embeddings = None, None, None, None
220
  with st.spinner("๐Ÿš€ AI ์นดํ”ผ๋ผ์ดํ„ฐ ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์ค‘..."):
221
  loaded_model, loaded_embedding_model, loaded_df, loaded_embeddings = load_system()
@@ -224,6 +201,7 @@ if loaded_model is None or loaded_embedding_model is None or loaded_df is None o
224
  st.error("โŒ ์‹œ์Šคํ…œ์„ ๋กœ๋”ฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๊ฑฐ๋‚˜ ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ํ•˜์„ธ์š”.")
225
  st.stop()
226
 
 
227
  # ์‚ฌ์ด๋“œ๋ฐ” ์„ค์ • (์‹œ์Šคํ…œ ๋กœ๋”ฉ ์„ฑ๊ณต ํ›„)
228
  st.sidebar.success("๐ŸŽ‰ RAG ์‹œ์Šคํ…œ ์ค€๋น„ ์™„๋ฃŒ!")
229
 
@@ -232,21 +210,24 @@ categories = ['์ „์ฒด'] + sorted(loaded_df['์นดํ…Œ๊ณ ๋ฆฌ'].unique().tolist())
232
  selected_category = st.sidebar.selectbox(
233
  "๐Ÿ“‚ ์นดํ…Œ๊ณ ๋ฆฌ",
234
  categories,
235
- help="ํŠน์ • ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๊ฒ€์ƒ‰์„ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค"
 
236
  )
237
 
238
  # ํƒ€๊ฒŸ ๊ณ ๊ฐ ์„ค์ •
239
  target_audience = st.sidebar.selectbox(
240
  "๐ŸŽฏ ํƒ€๊ฒŸ ๊ณ ๊ฐ",
241
  ['20๋Œ€', '30๋Œ€', '์ผ๋ฐ˜', '10๋Œ€', '40๋Œ€', '50๋Œ€+', '๋‚จ์„ฑ', '์—ฌ์„ฑ', '์ง์žฅ์ธ', 'ํ•™์ƒ', '์ฃผ๋ถ€'],
242
- help="ํƒ€๊ฒŸ ๊ณ ๊ฐ์— ๋งž๋Š” ํ†ค์•ค๋งค๋„ˆ๋กœ ์นดํ”ผ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค"
 
243
  )
244
 
245
  # ๋ธŒ๋žœ๋“œ ํ†ค์•ค๋งค๋„ˆ
246
  brand_tone = st.sidebar.selectbox(
247
  "๐ŸŽจ ๋ธŒ๋žœ๋“œ ํ†ค",
248
  ['์„ธ๋ จ๋œ', '์นœ๊ทผํ•œ', '๊ณ ๊ธ‰์Šค๋Ÿฌ์šด', 'ํ™œ๊ธฐ์ฐฌ', '์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š”', '์ Š์€', '๋”ฐ๋œปํ•œ', '์ „๋ฌธ์ ์ธ'],
249
- help="์›ํ•˜๋Š” ๏ฟฝ๏ฟฝ๏ฟฝ๋žœ๋“œ ์ด๋ฏธ์ง€๋ฅผ ์„ ํƒํ•˜์„ธ์š”"
 
250
  )
251
 
252
  # ์ฐฝ์˜์„ฑ ์ˆ˜์ค€
@@ -254,7 +235,8 @@ creative_level = st.sidebar.select_slider(
254
  "๐Ÿง  ์ฐฝ์˜์„ฑ ์ˆ˜์ค€",
255
  options=['๋ณด์ˆ˜์ ', '๊ท ํ˜•', '์ฐฝ์˜์ '],
256
  value='๊ท ํ˜•',
257
- help="๋ณด์ˆ˜์ : ์•ˆ์ „ํ•œ ํ‘œํ˜„, ์ฐฝ์˜์ : ๋…์ฐฝ์  ํ‘œํ˜„"
 
258
  )
259
 
260
  # ๋ฉ”์ธ ์ž…๋ ฅ ์˜์—ญ
@@ -265,7 +247,7 @@ input_method = st.radio(
265
  "์ž…๋ ฅ ๋ฐฉ์‹ ์„ ํƒ:",
266
  ["์ง์ ‘ ์ž…๋ ฅ", "ํ…œํ”Œ๋ฆฟ ์„ ํƒ"],
267
  horizontal=True,
268
- key="input_method_radio" # ๊ณ ์œ  ํ‚ค ์ถ”๊ฐ€
269
  )
270
 
271
  if input_method == "์ง์ ‘ ์ž…๋ ฅ":
@@ -273,7 +255,7 @@ if input_method == "์ง์ ‘ ์ž…๋ ฅ":
273
  "์นดํ”ผ ์š”์ฒญ์„ ์ž์„ธํžˆ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”:",
274
  placeholder="์˜ˆ: 30๋Œ€ ์ง์žฅ ์—ฌ์„ฑ์šฉ ํ”„๋ฆฌ๋ฏธ์—„ ์Šคํ‚จ์ผ€์–ด ์‹ ์ œํ’ˆ ๋Ÿฐ์นญ ์นดํ”ผ",
275
  height=100,
276
- key="user_request_direct" # ๊ณ ์œ  ํ‚ค ์ถ”๊ฐ€
277
  )
278
  else:
279
  templates = {
@@ -305,7 +287,7 @@ else:
305
 
306
  # ๊ณ ๊ธ‰ ์˜ต์…˜
307
  with st.expander("๐Ÿ”ง ๊ณ ๊ธ‰ ์˜ต์…˜"):
308
- col1_adv, col2_adv = st.columns(2) # ๋ณ€์ˆ˜๋ช… ๋ณ€๊ฒฝ
309
  with col1_adv:
310
  num_concepts = st.slider("์ƒ์„ฑํ•  ์ปจ์…‰ ์ˆ˜:", 1, 5, 3, key="num_concepts_slider")
311
  min_similarity = st.slider("์ตœ์†Œ ์œ ์‚ฌ๋„:", 0.0, 1.0, 0.3, 0.1, key="min_similarity_slider")
@@ -314,90 +296,63 @@ with st.expander("๐Ÿ”ง ๊ณ ๊ธ‰ ์˜ต์…˜"):
314
  num_references = st.slider("์ฐธ๊ณ  ์นดํ”ผ ์ˆ˜:", 3, 10, 5, key="num_references_slider")
315
 
316
  # RAG ์นดํ”ผ ์ƒ์„ฑ ํ•จ์ˆ˜ (์ž„๋ฒ ๋”ฉ ๊ธฐ๋ฐ˜ ํ•„์ˆ˜!)
317
- def generate_copy_with_rag(user_req, category_filter, target_aud, brand_tn, creative_lvl, num_con): # ๋ณ€์ˆ˜๋ช… ๋ณ€๊ฒฝ
318
- """RAG ๊ธฐ๋ฐ˜ ์นดํ”ผ ์ƒ์„ฑ - ์ž„๋ฒ ๋”ฉ ํ•„์ˆ˜ ์‚ฌ์šฉ"""
319
  if not user_req.strip():
320
  st.error("โŒ ์นดํ”ผ ์š”์ฒญ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”")
321
  return None
322
 
323
  progress_bar = st.progress(0)
324
- status_text_gen = st.empty() # ๋ณ€์ˆ˜๋ช… ๋ณ€๊ฒฝ
325
 
326
  status_text_gen.text("๐Ÿ” ์˜๋ฏธ์  ๊ฒ€์ƒ‰ ์ค‘... (RAG ํ•ต์‹ฌ ๊ธฐ๋Šฅ)")
327
  progress_bar.progress(20)
328
 
329
  try:
330
  search_query = f"{user_req} {target_aud} ๊ด‘๊ณ  ์นดํ”ผ"
331
- from sklearn.metrics.pairwise import cosine_similarity # generate_copy_with_rag ๋‚ด์—์„œ ์ž„ํฌํŠธ
332
- query_embedding = loaded_embedding_model.encode([search_query]) # ๋กœ๋“œ๋œ ๋ชจ๋ธ ์‚ฌ์šฉ
333
 
334
  if category_filter != '์ „์ฒด':
335
- filtered_df_gen = loaded_df[loaded_df['์นดํ…Œ๊ณ ๋ฆฌ'] == category_filter].copy() # .copy() ์ถ”๊ฐ€
336
  else:
337
- filtered_df_gen = loaded_df.copy() # .copy() ์ถ”๊ฐ€
338
 
339
  progress_bar.progress(40)
340
 
341
  if filtered_df_gen.empty:
342
  st.warning(f"โš ๏ธ ์„ ํƒํ•˜์‹  ์นดํ…Œ๊ณ ๋ฆฌ '{category_filter}'์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
343
- progress_bar.empty()
344
- status_text_gen.empty()
345
- return None
346
-
347
 
348
  filtered_indices = filtered_df_gen.index.tolist()
349
- # loaded_embeddings์—์„œ ์ง์ ‘ ์ธ๋ฑ์‹ฑํ•˜๊ธฐ ์ „์—, filtered_indices๊ฐ€ loaded_embeddings์˜ ๋ฒ”์œ„ ๋‚ด์— ์žˆ๋Š”์ง€ ํ™•์ธ
350
  valid_indices_for_embedding = [idx for idx in filtered_indices if idx < len(loaded_embeddings)]
351
  if not valid_indices_for_embedding:
352
  st.warning(f"โš ๏ธ ์œ ํšจํ•œ ์ธ๋ฑ์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์–ด ์œ ์‚ฌ๋„ ๊ฒ€์ƒ‰์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. (์นดํ…Œ๊ณ ๋ฆฌ: {category_filter})")
353
- progress_bar.empty()
354
- status_text_gen.empty()
355
- return None
356
-
357
- # ์œ ํšจํ•œ ์ธ๋ฑ์Šค์— ํ•ด๋‹นํ•˜๋Š” ์ž„๋ฒ ๋”ฉ๋งŒ ์‚ฌ์šฉ
358
- # ์ด ๋ถ€๋ถ„์€ ์›๋ณธ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„(loaded_df)์˜ ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•จ
359
- # filtered_df_gen์˜ ์ธ๋ฑ์Šค๋Š” loaded_df์˜ ๋ถ€๋ถ„์ง‘ํ•ฉ์ด๋ฏ€๋กœ,
360
- # loaded_embeddings์—์„œ ์ด ์ธ๋ฑ์Šค๋“ค์„ ์ง์ ‘ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
361
- # ์ฃผ์˜: filtered_indices๋Š” loaded_df์˜ ์‹ค์ œ ์ธ๋ฑ์Šค ๊ฐ’์ด์–ด์•ผ ํ•จ.
362
- # ๋งŒ์•ฝ filtered_df_gen.index๊ฐ€ 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ์ƒˆ๋กœ์šด ์ธ๋ฑ์Šค๋ผ๋ฉด, ๋งคํ•‘ ํ•„์š”.
363
- # ํ˜„์žฌ ์ฝ”๋“œ๋Š” filtered_df.index.tolist()๊ฐ€ ์›๋ณธ ์ธ๋ฑ์Šค๋ฅผ ์œ ์ง€ํ•œ๋‹ค๊ณ  ๊ฐ€์ •.
364
 
365
  filtered_embeddings_for_search = loaded_embeddings[valid_indices_for_embedding]
366
- # ์œ ์‚ฌ๋„ ๊ณ„์‚ฐ ์‹œ query_embedding๊ณผ filtered_embeddings_for_search์˜ ์ฐจ์› ํ™•์ธ
367
  if query_embedding.shape[1] != filtered_embeddings_for_search.shape[1]:
368
  st.error(f"โŒ ์ž„๋ฒ ๋”ฉ ์ฐจ์› ๋ถˆ์ผ์น˜: ์ฟผ๋ฆฌ({query_embedding.shape[1]}D), ๋ฌธ์„œ({filtered_embeddings_for_search.shape[1]}D)")
369
  return None
370
 
371
-
372
  similarities = cosine_similarity(query_embedding, filtered_embeddings_for_search)[0]
373
-
374
- # ์ƒ์œ„ N๊ฐœ (num_references) ์„ ํƒ
375
- # similarities์˜ ๊ธธ์ด๋Š” valid_indices_for_embedding์˜ ๊ธธ์ด์™€ ๊ฐ™์Œ
376
- # top_indices๋Š” similarities ๋ฐฐ์—ด ๋‚ด์˜ ์ธ๋ฑ์Šค
377
  num_to_select = min(num_references, len(similarities))
378
- top_similarity_indices = np.argsort(similarities)[::-1][:num_to_select]
379
-
 
380
 
381
  reference_copies = []
382
  for i in top_similarity_indices:
383
- # i๋Š” similarities ๋ฐฐ์—ด์—์„œ์˜ ์ธ๋ฑ์Šค.
384
- # ์ด ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ valid_indices_for_embedding์—์„œ ์›๋ณธ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์˜ ์ธ๋ฑ์Šค๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•จ.
385
  original_df_idx = valid_indices_for_embedding[i]
386
- row = loaded_df.iloc[original_df_idx] # ์›๋ณธ df์—์„œ ๊ฐ€์ ธ์˜ด
387
  if similarities[i] >= min_similarity:
388
  reference_copies.append({
389
  'copy': row['์นดํ”ผ ๋‚ด์šฉ'],
390
  'brand': row['๋ธŒ๋žœ๋“œ'],
391
- 'similarity': float(similarities[i]) # float์œผ๋กœ ๋ณ€ํ™˜ (JSON ์ง๋ ฌํ™” ๋Œ€๋น„)
392
  })
393
  progress_bar.progress(60)
394
 
395
  if not reference_copies:
396
- st.warning(f"โš ๏ธ ์œ ์‚ฌ๋„ {min_similarity} ์ด์ƒ์ธ ์ฐธ๊ณ  ์นดํ”ผ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์œ ์‚ฌ๋„๋ฅผ ๋‚ฎ์ถฐ๋ณด์„ธ์š”.")
397
- # ์ฐธ๊ณ  ์นดํ”ผ๊ฐ€ ์—†์–ด๋„ LLM์—๊ฒŒ ์ƒ์„ฑ์„ ์š”์ฒญํ•  ์ˆ˜๋Š” ์žˆ๋„๋ก ํ•จ (์„ ํƒ์‚ฌํ•ญ)
398
- # progress_bar.empty()
399
- # status_text_gen.empty()
400
- # return None
401
  references_text_for_prompt = "์œ ์‚ฌ๋„ ๋†’์€ ์ฐธ๊ณ  ์นดํ”ผ๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค."
402
  else:
403
  references_text_for_prompt = "\n".join([
@@ -405,73 +360,50 @@ def generate_copy_with_rag(user_req, category_filter, target_aud, brand_tn, crea
405
  for j, ref in enumerate(reference_copies)
406
  ])
407
 
408
-
409
  status_text_gen.text("๐Ÿค– AI ์นดํ”ผ ์ƒ์„ฑ ์ค‘...")
410
  progress_bar.progress(80)
411
-
412
  creativity_guidance = {
413
- "๋ณด์ˆ˜์ ": "์•ˆ์ „ํ•˜๊ณ  ๊ฒ€์ฆ๋œ ํ‘œํ˜„์„ ์‚ฌ์šฉํ•˜์—ฌ",
414
- "๊ท ํ˜•": "์ฐฝ์˜์ ์ด๋ฉด์„œ๋„ ์ ์ ˆํ•œ ์ˆ˜์ค€์—์„œ",
415
  "์ฐฝ์˜์ ": "๋…์ฐฝ์ ์ด๊ณ  ํ˜์‹ ์ ์ธ ํ‘œํ˜„์œผ๋กœ"
416
  }
417
  prompt = f"""
418
  ๋‹น์‹ ์€ ํ•œ๊ตญ์˜ ์ „๋ฌธ ๊ด‘๊ณ  ์นดํ”ผ๋ผ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.
419
-
420
  **์š”์ฒญ์‚ฌํ•ญ:** {user_req}
421
  **ํƒ€๊ฒŸ ๊ณ ๊ฐ:** {target_aud}
422
  **๋ธŒ๋žœ๋“œ ํ†ค:** {brand_tn}
423
  **์ฐฝ์˜์„ฑ ์ˆ˜์ค€:** {creative_lvl} ({creativity_guidance[creative_lvl]})
424
-
425
  **์ฐธ๊ณ  ์นดํ”ผ๋“ค (์˜๋ฏธ์  ์œ ์‚ฌ๋„ ๊ธฐ๋ฐ˜ ์„ ๋ณ„):**
426
  {references_text_for_prompt}
427
-
428
  **์ž‘์„ฑ ๊ฐ€์ด๋“œ๋ผ์ธ:**
429
  1. ์œ„ ์ฐธ๊ณ  ์นดํ”ผ๋“ค์˜ ์Šคํƒ€์ผ๊ณผ ํ†ค์„ ๋ถ„์„ํ•˜๊ณ , ์š”์ฒญ์‚ฌํ•ญ์— ๋งž์ถฐ ์ƒˆ๋กœ์šด ์นดํ”ผ {num_con}๊ฐœ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.
430
  2. ๋งŒ์•ฝ ์ฐธ๊ณ  ์นดํ”ผ๊ฐ€ ์—†๋‹ค๋ฉด, ์š”์ฒญ์‚ฌํ•ญ๊ณผ ํƒ€๊ฒŸ ๊ณ ๊ฐ, ๋ธŒ๋žœ๋“œ ํ†ค, ์ฐฝ์˜์„ฑ ์ˆ˜์ค€์—๋งŒ ์ง‘์ค‘ํ•˜์—ฌ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.
431
  3. ๊ฐ ์นดํ”ผ๋Š” ํ•œ๊ตญ์–ด๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ๋งค๋ ฅ์ ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
432
  4. {target_aud}์—๊ฒŒ ์–ดํ•„ํ•  ์ˆ˜ ์žˆ๋Š” ํ‘œํ˜„์„ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”.
433
  5. {brand_tn} ํ†ค์•ค๋งค๋„ˆ๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”.
434
-
435
  **์ถœ๋ ฅ ํ˜•์‹ (๊ฐ ์นดํ”ผ์™€ ๊ฐ„๋‹จํ•œ ์„ค๋ช… ํฌํ•จ):**
436
  1. [์ƒ์„ฑ๋œ ์นดํ”ผ 1]
437
  - ์„ค๋ช…: (์ด ์นดํ”ผ๊ฐ€ ์™œ ํšจ๊ณผ์ ์ธ์ง€ ๋˜๋Š” ์–ด๋–ค ์˜๋„๋กœ ์ž‘์„ฑ๋˜์—ˆ๋Š”์ง€)
438
-
439
- 2. [์ƒ์„ฑ๋œ ์นดํ”ผ 2]
440
- - ์„ค๋ช…: (์ด ์นดํ”ผ๊ฐ€ ์™œ ํšจ๊ณผ์ ์ธ์ง€ ๋˜๋Š” ์–ด๋–ค ์˜๋„๋กœ ์ž‘์„ฑ๋˜์—ˆ๋Š”์ง€)
441
  ... (์š”์ฒญํ•œ ์ปจ์…‰ ์ˆ˜๋งŒํผ ๋ฐ˜๋ณต)
442
-
443
  **์ถ”์ฒœ ์นดํ”ผ:** (์œ„ ์ƒ์„ฑ๋œ ์นดํ”ผ ์ค‘ ๊ฐ€์žฅ ์ถ”์ฒœํ•˜๋Š” ๊ฒƒ ํ•˜๋‚˜์™€ ๊ทธ ์ด์œ )
444
  """
445
  response = loaded_model.generate_content(prompt)
446
- progress_bar.progress(100)
447
- status_text_gen.text("โœ… ์™„๋ฃŒ!")
448
- time.sleep(0.5)
449
- progress_bar.empty()
450
- status_text_gen.empty()
451
-
452
  return {
453
- 'references': reference_copies,
454
- 'generated_content': response.text,
455
  'search_info': {
456
- 'query': search_query,
457
- 'total_candidates': len(filtered_df_gen),
458
  'selected_references': len(reference_copies)
459
  },
460
  'settings': {
461
- 'category': category_filter,
462
- 'target': target_aud,
463
- 'tone': brand_tn,
464
- 'creative': creative_lvl
465
  }
466
  }
467
  except Exception as e_gen:
468
- st.error(f"โŒ ์นดํ”ผ ์ƒ์„ฑ ์‹คํŒจ: {e_gen}")
469
- st.error(f"์˜ค๋ฅ˜ ํƒ€์ž…: {type(e_gen)}") # ์˜ค๋ฅ˜ ํƒ€์ž… ์ถœ๋ ฅ
470
- import traceback # ์ƒ์„ธ ํŠธ๋ ˆ์ด์Šค๋ฐฑ
471
- st.error(traceback.format_exc())
472
- progress_bar.empty()
473
- status_text_gen.empty()
474
- return None
475
 
476
  # ์ƒ์„ฑ ๋ฒ„ํŠผ
477
  if st.button("๐Ÿš€ ์นดํ”ผ ์ƒ์„ฑํ•˜๊ธฐ", type="primary", use_container_width=True, key="generate_button"):
@@ -479,16 +411,11 @@ if st.button("๐Ÿš€ ์นดํ”ผ ์ƒ์„ฑํ•˜๊ธฐ", type="primary", use_container_width=Tru
479
  st.error("โŒ ์นดํ”ผ ์š”์ฒญ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”")
480
  else:
481
  result = generate_copy_with_rag(
482
- user_req=user_request,
483
- category_filter=selected_category,
484
- target_aud=target_audience,
485
- brand_tn=brand_tone,
486
- creative_lvl=creative_level,
487
- num_con=num_concepts
488
  )
489
  if result:
490
- st.markdown("## ๐ŸŽ‰ ์ƒ์„ฑ๋œ ์นดํ”ผ")
491
- st.markdown("---")
492
  st.info(f"๐Ÿ” **๊ฒ€์ƒ‰ ์ •๋ณด**: {result['search_info']['total_candidates']:,}๊ฐœ ํ›„๋ณด์—์„œ "
493
  f"{result['search_info']['selected_references']}๊ฐœ ์ฐธ๊ณ  ์นดํ”ผ ์„ ๋ณ„")
494
  if show_references and result['references']:
@@ -496,46 +423,49 @@ if st.button("๐Ÿš€ ์นดํ”ผ ์ƒ์„ฑํ•˜๊ธฐ", type="primary", use_container_width=Tru
496
  for i, ref in enumerate(result['references'], 1):
497
  st.markdown(f"**{i}.** \"{ref['copy']}\"")
498
  st.markdown(f" - ๋ธŒ๋žœ๋“œ: {ref['brand']}")
499
- st.markdown(f" - ์œ ์‚ฌ๋„: {ref['similarity']:.3f}")
500
- st.markdown("")
501
- st.markdown("### โœจ AI๊ฐ€ ์ƒ์„ฑํ•œ ์นดํ”ผ:")
502
- st.markdown(result['generated_content'])
503
  try:
504
  result_json = json.dumps({
505
- 'timestamp': datetime.now().isoformat(),
506
- 'request': user_request,
507
- 'settings': result['settings'],
508
- 'search_info': result['search_info'],
509
- 'generated_content': result['generated_content'],
510
- 'references': result['references'] # ์ฐธ๊ณ  ์นดํ”ผ๋„ JSON์— ํฌํ•จ
511
  }, ensure_ascii=False, indent=2)
512
  st.download_button(
513
- label="๐Ÿ’พ ๊ฒฐ๊ณผ ๋‹ค์šด๋กœ๋“œ (JSON)",
514
- data=result_json,
515
  file_name=f"copy_result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
516
- mime="application/json",
517
- key="download_button"
518
  )
519
- except Exception as e_json:
520
- st.error(f"โŒ ๊ฒฐ๊ณผ ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ ์ƒ์„ฑ ์‹คํŒจ: {e_json}")
521
-
522
 
523
  # ์‹œ์Šคํ…œ ์ •๋ณด (์‚ฌ์ด๋“œ๋ฐ” ํ•˜๋‹จ)
524
- st.sidebar.markdown("---")
525
- st.sidebar.markdown("### ๐Ÿ“Š RAG ์‹œ์Šคํ…œ ์ •๋ณด")
526
  if loaded_df is not None and loaded_embeddings is not None:
527
  st.sidebar.markdown(f"**์นดํ”ผ ๋ฐ์ดํ„ฐ**: {len(loaded_df):,}๊ฐœ")
528
  st.sidebar.markdown(f"**์นดํ…Œ๊ณ ๋ฆฌ**: {loaded_df['์นดํ…Œ๊ณ ๋ฆฌ'].nunique()}๊ฐœ")
529
  st.sidebar.markdown(f"**๋ธŒ๋žœ๋“œ**: {loaded_df['๋ธŒ๋žœ๋“œ'].nunique()}๊ฐœ")
530
- st.sidebar.markdown(f"**์ž„๋ฒ ๋”ฉ**: {loaded_embeddings.shape[1]}์ฐจ์›") # ๋กœ๋“œ๋œ ์ž„๋ฒ ๋”ฉ ์‚ฌ์šฉ
531
- st.sidebar.markdown("**๊ฒ€์ƒ‰ ์—”์ง„**: Korean SBERT")
532
- st.sidebar.markdown("**ํ˜ธ์ŠคํŒ…**: ๐Ÿค— Hugging Face")
533
 
534
  # ์‚ฌ์šฉ๋ฒ• ๊ฐ€์ด๋“œ
535
  with st.expander("๐Ÿ’ก RAG ์‹œ์Šคํ…œ ์‚ฌ์šฉ๋ฒ• ๊ฐ€์ด๋“œ"):
536
  st.markdown("""
537
  ### ๐ŸŽฏ ํšจ๊ณผ์ ์ธ ์‚ฌ์šฉ๋ฒ•
538
- (๊ธฐ์กด ๋‚ด์šฉ๊ณผ ๋™์ผ)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  """)
540
 
541
  # ํ‘ธํ„ฐ
@@ -546,12 +476,18 @@ st.markdown(
546
  )
547
 
548
  # ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง (๊ฐœ๋ฐœ์ž์šฉ)
549
- if os.getenv("DEBUG_MODE") == "true": # ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฐ’์„ ๋ฌธ์ž์—ด "true"๋กœ ๋น„๊ต
550
  st.sidebar.markdown("### ๐Ÿ”ง ๋””๋ฒ„๊ทธ ์ •๋ณด (ํ™œ์„ฑํ™”๋จ)")
551
- if 'loaded_embeddings' in locals() and loaded_embeddings is not None: # ๋กœ๋“œ๋œ ๋ณ€์ˆ˜ ์‚ฌ์šฉ
552
  st.sidebar.write(f"์ž„๋ฒ ๋”ฉ ๋ฉ”๋ชจ๋ฆฌ: {loaded_embeddings.nbytes / (1024*1024):.1f}MB")
553
  st.sidebar.write(f"Streamlit ๋ฒ„์ „: {st.__version__}")
554
  st.sidebar.write(f"Pandas ๋ฒ„์ „: {pd.__version__}")
555
- st.sidebar.write(f"Numpy ๋ฒ„์ „ (Global): {np.__version__ if 'np' in globals() else 'Not imported globally'}")
556
- st.sidebar.write(f"Torch ๋ฒ„์ „: {torch.__version__ if 'torch' in globals() else 'Torch not directly used here'}") # torch๋Š” sentence-transformers ๋‚ด๋ถ€ ์‚ฌ์šฉ
 
 
 
 
 
 
557
  st.sidebar.write(f"google-generativeai ๋ฒ„์ „: {genai.__version__}")
 
51
  st.sidebar.markdown("### โš™๏ธ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ ์ •๋ณด (๋””๋ฒ„๊น…์šฉ)")
52
  st.sidebar.text(f"Py Exec: {sys.executable}")
53
  st.sidebar.text(f"Py Ver: {sys.version.split()[0]}") # ๊ฐ„๋žตํ•˜๊ฒŒ ๋ฒ„์ „๋งŒ
 
54
  st.sidebar.text(f"PYTHONPATH: {os.environ.get('PYTHONPATH', 'Not Set')}")
55
  try:
 
56
  import numpy as np_runtime_check
57
  st.sidebar.text(f"NumPy Ver (Runtime): {np_runtime_check.__version__}")
 
58
  import numpy.core._multiarray_umath
59
  st.sidebar.markdown("โœ… NumPy core modules imported (Runtime)")
60
  except Exception as e:
 
72
 
73
  # 2. st.sidebar.text_input์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ API ํ‚ค๋ฅผ ์ž…๋ ฅ๋ฐ›๊ฑฐ๋‚˜,
74
  # ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๊ฐ€์ ธ์˜จ ๊ฐ’์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋ณด์—ฌ์คŒ
75
+ api_key_value = st.sidebar.text_input(
76
  "๐Ÿ”‘ Gemini API ํ‚ค",
77
+ value=default_api_key,
78
  type="password",
79
  help="ํ™˜๊ฒฝ๋ณ€์ˆ˜์— GEMINI_API_KEY๋กœ ์„ค์ •ํ•˜๋ฉด ์ž๋™ ์ž…๋ ฅ๋ฉ๋‹ˆ๋‹ค",
80
+ key="gemini_api_key_input"
81
  )
82
 
83
  # 3. ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๊ฐ€์ ธ์˜จ API ํ‚ค ๊ฐ’์„ ์‚ฌ์šฉ
84
+ if not api_key_value:
85
  st.warning("โš ๏ธ Gemini API ํ‚ค๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”")
86
  st.info("๐Ÿ’ก Settings โ†’ Repository secrets์—์„œ GEMINI_API_KEY๋ฅผ ์„ค์ •ํ•˜์„ธ์š”")
87
  st.stop()
 
92
  def load_system():
93
  """์‹œ์Šคํ…œ ์ปดํฌ๋„ŒํŠธ ๋กœ๋”ฉ - ์ž„๋ฒ ๋”ฉ ๊ธฐ๋ฐ˜ RAG ์‹œ์Šคํ…œ"""
94
 
 
95
  st.write("--- load_system() ์‹œ์ž‘ ---")
96
  st.write(f"Python Executable (load_system): {sys.executable}")
97
  st.write(f"Python Version (load_system): {sys.version}")
 
98
  st.write(f"PYTHONPATH (load_system): {os.environ.get('PYTHONPATH')}")
99
  try:
100
+ import numpy as np_load_system_check
101
  st.write(f"NumPy version (load_system start): {np_load_system_check.__version__}")
102
  import numpy.core._multiarray_umath
103
  st.write("load_system start: Successfully imported numpy.core._multiarray_umath")
104
  except Exception as e:
105
  st.write(f"load_system start: Error importing NumPy parts: {e}")
 
106
 
107
  progress_container = st.container()
108
 
109
  with progress_container:
 
110
  total_progress = st.progress(0)
111
  status_text = st.empty()
112
 
 
113
  status_text.text("๐Ÿ”‘ Gemini API ์ดˆ๊ธฐํ™” ์ค‘...")
114
  try:
115
+ # ์ „์—ญ ๋ณ€์ˆ˜ api_key_value๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉ
116
+ genai.configure(api_key=api_key_value)
117
+ model_llm = genai.GenerativeModel('gemini-1.5-flash')
118
  total_progress.progress(10)
119
  st.success("โœ… Gemini API ์„ค์ • ์™„๋ฃŒ")
120
  except Exception as e:
121
  st.error(f"โŒ Gemini API ์„ค์ • ์‹คํŒจ: {e}")
122
  return None, None, None, None
123
 
 
124
  status_text.text("๐Ÿค– ํ•œ๊ตญ์–ด ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘... (1-2๋ถ„ ์†Œ์š”)")
125
+ embedding_model_instance = None
 
126
  try:
 
127
  from sentence_transformers import SentenceTransformer
 
 
128
  embedding_model_instance = SentenceTransformer('jhgan/ko-sbert-nli',
129
+ cache_folder=SENTENCE_TRANSFORMERS_HOME_DIR)
130
  total_progress.progress(40)
131
  st.success("โœ… ํ•œ๊ตญ์–ด ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ")
 
132
  except Exception as e:
133
  st.error(f"โŒ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
134
  st.error("๐Ÿšจ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ์—†์ด๋Š” RAG ์‹œ์Šคํ…œ์ด ์ž‘๋™ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
135
  return None, None, None, None
136
 
 
137
  status_text.text("๐Ÿ“Š ์นดํ”ผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋กœ๋”ฉ ์ค‘...")
138
+ df_data = None
139
  try:
140
  df_data = pd.read_excel('๊ด‘๊ณ ์นดํ”ผ๋ฐ์ดํ„ฐ_๋ธŒ๋žœ๋“œ์ถ”์ถœ์™„๋ฃŒ.xlsx')
141
  total_progress.progress(60)
 
144
  st.error(f"โŒ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
145
  return None, None, None, None
146
 
 
147
  status_text.text("๐Ÿ” ๋ฒกํ„ฐ ์ž„๋ฒ ๋”ฉ ๋กœ๋”ฉ ์ค‘... (RAG ์‹œ์Šคํ…œ ํ•ต์‹ฌ)")
148
+ embeddings_array = None
149
  try:
150
+ import numpy as np_pickle_check
 
151
  st.write(f"[DEBUG] NumPy version just before pickle.load: {np_pickle_check.__version__}")
152
  import numpy.core._multiarray_umath
153
  st.write("[DEBUG] Successfully imported numpy.core._multiarray_umath before pickle.load")
 
154
 
155
  with open('copy_embeddings.pkl', 'rb') as f:
156
  embeddings_data = pickle.load(f)
157
  embeddings_array = embeddings_data['embeddings']
158
  total_progress.progress(90)
159
  st.success(f"โœ… ์ž„๋ฒ ๋”ฉ ๋กœ๋”ฉ ์™„๋ฃŒ: {embeddings_array.shape[0]:,}๊ฐœ ร— {embeddings_array.shape[1]}์ฐจ์›")
160
+ except ModuleNotFoundError as mnfe:
161
  st.error(f"โŒ ์ž„๋ฒ ๋”ฉ ๋กœ๋”ฉ ์‹คํŒจ (ModuleNotFoundError): {mnfe}")
162
  st.error(f"๐Ÿšจ ํ•ด๋‹น ๋ชจ๋“ˆ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. sys.path: {sys.path}")
163
  st.error("๐Ÿšจ ์ž„๋ฒ ๋”ฉ ์—†์ด๋Š” ์˜๋ฏธ์  ๊ฒ€์ƒ‰์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค!")
 
164
  try:
165
  import numpy as np_final_check
166
  st.error(f"[DEBUG] NumPy object at failure: {np_final_check}")
 
173
  st.error("๐Ÿšจ ์ž„๋ฒ ๋”ฉ ์—†์ด๋Š” ์˜๋ฏธ์  ๊ฒ€์ƒ‰์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค!")
174
  return None, None, None, None
175
 
 
176
  status_text.text("โœจ ์‹œ์Šคํ…œ ๊ฒ€์ฆ ์ค‘...")
177
  if model_llm and embedding_model_instance and df_data is not None and embeddings_array is not None:
178
  total_progress.progress(100)
179
  status_text.text("๐ŸŽ‰ RAG ์‹œ์Šคํ…œ ๋กœ๋”ฉ ์™„๋ฃŒ!")
 
180
  success_col1, success_col2, success_col3 = st.columns(3)
181
  with success_col1:
182
  st.metric("์นดํ”ผ ๋ฐ์ดํ„ฐ", f"{len(df_data):,}๊ฐœ")
 
184
  st.metric("์ž„๋ฒ ๋”ฉ ์ฐจ์›", f"{embeddings_array.shape[1]}D")
185
  with success_col3:
186
  st.metric("๊ฒ€์ƒ‰ ์—”์ง„", "Korean SBERT")
 
187
  time.sleep(1)
188
  total_progress.empty()
189
  status_text.empty()
 
 
190
  return model_llm, embedding_model_instance, df_data, embeddings_array
191
  else:
192
  st.error("โŒ ์‹œ์Šคํ…œ ๋กœ๋”ฉ ์‹คํŒจ: ํ•„์ˆ˜ ๊ตฌ์„ฑ์š”์†Œ ๋ˆ„๋ฝ")
193
  return None, None, None, None
194
 
195
+ # ์‹œ์Šคํ…œ ๋กœ๋”ฉ
196
  loaded_model, loaded_embedding_model, loaded_df, loaded_embeddings = None, None, None, None
197
  with st.spinner("๐Ÿš€ AI ์นดํ”ผ๋ผ์ดํ„ฐ ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์ค‘..."):
198
  loaded_model, loaded_embedding_model, loaded_df, loaded_embeddings = load_system()
 
201
  st.error("โŒ ์‹œ์Šคํ…œ์„ ๋กœ๋”ฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๊ฑฐ๋‚˜ ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ํ•˜์„ธ์š”.")
202
  st.stop()
203
 
204
+ # ์ดํ•˜ UI ๋ฐ ์นดํ”ผ ์ƒ์„ฑ ๋กœ์ง (์ด์ „๊ณผ ๋™์ผํ•˜๊ฒŒ ์œ ์ง€)
205
  # ์‚ฌ์ด๋“œ๋ฐ” ์„ค์ • (์‹œ์Šคํ…œ ๋กœ๋”ฉ ์„ฑ๊ณต ํ›„)
206
  st.sidebar.success("๐ŸŽ‰ RAG ์‹œ์Šคํ…œ ์ค€๋น„ ์™„๋ฃŒ!")
207
 
 
210
  selected_category = st.sidebar.selectbox(
211
  "๐Ÿ“‚ ์นดํ…Œ๊ณ ๋ฆฌ",
212
  categories,
213
+ help="ํŠน์ • ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๊ฒ€์ƒ‰์„ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค",
214
+ key="category_selectbox" # ํ‚ค ์ถ”๊ฐ€
215
  )
216
 
217
  # ํƒ€๊ฒŸ ๊ณ ๊ฐ ์„ค์ •
218
  target_audience = st.sidebar.selectbox(
219
  "๐ŸŽฏ ํƒ€๊ฒŸ ๊ณ ๊ฐ",
220
  ['20๋Œ€', '30๋Œ€', '์ผ๋ฐ˜', '10๋Œ€', '40๋Œ€', '50๋Œ€+', '๋‚จ์„ฑ', '์—ฌ์„ฑ', '์ง์žฅ์ธ', 'ํ•™์ƒ', '์ฃผ๋ถ€'],
221
+ help="ํƒ€๊ฒŸ ๊ณ ๊ฐ์— ๋งž๋Š” ํ†ค์•ค๋งค๋„ˆ๋กœ ์นดํ”ผ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค",
222
+ key="target_audience_selectbox" # ํ‚ค ์ถ”๊ฐ€
223
  )
224
 
225
  # ๋ธŒ๋žœ๋“œ ํ†ค์•ค๋งค๋„ˆ
226
  brand_tone = st.sidebar.selectbox(
227
  "๐ŸŽจ ๋ธŒ๋žœ๋“œ ํ†ค",
228
  ['์„ธ๋ จ๋œ', '์นœ๊ทผํ•œ', '๊ณ ๊ธ‰์Šค๋Ÿฌ์šด', 'ํ™œ๊ธฐ์ฐฌ', '์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š”', '์ Š์€', '๋”ฐ๋œปํ•œ', '์ „๋ฌธ์ ์ธ'],
229
+ help="์›ํ•˜๋Š” ๋ธŒ๋žœ๋“œ ์ด๋ฏธ์ง€๋ฅผ ์„ ํƒํ•˜์„ธ์š”",
230
+ key="brand_tone_selectbox" # ํ‚ค ์ถ”๊ฐ€
231
  )
232
 
233
  # ์ฐฝ์˜์„ฑ ์ˆ˜์ค€
 
235
  "๐Ÿง  ์ฐฝ์˜์„ฑ ์ˆ˜์ค€",
236
  options=['๋ณด์ˆ˜์ ', '๊ท ํ˜•', '์ฐฝ์˜์ '],
237
  value='๊ท ํ˜•',
238
+ help="๋ณด์ˆ˜์ : ์•ˆ์ „ํ•œ ํ‘œํ˜„, ์ฐฝ์˜์ : ๋…์ฐฝ์  ํ‘œํ˜„",
239
+ key="creative_level_slider" # ํ‚ค ์ถ”๊ฐ€
240
  )
241
 
242
  # ๋ฉ”์ธ ์ž…๋ ฅ ์˜์—ญ
 
247
  "์ž…๋ ฅ ๋ฐฉ์‹ ์„ ํƒ:",
248
  ["์ง์ ‘ ์ž…๋ ฅ", "ํ…œํ”Œ๋ฆฟ ์„ ํƒ"],
249
  horizontal=True,
250
+ key="input_method_radio"
251
  )
252
 
253
  if input_method == "์ง์ ‘ ์ž…๋ ฅ":
 
255
  "์นดํ”ผ ์š”์ฒญ์„ ์ž์„ธํžˆ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”:",
256
  placeholder="์˜ˆ: 30๋Œ€ ์ง์žฅ ์—ฌ์„ฑ์šฉ ํ”„๋ฆฌ๋ฏธ์—„ ์Šคํ‚จ์ผ€์–ด ์‹ ์ œํ’ˆ ๋Ÿฐ์นญ ์นดํ”ผ",
257
  height=100,
258
+ key="user_request_direct"
259
  )
260
  else:
261
  templates = {
 
287
 
288
  # ๊ณ ๊ธ‰ ์˜ต์…˜
289
  with st.expander("๐Ÿ”ง ๊ณ ๊ธ‰ ์˜ต์…˜"):
290
+ col1_adv, col2_adv = st.columns(2)
291
  with col1_adv:
292
  num_concepts = st.slider("์ƒ์„ฑํ•  ์ปจ์…‰ ์ˆ˜:", 1, 5, 3, key="num_concepts_slider")
293
  min_similarity = st.slider("์ตœ์†Œ ์œ ์‚ฌ๋„:", 0.0, 1.0, 0.3, 0.1, key="min_similarity_slider")
 
296
  num_references = st.slider("์ฐธ๊ณ  ์นดํ”ผ ์ˆ˜:", 3, 10, 5, key="num_references_slider")
297
 
298
  # RAG ์นดํ”ผ ์ƒ์„ฑ ํ•จ์ˆ˜ (์ž„๋ฒ ๋”ฉ ๊ธฐ๋ฐ˜ ํ•„์ˆ˜!)
299
+ def generate_copy_with_rag(user_req, category_filter, target_aud, brand_tn, creative_lvl, num_con):
 
300
  if not user_req.strip():
301
  st.error("โŒ ์นดํ”ผ ์š”์ฒญ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”")
302
  return None
303
 
304
  progress_bar = st.progress(0)
305
+ status_text_gen = st.empty()
306
 
307
  status_text_gen.text("๐Ÿ” ์˜๋ฏธ์  ๊ฒ€์ƒ‰ ์ค‘... (RAG ํ•ต์‹ฌ ๊ธฐ๋Šฅ)")
308
  progress_bar.progress(20)
309
 
310
  try:
311
  search_query = f"{user_req} {target_aud} ๊ด‘๊ณ  ์นดํ”ผ"
312
+ from sklearn.metrics.pairwise import cosine_similarity
313
+ query_embedding = loaded_embedding_model.encode([search_query])
314
 
315
  if category_filter != '์ „์ฒด':
316
+ filtered_df_gen = loaded_df[loaded_df['์นดํ…Œ๊ณ ๋ฆฌ'] == category_filter].copy()
317
  else:
318
+ filtered_df_gen = loaded_df.copy()
319
 
320
  progress_bar.progress(40)
321
 
322
  if filtered_df_gen.empty:
323
  st.warning(f"โš ๏ธ ์„ ํƒํ•˜์‹  ์นดํ…Œ๊ณ ๋ฆฌ '{category_filter}'์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
324
+ progress_bar.empty(); status_text_gen.empty(); return None
 
 
 
325
 
326
  filtered_indices = filtered_df_gen.index.tolist()
 
327
  valid_indices_for_embedding = [idx for idx in filtered_indices if idx < len(loaded_embeddings)]
328
  if not valid_indices_for_embedding:
329
  st.warning(f"โš ๏ธ ์œ ํšจํ•œ ์ธ๋ฑ์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์–ด ์œ ์‚ฌ๋„ ๊ฒ€์ƒ‰์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. (์นดํ…Œ๊ณ ๋ฆฌ: {category_filter})")
330
+ progress_bar.empty(); status_text_gen.empty(); return None
 
 
 
 
 
 
 
 
 
 
331
 
332
  filtered_embeddings_for_search = loaded_embeddings[valid_indices_for_embedding]
 
333
  if query_embedding.shape[1] != filtered_embeddings_for_search.shape[1]:
334
  st.error(f"โŒ ์ž„๋ฒ ๋”ฉ ์ฐจ์› ๋ถˆ์ผ์น˜: ์ฟผ๋ฆฌ({query_embedding.shape[1]}D), ๋ฌธ์„œ({filtered_embeddings_for_search.shape[1]}D)")
335
  return None
336
 
 
337
  similarities = cosine_similarity(query_embedding, filtered_embeddings_for_search)[0]
 
 
 
 
338
  num_to_select = min(num_references, len(similarities))
339
+ # numpy๋ฅผ ์—ฌ๊ธฐ์„œ ๋‹ค์‹œ ์ž„ํฌํŠธํ•˜์—ฌ ์‚ฌ์šฉ (np ๋ณ„์นญ ์‚ฌ์šฉ)
340
+ import numpy as np_generate_rag
341
+ top_similarity_indices = np_generate_rag.argsort(similarities)[::-1][:num_to_select]
342
 
343
  reference_copies = []
344
  for i in top_similarity_indices:
 
 
345
  original_df_idx = valid_indices_for_embedding[i]
346
+ row = loaded_df.iloc[original_df_idx]
347
  if similarities[i] >= min_similarity:
348
  reference_copies.append({
349
  'copy': row['์นดํ”ผ ๋‚ด์šฉ'],
350
  'brand': row['๋ธŒ๋žœ๋“œ'],
351
+ 'similarity': float(similarities[i])
352
  })
353
  progress_bar.progress(60)
354
 
355
  if not reference_copies:
 
 
 
 
 
356
  references_text_for_prompt = "์œ ์‚ฌ๋„ ๋†’์€ ์ฐธ๊ณ  ์นดํ”ผ๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค."
357
  else:
358
  references_text_for_prompt = "\n".join([
 
360
  for j, ref in enumerate(reference_copies)
361
  ])
362
 
 
363
  status_text_gen.text("๐Ÿค– AI ์นดํ”ผ ์ƒ์„ฑ ์ค‘...")
364
  progress_bar.progress(80)
 
365
  creativity_guidance = {
366
+ "๋ณด์ˆ˜์ ": "์•ˆ์ „ํ•˜๊ณ  ๊ฒ€์ฆ๋œ ํ‘œํ˜„์„ ์‚ฌ์šฉํ•˜์—ฌ", "๊ท ํ˜•": "์ฐฝ์˜์ ์ด๋ฉด์„œ๋„ ์ ์ ˆํ•œ ์ˆ˜์ค€์—์„œ",
 
367
  "์ฐฝ์˜์ ": "๋…์ฐฝ์ ์ด๊ณ  ํ˜์‹ ์ ์ธ ํ‘œํ˜„์œผ๋กœ"
368
  }
369
  prompt = f"""
370
  ๋‹น์‹ ์€ ํ•œ๊ตญ์˜ ์ „๋ฌธ ๊ด‘๊ณ  ์นดํ”ผ๋ผ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.
 
371
  **์š”์ฒญ์‚ฌํ•ญ:** {user_req}
372
  **ํƒ€๊ฒŸ ๊ณ ๊ฐ:** {target_aud}
373
  **๋ธŒ๋žœ๋“œ ํ†ค:** {brand_tn}
374
  **์ฐฝ์˜์„ฑ ์ˆ˜์ค€:** {creative_lvl} ({creativity_guidance[creative_lvl]})
 
375
  **์ฐธ๊ณ  ์นดํ”ผ๋“ค (์˜๋ฏธ์  ์œ ์‚ฌ๋„ ๊ธฐ๋ฐ˜ ์„ ๋ณ„):**
376
  {references_text_for_prompt}
 
377
  **์ž‘์„ฑ ๊ฐ€์ด๋“œ๋ผ์ธ:**
378
  1. ์œ„ ์ฐธ๊ณ  ์นดํ”ผ๋“ค์˜ ์Šคํƒ€์ผ๊ณผ ํ†ค์„ ๋ถ„์„ํ•˜๊ณ , ์š”์ฒญ์‚ฌํ•ญ์— ๋งž์ถฐ ์ƒˆ๋กœ์šด ์นดํ”ผ {num_con}๊ฐœ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.
379
  2. ๋งŒ์•ฝ ์ฐธ๊ณ  ์นดํ”ผ๊ฐ€ ์—†๋‹ค๋ฉด, ์š”์ฒญ์‚ฌํ•ญ๊ณผ ํƒ€๊ฒŸ ๊ณ ๊ฐ, ๋ธŒ๋žœ๋“œ ํ†ค, ์ฐฝ์˜์„ฑ ์ˆ˜์ค€์—๋งŒ ์ง‘์ค‘ํ•˜์—ฌ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.
380
  3. ๊ฐ ์นดํ”ผ๋Š” ํ•œ๊ตญ์–ด๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ๋งค๋ ฅ์ ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
381
  4. {target_aud}์—๊ฒŒ ์–ดํ•„ํ•  ์ˆ˜ ์žˆ๋Š” ํ‘œํ˜„์„ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”.
382
  5. {brand_tn} ํ†ค์•ค๋งค๋„ˆ๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”.
 
383
  **์ถœ๋ ฅ ํ˜•์‹ (๊ฐ ์นดํ”ผ์™€ ๊ฐ„๋‹จํ•œ ์„ค๋ช… ํฌํ•จ):**
384
  1. [์ƒ์„ฑ๋œ ์นดํ”ผ 1]
385
  - ์„ค๋ช…: (์ด ์นดํ”ผ๊ฐ€ ์™œ ํšจ๊ณผ์ ์ธ์ง€ ๋˜๋Š” ์–ด๋–ค ์˜๋„๋กœ ์ž‘์„ฑ๋˜์—ˆ๋Š”์ง€)
 
 
 
386
  ... (์š”์ฒญํ•œ ์ปจ์…‰ ์ˆ˜๋งŒํผ ๋ฐ˜๋ณต)
 
387
  **์ถ”์ฒœ ์นดํ”ผ:** (์œ„ ์ƒ์„ฑ๋œ ์นดํ”ผ ์ค‘ ๊ฐ€์žฅ ์ถ”์ฒœํ•˜๋Š” ๊ฒƒ ํ•˜๋‚˜์™€ ๊ทธ ์ด์œ )
388
  """
389
  response = loaded_model.generate_content(prompt)
390
+ progress_bar.progress(100); status_text_gen.text("โœ… ์™„๋ฃŒ!"); time.sleep(0.5)
391
+ progress_bar.empty(); status_text_gen.empty()
 
 
 
 
392
  return {
393
+ 'references': reference_copies, 'generated_content': response.text,
 
394
  'search_info': {
395
+ 'query': search_query, 'total_candidates': len(filtered_df_gen),
 
396
  'selected_references': len(reference_copies)
397
  },
398
  'settings': {
399
+ 'category': category_filter, 'target': target_aud,
400
+ 'tone': brand_tn, 'creative': creative_lvl
 
 
401
  }
402
  }
403
  except Exception as e_gen:
404
+ st.error(f"โŒ ์นดํ”ผ ์ƒ์„ฑ ์‹คํŒจ: {e_gen}"); st.error(f"์˜ค๋ฅ˜ ํƒ€์ž…: {type(e_gen)}")
405
+ import traceback; st.error(traceback.format_exc())
406
+ progress_bar.empty(); status_text_gen.empty(); return None
 
 
 
 
407
 
408
  # ์ƒ์„ฑ ๋ฒ„ํŠผ
409
  if st.button("๐Ÿš€ ์นดํ”ผ ์ƒ์„ฑํ•˜๊ธฐ", type="primary", use_container_width=True, key="generate_button"):
 
411
  st.error("โŒ ์นดํ”ผ ์š”์ฒญ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”")
412
  else:
413
  result = generate_copy_with_rag(
414
+ user_req=user_request, category_filter=selected_category, target_aud=target_audience,
415
+ brand_tn=brand_tone, creative_lvl=creative_level, num_con=num_concepts
 
 
 
 
416
  )
417
  if result:
418
+ st.markdown("## ๐ŸŽ‰ ์ƒ์„ฑ๋œ ์นดํ”ผ"); st.markdown("---")
 
419
  st.info(f"๐Ÿ” **๊ฒ€์ƒ‰ ์ •๋ณด**: {result['search_info']['total_candidates']:,}๊ฐœ ํ›„๋ณด์—์„œ "
420
  f"{result['search_info']['selected_references']}๊ฐœ ์ฐธ๊ณ  ์นดํ”ผ ์„ ๋ณ„")
421
  if show_references and result['references']:
 
423
  for i, ref in enumerate(result['references'], 1):
424
  st.markdown(f"**{i}.** \"{ref['copy']}\"")
425
  st.markdown(f" - ๋ธŒ๋žœ๋“œ: {ref['brand']}")
426
+ st.markdown(f" - ์œ ์‚ฌ๋„: {ref['similarity']:.3f}"); st.markdown("")
427
+ st.markdown("### โœจ AI๊ฐ€ ์ƒ์„ฑํ•œ ์นดํ”ผ:"); st.markdown(result['generated_content'])
 
 
428
  try:
429
  result_json = json.dumps({
430
+ 'timestamp': datetime.now().isoformat(), 'request': user_request,
431
+ 'settings': result['settings'], 'search_info': result['search_info'],
432
+ 'generated_content': result['generated_content'], 'references': result['references']
 
 
 
433
  }, ensure_ascii=False, indent=2)
434
  st.download_button(
435
+ label="๐Ÿ’พ ๊ฒฐ๊ณผ ๋‹ค์šด๋กœ๋“œ (JSON)", data=result_json,
 
436
  file_name=f"copy_result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
437
+ mime="application/json", key="download_button"
 
438
  )
439
+ except Exception as e_json: st.error(f"โŒ ๊ฒฐ๊ณผ ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ ์ƒ์„ฑ ์‹คํŒจ: {e_json}")
 
 
440
 
441
  # ์‹œ์Šคํ…œ ์ •๋ณด (์‚ฌ์ด๋“œ๋ฐ” ํ•˜๋‹จ)
442
+ st.sidebar.markdown("---"); st.sidebar.markdown("### ๐Ÿ“Š RAG ์‹œ์Šคํ…œ ์ •๋ณด")
 
443
  if loaded_df is not None and loaded_embeddings is not None:
444
  st.sidebar.markdown(f"**์นดํ”ผ ๋ฐ์ดํ„ฐ**: {len(loaded_df):,}๊ฐœ")
445
  st.sidebar.markdown(f"**์นดํ…Œ๊ณ ๋ฆฌ**: {loaded_df['์นดํ…Œ๊ณ ๋ฆฌ'].nunique()}๊ฐœ")
446
  st.sidebar.markdown(f"**๋ธŒ๋žœ๋“œ**: {loaded_df['๋ธŒ๋žœ๋“œ'].nunique()}๊ฐœ")
447
+ st.sidebar.markdown(f"**์ž„๋ฒ ๋”ฉ**: {loaded_embeddings.shape[1]}์ฐจ์›")
448
+ st.sidebar.markdown("**๊ฒ€์ƒ‰ ์—”์ง„**: Korean SBERT"); st.sidebar.markdown("**ํ˜ธ์ŠคํŒ…**: ๐Ÿค— Hugging Face")
 
449
 
450
  # ์‚ฌ์šฉ๋ฒ• ๊ฐ€์ด๋“œ
451
  with st.expander("๐Ÿ’ก RAG ์‹œ์Šคํ…œ ์‚ฌ์šฉ๋ฒ• ๊ฐ€์ด๋“œ"):
452
  st.markdown("""
453
  ### ๐ŸŽฏ ํšจ๊ณผ์ ์ธ ์‚ฌ์šฉ๋ฒ•
454
+ **1. ๊ตฌ์ฒด์ ์ธ ์š”์ฒญํ•˜๊ธฐ:**
455
+ - โŒ "์นดํ”ผ ์จ์ค˜"
456
+ - โœ… "30๋Œ€ ์ง์žฅ ์—ฌ์„ฑ์šฉ ํ”„๋ฆฌ๋ฏธ์—„ ์Šคํ‚จ์ผ€์–ด ์‹ ์ œํ’ˆ ๋Ÿฐ์นญ ์นดํ”ผ"
457
+ **2. RAG ์‹œ์Šคํ…œ์˜ ์žฅ์ :**
458
+ - ๐Ÿง  **์˜๋ฏธ์  ๊ฒ€์ƒ‰**: ํ‚ค์›Œ๋“œ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์˜๋ฏธ๊นŒ์ง€ ์ดํ•ด
459
+ - ๐ŸŽฏ **๋ฌธ๋งฅ ๋งค์นญ**: ํƒ€๊ฒŸ๊ณผ ์ƒํ™ฉ์— ๋งž๋Š” ์นดํ”ผ ์ž๋™ ์„ ๋ณ„
460
+ - ๐Ÿ“Š **๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜**: 37,671๊ฐœ ์‹ค์ œ ์นดํ”ผ์—์„œ ํ•™์Šตํ•œ ํŒจํ„ด
461
+ **3. ์ฐฝ์˜์„ฑ ์กฐ์ ˆ:**
462
+ - **๋ณด์ˆ˜์ **: ์•ˆ์ „ํ•œ ํด๋ผ์ด์–ธํŠธ, ๊ฒ€์ฆ๋œ ์ ‘๊ทผ
463
+ - **๊ท ํ˜•**: ์ผ๋ฐ˜์ ์ธ ํ”„๋กœ์ ํŠธ (์ถ”์ฒœ!)
464
+ - **์ฐฝ์˜์ **: ํ˜์‹ ์  ๋ธŒ๋žœ๋“œ, ํŒŒ๊ฒฉ์  ์บ ํŽ˜์ธ
465
+ **4. ์ฐธ๊ณ  ์นดํ”ผ ํ™œ์šฉ:**
466
+ - ์ƒ์„ฑ๋œ ์นดํ”ผ์™€ ์ฐธ๊ณ  ์นดํ”ผ๋ฅผ ๋น„๊ต ๋ถ„์„
467
+ - ํŠธ๋ Œ๋“œ์™€ ํŒจํ„ด ํŒŒ์•… ๊ฐ€๋Šฅ
468
+ - ๊ฒฝ์Ÿ์‚ฌ ๋ถ„์„ ์ž๋ฃŒ๋กœ ํ™œ์šฉ
469
  """)
470
 
471
  # ํ‘ธํ„ฐ
 
476
  )
477
 
478
  # ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง (๊ฐœ๋ฐœ์ž์šฉ)
479
+ if os.getenv("DEBUG_MODE") == "true":
480
  st.sidebar.markdown("### ๐Ÿ”ง ๋””๋ฒ„๊ทธ ์ •๋ณด (ํ™œ์„ฑํ™”๋จ)")
481
+ if 'loaded_embeddings' in locals() and loaded_embeddings is not None:
482
  st.sidebar.write(f"์ž„๋ฒ ๋”ฉ ๋ฉ”๋ชจ๋ฆฌ: {loaded_embeddings.nbytes / (1024*1024):.1f}MB")
483
  st.sidebar.write(f"Streamlit ๋ฒ„์ „: {st.__version__}")
484
  st.sidebar.write(f"Pandas ๋ฒ„์ „: {pd.__version__}")
485
+ # np ๋ณ„์นญ์ด ๋กœ์ปฌ์—์„œ ์ •์˜๋˜์–ด ์žˆ์ง€ ์•Š์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, import๋œ numpy ์‚ฌ์šฉ
486
+ try:
487
+ import numpy as np_debug_version
488
+ st.sidebar.write(f"Numpy ๋ฒ„์ „ (Global): {np_debug_version.__version__}")
489
+ except ImportError:
490
+ st.sidebar.write("Numpy ๋ฒ„์ „ (Global): Not imported or error")
491
+
492
+ # torch๋Š” ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, sentence_transformers ๋‚ด๋ถ€ ๋ฒ„์ „์„ ์•Œ๊ธฐ๋Š” ์–ด๋ ค์›€
493
  st.sidebar.write(f"google-generativeai ๋ฒ„์ „: {genai.__version__}")