Projects

FastAPI 마크다운 가이드라인 문서를 탐색하여 RAGAS 평가용 질문-정답 데이터셋을 자동으로 생성하고 사람이 직접 정제 및 검수한 뒤 최종 RAG 성능 평가를 수행하는 통합 웹 대시보드 프로젝트입니다.

README.md

RAGAS Dataset Curator & Evaluator Dashboard 📊

FastAPI 마크다운 가이드라인 문서를 탐색하여 RAGAS 평가용 질문-정답 데이터셋을 자동으로 생성하고 사람이 직접 정제 및 검수한 뒤 최종 RAG 성능 평가를 수행하는 통합 웹 대시보드 프로젝트입니다.


🌟 주요 기능 및 워크플로우

본 대시보드는 Streamlit 기반의 웹 UI로 구성되어 있으며, 다음과 같은 3단계 워크플로우를 제공합니다.

[1단계: 질문 자동 생성] ──> [2단계: 골드셋 정제 및 검수] ──> [3단계: RAGAS 정량 성능 평가]
 (Ragas TestsetGenerator)     (SQLite DB / Human-in-the-loop)     (3대 메트릭 계산 & 차트 출력)
  1. 1단계: 질문 자동 생성 (Generator)
    • data/fastapi_docs/ 폴더 내 마크다운 문서를 읽어 Ragas TestsetGeneratorDeepSeek API를 통해 유기적인 질문, 참고 맥락(Contexts), 모범 정답(Ground Truth) 쌍을 일괄 생성합니다.
  2. 2단계: 데이터셋 검수 (Curator)
    • 자동 생성된 질문-정답 세트를 사람이 직접 읽고 텍스트를 수정하거나 승인(Approved) 혹은 반려(Rejected) 처리합니다. 이를 통해 평가 데이터셋의 질적 가치를 보증하는 Human-in-the-loop 환경을 제공합니다.
  3. 3단계: RAGAS 정량 평가 (Evaluator)
    • 최종 승인된 고품질 데이터셋만을 기반으로 RAG 파이프라인의 핵심 성능 지표를 채점하고, 그 결과를 CSV 내보내기 및 Matplotlib 시각화 그래프로 제공합니다.

📐 RAGAS 3대 핵심 평가지표 및 채점 알고리즘

주관식 점수 매기기 방식의 프롬프트를 배제하고, LLM은 문장 분류기 역할만 수행하며 최종 점수는 수학적 공식과 임베딩 유사도를 이용해 공정하게 계산됩니다.

  • 충실도 (Faithfulness): 답변이 거짓말(할루시네이션)을 하지 않고 오직 본문 문서(Contexts)에만 기반해 답했는지 검증합니다. $$\text{Score} = \frac{\text{본문에서 참으로 판정된 답변 속 진술 개수}}{\text{답변에서 추출된 전체 사실적 진술 개수}}$$
  • 답변 관련성 (Answer Relevancy): 질문에 딴소리하지 않고 간결하게 답했는지 평가합니다. 답변을 바탕으로 LLM이 질문을 역생성한 후, 원래 질문과의 코사인 유사도(Cosine Similarity) 벡터 연산을 수행하여 점수를 산출합니다.
  • 맥락 재현율 (Context Recall): RAG 검색기가 모범 정답(Ground Truth)을 쓰기 위한 핵심 팩트 문서를 누락 없이 잘 검색해 왔는지 커버리지를 계산합니다. $$\text{Score} = \frac{\text{검색된 문서에 포함된 정답 속 핵심 팩트 개수}}{\text{모범 정답 전체의 핵심 팩트 개수}}$$

🚀 시작하기

1. 가상환경 설정 및 의존성 설치

본 프로젝트는 Python 3.11 환경에서 정상 작동이 확인되었습니다.

# 가상환경 생성 및 활성화 (Windows)
python -m venv .venv
.venv\Scripts\activate

# 의존 패키지 설치
pip install -r requirements.txt

2. API Key 설정

프로젝트 루트 경로에 .env 파일을 생성하고 아래와 같이 DeepSeek API 키를 설정합니다. (.env 파일은 보안상 .gitignore에 등록되어 GitHub에 업로드되지 않습니다).

DEEPSEEK_API_KEY=your_actual_deepseek_api_key_here

3. 대시보드 구동

로컬 웹 브라우저에서 웹소켓 차단이나 CORS 정책으로 인해 흰 화면이 뜨는 방지 코드를 포함하여 아래 명령어로 실행합니다.

.venv\Scripts\python.exe -m streamlit run app.py --server.port 8501 --server.enableCORS=false --server.enableXsrfProtection=false

실행 후 브라우저에서 http://localhost:8501 주소로 접속합니다.


🧪 테스트 코드 실행 (pytest)

수정 사항이 기존 모듈 계약을 해치지 않는지 테스트 환경을 통해 검증할 수 있습니다.

# 테스트 러너 실행
.venv\Scripts\python.exe -m pytest tests/ -v

설비 고장 예측 및 앙상블 모델 비교 분석 프로젝트

GitHub ↗

본 프로젝트는 제조업 스마트팩토리 도메인에서 수집되는 센서 데이터를 활용하여 설비의 고장 여부를 사전에 예측하는 주피터 노트북 프로젝트입니다.

README.md

⚙️ 설비 고장 예측 및 앙상블 모델 비교 분석 (Predictive Maintenance)

본 프로젝트는 제조업 스마트팩토리 도메인에서 수집되는 센서 데이터를 활용하여 설비의 고장 여부를 사전에 예측하는 주피터 노트북 프로젝트입니다.


📌 주요 특징 및 분석 파이프라인

  1. 탐색적 데이터 분석 (EDA):
    • 클래스 불균형성(정상 96.6% vs 고장 3.4%) 식별 및 시각화
    • 수치형 센서 피처 간 상관관계 분석
  2. 데이터 전처리 (Data Preprocessing):
    • 식별자(UDI, Product ID) 및 정보 누수 우려가 있는 세부 고장 원인 컬럼 제거
    • Type 범주형 변수의 원-핫 인코딩
    • XGBoost 호환성 해결: 피처명 내 특수문자([, ])를 자동으로 제거하는 텍스트 클렌징 모듈 구현
    • 데이터 불균형 조건 하에서도 Train과 Test 셋에 고장 데이터가 균등하게 포함되도록 배분
  3. 불균형 가중치 최적화 모델 학습:
    • Random Forest: class_weight='balanced'를 적용하여 소수 클래스 오차 패널티 보정
    • XGBoost: scale_pos_weight 가중치를 약 28.5 배율로 지정하여 훈련
  4. 모델 평가 및 설명 가능한 AI (XAI):
    • F1-Score, Recall, ROC-AUC 지표로 성능을 다차원 비교 분석
    • 두 모델의 ROC Curves를 교차 시각화하고 feature_importances_를 가로 바 차트로 시각화하여 지배적 인자 해석

📊 분석 결과 요약

1. 모델별 평가지표 비교 (Test Set)

