Skip to main content
미들웨어는 에이전트 내부에서 발생하는 동작을 더욱 정밀하게 제어할 수 있는 방법을 제공합니다. 핵심 에이전트 루프는 모델 호출, 모델이 실행할 도구 선택, 그리고 더 이상 도구를 호출하지 않을 때 종료되는 과정을 포함합니다:
핵심 에이전트 루프 다이어그램
미들웨어는 이러한 각 단계의 전후에 훅을 노출합니다:
미들웨어 흐름 다이어그램

미들웨어로 할 수 있는 일

모니터링

로깅, 분석 및 디버깅을 통해 에이전트 동작을 추적합니다

수정

프롬프트, 도구 선택 및 출력 형식을 변환합니다

제어

재시도, 폴백 및 조기 종료 로직을 추가합니다

강제

속도 제한, 가드레일 및 PII 탐지를 적용합니다
@[create_agent]에 미들웨어를 전달하여 추가합니다:
import {
  createAgent,
  summarizationMiddleware,
  humanInTheLoopMiddleware,
} from "langchain";

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [...],
  middleware: [summarizationMiddleware, humanInTheLoopMiddleware],
});

내장 미들웨어

LangChain은 일반적인 사용 사례를 위한 사전 구축된 미들웨어를 제공합니다:

요약

토큰 제한에 근접할 때 대화 기록을 자동으로 요약합니다.
다음과 같은 경우에 적합:
  • 컨텍스트 창을 초과하는 장기 실행 대화
  • 광범위한 기록이 있는 다중 턴 대화
  • 전체 대화 컨텍스트 보존이 중요한 애플리케이션
import { createAgent, summarizationMiddleware } from "langchain";

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [weatherTool, calculatorTool],
  middleware: [
    summarizationMiddleware({
      model: "openai:gpt-4o-mini",
      maxTokensBeforeSummary: 4000, // 4000 토큰에서 요약 트리거
      messagesToKeep: 20, // 요약 후 마지막 20개 메시지 유지
      summaryPrompt: "Custom prompt for summarization...", // 선택사항
    }),
  ],
});
model
string
required
요약 생성을 위한 모델
maxTokensBeforeSummary
number
요약을 트리거하는 토큰 임계값
messagesToKeep
number
default:"20"
보존할 최근 메시지 수
tokenCounter
function
사용자 정의 토큰 카운팅 함수. 기본값은 문자 기반 카운팅입니다.
summaryPrompt
string
사용자 정의 프롬프트 템플릿. 지정되지 않으면 내장 템플릿을 사용합니다.
summaryPrefix
string
default:"## Previous conversation summary:"
요약 메시지의 접두사

휴먼 인 더 루프

도구 호출이 실행되기 전에 사람의 승인, 편집 또는 거부를 위해 에이전트 실행을 일시 중지합니다.
다음과 같은 경우에 적합:
  • 사람의 승인이 필요한 고위험 작업(데이터베이스 쓰기, 금융 거래)
  • 사람의 감독이 필수인 규정 준수 워크플로
  • 에이전트 가이드를 위해 사람의 피드백이 사용되는 장기 실행 대화
import { createAgent, humanInTheLoopMiddleware } from "langchain";

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [readEmailTool, sendEmailTool],
  middleware: [
    humanInTheLoopMiddleware({
      interruptOn: {
        // 이메일 전송에 대한 승인, 편집 또는 거부 필요
        send_email: {
          allowAccept: true,
          allowEdit: true,
          allowRespond: true,
        },
        // 이메일 읽기 자동 승인
        read_email: false,
      }
    })
  ]
});
interruptOn
object
required
도구 이름을 승인 설정에 매핑
도구 승인 설정 옵션:
allowAccept
boolean
default:"false"
승인 허용 여부
allowEdit
boolean
default:"false"
편집 허용 여부
allowRespond
boolean
default:"false"
응답/거부 허용 여부
중요: 휴먼 인 더 루프 미들웨어는 중단 간 상태를 유지하기 위해 체크포인터가 필요합니다.전체 예제 및 통합 패턴은 휴먼 인 더 루프 문서를 참조하세요.

