From 52a0fdcdf041f0b7a0ef6132558b6c464b03090b Mon Sep 17 00:00:00 2001 From: hbyang Date: Fri, 18 Sep 2026 15:42:34 +0900 Subject: [PATCH] feat: expand infringement case matching to v1.3 precedent mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 태그 동점 시 첫 케이스만 반환해 나머지를 버리던 문제를 고친다. 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) --- app/api/routes.py | 6 + app/api/schemas.py | 20 + app/engine/detector.py | 54 +- app/engine/taxonomy.py | 62 ++- data/taxonomy/cases_v1.2.json | 99 ---- data/taxonomy/cases_v1.3.json | 907 ++++++++++++++++++++++++++++++++++ docs/API_SPEC_BAIKAL.md | 8 +- docs/PRECEDENT_SOURCE_GAP.md | 25 + tests/test_case_coverage.py | 44 ++ tests/test_pdf_compliance.py | 28 +- 10 files changed, 1120 insertions(+), 133 deletions(-) delete mode 100644 data/taxonomy/cases_v1.2.json create mode 100644 data/taxonomy/cases_v1.3.json create mode 100644 docs/PRECEDENT_SOURCE_GAP.md diff --git a/app/api/routes.py b/app/api/routes.py index 90e8f0d..14602a0 100644 --- a/app/api/routes.py +++ b/app/api/routes.py @@ -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, diff --git a/app/api/schemas.py b/app/api/schemas.py index 923d0e9..44ffc55 100644 --- a/app/api/schemas.py +++ b/app/api/schemas.py @@ -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 diff --git a/app/engine/detector.py b/app/engine/detector.py index d953f4a..420c238 100644 --- a/app/engine/detector.py +++ b/app/engine/detector.py @@ -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} " diff --git a/app/engine/taxonomy.py b/app/engine/taxonomy.py index cb07dc8..2ee95c8 100644 --- a/app/engine/taxonomy.py +++ b/app/engine/taxonomy.py @@ -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"] ) diff --git a/data/taxonomy/cases_v1.2.json b/data/taxonomy/cases_v1.2.json deleted file mode 100644 index 923c30e..0000000 --- a/data/taxonomy/cases_v1.2.json +++ /dev/null @@ -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": "명예훼손 영역, 약관·운영 절차로 처리"} - ] -} diff --git a/data/taxonomy/cases_v1.3.json b/data/taxonomy/cases_v1.3.json new file mode 100644 index 0000000..44b5ed1 --- /dev/null +++ b/data/taxonomy/cases_v1.3.json @@ -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 + } + ] +} diff --git a/docs/API_SPEC_BAIKAL.md b/docs/API_SPEC_BAIKAL.md index e07adf0..9459a0d 100644 --- a/docs/API_SPEC_BAIKAL.md +++ b/docs/API_SPEC_BAIKAL.md @@ -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` | 엔진 상태·코퍼스 크기·버전 | ### 맞춤 요약 옵션 diff --git a/docs/PRECEDENT_SOURCE_GAP.md b/docs/PRECEDENT_SOURCE_GAP.md new file mode 100644 index 0000000..7cff346 --- /dev/null +++ b/docs/PRECEDENT_SOURCE_GAP.md @@ -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 | diff --git a/tests/test_case_coverage.py b/tests/test_case_coverage.py index 1fdfa15..af0295b 100644 --- a/tests/test_case_coverage.py +++ b/tests/test_case_coverage.py @@ -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 diff --git a/tests/test_pdf_compliance.py b/tests/test_pdf_compliance.py index 8231a17..0895698 100644 --- a/tests/test_pdf_compliance.py +++ b/tests/test_pdf_compliance.py @@ -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():