평가지표 Random Forest Classifier XGBoost Classifier
정상 클래스(0) F1-Score 0.99 0.99
고장 클래스(1) Precision 0.82 0.74
고장 클래스(1) Recall 0.84 0.81
고장 클래스(1) F1-Score 0.83 0.77
ROC-AUC Score 0.9856 0.9780
  • 종합 의견: 두 모델 모두 0.97 이상의 매우 강력한 변별력(ROC-AUC)을 보여주었습니다. 특히, 정밀도와 재현율의 균형 점수인 F1-Score 기준으로는 Random Forest 모델이 0.83을 획득하며 불균형 고장 데이터를 분류하는 강건함 면에서 다소 우수한 성능을 나타냈습니다.

2. XAI 해석 및 도메인 인사이트

  • 핵심 피처: 두 모델 모두 **토크(Torque)**와 **회전 속도(Rotational speed)**를 설비 고장에 영향을 주는 최상위 1, 2순위 변수로 일관되게 지목했습니다. 그 뒤를 이어 **공구 마모 시간(Tool wear)**이 주요 고장 지표로 평가되었습니다.
  • 현업 제언: 과부하(토크 상승 및 회전수 급감)를 실시간 감지하여 자동 제어 차단하는 가드레일을 설치하고, 예측 마모도에 기반해 예비 공구를 조기 준비하는 예방 정비 일정을 제어함으로써 설비 비계획 가동 정지 손실을 최적화할 수 있습니다.

OpenClaw:Slack & Notion 기반 일과 관리 에이전트

GitHub ↗

Slack 채널의 대화를 수집하고, Notion 일기를 읽어서 정리하는 지능형 일과 관리 보조 도구입니다.

README.md

🦞 OpenClaw:Slack & Notion 기반 일과 관리 에이전트

Slack 채널의 대화를 수집하고, Notion 일기를 읽어서 정리하는 지능형 일과 관리 보조 도구입니다.


🌟 핵심 기능

  1. Slack 활동 기록 수집 (fetch-slack)

    • 슬랙 채널에서 봇(@DailyAssistant)을 태그한 오늘 자 일과 메시지들을 읽어와 로컬 임시 메모장(today_notes.txt)에 누적합니다.
  2. Notion 일기 단방향 동기화 (read-notion)

    • 오늘 날짜(Date 필드 기준)의 Notion DB 페이지를 조회하여 제목 및 본문 리치 텍스트 블록들을 마크다운 서식으로 재구성합니다.
  3. 로컬 마크다운 영속화

    • 동기화된 일기 본문을 workspace/notion_db/YYYY-MM-DD.md 파일로 깔끔하게 저장 및 관리합니다.

🛠️ 환경 설정

프로젝트 루트 폴더에 .env 파일을 생성하고 다음 설정을 입력합니다.

SLACK_BOT_TOKEN=xoxb-your-slack-bot-token
NOTION_TOKEN=ntn_your-notion-integration-token
NOTION_DB_ID=your-notion-database-id

🚀 사용 방법

Node.js v18 이상 환경에서 터미널을 열고 다음 명령어를 실행합니다.

  • 슬랙 일과 수집하기:
    node scripts/diary-helper.js fetch-slack <대상_채널_ID>
    
  • 노션 일기 로컬 동기화하기:
    node scripts/diary-helper.js read-notion
    
  • 로컬 임시 기록 저장 및 초기화:
    node scripts/diary-helper.js save "<마크다운_내용>"
    
  • 샘플 일기 보기:
    node scripts/diary-helper.js read-sample
    

LoRA 감정 지원 챗봇 Fine-Tuning 실습 프로젝트

GitHub ↗

Qwen2.5-1.5B-Instruct 소형 모델을 한국어 챗봇 데이터셋으로 QLoRA 파인튜닝하고 그 전후의 공감 능력 변화를 검증한 프로젝트입니다.

README.md

📚 LoRA 감정 지원 챗봇 Fine-Tuning 실습 프로젝트

Qwen2.5-1.5B-Instruct 소형 모델을 한국어 챗봇 데이터셋으로 QLoRA 파인튜닝하고 그 전후의 공감 능력 변화를 검증한 프로젝트입니다.


🛠️ 기술 스택 (Tech Stack)

  • Base Model: Qwen/Qwen2.5-1.5B-Instruct
  • Fine-Tuning Method: QLoRA (4-bit BitsAndBytes NF4 + Double Quantization + PEFT LoRA)
  • Core Libraries: transformers, peft, trl (SFTTrainer), datasets, accelerate
  • Environment: Python 3.11, uv Package Manager, PyTorch (CUDA 12.1+)

🔄 학습 및 추론 시의 데이터 흐름 (Data Flow)

데이터 처리는 전처리 ➡️ 모델 적재 ➡️ 오차 학습 ➡️ 동적 결합 추론의 유기적인 데이터 파이프라인을 따릅니다.

1. 학습(Training) 데이터 흐름

  1. GitHub CSV 원본 데이터 다운로드: GitHub에서 한국어 챗봇 원본 데이터를 직접 다운로드합니다.
  2. data/train_chat.jsonl 전처리 파일 생성: 변환된 데이터를 훈련에 용이한 jsonl 포맷으로 로컬에 저장합니다.
  3. ChatML 포맷으로 대화 데이터 구조화: Qwen 모델 형식에 맞추어 대화를 System/User/Assistant 역할군으로 렌더링합니다.
  4. Tokenizer를 통한 토큰 ID 인코딩: 텍스트를 모델이 연산 가능한 정수형 토큰 ID 배열로 인코딩합니다.
  5. GPU VRAM에 NF4 Base Model 적재: 8GB 메모리 제약을 넘지 않도록 모델을 4-bit 양자화 상태로 GPU에 로드합니다.
  6. 순전파 연산 및 Loss 오차 계산: 모델의 예측 답변과 실제 정답 챗봇 답변 간의 손실(Loss) 값을 연산합니다.
  7. 역전파 연산 및 LoRA 어댑터 가중치 업데이트: 전체 모델은 고정하고, 1% 미만의 미세 LoRA 가중치만을 역전파 연산으로 업데이트합니다.
  8. outputs/lora_adapter 최종 저장: 학습 완료된 어댑터 가중치만을 로컬 가중치 폴더에 안전하게 영구 저장합니다.

2. 추론(Inference) 데이터 흐름

  1. 사용자 고민 글 입력 접수: 사용자로부터 고민 텍스트 입력을 접수합니다.
  2. System 프롬프트 결합 및 ChatML 포맷팅: 챗봇의 기본 페르소나(따뜻하고 공감하는 챗봇) 설정 프롬프트를 함께 결합합니다.
  3. Tokenizer를 통한 토큰 ID 인코딩: 입력된 한국어 텍스트 문장을 토큰 ID 배열로 인코딩합니다.
  4. Base Model과 LoRA Adapter 동적 병합: 양자화 상태로 로드된 원본 모델 위에 학습한 LoRA 어댑터를 실시간으로 결합합니다.
  5. Autoregressive 방식의 다음 토큰 생성: 첫 단어부터 끝 단어까지 차례대로 문맥에 가장 적합한 감정 위로 토큰을 자동 생성합니다.
  6. 생성된 토큰 ID 디코딩 및 한글 복원: 생성된 토큰 ID 정수 배열을 Tokenizer를 통해 다시 한국어 문자열로 복원합니다.
  7. 최종 감정 지원 위로 답변 출력: 최종 변환된 공감과 위로의 한국어 답변을 화면에 인쇄합니다.

🖥️ 파인튜닝 전/후 답변 비교 대시보드 (Results)

