ll7098ll commited on
Commit
a35c585
·
verified ·
1 Parent(s): 7ef6ba4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +58 -45
app.py CHANGED
@@ -631,6 +631,7 @@ def generate_article_interface(game_state):
631
  }
632
  return None
633
 
 
634
  # --- 기사 평가 및 데스크 피드백 (OpenAI 사용) ---
635
  def evaluate_article_and_get_feedback_openai(article, game_state, assignment_data):
636
  status = game_state['status']
@@ -641,10 +642,10 @@ def evaluate_article_and_get_feedback_openai(article, game_state, assignment_dat
641
  article_tone_text = get_text_func(f"article_tone_{article['tone']}")
642
 
643
  prompt = f"""
644
- 당신은 어린이 신문사의 매우 친절하고 격려를 잘 해주는 편집장입니다. 우리 신문사의 새내기 기자가 아래와 같은 취재 지시를 받고, 수집한 노트 내용을 바탕으로 기사를 작성했습니다.
645
- 이 기자는 초등학생 독자들도 쉽게 이해할 수 있도록 쉽고 명확한 단어와 문장을 사용하려고 애쓰고 있습니다. 점을 기억하고, 기자의 노력을 칭찬하면서 구체적인 조언을 해주세요.
646
 
647
- [오늘의 할 일]
648
  {current_assignment_text}
649
 
650
  [기자가 적은 내용 (취재 노트)]
@@ -656,69 +657,83 @@ def evaluate_article_and_get_feedback_openai(article, game_state, assignment_dat
656
  - 선택한 분위기: {article_tone_text}
657
 
658
  [평가 항목 및 피드백 가이드]
659
- 1. **내용의 정확성과 중요도 (40점 만점):**
660
- * 기자가 취재한 내용(노트)과 오늘 일의 핵심 내용을 기사에 담았나요?
661
- * 빠뜨린 중요한 사실은 없나요? 혹시 잘못 이해한 부분은 없을까요?
662
- * **피드백 작성 시:** 어떤 점이 좋았는지, 어떤 사실을 추가하면 기사가 더 풍부해질지 구체적으로 알려주세요. "기자님이 OOO 내용을 잘 찾아내서 기사에 담아주니 독자들이 사건을 더 잘 이해할 수 있을 거예요!" 와 같이 칭찬으로 시작하고, "혹시 XXX 내용을 조금 더 자세히 설명해주거나, YYY 관점도 살짝 넣어보면 어떨까요?" 와 같이 부드럽게 제안해주세요.
663
-
664
- 2. **글의 흐름과 이해도 (30점 만점):**
665
- * 기사 내용이 자연스럽게 이어지나요? 초등학생 독자들이 읽고 바로 이해할 있을 만큼 쉽고 명확하게 쓰였나요?
666
- * 어려운 단어나 표현은 없었나요? 있다면 어떻게 바꾸면 좋을지 예시와 함께 알려주세요.
667
- * **피드백 작성 시:** "문장들이 술술 읽혀서 정말 좋았어요. 특히 'OOO'라는 표현은 어린이 독자들도 재미있게 읽을 수 있겠어요!" 처럼 긍정적인 부분을 먼저 언급하고, "혹시 'XXX'라는 단어는 'YYY'나 'ZZZ'처럼 더 쉬운 말로 바꿔보면 어떨까요? 그러면 더 많은 친구들이 기사를 쉽게 이해할 수 있을 거예요." 와 같이 구체적인 대안을 제시하며 조언해주세요.
668
-
669
- 3. **기사 분위기의 적절성과 영향력 (30점 만점):**
670
- * 선택한 기사 분위기가 지금 상황과 기사 내용에 어울리나요?
671
- * 기사가 독자들에게 어떤 생각이나 느낌을 줄 수 있을까요? (기자님의 안전도 고려해서 조언해주세요. 너무 강한 비판은 위험할 수 있다는 점도 부드럽게 언급 가능합니다.)
672
- * **피드백 작성 시:** "기자님이 선택한 '{article_tone_text}' 분위기가 이번 사건의 OOO 면을 잘 보여주는 것 같아요." 라고 공감해주고, "이런 분위기의 기사는 독자들이 XXX 감정을 느끼게 해서 사건에 관심을 갖게 만들 있어요. 다만, 너무 강한 표현은 기자님이 위험해질 수도 있으니, YYY처럼 조금 더 조심스럽게 표현하는 것도 좋은 방법이랍니다." 와 같이 안전을 고려한 조언을 덧붙여주세요.
673
-
674
- [출력 형식]
675
- 평가 항목에 따라 항목별 점수와 함께, **칭찬과 구체적인 조언이 담긴 따뜻한 피드백**을 작성해주세요.
676
- 그리고 총점(100점 만점)과 함께, 기자에게 용기를 주고 앞으로 더 성장할 수 있도록 돕는 **종합적인 격려의 메시지 (2-3문장)**를 "overall_feedback"으로 제공해주세요. "기자님, 이번 기사 정말 잘 썼어요! 특히 OOO 부분이 인상 깊었고, 앞으로 XXX 부분만 조금 신경 써주면 더욱 훌륭한 기자가 될 있을 거예요. 항상 응원할게요!" 와 같은 느낌으로요.
 
 
 
 
 
 
677
 
678
  반드시 아래 JSON 형식으로 응답해주세요:
679
  {{
680
  "score_fact": 점수(숫자),
681
- "comment_fact": "사실 중요도에 대한 칭찬과 구체적 조언 (문자열)",
682
  "score_logic": 점수(숫자),
683
- "comment_logic": "글의 흐름과 이해도에 대한 칭찬과 구체적 조언 (문자열)",
684
  "score_tone": 점수(숫자),
685
- "comment_tone": "기사 분위기와 영향력에 대한 칭찬과 구체적 조언 (문자열, 안전 고려 포함)",
686
  "total_score": 총점(숫자),
687
- "overall_feedback": "종합적인 격려와 응원의 메시지 (문자열)"
688
  }}
689
  """
690
  try:
691
- with st.spinner("AI 편집장님이 기사를 읽고 있어요..."):
692
  response = client.chat.completions.create(
693
- model="gpt-4o-mini", # 또는 gpt-4o
694
  messages=[{"role": "user", "content": prompt}],
695
  response_format={"type": "json_object"},
696
- temperature=0.4, # 약간 높여서 다채로운 격려 유도
697
- max_tokens=800 # 피드백이 길어질 수 있으므로 넉넉하게
698
  )
699
  ai_evaluation_str = response.choices[0].message.content
700
  ai_evaluation = json.loads(ai_evaluation_str)
701
 
702
  total_score = ai_evaluation.get("total_score", 0)
703
- overall_feedback = ai_evaluation.get("overall_feedback", "AI 편집장님 연결에 문제가 생겨서 점수를 매겼어요. 하지만 기자님의 노력은 분명 빛날 거예요! 다음 기사도 기대할게요!")
704
 
705
  except Exception as e:
706
  st.error(get_text_func("error_openai_api").format(error=str(e)))
707
- total_score = random.randint(40, 70)
708
- overall_feedback = "AI 편집장님 시스템에 문제가 생겼지만, 기자님의 열정은 느껴져요! 이번 경험을 바탕으로 다음엔 멋진 기사를 수 있을 거예요. 화이팅!"
709
 
710
- article['ai_score'] = total_score # 기사 딕셔너리에 AI 점수 저장
711
 
712
- trust_change = (total_score - 50) // 5
713
  status['public_trust'] = max(0, min(100, status['public_trust'] + trust_change))
714
 
715
- if article['tone'] == "critical" and total_score > 60:
716
- freedom_change = -random.randint(5, 10)
717
- safety_change = -random.randint(3, 8)
718
- status['press_freedom'] = max(0, min(100, status['press_freedom'] + freedom_change))
719
- status['reporter_safety'] = max(0, min(100, status['reporter_safety'] + safety_change))
720
- if freedom_change != 0: game_state['event_log'].append(get_text_func("log_freedom_change_article").format(change=freedom_change))
721
- if safety_change != 0: game_state['event_log'].append(get_text_func("log_safety_change_article").format(change=safety_change))
 
 
 
 
 
 
 
 
722
 
723
  status['article_score_total'] += total_score
724
  status['article_count'] += 1
@@ -728,12 +743,10 @@ def evaluate_article_and_get_feedback_openai(article, game_state, assignment_dat
728
  game_state['event_log'].append(get_text_func("log_desk_feedback").format(feedback=overall_feedback))
729
  if trust_change != 0: game_state['event_log'].append(get_text_func("log_trust_change").format(change=trust_change))
730
 
731
- game_state['submitted_articles'].append(article) # 점수가 포함된 article 저장
732
  game_state['reporter_notebook'] = []
733
 
734
- # UI에 전체 피드백 객체를 전달할 수도 있지만, 현재는 overall_feedback만 사용
735
- # return ai_evaluation
736
- return overall_feedback
737
 
738
  # --- UI 표시 함수들 ---
739
  def display_reporter_dashboard(game_state):
 
631
  }
632
  return None
633
 
634
+ # --- 기사 평가 및 데스크 피드백 (OpenAI 사용) ---
635
  # --- 기사 평가 및 데스크 피드백 (OpenAI 사용) ---
636
  def evaluate_article_and_get_feedback_openai(article, game_state, assignment_data):
637
  status = game_state['status']
 
642
  article_tone_text = get_text_func(f"article_tone_{article['tone']}")
643
 
644
  prompt = f"""
645
+ 당신은 어린이 신문사의 엄격하지만 공정한 편집장입니다. 당신의 목표는 새내기 기자가 최고의 기사를 있도록 객관적으로 평가하고, 명확한 개선 방향을 제시하는 것입니다.
646
+ 이 기자는 초등학생 독자들도 이해할 수 있는 글을 쓰려고 노력하고 있지만, 아직 미숙한 점이 많을 수 있습니다. 칭찬은 정말 뛰어난 부분에 한정하고, 대부분의 피드백은 구체적인 문제점 지적과 개선 방안 제시에 집중해주세요.
647
 
648
+ [오늘의 할 일 (취재 지시)]
649
  {current_assignment_text}
650
 
651
  [기자가 적은 내용 (취재 노트)]
 
657
  - 선택한 분위기: {article_tone_text}
658
 
659
  [평가 항목 및 피드백 가이드]
660
+ 항목별로 점수를 부여하고, **매우 구체적이고 비��적인 관점에서 개선점을 명확히 지적**해주세요.
661
+ 피드백은 기자가 무엇을 잘못했고, 어떻게 고쳐야 하는지 명확히 있도록 작성합니다.
662
+
663
+ 1. **내용의 정확성, 중요도 깊이 (40점 만점):**
664
+ * 취재 노트의 핵심 정보와 취재 지시의 요구사항이 기사에 정확하고 충분히 반영되었는가?
665
+ * 단순 사실 나열을 넘어, 사건의 의미나 배경에 대한 고민이 담겨 있는가? (초등학생 눈높이에서)
666
+ * 불필요하거나 중요도가 낮은 내용이 포함되지는 않았는가? 혹은 중요한 내용이 누락되지는 않았는가?
667
+ * **피드백 예시:** "취재 노트에 있는 OOO 사실은 중요하지만 기사에 충분히 설명되지 않았습니다. 이 부분을 XXX 방식으로 보강해야 합니다." 또는 "YYY 내용은 현재 취재 지시와 관련성이 낮아 보입니다. 대신 ZZZ 관점을 추가하는 것을 고려해보세요."
668
+
669
+ 2. **논리적 흐름, 명확성 및 어휘 수준 (30점 만점):**
670
+ * 기사의 논리가 명확하고, 문장 연결이 자연스러운가?
671
+ * 초등학생 독자가 이해하기 어려운 단어나 복잡한 문장 구조는 없는가? 있다면 구체적으로 지적하고, 쉬운 대안을 제시해야 합니다.
672
+ * 주장이나 설명에 대한 근거가 명확한가?
673
+ * **피드백 예시:** "기사 전반적으로 논리적 비약이 보입니다. 특히 OOO 부분에서 XXX 넘어가는 연결이 부자연스럽습니다. 부분을 YYY와 같이 수정하여 독자의 이해를 도와야 합니다." 또는 "'ABC'라는 단어는 초등학생에게 어렵습니다. '가나다' 또는 '라마바'와 같이 쉬운 표현으로 변경하세요."
674
+
675
+ 3. **기사 분위기의 적절성, 객관성 및 잠재적 영향 (30점 만점):**
676
+ * 선택한 기사 분위기가 사건의 본질과 기사 내용에 부합하는가? 감정에 치우치지 않고 객관성을 유지했는가?
677
+ * 기사가 독자에게 미칠 영향을 고려했는가? (특히 비판적 기사의 경우, 과도한 표현으로 인한 기자 안전 문제나 여론 왜곡 가능성을 지적할있어야 합니다.)
678
+ * **피드백 예시:** "선택한 '{article_tone_text}' 분위기는 사건의 심각성을 전달하려는 의도는 알겠으나, 일부 표현(예: 'XXX')이 과도하여 객관성을 해칠 수 있습니다. YYY와 같이 좀 더 절제된 표현을 사용하는 것이 좋겠습니다." 또는 "이 기사는 독자들에게 ZZZ와 같은 영향을 줄 수 있습니다. 이 점을 인지하고, 균형 잡힌 시각을 제공하기 위해 노력해야 합니다."
679
+
680
+ [총평 및 최종 조언]
681
+ 각 항목별 점수와 피드백을 바탕으로 총점(100점 만점)을 계산하고, "overall_feedback"에는 기사의 전반적인 문제점과 가장 시급히 개선해야 할 부분을 명확히 요약하여 전달합니다.
682
+ **칭찬은 정말 객관적으로 뛰어난 성과가 있을 때만 간략하게 언급하고, 대부분은 기자가 성장하기 위해 필요한 냉철한 조언을 담아야 합니다.**
683
+ 예시: "이번 기사는 OOO점에서 가능성을 보였으나, 전반적으로 XXX와 YYY 문제가 두드러집니다. 특히 ZZZ 부분을 개선하는 데 집중해야 다음 기사에서 발전된 모습을 보일 수 있을 것입니다."
684
 
685
  반드시 아래 JSON 형식으로 응답해주세요:
686
  {{
687
  "score_fact": 점수(숫자),
688
+ "comment_fact": "내용의 정확성, 중요도, 깊이에 대한 구체적이고 비판적인 피드백 (문자열)",
689
  "score_logic": 점수(숫자),
690
+ "comment_logic": "논리적 흐름, 명확성, 어휘 수준에 대한 구체적이고 비판적인 피드백 (문자열)",
691
  "score_tone": 점수(숫자),
692
+ "comment_tone": "기사 분위기의 적절성, 객관성, 영향에 대한 구체적이고 비판적인 피드백 (문자열)",
693
  "total_score": 총점(숫자),
694
+ "overall_feedback": "기사의 전반적인 문제점과 개선 방향에 대한 냉철한 조언 (문자열, 칭찬은 최소화)"
695
  }}
696
  """
697
  try:
698
+ with st.spinner("AI 편집장님이 기사를 꼼꼼히 검토하고 있습니다..."): # 스피너 메시지 변경
699
  response = client.chat.completions.create(
700
+ model="gpt-4.1-mini", # 정교한 평가를 위해 gpt-4o 권장
701
  messages=[{"role": "user", "content": prompt}],
702
  response_format={"type": "json_object"},
703
+ temperature=0.1, # 매우 객관적이고 일관된 평가를 위해 낮춤
704
+ max_tokens=900 # 피드백이 상세해질 수 있으므로 넉넉하게
705
  )
706
  ai_evaluation_str = response.choices[0].message.content
707
  ai_evaluation = json.loads(ai_evaluation_str)
708
 
709
  total_score = ai_evaluation.get("total_score", 0)
710
+ overall_feedback = ai_evaluation.get("overall_feedback", "AI 편집장 연결 오류로 자동 평가되었습니다. 기사 내용의 객관성과 논리성을 다시 한번 점검해보시기 바랍니다.")
711
 
712
  except Exception as e:
713
  st.error(get_text_func("error_openai_api").format(error=str(e)))
714
+ total_score = random.randint(30, 60) # 점수 범위를 낮춤
715
+ overall_feedback = "AI 편집장 시스템 오류로 자동 평가되었습니다. 기사의 핵심 내용 전달과 논리적 흐름을 중점적으로 검토하고 개선할 부분을 찾아보세요."
716
 
717
+ article['ai_score'] = total_score
718
 
719
+ trust_change = (total_score - 60) // 6 # 신뢰도 변화 기준을 더 엄격하게
720
  status['public_trust'] = max(0, min(100, status['public_trust'] + trust_change))
721
 
722
+ # 비판적 기사에 대한 패널티는 유지하되, 점수가 낮아도 발생할 수 있도록 조정
723
+ if article['tone'] == "critical":
724
+ freedom_penalty_chance = 0.2 + ( (50 - status['press_freedom']) / 200 ) # 언론자유 낮을수록 확률 증가
725
+ safety_penalty_chance = 0.2 + ( (40 - status['reporter_safety']) / 200 ) # 기자안전 낮을수록 확률 증가
726
+
727
+ if random.random() < freedom_penalty_chance:
728
+ freedom_change = -random.randint(3, 8)
729
+ status['press_freedom'] = max(0, min(100, status['press_freedom'] + freedom_change))
730
+ if freedom_change != 0: game_state['event_log'].append(get_text_func("log_freedom_change_article").format(change=freedom_change))
731
+
732
+ if random.random() < safety_penalty_chance:
733
+ safety_change = -random.randint(5, 12)
734
+ status['reporter_safety'] = max(0, min(100, status['reporter_safety'] + safety_change))
735
+ if safety_change != 0: game_state['event_log'].append(get_text_func("log_safety_change_article").format(change=safety_change))
736
+
737
 
738
  status['article_score_total'] += total_score
739
  status['article_count'] += 1
 
743
  game_state['event_log'].append(get_text_func("log_desk_feedback").format(feedback=overall_feedback))
744
  if trust_change != 0: game_state['event_log'].append(get_text_func("log_trust_change").format(change=trust_change))
745
 
746
+ game_state['submitted_articles'].append(article)
747
  game_state['reporter_notebook'] = []
748
 
749
+ return overall_feedback
 
 
750
 
751
  # --- UI 표시 함수들 ---
752
  def display_reporter_dashboard(game_state):