비표준 텍스트 번역 및 의미 확인 구축 방법: Python과 LLM을 활용한 문맥 인식 도서 번역 파이프라인
요약
관용구, 은유 등 비표준 텍스트를 정확하게 번역하기 위한 Python 기반 LLM 파이프라인 구축 사례를 소개합니다. DeepSeek를 활용한 문장 탐지와 Claude 3.5 Sonnet을 이용한 문맥 기반 번역 및 설명 생성 과정을 다룹니다.
핵심 포인트
- 비표준 텍스트(관용구, 은유 등) 식별을 위한 3단계 파이프라인 구축
- DeepSeek를 활용한 저비용·고속 문장 탐지 단계 구현
- Claude 3.5 Sonnet을 통한 고품질 문맥 번역 및 의미 설명 생성
- FastAPI와 비동기 처리를 통한 효율적인 워크플로우 오케스트레이션
AI 기반 도서 번역에서 관용구, 문화적 참조, 모호한 문구를 해결하는 방법.
LectuLibre에서 저희는 AI 기반 도서 번역 서비스를 개발해 왔습니다. 저희가 직면한 가장 어려운 과제 중 하나는 단순한 문장이 아니었습니다. 바로 기계 번역이 지속적으로 망쳐놓는 관용구(idioms), 은유(metaphors), 문화적 참조(cultural references), 그리고 모호한 문구와 같은 비표준 텍스트(non-standard text)였습니다. 저희는 이러한 문구들을 정확하게 번역할 수 있는 방법뿐만 아니라, 사용자가 번역을 확인하고 편집할 수 있는 방법도 필요했습니다. 문학 작품에서는 번역이 틀리면 독서 경험 전체가 깨지기 때문입니다.
그렇게 해서 저희는 非标准文本翻译与含义确认 (비표준 텍스트 번역 및 의미 확인) 기능을 구축했습니다. 이는 까다로운 문장을 감지하고, 전체적인 의미 설명을 포함한 문맥 기반 번역을 제안하며, 사용자에게 최종 결정권을 부여하는 파이프라인입니다. 여기 그 과정에서의 시행착오를 포함한 엔지니어링 이야기를 소개합니다.
문제점
표준 LLM 번역은 사실적이고 문자 그대로의 텍스트에 대해서는 인상적인 성능을 보여줍니다. 하지만 책에서 “it’s raining cats and dogs”라고 말할 때, 대상 언어에서 이를 “동물들이 비처럼 내린다”라고 번역할 수 있는데, 이는 문맥에 따라 매우 탁월하거나 혹은 황당할 수 있습니다. 관용구는 단순한 단어 대 단어 번역으로는 놓치기 쉬운 문화적 무게를 담고 있는 경우가 많습니다. 또한, 은유와 모호한 문구는 여러 가지 유효한 해석을 가질 수 있습니다. 번역가에게 문구 뒤에 숨겨진 **의도(intent)**를 이해하는 것은 작업의 절반을 차지합니다.
저희는 다음과 같은 시스템을 원했습니다:
- 비표준 언어가 포함된 문장을 자동으로 식별합니다.
- 단순히 글자 그대로의 단어가 아닌, 원래의 의미를 보존하는 번역을 생성합니다.
- 사용자가 번역의 정확성을 판단할 수 있도록 해당 문구가 실제로 무엇을 의미하는지에 대한 쉬운 언어 설명(예: “이것은 비가 아주 많이 온다는 뜻의 영어 관용구입니다”)을 제공합니다.
- 사용자가 해당 세그먼트를 확인, 편집 또는 재번역할 수 있도록 합니다.
책 한 권은 쉽게 수십만 단어에 달할 수 있으므로, 비용과 속도가 매우 중요했습니다. 우리는 단순히 모든 데이터를 하나의 고성능 LLM에 던져 넣고 끝낼 수는 없었습니다.
우리의 접근 방식 (Our Approach)
우리는 문제를 세 단계의 파이프라인으로 나누었습니다:
- 탐지 (Detection) – 모든 문장을 스캔하여 관용구(Idioms), 은유(Metaphors) 또는 문화적 참조(Cultural references)가 포함될 가능성이 있는 문장에 플래그를 지정합니다.
- 번역 및 설명 (Translation & Explanation) – 플래그가 지정된 각 문장에 대해, 더 강력한 모델을 호출하여 번역, 주요 용어 용어집(Glossary), 그리고 의미 설명을 생성합니다.
- 사용자 확인 (User Confirmation) – UI에 결과를 제시하고, 사용자가 검토 및 승인하거나 편집할 수 있도록 합니다.
이 파이프라인은 우리의 FastAPI 백엔드 내부에서 비동기(Asynchronously) 방식으로 오케스트레이션됩니다. 탐지 단계에는 빠르고 저렴한 DeepSeek를 선택했고, 최종 번역 단계에는 테스트 결과 일관되게 가장 뛰어난 문맥적 결과물을 제공한 Claude 3.5 Sonnet을 선택했습니다. 두 모델 모두 공식 Python SDK(DeepSeek를 위한 openai, Claude를 위한 anthropic)를 사용하여 REST API를 통해 접근했습니다.
탐지 프롬프트 (Detection Prompt)
탐지 프롬프트는 퓨샷(Few-shot) 방식으로, 모델이 is_non_standard (boolean)와 reason (string)을 포함하는 JSON 객체를 출력하도록 요청합니다. 또한 무엇이 비표준이고 무엇이 비표준이 아닌지에 대한 몇 가지 예시를 함께 제공합니다.
detection_prompt = """
You are a linguistic classifier. Your job is to determine whether a sentence contains non‑standard language:
- Idioms (e.g., "kick the bucket")
...
우리는 reason을 요청하는 것이 모델로 하여금 조금 더 깊이 생각하게 만들어, 오탐(False positives)을 줄인다는 것을 발견했습니다. 2,000개의 문장으로 구성된 테스트 세트에서 탐지 정확도는 약 92%(정밀도(Precision) 0.88, 재현율(Recall) 0.91)였습니다. 미탐(False negatives)은 인간의 검토를 전혀 거치지 못하기 때문에 매우 치명적이었습니다. 이를 완화하기 위해, 사용자가 어떤 문장이든 수동으로 비표준으로 표시할 수 있는 UI 토글을 추가했습니다.
번역 프롬프트 (Translation Prompt)
플래그(flagged)가 지정된 문장의 경우, 우리는 더 정교한 프롬프트와 함께 Claude를 호출합니다. 모델은 번역문, 대상 언어로 작성된 meaning_explanation(사용자가 쉽게 읽을 수 있도록), 그리고 선택 사항인 주요 문화 특화 용어 용어집(glossary)을 포함하는 JSON 객체를 반환해야 합니다.
translation_prompt = """
당신은 문학 번역가입니다. 다음 문장을 {source_lang}에서 {target_lang}로 번역하세요.
문장에 관용구, 은유 또는 문화적 참조가 포함되어 있다면, 글자 그대로의 단어가 아닌 의미가 보존되도록 적응시키세요.
...
"""
LLM이 가끔 유효하지 않은 JSON을 출력할 수 있기 때문에, 우리는 json.loads를 재시도 루프(retry loop) 및 폴백(fallback) 로직으로 감싸 처리합니다. 몇 번의 반복 후에도 파싱에 실패하면, 원본 출력(raw output)을 저장하고 수동 검토를 위해 플래그를 지정합니다.
비동기 파이프라인 (The Async Pipeline)
책 한 권 전체를 처리한다는 것은 수천 개의 문장을 처리함을 의미합니다. 우리는 사용자의 요청을 차단하고 싶지 않았기에, 파이프라인을 백그라운드 asyncio 태스크로 실행합니다. 컨트롤러는 다음과 같은 단순한 함수로 구성됩니다:
- 정규 표현식(regex)과 언어 인식 토크나이저(language-aware tokenizer)를 사용하여 책을 문장 단위로 분할합니다 (유럽 언어의 경우
sacremoses를 사용했습니다). - API 호출을 줄이기 위해 문장을 5개씩 묶어 배치(batch)로 만듭니다.
- 동시성 세마포어(concurrency semaphore)를 사용하여 탐지(detection) 배치를 처리합니다.
- 플래그가 지정된 문장들을 수집하고, 번역을 위해 또 다른 세마포어를 사용하여 병렬로 처리합니다.
- SQLAlchemy 비동기(async)를 통해 PostgreSQL에 결과를 저장합니다.
다음은 핵심 루프의 스케치입니다:
import asyncio
# 속도 제한(rate limits)을 준수하기 위한 세마포어
...
단순함을 위해 asyncio.gather를 사용했습니다. 실제 운영 환경(production)에서는 적절한 에러 핸들링(error handling)과 호출당 재시도 로직(exponential backoff, 지수 백오프)을 추가했습니다. 일반적인 5만 단어 분량의 도서(3,000개 문장)를 기준으로, 탐지(detection) 단계는 약 2분(동시성 10, 호출당 약 200ms, 총 600회 API 호출)이 소요되었고, 플래그가 지정된 20%의 문장에 대한 번역(동시성 5, 호출당 약 1s, 총 600회 호출)에 추가로 2분이 소요되었습니다. 총 처리 시간은 약 45분이었습니다. 비용은 출력 길이에 따라 탐지(DeepSeek)에 약 $0.50, 번역(Claude)에 약 $4~$8가 발생했습니다. 무료는 아니지만, 품질 향상을 고려하면 수용 가능한 수준입니다.
사용자 확인 및 데이터베이스
마지막 단계는 사용자가 검토하고 편집할 수 있도록 하는 것이었습니다. 우리는 모든 번역을 translations 테이블에 저장했습니다. 각 행(row)은 다음 항목을 포함합니다:
sentence_index(도서 내 위치)original_text(원문)is_non_standard(비표준 여부, bool)translation(번역문, nullable)meaning_explanation(의미 설명, nullable)glossary(용어집, JSONB)user_confirmed(사용자 확인 여부, bool, 기본값 false)user_translation(사용자 번역문, nullable, 사용자가 덮어쓸 때 설정)
SQLAlchemy 비동기 모델 (단순화 버전):
from sqlalchemy import Column, Integer, String, Boolean, JSON, ForeignKey
from sqlalchemy.ext.asyncio import AsyncAttrs
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
...
FastAPI 엔드포인트 POST /books/{book_id}/translations/{translation_id}/confirm는 선택적인 편집 사항을 수락하고 확인 플래그(confirmation flag)를 설정합니다. 이를 통해 사용자는 번역을 개별적으로 승인하거나, 검토 후 모든 항목을 일괄 확인(bulk-confirm)할 수 있습니다.
교훈 및 트레이드오프 (Trade-offs)
프롬프트 엔지니어링 (Prompt engineering)이 승패의 절반을 결정합니다. 탐지 단계에서는 초기에 미묘한 은유(metaphor)에 대해 많은 미탐(false negatives)이 발생했습니다. reason (이유) 필드를 추가하고 몇 가지 부정적 예시(negative examples)를 제공함으로써 에러율을 크게 낮출 수 있었습니다. 번역의 경우, 대상 언어(영어가 아닌)로 설명을 요청하도록 설정한 것이 영어를 사용하지 않는 사용자들에게 훨씬 더 유용하게 작용했습니다.
탐지에는 저렴한 모델을, 번역에는 고비용 모델을 사용합니다. 이는 전형적인 비용-정확도 트레이드오프 (cost-accuracy trade-off)입니다. DeepSeek는 Claude보다 10배 이상 저렴하며, 이진 분류 (binary classification) 작업에서 잘 작동합니다. 하지만 약 8%의 미탐율 (false negative rate)을 감수해야 했으며, 이를 사용자 친화적인 수동 수정 (manual override) 기능으로 보완했습니다.
세마포어 (semaphores)를 이용한 동시성 제어는 간단하지만 한계가 있습니다. 현재로서는 우리의 VPS가 부하를 충분히 처리하고 있지만, 동시에 업로드되는 도서의 양이 늘어난다면 Celery와 같은 적절한 태스크 큐 (task queue)가 필요할 것입니다. 현재의 처리량에는 asyncio 세마포어와 FastAPI 백그라운드 태스크 (background tasks)만으로도 충분했기에 구조를 단순하게 유지했습니다.
사용자 확인 단계는 번거로움을 유발하지만 신뢰를 구축합니다. 출판사들은 AI가 왜 특정 선택을 했는지 확인할 수 있는 점을 매우 좋아했습니다. 일반 사용자들은 이를 다소 지루하게 느꼈기에, 모든 것을 사전 승인하되 여전히 점검(spot-check)은 가능하도록 하는 "빠른 승인 (quick approve)" 모드를 추가했습니다.
배치 탐지 (Batch detection)는 API 호출을 절약하지만 문맥을 놓칠 위험이 있습니다. 5개의 문장을 하나의 프롬프트 (prompt)로 그룹화함으로써 탐지 호출을 80% 줄였지만, 여러 문장에 걸쳐 있는 관용구 (idioms)를 간혹 놓치는 경우가 발생했습니다. 현재 번역 전에 플래그(flag)가 지정된 인접 문장들을 병합하는 방식을 실험하고 있습니다.
향후 계획
우리는 탐지 비용을 거의 제로에 가깝게 만들기 위해, 선별된 관용구 세트와의 임베딩 유사도 (embedding similarity)를 사전 필터 (pre-filter)로 사용하는 방안을 검토하고 있습니다. 또한, 탐지 전용 소규모 로컬 모델을 파인튜닝 (fine-tune)하여 API 비용을 완전히 제거할 계획입니다.
전체 파이프라인을 직접 시도해 보실 수 있습니다. 코드는 아직 오픈 소스가 아니지만, 프롬프트 템플릿과 비동기 워커 (async worker)의 요약본을 저희 GitHub (placeholder link)에 공유해 두었습니다.
비슷한 시스템을 구축해 보셨다면, 탐지 단계(detection phase)를 어떻게 처리하셨나요? 댓글을 통해 다른 접근 방식들을 듣고 싶습니다.
핵심 요약 (Takeaway): 명확한 사용자 확인 단계가 포함된 2단계 LLM 파이프라인은 문학 텍스트의 번역 품질을 획기적으로 향상시킬 수 있습니다. 다만, 프롬프트 튜닝 (prompt tuning)과 비동기 오케스트레이션 (asynchronous orchestration)에 투자할 준비가 되어 있어야 합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기