/** * INFINITH AI Discovery AEO/GEO 채점 타입 * * 설계 원칙 * - 기준표(rubric)와 결과(result)를 분리한다. 결과는 criterionId → level 매핑만 가진다. * - 미검증(unverified) 항목은 0점이 아니라 분모에서 제외한다. * - 모든 항목에 근거 등급(evidenceGrade)과 통제 가능성(control)을 붙인다. */ /** 0 = 없음/치명, 1 = 미흡, 2 = 양호, 3 = 우수 */ export type CriterionLevel = 0 | 1 | 2 | 3; /** 근거 등급 (ado2-aeo-geo 정직성 규약) */ export type EvidenceGrade = 'measured' | 'academic' | 'vendor' | 'estimate'; /** 업체가 통제할 수 있는 정도 */ export type ControlLevel = 'direct' | 'influence' | 'none'; /** 자동 채점 가능 여부 */ export type EvidenceMethod = 'auto' | 'semi' | 'manual'; export type CategoryId = | 'access' | 'entity' | 'content' | 'surface' | 'measure' | 'hygiene'; export interface RubricLevelSpec { level: CriterionLevel; label: string; } export interface RubricCriterion { id: string; category: CategoryId; name: string; /** 왜 AI 답변 노출에 영향을 주는가 */ rationale: string; /** 배점 (카테고리 합 = 카테고리 weight) */ weight: number; evidenceGrade: EvidenceGrade; control: ControlLevel; method: EvidenceMethod; /** 어떻게 확인하는가 (자동화 시 구현 스펙) */ howToCheck: string; levels: RubricLevelSpec[]; } export interface RubricCategory { id: CategoryId; code: string; name: string; nameEn: string; description: string; /** 100점 만점 중 배점 */ weight: number; } export interface Rubric { version: string; updatedAt: string; categories: RubricCategory[]; criteria: RubricCriterion[]; } /** 개별 항목 채점 결과 */ export interface CriterionResult { criterionId: string; /** 'unverified' → 분모 제외 */ level: CriterionLevel | 'unverified'; /** 실측 근거 (한 줄) */ evidence: string; /** 실측 원문·URL 등 */ source?: string; } export type ActionPriority = 'P0' | 'P1' | 'P2'; export interface ActionItem { priority: ActionPriority; title: string; detail: string; /** 연결된 기준 항목 */ criterionIds: string[]; /** 예상 소요 */ effort: '1일' | '1주' | '2~4주' | '지속'; } export interface DiscoveryResult { id: string; clinicName: string; url: string; industry: string; auditedAt: string; rubricVersion: string; /** 측정 조건 명시 (정직성 규약) */ conditions: string[]; results: CriterionResult[]; /** AI가 지금 이 업체를 어떻게 읽는지. 핵심 발견 */ keyFindings: { title: string; detail: string; severity: 'critical' | 'warning' | 'good' }[]; actions: ActionItem[]; /** 하지 않을 것 + 이유 */ notDoing: { item: string; reason: string }[]; } /** 집계 결과 */ export interface CategoryScore { category: RubricCategory; earned: number; possible: number; // 검증된 항목의 배점 합 weight: number; // 원래 배점 pct: number; // 0~100 verifiedCount: number; totalCount: number; } export interface OverallScore { score: number; // 0~100 (검증 항목 기준 정규화) earned: number; possible: number; categories: CategoryScore[]; grade: 'A' | 'B' | 'C' | 'D'; }