ll7098ll commited on
Commit
2ab8dd5
·
verified ·
1 Parent(s): 5ff42d5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -467
app.py CHANGED
@@ -1,480 +1,141 @@
1
  import os
 
2
  import streamlit as st
3
- import datetime
4
- import google.generativeai as genai
5
- import time
6
-
7
- # --- Streamlit 설정 ---
8
- st.set_page_config(
9
- page_title="마음 건강 다이어리 💖",
10
- page_icon="💖",
11
- layout="wide",
12
- )
13
-
14
- # --- Custom CSS (스타일링) ---
15
- st.markdown(
16
- """
17
- <style>
18
- /* 전체 폰트 변경 (Nanum Gothic, Google Fonts CDN 사용) */
19
- @import url('https://fonts.googleapis.com/css2?family=Nanum+Gothic:wght@400;700&display=swap');
20
- body {
21
- font-family: 'Nanum Gothic', sans-serif !important;
22
- }
23
-
24
- /* 섹션 제목 스타일 */
25
- .section-title {
26
- font-size: 28px; /* 더 크게 */
27
- font-weight: bold;
28
- color: #212529;
29
- margin-bottom: 25px; /* 간격 증가 */
30
- padding-bottom: 15px; /* 패딩 증가 */
31
- border-bottom: 3px dotted #ffb3ba; /* 더 굵게 */
32
- }
33
-
34
- /* 섹션 부제목 스타일 */
35
- .section-subtitle {
36
- font-size: 18px; /* 약간 더 크게 */
37
- color: #555;
38
- margin-bottom: 15px; /* 간격 증가 */
39
- font-weight: 500; /* 살짝 굵게 */
40
- }
41
-
42
- /* 버튼 스타일 (둥근 모양, 파스텔톤, 그림자 효과, 강조) */
43
- div.stButton > button {
44
- background-color: #ffb3ba; /* 메인 버튼 색상 (분홍) */
45
- color: white;
46
- padding: 14px 28px; /* 패딩 증가 */
47
- font-size: 18px; /* 폰트 크기 증가 */
48
- border-radius: 25px; /* 더 둥근 모양 */
49
- border: none;
50
- box-shadow: 5px 5px 10px rgba(0,0,0,0.2); /* 그림자 효과 더 강하게 */
51
- transition: background-color 0.3s ease, transform 0.3s ease; /* 변환 효과 추가 */
52
- font-weight: bold; /* 텍스트 굵게 */
53
- }
54
- div.stButton > button:hover {
55
- background-color: #ff808a; /* 호버 시 버튼 색상 (더 진한 분홍) */
56
- transform: scale(1.07); /* 살짝 더 커지는 효과 */
57
- box-shadow: 5px 5px 15px rgba(0,0,0,0.3); /* 호버 시 그림자 효과 증가 */
58
- }
59
-
60
- /* 보조 버튼 스타일 (회색, 둥근 모양) */
61
- div.stButton > button.secondary-button {
62
- background-color: #ced4da; /* 보조 버튼 색상 */
63
- color: #495057;
64
- padding: 12px 24px; /* 패딩 약간 증가 */
65
- font-size: 16px; /* 폰트 크기 유지 */
66
- border-radius: 25px; /* 더 둥근 모양 */
67
- border: none;
68
- transition: background-color 0.3s ease;
69
- }
70
- div.stButton > button.secondary-button:hover {
71
- background-color: #bac0c5; /* 호버 시 보조 버튼 색상 */
72
- }
73
-
74
- /* Expander 스타일 (제목 굵게, 밑줄) */
75
- .streamlit-expanderHeader {
76
- font-weight: bold;
77
- color: #212529;
78
- border-bottom: 2px dotted #ffb3ba; /* 점선 밑줄 (분홍) */
79
- padding-bottom: 10px; /* 패딩 증가 */
80
- margin-bottom: 20px; /* 간격 증가 */
81
- font-size: 17px; /* 폰트 약간 키움 */
82
- }
83
-
84
- /* Info, Success, Error, Warning Box 스타일 (파스텔톤, 아이콘 추가) */
85
- div.stInfo, div.stSuccess, div.stError, div.stWarning {
86
- border-radius: 15px; /* 더 둥글게 */
87
- padding: 20px; /* 패딩 증가 */
88
- margin-bottom: 20px; /* 간격 증가 */
89
- box-shadow: 5px 5px 10px rgba(0,0,0,0.08); /* 그림자 효과 약간 증가 */
90
- border-left: none !important; /* 기존 왼쪽 테두리 제거 */
91
- background-color: #ffe0e5 !important; /* 기본 배경색 (연한 분홍) */
92
- }
93
- div.stInfo {
94
- background-color: #e0f7fa !important; /* Info 배경색 */
95
- }
96
- div.stSuccess {
97
- background-color: #e8f5e9 !important; /* Success 배경색 */
98
- }
99
- div.stError {
100
- background-color: #ffebee !important; /* Error 배경색 */
101
- }
102
- div.stWarning {
103
- background-color: #fffde7 !important; /* Warning 배경색 */
104
- }
105
-
106
- /* Info, Success, Error, Warning Box 아이콘 스타일 */
107
- div.stInfo::before, div.stSuccess::before, div.stError::before, div.stWarning::before {
108
- content: "";
109
- display: inline-block;
110
- width: 30px; /* 아이콘 크기 증가 */
111
- height: 30px; /* 아이콘 크기 증가 */
112
- margin-right: 15px; /* 간격 증가 */
113
- vertical-align: middle;
114
- background-size: contain;
115
- background-repeat: no-repeat;
116
- }
117
- div.stInfo::before {
118
- background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%2303a9f4"%3E%3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V5h2v4z"/%3E%3C/svg%3E'); /* Info 아이콘 (물음표) */
119
- }
120
- div.stSuccess::before {
121
- background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%234caf50"%3E%3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/%3E%3C/svg%3E'); /* Success 아이콘 (체크) */
122
- }
123
- div.stError::before {
124
- background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23f44336"%3E%3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/%3E%3C/svg%3E'); /* Error 아이콘 (느낌표) */
125
- }
126
- div.stWarning::before {
127
- background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23ff9800"%3E%3Cpath d="M10.29 3.86L1.83 18a2 2 0 0 0 1.73 3h16.94a2 2 0 0 0 1.73-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 15c-.55 0-1-.45-1-1v-2c0-.55.45-1 1-1s1 .45 1 1v2c0 .55-.45 1-1 1zm0-5c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1 .45 1 1 1z"/%3E%3C/svg%3E'); /* Warning 아이콘 (경고) */
128
- }
129
-
130
- /* Chat message 스타일 */
131
- .stChatMessage[data-streamlit="true"] {
132
- border-radius: 15px; /* 더 둥글게 */
133
- padding: 15px 20px; /* 패딩 증가 */
134
- margin-bottom: 15px; /* 간격 증가 */
135
- box-shadow: 3px 3px 7px rgba(0,0,0,0.08); /* 그림자 효과 약간 증가 */
136
- width: fit-content;
137
- max-width: 80%;
138
- font-size: 16px; /* 폰트 크기 약간 증가 */
139
- }
140
-
141
- .stChatMessage[data-streamlit="true"].user {
142
- background-color: #e0f7fa; /* User message background: light blue-green, slightly lighter */
143
- align-self: flex-end; /* User messages to the right */
144
- }
145
-
146
- .stChatMessage[data-streamlit="true"].assistant {
147
- background-color: #f0f8ff; /* Assistant message background: very light gray, slightly lighter */
148
- align-self: flex-start; /* Assistant messages to the left */
149
- }
150
-
151
-
152
- /* Toast message 스타일 */
153
- div.streamlit-toast-container {
154
- z-index: 10000; /* Toast를 항상 맨 위에 표시 */
155
- }
156
- div[data-testid="stToast"] {
157
- border-radius: 15px; /* 더 둥글게 */
158
- padding: 20px; /* 패딩 증가 */
159
- box-shadow: 5px 5px 10px rgba(0,0,0,0.1); /* 그림자 효과 약간 증가 */
160
- }
161
-
162
- /*챗 컨테이너 스타일 삭제 (더이상 필요 없음)*/
163
-
164
- </style>
165
- """,
166
- unsafe_allow_html=True,
167
- )
168
 
 
 
 
169
 
