모더레이션(콘텐츠 검열)

모더레이션(콘텐츠 검열)

OpenAI 모더레이션 모델은 텍스트와 이미지에서 유해 콘텐츠를 감지하는 데 쓰여요. 별도 입력을 모더레이션 엔드포인트로 분류하거나, 생성 응답과 함께 모더레이션 점수를 요청할 수 있죠. 그 결과로 콘텐츠 필터링, 요청 검토 라우팅, 플래그된 콘텐츠를 제출한 계정에 대한 개입처럼 애플리케이션 정책을 집행할 수 있어요. omni-moderation-latest 모델은 텍스트와 이미지 입력을 받지만 오디오는 분류하지 않고, 모더레이션 엔드포인트는 무료이며 이미지 파일은 최대 20MB까지 지원해요.

출처: 공식문서

모더레이션 워크플로 고르기

어떤 상황에서 어떤 워크플로를 쓸지는 이렇게 나뉩니다.

워크플로 언제 쓰는지
생성 콘텐츠 모더레이션 Responses API나 Chat Completions API로 텍스트를 생성하며 모더레이션 신호가 필요할 때
독립 입력 분류 모델 응답 생성 없이 텍스트·이미지 분류가 필요할 때
모더레이션 결과 이해 플래그·카테고리·점수 또는 적용된 입력 타입을 해석해야 할 때
지원 카테고리 확인 어떤 유해 카테고리가 텍스트·이미지 또는 둘 다에 적용되는지 알아야 할 때

생성 콘텐츠 모더레이션

생성 텍스트와 모더레이션 점수가 함께 필요하다면, 생성 요청에 최상위 moderation 객체를 넣으면 돼요. 그러면 별도의 모더레이션 요청 없이도 모델 입력과 생성 출력에 대한 모더레이션 점수가 함께 반환됩니다. 모델은 여전히 정상적으로 생성되고, 출력을 사용자에게 보여주거나 다운스트림 작업을 하기 전에 모더레이션 결과를 검토하면 됩니다.

생성 요청을 만들 때 moderation.model을 설정해요:

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-6-astra",
    input=[
        {
            "role": "user",
            "content": (
                "A user asks for instructions to make a harmful weapon. "
                "Draft a brief refusal and offer a safer alternative."
            ),
        }
    ],
    moderation={"model": "omni-moderation-latest"},
)

input_moderation = response.moderation.input
output_moderation = response.moderation.output
if input_moderation.type == "error":
    raise RuntimeError(input_moderation.message)
if output_moderation.type == "error":
    raise RuntimeError(output_moderation.message)

print(input_moderation.flagged)
print(output_moderation.flagged)
import OpenAI from "openai";

const client = new OpenAI();

const response = await client.responses.create({
  model: "gpt-6-astra",
  input: [
    {
      role: "user",
      content:
        "A user asks for instructions to make a harmful weapon. Draft a brief refusal and offer a safer alternative.",
    },
  ],
  moderation: { model: "omni-moderation-latest" },
});

const inputModeration = response.moderation.input;
const outputModeration = response.moderation.output;
if (inputModeration.type === "error") {
  throw new Error(inputModeration.message);
}
if (outputModeration.type === "error") {
  throw new Error(outputModeration.message);
}

console.log(inputModeration.flagged);
console.log(outputModeration.flagged);

Responses API는 response.moderation.input에 입력용 moderation_result 객체를, response.moderation.output에 출력용 객체를 반환해요. 인라인 모더레이션 결과는 독립 모더레이션 결과와 같은 카테고리 필드를 사용해요. 첫 판단은 flagged로 시작하고, 로깅·라우팅·감사 추적·수동 검토 큐에는 categoriescategory_scores를 살펴보세요. 거부나 다른 안전 인지 응답도 유해 콘텐츠를 논의하면 플래그가 붙을 수 있어요. 모더레이션 점수는 자동 차단 결정이 아니라 애플리케이션 정책의 신호로 취급해야 해요.

