이메일 본문에서 뽑은 엔티티가 그래프를 조용히 오염시키고 있습니다
그래프가 조용히 오염되고 있었습니다
이메일을 지식그래프에 임포트할 때 LLM에게 본문을 읽혀서 엔티티를 뽑아왔거든요. System, Process, ErrorCode, 담당자 같은 것들을 자동으로 찾아내서 노드로 만들고, 그 노드들 사이에 관계까지 자동으로 걸어주는 방식이었어요.
처음엔 효율적으로 보였습니다. 사람 손 안 거치고 그래프가 풍성하게 채워졌거든요. 그런데 문서가 쌓일수록 이상한 일이 생기기 시작했어요.
같은 이름인데 라벨만 다른 노드들이 늘어났습니다 (System:CDS와 Service:CDS가 따로 존재하는 식)
본문에 살짝 등장한 단어가 잘못된 기존 노드에 연결됐습니다
ErrorCode가 엉뚱한 시스템에 붙기도 했고요
의존 관계(MANAGES, RUNS_ON)가 본문 추론으로 만들어졌는데 신뢰도가 들쭉날쭉이었습니다
직전 글에서 GDS로 사후 정리를 했지만, 사후 정리는 어디까지나 응급처치였어요. 임포트가 계속되는 한 같은 결의 오염이 또 쌓입니다. 호스가 새고 있는데 바닥만 닦는 격이거든요.
진짜 문제는 LLM에게 너무 많은 결정권을 줬다는 것이었습니다
원인을 들여다보니 단순했어요. 이메일 본문은 정제되지 않은 자유 텍스트입니다. "어제 결제 시스템 점검하다가 PMH 쪽에 영향이 갔는데..." 같은 문장에서 LLM은 "결제 시스템", "PMH", "점검", "영향" 모두를 엔티티 후보로 잡거나, 잘못된 관계를 추론하기 쉬워요.
"본문 LLM 추출"은 두 가지를 동시에 하고 있었습니다.
새 엔티티 만들기 — System, ErrorCode, Process 같은 노드를 새로 생성
기존 엔티티에 연결하기 — 본문에 등장한 단어를 기존 노드에 매칭해서 관계 생성
전자는 그래프에 잡음 노드가 늘어나는 문제고, 후자는 잘못된 매칭으로 기존 노드에 잘못된 관계가 붙는 문제였어요. 둘 다 사람 검토 없이 자동으로 일어나니까 통제가 안 됐습니다.
같은 결의 문제는 예전에 온톨로지 거버넌스에서도 풀었던 적이 있어요. LLM이 발견한 새 타입을 즉시 스키마에 넣지 않고, 거버넌스 큐에 쌓고 사람이 승인할 때만 정규 타입으로 승격하는 구조였거든요.
이메일 임포트도 같은 발상이 필요했어요. 그래프에는 사람이 승인한 것만 들어간다는 원칙을 적용해야 했습니다.
그래프엔 승인된 것만, 이메일 본문은 근거로만
새 구조의 핵심은 두 가지였어요.
첫째, 이메일 임포트에서 LLM 엔티티 추출을 대폭 축소했어요. Thread 요약만 남기고, System·ErrorCode·Process 같은 도메인 엔티티를 본문에서 만들어내는 건 전부 제거했습니다. 헤더 기반의 확정 정보(보낸사람·받는사람·소속)와 사전 매칭(이미 등록된 시스템 이름이 본문에 등장한 경우의 단순 언급 관계)만 남겨뒀어요.
둘째, 케이스(Case)라는 별도 레이어를 만들었습니다. 장애·패치·문의·회의 같은 구조화된 사건을 케이스라고 정의하고, MySQL에 별도 테이블에 저장합니다. 그래프에는 사람이 검토하고 확정한 케이스만 승격됩니다.
이메일 (Thread/Email 노드만, 메타데이터 + 요약)
↓
LLM 케이스 분류 (장애/패치/회의/요청/일반)
↓
MySQL case_record (candidate 상태)
↓
사람이 검토 + 14필드 채우기
↓
확정 시에만 → Neo4j (Incident 노드 + 관계 + Document)이메일 자체는 여전히 그래프에 있어요. Thread, Email, Person 같은 노드는 헤더 기반으로 확정 추출되니까 안전합니다. 다만 이메일 본문에서 추론된 도메인 엔티티는 케이스 확정을 거쳐야만 그래프에 붙어요.

