docs: add Sprint 0 Vision Analysis to pipeline improvement plan

Vision analysis addresses the critical gap that text-only scraping
misses ~40% of clinic website information (founding year in banners,
doctor photos, certification marks, social icons in images).

Sprint 0 adds: Firecrawl screenshot → Gemini Vision → structured
data extraction for founding year, doctors, certifications, services,
social icons, floating buttons, brand colors, slogans.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
claude/bold-hawking
Haewon Kam 2026-04-04 23:46:56 +09:00
parent ed37f23f78
commit 3f1a25e298
1 changed files with 133 additions and 44 deletions

View File

@ -1,58 +1,123 @@
# 검색/분석 파이프라인 종합 개선 계획
**작성일**: 2026-04-04
**상태**: 계획 완료, 구현 대기
**최종 수정**: 2026-04-04 (Vision Analysis 추가)
**상태**: Sprint 0 + Sprint 1 구현 중
## Context
현재 파이프라인의 3-phase 아키텍처(discover → collect → generate)는 구조적으로는 괜찮으나, **실행 신뢰성**에 심각한 문제가 있음. 3개 병렬 코드 탐색 결과:
현재 파이프라인의 3-phase 아키텍처(discover → collect → generate)는 구조적으로는 괜찮으나, **실행 신뢰성**과 **정보 수집 범위**에 심각한 문제가 있음.
### 핵심 문제
- **Vision 분석 부재**: 텍스트만 수집 → 이미지 속 정보(개원 연도, 의료진 사진, 인증 마크, 시술 전후) 100% 누락. 전체 정보의 약 40%를 놓침
- **Silent failure 16건**: 모든 catch 블록이 에러를 삼킴
- **데이터 품질 8건**: 잘못된 URL, 동명이인 병원, 평점 스케일 혼동
- **에러 복구 0건**: 재시도 로직 없음, 새로고침 시 데이터 유실
- **API 불안정 6건**: 타임아웃, 쿼터 소진, 인증 실패 미감지
**목표**: 검색 정확도 + 에러 회복력 + UX 투명성 대폭 개선
**목표**: Vision 분석 추가 + 검색 정확도 + 에러 회복력 + UX 투명성 대폭 개선
---
## 진행 상태 체크리스트
### Sprint 1: 데이터 품질 Quick Wins (~2.5h)
### Sprint 0: Vision Analysis 추가 ⭐ 최우선 (~2h)
- [ ] **WP-1**. YouTube Channel ID 정규식 수정 (20min)
- 파일: `discover-channels/index.ts`, `verifyHandles.ts`
- 변경: `/^UC[a-zA-Z0-9_-]{20,}$/``/^UC[a-zA-Z0-9_-]{22}$/`
- 검증: 24자가 아닌 channel ID가 reject되는지 확인
성형외과 홈페이지의 핵심 정보가 이미지로 제공됨 (배너, 의료진 사진, 인증 마크 등).
Firecrawl screenshot + Gemini Vision으로 이미지 속 정보를 추출.
- [ ] **WP-2**. Naver Place 동명이인 방지 (30min)
- 파일: `enrich-channels/index.ts` lines 319-343
- 변경: 카테고리 필터링 (성형/피부) + 검색어에 "성형외과" 추가
- 검증: "아이디병원" 검색 → "아이디마곡치과" 아닌 성형외과 반환
- [ ] **WP-V1**. Firecrawl screenshot 캡처 (30min)
- 파일: `discover-channels/index.ts` Stage A에 추가
- 변경: `formats: ["json", "links", "screenshot"]` — 메인 페이지 스크린샷 캡처
- 추가: 의료진 페이지, 시술 안내 페이지도 스크린샷 (siteMap에서 URL 추출)
- DB: `scrape_data.screenshots[]`에 base64 이미지 저장
- [ ] **WP-3**. Google Maps URL 수정 (20min)
- 파일: `collect-channel-data/index.ts`, `enrich-channels/index.ts`
- 변경: `place.website`(병원 사이트) 대신 `maps.google.com/search` URL
- 검증: 기타 채널의 구글 지도 링크가 Google Maps로 이동
- [ ] **WP-V2**. Gemini Vision 분석 (1h)
- 파일: 새 `_shared/visionAnalysis.ts`
- API: Google Gemini `gemini-2.0-flash-exp` (Vision 모델)
- 프롬프트: 스크린샷에서 다음 정보 추출:
- 개원 연도 / 운영 기간 ("SINCE 2004", "21년 무사고" 등)
- 의료진 이름 + 전문 분야 (프로필 사진 옆 텍스트)
- 인증/수상 마크 (JCI, 보건복지부, 의료관광 인증 등)
- 시술 카테고리 (메뉴 구조에서 추출)
- 소셜 미디어 아이콘 위치 + URL (이미지 기반 버튼 포함)
- 카카오톡/라인 플로팅 버튼 유무
- 브랜드 컬러 + 로고 디자인 요소
- 응답: JSON 구조로 반환
- [ ] **WP-4**. Naver Blog 공식 블로그 분리 (30min)
- 파일: `collect-channel-data/index.ts`
- 변경: verified_channels 핸들로 `blog.naver.com/{handle}` 생성, 검색 결과는 `blogMentions`
- 검증: 네이버 블로그 링크가 공식 블로그로 이동
- [ ] **WP-V3**. Vision 데이터 파이프라인 통합 (30min)
- 파일: `discover-channels/index.ts`, `collect-channel-data/index.ts`
- 변경:
- Vision에서 발견한 소셜 핸들 → 채널 발견에 추가 (Source 4)
- Vision에서 추출한 의료진 정보 → channel_data에 추가
- Vision에서 확인한 개원 연도 → clinic 정보에 반영
- 스크린샷 원본 → DB에 저장 (리포트에서 증거로 활용)
- 검증: 그랜드성형외과 분석 → "SINCE 2004" 개원 연도 추출 확인
- [ ] **WP-5**. 강남언니 평점 정규화 (30min)
- 파일: `collect-channel-data/index.ts`, `enrich-channels/index.ts`
- 변경: `rawRating` + `normalizedRating` 분리, ≤5.0이면 ×2
- 검증: 강남언니 평점이 `/10`으로 일관되게 표시
### Vision Analysis 프롬프트 설계
- [ ] **WP-6**. Perplexity 모델 상수화 (20min)
- 파일: 새 `_shared/config.ts`
- 변경: 환경변수 `PERPLEXITY_MODEL` 또는 기본값 `"sonar"`
- 검증: 모든 Edge Function에서 하드코딩 `"sonar"` 제거됨
```
System: You are a medical clinic website visual analyst. Extract structured
information from website screenshots. Respond ONLY with valid JSON.
- [ ] **WP-7**. Apify 타임아웃 증가 (10min)
- 파일: `discover-channels/index.ts`
- 변경: `waitForFinish=30``waitForFinish=45`
- 검증: Instagram 프로필 스크래핑 성공률 향상
User: Analyze this Korean plastic surgery clinic homepage screenshot.
Extract:
1. Founding year or operation duration (e.g., "SINCE 2004", "21년 무사고")
2. Doctor names and specialties shown in profile photos
3. Certification badges/marks (JCI, 보건복지부, medical tourism)
4. Main service categories from navigation menu
5. Social media icons/buttons visible (Instagram, YouTube, Blog, KakaoTalk, etc.)
6. Floating consultation buttons (KakaoTalk, LINE, WhatsApp)
7. Brand colors (primary, accent) from visual elements
8. Any promotional text or slogans in banners
{
"foundingYear": "2004",
"operationYears": 21,
"doctors": [{"name": "김OO", "specialty": "안면윤곽", "position": "대표원장"}],
"certifications": ["JCI", "보건복지부 인증"],
"serviceCategories": ["눈성형", "코성형", "가슴성형", "안면윤곽"],
"socialIcons": [{"platform": "instagram", "visible": true}, ...],
"floatingButtons": ["kakaotalk", "line"],
"brandColors": {"primary": "#C4A882", "accent": "#FF1493"},
"slogans": ["끊임없이 의료성형 뷰티 트렌드를 연구하는 그랜드 의료진"]
}
```
### Vision Analysis에 필요한 API/리소스
| API | 용도 | 비용 |
|-----|------|------|
| Firecrawl `formats: ["screenshot"]` | 페이지 스크린샷 캡처 | 기존 요금에 포함 |
| Gemini `gemini-2.0-flash-exp` | 이미지 분석 (Vision) | ~$0.002/이미지 |
| Gemini `GEMINI_API_KEY` | 이미 설정됨 (.env) | ✅ 사용 가능 |
### Vision으로 수집 가능한 추가 정보 (현재 누락)
| 정보 | 현재 수집 | Vision 추가 후 |
|------|---------|-------------|
| 개원 연도 | ❌ AI 추측 (자주 틀림) | ✅ 배너에서 직접 읽기 |
| 의료진 수/이름 | ⚠️ 강남언니에서만 | ✅ 홈페이지 프로필에서 추출 |
| 인증 마크 | ❌ 미수집 | ✅ JCI, 보건복지부 등 인식 |
| 시술 카테고리 | ⚠️ Firecrawl JSON | ✅ 네비게이션 메뉴에서 확인 |
| 카카오톡 상담 버튼 | ❌ JS 렌더링이라 못 잡음 | ✅ 플로팅 버튼 감지 |
| 브랜드 컬러 | ⚠️ CSS 추출 (부정확) | ✅ 실제 비주얼에서 추출 |
| 슬로건/태그라인 | ⚠️ 이미지 내 텍스트 누락 | ✅ 배너 텍스트 OCR |
---
### Sprint 1: 데이터 품질 Quick Wins (~2.5h) ✅ 완료
- [x] **WP-1**. YouTube Channel ID 정규식 수정 (20min)
- [x] **WP-2**. Naver Place 동명이인 방지 (30min)
- [x] **WP-3**. Google Maps URL 수정 (20min)
- [x] **WP-4**. Naver Blog 공식 블로그 분리 (30min)
- [x] **WP-5**. 강남언니 평점 정규화 (30min)
- [x] **WP-6**. Perplexity 모델 상수화 (20min)
- [x] **WP-7**. Apify 타임아웃 증가 (10min)
**추가 완료:**
- [x] 소셜 버튼 직접 추출 (Firecrawl actions + JS 렌더링 후 href 추출)
---
@ -126,13 +191,14 @@
| 파일 | Sprint | 설명 |
|------|--------|------|
| `supabase/functions/discover-channels/index.ts` | 1 | 채널 발견 |
| `supabase/functions/collect-channel-data/index.ts` | 1, 2, 3 | 데이터 수집 |
| `supabase/functions/enrich-channels/index.ts` | 1 | 레거시 enrichment |
| `supabase/functions/generate-report/index.ts` | - | 리포트 생성 (수정 없음) |
| `supabase/functions/discover-channels/index.ts` | 0, 1 | 채널 발견 + Vision |
| `supabase/functions/collect-channel-data/index.ts` | 0, 1, 2, 3 | 데이터 수집 |
| `supabase/functions/_shared/visionAnalysis.ts` (신규) | 0 | Vision 분석 유틸 |
| `supabase/functions/_shared/config.ts` | 1 | 공유 설정 |
| `supabase/functions/_shared/verifyHandles.ts` | 1, 2 | 핸들 검증 |
| `supabase/functions/_shared/config.ts` (신규) | 1 | 공유 설정 |
| `supabase/functions/_shared/retry.ts` (신규) | 3 | 재시도 유틸 |
| `supabase/functions/enrich-channels/index.ts` | 1 | 레거시 enrichment |
| `supabase/functions/generate-report/index.ts` | 0 | 리포트에 Vision 데이터 반영 |
| `supabase/migrations/20260404_channel_errors.sql` (신규) | 2 | DB 마이그레이션 |
| `src/pages/AnalysisLoadingPage.tsx` | 3 | 로딩 페이지 |
| `src/hooks/useEnrichment.ts` | 3 | Enrichment 훅 |
@ -141,10 +207,33 @@
## 총 예상 소요 시간
| Sprint | 시간 | 배포 범위 |
|--------|------|---------|
| Sprint 1: Quick Wins | ~2.5h | Edge Functions × 3 |
| Sprint 2: 에러 가시성 | ~2.25h | DB + Edge Functions |
| Sprint 3: 에러 회복 | ~5.25h | Edge Functions + Frontend + Vercel |
| Sprint 4: UX 마무리 | ~50min | Frontend + Vercel |
| **합계** | **~11h** | |
| Sprint | 시간 | 배포 범위 | 상태 |
|--------|------|---------|------|
| Sprint 0: Vision Analysis | ~2h | Edge Functions + Gemini | 🔜 다음 |
| Sprint 1: Quick Wins | ~2.5h | Edge Functions × 5 | ✅ 완료 |
| Sprint 2: 에러 가시성 | ~2.25h | DB + Edge Functions | 대기 |
| Sprint 3: 에러 회복 | ~5.25h | Edge Functions + Frontend + Vercel | 대기 |
| Sprint 4: UX 마무리 | ~50min | Frontend + Vercel | 대기 |
| **합계** | **~13h** | | |
---
## Vision Analysis 아키텍처
```
discover-channels (Stage A)
├─ A1. Firecrawl scrape (JSON + links) ← 기존
├─ A2. Firecrawl map ← 기존
├─ A3. Firecrawl branding ← 기존
├─ A4. Firecrawl social buttons (JS actions) ← 방금 추가
└─ A5. Firecrawl screenshot + Gemini Vision ← Sprint 0 신규
├─ 메인 페이지 스크린샷 캡처
├─ Gemini Vision 분석 (개원 연도, 의료진, 인증, 소셜 아이콘)
└─ 결과를 clinic 정보 + socialHandles에 병합
collect-channel-data
└─ Vision 데이터를 channel_data.visionAnalysis에 저장
generate-report
└─ Vision 데이터를 리포트 프롬프트에 포함 (실제 데이터 기반)
```