feat: expand infringement case matching to v1.3 precedent mapping

태그 동점 시 첫 케이스만 반환해 나머지를 버리던 문제를 고친다.
A1·A2·A3·A4·A5·A6·A15 는 주/보조 태그가 동일하고 실제 구별자는 원본의
종류라 태그로 좁혀지지 않는다. find_case -> find_cases 로 바꿔 동점군을
전부 내보내고, 확정은 사람이 원본 종류로 한다.

- cases_v1.3.json 신설(v1.2 삭제): 39건 전부에 대표판례·처리구분(●/○) 적재.
  판례 미지정 10건은 사유를 값으로 남긴다.
- detectable_internal 10 -> 19건 (v1.3 IX장 총괄표 ● 기준으로 정정)
- _assign_tags 주 태그 조합 3 -> 5종. 유사도로 판단 가능한 쟁점만 주 태그로
  낸다. 도달 케이스 3 -> 16건(● 19건 중).
- B3·C1·D1 은 게재 사실·편집 개입·성명표시가 필요해 텍스트로 알 수 없다.
  추측하지 않고 LegalContext 대기로 두며 경계를 테스트로 고정한다.
- 케이스 수 검증을 38~39 범위에서 39 로 고정. v1.3 본문 통계 줄(●16/○22=38)이
  같은 문서의 표(●19/○20=39)와 어긋난 것이 38/39 혼재의 원인이었다.
- docs/PRECEDENT_SOURCE_GAP.md: 대표판례이나 적재본에 없어 출처를 제시할 수
  없는 14건. 응답에서도 precedents_without_source 로 구분한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hbyang 2026-09-18 15:42:34 +09:00
parent aaabbdd8c6
commit 52a0fdcdf0
10 changed files with 1120 additions and 133 deletions

View File

