챕터 5

스코어링·추출·안전 점검 — 구조화 출력이 필요한 작업들

분류와 라우팅 말고도 결정모델이 잘 맞는 자리가 있습니다. 순서형 척도로 값을 매기는 스코어링, 정해진 필드로 값을 판정하는 추출·트리아지, 그리고 LLM 생성물 앞뒤에 세우는 안전 게이트입니다. 이 장을 마치면 환불 정책 적합성 평가나 탈옥 탐지 같은 작업을 Score와 Noul로 짜고, 신뢰도 임계값으로 자동·보류·사람 처리를 가를 수 있습니다.

라우팅 다음에 오는 세 가지 일

4장에서 요청 복잡도를 Score로 채점해 저비용·고성능 LLM으로 갈라 보내고, 의도를 Choice로 나눠 핸들러를 고르고, 도구 실행 전 Noul로 위험을 거르는 라우팅 패턴을 봤습니다. 라우팅은 결국 '분류 결과로 다음 코드 경로를 고르는' 일이었습니다.

그런데 결정모델이 하는 일이 경로 선택만은 아닙니다. TypeSafe의 공식 작업 유형 지도에는 분류·라우팅 외에도 Scoring(순서 있는 척도 위의 값), Feature Extraction(다운스트림 처리를 위한 확률적 신호 추출), Verification(특정 실패 모드에 대한 산출물 검증)이 따로 올라 있습니다. 이번 장은 그중 실무에서 가장 자주 쓰이는 세 축 — 스코어링, 추출·트리아지, 안전 점검을 다룹니다.

왜 이 셋을 묶어 보나

세 작업의 공통점은 결과가 사람이 읽을 문장이 아니라 코드가 곧바로 소비할 값이라는 점입니다. 우선순위 점수, 환불 가능 여부, 위험 플래그. 전부 스키마 안에서 떨어지는 값이라 Jev (TypeSafe AI)의 Score와 Noul이 그대로 들어맞습니다. 라우팅이 분류의 '응용'이었다면, 이번 장의 작업들은 분류의 '이웃'입니다.

스코어링 — 순서형 척도로 값을 매기기

스코어링은 답이 순서가 있는 척도 위에 있을 때 씁니다. '낮음·보통·높음'처럼 등급 사이에 순서가 있으면 Choice가 아니라 Score가 맞습니다.

Score 스키마: 레벨 배열과 확률가중평균

Score의 criteria는 숫자 min/max가 아니라 낮음에서 높음 순으로 정렬된 레벨 설명의 배열입니다. 레벨은 2~10개까지 정의합니다. 응답으로 오는 score는 단일 정수가 아니라 레벨 인덱스의 확률가중평균이라, 레벨 사이의 연속값도 나옵니다. 예를 들어 3레벨(0·1·2)로 정의하면 1.4 같은 값이 돌아올 수 있습니다.

함께 오는 필드는 probabilities(각 레벨 확률), confidence, 그리고 인덱스를 설명으로 매핑한 legend입니다. 문서는 "confidence 1.0은 모든 확률이 한 레벨에 몰렸다는 뜻"이라고 설명합니다.

Score 레벨 2~10개 · 응답은 레벨 인덱스의 확률가중평균 + probabilities + confidence + legend (docs.typesafe.ai)

지원 티켓 우선순위와 계정 리스크

Cloudflare Workers AI의 Jev 모델 페이지는 지원 티켓 처리, 환불 요청 정책 대조, 지원 부서 라우팅, 계정 리스크 평가·에스컬레이션 판정을 공식 사용 사례로 명시합니다. 문서의 지원 티켓 예시는 세 프리미티브를 한 요청에 섞습니다.

질문 타입 하는 일 예시 응답
담당 팀 Choice billing/technical/sales 중 분류 technical 85%
고객 짜증도 Score 0~2 순서형 척도 frustration 1.0
긴급성 Noul 긴급 여부 이진 판정 urgency 1.0

이렇게 나온 짜증도·긴급성 점수를 코드에서 조합해 큐 우선순위를 매기는 식입니다. 계정 리스크도 같은 틀입니다. 거래 내역·경보 이력 같은 근거를 state에 넣고 리스크를 Score로 평가한 뒤, 임계값을 넘으면 에스컬레이션으로 보냅니다.

스코어가 못 하는 일

