stale 영역 정리: - Qwen3.5-35B-A3B / nomic-embed-text / Qwen2.5-VL-7B → 역할별 표기 (실제 모델은 inventory) - Mac mini Tailscale 100.76.254.116 / GPU 100.111.160.84 / NAS 100.101.79.37 → 모두 폐기 (D21 closure 2026-05-12), LAN 표기만 유지 - Mac mini nginx 앞단 프록시 → 폐기 (home-caddy 가 직접 ingress) - "Mac mini 메인 docker compose" → GPU 가 메인 정정 추가: - 운영 변경 정책 (inventory → config → deploy → verify) - 머신 역할 표 / AI 파이프라인 역할 표 / 워커 스케줄 표 - 아침 브리핑 / global digest 진입점 + scheduler timezone - asyncpg multi-statement 1 파일 1 statement 규칙 (PR-MorningBriefing-1 fix 교훈) - 디자인 토큰 only 규칙 - 한국어 NFS 경로 NFC/NFD
7.4 KiB
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):
infra_inventory.md먼저 갱신config.yaml/credentials.env갱신- deploy (commit → push → GPU pull →
docker compose up -d --build) - 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 = 파일 원본.
핵심 문서
README.md— 외부 소개 (기술 스택 / 주요 기능 / Quick Start)docs/architecture.md— 전체 시스템 아키텍처docs/deploy.md— Docker Compose 배포 가이드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.
데이터 계층
- 원본 파일 — NAS
/volume4/Document_Server/PKM/. 유일한 원본, 위치 변경 없음 - 가공 데이터 — PostgreSQL
pkm(텍스트, AI 분류, 검색 인덱스, 메모, 태그, briefing, digest, …) - 파생물 — 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수동 이력 등록 필요할 수 있음
- SQL 에
- 디자인 시스템 토큰 only (
bg-surface,text-dim,border-default,text-accent, …).bg-[var(--*)]금지 (lint:tokens차단) - 커밋 메시지:
type(scope): summary(feat/fix/refactor/ops/incident/docs)
개발 / 배포 워크플로우
# 개발 (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 태그로 보존:
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 필수