o2o-plagiarism-ai/docs/API_GUIDE_BAIKAL.md

20 KiB
Raw Blame History

저작권 탐지 API 상세 연동 가이드 (오투오 → 바이칼)

나누구 앱 연동용. 각 엔드포인트 사용법 · 응답 변수 의미 · 저작권 탭 화면을 그리기 위한 호출 순서를 정리한다. 실시간 스키마: https://plagiarism.o2o.kr/docs 엔진: o2o-plagiarism-2.1.0-kosimcse 최종 갱신: 2026-08-18 · 판례 545건 및 GPT 판례 판단 연동 반영


0. 공통

항목
Base URL https://plagiarism.o2o.kr
공통 prefix 모든 API는 /v1 으로 시작
요청/응답 JSON, UTF-8, Content-Type: application/json
인증 개발 기본값은 무인증. 운영에서 설정된 경우 X-API-Key 헤더 필수 (/v1/health 공개 여부는 서버 설정)
시간 형식 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             → 사람이 읽는 판정 근거 문장
          • legal_risk            → 판례 Top 5 + GPT/규칙 기반 법적 검토 보조
          • score_semantics       → 점수·임계값의 의미와 잠정 여부

핵심: 탭 렌더링에 필요한 호출은 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 생성 의심도 unknown review_summary.ai_suspicion_level (low/medium/high/unknown)
(표절 시) 일치 구간 하이라이트 start~end matches[].evidence_spans[]
(표절 시) 침해 유형 배지 복제권 matches[].tags[].label_ko + case_id
판례 기반 검토 의견 판단 필요 legal_risk.llm_verdict, legal_risk.supporting_reasons[]
관련 판례 사건번호 legal_risk.llm_matched_precedent_ids[] 또는 precedent_ids[]

2. POST /v1/plagiarism/detect — 표절 탐지 (핵심)

본문 1건을 검사해 저작권 탭에 필요한 모든 데이터를 반환한다.

요청 본문

