판정 사유 API

# 판정 사유(Explanation) API 「왜 이렇게 분류됐나요?」에 답하는 공통 응답 필드입니다. **모든 코어핀 필터**(개인정보·문서보안·유해발화·정신건강, 그리고 이를 묶어 쓰는 문서·URL·크롤)가 같은 모양의 `explanation` 객체를 응답에 실어 줍니다. 파트너·고객은 이 객체를 그대로 저장해 자기 서비스 화면에 **「필터링 기록·사유」**로 보여줄 수 있습니다. - **스키마 버전**: `corepin.explanation/v1` - **기본값**: 켜져 있음. 응답 크기를 줄이려면 요청에 `"explain": false` (분류·탐지 API) - **변경 영향**: 기존 필드는 그대로이고 `explanation` 만 추가됩니다. 모르는 필드는 무시하면 됩니다. ## 1. 어디에서 받나요 | 필터 | 엔드포인트 | 응답 위치 | |---|---|---| | 개인정보 | `POST /v1/pii/detect` · `/redact` · `/batch` | `explanation` (배치는 `results[].explanation`) | | 문서보안 | `POST /v1/dlp/classify` · `/batch` | `explanation` | | 유해발화 | `POST /v1/moderation/classify` · `/batch` | `explanation` | | 정신건강 | `POST /v1/mental_health/classify` · `/batch` | `explanation` | | 문서 파서 | `POST /v1/doc/parse` · `/batch` · `/url` · 비동기 | `pii_applied.explanation` · `dlp_classification.explanation` · `moderation.explanation` · `mental_health.explanation` | | 웹 크롤러 | `POST /v1/crawl/scrape` · `/crawl` | 페이지·첨부별 위와 동일 | ## 2. 객체 구조 ```json { "schema_version": "corepin.explanation/v1", "filter": "dlp", "decision": "flagged", "summary_ko": "문서 보안 표식 때문에 등급이 ‘기밀’(으)로 올라갔습니다 (유형: 전략·기획).", "reasons": [ { "code": "CONFIDENTIAL", "label_ko": "기밀", "kind": "grade", "basis": "marking_rule", "decisive": true, "tier": "rule", "description_ko": "문서에 ‘대외비’ 표식이 있어 모델 판정(내부)을 ‘기밀’(으)로 올렸습니다.", "evidence": [{"text": "대외비", "start": 22, "end": 25, "rule_id": "marking.confidential"}] }, { "code": "STRATEGY", "label_ko": "전략·기획", "kind": "type", "basis": "model", "decisive": true, "tier": "t2_1", "score": 0.41, "threshold": 0.33, "description_ko": "‘전략·기획’ 유형 점수 0.41 가 반응 기준 0.33 이상입니다…" } ], "dismissed": [], "inputs": {"tier_used": "t2_1", "confidence": 0.74, "confidence_band": "medium"}, "notes_ko": ["신뢰도가 낮은 판정이면 자동 차단 대신 관리자 검토 대상으로 두세요."] } ``` | 필드 | 설명 | |---|---| | `filter` | `pii` · `dlp` · `moderation` · `mental_health` | | `decision` | `flagged`(검출됨) · `clear`(검출 없음) · `skipped`(검사 생략) | | `summary_ko` | **한 문장 요약.** 관리자 화면에 그대로 써도 되는 문장 | | `reasons[]` | 판정 이유. 등급·유형·라벨·축·카테고리 하나당 1건 | | `reasons[].basis` | `rule`·`marking_rule`(고정 규칙이 결정) · `model`·`t3_model`(모델 판단) · `policy`(조직 정책) | | `reasons[].decisive` | `false` 면 결과를 바꾸지 않은 **참고 정보** | | `reasons[].score` · `threshold` | 모델 점수와 반응 기준값. 규칙 판정이면 비어 있음 | | `reasons[].evidence[]` | 근거 위치. `text`(80자 발췌)·`start`·`end`·`segment`(문서를 문장 단위로 쪼갠 번호)·`rule_id` | | `dismissed[]` | **후보였으나 최종 결과에서 제외된 항목과 사유** (`why_code`/`why_ko`). 「왜 안 걸렸나」 추적용 | | `inputs` | 판정에 쓰인 입력 정보(길이·단계·신뢰 구간) | | `notes_ko` | 오탐 가능성·한계 안내 | | `policy` | `apply_policy=true` 일 때만 — 차단 결정과 맞은 등급·유형 | ### `dismissed[].why_code` | 코드 | 의미 | |---|---| | `not_target_grade` · `not_target_type` · `not_target_label` | 호출에서 지정한 대상 등급·유형·라벨이 아니라 응답에서 뺌 | | `injection_defense_text` | 인젝션을 「막기 위한」 지시문으로 판단해 M10 제외 | | `no_deterministic_evidence` | 문서 검사에서 규칙 근거 없는 M10(모델 판정) 제외 | | `document_context_no_request` | 문서 문장에 AI 에게 시키는 요청·의도 표현이 없고 용어를 서술적으로 언급한 것으로 판단해 제외 (보안·준법·인사 문서의 표준 용어 오탐 방지) | ## 3. 필터별 읽는 법 ### 문서보안(DLP) - 등급은 **문서 앞부분(약 512토큰, 한글 기준 천 자 안팎)** 을 분류 모델이 읽고 판단합니다. 신뢰도가 낮으면 심층 검증 모델이 한 번 더 봅니다 (`tier: t3`). - 문서에 `대외비`·`영업비밀`·`극비` 같은 **보안 표식**이 있으면 해당 등급 이상으로 올립니다(규칙). 이때 `basis=marking_rule` 과 표식 위치가 `evidence` 로 옵니다. 모델 판정은 `decisive:false` 참고 항목으로 남습니다. - 유형마다 **반응 기준(threshold)** 이 다릅니다(현행 모델: 인수·합병 0.33 · 계약·합의 0.35 · 인사·평가 0.39 · 전략·기획 0.48 · 구매·조달 0.49 · 재무·실적 0.51 · 고객 정보 0.58 · 보안·인증 0.59 · 법무·소송 0.70 · R&D·지식재산 0.72). `score` 가 `threshold` 를 막 넘은 값(0.10 이내)이면 경계선 판정입니다 — 파트너 쪽에서 점수 기준을 더해 차단 대상을 좁힐 수 있습니다. - `inputs.confidence_band`: `low`(<0.60) · `medium` · `high`(≥0.85). `low` 는 자동 차단 대신 검토를 권장합니다. ### 유해발화(Moderation) - 라벨마다 `basis` 가 **규칙**(표현 사전·패턴이 걸린 문구와 위치가 `evidence`)인지 **모델**(문장 의미 판단, 규칙 근거 없음)인지 나옵니다. 모델 판단은 오탐 가능성이 더 높으므로 검토 대상으로 분류하기 좋습니다. - 문서는 줄·문장 단위로 쪼개 검사합니다. `evidence[].segment` 는 그 문장의 순서, `start/end` 는 문서 본문 안 위치입니다. ### 정신건강(Mental health) - 감지된 축(`reasons[kind=axis]`)과 수준(`level`), 모델이 근거로 지목한 표현(`kind=indicator`)이 옵니다. - 이 필터는 **차단이 아니라 알림·안내** 용도입니다. 문서에서는 상담 기록·민원처럼 사람이 1인칭·구어체로 서술한 문장만 추려 검사하며, 해당 문장이 없으면 `decision: skipped` 로 건너뜁니다(과금 없음). ### 개인정보(PII) - 카테고리별 건수와 위치만 옵니다. **원문 텍스트는 싣지 않습니다**(`detected_spans[].text` 에 이미 있음). ## 4. 기록 조회 API 처리 시점 응답의 `explanation` 을 저장하는 것이 가장 풍부합니다. 저장하지 못했다면 아래로 코드 수준의 기록을 다시 볼 수 있습니다. 서버는 **본문·문장 발췌를 저장하지 않으므로** 기록에는 코드·점수·규칙 묶음만 있습니다. | 엔드포인트 | 설명 | |---|---| | `GET /v1/decisions/{request_id}` | 한 요청의 필터 기록 전체. 같은 조직의 다른 키로 처리한 요청도 조회 | | `GET /v1/doc/history` | 문서 처리 기록 — `summary.filters.files[]` 에 등급·라벨·위험 수준과 사유 코드(`why`) | | `GET /v1/{pii,dlp,moderation,mental_health}/history` | 필터별 기록. `summary.why` 에 사유 코드 | | `GET /v1/explain/catalog` | 모든 라벨의 정의·판단 기준·예시·판정 방식 (인증 불필요) | `summary.why` 는 압축 형식입니다. ```json {"d": "flagged", "r": [{"c": "CONFIDENTIAL", "b": "marking_rule"}, {"c": "STRATEGY", "b": "model", "s": 0.41, "t": 0.33}], "x": [{"c": "M_AND_A", "w": "not_target_type"}]} ``` `d`=decision · `r[]`=이유(`c` 코드, `b` basis, `s` score, `t` threshold, `l` level, `n` count, `x:0` 참고항목) · `x[]`=제외(`w` why_code). ## 5. 파트너 연동 권장 방식 1. 필터 호출 시 `explain` 을 끄지 말고, 응답의 **`explanation` 전체 JSON 과 `meta.request_id` 를 함께 저장**합니다. 2. 관리자 화면의 차단 목록에는 `summary_ko` 를 한 줄로, 상세 펼침에는 `reasons[].description_ko` 와 `evidence` 를 보여줍니다. 3. 오탐 문의가 들어오면 `basis`(규칙/모델)·`score`·`dismissed` 로 1차 판단하고, 저장된 `request_id` 로 코어핀에 문의합니다. 4. `evidence.text` 에는 고객 문서의 일부가 들어 있을 수 있습니다. 저장·열람 권한은 원문과 같은 수준으로 관리하세요. ## 6. 자주 묻는 질문 **마스킹까지 했는데 왜 업로드가 차단되나요?** 개인정보 마스킹과 문서보안 분류는 별개입니다. 문서보안은 문서의 *성격*(계약·재무·전략·고객 정보 등)과 *보안 표식*으로 등급·유형을 판단하므로, 개인정보를 가려도 같은 성격의 문서는 같은 등급·유형이 나옵니다. 마스킹 이후 본문을 분류한 경우 `notes_ko` 에도 같은 안내가 옵니다. 차단 사유는 `reasons` 에서 어떤 등급·유형이 몇 점으로 걸렸는지 확인하세요. **과하게 차단되는 느낌입니다.** ① `reasons[].score` 가 기준을 막 넘은 경계선 유형은 차단 대상에서 빼거나 검토로 돌리세요 ② 등급 신뢰도 `low` 는 자동 차단 대신 검토로 ③ 차단 유형을 업무상 꼭 필요한 것만 남기세요. 자세한 설정은 「필터 설정 가이드」를 보세요. **유해발화 라벨이 잡혔는데 어떤 문장인가요?** `reasons[].evidence[]` 의 `segment`·`start`·`end` 와 `text` 를 보세요. 규칙 판정이면 걸린 표현이, 모델 판정이면 그 문장의 발췌가 옵니다.