parkkyujin commited on
Commit
652d812
ยท
verified ยท
1 Parent(s): f3dd6a4

Update streamlit_app.py

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