| | import time |
| | import streamlit as st |
| | import google.generativeai as genai |
| | from streamlit_extras.colored_header import colored_header |
| | import markdown |
| |
|
| | |
| | st.set_page_config(page_title="MBTI 관계 시뮬레이터", page_icon="🤝", layout="wide") |
| |
|
| | |
| |
|
| | |
| | try: |
| | |
| | api_key = st.secrets.get("GEMINI_API_KEY") |
| | if not api_key: |
| | st.error("Streamlit secrets에 'GEMINI_API_KEY'를 설정해주세요.") |
| | st.info("Secrets 설정 방법: [Streamlit Docs](https://docs.streamlit.io/library/advanced-features/secrets-management)") |
| | st.stop() |
| | genai.configure(api_key=api_key) |
| | except Exception as e: |
| | st.error(f"API 키 설정 중 예상치 못한 오류 발생: {e}") |
| | st.stop() |
| |
|
| |
|
| | |
| | generation_config = { |
| | "temperature": 0.75, |
| | "top_p": 0.8, |
| | "top_k": 40, |
| | "max_output_tokens": 15000, |
| | } |
| |
|
| | |
| | |
| | |
| | target_model_name = "gemini-2.0-flash-thinking-exp-01-21" |
| |
|
| | try: |
| | model = genai.GenerativeModel( |
| | model_name=target_model_name, |
| | generation_config=generation_config, |
| | |
| | safety_settings=[ |
| | {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"}, |
| | {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_MEDIUM_AND_ABOVE"}, |
| | {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"}, |
| | {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"}, |
| | ] |
| | ) |
| | |
| | |
| | except Exception as e: |
| | st.error(f"Gemini 모델 '{target_model_name}' 로딩 중 오류 발생: {e}") |
| | st.error("모델 이름을 다시 확인하거나, 사용 가능한 다른 모델(예: 'gemini-1.5-flash-latest')을 시도해보세요.") |
| | |
| | st.info("사용 가능한 모델 목록은 Google AI Studio 또는 Gemini API 문서를 참조하세요.") |
| | st.stop() |
| |
|
| |
|
| | |
| | mbti_types = { |
| | "INTJ": "전략가 (Architect)", |
| | "INTP": "논리술사 (Logician)", |
| | "ENTJ": "통솔자 (Commander)", |
| | "ENTP": "변론가 (Debater)", |
| | "INFJ": "옹호자 (Advocate)", |
| | "INFP": "중재자 (Mediator)", |
| | "ENFJ": "선도자 (Protagonist)", |
| | "ENFP": "활동가 (Campaigner)", |
| | "ISTJ": "현실주의자 (Logistician)", |
| | "ISFJ": "수호자 (Defender)", |
| | "ESTJ": "경영자 (Executive)", |
| | "ESFJ": "관리자 (Consul)", |
| | "ISTP": "장인 (Virtuoso)", |
| | "ISFP": "모험가 (Adventurer)", |
| | "ESTP": "사업가 (Entrepreneur)", |
| | "ESFP": "연예인 (Entertainer)" |
| | } |
| |
|
| | mbti_descriptions = { |
| | "INTJ": "상상력이 풍부하며 결단력이 있는 전략가입니다. 모든 일에 계획을 세우며, 지식을 갈망하고 논리적 사고를 중시합니다. 독립적이며 때로는 비판적으로 보일 수 있습니다.", |
| | "INTP": "끊임없이 새로운 지식에 목말라 하는 혁신가입니다. 분석적이고 객관적이며, 복잡한 문제를 해결하는 데 뛰어난 능력을 보입니다. 때로는 추상적인 개념에 몰두하는 경향이 있습니다.", |
| | "ENTJ": "대담하며 상상력이 풍부한 강력한 의지의 소유자로, 항상 길을 찾거나 만들어냅니다. 타고난 리더이며, 목표 지향적이고 효율성을 추구합니다. 때로는 다른 사람의 감정을 간과할 수 있습니다.", |
| | "ENTP": "지적 도전을 즐기는 똑똑하고 호기심 많은 사색가입니다. 새로운 아이디어를 탐구하고 논쟁하는 것을 좋아하며, 틀에 박힌 것을 싫어합니다. 때로는 일관성이 부족할 수 있습니다.", |
| | "INFJ": "조용하고 신비로우면서도 샘솟는 영감으로 지칠 줄 모르는 이상주의자입니다. 깊은 통찰력과 강한 직관력으로 사람들을 돕고자 하며, 의미 있는 관계를 추구합니다. 때로는 지나치게 완벽주의적일 수 있습니다.", |
| | "INFP": "상냥한 성격의 이타주의자로, 건강하고 밝은 사회 건설에 앞장서는 낭만형입니다. 깊은 감수성과 공감 능력을 지녔으며, 자신의 가치관에 따라 행동합니다. 때로는 현실 감각이 부족할 수 있습니다.", |
| | "ENFJ": "넘치는 카리스마와 영향력으로 청중을 압도하는 리더형입니다. 사람들에게 영감을 주고 긍정적인 변화를 이끌어내는 것을 목표로 하며, 타인의 성장을 돕는 데 열정적입니다. 때로는 타인의 인정을 지나치게 갈망할 수 있습니다.", |
| | "ENFP": "창의적이며 항상 웃을 거리를 찾아다니는 활발한 성격으로, 사람들과 자유롭게 어울리기를 좋아합니다. 열정적이고 사교적이며, 새로운 가능성을 탐색하는 것을 즐깁니다. 때로는 쉽게 싫증을 느낄 수 있습니다.", |
| | "ISTJ": "사실에 근거하여 사고하며 이성적이고 믿을 수 있는 현실주의자입니다. 책임감이 강하고 철저하며, 전통과 질서를 중시합니다. 때로는 변화에 저항적일 수 있습니다.", |
| | "ISFJ": "소중한 이들을 보호하는 데 심혈을 기울이는 헌신적이고 따뜻한 수호자입니다. 세심하고 충실하며, 타인의 감정에 민감하고 실질적인 도움을 주고자 합니다. 때로는 자신의 필요를 간과할 수 있습니다.", |
| | "ESTJ": "사물이나 사람을 관리하는 데 타의 추종을 불허하는 뛰어난 실력의 소유자입니다. 조직적이고 단호하며, 규칙과 절차를 중요하게 생각합니다. 때로는 지나치게 통제하려 할 수 있습니다.", |
| | "ESFJ": "타인을 향한 세심한 관심과 사교적인 성향으로 사람들 내에서 인기가 많으며, 타인을 돕는 데 열성적입니다. 협조적이고 동정심이 많으며, 조화로운 관계를 중요시합니다. 때로는 비판에 민감할 수 있습니다.", |
| | "ISTP": "대담하고 현실적인 성향으로 다양한 도구를 능숙하게 다루는 탐험형입니다. 논리적이고 실용적이며, 문제 해결 능력이 뛰어납니다. 위기 상황에서 침착함을 유지합니다. 때로는 감정 표현에 서툴 수 있습니다.", |
| | "ISFP": "항상 새로운 것을 찾아 시도하거나 도전할 준비가 되어 있는 융통성 있는 성격의 매력 넘치는 예술가입니다. 온화하고 겸손하며, 현재의 순간을 즐기고 미적 감각이 뛰어납니다. 때로는 장기적인 계획 수립에 어려움을 겪을 수 있습니다.", |
| | "ESTP": "명석한 두뇌와 에너지, 그리고 뛰어난 직관력으로 위험을 기회로 만드는 재치 있는 사업가입니다. 행동 지향적이고 사교적이며, 현실적인 문제 해결에 능숙합니다. 때로는 충동적일 수 있습니다.", |
| | "ESFP": "주위에 있으면 인생이 지루할 새가 없을 정도로 즉흥적이며 열정과 에너지가 넘치는 연예인형입니다. 사교적이고 낙천적이며, 사람들과 어울리는 것을 즐깁니다. 때로는 깊이 있는 관계 형성에 어려움을 느낄 수 있습니다." |
| | } |
| |
|
| |
|
| | |
| | relationship_types_two = { |
| | "연인": "Romantic Couple", |
| | "부부": "Married Couple", |
| | "친구": "Friends", |
| | "가족 (형제자매, 부모자식 등)": "Family", |
| | "직장 동료": "Coworkers", |
| | "상사-부하": "Supervisor-Subordinate", |
| | "기타": "Others" |
| | } |
| |
|
| | relationship_types_multiple = { |
| | "친구들": "Friends Group", |
| | "가족": "Family", |
| | "직장 팀": "Work Team", |
| | "프로젝트 팀": "Project Team", |
| | "스터디 그룹": "Study Group", |
| | "동호회/모임": "Club/Social Group", |
| | "기타": "Others" |
| | } |
| |
|
| | |
| |
|
| | def generate_relationship_scenario(people, relationship, situation): |
| | """Generates the relationship scenario using the Gemini API.""" |
| |
|
| | |
| | def create_type_info(person): |
| | |
| | name = person.get('name', '이름없음') |
| | gender = person.get('gender', '미지정') |
| | mbti_type = person.get('type', '미지정') |
| | type_description = mbti_descriptions.get(mbti_type, '알 수 없는 유형') |
| | return f"{name} ({gender}, {mbti_type}): {type_description}" |
| |
|
| | |
| | if people: |
| | people_info = "\n".join([f"- {create_type_info(person)}" for person in people]) |
| | else: |
| | |
| | st.error("참여자 정보가 올바르게 전달되지 않았습니다. 사이드바 입력을 확인해주세요.") |
| | return "" |
| |
|
| | |
| | relationship_en = relationship_types_two.get(relationship, relationship_types_multiple.get(relationship, '')) |
| |
|
| | |
| | system_prompt = f""" |
| | 당신은 MBTI 심층 분석가이자 관계 코칭 전문가입니다. 제공된 정보를 바탕으로 매우 상세하고 통찰력 있는 관계 시나리오를 생성해주세요. 각 MBTI 유형의 대표적인 특징뿐만 아니라, 개인 간의 미묘한 상호작용과 심리적 역학에 초점을 맞춰 분석해야 합니다. |
| | |
| | **[기본 정보]** |
| | |
| | * **참여자:** |
| | {people_info} |
| | * **관계 유형:** {relationship} ({relationship_en}) |
| | * **주어진 상황:** {situation} |
| | |
| | **[요청 사항]** |
| | |
| | **1. 참여자 MBTI 심층 분석:** |
| | * 각 참여자의 MBTI 유형에 대해 핵심 가치, 주요 동기, 의사소통 스타일, 스트레스 반응, 잠재적 강점 및 약점을 깊이 있게 설명해주세요. |
| | * 단순한 유형 설명을 넘어, 해당 유형이 주어진 **상황**과 **관계** 속에서 어떻게 발현될 가능성이 높은지 예측해주세요. |
| | |
| | **2. 관계 시나리오 상세 묘사:** |
| | * 주어진 상황을 바탕으로, 참여자들 간의 상호작용을 단계별 시나리오로 구체화해주세요. |
| | * **각 단계별로 다음 요소를 반드시 포함하여 상세하게 묘사해주세요:** |
| | * **구체적인 대화:** 실제 대화처럼 자연스럽게 작성해주세요. |
| | * **관찰 가능한 행동:** 표정, 몸짓, 말투 등 비언어적 표현을 포함해주세요. |
| | * **⭐ 중요: 각 인물의 '속마음' 또는 '숨겨진 의도':** 대화나 행동 이면에 있는 각 인물의 생각, 감정, 진짜 원하는 것, 혹은 우려하는 바를 괄호 안에 명확하게 서술해주세요. (예: OO (속마음: 사실은 불안하지만, 약하게 보이고 싶지 않아.)) |
| | * **MBTI 기반 해석:** 각 인물의 대화, 행동, 속마음이 그들의 MBTI 유형적 특성(예: 외향/내향, 감각/직관, 사고/감정, 판단/인식)과 어떻게 연결되는지 구체적으로 설명해주세요. 특히, **유형 간의 차이**가 상호작용에 어떤 영향을 미치는지 분석해주세요. |
| | |
| | **3. 상호작용 분석 및 잠재적 오해 지점:** |
| | * 시나리오 전반에 걸쳐 나타나는 참여자들 간의 **긍정적 상호작용(시너지)**과 **부정적 상호작용(갈등/오해 유발 지점)**을 명확히 식별하고 분석해주세요. |
| | * MBTI 유형 차이(예: T/F의 의사결정 방식 차이, J/P의 계획성 차이 등)로 인해 발생할 수 있는 **구체적인 오해의 순간들**을 지적하고, 왜 그런 오해가 발생하는지 설명해주세요. |
| | |
| | **4. 관계 개선을 위한 실질적 조언:** |
| | * **각 참여자에게 맞춤화된 조언**을 제공해주세요. 이 조언은 시나리오에서 드러난 **구체적인 상호작용, 속마음, 오해 지점**을 직접적으로 다루어야 합니다. |
| | * 서로를 더 잘 이해하고 **건강한 관계**를 구축하기 위해 각자가 **시도해볼 수 있는 구체적인 말과 행동**을 제안해주세요. (예: "{people[0]['name']}({people[0]['type']})님, {people[1]['name']}({people[1]['type']})님이 아이디어를 낼 때 즉시 분석하기보다, 먼저 '흥미로운 생각인데!'라고 반응하며 {people[1]['name']}님의 열정을 인정해주세요. 그 후에 함께 현실적인 부분을 논의하는 것이 좋습니다.") |
| | * 조언에는 반드시 **모든 참여자의 이름, 성별, MBTI 유형**이 명시되어야 합니다. |
| | |
| | **[출력 형식]** |
| | |
| | * 결과는 Markdown 형식을 사용하여 가독성을 높여주세요. |
| | * 위 요청사항의 번호(1, 2, 3, 4)에 맞춰 명확한 제목 (예: `## 1. 참여자 MBTI 심층 분석`)을 사용하여 내용을 구분해주세요. |
| | * 특히 시나리오(2번 항목)에서는 대화, 행동, 속마음, MBTI 해석을 명확히 구분하여 작성해주세요. |
| | * **표 형식의 출력은 지양하고, headings, lists, paragraphs 위주로 마크다운을 사용해주세요.** |
| | """ |
| |
|
| | full_text = "" |
| | try: |
| | |
| | response = model.generate_content([system_prompt], stream=True) |
| | response_container = st.empty() |
| | for chunk in response: |
| | |
| | if chunk.text: |
| | full_text += chunk.text |
| | |
| | response_container.markdown(full_text, unsafe_allow_html=False) |
| | time.sleep(0.01) |
| | return full_text |
| |
|
| | except Exception as e: |
| | st.error(f"시나리오 생성 중 오류 발생: {e}") |
| | |
| | st.error(f"오류 상세 정보: {type(e).__name__} - {e}") |
| | |
| | if "API key" in str(e): |
| | st.error("API 키가 유효하지 않거나 할당량이 초과되었을 수 있습니다. Streamlit secrets 설정을 확인하세요.") |
| | elif "model" in str(e).lower(): |
| | st.error(f"모델 '{target_model_name}'을 찾을 수 없거나 접근 권한이 없을 수 있습니다. 모델 이름을 확인하거나 다른 모델을 시도해보세요.") |
| | elif "safety" in str(e).lower() or "filtered" in str(e).lower(): |
| | st.error("콘텐츠 안전 설정에 의해 응답이 필터링되었습니다. 입력 내용을 수정하거나 안전 설정을 조정해보세요.") |
| | return "" |
| |
|
| |
|
| | |
| |
|
| | |
| | colored_header( |
| | label="🤝 MBTI 관계 시뮬레이터 v2.1", |
| | description="참여자들의 MBTI, 관계, 상황을 입력하여 심층적인 관계 시나리오와 조언을 받아보세요.", |
| | color_name="blue-70" |
| | ) |
| |
|
| | |
| | with st.sidebar: |
| | st.header("⚙️ 설정") |
| |
|
| | |
| | st.markdown("##### 1. 참여자 정보 입력") |
| | st.markdown("참여자의 이름, 성별, MBTI 유형을 선택하세요.") |
| |
|
| | |
| | num_people = st.number_input("참여자 수", min_value=2, max_value=5, value=2, key="num_people", |
| | help="최소 2명, 최대 5명까지 설정할 수 있습니다.") |
| |
|
| | people = [] |
| | |
| | for i in range(num_people): |
| | |
| | with st.expander(f"👤 참여자 {i+1}", expanded=(i == 0)): |
| | person = {} |
| | |
| | default_names = ['철수', '영희', '민준', '서연', '지우'] |
| | person['name'] = st.text_input(f"이름/닉네임", key=f"name_{i}", placeholder=f"예: {default_names[i % len(default_names)]}") |
| | person['gender'] = st.radio("성별", ["남성", "여성", "기타"], key=f"gender_{i}", horizontal=True) |
| | |
| | default_mbti_index = i % len(mbti_types) |
| | person['type'] = st.selectbox(f"MBTI 유형", list(mbti_types.keys()), format_func=lambda x: f"{x} ({mbti_types[x]})", key=f"type_{i}", index=default_mbti_index) |
| |
|
| | |
| | people.append(person) |
| | |
| |
|
| | st.divider() |
| |
|
| | |
| | st.markdown("##### 2. 관계 및 상황 설정") |
| | st.markdown("관계 유형과 구체적인 상황을 설정하세요.") |
| |
|
| | |
| | if num_people == 2: |
| | relationship_options = relationship_types_two |
| | else: |
| | relationship_options = relationship_types_multiple |
| | relationship = st.selectbox("관계 유형", list(relationship_options.keys()), key="relationship") |
| |
|
| | situation = st.text_area("구체적인 상황", key="situation", height=100, |
| | placeholder="예: 중요한 프로젝트 마감일을 앞두고 의견 충돌 발생") |
| |
|
| | st.divider() |
| |
|
| | |
| | st.markdown("##### 3. 시나리오 생성") |
| | generate_button = st.button("🚀 시나리오 생성하기", use_container_width=True, type="primary") |
| |
|
| |
|
| | |
| | st.header("💡 생성된 시나리오") |
| | |
| | scenario_output_area = st.container() |
| | |
| |
|
| | |
| | if generate_button: |
| | |
| | valid_input = True |
| | |
| | if not all(p.get('name', '').strip() for p in people): |
| | st.error("모든 참여자의 이름을 입력해주세요.") |
| | valid_input = False |
| | |
| | if not situation.strip(): |
| | st.error("구체적인 상황을 입력해주세요.") |
| | valid_input = False |
| | |
| | if not relationship: |
| | st.error("관계 유형을 선택해주세요.") |
| | valid_input = False |
| | |
| | if not people: |
| | st.error("참여자 정보가 없습니다. 페이지를 새로고침하거나 참여자 수를 다시 설정해보세요.") |
| | valid_input = False |
| |
|
| |
|
| | if valid_input: |
| | |
| | scenario_output_area.empty() |
| | with scenario_output_area: |
| | with st.spinner("🧠 MBTI 전문가가 시나리오 분석 및 생성 중... 잠시만 기다려주세요."): |
| | |
| | |
| | full_text_result = generate_relationship_scenario(people, relationship, situation) |
| |
|
| | if full_text_result: |
| | |
| | |
| | |
| | scenario_output_area.markdown(full_text_result, unsafe_allow_html=False) |
| | st.success("🎉 시나리오 생성 완료!") |
| | else: |
| | |
| | st.warning("시나리오 생성에 실패했습니다. 오류 메시지를 확인해주세요.") |
| | else: |
| | st.warning("입력값을 다시 확인해주세요.") |
| |
|
| | |
| | |
| | |
| | if not generate_button: |
| | scenario_output_area.info("왼쪽 사이드바에서 참여자 정보, 관계, 상황을 설정 후 '시나리오 생성하기' 버튼을 클릭하세요.") |