LangChain Agents Architecture

LangChain 에이전트의 구체적 아키텍처, 정적/동적 구성, Middleware 확장성.

설명

Agent의 기본 루프

LangChain 에이전트는 메시지 기반 상태 머신이다:

사용자 입력
  ↓
[1] AgentState 업데이트 (messages 리스트에 추가)
  ↓
[2] LLM 호출 (상태 전체를 프롬프트 컨텍스트로 제공)
  ↓
[3] LLM 응답 파싱 → 도구 호출 또는 최종 답변 판단
  ↓
[4-a] 도구 호출: Tool Executor가 실행 → 결과를 상태에 추가 → 반복
[4-b] 최종 답변: 사용자에게 반환

핵심: 모든 정보가 메시지 리스트에 누적된다. 따라서 멀티턴 대화에서도 전체 컨텍스트 보존.

정적 구성 (Static)

모델, 도구, 시스템 프롬프트를 agent 생성 시점에 고정:

from langchain.agents import create_agent
from langchain.chat_models import ChatAnthropic
 
model = ChatAnthropic(model="claude-opus", temperature=0.5)
tools = [calculator, wikipedia_search, database_query]
system_prompt = "You are a helpful assistant with access to tools."
 
agent = create_agent(
    model=model,
    tools=tools,
    system_prompt=system_prompt
)

장점:

  • 간단, 예측 가능
  • 프로덕션 배포에 안정적

단점:

  • 런타임에 동작 조정 불가
  • 모든 사용자/컨텍스트에 동일 설정

동적 구성 (Dynamic)

런타임 조건(사용자 역할, 대화 복잡도 등)에 따라 모델/도구를 변경:

동적 모델 선택

from langchain.agents import wrap_model_call
 
@wrap_model_call
def select_model(state):
    message_count = len(state["messages"])
    if message_count > 10:  # 복잡한 대화
        return ChatAnthropic(model="claude-opus", temperature=0.7)
    else:  # 단순 질문
        return ChatAnthropic(model="claude-haiku", temperature=0.5)
 
agent = create_agent(
    model=select_model,
    tools=tools
)

효과:

  • 저가 모델로 단순 쿼리 처리 → 비용 절감
  • 복잡 쿼리는 강력한 모델 사용 → 정확도 ↑

동적 도구 선택

def select_tools(state, user_role):
    base_tools = [calculator, search]
    
    if user_role == "admin":
        return base_tools + [database_query, system_admin]
    elif user_role == "user":
        return base_tools  # 관리 도구 제외
    else:
        return []

효과: 권한에 따른 도구 접근 제어, 보안 강화

동적 시스템 프롬프트

@dynamic_prompt
def generate_prompt(state, user_role):
    base_instruction = "You are a helpful assistant."
    
    if user_role == "admin":
        return base_instruction + "\nYou have access to admin tools."
    else:
        return base_instruction + "\nYou cannot access admin functions."
 
agent = create_agent(
    model=model,
    tools=tools,
    system_prompt=generate_prompt
)

Tool Use 패턴

Tool 등록

@tool
def calculator(expression: str) -> str:
    """Calculate a mathematical expression"""
    return str(eval(expression))
 
@tool  
def search_wiki(query: str) -> str:
    """Search Wikipedia for information"""
    ...
 
tools = [calculator, search_wiki]

Tool 호출 전략

순차 호출 (Sequential)

도구 1 → 결과 → 도구 2 (기본값, 안정적)

병렬 호출 (Parallel)

여러 도구 동시 실행 (I/O 작업 효율화)

# LangChain이 자동으로 병렬화 감지
tools_can_run_in_parallel = True
agent.invoke({...}, parallel_execution=True)

조건부 호출

이전 결과에 따라 도구 동적 선택

Memory와 State

AgentState 기본 구조

class AgentState(BaseModel):
    messages: List[BaseMessage]  # 메시지 누적

AgentState 확장

커스텀 정보 추적:

from langchain.schema import AgentState
 
class CustomAgentState(AgentState):
    user_preferences: dict  # 사용자 선호도
    session_goals: list[str]  # 현재 세션 목표
    conversation_sentiment: str  # 대화 분위기

효과: 에이전트가 사용자 문맥을 더 깊이 이해 가능

Middleware 패턴

모델 호출 전처리

@agent.middleware
def preprocess_state(state):
    # 토큰 수 제한 적용
    if len(state["messages"]) > 50:
        # 오래된 메시지 압축
        state["messages"] = compress_old_messages(state["messages"])
    return state

도구 오류 처리

@agent.on_tool_error
def handle_tool_error(error, tool_name):
    # 도구 실패 시 대체 도구 실행
    if tool_name == "database_query":
        return "Database unavailable. Using cached results."

응답 검증

@agent.post_process
def validate_response(response):
    # 환각 탐지
    if "I'm not sure" in response:
        # Reflection 패턴: 재시도
        return agent.invoke({...}, reflect=True)
    return response

실전 패턴

Reflection (자체 검증)

고위험 출력(코드, 법률 문서)을 LLM이 스스로 검증:

response = agent.invoke({...})
 
# Reflection
reflection = model.invoke(f"Is this answer correct? {response}")
if "correct" in reflection.lower():
    return response
else:
    return agent.invoke({...}, retry=True)

Tool Selector

복잡한 쿼리일 때 어떤 도구를 먼저 쓸지 고민:

# 에이전트가 자동으로 우선순위 결정
agent.invoke({
    "messages": [{
        "role": "user",
        "content": "Complex query..."
    }]
})
# → 에이전트가 tool_a 먼저, tool_b 나중에 결정

관련 개념

소스