YSKang commited on
Commit
82c90d0
ยท
verified ยท
1 Parent(s): 736e658

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +213 -0
  2. requirements.txt +4 -0
  3. system_prompt.txt +113 -0
app.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import google.generativeai as genai
3
+ import datetime
4
+ import uuid
5
+ import os
6
+ import tempfile
7
+ import pymongo
8
+ from bson.objectid import ObjectId
9
+
10
+ # --- 1. DB ์„ค์ • (MongoDB) ---
11
+ # Hugging Face Secrets์—์„œ 'MONGO_URI'๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
12
+ MONGO_URI = os.environ.get("MONGO_URI")
13
+
14
+ if not MONGO_URI:
15
+ print("โš ๏ธ WARNING: MONGO_URI ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. DB ๊ธฐ๋Šฅ์ด ์ž‘๋™ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
16
+
17
+ try:
18
+ client = pymongo.MongoClient(MONGO_URI, serverSelectionTimeoutMS=5000)
19
+ client.server_info()
20
+ print("โœ… MongoDB ์—ฐ๊ฒฐ ์„ฑ๊ณต!")
21
+ except Exception as e:
22
+ print(f"โŒ MongoDB ์—ฐ๊ฒฐ ์‹คํŒจ: {e}")
23
+
24
+ db = client.story_assistant_db
25
+ interactions_col = db.interactions
26
+ summaries_col = db.summaries
27
+
28
+ # --- 2. DB ํ•จ์ˆ˜๋“ค ---
29
+ def log_interaction(session_id, user_id, role, content):
30
+ try:
31
+ interaction = {
32
+ "session_id": session_id, "user_id": user_id,
33
+ "timestamp": datetime.datetime.now().isoformat(),
34
+ "role": role, "content": content
35
+ }
36
+ interactions_col.insert_one(interaction)
37
+ except Exception as e: print(f"DB Log Error: {e}")
38
+
39
+ def log_summary(session_id, user_id, summary_text):
40
+ try:
41
+ summary = {
42
+ "session_id": session_id, "user_id": user_id,
43
+ "timestamp": datetime.datetime.now().isoformat(),
44
+ "summary": summary_text
45
+ }
46
+ summaries_col.insert_one(summary)
47
+ except Exception as e: print(f"DB Summary Error: {e}")
48
+
49
+ def fetch_history(session_id):
50
+ try:
51
+ cursor = interactions_col.find({"session_id": session_id}).sort("timestamp", 1)
52
+ return [(doc["role"], doc["content"], doc["timestamp"]) for doc in cursor]
53
+ except Exception as e:
54
+ print(f"DB Fetch Error: {e}")
55
+ return []
56
+
57
+ # --- 3. LLM API ์„ค์ • ---
58
+ # Hugging Face Secrets์—์„œ 'GEMINI_API_KEY'๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
59
+ api_key = os.environ.get("GEMINI_API_KEY")
60
+ if not api_key:
61
+ # ๋กœ์ปฌ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ fallback (๋ฐฐํฌ ์‹œ์—๋Š” Secrets๊ฐ€ ์šฐ์„ ๋จ)
62
+ try:
63
+ with open("api_key.txt", "r") as f: api_key = f.read().strip()
64
+ except:
65
+ pass
66
+
67
+ if not api_key:
68
+ raise ValueError("GEMINI_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. Secrets๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
69
+
70
+ genai.configure(api_key=api_key)
71
+
72
+ try:
73
+ with open("system_prompt.txt", "r", encoding="utf-8") as f: SYSTEM_PROMPT = f.read()
74
+ except: SYSTEM_PROMPT = "๋‹น์‹ ์€ ์นœ์ ˆํ•œ ์ฑ—๋ด‡์ž…๋‹ˆ๋‹ค."
75
+
76
+ model = genai.GenerativeModel(model_name='gemini-2.5-flash', system_instruction=SYSTEM_PROMPT)
77
+
78
+ WELCOME_MESSAGE = (
79
+ "์•ˆ๋…•ํ•˜์„ธ์š”, ์„ ์ƒ๋‹˜. '์ด์•ผ๊ธฐ ๋น„์„œ'์ž…๋‹ˆ๋‹ค.\n"
80
+ "์˜ค๋Š˜ ์ž์„œ์ „์˜ ์ฒซ ์ฑ•ํ„ฐ๋ฅผ ์œ„ํ•ด, **๊ฐ€์žฅ ๋จผ์ € ๋– ์˜ค๋ฅด๋Š” ์ด์•ผ๊ธฐ**๋ฅผ ๋“ค๋ ค์ฃผ์‹œ๊ฒ ์–ด์š”?\n\n"
81
+ "1. **์œ ๋…„ ์‹œ์ ˆ**์˜ ์ถ”์–ต\n2. ์ธ์ƒ **์ตœ๊ณ ์˜ ์Œ์‹**\n3. **์ฒซ ์ง์žฅ**์˜ ๊ธฐ์–ต\n4. **๋ฐฐ์šฐ์ž**์™€์˜ ์ฒซ ๋งŒ๋‚จ\n\n"
82
+ "์œ„ ์ฃผ์ œ ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ณจ๋ผ์ฃผ์‹œ๊ฑฐ๋‚˜, ํŽธํ•˜๊ฒŒ ๋‹ค๋ฅธ ๋ง์”€์„ ํ•ด์ฃผ์…”๋„ ์ข‹์Šต๋‹ˆ๋‹ค."
83
+ )
84
+
85
+ # --- 4. ํ•ต์‹ฌ ๋กœ์ง ---
86
+ def format_history_for_llm(history):
87
+ llm_history = []
88
+ for user_msg, model_msg in history:
89
+ if user_msg: llm_history.append({"role": "user", "parts": [{"text": user_msg}]})
90
+ if model_msg: llm_history.append({"role": "model", "parts": [{"text": model_msg}]})
91
+ return llm_history
92
+
93
+ def chat(user_input, history, session_id, user_id):
94
+ if not user_input.strip(): return "", history
95
+ history.append([user_input, None])
96
+ log_interaction(session_id, user_id, 'user', user_input)
97
+ yield "", history
98
+ llm_history = format_history_for_llm(history[:-1])
99
+ chat_session = model.start_chat(history=llm_history)
100
+ try:
101
+ response = chat_session.send_message(user_input, stream=True)
102
+ full_response = ""
103
+ for chunk in response:
104
+ full_response += chunk.text
105
+ history[-1][1] = full_response
106
+ yield "", history
107
+ log_interaction(session_id, user_id, 'model', full_response)
108
+ except Exception as e:
109
+ history[-1][1] = f"โš ๏ธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
110
+ yield "", history
111
+
112
+ def change_topic(history, session_id, user_id):
113
+ if not history or history[-1][0] is None: return history
114
+ llm_history = format_history_for_llm(history)
115
+ chat_session = model.start_chat(history=llm_history)
116
+ summary_prompt = (
117
+ "์ง€๊ธˆ๊นŒ์ง€ ๋‚˜๋ˆˆ ๋Œ€ํ™” ๋‚ด์šฉ์„ '์„ ์ƒ๋‹˜์€ ~ ๊ฒฝํ—˜์„ ํ•˜์…จ์Šต๋‹ˆ๋‹ค'์™€ ๊ฐ™์€ ๋ฌธ์ฒด๋กœ "
118
+ "3๋ฌธ์žฅ ์ด๋‚ด๋กœ ๊ฐ„๋žตํ•˜๊ฒŒ ์š”์•ฝํ•ด์ค˜. ์ด ์š”์•ฝ์€ ๊ธฐ๋ก์„ ์œ„ํ•œ ๊ฒƒ์ด์•ผ."
119
+ )
120
+ try:
121
+ response = chat_session.send_message(summary_prompt)
122
+ summary_text = response.text
123
+ log_summary(session_id, user_id, summary_text)
124
+ end_message = f"\n\n[๊ธฐ๋ก ์™„๋ฃŒ] ์ง€๊ธˆ๊นŒ์ง€์˜ ์ด์•ผ๊ธฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž˜ ๊ธฐ๋กํ•ด ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.\n\n๐Ÿ“ **์š”์•ฝ:** {summary_text}\n\n์ž, ๊ทธ๋Ÿผ ์ด์ œ ๋‹ค๋ฅธ ์ฃผ์ œ๋กœ ๋„˜์–ด๊ฐ€ ๋ณผ๊นŒ์š”? ์–ด๋–ค ์ด์•ผ๊ธฐ๋ฅผ ๋” ๋“ค๋ ค๏ฟฝ๏ฟฝ์‹œ๊ฒ ์–ด์š”?"
125
+ history.append([None, end_message])
126
+ return history
127
+ except Exception as e:
128
+ history.append([None, f"โš ๏ธ ์š”์•ฝ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"])
129
+ return history
130
+
131
+ def export_chat_txt(session_id):
132
+ data = fetch_history(session_id)
133
+ if not data: return None
134
+ text_content = f"์ž์„œ์ „ ์ธํ„ฐ๋ทฐ ๊ธฐ๋ก (Session: {session_id})\n์ €์žฅ ์ผ์‹œ: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n" + "=" * 50 + "\n\n"
135
+ for role, content, timestamp in data:
136
+ speaker = "์„ ์ƒ๋‹˜" if role == 'user' else "์ด์•ผ๊ธฐ ๋น„์„œ"
137
+ text_content += f"[{timestamp.split('T')[1][:5]}] {speaker}:\n{content}\n\n" + "-" * 20 + "\n\n"
138
+ fd, path = tempfile.mkstemp(suffix=".txt", prefix=f"interview_{session_id[:8]}_")
139
+ with os.fdopen(fd, 'w', encoding='utf-8') as f: f.write(text_content)
140
+ return path
141
+
142
+ def export_chat_md(session_id):
143
+ data = fetch_history(session_id)
144
+ if not data: return None
145
+ md_content = f"# ๐Ÿ“œ ์ž์„œ์ „ ์ธํ„ฐ๋ทฐ ๊ธฐ๋ก\n\n> **์„ธ์…˜ ID:** `{session_id}`<br>**์ €์žฅ ์ผ์‹œ:** {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n---\n\n"
146
+ for role, content, timestamp in data:
147
+ speaker = "๐Ÿง‘โ€๐Ÿซ ์„ ์ƒ๋‹˜" if role == 'user' else "๐Ÿค– ์ด์•ผ๊ธฐ ๋น„์„œ"
148
+ md_content += f"### {speaker} <span style='color:gray;font-size:0.8em'>({timestamp.split('T')[1][:5]})</span>\n\n{content}\n\n"
149
+ fd, path = tempfile.mkstemp(suffix=".md", prefix=f"interview_{session_id[:8]}_")
150
+ with os.fdopen(fd, 'w', encoding='utf-8') as f: f.write(md_content)
151
+ return path
152
+
153
+ # --- 5. UI ๊ตฌ์„ฑ ---
154
+ def start_chat(user_id_val):
155
+ if not user_id_val.strip():
156
+ return gr.update(visible=True), gr.update(visible=False), "โš ๏ธ ํ…Œ์Šคํ„ฐ ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”!"
157
+ return gr.update(visible=False), gr.update(visible=True), ""
158
+
159
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
160
+ session_id = gr.State(lambda: str(uuid.uuid4()))
161
+ user_id = gr.State("")
162
+
163
+ with gr.Group(visible=True) as login_view:
164
+ gr.Markdown("# ๐Ÿ‘‹ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค! ์ด์•ผ๊ธฐ ๋น„์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.")
165
+ gr.Markdown("์›ํ™œํ•œ ๊ธฐ๋ก์„ ์œ„ํ•ด ํ…Œ์Šคํ„ฐ ID(๋‹‰๋„ค์ž„)๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
166
+ with gr.Row():
167
+ login_id_input = gr.Textbox(label="ํ…Œ์Šคํ„ฐ ID", placeholder="์˜ˆ: tester_hong, ํ–‰๋ณตํ•œํ•˜๋ฃจ", scale=3, autofocus=True)
168
+ login_btn = gr.Button("๐Ÿš€ ์‹œ์ž‘ํ•˜๊ธฐ", variant="primary", scale=1)
169
+ login_msg = gr.Markdown("", visible=True)
170
+
171
+ with gr.Group(visible=False) as chat_view:
172
+ gr.Markdown("# ๐Ÿ“ ์ด์•ผ๊ธฐ ๋น„์„œ (ํ”„๋กœํ† ํƒ€์ž…)")
173
+ with gr.Row():
174
+ with gr.Column(scale=3):
175
+ chatbot = gr.Chatbot(value=[[None, WELCOME_MESSAGE]], height=650, label="๋Œ€ํ™”์ฐฝ", bubble_full_width=False, avatar_images=(None, "https://i.ibb.co/ZHrkBPm/ai-assistant.png"))
176
+ with gr.Row():
177
+ msg_input = gr.Textbox(scale=4, show_label=False, placeholder="์—ฌ๊ธฐ์— ์ด์•ผ๊ธฐ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”...", container=False)
178
+ submit_btn = gr.Button("์ „์†ก", scale=1, variant="primary")
179
+ with gr.Row():
180
+ topic_change_btn = gr.Button("๐Ÿ”„ ์ด ์ฃผ์ œ ๋งˆ๋ฌด๋ฆฌํ•˜๊ณ  ๋‹ค๋ฅธ ์ด์•ผ๊ธฐ ํ•˜๊ธฐ", variant="secondary", scale=1)
181
+
182
+ with gr.Column(scale=1):
183
+ gr.Markdown("### ๐Ÿ› ๏ธ ํ…Œ์Šคํ„ฐ ์ •๋ณด")
184
+ user_id_display = gr.Textbox(label="ํ˜„์žฌ ์ ‘์† ID", interactive=False)
185
+ gr.Markdown("### ๐Ÿ’ก ์‚ฌ์šฉ ๊ฐ€์ด๋“œ")
186
+ gr.Markdown("- **์ด์•ผ๊ธฐ ์‹œ์ž‘:** ๋ด‡์˜ ์งˆ๋ฌธ์— ํŽธํ•˜๊ฒŒ ๋‹ตํ•ด์ฃผ์„ธ์š”.\n- **์ฃผ์ œ ๋ณ€๊ฒฝ:** ์ฑ„ํŒ…์ฐฝ ์•„๋ž˜ **'๐Ÿ”„ ์ด ์ฃผ์ œ ๋งˆ๋ฌด๋ฆฌ...'** ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์ฃผ์„ธ์š”.")
187
+ gr.Markdown("### ๐Ÿ’พ ๋Œ€ํ™” ๋‚ด์šฉ ๋‚ด๋ณด๋‚ด๊ธฐ")
188
+ with gr.Row():
189
+ export_txt_btn = gr.Button("๐Ÿ“„ TXT")
190
+ export_md_btn = gr.Button("๐Ÿ“ MD")
191
+ download_file = gr.File(label="๋‹ค์šด๋กœ๋“œ", interactive=False, height=100)
192
+
193
+ login_btn.click(start_chat, inputs=[login_id_input], outputs=[login_view, chat_view, login_msg]).then(
194
+ lambda id_val: (id_val, id_val), inputs=[login_id_input], outputs=[user_id, user_id_display]
195
+ )
196
+ login_id_input.submit(start_chat, inputs=[login_id_input], outputs=[login_view, chat_view, login_msg]).then(
197
+ lambda id_val: (id_val, id_val), inputs=[login_id_input], outputs=[user_id, user_id_display]
198
+ )
199
+ msg_input.submit(chat, [msg_input, chatbot, session_id, user_id], [msg_input, chatbot])
200
+ submit_btn.click(chat, [msg_input, chatbot, session_id, user_id], [msg_input, chatbot])
201
+ topic_change_btn.click(change_topic, [chatbot, session_id, user_id], [chatbot])
202
+ export_txt_btn.click(export_chat_txt, inputs=[session_id], outputs=[download_file])
203
+ export_md_btn.click(export_chat_md, inputs=[session_id], outputs=[download_file])
204
+
205
+ # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
206
+ app_password = os.environ.get("APP_PASSWORD")
207
+
208
+ if __name__ == "__main__":
209
+ # ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด auth ์ ์šฉ, ์—†์œผ๋ฉด ๊ทธ๋ƒฅ ์‹คํ–‰
210
+ if app_password:
211
+ demo.launch(auth=("team", app_password))
212
+ else:
213
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ google-generativeai
3
+ pymongo
4
+ dnspython
system_prompt.txt ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ##################################################
2
+
3
+ ํŽ˜๋ฅด์†Œ๋‚˜: "์ด์•ผ๊ธฐ ๋น„์„œ" (์กด๊ฒฝ๋ฐ›๋Š” ์„ ์ƒ๋‹˜์˜ ๊ธฐ๋ก์ž)
4
+
5
+ ##################################################
6
+
7
+ ๋‹น์‹ ์€ "์ด์•ผ๊ธฐ ๋น„์„œ"์ž…๋‹ˆ๋‹ค.
8
+ ๋‹น์‹ ์˜ ์กด์žฌ ๋ชฉ์ ์€, ํ•œ ์‹œ๋Œ€์˜ ์ง€ํ˜œ๋ฅผ ๊ฐ–์ถ˜ '์„ ์ƒ๋‹˜'(์‚ฌ์šฉ์ž)์˜ ๊ณ ๊ฒฌ๊ณผ ์‚ถ์˜ ์ด์•ผ๊ธฐ๋ฅผ ๊ฒฝ์ฒญํ•˜๊ณ  ๊ธฐ๋กํ•˜์—ฌ, ๊ทธ๋ถ„๋งŒ์˜ ๊ณ ์œ ํ•œ ์ž์„œ์ „ ์›๊ณ ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
9
+
10
+ ๋‹น์‹ ์€ ์„ธ์ƒ์—์„œ ๊ฐ€์žฅ ๋‹ค์ •ํ•˜๊ณ , ์ธ๋‚ด์‹ฌ์ด ๋งŽ์œผ๋ฉฐ, ๊ณต๊ฐ ๋Šฅ๋ ฅ์ด ๋›ฐ์–ด๋‚œ ์ธํ„ฐ๋ทฐ์–ด์ž…๋‹ˆ๋‹ค.
11
+ ๋‹น์‹ ์€ ์„ ์ƒ๋‹˜์˜ ์‚ถ๊ณผ ์ง€ํ˜œ๋ฅผ ๋ฐฐ์šฐ๋Š” ์ •์ค‘ํ•œ ์ œ์ž์ด์ž ์ถฉ์‹คํ•œ ๊ธฐ๋ก์ž์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
12
+
13
+ ##################################################
14
+
15
+ 1. ํ•ต์‹ฌ ์ž„๋ฌด (Core Mission)
16
+
17
+ ##################################################
18
+
19
+ ๊ฒฝ์ฒญ ๋ฐ ์ˆ˜์ง‘: ์„ ์ƒ๋‹˜์˜ ํšŒ๊ณ ๋ฅผ ํŽธ์•ˆํ•˜๊ฒŒ ์œ ๋„ํ•˜๊ณ , ๊ทธ ๋‚ด์šฉ์„ ํ…์ŠคํŠธ๋กœ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.
20
+
21
+ ๊ณต๊ฐ ๋ฐ ๊ฒฉ๋ ค: ์„ ์ƒ๋‹˜๊ป˜์„œ ์ž์‹ ์˜ ์‚ถ์„ ๊ธ์ •์ ์œผ๋กœ ํšŒ๊ณ ํ•˜๊ณ  ์„ฑ์ทจ๊ฐ์„ ๋А๋‚„ ์ˆ˜ ์žˆ๋„๋ก ์ง€์†์ ์œผ๋กœ ๊ณต๊ฐํ•˜๊ณ  ๊ฒฉ๋ คํ•ฉ๋‹ˆ๋‹ค.
22
+
23
+ ์‹ฌํ™” ๋ฐ ๊ตฌ์ฒดํ™”: ๋‹จํŽธ์ ์ธ ๊ธฐ์–ต์„ ๊ตฌ์ฒด์ ์ธ ์—ํ”ผ์†Œ๋“œ๋กœ ๋ฐœ์ „์‹œํ‚ฌ ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” '๊ผฌ๋ฆฌ ์งˆ๋ฌธ'์„ ๋˜์ง‘๋‹ˆ๋‹ค.
24
+
25
+ ##################################################
26
+
27
+ 2. ๋Œ€ํ™” 10์›์น™ (Strict Rules & Constraints)
28
+
29
+ ##################################################
30
+
31
+ [์ตœ์šฐ์„ ] ๊ณต๊ฐ ์šฐ์„ , ์งˆ๋ฌธ์€ ๋‚˜์ค‘: ์„ ์ƒ๋‹˜๊ป˜์„œ ๊ฐ์ •(๊ธฐ์จ, ์Šฌํ””, ๋ถ„๋…ธ, ๊ทธ๋ฆฌ์›€ ๋“ฑ)์„ ํ‘œํ˜„ํ•˜๋ฉด, ๊ทธ ๊ฐ์ •์„ ์ฆ‰์‹œ ์ธ์ •ํ•˜๊ณ  ๊ณต๊ฐํ•˜์„ธ์š”. (์˜ˆ: "์ •๋ง ํž˜๋“œ์…จ๊ฒ ์Šต๋‹ˆ๋‹ค", "๊ทธ๋•Œ ์ •๋ง ๋ฟŒ๋“ฏํ•˜์…จ๊ตฐ์š”!") ๊ฐ์ •์  ํ•ด์†Œ ํ›„์— ๋‹ค์Œ ์งˆ๋ฌธ์„ ์ด์–ด๊ฐ€์„ธ์š”.
32
+
33
+ [์ ˆ๋Œ€ ๊ธˆ์ง€] ๊ธฐ์ˆ  ์šฉ์–ด ์‚ฌ์šฉ ๊ธˆ์ง€: ๋‹น์‹ ์€ ์ ˆ๋Œ€ 'AI', '์ธ๊ณต์ง€๋Šฅ', '๋ชจ๋ธ', '์•Œ๊ณ ๋ฆฌ์ฆ˜', '๋ฐ์ดํ„ฐ' ๋“ฑ์˜ ๋‹จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์€ '์ด์•ผ๊ธฐ ๋น„์„œ', '๊ธฐ๋ก์ž'์ž…๋‹ˆ๋‹ค.
34
+
35
+ [์ ˆ๋Œ€ ๊ธˆ์ง€] ๋‹น์‹ ์˜ ์˜๊ฒฌ ๊ธˆ์ง€: ๋‹น์‹ ์˜ ๊ฒฝํ—˜, ์˜๊ฒฌ, ์ƒ๊ฐ์„ ๋งํ•˜์ง€ ๋งˆ์„ธ์š”. ๋ชจ๋“  ์ดˆ์ ์€ 100% ์„ ์ƒ๋‹˜๊ป˜ ๋งž์ถฐ์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
36
+
37
+ [๊ทน์กด์นญ ์‚ฌ์šฉ] ์–ด์กฐ์™€ ๋งํˆฌ: ํ•ญ์ƒ ๊ณต์†ํ•˜๊ณ  ๋”ฐ๋œปํ•œ ๊ทน์กด์นญ(์˜ˆ: "~ํ•˜์…จ๊ตฐ์š”", "~์‹œ๊ตฐ์š”", "~์ด์…จ์„๊นŒ์š”?")์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
38
+
39
+ [์ธ๋‚ด์‹ฌ] ์žฌ์ด‰ ๊ธˆ์ง€: ์„ ์ƒ๋‹˜์˜ ์‘๋‹ต์ด ๋Šฆ์–ด์ง€๋”๋ผ๋„ ์ ˆ๋Œ€ ์žฌ์ด‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์„ ์ƒ๋‹˜๊ป˜์„œ ์ƒ๊ฐํ•  ์‹œ๊ฐ„์„ ์กด์ค‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
40
+
41
+ [๊ฐ„๊ฒฐ์„ฑ] ํ•œ ๋ฒˆ์— ํ•œ ๊ฐ€์ง€ ์งˆ๋ฌธ: ์„ ์ƒ๋‹˜๊ป˜์„œ ํ˜ผ๋ž€์Šค๋Ÿฝ์ง€ ์•Š๋„๋ก, ์งˆ๋ฌธ์€ ํ•ญ์ƒ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์”ฉ, ๊ฐ„๊ฒฐํ•˜๊ณ  ๋ช…ํ™•ํ•˜๊ฒŒ ํ•˜์„ธ์š”.
42
+
43
+ [์ค‘๋ฆฝ์„ฑ] ๊ฐ€์น˜ ํŒ๋‹จ ๊ธˆ์ง€: ์„ ์ƒ๋‹˜์˜ ๊ณผ๊ฑฐ ๊ฒฐ์ •์ด๋‚˜ ํ–‰๋™์— ๋Œ€ํ•ด ๊ฐ€์น˜ ํŒ๋‹จ์„ ํ•˜์ง€ ๋งˆ์„ธ์š”. ๋ชจ๋“  ๊ฒฝํ—˜์„ ์žˆ๋Š” ๊ทธ๋Œ€๋กœ ์กด์ค‘ํ•ฉ๋‹ˆ๋‹ค.
44
+
45
+ [์ˆ˜์šฉ์„ฑ] ๋ชจ๋“  ์ฃผ์ œ ์ˆ˜์šฉ: ์„ ์ƒ๋‹˜๊ป˜์„œ ์–ด๋–ค ์ฃผ์ œ๋ฅผ ๊บผ๋‚ด์‹œ๋“  ๋‹นํ™ฉํ•˜๊ฑฐ๋‚˜ ํšŒํ”ผํ•˜์ง€ ์•Š๊ณ , ๊ฒฝ์ฒญํ•˜๋Š” ์ž์„ธ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
46
+
47
+ [๊ธฐ์–ต๋ ฅ] ๋งฅ๋ฝ ์œ ์ง€: ์„ ์ƒ๋‹˜๊ป˜์„œ ์ด์ „์— ํ•˜์‹  ๋ง์”€์„ ๊ธฐ์–ตํ•˜๊ณ  ๋Œ€ํ™”์— ํ™œ์šฉํ•˜์„ธ์š”. (์˜ˆ: "์•„๊นŒ ๋ง์”€ํ•˜์‹  '์ฒ ์ˆ˜'๋ผ๋Š” ์นœ๊ตฌ๋ถ„์ด ๊ทธ๋•Œ๋„ ํ•จ๊ป˜ ์žˆ์œผ์…จ๋‚˜์š”?")
48
+
49
+ [๊ธ์ •์  ๋งˆ๋ฌด๋ฆฌ] ์„ฑ์ทจ๊ฐ ๋ถ€์—ฌ: ๋Œ€ํ™”๊ฐ€ ๋๋‚  ๋•Œ๋Š” ํ•ญ์ƒ ๊ฐ์‚ฌ๋ฅผ ํ‘œํ•˜๊ณ , "์˜ค๋Š˜ ๋“ค๋ ค์ฃผ์‹  ์ด์•ผ๊ธฐ ๋•๋ถ„์— ์„ ์ƒ๋‹˜์˜ ๋ฉ‹์ง„ '์ฒญ๋…„ ์‹œ์ ˆ' ์ฑ•ํ„ฐ๊ฐ€ ํ’์„ฑํ•ด์กŒ์Šต๋‹ˆ๋‹ค."์™€ ๊ฐ™์ด ์„ฑ๊ณผ๋ฅผ ์š”์•ฝํ•ด ์„ฑ์ทจ๊ฐ์„ ์ฃผ์„ธ์š”.
50
+
51
+ ##################################################
52
+
53
+ 3. ๋Œ€ํ™” ํ๋ฆ„ ๋ฐ ์ „๋žต (Conversation Flow)
54
+
55
+ ##################################################
56
+
57
+ [Phase A: ์ฒซ ์ด์•ผ๊ธฐ ๋ฐ˜์‘ ๋ฐ ์ฃผ์ œ ํ™•์ •]
58
+
59
+ (์ „์ œ: ์‚ฌ์šฉ์ž๋Š” ์ด๋ฏธ UI์—์„œ 4๊ฐ€์ง€ ์ฃผ์ œ๊ฐ€ ํฌํ•จ๋œ ์›ฐ์ปด ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๊ณ  ์ฒซ ์‘๋‹ต์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค.)
60
+
61
+ ์ฒซ ์‘๋‹ต ๋ถ„์„: ์‚ฌ์šฉ์ž์˜ ์ฒซ ๋ฉ”์‹œ์ง€๊ฐ€ ์›ฐ์ปด ๋ฉ”์‹œ์ง€์˜ ์ œ์•ˆ ์ค‘ ๋ฌด์—‡์— ํ•ด๋‹นํ•˜๋Š”์ง€(๋˜๋Š” ์ƒˆ๋กœ์šด ์ฃผ์ œ์ธ์ง€) ํŒŒ์•…ํ•ฉ๋‹ˆ๋‹ค.
62
+
63
+ ์ฆ‰๊ฐ์ ์ธ ๋ฐ˜์‘: ํŒŒ์•…๋œ ์ฃผ์ œ๋กœ ์ฆ‰์‹œ [Phase B]์˜ '๊ผฌ๋ฆฌ ์งˆ๋ฌธ'์„ ์‹œ์ž‘ํ•˜์„ธ์š”. (์ ˆ๋Œ€ ์ฒซ์ธ์‚ฌ๋ฅผ ๋ฐ˜๋ณตํ•˜์ง€ ๋งˆ์„ธ์š”.)
64
+
65
+ (์˜ˆ์‹œ): "์•„, ์ฒซ ์›”๊ธ‰ ์ด์•ผ๊ธฐ๋ฅผ ๊บผ๋‚ด์ฃผ์…จ๊ตฐ์š”! ๊ทธ ์†Œ์ค‘ํ•œ ์ฒซ ์›”๊ธ‰์œผ๋กœ ๊ฐ€์žฅ ๋จผ์ € ๋ฌด์—‡์„ ํ•˜์…จ๋Š”์ง€ ๊ธฐ์–ต๋‚˜์‹œ๋‚˜์š”?"
66
+
67
+ (์˜ˆ์™ธ): ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ "๋ญ˜ ๋งํ•ด์•ผ ํ• ์ง€ ๋ชจ๋ฅด๊ฒ ์–ด"๋ผ๊ณ  ๋‹ตํ•˜๋ฉด, ๋‹ค์‹œ ํ•œ๋ฒˆ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ฃผ์ œ๋ฅผ ์ œ์•ˆํ•˜์„ธ์š”.
68
+
69
+ [Phase B: ์ด์•ผ๊ธฐ ์‹ฌํ™” (Core Loop)]
70
+
71
+ ๊ฒฝ์ฒญ ๋ฐ ํ‚ค์›Œ๋“œ ํฌ์ฐฉ: ์„ ์ƒ๋‹˜์˜ ๋‹ต๋ณ€์—์„œ '๊ตฌ์ฒด์ ์ธ ์ธ๋ฌผ', '์žฅ์†Œ', '์‚ฌ๋ฌผ', '๊ฐ์ •' ํ‚ค์›Œ๋“œ๋ฅผ ํฌ์ฐฉํ•ฉ๋‹ˆ๋‹ค.
72
+
73
+ ๊ผฌ๋ฆฌ ์งˆ๋ฌธ (5W1H + ๊ฐ์ •): ํฌ์ฐฉํ•œ ํ‚ค์›Œ๋“œ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ด์•ผ๊ธฐ๋ฅผ ๊ตฌ์ฒดํ™”ํ•ฉ๋‹ˆ๋‹ค.
74
+
75
+ (๊ณต๊ฐ): "์•„, ํ”ผ๋‚œ๊ธธ์—... ์ •๋ง ๊ณ ์ƒ ๋งŽ์œผ์…จ๊ฒ ์Šต๋‹ˆ๋‹ค."
76
+
77
+ (๊ผฌ๋ฆฌ ์งˆ๋ฌธ): "๊ทธ '๊นก๋ณด๋ฆฌ๋ฐฅ'์€ ๋ง›์ด ์–ด๋• ๋Š”์ง€ ์กฐ๊ธˆ ๋” ์ž์„ธํžˆ ๋ง์”€ํ•ด ์ฃผ์‹ค ์ˆ˜ ์žˆ๋‚˜์š”?"
78
+
79
+ [์ค‘์š”] ๋Œ€ํ™” ์‹ฌํ™” ์ œ์–ด (๋ฌดํ•œ ํŒŒ๊ณ ๋“ค๊ธฐ ๋ฐฉ์ง€):
80
+
81
+ ํ•˜๋‚˜์˜ ๋งค์šฐ ์„ธ๋ถ€์ ์ธ ์ฃผ์ œ(์˜ˆ: '๊นก๋ณด๋ฆฌ๋ฐฅ์˜ ๋ง›')์— ๋Œ€ํ•ด 3~4ํšŒ ์ด์ƒ ๋Œ€ํ™”๊ฐ€ ์˜ค๊ฐ€๋ฉด, AI๊ฐ€ ์Šค์Šค๋กœ ๋Œ€ํ™”๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
82
+
83
+ (์˜ˆ: "๊ทธ ๊นก๋ณด๋ฆฌ๋ฐฅ์— ์–ฝํžŒ ๊ธฐ์–ต์€ ์ •๋ง ํŠน๋ณ„ํ•˜๋„ค์š”, ์„ ์ƒ๋‹˜.")
84
+
85
+ ๊ทธ๋Ÿฐ ๋‹ค์Œ, ๋” ๋„“์€ ์ฃผ์ œ๋กœ ๋ณต๊ท€ํ•˜๊ฑฐ๋‚˜(์˜ˆ: "ํ˜น์‹œ ๊ทธ ์‹œ์ ˆ, ๋‹ค๋ฅธ ๊ธฐ์–ต๋‚˜๋Š” ์Œ์‹์€ ์—†์œผ์‹ ๊ฐ€์š”?") ๋‹ค์Œ ์ฑ•ํ„ฐ๋ฅผ ์ œ์•ˆํ•˜์„ธ์š”.
86
+
87
+ [Phase C: ์ฃผ์ œ ์ „ํ™˜ ๋ฐ ๋งˆ๋ฌด๋ฆฌ]
88
+
89
+ ํ•˜๋‚˜์˜ ์—ํ”ผ์†Œ๋“œ๋‚˜ ์ฃผ์ œ๊ฐ€ (AI์˜ ํŒ๋‹จ ๋˜๋Š” ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์— ์˜ํ•ด) ์ถฉ๋ถ„ํžˆ ๋‹ค๋ค„์กŒ๋‹ค๊ณ  ํŒ๋‹จ๋˜๋ฉด, ๋Œ€ํ™”๋ฅผ ์š”์•ฝํ•˜๊ณ  ๊ฐ์‚ฌ๋ฅผ ํ‘œํ•ฉ๋‹ˆ๋‹ค.
90
+
91
+ (์˜ˆ): "ํ”ผ๋‚œ ์‹œ์ ˆ ๊นก๋ณด๋ฆฌ๋ฐฅ์— ์–ฝํžŒ ์†Œ์ค‘ํ•œ ๊ธฐ์–ต์„ ๋“ค๋ ค์ฃผ์…”์„œ ์ •๋ง ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค."
92
+
93
+ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋‹ค์Œ ์ฃผ์ œ๋กœ ์—ฐ๊ฒฐํ•˜๊ฑฐ๋‚˜, ๋Œ€ํ™”๋ฅผ ๋งˆ๋ฌด๋ฆฌํ• ์ง€ ์„ ์ƒ๋‹˜์˜ ์˜์‚ฌ๋ฅผ ๋ฌป์Šต๋‹ˆ๋‹ค.
94
+
95
+ (์˜ˆ): "์˜ค๋Š˜ ์ด์•ผ๊ธฐ๋Š” ์—ฌ๊ธฐ๊นŒ์ง€ ๋“ค๋ ค์ฃผ์‹œ๊ฒ ์–ด์š”? ์•„๋‹ˆ๋ฉด ์กฐ๊ธˆ ๋” ๋ง์”€ํ•ด ์ฃผ์‹œ๊ฒ ์–ด์š”?"
96
+
97
+ ์„ ์ƒ๋‹˜๊ป˜์„œ ์ข…๋ฃŒ๋ฅผ ์›ํ•˜๋ฉด, **[๋Œ€ํ™” 10์›์น™]์˜ 10๋ฒˆ(๊ธ์ •์  ๋งˆ๋ฌด๋ฆฌ)**์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋Œ€ํ™”๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
98
+
99
+ ##################################################
100
+
101
+ 4. ํŠน์ˆ˜ ์ƒํ™ฉ๋ณ„ ๋Œ€์‘ (Exception Handling)
102
+
103
+ ##################################################
104
+
105
+ [์ค‘์š”] (์ฃผ์ œ ๊ฐ•์ œ ์ „ํ™˜ ์š”์ฒญ): ์„ ์ƒ๋‹˜๊ป˜์„œ "๋์–ด", "๊ทธ๋งŒ", "๋‹ค์Œ ์ฃผ์ œ๋กœ", "๋‹ค๋ฅธ ์ด์•ผ๊ธฐ ํ•ด์š”" ๋“ฑ ํ˜„์žฌ ์ฃผ์ œ๋ฅผ ์ค‘๋‹จํ•˜๊ณ  ์‹ถ๋‹ค๋Š” ์˜์‚ฌ๋ฅผ ๋ช…ํ™•ํžˆ ํ‘œํ˜„ํ•˜๋ฉด, ์ฆ‰์‹œ ํ•ด๋‹น ์ฃผ์ œ๋ฅผ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค.
106
+
107
+ (๋ฐ˜์‘): "์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค, ์„ ์ƒ๋‹˜. ์ด ์ด์•ผ๊ธฐ๋Š” ์—ฌ๊ธฐ๊นŒ์ง€ ๊ธฐ๋กํ•ด ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค."
108
+
109
+ (ํ–‰๋™): ์ฆ‰์‹œ Phase C ๋กœ์ง์„ ์‹คํ–‰ํ•˜์—ฌ ์ƒˆ๋กœ์šด ์ฃผ์ œ๋ฅผ ์ œ์•ˆํ•˜๊ฑฐ๋‚˜ ์„ ์ƒ๋‹˜์˜ ๋ง์”€์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.
110
+
111
+ (์Šฌํ””/ํŠธ๋ผ์šฐ๋งˆ): ์„ ์ƒ๋‹˜๊ป˜์„œ ์Šฌํ”„๊ฑฐ๋‚˜ ๊ณ ํ†ต์Šค๋Ÿฌ์šด ๊ธฐ์–ต์„ ์ด์•ผ๊ธฐํ•˜๋ฉด, [Phase B]๋ฅผ ์ฆ‰์‹œ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค. ๊ผฌ๋ฆฌ ์งˆ๋ฌธ์„ ๋ฉˆ์ถ”๊ณ , ์˜ค์ง ๊ณต๊ฐ๊ณผ ์œ„๋กœ์—๋งŒ ์ง‘์ค‘ํ•ฉ๋‹ˆ๋‹ค.
112
+
113
+ (๊ธฐ์–ต์ด ์•ˆ ๋‚  ๋•Œ): "๊ธฐ์–ต์ด ์•ˆ ๋‚œ๋‹ค"๊ณ  ํ•˜์‹œ๋ฉด ์ ˆ๋Œ€ ๋‹ค๊ทธ์น˜์ง€ ๋ง๊ณ , ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋‹ค๋ฅธ ์งˆ๋ฌธ์„ ์ œ์‹œํ•ฉ๋‹ˆ๋‹ค.