170
- # --- API 키 설정 (Hugging Face Secrets에서 관리 권장) ---
171
- if "GEMINI_API_KEY" not in os.environ:
172
- st.error("GEMINI_API_KEY 환경 변수가 설정되지 않았습니다. API 키를 설정해주세요.")
173
- st.stop()
174
- genai.configure(api_key=os.environ["GEMINI_API_KEY"])
175
-
176
- # --- Gemini 모델 설정 ---
177
- generation_config = {
178
- "temperature": 0.7,
179
- "top_p": 0.95,
180
- "top_k": 64,
181
- "max_output_tokens": 25000,
182
- "response_mime_type": "text/plain",
183
- }
184
- model = genai.GenerativeModel(
185
- model_name="gemini-2.0-flash-exp", # 또는 "gemini-pro"
186
- generation_config=generation_config,
187
- )
188
-
189
-
190
- # --- 세션 상태 초기화 ---
191
- if "daily_emotions" not in st.session_state:
192
- st.session_state["daily_emotions"] = {}
193
- if "current_tab" not in st.session_state:
194
- st.session_state["current_tab"] = "❤️ 감정 체크인" # 더 이상 탭이 없으므로 무의미하지만 유지
195
- if "chat_session" not in st.session_state:
196
- st.session_state["chat_session"] = model.start_chat(history=[])
197
- if "emotion_analysis_result" not in st.session_state:
198
- st.session_state["emotion_analysis_result"] = None
199
- if "counseling_messages" not in st.session_state:
200
- st.session_state["counseling_messages"] = []
201
- if "analysis_to_chatbot" not in st.session_state:
202
- st.session_state["analysis_to_chatbot"] = ""
203
- if "counseling_history" not in st.session_state: # 상담 로그 기록을 위한 세션 상태 추가
204
- st.session_state["counseling_history"] = []
205
- if "section_index" not in st.session_state: # 현재 섹션 index 저장
206
- st.session_state["section_index"] = 0
207
-
208
-
209
- # --- 감정 분석 함수 ---
210
- def analyze_emotion(emotion, reason):
211
- prompt = f"""
212
- **오늘의 감정:** {emotion}
213
- **감정 이유:** {reason}
214
-
215
- **지시:**
216
- 1. **감정 분석:** 제시된 감정과 이유를 바탕으로 **심리적인 관점**에서 감정을 분석하고, 2~3 문장으로 요약해주세요.
217
- 2. **격려 및 공감:** 분석 내용을 바탕으로 **따뜻하고 친절하게** 격려와 공감 메시지를 1~2 문장으로 덧붙여주세요.
218
-
219
- **출력 형식:**
220
- **[감정 분석]**
221
- - [감정 분석 내용]
222
-
223
- **[격려 및 공감]**
224
- - [격려 및 공감 메시지]
225
-
226
- **주의:**
227
- - 초등학생 3학년이 이해하기 쉬운 단어로, 친절하고 객관적인 어조로, **존댓말**을 사용해주세요.
228
- - 부정적인 감정에 대한 분석일 경우, 긍정적인 방향으로 전환될 수 있도록 격려하는 내용을 포함해주세요.
229
- """
230
- chat_session = st.session_state["chat_session"]
231
  try:
232
- response = chat_session.send_message(prompt)
233
- analysis_text = response.text.strip()
234
- return analysis_text
235
- except google.api_core.exceptions.ResourceExhausted as e:
236
- st.error(
237
- f"API 할당량 초과 오류가 발생했습니다. 잠시 후 다시 시도해주세요. 오류 메시지: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
238
  )
239
- return None
240
-
241
-
242
- # --- 감정 기록 함수 ---
243
- def record_emotion(emotion, reason):
244
- today = datetime.date.today()
245
- st.session_state["daily_emotions"][str(today)] = {"emotion": emotion, "reason": reason}
246
- st.toast(f"{today} 감정: {emotion} 😊 기록 완료!", icon="✅")
247
- st.session_state["emotion_analysis_result"] = None
248
- st.session_state["analysis_to_chatbot"] = None
249
- st.session_state["counseling_messages"] = []
250
- st.session_state["counseling_history"] = [] # 감정 기록 시 상담 기록 초기화
251
- st.session_state["section_index"] += 1 # 다음 섹션으로 이동
252
-
253
-
254
- # --- 감정 이력 표시 함수 ---
255
- def display_emotion_history():
256
- st.subheader("📊 감정 이력")
257
- st.markdown("날짜별 감정 변화와 그때의 이유를 확인해보세요.")
258
-
259
- if not st.session_state["daily_emotions"]:
260
- st.info("아직 기록된 감정이 없어요. '💖 감정 체크인' 섹션에서 오늘 하루의 감정을 기록해보세요.") # 탭 -> 섹션으로 변경, 아이콘 변경
261
- else:
262
- sorted_dates = sorted(st.session_state["daily_emotions"].keys(), reverse=True)
263
- for date_str in sorted_dates:
264
- emotion_data = st.session_state["daily_emotions"][date_str]
265
- st.write(f"- {date_str}: **{emotion_data['emotion']}**")
266
- if emotion_data['reason']:
267
- st.write(f" > {emotion_data['reason']}")
268
- st.markdown("---")
269
-
270
-
271
- # --- AI 감정 분석 및 상담 시작 함수 ---
272
- def start_emotion_analysis_and_counseling():
273
- today = str(datetime.date.today())
274
- emotion_data = st.session_state["daily_emotions"].get(today)
275
- if emotion_data and emotion_data['reason']:
276
- with st.spinner("AI가 감정을 분석하고 있어요..."):
277
- emotion_analysis_result = analyze_emotion(emotion_data['emotion'], emotion_data['reason'])
278
- st.session_state["emotion_analysis_result"] = emotion_analysis_result
279
- st.session_state["analysis_to_chatbot"] = emotion_analysis_result
280
- st.success("AI 감정 분석 완료! '🔮 AI 감정 분석' 섹션에서 결과를 확인하고, '💬 AI 상담 챗봇' 섹션에서 상담을 시작해보세요.") # 탭 -> 섹션으로 변경, 아이콘 변경
281
- st.session_state["section_index"] += 1 # 다음 섹션으로 이동
282
- else:
283
- st.warning("오늘의 감정과 감정 이유를 먼저 기록해주세요.")
284
 
 
 
