Harness Case Study

카파시의 트윗 한 줄이 3시간 만에
진짜 명령어 도구가 됐다

트윗 한 줄짜리 아이디어를 실제로 돌아가는 프로그램으로 만들다 보면, 눈에 띄는 기능 몇 개만 만들고 완성이라 부르기 쉽다. 이 글은 그 함정을 하네스(harness, AI가 ‘다 됐다’고 대충 끝내지 못하게 증거를 강제하는 자동 감시 장치)로 막으면서 만든 기록이다. 아이디어의 출처는 AI 연구자 안드레이 카파시가 트위터에 던진 한 줄, “LLM(ChatGPT 같은 AI)이 스스로 관리하는 지식 저장소”다. 결과는 커밋(코드 저장 기록) 4번, 파이썬 코드 2,214줄, 원글이 요구한 기능 100% 구현이었다.

4
Commits
2,214
Lines of Code
23
Tests Passing
100%
Feature Coverage
~3h
Total Time
시작은 카파시의 트윗 한 줄이었다
“It would be nice to have a single markdown-based knowledge base where an LLM is responsible for maintaining and compiling it. You feed it raw data (articles, papers, images, clippings), and the LLM compiles it into a structured wiki with backlinks and indexes. You then query it, generate slides, charts, reports from it.” — Andrej Karpathy, @karpathy on X

이 트윗 한 줄이 실제로 쓰는 명령어 도구가 되려면 무엇이 필요했나. 답은 제대로 된 하네스, 즉 작업이 진짜 끝났는지 자동으로 검증해 주는 장치였다. 여기서 명령어 도구란 터미널에 명령을 타이핑해 실행하는 프로그램을 말한다. 개발자들이 줄여 부르는 영어 약어는 CLI

raw/
LLM compile
wiki/
LLM query
output/
articles, papers, images → structured wiki → slides, charts, reports
구현 과정을 커밋 4번으로 따라가기
8093409 · 2026-04-05 00:33
지식 저장소 전체를 한 번에 구현 (카파시가 제안한 구조 그대로)
커밋 한 번에 프로젝트 뼈대를 통째로 세웠다. (이 타임라인에서 각 항목 머리의 일곱 자리 영숫자는 커밋 해시, 즉 그 커밋을 가리키는 식별 번호다.) 담긴 것은 이렇다. 명령어 도구의 하위 명령 7개, Claude를 코드에서 부르는 공식 라이브러리(Anthropic SDK) 연결, 바뀐 문서만 다시 처리하는 증분 컴파일, 슬라이드 만들기(Marp)와 차트 그리기(matplotlib), 파이썬 웹 화면(Flask), 자동 테스트 23개(pytest), 그리고 단어 빈도로 문서를 찾는 검색이다. 이 검색 방식을 부르는 이름은 TF-IDF
39 files · +3,073 lines · src/lkb/(11) + tests/(5) + .claude/(5) + wiki/(5) + samples
dadd74d · 2026-04-05 00:50
AI를 부르는 경로를 두 갈래로 나눠 구독(Claude Code)과 API 키(Anthropic SDK)를 둘 다 지원
유료 API 키가 없어도 Claude Code 구독만으로 돌아가게 했다. 그러려고 AI를 부르는 경로를 둘로 나눴다. 어떤 경로를 쓸지는 프로그램이 자동으로 감지하고, LKB_BACKEND 라는 실행 설정값으로 직접 지정할 수도 있다. 그리고 llm.py 라는 중간 연결 코드가 두 경로를 티 안 나게 알아서 이어준다.
src/lkb/llm.py · 코드 135줄 추가
e7e359e · 2026-04-05 00:58
버그 수정: AI 답변에서 슬라이드·리포트에 쓸 문서 서식(마크다운)만 자동으로 뽑아내기
Claude Code가 슬라이드 내용에 잡담 같은 설명 문장을 섞어서 답하던 문제를 고쳤다. 텍스트에서 원하는 패턴만 골라내는 규칙(정규식)을 써서 마크다운 부분만 뽑아낸다. 그래서 깔끔한 슬라이드와 리포트가 나온다.
src/lkb/output.py · +60 -15 lines
08a1a19 · 2026-04-05 01:33
누락됐던 기능 4개를 추가해 원글이 요구한 기능을 100% 채움
카파시의 원글을 다시 읽어 빠진 기능 4개를 찾아 넣었다. 추가한 기능은 차례로 다음과 같다. lint --deep(AI가 문서 뜻을 읽고 빠진 주제를 찾는 깊은 점검), output --file(만든 결과물을 위키 저장소에 되넣기), websearch.py(웹 검색 모듈. Tavily 서비스로 찾고, 안 되면 Claude가 대신 찾는다), ask further questions(답변 뒤에 이어서 파고들 질문 3개를 자동으로 붙이기).
8 files · +481 lines · lint.py +143 · websearch.py +111 · ask.py/output.py/cli.py 코드 확장
하네스가 대신 처리해 준 것들

코드 2,214줄과 테스트 23개가 단 3시간 만에 나온 이유가 여기 있다. 하네스가 아래 일들을 자동으로 처리해 줬기 때문이다. 각 카드의 제목은 하네스 안에서 그 일을 맡는 명령이나 파일의 이름이다.

