기술

LangGraph로 이메일 RAG 돌려봤지만, 결국 지식 그래프로 갈아탔다

2026.03.237분 읽기

기업 이메일 데이터를 AI로 분석하는 프로젝트를 진행하고 있거든요. LangGraph로 RAG 시스템을 만들어서, 이메일을 검색하면 AI가 답변해주는 것까지는 구현한 상태였습니다.

여기까지는 순조로웠어요. 문제는 답변 품질을 테스트하면서부터 터졌습니다.


팀 메일을 사람으로 인식한다

aaa@comp.com이라는 주소가 있었는데, 이건 팀 공용 메일이거든요. 그런데 RAG이 이걸 "aaa"라는 사람으로 잡더라고요. "aaa가 보낸 메일 찾아줘"라고 하면, 팀 메일에서 발송된 공지성 메일을 특정 개인이 보낸 것처럼 답변을 만들어요.

왜 이런 일이 생기냐면, RAG은 이메일 본문을 텍스트 청크로 쪼개서 벡터로 저장하거든요. 이 과정에서 "이 주소가 팀 메일인지 개인 메일인지", "이 사람이 발신자인지 참조자인지" 같은 관계 정보가 날아가는 거예요.

사람이 보면 바로 아는 거잖아요. aaa@comp.com은 팀 메일이고, hong@comp.com은 홍길동 개인 메일이라는 걸. 그런데 RAG은 둘 다 그냥 "발신자 필드에 있는 텍스트"로만 처리하니까, 팀인지 사람인지를 구분할 수가 없는 거예요.

이게 한 건이면 넘어갈 수 있는데, 이메일 데이터 특성상 이런 케이스가 계속 쌓입니다. 수신자/참조자가 뒤섞이고, 포워딩된 메일에서 원래 발신자가 사라지고, 스레드가 길어지면 맥락이 완전히 꼬이더라고요.


RAG과 지식 그래프는 "검색하는 방식" 자체가 다르다

청크 사이즈를 줄여보기도 하고, 메타데이터 필터를 붙여보기도 했는데 근본적인 구조가 바뀌지 않으니까 한계가 있었어요. 그래서 지식 그래프 쪽을 들여다보기 시작했는데, 비교해보니까 둘의 차이가 명확했습니다.

RAG: "비슷한 텍스트"를 찾는다

RAG의 검색은 본질적으로 텍스트 유사도 매칭이에요. 질문을 벡터로 바꾸고, 저장된 청크 중에서 벡터 거리가 가장 가까운 걸 가져오는 구조입니다.

도서관에서 키워드로 책을 검색하는 거랑 비슷해요. "aaa 메일"이라고 검색하면 aaa가 언급된 청크를 다 가져오는데, 그게 팀 메일인지 사람인지, 발신인지 참조인지는 구분하지 않는 거예요.

단발성 질문에는 잘 맞습니다. "지난주 회의록 찾아줘" 같은 단순 검색이면, 관련 텍스트 아무거나 가져와도 답변 품질이 괜찮아요. 하지만 "aaa 팀에서 B 프로젝트 관련해서 보낸 메일만 찾아줘" 같은 맥락 질문이 들어오면, 유사도만으로는 정확한 메일을 특정할 수 없는 거예요.

지식 그래프: "관계"를 따라간다

지식 그래프는 접근 자체가 다릅니다. 데이터를 텍스트 조각이 아니라 엔티티(노드)와 관계(엣지)로 저장해요.

aaa@comp.com은 "팀" 타입 노드로, hong@comp.com은 "사람" 타입 노드로 저장되고, "hong → 소속 → aaa팀", "aaa팀 → 발신 → 공지 메일", "hong → 발신 → 업무 메일" 이런 식으로 관계가 명시됩니다.

질문이 들어오면 텍스트 유사도를 계산하는 게 아니라, 그래프 위에서 관계를 타고 이동해요. "aaa 팀에서 보낸 메일"을 물으면 aaa 노드(팀 타입)에서 출발해서 "발신" 관계를 따라가고, "hong이 보낸 메일"을 물으면 hong 노드(사람 타입)에서 출발하는 거예요. 팀과 사람이 섞일 일이 없거든요.

비유하자면 위키피디아에서 링크를 타고 들어가는 것과 비슷합니다. "마케팅팀" 페이지에 들어가면 "소속 인원" 링크, "발송한 공지" 링크가 각각 있고, 거기를 따라가면 정확한 정보가 나오는 구조예요.

핵심 차이 정리

RAG (벡터 검색)

지식 그래프

저장 단위

텍스트 청크

엔티티 + 관계

검색 방식

벡터 유사도 (코사인 거리)

