morethanair commited on
Commit
bdc9d3f
·
1 Parent(s): fda2213

공감 비서 프롬프트 및 설문조사 링크 위치 개선

Browse files
Files changed (1) hide show
  1. app.py +92 -89
app.py CHANGED
@@ -278,7 +278,7 @@ A: 단기적으로는 맞습니다. 성과 없이 퇴사하면 이력서에 남
278
  logger.error(f"Error during OpenAI API call: {e}", exc_info=True)
279
  return "답변을 생성하는 중에 문제가 발생했습니다. OpenAI API 키 또는 서비스 상태를 확인해주세요."
280
 
281
- # --- Streamlit 앱 UI (사이드바 메뉴 추가) ---
282
  st.set_page_config(page_title="Khan 멘토 (PM 영상 기반)", layout="wide")
283
 
284
  # --- 사이드바 메뉴 ---
@@ -286,16 +286,18 @@ menu = st.sidebar.radio(
286
  "기능 선택",
287
  ("Khan 멘토에게 상담하기", "상사에게 잘보이기")
288
  )
 
 
 
 
 
 
289
 
290
  openai_client = init_openai_client()
291
 
292
  if menu == "Khan 멘토에게 상담하기":
293
  st.title("✨ Khan 멘토에게 질문하기")
294
- st.markdown(
295
- '<a href="https://forms.gle/SUqrGBT3dktSB7v26" target="_blank" style="display:inline-block; background:#f9e79f; color:#1a237e; font-weight:bold; padding:0.5em 1.2em; border-radius:8px; text-decoration:none; font-size:1.1em; margin-bottom:16px;">📝 서비스 사용성 설문조사 참여하기</a>',
296
- unsafe_allow_html=True
297
- )
298
- st.markdown("PM 관련 영상 내용을 기반으로 Khan 멘토가 답변해 드립니다.")
299
 
300
  # --- API 키 확인 및 리소스 초기화 ---
301
  pc = init_pinecone()
@@ -303,99 +305,76 @@ if menu == "Khan 멘토에게 상담하기":
303
  index = get_pinecone_index(pc, INDEX_NAME)
304
 
305
  # --- 상태 관리 ---
306
- if 'user_state' not in st.session_state:
307
- st.session_state['user_state'] = ''
308
  if 'empathy_message' not in st.session_state:
309
  st.session_state['empathy_message'] = ''
310
- if 'example_questions' not in st.session_state:
311
- st.session_state['example_questions'] = []
312
- if 'selected_question' not in st.session_state:
313
- st.session_state['selected_question'] = ''
314
  if 'khan_answer' not in st.session_state:
315
  st.session_state['khan_answer'] = ''
316
- if 'step' not in st.session_state:
317
- st.session_state['step'] = 1
318
-
319
- # --- 1단계: 사용자 상태 입력 ---
320
- if st.session_state['step'] == 1:
321
- user_state = st.text_area("지금 어떤 상황이신가요? 고민을 자유롭게 적어주세요.", value=st.session_state['user_state'])
322
- if st.button("나의 이야기 들려주기 ✍️"):
323
- st.session_state['user_state'] = user_state
324
- # 2단계로 이동
325
- st.session_state['step'] = 2
326
- st.rerun()
327
-
328
- # --- 2단계: 공감 메시지 + 예시 질문 생성 ---
329
- if st.session_state['step'] == 2:
330
- with st.spinner("어떤 상황인지 고민해볼께요..."):
331
- # 1. 공감 메시지 생성
332
  empathy_prompt = f"""
333
- 너는 따뜻하고 공감 능력이 뛰어난 상담가야.
334
- 아래 사용자의 상황을 듣고, 충분히 감정적으로 공감해주고, 용기를 북돋아주는 말을 해줘.
335
- 부를 때는 '당신은', '당신이'처럼 친근한 말투로 부르고, 물음표는 사용하지 않아.
336
- 상황: "{st.session_state['user_state']}"
337
  """
338
  try:
339
  empathy_response = openai_client.chat.completions.create(
340
- model="gpt-4o",
341
  messages=[{"role": "system", "content": empathy_prompt}],
342
  temperature=0.7,
343
  )
344
  st.session_state['empathy_message'] = empathy_response.choices[0].message.content.strip()
345
  except Exception as e:
346
  st.session_state['empathy_message'] = f"공감 메시지 생성 중 오류: {e}"