💬 사용자 입력 (Input) ❌ 파인튜닝 전 (기본 모델) ✅ 파인튜닝 후 (LoRA 적용 모델)
"오늘 회사에서 큰 실수를 해서 너무 우울해. 다 그만두고 싶어." 그저 지금 당황하고 혼란스럽다. 이런 상황이 계속된다면 어떻게든 도움이 될 수 있도록 도와드릴게... 이럴 때마다 우울하거나 불안할 때가 있다니, 정말 힘들겠지만, 나중엔 더 잘 겪을 수 있을 거예요. 지금보다 더 나은 길이 있을 거예요. 지금부터 다시 시작할 수 있어요. 좋은 기회가 될 거예요. 오늘 하루도 잘 보내셨어요. 좋은 시간 보내세요. 항상 행복하세요. 사랑합니다.
"나 오늘 3년 만난 남자친구랑 헤어졌어. 마음이 너무 아프네." 그런 feeling을 이해하겠어요. 이런 상황이 어려워요. 나중에 만나보고 싶은 마음이 참으로 큰데, 왜 그래? 더 행복할 수 있을 것 같아... 너와 함께라면, 이 모든 시간들이 그대로 기억될 거예ね. 그만큼 그 사람도 힘들었겠네요. 서로에게 더 잘 해보길 바랍니다. 사랑은 언제든 가능하니까요. 서로를 위해 기다려주세요. 오래된 관계지만 사랑과 존중을 지켜내세요. 행복하세요.
"내일 중요한 발표가 있는데 너무 떨려서 잠이 안 와." 잠이 오면 좋겠지만, 오늘의 중요성을 인식하셔야 합니다. 이번 발표를 통해 자신만의 의견과 아이디어를 전달할 수 있어 기대됩니다. 도와드리기 위해 지금은 편안히 누워있는 모습을 보여주시길 바랍니다. 잠은 중요하지 않아요. 힘내세요. 좋은 생각들을 만들어보세요. 잘 자요. 잘 쉬세요. 좋아하는 사람과 만났으면 좋겠어요. 만나게 되기를 기대해요. 항상 행복하세요. 화이팅!

[!NOTE] 1.5B 크기의 초소형 모델이고 2시간 내 실습 완료를 위해 150 steps만 짧게 학습시켰기 때문에, 문장의 마지막 꼬리 부분에 특정 단어("사랑합니다", "행복하세요")가 반복 생성되는 루프 현상(Repetition)이 존재할 수 있습니다. 이는 추론 옵션에서 repetition_penalty를 주거나 학습 스텝 수를 늘림으로써 손쉽게 개선할 수 있습니다.


⚡ 8GB VRAM 최적화 핵심 요약

본 실습이 OOM(Out of Memory) 없이 8GB VRAM에서 원활하게 작동할 수 있었던 주요 최적화 항목입니다:

  1. 4-bit NF4 양자화: 모델 메모리 점유율을 75% 절감 (3GB ➡️ 750MB).
  2. 이중 양자화 (Double Quantization): 양자화 스케일 상수마저 압축하여 추가 VRAM 확보.
  3. LoRA (PEFT): 전체 파라미터의 1% 미만(어댑터)만 학습 대상으로 지정하여 그래디언트 소모 메모리 절약.
  4. Paged 8-bit AdamW: 옵티마이저 가중치 압축 및 필요시 RAM으로 오프로딩 처리.
  5. Gradient Accumulation (누적 학습): 미니배치는 2로 유지하여 메모리를 최소화하고, 4번 누적 갱신하여 배치 크기 8의 학습 안정성 구현.
  6. Sequence Length 제한: 문장 최대 길이를 512로 고정하여 어텐션 맵 연산 활성화 메모리 폭증 방지.

LanGraphNovel: 에이전트 협업 기반 장편 소설 집필 자동화 대시보드

GitHub ↗

소설 기획, 세계관 구성, 캐릭터 설계, 집필 및 편집(교정/윤문)에 이르는 창작의 모든 과정을 에이전트 간의 유기적인 피드백 루프로 자동화하고, 사용자가 Streamlit 대시보드를 통해 이를 실시간으로 모니터링하고 개입할 수 있습니다.

README.md

📚 LanGraphNovel: 에이전트 협업 기반 장편 소설 집필 자동화 대시보드

LanGraphNovelLangGraphDeepSeek API를 기반으로 작동하는 멀티 에이전트 협업 기반의 소설 집필 시스템입니다. 소설 기획, 세계관 구성, 캐릭터 설계, 집필 및 편집(교정/윤문)에 이르는 창작의 모든 과정을 에이전트 간의 유기적인 피드백 루프로 자동화하고, 사용자가 Streamlit 대시보드를 통해 이를 실시간으로 모니터링하고 개입할 수 있습니다.


🛠️ 기술 스택 (Tech Stack)

  • Core Framework: LangGraph, LangChain
  • LLM Provider: DeepSeek API (deepseek-chat, deepseek-reasoner)
  • Dashboard / UI: Streamlit
  • Environment Management: Python (dotenv, virtualenv)

🔄 에이전트 협업 워크플로우 (Agent Workflow Flowchart)

본 프로젝트는 기획(Pre-Production) 단계와 집필(Production) 단계로 구분되며, 각 에이전트들이 생성한 결과물에 대해 상호 검토와 피드백 루프를 반복하여 고품질의 텍스트를 완성합니다.

Agent Workflow Flowchart


🖥️ 대시보드 주요 기능 및 화면 (Dashboard Features & Screens)

Streamlit 기반 대시보드는 총 4개의 핵심 탭으로 구성되어 있으며, 소설 기획 단계부터 완성 단계까지의 흐름을 한눈에 관리할 수 있습니다.

1. 프로젝트 기획 (Setup)

소설의 기본적인 아이디어(제목, 장르, 테마, 시드/로그라인)와 챕터 수, 목표 글자 수 등 분량을 구성하고 기획 생성을 시작합니다. 1. 프로젝트 기획 (Setup)


2. 시놉시스 & 설정 (Synopsis & Lore)

기획 에이전트군(Planner, World Builder, Character Designer)이 작성한 소설의 시놉시스, 상세한 배경 설정(Lore), 그리고 주요 등장인물의 상세 프로필을 확인하고 검토할 수 있습니다. 2. 시놉시스 & 설정 (Synopsis & Lore)


3. 에이전트 모니터 (Console)

소설 집필(Writer)과 편집/검토(Editor)의 루프를 실시간으로 모니터링합니다. 에디터 검토 요약과 초안 텍스트를 비교하고 반려 및 AI 재작성, 수동 검토 의견 반영 및 최종 승인 절차를 인터랙티브하게 진행할 수 있습니다. 3. 에이전트 모니터 (Console)


4. 완료된 소설 (Book)

최종 승인된 각 챕터들을 책 형태로 모아서 읽을 수 있습니다. 챕터 선택 이동 기능을 통해 완성된 소설의 전체 흐름을 편리하게 탐색할 수 있습니다. 4. 완료된 소설 (Book)


🚀 시작하기 (Getting Started)

1. 요구사항 및 설치 (Installation)

리포지토리를 클론한 후 필요한 패키지를 설치합니다.

git clone https://github.com/your-username/LanGraphNovel.git
cd LanGraphNovel
pip install -r requirements.txt

