docs: Viewclinic 리포트 덱을 INFINITH Deck 규격(infinith-deck 스킬)으로 재생성

- 20×11.25in, F7F8FC/021341 플랫 배경, Pretendard + Playfair, 상태색 칩
- 빌드 스크립트 build_viewclinic_deck.py는 ~/.claude/skills/infinith-deck/scripts/ifd.py 사용

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Haewon Kam 2026-08-28 11:19:13 +09:00
parent 978073dd0e
commit 99d770b75f
4 changed files with 169 additions and 166 deletions

View File

@ -1,210 +1,213 @@
import sys """
sys.path.insert(0, "/Users/haewonkam/.claude/skills/haewon_signature_deck/scripts") Viewclinic AI Answer Readiness Report 빌드 (INFINITH Deck 규격)
import hsd
from hsd import Deck
OUT = sys.argv[1] python3 docs/reports/build_viewclinic_deck.py docs/reports/Viewclinic_AI_Answer_Readiness_Report.pptx
d = Deck(footer_base="INFINITH · Viewclinic AI Answer Readiness") """
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.pptx"
d = Deck()
# 01 Cover # 01 Cover
d.cover( d.cover(
eyebrow="INFINITH AI DISCOVERY · AEO / GEO AUDIT v1.0", eyebrow="AI DISCOVERY · AEO / GEO AUDIT v1.0",
title="VIEWCLINIC", title_lines=["AI Answer", "Readiness Report."],
subtitle="AI Answer Readiness Report", subtitle="뷰성형외과 · viewclinic.com · 36개 항목 실측 · 2026. 08. 28",
lede=["뷰성형외과 홈페이지가 ChatGPT·Gemini·Perplexity 답변에 인용될 준비가 됐는지 36개 항목으로 진단했습니다.", meta_right="AI Discovery Report · 2026",
"결과는 48점(C). 크롤러 접근과 강남언니는 강하지만, 홈페이지에 인용할 문장과 엔티티 근거가 없습니다."],
meta=[("PREPARED FOR", "뷰성형외과"),
("PREPARED BY", "INFINITH AI Discovery"),
("AUDITED", "2026. 08. 28"),
("SCOPE", "viewclinic.com · 36 criteria")],
) )
# 02 Executive summary (KPI 4-up) # 02 Executive summary
s = d.slide(eyebrow="EXECUTIVE SUMMARY", s = d.slide(eyebrow="Executive Summary",
title="읽을 수는 있지만, ", title_accent="인용할 근거가 없다", title="읽을 수는 있지만, ", title_accent="인용할 근거가 없다",
lede=["36개 항목 중 31개를 실측했고, 오너 접근이 필요한 5개는 점수 분모에서 제외했습니다.", lede=["36개 항목 중 31개를 실측했고, 오너 계정이 필요한 5개는 점수 분모에서 제외했습니다.",
"접근성(A) 92%·표면(D) 72%는 양호, 엔티티(B) 30%·콘텐츠(C) 20%가 총점을 끌어내립니다."]) "접근성(A) 92%와 표면(D) 72%는 양호. 엔티티(B) 30%와 콘텐츠(C) 20%가 총점을 끌어내립니다."],
s.kpi_4up([ section="Summary")
("48 / 100", "AI Answer Readiness", "등급 C. 검증 항목 88점 만점 중 42.1점"), k1, k2, k3, k4 = s.cards4(y=s.band_y, h=3.4)
("92%", "A · 크롤러 접근성", "AI 검색봇 12종 전부 허용, SSR 렌더링"), k1.kpi("48 / 100", "AI ANSWER READINESS", "등급 C · 42.1 / 88점")
("20%", "C · 인용 가능 콘텐츠", "정의문 0, FAQ 0, HTML 표 0, H1 5개"), k2.kpi("92%", "A · 크롤러 접근성", "AI 검색봇 12종 전부 허용")
("5 / 36", "미검증 항목", "GA4·서버로그·GSC·Bing은 오너 계정 필요"), k3.kpi("20%", "C · 인용 가능 콘텐츠", "정의문 0 · FAQ 0 · HTML 표 0")
k4.kpi("5 / 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가 OAI-SearchBot·PerplexityBot 등 12종을 허용. Cloudflare 차단 없음."),
("서버 렌더링 본문", "WordPress가 본문을 서버에서 렌더. JS 없이 10,757자 추출."),
("강남언니·YouTube 활발", "강남언니 후기 19,316건, 의료진 26명. YouTube 채널이 최신 영상을 꾸준히 발행."),
])
R.chip("치명", "critical").rows([
("누구인지 확정 불가", "MedicalClinic 스키마 없음, sameAs 비어 있음. 시술 목록 JSON-LD는 문법 오류로 무효."),
("인용할 문장 없음", "H1 5개가 전부 영문 라벨. FAQ 0개, HTML 표 0개, 정의문 0개."),
("표면 간 NAP 불일치", "GBP에 전화번호 없음. 웹사이트가 viewplasticsurgery.com. 주소 표기 3종."),
]) ])
# 03 What AI sees # 04 Scorecard
s = d.slide(eyebrow="WHAT AI SEES · 진단의 결론", s = d.slide(eyebrow="Scorecard · 6 Categories",
title="문제는 차단이 아니라 ", title_accent="내용이다")
L, R = s.cards_2up()
L.pill("정상").rows([
("크롤러 접근 정상", "robots.txt가 OAI-SearchBot·PerplexityBot 등 12종을 허용. Cloudflare 차단 없음"),
("서버 렌더링 본문", "WordPress가 본문을 서버에서 렌더. JS 없이 10,757자 추출됨"),
("YouTube 활발", "VIEW SHORTS·FILM 채널이 최신 영상을 꾸준히 발행. 오픈웹 언급 자산"),
])
R.pill("치명", tone="rival").rows([
("누구인지 확정 불가", "MedicalClinic 스키마 없음, sameAs 비어 있음, 시술 목록 JSON-LD는 문법 오류로 무효"),
("인용할 문장 없음", "H1 5개가 전부 영문 라벨. FAQ 0개, HTML 표 0개, 정의문 0개"),
("15개월 갱신 신호 없음", "post-sitemap 최신 lastmod 2024-05-31. dateModified 마크업 없음"),
])
# 04 Scorecard table
s = d.slide(eyebrow="SCORECARD · 6 CATEGORIES",
title="카테고리별 점수와 ", title_accent="검증 범위", title="카테고리별 점수와 ", title_accent="검증 범위",
lede="점수는 검증된 항목만으로 정규화했습니다. 미검증 항목을 확인하면 총점이 바뀔 수 있습니다.") lede="점수는 검증된 항목만으로 정규화했습니다. 미검증 항목을 확인하면 총점이 바뀔 수 있습니다.",
W, Rail = s.wide_rail() section="Scorecard")
W, Rail = s.cards2(y=s.band_y, split=0.66)
W.grid( W.grid(
headers=["카테고리", "배점", "획득", "판정"], headers=["카테고리", "배점", "획득", "검증", "판정"],
col_w=[2.45, 0.80, 0.95, 0.85], start=0.20, col_w=[4.6, 1.3, 2.0, 1.4, 1.6],
header_tones=[hsd.MUTED, hsd.MUTED, hsd.VIOLET, hsd.MUTED],
rows=[ rows=[
["A · AI 크롤러 접근성", "20", "15.7 / 17", "92% 우수"], ["A · AI 크롤러 접근성", "20", "15.7 / 17", "6 / 7", "우수"],
["B · 엔티티 식별·구조화", "20", "6.0 / 20", "30% 미흡"], ["B · 엔티티 식별·구조화", "20", "6.0 / 20", "7 / 7", "미흡"],
["C · 답변 인용 가능 콘텐츠", "25", "5.0 / 25", "20% 치명"], ["C · 답변 인용 가능 콘텐츠", "25", "5.0 / 25", "8 / 8", "치명"],
["D · AI 인용 표면 확보", "20", "13.7 / 19", "72% 양호"], ["D · AI 인용 표면 확보", "20", "13.7 / 19", "5 / 6", "양호"],
["E · AI 가시성 측정 인프라", "10", "0 / 2", "0% 미설치"], ["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, "critical"], [None, None, None, None, "good"],
[None, None, None, None, "critical"], [None, None, None, None, "warning"]],
row_h=0.62,
) )
Rail.bullets("읽는 법", items=[ Rail.title("읽는 법").bullets([
"획득 분모는 검증 항목 배점 합. E는 미검증 3개라 분모가 2점뿐", "획득 분모는 검증 항목 배점 합. E는 미검증 3개라 분모가 2점뿐.",
"F 기술 위생은 5점 중 1.7점(33%). 표에서 생략", "총점 42.1 / 88 = 48점, 등급 C.",
"총점 = 42.1 / 88 = 48점, 등급 C", "미검증 5개는 GA4·서버로그·GSC·Bing 계정 접근으로 1일 안에 채운다.",
]) ])
# 05 A 접근성 # 05 A 접근성
s = d.slide(eyebrow="A · ACCESS & CRAWLABILITY · 92%", s = d.slide(eyebrow="A · Access & Crawlability · 92%",
title="크롤러 접근은 ", title_accent="열려 있다") title="크롤러 접근은 ", title_accent="열려 있다", section="A · Access")
L, R = s.cards_2up() L, R = s.cards2(y=s.band_y)
L.pill("우수 · 3/3").rows([ L.chip("우수 · 3/3", "info").rows([
("A1 검색용 AI 봇 허용", "robots.txt: Disallow는 /wp-admin/ 뿐. 12종 UA 실제 GET 전부 200"), ("A1 검색용 AI 봇 허용", "robots.txt Disallow는 /wp-admin/ 뿐. 12종 UA 실제 GET, 전부 200."),
("A2 CDN 차단 없음", "nginx 직접 응답. Cloudflare 헤더·AI UA 챌린지 없음"), ("A2 CDN 차단 없음", "nginx 직접 응답. Cloudflare 헤더와 AI UA 챌린지 없음."),
("A4 HTTPS 정합", "http://viewclinic.com → 301 → https://www.viewclinic.com/ 정본 수렴"), ("A4 HTTPS 정합", "http://viewclinic.com → 301 → https://www.viewclinic.com/ 정본 수렴."),
]) ])
R.pill("보완 · 2/3", tone="neutral").rows([ R.chip("보완 · 2/3", "warning").rows([
("A5 sitemap.xml", "선언·200 응답. 페이지 249개. 단 post lastmod 최신이 2024-05-31"), ("A5 sitemap.xml", "선언·200 응답, 페이지 249개. 단 post lastmod 최신이 2024-05-31."),
("A7 응답 속도·무게", "TTFB 0.135s는 양호. 초기 HTML 838KB로 기준 300KB의 2.8배"), ("A7 응답 속도·무게", "TTFB 0.135s는 양호. 초기 HTML 838KB로 기준 300KB의 2.8배."),
("A6 Bing 색인 (미검증)", "ChatGPT 검색은 Bing 인덱스 의존. Webmaster 등록 여부 오너 확인 필요"), ("A6 Bing 색인 (미검증)", "ChatGPT 검색은 Bing 인덱스에 의존. Webmaster 등록 여부는 계정 확인 필요."),
]) ])
# 06 B 엔티티 # 06 B 엔티티 (dark)
s = d.slide(eyebrow="B · ENTITY & STRUCTURED DATA · 33%", s = d.slide(eyebrow="B · Entity & Structured Data · 30%",
title="AI가 이 병원을 ", title_accent="하나의 엔티티로 못 묶는다") title="AI가 이 병원을 ", title_accent="하나의 엔티티로 못 묶는다", dark=True, section="B · Entity")
L, R = s.cards_2up() L, R = s.cards2(y=s.band_y)
L.pill("치명", tone="rival").rows([ L.chip("치명", "critical").rows([
("B1 JSON-LD 문법 무효", "ItemList 블록 쉼표 누락(line 11). 시술 목록 구조화 데이터 전체가 파서에서 버려짐"), ("B1 JSON-LD 문법 무효", "ItemList 블록 쉼표 누락(line 11). 시술 목록 구조화 데이터 전체가 파서에서 버려짐."),
("B2 MedicalClinic 없음", "Organization·WebSite만 존재. 주소·전화·진료시간·전문분야 구조화"), ("B2 MedicalClinic 없음", "Organization·WebSite만 존재. 주소·전화·진료시간·전문분야 구조화되지 않음."),
("B3 Physician 없음", "의료진 정보는 텍스트·이미지로만. 누가 수술하는지 기계가 못 읽음"), ("B3 Physician 없음", "의료진 정보는 텍스트와 이미지로만 존재. 누가 수술하는지 기계가 읽지 못함."),
]) ])
R.pill("미흡", tone="neutral").rows([ R.chip("미흡", "warning").rows([
("B5 sameAs 비어 있음", "푸터에 YouTube·Instagram·Facebook 링크가 있으나 스키마에 연결"), ("B5 sameAs 비어 있음", "푸터에 YouTube·Instagram·Facebook 링크가 있으나 스키마에 연결되지 않음."),
("B4 NAP 표기 2종", "\"서울 강남구 봉은사로 107\" / \"서울특별시 강남구 봉은사로 107,\" 혼재"), ("B4 NAP 표기 2종", "\"서울 강남구 봉은사로 107\" / \"서울특별시 강남구 봉은사로 107,\" 혼재."),
("B7 영문 사이트 미연결", "viewplasticsurgery.com이 별도로 있으나 hreflang 없음. 두 도메인이 서로 모름"), ("B7 영문 사이트 미연결", "viewplasticsurgery.com이 별도로 있으나 hreflang 없음. 두 도메인이 서로 모름."),
]) ])
# 07 C 콘텐츠 # 07 C 콘텐츠
s = d.slide(eyebrow="C · ANSWER-READY CONTENT · 20%", s = d.slide(eyebrow="C · Answer-Ready Content · 20%",
title="답변엔진이 잘라 갈 ", title_accent="단락이 없다") title="답변엔진이 잘라 갈 ", title_accent="단락이 없다", section="C · Content")
L, R = s.cards_2up() L, R = s.cards2(y=s.band_y)
L.pill("0점 항목", tone="rival").rows([ L.chip("0점 항목", "critical").rows([
("C1 정의문 없음", "메인 첫 화면은 슬라이더와 \"VIEW SELFIE\" 라벨. 시술 페이지 첫 텍스트는 상담 폼"), ("C1 정의문 없음", "메인 첫 화면은 슬라이더와 \"VIEW SELFIE\" 라벨. 시술 페이지 첫 텍스트는 상담 폼."),
("C2 FAQ 없음", "메인·가슴성형 페이지에서 FAQ·Q&A 섹션 탐지 안 됨"), ("C2 FAQ 없음", "메인·가슴성형 페이지에서 FAQ·Q&A 섹션이 탐지되지 않음."),
("C3 H1 5개, 전부 라벨", "VIEW SELFIE / EVENT / SHORTS / FILM / SPECIALITY. 시술 페이지는 H1 없음"), ("C3 H1 5개, 전부 라벨", "VIEW SELFIE / EVENT / SHORTS / FILM / SPECIALITY. 시술 페이지는 H1 없음."),
]) ])
R.pill("부분 점수", tone="neutral").rows([ R.chip("부분 점수", "warning").rows([
("C7 의료진 권위 신호 있음", "모티바 KOL·복지부 표창·갈더마 방문 등 17회 언급. 단 구조화 안 됨"), ("C7 의료진 권위 신호 있음", "모티바 KOL·복지부 표창·갈더마 방문 등 17회 언급. 단 구조화되지 않음."),
("C4 수치 표기 미흡", "\"보형물 사용량 1위\" 같은 주장형. 가격대·수술시간·회복기간 표 없음"), ("C4 수치 표기 미흡", "\"보형물 사용량 1위\" 같은 주장형. 가격대·수술시간·회복기간 표 없음."),
("C6 신선도 신호 약함", "dateModified 없음. 유튜브 활동이 사이트 갱신으로 전달되지 않음"), ("C6 신선도 신호 약함", "dateModified 없음. 유튜브 활동이 사이트 갱신으로 전달되지 않음."),
]) ])
# 08 D 표면 + E 측정 # 08 D 표면 / E 측정 (dark)
s = d.slide(eyebrow="D · SURFACES / E · MEASUREMENT", s = d.slide(eyebrow="D · Surfaces / E · Measurement",
title="강남언니는 강하고, ", title_accent="GBP는 전화번호가 없다") title="강남언니는 강하고, ", title_accent="GBP는 전화번호가 없다", dark=True, section="D · Surfaces")
L, R = s.cards_2up() L, R = s.cards2(y=s.band_y)
L.pill("D · 표면 72%").rows([ L.chip("D · 표면 72%", "good").rows([
("D1 강남언니 우수", "후기 19,316건, 의료진 26명, 이벤트 52건. 병의원 AI 인용의 핵심 표면 확보"), ("D1 강남언니 우수", "후기 19,316건, 의료진 26명, 이벤트 52건. 병의원 AI 인용의 핵심 표면 확보."),
("D2 GBP 4.9점 (901)", "소유자 게시물 활성. 단 전화번호 미등록, 웹사이트가 viewplasticsurgery.com"), ("D2 GBP 4.9점 (901)", "소유자 게시물 활성. 단 전화번호 미등록, 웹사이트가 viewplasticsurgery.com."),
("D4 NAP 표기 3종", "홈페이지·강남언니·GBP 주소 표기가 전부 다름. AI가 한 병원으로 묶을 근거 약함"), ("D4 NAP 표기 3종", "홈페이지·강남언니·GBP 주소 표기가 전부 다름. 한 병원으로 묶을 근거 약함."),
]) ])
R.pill("E · 측정 0%", tone="rival").rows([ R.chip("E · 측정 0%", "critical").rows([
("E1 크롤러 로그", "nginx 직접 호스팅. AI 봇 히트 추출·IP 검증 파이프라인 없음"), ("E1 크롤러 로그", "nginx 직접 호스팅. AI 봇 히트 추출과 IP 검증 파이프라인 없음."),
("E2 GA4 AI 유입 채널", "chatgpt.com·perplexity.ai 커스텀 채널그룹 없음. 오너 접근 필요"), ("E2 GA4 AI 유입 채널", "chatgpt.com·perplexity.ai 커스텀 채널그룹 없음. 계정 접근 필요."),
("E4 기준선 스팟체크", "미실시. 질의 10개 × 5회 반복 측정이 개선 증명의 전제"), ("E4 기준선 스팟체크", "미실시. 질의 10개 × 5회 반복 측정이 개선 증명의 전제."),
]) ])
# 09 Action P0 / P1 (dark) # 09 Action plan
s = d.slide(eyebrow="ACTION PLAN · P0 / P1", s = d.slide(eyebrow="Action Plan",
title="즉시 고칠 것과 ", title_accent="4주 안에 만들 것", dark=True) title="즉시 고칠 것과 ", title_accent="4주 안에 만들 것", section="Action Plan")
L, R = s.cards_2up() a, b, c = s.cards3(y=s.band_y)
L.dark_block("P0 · 즉시 (1일~1주)", "엔티티·표면·측정", [ a.chip("P0 · 1일~1주", "critical").title("엔티티·표면·측정").bullets([
"깨진 JSON-LD 수정 + MedicalClinic 스키마 삽입, sameAs 연결", "깨진 JSON-LD 수정, MedicalClinic 스키마 삽입, sameAs 연결.",
"GBP 전화번호 등록·웹사이트 교정, 4개 표면 NAP 통일", "GBP 전화번호 등록, 웹사이트를 viewclinic.com으로 교정.",
"GA4 AI 채널그룹 + 서버로그 IP 검증 + GSC·Bing 등록", "4개 표면 NAP를 한 글자까지 통일. 두 도메인 hreflang 연결.",
"GA4 AI 채널그룹, 서버로그 IP 검증, GSC·Bing 등록.",
]) ])
R.dark_block("P1 · 4주 내", "콘텐츠 구조", [ b.chip("P1 · 4주 내", "warning").title("콘텐츠 구조").bullets([
"핵심 페이지 첫 단락에 정의문, 시술별 FAQ 5~8개 + FAQPage 스키마", "핵심 페이지 첫 단락에 정의문. 시술별 FAQ 5~8개와 FAQPage 스키마.",
"페이지당 H1 1개 서술형, 비교표를 HTML table로, Physician 스키마", "페이지당 H1 1개 서술형. 비교표를 HTML table로. Physician 스키마.",
"WordPress 5.5.3 업그레이드, alt 누락 111개 보완, HTML 무게 절감", "WordPress 5.5.3 업그레이드, alt 누락 111개 보완, HTML 무게 절감.",
])
c.chip("P2 · 지속", "good").title("신선도·시딩").bullets([
"dateModified 마크업 활성화, 월 1회 사이트 갱신.",
"유튜브 영상을 사이트 글로 전사해 신선도를 전달.",
"티스토리·언론 기고로 naver.com 외 언급 도메인 확보.",
"분기 1회 반복 측정. 언급률과 신뢰구간으로만 보고.",
]) ])
# 10 Roadmap (asym) # 10 Roadmap (stats style, dark)
s = d.slide(eyebrow="ROADMAP · 8 WEEKS", s = d.slide(eyebrow="Roadmap · 8 Weeks",
title="8주 안에 ", title_accent="C에서 B로") title="8주 안에 ", title_accent="C에서 B로", dark=True, section="Roadmap")
L, R = s.cards_asym() s.stats3([
L.timeline("실행 순서", [ ("W12", "스키마·NAP·측정 인프라 설치.\n기준선 스팟체크 1회."),
("Week 1", "JSON-LD 수정, MedicalClinic·sameAs 삽입. GBP 전화·웹사이트 교정"), ("W36", "정의문·FAQ·H1 재설계·HTML 표.\nWordPress 업그레이드."),
("Week 2", "측정 인프라 설치. 기준선 스팟체크 1회 (질의 10개 × 5회)"), ("W78", "재진단. 미검증 5개 반영해\n총점 재산정."),
("Week 36", "정의문·FAQ·H1 재설계·HTML 표. WordPress 업그레이드"), ], y=s.band_y + 0.6)
("Week 78", "재진단. 미검증 5개 항목 반영해 총점 재산정"),
# 11 What we don't sell
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("만들지 않는 것").bullets([
"llms.txt. Google 공식 효과 0, 채택률 0.13%, 서버 로그에 요청 자체가 없음.",
"네이버 블로그 증량. robots.txt가 AI 크롤링을 전면 금지, 글로벌 AI 기여도 0.",
]) ])
R.numbered("P2 · 지속 항목", [ b.icon_square().title("약속하지 않는 것").bullets([
"dateModified 마크업 활성화, 월 1회 사이트 갱신", "\"스키마 넣으면 인용됩니다\". 인과 증거 없음, 위생 조치로만 설명.",
"유튜브 영상을 사이트 글로 전사해 신선도 전달", "순위·노출 보장. LLM 답변은 동일 질문에도 10~34% 흔들림.",
"티스토리·언론 기고로 naver.com 외 언급 도메인 확보", ])
"분기 1회 반복 측정, 언급률과 신뢰구간으로만 보고", c.icon_square().title("측정 규율").bullets([
"단발 조회로 성과 보고 금지. 반복 측정과 신뢰구간만.",
"답변엔진 UI 자동 조회 금지. OpenAI·Perplexity 약관 위반.",
"AI 유입은 항상 하한값. 35~70%가 Direct로 유실.",
]) ])
# 11 What we don't sell (3-up) # 12 Method
s = d.slide(eyebrow="WHAT WE DON'T SELL · 정직성 규약", s = d.slide(eyebrow="Method · AEO / GEO Rubric v1.0",
title="근거 없는 것은 ", title_accent="팔지 않는다") title="36개 항목, 100점, ", title_accent="근거 등급을 명시", section="Method")
c1, c2, c3 = s.cards_3up() a, b, c = s.cards3(y=s.band_y)
c1.bullets("만들지 않는 것", items=[ a.icon_square().title("채점 방식").bullets([
"llms.txt. Google 공식 효과 0, 채택률 0.13%, 서버 로그에 요청 자체가 없음", "항목별 0~3 레벨 × 배점. 미검증은 0점이 아니라 분모 제외.",
"네이버 블로그 증량. robots.txt가 AI 크롤링 전면 금지, 글로벌 AI 기여도 0", "A 20 · B 20 · C 25 · D 20 · E 10 · F 5.",
"등급 A 81+ · B 61~80 · C 41~60 · D 40 이하.",
]) ])
c2.bullets("약속하지 않는 것", items=[ b.icon_square().title("배점 근거").bullets([
"\"스키마 넣으면 인용됩니다\". 인과 증거 없음, 위생 조치로만 설명", "차단되면 절대 인용될 수 없다는 사실은 논쟁이 없다. A·B에 무게.",
"순위·노출 보장. LLM 답변은 동일 질문에도 10~34% 흔들림", "GEO 콘텐츠 전술의 효과는 학술적으로 논쟁 중. C는 좋은 콘텐츠 원칙으로만.",
"병의원은 강남언니가 인용을 장악. D1 단일 항목 최고 배점 6.",
]) ])
c3.bullets("측정 규율", items=[ c.icon_square().title("항목마다 붙는 태그").bullets([
"단발 조회로 성과 보고 금지. 반복 측정 + 신뢰구간만", "근거 등급: 실측 / 학술 / 벤더 / 추정.",
"답변엔진 UI 자동 조회 금지. OpenAI·Perplexity 약관 위반", "통제 가능성: 직접 / 영향 / 불가.",
"AI 유입은 항상 하한값. 35~70%가 Direct로 유실", "채점 방식: 자동 / 반자동 / 수동. 자동 항목은 Edge Function으로 이식.",
])
# 12 Rubric method (3-up)
s = d.slide(eyebrow="METHOD · AEO / GEO RUBRIC v1.0",
title="36개 항목, 100점, ", title_accent="근거 등급을 명시")
c1, c2, c3 = s.cards_3up()
c1.bullets("채점 방식", items=[
"항목별 0~3 레벨 × 배점. 미검증은 0점이 아니라 분모 제외",
"A 20 · B 20 · C 25 · D 20 · E 10 · F 5",
"등급 A 81+ · B 61~80 · C 41~60 · D 40 이하",
])
c2.bullets("배점 근거", items=[
"차단되면 절대 인용될 수 없다는 사실은 논쟁이 없다. A·B에 무게",
"GEO 콘텐츠 전술 효과는 학술적으로 논쟁 중. C는 좋은 콘텐츠 원칙으로만",
"병의원은 강남언니가 인용을 장악. D1 단일 항목 최고 배점 6",
])
c3.bullets("항목마다 붙는 태그", items=[
"근거 등급: 실측 / 학술 / 벤더 / 추정",
"통제 가능성: 직접 / 영향 / 불가",
"채점 방식: 자동 / 반자동 / 수동. 자동 항목은 Edge Function으로 이식",
]) ])
# 13 Closing # 13 Closing
d.closing(eyebrow="NEXT STEP", d.closing(eyebrow="Next Step",
title="표면은 확보됐다. ", title_accent="홈페이지가 따라가면 됩니다.", title_lines=["표면은 확보됐습니다.", "홈페이지가 따라가면 됩니다."],
lede=["강남언니·GBP는 이미 강합니다. 홈페이지 스키마와 NAP 통일, 측정 인프라 세 가지가 P0입니다.", lede="P0 세 가지: 스키마, NAP 통일, 측정 인프라. 미검증 5개는 계정 접근 후 1일 안에 채웁니다.",
"남은 미검증 5개는 GA4·서버로그·GSC·Bing 계정 접근만 주시면 1일 안에 채웁니다."], cta_primary="P0 착수하기 →", cta_secondary="리포트 페이지 보기",
signature="INFINITH AI Discovery · AEO / GEO Audit v1.0 · 2026. 08. 28") contact="infinith.kr · hello@infinith.kr")
d.save(OUT) d.save(OUT)
print("saved", OUT) print("saved", OUT)