여기서 한 번 짚고 갈 지점이 있습니다. TypeSafe는 jev-1.13 기준으로 모델의 약점('jaggedness')을 스스로 공개했는데, 스코어링과 직접 관련된 항목이 있습니다. "계산기가 아니다 — 수학 로직은 코드로 구현하라. 신뢰성 있게 개수를 세지 못한다", "두 값이 서로 가까운지 신뢰성 있게 판단하지 못한다", "날짜를 순서 있는 양이 아니라 텍스트로 읽는다"는 것입니다. 정확한 산술·카운팅·날짜 비교는 모델이 아니라 코드가 맡고, Score는 어디까지나 의미적 등급 판단에만 쓰는 편이 안전합니다.

추출·트리아지 — 정해진 필드로 값을 판정

추출·트리아지는 자유 텍스트에서 미리 정한 필드의 값을 판정해 채우는 일입니다. 2장에서 '추출'이 산출물 모양에 따라 갈린다고 정리했는데, 정해진 필드로 값을 판정하는 쪽이 바로 결정모델의 자리입니다.

환불 정책 적합성: state에 근거를 담는다

가장 명료한 공식 예시는 환불 정책 적합성 평가입니다. 판단에 필요한 근거를 전부 state에 담는 것이 핵심입니다.

{
  "ticket": { "subject": "Duplicate charge", "messages": ["..."] },
  "order": { "id": "A-104", "charges": [
    { "amount_usd": 49, "status": "captured" },
    { "amount_usd": 49, "status": "captured" }
  ] },
  "refund_policy": "Duplicate charges are eligible for a refund."
}

(문서 형태를 참고한 개념 예시입니다.) 같은 청구가 두 번 잡힌 주문과 "중복 청구는 환불 대상"이라는 정책 문장을 함께 넣으면, 모델은 정책과 사실을 대조해 환불 적합 여부를 판정합니다. 제어 흐름은 코드가 쥐고, 의미적 대조만 모델이 맡는 구조입니다.

fan-out으로 미리 병렬 질문

TypeSafe의 fan-out 패턴은 아직 환불 요청 여부가 확정되지 않은 티켓에도 다른 질문과 함께 refund_requested 같은 Noul을 미리 병렬로 물어 둡니다. 그 결과를 임계값으로 갈라 라우팅합니다.

# docs.typesafe.ai 예시를 옮긴 개념 코드
if refund.noul > 0.7:
    route_to_billing_with_flag(ticket_id, refund_likely=True)
else:
    route_to_billing(ticket_id)

질문을 여러 개 얹어도 응답 시간은 거의 그대로이고 추가 질문의 토큰 비용만 드는 구조라, 트리아지 단계에서 필요한 필드를 한 번에 뽑는 편이 효율적입니다.

이메일 트리아지는 어떻게 짜나

받은 메일을 '긴급/일반/스팸' 같은 큐로 나누고 담당·의도를 붙이는 이메일 트리아지도 같은 원리로 짤 수 있습니다. 다만 이 구체적 워크플로에 대한 TypeSafe·Cloudflare·LangChain의 공식 예시 문서는 확인하지 못했으니, 위 지원 티켓·환불 예시를 이메일 필드(발신자 의도·긴급성·카테고리)로 옮긴 응용으로 이해하시는 편이 정확합니다.

안전 점검 — 생성 앞뒤에 가드를 세우기

세 번째 축은 안전입니다. LLM이 무엇을 받아 무엇을 내놓는지, 그 앞뒤에 값싼 결정모델을 게이트로 세우는 패턴입니다.

Noul 가드레일 배터리

TypeSafe의 가드레일 쿡북은 위험 유형별로 Noul을 하나씩 정의한 '배터리'를 씁니다. jailbreak(탈옥 시도), harmful_request(유해 요청), medical_advice(의료 조언), self_harm 같은 항목을 각각 criteria.true/criteria.false로 정의합니다. Noul은 별도 confidence 필드 없이 0~1 확률 자체가 답이라, 그 값을 그대로 임계값과 비교합니다. 위해의 정도는 Score(0~3: No harm → Mild → Serious → Severe)로 따로 잽니다.

