Skip to main content
메모리는 이전 상호작용에 대한 정보를 기억하는 시스템입니다. AI 에이전트에게 메모리는 이전 상호작용을 기억하고, 피드백으로부터 학습하며, 사용자 선호도에 적응할 수 있게 해주기 때문에 매우 중요합니다. 에이전트가 수많은 사용자 상호작용을 포함하는 더 복잡한 작업을 처리할수록, 이 기능은 효율성과 사용자 만족도 모두에 필수적이 됩니다. 이 개념 가이드는 회상 범위를 기준으로 두 가지 유형의 메모리를 다룹니다:
  • 단기 메모리 또는 스레드 범위 메모리는 세션 내에서 메시지 기록을 유지하여 진행 중인 대화를 추적합니다. LangGraph는 단기 메모리를 에이전트의 상태의 일부로 관리합니다. 상태는 체크포인터를 사용하여 데이터베이스에 저장되므로 스레드를 언제든지 재개할 수 있습니다. 단기 메모리는 그래프가 호출되거나 단계가 완료될 때 업데이트되며, 상태는 각 단계의 시작 시 읽힙니다.
  • 장기 메모리는 세션 간에 사용자별 또는 애플리케이션 수준 데이터를 저장하며, 대화 스레드 전체에 걸쳐 공유됩니다. 언제든지 그리고 어떤 스레드에서든 회상할 수 있습니다. 메모리는 단일 스레드 ID 내에서만이 아니라 임의의 사용자 정의 네임스페이스로 범위가 지정됩니다. LangGraph는 장기 메모리를 저장하고 회상할 수 있도록 스토어 (참조 문서)를 제공합니다.

단기 메모리

단기 메모리는 애플리케이션이 단일 스레드 또는 대화 내에서 이전 상호작용을 기억할 수 있게 합니다. 스레드는 이메일이 단일 대화에서 메시지를 그룹화하는 방식과 유사하게 세션 내 여러 상호작용을 구성합니다. LangGraph는 단기 메모리를 스레드 범위 체크포인트를 통해 저장된 에이전트 상태의 일부로 관리합니다. 이 상태는 일반적으로 대화 기록과 함께 업로드된 파일, 검색된 문서 또는 생성된 아티팩트와 같은 다른 상태 데이터를 포함할 수 있습니다. 이를 그래프의 상태에 저장함으로써 봇은 서로 다른 스레드 간 분리를 유지하면서 특정 대화에 대한 전체 컨텍스트에 액세스할 수 있습니다.

단기 메모리 관리

대화 기록은 단기 메모리의 가장 일반적인 형태이며, 긴 대화는 현재 LLM에 어려움을 제기합니다. 전체 기록이 LLM의 컨텍스트 윈도우에 맞지 않아 복구할 수 없는 오류가 발생할 수 있습니다. LLM이 전체 컨텍스트 길이를 지원하더라도 대부분의 LLM은 여전히 긴 컨텍스트에서 성능이 저하됩니다. 오래되었거나 주제에서 벗어난 콘텐츠에 “산만해지며”, 응답 시간이 느려지고 비용이 증가합니다. 채팅 모델은 개발자가 제공한 지침(시스템 메시지)과 사용자 입력(사람 메시지)을 포함하는 메시지를 사용하여 컨텍스트를 받습니다. 채팅 애플리케이션에서 메시지는 사람 입력과 모델 응답 사이를 번갈아 가며, 시간이 지남에 따라 점점 더 길어지는 메시지 목록을 생성합니다. 컨텍스트 윈도우가 제한되어 있고 토큰이 많은 메시지 목록은 비용이 많이 들 수 있으므로, 많은 애플리케이션은 오래된 정보를 수동으로 제거하거나 잊는 기술을 사용하는 것이 유익합니다. 메시지 관리를 위한 일반적인 기술에 대한 자세한 내용은 메모리 추가 및 관리 가이드를 참조하세요.

장기 메모리

