# hyungi_Document_Server — Claude Code 작업 가이드 ## Infrastructure Reference 📌 운영 사실 (모델명 / 엔드포인트 / IP / 컨테이너 / 포트 / drift) 의 단일 진실 소스(SSOT): **`~/.claude/projects/-Users-hyungiahn/memory/infra_inventory.md`** 이 파일과 inventory 가 충돌하면 **inventory 가 정답**. 본 CLAUDE.md 는 코딩 규칙·워크플로우·코드 구조에 집중하고 운영 값은 박지 않는다. 운영 변경 정책 (inventory → config → deploy → verify): 1. `infra_inventory.md` 먼저 갱신 2. `config.yaml` / `credentials.env` 갱신 3. deploy (commit → push → GPU pull → `docker compose up -d --build`) 4. verify (smoke endpoint, postgres count, 모니터링) 순서 어기면 drift. 발견 시 inventory `Drift Log` 등록. **Search experiment soft lock**: Phase 2 search refactor / QueryAnalyzer / run_eval 진행 중일 때 GPU 서버의 `docker compose restart`, `config.yaml` 수정, Ollama pull 금지. flag = `~/.claude/.search-experiment-active`. --- ## 프로젝트 개요 Self-hosted PKM(Personal Knowledge Management) + 다국 뉴스 비교 분석 웹 애플리케이션. GPU 서버가 메인 (Docker Compose / DB / 검색 / OCR / 마커), Mac mini = MLX 추론 + Whisper STT, Synology NAS = 파일 원본. ## 핵심 문서 1. `README.md` — 외부 소개 (기술 스택 / 주요 기능 / Quick Start) 2. `docs/architecture.md` — 전체 시스템 아키텍처 3. `docs/deploy.md` — Docker Compose 배포 가이드 4. `docs/development-stages.md` — Phase roadmap (역사적 맥락) ## 기술 스택 | 영역 | 기술 | |------|------| | 백엔드 | FastAPI (Python 3.11+), SQLAlchemy 2.0 async, APScheduler | | DB | PostgreSQL 16 + pgvector + pg_trgm (단일 `pkm` DB) | | 프론트엔드 | SvelteKit 5 (runes mode) + Tailwind CSS 4 | | 문서 파싱 | kordoc (HWP/HWPX/PDF → MD), LibreOffice headless (오피스), marker (PDF → markdown) | | OCR | Surya OCR (docker compose `ocr-service`, GPU) | | STT | MLX Whisper (Mac mini), GPU faster-whisper 는 legacy profile | | 리버스 프록시 | Caddy (HTTP only, 앞단 home-caddy 가 HTTPS 종료) | | 인증 | JWT (access) + HttpOnly cookie (refresh) + TOTP 2FA | | 컨테이너 | Docker Compose | ## 머신 역할 (자세한 IP / 포트 → inventory) | 머신 | 역할 | |------|------| | GPU 서버 | Docker Compose 메인: fastapi · frontend · postgres `pkm` · kordoc · ocr-service · marker-service · reranker (TEI) · caddy. Ollama (embedding / 4B 추론). home-gateway 별 compose (ingress + 나노클로 + searxng) | | Mac mini | MLX 26B 추론 endpoint + MLX Whisper STT. ingress 역할 0 | | Synology NAS | 파일 원본 (`/volume4/Document_Server/PKM/` → GPU `/mnt/nas/Document_Server` NFS), Synology Office/Drive/Calendar/MailPlus | | VPS-2 (OVH) | 메일 relay (`relay.hyungi.net:587`), Gitea bare mirror, Secondary MX | ## AI 파이프라인 (역할 기준 — 실제 모델 매핑은 inventory) | 역할 | 위치 | |------|------| | 분류/심층 요약 primary | Mac mini MLX 26B | | Triage (1차 분류) / Fallback / Chat | GPU Ollama 4B | | Embedding | GPU Ollama (1024d, 다국어) | | Reranker | GPU TEI 컨테이너 | | OCR | docker compose `ocr-service` (Surya OCR GPU) — `ai.models.vision` 미사용 | | STT | Mac mini MLX Whisper large-v3 | | Premium (수동 trigger) | Anthropic API (`require_explicit_trigger`, 일일 한도) | 호출 시 반드시 `app/ai/client.py` 의 `AIClient` 사용 (`call_triage` / `call_primary` / `call_fallback`). 직접 HTTP 호출 금지. ## 문서 처리 파이프라인 ``` 파일 업로드 (드래그 앤 드롭 or file_watcher) ↓ extract (텍스트 추출) - kordoc: HWP, HWPX, PDF → Markdown - LibreOffice: xlsx, docx, pptx 등 → txt/csv - 직접 읽기: md, txt, csv, json, xml, html ↓ ↓ classify_worker (tier triage) preview / marker - 4B Ollama → TriageOutput - LibreOffice → PDF 변환 - escalate_to_26b 시 deep_summary - marker → PDF → markdown - ai_tldr / ai_bullets / inconsistencies ↓ embed_worker (bge-m3 1024d, doc-level) chunk_worker (문서 유형별 chunking) ``` 핵심 원칙: - 파일은 업로드 위치에 그대로 유지 (물리적 이동 없음) - 분류 (`ai_domain` / `ai_sub_group` / `ai_tags` / `category` / `tier`) 는 DB 메타데이터로만 관리 - preview / marker 는 classify 와 병렬 ## 워커 / 스케줄러 (`app/main.py` 의 scheduler.add_job) - queue_consumer (interval 1m), file_watcher (5m), upload_cleanup (10m) - study_q_embed (1m), study_q_related_refresh (1m), study_queue (1m), study_session_queue (1m) - tier_backfill (30m) - law_monitor (07:00 KST), mailplus_archive (07/18:00 KST) - daily_digest (20:00 KST) - **global_digest** (04:00 KST) — Phase 4 country×topic 7일 rolling - **morning_briefing** (05:10 KST) — 야간 KST 0~5h 수집 뉴스 topic×country 비교 scheduler timezone = `Asia/Seoul`. ## 데이터 계층 1. **원본 파일** — NAS `/volume4/Document_Server/PKM/`. 유일한 원본, 위치 변경 없음 2. **가공 데이터** — PostgreSQL `pkm` (텍스트, AI 분류, 검색 인덱스, 메모, 태그, briefing, digest, …) 3. **파생물** — pgvector embedding, PDF preview 캐시 (`.preview/`), marker 결과 (markdown + extracted_images NAS 저장) ## 코딩 규칙 - Python 3.11+, asyncio, type hints - SQLAlchemy 2.0+ async 세션 - Svelte 5 runes mode (`$state`, `$derived`, `$effect` — `$:` 금지) - 인증 정보는 `credentials.env` 에서 로딩 (하드코딩 금지) - 로그는 `logs/` (Docker 볼륨) - AI 호출은 반드시 `app/ai/client.py` 의 `AIClient` 경유 - 한글 주석 사용 - Migration: `migrations/NNN_*.sql`, `init_db()` 자동 실행 (`schema_migrations` 추적) - SQL 에 `BEGIN/COMMIT` 금지 (외부 트랜잭션 깨짐) - asyncpg `prepared statement` 가 multi-statement 불허 → 1 statement 1 파일 분리 - 기존 DB 에서는 `schema_migrations` 수동 이력 등록 필요할 수 있음 - 디자인 시스템 토큰 only (`bg-surface`, `text-dim`, `border-default`, `text-accent`, …). `bg-[var(--*)]` 금지 (`lint:tokens` 차단) - 커밋 메시지: `type(scope): summary` (`feat` / `fix` / `refactor` / `ops` / `incident` / `docs`) ## 개발 / 배포 워크플로우 ```bash # 개발 (MacBook Pro) cd ~/Documents/code/hyungi_Document_Server/ # 코드 작성 → git commit → push (Gitea) # 배포 (GPU 서버) ssh gpu cd ~/Documents/code/hyungi_Document_Server/ git pull docker compose up -d --build fastapi frontend ``` PR 머지는 Gitea UI **Rebase and merge** 기본 (선형 히스토리 + force-push 충돌 회피). 단독 작업 확증 시만 로컬 rebase+FF. ## v1 코드 참조 v1 (DEVONthink 기반) 코드는 `v1-final` 태그로 보존: ```bash git show v1-final:scripts/law_monitor.py git show v1-final:scripts/pkm_utils.py ``` ## 주의사항 - `credentials.env` 는 git 에 올리지 않음 (`.gitignore`) - NAS NFS 마운트: Docker 컨테이너 내 `/documents`. FastAPI 시작 시 `/documents/PKM` 존재 확인 - 법령 API (LAW_OC) 는 승인 대기 중 - Ollama 는 127.0.0.1 바인딩 (외부 접근 차단) - Caddy 는 `auto_https off` + `http://` only (HTTPS 종료는 앞단 home-caddy 가 처리) - Synology Office 편집은 새 탭 열기 방식 (iframe 미사용, `edit_url` 수동 등록) - 한국어 NFS 경로는 NFC↔NFD 비대칭 — 경로 수신 시 NFC→NFD→parent glob fallback 필수