- viewclinic/01_readiness_report (진단 덱 v1.0·v2_표준캔버스·v1.1 + 빌더) / 02_poc_proposal (POC 정본·v3 + 빌더) / 03_question_bank (QB 120문항·실측·엑셀) / 04_supporters (GEO 계획·콘텐츠 기획·감사 엑셀) - taeha/ (태하 리포트 + 빌더), ibk/ (기업은행 블로그 감사), landing/ (랜딩 시안·스크린샷·HTML 내보내기) - README.md에 구조·정본/파생본·빌드 명령 안내 - 경로 참조 갱신: 핸드오버 문서 4개, QB 스크립트 3개, 빌더 docstring, discovery 페이지 주석 - 이전에 삭제된 POC v2_표준캔버스 2파일 삭제 반영 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
214 lines
12 KiB
Python
214 lines
12 KiB
Python
"""
|
||
법무법인 태하 AI Answer Readiness Report 덱 빌드 (INFINITH Deck 규격)
|
||
|
||
python3 docs/reports/taeha/build_taeha_deck.py docs/reports/taeha/Taeha_AI_Answer_Readiness_Report.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 "Taeha_AI_Answer_Readiness_Report.pptx"
|
||
d = Deck()
|
||
|
||
# 01 Cover
|
||
d.cover(
|
||
eyebrow="AI DISCOVERY · AEO / GEO AUDIT v1.0 · LEGAL",
|
||
title_lines=["AI Answer", "Readiness Report."],
|
||
subtitle="법무법인 태하 · taehadrug.com · 네이버 마약 변호사 파워링크 1위 · 2026. 08. 28",
|
||
meta_right="AI Discovery Report · 2026",
|
||
)
|
||
|
||
# 02 Executive summary
|
||
s = d.slide(eyebrow="Executive Summary",
|
||
title="잘 만든 사이트를 ", title_accent="ChatGPT가 못 읽는다",
|
||
lede=["36개 항목 중 31개를 실측했고, 오너 계정이 필요한 5개는 점수 분모에서 제외했습니다.",
|
||
"구조화·콘텐츠·위생은 상위권. 메인 도메인의 WAF 차단과 도메인 6개 분산이 총점을 끌어내립니다."],
|
||
section="Summary")
|
||
k1, k2, k3, k4 = s.cards4(y=s.band_y, h=3.4)
|
||
k1.kpi("68 / 100", "AI ANSWER READINESS", "등급 B · 60 / 88점")
|
||
k2.kpi("87%", "F · 기술 위생", "alt 누락 0 · HTML 274KB")
|
||
k3.kpi("65%", "C · 인용 가능 콘텐츠", "FAQ 14문항 · 전관 경력 텍스트")
|
||
k4.kpi("403", "메인 도메인 → GPTBot", "taehalaw.com WAF가 AI 검색봇 차단")
|
||
|
||
# 03 What AI sees (dark)
|
||
s = d.slide(eyebrow="What AI Sees · 진단의 결론",
|
||
title="엔티티가 6개로 ", title_accent="쪼개져 있다", dark=True, section="Findings")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("정상", "good").rows([
|
||
("마약 사이트 구조화 우수", "JSON-LD 3블록 전부 유효. LegalService·Attorney·FAQPage·Breadcrumb가 @graph로 연결."),
|
||
("전관 경력이 텍스트로 있다", "지청장·판사·부장검사 역임이 H3로 노출. AI가 읽을 수 있는 권위 신호."),
|
||
("FAQ 14문항, 실제 질문형", "\"초범인데도 실형이 나올 수 있나요?\" 같은 의뢰인 언어 그대로. FAQPage 스키마 병행."),
|
||
])
|
||
R.chip("치명", "critical").rows([
|
||
("메인 도메인이 ChatGPT를 차단", "taehalaw.com 가비아 WAF가 GPTBot·OAI-SearchBot을 403으로. 브라우저·Perplexity는 200."),
|
||
("도메인 6개, 전화번호 4종", "마약·형사·이혼·군형법·학폭 사이트가 따로. 콜트래킹 번호와 안심번호가 전부 다름."),
|
||
("canonical이 http://, 경로 불일치", "메인 canonical http://taehadrug.com/. 성공사례 canonical이 실제 URL과 다름."),
|
||
])
|
||
|
||
# 04 Scorecard
|
||
s = d.slide(eyebrow="Scorecard · 6 Categories",
|
||
title="카테고리별 점수와 ", title_accent="검증 범위",
|
||
lede="점수는 검증된 항목만으로 정규화했습니다. 미검증 항목을 확인하면 총점이 바뀔 수 있습니다.",
|
||
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", "15.0 / 17", "6 / 7", "양호"],
|
||
["B · 엔티티 식별·구조화", "20", "13.7 / 20", "7 / 7", "양호"],
|
||
["C · 답변 인용 가능 콘텐츠", "25", "16.3 / 25", "8 / 8", "양호"],
|
||
["D · AI 인용 표면 확보", "20", "10.7 / 19", "5 / 6", "보완"],
|
||
["E · AI 가시성 측정 인프라", "10", "0 / 2", "1 / 4", "미설치"],
|
||
["F · 기술 위생", "5", "4.3 / 5", "4 / 4", "우수"],
|
||
],
|
||
tones=[[None, None, None, None, "good"], [None, None, None, None, "good"],
|
||
[None, None, None, None, "good"], [None, None, None, None, "warning"],
|
||
[None, None, None, None, "critical"], [None, None, None, None, "info"]],
|
||
row_h=0.62,
|
||
)
|
||
Rail.title("읽는 법").bullets([
|
||
"획득 분모는 검증 항목의 배점 합. E는 미검증 3개라 분모가 2점뿐.",
|
||
"총점 60 / 88 = 68점, 등급 B. 뷰성형외과 48점과 20점 차이.",
|
||
"A가 88%인 이유: 마약 사이트는 열려 있지만 메인 도메인 WAF 차단으로 A2 감점.",
|
||
])
|
||
|
||
# 05 A 접근성 (dark)
|
||
s = d.slide(eyebrow="A · Access & Crawlability · 88%",
|
||
title="마약 사이트는 열려 있고, ", title_accent="메인은 막혀 있다", dark=True, section="A · Access")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("taehadrug.com · 정상", "good").rows([
|
||
("A1 검색용 AI 봇 허용", "robots.txt User-agent * Allow /. 12종 UA로 실제 GET, 전부 200."),
|
||
("A4 · A5 정합·sitemap", "http·www 변형 전부 301 수렴. sitemap 326개 URL, lastmod 2026-08-06."),
|
||
("A7 속도·무게", "TTFB 0.31s, 초기 HTML 274KB. 기준 300KB 이내."),
|
||
])
|
||
R.chip("taehalaw.com · 차단", "critical").rows([
|
||
("A2 WAF가 AI 검색봇 차단", "GPTBot → errdoc.gabia.io/403.html, OAI-SearchBot → modsecurity.html로 302."),
|
||
("결과", "ChatGPT 검색은 \"법무법인 태하\" 메인 사이트를 읽지 못한다. Perplexity는 읽는다."),
|
||
("A6 Bing 색인 (미검증)", "ChatGPT 검색은 Bing 인덱스에 의존. Webmaster 등록 여부는 계정 확인 필요."),
|
||
])
|
||
|
||
# 06 B 엔티티
|
||
s = d.slide(eyebrow="B · Entity & Structured Data · 68%",
|
||
title="스키마는 좋은데 ", title_accent="도메인끼리 서로 모른다", section="B · Entity")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("우수", "info").rows([
|
||
("B1 JSON-LD 전부 유효", "Organization·WebSite·LegalService×4·WebPage·Breadcrumb·FAQPage가 @graph로 연결."),
|
||
("B5 sameAs 6개", "네이버 블로그·Instagram·YouTube·카카오채널·네이버 지도·Google 지도."),
|
||
("B2 · B3 LegalService·Attorney", "address·geo·areaServed 있음. Attorney 2명 name·jobTitle·knowsAbout."),
|
||
])
|
||
R.chip("보완", "warning").rows([
|
||
("B6 canonical 오류", "http:// 지정. 성공사례 canonical /success-story/ vs 실제 /success/success_story/."),
|
||
("B3 변호사 6명 중 2명만 구조화", "전관 경력(지청장·판사·부장검사)이 스키마에 없음. openingHours 없음."),
|
||
("B4 전화 2종, B7 단일 언어", "taehadrug 1533-2403 / taehalaw 1533-1403. hreflang 없음."),
|
||
])
|
||
|
||
# 07 C 콘텐츠 (dark)
|
||
s = d.slide(eyebrow="C · Answer-Ready Content · 65%",
|
||
title="FAQ와 사례는 있고, ", title_accent="정의문과 표가 없다", dark=True, section="C · Content")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("우수", "good").rows([
|
||
("C2 FAQ 14문항 + FAQPage", "\"마약 양이 적어도 구속될 수 있어요?\" 등 의뢰인 질문 그대로 H3."),
|
||
("C7 전관 경력 텍스트", "지청장·판사·부장검사 역임이 H3로 노출. Attorney 스키마 병행."),
|
||
("C8 · C6 사례 자기완결·갱신", "성공사례 H2가 [결과] 사건 유형 형태. sitemap 3주 전 갱신."),
|
||
])
|
||
R.chip("보완", "warning").rows([
|
||
("C1 정의문 없음", "H1 \"마약 사건엔 태하가 답입니다\"는 슬로건. 누구·어디·무엇 정의문 없음."),
|
||
("C5 HTML 표 없음", "\"마약사건 처벌현황\"이 표가 아닌 이미지·블록. 형량 비교표가 있어야 인용된다."),
|
||
("C4 · C3 수치·제목", "수임료 범위·처리 기간 수치 없음. 첫 H2가 \"#필로폰 #MDMA\" 해시태그."),
|
||
])
|
||
|
||
# 08 D 표면 / E 측정
|
||
s = d.slide(eyebrow="D · Surfaces / E · Measurement",
|
||
title="광고는 1위, ", title_accent="브랜드 검색은 경쟁사 차지", section="D · Surfaces")
|
||
L, R = s.cards2(y=s.band_y)
|
||
L.chip("D · 표면 56%", "warning").rows([
|
||
("D4 전화번호 4종", "1533-2403 / 1533-1403 / 0507-1495-4404 / 050-71496-4404. NAP가 안 맞는다."),
|
||
("D2 GBP 본점 평점 미표시", "수원분사무소는 5.0. 브랜드 검색에 법무법인YK·로엘 스폰서가 상단 노출."),
|
||
("D1 · D5 로톡·플레이스", "로톡은 변호사 개인 프로필만 확인. 플레이스 리뷰 220건, 분사무소 5곳."),
|
||
])
|
||
R.chip("E · 측정 0%", "critical").rows([
|
||
("E1 크롤러 로그", "Apache 직접 호스팅. AI 봇 히트 추출과 IP 검증 파이프라인 없음."),
|
||
("E2 GA4 AI 유입 채널", "광고에 utm_source=naver는 쓰고 있음. AI 채널그룹은 미확인, 계정 접근 필요."),
|
||
("E4 기준선 스팟체크", "미실시. \"마약 변호사\", \"필로폰 초범 집행유예\" 질의 10개 × 5회가 전제."),
|
||
])
|
||
|
||
# 09 Action plan
|
||
s = d.slide(eyebrow="Action Plan",
|
||
title="1일 안에 고칠 것과 ", title_accent="4주 안에 만들 것", section="Action Plan")
|
||
a, b, c = s.cards3(y=s.band_y)
|
||
a.chip("P0 · 1일~1주", "critical").title("접근·엔티티·NAP").bullets([
|
||
"가비아 WAF에서 GPTBot·OAI-SearchBot·Claude-SearchBot 화이트리스트.",
|
||
"canonical https 교정, 6개 도메인 sameAs 상호 연결.",
|
||
"대표번호 1개로 홈페이지 6개·플레이스·Google 지도 통일.",
|
||
"메인 taehalaw.com에 LegalService + Attorney 전원 스키마.",
|
||
])
|
||
b.chip("P1 · 4주 내", "warning").title("콘텐츠 구조·측정").bullets([
|
||
"각 도메인 첫 단락에 정의문. 메인 H1을 서술형으로.",
|
||
"성공사례 결과 분포·처리 기간을 HTML 표로.",
|
||
"GA4 AI 채널그룹, 서버로그 IP 검증, GSC·Bing 등록, 기준선 측정.",
|
||
])
|
||
c.chip("P2 · 지속", "good").title("신선도·시딩").bullets([
|
||
"성공사례에 datePublished·dateModified 마크업.",
|
||
"YouTube @with_lawyer 영상을 사이트 글로 전사.",
|
||
"티스토리·언론 기고로 naver.com 외 언급 도메인 확보.",
|
||
"분기 1회 반복 측정. 언급률과 신뢰구간으로만 보고.",
|
||
])
|
||
|
||
# 10 Roadmap (dark)
|
||
s = d.slide(eyebrow="Roadmap · 8 Weeks",
|
||
title="8주 안에 ", title_accent="B에서 A로", dark=True, section="Roadmap")
|
||
s.stats3([
|
||
("W1–2", "WAF 해제·canonical·sameAs·NAP 통일.\n기준선 스팟체크 1회."),
|
||
("W3–6", "정의문·HTML 표·메인 H1 재설계.\n측정 인프라 설치."),
|
||
("W7–8", "재진단. 미검증 5개 반영해\n총점 재산정."),
|
||
], y=s.band_y + 0.6)
|
||
|
||
# 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%, 서버 로그에 요청 자체가 없음.",
|
||
"도메인 통합 강요. 분야별 랜딩은 광고 전환에 유리하다. sameAs·NAP로 묶는 쪽이 싸다.",
|
||
])
|
||
b.icon_square().title("약속하지 않는 것").bullets([
|
||
"\"스키마 넣으면 인용됩니다\". 태하는 스키마가 좋은데도 ChatGPT가 메인을 못 읽는다.",
|
||
"순위·노출 보장. LLM 답변은 동일 질문에도 10~34% 흔들림.",
|
||
])
|
||
c.icon_square().title("측정 규율").bullets([
|
||
"단발 조회로 성과 보고 금지. 반복 측정과 신뢰구간만.",
|
||
"답변엔진 UI 자동 조회 금지. OpenAI·Perplexity 약관 위반.",
|
||
"AI 유입은 항상 하한값. 35~70%가 Direct로 유실.",
|
||
])
|
||
|
||
# 12 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("채점 방식").bullets([
|
||
"항목별 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 이하.",
|
||
])
|
||
b.icon_square().title("법률 업종 적용").bullets([
|
||
"업종 스키마: LegalService · 전문가 스키마: Attorney.",
|
||
"버티컬 표면: 로톡·로앤굿 (병의원의 강남언니 자리).",
|
||
"E-E-A-T: 전관 경력·전문분야 등록을 텍스트로.",
|
||
])
|
||
c.icon_square().title("항목마다 붙는 태그").bullets([
|
||
"근거 등급: 실측 / 학술 / 벤더 / 추정.",
|
||
"통제 가능성: 직접 / 영향 / 불가.",
|
||
"채점 방식: 자동 / 반자동 / 수동. 자동 항목은 Edge Function으로 이식.",
|
||
])
|
||
|
||
# 13 Closing
|
||
d.closing(eyebrow="Next Step",
|
||
title_lines=["WAF 한 줄이 ", "총점을 가르고 있습니다."],
|
||
lede="메인 도메인에서 AI 검색봇을 허용하는 데 1일. 그 다음 canonical·sameAs·전화번호 통일까지 1주.",
|
||
cta_primary="P0 착수하기 →", cta_secondary="리포트 페이지 보기",
|
||
contact="infinith.kr · hello@infinith.kr")
|
||
|
||
d.save(OUT)
|
||
print("saved", OUT)
|