LangGraph의 장기 메모리는 시스템이 다양한 대화나 세션 간에 정보를 유지할 수 있게 합니다. 스레드 범위인 단기 메모리와 달리, 장기 메모리는 사용자 정의 “네임스페이스” 내에 저장됩니다. 장기 메모리는 모든 상황에 맞는 단일 솔루션이 없는 복잡한 과제입니다. 그러나 다음 질문들은 다양한 기술을 탐색하는 데 도움이 되는 프레임워크를 제공합니다:
  • 메모리의 유형은 무엇인가요? 인간은 사실(의미 메모리), 경험(일화 메모리), 규칙(절차 메모리)을 기억하기 위해 메모리를 사용합니다. AI 에이전트도 동일한 방식으로 메모리를 사용할 수 있습니다. 예를 들어, AI 에이전트는 작업을 수행하기 위해 사용자에 대한 특정 사실을 기억하기 위해 메모리를 사용할 수 있습니다.
  • 언제 메모리를 업데이트하고 싶으신가요? 메모리는 에이전트의 애플리케이션 로직의 일부로 업데이트할 수 있습니다(예: “핫 패스에서”). 이 경우 에이전트는 일반적으로 사용자에게 응답하기 전에 사실을 기억하기로 결정합니다. 또는 메모리를 백그라운드 작업(백그라운드에서 비동기적으로 실행되고 메모리를 생성하는 로직)으로 업데이트할 수 있습니다. 아래 섹션에서 이러한 접근 방식 간의 트레이드오프를 설명합니다.

메모리 유형

애플리케이션마다 다양한 유형의 메모리가 필요합니다. 비유가 완벽하지는 않지만 인간의 메모리 유형을 살펴보는 것은 통찰력을 제공합니다. 일부 연구(예: CoALA 논문)는 이러한 인간 메모리 유형을 AI 에이전트에서 사용되는 것과 매핑하기도 했습니다.
메모리 유형저장되는 것인간의 예에이전트의 예
의미사실학교에서 배운 것들사용자에 대한 사실
일화경험내가 한 일들과거 에이전트 행동
절차지침본능 또는 운동 기술에이전트 시스템 프롬프트

의미 메모리

