Claude를 사용하여 토큰 제한 없이 300페이지 분량의 책을 번역하는 방법
요약
Claude의 컨텍스트 윈도우 제한을 극복하여 300페이지 분량의 긴 문서를 번역하는 기술적 방법론을 소개합니다. 문단 단위의 슬라이딩 윈도우 청킹과 문맥 유지를 위한 중첩(overlap) 전략을 사용하여 번역 품질을 높이는 구현 방식을 다룹니다.
핵심 포인트
- 단순 분할 시 발생하는 문맥 단절 및 품질 저하 문제 해결
- 문단 기준 슬라이딩 윈도우 청킹 알고리즘 채택
- 이전 청크의 마지막 문단을 포함하는 중첩(overlap) 전략 사용
- FastAPI와 Python을 활용한 번역 파이프라인 구축
긴 문서를 중첩된 청크(chunks)로 나누고, 문맥을 보존하며, FastAPI를 사용하여 재조립하기
LectuLibre에서 우리는 거대 언어 모델(LLM)을 사용하여 EPUB 및 PDF 형태의 책 전체를 번역하는 AI 기반 플랫폼을 구축했습니다. 처음 Claude의 API를 연결했을 때, 우리는 순진하게도 300페이지 분량의 PDF를 단 한 번의 요청으로 입력했습니다. 결과는 즉시 실패였습니다. Claude 3 Opus는 200K 토큰의 컨텍스트 윈도우(context window)를 가지고 있지만, 300페이지 분량의 책은 쉽게 300K 토큰 이상이 될 수 있습니다. 설령 억지로 끼워 넣는다 하더라도, 출력 결과가 잘리거나 컨텍스트 윈도우의 한계 지점에서 품질이 저하될 것입니다.
따라서 우리는 전형적인 긴 문서 문제에 직면했습니다: 모델의 컨텍스트 윈도우보다 큰 책을 어떻게 번역할 것인가? 다음은 우리가 최종적으로 채택한 실제 접근 방식과 작성한 코드, 그리고 우리가 배운 교훈들입니다.
문제점: 토큰 제한은 실재한다
Claude 3 Opus 및 Haiku 모델(그리고 대부분의 LLM)은 최대 컨텍스트 길이(maximum context length)를 가집니다. Opus의 경우 200,000 토큰입니다. 토큰은 대략 단어의 3/4 정도입니다. 약 75,000 단어로 구성된 300페이지 분량의 소설은 약 100K 토큰으로 변환되므로, 이론적으로는 맞아야 합니다. 그렇지 않나요? 하지만 영어에서 스페인어로의 번역은 15~20% 정도 분량이 늘어날 수 있으며, 프롬프트 지침(prompt instructions), 시스템 메시지(system message), 그리고 사용자 메시지(user message) 자체가 모두 이 예산(budget)을 소모합니다. 게다가 모델에게 완전한 문맥을 제공하려면 매 호출마다 전체 원문 텍스트를 보내야 했습니다. 이는 실행 불가능한 방식입니다.
우리는 단순히 책을 임의의 페이지 경계에서 잘라 부분적으로 번역하는 단순한 분할 방식을 시도할 수도 있었습니다. 하지만 이는 처참하게 실패합니다. 서사가 문장 중간에 끊기고, “이전 장(the previous chapter)”과 같은 구절은 지칭 대상을 잃어버립니다. 우리에게는 더 지능적인 청킹(chunking) 전략이 필요했습니다.
우리의 접근 방식: 문단 중첩을 활용한 슬라이딩 윈도우(Sliding Window)
우리는 문단을 기준으로 하고 넉넉한 중첩(overlap)을 두는 **슬라이딩 윈도우 청킹 알고리즘(sliding window chunking algorithm)**을 채택했습니다. 아이디어는 다음과 같습니다:
- 소스 텍스트를 문단 단위(
\n\n사용)로 분할합니다. max_chunk_tokens크기의 청크(chunk)를 생성합니다(안전 마진을 두기 위해 180,000개를 사용했습니다). 문단을 하나씩 추가하며tiktoken으로 토큰 수를 계산합니다.- 청크가 제한을 초과하면 새로운 청크를 시작하되, 이전 청크의 마지막 몇 문단을 컨텍스트(context)로 포함합니다. 이 중첩(overlap, 우리는 5개 문단을 사용했습니다)은 모델이 청크 경계에서도 연속성을 유지할 수 있게 해줍니다.
- 각 청크를 독립적으로 번역한 다음, 중첩된 부분을 제거하고 다시 하나로 결합합니다.
이 방식이 완벽하지는 않습니다. 일부 장(chapter)은 여전히 분할될 수 있지만, 고정 크기 분할(fixed-size split) 방식보다 훨씬 더 많은 컨텍스트를 보존합니다.
FastAPI를 이용한 Python 구현
우리는 FastAPI의 백그라운드 태스크(background task) 내에 번역 파이프라인을 구축했습니다. 핵심 청킹(chunking) 함수는 다음과 같습니다:
import tiktoken
from typing import List
from langchain_text_splitters import RecursiveCharacterTextSplitter
...
그 다음, Anthropic의 Python SDK를 사용하여 각 청크를 번역하며, 속도 제한(rate limits)을 처리하기 위해 백프레셔(back-pressure) 및 재시도(retry) 로직을 적용합니다:
from anthropic import Anthropic, RateLimitError
import asyncio
from tenacity import retry, stop_after_attempt, wait_exponential
...
Anthropic SDK가 동기식(synchronous) 방식이기 때문에 asyncio.to_thread를 사용합니다. FastAPI 앱에서 이벤트 루프(event loop)를 차단하고 싶지 않기 때문입니다. tenacity 라이브러리는 속도 제한에 대해 지수 백오프(exponential backoff) 기능을 제공합니다. asyncio.gather를 통해 모든 청크를 병렬로 번역한 후, 다음과 같이 병합합니다:
def merge_chunks(translated_chunks: List[str], overlap_paragraphs: int = 5) -> str:
"""
중첩된 문단을 제거하며 번역된 청크들을 결합합니다
...
병렬 번역 및 성능
병렬 번역 및 성능
우리는 모든 청크 (chunk) 번역을 동시에 실행합니다. 300페이지 분량의 책의 경우, 일반적으로 각각 약 180K 토큰인 58개의 청크가 생성됩니다. Claude 3 Opus를 사용하면 각 청크를 번역하는 데 약 1530초가 소요됩니다. Anthropic의 속도 제한 (rate caps)에 걸리는 것을 방지하기 위해 동시 호출 수를 4개로 제한합니다. 전체적으로 책 한 권의 번역은 2~5분 내에 완료됩니다.
비용 (Cost): Claude 3 Opus는 비용이 많이 듭니다. 입력 토큰 100만 개당 15달러를 기준으로 할 때, 300페이지 분량의 책(청크당 약 100K 입력 토큰, 약 8개 청크)은 약 12~15달러가 소요됩니다. 우리는 대안으로 Claude 3 Haiku (더 저렴하고 빠르지만 품질은 낮음)와 DeepSeek을 제공함으로써 이를 완화했습니다. 사용자가 직접 선택할 수 있습니다.
품질 트레이드오프 (Quality trade-offs): 중첩 (overlap) 전략은 대부분의 텍스트에 잘 작동하지만, 때때로 장 (chapter)이 정확히 청크 경계에서 끝날 경우 서사의 흐름이 다소 끊기는 느낌을 줄 수 있습니다. 우리는 장 표시 (chapter markers)를 기반으로 한 동적 중첩 (예: 장 제목에서만 강제로 분할)을 실험해 보았으나, 이는 복잡성을 증가시키고 항상 토큰 제한과 일치하지는 않았습니다. 현재는 문단 수준의 중첩 방식을 유지하고 있습니다.
교훈 (Lessons Learned)
- 토큰 계산은 까다롭습니다. tiktoken의
cl100k_base는 Claude의 토크나이저 (tokenizer)와 유사하지만 동일하지는 않습니다. 토큰 수에서 5%의 차이를 확인했으므로, 제한 범위보다 20K 토큰 정도의 안전 마진을 두었습니다. - 중첩 크기가 중요합니다. 중첩이 너무 적으면 문맥 (context)을 놓치고, 너무 많으면 토큰과 비용을 낭비하게 됩니다. 대부분의 책에는 5개 문단이 가장 적절한 지점 (sweet spot)임이 증명되었습니다.
- 속도 제한 (Rate limits) 때문에 견고한 재시도 (retries) 로직을 구축해야 했습니다. Anthropic의 API는 너무 많은 동시 요청을 보내면 공격적으로 429 오류를 반환합니다.
tenacity와 동시성 세마포어 (concurrency semaphore)가 우리를 구했습니다. - 병합 (merge) 단계는 서식을 처리할 수 있어야 합니다.
\n\n을 기준으로 분할하고 다시 결합하는 방식은 산문 (prose)에는 작동하지만, 표 (tables), 목록 (lists), 코드 블록 (code blocks)은 망가집니다. 현재 우리는 마크다운 인식 분할기 (markdown-aware splitter)를 탐색 중입니다. - 비용 투명성이 매우 중요합니다. 사용자들은 300페이지 분량의 책을 번역하는 것이 무료가 아니라는 점을 이해합니다. 우리는 토큰 수를 기반으로 한 사전 비용 추정치를 보여줍니다.
현재 단계
LectuLibre의 번역 파이프라인은 현재 약 1,000페이지 분량의 EPUB 및 PDF를 처리할 수 있습니다. 우리는 소설, 기술 매뉴얼, 심지어 박사 학위 논문까지 번역해 왔습니다. 청킹 (Chunking) 방식은 놀라울 정도로 잘 버텨주었지만, 개선의 여지는 남아 있습니다: 동적 중첩 탐지 (Dynamic overlap detection), 더 나은 표 처리 (Table handling), 그리고 각 청크의 문맥을 먼저 요약하는 2단계 번역 (Two-stage translation) 방식 등이 그 예입니다.
만약 여러분이 유사한 시스템을 구축하고 있다면, 병합 로직 (Merge logic)을 과소평가하지 마세요. 청킹은 쉽지만, 최종 결과물이 하나의 일관된 책처럼 읽히도록 만드는 것이 진짜 도전 과제입니다.
장문 AI 번역에 대한 여러분의 경험은 어떠신가요? 더 나은 청킹 휴리스틱 (Chunking heuristic)을 발견하셨나요? 댓글을 통해 여러분의 생각을 들려주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기