AI·로컬 LLM

LLM 지식 그래프 오염: PKM 자동화 시스템 복구 경험

홈랩지기 2026. 8. 22. 13:25

automoney 프로젝트를 시작하며 개인 지식관리 시스템(PKM)을 자동화하는 데 공을 많이 들였다. 궁극적인 목표는 내가 직접 개입하지 않아도 콘텐츠가 생성되고, 관리되고, 발행되는 파이프라인을 구축하는 거였다.

이걸로 https://write83676.tistory.com에 글을 30개 채워 애드센스 승인을 받는 게 당면 과제였다. 이미 #5 라다 스왑 삽질기, #6 플레이라이트 경험 노트 등 몇 개의 글을 발행했지만, 그 뒤에는 복잡한 시스템이 돌아가고 있었다. 로컬 LLM이 자료를 조사하고, 정리하고, 지식 그래프를 업데이트하는 과정이 매일 오전 9시 30분 cron에 맞춰 publish/daily_run.py 스크립트를 통해 자동으로 진행됐다.

초반에는 꽤 만족스러웠다. 새로운 자료가 들어오면 관련 태그와 요약이 자동으로 붙고, 기존 지식 그래프에 매끄럽게 연결되는 것처럼 보였다.

내 옵시디언(Obsidian) 노트는 마치 살아있는 유기체처럼 스스로 진화하는 듯했다. 내가 직접 신경 쓸 필요 없이, 시스템이 알아서 지식을 큐레이션하고 연결해준다는 생각에 뿌듯했다.

 

문제의 조짐은 어느 날 옵시디언 그래프 뷰를 열었을 때 포착됐다. 분명히 본 적 없는 텅 빈 노트들이 그래프 한켠에 잔뜩 튀어나와 있었다. 그뿐만 아니었다. 기존에 잘 연결되어 있던 문서들 사이의 링크가 엉뚱한 곳을 가리키거나, 아니면 아예 링크 자체가 끊어져 있었다.

마치 잘 정리된 도서관에 누군가 쓰레기 더미를 던져 넣고 책장 간의 연결을 뒤죽박죽 만들어 놓은 기분이었다. 처음엔 대수롭지 않게 여겼다. 가끔 LLM이 헛소리를 할 때도 있으니, 일시적인 오류겠거니 했다. 🤨

하지만 시간이 지날수록 그 오염은 심각해졌다. daily_run.py가 돌 때마다 지식 그래프는 점점 더 복잡하고 엉망진창이 됐다. 유효한 정보보다 알맹이 없는 '스텁 개체'들이 그래프를 가득 채웠다.

스텁 개체는 지식 그래프에서 특정 개념을 나타내는 빈 카드 같은 건데, 여기에 내용이 없으니 마치 제목만 있고 내용은 없는 위키피디아 페이지 수천 개가 내 시스템에 생긴 꼴이었다. 게다가 이 빈 카드들이 다른 유효한 카드와 엉뚱하게 연결되면서, 정보의 흐름을 완전히 방해했다. 관련 자료를 찾으려 해도 엉뚱한 곳으로 안내되니, 자동화 시스템의 의미가 퇴색되는 순간이었다.

문제 해결, 어디부터 시작해야 할까?

처음엔 캐시 문제인 줄 알았다. 그래서 관련 캐시를 지우고 시스템을 재시작해봤다. 소용 없었다. 옵시디언 플러그인 충돌일까 싶어 하나씩 비활성화해보기도 했다. 역시 아니었다.

몇 시간을 헤매고 나서야 이건 단순한 시스템 오류가 아니라, 내 자동화 로직 어딘가에 심각한 결함이 있다는 걸 직감했다. 스크립트 로그를 뒤지기 시작했다. daily_run.py가 돌아가는 과정을 한 단계씩 추적하며 어떤 데이터를 만들어내는지, 그 데이터가 그래프에 어떻게 반영되는지 면밀히 살펴봤다.

사흘 밤낮을 붙들고 코드와 로그를 파고들었다. 마치 수만 개의 레고 블록 중 잘못 조립된 몇 개를 찾는 기분이었다. 원인은 크게 네 가지였다.

  • 개체 노트 미생성 문제는 LLM이 특정 개념을 추출했는데, 그 개념에 대한 실제 옵시디언 노트가 제대로 생성되지 않은 경우였다. 지식 그래프에는 '이런 개념이 있다'는 노드(Node)만 생기고, 그 노드가 실제 가리켜야 할 노트 파일은 없는 셈이었다. 당연히 링크를 따라가도 아무것도 나오지 않았다.
  • 별칭 미등록은 같은 개념인데 다른 이름으로 불리는 경우(예: 'LLM'과 '대규모 언어 모델') 시스템이 한쪽만 인식하고 다른 쪽은 놓치는 경우가 발생했다. 이 때문에 지식 그래프상에서는 별개의 개념으로 인식되어 연결이 끊기거나, 혹은 연결돼야 할 곳에 제대로 연결되지 않는 문제가 생겼다. 별칭 처리가 중요한데, 이를 간과한 거였다.
  • 링크 목표 중복은 여러 개의 개념이 동일한 목표 링크를 가지게 되는 경우가 있었다. 예를 들어, 서로 다른 두 문서가 'AI 에이전트'라는 동일한 노드를 참조하게 되면서, 시스템 내부에서 어떤 문서가 실제 'AI 에이전트'의 메인 문서인지 혼동하는 상황이었다. 이는 데이터베이스의 유니크 제약 조건을 무시하고 데이터를 넣으려 할 때 발생할 수 있는 문제와 비슷했다. 마치 두 사람이 같은 주민등록번호를 가졌다고 착각하는 셈이다.
  • 잡개체 필터 부재는 LLM이 지식을 추출하는 과정에서 불필요하거나 임시적인 '잡개체(junk object)'까지 모두 지식 그래프에 반영해버렸다. 예를 들어, 단순한 수사적 표현이나 문맥상 의미 없는 단어들이 개념으로 추출되어 독립적인 노드로 박히는 식이었다. 이런 잡개체들이 그래프에 쌓이니 지식의 밀도는 낮아지고, 노이즈만 가득해졌다. SearXNG로 주간 화제를 수집하는 publish/trends.py에서 불필요한 키워드가 걸러지지 않고 그대로 유입되는 문제도 있었다. SearXNG는 쿼리 간 1.5초 간격으로 호출하며 rate limit에 걸리지 않도록 조심했지만, 그래도 LLM은 때때로 의도치 않은 결과를 만들어냈다.

 

