docs: architecture.md 대규모 갱신 — GPU 서버 재구성 반영
- ChromaDB → Qdrant 전체 치환 (28건) - nomic-embed-text → bge-m3 (1024차원) 전체 치환 (12건) - Qwen2.5-VL-7B → Surya OCR (:8400) 전체 치환 (5건) - VRAM 다이어그램 갱신 (~11.3GB → ~7-8GB) - 3-Tier 라우팅 전략, 모델 협업 파이프라인 갱신 - Komga 만화 서버 GPU 서버 이전 반영 - embed_to_chroma.py 삭제 (embed_to_qdrant.py로 대체) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -169,12 +169,12 @@ DEVONthink 4의 커스텀 메타데이터 필드를 활용합니다.
|
||||
### AI 결과물 저장 전략 — 중복 저장 금지
|
||||
|
||||
GPU 서버에서 처리된 AI 결과물은 **각자 목적에 맞는 곳에만** 저장합니다.
|
||||
DEVONthink와 ChromaDB에 같은 정보를 이중으로 넣지 않습니다.
|
||||
DEVONthink와 Qdrant에 같은 정보를 이중으로 넣지 않습니다.
|
||||
|
||||
```
|
||||
처리 결과 저장 위치 이유
|
||||
───────────────────────────────────────────────────────
|
||||
벡터 임베딩 ChromaDB만 시맨틱 검색 전용, DEVONthink에선 쓸모없음
|
||||
벡터 임베딩 Qdrant만 시맨틱 검색 전용, DEVONthink에선 쓸모없음
|
||||
비전 OCR 텍스트 DEVONthink 본문에 병합 검색 가능한 텍스트가 되어야 하므로 필수
|
||||
리랭킹 점수 저장 안 함 (휘발) 쿼리 시점에만 의미 있는 일회성 데이터
|
||||
태그/분류 DEVONthink 태그만 Smart Group, 브라우징에 활용
|
||||
@@ -183,10 +183,10 @@ OmniFocus 역링크 DEVONthink 메타데이터 양방향 참조에 필요
|
||||
```
|
||||
|
||||
**핵심 원칙:**
|
||||
- ChromaDB = 벡터 검색 엔진. 여기엔 임베딩만 들어감
|
||||
- Qdrant = 벡터 검색 엔진. 여기엔 임베딩만 들어감
|
||||
- DEVONthink = 원본 문서 + 사람이 읽는 메타데이터(태그, 링크)
|
||||
- 요약/분석은 RAG로 실시간 생성하면 되므로 별도 캐싱 불필요
|
||||
- 비전 모델의 OCR 결과만 DEVONthink 본문에 반드시 병합 (검색성 확보)
|
||||
- Surya OCR 결과만 DEVONthink 본문에 반드시 병합 (검색성 확보)
|
||||
|
||||
---
|
||||
|
||||
@@ -211,7 +211,7 @@ OmniFocus 역링크 DEVONthink 메타데이터 양방향 참조에 필요
|
||||
DEVONagent ────┤ ┌──────────────┐
|
||||
스캔 문서 ──────┼──► Inbox ──►│ Smart Rule │──► 자동 태깅
|
||||
이메일 ────────┤ │ + Ollama API │ + 적절한 DB로 이동
|
||||
파일 드롭 ──────┘ │ + GPU 서버 │ + 벡터 인덱싱 (ChromaDB)
|
||||
파일 드롭 ──────┘ │ + GPU 서버 │ + 벡터 인덱싱 (Qdrant)
|
||||
└──────────────┘ + OCR 텍스트 병합 (스캔 시)
|
||||
▼
|
||||
OmniFocus 작업 생성
|
||||
@@ -225,9 +225,9 @@ DEVONagent ────┤ ┌─────────────
|
||||
트리거: Inbox DB에 새 문서 추가
|
||||
조건: 태그가 비어있음
|
||||
동작:
|
||||
1. 이미지/스캔 문서 → GPU 서버 VL-7B로 OCR → 본문에 병합
|
||||
1. 이미지/스캔 문서 → GPU 서버 Surya OCR(:8400)로 OCR → 본문에 병합
|
||||
2. Mac mini 35B → 태그 + 분류 대상 DB 생성 → DEVONthink 태그에만 저장
|
||||
3. GPU 서버 nomic-embed → 벡터화 → ChromaDB에만 저장
|
||||
3. GPU 서버 bge-m3 → 벡터화 → Qdrant에만 저장
|
||||
4. 태그 기반 도메인 DB 자동 이동:
|
||||
#주제/프로그래밍, #주제/AI-ML → 05_Programming
|
||||
#주제/공학, #주제/네트워크 → 03_Engineering
|
||||
@@ -249,7 +249,7 @@ DEVONagent ────┤ ┌─────────────
|
||||
동작:
|
||||
1. 발신자 기준 그룹 자동 생성/분류
|
||||
2. 첨부파일 추출 → 태그 기반 도메인 DB로 복제 (기술문서→03, 도면→97 등)
|
||||
3. GPU 서버에서 벡터 임베딩 → ChromaDB 인덱싱
|
||||
3. GPU 서버에서 벡터 임베딩 → Qdrant 인덱싱
|
||||
※ 이메일 요약은 저장하지 않음 (RAG로 검색 시 생성)
|
||||
```
|
||||
|
||||
@@ -336,8 +336,8 @@ on performSmartRule(theRecords)
|
||||
end if
|
||||
end try
|
||||
|
||||
-- Step 4: GPU 서버 → 벡터 임베딩 → ChromaDB 인덱싱 (비동기)
|
||||
do shell script "python3 ~/scripts/embed_to_chroma.py " & ¬
|
||||
-- Step 4: GPU 서버 → 벡터 임베딩 → Qdrant 인덱싱 (비동기)
|
||||
do shell script "python3 ~/scripts/embed_to_qdrant.py " & ¬
|
||||
quoted form of docUUID & " &"
|
||||
|
||||
-- Step 5: 처리 완료 표시
|
||||
@@ -567,59 +567,59 @@ if __name__ == "__main__":
|
||||
│ RTX 4070 Ti Super 16GB VRAM │
|
||||
│ │
|
||||
│ ┌──────────────────────┐ ┌──────────────────────────────────┐ │
|
||||
│ │ 👁️ 비전 모델 │ │ 🔍 리랭커 (Reranker) │ │
|
||||
│ │ Qwen2.5-VL-7B (8Q) │ │ bge-reranker-v2-m3 │ │
|
||||
│ │ VRAM: ~8GB │ │ VRAM: ~1GB │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ 용도: │ │ 용도: │ │
|
||||
│ │ · 스캔 문서 분석 │ │ · RAG 검색 품질 극대화 │ │
|
||||
│ │ · 이미지 캡션/태깅 │ │ · 임베딩 검색 후 정밀 재정렬 │ │
|
||||
│ │ · 차트/그래프 해석 │ │ · Top-K → Top-N 정확도 향상 │ │
|
||||
│ │ · 사진 자동 분류 │ │ │ │
|
||||
│ │ · OCR 보완 │ │ │ │
|
||||
│ └──────────────────────┘ └──────────────────────────────────┘ │
|
||||
│ │ 📄 Surya OCR │ │ 🔍 리랭커 (Reranker) │ │
|
||||
│ │ FastAPI :8400 │ │ bge-reranker-v2-m3 │ │
|
||||
│ │ VRAM: ~2-3GB │ │ VRAM: ~1GB │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ 용도: │ │ 용도: │ │
|
||||
│ │ · 스캔 문서 OCR │ │ · RAG 검색 품질 극대화 │ │
|
||||
│ │ · 이미지 텍스트 추출 │ │ · 임베딩 검색 후 정밀 재정렬 │ │
|
||||
│ │ · 만화 말풍선 OCR │ │ · Top-K → Top-N 정확도 향상 │ │
|
||||
│ │ · 한/영/일 다국어 │ │ │ │
|
||||
│ └───────────────────────┘ └──────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────┐ ┌──────────────────────────────────┐ │
|
||||
│ │ 🔗 임베딩 모델 │ │ 📊 VRAM 배분 │ │
|
||||
│ │ nomic-embed-text │ │ │ │
|
||||
│ │ VRAM: ~0.3GB │ │ 비전 모델 (8Q): ~8GB │ │
|
||||
│ │ bge-m3 (1024차원) │ │ │ │
|
||||
│ │ VRAM: ~1.5GB │ │ Surya OCR: ~2-3GB │ │
|
||||
│ │ │ │ 리랭커: ~1GB │ │
|
||||
│ │ 용도: │ │ 임베딩: ~0.3GB │ │
|
||||
│ │ · 문서 벡터 임베딩 │ │ 시스템: ~2GB │ │
|
||||
│ │ 용도: │ │ 임베딩: ~1.5GB │ │
|
||||
│ │ · 문서 벡터 임베딩 │ │ Plex HW 트랜스: ~1-2GB │ │
|
||||
│ │ · RAG 인덱싱 │ │ ───────────────────── │ │
|
||||
│ │ · 쿼리 임베딩 │ │ 합계: ~11.3GB / 16GB │ │
|
||||
│ │ │ │ 여유: ~4.7GB ✅ │ │
|
||||
│ │ · 쿼리 임베딩 │ │ 합계: ~7-8GB / 16GB │ │
|
||||
│ │ │ │ 여유: ~8-9GB ✅ │ │
|
||||
│ │ ※ GPU 가속으로 │ │ │ │
|
||||
│ │ 대량 임베딩 시 유리 │ │ │ │
|
||||
│ └──────────────────────┘ └──────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 🎬 미디어 서비스 │ │
|
||||
│ │ Plex Media Server — GPU 하드웨어 트랜스코딩 활용 │ │
|
||||
│ │ 🎬 미디어 + 만화 서비스 │ │
|
||||
│ │ Plex Media Server — GPU 하드웨어 트랜스코딩 │ │
|
||||
│ │ Komga — 만화 서버 (Docker, NFS → NAS /Comic) │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 임베딩을 GPU 서버로 이전하는 이유
|
||||
|
||||
임베딩 모델(nomic-embed-text)을 Mac mini에서 GPU 서버로 이전하는 것을 **권장**합니다:
|
||||
임베딩 모델(bge-m3)을 Mac mini에서 GPU 서버로 이전하는 것을 **권장**합니다:
|
||||
|
||||
| 비교 항목 | Mac mini에서 실행 | GPU 서버에서 실행 |
|
||||
|---|---|---|
|
||||
| **대량 인덱싱 속도** | CPU 기반, 느림 | CUDA 가속, 5-10배 빠름 |
|
||||
| **Mac mini 부하** | 35B 모델 + 임베딩 동시 시 경합 | 35B 모델 전용, 쾌적 |
|
||||
| **VRAM 영향** | 해당 없음 | +0.3GB (무시할 수준) |
|
||||
| **VRAM 영향** | 해당 없음 | +1.5GB (bge-m3, 1024차원) |
|
||||
| **네트워크 레이턴시** | 없음 | 2.5G 네트워크, 1ms 미만 |
|
||||
| **배치 처리** | 문서 100개 인덱싱 시 수분 | 문서 100개 인덱싱 시 수십초 |
|
||||
| **ChromaDB 위치** | Mac mini 유지 | Mac mini 유지 (변동 없음) |
|
||||
| **Qdrant 위치** | Mac mini 유지 | Mac mini 유지 (변동 없음) |
|
||||
|
||||
**결론:** 임베딩 모델은 단일 요청 레이턴시보다 **배치 처리량**이 중요합니다.
|
||||
GPU 서버의 CUDA 가속을 활용하면 대량 문서 인덱싱이 훨씬 빨라지고,
|
||||
Mac mini의 통합메모리를 35B 모델에 온전히 할당할 수 있습니다.
|
||||
nomic-embed-text는 0.3GB에 불과해 GPU 서버 VRAM에 거의 영향이 없고,
|
||||
bge-m3는 ~1.5GB로 GPU 서버 VRAM 16GB 대비 여유 충분하고,
|
||||
2.5G 네트워크 환경이라 API 호출 레이턴시도 무시할 수준입니다.
|
||||
|
||||
다만 **ChromaDB는 Mac mini에 유지**합니다. RAG 질의 시 벡터 검색 →
|
||||
다만 **Qdrant는 Mac mini에 유지**합니다. RAG 질의 시 벡터 검색 →
|
||||
리랭킹 → 35B 응답 생성이 연속으로 일어나는데, 벡터 DB가 로컬에 있어야
|
||||
이 파이프라인이 가장 빠릅니다.
|
||||
|
||||
@@ -638,15 +638,15 @@ nomic-embed-text는 0.3GB에 불과해 GPU 서버 VRAM에 거의 영향이 없
|
||||
│ Mac mini │ │ Claude │ │ GPU 서버 │
|
||||
│ (메인) │ │ (클라우드) │ │ (보조) │
|
||||
├─────────────────┤ ├──────────────┤ ├────────────────────┤
|
||||
│ Qwen3.5-35B-A3B │ │ Sonnet 4.6 │ │ Qwen2.5-VL-7B (8Q)│
|
||||
│ Qwen3.5-35B-A3B │ │ Sonnet 4.6 │ │ Surya OCR (:8400) │
|
||||
│ 4Q / ~80 tok/s │ │ │ │ bge-reranker-v2-m3 │
|
||||
│ │ │ │ │ nomic-embed-text │
|
||||
│ │ │ │ │ bge-m3 (1024차원) │
|
||||
├─────────────────┤ ├──────────────┤ ├────────────────────┤
|
||||
│ · 자동 태깅/분류 │ │ · 심층 분석 │ │ · 이미지/스캔 분석 │
|
||||
│ · 자동 태깅/분류 │ │ · 심층 분석 │ │ · 스캔/이미지 OCR │
|
||||
│ · 문서 요약 │ │ · 리서치 합성 │ │ · RAG 리랭킹 │
|
||||
│ · 메타데이터 │ │ · 보고서 생성 │ │ · 문서 임베딩/인덱싱│
|
||||
│ · 액션아이템추출 │ │ · 복잡한 추론 │ │ · 사진 자동 분류 │
|
||||
│ · RAG 응답생성 │ │ · 다국어 번역 │ │ · OCR 후처리 │
|
||||
│ · 액션아이템추출 │ │ · 복잡한 추론 │ │ · 만화 텍스트 추출 │
|
||||
│ · RAG 응답생성 │ │ · 다국어 번역 │ │ · 한/영/일 다국어 │
|
||||
├─────────────────┤ ├──────────────┤ ├────────────────────┤
|
||||
│ 속도: ~80 tok/s │ │ 속도: ~3초 │ │ 속도: GPU 가속 │
|
||||
│ 비용: 무료 │ │ 비용: 과금 │ │ 비용: 무료 │
|
||||
@@ -659,13 +659,13 @@ nomic-embed-text는 0.3GB에 불과해 GPU 서버 VRAM에 거의 영향이 없
|
||||
| 조건 | 라우팅 | 이유 |
|
||||
|---|---|---|
|
||||
| 텍스트 문서 + 태깅/분류/요약 | Tier 1 (Mac mini 35B) | 메인 범용, 품질 충분 |
|
||||
| 이미지 포함 문서 / 스캔 PDF | Tier 3 → Tier 1 | 비전 모델로 텍스트 추출 후 35B로 분석 |
|
||||
| 이미지 포함 문서 / 스캔 PDF | Tier 3 → Tier 1 | Surya OCR로 텍스트 추출 후 35B로 분석 |
|
||||
| 심층 분석 / 긴 보고서 생성 | Tier 2 (Claude API) | 최고 품질 필요 시 |
|
||||
| RAG 검색 결과 리랭킹 | Tier 3 (GPU reranker) | 검색 정확도 극대화 |
|
||||
| RAG 최종 응답 생성 | Tier 1 (Mac mini 35B) | 컨텍스트 기반 응답 |
|
||||
| 새 문서 벡터 인덱싱 | Tier 3 (GPU embed) | CUDA 가속 배치 처리 |
|
||||
| 대량 배치 (100+ 문서) | Tier 1 + Tier 3 병렬 | 양쪽 분산 처리 |
|
||||
| Synology Photos 자동 태깅 | Tier 3 (GPU vision) | 이미지 분석 특화 |
|
||||
| 만화 OCR (Komga 연동) | Tier 3 (GPU Surya OCR) | GPU 서버 로컬 처리 |
|
||||
|
||||
### 모델 간 협업 파이프라인
|
||||
|
||||
@@ -674,26 +674,26 @@ nomic-embed-text는 0.3GB에 불과해 GPU 서버 VRAM에 거의 영향이 없
|
||||
|
||||
1. [Smart Rule 트리거] 새 PDF 감지, 이미지 기반 문서로 판단
|
||||
│
|
||||
2. [GPU 서버 · Qwen2.5-VL-7B 8Q]
|
||||
이미지 분석 → 텍스트 추출 (OCR) → DEVONthink 본문에 병합
|
||||
2. [GPU 서버 · Surya OCR :8400]
|
||||
이미지/스캔 PDF → OCR 텍스트 추출 → DEVONthink 본문에 병합
|
||||
│
|
||||
3. [Mac mini · Qwen3.5-35B-A3B]
|
||||
추출된 텍스트로 태그 생성 → DEVONthink 태그에만 저장
|
||||
│
|
||||
4. [GPU 서버 · nomic-embed-text]
|
||||
문서 벡터 임베딩 → ChromaDB에만 저장
|
||||
4. [GPU 서버 · bge-m3]
|
||||
문서 벡터 임베딩 → Qdrant에만 저장
|
||||
│
|
||||
5. [결과] DEVONthink에는 본문(OCR)+태그+처리일시만
|
||||
ChromaDB에는 벡터만. 요약은 저장하지 않음 (RAG로 실시간 생성)
|
||||
Qdrant에는 벡터만. 요약은 저장하지 않음 (RAG로 실시간 생성)
|
||||
|
||||
|
||||
예시: RAG 질의 시
|
||||
|
||||
1. [사용자 질문] "서버 마이그레이션 관련 자료 정리해줘"
|
||||
│
|
||||
2. [GPU 서버 · nomic-embed-text] 쿼리 임베딩
|
||||
2. [GPU 서버 · bge-m3] 쿼리 임베딩
|
||||
│
|
||||
3. [Mac mini · ChromaDB] 벡터 유사도 검색 → Top-20 후보
|
||||
3. [Mac mini · Qdrant] 벡터 유사도 검색 → Top-20 후보
|
||||
│
|
||||
4. [GPU 서버 · bge-reranker-v2-m3]
|
||||
Top-20 → 정밀 리랭킹 → Top-5 선정
|
||||
@@ -714,9 +714,9 @@ OLLAMA_MAX_LOADED_MODELS=3 # 동시 로드 모델 3개 (비전+리랭커+
|
||||
OLLAMA_KEEP_ALIVE=10m # 미사용 시 10분 후 언로드
|
||||
|
||||
# 모델 다운로드
|
||||
ollama pull qwen2.5-vl:7b-instruct-q8_0 # 비전 모델 8Q (~8GB)
|
||||
# Surya OCR은 별도 systemd 서비스로 운영 (:8400)
|
||||
ollama pull bge-reranker-v2-m3 # 리랭커 (~1GB)
|
||||
ollama pull nomic-embed-text # 임베딩 (~0.3GB)
|
||||
ollama pull bge-m3 # 임베딩 (~1.5GB, 1024차원)
|
||||
|
||||
# Mac mini에서 GPU 서버 호출 예시
|
||||
# 비전 분석
|
||||
@@ -725,11 +725,11 @@ curl http://gpu-server:11434/api/generate \
|
||||
|
||||
# 임베딩 (배치)
|
||||
curl http://gpu-server:11434/api/embed \
|
||||
-d '{"model":"nomic-embed-text", "input":["문서1 텍스트", "문서2 텍스트", ...]}'
|
||||
-d '{"model":"bge-m3", "input":["문서1 텍스트", "문서2 텍스트", ...]}'
|
||||
```
|
||||
|
||||
**`keep_alive` 활용 전략:**
|
||||
- 비전 모델 (8Q): `keep_alive: "30m"` — 자주 사용, 항상 대기
|
||||
- Surya OCR: systemd 서비스로 상시 구동 (포트 8400)
|
||||
- 리랭커: `keep_alive: "10m"` — RAG 쿼리 시 활성
|
||||
- 임베딩: `keep_alive: "30m"` — 새 문서 인덱싱 빈도에 맞춰
|
||||
|
||||
@@ -750,20 +750,20 @@ curl http://gpu-server:11434/api/embed \
|
||||
│ [청킹] → 의미 단위로 텍스트 분할 (500토큰) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ [임베딩] → GPU 서버 Ollama (nomic-embed-text, CUDA) │
|
||||
│ [임베딩] → GPU 서버 Ollama (bge-m3, CUDA) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ [벡터 저장] → ChromaDB (Mac mini 로컬) │
|
||||
│ [벡터 저장] → Qdrant (Mac mini 로컬) │
|
||||
│ │ │
|
||||
│ ─ ─ ─ ─ ─ ─ 쿼리 시 ─ ─ ─ ─ ─ ─ │
|
||||
│ │ │
|
||||
│ [질문 입력] │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ [쿼리 임베딩] → GPU 서버 (nomic-embed-text) │
|
||||
│ [쿼리 임베딩] → GPU 서버 (bge-m3) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ [유사도 검색] → ChromaDB (Mac mini, Top-20) │
|
||||
│ [유사도 검색] → Qdrant (Mac mini, Top-20) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ [리랭킹] → GPU 서버 (bge-reranker, Top-5 선정) │
|
||||
@@ -841,7 +841,7 @@ Smart Rule 2차: 하위 그룹 라우팅
|
||||
→ 80_Reference/Standards/
|
||||
│
|
||||
▼
|
||||
ChromaDB 벡터 인덱싱 (비동기)
|
||||
Qdrant 벡터 인덱싱 (비동기)
|
||||
→ RAG 검색에 즉시 반영
|
||||
│
|
||||
▼
|
||||
@@ -1023,7 +1023,7 @@ Mac mini에서는 **자동 스케줄 리서치**, 맥북에서는 **현장 수
|
||||
│ 배치 + 자동화 중심 │ 인터랙티브 + 즉시성 중심 │
|
||||
├────────────────────────┴────────────────────────────────┤
|
||||
│ 공통: 결과는 모두 DEVONthink Inbox → CloudKit 동기화 │
|
||||
│ → Mac mini Smart Rule이 자동 태깅 + ChromaDB 인덱싱 │
|
||||
│ → Mac mini Smart Rule이 자동 태깅 + Qdrant 인덱싱 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -1095,7 +1095,7 @@ DEVONthink에서 자료 검색/열람 (동기화된 DB)
|
||||
|
||||
[RAG 질의 시]
|
||||
Tailscale 연결 → RAG API에 자연어 질문
|
||||
→ Mac mini에서 GPU 임베딩 → ChromaDB 검색 → 리랭킹 → 35B 응답
|
||||
→ Mac mini에서 GPU 임베딩 → Qdrant 검색 → 리랭킹 → 35B 응답
|
||||
→ 결과에 x-devonthink-item:// 링크 포함
|
||||
→ 맥북 DEVONthink에서 해당 문서 바로 열기
|
||||
|
||||
@@ -1183,7 +1183,7 @@ RAG 시스템으로 내 지식베이스에 질문
|
||||
│ 완료 5건 | 신규 3건 | 기한초과 1건 │
|
||||
│ │
|
||||
│ ■ 시스템 상태 │
|
||||
│ ChromaDB 벡터: 12,847개 (+15) │
|
||||
│ Qdrant 벡터: 12,847개 (+15) │
|
||||
│ Inbox 잔여: 2건 │
|
||||
│ NAS 동기화: 정상 │
|
||||
└─────────────────────────────────────────────┘
|
||||
@@ -1194,7 +1194,7 @@ RAG 시스템으로 내 지식베이스에 질문
|
||||
· Inbox 미처리 3건 이상 → "Inbox 정리 필요 (N건 미분류)"
|
||||
· 시정조치 overdue → "시정조치 기한초과: [내용]" (긴급 플래그)
|
||||
· 분류 실패 문서 존재 → "수동 분류 필요 (N건)"
|
||||
· ChromaDB 인덱싱 실패 → "벡터 인덱싱 오류 점검"
|
||||
· Qdrant 인덱싱 실패 → "벡터 인덱싱 오류 점검"
|
||||
|
||||
출력 3 — Synology Chat 알림 (선택, 한 줄 요약):
|
||||
"📋 오늘 다이제스트: 신규 12건, 법령변경 2건, overdue 1건 ⚠"
|
||||
@@ -1222,7 +1222,7 @@ RAG 시스템으로 내 지식베이스에 질문
|
||||
end tell
|
||||
|
||||
5. 시스템 상태 — Python
|
||||
ChromaDB collection.count(), NAS ping, sync 로그 확인
|
||||
Qdrant collection.count(), NAS ping, sync 로그 확인
|
||||
|
||||
6. 상위 뉴스 요약 — Ollama 35B
|
||||
오늘 수집된 뉴스 중 상위 3건을 2-3문장으로 요약
|
||||
@@ -1259,8 +1259,8 @@ OmniFocus 리뷰 → 완료 작업의 DEVONthink 메타데이터 업데이트
|
||||
□ DEVONsphere Express 설치
|
||||
□ OmniFocus, OmniOutliner, OmniGraffle, OmniPlan 설치
|
||||
□ Ollama 확인 (이미 설치됨)
|
||||
□ GPU 서버에 nomic-embed-text, Qwen2.5-VL-7B 8Q, bge-reranker 다운로드
|
||||
□ ChromaDB 설치 (pip install chromadb) — Mac mini
|
||||
□ GPU 서버에 bge-m3, bge-reranker 다운로드 + Surya OCR 서비스 설치
|
||||
□ Qdrant (Docker, Mac mini) — pkm_documents 컬렉션 (1024차원, Cosine)
|
||||
□ Python 환경 설정 (venv 권장)
|
||||
□ Plex Media Server를 GPU 서버로 이전
|
||||
```
|
||||
@@ -1288,7 +1288,7 @@ OmniFocus 리뷰 → 완료 작업의 DEVONthink 메타데이터 업데이트
|
||||
```
|
||||
□ Ollama 태깅/분류 프롬프트 최적화
|
||||
□ Claude API 키 Keychain 등록
|
||||
□ RAG 파이프라인 구축 (GPU 서버 임베딩 + Mac mini ChromaDB)
|
||||
□ RAG 파이프라인 구축 (GPU bge-m3 임베딩 + Mac mini Qdrant + MLX 35B 응답)
|
||||
□ DEVONthink Smart Rule과 AI 연동 테스트
|
||||
□ DEVONagent 자동 검색 스케줄 설정
|
||||
```
|
||||
@@ -1325,7 +1325,7 @@ OmniPlan 0.5GB 낮음
|
||||
OmniOutliner 0.3GB 낮음
|
||||
OmniGraffle 0.5GB 낮음
|
||||
MLX (Qwen3.5-35B-A3B 4bit) ~20GB 중간 MoE: 3B만 활성
|
||||
ChromaDB 1-2GB 낮음
|
||||
Qdrant (Docker) 1-2GB 낮음
|
||||
Roon Core 2-4GB 낮음
|
||||
Komga 0.5GB 낮음
|
||||
기타 시스템 4-6GB -
|
||||
@@ -1347,9 +1347,9 @@ Plex를 GPU 서버로 이전하고 임베딩도 GPU로 넘김으로써, Mac mini
|
||||
```
|
||||
서비스 VRAM 상태 비고
|
||||
─────────────────────────────────────────────────────────────
|
||||
Qwen2.5-VL-7B (8Q) ~8GB 상주 비전/이미지 분석
|
||||
Surya OCR (systemd) ~2-3GB 상주 문서/만화 OCR
|
||||
bge-reranker-v2-m3 ~1GB 상주 RAG 리랭킹
|
||||
nomic-embed-text ~0.3GB 상주 임베딩 (CUDA 가속)
|
||||
bge-m3 (1024차원) ~1.5GB 상주 임베딩 (CUDA 가속)
|
||||
Plex HW Transcoding ~1-2GB 간헐적 NVENC/NVDEC 활용
|
||||
시스템 오버헤드 ~2GB -
|
||||
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
벡터 임베딩 스크립트
|
||||
- DEVONthink 문서 UUID로 텍스트 추출
|
||||
- GPU 서버(nomic-embed-text)로 임베딩 생성
|
||||
- ChromaDB에 저장
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
from pkm_utils import setup_logger, load_credentials, run_applescript_inline
|
||||
|
||||
logger = setup_logger("embed")
|
||||
|
||||
# ChromaDB 저장 경로
|
||||
CHROMA_DIR = Path.home() / ".local" / "share" / "pkm" / "chromadb"
|
||||
CHROMA_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def get_document_text(uuid: str) -> tuple[str, str]:
|
||||
"""DEVONthink에서 UUID로 문서 텍스트 + 제목 추출"""
|
||||
script = f'''
|
||||
tell application id "DNtp"
|
||||
set theRecord to get record with uuid "{uuid}"
|
||||
set docText to plain text of theRecord
|
||||
set docTitle to name of theRecord
|
||||
return docTitle & "|||" & docText
|
||||
end tell
|
||||
'''
|
||||
result = run_applescript_inline(script)
|
||||
parts = result.split("|||", 1)
|
||||
title = parts[0] if len(parts) > 0 else ""
|
||||
text = parts[1] if len(parts) > 1 else ""
|
||||
return title, text
|
||||
|
||||
|
||||
def get_embedding(text: str, gpu_server_ip: str) -> list[float] | None:
|
||||
"""GPU 서버의 nomic-embed-text로 임베딩 생성"""
|
||||
url = f"http://{gpu_server_ip}:11434/api/embeddings"
|
||||
try:
|
||||
resp = requests.post(url, json={
|
||||
"model": "nomic-embed-text",
|
||||
"prompt": text[:8000] # 토큰 제한
|
||||
}, timeout=60)
|
||||
resp.raise_for_status()
|
||||
return resp.json().get("embedding")
|
||||
except Exception as e:
|
||||
logger.error(f"임베딩 생성 실패: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def store_in_chromadb(doc_id: str, title: str, text: str, embedding: list[float]):
|
||||
"""ChromaDB에 저장"""
|
||||
import chromadb
|
||||
client = chromadb.PersistentClient(path=str(CHROMA_DIR))
|
||||
collection = client.get_or_create_collection(
|
||||
name="pkm_documents",
|
||||
metadata={"hnsw:space": "cosine"}
|
||||
)
|
||||
collection.upsert(
|
||||
ids=[doc_id],
|
||||
embeddings=[embedding],
|
||||
documents=[text[:2000]],
|
||||
metadatas=[{"title": title, "source": "devonthink"}]
|
||||
)
|
||||
logger.info(f"ChromaDB 저장: {doc_id} ({title[:30]})")
|
||||
|
||||
|
||||
def run(uuid: str):
|
||||
"""단일 문서 임베딩 처리"""
|
||||
logger.info(f"임베딩 처리 시작: {uuid}")
|
||||
|
||||
creds = load_credentials()
|
||||
gpu_ip = creds.get("GPU_SERVER_IP")
|
||||
if not gpu_ip:
|
||||
logger.warning("GPU_SERVER_IP 미설정 — 임베딩 건너뜀")
|
||||
return
|
||||
|
||||
try:
|
||||
title, text = get_document_text(uuid)
|
||||
if not text or len(text) < 10:
|
||||
logger.warning(f"텍스트 부족 [{uuid}]: {len(text)}자")
|
||||
return
|
||||
|
||||
embedding = get_embedding(text, gpu_ip)
|
||||
if embedding:
|
||||
store_in_chromadb(uuid, title, text, embedding)
|
||||
logger.info(f"임베딩 완료: {uuid}")
|
||||
else:
|
||||
logger.error(f"임베딩 실패: {uuid}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"임베딩 처리 에러 [{uuid}]: {e}", exc_info=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("사용법: python3 embed_to_chroma.py <DEVONthink_UUID>")
|
||||
sys.exit(1)
|
||||
run(sys.argv[1])
|
||||
Reference in New Issue
Block a user