트윗 한 줄짜리 아이디어를 실제로 돌아가는 프로그램으로 만들다 보면, 눈에 띄는 기능 몇 개만 만들고 완성이라 부르기 쉽다. 이 글은 그 함정을 하네스(harness, AI가 ‘다 됐다’고 대충 끝내지 못하게 증거를 강제하는 자동 감시 장치)로 막으면서 만든 기록이다. 아이디어의 출처는 AI 연구자 안드레이 카파시가 트위터에 던진 한 줄, “LLM(ChatGPT 같은 AI)이 스스로 관리하는 지식 저장소”다. 결과는 커밋(코드 저장 기록) 4번, 파이썬 코드 2,214줄, 원글이 요구한 기능 100% 구현이었다.
이 트윗 한 줄이 실제로 쓰는 명령어 도구가 되려면 무엇이 필요했나. 답은 제대로 된 하네스, 즉 작업이 진짜 끝났는지 자동으로 검증해 주는 장치였다. 여기서 명령어 도구란 터미널에 명령을 타이핑해 실행하는 프로그램을 말한다. 개발자들이 줄여 부르는 영어 약어는 CLI
8093409 · 2026-04-05 00:33TF-IDFsrc/lkb/(11) + tests/(5) + .claude/(5) + wiki/(5) + samplesdadd74d · 2026-04-05 00:50LKB_BACKEND 라는 실행 설정값으로 직접 지정할 수도 있다. 그리고 llm.py 라는 중간 연결 코드가 두 경로를 티 안 나게 알아서 이어준다.
src/lkb/llm.py · 코드 135줄 추가e7e359e · 2026-04-05 00:58src/lkb/output.py · +60 -15 lines08a1a19 · 2026-04-05 01:33lint --deep(AI가 문서 뜻을 읽고 빠진 주제를 찾는 깊은 점검), output --file(만든 결과물을 위키 저장소에 되넣기), websearch.py(웹 검색 모듈. Tavily 서비스로 찾고, 안 되면 Claude가 대신 찾는다), ask further questions(답변 뒤에 이어서 파고들 질문 3개를 자동으로 붙이기).
lint.py +143 · websearch.py +111 · ask.py/output.py/cli.py 코드 확장코드 2,214줄과 테스트 23개가 단 3시간 만에 나온 이유가 여기 있다. 하네스가 아래 일들을 자동으로 처리해 줬기 때문이다. 각 카드의 제목은 하네스 안에서 그 일을 맡는 명령이나 파일의 이름이다.
/init-project.claude/CLAUDE.md라는 규칙 파일에 빌드 명령·구조·기술 스택 표로 정리해 준다..claude/skills//team Phase 0~3src/lkb/ 구조를 먼저 설계한 뒤, 구현을 나눠 맡긴다. 명령어·AI 연결·컴파일·검색·출력·점검·웹 화면을 각 담당 에이전트(역할을 나눠 맡은 AI 일꾼)에게 분배한다./qa-scenario-genCRUD/qa-cycle/self-improve/loopy-era-eval.completion-check.md| 모듈(기능 단위) | 줄 수 | 역할 |
|---|---|---|
cli.py | 155 | 사용자가 명령어를 입력하는 진입점(Click 라이브러리 사용). 하위 명령 7개: 수집·컴파일·질문·검색·출력·점검·웹 |
compile.py | 327 | 원본(raw/) 폴더를 정리된 위키(wiki/)로 변환. 파일 수정 시각(mtime)을 기준으로 바뀐 것만 다시 처리하고, 문서끼리의 연결(백링크)을 자동 생성 |
output.py | 323 | 슬라이드(Marp)·차트(matplotlib)·마크다운 리포트 생성. 여기에 --file 옵션을 붙이면 결과물을 위키에 되넣는다 |
llm.py | 281 | AI 호출 경로 2개(구독형 Claude Code, API 키형 Anthropic SDK)를 관리. 사용량(토큰) 집계와 실패 시 재시도 포함 |
lint.py | 359 | 문서를 실행 없이 검사해 깨진 링크를 찾고, --deep 옵션을 켜면 AI가 뜻까지 읽어 빠진 주제나 앞뒤 모순을 잡아낸다 |
ingest.py | 174 | 파일이나 웹 주소(URL)에서 문서를 가져온다. 이미지는 자동으로 내려받고, 브라우저 웹 스크랩 도구와도 호환 |
web.py | 195 | 파이썬 웹 서버(Flask)로 만든 다크 모드 웹 화면. 문서 둘러보기와 검색을 맡는다 |
search.py | 124 | TF-IDF라는 단어 빈도 방식의 본문 검색. 한 문서 안에서는 자주 나오고 다른 문서에는 드문 단어에 무게를 주는 고전적인 방법이다(명령어와 웹 화면 양쪽에서 사용) |
websearch.py | 111 | 웹 검색은 Tavily 서비스로, 안 되면 Claude가 대신. 점검(lint)에서 빠진 주제의 참고 링크를 제안 |
ask.py | 85 | 질문·답변 기능 + 이어서 파고들 질문 3개 자동 생성 |
config.py | 77 | 실행 설정값과 파일 경로 관리 |
PASS| 카파시가 원글에서 말한 것 | 구현(초록 글자는 통과) | 증거 |
|---|---|---|
| raw → compile → wiki | PASS | compile.py 바뀐 것만 처리하는 증분 컴파일 |
| 목차·요약 자동 생성 | PASS | _index.md, _summaries.md |
| 문서 간 연결(백링크, 노트 앱 Obsidian과 호환) | PASS | [[...]] 이중 대괄호 표기 문법 |
| Q&A | PASS | ask.py |
| 이어서 파고들 질문 제안 | PASS | 답변 끝에 ‘더 알아볼 질문’ 3개 첨부 |
| 슬라이드 생성(Marp) | PASS | output slides |
| 차트 생성(matplotlib) | PASS | output chart |
| 마크다운 리포트 | PASS | output report |
| Output → Wiki filing | PASS | --file → wiki/outputs/ |
| 문서 점검(깨진 링크 찾기) | PASS | lint.py 실행 없이 검사 |
| AI 의미 점검(deep lint) | PASS | lint --deep: 빠진 글 4개 탐지 |
| 빠진 자료를 웹 검색으로 보완 | PASS | websearch.py Tavily, 안 되면 Claude |
| 본문 검색(명령어) | PASS | search.py |
| 웹 화면 | PASS | web.py Flask |
CLI as LLM tool | PASS | 다른 프로그램에서 불러 쓰는 예시를 안내 문서에 명시 |
| Obsidian Web Clipper | PASS | 안내 문서에 연동 가이드 수록 |
| AI 호출 경로 2개 | PASS | Claude Code + SDK |
| 바뀐 것만 처리하는 증분 컴파일 | PASS | 파일 수정 시각 기준 |
굵은 글씨 6개는 첫 버전(v0.1)에서 빠졌다가, 하네스의 self-improve(자기 개선) 단계가 원글을 다시 분석해 두 번째 버전(v0.2)에 채워 넣은 기능이다.
트윗을 읽고 “오, 흥미로운데” 하고 그냥 넘긴다. 만든다 해도 기본 변환 기능(compile.py)까지만 만든다. 문서 점검(lint)과 두 번째 AI 호출 경로, 결과 되넣기(filing)는 전부 빠뜨린다. 원글을 다시 펼쳐 볼 이유가 없기 때문이다.
3시간 만에 기능 11개, 코드 2,214줄이 나온다. 게다가 self-improve 단계가 ‘다 끝났다’는 선언을 의심하고 원글을 다시 검증해서, 빠진 기능 4개를 스스로 찾아낸다. 원글 요구의 100% 달성까지 자동으로 추적한다.
첫 버전(v0.1)의 ‘완성’을 의심하지 않았다면 lint --deep, output --file, 웹 검색(websearch), 후속 질문(further questions), 이 4개 기능은 영영 빠진 채였을 것이다. 앞의 두 명령은 차례로 AI 의미 점검(문서의 뜻까지 읽어 빠진 주제를 찾기)과 결과 되넣기(만든 결과물을 위키에 저장하기)다. 하네스의 self-improve 단계가 ‘원글을 다시 읽어라’는 규칙을 강제로 실행한 결과가 바로 08a1a19 커밋, 즉 네 번째 코드 저장 기록이다.
llm-knowledge-base(LKB)가 이 글에서 다룬 프로젝트의 저장소 이름이고, 괄호 안은 그 줄임말이다 (2026-04-05 작업).
Inspired by @karpathy's LLM Knowledge Bases