@ -80,6 +80,12 @@ async def taxonomy(request: Request) -> TaxonomyResponse:
],
cases=[
{"case_id": c.case_id, "old_no": c.old_no, "subgroup": c.subgroup,
"handling": c.handling,
"representative_precedents": [
{"case_id": p.case_id, "in_runtime_corpus": p.in_runtime_corpus}
for p in c.representative_precedents
],
"representative_precedent_note": c.representative_precedent_note,
"title": c.title, "actor": c.actor,
"primary_tags": list(c.primary_tags), "secondary_tags": list(c.secondary_tags),
"detectable_internal": c.detectable_internal, "high_risk": c.high_risk,

View File

@ -122,6 +122,21 @@ class PartialPlagiarismSignal(BaseModel):
changed_elements: list[str] = Field(default_factory=list) # 바꿔치기한 요소
class CaseCandidate(BaseModel):
"""판정된 침해 케이스 후보 1건 + v1.3 총괄표의 대표판례."""
case_id: str
title: str
handling: Literal["technical_detection", "terms_or_report"]
representative_precedents: list[str] = Field(default_factory=list)
precedents_without_source: list[str] = Field(
default_factory=list,
description="대표판례이나 운영 적재본에 없어 출처를 제시할 수 없는 사건번호.",
)
precedent_note: str | None = Field(
default=None, description="대표판례 미지정 사유 (예: '확립 판례 없음').",
)
class MatchResult(BaseModel):
source_doc: str
source_title: str | None = None
@ -129,6 +144,11 @@ class MatchResult(BaseModel):
tags: list[InfringementTag] = Field(default_factory=list)
case_id: str | None = None
case_title: str | None = None
case_candidates: list[CaseCandidate] = Field(
default_factory=list,
description="태그 동점 케이스 전부. 원본 종류(가사·기사·위키·교재 등)로 "
"사람이 확정한다. case_id 는 이 목록의 첫 항목이다.",
)
infringement_type: InfringementType = "unknown"
evidence_spans: list[EvidenceSpan] = Field(default_factory=list)
score_breakdown: ScoreBreakdown | None = None

View File

@ -17,6 +17,7 @@ import logging
from datetime import datetime, timezone
from app.api.schemas import (
CaseCandidate,
TAG_LABEL_KO,
AiGenerationSignal,
DetectOptions,
@ -604,15 +605,30 @@ class PlagiarismDetector:
) -> MatchResult:
legacy_type = _classify_legacy(hit)
tags = self._assign_tags(hit, legacy_type)
case = self.taxonomy.find_case([t.tag for t in tags if t.role == "primary"]) if self.taxonomy else None
cases = (self.taxonomy.find_cases([t.tag for t in tags if t.role == "primary"])
if self.taxonomy else ())
candidates = [
CaseCandidate(
case_id=c.case_id,
title=c.title,
handling=c.handling,
representative_precedents=[p.case_id for p in c.representative_precedents],
precedents_without_source=[
p.case_id for p in c.representative_precedents if not p.in_runtime_corpus
],
precedent_note=c.representative_precedent_note,
)
for c in cases
]
return MatchResult(
source_doc=hit.doc_id,
source_title=hit.title,
similarity=round(hit.score, 4),
tags=tags,
case_id=case.case_id if case else None,
case_title=case.title if case else None,
case_id=cases[0].case_id if cases else None,
case_title=cases[0].title if cases else None,
case_candidates=candidates,
infringement_type=legacy_type,
evidence_spans=hit.evidence if return_evidence else [],
score_breakdown=ScoreBreakdown(
@ -633,6 +649,12 @@ class PlagiarismDetector:
- lemma만 매우 높음 → 인용 표시 누락(주 보조)
- 인물 일치도 매우 높음(서사·구조 차용) → 2차적저작물작성권(주) + 자기창작인양표시(보조)
- 구조 미달 가공 신호 (text 낮음 + lemma 중간) → 2차적저작물 미달 가공
주 태그는 케이스 후보를 좁히는 유일한 입력이므로, 유사도 분포로 판단할 수
있는 쟁점은 보조가 아니라 주 태그로 낸다. 반대로 게재 사실·편집 개입·성명
표시처럼 텍스트에서 알 수 없는 사실은 태그로 만들지 않는다. 그래서 B3·C1·
D1 은 이 함수만으로 후보에 오르지 않으며, LegalContext 로 사람이 확인한
사실이 들어와야 한다.
"""
text_sim = hit.text_sim
lemma_sim = hit.lemma_sim
@ -649,14 +671,25 @@ class PlagiarismDetector:
# 표절 실무 - 인용 누락
primary.append("citation_missing")
# 표면 복제 + 구조 차용 동시 성립 (재수록·편집 수록형) → A10
elif lemma_sim >= 0.50 and char_sim >= 0.40:
primary.append("reproduction")
primary.append("derivative_work")
secondary.append("publication")
secondary.append("attribution")
# 2차적저작물작성권: 구조·서사 차용 (인물/모티프 일치 + 표면은 낮음)
elif (char_sim >= 0.40 or motif_sim >= 0.50) and text_sim < 0.40:
primary.append("derivative_work")
secondary.append("attribution")
secondary.append("citation_missing")
# 미달 가공 가능성
# 구조를 차용하고 출처 표시가 없으면 인용 누락이 주 쟁점이다 → A16
if lemma_sim >= 0.40:
primary.append("citation_missing")
else:
secondary.append("citation_missing")
# 미달 가공: 원문 표현을 거의 남기지 않은 구조 차용 → A7/B2
if text_sim < 0.30 and lemma_sim < 0.50:
secondary.append("substandard_derivative")
primary.append("substandard_derivative")
# 부분 변형 (text 중간 + 인물 일치)
elif text_sim >= 0.40 and char_sim >= 0.30:
@ -690,7 +723,14 @@ class PlagiarismDetector:
)
primary_labels = [t.label_ko for t in top.tags if t.role == "primary"]
tag_summary = ", ".join(primary_labels) if primary_labels else "확인 필요"
case_part = f" 추정 케이스 {top.case_id} ({top.case_title})." if top.case_id else ""
if len(top.case_candidates) > 1:
ids = ", ".join(c.case_id for c in top.case_candidates)
case_part = (f" 추정 케이스 후보 {ids} — 태그만으로는 구별되지 않으므로 "
f"베껴온 원본의 종류를 확인해 확정해야 합니다.")
elif top.case_id:
case_part = f" 추정 케이스 {top.case_id} ({top.case_title})."
else:
case_part = ""
return (
f"'{top.source_title}'와 검색 유사도 {top.similarity:.2%}로 후보 매칭. "
f"검토 가설 태그: {tag_summary}.{case_part}{breakdown} "

View File

@ -1,6 +1,10 @@
"""10종 메타 태그 분류체계 + 38개 케이스 로더.
"""10종 메타 태그 분류체계 + 39개 침해 케이스 로더.
PDF IV/IX장 그대로 JSON으로 보관 (data/taxonomy/). 부팅 시 1회 로드.
IV장(메타태그)과 IX장 「침해 케이스 ↔ 메타태그 매핑 총괄표」를 그대로 JSON으로
보관 (data/taxonomy/). 부팅 시 1회 로드.
케이스 데이터는 실무방안 v1.3 기준이며, 케이스별 **대표판례**와 처리 구분
(● 기술검출 근거 / ○ 약관·신고)을 함께 담는다.
"""
from __future__ import annotations
@ -26,6 +30,12 @@ class MetaTagDef:
description: str
@dataclass(frozen=True)
class RepresentativePrecedent:
case_id: str # 법원 사건번호 (케이스 ID 아님)
in_runtime_corpus: bool # precedents.jsonl 적재 여부. False 면 인용 시 출처 미확보.
@dataclass(frozen=True)
class CaseDef:
case_id: str # A1, A7, B1, ...
@ -38,6 +48,11 @@ class CaseDef:
detectable_internal: bool = False
high_risk: bool = False
note: str | None = None
#: v1.3 총괄표의 처리 구분. technical_detection(●) / terms_or_report(○)
handling: str = "terms_or_report"
representative_precedents: tuple[RepresentativePrecedent, ...] = field(default_factory=tuple)
#: 대표판례가 지정되지 않은 이유("확립 판례 없음" 등). 빈 목록과 구분한다.
representative_precedent_note: str | None = None
@dataclass(frozen=True)
@ -53,35 +68,42 @@ class Taxonomy:
return t.label_ko
return tag_id
def find_case(self, primary_tags: Iterable[str]) -> CaseDef | None:
"""주 태그 조합이 가장 잘 매칭되는 케이스 추정.
def find_cases(self, primary_tags: Iterable[str]) -> tuple[CaseDef, ...]:
"""주 태그 조합에 가장 잘 맞는 케이스 **전부**를 반환한다.
완벽 일치 우선, 없으면 부분 일치 (jaccard).
내부 검출 가능 케이스(A1~A5, A24, A25, B1, B2, D1)에 가중치.
태그만으로는 케이스가 1:1로 좁혀지지 않는다. A1·A2·A3·A4·A5·A6·A15 는
주/보조 태그가 완전히 같고, 실제 구별자는 베껴온 **원본의 종류**(가사·기사·
위키·교재·타인 자서전)다. 원본 종류 라벨은 코퍼스에 아직 없으므로, 동점
케이스 중 하나를 임의로 고르는 대신 동점군을 그대로 돌려준다. 호출자는
이를 "이 중 하나"로 제시하고 사람이 원본 종류로 확정한다.
# ponytail: 원본 종류 라벨이 코퍼스에 들어오면 이 함수에 source_type 인자를
# 추가해 동점군을 좁힌다. 라벨 없는 상태에서 미리 배선하면 항상 None 이다.
"""
target = set(primary_tags)
if not target:
return None
best: CaseDef | None = None
best_score = -1.0
return ()
scored: list[tuple[float, CaseDef]] = []
for c in self.cases:
primary = set(c.primary_tags)
if not primary:
continue
inter = len(target & primary)
union = len(target | primary)
score = inter / max(1, union)
score = len(target & primary) / len(target | primary)
# v1.3 에서 기술검출 근거(●)로 확인된 케이스에 작은 가중치.
if c.detectable_internal:
score += 0.1
if score > best_score:
best_score = score
best = c
return best if best_score > 0.3 else None
scored.append((score, c))
if not scored:
return ()
best = max(score for score, _ in scored)
if best <= 0.3:
return ()
return tuple(c for score, c in scored if abs(score - best) < 1e-9)
def load_taxonomy(taxonomy_dir: Path) -> Taxonomy | None:
mt_path = taxonomy_dir / "meta_tags_v1.0.json"
cs_path = taxonomy_dir / "cases_v1.2.json"
cs_path = taxonomy_dir / "cases_v1.3.json"
if not mt_path.exists() or not cs_path.exists():
logger.warning("Taxonomy files not found in %s", taxonomy_dir)
return None
@ -108,6 +130,12 @@ def load_taxonomy(taxonomy_dir: Path) -> Taxonomy | None:
detectable_internal=c.get("detectable_internal", False),
high_risk=c.get("high_risk", False),
note=c.get("note"),
handling=c.get("handling", "terms_or_report"),
representative_precedents=tuple(
RepresentativePrecedent(case_id=p["case_id"], in_runtime_corpus=p["in_runtime_corpus"])
for p in c.get("representative_precedents", [])
),
representative_precedent_note=c.get("representative_precedent_note"),
)
for c in cs_data["cases"]
)

View File

@ -1,99 +0,0 @@
{
"version": "1.2",
"source": "나누구_저작권침해_아카이빙_실무방안_v1.2.pdf (IX장)",
"total_cases": 38,
"groups": {
"A": {
"label": "저자(가해)",
"count": 27,
"share": 0.711,
"role": "본문 텍스트 표절 검출의 핵심 대상"
},
"B": {
"label": "저자(피해)",
"count": 4,
"share": 0.105,
"role": "신고·삭제 절차(notice & takedown) 대상"
},
"C": {
"label": "플랫폼",
"count": 3,
"share": 0.079,
"role": "약관·라이선스·동의 절차 대상"
},
"D": {
"label": "다른 사용자",
"count": 1,
"share": 0.026,
"role": "2차 창작 기능 운영 정책 대상"
},
"E": {
"label": "유족",
"count": 2,
"share": 0.053,
"role": "사후 권리 가이드라인 대상"
},
"X": {
"label": "분류체계 외",
"count": 2,
"share": 0.053,
"role": "별도 약관·운영 절차"
}
},
"cases": [
{"case_id": "A1", "old_no": 1, "subgroup": "A-1 외부 텍스트 인용·수록", "title": "시·노래 가사 본문 무단 인용", "actor": "저자(가해)", "primary_tags": ["reproduction", "citation_missing"], "secondary_tags": ["public_transmission", "distribution"], "detectable_internal": true},
{"case_id": "A2", "old_no": 2, "subgroup": "A-1 외부 텍스트 인용·수록", "title": "소설·수필 본문 발췌 무단 수록", "actor": "저자(가해)", "primary_tags": ["reproduction", "citation_missing"], "secondary_tags": ["public_transmission", "distribution"], "detectable_internal": true},
{"case_id": "A3", "old_no": 3, "subgroup": "A-1 외부 텍스트 인용·수록", "title": "신문·잡지 기사 전문 옮겨 적기", "actor": "저자(가해)", "primary_tags": ["reproduction", "citation_missing"], "secondary_tags": ["public_transmission", "distribution"], "detectable_internal": true},
{"case_id": "A4", "old_no": 4, "subgroup": "A-1 외부 텍스트 인용·수록", "title": "인터넷 블로그·SNS 글 무단 수록", "actor": "저자(가해)", "primary_tags": ["reproduction", "citation_missing"], "secondary_tags": ["public_transmission", "attribution"], "detectable_internal": true},
{"case_id": "A5", "old_no": 5, "subgroup": "A-1 외부 텍스트 인용·수록", "title": "위키백과·백과사전 본문 그대로 사용", "actor": "저자(가해)", "primary_tags": ["reproduction", "citation_missing"], "secondary_tags": ["public_transmission", "attribution"], "detectable_internal": true},
{"case_id": "A6", "old_no": 6, "subgroup": "A-2 타인 자서전·회고", "title": "유명인 자서전 일부 베끼기", "actor": "저자(가해)", "primary_tags": ["reproduction", "citation_missing"], "secondary_tags": ["public_transmission", "distribution"], "detectable_internal": false},
{"case_id": "A7", "old_no": 7, "subgroup": "A-2 타인 자서전·회고", "title": "다른 자서전 줄거리·서사 변형 차용", "actor": "저자(가해)", "primary_tags": ["derivative_work", "substandard_derivative"], "secondary_tags": ["reproduction", "publication", "attribution"], "detectable_internal": false},
{"case_id": "A8", "old_no": 8, "subgroup": "A-2 타인 자서전·회고", "title": "친구·가족 회고를 본인 글로 옮김", "actor": "저자(가해)", "primary_tags": ["reproduction", "publication"], "secondary_tags": ["attribution", "false_authorship"], "detectable_internal": false},
{"case_id": "A9", "old_no": 9, "subgroup": "A-2 타인 자서전·회고", "title": "가족 일기·편지 무단 수록 (미공표)", "actor": "저자(가해)", "primary_tags": ["reproduction", "publication"], "secondary_tags": ["public_transmission", "attribution"], "detectable_internal": false},
{"case_id": "A10", "old_no": 10, "subgroup": "A-2 타인 자서전·회고", "title": "부모·조부모 자서전 통째 재수록", "actor": "저자(가해)", "primary_tags": ["reproduction", "derivative_work"], "secondary_tags": ["distribution", "publication", "attribution"], "detectable_internal": false, "high_risk": true},
{"case_id": "A11", "old_no": 11, "subgroup": "A-2 타인 자서전·회고", "title": "학창시절 친구의 시·편지 수록", "actor": "저자(가해)", "primary_tags": ["reproduction", "attribution"], "secondary_tags": ["publication"], "detectable_internal": false},
{"case_id": "A12", "old_no": 12, "subgroup": "A-2 타인 자서전·회고", "title": "동료 이메일·업무문서 인용", "actor": "저자(가해)", "primary_tags": ["reproduction", "publication"], "secondary_tags": ["attribution"], "detectable_internal": false},
{"case_id": "A13", "old_no": 36, "subgroup": "A-3 구술·강연·녹음", "title": "부모님·스승의 강연·설교 녹음해 본문에 옮김", "actor": "저자(가해)", "primary_tags": ["reproduction", "publication"], "secondary_tags": ["public_transmission", "attribution"], "detectable_internal": false, "high_risk": true},
{"case_id": "A14", "old_no": 38, "subgroup": "A-4 학술·교육 자료", "title": "본인 과거 논문·학위논문 발췌 무단 수록", "actor": "저자(가해)", "primary_tags": ["reproduction"], "secondary_tags": ["public_transmission", "distribution", "attribution"], "detectable_internal": false},
{"case_id": "A15", "old_no": 39, "subgroup": "A-4 학술·교육 자료", "title": "교과서·교재 본문 그대로 수록", "actor": "저자(가해)", "primary_tags": ["reproduction", "citation_missing"], "secondary_tags": ["public_transmission", "distribution"], "detectable_internal": false},
{"case_id": "A16", "old_no": 31, "subgroup": "A-5 번역물", "title": "외국 자서전·도서 직접 번역해 본인 글로 수록", "actor": "저자(가해)", "primary_tags": ["derivative_work", "citation_missing"], "secondary_tags": ["reproduction", "public_transmission", "distribution"], "detectable_internal": false},
{"case_id": "A17", "old_no": 13, "subgroup": "A-6 이미지·시각 자산", "title": "인터넷 옛 사진·포스터 본문 삽입", "actor": "저자(가해)", "primary_tags": ["reproduction"], "secondary_tags": ["public_transmission", "attribution"], "detectable_internal": false},
{"case_id": "A18", "old_no": 14, "subgroup": "A-6 이미지·시각 자산", "title": "졸업앨범 단체사진 무단 사용", "actor": "저자(가해)", "primary_tags": ["reproduction"], "secondary_tags": [], "detectable_internal": false},
{"case_id": "A19", "old_no": 15, "subgroup": "A-6 이미지·시각 자산", "title": "신문 스크랩·잡지 표지 사진 사용", "actor": "저자(가해)", "primary_tags": ["reproduction"], "secondary_tags": ["public_transmission", "attribution"], "detectable_internal": false},
{"case_id": "A20", "old_no": 44, "subgroup": "A-6 이미지·시각 자산", "title": "만화 캐릭터·브랜드 로고 본문 삽입", "actor": "저자(가해)", "primary_tags": ["reproduction"], "secondary_tags": ["public_transmission", "attribution"], "detectable_internal": false},
{"case_id": "A21", "old_no": 16, "subgroup": "A-7 음원·영상", "title": "오디오북 BGM에 상업 음원 사용", "actor": "저자(가해)", "primary_tags": ["reproduction", "public_transmission"], "secondary_tags": ["attribution"], "detectable_internal": false, "high_risk": true},
{"case_id": "A22", "old_no": 49, "subgroup": "A-8 디지털 사적 통신", "title": "동창회 카톡방·단톡방 대화 캡처 수록", "actor": "저자(가해)", "primary_tags": ["reproduction", "publication"], "secondary_tags": ["public_transmission", "attribution"], "detectable_internal": false, "high_risk": true},
{"case_id": "A23", "old_no": 51, "subgroup": "A-9 사후·고인 자료", "title": "사망한 친구·동료의 유작·일기 본문 수록", "actor": "저자(가해)", "primary_tags": ["reproduction", "publication", "attribution"], "secondary_tags": [], "detectable_internal": false, "high_risk": true},
{"case_id": "A24", "old_no": 17, "subgroup": "A-10 AI 도구 사용", "title": "AI memorization 반환 결과 사용", "actor": "저자(가해, 비의도)", "primary_tags": ["reproduction"], "secondary_tags": ["public_transmission", "citation_missing"], "detectable_internal": true},
{"case_id": "A25", "old_no": 18, "subgroup": "A-10 AI 도구 사용", "title": "AI 생성물 우연 유사 (기존 저작물)", "actor": "저자(가해, 비의도)", "primary_tags": ["reproduction"], "secondary_tags": ["public_transmission"], "detectable_internal": true},
{"case_id": "A26", "old_no": 19, "subgroup": "A-10 AI 도구 사용", "title": "AI 결과물을 본인 저작인 양 표시", "actor": "저자(가해)", "primary_tags": ["false_authorship"], "secondary_tags": ["attribution"], "detectable_internal": false},
{"case_id": "A27", "old_no": 20, "subgroup": "A-11 대필", "title": "대필 작가 작성분을 저자 단독 명의 출간", "actor": "저자·플랫폼", "primary_tags": ["false_authorship"], "secondary_tags": ["attribution"], "detectable_internal": false},
{"case_id": "B1", "old_no": 22, "subgroup": "B 저자(피해)", "title": "다른 사용자가 내 자서전 베낌", "actor": "저자(피해)", "primary_tags": ["reproduction"], "secondary_tags": ["public_transmission", "distribution", "attribution", "citation_missing", "false_authorship"], "detectable_internal": true},
{"case_id": "B2", "old_no": 23, "subgroup": "B 저자(피해)", "title": "다른 사용자가 내 서사·구조 차용", "actor": "저자(피해)", "primary_tags": ["derivative_work", "substandard_derivative"], "secondary_tags": ["attribution", "citation_missing"], "detectable_internal": true},
{"case_id": "B3", "old_no": 24, "subgroup": "B 저자(피해)", "title": "외부 사이트·SNS의 내 자서전 무단 게재", "actor": "저자(피해)", "primary_tags": ["reproduction", "public_transmission"], "secondary_tags": ["attribution"], "detectable_internal": false, "high_risk": true},
{"case_id": "B4", "old_no": 25, "subgroup": "B 저자(피해)", "title": "외부의 AI 학습 데이터로 무단 수집", "actor": "저자(피해)", "primary_tags": ["reproduction", "public_transmission"], "secondary_tags": [], "detectable_internal": false, "high_risk": true},
{"case_id": "C1", "old_no": 21, "subgroup": "C 플랫폼", "title": "편집자·교정자 손이 많이 들어간 경우", "actor": "플랫폼", "primary_tags": ["integrity"], "secondary_tags": ["derivative_work", "attribution"], "detectable_internal": false},
{"case_id": "C2", "old_no": 42, "subgroup": "C 플랫폼", "title": "유료 폰트 무단 사용 (본문·표지)", "actor": "저자·플랫폼", "primary_tags": ["reproduction"], "secondary_tags": ["distribution"], "detectable_internal": false},
{"case_id": "C3", "old_no": 54, "subgroup": "C 플랫폼", "title": "플랫폼이 사용자 자서전을 AI 학습 데이터로 사용", "actor": "플랫폼", "primary_tags": ["reproduction", "public_transmission"], "secondary_tags": ["derivative_work"], "detectable_internal": false, "high_risk": true},
{"case_id": "D1", "old_no": 26, "subgroup": "D 다른 사용자", "title": "2차 창작 기능에서 원저자 동의 없는 변형", "actor": "다른 사용자", "primary_tags": ["derivative_work", "attribution", "integrity"], "secondary_tags": ["publication"], "detectable_internal": true, "high_risk": true},
{"case_id": "E1", "old_no": 27, "subgroup": "E 유족", "title": "저자 사후 유족이 본문 임의 수정", "actor": "유족", "primary_tags": ["integrity"], "secondary_tags": ["reproduction", "distribution"], "detectable_internal": false},
{"case_id": "E2", "old_no": 28, "subgroup": "E 유족", "title": "사후 유고 자서전에 미공표 일기 포함", "actor": "유족", "primary_tags": ["reproduction", "publication"], "secondary_tags": ["attribution"], "detectable_internal": false},
{"case_id": "X1", "old_no": 29, "subgroup": "X 분류체계 외", "title": "자서전 등장 제3자의 사적 정보 노출", "actor": "저자(가해)", "primary_tags": [], "secondary_tags": [], "detectable_internal": false, "note": "사생활 침해 영역, 약관·운영 절차로 처리"},
{"case_id": "X2", "old_no": 30, "subgroup": "X 분류체계 외", "title": "자서전 등장 제3자의 명예 훼손 묘사", "actor": "저자(가해)", "primary_tags": [], "secondary_tags": [], "detectable_internal": false, "note": "명예훼손 영역, 약관·운영 절차로 처리"}
]
}

View File

@ -0,0 +1,907 @@
{
"version": "1.3",
"source": "v1.3 IX장 침해 케이스 ↔ 메타태그 매핑 총괄표",
"cases": [
{
"case_id": "A1",
"old_no": 1,
"subgroup": "A-1 외부 텍스트 인용·수록",
"title": "시·노래 가사 본문 무단 인용",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"citation_missing"
],
"secondary_tags": [
"public_transmission",
"distribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2012다73493",
"in_runtime_corpus": true
},
{
"case_id": "2021가합588060",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A2",
"old_no": 2,
"subgroup": "A-1 외부 텍스트 인용·수록",
"title": "소설·수필 본문 발췌 무단 수록",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"citation_missing"
],
"secondary_tags": [
"public_transmission",
"distribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2012다73493",
"in_runtime_corpus": true
},
{
"case_id": "2010다70520",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "A3",
"old_no": 3,
"subgroup": "A-1 외부 텍스트 인용·수록",
"title": "신문·잡지 기사 전문 옮겨 적기",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"citation_missing"
],
"secondary_tags": [
"public_transmission",
"distribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2007다354",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A4",
"old_no": 4,
"subgroup": "A-1 외부 텍스트 인용·수록",
"title": "인터넷 블로그·SNS 글 무단 수록",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"citation_missing"
],
"secondary_tags": [
"public_transmission",
"attribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2011가합60365",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "A5",
"old_no": 5,
"subgroup": "A-1 외부 텍스트 인용·수록",
"title": "위키백과·백과사전 본문 그대로 사용",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"citation_missing"
],
"secondary_tags": [
"public_transmission",
"attribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2012다73493",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "A6",
"old_no": 6,
"subgroup": "A-2 타인 자서전·회고",
"title": "유명인 자서전 일부 베끼기",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"citation_missing"
],
"secondary_tags": [
"public_transmission",
"distribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2006나16757",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A7",
"old_no": 7,
"subgroup": "A-2 타인 자서전·회고",
"title": "다른 자서전 줄거리·서사 변형 차용",
"actor": "저자(가해)",
"primary_tags": [
"derivative_work",
"substandard_derivative"
],
"secondary_tags": [
"reproduction",
"publication",
"attribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2006나16757",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A8",
"old_no": 8,
"subgroup": "A-2 타인 자서전·회고",
"title": "친구·가족 회고를 본인 글로 옮김",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"publication"
],
"secondary_tags": [
"attribution",
"false_authorship"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "2010도4468",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A9",
"old_no": 9,
"subgroup": "A-2 타인 자서전·회고",
"title": "가족 일기·편지 무단 수록 (미공표)",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"publication"
],
"secondary_tags": [
"public_transmission",
"attribution"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "2010도4468",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A10",
"old_no": 10,
"subgroup": "A-2 타인 자서전·회고",
"title": "부모·조부모 자서전 통째 재수록",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"derivative_work"
],
"secondary_tags": [
"distribution",
"publication",
"attribution"
],
"detectable_internal": true,
"high_risk": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2012다73493",
"in_runtime_corpus": true
},
{
"case_id": "2011도3599",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "A11",
"old_no": 11,
"subgroup": "A-2 타인 자서전·회고",
"title": "학창시절 친구의 시·편지 수록",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"attribution"
],
"secondary_tags": [
"publication"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "공표권·복제 법리"
},
{
"case_id": "A12",
"old_no": 12,
"subgroup": "A-2 타인 자서전·회고",
"title": "동료 이메일·업무문서 인용",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"publication"
],
"secondary_tags": [
"attribution"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "확립 판례 없음"
},
{
"case_id": "A13",
"old_no": 36,
"subgroup": "A-3 구술·강연·녹음",
"title": "부모님·스승의 강연·설교 녹음해 본문에 옮김",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"publication"
],
"secondary_tags": [
"public_transmission",
"attribution"
],
"detectable_internal": false,
"high_risk": true,
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "확립 판례 없음"
},
{
"case_id": "A14",
"old_no": 38,
"subgroup": "A-4 학술·교육 자료",
"title": "본인 과거 논문·학위논문 발췌 무단 수록",
"actor": "저자(가해)",
"primary_tags": [
"reproduction"
],
"secondary_tags": [
"public_transmission",
"distribution",
"attribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2016고정432",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A15",
"old_no": 39,
"subgroup": "A-4 학술·교육 자료",
"title": "교과서·교재 본문 그대로 수록",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"citation_missing"
],
"secondary_tags": [
"public_transmission",
"distribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2010다70520",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "A16",
"old_no": 31,
"subgroup": "A-5 번역물",
"title": "외국 자서전·도서 직접 번역해 본인 글로 수록",
"actor": "저자(가해)",
"primary_tags": [
"derivative_work",
"citation_missing"
],
"secondary_tags": [
"reproduction",
"public_transmission",
"distribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2011도3599",
"in_runtime_corpus": true
},
{
"case_id": "2007가합43936",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A17",
"old_no": 13,
"subgroup": "A-6 이미지·시각 자산",
"title": "인터넷 옛 사진·포스터 본문 삽입",
"actor": "저자(가해)",
"primary_tags": [
"reproduction"
],
"secondary_tags": [
"public_transmission",
"attribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2007가합16095",
"in_runtime_corpus": false
},
{
"case_id": "2019가단5207564",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A18",
"old_no": 14,
"subgroup": "A-6 이미지·시각 자산",
"title": "졸업앨범 단체사진 무단 사용",
"actor": "저자(가해)",
"primary_tags": [
"reproduction"
],
"secondary_tags": [],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "2007가합16095",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A19",
"old_no": 15,
"subgroup": "A-6 이미지·시각 자산",
"title": "신문 스크랩·잡지 표지 사진 사용",
"actor": "저자(가해)",
"primary_tags": [
"reproduction"
],
"secondary_tags": [
"public_transmission",
"attribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2007가합16095",
"in_runtime_corpus": false
},
{
"case_id": "2007다354",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A20",
"old_no": 44,
"subgroup": "A-6 이미지·시각 자산",
"title": "만화 캐릭터·브랜드 로고 본문 삽입",
"actor": "저자(가해)",
"primary_tags": [
"reproduction"
],
"secondary_tags": [
"public_transmission",
"attribution"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "어문 밖(상표)"
},
{
"case_id": "A21",
"old_no": 16,
"subgroup": "A-7 음원·영상",
"title": "오디오북 BGM에 상업 음원 사용",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"public_transmission"
],
"secondary_tags": [
"attribution"
],
"detectable_internal": false,
"high_risk": true,
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "2006가합8583",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A22",
"old_no": 49,
"subgroup": "A-8 디지털 사적 통신",
"title": "동창회 카톡방·단톡방 대화 캡처 수록",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"publication"
],
"secondary_tags": [
"public_transmission",
"attribution"
],
"detectable_internal": false,
"high_risk": true,
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "확립 판례 없음"
},
{
"case_id": "A23",
"old_no": 51,
"subgroup": "A-9 사후·고인 자료",
"title": "사망한 친구·동료의 유작·일기 본문 수록",
"actor": "저자(가해)",
"primary_tags": [
"reproduction",
"publication",
"attribution"
],
"secondary_tags": [],
"detectable_internal": false,
"high_risk": true,
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "2013나2004096",
"in_runtime_corpus": false
},
{
"case_id": "2012다204587",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "A24",
"old_no": 17,
"subgroup": "A-10 AI 도구 사용",
"title": "AI memorization 반환 결과 사용",
"actor": "저자(가해, 비의도)",
"primary_tags": [
"reproduction"
],
"secondary_tags": [
"public_transmission",
"citation_missing"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "확립 판례 없음"
},
{
"case_id": "A25",
"old_no": 18,
"subgroup": "A-10 AI 도구 사용",
"title": "AI 생성물 우연 유사 (기존 저작물)",
"actor": "저자(가해, 비의도)",
"primary_tags": [
"reproduction"
],
"secondary_tags": [
"public_transmission"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2012다73493",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "A26",
"old_no": 19,
"subgroup": "A-10 AI 도구 사용",
"title": "AI 결과물을 본인 저작인 양 표시",
"actor": "저자(가해)",
"primary_tags": [
"false_authorship"
],
"secondary_tags": [
"attribution"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "2019가단31377",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "A27",
"old_no": 20,
"subgroup": "A-11 대필",
"title": "대필 작가 작성분을 저자 단독 명의 출간",
"actor": "저자·플랫폼",
"primary_tags": [
"false_authorship"
],
"secondary_tags": [
"attribution"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "계약·성명표시(판례 약함)"
},
{
"case_id": "B1",
"old_no": 22,
"subgroup": "B 저자(피해)",
"title": "다른 사용자가 내 자서전 베낌",
"actor": "저자(피해)",
"primary_tags": [
"reproduction"
],
"secondary_tags": [
"public_transmission",
"distribution",
"attribution",
"citation_missing",
"false_authorship"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2012다73493",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "B2",
"old_no": 23,
"subgroup": "B 저자(피해)",
"title": "다른 사용자가 내 서사·구조 차용",
"actor": "저자(피해)",
"primary_tags": [
"derivative_work",
"substandard_derivative"
],
"secondary_tags": [
"attribution",
"citation_missing"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2006나16757",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "B3",
"old_no": 24,
"subgroup": "B 저자(피해)",
"title": "외부 사이트·SNS의 내 자서전 무단 게재",
"actor": "저자(피해)",
"primary_tags": [
"reproduction",
"public_transmission"
],
"secondary_tags": [
"attribution"
],
"detectable_internal": true,
"high_risk": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2008다53812",
"in_runtime_corpus": false
},
{
"case_id": "2017도19025",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "B4",
"old_no": 25,
"subgroup": "B 저자(피해)",
"title": "외부의 AI 학습 데이터로 무단 수집",
"actor": "저자(피해)",
"primary_tags": [
"reproduction",
"public_transmission"
],
"secondary_tags": [],
"detectable_internal": false,
"high_risk": true,
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "확립 판례 없음"
},
{
"case_id": "C1",
"old_no": 21,
"subgroup": "C 플랫폼",
"title": "편집자·교정자 손이 많이 들어간 경우",
"actor": "플랫폼",
"primary_tags": [
"integrity"
],
"secondary_tags": [
"derivative_work",
"attribution"
],
"detectable_internal": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2010다79923",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "C2",
"old_no": 42,
"subgroup": "C 플랫폼",
"title": "유료 폰트 무단 사용 (본문·표지)",
"actor": "저자·플랫폼",
"primary_tags": [
"reproduction"
],
"secondary_tags": [
"distribution"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "98나23616",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "C3",
"old_no": 54,
"subgroup": "C 플랫폼",
"title": "플랫폼이 사용자 자서전을 AI 학습 데이터로 사용",
"actor": "플랫폼",
"primary_tags": [
"reproduction",
"public_transmission"
],
"secondary_tags": [
"derivative_work"
],
"detectable_internal": false,
"high_risk": true,
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "확립 판례 없음"
},
{
"case_id": "D1",
"old_no": 26,
"subgroup": "D 다른 사용자",
"title": "2차 창작 기능에서 원저자 동의 없는 변형",
"actor": "다른 사용자",
"primary_tags": [
"derivative_work",
"attribution",
"integrity"
],
"secondary_tags": [
"publication"
],
"detectable_internal": true,
"high_risk": true,
"handling": "technical_detection",
"representative_precedents": [
{
"case_id": "2021가합25193",
"in_runtime_corpus": true
},
{
"case_id": "2011도3599",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "E1",
"old_no": 27,
"subgroup": "E 유족",
"title": "저자 사후 유족이 본문 임의 수정",
"actor": "유족",
"primary_tags": [
"integrity"
],
"secondary_tags": [
"reproduction",
"distribution"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "2010다79923",
"in_runtime_corpus": true
},
{
"case_id": "2012다204587",
"in_runtime_corpus": true
}
],
"representative_precedent_note": null
},
{
"case_id": "E2",
"old_no": 28,
"subgroup": "E 유족",
"title": "사후 유고 자서전에 미공표 일기 포함",
"actor": "유족",
"primary_tags": [
"reproduction",
"publication"
],
"secondary_tags": [
"attribution"
],
"detectable_internal": false,
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "2013나2004096",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
},
{
"case_id": "X1",
"old_no": 29,
"subgroup": "X 분류체계 외",
"title": "자서전 등장 제3자의 사적 정보 노출",
"actor": "저자(가해)",
"primary_tags": [],
"secondary_tags": [],
"detectable_internal": false,
"note": "사생활 침해 영역, 약관·운영 절차로 처리",
"handling": "terms_or_report",
"representative_precedents": [],
"representative_precedent_note": "저작권 밖(개인정보)"
},
{
"case_id": "X2",
"old_no": 30,
"subgroup": "X 분류체계 외",
"title": "자서전 등장 제3자의 명예 훼손 묘사",
"actor": "저자(가해)",
"primary_tags": [],
"secondary_tags": [],
"detectable_internal": false,
"note": "명예훼손 영역, 약관·운영 절차로 처리",
"handling": "terms_or_report",
"representative_precedents": [
{
"case_id": "2012도13718",
"in_runtime_corpus": false
}
],
"representative_precedent_note": null
}
]
}

View File

@ -173,7 +173,11 @@
| `ai_generation` | object | AI 생성 의심도 상세. 현재 학습 모델 사용 시 `available=true`, `is_stub=false` |
| `matches[]` | array | 매칭된 원본별 상세 (표절 시). 태그·케이스·근거 구간 포함 |
| `matches[].tags[]` | array | 법령 태그. `role`: `primary`(주)/`secondary`(보조), `label_ko` 한글 표기 |
| `matches[].case_id` | string | 39종 침해 케이스 ID (예: A6) |
| `matches[].case_id` | string | 39종 침해 케이스 ID. `case_candidates[0]` 과 같다 |
| `matches[].case_candidates[]` | array | 태그 동점 케이스 **전부**. 태그만으로는 1:1로 좁혀지지 않으므로(A1·A2·A3·A4·A5·A6·A15 는 태그가 동일) 원본의 종류로 사람이 확정한다 |
| `matches[].case_candidates[].handling` | string | `technical_detection`(v1.3 ●) / `terms_or_report`(○) |
| `matches[].case_candidates[].representative_precedents[]` | array | v1.3 IX장 총괄표의 대표판례 사건번호 |
| `matches[].case_candidates[].precedents_without_source[]` | array | 대표판례이나 적재본에 없어 출처 제시 불가. `docs/PRECEDENT_SOURCE_GAP.md` 참조 |
| `matches[].evidence_spans[]` | array | 본문 내 일치 구간 `{start, end, matched}` — 하이라이트용 |
| `matches[].partial_signal` | object | 군집화 기반 부분 표절(인물만 교체 등) 분해 — 침해요소 DB 적재용 |
| `ccl_basis` | string\|null | 사람이 읽는 판정 근거 문장 |
@ -193,7 +197,7 @@
| GET | `/v1/plagiarism/batch/{job_id}` | 배치 상태·결과 조회 |
| POST | `/v1/summary` | 스토리 요약 (과제2 ②) |
| GET | `/v1/precedents` | 전체 판례 검색·등급·저작물 유형 필터 |
| GET | `/v1/taxonomy` | 10종 법령 태그 + 39 케이스 정의 (동일 라벨 공유용) |
| GET | `/v1/taxonomy` | 10종 법령 태그 + 39 케이스 정의 + 케이스별 대표판례 (동일 라벨 공유용) |
| GET | `/v1/health` | 엔진 상태·코퍼스 크기·버전 |
### 맞춤 요약 옵션

View File

@ -0,0 +1,25 @@
# 대표판례 출처 미확보 목록 — 컴북스 요청 대상
v1.3 IX장 총괄표가 지목한 대표판례 중 운영 적재본(`data/precedents/precedents.jsonl`, 545건)에
없어 결과에 출처를 제시할 수 없는 사건번호 **14건**이다.
대부분 `selection_audit.json`의 `excel_without_official_detail_case_ids`(103건)에 속한다 —
한국저작권위원회 공식 상세 페이지를 찾지 못해 출처를 임의 생성하지 않고 제외한 건들이다.
요청 내용: 판결문 원문 또는 검증 가능한 공식 출처 URL.
| 사건번호 | 영향받는 침해 케이스 |
|---|---|
| 2006가합8583 | A21 |
| 2006나16757 | A6, A7, B2 |
| 2007가합16095 | A17, A18, A19 |
| 2007가합43936 | A16 |
| 2007다354 | A19, A3 |
| 2008다53812 | B3 |
| 2010도4468 | A8, A9 |
| 2012도13718 | X2 |
| 2013나2004096 | A23, E2 |
| 2016고정432 | A14 |
| 2017도19025 | B3 |
| 2019가단31377 | A26 |
| 2019가단5207564 | A17 |
| 2021가합588060 | A1 |

View File

@ -41,3 +41,47 @@ def test_out_of_scope_excluded_from_request():
ids = {x["case_id"] for x in rep["data_request_list"]}
assert "C1" not in ids
assert rep["need_data_cases"] == 0
# --- 태그 → 케이스 도달 범위 회귀 (v1.3) ---------------------------------
from pathlib import Path
from app.engine.taxonomy import load_taxonomy
ROOT = Path(__file__).resolve().parents[1]
#: _assign_tags 가 실제로 낼 수 있는 주 태그 조합 전부.
#: 이 목록이 곧 케이스 도달 범위의 상한이므로 조합이 바뀌면 여기도 바뀌어야 한다.
_EMITTED_PRIMARY = [
["reproduction", "citation_missing"],
["reproduction", "derivative_work"],
["derivative_work", "citation_missing"],
["derivative_work", "substandard_derivative"],
["reproduction"],
]
def _reachable():
tax = load_taxonomy(ROOT / "data/taxonomy")
return {c.case_id for combo in _EMITTED_PRIMARY for c in tax.find_cases(combo)}
def test_reachable_cases_cover_most_technical_detection_cases():
"""v1.3 ● 19건 중 16건이 태그만으로 후보에 오른다.
남은 B3·C1·D1 은 게재 사실·편집 개입·성명표시처럼 텍스트에서 알 수 없는
사실이 필요해 LegalContext 없이는 도달할 수 없다. 이 경계를 고정한다.
"""
tax = load_taxonomy(ROOT / "data/taxonomy")
detectable = {c.case_id for c in tax.cases if c.detectable_internal}
reachable = _reachable()
assert detectable - reachable == {"B3", "C1", "D1"}
assert len(reachable & detectable) == 16
def test_no_tie_group_is_silently_collapsed():
"""동점군이 2건 이상이면 전부 반환된다 — 하나만 남으면 나머지가 사장된다."""
tax = load_taxonomy(ROOT / "data/taxonomy")
assert len(tax.find_cases(["reproduction", "citation_missing"])) == 7
assert len(tax.find_cases(["derivative_work", "substandard_derivative"])) == 2

View File

@ -27,9 +27,11 @@ def test_taxonomy_loads_10_tags_and_cases():
tax = load_taxonomy(ROOT / "data/taxonomy")
assert tax is not None
assert len(tax.meta_tags) == 10, f"PDF IV장: 10종 메타 태그 필요, 현재 {len(tax.meta_tags)}"
# PDF 본문에는 "38개"라고 명시되어 있으나 그룹별 통계
# (A27+B4+C3+D1+E2+X2)는 39건이므로 그룹별 통계를 기준으로 검증
assert 38 <= len(tax.cases) <= 39, f"PDF IX장: 38~39 케이스, 현재 {len(tax.cases)}"
# v1.3 IX장 총괄표 본문의 통계 줄은 "●16/○22 = 38"로 적혀 있으나 표를 실제로
# 세면 ●19/○20 = 39 다. 표를 기준으로 고정한다.
assert len(tax.cases) == 39, f"v1.3 IX장 총괄표: 39 케이스, 현재 {len(tax.cases)}"
assert sum(c.detectable_internal for c in tax.cases) == 19
assert sum(c.handling == "technical_detection" for c in tax.cases) == 19
def test_taxonomy_has_required_legal_tags():
@ -51,12 +53,22 @@ def test_taxonomy_case_groups():
assert len(a_cases) == 27, f"PDF: A그룹(저자 가해) 27건, 현재 {len(a_cases)}"
def test_case_mapping_finds_a1_for_reproduction_citation():
def test_case_mapping_returns_full_tie_group_not_one_arbitrary_pick():
"""reproduction+citation_missing 은 7개 케이스가 주/보조 태그까지 동일하다.
하나만 고르면 나머지 6건이 결과에 영원히 등장하지 않으므로 동점군을 모두 낸다.
"""
tax = load_taxonomy(ROOT / "data/taxonomy")
# A1: 시·노래 가사 본문 무단 인용 — 주 태그: reproduction, citation_missing
case = tax.find_case(["reproduction", "citation_missing"])
assert case is not None
assert case.case_id.startswith("A"), f"reproduction+citation은 A그룹 매칭 기대, got {case.case_id}"
ids = [c.case_id for c in tax.find_cases(["reproduction", "citation_missing"])]
assert set(ids) == {"A1", "A2", "A3", "A4", "A5", "A6", "A15"}, ids
assert all(i.startswith("A") for i in ids)
def test_every_case_carries_v13_precedent_mapping():
"""39건 전부가 대표판례를 갖거나, 없는 이유를 명시한다. 빈칸은 허용하지 않는다."""
tax = load_taxonomy(ROOT / "data/taxonomy")
for c in tax.cases:
assert c.representative_precedents or c.representative_precedent_note, c.case_id
def test_high_risk_cases_marked():