네 가지 문제, 해결 방법은?

원인을 파악하고 나니 해결책은 비교적 명확해졌다. 각 원인에 맞춰 daily_run.py의 로직을 수정했다.

  • 개체 노트 미생성 문제는 노트 생성 전에 해당 개념의 유효성을 다시 한번 검증하고, 노트가 생성되지 않았을 경우 강제로 재생성하거나 경고를 띄우도록 했다.
  • 별칭 미등록은 LLM이 생성한 별칭 목록을 확인하고, 기존 지식 그래프의 별칭과 비교하여 누락된 부분을 채워 넣는 로직을 추가했다.
  • 링크 목표 중복은 새로운 링크를 추가하기 전에 기존 그래프에서 동일한 목표를 가진 링크가 있는지 확인하고, 있다면 기존 링크를 업데이트하거나 적절한 병합 로직을 적용하도록 수정했다.
  • 잡개체 필터 부재는 LLM의 출력물에서 불필요한 개체를 걸러내는 정규식 기반의 필터링 모듈을 추가했다. 특정 단어 길이나 문맥 패턴을 기준으로 필터링하니, 훨씬 깔끔한 지식 그래프가 만들어졌다.

가장 중요한 건, 이미 오염된 과거 데이터를 어떻게 할 것인가였다. 단순히 코드만 수정해서는 지식 그래프에 쌓인 수많은 쓰레기가 사라지지 않았다. 그래서 daily_run.py 스크립트에 --migrate라는 마이그레이션 플래그를 추가했다.

이 플래그를 붙여 스크립트를 실행하면, 기존에 저장된 모든 지식 그래프 데이터를 처음부터 다시 스캔하여 위에 언급된 네 가지 문제를 기준으로 클리닝 작업을 수행하도록 했다. 마치 엉망진창이 된 도서관을 폐쇄하고, 모든 책을 다시 분류하고 정리하는 대규모 작업과 비슷했다. 이 작업은 꼬박 12시간 가까이 걸렸지만, 깨끗해진 그래프를 보니 속이 다 시원했다. ✨

예상치 못한 함정: 파일 변경 감지의 배신 🫠

삽질 끝에 문제 해결에 성공했다고 생각했다. 그런데 기쁨도 잠시, 또 다른 문제가 수면 위로 떠올랐다. daily_run.py에는 publish/trends.py를 통해 수집된 최신 트렌드를 반영하거나, 블로그 발행을 위한 미러링 작업 등 여러 부가 기능이 포함되어 있었다. 이 기능들이 파일 변경 감지에만 의존한다는 것을 뒤늦게 깨달은 것이다.

즉, 며칠간 새 자료 입력이 없자, 시스템이 '할 일이 없다'고 판단하고 모든 작업을 멈춰버린 것이었다. 미러링 작업까지 같이 멈춘 걸 모르고 멍하니 기다리다가 며칠 분량의 발행이 밀린 적도 있었다.

이는 매일 오전 9시 30분에 cron으로 스크립트가 실행되는 것과는 별개로, 스크립트 내부의 특정 감시 루프가 파일 변경 이벤트를 기다리는 로직으로 설계되었기 때문이었다. 아무런 입력이 없으면 그저 대기 상태로 유지되는 것이다. 이런 상황을 막기 위해 감시 루프에 주기 실행 로직을 병행하도록 수정했다.

파일 변경이 없더라도 최소한 하루에 한 번은 중요한 미러링이나 트렌드 업데이트 작업을 강제로 실행하게 만든 것이다. 예를 들어, 0.1%의 미러링 누락이 1시간에 3.6초라면, 한 달이면 거의 3분에 가까운 누락이 생기는 셈이다.

이런 미세한 차이가 결국 전체 시스템의 신뢰도를 갉아먹는다.

이번 경험을 통해 데이터 무결성의 중요성과 시스템 설계의 견고함에 대해 다시 한번 생각하게 됐다. 특히 AI를 활용한 자동화 시스템에서는 LLM의 유연성 뒤에 숨어있는 데이터 오염 가능성을 늘 염두에 두어야 한다.

그리고 단순히 기능을 구현하는 것을 넘어, 시스템이 예상치 못한 상황에서도 안정적으로 동작하도록 다양한 예외 상황과 엣지 케이스를 고려하는 것이 중요하다. 어쨌든 이 삽질 덕분에 automoney 프로젝트는 한층 더 단단해졌다.

※ 이 글은 직접 겪은 작업 경험을 바탕으로 AI의 도움을 받아 작성했습니다.