docs 24 -> 12. 성적 수치 문서와 운영에 필요한 문서만 남긴다. 삭제분은 git 이력에 남아 있으므로 필요하면 되살린다. 남김: TEST_PLAN_2026_PHASE2, PERF_EVIDENCE_CAPTURE_2026, PRECISION_TEST_ PROCEDURE, SCOPE_AND_METRICS, AI_TRAINING_RESULT x2, CASE_MATCHING_KPI (성적 수치) / IMPLEMENTATION_RUNBOOK(King 배포 절차), API_SPEC_BAIKAL(바이칼 연동), AI_DETECTION·PRECEDENT_LISTUP(코드가 참조), COMBOOKS 회신(미발송). 지우면 끊어질 내용은 버리지 않고 합쳤다. - CASE_MATCHING_API.md -> API_SPEC_BAIKAL.md 7절 - PRECEDENT_SOURCE_GAP.md 의 출처 미확보 14건 표 -> COMBOOKS 회신 2절 남은 문서 6곳과 analyze_case_coverage.py 의 끊어진 링크도 함께 정리했다. reports: 파라미터 스윕 중간본 4개(batch_*.jsonl, 42MB)를 삭제한다. 참조가 없고 xlsx 짝도 없는 탐색용 산출물이다. 날짜가 붙은 실측 배치(infringement_batch_*)는 xlsx 와 함께 증빙으로 유지한다. 진행 중인 요약(No.7) 작업 파일은 건드리지 않았다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
308 lines
14 KiB
Markdown
308 lines
14 KiB
Markdown
# 저작권 탐지 API 양식서 (오투오 → 바이칼)
|
||
|
||
> 나누구 앱 **저작권 탭** 연동용 API 계약. 본문 입력 → 표절 탐지 결과 + 저작권 탭
|
||
> 표시 항목을 반환한다. 본 문서 기준으로 UI를 연동하면 된다.
|
||
> 엔진: `o2o-plagiarism-api`
|
||
|
||
---
|
||
|
||
## 0. 기본 정보
|
||
|
||
| 항목 | 값 |
|
||
|---|---|
|
||
| Base URL (개발/통합 테스트) | `https://plagiarism.o2o.kr` |
|
||
| 프로토콜 | HTTPS, `Content-Type: application/json` (UTF-8) |
|
||
| 인증 | 운영 설정 시 `X-API-Key` 헤더 사용 |
|
||
| Swagger(자동 명세) | `{BASE}/docs` — 실시간 스키마 확인 가능 |
|
||
| 실서비스 배포 | 개인정보(자서전) 보호를 위해 **Docker 온프레미스 납품** 예정 (실사용자 본문은 오투오 서버로 전송 안 함) |
|
||
|
||
---
|
||
|
||
## 1. 저작권 탭 화면 ↔ API 필드 매핑 (핵심)
|
||
|
||
나누구 저작권 탭의 각 표시 항목은 경량 응답에서 그대로 읽으면 된다. **별도 계산 불필요**
|
||
— 독창성 환산과 한글 라벨까지 서버가 완료해 제공한다.
|
||
|
||
| 화면 표시 | 예시 | API 필드 |
|
||
|---|---|---|
|
||
| 독창성 **98%** | 98 | `copyright.originality_percent` |
|
||
| 저작권 · 유사도 **2%** | 2 | `copyright.similarity_percent` |
|
||
| 유사 문장 **0건** | 0건 | `similar_sentences.label` |
|
||
| 대조 결과 설명 | 문장 | `copyright.description` |
|
||
| 표절 의심 구간 **없음** | false | `copyright.has_suspicion` |
|
||
| AI 생성 의심도 **낮음** | 낮음 | `ai_generation_suspicion.label` |
|
||
| 판례 판단 | 침해 의심 낮음 | `legal_judgment.label` |
|
||
| 판례 근거 | 사건번호 포함 문장 | `legal_judgment.summary` |
|
||
|
||
> `ai_generation_suspicion.level` 값은 `low`, `medium`, `high`, `unknown`이며,
|
||
> 화면은 함께 반환되는 한글 `label`을 그대로 표시한다.
|
||
|
||
---
|
||
|
||
## 2. 저작권 탭 경량 검사 — `POST /v1/plagiarism/review`
|
||
|
||
모바일 저작권 탭은 이 API를 사용한다. 내부 상세 탐지 결과 중 화면에 필요한
|
||
독창성, 유사도, 대조 건수, 유사 문장 수, AI 의심도, 판례 판단만 반환한다.
|
||
요청 형식은 기존 `POST /v1/plagiarism/detect`와 동일하며 응답 계약은
|
||
본 문서를 따른다.
|
||
|
||
## 3. 상세 표절 탐지 — `POST /v1/plagiarism/detect`
|
||
|
||
본문 1건을 검사한다.
|
||
|
||
### 요청
|
||
|
||
```json
|
||
{
|
||
"doc_id": "episode-001",
|
||
"text": "창밖으로 보이는 숲은 오늘따라 유난히 푸르게 보였다. ...",
|
||
"metadata": { "title": "에피소드 1", "author": "홍길동", "genre": "자서전" },
|
||
"options": {
|
||
"return_evidence": true,
|
||
"threshold": null,
|
||
"top_k": 5,
|
||
"autobiography_mode": null
|
||
},
|
||
"legal_context": {
|
||
"work_type": "literary",
|
||
"access_evidence": null,
|
||
"protected_expression_reviewed": false,
|
||
"rights_verified": false
|
||
}
|
||
}
|
||
```
|
||
|
||
| 필드 | 타입 | 필수 | 설명 |
|
||
|---|---|---|---|
|
||
| `doc_id` | string | ✅ | 호출측 문서 식별자 (응답에 그대로 반환) |
|
||
| `text` | string | ✅ | 검사할 본문 (1자 이상) |
|
||
| `metadata` | object | – | `title`/`author`/`genre`/`publisher`/`publication_year` (모두 선택) |
|
||
| `options.return_evidence` | bool | – | 일치 구간 하이라이트 반환 (기본 true) |
|
||
| `options.threshold` | float\|null | – | 판정 임계값. null이면 서버 기본(0.85) |
|
||
| `options.top_k` | int | – | 최대 매칭 수 (기본 5) |
|
||
| `options.autobiography_mode` | bool\|null | – | 자서전 특화 전처리. null이면 서버 설정 |
|
||
| `legal_context` | object | – | 사람이 확인한 접근 가능성·창작성·권리관계. 없으면 미확인으로 처리 |
|
||
|
||
### 응답 (저작권 탭에 표시되는 "깨끗한 글" 예시)
|
||
|
||
```json
|
||
{
|
||
"doc_id": "episode-001",
|
||
"is_infringement": false,
|
||
"confidence": 0.32,
|
||
"review_summary": {
|
||
"originality_percent": 98,
|
||
"similarity_percent": 2,
|
||
"similar_sentence_count": 0,
|
||
"compared_count": 37891,
|
||
"has_suspicion": false,
|
||
"ai_suspicion_level": "low"
|
||
},
|
||
"ai_generation": {
|
||
"suspicion_level": "low",
|
||
"score": 0.02,
|
||
"is_stub": false,
|
||
"available": true,
|
||
"model_version": "hgb-nolen-kf-ko-v1-Qwen3.8-27B-gpt-4.1-mini-gpt-4o-mini-Korean-autobiography-v2-auroc0.997",
|
||
"note": "Qwen·GPT 한국어 자서전 범위의 검토 우선순위 점수이며 AI 작성 확정 판정이 아님"
|
||
},
|
||
"matches": [],
|
||
"extracted_elements": {
|
||
"characters": [], "motifs": [], "genre": "자서전", "keywords": ["정원", "숲"]
|
||
},
|
||
"ccl_basis": null,
|
||
"autobiography_mode": true,
|
||
"candidates_before_filter": 0,
|
||
"engine_version": "o2o-plagiarism-2.1.0-kosimcse",
|
||
"analyzed_at": "2026-08-04T09:30:00Z"
|
||
}
|
||
```
|
||
|
||
### 응답 (표절이 탐지된 경우 — matches가 채워짐)
|
||
|
||
```json
|
||
{
|
||
"doc_id": "episode-002",
|
||
"is_infringement": true,
|
||
"confidence": 0.88,
|
||
"review_summary": {
|
||
"originality_percent": 45,
|
||
"similarity_percent": 55,
|
||
"similar_sentence_count": 1,
|
||
"compared_count": 37891,
|
||
"has_suspicion": true,
|
||
"ai_suspicion_level": "low"
|
||
},
|
||
"ai_generation": { "suspicion_level": "unknown", "score": null, "available": false, "is_stub": false, "model_version": "unavailable", "note": "학습된 모델이 없어 채점하지 않음" },
|
||
"matches": [
|
||
{
|
||
"source_doc": "auto-0003",
|
||
"source_title": "○○○ 자서전",
|
||
"similarity": 0.88,
|
||
"tags": [
|
||
{ "tag": "reproduction", "role": "primary", "label_ko": "복제권" },
|
||
{ "tag": "citation_missing", "role": "primary", "label_ko": "인용 표시 누락" }
|
||
],
|
||
"case_id": "A6",
|
||
"case_title": "유명인 자서전 일부 베끼기",
|
||
"infringement_type": "copy",
|
||
"evidence_spans": [ { "start": 12, "end": 48, "matched": "일치한 본문 구간 ..." } ],
|
||
"score_breakdown": { "text_sim": 0.86, "lemma_sim": 0.94, "character_sim": 0.8, "motif_sim": 0.7, "lsh_jaccard": 0.62 },
|
||
"partial_signal": {
|
||
"cluster_id": 2, "verdict": "element_swap_plagiarism", "signature_score": 0.74,
|
||
"per_element": { "lemmas": 0.92, "keywords": 0.88, "characters": 0.0, "motifs": 0.1 },
|
||
"retained_elements": ["lemmas", "keywords"], "changed_elements": ["characters", "motifs"]
|
||
}
|
||
}
|
||
],
|
||
"ccl_basis": "'○○○ 자서전'와 결합 유사도 88%로 매칭. 주 침해 태그: 복제권, 인용 표시 누락. 추정 케이스 A6 ...",
|
||
"autobiography_mode": true,
|
||
"candidates_before_filter": 4,
|
||
"engine_version": "o2o-plagiarism-2.1.0-kosimcse",
|
||
"analyzed_at": "2026-08-04T09:31:00Z"
|
||
}
|
||
```
|
||
|
||
### 응답 필드 상세
|
||
|
||
| 필드 | 타입 | 설명 |
|
||
|---|---|---|
|
||
| `is_infringement` | bool | 후방호환용 임계 초과 매칭 여부. 법적 침해 확정 아님 |
|
||
| `confidence` | float | 최상위 매칭 결합 유사도 원값(0~1). **화면 표시는 `review_summary` 사용 권장** |
|
||
| `review_summary` | object | **저작권 탭 UI 직접 매핑** (§1 표 참조) |
|
||
| `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. `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/COMBOOKS_CASE_MATCHING_REPLY_20260918.md` 2절 |
|
||
| `matches[].evidence_spans[]` | array | 본문 내 일치 구간 `{start, end, matched}` — 하이라이트용 |
|
||
| `matches[].partial_signal` | object | 군집화 기반 부분 표절(인물만 교체 등) 분해 — 침해요소 DB 적재용 |
|
||
| `ccl_basis` | string\|null | 사람이 읽는 판정 근거 문장 |
|
||
| `legal_risk` | object\|null | 판례 기반 위험도, 사건번호, A/B/C 검토 등급과 판단 근거 |
|
||
| `score_semantics` | object\|null | 검색 점수·임계값·일치 범위의 의미 |
|
||
| `has_similarity_match` | bool\|null | 등록 코퍼스 유사 원문 확인 여부 |
|
||
| `corpus_scope_note` | string\|null | 검색 대상 코퍼스 범위 안내 |
|
||
| `engine_version` | string | 엔진 버전 |
|
||
|
||
---
|
||
|
||
## 4. 부가 엔드포인트
|
||
|
||
| Method | Path | 용도 |
|
||
|---|---|---|
|
||
| POST | `/v1/plagiarism/batch` | 배치 검사(≤500건). `202` + `job_id` 반환 → 아래 상태 조회 |
|
||
| GET | `/v1/plagiarism/batch/{job_id}` | 배치 상태·결과 조회 |
|
||
| POST | `/v1/summary` | 스토리 요약 (과제2 ②) |
|
||
| GET | `/v1/precedents` | 전체 판례 검색·등급·저작물 유형 필터 |
|
||
| GET | `/v1/taxonomy` | 10종 법령 태그 + 39 케이스 정의 + 케이스별 대표판례 (동일 라벨 공유용) |
|
||
| GET | `/v1/health` | 엔진 상태·코퍼스 크기·버전 |
|
||
|
||
### 맞춤 요약 옵션
|
||
|
||
`POST /v1/summary`는 `detail`(`brief|standard|detailed`), `emphasis`(최대 10개),
|
||
`ratio`, `max_sentences`, `use_abstractive`를 받는다. `ratio`를 지정하면 `detail`보다
|
||
우선한다.
|
||
|
||
### 판례 조회
|
||
|
||
`GET /v1/precedents`는 `q`, `grade`(`A|B|C|unreviewed`), `work_type`, `offset`,
|
||
`limit` 쿼리를 지원한다. 운영 판례 545건 전체가 검색·인용 후보이며 A/B/C 등급은
|
||
제외 조건이 아니라 검토 품질과 직접성에 따른 우선순위다.
|
||
|
||
### 상태 확인 확장
|
||
|
||
`GET /v1/health`에는 `corpus_documents`, `index_backend`, `ai_model_ready`,
|
||
`precedent_count`가 추가되었다.
|
||
|
||
---
|
||
|
||
## 5. 에러 포맷
|
||
|
||
FastAPI 표준. HTTP 상태코드 + `detail`.
|
||
|
||
```json
|
||
{ "detail": "에러 메시지" }
|
||
```
|
||
|
||
| 코드 | 상황 |
|
||
|---|---|
|
||
| 400 | 잘못된 요청(빈 본문 등) |
|
||
| 404 | 배치 job_id 없음 |
|
||
| 422 | 스키마 검증 실패 |
|
||
| 503 | 분류체계 미로딩 |
|
||
|
||
---
|
||
|
||
## 6. 연동 시 주의
|
||
|
||
1. **AI 생성 의심도는 확정값이 아님** — `available`/`is_stub`/`model_version`을 확인하고 사람 검토 우선순위로만 사용할 것.
|
||
출판 승인/거절 게이트로 쓰지 말 것(오탐 리스크).
|
||
2. **화면 값은 `review_summary` 사용** — `confidence`(원값)는 임베딩 성분 때문에 무관한 글도
|
||
높게 나오므로 직접 표시 금지. 독창성 환산은 오투오가 `review_summary`에서 완료해 제공.
|
||
3. **개인정보** — 실사용자 자서전 본문은 개인정보. 실서비스는 온프레미스(Docker) 납품으로
|
||
전환 예정. 개발 단계 REST 호출 시에도 테스트 데이터 사용 권장.
|
||
4. `compared_count` 는 현재 로딩된 검색 세그먼트 수다. 문서 수와 혼동하지 않는다.
|
||
5. 현재 운영 코퍼스는 612개 문서, 37,891개 세그먼트이며 판례는 545건이다. 데이터
|
||
증분 적재 시 수치는 달라질 수 있으므로 화면에서는 API 반환값을 그대로 사용한다.
|
||
|
||
---
|
||
|
||
## 7. 케이스 매칭 응답 계약 (2026-09-18)
|
||
|
||
### 7-1. 표시 대상
|
||
|
||
`POST /v1/plagiarism/detect`와 `POST /v1/plagiarism/batch`의 options에
|
||
`"audience": "author"`를 지정하면 저자용 JSON으로 직렬화한다.
|
||
생략 시 `admin`이며 기존 케이스 후보·판례·법률 검토 응답이 유지된다.
|
||
허용하지 않은 audience 값은 422다. 이 선택은 권한 인증을 대신하지 않는다.
|
||
|
||
```json
|
||
{
|
||
"doc_id": "manuscript-123",
|
||
"text": "검사할 원고 본문",
|
||
"options": {"audience": "author", "return_evidence": true}
|
||
}
|
||
```
|
||
|
||
저자용 응답은 `legal_risk`, `is_infringement`를 생략한다. matches 각각에서
|
||
`case_id`, `case_title`, `case_candidates`, `tags`, `infringement_type`,
|
||
`publication_verdict`, `publication_verdict_status`를 생략한다(빈 값 반환이 아닌 키 생략).
|
||
일치 원천·좌표·증거 구간·점수·match_reasons는 유지한다. `return_evidence=false`이면
|
||
기존처럼 evidence_spans는 빈 목록이다. 자유형 `ccl_basis`는 다음 고정 문장을 사용한다.
|
||
|
||
- 매칭 있음: `확인이 필요한 부분이 있습니다.`
|
||
- 매칭 없음: `등록된 비교 자료에서 일치 구간을 찾지 못했습니다.`
|
||
|
||
매칭 없음은 비침해/출간 가능 판정이 아니다.
|
||
배치에서는 항목별 결과에 동일 규칙이 적용된다. 경량 `/v1/plagiarism/review`도
|
||
같은 옵션을 받고 legal_judgment의 판례 ID를 비우고 코드 없는 검토 문장을 제공한다.
|
||
기존 review 응답의 필드 구성은 유지한다.
|
||
|
||
### 7-2. 출간 판정
|
||
|
||
taxonomy 케이스와 관리자용 case_candidates 각각에 `publication_verdict`를 제공한다.
|
||
현재 값은 전부 null이다. 컴북스 코드표와 케이스 대응표가 없기 때문이다.
|
||
null은 출간 허용·불가 중 어느 쪽도 의미하지 않는다.
|
||
|
||
관리자용 match의 `publication_verdict`는 대표값이고, 아직 null이다.
|
||
`publication_verdict_status=source_pending`은 후보 판정 자료 미확보다.
|
||
일부 후보에 판정이 들어왔어도 우선순위 규칙을 확보하기 전에는 대표값을 만들지 않고
|
||
`review_required`를 반환한다. 코드표와 보수적 순서가 확정되면 이를 검증하는
|
||
회귀 테스트와 함께 대표 선정 로직을 추가한다. 후보별 판정은 삭제하지 않는다.
|
||
현재 스키마의 문자열은 연결 지점이며 공식 9종 enum 정의가 아니다.
|
||
|
||
### 7-3. 감사 기록 연결
|
||
|
||
상세 detect와 배치 항목에 `request_id`(매 호출별 UUID), `engine_version`,
|
||
`taxonomy_version`, `analyzed_at`이 들어간다. 같은 doc_id로 재검사하면 request_id는 달라진다.
|
||
새 기본 엔진 버전은 `o2o-plagiarism-2.2.1-cases-v1.3`이다. 환경변수로 버전을
|
||
덮어쓰는 운영 환경은 배포 시 같은 값을 반영해야 한다.
|
||
|
||
바이칼은 관리자용 원본 응답을 보관하고 요청/원천 ID와 연결해 최종 확정·수정 이유·
|
||
검토자·시각을 기록한다. 저자용 JSON에는 관리자의 확정에 필요한 후보가 없으므로
|
||
그것만 보관하면 케이스 정확도 평가를 할 수 없다. 감사 로그 5년 저장은 API UUID를
|
||
추가했다고 구현되는 기능이 아니며 바이칼 저장 계층에서 별도 구현해야 한다.
|