커뮤니티 200개를 LLM으로 요약했더니, 20장으로 전체가 커버됐습니다
커뮤니티를 찾았는데, 그대로는 못 쓰는 이유
이전 글에서 Leiden 알고리즘으로 그래프에서 밀접하게 연결된 노드 클러스터를 자동으로 찾는 이야기를 했거든요. 인증서 갱신 관련 이메일 15건이 하나의 클러스터, 서버 교체 관련 20건이 또 하나의 클러스터로 잡히는 식이었습니다.
그런데 클러스터를 찾은 것과 검색에 쓸 수 있는 것은 다른 문제였어요. Leiden이 알려주는 건 "이 노드들이 한 덩어리다"라는 정보뿐이거든요. 이 덩어리가 "무엇에 관한 건지"는 모릅니다.
넓은 질문이 왔을 때 "이 클러스터를 봐"라고 할 수 있으려면, 클러스터의 내용을 요약해둬야 했어요. 그래야 "올해 인프라 이슈 정리해줘"라는 질문에 요약을 검색해서 매칭할 수 있으니까요.
클러스터 안의 문서를 LLM으로 요약했습니다
요약 방식은 단순합니다. 클러스터에 속한 노드들의 정보를 모아서, LLM한테 "이 묶음이 무엇에 관한 건지 요약해줘"라고 맡기는 거예요.
예를 들어 인증서 갱신 클러스터에 이메일 15건이 속해 있으면, 15건의 제목·본문·관련 엔티티를 LLM에 넣고 요약 1장을 뽑습니다. "2025년 3~4월 물류서버 SSL 인증서 갱신 과정. 담당자 A가 진행, 중간에 와일드카드 인증서 이슈로 2주 지연. 최종적으로 ACM 인증서로 교체 완료" — 이런 식의 요약이 나오는 거죠.
이 요약을 Neo4j에 별도 노드로 저장하고, Qdrant에도 임베딩으로 넣습니다. 그러면 넓은 질문이 왔을 때 개별 이메일 200건이 아니라 요약 20~30장을 벡터 검색할 수 있게 돼요. 검색 대상이 200건 → 20장으로 줄었는데, 전체 내용은 다 커버되는 구조입니다.

요약을 계층으로 쌓으면 줌인/줌아웃이 됩니다
요약을 한 단계만 만들면 "인증서 갱신"이라는 세부 주제만 잡힙니다. 그런데 "올해 인프라 이슈 전체 정리해줘"라는 질문은 인증서 갱신보다 훨씬 넓은 범위거든요. 세부 요약 20~30장을 전부 검색해서 LLM에 넣으면 토큰이 너무 커지고요.
여기서 계층 구조가 필요해졌습니다. 작은 요약을 다시 묶어서 큰 요약을 만드는 거예요.
Level 0 (작은 덩어리)
- 인증서 갱신 관련 15건 → 요약 1장
- 서버 교체 관련 20건 → 요약 1장
Level 1 (중간 덩어리)
- 물류시스템 전체 (Level 0 묶음) → 요약 1장
Level 2 (큰 덩어리)
- 인프라 운영 전반 (Level 1 묶음) → 요약 1장Level 0 요약은 원본 문서를 직접 보고 만들고, Level 1 요약은 Level 0 요약들을 보고 만들고, Level 2 요약은 Level 1 요약들을 보고 만듭니다. 요약의 요약을 만드는 거예요.
이러면 질문의 범위에 따라 검색 레벨을 바꿀 수 있습니다. "인증서 갱신 누가 했어?"는 Level 0에서 답을 찾고, "올해 물류시스템 이슈 정리해줘"는 Level 1에서, "인프라 전체 현황 알려줘"는 Level 2에서 찾는 거죠. 줌인/줌아웃이 되는 겁니다.

모든 문서가 반드시 어떤 커뮤니티에 속합니다
이 구조에서 중요한 특성이 하나 있어요. Leiden 알고리즘은 그래프의 모든 노드를 어떤 커뮤니티에 배정합니다. 빠지는 노드가 없거든요.
이전 글에서 다뤘던 top-K 문제를 떠올려보면, 관련 문서 200건 중 top-K 20건만 가져오면 나머지 180건은 검색 자체가 안 되는 게 문제였잖아요. 커뮤니티 요약에서는 그 180건도 어떤 클러스터에 속해 있고, 그 클러스터의 요약에 내용이 반영돼 있습니다. 데이터 누락이 구조적으로 불가능한 거예요.
top-K를 올려서 더 많이 가져오는 접근과 근본적으로 다릅니다. top-K를 200으로 올리면 리랭크 시간이 폭발하고 토큰도 감당이 안 되는데, 요약 20~30장을 검색하는 건 기존 top-K 20과 비슷한 비용이거든요. 비용은 같은데 커버리지가 100%인 셈이에요.
파이프라인 어디에 들어가는가
커뮤니티 요약은 엔티티 추출과 그래프 임포트가 끝난 뒤에 실행됩니다. 그래프 구조를 기반으로 클러스터링하는 거니까, 노드와 관계가 다 들어간 상태에서만 의미가 있거든요.
전처리 → 엔티티 추출 → 그래프 임포트 → [커뮤니티 탐지 + 요약] → RAG 검색기존 파이프라인을 건드리지 않고, 그래프 임포트와 RAG 검색 사이에 한 단계를 추가하는 구조예요. 기존 벡터 검색(Local Search)은 그대로 동작하고, 커뮤니티 요약 검색(Global Search)이 병렬로 추가되는 겁니다.
요약 노드는 Neo4j에 CommunityReport 타입으로 저장하고, 어떤 노드들이 이 커뮤니티에 속하는지 관계로 연결해둡니다. Qdrant에도 요약 텍스트의 임베딩을 넣어서 벡터 검색이 가능하게 하고요.
Leiden resolution 파라미터가 요약 품질을 좌우합니다
실제로 요약을 만들어보니까, Leiden의 resolution 파라미터에 따라 결과가 크게 달라지더라고요.
resolution이 너무 높으면 클러스터가 잘게 쪼개져서 이메일 2~3건짜리 클러스터가 대량으로 생깁니다. 요약할 내용이 너무 적어서 요약이 원본과 거의 같아지는 문제가 생겨요. 반대로 너무 낮으면 클러스터가 커져서 관련 없는 문서끼리 묶이고, 요약이 뭉뚱그려져서 검색 정확도가 떨어집니다.
데이터를 보면서 조절해야 하는 부분이에요. 클러스터당 노드 수가 10~30개 정도일 때 요약 품질이 가장 괜찮았고, Level 0에서 이 크기가 나오도록 resolution을 맞추고 나서 Level 1, 2를 쌓는 순서로 진행했습니다.
요약이 있으면 검색 경로가 달라집니다
커뮤니티 요약이 준비되면, 넓은 질문과 좁은 질문에서 검색 경로가 나뉘게 됩니다. 좁은 질문은 기존 벡터 검색(개별 문서 top-K)으로 처리하고, 넓은 질문은 커뮤니티 요약을 검색해서 처리하는 구조예요.
이 경로를 어떻게 나누느냐 — Global Search와 Local Search를 어떤 기준으로 선택하느냐가 다음 과제입니다. LLM이 질문의 범위를 판단해서 자동으로 경로를 선택하게 할지, 아니면 다른 방법이 있을지는 다음 글에서 정리하겠습니다.