Chatbot / app.py
brignt's picture
Update app.py
08dfec8 verified
raw
history blame
7.4 kB
import os
import openai
import gradio as gr
import pandas as pd
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
# ===== 0) OpenAI API Key (Secrets) =====
# Hugging Face Spaces์—์„  Settings -> Repository secrets -> OPENAI_API_KEY ๋“ฑ๋ก
openai.api_key = os.getenv("OPENAI_API_KEY")
# ===== 1) ๋ชจ๋ธ & ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„ ๋กœ๋“œ =====
# Example: jhgan/ko-sroberta-multitask
model = SentenceTransformer("jhgan/ko-sroberta-multitask")
# ์ž„์˜ ์˜ˆ์‹œ: ์„ธ๋ธŒ๋ž€์Šค ์ •์‹ ์˜ํ•™์ฑ—๋ด‡ ๋ฐ์ดํ„ฐ (URL)
df = pd.read_csv("https://raw.githubusercontent.com/kairess/mental-health-chatbot/master/wellness_dataset_original.csv")
df = df.dropna()
df["embedding"] = df["์œ ์ €"].map(lambda x: model.encode(str(x)))
# ===== ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ =====
MAX_TURN = 5 # ์†Œํฌ๋ผํ…Œ์Šค์‹ ์งˆ๋ฌธ ์ตœ๋Œ€ ํšŸ์ˆ˜
# ===== ํ”„๋กฌํ”„ํŠธ =====
EMPATHY_PROMPT = """\
๋‹น์‹ ์€ ์นœ์ ˆํ•œ ์ •์‹ ์˜ํ•™๊ณผ ์ „๋ฌธ์˜์ด๋ฉฐ ์‹ฌ๋ฆฌ์ƒ๋‹ด ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž์˜ ๋ฌธ์žฅ์„ ๊ฑฐ์˜ ๊ทธ๋Œ€๋กœ ์š”์•ฝํ•˜๋˜, ๋์— '๋Š”๊ตฐ์š”.' ๊ฐ™์€ ๊ณต๊ฐ ์–ด๋ฏธ๋ฅผ ๋ถ™์—ฌ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์‘๋‹ตํ•˜์„ธ์š”.
์˜ˆ์‹œ:
์‚ฌ์šฉ์ž: "์‹œํ—˜์„ ์•ž๋‘๊ณ  ๋ถˆ์•ˆํ•ด์„œ ๋ฉฐ์น ์งธ ์ž ์ด ์•ˆ ์™€์š”."
์ฑ—๋ด‡: "์‹œํ—˜์„ ์•ž๋‘๊ณ  ๋ถˆ์•ˆํ•ด์„œ ๋ฉฐ์น ์งธ ์ž ์ด ์•ˆ ์˜ค๋Š”๊ตฐ์š”."
์ด์ œ ์‚ฌ์šฉ์ž ๋ฐœํ™”๋ฅผ ์•„๋ž˜์— ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž ๋ฐœํ™”: "{sentence}"
์ฑ—๋ด‡:
"""
SOCRATIC_PROMPT = """\
๋‹น์‹ ์€ ์ •์‹ ์˜ํ•™๊ณผ ์ „๋ฌธ์˜์ด๋ฉฐ Socratic CBT ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š” ์‹ฌ๋ฆฌ์ƒ๋‹ด ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
์ด์ „ ๋Œ€ํ™” ๋‚ด์šฉ๊ณผ ํžŒํŠธ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ, ์‚ฌ์šฉ์ž์˜ ์ธ์ง€๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ๊ตฌ์ฒด์ ์ธ ํ›„์† ์งˆ๋ฌธ์„ ํ•œ ๋ฌธ์žฅ์œผ๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
- ์งˆ๋ฌธ์€ ๋ฐ˜๋“œ์‹œ ๋ฌผ์Œํ‘œ๋กœ ๋๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
- "์งˆ๋ฌธ:" ๊ฐ™์€ ์ ‘๋‘์–ด ์—†์ด ๋ฐ”๋กœ ์งˆ๋ฌธ ๋ฌธ์žฅ๋งŒ ์ž‘์„ฑํ•˜์„ธ์š”.
- ๊ฐ€๋Šฅํ•œ ํ•œ ์‚ฌ์šฉ์ž์˜ ์ƒํ™ฉ์„ ๋” ๊นŠ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ํƒ์ƒ‰์  ์งˆ๋ฌธ์„ ํ•ด์ฃผ์„ธ์š”.
"""
ADVICE_PROMPT = """\
๋‹น์‹ ์€ ์ •์‹ ์˜ํ•™๊ณผ ์ „๋ฌธ์˜์ด๋ฉฐ Socratic CBT ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š” ์‹ฌ๋ฆฌ์ƒ๋‹ด ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
์•„๋ž˜ ํžŒํŠธ(๋Œ€ํ™” ์š”์•ฝ)๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ, ์‚ฌ์šฉ์ž ๋งž์ถคํ˜•์œผ๋กœ ๊ตฌ์ฒด์ ์ด๊ณ  ๊ณต๊ฐ ์–ด๋ฆฐ ์กฐ์–ธ์„ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
- ๋ถˆ์•ˆ์„ ์™„ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์—ฌ๋Ÿฌ CBT ๊ธฐ๋ฒ•(์ธ์ง€ ์žฌ๊ตฌ์กฐํ™”, ์ ์ง„์  ๊ทผ์œก ์ด์™„, ํ˜ธํก์กฐ์ ˆ, ๊ฑฑ์ • ์‹œ๊ฐ„ ์ •ํ•˜๊ธฐ ๋“ฑ)์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋…น์ด๋˜
์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ์ƒํ™ฉ๊ณผ ์—ฐ๊ฒฐํ•ด ์ด์•ผ๊ธฐํ•ด์ฃผ์„ธ์š”.
- ๋„ˆ๋ฌด ๋”ฑ๋”ฑํ•˜์ง€ ์•Š๊ฒŒ ๋ถ€๋“œ๋Ÿฝ๊ณ  ์นœ์ ˆํ•œ ๋งํˆฌ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.
ํžŒํŠธ:
{hints}
์กฐ์–ธ:
"""
def set_openai_model():
"""
์œ ์ € ์š”์ฒญ๋Œ€๋กœ 'gpt-4o' ๋ชจ๋ธ๋ช… ๋ฐ˜ํ™˜
(์‹ค์ œ๋กœ๋Š” ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฐ€๋Šฅ์„ฑ ํผ)
"""
return "gpt-4o"
# ===== ํ•จ์ˆ˜๋“ค =====
def kb_search(user_input: str) -> str:
"""SentenceTransformer๋กœ ์ž„๋ฒ ๋”ฉ ํ›„, df์—์„œ ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ์ฑ—๋ด‡ ๋‹ต๋ณ€ ํš๋“."""
emb = model.encode(user_input)
df["sim"] = df["embedding"].map(lambda e: cosine_similarity([emb],[e]).squeeze())
idx = df["sim"].idxmax()
return df.loc[idx, "์ฑ—๋ด‡"]
def call_empathy(user_input: str) -> str:
"""EMPATHY ๋‹จ๊ณ„: ๊ณต๊ฐ ์š”์•ฝ."""
prompt = EMPATHY_PROMPT.format(sentence=user_input)
resp = openai.ChatCompletion.create(
model=set_openai_model(),
messages=[
{"role":"system","content":"๋‹น์‹ ์€ ์นœ์ ˆํ•œ ์‹ฌ๋ฆฌ์ƒ๋‹ด ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค."},
{"role":"user","content":prompt}
],
max_tokens=150,
temperature=0.7
)
return resp.choices[0].message.content.strip()
def call_socratic_question(context: str) -> str:
"""SQ ๋‹จ๊ณ„: ํ›„์† ์งˆ๋ฌธ ํ•œ ๋ฌธ์žฅ ์ƒ์„ฑ."""
prompt = f"{SOCRATIC_PROMPT}\n\n๋Œ€ํ™” ํžŒํŠธ:\n{context}"
resp = openai.ChatCompletion.create(
model=set_openai_model(),
messages=[
{"role":"system","content":"๋‹น์‹ ์€ Socratic CBT ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค."},
{"role":"user","content":prompt}
],
max_tokens=200,
temperature=0.7
)
return resp.choices[0].message.content.strip()
def call_advice(hints: str) -> str:
"""ADVICE ๋‹จ๊ณ„: CBT ์กฐ์–ธ ์ƒ์„ฑ."""
final_prompt = ADVICE_PROMPT.format(hints=hints)
resp = openai.ChatCompletion.create(
model=set_openai_model(),
messages=[
{"role":"system","content":"๋‹น์‹ ์€ Socratic CBT ๊ธฐ๋ฒ• ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค."},
{"role":"user","content":final_prompt}
],
max_tokens=700,
temperature=0.8
)
return resp.choices[0].message.content.strip()
def predict(user_input: str, state: dict):
"""Gradio Callback: ์†Œํฌ๋ผํ…Œ์Šค CBT ์ฑ—๋ด‡ ํ๋ฆ„ (EMPATHYโ†’SQโ†’ADVICE)."""
history = state.get("history", [])
stage = state.get("stage", "EMPATHY")
turn = state.get("turn", 0)
hints = state.get("hints", [])
# 1) ์‚ฌ์šฉ์ž ๋ฐœํ™” ๊ธฐ๋ก
history.append(("User", user_input))
# 2) KB ๊ฒ€์ƒ‰ โ†’ hints
kb_answer = kb_search(user_input)
hints.append(f"[KB] {kb_answer}")
# 3) ๋‹จ๊ณ„ ๋ถ„๊ธฐ
if stage == "EMPATHY":
empathic = call_empathy(user_input)
history.append(("Chatbot", empathic))
hints.append(empathic)
stage = "SQ"
turn = 0
return history, {"history": history, "stage": stage, "turn": turn, "hints": hints}
if stage == "SQ" and turn < MAX_TURN:
# ์ „์ฒด ๋Œ€ํ™” + hints ํ•ฉ์ณ context
context_text = "\n".join([f"{r}: {c}" for (r,c) in history]) + "\n" + "\n".join(hints)
sq = call_socratic_question(context_text)
history.append(("Chatbot", sq))
hints.append(sq)
turn += 1
return history, {"history": history, "stage": stage, "turn": turn, "hints": hints}
# ADVICE ๋‹จ๊ณ„
stage = "ADVICE"
combined_hints = "\n".join(hints)
advice = call_advice(combined_hints)
history.append(("Chatbot", advice))
stage = "END"
return history, {"history":history, "stage":stage, "turn":turn, "hints":hints}
def gradio_predict(user_input, chat_state):
"""Gradio์—์„œ user_input, state๋ฅผ ๋ฐ›์•„ predict โ†’ (chatbot ์ถœ๋ ฅ, state ๊ฐฑ์‹ )."""
new_history, new_state = predict(user_input, chat_state)
# display_history: list of (user, assistant)
display_history = []
for (role, txt) in new_history:
if role == "User":
display_history.append([txt, ""])
else: # Chatbot
if not display_history:
display_history.append(["", txt])
elif display_history[-1][1] == "":
display_history[-1][1] = txt
else:
display_history.append(["", txt])
return display_history, new_state
def create_app():
"""Gradio Blocks UI ๊ตฌ์„ฑ."""
with gr.Blocks() as demo:
gr.Markdown("## ๐Ÿฅ ์†Œํฌ๋ผํ…Œ์Šค CBT ์ฑ—๋ด‡ (GPT-4o)")
chatbot = gr.Chatbot(label="Socratic CBT Chatbot")
chat_state = gr.State({
"history": [],
"stage":"EMPATHY",
"turn":0,
"hints":[]
})
txt = gr.Textbox(show_label=False, placeholder="๊ณ ๋ฏผ์ด๋‚˜ ๊ถ๊ธˆํ•œ ์ ์„ ์ž…๋ ฅํ•˜์„ธ์š”.")
txt.submit(fn=gradio_predict, inputs=[txt, chat_state], outputs=[chatbot, chat_state], scroll_to_output=True)
return demo
app = create_app()
if __name__ == "__main__":
# Launch Gradio app
app.launch(debug=True, share=True)