정정 (원본 HTML 5개 URL 재파싱): - 블로그 정지(2024-05) → 오판. www post-sitemap만 본 결과. blog.viewclinic.com은 09-07까지 발행, robots가 AI 봇 14종 명시 허용 - FAQ 0개 → 프리저베 페이지 FAQ 7문항 + 유효 FAQPage 스키마. C2 0→2 - 수술시간·회복 표기 없음 → 프리저베·코·윤곽 페이지에 텍스트 존재(가격대 없음). C4 1→2 - 총점 50 → 54 (C 유지), C 카테고리 28% → 44%. 그 외 판정(H1 5개, ItemList 파싱 실패, sameAs 빈 값, 표 0, 진료시간 이미지, WP 5.5.3)은 재확인 산출물: - src/data/discovery_viewclinic.ts: 레벨·근거·keyFindings·actions 정정, auditedAt 2026-09-07 (/discovery/viewclinic 반영) - 덱 v1.1 (16장): Corrections 슬라이드 + "뷰성형외과에 부탁드리는 것"(정보·확인·액션 3열) 추가. haewon 편집 원본 v1.0은 보존 - 20in 원본 + 표준캔버스 축소본, PDF 각 1부 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
284 lines
16 KiB
Python
284 lines
16 KiB
Python
"""
|
||
Viewclinic AI Answer Readiness Report v1.1 덱 빌드 (INFINITH Deck 규격)
|
||
|
||
v1.0(2026-09-01)을 2026-09-07 재검증으로 정정한 판. 원본 v1.0 파일은 haewon이 폰트를 직접 손본 정본이라
|
||
덮어쓰지 않고 v1.1 파일로 새로 저장한다.
|
||
|
||
정정 3건: 블로그 정지(오판) · FAQ 0개(프리저베 페이지 FAQ 7문항) · 수술시간·회복 표기 없음(프리저베·코·윤곽 페이지에 있음)
|
||
점수: 50 → 54 (C 유지). C 카테고리 28% → 44%.
|
||
추가 2장: 재검증 정정 내역 · 뷰성형외과에 드리는 요청(서포터즈 강화).
|
||
|
||
폰트: haewon 확정 기준(본문 20 / 리드 24 / 카드 제목 28). ifd 분량 검사(폰트 축소 금지) 안에서 카피를 줄였다.
|
||
|
||
python3 docs/reports/build_viewclinic_deck_v1_1.py docs/reports/Viewclinic_AI_Answer_Readiness_Report_v1.1.pptx
|
||
"""
|
||
import sys
|
||
sys.path.insert(0, "/Users/haewonkam/.claude/skills/infinith-deck/scripts")
|
||
from ifd import Deck
|
||
|
||
OUT = sys.argv[1] if len(sys.argv) > 1 else "Viewclinic_AI_Answer_Readiness_Report_v1.1.pptx"
|
||
BODY, HEAD = 20.0, 28.0
|
||
d = Deck()
|
||
|
||
# 01 Cover
|
||
d.cover(
|
||
eyebrow="AI DISCOVERY · AEO / GEO AUDIT v1.1",
|
||
title_lines=["AI Answer", "Readiness Report."],
|
||
subtitle="뷰성형외과 · viewclinic.com · 실측 2026. 08. 28 · 재채점 09. 01 · 재검증 정정 09. 07",
|
||
meta_right="AI Discovery Report · 2026",
|
||
)
|
||
|
||
# 02 Executive summary
|
||
s = d.slide(eyebrow="Executive Summary",
|
||
title="읽을 수는 있지만, ", title_accent="인용할 근거가 부족하다",
|
||
lede=["36개 항목 중 32개를 실측했고, 오너 계정이 필요한 4개는 분모에서 제외했습니다.",
|
||
"접근성(A) 98%와 표면(D) 68%는 양호. 엔티티(B) 25%와 콘텐츠(C) 44%가 총점을 끌어내립니다."],
|
||
section="Summary")
|
||
k1, k2, k3, k4 = s.cards4(y=s.band_y, h=3.4)
|
||
k1.kpi("54 / 100", "AI ANSWER READINESS", "등급 C · 48.0 / 89점 · v1.0 50점 정정")
|
||
k2.kpi("98%", "A · 크롤러 접근성", "AI 검색봇 12종 전부 허용")
|
||
k3.kpi("44%", "C · 인용 가능 콘텐츠", "정의문 0 · FAQ 1페이지 · 표 0")
|
||
k4.kpi("4 / 36", "미검증 항목", "GA4 · 서버로그 · GSC · Bing")
|
||
|
||
# 03 What AI sees (dark)
|
||
s = d.slide(eyebrow="What AI Sees · 진단의 결론",
|
||
title="문제는 차단이 아니라 ", title_accent="내용이다", dark=True, section="Findings")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("정상", "good").rows([
|
||
("크롤러 접근 정상", "robots.txt가 AI 검색봇 12종을 허용. Cloudflare 차단 없음."),
|
||
("서버 렌더링 본문", "WordPress가 본문을 서버에서 렌더. JS 없이 10,757자 추출."),
|
||
("표면이 넓다", "강남언니 후기 19,373건. 유튜브 1,215편. 블로그는 발행 중이고 AI 봇 허용."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
R.chip("치명", "critical").rows([
|
||
("누구인지 확정 불가", "MedicalClinic 스키마 없음, sameAs 비어 있음. 시술 목록 JSON-LD 무효."),
|
||
("인용할 문장 부족", "H1 5개가 영문 라벨. HTML 표 0, 병원 정의문 0. FAQ는 프리저베뿐."),
|
||
("표면 간 NAP 불일치", "GBP 전화번호 없음. 웹사이트가 영문 도메인. 주소 표기 3종."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
|
||
# 04 Scorecard
|
||
s = d.slide(eyebrow="Scorecard · 6 Categories",
|
||
title="카테고리별 점수와 ", title_accent="검증 범위",
|
||
lede="검증된 항목만으로 정규화했습니다. 09-07 재검증으로 C2·C4를 정정해 C가 28%에서 44%가 됐습니다.",
|
||
section="Scorecard")
|
||
W, Rail = s.cards2(y=s.band_y, split=0.66)
|
||
W.grid(
|
||
headers=["카테고리", "배점", "획득", "검증", "판정"],
|
||
col_w=[4.6, 1.3, 2.0, 1.4, 1.6],
|
||
rows=[
|
||
["A · AI 크롤러 접근성", "20", "16.7 / 17", "6 / 7", "우수"],
|
||
["B · 엔티티 식별·구조화", "20", "5.0 / 20", "7 / 7", "미흡"],
|
||
["C · 답변 인용 가능 콘텐츠", "25", "11.0 / 25", "8 / 8", "미흡"],
|
||
["D · AI 인용 표면 확보", "20", "13.7 / 20", "6 / 6", "양호"],
|
||
["E · AI 가시성 측정 인프라", "10", "0 / 2", "1 / 4", "미설치"],
|
||
["F · 기술 위생", "5", "1.7 / 5", "4 / 4", "미흡"],
|
||
],
|
||
tones=[[None, None, None, None, "info"], [None, None, None, None, "warning"],
|
||
[None, None, None, None, "warning"], [None, None, None, None, "good"],
|
||
[None, None, None, None, "critical"], [None, None, None, None, "warning"]],
|
||
row_h=0.62, size=BODY,
|
||
)
|
||
Rail.title("읽는 법", size=HEAD).bullets([
|
||
"분모는 검증 항목의 배점 합. E는 분모가 2점뿐.",
|
||
"48.0 / 89 = 54점, 등급 C. v1.0 50점은 오판 포함.",
|
||
"미검증 4개는 계정 접근 후 1일 안에 채운다.",
|
||
], size=BODY)
|
||
|
||
# 05 A 접근성
|
||
s = d.slide(eyebrow="A · Access & Crawlability · 98%",
|
||
title="크롤러 접근은 ", title_accent="열려 있다", section="A · Access")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("우수 · 3/3", "info").rows([
|
||
("A1 검색용 AI 봇 허용", "Disallow는 /wp-admin/ 뿐. 12종 UA로 GET, 전부 200."),
|
||
("A2 CDN 차단 없음", "nginx 직접 응답. Cloudflare 헤더와 AI UA 챌린지 없음."),
|
||
("A4 HTTPS 정합", "http → 301 → https://www.viewclinic.com/ 정본 수렴."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
R.chip("보완·미검증", "warning").rows([
|
||
("A5 sitemap 우수", "page·portfolio lastmod 2026-08-31. 블로그는 별도 서브도메인(정정)."),
|
||
("A7 응답 속도·무게", "TTFB 0.135s 양호. 초기 HTML 838KB, 기준 300KB의 2.8배."),
|
||
("A6 Bing 색인 (미검증)", "ChatGPT 검색은 Bing 인덱스 의존. 계정 확인 필요."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
|
||
# 06 B 엔티티 (dark)
|
||
s = d.slide(eyebrow="B · Entity & Structured Data · 25%",
|
||
title="AI가 이 병원을 ", title_accent="하나의 엔티티로 못 묶는다", dark=True, section="B · Entity")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("치명", "critical").rows([
|
||
("B1 JSON-LD 문법 무효", "ItemList 블록 쉼표 누락. 09-07에도 그대로. 파서가 통째로 버림."),
|
||
("B2 MedicalClinic 없음", "Organization·WebSite·WebPage만. 주소·전화·진료시간 미구조화."),
|
||
("B3 Physician 없음", "의료진은 텍스트와 이미지뿐. 누가 수술하는지 기계가 못 읽음."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
R.chip("미흡", "warning").rows([
|
||
("B5 sameAs 비어 있음", "푸터에 유튜브·인스타·블로그 링크가 있으나 스키마 미연결."),
|
||
("B4 NAP 표기 3종 혼재", "주소 표기 3종. 전화도 2회선과 휴대폰 혼재."),
|
||
("B7 영문 사이트 미연결", "viewplasticsurgery.com이 별도. hreflang 없음."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
|
||
# 07 C 콘텐츠
|
||
s = d.slide(eyebrow="C · Answer-Ready Content · 44%",
|
||
title="프리저베 한 페이지만 ", title_accent="인용할 구조를 갖췄다", section="C · Content")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("0점 항목", "critical").rows([
|
||
("C1 병원 정의문 없음", "메인은 슬라이더와 라벨. 가슴성형 페이지 첫 텍스트는 상담 폼."),
|
||
("C3 H1 5개, 전부 라벨", "가슴·코·오시는길은 H1 없음. 프리저베만 H1 1개 서술형."),
|
||
("C5 HTML 표 없음", "5개 페이지 모두 table 0개. 비교 정보는 이미지."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
R.chip("정정·부분 점수", "warning").rows([
|
||
("C2 FAQ는 프리저베에만 (정정)", "FAQ 7문항과 유효한 FAQPage 스키마. 다른 시술 페이지는 없음."),
|
||
("C4 수치는 시술 상세에 (정정)", "프리저베 20~40분·회복 1~3일, 코 1~2시간·실밥 7일. 가격대 없음."),
|
||
("C6 신선도 정상", "시술 페이지 2026-04-17, sitemap 08-31, 블로그 09-07 게시."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
|
||
# 08 D 표면 / E 측정 (dark)
|
||
s = d.slide(eyebrow="D · Surfaces / E · Measurement",
|
||
title="강남언니는 강하고, ", title_accent="GBP는 전화번호가 없다", dark=True, section="D · Surfaces")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("D · 표면 68%", "good").rows([
|
||
("D1 강남언니 우수", "후기 19,373건, 의료진 26명, 이벤트 52건. 핵심 표면 확보."),
|
||
("D2 GBP 4.9점 (901)", "게시물 활성. 전화번호 미등록, 웹사이트 영문 도메인(08-28 실측)."),
|
||
("D3 오픈웹 표면", "유튜브 1,215편, 블로그 발행 중, 언론 1,137건. NAP 불일치로 3점 보류."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
R.chip("E · 측정 0%", "critical").rows([
|
||
("E1 크롤러 로그", "nginx 직접 호스팅. AI 봇 히트 추출 파이프라인 없음."),
|
||
("E2 GA4 AI 유입 채널", "chatgpt.com·perplexity.ai 채널그룹 없음. 계정 접근 필요."),
|
||
("E4 기준선 반복 측정", "120문항 API 실측 1회(09-01). 반복 측정은 미실시."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
|
||
# 09 Action plan
|
||
s = d.slide(eyebrow="Action Plan",
|
||
title="즉시 고칠 것과 ", title_accent="4주 안에 만들 것", section="Action Plan")
|
||
a, b, c = s.cards3(y=s.band_y)
|
||
a.chip("P0 · 1일~1주", "critical").title("엔티티·표면·측정", size=HEAD).bullets([
|
||
"깨진 JSON-LD 수정, MedicalClinic 스키마.",
|
||
"GBP 전화번호 등록, 웹사이트 교정.",
|
||
"4개 표면 NAP 통일. hreflang 연결.",
|
||
"GA4 AI 채널, 서버로그, GSC·Bing.",
|
||
], size=BODY)
|
||
b.chip("P1 · 4주 내", "warning").title("콘텐츠 구조", size=HEAD).bullets([
|
||
"프리저베 구조를 가슴·코·눈·윤곽으로 확산.",
|
||
"핵심 페이지 첫 단락에 병원 정의문.",
|
||
"비교표를 HTML 표로. Physician 스키마.",
|
||
"WordPress 업그레이드, alt 111개 보완.",
|
||
], size=BODY)
|
||
c.chip("P2 · 지속", "good").title("연결·시딩", size=HEAD).bullets([
|
||
"블로그 글과 시술 페이지를 내부 링크로.",
|
||
"유튜브 영상을 블로그 글로 전사.",
|
||
"월 1회 갱신, 분기 1회 반복 측정.",
|
||
], size=BODY)
|
||
|
||
# 10 Roadmap (dark)
|
||
s = d.slide(eyebrow="Roadmap · 8 Weeks",
|
||
title="8주 안에 ", title_accent="C에서 B로", dark=True, section="Roadmap")
|
||
s.stats3([
|
||
("W1–2", "스키마·NAP·측정 인프라 설치.\n기준선 반복 측정 1회."),
|
||
("W3–6", "정의문·FAQ·H1 재설계·HTML 표.\nWordPress 업그레이드."),
|
||
("W7–8", "재진단. 미검증 4개 반영해\n총점 재산정."),
|
||
], y=s.band_y + 0.6)
|
||
|
||
# 11 Validation
|
||
s = d.slide(eyebrow="Validation · 120-Question Live Test",
|
||
title="진단을 120문항 실측으로 ", title_accent="검증했다",
|
||
lede="잠재 환자 질문 120개를 OpenAI·Perplexity 정식 API로 질의해 예측력을 채점했습니다 (09-01).",
|
||
section="Validation")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("예측 적중", "good").rows([
|
||
("논브랜드 질문에서 부재", "84문항 중 Perplexity 3, ChatGPT 10문항만 언급. 시술 정보 52문항은 0건."),
|
||
("원인은 콘텐츠(C)", "병원 정의문 0, HTML 표 0, FAQ는 한 페이지. 시술별 인용 단락 부재."),
|
||
("표면(D)이 브랜드 방어", "병원명을 아는 36문항은 97~100% 답변. 근거는 강남언니·GBP·뉴스."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
R.chip("진단 밖 발견 · 정정 반영", "info").rows([
|
||
("제3자 표면이 더 넓다", "뉴스·채용공고·바비톡·네이버 예약이 개원연도·주차·휴진을 답함."),
|
||
("발행 중인 블로그가 인용됨", "ChatGPT 추천 근거가 blog.viewclinic.com. v1.0은 정지로 오판."),
|
||
("엔진별 오답 편차", "주차 질문에 Perplexity는 정답, ChatGPT는 추측. 구조화 부재의 비용."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
|
||
# 12 Corrections (dark)
|
||
s = d.slide(eyebrow="Corrections · 2026-09-07 재검증",
|
||
title="v1.0에서 틀린 것 ", title_accent="세 가지를 바로잡습니다", dark=True,
|
||
lede="홈·프리저베·가슴·코·오시는길 5개 URL의 원본 HTML을 다시 받아 파싱하고 점수를 다시 계산했습니다.",
|
||
section="Corrections")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("정정", "warning").rows([
|
||
("블로그 정지 → 발행 중", "www post-sitemap만 본 오판. blog.viewclinic.com은 09-07까지 발행, AI 봇 14종 허용."),
|
||
("FAQ 0개 → 프리저베 7문항", "2개 URL 결과를 전체로 일반화. 프리저베에 FAQ 7문항과 FAQPage 스키마. C2 0→2."),
|
||
("수치 없음 → 시술 상세에 있음", "프리저베·코·윤곽 페이지에 수술시간·마취·회복 텍스트. 가격대는 없음. C4 1→2."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
R.chip("유지 · 재확인", "info").rows([
|
||
("그대로 확인된 것", "H1 5개 라벨, ItemList 파싱 실패, sameAs 빈 값, 표 0개, 진료시간 이미지, WP 5.5.3."),
|
||
("점수", "44.0/89 = 50점 → 48.0/89 = 54점. 등급 C 유지. C 카테고리 28% → 44%."),
|
||
("재확인 필요", "GBP 전화번호·웹사이트는 08-28 실측값. 병원이 고쳤다면 D2·D4 재채점."),
|
||
], t_size=HEAD, b_size=BODY)
|
||
|
||
# 13 Request to the clinic
|
||
s = d.slide(eyebrow="Request · 뷰 서포터즈 강화를 위해",
|
||
title="뷰성형외과에 ", title_accent="부탁드리는 것",
|
||
lede=["서포터즈 사이트 18편(검토 완료 10편)을 병원의 공식 답으로 완성하려면 병원만 아는 정보가 필요합니다.",
|
||
"아래 항목은 담당자 한 분이 1~2주 안에 처리하실 수 있는 범위로 추렸습니다."],
|
||
section="Request")
|
||
a, b, c = s.cards3(y=s.band_y)
|
||
a.chip("정보 · 병원만 아는 사실", "info").title("알려 주시면 반영합니다", size=HEAD).bullets([
|
||
"가슴성형 견적 기본 포함 항목과 결제 방식.",
|
||
"가슴성형 후 출근 기준과 걷기 시작 시점.",
|
||
"타원 수술 재수술 상담 가능 여부.",
|
||
"코·윤곽 재수술 대기 기간, 코 실밥 제거일.",
|
||
], size=BODY)
|
||
b.chip("확인 · 검토와 승인", "warning").title("확인해 주시면 켜집니다", size=HEAD).bullets([
|
||
"검토 대기 5편의 담당 원장 확정과 검토일.",
|
||
"검토 완료 10편의 기록을 병원 기록과 대조.",
|
||
"서포터즈 지원 관계 문구 확정.",
|
||
"작성자 실명·편집 책임자 표기 승인.",
|
||
], size=BODY)
|
||
c.chip("액션 · 홈페이지 쪽", "critical").title("병원이 직접 고쳐야 합니다", size=HEAD).bullets([
|
||
"ItemList JSON-LD 오류 수정, MedicalClinic 추가.",
|
||
"GBP 전화번호 등록, 웹사이트 교정.",
|
||
"프리저베 구조를 다른 시술 페이지로 확산.",
|
||
"서포터즈 정식 도메인 확정과 링크 연결.",
|
||
], size=BODY)
|
||
|
||
# 14 Integrity
|
||
s = d.slide(eyebrow="What We Don't Sell · 정직성 규약",
|
||
title="근거 없는 것은 ", title_accent="팔지 않는다", section="Integrity")
|
||
a, b, c = s.cards3(y=s.band_y)
|
||
a.icon_square().title("만들지 않는 것", size=HEAD).bullets([
|
||
"llms.txt. Google 공식 효과 0, 요청 로그 없음.",
|
||
"네이버 블로그 증량. AI 크롤링 금지 도메인.",
|
||
], size=BODY)
|
||
b.icon_square().title("약속하지 않는 것", size=HEAD).bullets([
|
||
"\"스키마 넣으면 인용됩니다\". 인과 증거 없음.",
|
||
"순위·노출 보장. 답변은 10~34% 흔들림.",
|
||
], size=BODY)
|
||
c.icon_square().title("측정 규율", size=HEAD).bullets([
|
||
"단발 조회 보고 금지. 반복 측정만.",
|
||
"답변엔진 UI 자동 조회 금지. 약관 위반.",
|
||
"틀린 판정은 정정 슬라이드로 남긴다.",
|
||
], size=BODY)
|
||
|
||
# 15 Method
|
||
s = d.slide(eyebrow="Method · AEO / GEO Rubric v1.0",
|
||
title="36개 항목, 100점, ", title_accent="근거 등급을 명시", section="Method")
|
||
a, b, c = s.cards3(y=s.band_y)
|
||
a.icon_square().title("채점 방식", size=HEAD).bullets([
|
||
"항목별 0~3 레벨 × 배점. 미검증은 분모 제외.",
|
||
"A 20 · B 20 · C 25 · D 20 · E 10 · F 5.",
|
||
"등급 A 81+ · B 61~80 · C 41~60 · D 40 이하.",
|
||
], size=BODY)
|
||
b.icon_square().title("배점 근거", size=HEAD).bullets([
|
||
"차단되면 인용될 수 없다. A·B에 무게.",
|
||
"콘텐츠 전술 효과는 논쟁 중. C는 원칙만.",
|
||
"병의원은 강남언니가 장악. D1 배점 6.",
|
||
], size=BODY)
|
||
c.icon_square().title("항목마다 붙는 태그", size=HEAD).bullets([
|
||
"근거 등급: 실측 / 학술 / 벤더 / 추정.",
|
||
"통제 가능성: 직접 / 영향 / 불가.",
|
||
"채점 방식: 자동 / 반자동 / 수동.",
|
||
], size=BODY)
|
||
|
||
# 16 Closing
|
||
d.closing(eyebrow="Next Step",
|
||
title_lines=["표면은 확보됐습니다.", "홈페이지가 따라가면 됩니다."],
|
||
lede="P0 세 가지: 스키마, NAP 통일, 측정 인프라. 서포터즈 요청은 담당자 한 분이 처리할 수 있습니다.",
|
||
cta_primary="P0 착수하기 →", cta_secondary="서포터즈 사이트 보기",
|
||
contact="o2oteam@o2o.kr")
|
||
|
||
d.save(OUT)
|
||
print("saved", OUT)
|