의미 메모리는 인간과 AI 에이전트 모두에서 특정 사실과 개념의 보유를 포함합니다. 인간의 경우 학교에서 배운 정보와 개념 및 그 관계에 대한 이해를 포함할 수 있습니다. AI 에이전트의 경우 의미 메모리는 과거 상호작용에서 사실이나 개념을 기억하여 애플리케이션을 개인화하는 데 자주 사용됩니다.
의미 메모리는 “의미론적 검색”과 다릅니다. 의미론적 검색은 “의미”(일반적으로 임베딩으로)를 사용하여 유사한 콘텐츠를 찾는 기술입니다. 의미 메모리는 심리학에서 나온 용어로 사실과 지식을 저장하는 것을 의미하는 반면, 의미론적 검색은 정확한 일치가 아닌 의미를 기반으로 정보를 검색하는 방법입니다.
프로필
의미 메모리는 다양한 방식으로 관리할 수 있습니다. 예를 들어, 메모리는 사용자, 조직 또는 다른 엔티티(에이전트 자체 포함)에 대한 잘 정의되고 구체적인 정보를 지속적으로 업데이트하는 단일 “프로필”이 될 수 있습니다. 프로필은 일반적으로 도메인을 나타내기 위해 선택한 다양한 키-값 쌍이 있는 JSON 문서입니다. 프로필을 기억할 때는 매번 프로필을 업데이트하고 있는지 확인해야 합니다. 따라서 이전 프로필을 전달하고 모델에게 새 프로필을 생성하도록 요청하거나(또는 이전 프로필에 적용할 JSON 패치) 해야 합니다. 프로필이 커질수록 오류가 발생하기 쉬우며, 프로필을 여러 문서로 분할하거나 메모리 스키마가 유효한 상태로 유지되도록 문서 생성 시 엄격한 디코딩을 사용하는 것이 도움이 될 수 있습니다.
컬렉션
또는 메모리를 시간이 지남에 따라 지속적으로 업데이트되고 확장되는 문서 컬렉션으로 만들 수 있습니다. 각 개별 메모리는 더 좁은 범위를 가지며 생성하기 쉬워서 시간이 지나도 정보를 잃을 가능성이 낮습니다. LLM이 기존 프로필과 새 정보를 조정하는 것보다 새 정보에 대해 새로운 객체를 생성하는 것이 더 쉽습니다. 그 결과 문서 컬렉션은 다운스트림에서 더 높은 재현율로 이어지는 경향이 있습니다. 그러나 이는 메모리 업데이트에 약간의 복잡성을 가져옵니다. 모델은 이제 목록의 기존 항목을 _삭제_하거나 _업데이트_해야 하는데, 이는 까다로울 수 있습니다. 또한 일부 모델은 기본적으로 과도하게 삽입하는 경향이 있고 다른 모델은 과도하게 업데이트하는 경향이 있습니다. 이를 관리하는 한 가지 방법은 Trustcall 패키지를 참조하고, 동작을 조정하는 데 도움이 되도록 평가(LangSmith와 같은 도구 사용)를 고려하세요. 문서 컬렉션으로 작업하면 목록에 대한 메모리 검색의 복잡성도 증가합니다. Store는 현재 의미론적 검색콘텐츠별 필터링 모두를 지원합니다. 마지막으로, 메모리 컬렉션을 사용하면 모델에 포괄적인 컨텍스트를 제공하기 어려울 수 있습니다. 개별 메모리가 특정 스키마를 따를 수 있지만, 이 구조는 메모리 간의 전체 컨텍스트나 관계를 캡처하지 못할 수 있습니다. 그 결과, 이러한 메모리를 사용하여 응답을 생성할 때 모델은 통합 프로필 접근 방식에서 더 쉽게 사용할 수 있었을 중요한 컨텍스트 정보가 부족할 수 있습니다. 메모리 관리 접근 방식과 관계없이 핵심은 에이전트가 의미 메모리를 사용하여 응답을 근거화하며, 이는 종종 더 개인화되고 관련성 있는 상호작용으로 이어진다는 것입니다.

일화 메모리

일화 메모리는 인간과 AI 에이전트 모두에서 과거의 사건이나 행동을 회상하는 것을 포함합니다. CoALA 논문은 이를 잘 설명합니다: 사실은 의미 메모리에 기록될 수 있는 반면, 경험은 일화 메모리에 기록될 수 있습니다. AI 에이전트의 경우 일화 메모리는 에이전트가 작업을 수행하는 방법을 기억하는 데 자주 사용됩니다. 실제로 일화 메모리는 에이전트가 과거 시퀀스로부터 학습하여 작업을 올바르게 수행하는 퓨샷 예제 프롬프팅을 통해 구현되는 경우가 많습니다. 때로는 “말하는” 것보다 “보여주는” 것이 더 쉬우며 LLM은 예제로부터 잘 학습합니다. 퓨샷 학습을 통해 의도한 동작을 설명하는 입력-출력 예제로 프롬프트를 업데이트하여 LLM을 “프로그래밍”할 수 있습니다. 퓨샷 예제를 생성하기 위해 다양한 모범 사례를 사용할 수 있지만, 종종 문제는 사용자 입력에 따라 가장 관련성 있는 예제를 선택하는 데 있습니다. 메모리 스토어는 데이터를 퓨샷 예제로 저장하는 한 가지 방법일 뿐입니다. 더 많은 개발자 참여를 원하거나 퓨샷을 평가 하네스와 더 긴밀하게 연결하려면 LangSmith 데이터셋을 사용하여 데이터를 저장할 수도 있습니다. 그런 다음 동적 퓨샷 예제 선택기를 즉시 사용하여 동일한 목표를 달성할 수 있습니다. LangSmith는 데이터셋을 인덱싱하고 키워드 유사성을 기반으로 사용자 입력과 가장 관련성 있는 퓨샷 예제의 검색을 가능하게 합니다. LangSmith에서 동적 퓨샷 예제 선택의 사용 예시는 이 동영상을 참조하세요. 또한 도구 호출 성능을 개선하기 위한 퓨샷 프롬프팅을 소개하는 이 블로그 게시물과 퓨샷 예제를 사용하여 LLM을 인간 선호도에 정렬하는 이 블로그 게시물도 참조하세요.

