내가 정리한 노트(Word/Markdown/노션)로 자기 점검 퀴즈를 만들고 푸는 로컬 도구.
핵심은 퀴즈가 아니라 근거 인용입니다. 모든 문항은 내 노트의 특정 구간에서 나오고,
evidence가 원문에 실제로 존재하는지 서버가 검증합니다. 없으면 그 문항은 버려집니다.
그래서 할루시네이션이 구조적으로 차단되고, 동시에 정리가 빈 곳이 드러납니다 —
퀴즈를 못 만드는 구간은 "정리 갭 카드"로 표시됩니다.
- 할루시네이션이 구조적으로 막힘 — 프롬프트로 "노트에서만 내라"고 말하는 게 아니라, evidence가 원문에 실제로 있는지 코드가 검증. 없으면 그 문항 자체가 안 만들어짐
- 정리가 부실한 곳을 스스로 알려줌 — 퀴즈를 못 만든 구간이 "정리 갭 카드"로 뜸. 복습 도구이자 동시에 노트 정리 상태를 점검하는 도구
- 설치 마찰이 거의 없음 — 아래 "시작하기 A"처럼, 스킬만 쓸 거면
git clone+ 스크립트 한 줄로 끝. venv도 pip install도 API 키도 필요 없음 - API 비용 0원 (스킬 경로) —
/quiz-add는 Claude Code가 직접 생성하고 검증 스크립트만 돌리는 구조라 LLM 호출 자체가 없음 - 반복 사용 비용도 0 — 문항을 미리 만들어 SQLite에 저장해두는 구조라 풀기·채점·복습·오답노트가 전부 LLM 호출 없이 동작
- 복습 타이밍을 안 챙겨도 됨 — 간격 반복(Leitner)이 "곧 잊어버릴 문항"만 알아서 골라서 다시 띄워줌
- 노트 소스가 다양함 —
.md/.docx/.txt는 물론 노션 페이지도 그대로 연동. 정리하던 도구를 안 바꿔도 됨 - 알림이 안 질림 — 하루 한 번만 뜨도록 설계돼서, 매번 뜨는 알림형 도구처럼 며칠 안에 꺼버리게 되는 일이 적음
- 완전 로컬 — 문항 생성 시 API 호출 한 번 빼면 전부 내 컴퓨터 안에서 도는 개인용 도구. 노트 내용이 서버에 안 쌓임
처음 써보는 분들을 위한 체크리스트입니다. 컴퓨터에 뭐가 깔려 있어야 하는지 잘 모르겠다면 여기부터 확인하세요.
| 필요한 것 | 왜 필요한가 | 확인 방법 |
|---|---|---|
| 터미널(명령줄) 사용 | 아래 명령어들을 복사해서 붙여넣고 실행할 줄 알면 충분합니다. macOS는 "터미널" 앱, Windows는 아래 참고 | — |
| Git | 이 저장소를 내려받기 위해 필요 (또는 GitHub에서 zip으로 다운로드해도 됨) | git --version |
| Python 3.10 이상 | 이 도구 자체가 파이썬 스크립트(quiz.py)로 되어 있음 |
python3 --version |
| Claude Code (A 경로에만 필요) | /quiz-add, /quiz-review 스킬이 Claude Code 위에서 동작함. 아직 없다면 먼저 설치·로그인부터 하세요 |
Claude Code 실행해서 채팅 되는지 확인 |
| 노션 계정 + Integration (선택) | 노트를 노션에 정리해뒀고 그걸 그대로 연동하고 싶을 때만 필요. 안 쓰면 몰라도 됨 | 아래 "노션 연동" 참고 |
| Anthropic API 키 (B 경로에만, 선택) | Claude Code 없이 터미널만으로 문항을 생성하고 싶을 때만 필요 | 아래 "시작하기 B" 참고 |
Windows 사용자라면: install.sh는 macOS/Linux용 셸 스크립트라 Windows
기본 명령 프롬프트(cmd)에서는 안 돌아갑니다. Git Bash(Git 설치 시 같이 깔림)나
**WSL(Windows용 Linux)**을 열어서 그 안에서 아래 명령어들을 실행하세요.
또한 python3 --version이 버전 번호 없이 Python만 찍고 끝나거나 아무 반응이
없다면, Python을 python.org에서 설치했는데 python3가 Microsoft Store의
빈 스텁으로 잡혀 있는 경우입니다. 이럴 땐 이 문서의 모든 python3를
python으로 바꿔서 실행하세요.
macOS/Linux는 터미널을 그냥 열어서 그대로 따라 하면 됩니다.
두 가지 방법이 있습니다. 뭘 쓸지 모르겠으면 A로 시작하세요 — 제일 빠르고, 나중에 언제든 B로 확장할 수 있습니다.
노트로 퀴즈를 만들고 푸는 것까지 전부 채팅 안에서 끝납니다. venv도, pip install도, API 키도 필요 없습니다. Claude Code, Codex CLI 둘 다 지원하며 아래에서 골라 쓰면 됩니다.
git clone https://github.com/dokwon33/knowledge-recall.git
cd knowledge-recall
./claude-setup/install.sh # 이 폴더에서만 쓰려면
./claude-setup/install.sh --global # 어느 폴더에서든 쓰려면설치하면 세 가지가 생깁니다.
| 설명 | |
|---|---|
/quiz-add |
노트로 퀴즈 생성. Claude Code가 직접 생성하므로 API 비용 0원 |
/quiz-review |
오늘 복습할 문항을 대화로 출제·채점 |
| SessionStart 훅 | 하루 첫 실행에만 오늘 복습할 문항 3개를 컨텍스트에 주입 |
Claude Code가 노트를 읽고 문항을 만든 뒤 validate.py로 검증만 돌리는 구조라,
span 검증이라는 보증은 그대로 유지하면서 비용만 사라집니다.
1) 노트 정리하고 나서 → 퀴즈 만들기
Claude Code 채팅창에 노트 파일을 가리키면서 이렇게 말하면 됩니다.
/quiz-add notes/spring-ai.docx --subject "Spring AI"
또는 그냥 "이 노트로 퀴즈 만들어줘"처럼 자연어로 말하고 파일 경로만 알려줘도 스킬이 알아서 반응합니다. Claude가
- 노트를 청크로 나눠서 미리보기 (
--dry-run) - 직접 문항을 만들고 (LLM API 호출 없음 — Claude Code 자신이 생성)
validate.py로 "원문에 실제로 있는 문장인가"만 검증- 저장된 문항 수 / 탈락한 문항 수 / 정리 갭을 채팅으로 보고합니다.
정리가 부실해서 문항을 못 만든 부분은 "정리 갭"으로 알려주는데, 이게 이 도구를 쓰는 진짜 이유입니다 — 노트 정리 상태를 점검하는 부수 효과.
2) 복습할 때 → 대화로 풀기
/quiz-review
또는 그냥 "복습하자", "오늘 복습할 거 있어?"라고 말해도 됩니다. 그러면:
- 오늘 간격 반복 스케줄에 걸린 문항(기본 5개)을 한 문항씩 채팅으로 냅니다
- 다 답할 때까지 정답을 먼저 보여주지 않습니다
- 전부 답하면 한 번에 채점하고, 문항마다 내 노트 원문 인용 + 해설을 보여줍니다
- 틀린 문항은 내일 다시 나옵니다 (간격 반복 박스가 0으로 리셋)
대화보다 브라우저가 편하면 /quiz-review에게 "웹으로 풀래"라고 말하세요.
스킬이 알아서 flask만 (venv나 requirements.txt 전체 없이) 한 번 설치하고
quiz.py serve를 백그라운드로 띄운 뒤 http://127.0.0.1:5000/due 주소를 줍니다.
직접 터미널에서 하고 싶으면 아래처럼도 가능합니다:
python quiz.py due # 터미널에서 바로 (표준 라이브러리만 있으면 됨)
python quiz.py serve # 브라우저 UI (flask 설치 필요 — pip install flask)훅 동작 확인 (Claude Code SessionStart 훅 — 하루 첫 실행에만 복습 알림):
echo '{"source":"startup"}' | python3 hooks/session_start.py
rm .last_review # 하루 한 번 제한 해제 (다시 테스트할 때)/quiz-add, /quiz-review와 동일한 스킬이 .agents/skills/ 에
Codex의 Skills 포맷(SKILL.md)으로도 들어 있습니다. 이 경로는 Codex가 저장소를
열면 자동으로 스캔하므로 설치 스크립트가 필요 없습니다 (claude-setup/install.sh는
Claude Code 전용).
git clone https://github.com/dokwon33/knowledge-recall.git
cd knowledge-recall
codex # 이 폴더에서 그대로 실행Codex 채팅에서 $quiz-add, $quiz-review로 명시 호출하거나, "노트로 퀴즈
만들어줘"처럼 자연어로 말해도 자동으로 인식합니다(암묵 호출). 동작 방식과
절대 규칙(노트에 없는 내용 금지, span 검증 등)은 Claude Code 스킬과 완전히
동일합니다 — Claude Code용 SessionStart 훅(하루 첫 복습 알림)만 Codex에는 없습니다.
왜 "매번"이 아니라 "하루 한 번"인가
VS Code는 하루에 10~20번 엽니다. 매번 퀴즈가 뜨면 며칠 안에 꺼버리게 됩니다.
알림형 학습 도구가 죽는 가장 흔한 패턴이라, .last_review에 날짜를 남겨
하루 한 번으로 제한했습니다. /clear·리줌·컴팩션에도 뜨지 않습니다.
브라우저 UI(serve)로 풀고 싶거나, Claude Code 없이 Anthropic API로 직접
생성하고 싶다면:
cd knowledge-recall
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # ANTHROPIC_API_KEY 채우기 (노션 쓰려면 NOTION_API_KEY도)# 1) 파싱 결과만 미리보기 (API 호출 없음, 무료)
python quiz.py add samples/spring-ai.docx --dry-run
# 2) 퀴즈 생성 (Anthropic API 사용)
python quiz.py add notes/spring-ai.docx --subject "Spring AI" -n 10
# 3) 브라우저에서 풀기
python quiz.py serve # → http://127.0.0.1:5000API 키 없이 전체 흐름만 먼저 보고 싶다면:
python quiz.py add samples/spring-ai.docx --subject "Spring AI" \
--mock samples/mock-response.json
python quiz.py servesamples/mock-response.json에는 정상 문항 3개 + 할루시네이션 2개 + 중복 1개 +
정답 오류 1개가 섞여 있습니다. 7개 중 3개만 통과하는 걸 확인해보세요.
pip install -r requirements-dev.txt
pytest # 55개
pytest -k validate # span 검증만python quiz.py add <경로 또는 노션 URL/ID> [옵션] # 문서 등록 + 퀴즈 생성
python quiz.py notion-list <노션 페이지 URL/ID> # 하위 페이지/DB 행 목록
python quiz.py list # 문서 목록 (문항/오답/갭 수)
python quiz.py due [-n N] [--json] # 오늘 복습할 문항
python quiz.py stats # 누적 통계 + span 통과율 + 박스 분포
python quiz.py serve [--port PORT] [--debug] # 브라우저에서 풀기 (기본 :5000)add의 주요 옵션:
| 옵션 | 설명 |
|---|---|
--subject "이름" |
과목 태그 |
-n N |
요청 문항 수 (기본 config.DEFAULT_ITEM_COUNT) |
--dry-run |
파싱/청킹 결과만 미리보기, API 호출 없음, 저장 안 함 |
--force |
이미 등록된 문서(동일 content_hash)라도 문항 재생성 |
--mock <json경로> |
LLM 대신 저장된 JSON 응답 사용 (테스트·Claude Code 스킬용) |
--prompt-ver v2 |
prompts/v2.txt로 생성 (A/B 비교) |
--no-retry |
통과율이 낮아도 1회 재생성을 시도하지 않음 |
(1) 액세스 토큰이 없다면 처음 발급 (추후에는 바로 3번 단계로 넘어가면 됩니다)
(2) 액세스 토큰 복사+붙여넣기 하여 .env에 NOTION_API_KEY={your_access_token} 추가
(3) 해당 페이지(하단 페이지 자동 사용하고싶다면 상위 페이지)에서 ⋯ 클릭 후 Integration 공유
로컬 파일 대신 노션 페이지를 바로 넣을 수 있습니다 (A/B 두 경로 모두 사용 가능).
새 의존성은 없습니다 (urllib 표준 라이브러리만 사용).
# .env 에 NOTION_API_KEY=ntn_... 추가
# (https://app.notion.com/developers/connections 에서 발급, 대상 페이지에
# ⋯ → 연결 추가 로 그 Integration을 공유해야 함)
python quiz.py add "https://notion.so/내-노트-abcdef..." --subject "통계"페이지 안에 하위 페이지나 데이터베이스가 있으면(예: 강의 노트가 데이터베이스의 행 하나씩인 경우) 자동으로 따라 들어가지 않습니다 — 이 도구는 "노트 하나 = 문서 하나"가 기본 단위라, 무작정 합치면 서로 다른 노트가 뒤섞이고 span 위치도 꼬입니다. 대신 목록만 보여줍니다:
python quiz.py notion-list "https://notion.so/메인-페이지-abcdef..."
# → 하위 페이지/데이터베이스 행 목록 + id
python quiz.py add <원하는 id> --subject "<과목>"기본은 항상 "노트에 없으면 갭으로 보고"입니다. 다만 /quiz-add로 만들다가
특정 갭을 콕 집어 "그냥 보충해서 내줘"처럼 명시적으로 요청하면, 그 갭에
한해 LLM 지식으로 보충한 문항을 만들 수 있습니다.
- 그 문항은
grounding: "llm_supplement"로 저장되고, evidence를 원문에서 찾지 않는 대신 관련 청크 위치를 span으로 씁니다. - 화면(풀기 화면·채점 결과)에는 항상 "[보충됨]" 배지가 붙어서 일반 문항과 섞이지 않습니다.
- 사용자가 요청하지 않은 갭까지 알아서 보충하지는 않습니다 — 매 갭 단위로
명시적 승인이 전제입니다. 자세한 규칙은
claude-setup/skills/quiz-add/SKILL.md참고.
랜덤 복습 대신, 곧 잊어버릴 문항만 골라서 다시 띄웁니다.
| 박스 | 다음 복습까지 | 이동 |
|---|---|---|
| 0 | 1일 | 맞히면 ↑ / 틀리면 0으로 |
| 1 | 3일 | |
| 2 | 7일 | |
| 3 | 21일 | |
| 4 | 60일 | 사실상 졸업 |
새로 만든 문항은 당일 복습 대상입니다. python quiz.py stats로 박스 분포를 볼 수 있어요.
문서를
--force로 재생성하면 문항이 새로 만들어지므로 박스 진행도가 초기화됩니다. 노트를 크게 고쳤을 때만 쓰세요.
.docx / .md / 노션 페이지
│
▼
[ingest] 제목 경계로 청킹 → heading_path 보존 → 품질 판정
│ · ok → 출제 대상
│ · thin → 정리 갭 카드
│ · image_only → 정리 갭 카드
▼
[generate] 문서 전체를 프롬프트에 넣고 1회 호출 (RAG 없음)
│
▼
[validate] ★ evidence가 원문에 실제로 있는지 확인
│ 없으면 문항 폐기 · 통과율 60% 미만이면 1회 재생성
▼
[db] SQLite 저장 — 이후 풀기/채점/해설은 LLM 호출 0회
│
▼
[app] Flask 로컬 UI: 풀기 → 채점 → 원문 인용 + 해설 → 오답 복습
| 결정 | 이유 |
|---|---|
| 퀴즈 사전 생성 | 풀기·채점·해설·재풀이가 전부 LLM 호출 0회. 문서 1개당 약 32원으로 끝 |
| RAG / 임베딩 없음 | 노트 하나가 5~10k 토큰. 통째로 넣는 게 더 싸고 정확 |
| span 검증 | 프롬프트로 "문서에서만 내라"고 말하는 것만으론 안 됨. 구조로 강제 |
| 갭 카드 | 퀴즈를 못 만드는 건 실패가 아니라 알려줘야 할 정보 |
| 파일 | 역할 |
|---|---|
quiz.py |
CLI 엔트리 (add / notion-list / list / due / stats / serve) |
ingest.py |
docx·md 파싱, 청킹, 품질 판정 |
notion_ingest.py |
노션 페이지 → ingest.Block (표준 라이브러리만 사용) |
generate.py |
프롬프트 조립, Anthropic 호출, JSON 파싱 |
validate.py |
span 검증 — 이 프로젝트의 핵심 |
srs.py |
간격 반복 스케줄 (Leitner 박스) |
db.py |
SQLite 스키마 + 쿼리 |
app.py |
Flask 라우트 |
hooks/session_start.py |
하루 첫 실행 복습 알림 |
claude-setup/ |
Claude Code 스킬 2종 + 훅 설정 + install.sh |
.agents/skills/ |
같은 스킬 2종의 Codex CLI 버전 (설치 스크립트 불필요, 자동 스캔) |
prompts/v1.txt |
시스템 프롬프트 (API 경로용) |
templates/ |
화면 |
config.py |
임계값·단가·모델. 튜닝은 여기서만 |
config.py에서:
| 값 | 기본 | 언제 바꾸나 |
|---|---|---|
THIN_CHARS |
80 | 멀쩡한 정리가 자꾸 thin으로 걸리면 낮추기 |
MIN_SENTENCE_RATIO |
0.3 | 개조식 노트가 자꾸 thin으로 걸리면 낮추기 |
MERGE_TARGET_CHARS |
1200 | 문항이 지엽적이면 올리기 (맥락이 늘어남) |
FUZZY_THRESHOLD |
0.92 | span 탈락이 너무 많으면 0.88까지 낮춰보기 |
MIN_PASS_RATIO |
0.6 | 재생성 트리거 기준 |
MODEL |
claude-sonnet-5 |
Haiku로 내리면 싸지지만 span 통과율이 떨어질 것 |
프롬프트를 고칠 때는 prompts/v2.txt를 새로 만들고 --prompt-ver v2로 돌리세요.
gen_logs 테이블에 버전별 통과율이 쌓여서 비교할 수 있습니다.
SELECT prompt_ver, model,
SUM(generated) AS 통과, SUM(rejected) AS 탈락,
ROUND(100.0*SUM(generated)/(SUM(generated)+SUM(rejected)),1) AS 통과율,
ROUND(SUM(cost_usd),4) AS 비용
FROM gen_logs GROUP BY prompt_ver, model;이 쿼리 하나가 나중에 오픈소스 LLM 비교 실험(SPEC §9.1)의 결과표가 됩니다.
v0를 만든 목적은 가설 하나를 검증하는 겁니다.
내가 정리한 문서로 만든 퀴즈가 실제로 풀 만한가?
노트 3개(잘 쓴 것 / 보통 것 / 부실한 것)로 30문항을 만들어 직접 채점해보세요.
- 실제로 풀 만한가? → 목표 20/30 이상
- evidence가 진짜 내 문장인가? → 목표 27/30 이상
- 부실한 노트에서 억지 출제했나, 갭으로 보고했나?
기준을 못 넘기면 prompts/v2.txt를 만들어 2~3회 고쳐서 재시도하세요.
여기서 품질이 안 나오면 나머지를 아무리 잘 만들어도 안 쓰게 됩니다.
- 이미지/캡처는 처리하지 않습니다. 캡처만 있는 구간은
image_only갭 카드로 표시만. - PDF 미지원.
.docx/.md/.txt/ 노션 페이지만. - 노션 하위 페이지·데이터베이스는 자동으로 안 따라 들어갑니다.
notion-list로 목록을 보고 원하는 걸 개별 문서로 추가하세요 (위 "노션 연동" 참고). - 인증 없음. 내 컴퓨터에서만 도는 단일 사용자용입니다.
- 네트워크 드라이브나 동기화 폴더에서 SQLite 잠금 오류가 나면
export QUIZ_DB=~/quiz.sqlite로 로컬 경로를 지정하세요.
다음 단계(Spring + Vue 포팅, 배포)는 docs/SPEC.md 참고. 단, v0를 2주 이상 실제로 써본 뒤에 시작하세요.