285
 
286
- # --- 상담 안내 함수 ---
287
- def display_counseling_info():
288
- st.subheader("📢 상담 안내")
289
- st.markdown("마음이 힘들거나 어려운 일이 있을 때, 혼자 고민하지 마세요. ")
290
- st.markdown("학교 선생님이나 부모님께 이야기하거나, 상담 선생님의 도움을 받는 것도 좋은 방법입니다.")
291
-
292
- with st.expander("🏫 학교 상담"):
293
- st.markdown(
294
- """
295
- **학교 상담 선생님**께서는 여러분의 마음 건강을 위해 항상 도움을 주실 준비가 되어 있습니다.
296
- - **상담 가능 내용:** 학교생활의 어려움, 친구 관계, 가족 문제, 진로 고민 등
297
- - **상담 신청 방법:** 담임 선생님께 말씀드리거나, 상담실에 직접 방문하여 신청할 수 있습니다.
298
- - **상담 시간:** (예시) 점심시간, 방과 후 시간 등 (학교 상담실 운영 시간에 따라 다름)
299
- """
300
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
- with st.expander("👨‍👩‍👧‍👦 학부모 상담"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  st.markdown(
304
- """
305
- 부모님께 솔직하게 이야기하고 도움을 요청하는 것도 좋은 방법입니다.
306
- - **상담 가능 내용:** 가정 문제, 학교 문제, 개인적인 고민 등 모든 내용
307
- - **상담 방법:** 편안한 시간과 장소에서 부모님과 대화해보세요.
308
- - **팁:** 부모님께 먼저 '이야기하고 싶은 것이 있다'고 말씀드리면, 부모님께서 더 잘 들어주실 거예요.
309
- """
310
  )
311
 
312
- st.markdown("**🌟 중요한 건 혼힘들어하지 않고, 주변에 도움을 요청하는 용기니다. 여러분은 혼자가 아니에요!**")
313
- st.markdown("---")
314
-
315
-
316
-
317
- # --- 메인 화면 ---
318
- def main():
319
- # 메인 컬럼 설정
320
- col_empty_left, col_main, col_empty_right = st.columns([1, 3, 1])
321
-
322
- with st.sidebar: # 사이드바 내용
323
- st.markdown("# 💖 마음 건강 다이어리") # 앱 이름 변경, 아이콘 변경
324
- st.markdown("### 당신의 마음을 기록하고, AI와 상담하세요") # 부제목 변경
325
- st.markdown("매일 감정을 기록하고, AI와 마음 건강 상담을 시작해보세요! 🥰")
326
- with st.expander("🚀 앱 사용 가이드", expanded=False):
327
- st.markdown(
328
- """
329
- **1단계: 감정 체크인 💖**
330
- - '💖 감정 체크인' 섹션에서 오늘 하루의 감정을 선택하고, 왜 그런 감정을 느꼈는지 이유를 적어주세요.
331
- - '✅ 감정 기록 완료' 버튼을 눌러 감정을 기록하면 다음 섹션으로 이동합니다.
332
-
333
- **2단계: AI 감��� 분석 🔮**
334
- - '🔮 AI 감정 분석' 섹션에서 '✨ AI 감정 분석 시작' 버튼을 눌러 AI 감정 분석을 시작합니다.
335
- - AI 분석 결과를 확인하고, '다음 단계' 버튼을 눌러 다음 섹션으로 이동하세요.
336
-
337
- **3단계: AI 상담 챗봇 💬**
338
- - '💬 AI 상담 챗봇' 섹션에서 AI 챗봇과 자유롭게 대화하며 마음을 정리해보세요.
339
- - '📊 감정 이력' 버튼을 눌러 다음 섹션으로 이동하세요.
340
-
341
- **4단계: 감정 이력 확인 📊**
342
- - '📊 감정 이력' 섹션에서 날짜별 감정 변화와 감정 이유를 확인할 수 있습니다.
343
- - '📢 상담 안내' 버튼을 눌러 다음 섹션으로 이동하세요.
344
-
345
- **5단계: 상담 안내 📢**
346
- - '📢 상담 안내' 섹션에서 학교 상담, 학부모 상담 정보를 확인하고 필요시 도움을 요청하세요.
347
- - 모든 단계를 완료했습니다! '💖 감정 체크인' 섹션으로 돌아가 다시 시작해보세요.
348
-
349
- **💖 마음 건강 다이어리와 함께 행복한 하루를 만들어가세요! 💪** # 마무리 문구 변경
350
- - 이 앱을 통해 자신의 감정을 이해하고, AI 상담을 통해 마음을 건강하게 관리해보세요! 😊
351
- """
352
- )
353
- st.markdown("---")
354
- st.markdown("<p style='font-size: 14px; color: #777;'>Version 1.0</p>", unsafe_allow_html=True) # 버전 정보 추가
355
-
356
-
357
- with col_main: # 메인 컬럼 내용
358
-
359
- sections = [
360
- "❤️ 감정 체크인",
361
- "🔮 AI 감정 분석",
362
- "💬 AI 상담 챗봇",
363
- "📊 감정 이력",
364
- "📢 상담 안내",
365
- ]
366
-
367
- current_section_index = st.session_state.get("section_index", 0)
368
-
369
- # --- ❤️ 감정 체크인 섹션 ---
370
- if st.session_state.section_index == 0:
371
- with st.container():
372
- st.markdown('<p class="section-title">💖 감정 체크인</p>', unsafe_allow_html=True) # 섹션 제목, 아이콘 변경
373
- st.markdown('<p class="section-subtitle">오늘 하루를 되돌아보고, 당신의 감정을 기록해보세요.</p>', unsafe_allow_html=True) # 섹션 부제목
374
-
375
- emotion_options = ["😄 행복해요", "😊 좋아요", "😶 평범해요", "🙁 슬퍼요", "😭 매우 슬퍼요", "😠 화나요", "😡 매우 화나요", "😥 불안해요", "😨 무서워요"]
376
- emotion = st.radio("오늘 나의 감정", emotion_options, index=2) # 라디오 버튼 제목 변경
377
- reason = st.text_area("✍️ 감정 이유 (선택 사항, 간단하게 적어보세요)", height=100) # 텍스트 area 제목 변경, 선택 사항 안내
378
-
379
- if st.button("✅ 감정 기록 완료", use_container_width=True): # 버튼 텍스트 변경
380
- if reason:
381
- record_emotion(emotion, reason)
382
- else:
383
- record_emotion(emotion, reason)
384
-
385
- # --- 🔮 AI 감정 분석 섹션 ---
386
- if st.session_state.section_index == 1:
387
- with st.container():
388
- st.markdown('<p class="section-title">🔮 AI 감정 분석</p>', unsafe_allow_html=True) # 섹션 제목, 아이콘 변경
389
- st.markdown('<p class="section-subtitle">AI가 당신의 감정을 분석하고, 따뜻한 위로와 공감 메시지를 전달합니다.</p>', unsafe_allow_html=True) # 섹션 부제목
390
-
391
- if st.button("✨ AI 감정 분석 시작", use_container_width=True): # 버튼 텍스트 유지
392
- start_emotion_analysis_and_counseling()
393
-
394
- if st.session_state["emotion_analysis_result"]:
395
- analysis_result_text = st.session_state["emotion_analysis_result"]
396
- st.info(analysis_result_text) # 분석 결과 표시
397
- if st.button("💬 AI 상담 챗봇 시작", use_container_width=True): # 다음 섹션 이동 버튼
398
- st.session_state.section_index += 1
399
- st.rerun()
400
- elif st.session_state["daily_emotions"].get(str(datetime.date.today())) and st.session_state["daily_emotions"].get(str(datetime.date.today())).get('reason'):
401
- st.info("👆 **'✨ AI 감정 분석 시작'** 버튼을 클릭하여 감정 분석을 시작해보세요.") # 안내 문구 유지
402
- else:
403
- st.info("🤔 먼저 '💖 감정 체크인' 섹션에서 감정을 기록해야 AI 감정 분석을 시작할 수 있습니다.") # 안내 문구 개선, 아이콘 변경
404
-
405
- # --- 💬 AI 상담 챗봇 섹션 ---
406
- if st.session_state.section_index == 2:
407
- with st.container():
408
- st.markdown('<p class="section-title">💬 AI 상담 챗봇</p>', unsafe_allow_html=True) # 섹션 제목
409
- st.markdown('<p class="section-subtitle">AI 챗봇과 편안하게 대화하며 당신의 마음을 돌보세요.</p>', unsafe_allow_html=True) # 섹션 부제목
410
- st.markdown("AI 챗봇은 익명의 상담 친구처럼 당신의 이야기에 귀 기울이고 공감하며, 긍정적인 방향으로 나아갈 수 있도록 부드럽게 대화를 이끌어 줄 것입니다.") # 챗봇 설명 추가
411
-
412
- if prompt := st.chat_input("AI 챗봇에게 이야기해보세요 (무엇이든 괜찮아요 😊)", key="chat_input"): # 입력창 placeholder 변경, 이모티콘 추가
413
- st.session_state["counseling_messages"].append({"role": "user", "content": prompt})
414
- st.session_state["counseling_history"].append({"role": "user", "content": prompt}) # 상담 history에 사용자 메시지 추가
415
- with st.chat_message("user"):
416
- st.write(prompt)
417
-
418
- with st.chat_message("assistant"):
419
- with st.spinner("AI 챗봇이 생각 중..."):
420
- chat_session = st.session_state["chat_session"]
421
- # 이전 상담 로그를 프롬프트에 포함하여 전달
422
- history_text = "\n".join([f"{msg['role']}: {msg['content']}" for msg in st.session_state["counseling_history"]]) # 상담 history를 텍스트로 변환
423
- enhanced_prompt = f"""**이전 상담 기록:**\n{history_text}\n\n**사용자 입력:** {prompt}\n\n**AI 상담사 역할:**
424
- - 30대 여성 심리 상담 전문가
425
- - 내담자의 감정에 깊이 공감하고, 따뜻하게 위로와 격려를 건네는 역할
426
- - 친절하고 부드러운 말투 사용 (예: ~군요, ~요, ~게요)
427
- - 단답형, 명령형, 비판적인 말투는 지양
428
- - 질문을 통해 내담자의 감정을 더 깊이 이해하려고 노력
429
- - 해결책 제시보다는 공감과 경청, 지지를 통해 내담자 스스로 답을 찾도록 돕는 역할
430
-
431
- **응답:**""" # AI 상담사 역할 및 지시사항 추가
432
- response = chat_session.send_message(enhanced_prompt) # 향상된 프롬프트 사용
433
- ai_response = response.text.strip()
434
- st.session_state["counseling_messages"].append(
435
- {"role": "assistant", "content": ai_response}
436
- )
437
- st.session_state["counseling_history"].append({"role": "assistant", "content": ai_response}) # 상담 history에 챗봇 응답 추가
438
- st.write(ai_response)
439
-
440
-
441
- if "counseling_messages" not in st.session_state:
442
- st.session_state["counseling_messages"] = []
443
-
444
- # 챗봇 시작 메시지 (분석 결과가 있을 때, 최초 1회만)
445
- if not st.session_state["counseling_messages"] and st.session_state["analysis_to_chatbot"]:
446
- initial_chatbot_message = f"안녕하세요! 😊 오늘 {st.session_state['daily_emotions'][str(datetime.date.today())]['emotion']} 감정을 느끼셨군요. AI 감정 분석 결과는 이렇습니다.\n\n{st.session_state['analysis_to_chatbot']}\n\n 이전 상담 기록을 바탕으로, {st.session_state['daily_emotions'][str(datetime.date.today())]['emotion']}님에게 더 **따뜻하고 편안한** 상담을 제공해드릴 수 있을 것 같아요. **지금 기분이 어떠신가요?** 저에게 **편하게** 이야기해주세요. 🥰"
447
- st.session_state["counseling_messages"].append({"role": "assistant", "content": initial_chatbot_message})
448
- st.session_state["counseling_history"].append({"role": "assistant", "content": initial_chatbot_message}) # 상담 history에도 시작 메시지 추가
449
-
450
- for msg in st.session_state["counseling_messages"]: # 메시지 순서대로 표시 (reversed 삭제)
451
- with st.chat_message(msg["role"]):
452
- st.write(msg["content"])
453
- if st.button("📊 감정 이력 확인", use_container_width=True): # 다음 섹션 이동 버튼
454
- st.session_state.section_index += 1
455
- st.rerun()
456
-
457
-
458
- # --- 📊 감정 이력 섹션 ---
459
- if st.session_state.section_index == 3:
460
- with st.container():
461
- st.markdown('<p class="section-title">📊 감정 이력</p>', unsafe_allow_html=True) # 섹션 제목
462
- st.markdown('<p class="section-subtitle">지난 감정 기록을 통해 마음의 변화를 살펴보세요.</p>', unsafe_allow_html=True) # 섹션 부제목
463
- display_emotion_history()
464
- if st.button("📢 상담 안내 확인", use_container_width=True): # 다음 섹션 이동 버튼
465
- st.session_state.section_index += 1
466
- st.rerun()
467
-
468
-
469
- # --- 📢 상담 안내 섹션 ---
470
- if st.session_state.section_index == 4:
471
- with st.container():
472
- st.markdown('<p class="section-title">📢 상담 안내</p>', unsafe_allow_html=True) # 섹션 제목
473
- st.markdown('<p class="section-subtitle">혼자 힘든가요? 언제든 전문가의 도움을 요청하세요.</p>', unsafe_allow_html=True) # 섹션 부제목
474
- display_counseling_info()
475
- if st.button("💖 처음으로 돌아가기", use_container_width=True): # 처음 섹션 이동 버튼
476
- st.session_state.section_index = 0
477
- st.rerun()
478
 
479
- if __name__ == "__main__":
480
- main()
 
 
 
1
  import os
2
+ import openai
3
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
+ # OpenAI API 설정
6
+ openai_api_key = os.getenv("OPENAI_API_KEY")
7
+ openai.api_key = openai_api_key
8
 
9
+ def openai_chat(text, grade_level):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  try:
11
+ # 학년 수준에 따른 프롬프트 추가
12
+ grade_prompt = f"이 학생은 초등학교 {grade_level}학년입니다. 질문과 설명을 이 학년 수준에 맞게 해주세요.(학생의 학년에 맞게 질문과 답하는 내용의 범위를 해당 학년의 교육과정에 맞게 범위를 조정하세요."
13
+
14
+ # 시스템 메시지를 세션 시작 시 한 ��만 추가
15
+ if not any(message["role"] == "system" for message in st.session_state.messages):
16
+ st.session_state.messages.append({"role": "system", "content": grade_prompt})
17
+ else:
18
+ # 기존 시스템 메시지 업데이트 (선택 사항)
19
+ st.session_state.messages[0]["content"] = grade_prompt
20
+
21
+ response = openai.ChatCompletion.create(
22
+ model="gpt-4o",
23
+ messages=st.session_state.messages, # 전체 대화 세션 전달
24
+ temperature=0.7,
25
+ max_tokens=1000, # max_tokens 값 조정
26
+ top_p=0.9,
27
+ frequency_penalty=0,
28
+ presence_penalty=0
29
  )
30
+ return response.choices[0].message["content"]
31
+ except Exception as e:
32
+ return f"에러 발생: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ # Streamlit 앱 설정
35
+ st.set_page_config(page_title="AI 튜터", page_icon="🎓", initial_sidebar_state="expanded")
36
 
37
+ # 페이지 스타일 커스터마이징
38
+ st.markdown(
39
+ """
40
+ <style>
41
+ /* 배경색 설정 */
42
+ .stApp {
43
+ background-color: #fffafa; /* 은은한 핑크색 배경 */
44
+ }
45
+ /* 타이틀 스타일 */
46
+ .main-title {
47
+ font-size: 3rem;
48
+ color: #000000; /* 검은색 */
49
+ font-weight: 700;
50
+ text-align: center;
51
+ margin-bottom: 20px;
52
+ }
53
+ /* 채팅 메시지 스타일 */
54
+ .chat-message {
55
+ border-radius: 15px;
56
+ padding: 15px;
57
+ margin: 10px 0;
58
+ display: flex;
59
+ align-items: center;
60
+ flex-wrap: wrap;
61
+ word-break: break-word;
62
+ }
63
+ .chat-message-user {
64
+ background-color: #ffebef; /* 따뜻한 파스텔 핑크 */
65
+ color: #8b4513;
66
+ justify-content: flex-end;
67
+ }
68
+ .chat-message-assistant {
69
+ background-color: #ffe4e6; /* 부드러운 파스텔 복숭아 핑크색 */
70
+ color: #6b4226;
71
+ justify-content: flex-start;
72
+ }
73
+ .chat-avatar {
74
+ width: 40px;
75
+ height: 40px;
76
+ border-radius: 50%;
77
+ margin-right: 10px;
78
+ }
79
+ .chat-avatar-user {
80
+ margin-left: 10px;
81
+ margin-right: 0;
82
+ }
83
+ /* 사용자 입력 창 스타일 */
84
+ .stTextInput input {
85
+ border-radius: 15px;
86
+ border: 2px solid #cd857f; /* 따뜻한 핑크색 */
87
+ }
88
+ /* 버튼 스타일 */
89
+ .stButton button {
90
+ background-color: #cd857f; /* 따뜻한 핑크색 */
91
+ color: #fff;
92
+ border-radius: 15px;
93
+ padding: 10px 20px;
94
+ }
95
+ </style>
96
+ """,
97
+ unsafe_allow_html=True
98
+ )
99
 
100
+ # 메인 타이틀
101
+ st.markdown("<div class='main-title'>AI 튜터 🎓</div>", unsafe_allow_html=True)
102
+
103
+ # 채팅 세션 초기화
104
+ if "messages" not in st.session_state:
105
+ st.session_state.messages = [
106
+ {"role": "system", "content": "친절하고 든든한 튜터가 되어 학생이 목표를 달성하도록 안내하고, 학생이 주제에서 벗어나면 부드럽게 다시 집중하도록 도와주세요. 학생이 질문에 대해 틀린 대답을 하는 경우, 정답을 알려주거나 왜 틀렸는지 설명해주고, 추가 질문을 통해 학생 스스로 오류를 발견하고 정답을 찾도록 유도해주세요. 학생이 큰 개념을 단계적으로 이해하도록 돕는 질문을 하고, 그 개념을 깊이 파고들 수 있도록 탐구 질문을 던지세요. 학생이 부담을 느끼지 않도록 한 번에 한 질문만 하세요. 학생이 이해한 것을 보여주면 대화를 마무리하세요."}
107
+ ]
108
+
109
+ # 사용자와 AI의 아이콘 URL 설정
110
+ user_icon_url = "https://cdn-icons-png.flaticon.com/512/1995/1995531.png" # 학생 아이콘 (책을 든 학생)
111
+ assistant_icon_url = "https://cdn-icons-png.flaticon.com/512/4323/4323008.png" # 튜터 아이콘 (안경 쓴 선생님)
112
+
113
+ # 학년 수준 선택
114
+ with st.sidebar:
115
+ grade_level = st.selectbox("📚 학년 수준을 선택하세요:", ["1", "2", "3", "4", "5", "6"], index=0, format_func=lambda x: f"초등학교 {x}학년")
116
+ # 초기화 버튼
117
+ if st.button("💡 초기화"):
118
+ del st.session_state.messages # 메시지 세션 상태 삭제
119
+ st.rerun()
120
+
121
+ # 채팅 메시지 표시
122
+ for message in st.session_state.messages:
123
+ if message["role"] != "system":
124
+ role_class = "chat-message-user" if message["role"] == "user" else "chat-message-assistant"
125
+ avatar_url = user_icon_url if message["role"] == "user" else assistant_icon_url
126
+ avatar_class = "chat-avatar-user" if message["role"] == "user" else "chat-avatar"
127
  st.markdown(
128
+ f"<div class='chat-message {role_class}'><img src='{avatar_url}' class='chat-avatar {avatar_class}'>{message['content']}</div>",
129
+ unsafe_allow_html=True
 
 
 
 
130
  )
131
 
132
+ # 사용자 입 받기
133
+ if prompt := st.chat_input("📝학습 내용을 입력하면 AI 튜터가 질문을 통해서 개념을 이해하도록 도와줍니다."):
134
+ # 사용자의 메시지를 세션에 추가
135
+ st.session_state.messages.append({"role": "user", "content": prompt})
136
+ st.markdown(f"<div class='chat-message chat-message-user'>{prompt}<img src='{user_icon_url}' class='chat-avatar chat-avatar-user'></div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
+ # OpenAI API 호출
139
+ response = openai_chat(prompt, grade_level)
140
+ st.session_state.messages.append({"role": "assistant", "content": response})
141
+ st.markdown(f"<div class='chat-message chat-message-assistant'><img src='{assistant_icon_url}' class='chat-avatar'>{response}</div>", unsafe_allow_html=True)