LLM Wiki로 20년치 흩어진 문서 정리하기

소개

20여 년 회사 생활 동안 쌓인 문서가 여기저기 흩어져 있었습니다. Dropbox, 로컬 PC, 외장하드, 구글드라이브에 나뉘어 있고, 폴더 이름만 봐서는 어디에 뭐가 있는지 알 수 없었습니다. 문제는 두 가지였습니다.

  1. 필요할 때 못 찾는다 — "그 견적서 어디 있더라"

  2. 여러 자료를 종합해 결론을 내기 어렵다 — 자료가 5개 폴더에 흩어져 있으면 전체 그림이 안 보인다

개인 자료(비망록·교회·재무)도 같은 상태였습니다.

RAG로 문서를 올려두고 질문하는 방식은 써봤지만, 질문할 때마다 처음부터 다시 찾는다는 한계가 있었습니다. 쌓이는 게 없었습니다.

그러던 중 Andrej Karpathy가 공개한 LLM Wiki 패턴을 봤습니다. 핵심은 이겁니다 — LLM이 마크다운 위키를 직접 쓰고 유지하게 하면, 지식이 질문할 때마다 재구성되는 게 아니라 누적되는 자산이 된다. 사람은 자료를 고르고 질문하고, 요약·교차참조·정리 같은 잡일은 LLM이 한다는 역할 분담도 마음에 들었습니다.

그래서 회사 업무용과 개인용 위키 두 개를 만들어봤습니다.


진행 방법

사용 도구

  • Claude Code (Opus) — 위키를 실제로 쓰고 유지하는 주체

  • Python 표준 라이브러리 + openpyxl / pypdf / pymupdf — 엑셀·PDF 판독

  • git — 위키가 마크다운이라 버전 관리가 공짜로 따라옵니다

시작 프롬프트

처음 던진 프롬프트는 이 한 줄이 전부였습니다.

https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f 읽고
나만의 llm wiki 구조를 만들어줘

Claude가 gist를 읽고 도메인·저장 위치를 물어본 뒤, 저장소 뼈대를 만들었습니다.

구조 — Karpathy 원문은 3레이어, 저는 4레이어

원문은 raw(원본) / wiki(LLM이 쓰는 지식) / schema(운영 규칙) 3층입니다. 그런데 문서가 2만 건이 넘으니 전부 위키에 넣는 건 불가능했습니다. 그래서 층을 하나 더 얹었습니다.

1. 카탈로그   "어디 있는지"    ← 파일 목록만 훑음. 토큰 0
 ↓ 선별 (사람이 덩어리 단위로 판단)
2. 원본 제자리에 그대로 ← 옮기지 않음
 ↓
3. 위키 "무엇을 아는지" ← 고른 것만 ingest
 ↑
4. 스키마 CLAUDE.md ← 운영 규칙서

이 카탈로그 층이 이번 시도에서 가장 큰 수확이었습니다. 파일을 열지 않고 목록만 훑으니 2만 8천 건 스캔에 1분, 토큰은 0입니다. 그런데 "못 찾겠다" 문제의 절반이 여기서 바로 해결됐습니다.

실제 디렉터리

llm-wiki/
├── CLAUDE.md # 운영 규칙서 (핵심)
├── catalog/ # 원본 문서 목록 (스캔 산출물)
│ ├── work-dropbox.csv # 파일 1건 = 1행. 엑셀에서 열림
│ └── work-dropbox.md # 사람이 읽는 요약
├── index.md # 전역 카탈로그
├── log.md # append-only 연대기
├── raw/ # 원본 (불변)
├── wiki/
│ └── <도메인>/
│ ├── index.md
│ ├── sources/ # 소스 1개 = 페이지 1개
│ ├── entities/ # 사람·회사·제품
│ ├── concepts/ # 개념·패턴
│ └── syntheses/ # 질문에서 승격된 분석
├── templates/
└── tools/

가장 중요한 건 CLAUDE.md

232줄짜리 운영 규칙서입니다. 이게 없으면 LLM은 그냥 챗봇이고, 있으면 규율 있는 위키 관리자가 됩니다. 핵심 규칙 몇 개만 옮기면:

## 3. INGEST — 새 소스 넣기
1. 읽는다
2. 논의한다 — 핵심 요지 3~5개를 사람에게 먼저 말한다
3. 도메인을 정한다
4. 소스 페이지를 쓴다
5. 파급 페이지를 갱신한다
 - 한 소스가 5~15개 페이지를 건드리는 게 정상이다.
 1개만 건드렸다면 교차참조를 놓친 것이다.
6. 모순을 처리한다 — 지우지 말고 ## 미해결 쟁점에 양쪽을 남긴다
7. 인덱스를 갱신한다
8. 원본을 처리한다 (카탈로그에서 찾은 소스는 옮기지도 복사하지도 않는다)
9. 로그를 남긴다
10. 보고한다
## 10. 절대 하지 말 것
- raw/ 안의 파일 내용 수정·삭제
- 인덱스·로그 갱신 없이 위키 페이지만 고치기
- 근거 소스 없이 사실을 단정하기 (근거 없으면 (추론) 표시)
- 모순을 조용히 덮어쓰기 — 항상 양쪽을 남긴다
- 사람 승인 없이 페이지 삭제·병합, 새 도메인 생성, 이 스키마 변경

시행착오를 겪을 때마다 이 파일에 규칙을 추가했습니다. 그게 다음 세션으로 전달되는 유일한 방법이라서요.

도구 6종 (656줄, 표준 라이브러리만)

python tools/catalog-scan.py "<폴더경로>" --name <이름> [--class 회사]
python tools/catalog-find.py <낱말> [--구분 회사] [--ext xlsx] [--since 2020]
python tools/wiki-search.py "질의어" [--domain d] [--type concept]
python tools/wiki-lint.py
python tools/new-domain.py <slug> "<표시 이름>" "<  설명>"

카탈로그 스캐너의 핵심 부분입니다. 파일을 열지 않고 메타데이터만 수집합니다.

def scan(root: Path):
 rows, errors = [], 0
 root = root.resolve()
 for dirpath, dirnames, filenames in os.walk(root, onerror=lambda e: None):
 dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS]
 d = Path(dirpath)
 rel_dir = d.relative_to(root)
 top = rel_dir.parts[0] if rel_dir.parts else "(최상위)"
 for fn in filenames:
 fp = d / fn
 st = fp.stat() # 메타데이터만
 rows.append({
 "구분": CLASSIFY.get(top, "미분류"), # 회사/개인 자동 분류
 "최상위폴더": top,
 "경로": str((rel_dir / fn).as_posix()),
 "파일명": fn,
 "확장자": fp.suffix.lower().lstrip(".") or "(없음)",
 "크기KB": round(st.st_size / 1024),
 "수정일": datetime.fromtimestamp(st.st_mtime).strftime("%Y-%m-%d"),
 })
 return rows, errors

wiki-lint.py는 위키 건강검진입니다. 기계가 할 수 있는 것만 잡고, 모순·낡은 주장 같은 의미 점검은 LLM이 따로 합니다.

frontmatter 누락 / slug 중복 / 깨진 [[링크]] / 고아 페이지 / 인덱스 미등재 / 400줄 초과 / 180일 미갱신

스캔·검색 실제 화면

$ python tools/catalog-scan.py "C:/Users/Admin/Dropbox" --name work-dropbox --class 회사
스캔 중: C:\Users\Admin\Dropbox …
 구분 '회사' 만 담는다
파일 6,316개 수집
 → catalog/work-dropbox.csv
 → catalog/work-dropbox.md
$ python tools/catalog-find.py 견적 --ext xlsx --since 2020
2026-07-29 [회사] 16KB 모던디앤씨 기타 견적현황 260413.xlsx
 C:\Users\Admin\Dropbox\절삭공구\WORLDIA\...\모던디앤씨 기타 견적현황 260413.xlsx
2026-07-02 [회사] 10KB 견적검토 260702.xlsx
 C:\Users\Admin\Dropbox\절삭공구\WORLDIA\...\미코세라믹스\견적검토 260702.xlsx