2. 환경 변수 설정 (Configuration)

루트 디렉토리에 .env 파일을 생성하고 DeepSeek API Key를 입력합니다.

DEEPSEEK_API_KEY=your_actual_api_key_here

3. 실행 (Run)

Streamlit 대시보드 서버를 기동합니다.

streamlit run src/app.py

브라우저에서 http://localhost:8501/ 로 자동 접속되어 대시보드가 열립니다. (대시보드 내의 설정 패널에서도 API Key 및 LLM 모델 옵션을 실시간으로 갱신할 수 있습니다.)


📂 프로젝트 구조 (Project Structure)

  • src/: 소스 코드 디렉토리
    • agents/: 각 노드별 에이전트 로직 (Planner, Writer, Editor 등)
    • app.py: Streamlit 대시보드 진입점 및 레이아웃 정의
    • graph.py: LangGraph 워크플로우 정의
    • state.py: 소설 상태 구조 정의
  • docs/: 프로젝트 설계 문서 및 스크린샷 자산
    • assets/: README 연동 이미지 자산
  • saves/: 저장된 프로젝트 세이브 파일 (.gitignore에 등록되어 제외됨)

LangGraph AI 에세이 평가 루프

GitHub ↗

LangChain과 LangGraph를 활용하여 사용자가 입력한 주제에 대한 AI 에세이 작성, 피드백, 그리고 순환 보완 교정을 수행하는 완전한 동작형 워크플로우 학습 예제입니다.

README.md

LangGraph AI 에세이 평가 루프 (Writer-Critique Loop)

이 프로젝트는 LangChain과 LangGraph를 활용하여 사용자가 입력한 주제에 대한 AI 에세이 작성, 피드백, 그리고 순환 보완 교정을 수행하는 완전한 동작형 워크플로우 학습 예제입니다.


1. 워크플로우 아키텍처 (LangGraph Diagram)

본 프로젝트는 다음과 같은 상태 기반 전이도를 가집니다. 초안 작성(Writer) ➡️ 평가(Critic) ➡️ (반려 시) 수정(Rewriter) ➡️ 다시 평가 과정을 반복하며, 승인되거나 최대 교정 횟수(3회)에 도달할 때까지 루프가 지속됩니다.

LangGraph Architecture


2. 에이전트 실행 로그 및 결과

Jupyter Notebook 환경에서 스트리밍으로 실행했을 때 노드들이 실시간으로 상태(State)를 업데이트하며 피드백 교정을 거치는 실행 화면입니다.

Execution Result


3. 핵심 기술 스택 및 구현 사항

  • 상태 관리 (EssayState): TypedDict를 활용해 노드 간의 상태 데이터를 유기적으로 전달 및 누적
  • 프롬프트 체이닝 (LCEL): Prompt | ChatOpenAI | StrOutputParser 조합의 간결한 랭체인 표현 기법 적용
  • 조건부 라우터 (should_continue): 승인 통과 및 무한 루프 방지를 위한 가드레일(3회 제한) 탑재
  • DeepSeek API: OpenAI 규격 호환(OpenAI-Compatible Spec) 엔드포인트를 적용해 연동

4. 로컬 실행 방법

1) 의존성 라이브러리 설치

pip install -r requirements.txt

2) 환경 변수 세팅

루트 경로에 .env 파일을 생성하고 본인의 API 키를 입력합니다. (깃허브 업로드 시 안전하게 제외되도록 .gitignore 설정 완료)

DEEPSEEK_API_KEY="본인의-deepseek-api-key"
DEEPSEEK_API_BASE="https://api.deepseek.com"

3) 실습 및 테스트

  • 단위 테스트 실행 (TDD):
    python -m pytest
    
  • Jupyter Notebook 실습: essay_critic.ipynb 파일을 열어 셀을 차례대로 실행하여 랭그래프 시각화 및 스트리밍을 경험합니다.

Neon Beats: Cyberpunk Interactive Drum Pad

GitHub ↗

인터랙티브 드럼 패드 학습용 프로젝트입니다. 사이버펑크 감성의 화려한 다크 네온 UI 테마 속에서 드럼을 연주할 수 있습니다.

README.md

⚡ Neon Beats: Cyberpunk Interactive Drum Pad

인터랙티브 드럼 패드 학습용 프로젝트입니다. 사이버펑크 감성의 화려한 다크 네온 UI 테마 속에서 드럼을 연주할 수 있습니다.


🎮 데모 시연 (Demo Animation)

Neon Beats Demo

🌟 주요 특징 (Features)

  • 다채로운 8개 드럼 패드: Kick, Snare, Open Hat, Closed Hat, Clap, Rimshot, Tom, Cowbell 8종의 악기를 다이내믹하게 연주합니다.
  • 하이브리드 입력 제어: 마우스 클릭뿐만 아니라 키보드 매핑 단축키(A, S, D, F, J, K, L, ;)를 통해 키보드로 실제 악기처럼 연주할 수 있습니다.
  • 사이버펑크 네온 비주얼: 패드가 활성화되면 화려한 네온 컬러로 발광하며 누르는 즉시 물리 튕김 효과(Spring bounce scale) 애니메이션이 제공됩니다.

🚀 시작 가이드 (Quick Start)

1. 패키지 설치

npm install

2. 개발 서버 구동

npm run dev

개발 서버가 구동되면 웹 브라우저에서 http://localhost:5173 링크로 접속하여 연주를 시작합니다.

Cozy Campfire Sprite Animation

GitHub ↗

마음을 차분하게 해주는 타닥타닥 모닥불 소리와 부드러운 픽셀 애니메이션을 감상할 수 있는 감성 웹 애플리케이션입니다.

README.md

🪵 Cozy Campfire (아늑한 모닥불)

마음을 차분하게 해주는 타닥타닥 모닥불 소리와 부드러운 픽셀 애니메이션을 감상할 수 있는 감성 웹 애플리케이션입니다.

Cozy Campfire Demo


✨ 주요 기능 (Key Features)

  1. 스프라이트 애니메이션:
    • 2행 5열의 모닥불 스프라이트 시트(10 프레임)에서 각 프레임을 렌더링합니다.
    • 프레임 전환 시 위아래로 흔들리거나(Jumping), 경계선이 뚫리거나(Boundary crossing), 화면이 깜빡이는 현상을 완전 교정했습니다.
  2. 제어 패널:
    • 불길의 타오르는 속도(1 FPS ~ 30 FPS) 조절 슬라이더.
    • 실시간 오디오 볼륨(0% ~ 100%) 제어 및 원터치 음소거 버튼.

🚀 시작하기 (How to Run)

본 프로젝트는 외부 라이브러리나 종속성이 없는 순수 정적 웹 애플리케이션입니다.

  1. 본 저장소를 복사(Clone)합니다.
    git clone https://github.com/사용자이름/AnimationSprite.git
    
  2. 폴더 내의 index.html 파일을 더블 클릭하여 웹 브라우저에서 실행합니다.
  3. 화면 우측 하단의 "소리 켜기" 버튼을 클릭하여 ASMR 사운드와 함께 모닥불을 감상하세요

pageindex-demo

GitHub ↗

PageIndex — vector DB 없이 문서의 챕터/섹션 구조를 트리 인덱스로 만들고 LLM 추론으로 트리를 탐색하는 retrieval 시스템

