에이전트(Agent) - 랭체인 에이전트 심화 (3)
2026. 6. 22. 15:50ㆍAI/LLM
1. 라우터 체인 (RouterChain)
- 조건부 분기 패턴으로써, 입력에 따라 적절한 체인 혹은 Runnable으로 분기하는 패턴이다.
- 분류기가 입력을 분석하고 적절한 체인으로 라우팅 해줌.
- 분류기는 LLM 기반, 규칙 기반으로 분석
- LCEL 방식 : RunnableLambda로 분기 함수를 정의하고 | 으로 흐름 연결
- 예시 : 질문 유형 (코딩 / 수학 / 일반)에 따라 각기 다른 전문 프롬프트 적용
- RunnableBranch -> 조건에 따른 다른 Runnable 선택하는 LCEL 라우팅 객체
동작 흐름

RunnableLambda 분기
parser = StrOutputParser()
# 수학체인
math_chain = ChatPromptTemplate.from_messages([
("system", "당신은 수학 전문가입니다. 풀이 과정을 단계별로 설명하세요."),
("human","{question}")
]) | openai_llm | parser
# 코드체인
code_chain = ChatPromptTemplate.from_messages([
("system", "당신은 시니어 개발자입니다. 코드와 주석을 함께 제공하세요"),
("human", "{question}")
]) | openai_llm | parser
# 일반체인
general_chain = ChatPromptTemplate.from_messages([
("system", "당신은 친절한 AI 어시스턴트입니다. 한국어로 답변하세요"),
("human", "{question}")
]) | openai_llm | parser
# 분류기 : 질문을 읽고 유형을 반환
classify_chain = ChatPromptTemplate.from_messages([
("system", "질문 유형을 math/code/general 중 하나로만 답하세요"),
("human", "{question}")
]) | openai_llm | parser
# 라우터 함수
def route(inputs: dict):
category = classify_chain.invoke(inputs).strip().lower()
print(f" => 분류 결과 {category}")
if 'math' in category: return math_chain
elif 'code' in category: return code_chain
else: return general_chain
# 라우터 체인 조합
router_chain = RunnableLambda(route) | RunnableLambda(lambda chain:chain)
# 실행
questions = [
{"question": "피타고라스 정리를 증명해줘"},
{"question": "파이썬으로 버블정렬 구현해줘"},
{"question": "오늘 기분 좋아지는 말 해줘"}
]
for q in questions:
print(f"Q: {q['question']}")
print(f"A: {router_chain.invoke(q)[:80]}...\n")

RunnableBranch 선언형 분기
from langchain_core.runnables import RunnableBranch
# RunnableBranch((조건1,체인1),(조건2,체인2).....)
branch = RunnableBranch(
(lambda x:'math' in x.get('topic',''),math_chain),
(lambda x:'code' in x.get('topic',''), code_chain),
(lambda x:'cooking' in x.get('topic',''),ChatPromptTemplate.from_messages([
("system","당신은 요리 전문가입니다"),
("human","{question}")
]) | openai_llm | parser),
general_chain
)
print(branch.invoke({'topic':'math', 'question': '미분이란?'}))
print(branch.invoke({'topic':'cooking', 'question': '김치찌개 레시피'}))
print(branch.invoke({'topic':'other', 'question': '안녕하세요'}))
print(branch.invoke({'topic':'code', 'question': '파이썬으로 선택 정렬 구현'}))
2. 단계별 순차 파이프라인(SequentialChain)
- 체인의 기본 흐름은 앞 단계 출력이 뒷 단계의 입력으로 흘러 들어간다
- 연산자 | 자체가 SequentialChain 역할이다.
- assign() 메소드로 중간 결과를 보존하며, 여러 단계를 누적할 수 있다.
- 각 단계의 출력 타입과 입력 타입이 일치해야 한다.
- 예시 -> 번역 => 요약 => 감정 분석 => 보고서 생성 등 다단계 처리
다단계 체인 연동 : 번역 -> 요약 -> 감정 분석 -> 보고서 생성
# 체인 정의
translate_chain = ChatPromptTemplate.from_messages([
("system", "다음 텍스트를 한국어로 번역하세요. 번역문만 출력: \n{text}"),
]) | openai_llm | parser
summarize_chain = ChatPromptTemplate.from_messages([
("system", "다음 텍스트를 3문장으로 요약하세요.\n{text}"),
]) | openai_llm | parser
sentiment_chain = ChatPromptTemplate.from_messages([
("system", "다음 텍스트의 감정을 긍정/부정/중립 중 하나로만 답하세요:\n {summary}"),
]) | openai_llm | parser
report_chain = (
ChatPromptTemplate.from_template(
'아래 분석 결과를 바탕으로 한 줄 최종 보고서를 작성하세요.\n'
'원문: {text}\n번역: {translated}\n요약: {summary}\n감정: {sentiment}'
) | openai_llm | parser
)
pipeline = (RunnablePassthrough.assign(translated=translate_chain).assign(summary=summarize_chain).assign(sentiment=sentiment_chain).assign(report=report_chain))
result = pipeline.invoke({"text" : "Python is a versatile language loved by developers worldwide."})
print("번역 : ", result['translated'])
print("요약 : ", result['summary'])
print("감정 : ", result['sentiment'])
print("보고서 : ", result['report'])

