File size: 21,712 Bytes
f5b0d3e 1d5b6a4 86cdc58 fabb74d 0b70568 f4ce89b a4863ca f4ce89b 86cdc58 f4ce89b 86cdc58 f4ce89b 22032f3 86cdc58 fabb74d 4cac0d6 86cdc58 fabb74d f4ce89b 86cdc58 f4ce89b 86cdc58 f4ce89b 86cdc58 f4ce89b 86cdc58 f4ce89b fabb74d 5e6a8f1 0a67eb6 b428688 0a67eb6 b428688 5e6a8f1 0a67eb6 b428688 0a67eb6 f4ce89b 0a67eb6 5e6a8f1 f4ce89b 5e6a8f1 f4ce89b 5e6a8f1 f4ce89b 5e6a8f1 0a67eb6 f4ce89b 5e6a8f1 f4ce89b 5e6a8f1 f4ce89b 5e6a8f1 86cdc58 0a67eb6 86cdc58 5e6a8f1 b428688 5e6a8f1 f4ce89b 5e6a8f1 86cdc58 f4ce89b 86cdc58 f4ce89b b428688 fabb74d feb03cf 1d5b6a4 f4ce89b 1d5b6a4 f4ce89b 1d5b6a4 86cdc58 f4ce89b 86cdc58 f4ce89b 1d5b6a4 f4ce89b 86cdc58 f4ce89b 86cdc58 1d5b6a4 f4ce89b 86cdc58 f4ce89b b428688 f4ce89b b428688 f4ce89b 86cdc58 f4ce89b 86cdc58 b428688 86cdc58 b428688 86cdc58 b428688 f4ce89b b428688 f4ce89b 86cdc58 b428688 5e6a8f1 f4ce89b 7a425d6 b428688 f4ce89b b428688 f4ce89b 86cdc58 b428688 86cdc58 f4ce89b b428688 0a67eb6 86cdc58 f4ce89b 86cdc58 f4ce89b 86cdc58 f4ce89b 86cdc58 f4ce89b 86cdc58 f4ce89b 86cdc58 f4ce89b b428688 f4ce89b 86cdc58 f4ce89b b428688 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | import time
import streamlit as st
import google.generativeai as genai
from streamlit_extras.colored_header import colored_header
import markdown # markdown 라이브러리 임포트 확인
# --- Must be the first Streamlit command ---
st.set_page_config(page_title="MBTI 관계 시뮬레이터", page_icon="🤝", layout="wide")
# --- Configuration ---
# Google Gemini API Key 설정 (Streamlit secrets 사용)
try:
# secrets에서 키 로드 시도
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,
}
# 특정 모델 이름으로 설정 (사용자 요청 반영)
# 참고: 'gemini-2.0-flash-thinking-exp-01-21'는 실험적 모델일 수 있습니다.
# 안정적인 최신 모델을 원하시면 'gemini-1.5-flash-latest' 또는 'gemini-1.5-pro-latest' 사용을 고려하세요.
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 설정 추가 (콘텐츠 필터링 관련)
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"},
]
)
# 모델 로딩 성공 시 간단한 정보 표시 (선택 사항)
# st.sidebar.caption(f"Using model: {target_model_name}")
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 유형 정보 (상세 설명 고도화)
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": "주위에 있으면 인생이 지루할 새가 없을 정도로 즉흥적이며 열정과 에너지가 넘치는 연예인형입니다. 사교적이고 낙천적이며, 사람들과 어울리는 것을 즐깁니다. 때로는 깊이 있는 관계 형성에 어려움을 느낄 수 있습니다."
}
# 관계 유형 (2인 관계와 다인 관계 분리)
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"
}
# --- Functions ---
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}"
# 참여자 정보 목록 생성 (people 리스트가 비어있지 않을 때만 생성)
if people:
people_info = "\n".join([f"- {create_type_info(person)}" for person in people])
else:
# people 리스트가 비어있는 예외적인 경우 처리
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:
# chunk.text가 None이 아닌지 확인
if chunk.text:
full_text += chunk.text # Append chunk text to full_text
# Markdown으로 실시간 업데이트 (unsafe_allow_html=False가 더 안전)
response_container.markdown(full_text, unsafe_allow_html=False) # HTML 대신 마크다운 직접 렌더링
time.sleep(0.01) # 딜레이 약간 줄임
return full_text # 최종 전체 텍스트 반환
except Exception as e:
st.error(f"시나리오 생성 중 오류 발생: {e}")
# 오류 유형과 메시지를 함께 출력하여 디버깅에 도움
st.error(f"오류 상세 정보: {type(e).__name__} - {e}")
# API 호출 관련 오류일 수 있으므로 추가 정보 제공
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 ""
# --- Streamlit UI ---
# 헤더 색상 적용
colored_header(
label="🤝 MBTI 관계 시뮬레이터 v2.1", # 버전 업데이트
description="참여자들의 MBTI, 관계, 상황을 입력하여 심층적인 관계 시나리오와 조언을 받아보세요.",
color_name="blue-70"
)
# UI/UX 개선
with st.sidebar:
st.header("⚙️ 설정")
# Instructions at the top of sidebar
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)
# 기본 MBTI 선택 인덱스 계산 (오류 방지)
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()
# Instructions for relationship and situation
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()
# Generate button at the bottom, with clear instruction
st.markdown("##### 3. 시나리오 생성")
generate_button = st.button("🚀 시나리오 생성하기", use_container_width=True, type="primary")
# Main area - Scenario output
st.header("💡 생성된 시나리오")
# Use st.container() for better layout control if needed, otherwise direct markdown is fine
scenario_output_area = st.container()
# scenario_output_area = st.empty() # Use empty() if you want to replace content completely on each run
# Button click logic
if generate_button:
# Input validation 강화
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
# 관계 유형 선택 확인 (selectbox는 기본값이 있으므로 일반적으로 문제는 없으나 명시적으로 확인)
if not relationship:
st.error("관계 유형을 선택해주세요.")
valid_input = False
# people 리스트 자체 확인 (만약을 대비)
if not people:
st.error("참여자 정보가 없습니다. 페이지를 새로고침하거나 참여자 수를 다시 설정해보세요.")
valid_input = False
if valid_input:
# 시나리오 생성 영역 초기화 (이전 결과 제거)
scenario_output_area.empty()
with scenario_output_area: # 컨테이너 내에서 스피너와 결과 표시
with st.spinner("🧠 MBTI 전문가가 시나리오 분석 및 생성 중... 잠시만 기다려주세요."):
# people 리스트가 제대로 채워졌는지 확인 (디버깅용)
# st.write("전달되는 참여자 정보:", people)
full_text_result = generate_relationship_scenario(people, relationship, situation)
if full_text_result:
# 최종 결과를 Markdown으로 렌더링 (unsafe_allow_html=True 사용 시 주의)
# html_output = markdown.markdown(full_text_result)
# scenario_output_area.markdown(html_output, unsafe_allow_html=True)
scenario_output_area.markdown(full_text_result, unsafe_allow_html=False) # 마크다운 직접 렌더링 권장
st.success("🎉 시나리오 생성 완료!")
else:
# generate_relationship_scenario 함수 내에서 이미 오류 메시지가 표시됨
st.warning("시나리오 생성에 실패했습니다. 오류 메시지를 확인해주세요.")
else:
st.warning("입력값을 다시 확인해주세요.")
# Initial message when the app loads or before generation
# Check if the button has been clicked at least once using session state if needed
# For simplicity, just show if the button wasn't clicked in this run
if not generate_button:
scenario_output_area.info("왼쪽 사이드바에서 참여자 정보, 관계, 상황을 설정 후 '시나리오 생성하기' 버튼을 클릭하세요.") |