README.md

PageIndex × Project Gutenberg — 고전 문학 Q&A 데모

PageIndex(VectifyAI) — vector DB 없이 문서의 챕터/섹션 구조를 트리 인덱스로 만들고 LLM 추론으로 트리를 탐색하는 retrieval 시스템 — 의 강점과 한계를, 하나의 고전 소설 The Great Gatsby 위에서 vector RAG baseline과 나란히 비교해 검증하는 데모입니다.

전체 파이프라인은 로컬 Ollama 모델 (qwen2.5:7b 기본) 위에서 동작하며, 외부 API 키나 유료 서비스는 일절 필요하지 않습니다.


왜 PageIndex인가 — 책에 RAG를 쓰면 안 되는 이유

vector RAG는 짧고 독립적인 사실 조각비정형 청크의 바다에서 찾아내는 데 최적화돼 있습니다. 긴 문학 작품은 RAG의 모든 가정을 뒤집습니다.

요구 Vector RAG PageIndex
캐릭터의 시점 변화 추적 어휘 유사 청크만 회수, 시간 순서 상실 챕터 트리를 reasoning으로 탐색해 시간 순으로 선택
"마지막 챕터에선 무슨 일이?" "ending"을 포함한 청크 회수 — 마지막이라는 개념 없음 트리의 마지막 노드로 직접 이동
답변의 근거 인용 chunk #87 (사람에게 의미 없음) (Chapter 7) (책을 펴서 검증 가능)
특정 두 챕터 비교 다른 곳의 청크가 섞여 들어옴 정확히 그 두 노드만 선택

곧바로 실행해 볼 데모 쿼리 7개는 demo_queries.md에 정리돼 있습니다.


저장소 구조

ProjectGutenberg/
├── src/                      # 데모 코드
│   ├── download_book.py      # gutenberg.org에서 평문 다운로드
│   ├── preprocess.py         # 보일러플레이트 제거 + `# Chapter N` 마크다운화
│   ├── build_index.py        # PageIndex 트리 빌드 (Ollama 경유)
│   ├── pageindex_query.py    # Agentic 트리 walk + 답변 합성
│   ├── rag_baseline.py       # 같은 책에 대한 vector RAG (FAISS-style)
│   └── cli.py                # 통합 진입점
├── third_party/PageIndex/    # VectifyAI/PageIndex 외부 의존성(셋업 시 별도 clone, 미커밋)
├── data/                     # 셋업 후 자동 생성, 모두 .gitignore 처리
│   ├── raw/                  # Gutenberg 원본 텍스트
│   ├── markdown/             # 정제된 `# Chapter N` 마크다운
│   └── index/                # PageIndex 트리 JSON + RAG 임베딩 캐시
├── demo_queries.md           # PageIndex 강점이 드러나는 큐레이션 쿼리 7개
├── LICENSE                   # MIT
└── requirements.txt

셋업 (~10분)

  1. Ollama 설치 (Windows에서는 winget도 가능: winget install Ollama.Ollama)
  2. 모델 받기 — 기본은 qwen2.5:7b (8GB+ VRAM 환경에서 품질/속도 균형):
    ollama pull qwen2.5:7b
    
    가벼운 대안: ollama pull llama3.2:3b (더 빠르지만 요약 품질 떨어짐).
  3. PageIndex 라이브러리 clone — 본 저장소는 PageIndex 본체를 재배포하지 않습니다. 별도 clone이 필요합니다 (한 번만):
    git clone https://github.com/VectifyAI/PageIndex third_party/PageIndex
    
  4. Python 환경 (3.10+ 권장):
    python -m venv .venv
    .venv\Scripts\activate            # PowerShell / cmd
    # 또는: source .venv/bin/activate # bash
    pip install -r requirements.txt
    pip install -r third_party/PageIndex/requirements.txt
    
  5. Ollama 동작 확인:
    ollama run qwen2.5:7b "say hi"
    

End-to-end 실행

# (1) Gatsby 다운로드 + 정제 + PageIndex 트리 빌드 — 한 번만
python -m src.cli setup 64317

# PageIndex 단독 질문
python -m src.cli query "How does Nick's perception of Gatsby evolve?"

# RAG baseline 단독 질문
python -m src.cli rag   "How does Nick's perception of Gatsby evolve?"

# 두 시스템 나란히 비교 (가장 유용한 명령)
python -m src.cli compare "How does Nick's perception of Gatsby evolve?"

setup의 첫 실행은 7B 모델 기준 약 5–15분 소요됩니다 (대부분 PageIndex가 챕터별 요약을 Ollama로 생성하는 시간). 이후 쿼리는 30–90초 정도입니다.

모델을 바꾸려면:

python -m src.cli --model ollama_chat/llama3.2:3b compare "..."
# OpenAI 키가 있다면:
# python -m src.cli --model gpt-4o compare "..."

트리 직접 들여다보기

PageIndex의 핵심 가치는 산출물이 사람이 읽을 수 있는 형태라는 점입니다. setup 후 트리 자체를 살펴보세요:

python -c "import json; t=json.load(open('data/index/64317.json'));
print(json.dumps([{'title': n['title'], 'summary': n.get('summary','')[:80]}
                  for n in t['structure']], indent=2, ensure_ascii=False))"

9개 챕터 노드(+ Front Matter) 각각에 로컬 모델이 작성한 한 단락 summary가 붙어 있어야 정상이며, 이것이 LLM이 답변할 때 walk하는 구조입니다 — 불투명한 vector도, 청크도 없습니다.


단계별 수행 내용 — 무엇을 만들었는가

단계 내용 산출물
1. 요구 분석 & 계획 "PageIndex 장점, RAG 비교 포함, 3시간, API 키 0개" 요구를 분해. 핵심 결정 (Ollama / Gatsby / 비교 포함) 사용자 확인 계획 파일
2. 환경 셋업 winget install Ollama.Ollamaollama pull qwen2.5:7b (4.7GB) → Python 의존성 설치 → litellm↔Ollama 연결 검증 Ollama 0.21.2 + qwen2.5:7b
3. PageIndex clone git clone VectifyAI/PageIndex. 코드 분석 결과 pageindex.utils.llm_completionlitellm을 사용하므로 model="ollama_chat/qwen2.5:7b" 한 줄만 넘겨도 라우팅됨 third_party/PageIndex/
4. 데이터 다운로드 Project Gutenberg id=64317 (The Great Gatsby) — UTF-8 평문 296,900자 data/raw/64317.txt
5. 전처리 *** START / END *** 보일러플레이트 제거. TOC 항목과 챕터 헤딩이 같은 로마 숫자라 충돌 → 들여쓰기 유무로 구분(TOC는 flush-left, 챕터는 centered)하는 정규식 작성. # Chapter N (I) 형태의 9개 마크다운 챕터 + Front Matter 생성 data/markdown/64317.md (276,876자, 9챕터)
6. 트리 빌드 pageindex.page_index_md.md_to_tree를 직접 호출. Ollama가 9개 챕터 + Front Matter 각각에 한 단락 summary 생성 data/index/64317.json (10 노드)
7. PageIndex retrieval Agentic 트리 walk을 직접 구현 (PageIndex의 openai-agents SDK 데모는 OpenAI 강결합이라 Ollama와 호환 약함). 각 레벨에서 (title, summary, node_id) 목록을 LLM에 보여주고 "관련 노드 ID JSON 배열"을 응답받아 재귀. leaf의 text를 모아 synthesis. System prompt로 챕터 인용 강제 src/pageindex_query.py
8. RAG baseline 의도적으로 단순하게: 같은 마크다운에서 챕터 헤딩을 제거 → 500-word/50-overlap 청크 → sentence-transformers/all-MiniLM-L6-v2 임베딩 → 코사인 top-5 → 같은 Ollama 모델에 투입. 챕터 정보가 청크 단위에서 사라진다는 점이 핵심 src/rag_baseline.py
9. CLI 통합 setup / build / query / rag / compare 5개 서브커맨드 src/cli.py
10. 실행 검증 두 개의 비교 쿼리를 end-to-end로 실행. 인덱스/쿼리/RAG/compare 출력을 stdout으로 확인 (로그 파일은 python -m src.cli ... | tee data/index/x.log로 자유롭게 보존 가능) stdout

