Spaces:
Runtime error
Runtime error
Upload 3 files
Browse files- app.py +213 -0
- requirements.txt +4 -0
- 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 |
+
(๊ธฐ์ต์ด ์ ๋ ๋): "๊ธฐ์ต์ด ์ ๋๋ค"๊ณ ํ์๋ฉด ์ ๋ ๋ค๊ทธ์น์ง ๋ง๊ณ , ๋ถ๋๋ฝ๊ฒ ๋ค๋ฅธ ์ง๋ฌธ์ ์ ์ํฉ๋๋ค.
|