Rebuild estimate engine to effort-based SOW model
Replace flat per-service ranges with OurDigital's real quoting model (role_rate × billing_rate 0.70 × standard hours), sourced from the 06_Working Template quotes: - rate_card.yaml: role rate card, billing/basis/terms, tools, scaling bands - sow_templates.yaml: basic + treatment task-hour templates - estimate.py: assemble SOW from findings, scale Technical/On-page hours by properties_total, 제안가 = 합계 floored to 500k - build_deck.py: estimate slide shows module 소계 + 제안가 (point) - findings_to_service.md / SKILL.md / DESIGN.md: synced to new model Validated: reproduces real Basic ₩10.5M and Treatment ₩25.0M exactly; SHR (25 properties) scales to ₩71.5M, L'Escape (1) = ₩25.0M. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,26 +1,40 @@
|
||||
# Findings → service rubric
|
||||
# Findings → estimate mapping (effort-based)
|
||||
|
||||
How `estimate.py` maps detected findings (from `findings.json`) to `rate_card.yaml`
|
||||
service lines. The script reads the structured signals below; this file is the
|
||||
human-readable source of truth for the rules.
|
||||
`estimate.py` builds the 견적 from `sow_templates.yaml` priced via `rate_card.yaml`:
|
||||
**cost = role_rate × billing_rate (0.70) × standard_hours**, grouped by module;
|
||||
제안가 = 합계 floored to `rounding_unit`. This mirrors OurDigital/D.intelligence's
|
||||
real SOW-based quoting.
|
||||
|
||||
| Trigger (signal in findings.json) | Service line(s) | Scope driver |
|
||||
|---|---|---|
|
||||
| `discovery.sitemap_status != 200` OR `discovery.robots_sitemap_declared == false` OR `discovery.discoverable_urls` low vs `estimated_pages` | `technical_audit` + `technical_remediation` | site size / # templates |
|
||||
| `technical.cwv.perf < 0.5` OR `cls > 0.1` OR `lcp_ms > 2500` OR `ttfb_ms > 600` | `technical_audit` (if not already) + `technical_remediation` | # templates |
|
||||
| `technical.schema.org == "bare"/"none"` OR `entity.panel != "hotel"` OR `entity.name_split` OR `entity.legacy_contamination` OR `entity.subbrands_with_entity == 0` | `schema_build` (one-time) + `onpage_entity` (retainer) | # sub-brands + # properties |
|
||||
| `entity.properties_with_entity == 0` OR `url_hygiene` contains GBP/local mismatch | `local_seo` | # properties |
|
||||
| `findings[].class` includes measurement gap / no GSC-GA4 | `ga4_impl` and/or `dashboard` (+ `gtm_setup` if tag gaps) | — |
|
||||
| `technical.meta_dupe` OR `technical.title_i18n_mismatch` OR `technical.hreflang == "incomplete"` | `onpage_entity` | # templates |
|
||||
## Baseline selection (basic vs treatment)
|
||||
- `treatment` if any finding `severity == critical` **OR** `entity.properties_total > 3`
|
||||
- else `basic`
|
||||
- override with `--baseline`.
|
||||
|
||||
**Severity → priority** (for the brief/deck ordering, not pricing):
|
||||
- `critical`: crawl/index blocking, CWV failing, entity mistyped
|
||||
- `high`: entity/sub-brand gaps, duplicate URLs, meta dupes
|
||||
- `medium`: hreflang, H1, hygiene
|
||||
## Module inclusion
|
||||
Each baseline carries the standard module set (P&M · Technical SEO · On-page SEO ·
|
||||
SEO Growth), matching real quotes. Findings justify modules via the `trigger` field
|
||||
in `sow_templates.yaml`:
|
||||
|
||||
**Quantity rules**
|
||||
- `monthly` line items use `rate_card.defaults.retainer_months` (default 6).
|
||||
- `local_seo` scope note scales with property count (`entity` / discovery counts).
|
||||
- One-time items counted once even if triggered by multiple findings.
|
||||
| Module | trigger finding classes |
|
||||
|---|---|
|
||||
| Planning & Management | always |
|
||||
| Technical SEO | crawlability, cwv, schema_entity |
|
||||
| On-page SEO | onpage, schema_entity |
|
||||
| SEO Growth | measurement, always |
|
||||
|
||||
Edit this file and `rate_card.yaml` together when rates or rules change.
|
||||
## Hours scaling (portfolio)
|
||||
Tasks marked `scale: true` (Technical SEO + On-page SEO) have their **hours**
|
||||
multiplied sub-linearly by `entity.properties_total` per `rate_card.scaling.bands`.
|
||||
P&M and SEO Growth stay fixed (management/KPI overhead is ~flat). A single-property
|
||||
prospect → ×1.0, which reproduces the real single-site quotes exactly.
|
||||
|
||||
## Tools & terms
|
||||
`tools` (e.g. SEMrush Guru) are listed separately — client-subscribed, with +15%
|
||||
procurement markup if billed through us. VAT 별도 · 유효기간 14d · 현금 · 절사 from
|
||||
`rate_card.terms`.
|
||||
|
||||
## Validated reproduction (2026-05-28)
|
||||
- `treatment` ×1.0 → 합계 25,340,000 → 제안가 **25,000,000** (real Treatment quote)
|
||||
- `basic` ×1.0 → 합계 10,612,000 → 제안가 **10,500,000** (real Basic quote)
|
||||
|
||||
Edit `rate_card.yaml` and `sow_templates.yaml` together when rates or standard hours change.
|
||||
|
||||
@@ -1,63 +1,57 @@
|
||||
# OurDigital service rate card — single source for estimate.py
|
||||
# Mirrors the ourdigital-backoffice quote ranges. Values are KRW, treated as
|
||||
# pre-sales estimate RANGES (finalize after a precise diagnostic with access).
|
||||
quote_prefix: OD # quote number format: OD-YYYY-NNN
|
||||
# OurDigital / D.intelligence — rate card (effort-based)
|
||||
# Source of truth: 06_Working Template 견적서 (Google Drive), 2026-05.
|
||||
# Model: 비용 = 시간단가(role) × 청구율(billing_rate) × 표준 업무시간(hours).
|
||||
# Estimates are assembled bottom-up from sow_templates.yaml. Keep PRIVATE.
|
||||
|
||||
company:
|
||||
legal_name: "(주)디인텔리전스 (D.intelligence Lab)"
|
||||
brand: "OurDigital"
|
||||
ceo: "임명재"
|
||||
contact: "info@ourdigital.org"
|
||||
address: "경기도 성남시 수정구 창업로 43 판교글로벌비즈센터 업무동 1층 128호"
|
||||
|
||||
quote_prefix: OD # OD-YYYY-NNN
|
||||
currency: KRW
|
||||
|
||||
services:
|
||||
technical_audit:
|
||||
label_ko: "Technical Audit / 기술 SEO 진단"
|
||||
unit: one_time # one_time | monthly | project
|
||||
min: 3000000
|
||||
max: 5000000
|
||||
technical_remediation:
|
||||
label_ko: "기술 개선 실행 (sitemap/CWV/SSR)"
|
||||
unit: project
|
||||
min: 3000000
|
||||
max: 8000000
|
||||
onpage_entity:
|
||||
label_ko: "On-Page / Entity Optimization (월 운영)"
|
||||
unit: monthly
|
||||
min: 1500000
|
||||
max: 3000000
|
||||
schema_build:
|
||||
label_ko: "구조화 데이터(Schema) 구축 (1회)"
|
||||
unit: one_time
|
||||
min: 2000000
|
||||
max: 4000000
|
||||
local_seo:
|
||||
label_ko: "Local SEO (프로퍼티 로컬 최적화)"
|
||||
unit: monthly
|
||||
min: 1000000
|
||||
max: 2000000
|
||||
gtm_setup:
|
||||
label_ko: "GTM Setup / 태그 관리 구축"
|
||||
unit: project
|
||||
min: 2000000
|
||||
max: 4000000
|
||||
ga4_impl:
|
||||
label_ko: "GA4 Implementation / 분석 환경 구축"
|
||||
unit: project
|
||||
min: 1500000
|
||||
max: 3000000
|
||||
dashboard:
|
||||
label_ko: "Dashboard Development / 대시보드 개발"
|
||||
unit: project
|
||||
min: 3000000
|
||||
max: 6000000
|
||||
billing_rate: 0.70 # 청구율 표준 70%
|
||||
billing_rate_floor: 0.60 # 협상 시 -10%p 까지(>10%p 조정은 CEO/CFO 승인)
|
||||
basis:
|
||||
hours_per_day: 8 # 일 8시간
|
||||
weeks_per_month: 4 # 월 4주
|
||||
procurement_markup: 0.15 # 조달-관리 수수료 15% (외부 조달 물품/서비스)
|
||||
rounding_unit: 500000 # 제안가 = 합계 절사. 50만원 단위가 실제 견적(25,340,000→25,000,000;
|
||||
# 10,612,000→10,500,000)을 재현. 필요시 100000/1000000 으로 조정.
|
||||
|
||||
defaults:
|
||||
retainer_months: 6 # default contract length for monthly line items
|
||||
disclaimer_ko: "본 견적은 공개 데이터 기반 사전 추정 범위이며, Search Console/Analytics 권한 확보 후 정밀 진단을 통해 확정됩니다."
|
||||
terms:
|
||||
vat: "부가세 별도"
|
||||
validity_days: 14 # 견적 유효기간(약 2주)
|
||||
payment: "현금"
|
||||
|
||||
# Scope scaling — monthly line items scale (sub-linearly) by portfolio size.
|
||||
# driver: a count under findings.entity (properties_total | subbrands_total).
|
||||
# bands: ordered [max_count, multiplier]; first band whose max_count >= count wins.
|
||||
# A 25-property chain costs more to run than a single hotel, but not 25x.
|
||||
# 시간 단가 (KRW/hr) — 직급 기준. sow_templates.yaml의 task.role 이 이 키를 참조.
|
||||
role_rates:
|
||||
ceo: 180000 # 대표
|
||||
evp: 150000 # 전무
|
||||
svp: 120000 # 상무
|
||||
technical_advisor: 120000 # 기술고문
|
||||
director: 100000 # 이사 / 본부장
|
||||
senior_manager: 90000 # 부장 (Senior SEO·PM·Senior Analyst·Account Director(국장))
|
||||
deputy_manager: 80000 # 차장
|
||||
manager: 70000 # 과장 (SEO·Data Analyst·Data Engineer·Engineer|Developer)
|
||||
assistant_manager: 60000 # 대리
|
||||
junior: 50000 # 주임
|
||||
associate: 30000 # 사원
|
||||
intern: 12000 # 인턴
|
||||
|
||||
# 포트폴리오 규모에 따른 '시간' 스케일(서브선형). scale:true 인 task 에만 적용.
|
||||
# driver: findings.entity 의 카운트. bands: [최대값, 배수]; count <= 최대값 인 첫 밴드.
|
||||
scaling:
|
||||
local_seo:
|
||||
driver: properties_total
|
||||
bands: [[1, 1.0], [5, 1.6], [15, 2.8], [30, 4.5], [999999, 6.5]]
|
||||
onpage_entity:
|
||||
driver: subbrands_total
|
||||
bands: [[1, 1.0], [3, 1.6], [6, 2.2], [999999, 3.2]]
|
||||
driver: properties_total
|
||||
bands: [[1, 1.0], [5, 1.6], [15, 2.8], [30, 4.5], [999999, 6.5]]
|
||||
|
||||
# 별도 조달 항목(인력비와 분리). 청구 시 procurement_markup 적용 가능.
|
||||
tools:
|
||||
semrush_guru:
|
||||
label: "Advanced SEO Tools — SEMrush Guru"
|
||||
unit: "월간 구독"
|
||||
price_usd: 249.95
|
||||
note: "고객사 별도 구독(TBD), 사용자별 과금"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# SOW task templates — standard 업무 시간(hours) by module.
|
||||
# Seeded from the two real OurDigital quotes so estimate.py reproduces them at
|
||||
# billing_rate 0.70:
|
||||
# basic -> 제안가 ₩10,500,000 (SEO Basic & Coaching, 3개월 프로젝트)
|
||||
# treatment -> 제안가 ₩25,000,000 (SEO Audit & Treatment, 월 정기)
|
||||
# task.role references rate_card.role_rates. scale:true → hours scaled by portfolio.
|
||||
# trigger: finding classes that justify the module (for annotation + selection).
|
||||
|
||||
baselines:
|
||||
|
||||
basic:
|
||||
service: "SEO Audit & Basic Treatment"
|
||||
modules:
|
||||
- name: "Planning & Management"
|
||||
trigger: [always]
|
||||
tasks:
|
||||
- {task: "업무 관리", desc: "프로젝트 설계·과업 정의·일정/산출물 관리·리포팅", role: senior_manager, hours: 20, scale: false}
|
||||
- {task: "웹 사이트 분석", desc: "사용자 유입·채널 내 행동·전환 flow 분석", role: senior_manager, hours: 12, scale: false}
|
||||
- name: "Technical SEO"
|
||||
trigger: [crawlability, cwv, schema_entity]
|
||||
tasks:
|
||||
- {task: "Crawling & Indexing 설정", desc: "검색사이트 등록/수집 관리, Site Health Check 도구 설정", role: technical_advisor, hours: 16, scale: true}
|
||||
- {task: "속도·UX·수집 설정", desc: "로딩속도·페이지 UX·링크·수집 제외 설정", role: manager, hours: 16, scale: true}
|
||||
- {task: "사이트/URL 구조·메타", desc: "구조·URL·메타데이터·사이트맵·리다이렉션", role: senior_manager, hours: 12, scale: true}
|
||||
- {task: "색인·CWV 진단", desc: "GSC·SEO Tools 활용 색인/크롤오류/Core Web Vitals 진단", role: senior_manager, hours: 16, scale: true}
|
||||
- name: "On-page SEO"
|
||||
trigger: [onpage, schema_entity]
|
||||
tasks:
|
||||
- {task: "키워드·템플릿·메타 진단", desc: "중점 키워드·페이지 템플릿·메타·개체 활용 진단", role: manager, hours: 24, scale: true}
|
||||
- {task: "링크·콘텐츠 도구 진단", desc: "내외부 링크·공유 메타·콘텐츠 생성-관리 도구", role: manager, hours: 12, scale: true}
|
||||
- {task: "on-page 관리 가이드", desc: "타이틀·메타·헤더태그·링크·이미지 메타 가이드", role: manager, hours: 36, scale: true}
|
||||
- name: "SEO Growth"
|
||||
trigger: [measurement, always]
|
||||
tasks:
|
||||
- {task: "성과 지표 관리", desc: "SEO 관리지표·중점 키워드·포지셔닝 트래킹·3rd party 데이터 설정", role: manager, hours: 24, scale: false}
|
||||
|
||||
treatment:
|
||||
service: "SEO Audit & Treatment"
|
||||
modules:
|
||||
- name: "Planning & Management"
|
||||
trigger: [always]
|
||||
tasks:
|
||||
- {task: "업무 관리", desc: "프로젝트 설계·과업 정의·일정/산출물 관리·리포팅", role: senior_manager, hours: 36, scale: false}
|
||||
- {task: "웹 사이트 분석", desc: "사용자 유입·채널 내 행동·전환 flow 분석", role: senior_manager, hours: 60, scale: false}
|
||||
- {task: "측정 계획 수립", desc: "채널 운영 목적·사용자 세그먼트·기대행동 가설 도출", role: senior_manager, hours: 20, scale: false}
|
||||
- name: "Technical SEO"
|
||||
trigger: [crawlability, cwv, schema_entity]
|
||||
tasks:
|
||||
- {task: "Crawling & Indexing 설정", desc: "검색사이트 등록/수집 관리, Site Health Check 도구 설정", role: manager, hours: 24, scale: true}
|
||||
- {task: "속도·UX·리다이렉트", desc: "로딩속도·페이지 UX·링크·리다이렉트·수집 제외 설정", role: technical_advisor, hours: 30, scale: true}
|
||||
- {task: "사이트/URL 구조·보안", desc: "구조·URL·메타데이터·사이트맵·보안 관리 진단", role: senior_manager, hours: 24, scale: true}
|
||||
- {task: "모바일·CWV·개선과제", desc: "모바일 최적화·Core Web Vitals 진단·개선 과제 도출", role: manager, hours: 20, scale: true}
|
||||
- name: "On-page SEO"
|
||||
trigger: [onpage, schema_entity]
|
||||
tasks:
|
||||
- {task: "키워드·템플릿·메타 진단", desc: "중점 키워드·페이지 템플릿·메타·개체 활용 진단", role: manager, hours: 48, scale: true}
|
||||
- {task: "링크·콘텐츠 도구 진단", desc: "내외부 링크·공유 메타·콘텐츠 생성-관리 도구", role: manager, hours: 36, scale: true}
|
||||
- {task: "on-page 관리 가이드", desc: "타이틀·메타·헤더태그·링크·이미지 메타 가이드", role: manager, hours: 60, scale: true}
|
||||
- name: "SEO Growth"
|
||||
trigger: [measurement, always]
|
||||
tasks:
|
||||
- {task: "핵심 성과 지표 설정", desc: "중점과제·목표·SEO 핵심성과지표·모니터링 지표 설정", role: senior_manager, hours: 20, scale: false}
|
||||
- {task: "지표 정의·리포팅 설계", desc: "데이터 소스·지표 산식·리포팅/진단 주기 설정", role: manager, hours: 16, scale: false}
|
||||
- {task: "성과 대시보드 지표", desc: "관리 지표·중점 키워드·포지셔닝 트래킹·3rd party 데이터", role: manager, hours: 20, scale: false}
|
||||
- {task: "GSC 대시보드·파이프라인", desc: "GSC 대시보드 구성·연관 데이터 파이프라인 구축", role: manager, hours: 36, scale: false}
|
||||
Reference in New Issue
Block a user