Windows에서 발견된 한 가지 이슈: cp949 콘솔 코덱이 PageIndex의 em-dash() 출력을 처리하지 못해 빌드 후 print_tree가 죽었습니다. 트리 JSON 자체는 정상 저장되며, src/cli.pysrc/build_index.py 상단에서 stdout/stderr를 UTF-8로 reconfigure하도록 패치했습니다.


평가 — qwen2.5:7b 기준 측정 결과

5축 비교

기준 PageIndex Vector RAG 비고
Retrieval 정확도 ★★★★☆ ★★★☆☆ PageIndex는 챕터 단위로 정확히 선택. Q1(Ch1↔Ch9 시점 변화)에서 Ch1, 5, 6, 9를 정확히 골랐고, Q2(첫↔마지막 챕터)에서는 Ch9만 골라 Ch1을 놓침 — 모델 reasoning 한계
Synthesis 정확도 ★★★☆☆ ★★★★☆ 7B 모델이 6KB 챕터 본문 4–6개에 휘둘려 답변이 산만해지거나 환각(없는 챕터 인용) 발생. RAG는 작은 청크에 집중. 모델을 키우면(8B+, GPT-4o) 즉시 역전
근거 (Traceability) ★★★★★ ★☆☆☆☆ PageIndex 답변은 항상 (Chapter 9)처럼 사람이 검증 가능한 인용. RAG는 chunk #29 (word offset 13050) 같은 불투명 번호. PageIndex의 핵심 우위가 가장 명확히 드러나는 축
쿼리 속도 ★★☆☆☆ (~30–60s) ★★★★☆ (~10–30s) PageIndex는 multi-step (트리 walk N회 + synthesis 1회). RAG는 1-shot
인덱싱 속도 ★★☆☆☆ (~수 분) ★★★★☆ (~30s) PageIndex는 노드별 LLM 요약 호출 10회. RAG는 임베딩만

토큰 사용량 (대략 추정)

단계 PageIndex Vector RAG
1회성 인덱스 빌드 (LLM) ~70K input + ~5K output (10챕터 × 7K 토큰 본문 → summary) 0 (LLM 호출 없음, 임베딩 모델만)
인덱스 빌드 (임베딩) 0 ~70K 토큰을 MiniLM에 통과
쿼리 1회 입력 ~30–50K 토큰 (트리 walk 프롬프트 + leaf 본문 최대 6×6KB) ~3–5K 토큰 (top-5 × 500 단어)
쿼리 1회 출력 ~500–1,000 토큰 ~200–500 토큰

PageIndex 쿼리는 RAG의 약 5–10배 토큰 소모. 로컬 Ollama라 비용은 0이지만, 클라우드 API라면 같은 비율로 비용이 발생합니다. 대신 인덱스를 한 번 만들고 여러 번 쿼리하는 시나리오라면 빌드 비용은 amortize 됩니다.

정성 평가

잘 동작한 것

  • 트리 구조 인식: 9개 챕터 + Front Matter가 한 번에 정확히 노드화. PageIndex가 마크다운 # 헤딩만 보고 트리를 만들기 때문에, preprocess 정규식이 정확하면 인덱싱은 deterministic.
  • 챕터-aware 노드 선택: Q1에서 Ch1/5/6/9를 고른 동작은 vector RAG로는 흉내낼 수 없음. "Chapter 1부터 9까지의 변화"라는 구조적 의도를 retrieval 단계에서 LLM이 이해하고 반영.
  • Traceability: 모든 답변이 챕터 번호로 인용 → 사용자가 즉시 책을 펴 검증 가능. RAG의 "chunk #29"는 사용자에게 의미 있는 정보를 제공하지 못함.

잘 동작하지 않은 것

  • Cross-chapter 추론 실패: Q2의 "ending mirrors the opening"에서 Ch1과 Ch9를 둘 다 골라야 했지만 Ch9만 선택. 시스템의 한계가 아니라 qwen2.5:7b의 reasoning 한계. 답변 본문에서는 존재하지 않는 데이터로 Ch2를 인용하는 환각도 발생.
  • 긴 컨텍스트 부담: 챕터 본문 6KB × 4–6개 = 약 30KB를 7B 모델에 한 번에 넣으면 답변이 산만해지는 경향. RAG 쪽은 짧은 청크라 모델이 집중하기 쉬움.

핵심 발견

*PageIndex의 retrieval 우위는 모델과 무관하게 일관되며, synthesis 품질은 LLM 능력에 비례한다.* 7B 모델로도 PageIndex의 핵심 가치(챕터 단위 traceability, 구조 인식)는 명확히 시연되며, 모델만 강화하면 답변 품질도 같이 올라가는 구조.


검증 체크리스트

  • data/index/64317.json에 9 챕터 노드(+ Front Matter)가 있고 각 노드에 summary 필드가 채워져 있다.
  • python -m src.cli compare "..." 출력에서 PageIndex 답변에 (Chapter X) 형태의 인용이 포함되고, RAG 답변에는 chunk #N (word offset M) 형태의 청크 번호만 있다.
  • 쿼리 실행 중 ollama psqwen2.5:7b가 활성 상태이며, 외부 네트워크 호출이 발생하지 않는다.

트러블슈팅

  • Connection refused: Ollama가 실행되지 않았습니다. ollama serve 또는 Ollama 앱을 실행하세요.
  • 트리 빌드가 너무 오래 걸림: 7B 기준 Gatsby는 약 5–15분이면 끝납니다. 빌드를 건너뛰고 파이프라인의 나머지를 먼저 보고 싶다면 --no-summary로 빌드 (트리 walk가 제목만 보고 노드를 선택하게 됨, 품질 저하).
  • VRAM 부족: --model ollama_chat/llama3.2:3b 또는 qwen2.5:3b 같은 작은 모델로 교체.
  • Windows 콘솔에서 답변 출력 깨짐: cp949 코덱이 em-dash나 한글을 처리 못 하는 경우입니다. CLI는 stdout을 UTF-8로 자동 reconfigure 하지만, 직접 모듈 실행 시 문제가 보이면 set PYTHONIOENCODING=utf-8 (Windows) / export PYTHONIOENCODING=utf-8 (bash) 후 재실행.

안내

