- integrations/color_extractor → integrations/site_fetcher (HTTP) + services/brand_parser (파싱) 분리
- integrations/vision → integrations/llm/gemini_vision 이동
- services/collect_extras → services/collect.collect_brand_basics (collect) + services/branding (report) 분리
- Vision prompt 에 logo_colors_hex 5개 강제 + 길이 fallback (4·6개 들어와도 5개로 정규화)
- branding 단계: HTML parser canonical logo URL 을 Vision 에 1순위 전달
→ firecrawl 가 잘못된 이미지 (마케팅 배너 등) 를 logo 로 잡는 케이스 회피
- select_run 에서 큰 JSON 컬럼 (report_data/plan_data) 빼서 meta only
→ generate_plan 만 select_run_report_data 별도 조회. 4군데 호출자는 가벼워짐
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- collect_brand_assets: Vision 결과의 logo_images 를 JSON 에서 제거하고
진짜 로고(logo/og 매칭) 인 경우만 raw_info.logo_url 컬럼에 저장.
favicon-only 매칭은 컬럼 저장 X (옛 logic 동일).
- analysis._build_overrides: select_branding_logo_url 로 컬럼 읽어
ClinicSnapshot.logo_images 를 horizontal=logo_url 로 재구성.
- branding raw_data 가 "사실 데이터(URL/hex)" vs "Vision 분석 텍스트(묘사)"
섞이던 문제 일부 해소 — URL 은 컬럼, 텍스트만 JSON 에 잔존.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- common/db.py 단일 파일 → common/db/ 패키지로 분리 (hospital/source/run/market/file_data)
- 모든 채널 데이터를 raw_info 단일 테이블로 통일 (hospital_baseinfo.raw_data / 채널별 *_data 테이블 제거)
- 부가 채널(tiktok/instagram_en/facebook_en/kakaotalk/naver_cafe)도 remote_source+raw_info 로 일원화
- EN 채널은 같은 source_type + language='EN' 으로 구분, select_run_raw_data 가 합성키로 반환
- SourceType.BRANDING 추가 — brand_assets/channel_logos 결과를 하나의 raw_info entry 에 머지
- collect.collect_all: main wave gather → branding 2단계 순차 실행
- mock_urls 매칭 + _with_scheme 보정 유지
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
채널 확장 + 브랜드 자산 파이프라인을 main에 통합.
신규/주요 변경:
- 5채널 외 부가 수집 (틱톡/IG·FB 영문/네이버 카페/카카오톡) — collect_extras.py
- 브랜드 자산: 홈페이지 로고 URL + CSS 색상 추출 (color_extractor.py) + Gemini Vision 로고 묘사 (vision.py)
- 채널 로고 비교: 공식 로고와 각 채널 프로필 이미지 일치 여부 평가
- 인스타/페북 audit 빌더 분리 (instagram_audit.py, facebook_audit.py)
- mock_urls.py: 78개 병원 영문 채널 51건 + 필드 캐노니컬 순서 정규화
- ReportInput/PlanInput 신규 채널 필드 추가
- ChannelBrandingRule literal "missing" → "N/A"
teammate eed5772와의 conflict 해결:
- ClinicSnapshot/YouTubeAudit: teammate가 신뢰 못하는 필드 제거 (established/years_in_business/price_range/media_appearances/medical_tourism/nearest_station/subscriber_rank)
- services/analysis.py: teammate의 _build_clinic_snapshot/_build_youtube_audit/duration helpers + 우리의 _naver_blog_summary 둘 다 보존
- imports: youtube_diagnosis_prompt + build_instagram_accounts/build_facebook_pages 모두 채택
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
머지 본체:
- 5채널 외 부가 수집(틱톡/IG·FB EN/네이버 카페/카카오톡)
- 브랜드 자산/채널 로고 Vision 분석
- ReportInput/PlanInput에 신규 채널 필드 추가
- ChannelBrandingRule literal "missing" → "N/A"
후속 로컬 작업 (분리 커밋 예정):
- fix(report): ClinicSnapshot/YouTubeAudit/Instagram*/Facebook* required→Optional (LLM null 응답 대응)
- refactor: enrichment.py → collect_extras.py (네이밍 명확화)
- data(mock_urls): 38개 병원 영문 채널 51건 추가 + 78개 필드 캐노니컬 순서 정규화
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
apify.py: 라이브 actor id 들을 모두 모듈 상단 상수로 통일 (TIKTOK_ACTOR 추가).
fetch_tiktok_profile 이 raw 문자열 'clockworks~tiktok-scraper' 쓰던 것 정리.
이제 IG_PROFILE / IG_HIGHLIGHTS / FB_PAGES / FB_POSTS / TIKTOK 5개 상수.
수집기 옵저버빌리티 정리:
- collect.py: 채널별 done 로그에 붙이던 _summarize (followers/posts 등 데이터
shape inspection) 제거 — production 로그가 아니라 진단용에 가까워 test_raw.py
의 summarize() 로 대신 충분.
- enrichment.py / pipeline.py / collect.py: 저레벨 수집기의 timing instrumentation
은 정리. orchestrator 레벨(pipeline 의 stage_times, analysis/market 의 LLM
호출 timing)은 유지.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
네이버 블로그 채널 추가:
- naver.fetch_blog_total_count: RSS에 totalCount 없으면 blog.naver.com 의 PostList
페이지 HTML에서 '(\d+)개의 글' 패턴으로 진짜 전체 글 수 추출
(RSS는 최근 50개만 줘서 그동안 totalResults=50 으로 잘못 박혔음 — 뷰성형외과 실제 554개)
- analysis._naver_blog_summary 다이어트: totalPosts + latestPostDate 만 LLM에 보냄
(posts 본문/링크/제목 빼서 토큰 절약 + LLM의 무관 정보 hallucinate 방지)
- plan_prompt: channelStrategies 리스트에 네이버 블로그 명시 포함
brand_guide.channel_branding.profile_photo 코드 박기:
- 기존: LLM이 "공식 로고로 통일 (가이드 미보유)" 같은 fallback 문구 hallucinate
- 수정: analysis._patch_plan 이 모든 채널의 profile_photo 를 brand_assets.logo_description
으로 일괄 박음 (채널 통일 전략이라 모두 동일 값)
- plan_prompt: "profilePhoto 는 빈 문자열로 두세요 — 시스템이 채웁니다" 명시
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>