
체이닝(Chaining)이 "하나씩 순서대로" 였다면, 라우팅(Routing)은 "입력 종류에 따라 다른 길로" 보내는 패턴입니다. 🛤️
오늘 글의 핵심 질문은 이거예요. "같은 챗봇 / 같은 도구라도, 사용자 요청 유형이 너무 달라서 하나의 프롬프트로 다 잘 처리할 수 없을 때 어떻게 할까?" 답은 의외로 단순합니다 — 입력을 먼저 분류한 다음, 그에 맞는 전문 파이프라인으로 보낸다. 우리가 평소에 병원 접수처에서 진료과별로 흩어지는 것과 똑같은 아이디어예요. 🏥
원문 강의의 시나리오로 시작해봅시다. 사용자 주제 → 영상 스크립트 자동 생성 마케팅 도구.
| 사용자 입력 | 적합한 콘텐츠 톤 |
|---|---|
| "Python 함수" | 📚 교육적, 명확한 정의·예제 |
| "서핑" | 🎉 엔터테인먼트, 흥분과 시각적 매력 |
| "최신 노트북 리뷰" | ⚖️ 분석적, 장단점 위주 |
| "내 첫 마라톤 후기" | 💬 친근한 vlog 톤 |
이 모두에 같은 프롬프트를 쓰면 어떻게 될까요?
> 다음 주제로 영상 스크립트를 만들어줘: <topic>결과:
🎯 문제의 본질: 같은 모델·같은 프롬프트가 모든 도메인에 최적이긴 어렵습니다. 모델은 만능이지만, 프롬프트는 도메인 종속적이거든요.
[사용자 입력]
▼
[Router] — 입력 종류를 분류
▼
┌────┬────┬────┬────┐
▼ ▼ ▼ ▼ ▼
[Pipe A] [Pipe B] [Pipe C] [Pipe D] ...
각 파이프라인 = 특정 카테고리 전용 프롬프트/도구핵심 규칙: 입력은 단 하나의 파이프라인에만 흘러갑니다. 모든 파이프라인을 거치지 않아요.
| 일반 프롬프트 | 라우팅 워크플로우 |
|---|---|
| 1개의 만능 프롬프트 | N개의 전문 프롬프트 |
| 한 번 호출 | 분류 1회 + 처리 1회 = 2회 |
| 결과 품질 평균 | 카테고리별 고품질 |
| 새 도메인 추가 어려움 | 새 파이프라인만 추가 |
💡 트레이드오프: 호출 횟수가 늘어 비용·지연이 약간 증가하지만, 품질 향상이 훨씬 큽니다. 다양한 도메인을 다루는 앱에선 거의 필수.
먼저 앱이 다룰 콘텐츠 종류를 명확히 분류합니다.
| 카테고리 | 특징 | 톤 |
|---|---|---|
| 🎉 Entertainment | 트렌드 언어, 문화적 맥락 | 고에너지 |
| 📚 Educational | 명확한 설명, 친근한 예시 | 차분하고 친절 |
| 🎭 Comedy | 의외의 전개, 타이밍 | 위트 |
| 📹 Personal vlog | 진솔한 스토리텔링 | 대화체 |
| ⚖️ Reviews | 장단점 비교, 경험 기반 | 단호하고 분석적 |
| 📖 Storytelling | 생생한 디테일, 감정 연결 | 몰입형 |
📌 카테고리 설계 팁: 너무 많으면 분류가 헷갈리고, 너무 적으면 차별화가 없습니다. 5~8개 사이가 보통 sweet spot 이에요.
각 카테고리 전용 프롬프트를 미리 작성해둡니다.
PROMPT_TEMPLATES = {
"Educational": """
당신은 친근한 IT 강사입니다. 복잡한 개념을 일상 비유로 풀어주세요.
주제: {topic}
다음을 포함하세요:
- 한 줄 핵심 정의
- 일상에서 본 적 있을 비유 1개
- 코드 또는 실습 예시 1개
- 시청자에게 던지는 질문 1개로 마무리
""",
"Entertainment": """
당신은 SNS 트렌드를 잘 아는 크리에이터입니다.
주제: {topic}
- 후킹 강한 첫 5초
- 시각적 임팩트 가능한 표현 다수
- 트렌디한 표현·밈 적극 활용
- 공유하고 싶게 만드는 마무리
""",
"Reviews": """...""",
# ... 나머지 카테고리
}
🎯 각 템플릿이 그 도메인의 베테랑처럼 작동하게 만드는 게 핵심. 일반 프롬프트보다 훨씬 강한 페르소나·제약을 박아도 됩니다.
def classify_topic(topic: str) -> str:
messages = []
add_user_message(messages, f"""
다음 주제를 한 카테고리로 분류해. 정확히 카테고리명만 출력.
<topic>{topic}</topic>
<categories>
- Educational
- Entertainment
- Comedy
- Personal vlog
- Reviews
- Storytelling
</categories>
""")
return chat(messages, temperature=0).strip()
💡 분류는
temperature=0으로 결정성을 극대화하세요. 같은 입력에 같은 카테고리가 나와야 디버깅이 쉬워집니다.
def generate_video_script(topic: str) -> str:
category = classify_topic(topic)
template = PROMPT_TEMPLATES.get(category, PROMPT_TEMPLATES["Educational"])
messages = []
add_user_message(messages, template.format(topic=topic))
return chat(messages, temperature=0.7)
| 사용자 입력 | classify_topic 결과 | 사용된 템플릿 |
|---|---|---|
| "Python 함수" | Educational | educational_prompt |
| "서핑" | Entertainment | entertainment_prompt |
| "M3 Max 노트북 리뷰" | Reviews | reviews_prompt |
| "유럽 여행 후기" | Personal vlog | vlog_prompt |
🎯 사용자는 카테고리 존재를 모릅니다. 그저 더 자기 입력에 어울리는 결과를 받아볼 뿐이에요. UX 매끄럽게 유지하면서 품질만 올라가는 패턴.
# Claude 가 가끔 이렇게 답할 수 있음:
"Educational/Entertainment 혼합"
"Educational - because it's about programming"
방어:
VALID_CATEGORIES = {"Educational", "Entertainment", "Comedy",
"Personal vlog", "Reviews", "Storytelling"}
def classify_topic(topic: str) -> str:
raw = chat(...).strip()
# 카테고리명만 추출
for cat in VALID_CATEGORIES:
if cat.lower() in raw.lower():
return cat
return "Educational" # 안전한 기본값
📌 post 09 에서 배운 prefill + stop_sequences 패턴을 적용하면 더 안전합니다.
add_assistant_message(messages, "<category>")
raw = chat(messages, stop_sequences=["</category>"])
> "AI" ← 너무 광범위
> "음... 뭔가" ← 무의미방어:
def classify_with_fallback(topic: str) -> str:
if len(topic) < 3 or topic.isspace():
return "Educational" # 기본값
if not is_meaningful(topic): # 추가 검증
return ask_user_to_clarify()
return classify_topic(topic)
# 동적 카테고리 — 코드 변경 없이 카테고리 추가
CATEGORIES = load_from_yaml("categories.yaml")
def build_categorize_prompt(topic, categories):
cat_list = "\n".join(f"- {c['name']}: {c['description']}" for c in categories)
return f"""주제: <topic>{topic}</topic>\n카테고리:\n{cat_list}\n..."""
💡 카테고리 정의를 YAML/DB 등으로 외부화하면 운영 중 추가가 쉬워집니다. 비즈니스 변화에 빠르게 적응 가능.
라우터의 분류 정확도가 떨어지면 이후 모든 파이프라인의 품질이 무의미해집니다. 그래서 라우터는 반드시 챕터 2 의 eval 워크플로우로 측정하세요.
test_cases = [
{"topic": "Python 함수", "expected": "Educational"},
{"topic": "서핑", "expected": "Entertainment"},
{"topic": "M3 Max 후기", "expected": "Reviews"},
{"topic": "오늘 점심 먹은 가게", "expected": "Personal vlog"},
# ...
]
correct = sum(
classify_topic(c["topic"]) == c["expected"]
for c in test_cases
)
print(f"정확도: {correct}/{len(test_cases)}")
🎯 운영 환경에서 카테고리별 호출 수와 정확도를 추적하면 어느 카테고리가 자주 오분류되는지 보입니다. 거기에 더 명확한 카테고리 description 을 주거나 키워드 힌트를 추가하면 정확도가 점프해요.
[사용자 입력]
│
▼
┌──────────┐
│ Router │ ← Claude (temp=0, 분류만)
└────┬─────┘
│ category
┌──────────┼──────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Pipe A │ │ Pipe B │ │ Pipe C │
│ Edu 전용 │ │ Ent 전용 │ │ Rev 전용 │
└────┬─────┘ └────┬─────┘ └────┬─────┘
▼ ▼ ▼
[최종 결과]
🔧 각 Pipe 는 자체 프롬프트, 자체 도구, 자체 후처리를 가질 수 있습니다. 한 라인은 RAG 를, 다른 라인은 단일 호출만, 또 다른 라인은 Evaluator-Optimizer 를 쓸 수도 있죠.
원문이 짚은 4가지 적합 조건을 확장해봅니다.
| 상황 | 추천 |
|---|---|
| 모든 입력이 비슷한 성격 | 단일 프롬프트 |
| 카테고리 경계가 너무 모호 | 단일 + Evaluator-Optimizer |
| 이미 도구 사용으로 자율 분기 가능 | 에이전트 |
⚠️ 카테고리 ≠ 본질적 차이. 라우팅은 다른 처리 전략이 정말 필요할 때만 도입하세요. 같은 프롬프트에 약간의 변수만 다른 거라면 체이닝의 변수 주입으로 해결할 수 있어요.
분류는 단순 작업이라 Haiku 같은 작은 모델로 충분합니다.
def classify_topic(topic: str) -> str:
return chat(
messages,
model="claude-haiku-4-5", # 작은 모델
temperature=0
).strip()
def generate_content(topic, category) -> str:
return chat(
messages,
model="claude-sonnet-4-6", # 큰 모델
temperature=0.7
)
💰 혼합 모델 전략: 분류는 빠르고 싸게, 본 처리는 강력하게. Anthropic 도 이 패턴을 권장합니다.
사용자 입력은 한국어, 카테고리 라벨은 영어인 경우가 많습니다.
add_user_message(messages, f"""
한국어 주제를 영어 카테고리로 분류해. 카테고리명만 출력.
<topic>{topic}</topic>
<categories>
- Educational (교육·강의)
- Entertainment (오락·트렌드)
- Reviews (리뷰·비교)
- ...
</categories>
""")
💡 카테고리 옆에 한국어 설명을 붙여주면 다국어 입력에서도 정확도가 안정적입니다.
같은 입력은 같은 카테고리. 프로덕션에선 캐싱하세요.
@lru_cache(maxsize=10000)
def classify_topic_cached(topic: str) -> str:
return classify_topic(topic)
📌 또는 Redis·Memcached 같은 분산 캐시 사용. 인기 키워드의 분류 결과는 거의 무한 재사용 가능.
분류 실패 시 어디로 보내냐가 중요합니다.
DEFAULT_CATEGORY = "Educational" # 무난한 톤
# 또는
DEFAULT_CATEGORY = "Generic" # 별도의 만능 파이프라인
⚠️ Fallback 을 가장 좁은 카테고리로 두면 큰 사고가 납니다. 가장 부작용이 작은 파이프라인으로 라우팅하세요.
도메인별 추천 카테고리 셋업:
| 도메인 | 추천 카테고리 |
|---|---|
| 🛍️ 쇼핑 챗봇 | 환불 / 배송 / 상품문의 / 일반문의 |
| 🏥 헬스케어 | 증상문의 / 예약 / 처방 / 일반 |
| 🏦 금융 | 거래조회 / 송금 / 투자상담 / 사고신고 |
| 📚 교육 플랫폼 | 강의문의 / 학습질문 / 진로상담 / 결제 |
| 🍔 배달 앱 | 주문 / 환불 / 라이더 문의 / 일반 |
🎯 고객 응대 로그를 클러스터링해서 자주 나오는 의도를 카테고리로 뽑으면 정확도가 매우 높아집니다.
본 글은 Anthropic Academy의 "Building with the Claude API" 코스 중 'Routing workflows' 강의 내용을 한국어로 정리·요약한 것입니다.
⚠️ 본 글은 학습 목적의 요약본이며, 정확하고 최신화된 내용은 반드시 Anthropic 공식 가이드 — Building effective agents 를 참고해주세요.
📝 이 글이 도움이 되셨다면 공감 ♥ 과 구독 부탁드립니다!
실제 프로젝트에서 어떤 카테고리 셋으로 라우팅을 설계하셨는지, 혹은 분류 정확도를 끌어올린 노하우가 있다면 댓글로 공유해 주세요. 다음 강의는 "Agents and tools — 에이전트와 도구의 결합" 입니다. 🤖
#Workflow #RoutingWorkflow #AnthropicAcademy #ClaudeAI #LLM #AgentDesign #SystemDesign #PromptEngineering #AI개발 #API개발 #챗봇 #ClaudeAPI
| # 환경 점검(Environment Inspection) — 눈먼 에이전트가 시야를 갖는 순간 (0) | 2026.06.13 |
|---|---|
| # 에이전트와 도구 — 도구를 "추상적으로" 설계해야 에이전트가 똑똑해지는 이유 (0) | 2026.06.12 |
| # 체이닝(Chaining) 워크플로우 — "한 방에 다 시키지 말고, 한 번에 하나씩" 시키는 기술 (0) | 2026.06.10 |
| # 워크플로우(Workflow) vs 에이전트(Agent) — 둘 다 만들 줄 아는 개발자가 되기 위한 분기점 (0) | 2026.06.08 |
| # Claude Code × MCP — `claude mcp add` 한 줄로 만드는 나만의 AI 워크플로우 허브 (0) | 2026.06.07 |