o2o-plagiarism-ai/tests/test_summary_consensus.py
hbyang 8ae52b0a82 feat: add summary (No.7) consensus and grounded generation trials
요약 성능지표(No.7, ROUGE 65점) 실험 일습을 커밋한다.

- app/engine/summary_consensus.py: 후보 5개 생성 후 합의 선택(select_consensus).
  문자/어절 2-gram 가중 합의로 고르며, 유효 후보가 없으면 valid=False 를 낸다.
- app/engine/summary_grounded.py: 근거 추출 후 압축하는 2단계 생성.
- summarizer.py: 추출 전략 3종(textrank/coverage/lead). coverage 는 MMR 로
  중복 문장을 눌러 문서 전체를 넓게 담는다. 기본값은 textrank 로 유지한다.

스크립트는 생성·평가·검증을 분리했다. verify_* 는 API 호출 없이 저장된 산출물만
재계산하는 독립 검증기라 지표를 공용 함수로 합치지 않는다. 합치면 검증이 성립하지
않는다. tune_summarizer 는 사람 검수 참조(build_summary_annotation_packet ->
export_summary_annotations)를 입력으로 받아 선택과 최종 보고를 분리한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 10:02:04 +09:00

41 lines
1.5 KiB
Python

from app.engine.summary_consensus import character_f1, select_consensus
def test_medoid_prefers_agreed_event_over_outlier():
candidates = ["김씨는 학교를 세웠다.", "김씨는 학교를 세웠다.", "김씨는 미국으로 이민했다."]
assert select_consensus(candidates) == 0
def test_overlength_consensus_cannot_win():
long = "긴" * 51
assert select_consensus([long, long, "학교를 세웠다."]) == 2
assert select_consensus([long, ""]) == -1
def test_empty_outputs_do_not_change_candidate_choice():
assert select_consensus(["", "학교를 세웠다.", "학교를 세웠다."]) == 1
assert character_f1("", "") == 0
def test_bigram_overlap_keeps_multiplicity():
assert character_f1("가가가", "가가") == 2 / 3
def test_word_overlap_and_invalid_weight():
import pytest
from app.engine.summary_consensus import word_f1
assert word_f1("a b c", "a b") == 2 / 3
assert word_f1("a", "a") == 0
with pytest.raises(ValueError):
select_consensus(["요약"], word_weight=1.5)
def test_public_summary_reports_invalid_when_every_candidate_fails(monkeypatch):
from app.engine import summary_consensus
monkeypatch.setattr(summary_consensus, "generate_candidates", lambda client, text: [
{"summary": "", "finish_reason": "length"}, {"summary": "긴" * 51, "finish_reason": "stop"}])
result = summary_consensus.summarize_short(None, "원문")
assert result["valid"] is False
assert result["summary"] == ""
assert result["selected_index"] == -1