절차 메모리

절차 메모리는 인간과 AI 에이전트 모두에서 작업을 수행하는 데 사용되는 규칙을 기억하는 것을 포함합니다. 인간의 경우 절차 메모리는 기본 운동 기술과 균형을 통해 자전거를 타는 것과 같은 작업을 수행하는 방법에 대한 내면화된 지식과 같습니다. 반면에 일화 메모리는 보조 바퀴 없이 자전거를 성공적으로 탄 첫 번째 경험이나 경치 좋은 경로를 따라 자전거를 타던 기억에 남는 경험과 같은 특정 경험을 회상하는 것을 포함합니다. AI 에이전트의 경우 절차 메모리는 모델 가중치, 에이전트 코드, 에이전트의 프롬프트의 조합으로 에이전트의 기능을 총체적으로 결정합니다. 실제로 에이전트가 모델 가중치를 수정하거나 코드를 다시 작성하는 것은 상당히 드뭅니다. 그러나 에이전트가 자체 프롬프트를 수정하는 것은 더 일반적입니다. 에이전트의 지침을 개선하는 효과적인 접근 방식 중 하나는 “반성” 또는 메타 프롬프팅입니다. 이는 현재 지침(예: 시스템 프롬프트)과 최근 대화 또는 명시적 사용자 피드백을 함께 에이전트에 프롬프트하는 것을 포함합니다. 그런 다음 에이전트는 이 입력을 기반으로 자체 지침을 개선합니다. 이 방법은 지침을 사전에 지정하기 어려운 작업에 특히 유용하며, 에이전트가 상호작용에서 학습하고 적응할 수 있게 합니다. 예를 들어, 외부 피드백과 프롬프트 재작성을 사용하여 Twitter용 고품질 논문 요약을 생성하는 트윗 생성기를 구축했습니다. 이 경우 특정 요약 프롬프트를 사전에 지정하기는 어려웠지만, 사용자가 생성된 트윗을 비평하고 요약 프로세스를 개선하는 방법에 대한 피드백을 제공하는 것은 상당히 쉬웠습니다. 아래 의사 코드는 LangGraph 메모리 스토어를 사용하여 이를 구현하는 방법을 보여줍니다. 스토어를 사용하여 프롬프트를 저장하고, update_instructions 노드가 현재 프롬프트(state["messages"]에 캡처된 사용자와의 대화에서 피드백도 함께)를 가져오고, 프롬프트를 업데이트하고, 새 프롬프트를 스토어에 다시 저장합니다. 그런 다음 call_model이 스토어에서 업데이트된 프롬프트를 가져와 응답을 생성하는 데 사용합니다.
// Node that *uses* the instructions
const callModel = async (state: State, store: BaseStore) => {
    const namespace = ["agent_instructions"];
    const instructions = await store.get(namespace, "agent_a");
    // Application logic
    const prompt = promptTemplate.format({
        instructions: instructions[0].value.instructions
    });
    // ...
};

// Node that updates instructions
const updateInstructions = async (state: State, store: BaseStore) => {
    const namespace = ["instructions"];
    const currentInstructions = await store.search(namespace);
    // Memory logic
    const prompt = promptTemplate.format({
        instructions: currentInstructions[0].value.instructions,
        conversation: state.messages
    });
    const output = await llm.invoke(prompt);
    const newInstructions = output.new_instructions;
    await store.put(["agent_instructions"], "agent_a", {
        instructions: newInstructions
    });
    // ...
};

메모리 쓰기

에이전트가 메모리를 쓰는 두 가지 주요 방법이 있습니다: “핫 패스에서”“백그라운드에서”입니다.

