티스토리 뷰
LangChain long-term memory 품질은 어떻게 올릴까? 기억할 정보 추출, 중복 제거, profile vs collection 튜닝 방법
octo54 2026. 6. 8. 11:03LangChain long-term memory 품질은 어떻게 올릴까? 기억할 정보 추출, 중복 제거, profile vs collection 튜닝 방법
LangChain long-term memory 품질을 올리려면 무엇이든 다 저장하는 방식부터 버려야 합니다. 공식 문서도 long-term memory는 여러 대화를 넘어가는 user-specific 또는 application-level data를 저장하는 용도라고 설명하고, memory를 언제 저장할지에 대해 hot path와 background를 구분해 보라고 안내합니다. 또 semantic memory 설계에서는 profile 방식과 collection 방식이 각각 장단점이 있다고 분명히 말해요. 즉, 잘 되는 long-term memory는 “기억을 많이 하는 시스템”이 아니라 기억할 정보만 고르고, 중복을 줄이고, 저장 구조를 의도적으로 설계한 시스템에 가깝습니다. (LangChain Docs)
저는 이 구간이 되게 중요하다고 생각합니다.
long-term memory를 처음 붙이면, 뭔가 엄청 똑똑해질 것 같거든요.
근데 막상 해보면 반대예요.
아무 말이나 다 저장하면 오히려 더 멍청해집니다.
- 중요하지 않은 말이 너무 많아지고
- 비슷한 기억이 중복 저장되고
- 지금 질문과 상관없는 정보가 계속 끼어들고
- 검색 결과가 지저분해집니다
그래서 이번 글은 long-term memory를 붙이는 법이 아니라,
long-term memory를 쓸 만하게 만드는 법에 집중하겠습니다.
이 글에서 바로 답하는 질문
- LangChain long-term memory는 어떤 정보를 저장해야 할까
- profile 방식과 collection 방식은 무엇이 다를까
- 중복 memory는 어떻게 줄일까
- hot path 저장과 background 저장은 언제 나눠야 할까
- semantic search 품질은 어떻게 올릴까
- FastAPI 백엔드에서 long-term memory 품질 관리는 어디서 할까
long-term memory 품질이 안 나오는 가장 흔한 이유
가장 흔한 이유는 정말 단순합니다.
기억할 가치가 없는 것까지 다 저장하기 때문입니다.
LangChain 메모리 개요 문서는 memory update를 hot path에서 할 수도 있고 background에서 할 수도 있다고 설명하면서, 결국 중요한 건 무엇을 언제 업데이트할지라고 말합니다. long-term memory는 여러 세션을 넘어 공유되기 때문에, short-term memory보다 훨씬 더 선별적으로 다뤄야 해요. (LangChain Docs)
예를 들어 이런 건 long-term memory로 별로입니다.
- “고마워”
- “오케이”
- “다시 말해줘”
- “오늘은 피곤하네”
반면 이런 건 long-term memory 후보가 될 수 있습니다.
- “앞으로는 짧게 답해줘”
- “나는 재무팀에서 일해”
- “관리자 권한은 없어”
- “다크 모드를 선호해”
- “출장비 정책을 자주 물어볼 거야”
즉 long-term memory의 출발점은
사용자 장기 성향, 선호, 반복되는 사실, 지속적인 제약입니다.
이 기준이 없으면 품질이 금방 무너집니다. (LangChain Docs)
profile 방식 vs collection 방식, 무엇이 더 좋을까?
LangChain 공식 문서는 semantic memory 설계에서 profile 방식과 collection 방식을 따로 설명합니다. profile은 사용자 정보를 하나의 JSON 문서처럼 유지하는 방식이고, collection은 개별 memory 문서를 여러 개 저장하는 방식입니다. 공식 문서도 둘 다 장단점이 있다고 설명해요. (LangChain Docs)
1. profile 방식
예를 들면 이런 구조입니다.
{
"response_style": "short",
"theme_preference": "dark",
"department": "finance",
"job_role": "employee"
}
좋은 점은 깔끔하다는 겁니다.
- 한 번에 보기 쉽고
- 프론트나 백엔드에서 읽기 편하고
- 설정처럼 다루기 좋습니다
문제는 업데이트입니다.
공식 문서도 profile 문서는 구조적이지만, 더 많은 메모리를 한 문서에 압축할수록 업데이트가 어려워질 수 있다고 설명합니다. 기존 사실과 새 사실이 충돌할 때 안전하게 merge하는 것도 까다롭고요. (LangChain Docs)
2. collection 방식
예를 들면 이런 식입니다.
- User prefers short answers
- User works in finance team
- User likes dark mode
좋은 점은 추가가 쉽다는 겁니다.
- 새 정보 하나를 문서처럼 더하면 되고
- semantic search와 잘 맞고
- recall이 좋아질 수 있습니다
공식 문서도 collection 접근은 일반적으로 더 높은 recall을 제공할 수 있다고 설명합니다. 대신 저장된 memory가 많아질수록 검색, 정리, 중복 제거가 중요해진다고 볼 수 있어요. (LangChain Docs)
그래서 뭘 선택하면 좋을까?
처음엔 collection 방식이 더 편합니다.
정말로요.
이유는 간단합니다.
- 추가가 쉽고
- semantic search 붙이기 좋고
- 실패해도 profile 전체를 망치지 않습니다
반면 profile은 깔끔하지만,
업데이트 전략이 성숙하지 않으면 오히려 더 쉽게 꼬입니다.
그래서 초반엔 collection, 나중에 안정화되면 일부 profile 필드만 따로 유지하는 식이 꽤 현실적입니다. 이건 공식 문서의 장단점 설명과도 잘 맞습니다. (LangChain Docs)
어떤 정보를 저장해야 진짜 도움이 될까?
이게 핵심입니다.
저는 보통 long-term memory 저장 후보를 아래 네 가지로 봅니다.
1. 답변 스타일 선호
- 짧게 답해달라
- 예시 위주를 좋아한다
- 표보다 문장 설명을 좋아한다
2. 사용자 고정 정보
- 소속 팀
- 역할
- 권한 범위
- 주로 다루는 업무
3. 반복되는 관심사
- 출장비 정책을 자주 본다
- 배포 관련 질문을 자주 한다
- 문서 검색보다 상태 조회를 자주 쓴다
4. 지속적인 제약
- 관리자 권한 없음
- 특정 시스템 접근 불가
- 영어보다 한국어 선호
공식 문서에서 말하는 semantic memory는 결국 이런 류의 “user-specific facts and preferences”에 가깝습니다. 장기적으로 도움이 되는 사실이어야 하고, thread를 넘어 재사용 가치가 있어야 합니다. (LangChain Docs)
반대로 “이번 대화 안에서만 필요한 내용”은 short-term memory에 맡기는 게 맞습니다.
이 구분이 안 되면 long-term memory store가 금방 지저분해집니다. (LangChain Docs)
중복 memory는 왜 생기고, 어떻게 줄일까?
이건 long-term memory 품질을 무너뜨리는 아주 흔한 원인입니다.
예를 들어 사용자가 세 번에 걸쳐 비슷한 말을 할 수 있어요.
- “앞으로 짧게 답해줘”
- “답변은 길지 않았으면 좋겠어”
- “간단하게만 말해줘”
이걸 그대로 세 개 저장하면, semantic search 때 비슷한 memory가 여러 개 튀어나옵니다.
그러면 retrieval이 산만해집니다.
공식 문서가 collection 방식의 장점으로 recall을 말하는 대신, 사실상 그만큼 정리 전략이 중요하다는 뜻이기도 해요. 그리고 hot path와 background 저장을 구분하는 이유 중 하나도 이런 consolidation 문제입니다. (LangChain Docs)
중복 줄이는 가장 쉬운 방법 3가지
1. 저장 전 문자열 정규화
예:
- 소문자화
- 공백 정리
- 너무 짧은 문장 제외
2. semantic search로 기존 유사 memory 먼저 찾기
새 memory를 넣기 전에 같은 namespace에서 비슷한 memory를 검색합니다.
3. 비슷하면 새로 넣지 않고 skip하거나 merge
완전히 같은 뜻이면 추가 저장하지 않는 거예요.
아래는 아주 단순한 예시입니다.
import uuid
from langgraph.store.memory import InMemoryStore
from langchain.embeddings import init_embeddings
embeddings = init_embeddings("openai:text-embedding-3-small")
store = InMemoryStore(
index={
"embed": embeddings,
"dims": 1536,
}
)
def save_user_memory_if_new(user_id: str, text: str) -> bool:
namespace = ("memories", user_id)
similar = store.search(namespace, query=text, limit=3)
for item in similar:
existing = item.value["data"].strip().lower()
current = text.strip().lower()
if existing == current:
return False # 완전 중복이면 저장 안 함
store.put(
namespace,
str(uuid.uuid4()),
{"data": text},
)
return True
Store.search()와 namespace 기반 저장은 LangGraph memory 문서의 기본 패턴입니다. semantic search를 같이 쓰면 유사 memory 검사도 같은 방식으로 붙일 수 있어요. (LangChain Docs)
물론 운영에서는 완전 문자열 일치보다
semantic similarity threshold 기반 중복 제거가 더 좋습니다.
하지만 출발점으로는 이 정도만 해도 꽤 효과가 있습니다.
hot path 저장 vs background 저장, 언제 갈라야 할까?
LangChain 공식 문서는 memory update를 크게 두 방식으로 나눕니다.
- hot path: 응답 생성 과정 안에서 바로 저장
- background: 응답 이후 비동기/배치로 저장
그리고 deep agents 문서는 대부분의 앱에서는 hot path로 시작해도 되지만, memory 품질을 더 올리거나 latency를 줄이고 싶을 때 background consolidation을 고려하라고 설명합니다. (LangChain Docs)
hot path가 좋은 경우
- 저장 규칙이 단순하다
- 즉시 반영이 중요하다
- 저장량이 많지 않다
예:
- “앞으로 짧게 답해줘” → 바로 저장
background가 좋은 경우
- 대화 전체를 보고 요약해야 한다
- 여러 memory를 merge해야 한다
- 중복 제거와 정리가 중요하다
- 응답 latency를 줄이고 싶다
예:
- 하루치 대화를 훑고 사용자 선호를 정리
- 비슷한 memory들을 하나로 묶기
저는 초반엔 hot path가 좋다고 봅니다.
이유는 단순해요. 이해하기 쉽고, 바로 효과를 보기 쉽거든요.
다만 memory가 늘어나기 시작하면 background consolidation이 필요해집니다. 공식 문서도 이 흐름을 분명히 설명합니다. (LangChain Docs)
semantic search 품질은 어떻게 올릴까?
long-term memory는 보통 semantic search와 같이 갑니다.
그래서 저장 품질뿐 아니라 검색 품질도 중요해요.
LangGraph memory 문서는 InMemoryStore(index={...})로 semantic search를 켜고, query 기반으로 관련 memory를 찾는 패턴을 보여줍니다. 또한 namespace로 검색 범위를 나누는 게 기본입니다. (LangChain Docs)
검색 품질을 올리는 현실적인 방법
1. memory 문장을 너무 짧게 저장하지 말기
나쁜 예:
- short answers
좋은 예:
- User prefers short answers
2. memory 문장을 너무 길게 저장하지 말기
너무 길면 핵심이 흐려집니다.
3. namespace를 잘 나누기
예:
- ("memories", user_id)
- ("preferences", user_id)
- ("facts", user_id)
LangGraph Store는 namespace tuple을 지원하고, 검색 범위 설계에 이걸 적극 활용하라고 보는 게 맞습니다. (LangChain Docs)
4. 질문에 맞는 검색 query를 만들기
그냥 마지막 사용자 메시지를 그대로 넣는 대신,
필요하면 “이 사용자의 답변 스타일 선호” 같은 의도형 query를 만들 수도 있습니다.
예를 들어 사용자가
“요약해서 말해줘”
라고 물었을 때, memory 검색 query를
user response style preference
같이 더 명시적으로 바꿔볼 수 있어요.
이건 문서에 직접 적혀 있는 고정 패턴은 아니지만, 공식 문서의 namespace와 semantic search 개념을 응용한 실전적인 튜닝 포인트입니다. (LangChain Docs)
FastAPI 백엔드에서는 어디서 memory 품질을 관리해야 할까?
이건 꽤 중요합니다.
저는 보통 이렇게 봅니다.
router
HTTP 처리만
service
- user_id와 thread_id 연결
- memory 저장/조회 호출 타이밍 결정
agent
- memory를 프롬프트 문맥에 반영
- 최종 답변 생성
store layer
- namespace 설계
- 중복 제거
- 검색 정책
- persistent backend 교체
FastAPI는 큰 앱에서 여러 파일과 계층을 나누라고 권장하고, LangChain도 state, store, tool, agent를 성격별로 분리하는 방향이 강합니다. 그러니까 long-term memory 품질 관리는 보통 service + store layer에 두는 게 가장 자연스럽습니다. (LangChain Docs)
즉 “무엇을 기억할지”는 service 정책이고,
“어떻게 저장하고 찾을지”는 store 정책입니다.
운영으로 갈 때 꼭 바뀌는 것
공식 문서는 개발 단계에서 InMemoryStore 예제를 많이 보여주지만, 운영에서는 PostgreSQL, Redis, MongoDB 같은 persistent backend 예시를 함께 제공합니다. long-term memory 문서는 store가 JSON 문서를 custom namespace와 key 아래 저장한다고 설명하고, production backend 예시도 보여줘요. (LangChain Docs)
즉 운영으로 가면 보통 이런 변화가 생깁니다.
- InMemoryStore → PostgresStore 또는 Redis/Mongo 계열
- 단순 hot path 저장 → background consolidation 추가
- 단일 namespace → memory 종류별 namespace 분리
- 단순 exact duplicate 제거 → semantic similarity dedupe
이걸 처음부터 다 할 필요는 없지만,
“지금 구조가 나중에 persistent backend로 바뀔 수 있어야 한다”는 감각은 꼭 가져가면 좋습니다.
비교: profile 방식 vs collection 방식
구분profile 방식collection 방식
| 저장 형태 | 사용자당 하나의 JSON 문서 | 여러 memory 문서 |
| 장점 | 깔끔하고 읽기 쉬움 | 추가 쉽고 recall 좋음 |
| 단점 | 업데이트/merge 까다로움 | 중복/정리 필요 |
| 초반 난이도 | 조금 높음 | 비교적 쉬움 |
| 추천 시점 | 구조가 안정됐을 때 | 처음 시작할 때 |
이 비교는 공식 문서가 설명하는 장단점을 그대로 실전 관점으로 풀어쓴 겁니다. 저는 특히 처음엔 collection 쪽이 더 낫다고 봅니다. (LangChain Docs)
FAQ
Q. LangChain long-term memory는 꼭 semantic search를 붙여야 하나요?
꼭 그런 건 아닙니다. 하지만 공식 문서도 store에 semantic search를 붙이는 예시를 기본적으로 보여주고, long-term memory retrieval에 꽤 잘 맞습니다. 특히 collection 방식에서는 semantic search가 거의 필수에 가까워집니다. (LangChain Docs)
Q. profile 방식이 더 정돈돼 보이는데 왜 collection을 먼저 추천하나요?
profile은 예쁘지만 업데이트가 더 어렵습니다. 공식 문서도 profile 문서가 커질수록 update/delete가 어려워질 수 있다고 설명합니다. collection은 시작이 훨씬 단순해서 초반엔 실수가 적습니다. (LangChain Docs)
Q. “짧게 답해줘” 같은 말은 바로 저장해도 되나요?
네, 그런 건 hot path에서 바로 저장하기 좋은 예입니다. 공식 문서도 hot path memory update를 자연스러운 선택지로 설명합니다. 다만 저장 규칙이 너무 많아지면 응답 latency가 늘 수 있으니, 나중엔 background로 일부를 옮기는 걸 고려하면 됩니다. (LangChain Docs)
Q. 중복 memory는 어떻게 없애나요?
가장 쉬운 시작은 저장 전 store.search()로 비슷한 memory를 먼저 찾고, 완전 중복이면 저장하지 않는 겁니다. 운영으로 가면 semantic dedupe 정책을 더 정교하게 만들 수 있습니다. (LangChain Docs)
Q. long-term memory를 운영에 바로 InMemoryStore로 써도 되나요?
권장하진 않습니다. 개발 단계에서는 괜찮지만, 운영에서는 persistent backend가 필요합니다. 공식 문서도 Postgres, Redis, MongoDB 계열 store 예시를 같이 제시합니다. (LangChain Docs)
핵심 요약
이번 글을 진짜 짧게 줄이면 이겁니다.
- long-term memory는 장기적으로 다시 쓸 가치가 있는 정보만 저장해야 합니다. (LangChain Docs)
- 처음엔 collection 방식이 실용적입니다. (LangChain Docs)
- 저장 전 중복 제거를 꼭 고려해야 합니다. (LangChain Docs)
- 초반엔 hot path, 커지면 background consolidation을 고려하면 됩니다. (LangChain Docs)
- 운영으로 가면 InMemoryStore 대신 persistent store로 바뀌어야 합니다. (LangChain Docs)
마무리
저는 long-term memory를 처음 붙였을 때
무조건 많이 기억하는 게 좋은 줄 알았어요.
근데 해보니까 그게 아니더라고요.
많이 기억하는 시스템보다
좋은 걸 골라서 기억하는 시스템이 훨씬 똑똑해 보였습니다.
어떤 정보를 저장할지,
비슷한 건 어떻게 묶을지,
지금 바로 저장할지 나중에 정리할지.
결국 품질은 그 디테일에서 갈립니다.
그리고 저는 그게 꽤 개발자다운 문제라고 느꼈어요.
“기억을 붙였다”보다
“기억을 설계했다” 쪽에 더 가깝거든요.
다음 글 예고
다음 글에서는
LangChain 메모리 운영은 어떻게 해야 할까? PostgresStore, Redis, namespace 설계, 삭제 정책까지 실무 관점으로 정리
로 이어가겠습니다.
이제부터는 memory를 개념으로 이해하는 걸 넘어서,
실제 운영에서 어디까지 설계해야 오래 가는지 보게 될 거예요.
출처
- LangChain Memory overview: short-term memory와 long-term memory 구분, hot path vs background, profile vs collection 설명. (LangChain Docs)
- LangGraph Add memory: InMemoryStore, semantic search, namespace, context.user_id, production store 예시. (LangChain Docs)
- LangChain long-term memory: store가 JSON documents를 custom namespace와 key 아래 저장한다고 설명. (LangChain Docs)
LangChain, LangGraph, long-term memory, semantic memory, profile vs collection, AI Agent, 사용자기억, 사내도우미, LangChain Backend, 주니어개발자
'study > langchain' 카테고리의 다른 글
- Total
- Today
- Yesterday
- SpringBoot
- nextJS
- 개발블로그
- 백엔드개발
- REACT
- CI/CD
- Python
- 생성형AI
- 쿠버네티스
- PostgreSQL
- Next.js
- nodejs
- JAX
- LangChain
- rag
- DevOps
- Express
- fastapi
- flax
- llm
- SEO최적화
- seo 최적화 10개
- 딥러닝
- 주니어개발자
- kotlin
- JWT
- 웹개발
- node.js
- Prisma
- NestJS
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