이 프로젝트는 클로드 코드의 도움을 받아 작성되었습니다

NotionToBlog

GitHub ↗

Notion 일기 → Hugo 블로그 자동 발행 파이프라인

README.md

NotionToBlog

Notion 일기 DB의 오늘자 페이지를 한 명령으로 Hugo(GitHub Pages) 포스트로 발행하는 개인용 파이프라인. Self-dogfood 전용 — PyPI 배포 의도 없음, Windows + Python 3.14 단일 지원. (내부 패키지 식별자는 dayblog / dayblog_mcp.)

Highlights

  • Notion 일기 → Hugo + GH Pages 자동 발행 — 두 차례 후속 릴리스(v0.2.0 / v0.2.1)로 실사용 피드백을 반영한 실가동 도구
  • Claude Code의 Hook + Skill + MCP 3축을 의도적으로 한정 적용 (Subagent / Plugin / Scheduled task는 범위 외) — 깊이를 위해 너비를 포기한 설계
  • 124 tests · GitHub Actions CI · Notion API 2025-09-03 (data_sources.query) 마이그레이션 대응
  • 핵심 설계 결정과 트레이드오프는 docs/domain-notes.md에 한 페이지로 정리 — commit history 자체가 곧 개발 일지
┌─────────────────────────┐         ┌──────────────────────────┐
│  NotionToBlog (this)    │         │  Hugo site (external)    │
│  - src/dayblog/         │ writes  │  D:\vscodeprojects\blog  │
│  - src/dayblog_mcp/     │ ──────► │  - content/posts/<slug>/ │
│  - .claude/ hooks       │         │  - themes/PaperMod/      │
│                         │         │  - .git/hooks/pre-push   │
└─────────────────────────┘         └──────────────────────────┘
         ↑                                     ↑
  Claude Code harness                   hugo server + git push
  (Hook + Skill + MCP)                   (GH Pages 배포)

요구 사항

  • Python 3.14
  • Windows (서브프로세스 · 경로 규약이 Windows 전제)
  • Hugo Extended (PaperMod SCSS)
  • Notion Integration 토큰 + 대상 DB ID + 통합이 DB에 초대됨
  • 외부 Hugo 사이트 레포 (예: D:\vscodeprojects\blog) — PaperMod 테마가 sub-module로 등록돼 있으면 git submodule update --init --recursive 필수

설치

pip install -e .[mcp,dev]

[mcp] extra = fastmcp, notion-client, httpx (Notion 연동). [dev] = pytest, ruff. 최소 실행(네트워크 없는 Hugo 툴링만)은 core deps(pyyaml, python-dotenv)로 충분.

설정

레포 루트에 .env 작성:

NOTION_TOKEN=ntn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
NOTION_DATABASE_ID=<32-hex, dashes optional>
HUGO_SITE_ROOT=D:\vscodeprojects\blog

Notion DB 스키마 요구사항 (docs/domain-notes.md §4):

속성 타입 필수
Title (또는 Name) title
Date date
Status select (Draft / Ready / Published)
Tags multi_select
Category select
Summary rich_text

/publish-todayStatus == Ready 필터만 잡습니다.

실행

CLI

python -X utf8 -m dayblog publish-today [--page-id <ID>] [--date YYYY-MM-DD]
python -X utf8 -m dayblog new-post --title "<제목>" [--date YYYY-MM-DD]
python -X utf8 -m dayblog list-drafts
python -X utf8 -m dayblog validate <path>
python -X utf8 -m dayblog install-pre-push    # Hugo 레포에 훅 설치

Claude Code 슬래시 커맨드

  • /today — 오늘자 Ready 페이지 목록 (진단)
  • /publish-today [page_id?] — Notion → Hugo 번들 발행
  • /post-new [date?] <title> — 수동 드래프트 스케폴드 (Notion 우회)
  • /draft-list / /publish-queuedraft: true 포스트 나열

MCP 툴 (.mcp.json이 자동 등록)

  • notion_list_pages(date?) — Ready 페이지 요약 목록
  • notion_get_page(page_id) — 원본 metadata + top-level blocks (진단용)
  • notion_render_markdown(page_id) — Markdown + 이미지 매니페스트 + 경고

글 작성·수정 플로우

새 글 발행

  1. Notion dayblog-journal DB에서 오늘자 페이지 작성 (Date / Title / 일기 본문)
  2. 페이지 맨 아래에 Heading 1 블로그 추가 → 그 아래 블로그용 내용 작성 (이 마커가 없으면 발행이 거부됨, domain-notes §9)
  3. StatusReady 로 변경
  4. python -X utf8 -m dayblog publish-today (또는 Claude /publish-today) — created 또는 updated 출력
  5. hugo server → 로컬 프리뷰 확인 (-D 불필요, baseURL이 서브패스면 http://localhost:1313/blog/)
  6. git add -A && git commit -m "post: <slug>" && git push — pre-push 훅이 손편집 draft 검사 후 GH Pages 자동 배포

Notion Status == Ready가 이미 발행 게이트 역할을 하므로 publish-today는 항상 draft: false로 직행 — 수동 플립 단계 없음.

발행된 글 수정

  1. Notion에서 본문 수정 → last_edited_time 자동 갱신
  2. python -X utf8 -m dayblog publish-today → idempotency가 updated로 감지, 번들 덮어씀
  3. git diff 확인 → commit + push

삭제

  • Notion StatusDraft: 이후 publish-today 대상에서 빠짐 (기존 번들은 손대지 않음)
  • 블로그에서도 지우려면 Hugo 레포의 해당 번들 디렉토리를 수동 삭제 후 commit + push

트러블슈팅: 빈 포스트가 나옴

  • 결과가 skipped-no-marker 면 Notion 페이지 본문에 top-level Heading 1 블로그 마커가 없음. toggle/callout 안의 H1은 인식 안 함 — 페이지 최상위 형제로 둬야 함.
  • Title/Date/Status property 이름이 정확히 매치되는지 (Status == Ready).

Idempotency

/publish-today를 같은 날짜로 여러 번 돌려도 안전합니다 (domain-notes §2 #1):

  • 기존 번들의 source_notion_id가 같고 lastmod ≥ Notion.last_edited_timeskipped
  • 더 오래됐으면 → updated (덮어쓰기)
  • 다른 페이지가 같은 날짜 slug를 점유 중이면 → -2/-3/… 로 자동 증분

Draft 보호 (Double guard)

NotionToBlog는 draft: true 포스트가 실수로 GH Pages에 올라가는 걸 막기 위해 두 훅을 동시에 설치합니다. (Notion publish-today는 이미 draft: false로 직행하므로 자동 발행 흐름에서는 차단되지 않습니다 — 훅은 /post-new로 만든 수동 드래프트 + 손편집으로 draft:true가 된 포스트 보호 용도.)

1. .git/hooks/pre-push (터미널 git push 커버)

Hugo 레포에서 한 번:

cd D:\vscodeprojects\blog
python -X utf8 -m dayblog install-pre-push

설치 후 git push 시 pushed 범위 안에 draft 포스트가 있으면 exit 1로 차단하고 파일 경로를 stderr에 나열합니다.

2. Claude Code PreToolUse (Claude가 내는 Bash 커버)

.claude/settings.json에 등록돼 있음. Claude가 git push Bash 툴을 호출할 때 JSON deny로 차단합니다. 중요: 이 훅은 HUGO_SITE_ROOT를 읽어서 그 레포를 스캔합니다 — .env에 제대로 세팅돼 있어야 작동.

왜 둘 다인가

Claude Code PreToolUse 훅은 구조적으로 "Claude가 Bash 툴로 실행하는" push만 가로챕니다. 사용자가 직접 터미널에서 git push하면 이 훅은 호출되지 않습니다. 그래서 git의 네이티브 pre-push도 필수. 두 훅이 동일한 Python 모듈(dayblog.hooks.pre_push_guard)을 호출하므로 로직은 DRY.

훅 우회 (의도적 push)

draft를 유지한 채로도 push해야 할 (매우 드문) 경우:

  • 터미널: git push --no-verify
  • Claude: 세션에서 훅 비활성화 (권장하지 않음)

로컬 렌더 확인

cd D:\vscodeprojects\blog
hugo server -D
# baseURL 서브패스가 있으면 http://localhost:1313/blog/

-D는 drafts 포함. 플립 전에 로컬에서 한 번 보고 draft: false로 바꿔 push.

범위 외

  • PyPI 배포 (self-dogfood 전제)
  • 과거 Notion 일기 일괄 마이그레이션
  • Notion DB 역방향 동기화 (블로그 → Notion)
  • Windows 외 OS, Python 3.14 외 버전
  • Subagent / Plugin / Scheduled task / Status line (일부러 깊이 제한)

테스트

pytest              # 111+ tests
pytest -k smoke
ruff check src tests

문서

안내

이 프로젝트는 클로드 코드의 도움을 받아 작성되었습니다.

라이선스

MIT

SCP World

GitHub ↗

SCP Foundation 위키 콘텐츠를 RAG로 검색하고, 재단 페르소나(연구원/요원/SCP-079)로 답변하는 챗봇 데모. 100% 서버리스 (Cloud Run + Firebase Hosting + Firestore) 로 운영됩니다.

README.md

SCP World

SCP Foundation 위키 콘텐츠를 RAG로 검색하고, 재단 페르소나(연구원/요원/SCP-079)로 답변하는 챗봇 데모. 100% 서버리스 (Cloud Run + Firebase Hosting + Firestore) 로 운영됩니다.


Overview: 프로젝트 개요

기획 배경 및 목표

세계관을 탐구할 때, 단순히 위키를 읽는 것이 아니라 하나의 페르소나를 가진 캐릭터와 대화하며 알아가면 재밌을 것 같다고 생각했습니다. 방대한 자료와 오픈된 라이선스(CC BY-SA 3.0)를 가진 SCP 위키를 기반으로, 3가지 캐릭터를 통해 SCP 세계관에 대해 탐구할 수 있는 챗봇을 만들었습니다.

핵심 기능

기능 설명
페르소나 선택 연구원(Dr. [REDACTED]), 요원(Agent [REDACTED]), SCP-079(Old AI) 중 선택
RAG 기반 대화 SCP 위키 문서를 벡터 검색하여 근거 있는 답변 생성
SSE 스트리밍 토큰 단위 실시간 스트리밍으로 자연스러운 대화 경험
페르소나별 격리 캐릭터마다 독립된 대화 세션 유지
Google 로그인 OAuth 2.0 기반 인증, ID Token으로 모든 API 보호
출처 표시 답변에 사용된 SCP 위키 원문 URL 제공

기술 스택

레이어 기술
Frontend Flutter Web, Riverpod, go_router, Google Sign-In v7
Backend API FastAPI, Uvicorn, Python 3.11
LLM Serving vLLM (Qwen2.5-7B-Instruct), NVIDIA L4 GPU
Embedding BAAI/bge-m3 (1024차원)
Vector DB Firestore Native Vector Search (find_nearest, COSINE)
Infra Cloud Run (Scale-to-Zero), Firebase Hosting, Firestore
Auth Google OAuth 2.0, ID Token 검증

시스템 상세 설명


System Architecture

시스템 구성도

시스템 구성도

데이터 흐름도

데이터 수집 파이프라인 (오프라인)

데이터 수집 파이프라인

실시간 질의 파이프라인 (온라인)

실시간 질의 파이프라인


라이선스

두 라이선스는 서로 다른 대상에 적용됩니다. 본 저장소의 코드를 재사용할 때는 MIT, RAG로 제공되는 SCP 위키 텍스트를 재배포할 때는 CC-BY-SA 3.0 조건을 따라야 합니다.

안내

클로드 코드를 활용하여 작성되었습니다

RAG Chat Project

GitHub ↗

Retrieval-Augmented Generation 기반의 AI 채팅 애플리케이션 — 사용자가 직접 AI 페르소나를 생성하고 문서를 업로드하여 질의응답(Q&A)을 할 수 있는 풀스택 프로젝트

README.md

RAG Chat

사용자가 AI 페르소나를 생성하고, 문서(PDF/텍스트/오디오)를 업로드한 뒤, 해당 문서를 기반으로 질의응답(Q&A)을 할 수 있는 RAG 채팅 애플리케이션입니다.


Overview: 프로젝트 개요

기획 배경 및 목표

사용자가 직접 업로드한 문서들을 바탕으로 특색있는 페르소나를 지닌 챗봇과 대화할 수 있는 RAG 파이프라인을 구축했습니다. 단일 봇이 아닌 페르소나 단위로 문서 컨텍스트를 격리하여, 용도별 전문 AI를 만들 수 있는 풀스택 프로젝트입니다.

핵심 기능

기능 설명
페르소나 생성 사용자가 직접 AI 페르소나를 생성하고 문서 컨텍스트를 격리
RAG 기반 대화 업로드된 문서를 벡터 검색하여 근거 있는 답변 생성
SSE 스트리밍 토큰 단위 실시간 스트리밍으로 자연스러운 대화 경험
다중 세션 관리 페르소나마다 독립된 대화 세션을 여러 개 유지
문서 업로드 PDF, TXT, MD, CSV, 오디오 파일 지원 (오디오는 STT 변환)
Google 로그인 OAuth 2.0 기반 인증, JWT로 모든 API 보호
품질 모니터링 RAG 검색 유사도를 자동 기록하고 리포트로 시각화

기술 스택

레이어 기술
Frontend Flutter Desktop (Windows), Provider, Dio, Google Sign-In
Backend API FastAPI, Uvicorn, SQLAlchemy, Pydantic, LangChain
LLM Ollama (qwen3:8b), NVIDIA GPU 가속
Embedding BAAI/bge-m3 (1024차원), sentence-transformers
Vector DB Qdrant (COSINE similarity)
Database PostgreSQL 15 (대화 기록), Redis 7 (캐시)
Auth Google OAuth 2.0, JWT (HS256)
Infra Docker Compose, NVIDIA Container Toolkit, Nginx (프로덕션)

시스템 상세 설명


System Architecture

시스템 구성도

시스템 구성도

데이터 흐름도

문서 업로드 파이프라인

문서 업로드 파이프라인

실시간 질의 파이프라인

실시간 질의 파이프라인


앱 화면

RAG Chat 대화 화면


안내

이 프로젝트는 학습 및 개인 프로젝트 목적으로 작성되었습니다. 클로드 코드를 활용하여 작성되었습니다.