/init-project
프로젝트의 첫 설정을 잡아 주는 명령이다. 프로젝트를 훑어 파이썬 3.11 이상이라는 기술 구성을 자동으로 알아낸다. 그 내용을 .claude/CLAUDE.md라는 규칙 파일에 빌드 명령·구조·기술 스택 표로 정리해 준다.
.claude/skills/
이 프로젝트만의 작업 규칙을 담은 안내 파일 4개를 자동으로 만든다. 백엔드 패턴, 구현 계획, QA(품질 검증) 시나리오, QA 전략 네 가지다. 프로젝트 방식을 고정해, 만들 때마다 기준이 흔들리지 않게 한다.
/team Phase 0~3
기능 단위(모듈) 11개로 이뤄진 src/lkb/ 구조를 먼저 설계한 뒤, 구현을 나눠 맡긴다. 명령어·AI 연결·컴파일·검색·출력·점검·웹 화면을 각 담당 에이전트(역할을 나눠 맡은 AI 일꾼)에게 분배한다.
/qa-scenario-gen
테스트 시나리오를 자동으로 만든다. 저장한 데이터를 만들고·읽고·고치고·지운 뒤 다시 불러와 맞는지 확인하는 왕복 검사, 증분 컴파일, 문서끼리의 연결(백링크)이 안 깨졌는지, 인터넷 없이도 되는지 등 테스트 상황 23개를 뽑아냈다. 왕복 검사의 네 동작을 영문 머리글자로 줄여 부르는 이름은 CRUD
/qa-cycle
자동 테스트 23개를 전부(23/23) 통과시킨다. 코드 검사·정리 도구(ruff)도 반복해서 돌린다. 실패하면 버그 수정 담당을 자동으로 부른다. 앞의 세 번째 커밋이 바로 그렇게 나온 것이다.
/self-improve
원글을 다시 분석해 빠진 기능 4개를 스스로 찾아냈다(네 번째 커밋). ‘이미 100% 됐다’는 판단을 일부러 의심하고 되짚어 검증하는 순환 구조(폐루프)다.
/loopy-era-eval
‘완성’의 기준을 숫자 하나로 못 박는다. 원글 대비 구현 비율(커버리지 %)을 강제로 측정하고, 기준에 못 미치면 그 결과를 버린다.
.completion-check.md
기능 18개를 하나씩 ‘통과/실패’로 표시하고, 각 근거 파일을 연결한 표를 만든다. 원글의 어느 요구가 됐고 안 됐는지 추적할 수 있는 완료 점검 문서를 자동으로 유지한다. 아래 대조표의 초록색 영문 표시가 바로 그 ‘통과’다.
구현 결과: 기능 단위(모듈) 11개
모듈(기능 단위)줄 수역할
cli.py155사용자가 명령어를 입력하는 진입점(Click 라이브러리 사용). 하위 명령 7개: 수집·컴파일·질문·검색·출력·점검·웹
compile.py327원본(raw/) 폴더를 정리된 위키(wiki/)로 변환. 파일 수정 시각(mtime)을 기준으로 바뀐 것만 다시 처리하고, 문서끼리의 연결(백링크)을 자동 생성
output.py323슬라이드(Marp)·차트(matplotlib)·마크다운 리포트 생성. 여기에 --file 옵션을 붙이면 결과물을 위키에 되넣는다
llm.py281AI 호출 경로 2개(구독형 Claude Code, API 키형 Anthropic SDK)를 관리. 사용량(토큰) 집계와 실패 시 재시도 포함
lint.py359문서를 실행 없이 검사해 깨진 링크를 찾고, --deep 옵션을 켜면 AI가 뜻까지 읽어 빠진 주제나 앞뒤 모순을 잡아낸다
ingest.py174파일이나 웹 주소(URL)에서 문서를 가져온다. 이미지는 자동으로 내려받고, 브라우저 웹 스크랩 도구와도 호환
web.py195파이썬 웹 서버(Flask)로 만든 다크 모드 웹 화면. 문서 둘러보기와 검색을 맡는다
search.py124TF-IDF라는 단어 빈도 방식의 본문 검색. 한 문서 안에서는 자주 나오고 다른 문서에는 드문 단어에 무게를 주는 고전적인 방법이다(명령어와 웹 화면 양쪽에서 사용)
websearch.py111웹 검색은 Tavily 서비스로, 안 되면 Claude가 대신. 점검(lint)에서 빠진 주제의 참고 링크를 제안
ask.py85질문·답변 기능 + 이어서 파고들 질문 3개 자동 생성
config.py77실행 설정값과 파일 경로 관리
23/23
pytest pass
PASS
ruff check
18/18
feature coverage
2
LLM backends
원글이 요구한 기능, 100% 다 구현했나 대조표
카파시가 원글에서 말한 것구현(초록 글자는 통과)증거
raw → compile → wikiPASScompile.py 바뀐 것만 처리하는 증분 컴파일
목차·요약 자동 생성PASS_index.md, _summaries.md
문서 간 연결(백링크, 노트 앱 Obsidian과 호환)PASS[[...]] 이중 대괄호 표기 문법
Q&APASSask.py
이어서 파고들 질문 제안PASS답변 끝에 ‘더 알아볼 질문’ 3개 첨부
슬라이드 생성(Marp)PASSoutput slides
차트 생성(matplotlib)PASSoutput chart
마크다운 리포트PASSoutput report
Output → Wiki filingPASS--filewiki/outputs/
문서 점검(깨진 링크 찾기)PASSlint.py 실행 없이 검사
AI 의미 점검(deep lint)PASSlint --deep: 빠진 글 4개 탐지
빠진 자료를 웹 검색으로 보완PASSwebsearch.py Tavily, 안 되면 Claude
본문 검색(명령어)PASSsearch.py
웹 화면PASSweb.py Flask
CLI as LLM toolPASS다른 프로그램에서 불러 쓰는 예시를 안내 문서에 명시
Obsidian Web ClipperPASS안내 문서에 연동 가이드 수록
AI 호출 경로 2개PASSClaude 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