...
19건 중 상위 5건.

📷 (여기에 캡처 삽입: 카탈로그 검색 결과 / Obsidian 그래프 뷰 / 위키 페이지 화면)

세션을 이어가는 방법

며칠에 걸쳐 작업하다 보니 대화를 새로 시작해도 이어지게 만들어야 했습니다. wiki/_meta/session-YYYY-MM-DD-<주제>.md에 「한 일 / 결정과 근거 / 현재 상태 / 다음에 할 일 / 재개하는 법」을 남기고, 다음 세션은 이 한 줄로 시작합니다.

CLAUDE.md 읽고 wiki/_meta/의 최신 세션 기록부터 이어서 하자

결과와 배운 점

만들어진 것

회사 위키

개인 위키

커밋

9

17

위키 페이지

21

15

도메인

ESG, 기술

재무, 교회, 비망록

카탈로그

6,316건

15,194건

lint

ERROR 0

ERROR 0

도구 656줄, 운영 규칙서 232줄. 저장소 크기는 마크다운만 담아서 몇 MB입니다 (원본 바이너리는 gitignore).

실제로 나온 결과

숫자보다 이게 중요할 것 같습니다.

① ESG 진단 — 개선 우선순위가 뒤집혔습니다 협력사 ESG 진단 자료를 넣었더니 자가진단 397점 → 검증 359점이었습니다. 처음엔 "온실가스 측정이 +10점으로 최우선"이라는 결론이 나왔는데, 나중에 산업별 가중치(지배구조 53.96%)를 발견하고 다시 계산하니 온실가스는 +0.13점, 관리시스템이 +1.69점으로 완전히 역전됐습니다.

② 연금 계약 9건의 행방을 전부 찾았습니다 2023년과 2026년 통합연금포털 자료를 대조했더니 계약 5건이 "사라져" 있었습니다. 가입일이 열쇠였습니다 — 연금저축은 계좌이체를 해도 최초 가입일이 승계되는데, 그 날짜가 정확히 일치했습니다. 해지가 아니라 5개 기관에 흩어진 연금저축을 한 곳으로 통합한 것이었습니다.

③ 보험 대장이 5년 반 만에 현행화됐습니다 파일 폴더는 2020년 10월에 멈춰 있었는데, 협회 조회를 넣으니 폴더에 없던 유지 계약이 7건 나왔습니다.

④ 교회 자료 6,320건의 구조가 잡혔습니다 개별 문서를 한 건도 열지 않고 폴더·파일명만으로 두 폴더가 중복이 아니라 시기·역할로 갈렸다는 것, 20년 사역 이력을 정리했습니다.

시행착오 — 5가지 다 적습니다

1. 카탈로그 층을 처음부터 안 만들어서 사흘을 썼습니다 ESG 자료 5건을 전부 정독하는 방식으로 시작했습니다. 그런데 카탈로그를 만들고 나서 검색하니 더 최신인 2023년판 자료가 이미 있었습니다. 뭘 가졌는지 모르는 채로 가진 것 중 일부만 붙들고 작업한 셈입니다. → 규칙화: "카탈로그를 건너뛰고 대량 문서를 바로 ingest하려 들지 말 것. 끝나지 않는다."

2. 가중치 없는 점수로 우선순위를 매겨서 결론이 뒤집혔습니다 66개 지표를 단순 합산하면 모든 지표가 같은 무게가 됩니다. 실제로는 산업군 가중치가 붙었고, 순위가 완전히 달라졌습니다. → 교훈: 1차 자료의 원시 숫자만 보지 말고, 그 숫자가 최종 결론으로 가는 산식을 먼저 확인할 것.

3. 파일명 기반 추론이 세 번 빗나갔습니다

  • 개인정보 파일 10건으로 의심 → 실제로는 3건 (나머지는 "홍길동" 예시가 든 빈 서식)

  • 보험 대장을 파일명으로 작성 → 협회 조회와 크게 다름

  • 안전관리 기록 "2개월 공백" 발견 → 종이로 작성하고 계셨음 → 규칙화: "파일 목록에서 나온 판단은 항상 무엇이 파일로 남았는가이지 무엇을 했는가가 아니다. 파일 부재를 근거로 '안 했다'고 쓰지 말 것."

