XGEN 15일 무료 체험 — 설치 없이 브라우저에서 바로 시작하세요체험 신청
PlateerAI Labs
·Reading Time | 2 min
작성 | 김진수

모델 한도에 맞춰 추출 배치를 조절하는 방법(9편)

컨텍스트와 출력 예산을 함께 계산하고, 시간 초과와 JSON 절단이 발생한 배치만 더 작게 나눠 다시 처리합니다.

모델 한도에 맞춰 추출 배치를 조절하는 방법(9편) — 커버 일러스트Series · 온톨로지 개발기시리즈 9번째 글 · 전체 10편 보기

지식그래프 설계 · 9/10

온톨로지 추출 모델을 로컬 소형 모델로 바꾸자 같은 문서의 한 배치가 15분 가까이 끝나지 않거나, 작업은 성공으로 끝났는데 그래프는 비어 있는 경우가 생겼습니다. 고정된 10,000자 입력이 작은 모델의 컨텍스트와 출력 한도를 함께 압박했고, 잘린 JSON과 시간 초과가 빈 추출 결과처럼 처리되고 있었습니다.

배치 크기와 출력 토큰을 낮추면 당장은 통과했지만 다른 모델에서는 불필요하게 잘게 쪼개졌습니다. 모델 이름별 상수 목록도 실제 서빙 설정의 컨텍스트 크기가 바뀌면 곧 틀어졌습니다. 실패한 묶음 전체를 처음부터 다시 처리하는 비용도 컸습니다.

추출기가 현재 모델의 입력·출력 예산을 읽어 배치 크기를 계산하고, 시간 초과나 JSON 절단이 난 배치만 더 작게 나눠 재시도하도록 바꿨습니다. 모델마다 숫자를 외우는 대신 실행 시점의 수용량과 실패 신호가 처리 단위를 결정하게 했습니다.

입력 예산과 출력 예산 중 작은 쪽을 사용합니다

온톨로지 추출은 일반 요약보다 출력이 큽니다. 입력에 개체와 관계가 많으면 JSON 배열이 빠르게 늘어납니다. 컨텍스트 윈도우만 보고 입력을 크게 묶으면 출력 토큰 한도에서 잘릴 수 있습니다.

문자 예산은 두 값 중 작은 쪽으로 정합니다.

배치 문자 예산
  = min(
      컨텍스트 윈도우에서 시스템 프롬프트와 출력 여유를 뺀 입력 예산,
      최대 출력 토큰으로 감당할 수 있는 추출량 예산
    )

이 값은 tokenizer로 정확한 토큰 수를 세는 계산이 아닙니다. 문자당 토큰 비율과 구조화 출력의 팽창률을 운영 설정으로 조절해 잡는 보수적 추정치입니다. 모델과 언어가 바뀌면 실제 사용량을 다시 측정해 계수를 조정해야 합니다.

OpenAI 호환 서버는 /models/v1/models의 메타데이터에서 max_model_len, context_length, n_ctx 같은 값을 탐색합니다. 키의 위치와 이름이 서버마다 달라 중첩 메타데이터도 함께 확인합니다. 클라우드 API처럼 런타임 조회가 불가능한 경우에는 운영 설정을 사용하고, 둘 다 없을 때만 검증된 기본값으로 돌아갑니다.

provider 이름으로 “이 모델은 128K”라고 가정하지 않습니다. 모델의 능력과 현재 서빙 설정은 다를 수 있기 때문입니다.

큰 청크도 출처를 잃지 않고 나눕니다

배치 예산을 줄여도 청크 하나가 예산보다 클 수 있습니다. 특히 HTML 표나 OCR 결과는 하나의 청크가 매우 길어집니다. 문자열 앞부분만 잘라 보내면 뒤쪽의 행은 조용히 사라집니다.

큰 청크는 표의 행 경계를 우선 사용해 나누고, 행 경계가 없으면 문자 윈도우로 분할합니다. 분할 조각은 원래 chunk_id를 유지합니다. 모델 호출 단위는 작아져도 결과가 돌아갈 원문 출처는 바뀌지 않습니다.

고정 길이 슬라이스로 입력을 잘라내던 경로는 제거했습니다. 배치 조립 단계가 앞의 추정 예산 안에서 입력을 구성하므로 추출 함수는 전달받은 내용을 임의로 다시 자르지 않습니다.

시간 초과와 절단을 재시도 신호로 바꿨습니다

기존 JSON 호출에는 명시적인 시간 초과가 없었습니다. 응답이 느린 배치 하나가 전체 그룹의 완료를 막을 수 있었습니다. 또 provider가 finish_reason=lengthmax_tokens를 반환해도 파싱 실패 뒤 빈 결과로 처리될 수 있었습니다.

JSON 호출이 시간 초과, 출력 절단, 파싱 실패를 구분해 반환하도록 했습니다. 문제가 생긴 배치는 절반으로 나눠 다시 시도합니다. 정상 배치까지 되돌리지 않습니다.

배치 실행
  ├─ 성공 ─────────────→ 결과 병합
  └─ timeout/truncated
       ├─ 왼쪽 절반 ───→ 재실행
       └─ 오른쪽 절반 ─→ 재실행

재시도는 명시적인 스택으로 관리합니다. 동시 호출 수를 제한하는 세마포어의 슬롯을 잡은 상태에서 같은 함수를 재귀 호출하면 자기 자신이 새 슬롯을 기다릴 수 있기 때문입니다. 한 작업이 슬롯을 한 번만 확보하고 내부에서 분할 배치를 순서대로 처리한 뒤 결과를 합칩니다.

합칠 때 클래스와 속성은 식별자로 중복을 제거하고, 인스턴스와 관계, 값은 출처를 유지한 채 이어 붙입니다. 다만 더 나눌 수 없는 최소 단위에서 실패하면 현재 결과는 None으로 끝나고, 상위 집계가 예외나 빈 결과를 건너뛸 수 있습니다. 분할·병합 로직은 만들었지만 배치별 실패 수와 ID를 작업 오류로 승격해 부분 유실을 막는 계약은 아직 남아 있습니다.

로직 검증과 라이브 검증을 구분했습니다

분할, 병합, 절단 신호, 입력·출력 예산 계산, 세마포어 종료는 실제 클래스를 불러온 단위 테스트로 확인했습니다. 기존 JSON 호출부가 새 선택 인자 없이 같은 동작을 유지하는지도 점검했습니다.

다만 당시 대상 로컬 모델 서버에 연결할 수 없어 실제 모델 메타데이터 조회와 전체 라이브 빌드는 확인하지 못했습니다. 따라서 확인된 범위는 계산과 복구 로직이며, 특정 로컬 모델에서의 처리 시간 개선은 서버가 준비된 뒤 별도로 측정해야 합니다.

문서에서는 모델 차이를 흡수하는 것이 핵심이었습니다. 정형 데이터는 다른 접근이 가능합니다. 데이터베이스가 이미 타입, 기본키, 외래키를 제공하므로 행을 해석하는 데 LLM을 사용할 이유가 없습니다. 마지막 편에서는 SELECT 결과를 직접 그래프로 만들고 전체 교체와 증분 갱신을 안전하게 처리하는 구조를 다룹니다.


이전 편 → 열 개 CSV 클래스가 네 개가 된 이유 다음 편 → SELECT 결과를 안전하게 그래프로 교체하는 방법

#온톨로지#LLM 추출#장애 복구
블로그 목록으로