347
- # 2. 예시 질문 생성
348
- example_prompt = f"""
349
- 아래 상황에서 Khan 멘토에게 수 있는 구체적이고 실용적인 질문 3~4가지를 한국어로 만들어줘.\n각 질문은 한 문장으로, 실제로 도움이 될 만한 내용이어야 해.\n상황: "{st.session_state['user_state']}"
350
- """
 
 
 
 
 
 
 
351
  try:
352
- example_response = openai_client.chat.completions.create(
353
- model="gpt-4o",
354
- messages=[{"role": "system", "content": example_prompt}],
355
- temperature=0.5,
356
  )
357
- # 응답에서 질문만 추출 (숫자/기호/줄바꿈 등 정제)
358
  import re
359
- raw = example_response.choices[0].message.content.strip()
360
  questions = re.findall(r'\d+\.\s*(.+)', raw)
361
  if not questions:
362
- # 숫자 없이 줄바꿈만 있을 경우
363
  questions = [q.strip('-• ').strip() for q in raw.split('\n') if q.strip()]
364
- st.session_state['example_questions'] = questions[:4]
365
- except Exception as e:
366
- st.session_state['example_questions'] = [f"예시 질문 생성 중 오류: {e}"]
367
- # 3단계로 이동
368
- st.session_state['step'] = 3
369
- st.rerun()
370
-
371
- # --- 3단계: 공감 메시지 + 예시 질문 버튼/직접입력 + Khan 답변 ---
372
- if st.session_state['step'] == 3:
373
- st.success(st.session_state['empathy_message'])
374
- st.markdown("#### 이런 질문을 해볼 수 있어요!")
375
- cols = st.columns(len(st.session_state['example_questions']))
376
- for i, q in enumerate(st.session_state['example_questions']):
377
- if cols[i].button(q):
378
- st.session_state['selected_question'] = q
379
- st.session_state['step'] = 4
380
  st.rerun()
381
- st.markdown("---")
382
- user_q = st.text_input("직접 궁금한 점을 입력해도 좋아요!", value=st.session_state['selected_question'])
383
- if st.button("Khan 멘토에게 질문하기"):
384
- st.session_state['selected_question'] = user_q
385
- st.session_state['step'] = 4
386
- st.rerun()
387
 
388
- # --- 4단계: Khan 멘토 답변 ---
389
- if st.session_state['step'] == 4:
390
- with st.spinner("Khan 멘토가 답변을 준비하는 중..."):
391
- pinecone_results = search(st.session_state['selected_question'], top_k=5, _index=index, _model=model)
392
- khan_answer = generate_khan_answer(st.session_state['selected_question'], pinecone_results, openai_client)
393
- st.session_state['khan_answer'] = khan_answer
394
  st.subheader("💡 Khan 멘토의 답변")
395
  st.markdown(st.session_state['khan_answer'])
396
  # 참고 영상 정보 표시
 
397
  if pinecone_results:
398
- with st.expander("답변에 참고한 영상 정보 보기"):
399
  displayed_urls = set()
400
  for i, r in enumerate(pinecone_results):
401
  url = r.get('URL', 'N/A')
@@ -437,25 +416,49 @@ if menu == "Khan 멘토에게 상담하기":
437
  except Exception as e:
438
  logger.warning(f"st.video failed for non-YouTube URL {url}: {e}")
439
  st.markdown("---")
440
- # --- 예시 질문 버튼/직접입력 다시 노출 (후속 질문) ---
441
- st.markdown("#### 추가로 궁금한 점이 있으신가요? 아래 예시 질문을 클릭하거나 직접 입력해보세요!")
442
- cols = st.columns(len(st.session_state['example_questions']))
443
- for i, q in enumerate(st.session_state['example_questions']):
444
- if cols[i].button(q, key=f"followup_{i}"):
445
- st.session_state['selected_question'] = q
446
- st.session_state['step'] = 4
447
- st.rerun()
448
- user_q = st.text_input("직접 궁금한 점을 입력해도 좋아요! (후속 질문)", value="", key="followup_input")
449
- if st.button("Khan 멘토에게 추가 질문하기", key="followup_btn"):
450
- st.session_state['selected_question'] = user_q
451
- st.session_state['step'] = 4
452
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
  st.markdown("---")
454
  st.caption("Powered by Pinecone, Sentence Transformers, and OpenAI")
455
- # 다시 처음으로 돌아가기 버튼
456
- if st.button("처음으로 돌아가기"):
457
- for k in ['user_state','empathy_message','example_questions','selected_question','khan_answer','step']:
458
- st.session_state[k] = '' if k != 'step' else 1
459
  st.rerun()
460
  else:
461
  st.title("👔 상사에게 잘보이기: 맞춤 보고문 만들기")
 