비용 통제 — 싼 분류는 자동, 비싼 드래프트는 수동
여기서 한 가지 비용 문제가 생겨요. 모든 케이스 후보에 대해 LLM이 14개 필드를 채우는 드래프트를 만들면, 이메일이 수만 통일 때 LLM 호출 비용이 폭발합니다. 정작 사람이 검토할 케이스는 그중 일부거든요.
그래서 두 단계로 나눴어요.
1단계 (싼 분류, 자동): Thread마다 가벼운 모델로 "장애/패치/회의/요청/일반" 라벨만 붙임. case_type_hint != 일반이면 candidate 레코드 자동 생성
2단계 (비싼 드래프트, 수동): 사람이 리뷰 페이지에서 후보를 선택하고 "드래프트 생성" 버튼을 눌렀을 때만 14필드를 LLM이 채움
분류는 가볍게 모든 Thread에 돌리고, 14필드 드래프트는 진짜 검토할 것에만 돌리는 구조예요. 결과적으로 LLM 비용은 "검토 의도가 있는 케이스 수"에 비례하게 됐습니다.
단계 | 트리거 | 모델 | 대상 |
|---|---|---|---|
분류 | 자동 (이메일 임포트 시) | 가벼운 모델 | 모든 Thread |
14필드 드래프트 | 수동 (버튼 클릭) | 일반 모델 | 검토할 후보만 |
확정 (승격) | 수동 (확정 버튼) | — | 사람이 확정한 것만 |
14필드 + 근거 + 미상 상태
드래프트 생성 단계에서 LLM이 채우는 14필드는 이런 구조예요. 필수 3개, 권장 8개, 선택 3개.
필수 (3): 제목, 발생/복구 시간 중 최소 1개, 영향 시스템 1개 이상
권장 (8): 접수 시간, 영향 요약, 심각도, 장애 내역, 원인, 조치, 담당자
선택 (3): 에러 코드, 패치 결과, 재발 방지 대책
각 필드에는 두 가지 메타데이터가 같이 붙어요.
citation: 어느 메일·문서 어느 부분에서 가져온 정보인지
state: filled (값이 있음) / unknown (정보 없음을 명시) / empty (아직 검토 안 함)
unknown과 empty를 구분한 게 중요했어요. "기억 안 나는 정보"와 "아직 안 본 정보"는 의미가 다르거든요. 필수 필드가 unknown이면 확정 가능하지만, empty면 확정이 막힙니다. "모르겠으면 모르겠다고 표시하라"는 신호를 주는 거예요. 추측해서 채우는 걸 막는 장치이기도 하고요.
근거(citation)도 핵심이에요. 각 필드 옆에 "어디서 가져온 정보인가" 링크가 표시되니까, 사람이 검토할 때 원본을 바로 확인할 수 있습니다. 환각이 들어왔는지 1초 만에 검증되거든요.

승격 시 관계 프리뷰 체크박스
확정 단계에서 한 번 더 안전장치를 뒀어요. "확정 (승격)" 버튼을 누르기 전에 관계 프리뷰가 표시됩니다.
[승격 프리뷰]
☑ Incident 노드 생성
☑ AFFECTS → 결제팀 시스템
☑ AFFECTS → 정산팀 시스템
☐ CAUSES ← ErrorCode A001 (체크 해제 가능)
☑ REPORTED_IN → Thread #42LLM이 만든 14필드를 기반으로 어떤 관계가 그래프에 붙을지 미리 보여주는 거예요. 사람이 "이 ErrorCode 매칭은 잘못됐다"고 판단하면 체크를 해제할 수 있습니다. 체크 해제된 항목은 그래프에 안 들어가요.
이 체크박스가 있는 이유는, LLM이 14필드를 정확하게 채워도 매칭 단계에서 잘못 붙는 경우가 있기 때문이에요. ErrorCode "A001"이 본문에 있다고 해서 그게 이번 장애의 원인이라는 보장은 없거든요. 사람이 마지막에 한 번 더 거르는 단계입니다.
비슷한 패턴 — 사람 게이트는 그래프 운영의 공통 원칙
이 구조를 만들면서 새삼 느낀 건, 그래프에 데이터를 넣는 모든 경로마다 사람 게이트가 필요하다는 거였어요.
경로 | 사람 게이트 |
|---|---|
새 엔티티 타입 발견 | 온톨로지 거버넌스 (승격/기각) |
새 관계 타입 발견 | 온톨로지 거버넌스 |
이메일 본문 → 도메인 엔티티 | 케이스 리뷰 파이프라인 |
GDS 알고리즘 제안 | 후보 검토 + 확정 |
새 패턴이 발견될 때마다 별도의 거버넌스 큐를 만들지 말고, 같은 발상으로 묶어서 운영하면 효율이 올라갑니다.
마무리
이메일 임포트에서 LLM 엔티티 추출을 빼고, 케이스 리뷰 파이프라인을 사이에 끼웠습니다. 그래프엔 사람이 확정한 케이스만 들어가고, 이메일 본문은 근거로만 남아 있어요. 비용은 싼 분류 자동 / 비싼 드래프트 수동의 2단계로 통제했고, 승격 시 관계 프리뷰 체크박스로 마지막 한 번 더 검증합니다.
자동화는 입구를 열어주는 도구이지 무인 게이트가 아니라는 점을 다시 확인한 작업이었어요. 사후 정리(GDS)도 필요하지만, 입구에 사람 게이트를 두면 정리할 양 자체가 확 줄어듭니다.
LLM이 본문에서 자유롭게 엔티티를 뽑아서 그래프에 자동 연결하는 구조를 운영 중이라면, 그 경로 어딘가에 사람 검토 게이트를 끼우는 걸 추천합니다. 처음부터 케이스 레이어를 두는 게 가장 깔끔하지만, 운영 중이라면 사후 검증이라도 게이트로 만들어두는 게 좋아요.