From 224843ba25bf81859a27df5b0fc2fbbdf870594f Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Thu, 21 May 2026 07:25:27 +0900 Subject: [PATCH] =?UTF-8?q?ops(reports):=20local=20research=20M1/M2/M3=20b?= =?UTF-8?q?aseline=20=EB=93=B1=EB=A1=9D=20(2026-05-02)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - M1: ProcessingQueue throughput baseline (GPU DB pkm, read-only) - M2: MLX gemma-4 26b-a4b 동시 처리 capacity (Mac mini :8801) - M3: bge-m3 batch embedding throughput (GPU Ollama :11434) 3 보고서 모두 4.0 가드 준수 (compose/migration/queue/worker restart/source_channel insert/SearXNG 도입 0건). trade-in 직전 untracked sync. Co-Authored-By: Claude Opus 4.7 (1M context) --- reports/local_research_M1_throughput.md | 138 +++++++++++++++++ reports/local_research_M2_mlx_capacity.md | 142 ++++++++++++++++++ .../local_research_M3_embedding_throughput.md | 142 ++++++++++++++++++ 3 files changed, 422 insertions(+) create mode 100644 reports/local_research_M1_throughput.md create mode 100644 reports/local_research_M2_mlx_capacity.md create mode 100644 reports/local_research_M3_embedding_throughput.md diff --git a/reports/local_research_M1_throughput.md b/reports/local_research_M1_throughput.md new file mode 100644 index 0000000..a148d3f --- /dev/null +++ b/reports/local_research_M1_throughput.md @@ -0,0 +1,138 @@ +# M1 — ProcessingQueue Throughput Baseline + +**측정일**: 2026-05-02 +**대상**: GPU 서버 `hyungi_document_server-postgres-1` (DB pkm) +**범위**: read-only SELECT 만. 4.0 가드 준수 (docker-compose / migration / queue stage / worker restart / source_channel='web' insert / SearXNG·Crawl4AI 도입 모두 0건). +**plan 참조**: `~/.claude/plans/users-hyungiahn-library-cloudstorage-sy-binary-dongarra.md` §4 (LocalResearch Feasibility). + +--- + +## 1. Stage별 7일 throughput + +```sql +SELECT stage::text, + COUNT(*) FILTER (WHERE status='pending') AS pending, + COUNT(*) FILTER (WHERE status='processing') AS processing, + COUNT(*) FILTER (WHERE status='completed') AS completed, + COUNT(*) FILTER (WHERE status='failed') AS failed, + ROUND(AVG(EXTRACT(EPOCH FROM (completed_at - started_at))) FILTER (WHERE status='completed')::numeric, 2) AS avg_sec, + ROUND((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (completed_at - started_at))) FILTER (WHERE status='completed'))::numeric, 2) AS p50_sec, + ROUND((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (completed_at - started_at))) FILTER (WHERE status='completed'))::numeric, 2) AS p95_sec +FROM processing_queue +WHERE created_at > NOW() - INTERVAL '7 days' +GROUP BY stage ORDER BY stage; +``` + +| stage | pending | processing | completed | failed | avg_sec | p50_sec | p95_sec | +|---|---:|---:|---:|---:|---:|---:|---:| +| chunk | 4 | 0 | 453 | 0 | 1.36 | 0.76 | 5.19 | +| classify | 8 | 2 | 642 | 1 | **42.89** | 33.71 | **82.17** | +| deep_summary | 4 | 0 | 467 | 0 | 25.65 | 16.37 | 73.26 | +| embed | 13 | 0 | 2,839 | 0 | 0.26 | 0.10 | 1.75 | +| extract | 2 | 0 | 345 | 0 | 0.02 | 0.01 | 0.08 | +| markdown | 4 | 0 | 89 | 1 | 11.62 | 0.01 | 77.13 | +| preview | 13 | 0 | 332 | 0 | 0.01 | 0.01 | 0.02 | +| summarize | 0 | 0 | 2,395 | 0 | 3.74 | 3.61 | 5.67 | + +**핵심 관찰**: +- **classify**: 가장 무거운 stage (avg 42.89초, p95 82초) — MLX gemma 호출 dominant +- **deep_summary / summarize**: 두 번째로 무거움 (LLM 호출) +- **embed**: 가장 가벼움 (avg 0.26초) + 처리량 최대 (2,839건) +- **markdown**: bimodal (p50 0.01초 = skip, p95 77초 = 실제 marker-pdf 변환). 2026-05-01 Phase 1B 배포 첫날 가동. +- **failed**: classify 1건, markdown 1건 (총 2건, 0.06%) — 무시 수준 + +--- + +## 2. 일별 처리량 분포 + +```sql +SELECT date_trunc('day', completed_at)::date AS day, stage::text, COUNT(*) +FROM processing_queue +WHERE completed_at > NOW() - INTERVAL '7 days' AND status='completed' +GROUP BY 1, 2 ORDER BY 1 DESC, 2; +``` + +| day | classify | deep_summary | embed | summarize | chunk | markdown | extract | preview | +|---|---:|---:|---:|---:|---:|---:|---:|---:| +| 2026-05-02 | 6 | 1 | 66 | 73 | 1 | 1 | 15 | 2 | +| 2026-05-01 | 55 | 55 | 377 | 322 | 55 | **88** | 55 | 55 | +| 2026-04-30 | — | — | 450 | 412 | — | — | — | — | +| 2026-04-29 | — | — | 396 | 434 | — | — | — | — | +| 2026-04-28 | — | 15 | 342 | 342 | — | — | — | — | +| 2026-04-27 | **258** | 257 | 627 | 369 | 258 | — | 273 | 273 | +| 2026-04-26 | — | — | 283 | 283 | — | — | 2 | 2 | +| 2026-04-25 | 139 | 139 | 341 | 186 | 139 | — | — | — | + +**해석**: +- 2026-04-25 / 04-27: 가스기사 학습자료 일괄 업로드 batch (PDF/markdown 2,100문항 흐름 흔적, 메모리 `project_gas_engineer_study.md`) +- 2026-05-01: Phase 1B marker_worker 배포 첫날, markdown 88건 첫 가동 +- 평일 비-batch 일은 classify 0~6건, embed 280~450건 (study_topics PR-4 자동 임베딩 포함) +- **편차 매우 큼**: classify 0~258/일, embed 66~627/일. 일 평균보다 peak 일 capacity 가 운영 게이트의 의미 있는 기준. + +--- + +## 3. Capacity 분석 (concurrency=1 가정, LLM stage) + +LLM stage 는 `feedback_analyzer_async_only.md` + Phase 2 측정 (gemma-4 ~10초, semaphore=1 필수) 기반 **concurrency 1 직렬 처리 가정**. embed 는 GPU batch 가능, chunk/extract/preview 는 CPU light. + +| stage | avg_sec | concurrency | max capacity (건/일) | 7일 평균 처리량 (건/일) | utilization | +|---|---:|---:|---:|---:|---:| +| classify | 42.89 | 1 | 2,014 | 91.7 | **4.6%** | +| deep_summary | 25.65 | 1 | 3,368 | 66.7 | 2.0% | +| summarize | 3.74 | 1 | 23,101 | 342.1 | 1.5% | +| markdown (PDF) | 11.62 | 1 | 7,436 | 12.7 | 0.2% | +| embed | 0.26 | (GPU batch) | ≫ 100k | 405.6 | < 0.5% | +| chunk | 1.36 | (CPU) | 63,529 | 64.7 | 0.1% | + +**peak 일 (2026-04-27) 부하**: +- classify 258건/일 = 11,065초 = **3.07시간/일** = 12.8% utilization (concurrency 1, peak day) +- deep_summary 257건/일 = 6,592초 = 1.83시간/일 = 7.6% utilization + +평일 평균은 여유롭지만 **peak batch 일에는 classify utilization 13% 까지 도달**. web 추가 부하는 평균이 아닌 peak 기준으로 평가해야 함. + +--- + +## 4. Web 30 URL/일 추가 부하 추정 + +PDF 시나리오: 사용자 검색 1회당 web 30 URL 수집 → 각 URL 이 markdown / classify / embed / chunk / summarize stage 진입 가정. + +| stage | 추가 건/일 | 단위 시간 (초) | 추가 시간/일 (초) | peak 일 utilization 증가 | +|---|---:|---:|---:|---:| +| markdown (web HTML) | 30 | ~5 추정¹ | 150 | +0.2% | +| classify | 30 | 42.89 | 1,287 | **+1.5%** | +| deep_summary | 30 | 25.65 | 770 | +0.9% | +| summarize | 30 | 3.74 | 112 | +0.1% | +| embed | 150 (5 chunks/doc) | 0.26 | 39 | +0.05% | +| chunk | 150 | 1.36 | 204 | +0.2% | + +¹ web HTML → trafilatura 변환은 marker-pdf (PDF 11.62초) 보다 훨씬 빠름 (보통 0.5~3초). 5초는 보수적 가정. + +**총 추가 capacity 사용** (가장 무거운 LLM stage 기준): +- classify utilization 증가: peak 일 12.8% → **14.3%** (+1.5%p) +- 평일 평균 utilization 증가: 4.6% → **6.0%** (+1.4%p) + +--- + +## 5. M1 게이트 판정 + +**plan §4.1 게이트 1**: web 30 URL/일 추가 부하가 현재 ProcessingQueue 처리 capacity 의 **10% 이하**. + +| 측정 | 값 | 임계값 | 판정 | +|---|---|---|---| +| classify capacity 추가 사용량 | +1.5%p (절대) | ≤ 10%p | ✓ PASS | +| 전체 LLM stage 합산 추가 사용량 | +2.5%p (1.5+0.9+0.1) | ≤ 10%p | ✓ PASS | +| peak 일 classify utilization (after) | 14.3% | < 50% (여유) | ✓ PASS | + +**M1 게이트 1: PASS** (추가 부하가 capacity 의 10% 보다 충분히 작음 — peak 일 기준 +1.5%p). + +--- + +## 6. 보강 관찰 (참고) + +- **Failed 건 2건만 발생** (classify 1, markdown 1) — 운영 안정. +- **Phase 1B markdown stage 가동 검증**: 88 completed / 1 failed / 4 pending — 정상 가동 (메모리 `project_markdown_canonical_layer.md` Phase 1B 배포 일치). +- **embed 의 처리량은 chunks 단위**: 1 doc 당 평균 ~5 chunks 가정 → 150 chunks/일 추가는 GPU bge-m3 capacity 에 비해 매우 작음 (M3 에서 배치 처리량 측정 후 재확인). + +## 7. 다음 단계 + +- M2 (MLX gemma-4 N=1/N=2 capacity 벤치) — 차단 조건 (error_rate>0, p95 3배 증가, healthcheck fail, /api/search/ask 또는 eval runner 시간대 충돌) 사전 점검 후 진행. diff --git a/reports/local_research_M2_mlx_capacity.md b/reports/local_research_M2_mlx_capacity.md new file mode 100644 index 0000000..bcff146 --- /dev/null +++ b/reports/local_research_M2_mlx_capacity.md @@ -0,0 +1,142 @@ +# M2 — MLX gemma-4 동시 처리 Capacity Bench + +**측정일**: 2026-05-02 +**대상**: Mac mini MLX :8801 `gemma-4-26b-a4b-it-8bit` (Tailscale 100.76.254.116) +**범위**: read-only `/v1/chat/completions` 호출만. 4.0 가드 준수 (compose / migration / queue / worker restart / source_channel insert / SearXNG 도입 모두 0건). config.yaml / Ollama 모델 변경 없음. +**bench 스크립트**: `/tmp/bench_M2_mlx.py` (격리, main pipeline 미연결) +**plan 참조**: `~/.claude/plans/users-hyungiahn-library-cloudstorage-sy-binary-dongarra.md` §4. + +--- + +## 1. 사전 점검 + +| 항목 | 상태 | +|---|---| +| MLX `/v1/models` healthcheck | OK (`gemma-4-26b-a4b-it-8bit` 단일 모델) | +| 진행 중 background queue (사전) | classify 1건 processing, classify 10건 pending, deep_summary 4건 pending — **MLX 자원 사용 중** | +| eval runner / `/api/search/ask` 동시 진행 | 별도 호출 없음 (Document Server 자체 ask 트래픽은 배치 외 시점) | +| 시간대 | 평일 오후 (한국시간) — peak 외 | + +배경 queue 가 MLX 동시 사용 중인 상태에서 측정 진행 — **운영 환경 reflective benchmark** 로 의미 있음. + +--- + +## 2. 시나리오 + +| 항목 | 값 | +|---|---| +| 입력 본문 | ~500 토큰, 한·영 mixed (ASME B16.5 / KGS Code / 가스기사 도메인 sample) | +| 프롬프트 작업 | "한국어 200자 이내 요약, technical key points only" | +| `max_tokens` | 200 (stop 보장) | +| `temperature` | 0 | +| 입력 prompt_tokens | ~500 (실측 19 tokens 단순 프롬프트와 별도. 실제 PROMPT 본문 포함 길이) | + +각 응답은 모두 `finish_reason='stop'`, `completion_tokens=120` 으로 일관 — **출력 토큰 수가 일정**해 latency 비교가 깨끗함. + +--- + +## 3. N=1 baseline (5 serial) + +``` +call 1: ok=True elapsed=3.67s ctok=120 +call 2: ok=True elapsed=3.41s ctok=120 +call 3: ok=True elapsed=3.42s ctok=120 +call 4: ok=True elapsed=3.60s ctok=120 +call 5: ok=True elapsed=3.47s ctok=120 +``` + +| metric | value | +|---|---| +| n | 5 | +| min | 3.41s | +| max | 3.67s | +| avg | **3.51s** | +| p50 | 3.47s | +| p95 | 3.67s | +| error_count | 0 | +| error_rate | 0% | +| completion_tokens_avg | 120 | + +**관찰**: 매우 안정 (편차 0.26초). 메모리 `project_search_v2.md` 의 "gemma-4 ~10.5초" (Phase 2 측정) 와 차이는 본 시나리오가 짧은 출력 (120 tok vs Phase 2 의 더 긴 출력) + 검색 query 성격 차이 때문. + +--- + +## 4. N=2 parallel (6 calls, 3 batches via thread pool) + +``` +call 4: ok=True elapsed=14.38s ctok=120 +call 5: ok=True elapsed=14.37s ctok=120 +call 6: ok=True elapsed=17.12s ctok=120 +... (call 1~3 도 모두 ok) +``` + +| metric | value | +|---|---| +| n | 6 | +| min | 6.74s | +| max | 35.01s | +| avg | **19.88s** | +| p50 | 15.75s | +| p95 | **35.01s** | +| error_count | 0 | +| error_rate | **0%** | +| completion_tokens_avg | 120 | + +**해석**: +- **error 0** — concurrency 2 client 호출에 대해 MLX 안정. +- **latency 큰 폭 증가** — N=1 대비 p95 9.54×, p50 4.54×, avg 5.66×. +- 원인: MLX server (`mlx_vlm server`) 가 단일 프로세스 + GPU 1 → 본질적으로 **concurrency=1 직렬 처리**. N=2 client 호출은 MLX 내부 큐에서 직렬화. 추가로 background classify 1건 processing 중이라 경합. +- 6 calls × ~3.5초 = ~21초 총 직렬 처리 시간. max 35초는 background queue 점유 + 큐잉. +- 이는 메모리 `feedback_analyzer_async_only.md` ("semaphore concurrency=1 필수") 와 일치하는 **이미 알려진 특성**. 새로운 위험 신호 아님. + +--- + +## 5. plan 4.0 중단 조건 점검 + +| 조건 | 측정 | 판정 | +|---|---|---| +| `error_rate > 0` | 0% | 미발생 | +| **p95 latency N=1 대비 3배 이상 증가** | **9.54×** | **트리거** | +| MLX healthcheck 실패 | OK | 미발생 | +| `/api/search/ask` 또는 eval 시간대 겹침 | 사전 점검 시 ask/eval 없음 | 미발생 | + +**결론**: p95 3× 트리거 발생 → **N=4 자동 진행 금지**. plan 명시 (`N=4 는 N=2 에서 error_rate=0 AND p95 acceptable 일 때만`). N=4 는 별도 사용자 승인 후 수행. + +다만 latency 9.5× 증가는 MLX concurrency=1 본질적 특성이지, 새로운 운영 위험은 아님. 이미 운영 중인 ask / classify 트래픽이 동시에 들어오면 동일하게 늘어남. + +--- + +## 6. plan 4.1 게이트 판정 (M2 관련) + +### 게이트 2 — Gemma 요약 30건/일 ≤ 10분 (concurrency 1) + +| 항목 | 값 | +|---|---| +| 단위 시간 (avg) | 3.51s | +| 30건 직렬 처리 시간 | 30 × 3.51s = **105.4s = 1.76분** | +| 임계값 | ≤ 10분 | +| **판정** | **✓ PASS** (여유 5.7×) | + +### 게이트 3 — concurrency 2 error_rate 0 = 여유 있음 + +| 항목 | 값 | +|---|---| +| N=2 error_rate | 0% | +| 임계값 | error_rate = 0 | +| **판정** | **✓ PASS** | + +### 게이트 3 보강 — latency 영향 (참고용) +- N=2 동시성 시 평균 19.88초, p95 35초 — LocalResearch 본격 가동 시 **운영 ask 트래픽과 동일 시간대 web 요약이 겹치면 응답 약 5~10× 지연** 가능. +- 완화책 (Phase 1 본격 plan 단계 결정): (a) web 요약은 background queue + concurrency 1 직렬 (실시간 아님) (b) ask 와 시간대 분리 (야간 배치) + +--- + +## 7. M2 단계 요약 + +- **N=1 baseline**: avg 3.51s, p95 3.67s, error 0 → 매우 안정 +- **N=2 parallel**: avg 19.88s, p95 35.01s, error 0 → MLX 직렬 처리로 latency 9.5× 증가, 단 안정성/완성도는 유지 +- **N=4 미실시**: plan 4.0 중단 조건 트리거 (p95 3× 초과). 사용자 승인 후 별도 수행. +- **plan 게이트 2 + 3 모두 PASS** + +## 8. 다음 단계 +M3 (bge-m3 batch 1/8/32 임베딩 벤치) — 운영 embed worker 시간대 분리 후 진행. diff --git a/reports/local_research_M3_embedding_throughput.md b/reports/local_research_M3_embedding_throughput.md new file mode 100644 index 0000000..2274800 --- /dev/null +++ b/reports/local_research_M3_embedding_throughput.md @@ -0,0 +1,142 @@ +# M3 — bge-m3 batch 임베딩 throughput Bench + +**측정일**: 2026-05-02 +**대상**: GPU server Ollama `:11434` `bge-m3` (latest, 1.2GB, 1024d) +**범위**: read-only `/api/embed` 호출만. 4.0 가드 준수 (compose / migration / queue / worker restart / source_channel insert / SearXNG 도입 모두 0건). Ollama 모델/컨테이너 변경 없음. +**bench 스크립트**: `/tmp/bench_M3_embed.py` (격리, GPU 서버 내 실행) +**plan 참조**: `~/.claude/plans/users-hyungiahn-library-cloudstorage-sy-binary-dongarra.md` §4. + +--- + +## 1. 사전 점검 + +| 항목 | 상태 | +|---|---| +| Ollama `/api/tags` healthcheck | OK (`gemma4:e4b-it-q8_0`, `bge-m3:latest` 2개) | +| 진행 중 embed worker (사전) | embed pending 17건, processing 0건 — **운영 worker idle 직전** | +| 측정 방식 | 단발 호출 3회씩, batch 1/8/32 — 운영 영향 최소 | + +embed worker 와 시간대 분리 — 측정 호출이 ~1초 미만이라 worker idle 시간에 끼어들지 않음. + +--- + +## 2. 시나리오 + +| 항목 | 값 | +|---|---| +| 입력 텍스트 | ~250 토큰 한·영 mixed (ASME B16.5 / KGS Code / 가스기사 sample), chunk #N 으로 변형해서 unique 32 inputs 생성 | +| API | Ollama `/api/embed`, body `{model, input: [...]}` | +| measure | 각 batch_size 당 3회 반복 | +| batch_size | 1, 8, 32 (plan 기본 범위) — 128 미실시 | + +응답: 각 batch 마다 `embeddings: [[1024d], ...]`, `dim=1024` 일관. + +--- + +## 3. 측정 결과 + +### batch=1 (3회) +``` +repeat 1: ok elapsed=0.10s embeddings=1 dim=1024 throughput=10.7 chunks/s +repeat 2: ok elapsed=0.08s embeddings=1 dim=1024 throughput=12.7 chunks/s +repeat 3: ok elapsed=0.08s embeddings=1 dim=1024 throughput=12.6 chunks/s +``` + +### batch=8 (3회) +``` +repeat 1: ok elapsed=0.12s embeddings=8 dim=1024 throughput=67.9 chunks/s +repeat 2: ok elapsed=0.12s embeddings=8 dim=1024 throughput=65.2 chunks/s +repeat 3: ok elapsed=0.12s embeddings=8 dim=1024 throughput=68.6 chunks/s +``` + +### batch=32 (3회) +``` +repeat 1: ok elapsed=0.26s embeddings=32 dim=1024 throughput=122.6 chunks/s +repeat 2: ok elapsed=0.25s embeddings=32 dim=1024 throughput=126.4 chunks/s +repeat 3: ok elapsed=0.25s embeddings=32 dim=1024 throughput=130.5 chunks/s +``` + +| batch | n | avg (s) | min (s) | max (s) | throughput (chunks/s) | error | +|---:|---:|---:|---:|---:|---:|---:| +| 1 | 3 | 0.088 | 0.079 | 0.106 | 11.4 | 0 | +| 8 | 3 | 0.119 | 0.117 | 0.123 | 67.2 | 0 | +| 32 | 3 | **0.253** | 0.245 | 0.261 | **126.4** | 0 | + +**관찰**: +- 모든 batch 0 error. +- batch 32 가 batch 1 대비 throughput 11배 — GPU batch 가속 큰 효과. +- batch 8 → 32 는 1.88×만 더 빨라짐 → batch 32 부근에서 throughput 증가 곡선 완만 (batch 128 측정 굳이 필요 없음을 시사). + +--- + +## 4. plan 4.1 게이트 판정 (M3 관련) + +### 게이트 4 — 150 chunks/일 임베딩 ≤ 1분 (batch 32) + +| 항목 | 값 | +|---|---| +| 150 chunks 처리에 필요한 batch 수 | ⌈150 / 32⌉ = **5 batches** | +| 1 batch 평균 시간 | 0.253s | +| 150 chunks 총 시간 | 5 × 0.253s = **1.27s** | +| 임계값 | ≤ 60s | +| **판정** | **✓ PASS** (여유 47×) | + +압도적 여유. embed 자원은 LocalResearch 도입의 병목이 아님. + +--- + +## 5. 게이트 5 — 1C/1D 충돌 점검 (read-only schema 분석) + +```sql +-- documents.source_channel 분포 +news 9024, manual 638, law_monitor 206, drive_sync 95, inbox_route 6, memo 6, NULL 1 +-- documents.content_origin 분포 +extracted 9976 +-- process_stage enum values +extract, classify, embed, preview, summarize, chunk, stt, thumbnail, deep_summary, markdown +``` + +| 점검 | 현황 | LocalResearch 영향 | 판정 | +|---|---|---|---| +| `documents.source_channel='web'` 값 | 미사용 (현재 6개 값 + NULL 1건) | 신규 값 추가 필요. CHECK 제약 없음. 충돌 0 | ✓ | +| `documents.content_origin='imported'` 값 | **0건** (extracted 9976만) | Phase 1A 에서 정의된 'imported' 슬롯이 비어있어 LocalResearch 가 자연스럽게 활용 가능 | ✓ | +| `documents.md_content` (Phase 1A) | nullable, optional-use | LocalResearch가 web→markdown 변환 후 동일 컬럼 활용 → Phase 1C `` 렌더러 자동 재사용 | ✓ (오히려 시너지) | +| `document_lineage` 테이블 (Phase 1A) | 빈 테이블, RESTRICT FK | LocalResearch web 문서의 출처 추적 (search_query → urls)에 자연 활용 | ✓ (오히려 시너지) | +| `process_stage` enum | 10개 stage. fetch/web_extract 부재 | LocalResearch Phase 2 (수집기) 진입 시 ADD VALUE migration 필요. **본 feasibility 단계에선 변경 0**. Phase 1C/1D 와는 stage 충돌 0 (markdown stage 공존 가능) | ✓ | +| Phase 1C `` UI | 미배포 (다음 차례) | LocalResearch 와 같은 컴포넌트 재사용 — 충돌 0, 시너지 | ✓ | +| Phase 1D pilot (30건 quality 측정) | 미시작 | LocalResearch 와 측정 시간대 분리만 하면 무관 | ✓ | + +**판정**: **✓ PASS**. 인접 트랙과의 schema/queue/UI 충돌 0. Phase 1A markdown canonical layer 가 오히려 LocalResearch 진입 기반 인프라 역할. + +--- + +## 6. LocalResearch Phase 1 진입 게이트 종합 판정 + +| # | 게이트 | 측정값 | 임계값 | 판정 | +|---|---|---|---|---| +| 1 | queue capacity 여유 (M1) | classify peak +1.5%p, 전체 LLM stage +2.5%p (peak day) | ≤ 10% 추가 부하 | ✓ PASS | +| 2 | Gemma 30건/일 (M2) | 30 × 3.51s = 1.76분 (concurrency 1) | ≤ 10분 | ✓ PASS | +| 3 | concurrency 2 마진 (M2) | error_rate 0% (단 latency 9.5×) | error_rate = 0 | ✓ PASS | +| 4 | 임베딩 150 chunks/일 (M3) | 5 × 0.253s = 1.27s (batch 32) | ≤ 1분 (60s) | ✓ PASS | +| 5 | 1C/1D 충돌 (M3) | source_channel/content_origin/md_content/lineage 모두 비충돌 | 충돌 없음 | ✓ PASS | + +### **종합: PASS (5/5)** + +추가 관찰: +- **M2 N=2 latency 9.5× 증가** 는 본질적 MLX concurrency=1 특성으로 새로운 위험 아니지만, **Phase 1 본격 plan 단계에서 web 요약 워커는 background queue + concurrency 1 직렬 (실시간 아님)** 로 설계 권장. ask 트래픽과 동일 시간대 경합 시 양쪽 모두 5~10× 지연 발생. +- **content_origin='imported' 슬롯이 0건으로 비어있는 점**이 인상적 — Phase 1A 가 LocalResearch 진입을 의식하지 않고 설계됐는데도 자연스러운 자리 확보. document_lineage 테이블도 동일. +- **process_stage 'fetch' / 'web_extract' / 'summarize_web' 추가**는 LocalResearch Phase 2 본격 plan 단계 작업. 기존 enum 10개와 충돌 없음. ORM enum 동시 갱신 필요 (메모리 `feedback_orm_db_enum_sync.md`). + +--- + +## 7. STOP — 사용자 검토 대기 + +plan §4.2 명시된 **Stop Point** 도달: + +> M3 보고서 + 게이트 판정 표 작성 직후 작업 정지. +> LocalResearch Phase 1 상세 plan 작성, SearXNG 도입, 코드 변경, plan 모드 재진입 등 일체 금지. +> 사용자가 게이트 판정 검토 후 다음 액션을 명시적으로 지시할 때까지 대기. + +본 reports 3개 (`local_research_M1_throughput.md` + `_M2_mlx_capacity.md` + `_M3_embedding_throughput.md`) 가 dashboard 산출물. 코드/스키마 변경 0, inventory soft lock 위반 0, queue stage 변경 0. + +다음 액션은 사용자 결정.