LLM에게 추상적인 개념을 바로 분류시키면 같은 입력에도 판단이 흔들리고 오분류가 늘어난다. 구체적인 category는 모델이 고르게 하고, 그 category를 추상적인 상위 개념으로 올리는 일은 code가 deterministic하게 처리하면 오분류가 크게 줄어든다. 검색 의도(search intent)를 나누는 일에서 출발한 정리지만, LLM으로 무언가를 분류하는 거의 모든 작업에 같은 구조가 들어간다.
콘텐츠 계획은 검색 의도를 제대로 읽는 데서 시작한다. 분류가 틀리면 엉뚱한 글을 쓰게 되고, 그 앞에서 한 keyword research도 반쯤 버리게 된다. 문제는 “이 keyword를 인지·탐색·결정·실행 중 하나로 분류해줘” 같은 지시가 사람이 보기엔 간단해도 모델에게는 꽤 어려운 일이라는 점이다. 아래는 그 문제를 어떻게 쪼갰는지에 대한 메모다. 본문의 표는 패턴을 설명하려고 만든 예시이고, 특정 데이터셋에서 잰 정확도가 아니다.
직접 분류시키면 왜 틀리나
funnel stage 같은 framework를 그대로 모델에 넘기면 판단이 일정하지 않다. 자주 보이는 실패는 이런 모양이다.
| Keyword | 기대 분류 | 흔한 오분류 | 원인 |
|---|---|---|---|
| “X 서비스 가격” | 결정 | 탐색 | 가격 확인은 구매 직전 행동인데 “아직 알아보는 중”으로 읽음 |
| “X vs Y 비교” | 탐색 | 결정 | 비교는 탐색인데 “곧 결정할 사람”으로 읽음 |
| “X 설치 방법” | 실행 | 탐색 | 사용법 검색은 이미 결정한 뒤인데 “탐색 중”으로 읽음 |
| “X가 뭐야” | 인지 | 인지 | 개념 질문은 비교적 잘 맞힘 |
원인은 framework 쪽에 있다. funnel stage는 마케터가 정한 추상적인 틀이라, “탐색 단계”가 정확히 어디서 시작하고 끝나는지 사람마다 해석이 다르고 모델마다도 다르다. 기준이 애매한 걸 모델에게 넘기면 모델도 애매하게 답한다.
여기서 얻은 관찰은 단순하다. 모델은 구체적이고 관찰 가능한 것은 잘 판단하고, 추상적이고 해석이 필요한 것은 잘 못 판단한다.
- “이 keyword에 가격 관련 단어가 있나?” — 눈으로 확인되니 쉽다
- “이 keyword는 구매 여정의 어느 단계인가?” — 해석이 필요하니 어렵다
2단 구조로 쪼갠다
판단을 두 단계로 나눈다. 복잡한 작업을 module 단위로 분해하면 정확도가 오른다는 결과는 prompt 연구에서 반복해서 나온다 [1].
Step 1: 모델이 판단 (구체 category)
"가격" → qualify "vs" → compare "방법" → action
"뭐야" → concept "추천" → recommend "오류" → solve
→ 모델은 관찰 가능한 신호만 보면 된다
(deterministic code)
Step 2: code가 mapping (추상 상위 개념)
concept → 인지
recommend → 탐색
compare → 탐색
qualify → 결정
action → 실행
solve → 실행
모델에게는 쉬운 일(구체 category 고르기)만 주고, 어려운 일(추상 개념으로 올리기)은 code가 맡는다. prompt 하나로 끝내는 것보다 손이 더 가지만, 흔들리는 지점이 모델 쪽 한 군데로 줄어든다.
설계할 때 지킨 원칙 네 가지
1. 최상위 category는 서로 겹치지 않게
category 경계가 분명해야 한다. recommend와 compare는 개념상 가깝지만, “추천해줘” 같은 표현과 “vs” 같은 표현처럼 신호로 가를 수 있다. 신호로 가를 수 없는 두 category라면 하나로 합치는 게 낫다.
2. many-to-one mapping으로 오류를 무해하게
이 구조에서 가장 쓸모 있는 부분이다.
모델이 recommend와 compare를 헷갈렸다고 하자:
"X 추천해줘" → 모델: compare (오분류)
↓
code mapping: 탐색 ← 결과는 같다
"X 추천해줘" → 모델: recommend (정분류)
↓
code mapping: 탐색 ← 같은 결과
recommend와 compare가 둘 다 “탐색”으로 올라가니, 모델이 둘을 바꿔 말해도 최종 결과는 같다. 오분류를 전부 막을 수는 없다. 대신 자주 헷갈리는 category끼리 같은 상위 그룹에 묶어두면, 오분류가 나도 최종 결과에는 닿지 않는 구조가 된다. 어떤 쌍을 묶을지는 실제 confusion을 보고 정한다.
3. confidence가 낮으면 fallback
모델이 category를 고를 때 confidence가 낮으면 미리 정한 기본값으로 떨어지게 한다. 애매한 입력을 억지로 한 category에 밀어 넣는 것보다, 낮은 confidence를 표시하고 안전한 기본값으로 처리하는 쪽이 전체 시스템이 덜 흔들린다.
4. code mapping은 항상 deterministic하게
같은 입력에는 언제나 같은 출력이 나와야 한다. 모델의 nondeterminism을 code의 determinism으로 받쳐주는 게 이 구조의 요지라서, mapping 단계에 다시 모델을 끼워 넣으면 의미가 사라진다.
예시: 바꾸기 전과 후
Before (추상 개념을 바로 분류)
| Keyword | 모델 판단 | 정답 | 결과 |
|---|---|---|---|
| “X 요금제” | 탐색 | 결정 | 오분류 |
| “X 사용법” | 탐색 | 실행 | 오분류 |
| “X vs Y” | 결정 | 탐색 | 오분류 |
| “X란” | 인지 | 인지 | 정분류 |
After (구체 category → code mapping)
| Keyword | 모델 category | code mapping | 정답 | 결과 |
|---|---|---|---|---|
| “X 요금제” | qualify | 결정 | 결정 | 정분류 |
| “X 사용법” | action | 실행 | 실행 | 정분류 |
| “X vs Y” | compare | 탐색 | 탐색 | 정분류 |
| “X란” | concept | 인지 | 인지 | 정분류 |
모델이 고른 건 “요금제라는 단어가 있다”, “사용법을 묻는다” 같은 표면 신호뿐이다. 그 신호가 구매 여정의 어디에 해당하는지는 사람이 한 번 정해서 code에 박아뒀다.
검색 의도 말고도 쓰이는 곳
이 패턴은 search intent 전용이 아니다. LLM으로 무언가를 category로 나눠야 하는 일이면 대부분 같은 구조가 들어간다.
콘텐츠 주제 tagging: “이 글의 대주제가 뭐냐”를 바로 물으면 모델마다 답이 갈린다. 대신 “가격 언급이 있나”, “비교 표가 있나”, “단계별 절차가 있나” 같은 관찰 가능한 특징을 먼저 묻고, 그 조합으로 주제를 code가 정하면 훨씬 안정적이다.
문의·ticket 분류: 고객 문의를 “긴급·일반·낮음” 같은 추상 우선순위로 바로 나누면 기준이 흔들린다. “환불 언급이 있나”, “서비스 중단 언급이 있나” 같은 구체 신호를 먼저 뽑고, 신호 조합에 따라 우선순위를 code가 정하면 일관성이 올라간다.
경쟁사 콘텐츠 분류: “공격적·방어적” 같은 톤을 바로 묻기보다, “가격을 직접 언급하나”, “특정 브랜드명을 언급하나” 같은 신호를 먼저 잡고 상위 톤은 code로 mapping하면 재현성이 높아진다.
세 경우 모두 구조는 같다. 사람이 봐도 애매한 추상 판단을 모델에게 그대로 넘기지 않고, 그 판단을 이루는 관찰 가능한 조각으로 쪼갠 다음 조합은 code에 맡긴다.
분류가 쏠리면 콘텐츠도 쏠린다
분류가 특정 category, 보통 개념 설명형으로 쏠리면 인지 단계 글만 잔뜩 쓰게 된다. 그러면 탐색·결정·실행 단계 질문에서 generative engine에 인용될 기회를 놓친다 [3]. category를 정확히 나눠두면 어느 단계 글이 모자란지 숫자로 보이고, 그 빈 곳부터 채울 수 있다. 글이 검색 의도와 맞아야 한다는 검색 엔진 쪽 원칙과도 같은 방향이다 [2].
3단 이상은 언제 필요한가
대부분은 2단(구체 category → 추상 상위 개념)으로 충분했다. category가 아주 많거나 도메인이 복잡하면 “구체 신호 → 중간 category → 최종 funnel stage”처럼 한 단을 더 끼울 수 있다.
다만 단을 늘리면 비용도 같이 는다. mapping table이 두 개가 되면 관리할 곳이 두 군데가 되고, 한 단의 오류가 다음 단으로 쌓일 위험도 커진다. 복잡한 추론을 작은 하위 문제로 쪼개는 것 자체는 효과가 있지만 [1], 쪼갤수록 각 단의 정확도를 따로 챙겨야 하는 부담이 생긴다.
그래서 3단을 고민하기 전에 먼저 “2단으로 돌렸을 때 실제로 어떤 오분류가 남나”를 본다. 남은 오분류가 몇몇 category에 몰려 있으면, 전체를 3단으로 늘리기보다 그 category 하나만 쪼개는 편이 대체로 싸게 먹힌다.
내가 분류기를 짤 때 챙기는 것
- category는 6개 안팎에서 시작한다. 너무 적으면 구분이 뭉개지고, 너무 많으면 모델이 경계를 헷갈린다. 시작점일 뿐이고 실제 오분류 패턴을 보면서 조정한다.
- 모델에게는 관찰 가능한 판단만 묻는다. “구매 여정의 어느 단계인가”는 전문가끼리도 의견이 갈린다. “가격 관련 단어가 있나” 수준의 질문으로 내린다.
- many-to-one mapping은 오류를 0으로 만드는 장치가 아니다. 오류가 나도 결과에 닿지 않게 만드는 장치다. 그래서 어떤 쌍을 묶을지는 confusion을 보고 정한다.
- mapping table은 한 곳에만 둔다. 같은 mapping이 여러 파일에 흩어지면 언젠가 어긋난다.
- 정답 set을 따로 관리한다. 실제 keyword로 정답 label set을 만들고, prompt나 mapping을 바꿀 때마다 정확도를 다시 잰다. 분류는 콘텐츠 pipeline의 상류에 있어서 여기서 틀리면 뒤 단계가 전부 틀린 전제로 돌아간다.
신호가 명확한 입력은 정규식이나 keyword matching만으로도 상당 부분 걸러진다. 그런 건 code로 먼저 처리하고, 애매한 나머지에만 모델을 부르면 비용도 준다. mapping 규칙을 prompt에 다 적어 넣는 방법도 있지만, mapping이 복잡해질수록 모델이 그 규칙까지 일관되게 지키길 기대하기는 어렵다.
결국 이 구조는 모델이 잘하는 일과 못하는 일을 가르는 문제다. 모든 판단을 LLM에 넘기고 싶은 마음을 참고, 쉬운 판단만 맡긴 뒤 나머지를 deterministic code로 처리하면 production에서 덜 흔들리는 분류기가 된다.



