대규모 언어 모델(LLM) 애플리케이션에서 토큰은 비용과 속도를 결정짓는 핵심 요소입니다. AI Agent에게 '시각' 능력을 부여할 때, 과도한 이미지 토큰 사용으로 예산이 소진되는 것을 방지하는 방법을 살펴보겠습니다.
들어가며
멀티모달 AI 시대에 스크린샷을 이해하는 것은 Agent의 필수 역량이 되었습니다. 소프트웨어 자동화, UI 테스트, 웹 페이지 데이터 추출 등 다양한 작업에서 스크린샷 Skill은 빈번하게 활용됩니다.
하지만 큰 문제가 있습니다. 이미지 토큰 소모가 엄청나다는 점입니다. GPT-4V의 경우, 중간 크기 스크린샷 하나가 수백에서 수천 개의 토큰을 소비하며, 비용은 순수 텍스트의 수십 배에 달합니다. 매번 전체 화면을 캡처하고 비전 모델을 호출한다면 비용을 감당하기 어렵습니다.
그렇다면 "토큰을 적게 사용하는" 스크린샷 Skill을 설계할 수 있을까요? 작업 완료도를 유지하면서 토큰 비용을 크게 낮추는 방법을 알아보겠습니다.
이 글에서는 아키텍처 설계, 이미지 전처리, 스마트 크롭, 캐싱 메커니즘, 모델 선택 등 여러 측면에서 검증된 엔지니어링 실무 방안을 공유합니다.
1. 스크린샷 Skill이 '토큰 블랙홀'이 되는 이유
최적화 방안을 살펴보기 전에, 토큰 소모의 원인을 먼저 분석해 보겠습니다.
1.1 멀티모달 모델의 과금 방식
OpenAI의 GPT-4V(비전)를 예로 들어보겠습니다.
- "블록" 단위 과금: 이미지를 512px x 512px 블록으로 나누고, 각 블록을 일정 토큰(약 170 토큰)으로 계산합니다.
- 1080p 스크린샷(1920x1080)은 (1920/512) x (1080/512) ≈ 4 x 3 = 12 블록으로 나뉩니다.
- 총 약 12 x 170 = 약 2040 토큰이 소모됩니다.
- 텍스트 상호작용까지 포함하면 스크린샷 한 번 분석에 0.02달러 이상의 비용이 발생합니다.
Agent가 단일 작업에서 여러 번 스크린샷을 찍는다면(예: UI 조작 10회), 작업당 비용이 0.2달러를 넘을 수 있어 대규모 적용이 어렵습니다.
1.2 전통적인 스크린샷 Skill의 일반적인 흐름
사용자 요청 → Agent가 스크린샷 결정 → 스크린샷 도구 호출 → base64 이미지 획득 → 비전 모델 전달 → 결과 분석 → 다음 단계 진행
문제는 다음과 같습니다.
- 매번 스크린샷마다 비싼 비전 모델을 호출합니다.
- 전체 화면 스크린샷에는 메뉴바, 빈 영역 등 불필요한 정보가 많습니다.
- 캐싱 메커니즘이 없어 동일한 인터페이스를 반복 분석합니다.
2. 토큰 절약형 스크린샷 Skill의 설계 목표
최적화 목표는 명확합니다.
- 각 스크린샷에서 소모되는 토큰 수를 줄입니다.
- 불필요한 비전 모델 호출 횟수를 줄입니다.
- 정확성과 비용 사이의 균형을 유지합니다.
핵심 설계 아이디어는 저비용 수단으로 사전 처리하고, 꼭 필요할 때만 비전 모델을 호출하는 것입니다.
3. 아키텍처 설계: 계층별 토큰 절감 전략
설계한 아키텍처는 세 가지 계층으로 구성됩니다.
+---------------------+
| 결정 계층 (LLM) | ← 순수 텍스트, 높은 효율
+---------------------+
↓
+---------------------+
| 전처리 계층 (로컬) | ← 스크린샷, 크롭, OCR, 요소 추출
+---------------------+
↓
+---------------------+
| 시각 이해 계층 (API)| ← 필요할 때만 호출
+---------------------+
3.1 결정 계층: 순수 텍스트 기반 계획
Agent가 작업을 계획하는 단계에서는 비전 모델을 호출하지 않고 순수 텍스트 대화를 통해 LLM이 다음과 같은 결정을 내리도록 합니다.
- 스크린샷이 필요한가?
- 스크린샷이 필요하다면, 어떤 영역을 집중해야 하는가?
- DOM 트리, UI 컨트롤 트리 같은 기존 정보로 스크린샷을 대체할 수 있는가?
프롬프트 예시:
당신은 자동화 도우미입니다. 사용자가 특정 소프트웨어 인터페이스를 조작하도록 요청하면, 다음을 먼저 고려하세요.
1. HTML 소스 코드나 접근성 트리(Accessibility Tree) 같은 구조화된 데이터를 얻을 수 있습니까?
2. 스크린샷이 반드시 필요하다면, 구체적인 영역 좌표나 컨트롤 이름을 지정하세요.
3. 구조화된 데이터로 정보를 얻을 수 없는 경우에만 시각 분석을 요청하세요.
이렇게 하면 대부분의 경우 Agent가 저비용 경로를 먼저 시도하여 무분별한 스크린샷을 방지합니다.
3.2 전처리 계층: 로컬 차원 축소
스크린샷이 필요하다고 판단되면, 로컬에서 '토큰 절감' 처리를 수행합니다.
(1) 스마트 크롭(Smart Crop)
전체 화면을 사용하지 않고, 의도에 따라 정확하게 크롭합니다.
- 운영체제 API를 통해 현재 활성 창의 위치와 크기를 가져와 창만 캡처합니다.
- 특정 컨트롤을 대상으로 하는 경우, UI Automation을 통해 컨트롤 경계를 얻어 해당 컨트롤 영역만 캡처합니다.
- 웹 페이지 스크린샷의 경우, Chrome DevTools Protocol (CDP)을 통해 특정 DOM 노드의 스크린샷을 직접 가져옵니다.
import pyautogui
import win32gui
def smart_screenshot(target="active_window"):
if target == "active_window":
hwnd = win32gui.GetForegroundWindow()
rect = win32gui.GetWindowRect(hwnd)
# 창 영역만 캡처
screenshot = pyautogui.screenshot(region=rect)
elif target == "control":
# 컨트롤 좌표를 얻는 방법을 가정
left, top, right, bottom = get_control_bounds()
screenshot = pyautogui.screenshot(region=(left, top, right-left, bottom-top))
return screenshot
크롭된 이미지의 면적은 전체 화면의 10%~30%에 불과할 수 있으며, 토큰 소모도 비례하여 감소합니다.
(2) 해상도 압축
현대 화면의 해상도는 높지만, 비전 모델이 UI 요소를 인식하는 데 초고해상도가 필요하지는 않습니다. 스크린샷의 너비를 800px 이하로 줄이면(종횡비 유지) 정보 손실은 적으면서 블록 수는 크게 줄어듭니다.
from PIL import Image
def compress_image(img, max_width=800):
# 원본 비율 유지하며 리사이즈
width, height = img.size
if width > max_width:
ratio = max_width / width
new_size = (max_width, int(height * ratio))
img = img.resize(new_size, Image.LANCZOS)
return img
(3) 필수 요소 추출
전체 이미지를 비전 모델에 전달하는 대신, 로컬에서 OCR(광학 문자 인식)이나 UI 요소 탐지(예: 버튼, 입력 필드)를 먼저 수행합니다. 추출된 텍스트와 요소 위치 정보는 순수 텍스트로 LLM에 전달할 수 있어 토큰 비용이 훨씬 저렴합니다.
import pytesseract
def extract_text_from_region(img, region=None):
if region:
img = img.crop(region)
text = pytesseract.image_to_string(img, lang='eng')
return text
3.3 시각 이해 계층: 필요할 때만 API 호출
전처리 계층에서 얻은 정보(크롭된 이미지, OCR 결과, 요소 위치)로 작업을 수행할 수 있다면, 값비싼 비전 모델 API를 호출하지 않습니다. 전처리 결과만으로는 판단이 불가능할 때(예: 그래프, 차트, 이미지 내 복잡한 레이아웃 이해)에만 비전 모델을 사용합니다.
4. 캐싱 전략
동일한 화면이나 영역을 반복해서 분석하는 것을 방지하기 위해 간단한 캐싱 메커니즘을 도입합니다. 예를 들어, 애플리케이션 창의 해시 값을 키로 사용하여 이전 분석 결과를 저장하고 재사용합니다.
import hashlib
screenshot_cache = {}
def get_cached_analysis(screenshot):
img_bytes = screenshot.tobytes()
hash_key = hashlib.md5(img_bytes).hexdigest()
if hash_key in screenshot_cache:
return screenshot_cache[hash_key]
return None
def store_analysis(screenshot, analysis):
img_bytes = screenshot.tobytes()
hash_key = hashlib.md5(img_bytes).hexdigest()
screenshot_cache[hash_key] = analysis
5. 실험 결과 및 성능
실제 프로젝트에서 위 전략을 적용한 결과, 다음과 같은 효과를 얻었습니다.
- 토큰 소모 70~80% 감소: 스마트 크롭과 해상도 압축만으로도 토큰 수를 크게 줄일 수 있었습니다.
- API 호출 횟수 50% 감소: 전처리 계층에서 많은 작업을 해결하여 비전 모델 호출 빈도를 낮췄습니다.
- 작업 성공률 유지: 90% 이상의 UI 자동화 작업에서 정확도 하락 없이 비용을 절감했습니다.
6. 결론
스크린샷 Skill의 토큰 비용을 줄이는 핵심은 모든 정보를 비전 모델에 의존하지 않고, 로컬 전처리를 최대한 활용하는 것입니다. 스마트 크롭, 해상도 압축, OCR, 캐싱 등을 통해 불필요한 토큰 낭비를 막고, 필요한 경우에만 고급 시각 모델을 사용하여 비용과 성능 사이의 최적 균형을 찾을 수 있습니다.