Anthropic 프롬프트 캐싱

Anthropic 모델로 반복적인 프롬프트 접두사를 캐싱하여 비용을 절감합니다.
다음과 같은 경우에 적합:
  • 길고 반복되는 시스템 프롬프트가 있는 애플리케이션
  • 호출 간 동일한 컨텍스트를 재사용하는 에이전트
  • 대량 배포를 위한 API 비용 절감
Anthropic 프롬프트 캐싱 전략 및 제한 사항에 대해 자세히 알아보세요.
import { createAgent, HumanMessage, anthropicPromptCachingMiddleware } from "langchain";

const LONG_PROMPT = `
Please be a helpful assistant.

<Lots more context ...>
`;

const agent = createAgent({
  model: "anthropic:claude-sonnet-4-latest",
  prompt: LONG_PROMPT,
  middleware: [anthropicPromptCachingMiddleware({ ttl: "5m" })],
});

// 캐시 저장
await agent.invoke({
  messages: [new HumanMessage("Hi, my name is Bob")]
});

// 캐시 히트, 시스템 프롬프트가 캐싱됨
const result = await agent.invoke({
  messages: [new HumanMessage("What's my name?")]
});
ttl
string
default:"5m"
캐시된 콘텐츠의 유지 시간. 유효한 값: "5m" 또는 "1h"

모델 호출 제한

무한 루프나 과도한 비용을 방지하기 위해 모델 호출 수를 제한합니다.
다음과 같은 경우에 적합:
  • 너무 많은 API 호출을 하는 에이전트 방지
  • 프로덕션 배포에 대한 비용 제어 강제
  • 특정 호출 예산 내에서 에이전트 동작 테스트
import { createAgent, modelCallLimitMiddleware } from "langchain";

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [...],
  middleware: [
    modelCallLimitMiddleware({
      threadLimit: 10, // 스레드당 최대 10회 호출(실행 간)
      runLimit: 5, // 실행당 최대 5회 호출(단일 호출)
      exitBehavior: "end", // 또는 "error"로 예외 발생
    }),
  ],
});
threadLimit
number
스레드의 모든 실행에 걸친 최대 모델 호출 수. 기본값은 제한 없음입니다.
runLimit
number
단일 호출당 최대 모델 호출 수. 기본값은 제한 없음입니다.
exitBehavior
string
default:"end"
제한 도달 시 동작. 옵션: "end"(정상 종료) 또는 "error"(예외 발생)

도구 호출 제한

특정 도구 또는 모든 도구에 대한 도구 호출 수를 제한합니다.
다음과 같은 경우에 적합:
  • 비용이 많이 드는 외부 API에 대한 과도한 호출 방지
  • 웹 검색 또는 데이터베이스 쿼리 제한
  • 특정 도구 사용에 대한 속도 제한 강제
import { createAgent, toolCallLimitMiddleware } from "langchain";

// 모든 도구 호출 제한
const globalLimiter = toolCallLimitMiddleware({ threadLimit: 20, runLimit: 10 });

// 특정 도구 제한
const searchLimiter = toolCallLimitMiddleware({
  toolName: "search",
  threadLimit: 5,
  runLimit: 3,
});

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [...],
  middleware: [globalLimiter, searchLimiter],
});
toolName
string
제한할 특정 도구. 제공되지 않으면 모든 도구에 제한이 적용됩니다.
threadLimit
number
스레드의 모든 실행에 걸친 최대 도구 호출 수. 기본값은 제한 없음입니다.
runLimit
number
단일 호출당 최대 도구 호출 수. 기본값은 제한 없음입니다.
exitBehavior
string
default:"end"
제한 도달 시 동작. 옵션: "end"(정상 종료) 또는 "error"(예외 발생)

모델 폴백

기본 모델이 실패할 때 대체 모델로 자동 폴백합니다.
다음과 같은 경우에 적합:
  • 모델 중단을 처리하는 복원력 있는 에이전트 구축
  • 더 저렴한 모델로 폴백하여 비용 최적화
  • OpenAI, Anthropic 등의 제공업체 중복성