{
  "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.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이면 서버 설정
legal_context.work_type string 판례 검색용 저작물 유형. 기본 literary
legal_context.access_evidence bool | null 원저작물 접근·의거 가능성 확인 여부. 미확인은 null
legal_context.protected_expression_reviewed bool 보호되는 창작적 표현인지 사람이 검토했는지
legal_context.rights_verified bool 권리 귀속·이용허락·인용 요건 확인 여부

legal_context는 검토자가 확인한 사실만 전달한다. 서버와 GPT는 원고만 보고 접근 가능성, 권리 귀속 또는 이용허락 여부를 임의로 추론하지 않는다.

응답 — 전체 필드 의미

{
  "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": "unknown", "score": null, "available": false, "is_stub": false, "model_version": "unavailable", "note": "학습된 모델이 없어 채점하지 않음" },
  "matches": [],
  "extracted_elements": { "characters": [], "motifs": ["비밀의 정원"], "genre": null, "keywords": ["숲","전설"] },
  "ccl_basis": null,
  "has_similarity_match": false,
  "corpus_scope_note": "현재 등록된 원천 문서와 검색 세그먼트만 대조했습니다. 미매칭은 비침해 확정이 아닙니다.",
  "legal_risk": {
    "status": "no_registered_corpus_match",
    "risk_level": "low",
    "similarity_evidence": "검색 유사도 0.000, 질의 커버리지 0.000, 최장 연속 일치 0자",
    "protected_expression": "not_reviewed",
    "access_evidence": "not_provided",
    "missing_factors": ["보호되는 창작적 표현인지에 대한 사람 검토", "원저작물 접근·의거 가능성", "저작권 귀속·이용허락·인용 요건"],
    "precedent_ids": [],
    "judgment_method": "rule_based",
    "llm_verdict": null,
    "llm_confidence": null,
    "llm_review_required": null,
    "llm_matched_precedent_ids": [],
    "supporting_reasons": [],
    "counter_reasons": [],
    "judge_model": null,
    "judge_prompt_version": null,
    "judge_note": null,
    "judgment_summary": "등록 코퍼스에서 일치 증거가 확인되지 않아 판례 기반 침해 의심을 제시하지 않습니다. 다만 미매칭은 비침해 확정이 아닙니다.",
    "disclaimer": "이 결과는 등록 코퍼스와 판례에 기반한 검토 우선순위이며 법률상 침해 확정이 아닙니다."
  },
  "score_semantics": {
    "combined_score": 0.14,
    "score_kind": "lexical_lemma_blend",
    "threshold_used": 0.85,
    "threshold_source": "server_default",
    "threshold_calibrated": false,
    "provisional": true,
    "union_coverage": 0.0,
    "covered_chars": 0,
    "query_chars": 42,
    "evidence_truncated": false
  },
  "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 후방호환 필드. 법적 침해 확정이 아니라 임계 초과 매칭 존재 여부
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 상세 문구
has_similarity_match bool 등록 코퍼스에서 채택된 매칭 존재 여부 분기
corpus_scope_note string 미매칭의 한계를 설명하는 안내 문구 안내
legal_risk object 등록 판례와 선택적 GPT Judge 기반의 법적 검토 보조 검토 카드
score_semantics object 점수 종류·임계값·커버리지·잠정 여부 감사/상세
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/unknown AI 생성 의심도. 미학습/채점 불가는 unknown

ai_generation (AI 생성 의심도 상세)

필드 타입 의미
suspicion_level low/medium/high 의심도 등급
score float 0~1 참고 점수
available bool 학습 모델 또는 명시적으로 활성화한 baseline으로 채점했는지
is_stub bool true면 미검증 휴리스틱 baseline. 학습 모델은 false
model_version string 학습 아티팩트/특징 버전 추적
note string 안내 문구

학습 모델이 없으면 점수를 임의 생성하지 않고 available=false, score=null, suspicion_level=unknown을 반환한다. 학습 후에도 출판 승인 게이트나 저자 제재의 단독 근거로 쓰지 말 것(사람 검토 우선순위용).

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 적재용

등록된 국내 저작권 판례 545건 중 저작물 유형과 법적 태그가 가까운 Top 5를 먼저 선별한다. 운영에서 USE_LLM_LEGAL_JUDGE=true이고 OpenAI API 키가 설정되어 있으면, GPT가 탐지 증거와 이 Top 5만 비교해 구조화된 의견을 반환한다. 전체 판례를 매 요청에 전송하지 않는다.

필드 타입 의미
status review_required/no_registered_corpus_match/insufficient_precedent_data 사람 검토 상태. 법적 확정 판정이 아님
risk_level low/medium/high/null 규칙 기반 검토 우선순위
precedent_ids[] string[] 규칙 엔진이 검색한 판례 Top 5 사건번호
judgment_method llm/rule_based/rule_fallback GPT 성공, 기능 비활성, GPT 실패 후 폴백 구분
llm_verdict likely/unlikely/insufficient_evidence/null GPT의 비교 의견. 침해 확정값이 아님
llm_confidence float 0~1|null GPT 자기평가 값. 통계적 침해 확률이 아님
llm_review_required bool|null GPT 결과는 항상 true; 모델이 사람 검토를 해제할 수 없음
llm_matched_precedent_ids[] string[] GPT가 실제 근거로 선택한 사건번호. Top 5 밖의 ID는 서버가 거부
supporting_reasons[] string[] 의심 의견을 지지하는 근거
counter_reasons[] string[] 반대 근거·판단을 약화하는 사실
missing_factors[] string[] 보호 표현, 의거관계, 권리·허락 등 추가 확인사항
judge_model string|null 사용한 GPT 모델명
judge_prompt_version string|null 감사·재현용 프롬프트 버전
judge_note string|null 폴백 또는 법적 한계 안내
judgment_summary string 검증된 판례 사건번호와 핵심 사유를 결합한 사용자 표시용 의견

matches[].case_id는 39종 내부 침해유형 ID이고, legal_risk.*precedent_ids는 실제 판례 사건번호다. 두 필드를 혼용하지 않는다. UI에서는 llm_verdict만 단독 표시하지 말고 judgment_summary를 첫 문장으로 표시한 뒤 찬반 근거, 누락 요소, 판례 사건번호 및 면책 문구를 함께 보여준다. 모델명이나 “GPT 판례 비교” 같은 구현 용어는 사용자용 판정 제목으로 표시하지 않는다.

score_semantics (점수 해석)

  • combined_score는 검색 랭킹 점수이며 침해 확률이 아니다.
  • provisional=true이면 임계값이 실데이터로 확정되지 않은 잠정값이다.
  • union_coverage는 질의 전체에서 비중복 일치 구간이 차지하는 비율이다.
  • evidence_truncated=true이면 CPU 상한 때문에 일부 후보의 정밀 증거 계산이 생략됐다.

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
corpus_documents int 등록된 원천 문서 수
index_backend string 현재 검색 인덱스 구현
ai_model_ready bool 학습된 AI 생성 탐지 모델 준비 여부
precedent_count int 로딩된 판례 수. 현재 운영 데이터는 545건

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

{ "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_idcompleted 될 때까지 조회 → 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)

# 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" \
  -H "X-API-Key: 운영에서 발급된 키" \
  -d '{"doc_id":"episode-001","text":"검사할 본문 텍스트...","legal_context":{"work_type":"literary","access_evidence":null,"protected_expression_reviewed":false,"rights_verified":false}}'

개발 서버가 무인증 설정이면 X-API-Key 헤더를 생략할 수 있다. 운영 서버의 인증 적용 여부와 발급된 키를 확인한 뒤 연동한다.