fix: preview enum 누락 + AI summary thinking 제거 + CLAUDE.md 전면 갱신

- queue.py: process_stage enum에 'preview' 추가
- classify_worker: ai_summary에 strip_thinking() 적용
- CLAUDE.md: 현재 아키텍처 전면 반영 (파이프라인, UI, 인프라, 코딩규칙)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-03 12:38:59 +09:00
parent 6893ea132d
commit 733f730e16
3 changed files with 96 additions and 38 deletions

129
CLAUDE.md
View File

@@ -4,7 +4,7 @@
Self-hosted PKM(Personal Knowledge Management) 웹 애플리케이션. Self-hosted PKM(Personal Knowledge Management) 웹 애플리케이션.
FastAPI + PostgreSQL(pgvector) + SvelteKit + Docker Compose 기반. FastAPI + PostgreSQL(pgvector) + SvelteKit + Docker Compose 기반.
Mac mini M4 Pro를 애플리케이션 서버, Synology NAS를 파일 저장소, GPU 서버를 AI 추론에 사용한다. GPU 서버를 메인 서버, Mac mini를 AI 추론, Synology NAS를 파일 저장소 사용.
## 핵심 문서 ## 핵심 문서
@@ -18,9 +18,9 @@ Mac mini M4 Pro를 애플리케이션 서버, Synology NAS를 파일 저장소,
|------|------| |------|------|
| 백엔드 | FastAPI (Python 3.11+) | | 백엔드 | FastAPI (Python 3.11+) |
| 데이터베이스 | PostgreSQL 16 + pgvector + pg_trgm | | 데이터베이스 | PostgreSQL 16 + pgvector + pg_trgm |
| 프론트엔드 | SvelteKit | | 프론트엔드 | SvelteKit 5 (runes mode) + Tailwind CSS 4 |
| 문서 파싱 | kordoc (Node.js, HWP/HWPX/PDF → Markdown) | | 문서 파싱 | kordoc (HWP/HWPX/PDF → Markdown) + LibreOffice (오피스 → 텍스트/PDF) |
| 리버스 프록시 | Caddy (자동 HTTPS) | | 리버스 프록시 | Caddy (HTTP only, 앞단 프록시에서 HTTPS 처리) |
| 인증 | JWT + TOTP 2FA | | 인증 | JWT + TOTP 2FA |
| 컨테이너 | Docker Compose | | 컨테이너 | Docker Compose |
@@ -29,23 +29,23 @@ Mac mini M4 Pro를 애플리케이션 서버, Synology NAS를 파일 저장소,
``` ```
GPU 서버 (RTX 4070 Ti Super, Ubuntu, 메인 서버): GPU 서버 (RTX 4070 Ti Super, Ubuntu, 메인 서버):
- Docker Compose: FastAPI(:8000), PostgreSQL(:5432), kordoc(:3100), - Docker Compose: FastAPI(:8000), PostgreSQL(:5432), kordoc(:3100),
Caddy(:8080 HTTP only), Ollama(:11434), AI Gateway(:8081), frontend(:3000) Caddy(:8080 HTTP only), Ollama(127.0.0.1:11434), AI Gateway(127.0.0.1:8081), frontend(:3000)
- NFS 마운트: /mnt/nas/Document_Server → NAS /volume4/Document_Server - NFS 마운트: /mnt/nas/Document_Server → NAS /volume4/Document_Server
- 외부 접근: document.hyungi.net (앞단 프록시 → Caddy) - 외부 접근: document.hyungi.net (Mac mini nginx → Caddy)
- 로컬 IP: 192.168.1.186 - 로컬 IP: 192.168.1.186
Mac mini M4 Pro (AI 서버): Mac mini M4 Pro (AI 서버 + 앞단 프록시):
- MLX Server: http://100.76.254.116:8800/v1/chat/completions (Qwen3.5-35B-A3B) - MLX Server: http://100.76.254.116:8800/v1/chat/completions (Qwen3.5-35B-A3B)
- nginx: HTTPS 종료 → GPU 서버 Caddy(:8080)로 프록시
- Tailscale IP: 100.76.254.116 - Tailscale IP: 100.76.254.116
Synology NAS (DS1525+): Synology NAS (DS1525+):
- 도메인: ds1525.hyungi.net - LAN IP: 192.168.1.227
- Tailscale IP: 100.101.79.37 - Tailscale IP: 100.101.79.37
- 포트: 15001
- 파일 원본: /volume4/Document_Server/PKM/ - 파일 원본: /volume4/Document_Server/PKM/
- NFS export → GPU 서버 - NFS export → GPU 서버
- Synology Office: 문서 편집/미리보기 - Synology Drive: https://link.hyungi.net (문서 편집)
- Synology Calendar: CalDAV 태스크 관리 (OmniFocus 대체) - Synology Calendar: CalDAV 태스크 관리
- MailPlus: IMAP(993) + SMTP(465) - MailPlus: IMAP(993) + SMTP(465)
``` ```
@@ -71,52 +71,106 @@ Premium (Claude API, 종량제, 수동 트리거만):
→ 일일 한도 $5, require_explicit_trigger: true → 일일 한도 $5, require_explicit_trigger: true
Embedding (GPU Ollama, 같은 Docker 네트워크): Embedding (GPU Ollama, 같은 Docker 네트워크):
nomic-embed-text → 벡터 임베딩 → http://ollama:11434/api/embeddings nomic-embed-text → 벡터 임베딩
Qwen2.5-VL-7B → 이미지/도면 OCR → http://ollama:11434/api/generate Qwen2.5-VL-7B → 이미지/도면 OCR
bge-reranker-v2-m3 → RAG 리랭킹 → http://ollama:11434/api/rerank bge-reranker-v2-m3 → RAG 리랭킹
``` ```
## 프로젝트 구조 ## 프로젝트 구조
``` ```
hyungi_Document_Server/ hyungi_Document_Server/
├── docker-compose.yml ← Mac mini용 ├── docker-compose.yml
├── Caddyfile ├── Caddyfile ← HTTP only, auto_https off
├── config.yaml ← AI 엔드포인트, NAS 경로, 스케줄 ├── config.yaml ← AI 엔드포인트, NAS 경로, 스케줄
├── credentials.env.example ├── credentials.env.example
├── app/ ← FastAPI 백엔드 ├── app/ ← FastAPI 백엔드
│ ├── main.py │ ├── main.py ← 엔트리포인트 + APScheduler (watcher/consumer 포함)
│ ├── Dockerfile ← LibreOffice headless 포함
│ ├── core/ (config, database, auth, utils) │ ├── core/ (config, database, auth, utils)
│ ├── models/ (document, task, queue) │ ├── models/ (document, task, queue)
│ ├── api/ (documents, search, tasks, dashboard, export) │ ├── api/ (documents, search, dashboard, auth, setup)
│ ├── workers/(file_watcher, extract, classify, embed, law_monitor, mailplus, digest) │ ├── workers/ (file_watcher, extract, classify, embed, preview, law_monitor, mailplus, digest, queue_consumer)
│ ├── prompts/classify.txt │ ├── prompts/classify.txt
│ └── ai/client.py │ └── ai/client.py ← AIClient + parse_json_response (Qwen3.5 thinking 처리)
├── services/kordoc/ ← Node.js 마이크로서비스 ├── services/kordoc/ ← Node.js 마이크로서비스 (HWP/PDF 파싱)
├── gpu-server/ ← GPU 서버용 (별도 배포) ├── gpu-server/ ← AI Gateway (deprecated, 통합됨)
│ ├── docker-compose.yml ├── frontend/ ← SvelteKit 5
│ └── services/ai-gateway/ │ └── src/
├── frontend/ ← SvelteKit ├── routes/ ← 페이지 (documents, inbox, settings, login)
├── migrations/ ← PostgreSQL 스키마 │ └── lib/
├── scripts/migrate_from_devonthink.py │ ├── components/ ← Sidebar, DocumentCard, DocumentViewer, PreviewPanel,
│ │ TagPill, FormatIcon, UploadDropzone
│ ├── stores/ ← auth, ui
│ └── api.ts ← fetch wrapper (JWT 토큰 관리)
├── migrations/ ← PostgreSQL 스키마 (schema_migrations로 추적)
├── scripts/
├── docs/ ├── docs/
└── tests/ └── tests/
``` ```
## 데이터 3계층 ## 문서 처리 파이프라인
1. **원본 파일** (NAS `/volume4/Document_Server/PKM/`) — 유일한 진짜 원본 ```
2. **가공 데이터** (PostgreSQL) — 텍스트 추출, AI 메타데이터, 검색 인덱스 파일 업로드 (드래그 앤 드롭 or file_watcher)
3. **파생물** (pgvector + 캐시) — 벡터 임베딩, 썸네일
extract (텍스트 추출)
- kordoc: HWP, HWPX, PDF → Markdown
- LibreOffice: xlsx, docx, pptx, odt 등 → txt/csv
- 직접 읽기: md, txt, csv, json, xml, html
↓ ↓
classify (AI 분류) preview (PDF 미리보기 생성)
- Qwen3.5 → domain - LibreOffice → PDF 변환
- tags, summary - 캐시: PKM/.preview/{id}.pdf
embed (벡터 임베딩)
- nomic-embed-text (768차원)
```
**핵심 원칙:**
- 파일은 업로드 위치에 그대로 유지 (물리적 이동 없음)
- 분류(domain/sub_group/tags)는 DB 메타데이터로만 관리
- preview는 classify와 병렬로 실행 (AI 결과 불필요)
## UI 구조
```
┌──────────────────────────────────────────────────┐
│ [☰ 사이드바] [PKM / 문서] [ 정보] 버튼│ ← 상단 nav
├──────────────────────────────────────────────────┤
│ [검색바] [모드] [] │
│ 문서 목록 (30%) — 드래그 업로드 지원 │ ← 상단 영역
│ █ 문서카드 (domain 색상 바 + 포맷 아이콘) │
├──────────────────────────────────────────────────┤
│ 하단 뷰어/편집 (70%) — 전체 너비 │ ← 하단 영역
│ Markdown: split editor (textarea + preview) │
│ PDF: 브라우저 내장 뷰어 │
│ 오피스: PDF 변환 미리보기 + [편집] 새 탭 버튼 │
│ 이미지: img 태그 │
└──────────────────────────────────────────────────┘
사이드바: 평소 접힘, ☰로 오버레이 (domain 트리 + 스마트 그룹 + Inbox)
정보 패널: 버튼 → 우측 전체 높이 drawer (메모/태그 편집/메타/처리상태/편집 URL)
```
## 데이터 계층
1. **원본 파일** (NAS `/volume4/Document_Server/PKM/`) — 유일한 원본, 위치 변경 없음
2. **가공 데이터** (PostgreSQL) — 텍스트 추출, AI 분류, 검색 인덱스, 메모, 태그
3. **파생물** — 벡터 임베딩 (pgvector), PDF 미리보기 캐시 (`.preview/`)
## 코딩 규칙 ## 코딩 규칙
- Python 3.11+, asyncio, type hints - Python 3.11+, asyncio, type hints
- SQLAlchemy 2.0+ async 세션 - SQLAlchemy 2.0+ async 세션
- Svelte 5 runes mode ($state, $derived, $effect — $: 사용 금지)
- 인증 정보는 credentials.env에서 로딩 (하드코딩 금지) - 인증 정보는 credentials.env에서 로딩 (하드코딩 금지)
- 로그는 `logs/`에 저장 (Docker 볼륨) - 로그는 `logs/`에 저장 (Docker 볼륨)
- AI 호출은 반드시 `app/ai/client.py``AIClient`를 통해 (직접 HTTP 호출 금지) - AI 호출은 반드시 `app/ai/client.py``AIClient`를 통해 (직접 HTTP 호출 금지)
- 한글 주석 사용 - 한글 주석 사용
- Migration: `migrations/*.sql`에 작성, `init_db()`가 자동 실행 (schema_migrations 추적)
- SQL에 BEGIN/COMMIT 금지 (외부 트랜잭션 깨짐)
- 기존 DB에서는 schema_migrations에 수동 이력 등록 필요할 수 있음
## 개발/배포 워크플로우 ## 개발/배포 워크플로우
@@ -128,9 +182,10 @@ MacBook Pro (개발) → Gitea push → GPU 서버에서 pull
# 코드 작성 → git commit & push # 코드 작성 → git commit & push
GPU 서버 배포 (메인): GPU 서버 배포 (메인):
ssh hyungi@100.111.160.84
cd ~/Documents/code/hyungi_Document_Server/ cd ~/Documents/code/hyungi_Document_Server/
git pull git pull
docker compose up -d docker compose up -d --build fastapi frontend
``` ```
## v1 코드 참조 ## v1 코드 참조
@@ -139,12 +194,14 @@ v1(DEVONthink 기반) 코드는 `v1-final` 태그로 보존:
```bash ```bash
git show v1-final:scripts/law_monitor.py git show v1-final:scripts/law_monitor.py
git show v1-final:scripts/pkm_utils.py git show v1-final:scripts/pkm_utils.py
git show v1-final:scripts/prompts/classify_document.txt
``` ```
## 주의사항 ## 주의사항
- credentials.env는 git에 올리지 않음 (.gitignore) - credentials.env는 git에 올리지 않음 (.gitignore)
- NAS SMB 마운트 경로: Docker 컨테이너 내 `/documents` - NAS NFS 마운트 경로: Docker 컨테이너 내 `/documents`
- 법령 API (LAW_OC)는 승인 대기 중 — 스크립트만 만들고 실제 호출은 승인 후 - FastAPI 시작 시 `/documents/PKM` 존재 확인 (NFS 미마운트 방지)
- GPU 서버 Tailscale IP는 credentials.env에서 관리 - 법령 API (LAW_OC)는 승인 대기 중
- Ollama/AI Gateway 포트는 127.0.0.1 바인딩 (외부 접근 차단)
- Caddy는 `auto_https off` + `http://` only (HTTPS는 Mac mini nginx에서 처리)
- Synology Office 편집은 새 탭 열기 방식 (iframe 미사용, edit_url 수동 등록)

View File

@@ -14,7 +14,7 @@ class ProcessingQueue(Base):
id: Mapped[int] = mapped_column(BigInteger, primary_key=True) id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
document_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("documents.id"), nullable=False) document_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("documents.id"), nullable=False)
stage: Mapped[str] = mapped_column( stage: Mapped[str] = mapped_column(
Enum("extract", "classify", "embed", name="process_stage"), nullable=False Enum("extract", "classify", "embed", "preview", name="process_stage"), nullable=False
) )
status: Mapped[str] = mapped_column( status: Mapped[str] = mapped_column(
Enum("pending", "processing", "completed", "failed", name="process_status"), Enum("pending", "processing", "completed", "failed", name="process_status"),

View File

@@ -62,8 +62,9 @@ async def process(document_id: int, session: AsyncSession) -> None:
doc.data_origin = parsed["dataOrigin"] doc.data_origin = parsed["dataOrigin"]
# ─── 요약 ─── # ─── 요약 ───
from ai.client import strip_thinking
summary = await client.summarize(doc.extracted_text[:15000]) summary = await client.summarize(doc.extracted_text[:15000])
doc.ai_summary = summary doc.ai_summary = strip_thinking(summary)
# ─── 메타데이터 ─── # ─── 메타데이터 ───
doc.ai_model_version = "qwen3.5-35b-a3b" doc.ai_model_version = "qwen3.5-35b-a3b"