# O2O 저작권 침해 여부 탐지 API KOCCA 출판환경변화 과제 2단계 - 오투오 산출물. 자서전 본문을 입력하면 저작권 침해 여부를 판정해 법령 기반 메타 태그와 케이스 ID로 반환합니다. ## 기능 - **삼중 유사도 탐지** — 표면(임베딩) + 구조(형태소 lemma 교집합) + 요소(인물·모티프) 결합 판정 - **3단 캐스케이딩** — MinHash+LSH 1차 필터 → 정밀 비교 → 침해 유형 분류 - **법령 기반 10종 메타 태그** — 복제권 / 공중송신권 / 배포권 / 2차적저작물작성권 / 공표권 / 성명표시권 / 동일성유지권 / 인용 표시 누락 / 자기 창작인 양 표시 / 2차적저작물 미달 가공 - **39개 침해 케이스 자동 매핑** — 그룹 A(저자 가해) ~ X(분류체계 외) - **자서전 특화 처리** — 공통 표현 사전 제거 + NER 마스킹으로 오탐 방지 - **검토 콘솔 웹 UI** — 브라우저에서 본문 입력·검사·결과 분석, 코퍼스 관리 - **코퍼스 직접 관리** — 사용자가 자서전을 직접 업로드/삭제, 인덱스 자동 재빌드 - **배치 처리** — 비동기 잡으로 최대 500건 일괄 검사 ## 빠른 시작 ### Docker ```bash cp .env.example .env # .env 에서 HOST/PORT/LOG_LEVEL/OPENAI_API_KEY 등 조정 (선택) docker compose up --build ``` ### 로컬 (Python 3.13 권장) ```bash python3.13 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt # .env 의 HOST/PORT 따라 실행 python -m app.main # 또는 일회성 override PORT=9000 LOG_LEVEL=debug python -m app.main # 또는 uvicorn 직접 uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload ``` 접속: - `http://localhost:{PORT}/` — 검토 콘솔 (브라우저용) - `http://localhost:{PORT}/docs` — API 명세 (Swagger) `.env` 의 서버 바인딩 변수: | 변수 | 기본 | 설명 | |---|---|---| | `HOST` | `0.0.0.0` | 바인딩 호스트 | | `PORT` | `8000` | 포트 | | `LOG_LEVEL` | `info` | debug/info/warning/error | | `RELOAD` | `false` | 파일 변경 시 자동 재시작 (개발용) | ## 사용법 ### 1. 브라우저에서 사용 (검토 콘솔) `http://localhost:8000/` 접속. **[탐지 검토] 탭** 1. 본문 텍스트 붙여넣기 (또는 샘플 시나리오 버튼 클릭) 2. "검사 시작" → 결과 표시 - 큰 판정 배지 (침해 여부 + 결합 유사도 %) - 점수 분석 (text / lemma / character / motif 4개 막대) - 매칭된 레퍼런스 + **법령 태그 chip (주/보조)** + **케이스 ID** - 본문에 일치 구간 하이라이트 **[코퍼스 관리] 탭** 1. 좌측 폼에서 자서전 신규 등록 (제목 + 본문 또는 .txt 파일) 2. 우측 테이블에서 등록된 자서전 확인 / 삭제 3. 업로드/삭제 시 인덱스 자동 재빌드 ### 2. API로 사용 (curl) ```bash # 탐지 curl -X POST http://localhost:8000/v1/plagiarism/detect \ -H "Content-Type: application/json" \ -d '{ "doc_id": "test-001", "text": "검사할 본문 텍스트…", "metadata": {"title": "작품명", "author": "저자"} }' # 자서전 업로드 curl -X POST http://localhost:8000/v1/corpus \ -H "Content-Type: application/json" \ -d '{"title": "김OO 자서전", "text": "본문…"}' # .txt 파일 업로드 curl -X POST http://localhost:8000/v1/corpus/file \ -F "title=김OO 자서전" \ -F "file=@autobiography.txt" # 코퍼스 목록 curl http://localhost:8000/v1/corpus # 분류체계 조회 (10종 태그 + 39 케이스) curl http://localhost:8000/v1/taxonomy ``` ## 엔드포인트 요약 | Method | Path | 용도 | |---|---|---| | GET | `/` | 검토 콘솔 (HTML) | | GET | `/docs` | Swagger UI | | GET | `/v1/health` | 헬스체크 + 엔진 정보 | | GET | `/v1/taxonomy` | 분류체계 조회 | | POST | `/v1/plagiarism/detect` | 단건 동기 탐지 | | POST | `/v1/plagiarism/batch` | 배치 잡 등록 (≤500건) | | GET | `/v1/plagiarism/batch/{job_id}` | 배치 결과 조회 | | GET | `/v1/corpus` | 코퍼스 목록 | | POST | `/v1/corpus` | JSON 업로드 | | POST | `/v1/corpus/file` | .txt 파일 업로드 | | DELETE | `/v1/corpus/{doc_id}` | 삭제 | ## 응답 스키마 (탐지 결과) ```json { "doc_id": "test-001", "is_infringement": true, "confidence": 0.85, "extracted_elements": { "characters": ["..."], "motifs": ["..."], "genre": "...", "keywords": ["..."] }, "matches": [{ "source_doc": "ref-0001", "source_title": "원본 작품명", "similarity": 0.85, "tags": [ {"tag": "reproduction", "role": "primary", "label_ko": "복제권"}, {"tag": "citation_missing", "role": "primary", "label_ko": "인용 표시 누락"}, {"tag": "public_transmission", "role": "secondary", "label_ko": "공중송신권"} ], "case_id": "A1", "case_title": "시·노래 가사 본문 무단 인용", "evidence_spans": [{"start": 21, "end": 34, "matched": "…"}], "score_breakdown": { "text_sim": 0.43, "lemma_sim": 0.75, "character_sim": 0.0, "motif_sim": 0.67, "lsh_jaccard": 0.07 } }], "ccl_basis": "…", "autobiography_mode": true, "engine_version": "o2o-plagiarism-2.0.0-pdf-v1.2" } ``` ## 환경변수 (`.env`) | 변수 | 기본 | 설명 | |---|---|---| | `SIMILARITY_THRESHOLD` | `0.85` | 침해 판정 임계값 (정밀도 우선) | | `AUTOBIOGRAPHY_MODE` | `true` | 자서전 특화 전처리 (공통표현+NER) | | `USE_LSH_FILTER` | `true` | MinHash+LSH 1차 필터 | | `USE_LLM_EXTRACTOR` | `false` | OpenAI 기반 요소 추출 (키 필요) | | `USE_EMBEDDING_SIMILARITY` | `false` | OpenAI 임베딩 유사도 (키 필요) | | `OPENAI_API_KEY` | (빈값) | 비우면 룰 기반 + TF-IDF 폴백 | | `WEIGHT_TEXT_SIM` 등 | 0.30/0.45/0.15/0.10 | 삼중 유사도 가중치 (합 1.0) | 요청 단위 override 가능 항목: - `options.threshold` — 임계값 - `options.autobiography_mode` — 자서전 모드 ON/OFF ## 테스트 ```bash pytest tests/ -v # 31 passed — API, 삼중 유사도, 형태소 분석, PDF 분류체계, LSH, 자서전 필터, 다중 태그 부여 ``` ## 디렉토리 구조 ``` app/ ├── main.py # FastAPI 앱 ├── api/{routes,schemas}.py # 엔드포인트 + Pydantic 모델 ├── core/{config,auth}.py # 설정 + API Key 인증 ├── engine/ │ ├── detector.py # 탐지 파이프라인 오케스트레이션 │ ├── extractor.py # 콘텐츠 요소 추출 (룰 또는 OpenAI) │ ├── similarity.py # 삼중 유사도 + TF-IDF/임베딩 백엔드 │ ├── structural.py # 형태소 lemma 교집합 │ ├── lsh_filter.py # MinHash+LSH 1차 필터 │ ├── autobiography_filter.py # 자서전 공통표현 제거 + NER 마스킹 │ ├── corpus.py # 코퍼스 CRUD │ └── taxonomy.py # 10종 태그 + 39 케이스 매핑 ├── jobs/store.py # 배치 잡 in-memory 스토어 └── static/index.html # 검토 콘솔 UI data/ ├── reference/ # 자서전 코퍼스 (업로드 저장 위치) ├── taxonomy/{meta_tags,cases}.json # PDF v1.2 분류체계 └── autobiography/common_patterns.txt # 자서전 빈출 표현 사전 ```