create_agent 미들웨어 데코레이터 패턴

LangChain의 create_agent가 제공하는 미들웨어 데코레이터는 LLM 호출의 전후 처리를 중앙화하는 아키텍처 패턴이다.

4가지 데코레이터 타입

1. @wrap_model_call

용도: LLM 입출력 스키마 변환 및 응답 후처리

LLM의 원본 응답을 비즈니스 로직에 맞는 스키마로 변환한다. 예를 들어:

  • 토큰 수 추출
  • 응답 구조 정규화
  • 메타데이터 추가

2. @before_model

용도: 모델 호출 전 프롬프트 수정

LLM으로 보내기 전에 프롬프트를 동적으로 수정한다.

3. @dynamic_prompt

용도: 실행 시점 변수 기반 프롬프트 조정

프롬프트 템플릿에서 실행 시점의 변수를 주입한다. 템플릿 문자열의 정적 부분과 동적 부분을 분리하는 데 유용하다.

4. @before_agent

용도: 에이전트 실행 전 보안 검증

에이전트가 LLM 호출을 수행하기 전에 프롬프트를 검증한다. 특히 프롬프트 인젝션 방어에 사용된다.

설계 원칙

단일 책임 원칙 (SRP)

각 데코레이터는 하나의 책임만 가져야 한다:

  • 스키마 변환은 @wrap_model_call에서만
  • 보안 검증은 @before_agent에서만
  • 동적 조정은 @dynamic_prompt에서만

이렇게 분리하면 각 미들웨어를 독립적으로 테스트하고 조합할 수 있다.

중앙화의 장점

분산된 검증 로직     중앙화된 미들웨어
├─ API 엔드포인트1   @wrap_model_call
├─ API 엔드포인트2   @before_model
├─ API 엔드포인트3   @dynamic_prompt
└─ ...              @before_agent
    (중복된 코드)    (일관된 정책)

실전 패턴

패턴 1: 응답 스키마 정규화

@wrap_model_call
def normalize_response(response):
    return {
        "content": response.content,
        "finish_reason": response.finish_reason,
        "tokens_used": response.usage.total_tokens
    }

패턴 2: 프롬프트 인젝션 차단

@before_agent
def block_injections(prompt: str) -> str:
    dangerous = [r"'; DROP", r"<script>", r"__import__"]
    for pattern in dangerous:
        if re.search(pattern, prompt, re.IGNORECASE):
            raise SecurityError(f"Blocked: {pattern}")
    return prompt

패턴 3: 문맥 주입

@dynamic_prompt
def add_context(prompt: str, user_id: str) -> str:
    return f"[User: {user_id}]\n{prompt}"

관련 개념