import { createAgent, modelFallbackMiddleware } from "langchain";

const agent = createAgent({
  model: "openai:gpt-4o", // 기본 모델
  tools: [...],
  middleware: [
    modelFallbackMiddleware(
      "openai:gpt-4o-mini", // 오류 시 첫 번째 시도
      "anthropic:claude-3-5-sonnet-20241022" // 그 다음 이것
    ),
  ],
});
미들웨어는 폴백 모델을 나타내는 가변 개수의 문자열 인수를 순서대로 받습니다:
...models
string[]
required
기본 모델이 실패할 때 순서대로 시도할 하나 이상의 폴백 모델 문자열
modelFallbackMiddleware(
  "first-fallback-model",
  "second-fallback-model",
  // ... 더 많은 모델
)

PII 탐지

대화에서 개인 식별 정보를 탐지하고 처리합니다.
다음과 같은 경우에 적합:
  • 규정 준수 요구 사항이 있는 의료 및 금융 애플리케이션
  • 로그를 정제해야 하는 고객 서비스 에이전트
  • 민감한 사용자 데이터를 처리하는 모든 애플리케이션
import { createAgent, piiRedactionMiddleware } from "langchain";

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [...],
  middleware: [
    // 사용자 입력에서 이메일 수정
    piiRedactionMiddleware({
      piiType: "email",
      strategy: "redact",
      applyToInput: true,
    }),
    // 신용카드 마스킹(마지막 4자리 표시)
    piiRedactionMiddleware({
      piiType: "credit_card",
      strategy: "mask",
      applyToInput: true,
    }),
    // 정규식으로 사용자 정의 PII 유형
    piiRedactionMiddleware({
      piiType: "api_key",
      detector: /sk-[a-zA-Z0-9]{32}/,
      strategy: "block", // 탐지되면 오류 발생
    }),
  ],
});
piiType
string
required
탐지할 PII 유형. 내장 유형(email, credit_card, ip, mac_address, url) 또는 사용자 정의 유형 이름일 수 있습니다.
strategy
string
default:"redact"
탐지된 PII 처리 방법. 옵션:
  • "block" - 탐지 시 오류 발생
  • "redact" - [REDACTED_TYPE]으로 대체
  • "mask" - 부분 마스킹(예: ****-****-****-1234)
  • "hash" - 결정적 해시로 대체
detector
RegExp
사용자 정의 탐지기 정규식 패턴. 제공되지 않으면 PII 유형에 대한 내장 탐지기를 사용합니다.
applyToInput
boolean
default:"true"
모델 호출 전 사용자 메시지 검사
applyToOutput
boolean
default:"false"
모델 호출 후 AI 메시지 검사
applyToToolResults
boolean
default:"false"
실행 후 도구 결과 메시지 검사

계획

복잡한 다단계 작업을 위한 할 일 목록 관리 기능을 추가합니다.
이 미들웨어는 에이전트에게 write_todos 도구와 효과적인 작업 계획을 안내하는 시스템 프롬프트를 자동으로 제공합니다.
import { createAgent, HumanMessage, todoListMiddleware } from "langchain";

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [
    /* ... */
  ],
  middleware: [todoListMiddleware()] as const,
});

const result = await agent.invoke({
  messages: [new HumanMessage("Help me refactor my codebase")],
});
console.log(result.todos); // 상태 추적이 있는 할 일 항목 배열
사용 가능한 설정 옵션이 없습니다(기본값 사용).

LLM 도구 선택기

메인 모델을 호출하기 전에 LLM을 사용하여 관련 도구를 지능적으로 선택합니다.
다음과 같은 경우에 적합:
  • 대부분이 쿼리당 관련이 없는 많은 도구(10개 이상)가 있는 에이전트
  • 관련 없는 도구를 필터링하여 토큰 사용량 줄이기
  • 모델 집중도 및 정확도 향상