핫 패스에서

런타임 중에 메모리를 생성하는 것은 장점과 과제를 모두 제공합니다. 긍정적인 측면에서 이 접근 방식은 실시간 업데이트를 가능하게 하여 새 메모리를 후속 상호작용에서 즉시 사용할 수 있게 합니다. 또한 메모리가 생성되고 저장될 때 사용자에게 알릴 수 있으므로 투명성을 제공합니다. 그러나 이 방법은 과제도 제시합니다. 에이전트가 메모리에 커밋할 내용을 결정하기 위해 새 도구가 필요한 경우 복잡성이 증가할 수 있습니다. 또한 메모리에 저장할 내용에 대해 추론하는 프로세스가 에이전트 지연에 영향을 미칠 수 있습니다. 마지막으로 에이전트는 메모리 생성과 다른 책임 사이에서 멀티태스킹해야 하므로 생성된 메모리의 양과 품질에 영향을 미칠 수 있습니다. 예를 들어 ChatGPT는 save_memories 도구를 사용하여 메모리를 콘텐츠 문자열로 업서트하고, 각 사용자 메시지마다 이 도구를 사용할지 여부와 방법을 결정합니다. 참조 구현으로 memory-agent 템플릿을 참조하세요.

백그라운드에서

별도의 백그라운드 작업으로 메모리를 생성하는 것은 여러 가지 이점을 제공합니다. 기본 애플리케이션의 지연을 제거하고, 애플리케이션 로직을 메모리 관리와 분리하며, 에이전트가 더 집중적으로 작업을 완료할 수 있게 합니다. 이 접근 방식은 또한 중복 작업을 피하기 위해 메모리 생성 시점을 유연하게 조정할 수 있습니다. 그러나 이 방법에는 고유한 과제가 있습니다. 메모리 쓰기 빈도를 결정하는 것이 중요해지는데, 업데이트가 드물면 다른 스레드에 새로운 컨텍스트가 제공되지 않을 수 있습니다. 메모리 형성을 트리거하는 시점을 결정하는 것도 중요합니다. 일반적인 전략에는 설정된 시간 후에 예약(새 이벤트가 발생하면 다시 예약), cron 일정 사용, 사용자 또는 애플리케이션 로직에 의한 수동 트리거 허용이 포함됩니다. 참조 구현으로 memory-service 템플릿을 참조하세요.

메모리 저장

LangGraph는 장기 메모리를 스토어의 JSON 문서로 저장합니다. 각 메모리는 사용자 정의 namespace(폴더와 유사)와 고유한 key(파일 이름과 유사) 아래에 구성됩니다. 네임스페이스에는 종종 정보를 더 쉽게 구성할 수 있도록 사용자 또는 조직 ID 또는 기타 레이블이 포함됩니다. 이 구조는 메모리의 계층적 구성을 가능하게 합니다. 그런 다음 콘텐츠 필터를 통해 네임스페이스 간 검색이 지원됩니다.
import { InMemoryStore } from "@langchain/langgraph";

const embed = (texts: string[]): number[][] => {
    // Replace with an actual embedding function or LangChain embeddings object
    return texts.map(() => [1.0, 2.0]);
};

// InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use.
const store = new InMemoryStore({ index: { embed, dims: 2 } });
const userId = "my-user";
const applicationContext = "chitchat";
const namespace = [userId, applicationContext];

await store.put(
    namespace,
    "a-memory",
    {
        rules: [
            "User likes short, direct language",
            "User only speaks English & TypeScript",
        ],
        "my-key": "my-value",
    }
);

// get the "memory" by ID
const item = await store.get(namespace, "a-memory");

// search for "memories" within this namespace, filtering on content equivalence, sorted by vector similarity
const items = await store.search(
    namespace,
    {
        filter: { "my-key": "my-value" },
        query: "language preferences"
    }
);
메모리 스토어에 대한 자세한 내용은 지속성 가이드를 참조하세요.
Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I