parkkyujin commited on
Commit
4901d23
ยท
verified ยท
1 Parent(s): f9c490e

Upload 3 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ ๊ด‘๊ณ ์นดํ”ผ๋ฐ์ดํ„ฐ_๋ธŒ๋žœ๋“œ์ถ”์ถœ์™„๋ฃŒ.xlsx filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-1.5-flash')
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 ํ”Œ๋žซํผ")
copy_embeddings.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4e0f35715ecca21180ae89582ec91b9c55fd22650ae5a1eb5e213c6d7b9e636f
3
+ size 115725584
๊ด‘๊ณ ์นดํ”ผ๋ฐ์ดํ„ฐ_๋ธŒ๋žœ๋“œ์ถ”์ถœ์™„๋ฃŒ.xlsx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d38360acd129b9d8ec065a0231c42a0029f5ed6718d397d049f3601065d001d7
3
+ size 3584205