여기서 '탈옥 탐지'를 과장 없이 봐야 합니다. 이 배터리는 위험을 완벽히 막는 방패가 아니라, 확률적 신호를 코드가 쓸 수 있게 뽑아 주는 장치입니다. 최종 차단·통과 결정은 아래의 임계값 정책이 내립니다.

route(): 임계값으로 Pass/Review/Block

쿡북의 route() 함수는 Noul 배터리와 Score 결과를 임계값 세 개로 갈라 네 가지 결과를 냅니다.

임계값 기본값 역할
review_threshold 0.35 넘으면 사람 검토(Review)로
action_threshold 0.70~0.85 넘으면 차단(Block) 조치
severity_block 2.0 위해 Score가 넘으면 차단

결과는 Pass(통과)·Review(검토)·Block(차단)·Support(상담 연결)로 나뉩니다. strict/permissive 같은 정책 프로파일에 따라 같은 입력도 다른 임계값으로 판정됩니다. 문서가 강조하듯 "정확한 임계값은 도메인과 모델 성능에 의존"하니, 보수적으로 시작해 운영 데이터로 조정하는 것이 정석입니다.

입력과 출력 양쪽에 두 번

핵심은 같은 질문 배터리를 두 번 쓴다는 점입니다. 사용자 메시지가 들어올 때 LLM·도구 호출 전에 한 번 걸러 위험 요청을 막고, LLM 응답을 사용자에게 돌려주기 전에 다시 한 번 검증합니다. LangChain의 AutoModeMiddleware도 같은 발상으로, 도구 실행 직전에 Jev로 위험도를 평가해 위험하면 실행 대신 에러 메시지를 반환합니다. 전통적으로 풀 LLM 판사가 필요했을 안전 판단을 Choice·Score·Noul만으로 대체하는 사례입니다.

신뢰도로 자동·보류·사람을 가르기

스코어링이든 안전 점검이든, 마지막에 남는 질문은 하나입니다. 이 판정을 자동으로 실행할지, 사람에게 넘길지입니다.

세 단계 밴드

TypeSafe 문서는 신뢰도를 세 구간으로 나눠 운영하라고 권합니다. "지능형 시스템이 정직한 불확실성을 표현할 수 없다면 신뢰할 수 없다"는 것이 전제입니다.

신뢰도 처리
높음 자동 실행
중간 확인 요청 · 검토 플래그 · 추가 정보 수집
낮음 실행하지 않고 사람에게 라우팅하거나 다른 시스템으로 폴백

주의할 점은 임계값이 하나의 고정 숫자가 아니라는 것입니다. 같은 시스템 안에서도 결과의 중대성에 따라 액션별로 달라야 합니다. 기본 0.5 바닥을 두되, 파괴적·비가역적 작업은 0.9 위로 더 높게 잡는 식입니다.

calibrated의 진짜 의미

이 밴드가 성립하려면 신뢰도가 실제 정확도와 맞아야 합니다. TypeSafe가 말하는 '보정(calibrated)'의 정의는 이렇습니다. "0.2로 예측된 사건은 약 20% 발생해야 한다. 보정은 예측 그룹 단위로 측정되며 개별 답의 정답을 보장하지 않는다." 90%라고 나온 판정 하나하나가 옳다는 뜻이 아니라, 90%짜리 판정을 모아 놓으면 그중 약 90%가 맞는다는 통계적 성질입니다. 이 보정 품질에 대한 제3자 독립 검증치는 아직 확인되지 않았으니, 임계값은 자기 도메인 데이터로 직접 튜닝하는 것이 전제입니다.

정리

분류·라우팅 바깥의 결정모델 작업은 스코어링(순서형 척도로 등급 매기기), 추출·트리아지(정해진 필드로 값 판정), 안전 점검(생성 앞뒤의 게이트)으로 정리됩니다. 셋 모두 Score와 Noul로 짜고, 신뢰도 밴드로 자동·보류·사람 처리를 가른다는 뼈대는 같습니다. 산술·날짜 비교는 코드에 맡기고 임계값은 도메인 데이터로 조정한다는 원칙만 지키면 됩니다.

다음 장에서는 비용·지연 설계를 다룹니다. 결정모델을 앞단에 세워 값싸게 걸러 내고 꼭 필요한 요청만 LLM으로 넘기는 하이브리드 구조를, 70~500ms 지연과 100만 토큰당 $0.042 같은 실제 수치로 계산해 보겠습니다.