AILangChainLLMRAG

LangChain의 기억과 품질 관리

·8 min read

RAG, 대화 챗봇을 만들면 중요한게 대화 어떻게 기억하고 품질을 어떻게 관리하느냐다.

대화 기억 관리

LLM이 내용을 처리하고 기억하는 단위는 Context Window(4K 등...)다.

이 컨텍스트 윈도우를 넘어가는 데이터는 처리를 못하는데, 즉 기억을 못한다는 뜻이다.

정해진 용량이 있으면? 공간을 효율적으로 써야하는데, 효율적인 기억 처리를 위해 제공되는게 'Memory' 컴포넌트다.

ConversationBufferMemory

일단 효율적이지는 않고 아무튼 기억을 위해 모든 대화 기록을 원본 그대로 저장하고, 다음 요청 시 프롬프트에 전부 전달한다.

아주 심플이즈 베스트한 방법이다.

단점은 알겠지만 데이터가 누적(프롬프트가 복사가 된다고!)되니 대화가 길어지면 금방 Context Window를 초과해 버린다.

ConversationBufferWindowMemory

아까 방식에서 조금 타협을 봐서 최근 'K'개의 대화만 기억한다.

단점은 알겠지만 'K'개 이전의 대화는? 잊어버린다.

ConversationSummaryMemory

이제 효율성을 따지기 시작한다.

대화가 쌓일 때마다 LLM 호출해서 현재까지의 대화 내용을 요약한다.

다음 요청에는 전체 기록 대신 요약본을 프롬프트에 넣는다.

단점은 LLM 호출 비용이 추가로 발생한다는 것과, 디테일이 소실 될 수 있다는 것이다.

ConversationSummaryBufferMemory

처음에 Buffer가 있었고 아까 Summary를 소개했다.

이번거는 두 방식의 하이브리드로 최근 K개의 대화는 원본, 그 전에 대화는 요약해서 관리한다.

나름대로 균형적인 선택이다.

VectorStoreRetrieverMemory

대화 기록을 벡터 DB에 저장하고, 현재 사용자의 질문과 **의미론적으로 관련된 과거 대화만 검색(Retrieve)**해서 프롬프트에 주입하는 방식이다.

RAG 방식을 우리 대화에 응용한 거라고 보면 된다.

품질 유지 기법

아까 소개했듯이 RAG를 우리 기억에 활용할 수 있다.

다만 그냥 단순히 대화를 저장한다고 하면 일반적인 외부 지식이 되버린다.

저장된 벡터를 효율적으로 가져오는 방안이 필요하다.

Query Transformation (쿼리 변환)

사용자가 모호한 질문("그거 어땠지?")하면 LLM을 이용해 명확한 검색 쿼리("지난주 논의된 LangGraph 프로젝트 진행 상황 어땠지?")로 먼저 변환한 후 RAG를 수행하는 방식이다.

Hybrid Search(하이브리드 검색)

검색은 크게 **키워드 검색(BM25: 정확성)**과 **의미 기반(Vector Search: 유사성)**의 검색 방식이 있다.

두가지 방식을 결합해야 검색 누락을 방지 할 수 있다.

Re-ranking(재정렬)

품질을 신경쓰다보면 자연스럽게 연산 비용이 증가한다.

재정렬은 RAG 검색 시 일단 넉넉하게 2030개 정도 문서 가져오고, 더 가볍고 빠른 모델(Re-ranker)을 사용해 현재 질문과 **가장 관련성이 높은 TOP 35개만 추리는 방식**이다.

추려진 문서만 LLM에 전달하면? LLM이 처리할 Context 부담과 노이즈가 줄어드는 것이다.

품질 측정 및 제어

기억을 하기 위해 RAG가 들어가고, 고급화된 기법으로 품질 유지하면서 나름 자신있게 검색을 때렸다.

처음에는 잘 동작하리라 생각한다.

근데 AI라는게 사실 "잘 모르겠고 데이터 왕창 넣고 돌리니 나왔어요"라는 미지의 영역과 함께하는 놈이다.

시스템 복잡해지면 어느순간 품질 유지가 안되는 시점이 올 수 있다는 것이다.

품질에 대한 관리가 필요하다는 것이다.

아까의 과정들이 LangChain에 범위였다면, 지금부터는 LangSmith와 LangGraph가 들어간다.

LangSmith(추적 및 평가)

LangSmith는 잘 작동하는지 측정, 추적, 디버깅하는 애다.

Lang 친구들(Chain, Agent, Graph)이 동작하면 입출력, 호출, 뭐 썻는지, RAG 결과 까지 자동 수집해서 대시보드로 전송한다.

os.environ["LANGCHAIN_TRACING_V2"] = "true"

환경변수에 설정만 해줘도 동작하는 라이브러리다.

Smith 없으면 품질 작살났을때 "아니 왜 갑자기 이상현상이?" 싶은데,

애가 뭘 하다가 실패했는지 과정을 추적할 수 있으니 꼭 써보자

LangGraph: 자가 수정

LangChain이 A->B->C 라면

LangGraph는 **'상태'**와 **'순환'**을 도입한다.

반복해서 자기 점검이 가능하다는 것이다.

상태를 들고 있으니 조건부 처리가 가능한데, 생성->평가->재시도 루프의 순환 구조를 만들 수 있다는 것이다.

검색(RETRIVE): input 받고 document 검색해서 상태 업데이트

생성(GENERATE): 응답 생성하고 상태 업데이트

평가(EVALUATE): 문서랑 응답 받아서 답변이 문서를 잘 참조했는지 평가 <- 여기가 중요!!!

  • 잘했어? => PASS

  • 못했어? => 수정하고 다시 해 => 다음에 수정 노드 하나 있어서 여기서 INPUT 다시 가공

기본 구조가 위와 같으니 여기서 조건을 뭘 둘지,

그냥 IF로 '죄송' => 응 다시해 로 두거나

아예 평가자 에이전트 하나 둬가지고 외주 맡겨도 된다.

LangChain은 기본적인 실행 주고, LangGraph는 기본적인 구조를 주고, LangSmith는 기본적인 데이터를 준다.

기본 재료로 어떻게 잘 조리해다가 깊은 맛을 내는게 경험의 진수가 아닐까

그런 의미로 슬슬 나만의 시스템을 한번 만들어봐야겠다.

← Previous
AI 시대 생존 방법 by 노정석 대표, 카이스트 김대식 교수
Next →
데이터 AI한테 외주주기