import { createAgent, llmToolSelectorMiddleware } from "langchain";

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [tool1, tool2, tool3, tool4, tool5, ...], // 많은 도구
  middleware: [
    llmToolSelectorMiddleware({
      model: "openai:gpt-4o-mini", // 선택에 더 저렴한 모델 사용
      maxTools: 3, // 가장 관련성 높은 도구 3개로 제한
      alwaysInclude: ["search"], // 특정 도구 항상 포함
    }),
  ],
});
model
string
도구 선택을 위한 모델. 기본값은 에이전트의 메인 모델입니다.
maxTools
number
선택할 최대 도구 수. 기본값은 제한 없음입니다.
alwaysInclude
string[]
선택에 항상 포함할 도구 이름 배열

컨텍스트 편집

도구 사용을 트리밍, 요약 또는 지우는 방식으로 대화 컨텍스트를 관리합니다.
다음과 같은 경우에 적합:
  • 정기적인 컨텍스트 정리가 필요한 긴 대화
  • 컨텍스트에서 실패한 도구 시도 제거
  • 사용자 정의 컨텍스트 관리 전략
import { createAgent, contextEditingMiddleware, ClearToolUsesEdit } from "langchain";

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [...],
  middleware: [
    contextEditingMiddleware({
      edits: [
        new ClearToolUsesEdit({ maxTokens: 1000 }), // 오래된 도구 사용 지우기
      ],
    }),
  ],
});
edits
ContextEdit[]
default:"[new ClearToolUsesEdit()]"
적용할 ContextEdit 전략 배열
@[ClearToolUsesEdit] 옵션:
maxTokens
number
default:"1000"
편집을 트리거하는 토큰 수

사용자 정의 미들웨어

에이전트 실행 흐름의 특정 지점에서 실행되는 훅을 구현하여 사용자 정의 미들웨어를 구축합니다.

클래스 기반 미들웨어

두 가지 훅 스타일

노드 스타일 훅

특정 실행 지점에서 순차적으로 실행됩니다. 로깅, 유효성 검사 및 상태 업데이트에 사용합니다.

랩 스타일 훅

핸들러 호출을 완전히 제어하여 실행을 가로챕니다. 재시도, 캐싱 및 변환에 사용합니다.

노드 스타일 훅

실행 흐름의 특정 지점에서 실행됩니다:
  • beforeAgent - 에이전트 시작 전(호출당 한 번)
  • beforeModel - 각 모델 호출 전
  • afterModel - 각 모델 응답 후
  • afterAgent - 에이전트 완료 후(호출당 최대 한 번)
예제: 로깅 미들웨어
import { createMiddleware } from "langchain";

const loggingMiddleware = createMiddleware({
  name: "LoggingMiddleware",
  beforeModel: (state) => {
    console.log(`About to call model with ${state.messages.length} messages`);
    return;
  },
  afterModel: (state) => {
    const lastMessage = state.messages[state.messages.length - 1];
    console.log(`Model returned: ${lastMessage.content}`);
    return;
  },
});
예제: 대화 길이 제한
import { createMiddleware, AIMessage } from "langchain";

const createMessageLimitMiddleware = (maxMessages: number = 50) => {
  return createMiddleware({
    name: "MessageLimitMiddleware",
    beforeModel: (state) => {
      if (state.messages.length === maxMessages) {
        return {
          messages: [new AIMessage("Conversation limit reached.")],
          jumpTo: "end",
        };
      }
      return;
    },
  });
};

랩 스타일 훅

실행을 가로채고 핸들러 호출 시점을 제어합니다:
  • wrapModelCall - 각 모델 호출 주변
  • wrapToolCall - 각 도구 호출 주변
핸들러를 0회(단락), 1회(정상 흐름) 또는 여러 번(재시도 로직) 호출할지 결정합니다. 예제: 모델 재시도 미들웨어
import { createMiddleware } from "langchain";

const createRetryMiddleware = (maxRetries: number = 3) => {
  return createMiddleware({
    name: "RetryMiddleware",
    wrapModelCall: (request, handler) => {
      for (let attempt = 0; attempt < maxRetries; attempt++) {
        try {
          return handler(request);
        } catch (e) {
          if (attempt === maxRetries - 1) {
            throw e;
          }
          console.log(`Retry ${attempt + 1}/${maxRetries} after error: ${e}`);
        }
      }
      throw new Error("Unreachable");
    },
  });
};
예제: 동적 모델 선택
import { createMiddleware, initChatModel } from "langchain";