모더레이션 실패를 처리해야 한다면 점수를 읽기 전에 결과 타입부터 확인하세요. 모더레이션 단계가 완료되지 못하면 해당 입력·출력 모더레이션 필드에 점수 대신 오류가 들어갈 수 있어요.

도구 호출 요청에서 모더레이션은 대화 콘텐츠에 나타나는 도구 호출 인자와 도구 출력을 포함하지만, 도구 이름·설명·스키마·응답 형식 스키마는 포함하지 않아요. 생성 응답을 스트리밍하면 모더레이션 점수는 전체 생성 출력이 나온 뒤 도착하며, 부분 출력 델타에는 포함되지 않습니다.

독립 입력 분류

모델 응답 생성 없이 텍스트·이미지를 분류하려면 모더레이션 엔드포인트를 쓰세요. 아래는 OpenAI 라이브러리와 omni-moderation-latest 모델을 쓰는 예시입니다.

텍스트 입력 분류:

from openai import OpenAI

client = OpenAI()

response = client.moderations.create(
    model="omni-moderation-latest",
    input="...text to classify goes here...",
)

print(response)
curl https://api.openai.com/v1/moderations \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
    "model": "omni-moderation-latest",
    "input": "...text to classify goes here..."
  }'

이미지와 텍스트 모더레이션:

from openai import OpenAI

client = OpenAI()

response = client.moderations.create(
    model="omni-moderation-latest",
    input=[
        {"type": "text", "text": "...text to classify goes here..."},
        {
            "type": "image_url",
            "image_url": {
                "url": "https://example.com/image.png",
                # You can also use a Base64 encoded image URL.
                # "url": "data:image/jpeg;base64,abcdefg..."
            },
        },
    ],
)

print(response)
import OpenAI from "openai";
const openai = new OpenAI();

const moderation = await openai.moderations.create({
  model: "omni-moderation-latest",
  input: [
    { type: "text", text: "...text to classify goes here..." },
    {
      type: "image_url",
      image_url: {
        url: "https://example.com/image.png",
        // You can also use a Base64 encoded image URL.
        // url: "data:image/jpeg;base64,abcdefg...",
      },
    },
  ],
});

console.log(moderation);

모더레이션 결과 이해

전쟁 영화의 단일 프레임에서 가져온 이미지에 대한 전체 예시 출력을 살펴볼게요. 모델은 이미지에서 폭력의 징후를 감지하고, violence 카테고리 점수가 0.8보다 크다고 판단합니다.

{
  "id": "modr-970d409ef3bef3b70c73d8232df86e7d",
  "model": "omni-moderation-latest",
  "results": [
    {
      "flagged": true,
      "categories": {
        "sexual": false,
        "sexual/minors": false,
        "harassment": false,
        "harassment/threatening": false,
        "hate": false,
        "hate/threatening": false,
        "illicit": false,
        "illicit/violent": false,
        "self-harm": false,
        "self-harm/intent": false,
        "self-harm/instructions": false,
        "violence": true,
        "violence/graphic": false
      },
      "category_scores": {
        "sexual": 2.34135824776394e-7,
        "sexual/minors": 1.6346470245419304e-7,
        "harassment": 0.0011643905680426018,
        "harassment/threatening": 0.0022121340080906377,
        "hate": 3.1999824407395835e-7,
        "hate/threatening": 2.4923252458203563e-7,
        "illicit": 0.0005227032493135171,
        "illicit/violent": 3.682979260160596e-7,
        "self-harm": 0.0011175734280627694,
        "self-harm/intent": 0.0006264858507989037,
        "self-harm/instructions": 7.368592981140821e-8,
        "violence": 0.8599265510337075,
        "violence/graphic": 0.37701736389561064
      },
      "category_applied_input_types": {
        "sexual": ["image"],
        "sexual/minors": [],
        "harassment": [],
        "harassment/threatening": [],
        "hate": [],
        "hate/threatening": [],
        "illicit": [],
        "illicit/violent": [],
        "self-harm": ["image"],
        "self-harm/intent": ["image"],
        "self-harm/instructions": ["image"],
        "violence": ["image"],
        "violence/graphic": ["image"]
      }
    }
  ]
}