관계 탐색 (그래프 순회)

맥락 유지

청크 단위로 끊김

노드 간 연결로 맥락 보존

강점

비정형 텍스트 대량 처리, 단발 질의

엔티티 간 관계 추론, 다단계 질의

약점

관계·맥락 파악 어려움

초기 구축 비용, 스키마 설계 필요

gdh-2026-03-23T04-45-04-prompt-02-image-01.png

💡 이메일은 태생적으로 "관계 데이터"다

비교를 해보고 나니까, 내 데이터가 왜 RAG이랑 안 맞았는지가 보이더라고요.

이메일이라는 게 사실 텍스트 문서가 아니라 관계 덩어리예요. 모든 메일에 발신자, 수신자, 참조자가 붙어 있고, 스레드로 연결되고, 프로젝트나 주제 단위로 묶이거든요. "누가 → 누구에게 → 무슨 건으로 → 언제 보냈는가"가 핵심인 데이터입니다.

RAG은 이 관계를 무시하고 본문 텍스트만 청크로 자르니까, 가장 중요한 정보가 사라지는 거예요. aaa@comp.com이 팀 메일이라는 건 조직도와 메일 발송 패턴을 알아야 판단할 수 있는 건데, 텍스트 유사도에는 그런 정보가 없으니까요.

지식 그래프는 이 관계 자체를 데이터로 저장하는 구조입니다. "aaa@comp.com → 타입: 팀메일", "hong → 소속: aaa팀", "이 메일 → 발신자: aaa팀, 수신자: 전체" 같은 식으로 관계가 명시되니까, 팀 메일과 개인 메일을 구분하는 게 검색 이전에 이미 해결되어 있는 거예요.

물론 지식 그래프가 만능은 아닙니다. 스키마를 설계해야 하고, "사람", "팀", "프로젝트" 같은 엔티티를 정의하고 관계를 추출하는 초기 작업이 필요해요. RAG처럼 "문서 넣으면 바로 작동"하는 구조가 아니거든요.

하지만 이메일처럼 관계가 핵심인 데이터에서는, 그 초기 비용을 들여서라도 지식 그래프로 가는 게 맞다는 판단이 들었습니다.

2.png


LangGraph 위에 지식 그래프를 올렸다

방향을 정하고 나서, 기존 이메일 데이터 일부를 지식 그래프로 옮겨봤어요. 사람, 팀, 프로젝트, 메일 스레드 같은 엔티티를 노드로 정의하고, "발신", "수신", "참조", "소속" 같은 관계를 엣지로 연결하는 식입니다.

기존 LangGraph 파이프라인은 그대로 두고, 검색 단계에서 벡터 검색 대신 그래프 쿼리가 들어가는 구조로 바꿨어요.

같은 질문을 던져봤더니 차이가 바로 나더라고요. aaa@comp.com을 사람으로 잡던 문제가 사라졌습니다. 팀 노드와 사람 노드가 분리되어 있으니까, "aaa 팀에서 보낸 메일"과 "hong이 보낸 메일"이 완전히 다른 경로로 검색되는 거예요.

특히 "B 프로젝트 관련해서 aaa 팀이 보낸 메일 중에 hong이 참조된 것만 찾아줘" 같은 복합 질문에서 차이가 컸습니다. RAG에서는 아예 답을 못 만들거나 엉뚱한 조합을 내놓았던 질문인데, 지식 그래프에서는 조건별로 관계를 타고 좁혀가면서 정확한 결과를 가져오더라고요.


마무리

정리하면, RAG은 "비슷한 텍스트 조각을 빠르게 찾아오는 시스템"이고, 지식 그래프는 "엔티티 간 관계를 따라가는 시스템"이에요. 어느 쪽이 더 낫다가 아니라, 데이터의 성격과 질문의 유형에 따라 맞는 도구가 다릅니다.

단발성 검색이 대부분이고 비정형 텍스트가 대량이라면 RAG이 효율적이에요. 반면 이메일처럼 발신자·수신자·프로젝트 간 관계가 핵심이고, "누가 누구에게 무슨 건으로" 같은 맥락 추적이 필요하면 지식 그래프 쪽을 살펴보는 게 좋습니다.

RAG 프로토타입을 먼저 만들어본 게 오히려 도움이 됐어요. "내 데이터에서 RAG이 왜 틀리는지"를 직접 확인했기 때문에, 지식 그래프로 전환할 때 어떤 엔티티와 관계를 넣어야 하는지가 명확해졌거든요. 팀 메일을 사람으로 잡는 문제를 겪어봐야, 그래프에 "타입" 속성을 넣어야겠다는 판단이 나오더라고요.