조건부 단계 삽입
# 조건부 단계 삽입
# 언어 감지 → 필요 시 번역 → 처리 패턴
detect_lang_chain = (
ChatPromptTemplate.from_template(
'다음 텍스트의 언어를 korean/english/other 중 하나로만 답하세요:\n{text}'
) | openai_llm | parser
)
def maybe_translate(inputs: dict) -> dict:
lang = detect_lang_chain.invoke(inputs).strip().lower()
if 'english' in lang or 'other' in lang:
translated = translate_chain.invoke(inputs)
return {**inputs, 'text': translated, 'was_translated': True}
return {**inputs, 'was_translated': False}
smart_pipeline = (
RunnableLambda(maybe_translate)
| RunnablePassthrough.assign(summary=summarize_chain)
| RunnablePassthrough.assign(sentiment=sentiment_chain)
)
r1 = smart_pipeline.invoke({'text': 'Python is great!'}) # 번역됨
r2 = smart_pipeline.invoke({'text': '파이썬은 최고야!'}) # 번역 생략
print(r1['was_translated'], r1['summary'][:50])
print(r2['was_translated'], r2['summary'][:50])

3. 대용량 문서 처리 (MapReduceChain)
Map과 Reduce라는 2단계로 나누어진다.
Map은 문서를 청크로 나눠 각각 추출한다 (요약, 추출) 또한 병렬 실행이 가능하다.
Reduce는 Map 한 것을 합쳐서 최종 답변을 생성한다.
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
map_chain = ChatPromptTemplate.from_messages([
("system", "다음 텍스트를 두문장으로 요약하세요"),
("human", "{chunk}")
]) | openai_llm | parser
reduce_chain = ChatPromptTemplate.from_messages([
("system", "다음은 긴 문서의 섹션별 요약입니다. 전체를 5문장으로 통합 요약하세요"),
("user", "{summaries}")
]) | openai_llm | parser
def map_reduce_summarize(file_path):
loader = PyPDFLoader(file_path)
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(loader.load())
print(f"총 청크 수 : {len(chunks)}")
# ------- Map: 청크별 요약
chunk_inputs = [{"chunk": c.page_content} for c in chunks]
summaries = map_chain.batch(chunk_inputs, config={'max_concurrency': 5})
print(f"{len(summaries)}개 요약 생성")
# ------- Reduce : 요약 통합
# 문서 결합
combined = "\n\n".join(f"[섹션 {i+1}]" for i, s in enumerate(summaries))
final = reduce_chain.invoke({'summaries': combined})
return final
result = map_reduce_summarize("직무기술서/2026 상 삼성E&A 직무기술서.pdf")
print(result)
'AI > LLM' 카테고리의 다른 글
| 에이전트(Agent) - 리서치 자동화 에이전트 구현 (2) (0) | 2026.06.22 |
|---|---|
| 에이전트(Agent) - 개념 (1) (0) | 2026.06.22 |
| 검색 증강 생성(RAG) - RAG의 한계 (3) (0) | 2026.06.21 |
| 검색 증강 생성(RAG) - PDF RAG 학습 앱 (2) (0) | 2026.06.21 |
| 검색 증강 생성(RAG) - 개념 (1) (0) | 2026.06.20 |