메모리는 이전 상호작용에 대한 정보를 기억하는 시스템입니다. AI 에이전트에게 메모리는 이전 상호작용을 기억하고, 피드백으로부터 학습하며, 사용자 선호도에 적응할 수 있게 해주기 때문에 매우 중요합니다. 에이전트가 수많은 사용자 상호작용을 포함하는 더 복잡한 작업을 처리할수록, 이 기능은 효율성과 사용자 만족도 모두에 필수적이 됩니다.
단기 메모리는 애플리케이션이 단일 스레드 또는 대화 내에서 이전 상호작용을 기억할 수 있게 해줍니다.
스레드는 이메일이 하나의 대화에서 메시지를 그룹화하는 것과 유사하게, 세션 내의 여러 상호작용을 조직화합니다.
사용법
에이전트에 단기 메모리(스레드 수준 지속성)를 추가하려면, 에이전트를 생성할 때 checkpointer를 지정해야 합니다.
LangChain의 에이전트는 단기 메모리를 에이전트 상태의 일부로 관리합니다.이를 그래프의 상태에 저장함으로써, 에이전트는 서로 다른 스레드 간의 분리를 유지하면서 주어진 대화에 대한 전체 컨텍스트에 액세스할 수 있습니다.상태는 checkpointer를 사용하여 데이터베이스(또는 메모리)에 지속되므로, 스레드를 언제든지 재개할 수 있습니다.단기 메모리는 에이전트가 호출되거나 단계(도구 호출 등)가 완료될 때 업데이트되며, 각 단계의 시작 시 상태를 읽습니다.
import { createAgent } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
const checkpointer = new MemorySaver();
const agent = createAgent({
    model: "anthropic:claude-sonnet-4-5",
    tools: [],
    checkpointer,
});
await agent.invoke(
    { messages: [{ role: "user", content: "hi! i am Bob" }] },
    { configurable: { thread_id: "1" } }
);
프로덕션 환경
프로덕션 환경에서는 데이터베이스로 백업되는 checkpointer를 사용하세요:
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
const DB_URI = "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable";
const checkpointer = PostgresSaver.fromConnString(DB_URI);
에이전트 메모리 커스터마이징
기본적으로 에이전트는 @[AgentState]를 사용하여 단기 메모리를 관리하며, 특히 messages 키를 통해 대화 기록을 관리합니다.
@[AgentState]를 확장하여 추가 필드를 추가할 수 있습니다. 커스텀 상태 스키마는 @[state_schema] 파라미터를 사용하여 @[create_agent]에 전달됩니다.
import * as z from "zod";
import { createAgent, createMiddleware } from "langchain";
import { MessagesZodState, MemorySaver } from "@langchain/langgraph";
const customStateSchema = z.object({  
    messages: MessagesZodState.shape.messages,  # [!code highlight]
    userId: z.string(),  
    preferences: z.record(z.string(), z.any()),  
});  
const stateExtensionMiddleware = createMiddleware({
    name: "StateExtension",
    stateSchema: customStateSchema,  
});
const checkpointer = new MemorySaver();
const agent = createAgent({
    model: "openai:gpt-5",
    tools: [],
    middleware: [stateExtensionMiddleware] as const,  
    checkpointer,
});
// Custom state can be passed in invoke
const result = await agent.invoke({
    messages: [{ role: "user", content: "Hello" }],
    userId: "user_123",  
    preferences: { theme: "dark" },  
});
일반적인 패턴
단기 메모리가 활성화되면, 긴 대화가 LLM의 컨텍스트 윈도우를 초과할 수 있습니다. 일반적인 해결책은 다음과 같습니다:
이를 통해 에이전트는 LLM의 컨텍스트 윈도우를 초과하지 않으면서 대화를 추적할 수 있습니다.
메시지 트리밍
대부분의 LLM은 최대 지원 컨텍스트 윈도우(토큰 단위로 표시)를 가지고 있습니다.
메시지를 언제 자를지 결정하는 한 가지 방법은 메시지 기록의 토큰을 세고 그 한계에 근접할 때마다 잘라내는 것입니다. LangChain을 사용하는 경우, trim messages 유틸리티를 사용하여 목록에서 유지할 토큰 수와 경계를 처리하는 데 사용할 strategy(예: 마지막 maxTokens 유지)를 지정할 수 있습니다.
에이전트에서 메시지 기록을 트리밍하려면, trimMessages 함수와 함께 stateModifier를 사용하세요:
import {
    createAgent,
    trimMessages,
    type AgentState,
} from "langchain";
import { MemorySaver } from "@langchain/langgraph";
// This function will be called every time before the node that calls LLM
const stateModifier = async (state: AgentState) => {
    return {
        messages: await trimMessages(state.messages, {
        strategy: "last",
        maxTokens: 384,
        startOn: "human",
        endOn: ["human", "tool"],
        tokenCounter: (msgs) => msgs.length,
        }),
    };
};
const checkpointer = new MemorySaver();
const agent = createAgent({
    model: "openai:gpt-5",
    tools: [],
    preModelHook: stateModifier,
    checkpointer,
});
메시지 삭제
메시지 기록을 관리하기 위해 그래프 상태에서 메시지를 삭제할 수 있습니다.
이는 특정 메시지를 제거하거나 전체 메시지 기록을 지우려는 경우에 유용합니다.
그래프 상태에서 메시지를 삭제하려면, RemoveMessage를 사용할 수 있습니다. RemoveMessage가 작동하려면, MessagesZodState와 같이 messagesStateReducer 리듀서가 있는 상태 키를 사용해야 합니다.
특정 메시지를 제거하려면:
import { RemoveMessage } from "@langchain/core/messages";
const deleteMessages = (state) => {
    const messages = state.messages;
    if (messages.length > 2) {
        // remove the earliest two messages
        return {
        messages: messages
            .slice(0, 2)
            .map((m) => new RemoveMessage({ id: m.id })),
        };
    }
};
메시지를 삭제할 때는, 결과 메시지 기록이 유효한지 확인하세요. 사용 중인 LLM 제공업체의 제한 사항을 확인하세요. 예를 들어:
- 일부 제공업체는 메시지 기록이 user메시지로 시작하기를 기대합니다
- 대부분의 제공업체는 도구 호출이 있는 assistant메시지 뒤에 해당하는tool결과 메시지가 와야 합니다.
 import { RemoveMessage } from "@langchain/core/messages";
