RT-O3: 멀티 레이블 카테고리 분류 + 라우팅

🎯 왜 멀티 레이블인가?Single-label → Multi-label classification

1월 RT-O1의 분류 모델은 STD / EXPRESS / COLD mutually-exclusive 단일 레이블로 다뤘다. 그러나 실제 주문에는 "냉장(COLD_CHAIN) + 당일(EXPRESS)" 같은 복합 조건이 존재하며, 이를 단일 레이블로 강제할 경우 모델은 한 쪽만 선택해 라우팅 실패를 일으킨다. 1월 분류 오류 27건 중 21건이 이 충돌 케이스였다.

RT-O3는 카테고리를 EXPRESS / COLD_CHAIN / FRAGILE / OVERSIZED / HAZMAT 5종의 non-exclusive 레이블 집합으로 재정의하고, LLM에는 JSON mode로 배열 응답을 강제한다. 응답은 Pydantic 2.x strict schema로 검증되어 enum을 벗어나거나 빈 배열일 경우 즉시 escalate 분기로 빠진다. 라우팅 단계에서는 가장 제약이 강한 카테고리를 우선하는 룰셋(HAZMAT > COLD+EXPRESS > …)을 적용해, 위험물·콜드체인 거점이 누락되지 않도록 보장한다.

🧭 멀티레이블 분류 + 라우팅 파이프라인5 stages · features → priority → DOM route
STEP 1
extract_features

주문 메타(SKU·weight·temp_req·flammable·dim)를 파싱해 분류기 입력 벡터로 정규화.

STEP 2
multi_label_predict

LLM(JSON mode) 또는 transformer multi-label 분류기 호출 → categories: string[] 응답.

STEP 3
schema_validate

Pydantic 2.x로 enum·길이·중복 검증. 누락/위반 시 escalate 분기.

STEP 4
priority_route

HAZMAT > COLD+EXPRESS > … 우선순위 매핑으로 특수 거점 선택.

STEP 5
dom_route

우선순위에 걸리지 않는 일반 케이스는 dom_score_engine로 fallback 라우팅 (RT-O1 모델 재사용).

🐍 멀티 레이블 분류 prompt (Python)
# category 필드를 단일 값에서 배열로 확장
# 기존: {"category": "EXPRESS"}
# 변경: {"categories": ["EXPRESS", "COLD_CHAIN"]}

classification_prompt = """
주문을 분석하여 해당하는 모든 카테고리를 JSON 배열로 반환하세요.
가능한 카테고리: EXPRESS, COLD_CHAIN, FRAGILE, OVERSIZED, HAZMAT
형식: {"categories": ["카테고리1", "카테고리2"]}
"""

class CategoryEnum(str, Enum):
    EXPRESS    = "EXPRESS"
    COLD_CHAIN = "COLD_CHAIN"
    FRAGILE    = "FRAGILE"
    OVERSIZED  = "OVERSIZED"
    HAZMAT     = "HAZMAT"

class ClassificationOut(BaseModel):
    categories: List[CategoryEnum] = Field(min_length=0, max_length=5)
🐍 멀티 레이블 라우팅 로직 (Python)
def route_order(order, categories):
    # 가장 제약이 강한 카테고리 우선 적용
    if "HAZMAT" in categories:
        return route_to_hazmat_certified_node(order)
    if "COLD_CHAIN" in categories and "EXPRESS" in categories:
        return route_to_cold_express_node(order)   # 특수 거점
    # 일반 DOM 라우팅
    return dom_score_engine.route(order, categories)
➗ 평가 지표 & 카테고리 가중치F1 macro · 복합 매칭율 · 5-class
F1_macro = (1 / |C|) · Σ F1(c) for c ∈ {EXPRESS, COLD, FRAGILE, OVERSIZED, HAZMAT}
composite_match = correct_combinations / total_multi_combos
# 단일 레이블 정확도 대신 per-class F1의 macro 평균 + 복합 카테고리 매칭율을 함께 본다
EXPRESS .25당일/익일 출하 트래픽 비중이 가장 큰 카테고리. SLA 분당 비용으로 가중치 최상위.
COLD_CHAIN .25온도 이탈 시 즉시 폐기 → EXPRESS와 동률의 위험 가중치.
FRAGILE .15파손 클레임 비용 기반. 포장 룰셋과 직결.
OVERSIZED .15치수 한계 초과 → 차량/거점 제약. AGV 통과 가능 거점 한정.
HAZMAT .20위험물 인증 거점만 가능 → 분류 누락 시 컴플라이언스 위반. 우선순위 1순위.

단일 레이블 시절 사용하던 accuracy는 다중 레이블에서 희석되므로(샘플당 0/1 평가가 부적절), per-class F1의 macro 평균 composite_match를 KPI로 사용한다. composite_match는 2개 이상 레이블이 동시에 붙은 케이스에서 모든 레이블을 정확히 맞춘 비율이다.

🔗 카테고리 동시 발생 행렬 (Co-occurrence Matrix)2월 1주~2주 샘플 · 정규화 100 기준
EXPCOLDFRAOVRHAZ
EXP100221892
COLD221001441
FRA1814100111
OVR94111000
HAZ2110100

EXPRESS×COLD_CHAIN(22)이 가장 빈번한 복합 조건이며, 이는 1월에 오류를 일으킨 바로 그 케이스다. RT-O3의 route_to_cold_express_node()분기는 이 조합 전용 거점(현재 NODE-D 1개) 으로 라우팅한다. HAZMAT은 타 카테고리와 거의 결합되지 않아 단독 처리 비중이 높다.

참고문헌
[1] 김지훈, "멀티 레이블 카테고리 분류 + 우선순위 라우팅", IntraLogis 사내 보고서, 2026.02
[2] Tsoumakas, G., Katakis, I. "Multi-Label Classification: An Overview", IJDWM, 2007
[3] Read, J., Pfahringer, B., Holmes, G., Frank, E. "Classifier Chains for Multi-label Classification", ECML-PKDD, 2009
[4] OpenAI, JSON Mode Documentation, 2024
[5] Pydantic, Pydantic 2.x Strict Schema Validation Docs, 2025
[6] 한국산업표준, KS C IEC 60079 위험물 분류 표준