const dynamicModelMiddleware = createMiddleware({
  name: "DynamicModelMiddleware",
  wrapModelCall: (request, handler) => {
    // 대화 길이에 따라 다른 모델 사용
    const modifiedRequest = { ...request };
    if (request.messages.length > 10) {
      modifiedRequest.model = initChatModel("openai:gpt-4o");
    } else {
      modifiedRequest.model = initChatModel("openai:gpt-4o-mini");
    }
    return handler(modifiedRequest);
  },
});
예제: 도구 호출 모니터링
import { createMiddleware } from "langchain";

const toolMonitoringMiddleware = createMiddleware({
  name: "ToolMonitoringMiddleware",
  wrapToolCall: (request, handler) => {
    console.log(`Executing tool: ${request.toolCall.name}`);
    console.log(`Arguments: ${JSON.stringify(request.toolCall.args)}`);

    try {
      const result = handler(request);
      console.log("Tool completed successfully");
      return result;
    } catch (e) {
      console.log(`Tool failed: ${e}`);
      throw e;
    }
  },
});

사용자 정의 상태 스키마

미들웨어는 사용자 정의 속성으로 에이전트의 상태를 확장할 수 있습니다. 사용자 정의 상태 타입을 정의하고 state_schema로 설정합니다:
import { createMiddleware, createAgent, HumanMessage } from "langchain";
import * as z from "zod";

// 사용자 정의 상태 요구 사항이 있는 미들웨어
const callCounterMiddleware = createMiddleware({
  name: "CallCounterMiddleware",
  stateSchema: z.object({
    modelCallCount: z.number().default(0),
    userId: z.string().optional(),
  }),
  beforeModel: (state) => {
    // 사용자 정의 상태 속성 접근
    if (state.modelCallCount > 10) {
      return { jumpTo: "end" };
    }
    return;
  },
  afterModel: (state) => {
    // 사용자 정의 상태 업데이트
    return { modelCallCount: state.modelCallCount + 1 };
  },
});
const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [...],
  middleware: [callCounterMiddleware] as const,
});

// TypeScript가 필요한 상태 속성을 강제합니다
const result = await agent.invoke({
  messages: [new HumanMessage("Hello")],
  modelCallCount: 0, // 기본값으로 인해 선택사항
  userId: "user-123", // 선택사항
});

컨텍스트 확장

컨텍스트 속성은 실행 가능 설정을 통해 전달되는 설정 값입니다. 상태와 달리 컨텍스트는 읽기 전용이며 일반적으로 실행 중에 변경되지 않는 설정에 사용됩니다. 미들웨어는 에이전트의 설정을 통해 충족되어야 하는 컨텍스트 요구 사항을 정의할 수 있습니다:
import * as z from "zod";
import { createMiddleware, HumanMessage } from "langchain";

const rateLimitMiddleware = createMiddleware({
  name: "RateLimitMiddleware",
  contextSchema: z.object({
    maxRequestsPerMinute: z.number(),
    apiKey: z.string(),
  }),
  beforeModel: async (state, runtime) => {
    // 런타임을 통해 컨텍스트 접근
    const { maxRequestsPerMinute, apiKey } = runtime.context;

    // 속도 제한 로직 구현
    const allowed = await checkRateLimit(apiKey, maxRequestsPerMinute);
    if (!allowed) {
      return { jumpTo: "END" };
    }

    return state;
  },
});

// 컨텍스트는 설정을 통해 제공됩니다
await agent.invoke(
  { messages: [new HumanMessage("Process data")] },
  {
    context: {
      maxRequestsPerMinute: 60,
      apiKey: "api-key-123",
    },
  }
);

실행 순서

여러 미들웨어를 사용할 때 실행 순서를 이해하는 것이 중요합니다:
const agent = createAgent({
  model: "openai:gpt-4o",
  middleware: [middleware1, middleware2, middleware3],
  tools: [...],
});
Before 훅은 순서대로 실행됩니다:
  1. middleware1.before_agent()
  2. middleware2.before_agent()
  3. middleware3.before_agent()
