diff --git a/docs/reports/README.md b/docs/reports/README.md index 9bb97f1..d439fed 100644 --- a/docs/reports/README.md +++ b/docs/reports/README.md @@ -12,6 +12,7 @@ docs/reports/ │ └── 05_global_en/ 영문 사이트(viewplasticsurgery.com) 외국인 관점 AEO/GEO 진단 (2026-09-10) · 영문 60문항 뱅크 · Perplexity/OpenAI(뉴욕) 실측 · HTML/PDF 보고서 + 빌더 ├── taeha/ 태하 AI Answer Readiness 리포트 (html · pdf · pptx) + 빌더 ├── ibk/ 기업은행 블로그 AEO/GEO 감사 +├── answer_engines/ 답변엔진 확장 검토 보고서(.docx, 120문항 비용 추정 · 후보별 약관 검토) + 빌더 └── landing/ AI Discovery 랜딩 시안 (픽셀 정답 HTML) · 스크린샷 · 리포트 페이지 HTML 내보내기 스크립트 ``` diff --git a/docs/reports/answer_engines/INFINITH_답변엔진_확장검토_2026-09-15.docx b/docs/reports/answer_engines/INFINITH_답변엔진_확장검토_2026-09-15.docx new file mode 100644 index 0000000..00e61b0 Binary files /dev/null and b/docs/reports/answer_engines/INFINITH_답변엔진_확장검토_2026-09-15.docx differ diff --git a/docs/reports/answer_engines/build_engine_review_docx.py b/docs/reports/answer_engines/build_engine_review_docx.py new file mode 100644 index 0000000..27346a8 --- /dev/null +++ b/docs/reports/answer_engines/build_engine_review_docx.py @@ -0,0 +1,367 @@ +#!/usr/bin/env python3 +"""답변엔진 확장 검토 보고서(.docx) 빌더. + +질문 뱅크 120문항 기준으로 후보 답변엔진별 비용을 추정하고, 약관 검토 결과와 함께 워드 문서로 낸다. +단가는 2026-09-15 각사 공식 가격표 확인값. 토큰량은 가정이며 4장 표에 근거를 적는다. + +사용: + python3 docs/reports/answer_engines/build_engine_review_docx.py +""" +from pathlib import Path + +from docx import Document +from docx.enum.table import WD_TABLE_ALIGNMENT +from docx.enum.text import WD_ALIGN_PARAGRAPH +from docx.oxml import OxmlElement +from docx.oxml.ns import qn +from docx.shared import Cm, Pt, RGBColor + +OUT = Path(__file__).with_name("INFINITH_답변엔진_확장검토_2026-09-15.docx") + +INDIGO = RGBColor(0x1D, 0x00, 0x24) +VIOLET = RGBColor(0x4F, 0x1D, 0xA1) +GRAY = RGBColor(0x55, 0x55, 0x60) +FONT = "Pretendard" + +KRW = 1345 # 2026-09-13 USD/KRW 1,343.56 기준 반올림 +QUESTIONS = 120 +REPEAT = 5 + +M = 1_000_000 + +# ── 후처리 단가(답변 1건당): 병원명 추출 gpt-4o-mini + 어조 판정 gpt-4.1 ── +def post_cost(level): + rubric_in = {"low": 4000, "mid": 5000, "high": 6000}[level] + answer_in = {"low": 600, "mid": 1000, "high": 2900}[level] + extract = (500 + answer_in) * 0.15 / M + 150 * 0.60 / M + judge = (rubric_in + answer_in) * 2.00 / M + 300 * 8.00 / M + return extract + judge + + +# ── 엔진별 1문항 1회 호출 비용 모델 ── +# searches: 호출당 검색 횟수, search_fee: 검색 1회 단가, tin/tout: 입력·출력 토큰, pin/pout: 1M 토큰 단가 +ENGINES = [ + dict(key="gpt4o", name="ChatGPT (현행)", setup="gpt-4o + web_search", status="운영 중", + search_fee=0.025, pin=2.50, pout=10.00, + low=dict(searches=1, tin=300, tout=400), mid=dict(searches=1, tin=300, tout=1000), high=dict(searches=1, tin=300, tout=2900), + note="비추론 모델은 검색 호출 $25/1천 회, 검색 본문 토큰 무료. 출력은 실측 평균 962자·상위 10% 2,850자."), + dict(key="gpt56", name="ChatGPT (최신화)", setup="GPT-5.6 Sol + web_search", status="모델 교체안", + search_fee=0.010, pin=4.00, pout=20.00, + low=dict(searches=1, tin=8000, tout=1500), mid=dict(searches=2, tin=20000, tout=3000), high=dict(searches=3, tin=40000, tout=5000), + note="추론 모델은 검색 호출 $10/1천 회, 검색 본문 토큰을 모델 단가로 과금. 출력에 추론 토큰 포함."), + dict(key="sonar", name="Perplexity (현행)", setup="sonar, 검색 컨텍스트 low", status="운영 중", + search_fee=0.005, pin=1.00, pout=1.00, + low=dict(searches=1, tin=50, tout=400), mid=dict(searches=1, tin=50, tout=650), high=dict(searches=1, tin=50, tout=1000), + note="요청 수수료 $5/1천 회(low). 출력은 실측 평균 618자."), + dict(key="sonarpro", name="Perplexity (상위)", setup="sonar-pro, 검색 컨텍스트 medium", status="모델 교체안", + search_fee=0.010, pin=3.00, pout=15.00, + low=dict(searches=1, tin=50, tout=700), mid=dict(searches=1, tin=50, tout=1000), high=dict(searches=1, tin=50, tout=1400), + note="요청 수수료 $10/1천 회(medium)."), + dict(key="sonnet5", name="Claude", setup="Claude Sonnet 5 + web_search", status="추가 권고", + search_fee=0.010, pin=2.00, pout=10.00, + low=dict(searches=1, tin=12000, tout=800), mid=dict(searches=2, tin=30000, tout=1500), high=dict(searches=3, tin=60000, tout=3000), + note="검색 $10/1천 회. 검색 결과가 입력 토큰으로 과금되어 입력 토큰이 비용의 대부분."), + dict(key="haiku45", name="Claude (저가)", setup="Claude Haiku 4.5 + web_search", status="대안", + search_fee=0.010, pin=1.00, pout=5.00, + low=dict(searches=1, tin=12000, tout=800), mid=dict(searches=2, tin=30000, tout=1500), high=dict(searches=3, tin=60000, tout=3000), + note="Sonnet 5와 같은 토큰량 가정. 동적 필터링(입력 절감)은 4.6 이상 모델만 지원."), + dict(key="gemini", name="Gemini", setup="Gemini 3.8 Flash + Google 검색 그라운딩", status="약관 확인 전 보류", + search_fee=0.0, pin=0.75, pout=3.75, + low=dict(searches=1, tin=300, tout=1000), mid=dict(searches=2, tin=300, tout=2000), high=dict(searches=3, tin=300, tout=4000), + note="그라운딩 월 5,000회 무료(3.x 공통), 초과 시 $14/1천 회. 120문항 5회 반복은 최대 1,800회라 무료 범위 안."), + dict(key="grok", name="Grok", setup="Grok 4.3 + web search", status="후순위", + search_fee=0.005, pin=1.25, pout=2.50, + low=dict(searches=1, tin=8000, tout=800), mid=dict(searches=2, tin=20000, tout=1500), high=dict(searches=3, tin=40000, tout=3000), + note="검색 $5/1천 회는 제3자 정리 기준. 공식 가격표 재확인 필요."), + dict(key="bing", name="Copilot 계열", setup="Azure Foundry + Grounding with Bing + gpt-4o", status="제외 (약관)", + search_fee=0.014, pin=2.50, pout=10.00, + low=dict(searches=1, tin=5000, tout=400), mid=dict(searches=1, tin=10000, tout=1000), high=dict(searches=2, tin=20000, tout=2900), + note="참고용 산정. 결과 저장·데이터베이스화 금지 조항으로 측정에 쓸 수 없음."), + dict(key="aio", name="Google AI Mode", setup="DataForSEO AI Mode SERP API", status="결정 필요", + search_fee=0.004, pin=0.0, pout=0.0, + low=dict(searches=1, tin=0, tout=0), mid=dict(searches=1, tin=0, tout=0), high=dict(searches=1, tin=0, tout=0), + note="Live $0.004, Standard 대기열 $0.0012 / 요청. 모델 토큰 없음. 소비자 화면을 제3자가 수집하는 방식."), +] + + +def call_cost(e, level): + p = e[level] + return p["searches"] * e["search_fee"] + p["tin"] * e["pin"] / M + p["tout"] * e["pout"] / M + + +def per_question(e, level, with_post=True): + return call_cost(e, level) + (post_cost(level) if with_post else 0) + + +def usd(x): + return f"${x:,.2f}" if x >= 0.995 else f"${x:.3f}" + + +def won(x): + return f"₩{round(x * KRW, -1):,.0f}" + + +# ── 문서 헬퍼 ── +def set_font(run, size=11, bold=False, color=INDIGO): + run.font.name = FONT + run.font.size = Pt(size) + run.font.bold = bold + run.font.color.rgb = color + rpr = run._element.get_or_add_rPr() + rfonts = rpr.find(qn("w:rFonts")) + if rfonts is None: + rfonts = OxmlElement("w:rFonts") + rpr.append(rfonts) + for attr in ("w:ascii", "w:hAnsi", "w:cs", "w:eastAsia"): + rfonts.set(qn(attr), FONT) + + +def para(doc, text, size=11, bold=False, color=INDIGO, space_after=6, align=None): + p = doc.add_paragraph() + p.paragraph_format.space_after = Pt(space_after) + p.paragraph_format.line_spacing = 1.35 + if align: + p.alignment = align + set_font(p.add_run(text), size, bold, color) + return p + + +def heading(doc, text, level=1): + size = {0: 24, 1: 16, 2: 13}[level] + p = doc.add_paragraph() + p.paragraph_format.space_before = Pt(18 if level == 1 else 10) + p.paragraph_format.space_after = Pt(8) + p.paragraph_format.keep_with_next = True + set_font(p.add_run(text), size, True, INDIGO if level != 2 else VIOLET) + return p + + +def bullets(doc, items, size=11): + for t in items: + p = doc.add_paragraph(style="List Bullet") + p.paragraph_format.space_after = Pt(3) + p.paragraph_format.line_spacing = 1.3 + if isinstance(t, tuple): + set_font(p.add_run(t[0]), size, True) + set_font(p.add_run(t[1]), size) + else: + set_font(p.add_run(t), size) + + +def shade(cell, hex_fill): + tcpr = cell._element.get_or_add_tcPr() + shd = OxmlElement("w:shd") + shd.set(qn("w:val"), "clear") + shd.set(qn("w:color"), "auto") + shd.set(qn("w:fill"), hex_fill) + tcpr.append(shd) + + +def table(doc, header, rows, widths=None, size=9.5, bold_first_col=True, highlight_rows=()): + t = doc.add_table(rows=1, cols=len(header)) + t.style = "Table Grid" + t.alignment = WD_TABLE_ALIGNMENT.CENTER + for i, h in enumerate(header): + c = t.rows[0].cells[i] + c.text = "" + set_font(c.paragraphs[0].add_run(h), size, True, RGBColor(0xFF, 0xFF, 0xFF)) + shade(c, "1D0024") + trpr = t.rows[0]._tr.get_or_add_trPr() + for tag in ("w:tblHeader", "w:cantSplit"): + el = OxmlElement(tag) + el.set(qn("w:val"), "true") + trpr.append(el) + for ri, r in enumerate(rows): + cells = t.add_row().cells + for i, v in enumerate(r): + cells[i].text = "" + bold = (i == 0 and bold_first_col) or ri in highlight_rows + set_font(cells[i].paragraphs[0].add_run(str(v)), size, bold) + if ri in highlight_rows: + shade(cells[i], "F1ECFA") + if widths: + t.autofit = False + for i, w in enumerate(widths): + t.columns[i].width = Cm(w) + for row in t.rows: + for i, w in enumerate(widths): + row.cells[i].width = Cm(w) + doc.add_paragraph().paragraph_format.space_after = Pt(2) + return t + + +def build(): + E = {e["key"]: e for e in ENGINES} + doc = Document() + sec = doc.sections[0] + sec.page_width, sec.page_height = Cm(21.0), Cm(29.7) + sec.left_margin = sec.right_margin = Cm(2.0) + sec.top_margin = sec.bottom_margin = Cm(2.0) + + # 표지 블록 + para(doc, "INFINITH AI Discovery", 11, True, VIOLET, 4) + heading(doc, "답변엔진 확장 검토 보고서", 0) + para(doc, "질문 뱅크 120문항 기준 비용 추정과 후보 엔진별 약관 검토", 13, False, INDIGO, 4) + para(doc, "작성일 2026-09-15 · 단가 확인일 2026-09-15 · 환율 1 USD = ₩1,345 가정", 10, False, GRAY, 14) + + # 1. 요약 + heading(doc, "1. 요약") + gpt4o, sonar, sonnet, gem = E["gpt4o"], E["sonar"], E["sonnet5"], E["gemini"] + cur_q = per_question(gpt4o, "mid") + per_question(sonar, "mid") + add_claude_q = per_question(sonnet, "mid") + bullets(doc, [ + ("현재 실측 엔진은 2개입니다. ", "ChatGPT(gpt-4o + 웹 검색)와 Perplexity(sonar)입니다. 36개 진단 항목은 AI 모델 없이 규칙 검사로 판정합니다."), + ("바로 추가할 수 있는 엔진은 Claude입니다. ", "공식 API에 웹 검색 도구가 있고, 결과 저장·분석을 막는 조항을 확인하지 못했습니다."), + ("Gemini와 Copilot은 약관이 측정 용도와 충돌합니다. ", "Gemini는 그라운딩 결과의 분석·자동 수집을, Bing 그라운딩은 결과 저장·데이터베이스화를 금지합니다."), + ("비용은 크지 않습니다. ", + f"현행 2개 엔진의 120문항 1회 측정은 약 {usd(cur_q * QUESTIONS)}({won(cur_q * QUESTIONS)})이고, " + f"Claude를 더하면 1회 약 {usd(add_claude_q * QUESTIONS)}({won(add_claude_q * QUESTIONS)})가 늘어납니다. 모두 중앙 추정치이며 후처리(병원명 추출·어조 판정) 비용을 포함합니다."), + ("네이버 AI 브리핑은 측정 API가 없습니다. ", "수동 스팟체크를 유지합니다."), + ]) + + # 2. 현재 구성 + heading(doc, "2. 현재 측정 구성") + table(doc, ["역할", "모델 · 방식", "코드 위치"], [ + ["답변엔진 실측 ①", "OpenAI gpt-4o + Responses API web_search (서울 고정, 영문판 뉴욕)", "scripts/run_question_bank_openai.py"], + ["답변엔진 실측 ②", "Perplexity sonar (웹 검색 내장)", "scripts/run_question_bank_openai.py"], + ["병원명·순위 추출", "OpenAI gpt-4o-mini (키 없으면 Gemini 2.5 Flash)", "scripts/run_question_bank_openai.py"], + ["어조 판정", "OpenAI gpt-4.1, temperature 0", "scripts/sentiment_qb.py"], + ["36개 항목 진단", "AI 모델 미사용. 크롤링 규칙 검사 + Wikidata 조회", "scripts/audit_aeo_geo.py"], + ], widths=[3.2, 8.6, 5.2]) + para(doc, "측정은 공식 API 답변 기준입니다. 소비자 화면(chatgpt.com 등) 자동 조회는 약관상 하지 않으므로, API 답변과 화면 답변은 다를 수 있습니다.", 10, False, GRAY) + + # 3. 후보별 검토 + heading(doc, "3. 후보 엔진별 검토") + table(doc, ["엔진", "공식 API", "약관상 측정 가능 여부", "판정"], [ + ["Claude", "Messages API web_search 도구. user_location으로 도시·국가 지정", "저장·분석 금지 조항 미확인. 최종 사용자에게 보여줄 때 출처 표시 필요", "추가 권고"], + ["Gemini", "Grounding with Google Search. GEMINI_API_KEY 보유", "그라운딩 결과의 cache·analyze·자동 수집 금지. 질문한 사용자에게 보여주는 용도만 허용", "법무 확인 전 보류"], + ["Copilot 계열", "Azure Foundry Grounding with Bing Search", "결과의 store·cache·archive·데이터베이스화 금지. 검색 경험 앱에서만 사용", "제외"], + ["Grok", "xAI API web search 도구", "저장 금지 조항 별도 확인 안 함", "후순위 (국내 이용 비중 낮음)"], + ["Google AI Overview · AI Mode", "Google 공식 API 없음. DataForSEO·SerpApi가 결과 화면을 수집해 제공", "제3자가 소비자 화면을 수집하는 방식. 현재 수집 원칙과 불일치", "haewon 결정 필요"], + ["네이버 AI 브리핑", "없음. CLOVA Studio는 모델 API이며 브리핑 화면을 재현하지 못함", "해당 없음", "제외, 수동 스팟체크"], + ["DeepSeek · 뤼튼 · 라이너", "웹 검색이 붙은 공식 측정 API 미확인", "해당 없음", "제외"], + ], widths=[3.0, 5.2, 6.0, 2.8]) + + heading(doc, "3.1 엔진별 판단 근거", 2) + bullets(doc, [ + ("Claude. ", "검색 1,000회당 $10에 검색 결과가 입력 토큰으로 과금됩니다. 응답에 인용 URL과 인용 문장(최대 150자)이 구조화되어 와서 현재 추출 로직에 붙이기 쉽습니다. 국내 병원 고객의 이용 비중은 ChatGPT보다 낮습니다."), + ("Gemini. ", "기술 난이도는 가장 낮고 국내 비중은 Claude보다 높습니다. 다만 Gemini API 추가 약관이 Grounded Results를 \"cache, frame, syndicate, resell, analyze\" 하는 것을 금지하고, 자동 수집을 허용하지 않습니다. Vertex AI 경로의 약관이 다른지와, 진단을 요청한 고객에게 결과를 보여주는 용도로 인정되는지 법무 판단이 필요합니다."), + ("Copilot 계열. ", "Bing 그라운딩 약관이 결과를 \"Copy, store, cache, archive, or create a database of Output\" 하는 것을 금지합니다. 질문 뱅크 결과를 누적하는 방식과 양립하지 않습니다."), + ("Google AI Overview · AI Mode. ", "병원 검색에서 노출 비중이 큰 화면이고 비용도 가장 낮습니다. 대신 Google 검색 결과를 제3자 도구로 수집하는 구조라, \"소비자 화면을 자동 조회하지 않는다\"는 현재 원칙을 바꾸는 결정입니다."), + ("기존 엔진 모델 점검. ", "ChatGPT 소비자 화면은 gpt-4o보다 새 모델로 답합니다. 측정 모델을 최신 추론 모델로 바꾸면 화면 답변과의 차이가 줄지만, 문항당 비용이 약 3.5배 늘어납니다(5장 표)."), + ]) + + # 4. 비용 산정 기준 + heading(doc, "4. 비용 산정 기준") + table(doc, ["항목", "가정", "근거"], [ + ["문항 수", "120문항", "질문 뱅크 v0.1"], + ["반복", "1회, 5회 반복 두 가지", "답변 변동을 신뢰구간으로 보고하려면 5~7회 반복 필요"], + ["답변 길이", "ChatGPT 평균 962자(상위 10% 2,850자), Perplexity 평균 618자", "scripts/out 실측 결과 240건"], + ["한국어 토큰 환산", "1자 ≈ 1토큰 (보수적)", "모델별 토크나이저 차이는 범위(최소·최대)로 흡수"], + ["검색 결과 입력 토큰", "Claude 1.2만~6만, OpenAI 추론 모델 0.8만~4만, Grok 0.8만~4만", "검색 1~3회 × 결과 본문. 실측 전 가정"], + ["후처리", f"답변 1건당 약 {usd(post_cost('mid'))}", "gpt-4o-mini 추출 + gpt-4.1 어조 판정(기준표 프롬프트 약 5천 토큰)"], + ["환율", "1 USD = ₩1,345", "2026-09-13 USD/KRW 1,343.56"], + ["할인", "미적용", "Batch API 50% 할인은 적용 시 토큰 비용만 절감"], + ], widths=[3.2, 7.3, 6.5]) + + # 5. 엔진별 비용 + heading(doc, "5. 120문항 비용 추정 (엔진별)") + para(doc, "문항당 단가는 엔진 호출 1회와 후처리 1건을 합한 값입니다. 범위는 최소·최대 가정, 굵은 값은 중앙 추정입니다.", 10, False, GRAY) + rows = [] + for e in ENGINES: + lo, mid, hi = (per_question(e, lv) for lv in ("low", "mid", "high")) + rows.append([ + e["name"], e["setup"], e["status"], usd(mid), + f"{usd(mid * QUESTIONS)}\n{won(mid * QUESTIONS)}", + f"{usd(mid * QUESTIONS * REPEAT)}\n{won(mid * QUESTIONS * REPEAT)}", + f"{usd(lo * QUESTIONS * REPEAT)} ~ {usd(hi * QUESTIONS * REPEAT)}", + ]) + table(doc, ["엔진", "구성", "상태", "문항당", "120문항 1회", "120문항 5회", "5회 범위"], rows, + widths=[2.4, 3.9, 2.2, 1.6, 2.3, 2.3, 2.6], size=9, highlight_rows=(0, 2, 4)) + bullets(doc, [ + "Gemini 행은 그라운딩 무료 범위(월 5,000회) 안에서 계산했습니다. 무료 범위를 넘으면 검색 1,000회당 $14가 더해져 5회 반복 기준 최대 " + f"{usd(1800 * 0.014)}가 늘어납니다.", + "Copilot 계열은 약관상 사용할 수 없어 참고용으로만 적었습니다.", + "Google AI Mode는 모델 호출이 없어 비용 대부분이 후처리입니다. Standard 대기열($0.0012)을 쓰면 수집 비용이 70% 줄어듭니다.", + ], size=10) + heading(doc, "5.1 엔진별 산정 메모", 2) + table(doc, ["엔진", "과금 구조"], [[e["name"], e["note"]] for e in ENGINES], widths=[3.2, 13.8], size=9) + + # 6. 구성안 + heading(doc, "6. 구성안별 합계") + plans = [ + ("A. 현행 유지", ["gpt4o", "sonar"], "현재 운영 구성"), + ("B. 현행 + Claude", ["gpt4o", "sonar", "sonnet5"], "권고안. 랜딩에 ChatGPT·Perplexity·Claude 표기 가능"), + ("C. 모델 최신화 + Claude", ["gpt56", "sonarpro", "sonnet5"], "소비자 화면과의 차이를 줄이는 안"), + ("D. B + Gemini", ["gpt4o", "sonar", "sonnet5", "gemini"], "Gemini 약관 확인이 끝난 경우"), + ("E. D + Google AI Mode", ["gpt4o", "sonar", "sonnet5", "gemini", "aio"], "수집 원칙 변경을 결정한 경우"), + ] + prow = [] + for name, keys, desc in plans: + lo = sum(per_question(E[k], "low") for k in keys) + mid = sum(per_question(E[k], "mid") for k in keys) + hi = sum(per_question(E[k], "high") for k in keys) + prow.append([name, f"{len(keys)}개", desc, + f"{usd(mid * QUESTIONS)}\n{won(mid * QUESTIONS)}", + f"{usd(mid * QUESTIONS * REPEAT)}\n{won(mid * QUESTIONS * REPEAT)}", + f"{won(lo * QUESTIONS * REPEAT)} ~ {won(hi * QUESTIONS * REPEAT)}"]) + table(doc, ["구성안", "엔진", "설명", "120문항 1회", "120문항 5회", "5회 범위(원)"], prow, + widths=[3.2, 1.3, 4.8, 2.5, 2.5, 3.0], size=9, highlight_rows=(1,)) + para(doc, "병원 1곳을 월 1회, 5회 반복으로 측정하면 월 비용은 \"120문항 5회\" 열과 같습니다. 병원 수에 비례해 늘어납니다.", 10, False, GRAY) + + # 7. 권고 + heading(doc, "7. 권고와 결정 필요 사항") + heading(doc, "7.1 권고 순서", 2) + bullets(doc, [ + ("1단계. Claude 엔진 추가 (구성안 B). ", "run_question_bank_openai.py에 엔진 클래스를 하나 추가합니다. 10문항 시험 실행으로 실제 토큰량을 확인한 뒤 120문항을 돌립니다."), + ("2단계. 기존 엔진 모델 점검. ", "10문항을 gpt-4o와 최신 추론 모델로 나란히 돌려 병원 언급률 차이를 봅니다. 차이가 크면 구성안 C로 올립니다."), + ("3단계. Gemini 약관 확인. ", "Vertex AI 약관과 AI Studio 약관을 비교하고 법무 판단을 받습니다."), + ("4단계. Google AI Mode 수집 여부 결정. ", "수집 원칙 변경이 필요합니다."), + ]) + heading(doc, "7.2 haewon 결정 필요", 2) + table(doc, ["번호", "결정 사항", "선택지"], [ + ["1", "Claude 엔진 추가와 10문항 시험 실행", "진행 / 보류"], + ["2", "측정 모델 최신화 시험 (gpt-4o 대비 최신 모델)", "진행 / 보류"], + ["3", "Gemini 약관 법무 검토 요청", "요청 / 보류"], + ["4", "Google AI Overview · AI Mode 제3자 수집 허용", "허용 / 불허"], + ["5", "Anthropic API 회사 키 발급", "발급 / 기존 키 사용"], + ], widths=[1.4, 10.0, 5.6]) + + # 8. 한계 + heading(doc, "8. 이 추정의 한계") + bullets(doc, [ + "검색 결과 입력 토큰은 실측 전 가정입니다. Claude·OpenAI 추론 모델·Grok 비용은 이 값에 따라 2배 안팎 달라질 수 있습니다. 10문항 시험 실행 후 5장 표를 갱신합니다.", + "Grok 검색 단가는 제3자 정리 기준이고, 약관 조항은 요약본을 읽은 것입니다. 도입 판단 전에 원문을 다시 확인해야 합니다.", + "Gemini 3.x Flash 할인 단가는 2026-12-31까지이며, 2027-01-01부터 입력·출력 단가가 2배로 오릅니다.", + "API 답변은 각 서비스의 소비자 앱 답변과 같지 않습니다. 엔진을 늘려도 보고서에는 \"공식 API 답변 기준\"으로 표기해야 합니다.", + "환율 변동과 부가세는 반영하지 않았습니다.", + ], size=10) + + # 9. 출처 + heading(doc, "9. 출처 (2026-09-15 확인)") + for label, url in [ + ("OpenAI API Pricing", "https://developers.openai.com/api/docs/pricing"), + ("Perplexity Sonar Pricing", "https://docs.perplexity.ai/getting-started/pricing"), + ("Claude Pricing", "https://platform.claude.com/docs/en/about-claude/pricing"), + ("Claude Web search tool", "https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool"), + ("Gemini API Pricing", "https://ai.google.dev/gemini-api/docs/pricing"), + ("Gemini API Additional Terms", "https://ai.google.dev/gemini-api/terms"), + ("Grounding with Bing pricing", "https://www.microsoft.com/en-us/bing/apis/grounding-pricing"), + ("Grounding with Bing legal", "https://www.microsoft.com/en-us/bing/apis/grounding-legal"), + ("xAI models", "https://docs.x.ai/docs/models"), + ("xAI Grok API pricing (제3자 정리)", "https://mem0.ai/blog/xai-grok-api-pricing"), + ("DataForSEO Google AI Mode SERP API", "https://dataforseo.com/pricing/google-serp/google-ai-mode-serp-api"), + ("CLOVA Studio API 가이드", "https://api.ncloud-docs.com/docs/ai-naver-clovastudio-summary"), + ("원·달러 환율 (2026-09-13)", "https://ko.tradingeconomics.com/south-korea/currency"), + ]: + p = doc.add_paragraph() + p.paragraph_format.space_after = Pt(2) + set_font(p.add_run(f"{label} "), 9.5, True) + set_font(p.add_run(url), 9.5, False, GRAY) + + doc.save(OUT) + print(OUT) + + +if __name__ == "__main__": + build() diff --git a/docs/reports/answer_engines/~$FINITH_답변엔진_확장검토_2026-09-15.docx b/docs/reports/answer_engines/~$FINITH_답변엔진_확장검토_2026-09-15.docx new file mode 100644 index 0000000..fd5ab39 Binary files /dev/null and b/docs/reports/answer_engines/~$FINITH_답변엔진_확장검토_2026-09-15.docx differ diff --git a/supporters/public/favicon.svg b/supporters/public/favicon.svg deleted file mode 100644 index f157bd1..0000000 --- a/supporters/public/favicon.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/templates/supporters-astro/public/favicon.svg b/templates/supporters-astro/public/favicon.svg deleted file mode 100644 index f157bd1..0000000 --- a/templates/supporters-astro/public/favicon.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - -