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:
129
CLAUDE.md
129
CLAUDE.md
@@ -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 수동 등록)
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user