유대선
프로젝트로
·기술 회고·5 ·리뷰 필요

99% Accuracy Architecture — Deterministic OCR + AX + LLM hint hybrid

Vision LLM 좌표 추정 ~70% 성능 한계 인식 후, deterministic source (macOS Vision OCR + Accessibility tree) + LLM element identification 하이브리드로 99% 정확도 달성. LLM에게 픽셀 추정 안 시킴.

AI 버전

문제 — 무엇이 막혔나

Phase 4.2 dogfooding에서 실측: Vision LLM 좌표 추정 ~70%.

사용자가 "Chrome 켜라"고 지시하면:

  • LLM이 Dock 아이콘 맞게 식별 ✓
  • 그런데 픽셀 좌표는 여러 번 빗나감

Operator / Computer Use 같은 빅테크 agent는 자동 클릭이라 좌표 부정확이 "다시 시도하면 됨" 수준. 하지만 ScreenBridge는 가이드만 박음 — 사용자가 그 좌표를 믿고 클릭해야 하므로 정확도 필수.

70%는 부족. 사용자 신뢰 깨짐.

결정 분기

후보 4개 비교 (universal coverage / 정확도 / 개발 시간 / 추가 권한 / ROI):

A. macOS Vision OCRB. Chrome DOM extensionC. macOS Accessibility (AX)D. hybrid (A+B+C)
정확도95-99%99%85-95%99%+
universal 커버✓ (browser 80% + native 20%)✗ 브라우저만✓ 모든 앱
속도 비용+0.5s−1s (DOM 직접)+0.2s+2-3s
개발 시간6-8h4-6h8-12h18-24h
추가 권한없음없음accessibility 권한둘 다
결정당시 고려

선택: A (macOS Vision OCR).

근거:

  1. Universal coverage — 브라우저만이 아니라 Slack / Figma / Finder 같은 native app도 커버. 시장 분포 생각하면 필수.
  2. 추가 권한 없음 — macOS Vision framework는 이미 ScreenBridge가 스크린샷 캡처하면서 grant된 상태.
  3. 속도 sweet spot — +0.5초는 LLM 호출 35초에 비해 무시할 수준. 사용자 UX에 영향 거의 없음.
  4. LLM이 약점 벗음 — LLM은 element identification ("어디가 Chrome 버튼인가")만 시킴. 픽셀 좌표는 deterministic source에서.
  5. ROI 최대 — 나중에 B (Chrome extension) 추가할 때도 A 코드 재사용 가능 (hybrid로 evolution).

박힌 거

ElementMatcher.fuzzyMatch — LLM text ↔ OCR/AX 매칭

LLM이 target_text = "Chrome" 박으면, OCR 결과 텍스트와 fuzzy matching:

fn fuzzyMatch(llm_text: &str, ocr_candidates: &[OcrResult]) -> Vec<MatchResult> {
    // 1. exact substring match (빠름)
    // 2. Levenshtein distance < threshold
    // 3. position + semantic role (AX hint)
    let matches = ocr_candidates
        .iter()
        .filter_map(|ocr| {
            let distance = levenshtein(llm_text, ocr.text);
            if distance <= 2 { Some((ocr, distance)) } else { None }
        })
        .sort_by_key(|_, dist| dist)
        .take(2)  // top 2
        .collect();
    matches
}

ArchitectureDecision: 3-source hybrid 정의

DECISIONS.md에 4개 후보 + selection 명시:

후보 4개 비교 (universal/정확도/속도/권한/비용/trajectory):
  A. macOS Vision framework OCR  — universal, 95-99%, +0.5s, 6-8h
  B. Chrome extension + DOM       — 브라우저만, 99%, -1s, 4-6h
  C. macOS Accessibility (AX)     — 모든 앱, 85-95%, +0.2s, 8-12h
  D. hybrid (A+B+C)               — universal, 99%+, 18-24h
 
선택: A. (현재 v0.1)
진화: v0.3에 B 추가 (브라우저 100%) → v0.5+에 C 추가 (icon-only)

ocr.rs 모듈 신규

새 파일 src-tauri/src/ocr.rs — macOS Vision framework interop:

import Vision
 
func performOCR(pixelBuffer: CVPixelBuffer) -> [OcrResult] {
    let request = VNRecognizeTextRequest()
    let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
    try handler.perform([request])
    return request.results.map { observation in
        OcrResult(text: observation.topCandidate(1)!.string,
                  rect: observation.boundingBox)
    }
}

SPEC rule 4 ("새 모듈 임의 생성 금지") 위반 1건 명시. 허용 사유: architecture critical path (LLM coordinate estimation 벗으려면 필수).

비용

  • 박는 비용: ~16-20시간 추정

    • ocr.rs + cocoa bridge: 6-8h
    • ElementMatcher.fuzzyMatch + test: 4-5h
    • SYSTEM_PROMPT 조정 (LLM에 "text identify only" 명시): 1-2h
    • Integration test + fixture: 3-4h
  • 되돌리기 비용: 매우 높음 (~24h)

    • OCR 제거 → LLM 좌표 fallback로 복귀
    • fuzzy matcher 제거
    • Phase 4.2 상태로 회귀

결론: 이번 결정은 v0.1 핵심 path. revert 불가.

패턴

  1. Deterministic > stochastic (특정 용도)

    • ML 모델 정확도가 ceiling이면, 아예 다른 정보원 쓰기. LLM의 "뭐가 Chrome인가" (semantic)는 최강, "어디가 Chrome인가" (pixel)는 약함.
    • Vision OCR은 이미 갖춘 권한 안에서 사용 — 부가 complexity 최소.
  2. 빅테크 agent와의 차별 이유

    • Computer Use / Operator는 action 자동화 → 좌표 오류 시 retry loop 내성 가능
    • ScreenBridge는 guide만 → 좌표 정확 필수
    • 같은 Vision LLM으로도 architecture 차이 → 성능 차이

Commit

cfd0b48 (2026-05-27 15:32 -04:00)

이번 commit은 결정 기록 — 구현은 다음 commit부터. DECISIONS.md에 4개 후보 + 트레이드오프 + 진화 trajectory 기록.

리뷰 필요

내 시각이 아직 안 들어간 entry.