278
  logger.error(f"Error during OpenAI API call: {e}", exc_info=True)
279
  return "답변을 생성하는 중에 문제가 발생했습니다. OpenAI API 키 또는 서비스 상태를 확인해주세요."
280
 
281
+ # --- Streamlit 앱 UI (Khan 멘토 단일 루프 구조) ---
282
  st.set_page_config(page_title="Khan 멘토 (PM 영상 기반)", layout="wide")
283
 
284
  # --- 사이드바 메뉴 ---
 
286
  "기능 선택",
287
  ("Khan 멘토에게 상담하기", "상사에게 잘보이기")
288
  )
289
+ # 사이드바 맨 아래에 설문조사 링크
290
+ st.sidebar.markdown('<hr style="margin:1em 0;">', unsafe_allow_html=True)
291
+ st.sidebar.markdown(
292
+ '<a href="https://forms.gle/SUqrGBT3dktSB7v26" target="_blank" style="display:inline-block; background:#f9e79f; color:#1a237e; font-weight:bold; padding:0.5em 1.2em; border-radius:8px; text-decoration:none; font-size:1.1em; margin-bottom:16px;">📝 서비스 사용성 설문조사 참여하기</a>',
293
+ unsafe_allow_html=True
294
+ )
295
 
296
  openai_client = init_openai_client()
297
 
298
  if menu == "Khan 멘토에게 상담하기":
299
  st.title("✨ Khan 멘토에게 질문하기")
300
+
 
 
 
 
301
 
302
  # --- API 키 확인 및 리소스 초기화 ---
303
  pc = init_pinecone()
 
305
  index = get_pinecone_index(pc, INDEX_NAME)
306
 
307
  # --- 상태 관리 ---
308
+ if 'user_question' not in st.session_state:
309
+ st.session_state['user_question'] = ''
310
  if 'empathy_message' not in st.session_state:
311
  st.session_state['empathy_message'] = ''
 
 
 
 
312
  if 'khan_answer' not in st.session_state:
313
  st.session_state['khan_answer'] = ''
314
+ if 'pinecone_results' not in st.session_state:
315
+ st.session_state['pinecone_results'] = []
316
+ if 'extra_questions' not in st.session_state:
317
+ st.session_state['extra_questions'] = []
318
+ if 'current_input' not in st.session_state:
319
+ st.session_state['current_input'] = ''
320
+
321
+ # --- 질문 입력 및 답변 생성 ---
322
+ st.markdown("#### 당신의 고민을 알려 주세요!")
323
+ user_q = st.text_input("나의 고민은...", value=st.session_state['current_input'], key="main_input")
324
+ if st.button("질문하기", key="main_ask") or (user_q and st.session_state['user_question'] != user_q):
325
+ st.session_state['user_question'] = user_q
326
+ st.session_state['current_input'] = user_q
327
+ # 1. 공감 비서 메시지
328
+ with st.spinner("생각중..."):
 
329
  empathy_prompt = f"""
330
+ 너는 따뜻하고 친절한 비서야. 아래 사용자의 질문을 듣고, 감정적으로 충분하게 1~2문장으로 공감해주되 질문에 대한 답변은 하지마, 마지막에 '칸 멘토의 생각을 들어볼까요?'라고 안내해줘. \n질문: "{user_q}"
 
 
 
331
  """
332
  try:
333
  empathy_response = openai_client.chat.completions.create(
334
+ model="gpt-4o-mini",
335
  messages=[{"role": "system", "content": empathy_prompt}],
336
  temperature=0.7,
337
  )
338
  st.session_state['empathy_message'] = empathy_response.choices[0].message.content.strip()
339
  except Exception as e:
340
  st.session_state['empathy_message'] = f"공감 메시지 생성 중 오류: {e}"
341
+ # 2. Pinecone 검색 및 Khan 멘토 답변
342
+ with st.spinner("Khan 멘토가 뜸을 들이며..."):
343
+ pinecone_results = search(user_q, top_k=5, _index=index, _model=model)
344
+ st.session_state['pinecone_results'] = pinecone_results
345
+ khan_answer = generate_khan_answer(user_q, pinecone_results, openai_client)
346
+ st.session_state['khan_answer'] = khan_answer
347
+ # 3. 추가 질문 생성
348
+ with st.spinner("추가 질문을 생성하는 중..."):
349
+ extra_prompt = (
350
+ f"아래 질문에서 유사하게 궁금할 수 있는 추가 질문 3~4개를 한국어로 만들어줘. 지나치게 세부적인 툴에 대한 얘기보다는 프로덕트, 프로젝트, 리더십에 대한 일반적인 질문으로 만들어. 질문: \"{st.session_state['user_question']}"
351
+ )
352
  try:
353
+ extra_response = openai_client.chat.completions.create(
354
+ model="gpt-4o-mini",
355
+ messages=[{"role": "system", "content": extra_prompt}],
356
+ temperature=0.5
357
  )
 
358
  import re
359
+ raw = extra_response.choices[0].message.content.strip()
360
  questions = re.findall(r'\d+\.\s*(.+)', raw)
361
  if not questions:
 
362
  questions = [q.strip('-• ').strip() for q in raw.split('\n') if q.strip()]
363
+ st.session_state['extra_questions'] = questions[:4]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  st.rerun()
365
+ except Exception as e:
366
+ st.session_state['extra_questions'] = [f"추가 질문 생성 오류: {e}"]
367
+ st.rerun()
 
 
 
368
 
369
+ # --- 답변 추가질문 UI ---
370
+ if st.session_state['user_question']:
371
+ st.info(st.session_state['empathy_message'])
 
 
 
372
  st.subheader("💡 Khan 멘토의 답변")
373
  st.markdown(st.session_state['khan_answer'])
374
  # 참고 영상 정보 표시
375
+ pinecone_results = st.session_state['pinecone_results']
376
  if pinecone_results:
377
+ with st.expander("답변에 참고한 영상 정보 보기", expanded=True):
378
  displayed_urls = set()
379
  for i, r in enumerate(pinecone_results):
380
  url = r.get('URL', 'N/A')
 
416
  except Exception as e:
417
  logger.warning(f"st.video failed for non-YouTube URL {url}: {e}")
418
  st.markdown("---")
419
+ # --- 추가 질문 생성 타이밍 제어 ---
420
+ if 'extra_questions_ready' not in st.session_state or not st.session_state['extra_questions_ready']:
421
+ # 답변이 렌더링된 후에만 spinner 돌리기
422
+ st.session_state['extra_questions_ready'] = True
 
 
 
 
 
 
 
 
423
  st.rerun()
424
+ elif not st.session_state['extra_questions']:
425
+ # 백그라운드에서 추가 질문 생성 (스피너 없이)
426
+ extra_prompt = (
427
+ f"아래 질문에서 유사하게 궁금할 수 있는 추가 질문 3~4개를 한국어로 만들어줘. 지나치게 세부적인 툴에 대한 얘기보다는 프로덕트, 프로젝트, 리더십에 대한 일반적인 질문으로 만들어. 질문: \"{st.session_state['user_question']}"
428
+ )
429
+ try:
430
+ extra_response = openai_client.chat.completions.create(
431
+ model="gpt-4o-mini",
432
+ messages=[{"role": "system", "content": extra_prompt}],
433
+ temperature=0.5
434
+ )
435
+ import re
436
+ raw = extra_response.choices[0].message.content.strip()
437
+ questions = re.findall(r'\d+\.\s*(.+)', raw)
438
+ if not questions:
439
+ questions = [q.strip('-• ').strip() for q in raw.split('\n') if q.strip()]
440
+ st.session_state['extra_questions'] = questions[:4]
441
+ st.rerun()
442
+ except Exception as e:
443
+ st.session_state['extra_questions'] = [f"추가 질문 생성 중 오류: {e}"]
444
+ else:
445
+ st.markdown("#### 추가로 궁금한 점이 있으신가요? 아래 예시 질문을 클릭하거나 직접 입력해보세요!")
446
+ cols = st.columns(len(st.session_state['extra_questions']))
447
+ for i, q in enumerate(st.session_state['extra_questions']):
448
+ if cols[i].button(q, key=f"extra_{i}"):
449
+ st.session_state['current_input'] = q
450
+ st.session_state['user_question'] = ''
451
+ st.rerun()
452
+ user_extra = st.text_input("직접 추가 질문 입력", value="", key="extra_input")
453
+ if st.button("추가 질문하기", key="extra_btn"):
454
+ st.session_state['current_input'] = user_extra
455
+ st.session_state['user_question'] = ''
456
+ st.rerun()
457
  st.markdown("---")
458
  st.caption("Powered by Pinecone, Sentence Transformers, and OpenAI")
459
+ if st.button("다른 고민 상담하기"):
460
+ for k in ['user_question','empathy_message','khan_answer','pinecone_results','extra_questions','current_input','extra_questions_ready']:
461
+ st.session_state[k] = ''
 
462
  st.rerun()
463
  else:
464
  st.title("👔 상사에게 잘보이기: 맞춤 보고문 만들기")