import { AgentState, createAgent } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
const deleteMessages = (state: AgentState) => {
    const messages = state.messages;
    if (messages.length > 2) {
        // remove the earliest two messages
        return {
        messages: messages
            .slice(0, 2)
            .map((m) => new RemoveMessage({ id: m.id! })),
        };
    }
    return {};
};
const agent = createAgent({
    model: "openai:gpt-5-nano",
    tools: [],
    prompt: "Please be concise and to the point.",
    postModelHook: deleteMessages,
    checkpointer: new MemorySaver(),
});
const config = { configurable: { thread_id: "1" } };
const streamA = await agent.stream(
    { messages: [{ role: "user", content: "hi! I'm bob" }] },
    { ...config, streamMode: "values" }
);
for await (const event of streamA) {
    const messageDetails = event.messages.map((message) => [
        message.getType(),
        message.content,
    ]);
    console.log(messageDetails);
}
const streamB = await agent.stream(
    {
        messages: [{ role: "user", content: "what's my name?" }],
    },
    { ...config, streamMode: "values" }
);
for await (const event of streamB) {
    const messageDetails = event.messages.map((message) => [
        message.getType(),
        message.content,
    ]);
    console.log(messageDetails);
}
[['human', "hi! I'm bob"]]
[['human', "hi! I'm bob"], ['ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?']]
[['human', "hi! I'm bob"], ['ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?'], ['human', "what's my name?"]]
[['human', "hi! I'm bob"], ['ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?'], ['human', "what's my name?"], ['ai', 'Your name is Bob.']]
[['human', "what's my name?"], ['ai', 'Your name is Bob.']]
메시지 요약
위에서 보여준 것처럼 메시지를 트리밍하거나 제거하는 문제점은 메시지 큐를 제거함으로써 정보를 잃을 수 있다는 것입니다.
이 때문에, 일부 애플리케이션은 채팅 모델을 사용하여 메시지 기록을 요약하는 보다 정교한 접근 방식의 이점을 얻습니다.
 에이전트에서 메시지 기록을 요약하려면, 내장된
