o2o-plagiarism-ai/docs/API_SPEC_BAIKAL.md
hbyang bbdda66934 chore: prune docs to score-evidence set and drop sweep intermediates
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>
2026-09-28 09:55:02 +09:00

308 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 저작권 탐지 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를
추가했다고 구현되는 기능이 아니며 바이칼 저장 계층에서 별도 구현해야 한다.