parkkyujin commited on
Commit
fb5e152
ยท
verified ยท
1 Parent(s): 2360436

Delete app.py

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