에이전트에서 메시지 기록을 요약하려면, 내장된 summarizationMiddleware를 사용하세요:
import { createAgent, summarizationMiddleware } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
const checkpointer = new MemorySaver();
const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [],
  middleware: [
    summarizationMiddleware({
      model: "openai:gpt-4o-mini",
      maxTokensBeforeSummary: 4000,
      messagesToKeep: 20,
    }),
  ],
  checkpointer,
});
const config = { configurable: { thread_id: "1" } };
await agent.invoke({ messages: "hi, my name is bob" }, config);
await agent.invoke({ messages: "write a short poem about cats" }, config);
await agent.invoke({ messages: "now do the same but for dogs" }, config);
const finalResponse = await agent.invoke({ messages: "what's my name?" }, config);
console.log(finalResponse.messages.at(-1)?.content);
// Your name is Bob!
summarizationMiddleware를 참조하세요.
메모리 액세스
에이전트의 단기 메모리(상태)에 여러 방법으로 액세스하고 수정할 수 있습니다:
도구에서 단기 메모리 읽기
ToolRuntime 파라미터를 사용하여 도구에서 단기 메모리(상태)에 액세스합니다.
tool_runtime 파라미터는 도구 시그니처에서 숨겨져 있지만(모델이 보지 못함), 도구는 이를 통해 상태에 액세스할 수 있습니다.
import * as z from "zod";
import { createAgent, tool } from "langchain";
const stateSchema = z.object({
    userId: z.string(),
});
const getUserInfo = tool(
    async (_, config) => {
        const userId = config.context?.userId;
        return { userId };
    },
    {
        name: "get_user_info",
        description: "Get user info",
        schema: z.object({}),
    }
);
const agent = createAgent({
    model: "openai:gpt-5-nano",
    tools: [getUserInfo],
    stateSchema,
});
const result = await agent.invoke(
    {
        messages: [{ role: "user", content: "what's my name?" }],
    },
    {
        context: {
        userId: "user_123",
        },
    }
);
console.log(result.messages.at(-1)?.content);
// Outputs: "User is John Smith."
도구에서 단기 메모리 쓰기
실행 중에 에이전트의 단기 메모리(상태)를 수정하려면, 도구에서 직접 상태 업데이트를 반환할 수 있습니다.
이는 중간 결과를 지속하거나 정보를 후속 도구나 프롬프트에서 액세스할 수 있게 만드는 데 유용합니다.
import * as z from "zod";
import { tool, createAgent } from "langchain";
import { MessagesZodState, Command } from "@langchain/langgraph";
const CustomState = z.object({
    messages: MessagesZodState.shape.messages,
    userName: z.string().optional(),
});
const updateUserInfo = tool(
    async (_, config) => {
        const userId = config.context?.userId;
        const name = userId === "user_123" ? "John Smith" : "Unknown user";
        return new Command({
        update: {
            userName: name,
            // update the message history
            messages: [
            {
                role: "tool",
                content: "Successfully looked up user information",
                tool_call_id: config.toolCall?.id,
            },
            ],
        },
        });
    },
    {
        name: "update_user_info",
        description: "Look up and update user info.",
        schema: z.object({}),
    }
);
const greet = tool(
    async (_, config) => {
        const userName = config.context?.userName;
        return `Hello ${userName}!`;
    },
    {
        name: "greet",
        description: "Use this to greet the user once you found their info.",
        schema: z.object({}),
    }
);
const agent = createAgent({
    model,
    tools: [updateUserInfo, greet],
    stateSchema: CustomState,
});
await agent.invoke(
    { messages: [{ role: "user", content: "greet the user" }] },
    { context: { userId: "user_123" } }
);
프롬프트
미들웨어에서 단기 메모리(상태)에 액세스하여 대화 기록이나 커스텀 상태 필드를 기반으로 동적 프롬프트를 생성합니다.
import * as z from "zod";
import { createAgent, tool, SystemMessage } from "langchain";
const contextSchema = z.object({
    userName: z.string(),
});
const getWeather = tool(
    async ({ city }, config) => {
        return `The weather in ${city} is always sunny!`;
    },
    {
        name: "get_weather",
        description: "Get user info",
        schema: z.object({
        city: z.string(),
        }),
    }
);
const agent = createAgent({
    model: "openai:gpt-5-nano",
    tools: [getWeather],
    contextSchema,
    prompt: (state, config) => {
        return [
        new SystemMessage(
            `You are a helpful assistant. Address the user as ${config.context?.userName}.`
        ),
        ...state.messages,
    },
});
const result = await agent.invoke(
    {
        messages: [{ role: "user", content: "What is the weather in SF?" }],
    },
    {
        context: {
        userName: "John Smith",
        },
    }
);
for (const message of result.messages) {
    console.log(message);
}
/**
 * HumanMessage {
 *   "content": "What is the weather in SF?",
 *   // ...
 * }
 * AIMessage {
 *   // ...
 *   "tool_calls": [
 *     {
 *       "name": "get_weather",
 *       "args": {
 *         "city": "San Francisco"
 *       },
 *       "type": "tool_call",
 *       "id": "call_tCidbv0apTpQpEWb3O2zQ4Yx"
 *     }
 *   ],
 *   // ...
 * }
 * ToolMessage {
 *   "content": "The weather in San Francisco is always sunny!",
 *   "tool_call_id": "call_tCidbv0apTpQpEWb3O2zQ4Yx"
 *   // ...
 * }
 * AIMessage {
 *   "content": "John Smith, here's the latest: The weather in San Francisco is always sunny!\n\nIf you'd like more details (temperature, wind, humidity) or a forecast for the next few days, I can pull that up. What would you like?",
 *   // ...
 * }
 */
Before model
@[@before_model] 미들웨어에서 단기 메모리(상태)에 액세스하여 모델 호출 전에 메시지를 처리합니다.
import { RemoveMessage } from "@langchain/core/messages";
import { createAgent, createMiddleware, trimMessages, type AgentState } from "langchain";
const trimMessageHistory = createMiddleware({
  name: "TrimMessages",
  beforeModel: async (state) => {
    const trimmed = await trimMessages(state.messages, {
      maxTokens: 384,
      strategy: "last",
      startOn: "human",
      endOn: ["human", "tool"],
      tokenCounter: (msgs) => msgs.length,
    });
    return { messages: trimmed };
  },
});
const agent = createAgent({
    model: "openai:gpt-5-nano",
    tools: [],
    middleware: [trimMessageHistory],
});
After model
@[@after_model] 미들웨어에서 단기 메모리(상태)에 액세스하여 모델 호출 후에 메시지를 처리합니다.
import { RemoveMessage } from "@langchain/core/messages";
import { createAgent, createMiddleware, type AgentState } from "langchain";
const validateResponse = createMiddleware({
  name: "ValidateResponse",
  afterModel: (state) => {
    const lastMessage = state.messages.at(-1)?.content;
    if (typeof lastMessage === "string" && lastMessage.toLowerCase().includes("confidential")) {
      return {
        messages: [new RemoveMessage({ id: "all" }), ...state.messages],
      };
    }
    return;
  },
});
const agent = createAgent({
    model: "openai:gpt-5-nano",
    tools: [],
    middleware: [validateResponse],
});