엔드포인트 10종 상세 사용법 + 응답 필드별 의미 + 저작권 탭 렌더링 호출 순서(detect 1회 → review_summary로 화면 구성) 정리. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
291 lines
13 KiB
Markdown
291 lines
13 KiB
Markdown
# 저작권 탐지 API 상세 연동 가이드 (오투오 → 바이칼)
|
||
|
||
> 나누구 앱 연동용. 각 엔드포인트 사용법 · 응답 변수 의미 · **저작권 탭 화면을 그리기
|
||
> 위한 호출 순서**를 정리한다. 실시간 스키마: `https://plagiarism.o2o.kr/docs`
|
||
> 엔진: `o2o-plagiarism-2.1.0-kosimcse`
|
||
|
||
---
|
||
|
||
## 0. 공통
|
||
|
||
| 항목 | 값 |
|
||
|---|---|
|
||
| Base URL | `https://plagiarism.o2o.kr` |
|
||
| 공통 prefix | 모든 API는 `/v1` 으로 시작 |
|
||
| 요청/응답 | JSON, UTF-8, `Content-Type: application/json` |
|
||
| 인증 | 개발 단계 없음 (운영 전 토큰 추가 예정) |
|
||
| 시간 형식 | ISO 8601 UTC (예: `2026-08-04T04:37:00Z`) |
|
||
|
||
### 엔드포인트 한눈에
|
||
|
||
| # | Method | Path | 용도 | 화면 연동 |
|
||
|---|---|---|---|---|
|
||
| 1 | GET | `/v1/health` | 엔진 상태·코퍼스 크기 확인 | 앱 기동 시 1회 |
|
||
| 2 | POST | `/v1/plagiarism/detect` | **본문 1건 표절 탐지** | **저작권 탭 핵심** |
|
||
| 3 | POST | `/v1/plagiarism/batch` | 배치 탐지(≤500) 등록 | 대량 검사용 |
|
||
| 4 | GET | `/v1/plagiarism/batch/{job_id}` | 배치 결과 조회 | 대량 검사용 |
|
||
| 5 | POST | `/v1/summary` | 스토리 요약 | 요약 기능용 |
|
||
| 6 | GET | `/v1/taxonomy` | 법령 태그·케이스 정의 | 라벨 캐시용 |
|
||
| 7 | GET | `/v1/corpus` | 코퍼스 목록 | 운영/관리 |
|
||
| 8 | POST | `/v1/corpus` | 코퍼스 등록(JSON) | 운영/관리 |
|
||
| 9 | POST | `/v1/corpus/file` | 코퍼스 등록(.txt 업로드) | 운영/관리 |
|
||
| 10 | DELETE | `/v1/corpus/{doc_id}` | 코퍼스 삭제 | 운영/관리 |
|
||
|
||
---
|
||
|
||
## 1. 저작권 탭을 그리기 위한 호출 순서 ★
|
||
|
||
가장 중요한 부분. 저작권 탭 화면 하나를 채우는 데 필요한 호출 흐름이다.
|
||
|
||
```
|
||
[앱 최초 기동 / 세션 시작 시 — 1회]
|
||
① GET /v1/health → 엔진 정상·corpus_size 확인 (실패면 탭 비활성)
|
||
② GET /v1/taxonomy → 태그/케이스 한글 라벨 캐시 (표절 상세 표시용, 선택)
|
||
|
||
[사용자가 에피소드 작성 후 '저작권' 탭을 열 때 — 매 검사]
|
||
③ POST /v1/plagiarism/detect { doc_id, text: 에피소드 본문 }
|
||
└ 응답 1건으로 저작권 탭 전체를 렌더링:
|
||
• review_summary → 상단 요약(독창성/유사도/유사문장/AI 의심도)
|
||
• matches[] → 표절 의심 시 상세 목록
|
||
• matches[].evidence_spans → 본문 내 일치 구간 하이라이트
|
||
• matches[].tags / case_id → 침해 유형 배지 (②의 라벨과 결합)
|
||
• ccl_basis → 사람이 읽는 판정 근거 문장
|
||
```
|
||
|
||
**핵심: 탭 렌더링에 필요한 호출은 `detect` 단 1번**이다. `health`/`taxonomy`는
|
||
세션당 1회 캐시하면 된다. 화면 상단 숫자는 전부 `review_summary`에서 나오므로,
|
||
바이칼 쪽에서 별도 계산할 것은 없다.
|
||
|
||
### 화면 요소 ↔ 응답 필드 매핑
|
||
|
||
| 저작권 탭 화면 | 값 예시 | 응답 경로 |
|
||
|---|---|---|
|
||
| 독창성 98% | 98 | `review_summary.originality_percent` |
|
||
| 저작권 · 유사도 2% | 2 | `review_summary.similarity_percent` |
|
||
| "…3.5만 건과 대조한 결과 표절 의심 구간이 없습니다" | 35000 / false | `review_summary.compared_count`, `review_summary.has_suspicion` |
|
||
| 유사 문장 0건 | 0 | `review_summary.similar_sentence_count` |
|
||
| AI 생성 의심도 낮음 | low | `review_summary.ai_suspicion_level` (`low/medium/high`=낮음/중간/높음) |
|
||
| (표절 시) 일치 구간 하이라이트 | start~end | `matches[].evidence_spans[]` |
|
||
| (표절 시) 침해 유형 배지 | 복제권 | `matches[].tags[].label_ko` + `case_id` |
|
||
|
||
---
|
||
|
||
## 2. `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 }
|
||
}
|
||
```
|
||
|
||
| 필드 | 타입 | 필수 | 의미 |
|
||
|---|---|---|---|
|
||
| `doc_id` | string | ✅ | 호출측 문서 식별자. 응답에 그대로 반환(요청-응답 짝 맞춤용) |
|
||
| `text` | string | ✅ | 검사할 본문 (1자 이상) |
|
||
| `metadata.title` | string | – | 작품 제목 (선택) |
|
||
| `metadata.author` | string | – | 저자 (선택) |
|
||
| `metadata.genre` | string | – | 장르 (선택) |
|
||
| `metadata.publisher` | string | – | 출판사 (선택) |
|
||
| `metadata.publication_year` | int | – | 출판연도 (선택) |
|
||
| `options.return_evidence` | bool | – | 일치 구간(`evidence_spans`) 반환 여부. 기본 `true` |
|
||
| `options.threshold` | float 0~1 \| null | – | 표절 판정 임계값. `null`이면 서버 기본(0.85) |
|
||
| `options.top_k` | int | – | 반환할 최대 매칭 수. 기본 5 |
|
||
| `options.autobiography_mode` | bool \| null | – | 자서전 특화 전처리. `null`이면 서버 설정 |
|
||
|
||
### 응답 — 전체 필드 의미
|
||
|
||
```json
|
||
{
|
||
"doc_id": "episode-001",
|
||
"is_infringement": false,
|
||
"confidence": 0.14,
|
||
"review_summary": {
|
||
"originality_percent": 98,
|
||
"similarity_percent": 2,
|
||
"similar_sentence_count": 0,
|
||
"compared_count": 35000,
|
||
"has_suspicion": false,
|
||
"ai_suspicion_level": "low"
|
||
},
|
||
"ai_generation": { "suspicion_level": "low", "score": 0.06, "is_stub": true, "note": "더미 응답 — 실제 AI 생성 판별 결과가 아님" },
|
||
"matches": [],
|
||
"extracted_elements": { "characters": [], "motifs": ["비밀의 정원"], "genre": null, "keywords": ["숲","전설"] },
|
||
"ccl_basis": null,
|
||
"autobiography_mode": true,
|
||
"candidates_before_filter": 3,
|
||
"engine_version": "o2o-plagiarism-2.1.0-kosimcse",
|
||
"analyzed_at": "2026-08-04T04:37:00Z"
|
||
}
|
||
```
|
||
|
||
**최상위 필드**
|
||
|
||
| 필드 | 타입 | 의미 | 화면 사용 |
|
||
|---|---|---|---|
|
||
| `doc_id` | string | 요청의 doc_id 그대로 | 응답 매칭 |
|
||
| `is_infringement` | bool | 표절 판정 여부(임계 초과 매칭 존재). `review_summary.has_suspicion`과 동일 | — |
|
||
| `confidence` | float 0~1 | 최상위 매칭의 **결합 유사도 원값**. ⚠️ 임베딩 성분 때문에 무관한 글도 높게 나옴 → **화면 표시는 `review_summary` 사용, `confidence` 직접 표시 금지** | ✗ |
|
||
| `review_summary` | object | **저작권 탭 UI 요약** (아래 상세) | ★ |
|
||
| `ai_generation` | object | AI 생성 의심도 상세 (아래 상세) | 참고 |
|
||
| `matches` | array | 매칭된 원본별 상세. 표절 아니면 `[]` | 표절 상세 |
|
||
| `extracted_elements` | object | 본문에서 추출한 구성요소 `characters/motifs/genre/keywords` | 부가 |
|
||
| `ccl_basis` | string\|null | 사람이 읽는 판정 근거 문장. 표절 아니면 `null` | 상세 문구 |
|
||
| `autobiography_mode` | bool | 자서전 전처리 적용 여부 | — |
|
||
| `candidates_before_filter` | int\|null | LSH 1차 필터 통과 후보 수(내부 지표) | — |
|
||
| `engine_version` | string | 엔진 버전 | 로깅 |
|
||
| `analyzed_at` | datetime | 분석 시각(UTC) | 로깅 |
|
||
|
||
**`review_summary` (저작권 탭 요약 — 그대로 표시)**
|
||
|
||
| 필드 | 타입 | 의미 |
|
||
|---|---|---|
|
||
| `originality_percent` | int 0~100 | **독창성 %** = 100 − 유사도. 점수 변환(캘리브레이션)을 오투오가 완료해 제공 |
|
||
| `similarity_percent` | int 0~100 | **유사도 %** (무관한 글은 0~5%로 눌러 표시) |
|
||
| `similar_sentence_count` | int | **유사 문장 건수** = 임계 초과 매칭 수 |
|
||
| `compared_count` | int | **대조한 원본(코퍼스) 건수** |
|
||
| `has_suspicion` | bool | **표절 의심 구간 존재 여부** |
|
||
| `ai_suspicion_level` | `low/medium/high` | **AI 생성 의심도**(낮음/중간/높음). ⚠️ 현재 더미 |
|
||
|
||
**`ai_generation` (AI 생성 의심도 상세)**
|
||
|
||
| 필드 | 타입 | 의미 |
|
||
|---|---|---|
|
||
| `suspicion_level` | `low/medium/high` | 의심도 등급 |
|
||
| `score` | float 0~1 | 참고 점수 |
|
||
| `is_stub` | bool | **`true`면 더미(미구현)**. 정식 구현 시 `false` |
|
||
| `note` | string | 안내 문구 |
|
||
|
||
> ⚠️ **AI 생성 의심도는 현재 스텁**이다. `is_stub=true`이며 요청마다 더미 값을 낸다.
|
||
> UI 배지는 붙여두되 **출판 승인 게이트로 쓰지 말 것**(참고용).
|
||
|
||
**`matches[]` (표절 의심 시 채워짐)**
|
||
|
||
| 필드 | 타입 | 의미 |
|
||
|---|---|---|
|
||
| `source_doc` | string | 매칭된 원본 doc_id |
|
||
| `source_title` | string\|null | 매칭된 원본 제목 |
|
||
| `similarity` | float 0~1 | 해당 원본과의 결합 유사도 |
|
||
| `tags[]` | array | 법령 태그. `{ tag, role, label_ko }` |
|
||
| `tags[].tag` | string | 태그 코드 (예: `reproduction`) |
|
||
| `tags[].role` | `primary/secondary` | 주 침해 / 보조 |
|
||
| `tags[].label_ko` | string | 한글 표기 (예: `복제권`) — 배지에 그대로 사용 |
|
||
| `case_id` | string\|null | 39종 침해 케이스 ID (예: `A6`) |
|
||
| `case_title` | string\|null | 케이스 명칭 |
|
||
| `infringement_type` | string | 후방호환 단일 분류(`copy/transform/plot/character/unknown`) |
|
||
| `evidence_spans[]` | array | 본문 내 일치 구간 `{ start, end, matched }` — **하이라이트용** |
|
||
| `score_breakdown` | object | 유사도 분해 `text_sim/lemma_sim/character_sim/motif_sim/lsh_jaccard` |
|
||
| `partial_signal` | object\|null | 군집화 부분 표절(인물만 교체 등) 분해 — 침해요소 DB 적재용 |
|
||
|
||
---
|
||
|
||
## 3. `GET /v1/health` — 엔진 상태
|
||
|
||
앱 기동 시 호출해 엔진 준비·코퍼스 규모를 확인한다.
|
||
|
||
**응답**
|
||
|
||
| 필드 | 타입 | 의미 |
|
||
|---|---|---|
|
||
| `status` | `"ok"` | 정상 |
|
||
| `engine_version` | string | 엔진 버전 |
|
||
| `corpus_size` | int | 로딩된 대조 원본 수 (`review_summary.compared_count`와 동일) |
|
||
| `taxonomy_version` | string\|null | 분류체계 버전 |
|
||
| `autobiography_mode` | bool | 자서전 모드 on/off |
|
||
|
||
---
|
||
|
||
## 4. `GET /v1/taxonomy` — 법령 태그·케이스 정의
|
||
|
||
10종 태그와 39종 케이스 정의를 반환. 세션당 1회 받아 캐시하면, `detect` 응답의
|
||
`tag`/`case_id`를 화면에 설명과 함께 표시할 수 있다.
|
||
|
||
**응답**: `meta_tags_version`, `cases_version`, `meta_tags[]`, `cases[]`
|
||
- `meta_tags[]` 각 항목: `id`, `label_ko`, `category`, `law_ref`, `scope`, `description`
|
||
- `cases[]` 각 항목: `case_id`, `title`, `subgroup`, `actor`, `primary_tags[]`, `secondary_tags[]`, `detectable_internal`, `high_risk`, `note`
|
||
|
||
---
|
||
|
||
## 5. `POST /v1/plagiarism/batch` + `GET /v1/plagiarism/batch/{job_id}` — 배치
|
||
|
||
여러 건을 한 번에 검사(비동기). 대량 점검용.
|
||
|
||
**등록 요청** `POST /v1/plagiarism/batch`
|
||
```json
|
||
{ "items": [ { "doc_id": "e1", "text": "..." }, { "doc_id": "e2", "text": "..." } ],
|
||
"options": { "threshold": null } }
|
||
```
|
||
- `items`: 1~500건. 각 `{ doc_id, text, metadata? }`
|
||
- 응답(202): `{ job_id, status, total, created_at }`
|
||
|
||
**결과 조회** `GET /v1/plagiarism/batch/{job_id}`
|
||
|
||
| 필드 | 의미 |
|
||
|---|---|
|
||
| `status` | `queued/running/completed/failed` |
|
||
| `total` / `processed` | 전체 / 처리 완료 건수 (진행률) |
|
||
| `results` | `status=completed`일 때만. `DetectResponse` 배열(§2와 동일 구조) |
|
||
| `error` | 실패 시 사유 |
|
||
|
||
> 폴링: 등록 → `job_id`로 `completed` 될 때까지 조회 → `results` 사용.
|
||
|
||
---
|
||
|
||
## 6. `POST /v1/summary` — 스토리 요약
|
||
|
||
**요청**: `{ "text": "...", "ratio": 0.3, "max_sentences": null, "use_abstractive": true }`
|
||
- `ratio`: 요약 길이 비율(입력 대비). `use_abstractive`: LLM 결합(키 없으면 추출 요약 폴백)
|
||
|
||
**응답**: `extractive`(추출 요약), `abstractive`(추상 요약\|null), `final`(최종), `mode`(`extractive/hybrid`), `num_sentences_in/out`
|
||
|
||
---
|
||
|
||
## 7. 코퍼스 관리 (운영/관리용)
|
||
|
||
대조 원본을 등록·삭제. 업로드/삭제 시 인덱스 자동 재빌드.
|
||
|
||
| API | 설명 |
|
||
|---|---|
|
||
| `GET /v1/corpus` | 목록. `{ total, docs[{doc_id,title,size_bytes,filename}] }` |
|
||
| `POST /v1/corpus` | JSON 등록 `{ doc_id?(자동), title, text }` → `201` |
|
||
| `POST /v1/corpus/file` | multipart `.txt` 업로드 (`title`, `doc_id?`, `file`) → `201` |
|
||
| `DELETE /v1/corpus/{doc_id}` | 삭제 → `204` |
|
||
|
||
등록 응답(`201`): `{ doc_id, title, size_bytes, corpus_size_after, rebuilt }`
|
||
|
||
---
|
||
|
||
## 8. 에러 포맷
|
||
|
||
FastAPI 표준. `{ "detail": "메시지" }`
|
||
|
||
| 코드 | 상황 |
|
||
|---|---|
|
||
| 400 | 잘못된 요청(빈 본문/비 UTF-8 파일 등) |
|
||
| 404 | 배치 job_id 없음 / 코퍼스 doc_id 없음 |
|
||
| 409 | 코퍼스 doc_id 중복 |
|
||
| 422 | 스키마 검증 실패(필드 타입·범위) |
|
||
| 503 | 분류체계 미로딩 |
|
||
|
||
---
|
||
|
||
## 9. 빠른 시작 (curl)
|
||
|
||
```bash
|
||
# 1) 엔진 상태
|
||
curl https://plagiarism.o2o.kr/v1/health
|
||
|
||
# 2) 저작권 탭용 탐지 — 이 응답 1건으로 탭 렌더링
|
||
curl -X POST https://plagiarism.o2o.kr/v1/plagiarism/detect \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"doc_id":"episode-001","text":"검사할 본문 텍스트..."}'
|
||
```
|
||
|
||
문의: 오투오 양형배
|