JSON 응답에는 입력에 어떤 카테고리가 존재하는지, 그리고 모델이 각 카테고리에 얼마나 확신하는지를 나타내는 필드가 담겨요.

출력 카테고리 설명
flagged 모델이 콘텐츠를 잠재적으로 유해하다고 분류하면 true, 아니면 false
categories 카테고리별 위반 플래그 딕셔너리. 각 카테고리에 대해 위반으로 플래그되면 true, 아니면 false
category_scores 카테고리별 점수 딕셔너리. 각 점수는 입력에 해당 카테고리 콘텐츠가 있다는 모델의 확신으로, 0과 1 사이(클수록 높은 확신)
category_applied_input_types 점수가 적용되는 입력 타입. 예를 들어 violence/graphic이 이미지·텍스트 모두에 적용되면 ["image", "text"]

OpenAI는 모더레이션 엔드포인트의 기반 모델을 계속 업그레이드할 계획이에요. 따라서 category_scores에 의존하는 맞춤 정책은 시간이 지나면서 재보정이 필요할 수 있습니다.

지원 카테고리 확인

아래 표는 모더레이션 엔드포인트가 감지할 수 있는 콘텐츠 카테고리와 각 카테고리가 지원하는 입력 타입을 정리한 거예요. "텍스트 전용"으로 표시된 카테고리는 이미지 입력을 지원하지 않아요. 텍스트 없이 이미지만 omni-moderation-latest 모델로 보내면 이런 지원되지 않는 카테고리의 점수는 0이 반환되고, 이미지 파일은 20MB로 제한됩니다.

카테고리 설명 입력
harassment 어떤 대상에 대한 괴롭히는 언어를 표현·선동·조장하는 콘텐츠 텍스트 전용
harassment/threatening 어떤 대상에 대한 폭력이나 심각한 해를 포함하는 괴롭힘 콘텐츠 텍스트 전용
hate 인종·성별·민족·종교·국적·성적 지향·장애·카스트에 근거한 증오를 표현·선동·조장하는 콘텐츠. 보호 대상이 아닌 집단(예: 체스 선수)을 겨냥한 증오는 괴롭힘으로 분류됨 텍스트 전용
hate/threatening 인종·성별·민족·종교·국적·성적 지향·장애·카스트에 근거해 대상 집단을 향한 폭력·심각한 해를 포함하는 증오 콘텐츠 텍스트 전용
illicit 불법 행위를 저지르는 방법에 대한 조언·지시를 주는 콘텐츠. "how to shoplift(좀도둑질하는 법)" 같은 표현이 해당 텍스트 전용
illicit/violent illicit 카테고리가 플래그하는 것과 같은 콘텐츠지만 폭력이나 무기 조달 언급을 포함 텍스트 전용
self-harm 자살·자해·섭식 장애 같은 자해 행위를 조장·권장·묘사하는 콘텐츠 텍스트와 이미지
self-harm/intent 화자가 자살·자해·섭식 장애 같은 자해 행위를 하고 있거나 할 의도라고 표현하는 콘텐츠 텍스트와 이미지
self-harm/instructions 자살·자해·섭식 장애 같은 자해 행위를 격려하거나 그 방법에 대한 지시·조언을 주는 콘텐츠 텍스트와 이미지
sexual 성적 흥분을 유발하려는 콘텐츠(성행위 묘사 등)나 성적 서비스 홍보(성교육·웰빙 제외) 텍스트와 이미지
sexual/minors 18세 미만 개인이 포함된 성적 콘텐츠 텍스트 전용
violence 죽음·폭력·신체적 상해를 묘사하는 콘텐츠 텍스트와 이미지
violence/graphic 죽음·폭력·신체적 상해를 그래픽하게 상세히 묘사하는 콘텐츠 텍스트와 이미지

더 알아보기 (Learn more)