에이전트 루프 시작
  1. middleware1.before_model()
  2. middleware2.before_model()
  3. middleware3.before_model()
Wrap 훅은 함수 호출처럼 중첩됩니다:
  1. middleware1.wrap_model_call()middleware2.wrap_model_call()middleware3.wrap_model_call() → model
After 훅은 역순으로 실행됩니다:
  1. middleware3.after_model()
  2. middleware2.after_model()
  3. middleware1.after_model()
에이전트 루프 종료
  1. middleware3.after_agent()
  2. middleware2.after_agent()
  3. middleware1.after_agent()
주요 규칙:
  • before_* 훅: 처음부터 마지막까지
  • after_* 훅: 마지막부터 처음까지(역순)
  • wrap_* 훅: 중첩됨(첫 번째 미들웨어가 다른 모든 미들웨어를 감쌈)

에이전트 점프

미들웨어에서 조기에 종료하려면 jump_to가 포함된 딕셔너리를 반환합니다:
import { createMiddleware, AIMessage } from "langchain";

const earlyExitMiddleware = createMiddleware({
  name: "EarlyExitMiddleware",
  beforeModel: (state) => {
    // 조건 확인
    if (shouldExit(state)) {
      return {
        messages: [new AIMessage("Exiting early due to condition.")],
        jumpTo: "end",
      };
    }
    return;
  },
});
사용 가능한 점프 대상:
  • "end": 에이전트 실행의 끝으로 점프
  • "tools": 도구 노드로 점프
  • "model": 모델 노드로 점프(또는 첫 번째 before_model 훅)
중요: before_model 또는 after_model에서 점프할 때, "model"로 점프하면 모든 before_model 미들웨어가 다시 실행됩니다. 점프를 활성화하려면 훅을 @hook_config(can_jump_to=[...])로 장식합니다:
import { createMiddleware } from "langchain";

const conditionalMiddleware = createMiddleware({
  name: "ConditionalMiddleware",
  afterModel: (state) => {
    if (someCondition(state)) {
      return { jumpTo: "end" };
    }
    return;
  },
});

모범 사례

  1. 미들웨어를 집중적으로 유지 - 각각 한 가지 일을 잘 수행해야 함
  2. 오류를 우아하게 처리 - 미들웨어 오류로 인해 에이전트가 중단되지 않도록 함
  3. 적절한 훅 타입 사용:
    • 순차적 로직(로깅, 유효성 검사)에는 노드 스타일
    • 제어 흐름(재시도, 폴백, 캐싱)에는 랩 스타일
  4. 사용자 정의 상태 속성을 명확하게 문서화
  5. 통합하기 전에 미들웨어를 독립적으로 단위 테스트
  6. 실행 순서 고려 - 중요한 미들웨어를 목록의 첫 번째에 배치
  7. 가능한 경우 내장 미들웨어 사용, 바퀴를 재발명하지 마세요 :)

예제

도구 동적 선택

성능 및 정확도를 향상시키기 위해 런타임에 관련 도구를 선택합니다.
이점:
  • 더 짧은 프롬프트 - 관련 도구만 노출하여 복잡성 감소
  • 더 나은 정확도 - 모델이 더 적은 옵션에서 올바르게 선택
  • 권한 제어 - 사용자 액세스에 따라 도구를 동적으로 필터링
import { createAgent, createMiddleware } from "langchain";

const toolSelectorMiddleware = createMiddleware({
  name: "ToolSelector",
  wrapModelCall: (request, handler) => {
    // 상태/컨텍스트에 따라 작고 관련성 높은 도구 하위 집합 선택
    const relevantTools = selectRelevantTools(request.state, request.runtime);
    const modifiedRequest = { ...request, tools: relevantTools };
    return handler(modifiedRequest);
  },
});

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: allTools, // 사용 가능한 모든 도구를 미리 등록해야 함
  // 미들웨어를 사용하여 주어진 실행에 관련된 더 작은 하위 집합을 선택할 수 있습니다.
  middleware: [toolSelectorMiddleware],
});

추가 리소스


Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I