KSCOLD LogoKSCOLD
Blog
Feed
Product
Admin Night
Vault
Guestbook
Info

Vault 목록

Intent Classification

연결된 노트와 함께 읽기

Intent Classification

2026년 7월 16일

Intent Classification

Intent Classification은 사용자의 자연어 입력을 사전 정의된 의도 카테고리로 분류해, 어떤 에이전트, workflow, tool, graph node를 실행할지 정하는 단계이다.

챗봇/IVR 시절부터 있던 개념이지만, LLM Agent에서는 단순 텍스트 분류를 넘어 실행 경로 라우팅으로 이어진다. 특히 LangGraph StateGraph에서는 intent가 곧 다음 edge 선택 조건이 된다.

왜 필요한가

  • 한 에이전트가 모든 요청을 처리하면 system prompt가 비대해지고 정확도가 떨어진다.
  • 미리 분류해서 specialist, tool, sub-workflow로 보내면 각 경로가 자기 역할에 집중할 수 있다.
  • 불명확한 요청은 바로 실행하지 않고 clarify/fallback으로 보낼 수 있다.

분류 기법

Mermaid스크롤로 확대 · 드래그로 이동

1. Rule-based Classification

규칙 기반 분류는 명확한 명령어, slash command, 고정 키워드에 적합하다.

Python

장점은 빠르고 싸다는 점이다. 단점은 표현이 조금만 바뀌어도 놓칠 수 있다는 점이다.

2. Embedding KNN Classification

각 intent별 대표 문장을 embedding해두고, 사용자 입력과 가장 가까운 예시를 찾는 방식이다.

Python

동의어와 표현 변형에 강하지만, intent 경계가 가까우면 오분류가 생긴다.

3. LLM Classification

모호한 자연어는 LLM에게 구조화된 출력으로 분류시킨다.

Python

LLM 분류는 가장 유연하지만 느리고 비용이 든다. 그래서 보통 rule → embedding → LLM 순서의 cascade로 둔다.

LangGraph에서의 Intent Classification

LangGraph에서는 intent classification이 단순 라벨링으로 끝나지 않는다. 분류 결과가 State에 저장되고, add_conditional_edges의 router가 그 state를 읽어 다음 node를 고른다.

Mermaid스크롤로 확대 · 드래그로 이동

핵심은 LLM 호출은 node에서 하고, edge router는 state를 읽는 가벼운 함수로 유지하는 것이다.

Python

LangGraph 라우팅 패턴

패턴설명적합한 경우
classifier node + conditional edge분류 node가 state에 intent를 쓰고 router가 다음 node 선택가장 기본적인 intent routing
hierarchical routing큰 intent를 먼저 고르고, 하위 node에서 세부 intent 재분류intent가 많을 때
multi-intent fan-out하나의 요청을 여러 intent로 나눠 여러 node를 실행"요약하고 차트도 그려줘"
tool-call routingLLM의 tool call 여부를 보고 LangGraph ToolNode로 보냄ReAct agent, tool agent
fallback routingconfidence가 낮으면 clarify/default node로 보냄오분류 비용이 클 때

Multi-intent와 Conditional Fan-out

한 문장에 여러 의도가 있으면 단일 intent 라벨보다 list 형태가 낫다.

Python

LangGraph에서는 router가 여러 실행 대상을 반환하는 LangGraph Conditional Fan-out 패턴으로 연결할 수 있다. 이때 각 node가 같은 state key를 동시에 갱신한다면 LangGraph State Reducer가 필요하다.

Confidence와 Fallback

낮은 confidence를 그대로 실행하면 잘못된 tool이나 workflow가 호출된다.

일반적인 fallback:

  • clarify: 사용자에게 되묻기
  • default agent: 넓은 범위의 일반 답변 경로로 보내기
  • human handoff: 사람 검토로 넘기기
  • END: 실행하지 않고 종료하기

LangGraph에서는 이런 fallback도 그냥 node다. 즉, fallback은 예외 처리가 아니라 graph 설계의 일부가 된다.

운영 팁

  • intent label은 너무 많게 시작하지 않는다. 5~15개 정도에서 출발하고 필요하면 계층화한다.
  • unknown 비율을 모니터링하면 새 intent 추가 시점을 알 수 있다.
  • intent 분류 데이터셋을 버전 관리하고 회귀 평가한다.
  • LLM classifier 출력은 Structured Output과 Pydantic으로 고정한다.
  • graph route 이름과 intent label을 1:1로 묶을지, mapping table을 둘지 명확히 정한다.

관련

  • Supervisor 패턴 — 분류 결과로 전문 에이전트 라우팅.
  • Routing Workflow · Agent vs Workflow — Workflow 패턴 중 하나.
  • LangGraph Edge · LangGraph Conditional Fan-out — LangGraph에서 분류 결과를 실행 경로로 바꾸는 방법.
  • Structured Output · Pydantic — 신뢰성 있는 분류 출력.

Synapse Map Insight

def rule_classify(text: str) -> str | None: t = text.strip().lower() if t.startswith("/help"): return "help" if "환불" in t: return "refund" return None
intents = { "refund": ["환불해줘", "돈 돌려줘", "결제 취소하고 싶어"], "shipping": ["배송 언제 와", "택배 어디 있어", "주소 바꿔줘"], } def embed_classify(text: str, threshold=0.75) -> tuple[str, float] | None: vector = embed(text) hit = vector_store.search(vector, k=1) if hit.score < threshold: return None return hit.intent, hit.score
from pydantic import BaseModel from typing import Literal class IntentResult(BaseModel): intent: Literal["refund", "shipping", "general", "unknown"] confidence: float reason: str
from typing import Literal, TypedDict class AgentState(TypedDict): user_input: str intent: Literal["qa", "tool", "clarify"] confidence: float def classify_intent(state: AgentState) -> dict: result = classifier.invoke(state["user_input"]) return { "intent": result.intent, "confidence": result.confidence, } def route_by_intent(state: AgentState) -> str: if state["confidence"] < 0.6: return "clarify" return state["intent"] builder.add_node("classify", classify_intent) builder.add_conditional_edges( "classify", route_by_intent, { "qa": "general_qa", "tool": "tool_workflow", "clarify": "clarify", }, )
class IntentResult(BaseModel): intents: list[Literal["summarize", "visualize", "search"]]