4. 전제를 확정하지 않고 시작해서 13개 페이지를 두 번 고쳤습니다 "SK 평가와 NICE 진단 중 어느 쪽이 공식인가"라는 전제가 흔들리면서, 위상 표기를 하루 사이에 뒤집었다가 되돌렸습니다. → 교훈: 새 도메인은 "이 도메인의 기본 전제"를 먼저 확정하고 ingest에 들어갈 것. 가장 비싼 건 자료 판독이 아니라 전제가 바뀌어서 다시 고치는 것입니다.

5. 세션 인수인계를 빼먹으면 하루가 뒤처집니다 대화를 지우기 전에 세션 기록을 안 남겼더니, 새 세션이 하루 전 지점에서 시작하려 했습니다.

꿀팁 3가지

① 스캔 PDF는 pymupdf로 렌더링 + 컨택트 시트 154페이지 스캔 슬라이드를 이미지 4장으로 압축해서 읽었습니다.

# 1) 텍스트 레이어 확인 — 슬라이드는 제목만 남아 있어 목차를 만들 수 있다
# 2) 본문이 벡터면 이미지 추출이 안 되므로 렌더링
d = fitz.open(pdf); d[n].get_pixmap(dpi=110).save("page.png")
# 3) 페이지가 많으면 저해상도 썸네일을 격자로 합쳐 한 장으로 훑고,
# 필요한 페이지만 원해상도로 다시 렌더링

② 카탈로그 CSV는 gitignore, 요약 md만 커밋 5MB CSV가 스캔할 때마다 통째로 바뀌면 git 이력이 부풉니다. 재생성에 1분이면 되니 요약만 남깁니다.

③ 원본은 옮기지 말고 경로만 기록 88GB를 복제할 이유가 없습니다. 소스 페이지의 origin:에 절대경로를 씁니다.

도움이 필요한 부분

  • 비정형 개인 기록(비망록)의 페이지 구조를 어떻게 잡아야 할지 모르겠습니다. 현재 페이지 타입은 source / entity / concept / synthesis인데, 비망록의 단위는 사건과 시기라 잘 안 맞습니다. event 타입을 추가할까 고민 중입니다.

  • 필기 앱 백업(.nbk, .nsa) 같은 독자 포맷을 읽을 방법을 못 찾았습니다.

  • 위키가 수백 페이지로 커지면 인덱스+grep으로 부족할 텐데, qmd 같은 로컬 검색으로 갈아탈 시점을 언제로 잡아야 할지 궁금합니다.

앞으로의 계획

  1. 비망록 도메인 본격 착수 — 26년치 개인자료부터. 학습 이력(대학원 2번 포함)과 교회 사역 이력을 시간축으로 엮으면 뼈대가 나올 것 같습니다

  2. 회사 문서 6,316건 중 KBM_경영 4,312건(2004~2026, 20년치) 선별 ingest

  3. Obsidian vault로 열어서 그래프 뷰로 보기

  4. 카탈로그를 외장하드·구글드라이브까지 확장


도움 받은 글

  • Andrej Karpathy, "LLM Wiki"https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f 이 사례의 출발점입니다. 3레이어 구조와 ingest/query/lint 세 연산, index.md·log.md 규약을 그대로 가져왔습니다. 원문이 "구조는 도메인에 맞춰 각자 인스턴스화하라"고 명시하고 있어서, 카탈로그 층을 얹는 것도 그 취지에 맞다고 봤습니다.

  • qmdhttps://github.com/tobi/qmd (마크다운 로컬 검색. 아직 안 썼지만 규모가 커지면 도입 예정)

3
밀어주고 끌어주는

온·오프라인 AI 스터디

AI로 어디까지 할 수 있는지
직접 확인하실 분만 신청하세요.